启动社区Wiki,以收集用于插件开发的客观最佳实践。这个问题的灵感来自@EAMann对wp-hackers的评论。
想法是就可能的客观最佳实践进行协作,以便我们最终可以在某些社区协作审核过程中使用它们。
更新:在看到前几个回答后,很明显,我们每个答案只需要一个想法/建议/最佳实践,人们应该查看列表以确保发布前没有重复。
启动社区Wiki,以收集用于插件开发的客观最佳实践。这个问题的灵感来自@EAMann对wp-hackers的评论。
想法是就可能的客观最佳实践进行协作,以便我们最终可以在某些社区协作审核过程中使用它们。
更新:在看到前几个回答后,很明显,我们每个答案只需要一个想法/建议/最佳实践,人们应该查看列表以确保发布前没有重复。
Answers:
如果您认为人们想添加或更改一些数据,请在返回之前提供apply_filters()。
PS一件事,我感到有些失望,您要解决的问题是只为最终用户设计的插件所占的百分比,即没有自己的挂钩。想象一下WordPress是否像大多数插件一样设计?这将是不灵活且非常利基的解决方案。
如果WordPress能够自动安装其他插件所依赖的插件,也许情况会有所不同?因为通常情况下,我通常必须从头开始编写很多我需要的功能,因为客户需要某种方式的东西和可用的插件,尽管那里有90%的客户,但我却没有灵活性来更新剩余的10%。
我确实希望那些领导WordPress社区的人能找到一种方法,以确保对插件的遵循最佳实践(例如为其他开发人员添加钩子)得到奖励,就像在StackExchange网站上获得好的答案一样。
让我们以另一个问题为例:
示例:当某人转推一篇文章时,我想在插件中做些事情。如果在流行的转推插件中有一个自定义的钩子,我可以将其钩住并解雇,那将很棒。没有,所以我可以修改他们的插件以包括它,但这仅适用于我的副本,并且我不想尝试重新分发它。
wp_enqueue_script
和加载脚本/ CSSwp_enqueue_style
插件不应加载/尝试加载JS / CSS文件的重复版本,尤其是jQuery和WP Core中包含的其他JS文件。
链接JS和CSS文件时,插件应始终使用wp_enqueue_script
,wp_enqueue_style
并且永远不要直接通过<script>
标签使用。
所有输出字符串都应链接到适当的文本域,以允许感兴趣的各方进行国际化,即使开发人员对翻译自己的插件没有兴趣。
请注意,在init
操作过程中加载语言文件非常重要,这样用户才能参与到操作中。
请参阅Codex:适用于WordPress开发人员的I18n
以及本文:正确加载WP语言文件。
WP 4.6更改了加载顺序和检查的位置,这使得开发人员和用户更加容易。
考虑使用文本域为“ my-plugin”的插件,WordPress现在将首先在以下位置查找翻译文件:
/wp-content/languages/plugins/my-plugin-en_US.mo
如果找不到,则会在插件告诉它的位置寻找一个(通常是在pluigns“语言”文件夹中,如果遵循法典):
/ wp-content / plugins / my-plugin / languages / my- plugin-zh_CN.mo
最后,如果找不到语言文件,它将检查以下位置的默认位置:
/wp-content/languages/my-plugin-en_US.mo
第一次检查是在4.6中添加的,它为用户提供了一个定义的位置来添加语言文件,就像以前他们需要知道开发人员在何处添加语言文件一样,现在用户只需要知道插件的textdomain: / wp-content /语言/插件/TEXTDOMAIN-LOCAL.mo
[...]
最后,我想指出,在加载插件随附的语言文件之前,从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-config.php
文件,以便将WP_DEBUG
常量设置为true
。有关更多详细信息,请参见“ Debugx on Debug ”。
如果可以:使用WordPress核心中包含的现有功能,而不是编写自己的功能。仅当WordPress核心中没有适当的预先存在的函数时,才开发自定义PHP函数。
好处之一是您可以使用“日志弃用的通知”轻松监视应替换的功能。另一个好处是,即使他们不是经验丰富的PHP开发人员,用户也可以查看Codex中的功能文档并更好地了解该插件的功能。
wp-login.php
。因此,“如果可以的话”是答案的一个很好的
从WordPress安装中删除后,插件应删除其创建的所有文件,文件夹,数据库条目和表以及所创建的选项值。
插件可以提供导出/导入设置的选项,以便可以在删除之前将设置保存在WordPress之外。
插件应正确地为所有全局命名空间项(常量,函数,类,变量,甚至自定义分类法,帖子类型,小部件等)添加前缀。例如,不要创建一个名为init()
; 的函数。而是将其命名为jpb_init()
。
它的通用名称应该在名称前面使用三或四个字母的前缀,或者利用PHP命名空间功能。比较:PHP类常量的单字母前缀?
没有理由不编写干净的,面向对象的PHP5代码。在下一个版本(WP 3.1)之后,将逐步淘汰PHP4支持。当然,您可以为所有函数名称加上前缀endlessly_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
}
}
在卸载插件应该 提示,这将是删除它的数据的用户,并收到确认用户是好有这样和插件之前删除的数据应该也允许用户以保持数据的选项后卸载。(这个想法来自@EAMann。)
/ plugins / pluginname / {various}
用于该文件夹的“插件名称”应始终可更改。
这通常是通过定义常量并在整个插件中一致地使用它们来处理的。
不用说,许多流行的插件都是罪人。
plugins_url()
以便轻松链接到包含在插件中的资源。plugin_basename(__FILE__)
该插件的本地名称。这对于具有相同插件的副本(测试,其他位置的多个帐户,但每个插件仅一个帐户,...)也很有用。
不仅是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_Error
并wp_die()
从那里链接,随后将有另一个票证。评论,评论家等。
wp_die();
错误(反向)。关于您的Q)我不完全理解。当您设置WP_Error类的实例,你必须通过类似功能的全面访问其数据get_error_code();
,get_error_message();
,get_error_data();
和多个版本。您也只能在主题或插件的引导程序中实例化一次,并仅用于$error->add();
填充其他错误,最后将其输出到页脚中$error->get_error_messages();
以捕获所有错误。
插件应 通过最大程度地减少其添加到全局名称空间中的名称的数量来减少其影响。
这可以通过将插件的功能封装到一个类中或使用PHP名称空间功能来完成。给所有内容加上前缀也有帮助,但不够灵活。
在函数和类旁边,插件不应引入全局变量。使用类通常会使它们过时,并简化了插件维护。
与其通过add_option函数向数据库添加选项,不如使用设置API来将它们存储为数组,该API可以为您处理所有事情。
该修改API是一个非常简单的结构,并且允许添加和检索选项的安全方法。一切都保存为序列化值在您的数据库中。简单,安全和简单。
update_option
and never时add_option
,更新功能将在不存在该选项时创建该选项。:)
add_option
。有一个很好的用例add_option
,如果该选项已经设置,则不会更改,因此我在激活时使用它来保留可能已经存在的用户首选项。
add_option
是要显式禁用自动加载时。update_option
将强制自动加载为true,因此您要禁用自动加载,请add_option
在最初创建该选项时使用。
使用WordPress.org上提供的SVN存储库托管插件。它使更新用户体验变得更加容易,并且如果您以前从未使用过SVN,则可以通过在有理由的上下文中使用它来真正理解。
这在各个插件之间并不常见,但是如果您的插件具有(某些)设置,则应 提供导入/导出数据,例如配置和用户输入。
导入/导出提高了插件的可用性。
一个具有这种导入和导出功能(以及撤消机制)的示例插件是Breadcrumb NavXT(Wordpress插件)(完整披露:我在那里写了一些小代码,大部分是mtekk完成的)。
始终很难读取未按执行顺序编写的代码。首先包括/要求,定义,wp_enqueue_style和_script等,然后是插件/主题所需的功能,最后是构建器(例如管理屏幕,集成在主题中的内容等)。
尝试将css和js之类的内容放在自己的文件夹中。还要尝试使用仅作为辅助函数的函数,例如数组展平器和类似函数。保持“主”文件尽可能整洁和易于阅读是一种可以帮助用户,开发人员和您的方法,当您尝试在一年内进行更新并且很长时间没有看到代码时。
经常重复的结构也很不错,因此您始终可以找到自己的方式。在不同的项目上以已知的结构进行开发将使您有时间进行改进,即使您的客户切换到其他开发人员,您也永远不会听到“他留下了混乱”的声音。这可以建立您的声誉,应该是一个长期目标。
以适当的方式消亡
所有插件(甚至主题)功能都应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的评论):上面的示例将在一个类中使用
总是通过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
您也可以通过加载在mu_plugins_loaded多站点安装,见行动参考食品:http://codex.wordpress.org/Plugin_API/Action_Reference 另外这里,你看,怎么用这个钩子inlcude WP:HTTP:// adambrown。 info / p / wp_hooks / hook / plugins_loaded?version = 2.1&file = wp-settings.php 我经常使用它,并且不那么困难和早期,比使用new new class()更好。
//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
插件和主题应根据WordPress兼容许可进行许可。这使它们可以与WordPress作为“程序”一起重新分发。推荐的许可证是GPL。请注意,该插件随附的所有代码库均与同一许可证兼容。
如果使用插件,则插件应 通过缓存/数据提供者层来缓存/屏蔽Webservice和/或XMLRPC / SOAP请求,以免使前端请求等待(慢速)webservice响应。
包括下载RSS feed和其他页面。设计可以在后台请求数据的插件。
一种可能的步骤是(以发布到ping.fm为例):创建一个缓冲区表,比如说:ping_fm_buffer_post(日期,时间,消息,submitted_time,状态)
我们绝对应该在我们的插件开发环境中拥有一些测试工具。
基于此答案由伊桑·塞弗特到测试的问题,这些都是很好的做法如下: