以下是一些最佳实践,可帮助您组织代码,使其与 WordPress 核心及其他 WordPress 插件协同工作良好。

避免命名冲突

当您的插件使用与其他插件相同的名称来定义变量、函数或类时,就会发生命名冲突。

幸运的是,您可以通过使用以下方法来避免命名冲突。

过程式编码方法

默认情况下,所有变量、函数和类都在全局命名空间中定义,这意味着您的插件可能会覆盖其他插件设置的变量、函数和类,反之亦然。在函数或类内部定义的变量不受此影响。

为所有内容添加前缀

所有全局可访问的代码都应使用唯一的标识符进行前缀处理。前缀可防止与其他插件发生冲突,并防止它们覆盖您的变量或意外调用您的函数和类。

为了防止与其他插件发生冲突,您的前缀应至少为 4 个字母长,但我们建议为 5 个字母。您应避免使用常见的英文单词,而是选择与您插件相关的独特名称。仅在 WordPress.org 上我们就托管了数万个插件。在我们服务器之外还有数十万个更多。您肯定会遇到冲突。

一种好的做法是使用前缀。例如,如果您的插件名为“Easy Custom Post Types”,那么您可以使用如下名称:

  • function ecpt_save_post()
  • define( 'ECPT_LICENSE', true );
  • class ECPT_Admin{}
  • namespace EasyCustomPostTypes;
  • update_option( 'ecpt_settings', $settings );

因为您作为WordPress项目的一部分编写代码,所以必须避免使用与 WordPress 核心高度可能冲突的前缀。包括但不限于:__(双下划线)、wp_、WordPress或_(单下划线)。

如果您为“子”插件(例如 WooCommerce 扩展)编写代码,同样需要避免使用它们的正常/常见前缀(即 Woo、WooCommerce)。

您可以在类或命名空间内部使用它们,但不能作为独立的函数/命名空间/类使用。

如果您使用_n()或__()进行翻译,这是可以的。我们仅在谈论您为插件创建的函数,而不是来自 WordPress 的核心函数。事实上,这些核心功能正是您需要不在自己的插件中使用这些前缀的原因!您不希望为了您的用户而破坏 WordPress。

请记住:好的前缀名称是独特且专属于您的插件的。这将有助于您和下一个人在调试时工作,并防止冲突。

必须添加前缀的代码包括:

  • 函数(除非已命名空间化)
  • 类、接口和特性(除非已命名空间化)
  • 命名空间
  • 全局变量
  • 选项和临时数据

检查现有实现

PHP 提供了一些函数来验证变量、函数、类和常量的存在性。如果实体存在,所有这些函数都将返回 true。

请记住,使用(!function_exists('NAME ')) {围绕您所有的函数和类听起来是个好主意,直到您意识到其致命缺陷。如果其他东西具有相同名称的函数且它们的代码先加载,您的插件就会损坏。使用 if-exists 来替换/覆盖函数或类应仅保留给共享库。

示例

// Create a function called "wporg_init" if it doesn't already exist
if ( ! function_exists( 'wporg_init' ) ) {
    function wporg_init() {
        register_setting( 'wporg_settings', 'wporg_option_foo' );
    }
}

// Create a function called "wporg_get_foo" if it doesn't already exist
if ( ! function_exists( 'wporg_get_foo' ) ) {
    function wporg_get_foo() {
        return get_option( 'wporg_option_foo' );
    }
}

面向对象编程方法

解决命名冲突问题的一种更简单的方法是使用类来编写您的插件代码。

您仍然需要检查您想要的类的名称是否已被占用,但其余部分将由 PHP 处理。

示例

if ( ! class_exists( 'WPOrg_Plugin' ) ) {
    class WPOrg_Plugin {
        public static function init() {
            register_setting( 'wporg_settings', 'wporg_option_foo' );
        }

        public static function get_foo() {
            return get_option( 'wporg_option_foo' );
        }
    }

    WPOrg_Plugin::init();
    WPOrg_Plugin::get_foo();
}

文件组织

您的插件目录的根级别应包含plugin-name.php文件,以及可选的uninstall.php文件。所有其他文件应尽可能组织到子文件夹中。

文件夹结构

清晰的文件夹结构有助于您和其他参与您插件工作的人将类似的文件放在一起。

以下是一个供参考的示例文件夹结构:

/plugin-name
     plugin-name.php
     uninstall.php
     /languages
     /includes
     /admin
          /js
          /css
          /images
     /public
          /js
          /css
          /images

插件架构

您为插件选择的架构或代码组织方式很可能取决于插件的大小。

对于小型、单一用途且与 WordPress 核心、主题或其他插件交互有限的插件,构建复杂类几乎没有好处;除非您知道该插件将来会大幅扩展。

对于大型插件和大量代码,请从一开始就考虑使用类。将样式和脚本文件分开,甚至将构建相关文件也分开。这将有助于代码组织和插件的长期维护。

条件加载

将管理代码与公共代码分开很有帮助。使用条件is_admin()。您仍然需要执行能力检查,因为这并不表示用户已认证或具有管理员级别访问权限。请参阅检查用户能力。

例如:

if ( is_admin() ) {
    // we are in admin mode
    require_once __DIR__ . '/admin/plugin-name-admin.php';
}

避免直接文件访问

作为安全措施,如果未定义ABSPATH全局变量,则禁止访问是一种良好的做法。这仅适用于包含类或函数定义之外的代码的文件,例如主插件文件。

您可以通过在文件顶部包含以下代码来实现这一点:

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

架构模式

虽然存在多种可能的架构模式,但它们可以大致分为三种变体:

架构模式详解

上述更复杂的代码组织的特定实现已作为教程和幻灯片编写:

模板起始点

对于您编写的每个新插件,您可能不想从零开始,而是想从一个模板开始。使用模板的一个好处是使您的插件之间保持一致性。如果您使用其他人已经熟悉的模板,那么使用模板也会使其他人更容易为您的代码做出贡献。

这些也作为不同但可比较的架构的进一步示例。

  • WordPress 插件模板:一个用于 WordPress 插件开发的基础,旨在为构建您的插件提供清晰且一致的指南。
  • WordPress 插件 Bootstrap:使用 Grunt、Compass、GIT 和 SVN 开发 WordPress 插件的基本启动框架。
  • WP Skeleton Plugin:专注于单元测试和使用 Composer 进行开发的骨架插件。
  • WP CLI Scaffold:WP CLI 的 Scaffold 命令会创建一个骨架插件,选项包括 CI 配置文件等

当然,您可以采用这些和其他方面的不同方面来创建您自己的自定义模板。