WordPress插件开发教程手册 — 插件基础教程
WordPress插件开发基础
插件开发入门
简单来说,一个 WordPress插件是一个带有 WordPress插件头注释的 PHP 文件,我们强烈建议为插件创建一个目录,以便保持插件文件整齐有序、便于维护。
要创建一个新的 WordPress插件,请按照以下步骤进行。
- 切换到 WordPress 站点的 wp-content/plugins 目录
- 创建一个新目录,并将其命名为插件的名称,如(plugin-name)
- 切换到你新建的目录
- 创建一个 PHP 文件(插件名称最好和目录名称保持一致。当然,其他名称也是可以的)
下面是类 Unix 系统上,在命令行中完成上面步骤的过程
wordpress$ cd wp-content/plugins
plugins$ mkdir plugin-name
plugins$ cd plugin-name
plugin-name$ vi plugin-name.php
在上面的例子中,vi 是文本编辑器的名称,你可以使用自己喜欢的任何编辑器。
打开了上面创建的 PHP 文件之后,我们就可以开始开发 WordPress插件了,首先,我们需要为插件添加一个插件头注释,这是一个特殊格式的 PHP 块注释,其中包含了插件的元数据,例如插件名称和作者。头注释至少需要包含插件的名称,插件目录中只有一个文件可以包含插件头注释,如果你的插件有多个 PHP 文件,其他文件都不要包含这个头注释。
<?php
/*
Plugin Name: YOUR PLUGIN NAME
*/
保存上面的文件,然后打开 WordPress插件列表界面,我们就可以在 WordPress 已安装的插件中看到我们创建的插件了。
钩子:Action 和 Filter
WordPress 钩子可以让我们在特定的时机介入 WordPress 的代码执行流程,不需要编辑任何核心文件就可以改变 WordPress 的行为,。
WordPress 中有两种类型的钩子,Action 和 Filter,Action 可以让我们添加或修改 WordPress 功能,而 Filter 可以让我们修改用户提交的或展示给用户的内容。
除了在 WordPress插件中使用,钩子也被广泛应用于 WordPress 核心的许多功能中,一些钩子只是在 WordPress 埋下了一些占位符,以便我们开发的时候使用,这就是 WordPress 如此灵活的原因。
插件开发的基础钩子
创建插件时需要的 3 个基础钩子是 register_activation_hook(),register_deactivation_hook() 和 register_uninstall_hook()。
- register_activation_hook 我们激活插件时会运行,我们可以使用这个钩子挂载一个函数来设置我们的插件,例如在数据表中添加一些默认设置。
- register_deactivation_hook 在我们禁用插件时运行,我们可以挂载一个清理插件数据的函数来清理一些临时数据。
- register_uninstall_hook 在我们卸载插件时运行,我们可以挂载一个清理插件所有数据的函数来清理数据库中不再需要的插件数据。
添加自定义钩子
我们可以使用 do_action() 函数来添加我们自己的钩子,通过我们自己的自定义钩子,其他开发者可以通过扩展或修改我们的插件来适应他们的需求。
移除挂载到钩子上的函数
我们可以使用 remove_action() 来移除挂载到某个钩子上的函数,比如,如果一个插件是另外一个插件的扩展,我们可以使用 remove_action 来移除扩展插件添加到这个钩子上的回调函数,然后添加我们自己的回调函数,这种情况下,需要特色关注插件 action 的优先级,因为 remove_action() 需要在初始的 add_action() 之后运行。
从钩子中删除 action 或改变 action 的优先级时,我们应该非常小心,因为我们很难看出这些改变会如何影响挂载在同一钩子上的其他操作。使用这个操作的时候,强烈建议多做一些测试。
我们可以在本手册的钩子章节了解更多关于创建钩子、与钩子进行交互的信息。
WordPress API
你知道吗?WordPress 提供了很多 应用程序接口 (APIs)? 这些 API 可以大大减少我们在开发插件时需要写代码。我们不想重复发明轮子,特别是现有的轮子经过了很多开发和测试、已经足够稳定了之后。
这其中最常见的是 选项 API,它可以帮助我们很轻松的把数据保存到数据库中。如果您需要在插件中使用 cURL,那么您可能会对 HTTP API 感兴趣。
由于我们正在讨论怎么开发 WordPress插件,学习一下 插件API 是有必要的,其中的很多功能可以帮我们更快速的开发插件。
WordPress 怎么加载插件?
当我们打开 WordPress 后台的已安装插件列表时,WordPress 会搜索 wp-content/plugins 目录(及其子目录,也就是插件自己的目录),来查找带有 WordPress插件头文件的 PHP 文件,如果我们的插件只是一个 PHP文件,如 Hello Dolly,该文件可以直接放在 wp-content/plugins 目录的根目录中。但更常见的是做法是:插件会有自己的目录,插件文件存放在自己的目录中,以目录名称做为插件文件名称。
分享你的插件
有时候,我们仅为自己的站点开发插件,但更多人喜欢分享自己的插件到 WordPress 社区。在分享插件之前,我们需要选择一个许可证,许可证可以让用户知道他们被允许如何使用我们的代码,为了兼容 WordPress 核心的许可证,建议选择适用于 GNU 通用公共许可证(GPLv2 +)的许可证。
头文件要求
正如入门中所介绍的,插件头文件告诉了 WordPress 这是一个什么样的插件。头文件至少需要包含插件名称,也可以选择性的包含以下几个部分(通常应该包含):
- 插件名称:(必需)你的插件名称,会显示在 WordPress 后台的插件列表中。
- 插件 URI:插件主页,应该是唯一的URL,最好在你自己的网站上。对于你的插件来说,必须是独一无二的,不能使用 WordPress.org 上的网址。
- 描述:插件的简短描述,在 WordPress插件管理界面显示,不要超过 140 个字符。
- 版本:插件的当前版本号,例如 1.0 或 1.0.3。
- 作者:插件作者名字,如果插件有多个作者,作者之间可以使用逗号分隔开。
- 作者URI:作者网站或其他网站上的个人资料,如 WordPress.org。
- 许可证:插件许可证简称(slug)(例如GPL2)。有关许可的更多信息可以在WordPress.org 指南中找到。
- 许可证URI:许可证的全文链接(例如 https://www.gnu.org/licenses/gpl-2.0.html)。
- 文本域:插件的 Gettext 文本域。更多信息可以在如何国际化您的插件页面的文本域部分找到。
- 域名路径:域名路径告诉 WordPress 在哪里可以找到翻译。更多信息可以在如何国际化您的插件 页面的域路径部分找到。
插件头注释的正确格式可以参考如下示例:
<?php
/*
Plugin Name: WordPress.org Plugin
Plugin URI: https://developer.wordpress.org/plugins/the-basics/
Description: Basic WordPress Plugin Header Comment
Version: 20160911
Author: WordPress.org
Author URI: https://developer.wordpress.org/
License: GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: wporg
Domain Path: /languages
*/
包含软件许可证
大多数 WordPress插件都是使用 GPL 许可证发布的,WordPress 内核使用的也是这个许可证,当然,我们也可以选择其他许可证,不管使用哪个许可证,发布插件时,最好都明确指出插件许可证。
在头文件要求章节,我们简单说明了如何在插件头文件注释中指明插件许可证,另一个常见和鼓励的做法是在插件主文件顶部,包含一个许可证块注释,示例如下:
/*
{Plugin Name} is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
any later version.
{Plugin Name} is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with {Plugin Name}. If not, see {URI to Plugin License}.
*/
与插件头注释一起使用时,看起来应该像下面的样子。
<?php
/*
Plugin Name: WordPress.org Plugin
Plugin URI: https://developer.wordpress.org/plugins/the-basics/
Description: Basic WordPress Plugin Header Comment
Version: 20160911
Author: WordPress.org
Author URI: https://developer.wordpress.org/
Text Domain: wporg
Domain Path: /languages
License: GPL2
{Plugin Name} is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
any later version.
{Plugin Name} is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with {Plugin Name}. If not, see {License URI}.
*/
激活/禁用钩子
激活和禁用钩子可以让我们在激活或禁用插件时进行一些操作。
在激活时,我们可以挂载一个函数来添加 URL 重写规则,或者添加插件设置到数据库中。
禁用时,我们可以挂载一个函数来删除临时数据,如缓存和临时文件和目录。
激活
如果需要添加一个函数到激活钩子上,使用 register_activation_hook() 函数。
register_activation_hook( __FILE__, 'pluginprefix_function_to_run' );
禁用
如果需要添加一个函数到禁用钩子上,使用 register_deactivation_hook() 函数。
register_deactivation_hook( __FILE__, 'pluginprefix_function_to_run' );
每个函数中的第一个参数都指向插件的主文件(也就是包含插件头注释的文件),这两个函数通常在插件主文件中被触发,当然,我们也可以把这两个函数放在其他文件中,这中情况下,我们必须更新第一个参数到正确位置。
示例
激活钩子最常见的用途之一就是在插件注册自定义文章类型时,刷新 WordPress 固定链接缓存,这样做可以让我们避免遇到 404 错误。示例如下:
function pluginprefix_setup_post_type() {
// 注册 book 自定义文章类型
register_post_type( 'book', ['public' => 'true'] );
}
add_action( 'init', 'pluginprefix_setup_post_type' );
function pluginprefix_install() {
// 触发注册自定义文章类型操作
pluginprefix_setup_post_type();
// 注册文章类型之后,刷新固定链接缓存
flush_rewrite_rules();
}
register_activation_hook( __FILE__, 'pluginprefix_install' );
如果不知道怎么注册自定义文章类型,不用担心,我们稍后会介绍,上面的例子虽然简单,但很常见。
上面的示例是激活插件时的操作,我们来看一下如何在插件禁用时,撤销上面的操作。
inprefix_deactivation() {
// 反注册文章类型
unregister_post_type( 'book' );
// 刷新固定链接缓存
flush_rewrite_rules();
}
register_deactivation_hook( __FILE__, 'pluginprefix_deactivation' );
有关插件激活和禁用钩子的更多信息,请参考以下链接:
- register_activation_hook() WordPress 函数参考。
- register_deactivation_hook() WordPress 函数参考。
卸载方法
如果我们的插件添加了一些设置到数据库中,或者在用户使用插件时生成了一些缓存文件,当用户卸载插件时,这些设置和文件很可能已经没用了。我们需要在用户卸载插件时删除这些数据。下面的表格说明了停用和卸载钩子之间的区别。
操作 | 禁用钩子 | 卸载钩子 |
---|---|---|
刷新插件产生的缓存 | 是 | 否 |
刷新 Url 重写规则缓存 | 是 | 否 |
从 {$wpdb->prefix}_options 数据表中移除插件添加的数据 | 否 | 是 |
从 wpdb 中移除数据表 | 否 | 是 |
方法 1:register_uninstall_hook
挂载卸载函数到 register_uninstall_hook() 钩子上。
register_uninstall_hook(__FILE__, 'pluginprefix_function_to_run');
方法 2:uninstall.php
使用此方法,我们需要在插件根目录中创建一个名为 uninstall.php 的文件,这个文件中的函数会在用户卸载插件时自动运行。
例如: /plugin-name/uninstall.php
最佳实践
以下是关于 WordPress插件开发的一些最佳实践,可以帮助我们在开发插件时更合理的组织代码。
避免命名冲突
当我们的插件和其他主题或插件使用了同一个函数或类名时,就会发生命名冲突。幸运的是,我们可以通过下面的方法来避免命名冲突。
为什么会产品命名冲突
默认情况下,所有变量、函数和类全部在全局命名空间中定义,也就是说,一个插件或主题可以覆盖另外一个插件的变量、函数和类(在函数或类中定义的变量不会被覆盖)。
为所有定义加上前缀
所有变量、函数和类都应该有一个唯一前缀,前缀可以帮助我们避免和其他插件使用同一个变量、函数和类名,也可以避免这些定义被其他插件或主题覆盖。
检查所有的定义
PHP 提供了许多函数来验证变量、函数、类和常量是否存在,如果这些定义存在,这些函数将返回 true
- 变量: isset() (包含数组、对象等)
- 函数: function_exists()
- 类: class_exists()
- 常量: defined()
示例
<?php
//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' );
}
}
OOP 面向对象编程
解决命名空间冲突更简单的办法是使用类来组织插件的代码,使用这个方法时,我们仍然需要检查类名是否已经被其他插件或主题使用,相对于非 OOP 变成来说,我们只需要检查类名即可。
示例
<?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 内核、主题或其他插件交互有限的小型单一用途插件,使用一个文件就可以了。使用类这种复杂的结构没有任何好处,除非我们知道这个插件将来会有很多扩展。
对于有大量代码的大型插件,我们应该参考上面的目录结构,把样式、JavaScript 脚本独立出来,实现插件功能的不同函数或类也尽量独立出来,放在单独的子目录,这将有助于保持插件代码清晰和长期维护。
按需加载
将插件的管理代码和公共代码分开是很有必要的,我们可以使用 WordPress 条件函数 is_admin 来实现,例如:
<?php
if ( is_admin() ) {
// we are in admin mode
require_once( dirname( __FILE__ ) . '/admin/plugin-name-admin.php' );
}
架构模式
虽然有很多结构模式,但是大体上可以分为 3 种
- 单个文件,包含很多函数
- 单核插件文件,包含一个类,然后实例化这个类
- 有一个主插件文件,包含一个或多个类文件
入门样板插件
如果直接从头开始写一个插件,会做很多不必要的重复工作,我们可以使用样板插件来开发每个新插件。使用样板文件的一个优点是在我们的插件中保持一致性。如果插件的用户已经熟悉了插件样板,他们为插件贡献代码的时候,也更加轻松。下面是几个常用的 WordPress 样板插件项目。
- WordPress Plugin Boilerplate: WordPress插件开发的基础,旨在为构建插件提供清晰,一致的指导。
- WordPress Plugin Bootstrap: 使用 Grunt,Compass,Git 和 Svn 的样板插件。
- WP Skeleton Plugin: 关注单元测试和使用 Composer 的样板插件
- 在 GIthub 上面搜索入门样板插件
当然,我们也可以根据自己的需要和风格,定义自己的样板插件。