it-swarm.cn

插件开发的目标最佳实践?

启动社区wiki以收集objective插件开发的最佳实践。这个问题的灵感来自 @ EAMann对wp-hackers的评论

我们的想法是就哪些客观的最佳实践进行合作,以便我们最终可能在一些社区协作审核流程中使用它们。

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

131
MikeSchinkel

使用操作和过滤器

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

附:有一件事我觉得有点令人失望,你的问题解决的是仅为最终用户设计的插件的百分比,即没有自己的钩子。想象一下,如果WordPress的设计与大多数插件一样吗?它将是不灵活的,是一个非常小众的解决方案。

如果WordPress能够自动安装其他插件所依赖的插件,那么事情会有所不同吗?因为它是我通常必须从头开始编写我需要的许多功能,因为客户想要某种方式和可用的插件,而90%那里,不允许我灵活地更新剩余的10%。

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

让我们从 另一个问题

示例:当有人转发文章时,我想在我的插件中执行某些操作。如果有一个自定义钩子,无论流行的转推插件是什么,我可以挂钩和发射,这将是伟大的。没有,所以我可以修改他们的插件来包含它,但这只适用于我的副本,我不想尝试重新分发它。

有关

69
Arlen Beiler

使用wp_enqueue_scriptwp_enqueue_style加载脚本/ CSS

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

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

有关

53
Rick Curran

我支持

所有输出字符串都应链接到适当的文本域,以允许相关方进行国际化,即使开发人员不想翻译自己的插件。

请注意,在init操作期间加载语言文件非常重要,以便用户可以挂钩操作。

请参阅Codex: I18n for WordPress Developers

还有这篇文章: 正确加载WP语言文件

自WordPress 4.6+

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

考虑到一个带有textdomain'my-plugin'的插件,WordPress现在将首先在以下位置查找翻译文件:
/ wp-content/languages/plugins/my-plugin-en_US.mo

如果它找不到那里,那么它将寻找插件告诉它看的一个(通常在插件的'language'文件夹中,如果遵循codex):
/ wp-content/plugins/my-plugin/languages/my-plugin-en_US.mo

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

第一次检查是在4.6中添加的,并为用户提供了添加语言文件的定义位置,因为之前他们需要知道开发人员添加语言文件的位置,现在用户只需要知道插件的textdomain:/ 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/' 
    );
}
49
EAMann

确保插件使用WP_DEBUG生成无错误

始终使用 WP_DEBUG 开启测试您的插件,并在理想情况下在整个开发过程中将其打开。插件不应该在WP_DEBUG上引发任何错误。这包括已弃用的通知和未经检查的索引。

要打开调试,请编辑wp-config.php文件,以便将WP_DEBUG常量设置为true。有关详细信息,请参阅 Codex on Debug

47
John P Bloch

首先在WordPress核心中使用现有函数

如果你能: 使用WordPress核心中包含的现有函数 而不是编写自己的函数。只有在WordPress核心中没有适当的预先存在的函数时才开发自定义PHP函数。

一个好处是您可以使用“log deprecated notice”来轻松监控应该替换的功能。另一个好处是用户可以查看Codex中的函数文档,并更好地了解插件的功能,即使他们不是经验丰富的PHP开发人员。

有关

41
kaiser

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

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

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

有关

33
Travis Northcutt

使用输入数据防止SQL注入

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

请参阅: 格式化SQL语句

33
MikeSchinkel

使用面向对象的PHP5代码

没有理由不编写干净的,面向对象的PHP5代码。 PHP4支持将在下一个版本(WP 3.1)之后逐步淘汰。当然,你可以在所有函数名前加上最后的infinitely_long_function_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
    }
}
30
Husky

前缀所有全局命名空间项

插件应该正确地为所有全局命名空间项(常量,函数,类,变量,甚至自定义分类法,帖子类型,小部件等等)添加前缀。例如,不要创建一个名为init()的函数;相反,将其命名为jpb_init()

它的常见应该在名称前使用三个或四个字母前缀或使用 PHP命名空间功能 。比较: PHP类常量的单字母前缀?

有关

29
John P Bloch

停用不应引起数据丢失

插件不应该 删除停用时的任何数据。

有关

25
MikeSchinkel

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

如果您位于前端,请不要包含与管理区域相关的代码。

23
Denis de Bernardy

宣布插件卸载时数据丢失

卸载时 一个插件应该 提示用户它将删除它的数据 并在这样做之前收到用户可以删除数据的确认一个插件应该允许用户保留数据的选项卸载时。 (来自@EAMann的这个想法。)

有关

21
MikeSchinkel

使用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' );

您可以在 此Q 找到更多信息。修复WP_Errorwp_die()的“一起工作”的相关故障单从那里链接,然后是另一张故障单。评论,评论家和其他人表示赞赏。

19
kaiser

让插件的文件夹名称改变

/插件/ pluginname/{各种}

用于该文件夹的“pluginname”应始终可更改。

这通常通过定义常量并在整个插件中一致地使用它们来处理。

毋庸置疑,许多流行的插件都是罪人。

有关:

19
AndyBeard

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

插件应该 减少它的影响 尽可能多地通过 最小化它添加到全局命名空间的名称数量

这可以通过将插件的函数封装到类中或使用 PHP命名空间功能 来完成。前缀一切都有帮助,但不是那么灵活。

在函数和类旁边,插件不应该引入全局变量。使用类通常会废弃它们,它简化了插件维护。

有关

18
hakre

使用PhpDoc发表评论

最佳实践接近PhpDoc风格。如果你不使用IDE像“Eclipse”,你可以看看 在PhpDoc手册

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

17
kaiser

在add_option之前使用Settings API

不是通过add_option函数向数据库添加选项,而是应该使用 Settings API将它们存储为数组 它会为您处理所有事情。

在add_option之前使用Theme Modifications API

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

17
kaiser

保护插件用户隐私

(以前:匿名API通信)

如果插件与外部系统或API(例如某些Web服务)进行通信,则应该匿名进行,或者向用户提供匿名选项,以确保没有与插件用户相关的数据泄漏给不受控制的第二方。

16
EAMann

使用权限提供访问控制

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

至少,对插件可用于的所有不同类型的过程进行适当的功能检查。

15
eddiemoya

WordPress.org上的主机插件

使用 _svn _ repository 在WordPress.org上提供 用于托管插件。它可以更轻松地更新用户体验,如果您以前从未使用过SVN,它可以让您通过在适合它的上下文中使用它来实际理解它。

14
pixeline

整理你的代码

总是难以读取未按其执行顺序编写的代码。首先包括/ require,define,wp_enqueue_style和_script等,然后是插件/主题需要的功能,最后是构建器(例如管理员屏幕,集成在主题中的东西等)。

尝试在自己的文件夹中分隔css和js之类的东西。还尝试使用仅作为助手的函数(如数组拼合器等)来执行此操作。保持“主”文件尽可能干净易读是一种帮助用户,开发人员和您在一年内尝试更新并且没有长时间看到代码的方法。

拥有一个经常重复的结构也很好,所以你总能找到自己的方法。在不同项目的已知结构中进行开发将使您有时间使其更好,即使您的客户切换到另一个开发人员,您也永远不会听到“他留下了混乱”。这可以建立您的声誉,应该是一个长期目标。

12
kaiser

导入/导出插件设置

它在插件中并不常见,但如果您的插件有(某些)设置,则应该 提供数据的导入/导出,例如配置和用户输入

导入/导出可提高插件的可用性。

一个具有这种导入和导出功能(以及撤销机制)的示例插件是 Breadcrumb NavXT(Wordpress插件) (完全披露:我在那里的一些小代码,大多数是由mtekk完成的) 。

有关

12
hakre

死得风格

以合适的方式死亡 所有插件(甚至是主题)函数都应该在关键位置使用wp_die()来向用户提供有关已发生事件的一些信息。 Php错误很烦人,wp_die可以为用户提供关于插件(或者他们)错误的好样式的消息。另外,如果用户已停用调试,插件将会中断。

使用wp_die()也有助于你的插件/主题与 wordpress testsuite兼容

11
kaiser

为用户提供帮助屏幕

说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);

update/note: (参见kaiser的评论):上面的例子将在一个类中使用

11
edelwater

提供可扩展表单

当插件提供输入数据的可能性时,它应该总是在“提交”和/或“重置”按钮之前有一个钩子,因此开发人员可以轻松地扩展表单,不仅包括字段,还包括按钮。

请参阅: 设置API

有关

10
kaiser

包括总是通过Hook的功能,而不是直接。

例:

  • 不要使用包括插件的类通过新的无钩

  • 使用Hook plugins_loaded

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

更新: 一个小的实例: Plugin-svn-trunk-page 和一个伪示例

//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

你也可以在multisite-install上通过mu_plugins_loaded加载,请参阅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 我经常使用它,它不是那么辛苦而且早期,更好地作为一个很难的新类();

9
bueltge

您的插件描述应准确详细说明插件的功能。有10个精选的帖子插件。所有这些都显示特色帖子,但许多都有不同的功能。通过阅读说明,将插件与类似的插件进行比较应该很容易。

你应该避免吹嘘你的插件有多简单,除非它真的非常基本。您应该在说明中包含有用的链接,例如设置的链接。

8
Greg

GPL兼容许可下的许可插件

插件和主题 应该 在WordPress兼容许可下获得许可。这使得它们可以与WordPress一起作为“程序”重新分发。建议的许可证是 GPL 请注意插件中包含的所有代码库都与相同的许可证兼容。

(这有 一直是个问题 和严肃 辩论点 在过去和 现在 。)

8
EAMann

最大限度地减少远程数据源和Web服务的副作用

插件 应该 缓存/屏蔽Web服务 和/或 XMLRPC/SOAP通过缓存/数据提供程序层请求 如果您使用它们以便不等待前端请求(慢)webservice响应。

