启动一个社区Wiki收集 客观的 插件开发的最佳实践。这个问题的灵感来自 @eamann对WP-Hackers的评论.

这个想法是在可能是什么客观最佳实践上进行协作,以便我们可以最终在某些社区协作审查过程中使用它们。

更新: 在看到了前几个回复之后,很明显,我们只需要一个想法/建议/最佳实践答案,人们应该审查列表,以确保发布前没有重复。

有帮助吗?

解决方案

使用动作和过滤器

如果您认为人们想添加或更改一些数据: 提供 apply_filters() 返回之前.

PS我发现一件事令人失望,您的问题地址是仅专为最终用户设计的插件的百分比,即没有自己的钩子。想象一下WordPress是否像大多数插件一样设计?这将是不灵活的,并且是一个非常利基的解决方案。

如果WordPress能够自动启动插件在哪些其他插件上依赖于哪个插件,也许情况会有所不同?因为我通常必须写很多我需要从头开始需要的功能,因为客户需要某种方式和可用的插件,而那里的插件为90%,不允许我灵活地更新剩余的10%。

我确实希望那些领导WordPress社区的人能确定一种方法来确保插件获得遵循最佳实践(例如为其他开发人员添加挂钩)的奖励,就像在Stackexchange网站上获得了好的答案一样。

让我们以榜样为例 另一个问题:

示例:当有人转发文章时,我想在插件中做某事。如果在我可以挂接并开火的任何流行转发插件中都有自定义挂钩,那就太好了。没有,所以我可以修改它们的插件以包括在内,但是只能适用于我的副本,我不想尝试重新分配它。

有关的

其他提示

加载脚本/css wp_enqueue_scriptwp_enqueue_style

插件不应加载 /尝试加载JS / CSS文件的重复版本,尤其是jQuery和WP Core中包含的其他JS文件。

插件应始终使用 wp_enqueue_scriptwp_enqueue_style 链接JS和CSS文件时,从不直接通过 <script> 标签。

有关的

I18N支持

即使开发人员对翻译自己的插件没有兴趣,所有输出字符串均应链接到适当的文本域,以允许有兴趣的各方进行国际化。

请注意,加载语言文件在期间非常重要 init 操作使用户可以将其连接到动作中。

请参阅法典: WordPress开发人员的I18N

还有本文: 正确加载WP语言文件正确.

由于WordPress 4.6+

WP 4.6更改了负载订单和检查的位置,这使开发人员和用户更容易。

考虑使用文本域“ my-plugin”的插件,WordPress现在将首先查找一个翻译文件:
/wp-content/languages/plugins/my-plugin-en_us.mo

如果它找不到一个,然后将寻找插件告诉它的位置(如果遵循法典,则在pluigns的“语言”文件夹中使用):
/wp-content/plugins/my-plugin/languages/my-plugin-en_us.mo

最后,如果找不到语言文件,它将检查以下默认位置:
/wp-content/languages/my-plugin-en_us.mo

第一个检查以4.6添加,并为用户提供了一个定义的添加语言文件的位置,就像以前需要知道开发人员添加了语言文件的位置一样,现在用户只需要知道插件的文本domain:/wp-content/languages/plugins/textdomain-local.mo


以下是旧方法(由于WP 4.6+而不相关)

[...]
最后,我想指出的是,这对于 加载WP_LANG_DIR的自定义用户语言文件,然后加载带有插件的语言文件. 。当为同一域加载多个mo文件时,将使用第一个发现的翻译。这样,该插件提供的语言文件将作为无法由用户翻译的字符串的后备。

public function load_plugin_textdomain()
{
    $domain = 'my-plugin';
    // The "plugin_locale" filter is also used in load_plugin_textdomain()
    $locale = apply_filters( 'plugin_locale', get_locale(), $domain );

    load_textdomain( 
            $domain, 
            WP_LANG_DIR . '/my-plugin/' . $domain . '-' . $locale . '.mo' 
    );
    load_plugin_textdomain( 
            $domain, 
            FALSE, 
            dirname( plugin_basename(__FILE__) ) . '/languages/' 
    );
}

确保插件对WP_DEBUG不会产生任何错误

始终使用 WP_DEBUG 打开,理想情况下,它在整个开发过程中打开。插件不应与 WP_DEBUG 上。这包括未弃用的通知和未检查的索引。

要打开调试,请编辑您的 wp-config.php 文件以便 WP_DEBUG 常数设置为 true. 。看到 在调试上的法典 更多细节。

首先在WordPress Core中使用现有功能

如果你可以的话: 使用WordPress Core中包含的现有功能 而不是写自己的。仅在WordPress Core中没有适当的预先存在函数时,仅开发自定义PHP功能。

一个好处是您可以使用 “日志弃用通知” 轻松监视应更换的功能。另一个好处是,用户可以在法典中查看功能文档,并且即使不是经验丰富的PHP开发人员,也可以更好地了解插件的功能。

有关的

卸载应删除插件的所有数据

从WordPress安装中删除后, 插件应删除所有文件,文件夹,数据库条目和表 它以及 选项值 它创建了。

插件可以提供导出/导入设置的选项,因此可以在删除之前将设置保存在WordPress之外。

有关的

通过输入数据预防SQL注入

插件 应该 消毒直接或间接检索的所有用户输入(例如通过 $_POST 或者 $_GET)在使用输入值查询MySQL数据库之前。

看: 格式化SQL语句.

前缀所有全局名称空间项目

插件应适当前缀所有全局名称空间项目(常数,功能,类,变量,甚至诸如自定义分类法,邮政类型,小部件等)。例如,不要创建一个称为的函数 init();相反,命名类似 jpb_init().

它的常见应在名称面前使用三个或四个字母前缀或使用 PHP名称空间功能. 。相比: PHP类常数的单个字母前缀?

有关的

使用类和对象导向的PHP5代码

没有理由不编写干净,面向对象的PHP5代码。 PHP4支持将在下一个版本(WP 3.1)之后逐步淘汰。当然,您可以将所有函数名称前缀最终以endless_long_function_names_names_with_lots_of_underscores提供,但是只需写一个简单的类并捆绑所有内容就容易得多。另外,将您的班级放入单独的文件中并相应地命名,以便您可以轻松地扩展并维护它:

// in functions.php
require 'inc/class-my-cool-plugin.php';
new MyCoolPlugin();

// in inc/class-my-cool-plugin.php
class MyCoolPlugin {
    function __construct() {
        // add filter hooks, wp_enqueue_script, etc.

        // To assign a method from your class to a WP 
        // function do something like this
        add_action('admin_menu', array($this, "admin"));
    }

    public function admin() {
        // public methods, for use outside of the class
        // Note that methods used in other WP functions 
        // (such as add_action) should be public
    }

    private function somethingelse() {
        // methods you only use inside this class
    }
}

停用不应引起数据损害

插件 不应该 删除其任何数据 停用.

有关的

仅包括您需要的文件...

如果您处于前端,则不包括与管理区域有关的代码。

宣布有关插入插图的数据传播

卸载 插件 应该 提示用户将删除其数据 并收到确认用户可以在此之前删除数据的确认和插件 应该允许用户选项保留数据 卸载。 (@eamann的想法。)

有关的

让插件的文件夹名称更改

/plugins/pluginname/{verty}

该文件夹使用的“插件名”应始终可更改。

通常通过定义常数并在整个插件中始终使用它们来处理这一点。

不用说许多受欢迎的插件都是罪人。

有关的:

使用WordPress(内置)错误处理

不只是 return; 如果某些用户输入是错误的。向他们提供一些有关的信息是错误的。

function some_example_fn( $args = array() ) 
{
    // If value was not set, build an error message
    if ( ! isset( $args['some_value'] ) )
        $error = new WP_Error( 'some_value', sprintf( __( 'You have forgotten to specify the %1$s for your function. %2$s Error triggered inside %3$s on line %4$s.', TEXTDOMAIN ), '$args[\'some_value\']', "\n", __FILE__, __LINE__ ) );

    // die & print error message & code - for admins only!
    if ( isset( $error ) && is_wp_error( $error ) && current_user_can( 'manage_options' ) ) 
        wp_die( $error->get_error_code(), 'Theme Error: Missing Argument' );

    // Elseif no error was triggered continue...
}

一个错误(对象)

您可以在引导程序期间为主题或插件设置一个全局错误对象:

function bootstrap_the_theme()
{
    global $prefix_error, $prefix_theme_name;
    // Take the theme name as error ID:
    $theme_data = wp_get_theme();
    $prefix_theme_name = $theme_data->Name;
    $prefix_error = new WP_Error( $theme_data->Name );

    include // whatever, etc...
}
add_action( 'after_setup_theme', 'bootstrap_the_theme' );

稍后您可以按需添加无限错误:

function some_theme_fn( $args )
{
    global $prefix_error, $prefix_theme_name;
    $theme_data = wp_get_theme();
    if ( ! $args['whatever'] && current_user_can( 'manage_options' ) ) // some required value not set
        $prefix_error->add( $prefix_theme_name, sprintf( 'The function %1$s needs the argument %2$s set.', __FUNCTION__, '$args[\'whatever\']' ) );

    // continue function...
}

然后,您可以在主题结束时全部获取它们。这样,您就不会中断渲染页面,并且仍然可以输出所有错误以开发

function dump_theme_errors()
{
    global $prefix_error, $prefix_theme_name;

    // Not an admin? OR: No error(s)?
    if ( ! current_user_can( 'manage_options' ) ! is_wp_error( $prefix_error ) )
        return;

    $theme_errors = $prefix_error->get_error_messages( $prefix_theme_name );
    echo '<h3>Theme Errors</h3>';
    foreach ( $theme_errors as $error )
        echo "{$error}\n";
}
add_action( 'shutdown', 'dump_theme_errors' );

您可以在 这个问. 。一个相关的机票,以修复的“一起工作” WP_Errorwp_die() 从那里链接,然后将另一张票。评论,批评家等等。

最小化添加到全局名称空间的名称

插件 应该 减少影响 尽可能多 最小化其添加到全局名称空间的名称数量.

这可以通过将插件的功能封装到类中或使用该插件来完成 PHP名称空间功能. 。将所有内容的前缀也可以提供帮助,但并不是那么灵活。

在功能和类旁边,插件 不应该 引入全局变量。使用通常会使它们的类固定,并简化插件维护。

有关的

使用PHPDOC评论

最佳实践接近PHPDOC风格。如果您不使用诸如“ Eclipse”之类的IDE,则可以看一看 在PHPDOC手册中.

您不必确切知道这是如何工作的。无论如何,专业开发人员都可以阅读代码,只需要作为摘要即可。爱好编码人员和用户可能会欣赏您在相同的知识水平上解释它的方式。

在add_option之前使用设置API

您不应通过add_option函数将选项添加到db,而是将它们作为数组存储在数组中 设置API 这为您照顾了一切。

在add_option之前使用主题修改API

修改API 是一种非常简单的结构,也是一种允许添加和检索选项的安全方法。一切都将作为序列化值保存在数据库中。简单,安全和简单。

保护插件用户隐私

(以前:匿名API通信)

如果插件与外部系统或API通信(例如某些WebService),则应匿名进行操作,或为用户提供匿名选项,以确保与插件用户无关的数据泄漏到第二方不受控制。

wordpress.org上的主机插件

使用 SVN 存储库 在wordpress.org上提供 用于托管插件。它可以更轻松地更新用户体验,如果您以前从未使用过SVN,则可以通过在证明其合理的上下文中使用它来真正理解它。

使用权限提供访问控制

在许多情况下,用户可能不希望每个人都可以访问您的插件创建的区域,尤其是使用多个复杂操作的插件,单个硬编码的功能检查可能还不够。

至少,可以使用适当的功能检查您的插件可以使用的所有不同类型的过程。

导入 /导出插件设置

在插件之间并不常见,但是如果您的插件具有(某些)设置,则 应该 提供诸如配置和用户输入之类的数据导入 /导出.

导入/导出可改善插件的可用性。

具有这样的进出口功能(以及撤消机制)的示例 - 拼词是 BreadCrumb Navxt(WordPress插件) (完全披露:我在那里的一些小代码,大多数是由MTEKK完成的)。

有关的

组织您的代码

很难读取未按照其执行的顺序编写的代码。首先包括/require,定义,wp_enqueue_style&_script等,然后插件/主题所需的功能以及终于构建构建器(例如,admin屏幕,主题中集成的内容等)。

尝试将自己的文件夹中的CS和JS之类的内容分开。还要尝试使用仅是助手的功能,例如阵列扁平机和类似的助手。将“主”文件保持清洁和易于阅读是一种帮助用户,开发人员和您的方式,当您尝试在一年中更新并且没有更长的时间看待代码时。

最好经常重复一个结构,因此您总是找到自己的路。在不同的项目上开发已知结构将使您有时间使它变得更好,即使您的客户切换到另一个开发人员,您也永远不会听到“他留下混乱”。这建立了您的声誉,应该是一个长期目标。

以风格死亡

dec所有插件(甚至主题)函数都应使用 wp_die() 在关键的地方,向用户提供有关发生的事情的一些信息。 PHP错误很烦人, wp_die 可以为用户提供关于插件(或他们)做错什么的精美样式消息。另外,如果用户进行调试,则停用了插件,插件将断开。

