以下是 Jason Witt 的客座文章。我一直都知道我应该将 WordPress 主题中的 `functions.php` 文件中的许多内容移植到一个功能插件中。但是你知道的,一天中的时间就是这样。我最近让 Jason 为我处理了这个项目,他做了一份很棒的工作。如果你不知道我在说什么,请继续阅读。
向 WordPress 网站添加新功能就像在 WordPress 插件库中搜索插件并安装它一样简单。但是,您可能需要一些自定义功能,这些功能要么过于基本,要么过于定制,以至于没有相应的插件。这时 `functions.php` 文件就派上用场了。它本质上是主题的功能转储地。
但我们倾向于放在那里的某些代码最好放在别处。
`functions.php` 文件是一个在其中添加主题支持选项、自定义帖子类型、排队 JavaScript 以及(实际上)您可以想到的任何其他内容的简单位置。多年来,这已成为向 WordPress 主题添加自定义功能的实际方法。在过去几年里,出现了一种趋势,即将通常放在 `functions.php` 文件中的功能 **迁移到“功能插件”中。**
什么是功能插件?
功能插件与您在 WordPress 插件库中找到的任何其他插件一样。主要区别在于它不会公开发布,因为它特定于您的网站。它是一个自定义插件,涵盖了您所有网站的自定义功能。

