以下是 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-plugins)都可以安全地移除。这样网站内容或主题就不会依赖于任何插件。这很难强制执行,但会让我三思而后行是否安装自带短代码和自定义帖子类型的插件。卸载这些插件可能会破坏网站设计或导致用户输入的数据消失。至少意识到这些插件依赖关系非常重要。
因此,有三个地方可以放置自定义代码
/mu-plugins/
– 可能会影响网站数据或重要的后端功能/plugins/
– 卸载后不会影响网站数据或网站主题/functions.php
– 仅影响网站主题@cornelius,我非常喜欢这种插件/函数位置的“级联”。我将开始用这种方式思考它。
自定义帖子类型比较棘手,因为它们几乎总是有一些与之相关的特定模板代码。因此,虽然它们是特定于网站的,但它们也是特定于主题的。
那是真的,@sean,但我仍然认为它提供了更好的体验,原因有两个
1) 另一种方法是要求开发人员将相关代码提取并移植到新主题的
functions.php
文件中。这更容易出错且耗时。按照本文建议的方法更简洁,可以节省以后的时间,并且不会在开发的第一阶段增加任何时间。2) 虽然您完全正确地指出切换主题会失去一些体验,但至少创建功能插件可以确保基本帖子信息(标题、正文、特色图片、摘要等)会被新主题获取。此外,它可以让网站用户放心,数据不会丢失。无论哪种方式,数据都不会丢失,但在为其他人构建网站时,这种事情很重要。
我过去常常在插件中模块化我的功能,但现在我的主题中只有一个lib目录,然后我的functions.php文件包含一个循环,该循环迭代该目录中的文件并包含它们。只有当我真的想让该代码对多个应用程序通用且易于维护时,我才会将其提取到插件中。
我之前的评论提供了几个理由说明这样做仍然有利。
有一段时间我出于您提到的相同原因以及更多原因将所有内容都放在功能插件中,但我最终决定对于大多数主题功能来说,这是一个不必要的步骤。我发现相反的情况是,将内容放在插件中更耗时且略微令人沮丧。我发现将我的库代码放在
theme/lib
下的单独文件中然后迭代文件并包含它们,这种方式耗时更少。我喜欢部署时使用rsync来稀疏复制单个目录及其子目录。
将我的功能保存在主题中可以节省我在模板中使用这些功能时的时间。假设我编写了一个将在我的主题周围使用的函数。如果它在一个功能插件中,我必须通过将其包装在一个检查我的功能是否已定义的函数中来进行故障安全。如果该功能插件被禁用,则主题将中断。每次使用它时都要检查函数是否已定义很烦人。而且要现实一点,我可能仍然会忘记将所有内容都包装在该检查中。
另一个例子。有时我会回到我几年没修改过的主题。如果功能库在一个插件中,那么我必须转到目录结构的另一个部分才能找到它们。这听起来很小,但这些额外的步骤会很快累积起来。
启用/禁用功能听起来很酷,但我必须转到插件并仔细检查我的插件是否已启用。我太懒了,不想这样做。它听起来对故障排除也很有用,但我可以通过跟踪PHP日志来排除所有我需要排除的故障。
也许我只是个悲观的人,但我使用了这种构建方式,并发现它只在我想要将一段代码变成真正的“插件”并在其他地方使用时对我的开发风格有用。现在,我只是在我的主题的lib目录中创建一个文件,并确保在启用主题时会自动加载它。保持简单。
现在我的functions.php文件中只有这个。并且所有内容都井井有条,直到像
post_types.php
这样的文件(我在其中定义帖子类型)和header.php
这样的文件(我在其中放置清理页眉的代码)。现在我今天不戴黑帽子了。;)
据我所知,mu-plugins和插件的触发顺序存在差异,并非所有插件都适合mu-plugins文件夹。
在计划使用mu-plugin文件夹时,需要注意这一点。
非常好的观点。相关的WP Codex页面有一个注意事项部分,其中介绍了限制。也就是说,我已将它用于本文中列出的确切类型的内容,并且没有问题。
如果有人感兴趣,我有一个WordPress插件可以自动化功能插件的创建,因此不需要访问文件系统。http://wordpress.org/plugins/functionality/
@Shea Bunge,我将查看您的插件并试用一下。受这篇文章的启发,我决定围绕functions.php文件、功能插件以及保留自定义函数的替代方法,制作我下一次Meetup的演示。
感谢@govertz。您可能也对我的另一个插件感兴趣,该插件允许您从WordPress管理界面添加和管理自定义函数,然后在站点上执行这些函数,就像它们在插件或主题functions.php文件中一样。http://wordpress.org/plugins/code-snippets