这包括下载RSS提要和其他页面。设计他们在后台请求数据的插件。

一个可能的步骤是(以发布到ping.fm为例):创建一个缓冲表,比方说:ping_fm_buffer_post(日期,时间,消息,提交时间,状态)

  1. 每次要将更新提交到ping.fm时,请将其添加到此表中。
  2. 现在,我们需要创建一个插件来处理这些数据。此插件将通过crontab运行,以检查尚未提交的每个更新
  3. 因为我们有这个表,所以我们也可以列出提交给ping.fm的每条消息并检查每个帖子的状态。为了防止ping.fm方面出现问题,我们可以重新提交。
7
hakre

测试你的插件

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

基于 这个答案 / Ethan Seifert 到一个测试问题,这些都是好的做法:

  • 单元测试应该测试类可以执行的最小行为量。
  • 当您达到功能测试的水平时,您可以使用Wordpress依赖项测试代码。
  • 根据您的插件的作用 - 考虑使用基于Selenium的测试,该测试使用ID测试DOM中数据的存在
7
Fernando Briano

使用专有名称

名称钩子和过滤器 (类,函数和变量),所以 人们可以在一年内识别它们 ,当它们不再记得它们时,那​​块蛋糕或代码来自何处。钩子/过滤器名称是否变长并不重要。防爆。 youruniquename_hook/filter_whatitdoes。

  • 如果您的文件包含名为“dbdbInit”的类,则包含该类的文件应命名为“dbdbInit.class.php”。
  • 如果你在dbdbInit-类中有一个注册ex的函数。 custom_post_types,然后将其命名为register_custom_post_types()
  • 如果您有一个包含custom_post_types名称的数组,则调用数组所在的变量$custom_post_type_names
  • 如果你有一个处理数组的函数写function array_handler( $array ) { // handle the array} ..
  • 试着用一种方式命名事物,你知道它的名字是什么/它属于哪里。

另一件事:如果你必须调试一些东西,那么在99%的情况下,你不仅可以获得所有的消息,还可以获得wordpress的消息。所以尝试使用相同的前缀ex。您的类,公共函数和变量/对象的“dbdb”。这样您就可以在数百个文件之间轻松找到它们。 (Wordpress在主题之前加载了64个文件,大约有1,550个函数,而不是关于钩子和过滤器。)

6
kaiser

关心未来的WordPress和主题版本

注意:重新阅读此建议后,我现在退回此练习,因为检查每个存在的功能可能会降低您的网站速度。

检查函数是否已弃用 直接在主题中。

这是一个“可能就像那样”的例子。

if ( ! function_exists( 'wp_some_fn' ) ) 
{
    $theme_data = wp_get_theme();
    $error = new WP_Error( 'wp_some_fn', sprintf( __( 'The function %1$s is deprecated. Please inform the author', TEXTDOMAIN ), "Theme: {$theme_data->Name}: Version {$theme_data->Version}" );

    // abort
    if ( is_wp_error( $error ) )
        return print $error->get_error_message();
} 
// else if no error - the function works and exists
wp_some_fn();

对于正确/最佳实践错误处理,请参阅以下答案: link

你甚至可以将$ cause放入函数中。这将帮助您和您的用户跟踪主题中可能发生变化的功能或类。

6
kaiser

使用WordPress的编码标准

http://codex.wordpress.org/WordPress_Coding_Standards

您知道更新您所使用的代码与其他人编写的代码相比更容易吗?编码标准使任何从事项目工作的开发人员都能更容易地进入并查看正在发生的事情。

我们知道你的插件或主题是你自己的,你打破线条和添加花括号的方式是你个性的表达。每个缩进都是经过仔细考虑的声明。但是使用自定义代码,即使您的代码不在核心应用程序中,也会为WordPress做出贡献。编码标准可帮助开发人员快速掌握代码。

6
gabrielk

从WordPress核心代码解耦

一个插件应该 减少WordPress API对所需最小值的影响 所以从WordPress代码中分离插件代码。这减少了WordPress代码库中的更改对插件的影响。此外,这还提高了插件代码的跨版本兼容性。

这并不意味着不使用WordPress函数(使用它们,如 重用现有函数 建议),但不要过多地将代码与WordPress函数进行网格划分,而是要分离插件业务逻辑来自WordPress的功能。

6
hakre

对插件输出字符串使用wp选项

为了使插件易于使用和自定义,所有输出字符串都应该是可修改的。最好的方法是使用wp-options存储输出字符串并提供后端来更改默认值。插件不应使用使用插件后端无法轻易更改的显示字符串。

例如:Sociable - 使您能够更改图标部分“共享和享受”之前出现的句子:

5
hannit cohen

使用卸载,激活和停用挂钩

这有三种不同的钩子:

  • 卸载register_uninstall_hook();
  • 停用register_deactivation_hook();
  • 激活register_activation_hook();

可以在此处找到带有工作示例的详细说明。

3
kaiser