功能插件有什么优点?
为什么您要花时间构建一个插件,而将您的功能放在 `functions.php` 文件中如此简单?最大的优势是 **您可以跨主题重复使用您的功能。** 当您更新/更改主题时,`functions.php` 中的一些代码将保持不变,而另一些代码将更改。功能插件的理念是将不会随主题而改变的功能放到插件中。这样,您就不必在 `functions.php` 文件中查找要保留的内容,而是可以直接深入到新主题的设计中。
插件中包含什么内容?
这是个百万美元的问题。功能插件中到底包含什么内容?解决这个问题的最佳方法是确定哪些内容是特定于 *主题* 的,哪些内容是特定于 *网站* 的。例如,自定义帖子类型是 **特定于网站的**,而添加缩略图支持是 **特定于主题的**。
让我们暂停一下,以便能够非常清楚地理解这一点。
假设您的网站有一个关于聚会的版块。您为它们构建了一个自定义帖子类型,以便它们可以成为一种特殊类型的內容。在网站前端,您以特殊方式显示它们。在网站后端,您收集特定于聚会的特殊信息。就像在 这个 CSS-Tricks 视频 中一样。我们将其称为 **主题 A**。
如果您更改了网站主题(**主题 B**),您是否可能希望您的聚会信息消失?可能不会。那是您可能希望跨越任何特定主题的网站内容。
如果您在 **主题 A** 的 `functions.php` 文件中声明了所有自定义帖子类型内容(例如 register_post_type()
),然后切换到 **主题 B** - *您可能会感到轻微的心脏病发作*,当您注意到所有聚会信息都消失了。主题 A 的 `functions.php` 文件不再处于活动状态,因此所有声明自定义帖子类型的代码不再运行。菜单不会添加到管理面板中,内容将看起来消失了。
请放心,数据仍然存在,您只需要确保自定义帖子类型代码再次运行。
为什么还要经历这一切?只需将该代码移动到功能插件中,即使切换主题,它也会保持活动状态。
现在想象一下完全不同的事情:`functions.php` 文件中的代码排队了一个 JavaScript 库。您可能正在使用该库在网站前端执行某些操作。这对于主题来说是相当具体的。另一个主题可能完全使用不同的库,或者根本不使用。这种事情放在 `functions.php` 文件中是有意义的,因为它 *特定于主题*,而不是内容。
`functions.php` 中有意义的内容示例
- 主题支持函数,例如
add_theme_support('post-thumbnails');
- 与前端相关的 JavaScript
- 向主页帖子列表添加自定义帖子类型
- 注册侧边栏和导航菜单
- 添加一个外部 CSS 文件,例如 自定义字体
功能插件中有意义的内容示例
- 自定义帖子类型
- 自定义分类法
- 其他插件的自定义功能
- 自定义元字段
- 主要是自定义内容
入门
如果您以前从未创建过 WordPress 插件,那么这是一个获得一些经验的好方法。
首先,您将在插件目录中创建一个目录。将其命名为您想要的任何名称。避免使用数字和特殊字符;连字符和下划线是可以的。我通常使用类似 mysitename-functionality
的名称。
在您的新插件文件夹中,创建一个与文件夹名称相似的文件,例如 mysitename-functionality.php
。
在该文件顶部,您需要添加插件文件头信息。以下是一个示例,可以帮助您入门。
/**
* Plugin Name: Your Functionality Plugin Name
* Plugin URI: http://example.com/plugin-name-uri/
* Description: This is a short description of what the plugin does. It's displayed in the WordPress admin area.
* Version: 1.0.0
* Author: Your Name or Your Company
* Author URI: http://example.com/
* License: GPL-2.0+
* License URI: https://gnu.ac.cn/licenses/gpl-2.0.txt
* Text Domain: plugin-name
* Domain Path: /languages
*/
之后,尽情发挥并开始在下方添加您的功能。
您可以从 `functions.php` 文件中直接剪切和粘贴代码到此文件中,只要此插件处于活动状态,它就应该可以正常工作。
模块化设计
如果您像我一样喜欢井井有条,那么现在是时候对您放在插件中的代码使用模块化方法了。
一种简单的方法是将您的功能整理成类似的组,并为每个组提供自己的文件。然后,使用 PHP 包含将这些文件包含到插件的主文件中。确保您对函数进行注释,这样当您在以后的日期返回它们时,您就会知道发生了什么。
include 'mysitename-functionality-post-types.php';
include 'mysitename-functionality-extra-rss-feeds.php';
include 'mysitename-functionality-remove-unwanted-assets.php';
另一种方法是使用面向对象编程 (OOP)。这涉及创建 PHP 类和方法。如果您不熟悉面向对象编程,那么 Tom McFarlin 的一个很棒的教程名为 WordPress 中的面向对象编程。如果您有兴趣发展您的 WordPress 编码技能,您应该看看它。OOP 是组织代码的好方法,它可以让您的代码随着功能需求的变化而增长。
实际示例
如果您想浏览 CSS-Tricks 功能插件,这里有一个 GitHub 上的仓库,您可以查看。
经过这次转换,`functions.php` 文件中只剩下排队加载 jQuery 和一个覆盖默认 HTML 注释输出的钩子——这两项都非常特定于当前主题。
是的。好东西。
顺便说一下,我拥抱 DRY 原则,尽可能将功能抽象到框架/库中。因此,给定项目的定制插件/子插件(即包含的部分)仅仅是使特定魔法发生所需的“待办事项”参数。任何冗余的 WP 冗余都留给父类。
https://github.com/WPezClasses
仅供参考——在所有这些存储库中的某个地方有一个入门指南:) 而且是的,我正在努力完善文档,以及使用适当的 PHP 命名空间。
欢迎评论、问题和拉取请求。
至少对于那些交由技术水平较低的客户处理的网站来说,我认为将功能插件放入
/mu-plugins/
文件夹 是最好的选择!对于那些不知道的人来说,
/mu-plugins/
是最好的!“mu” 代表“必须使用”(令人困惑的是,不是“多用户”,尽管它来自旧的“WPMU”分支,那时还没有多站点)。/mu-plugins/
文件夹中的文件会自动运行(如果我没记错的话,是在其他插件之前),并且无法从“插件”页面禁用(尽管您可以从“插件”页面顶部的一个小链接查看它们)。对于帖子类型、分类法等等,您可能想要关闭功能插件的唯一时间可能是需要编辑它的时候。
感谢您指出这一点,@Mark。我已经在 `functions.php` 中使用模块化设计,但创建功能插件很有意义。
我完全同意!我正在尝试构建现在任何插件(除了 mu-插件)都可以安全删除的网站。这样网站内容或主题就不依赖于插件。这很难强制执行,但会让我三思而后行是否安装内置有短代码和自定义帖子类型的插件。卸载这些插件可能会破坏网站设计或使用户输入的数据消失。至少要意识到这些插件依赖关系非常重要。
所以有三个地方可以放置自定义代码
/mu-plugins/
– 可以影响网站数据或重要的后端功能/plugins/
– 卸载后不会影响网站数据或网站主题/functions.php
– 仅影响网站主题@cornelius,我真的很喜欢这个插件/功能位置的“级联”。我将开始从这个角度思考。
自定义帖子类型很棘手,因为它们几乎总是有一些与之相关的特定模板代码。因此,虽然它们是特定于网站的,但它们也是特定于主题的。
那是真的,@sean,但我仍然认为这提供了更好的体验,原因有两个
1) 另一种方法是要求开发人员将相关代码提取并移植到新主题的 `functions.php` 文件中。这更容易出错,而且耗时。按照本文建议的方法更简洁,可以节省以后的时间,并且不会在开发的第一阶段增加任何时间。
2) 虽然您 100% 正确地指出切换主题会失去一些体验,但创建功能插件至少可以确保基本帖子信息(标题、正文、特色图片、摘要等)会被新主题接收。此外,它让网站用户安心,数据不会丢失。无论哪种方式,数据都不会丢失,但这种事情在为他人构建网站时很重要。
我以前将功能模块化到插件中,但现在我的主题中只有一个 lib 目录,然后我的 functions.php 文件包含一个循环,它迭代该目录中的文件并包含它们。只有当我真正想使该代码通用并在多个应用程序中维护时,我才会将其提取到插件中。
我之前的评论提供了 几个理由,说明为什么以另一种方式做仍然有利。
有一段时间我将所有内容都放在功能插件中,原因与您提到的相同,还有更多,但我最终决定对于大多数主题功能来说,这都是不必要的步骤。我发现相反的情况是,将东西放在插件中更耗时,而且有点令人沮丧。我发现将我的 lib 代码片段放在 `theme/lib` 下的单独文件中,然后迭代这些文件并包含它们,这样效率更高。
我喜欢部署使用 rsync 来稀疏复制单个目录及其子目录。
将我的功能保留在主题中可以节省我在模板中使用这些功能的时间。假设我写了一个要在主题周围使用的函数。如果它在功能插件中,我必须通过将所有内容包装在一个检查我的功能是否已定义的函数中来对其进行故障保护。如果该功能插件被禁用,主题将崩溃。每次使用它时都检查函数是否已定义很烦人。而且现实地说,我可能会忘记将所有内容都包装在那个检查中。
另一个例子。有时我回到一个几年没有修改的主题。如果功能库在插件中,那么我必须在目录结构的另一个部分寻找它们。听起来微不足道,但这些额外的步骤很快就会加起来。
启用/禁用功能听起来很酷,但我必须进入插件并仔细检查我的插件是否已启用。我太懒了,不想那样做。它也听起来对故障排除很有用,但我可以通过跟踪 PHP 日志来排除我需要的所有故障。
也许我只是一个消极的南希,但我使用这种构建方式,发现它只在我想要将一小段代码做成真正的“插件”并在其他地方使用时,对我的开发方式才有用。现在,我只是在我的主题的 lib 目录中创建一个文件,并确信在启用主题时它会被自动加载。保持简单。
现在,我的 functions.php 文件中只有这些。一切都井井有条,一直到像 `post_types.php` 这样的文件,我在其中定义帖子类型,以及 `header.php`,我在其中放置清理标题的代码。
现在我今天不戴黑帽子了。;)
据我所知,mu-插件和插件的触发顺序不同,并非所有插件都适合 mu-plugins 文件夹。
在计划使用 mu-plugin 文件夹时,请记住这一点。
非常好的观点。相关的 WP Codex 页面有一个 注意事项部分 详细介绍了限制。也就是说,我已经将它用于本文中列出的确切类型的内容,并且没有问题。
如果有人感兴趣,我有一个 WordPress 插件可以自动创建功能插件,因此不需要访问文件系统。 http://wordpress.org/plugins/functionality/
@Shea Bunge,我会查看你的插件并试用一下。受这篇文章的启发,我决定将我的下一个 Meetup 演讲主题定为 functions.php 文件、功能插件以及保存自定义函数的替代方法。
感谢 @govertz。您可能也会对我的另一个插件感兴趣,它允许您从 WordPress 管理员中添加和管理自定义函数,然后这些函数会在网站上执行,就像它们在插件或主题 functions.php 文件中一样。 http://wordpress.org/plugins/code-snippets