使用 wp_die() 还可以帮助您的插件 /主题与 WordPress Testsuite.

有关的:

为用户提供帮助屏幕

说RTFM(单击帮助)比不必一次又一次回答问题要好。

/**
  * Add contextual help for this screen
  * 
  * @param $rtfm
  * @uses get_current_screen
  */ 
  function ContextualHelp( /*string*/ $rtfm) 
  { 
     $current_screen = get_current_screen();
     if ($current_screen->id == $this->_pageid) 
     {
        $rtfm .= '<h3>The WordPress Plugin - Screen A</h3>';
        $rtfm .= '<p>Here are some tips: donate to me ' .
     }
     return $rtfm; 
  }
add_action('contextual_help', array($this,'ContextualHelp'),1,1);

更新 /注意: (请参阅Kaiser的评论):上面的示例应在课堂上使用

提供可扩展的形式

当插件提供输入数据的可能性时,在“提交”和/或“重置”按钮之前,它始终应在末端有一个挂钩,因此开发人员可以轻松地使用字段,而且还可以使用按钮扩展表单。

看: 设置API

有关的

始终通过钩子包含功能,而不是直接。

例子:

  • 不要使用新的插件通过新的new now now

  • 使用Hook Plugins_loaded

    // add the class to WP                                   
    function my_plugin_start() {                                                               
        new my_plugin();   
    }                                                        
    add_action( 'plugins_loaded', 'my_plugin_start' );
    

更新:一个小的现场例子: 插件-SVN-Trunk页面和伪示例

//avoid direct calls to this file where wp core files not present
if (!function_exists ('add_action')) {
        header('Status: 403 Forbidden');
        header('HTTP/1.1 403 Forbidden');
        exit();
}

if ( !class_exists( 'plugin_class' ) ) {
    class plugin_class {

        function __construct() {
        }

    } // end class

    function plugin_start() {

        new plugin_class();
    }

    add_action( 'plugins_loaded', 'plugin_start' );
} // end class_exists

您也可以通过Mu_plugins_加载在多功能装置上加载,请参阅Codex以获取操作参考: http://codex.wordpress.org/plugin_api/action_reference您还可以看到,这个钩子如何插入wp: http://adambrown.info/p/wp_hooks/hook/plugins_loaded?version=2.1&file=wp-settings.php我经常使用它,而且它不是那么硬,更早,更好地像一个艰苦的新班();

GPL兼容许可证的许可证插件

插件和主题 应该 根据WordPress兼容许可获得许可。这使他们能够将其重新分配给WordPress作为“程序”。建议的许可是 GPL. 注意插件中包含的所有代码库都与相同的许可证兼容。

(这有 是一个问题 和认真 辩论点 过去和 当下.)

您的插件描述应准确详细详细介绍插件的功能。有10个特色后插件。所有这些都展示了特色帖子,但许多功能都具有不同的功能。通过阅读说明,可以很容易地将插件与类似插件进行比较。

您应该避免吹牛插件的简单程度,除非它确实是非常基本的。您应该在描述中包括有用的链接,例如指向设置的链接。

最小化远程数据源和网络服务的副作用

插件 应该 缓存/盾牌网站服务 和/或 XMLRPC/SOAP请求通过缓存/数据提供层 如果您使用它们,以免让(慢速)Web服务响应进行前要求。

其中包括下载RSS feed和其他页面。设计您的插件,以便在后台请求数据。

一个可能的步骤是(以ping.fm为例):创建一个缓冲表

  1. 对于您要提交更新到ping.fm时,将其添加到此表中。
  2. 现在,我们需要创建一个插件来处理此数据。该插件将通过crontab运行,以检查尚未提交的每个更新
  3. 因为我们有此表,所以我们还可以列出提交给ping.fm的每个消息并检查每个帖子的状态。以防万一ping.fm方面存在问题,我们可以重新提交它。

测试您的插件

我们应该在插件开发环境中确定有一些测试工具。

基于 这个答案 经过 Ethan Seifert 对于一个测试问题,这些是值得关注的良好实践:

  • 您的单元测试应测试类可以执行的最小行为。
  • 当您达到功能测试的级别时,您可以在其中使用WordPress依赖项测试代码。
  • 取决于您的插件的作用 - 考虑使用基于硒的测试,该测试通过使用ID来测试DOM中数据的存在
许可以下: CC-BY-SA归因
scroll top