启动社区wiki以收集objective 插件开发的最佳实践。这个问题的灵感来自@EAMann\'s comments on wp-hackers.
我们的想法是就什么样的客观最佳实践进行协作,以便我们最终能够在某些社区协作审查过程中使用它们。
UPDATE: 在看到最初的几个回复后,很明显,我们每个答案只需要一个想法/建议/最佳实践,人们应该在发布前查看列表,以确保没有重复。
启动社区wiki以收集objective 插件开发的最佳实践。这个问题的灵感来自@EAMann\'s comments on wp-hackers.
我们的想法是就什么样的客观最佳实践进行协作,以便我们最终能够在某些社区协作审查过程中使用它们。
UPDATE: 在看到最初的几个回复后,很明显,我们每个答案只需要一个想法/建议/最佳实践,人们应该在发布前查看列表,以确保没有重复。
P、 有一件事我觉得有点令人失望,你的问题是只为最终用户设计的插件的百分比,即没有自己的挂钩。想象一下,如果WordPress的设计与大多数插件一样?这将是一个非常灵活的解决方案。
如果WordPress能够自动安装其他插件所依赖的插件,情况可能会有所不同?事实上,我通常必须从头开始编写大量所需的功能,因为客户端需要某种方式和可用的插件,而90%的插件不允许我灵活地更新剩下的10%。
我真的希望WordPress社区的领导者能够找到一种方法,确保插件在遵循最佳实践(例如为其他开发人员添加挂钩)的同时获得奖励,就像在StackExchange网站上奖励好的答案一样。
让我们以another question:
示例:当有人转发文章时,我想在插件中做一些事情。如果在流行的retweet插件中有一个自定义的钩子,我可以钩住它,然后启动它,那就太好了。没有,所以我可以修改他们的插件以包含它,但这只适用于我的副本,我不想尝试重新分发它。
wp_enqueue_script
和wp_enqueue_style
插件应始终使用wp_enqueue_script
和wp_enqueue_style
当链接JS和CSS文件时,不要直接通过<script>
标签。
请注意,在init
操作,以便用户可以挂接到该操作中。
参见法典:I18n for WordPress Developers
还有这篇文章:Loading WP language files the correctly.
考虑到一个带有textdomain“我的插件”的插件,WordPress现在将首先在中查找翻译文件:
/wp-content/languages/plugins/my-plugin-en_US.mo
如果在那里找不到,它会在插件告诉它要查找的地方查找(如果遵循codex,通常在Pluings“language”文件夹中):
/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+)不相关<最后,我想指出,这对我们来说很重要load custom user language files from WP_LANG_DIR before you load the language files that ship with the plugin. 当为同一个域加载多个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
. 看到了吗Codex on Debug 更多细节。
一个好处是您可以使用;记录弃用通知”轻松监控应更换的功能。另一个好处是用户可以查看Codex中的函数文档,更好地理解插件的功能,即使他们不是经验丰富的PHP开发人员。
插件可以提供导出/导入设置的选项,以便在删除之前将设置保存在WordPress之外。
$_POST
or $_GET
) 在使用输入值查询MySQL数据库之前。// 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
}
}
init()
; 取而代之的是,将其命名为jpb_init()
.它的常见用法是在名字前面使用三个或四个字母的前缀,或者使用PHP Namespace Feature. 比较:Single-letter prefix for PHP class constants?
仅包括您需要的文件。。。
如果您位于前端,请不要包含与管理区域相关的代码。
用于文件夹的“pluginname”应始终是可更改的。
这通常通过定义常量并在整个插件中持续使用来处理。
不用说,许多流行插件都是罪人。
plugins_url()
为了方便链接到资源,插件中包含了这些资源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\' );
有关更多信息,请访问this Q. 修复“协作”的相关票据WP_Error
和wp_die()
从那里链接,然后会有另一张票。评论、批评和;这是值得赞赏的。这可以通过将插件的函数封装到类中或使用PHP namespaces feature. 在所有内容前加前缀也有帮助,但没有那么灵活。
除了函数和类之外,插件不应该引入全局变量。使用类通常会淘汰它们,从而简化插件维护。
你不必知道这是怎么回事。专业开发人员无论如何都可以阅读代码,只需要将其作为摘要。爱好编码者和用户可能会欣赏你在相同知识水平上解释它的方式。
如果插件与外部系统或API(例如某些Webservice)通信,则应匿名通信,或为用户提供匿名选项,以确保与插件用户相关的数据不会泄露给第二方。
至少,对插件可以用于的所有不同类型的过程进行适当的功能检查。
尝试将css和js等内容分别放在各自的文件夹中。还可以尝试使用仅起辅助作用的函数,如数组展平器等。保留;“主要”;当您试图在一年内进行更新,并且已经很长时间没有看到代码时,尽可能干净且易于阅读的文件是一种帮助用户、开发人员和您的方法。
有一个你经常重复的结构也很好,这样你总能找到自己的出路。在一个已知的结构中开发不同的项目会给你时间让它变得更好,即使你的客户转向另一个开发人员,你也永远不会听到;他留下了一片混乱;。这会建立你的声誉,应该是一个长期目标。
导入/导出提高了插件的可用性。
具有这种导入和导出功能(以及撤消机制)的插件示例如下Breadcrumb NavXT (Wordpress Plugin) (完全披露:其中一些小代码是我写的,大部分是mtekk写的)。
wp_die()
在关键的地方向用户提供一些关于发生了什么的信息。Php错误很烦人,而且wp_die
可以为用户提供一条关于插件(或他们)做错了什么的漂亮消息。另外,如果用户已经停用了调试,插件就会中断。使用wp_die()
还有助于你的插件/主题与wordpress testsuite.
相关:
/**
* 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的评论):上述示例将在类中使用始终通过钩子而不是直接包含函数。
例子:
不要使用通过new Withook包含插件类
使用加载的挂钩插件
// add the class to WP
function my_plugin_start() {
new my_plugin();
}
add_action( \'plugins_loaded\', \'my_plugin_start\' );
Update:一个小的活生生的例子: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\\u plugins\\u加载到多站点安装,请参阅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我经常使用它,它不那么难,也不那么早,不如一个新的硬类();(这有been a problem 而且很严重point of debate 过去和现在present.)
这包括下载RSS提要和其他页面。设计他们在后台请求数据的插件。
一个可能的步骤是(以发布到ping.fm为例):创建一个缓冲表,例如:ping\\u fm\\u buffer\\u post(日期、时间、消息、提交时间、状态)
每次您要向ping提交更新时。fm,将其添加到此表中
Use WordPress\'s Coding Standards
http://codex.wordpress.org/WordPress_Coding_Standards
你知道更新自己编写的代码比更新别人编写的代码要容易得多吗?编码标准使任何从事项目工作的开发人员都能更容易地进入项目并了解情况。我们知道你的插件或主题是你自己的,你打断台词和添加花括号的方式是你个性的表现。每一个缩进都是经过深思熟虑的陈述。但是,通过自定义代码,即使您的代码不在核心应用程序中,您也为WordPress做出了贡献。编码标准可以帮助开发人员快速了解您的代码。
基于this answer 通过Ethan Seifert 对于测试问题,应遵循以下良好做法:
单元测试应该测试类可以执行的最小数量的行为
Check if functions are deprecated 直接在你的主题中。
这是一个;“可能是这样的。”;实例
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放入函数中。这将帮助您和您的用户跟踪主题中可能更改的函数或类。
如果文件包含名为“dbdbInit”的类,则应将包含该类的文件命名为dbdbInit.class.php
“如果你进入dbdbInit
-类a函数,该函数注册ex.custom\\u post\\u类型,然后调用它register_custom_post_types()
.
$custom_post_type_names
.function array_handler( $array ) { // handle the array}..这并不意味着不使用WordPress函数(使用它们,如Re-Use existing functions 建议),但不要将您的代码与WordPress功能过度融合,而是将插件业务逻辑与WordPress功能分开。
例如:Sociable-使您能够更改图标部分“共享和享受:”
卸载register_uninstall_hook();
停用register_deactivation_hook();
register_activation_hook();A detailed instruction with a working example can be found here..
function parseTemplate($file, $options) {
$template = file_get_contents($file);
preg_match_all("!\\{([^{]*)\\}!", $template, $matches);
$replacements = array();
for ($i = 0; $i < count($matches[1]); $i++) {
$key = $matches[1][$i];
if (isset($options[$key])) {
$val = $matches[0][$i];
$template = str_replace($val, $options[$key], $template);
}
}
return $template;
}
然后像这样使用:// In mytemplate.html
<h1>{title}</h1>
<h2>{date}</h2>
<p>{text}</p>
// In your functions.php
echo parseTemplate("mytemplate.html", array(
"title" => $title,
"date" => $date,
"text" => $somethingelse
));