如何进行现代WordPress开发(第2部分)

本文概述

  • 现代WordPress开发最佳实践#1:遵循” 关注点分离”
  • 现代WordPress开发最佳实践#2:避免使用全局变量
  • 现代WordPress开发最佳实践#3:使用面向对象编程(OOP)
  • 现代PHP最佳实践#1:目标PHP 7.0+
  • 现代PHP最佳实践2:采用PHP行业标准(PSR-2编码样式指南)
  • 现代PHP最佳实践#3:使用PHP模板引擎
  • 现代PHP最佳实践4:使用Composer
  • 现代PHP最佳实践#5:使用命名空间
  • 现代PHP最佳实践#6:使用自动加载器
  • 现代WordPress开发最佳实践4:考虑使用根堆栈
  • 现代PHP和软件原理:使WordPress后端开发更可靠
WordPress是地球上使用最广泛的站点技术, 这是有充分理由的。然而, 遗留代码的核心是一团糟, 这个问题已蔓延到第三方开发人员。一些开发人员以此为借口在自己的WordPress PHP代码中偷工减料, 但是从长远来看, 这种方法对于除最琐碎的更改之外的所有更改而言都更加昂贵。
在由两部分组成的系列的第1部分中, 我们重点介绍了总体项目和工作流工具, 然后介绍了前端开发。现在是时候大声疾呼, 与PHP搏斗了:具体地说, 是在使用后端WordPress代码时如何遵循最佳实践。你可能会认为这是PHP / WordPress教程, 但假设你已经做过一些WordPress后端自定义, 则它会更高级。
那么, 哪些现代软件设计原则和PHP功能可以为你的时间带来最大的价值?以下是我强烈推荐的10种WordPress和PHP开发实践。
现代WordPress开发最佳实践#1:遵循” 关注点分离” 关注点分离意味着不应将具有不同功能或目的的WordPress PHP代码部分混在一起。相反, 应该将它们组织为不同的部分或模块, 并通过定义的接口将数据相互传递。 (接口是一组定义的参数, 模块将这些参数作为输入, 然后将其输出回去。)一个紧密相关的术语是单一职责原则:每个代码模块(或功能)应仅负责一件事情。
遵循这些原则的最终目标是生成模块化的, 因此可维护, 可扩展和可重用的代码。
那真是令人难以置信, 所以让我们看一个示例WordPress PHP(来自WordPress核心)的示例, 该示例将所有东西纠缠在一起。这种编码方式通常被称为” 意大利面条式编码” , 因为要了解其内部工作原理几乎是不可能的。为了简短起见, 以下摘录被删节;但是, 保留了原始样式和格式。
$id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; < table class="form-table"> < ?php $blog_prefix = $wpdb-> get_blog_prefix( $id ); $sql= "SELECT * FROM {$blog_prefix}options WHERE option_name NOT LIKE %s AND option_name NOT LIKE %s"; $query= $wpdb-> prepare( $sql, $wpdb-> esc_like( '_' ) . '%', '%' . $wpdb-> esc_like( 'user_roles' ) ); $options= $wpdb-> get_results( $query ); foreach ( $options as $option ) { if ( strpos( $option-> option_value, "\n" ) === false ) { ?> < tr class="form-field"> < th scope="row"> < label for="< ?php echo esc_attr( $option-> option_name ); ?> "> < ?php echo esc_html( ucwords( str_replace( '_', ' ', $option-> option_name ) ) ); ?> < /label> < /th> < ?php if ( $is_main_site & & in_array( $option-> option_name, array( 'siteurl', 'home' ) ) ) { ?> < td> < code> < ?php echo esc_html( $option-> option_value ); ?> < /code> < /td> < ?php } else { ?> < td> < input class="< ?php echo $class; ?> " name="option[< ?php echo esc_attr( $option-> option_name ); ?> ]" type="text" id="< ?php echo esc_attr( $option-> option_name ); ?> " value="http://www.srcmini.com/< ?php echo esc_attr( $option-> option_value ); ?>" size="40" < ?php disabled( $disabled ); ?> /> < /td> < ?php } ?> < /tr> < ?php } } // End foreach < /table>

首先, 这是完全无法理解的。我喜欢唯一的评论是End foreach, 这完全是多余的。我们具有数据库查询, 查询结果处理, HTML中嵌入的其他处理(如果你没有注意到, 则有if / else嵌套在其中), 输出转义以及将HTML模板混搭在一起的功能。另一个问题是$ id参数直接来自全局$ _REQUEST, 而不是将实际参数传递给函数。
鉴于此, 为什么WordPress核心多年来一直保持不变几乎是可以理解的。重构这种代码(尤其是在保持现有行为的同时)是一项真正的史诗任务, 没人愿意做。
那么我们如何正确地做呢?好吧, 要记住的一件事是, 没有一种真正的方法。我们提到了我们应该追求的上述素质:我们需要WordPress自定义PHP代码是可维护的和模块化的。让我们看看如何将以上代码拆分为模块。
  • 很明显, SQL查询应该在单独的模块中。 WordPress已经有一个很好抽象的WP_Query类, 应将其用作示例。
  • 所有HTML都进入模板。我们将在下面进一步介绍PHP模板。
  • 其余的PHP代码应包装在一个函数中(如果一个函数的代码太长或太复杂, 则应包含多个函数)。 $ id等参数是通过函数参数传递的。
这是上面示例的大大简化的重写:
function betterSiteSettings($args) { $data = http://www.srcmini.com/WP_Settings_Query($args); // process $data here $context = array_merge([], $data_processed, $other_data); return Template::render('template.name', $context); }

现代WordPress开发最佳实践#2:避免使用全局变量WordPress有太多的全局变量。为什么全局变量不好?它们使你的WordPress PHP代码难以遵循, 并使应用程序状态不可靠。任何PHP代码段(这意味着WordPress中安装的任何插件)都可以读取和写入全局变量, 因此不能保证它们包含有效数据。试图了解在” 循环” 之类的东西中使用了什么全局变量也不是一件容易的事。
让我们从实际的角度来看这件事。此示例来自WooCommerce。大概每个WordPress开发人员都知道它是什么-循环:
< ?php while ( have_posts() ) : the_post(); ?> < ?php wc_get_template_part( 'content', 'single-product' ); ?> < ?php endwhile; // end of the loop. ?>

上面的代码段呈现了一个产品模板。假设没有参数传递给wc_get_template_part, 它如何知道要显示什么产品?查看模板, 我们看到它以全局$ product; 开头, 因此就是当前产品对象的存储位置。
现在, 假设我们有一个用于搜索和过滤产品的目录页面, 并且我们希望在停留在同一页面上时显示” 产品详细信息” 弹出窗口。在后台, 前端脚本执行AJAX请求以获取该特定产品模板。我们不能简单地调用wc_get_template_part(‘ content’ , ‘ single-product’ ), 因为它不使用参数, 因此我们需要设置几个全局变量才能使其起作用。
更复杂的用例将涉及多个模板, 在这些模板中触发的挂钩以及第三方插件将其回调添加到这些挂钩中。它可以迅速升级。我们无法知道这些回调所依赖的全局状态。第三方插件可以随意修改其回调中的任何全局变量。我们开始使用系统而不是使用系统, 而遇到了来自不可靠全局状态的怪异错误。
将该产品ID作为参数传递会更明智吗?然后, 我们可以重用该模板, 而不必担心弄乱WordPress使用的全局变量。
现代WordPress开发最佳实践#3:使用面向对象编程(OOP)模块化导致了对象和面向对象编程的概念。在最基本的层次上, OOP是一种组织代码的方式。函数和变量捆绑在一起成为类, 分别称为类方法和属性。 WordPress插件手册建议使用OOP来组织WordPress自定义PHP代码。
OOP中的一个重要原则是限制对方法和属性的访问, 或者用PHP术语将其表示为私有或受保护, 因此只有其他类方法才能访问和更改它们。一个OOP术语是封装:将数据封装在类内部, 并且更改数据的唯一方法是使用提供的类方法。
与使用可以在整个代码库中的任何位置进行修改的全局变量相比, 这使调试和维护代码更加容易。考虑全局WordPress post变量。你可以在代码中的任何位置访问它, 许多功能取决于使用它。如果你可以将修改仅限于WordPress核心功能, 但任何人都可以阅读该怎么办?将全局post变量隐藏或封装在类中, 并围绕该类构建接口将使之成为可能。
这只是OOP的非常基本的描述, 以及如何将其用于现代WordPress开发中。为了进一步研究, 我彻底推荐了卡尔·亚历山大(Carl Alexander)的电子书, 《使用WordPress探索面向对象的编程》, 该书提供了有关WordPress中OOP主题的最全面, 最有用的内容。
重要的是要记住, OOP并非灵丹妙药:使用OOP可以像编写任何其他编程范例一样容易地编写错误代码。
【如何进行现代WordPress开发(第2部分)】让我们深入了解有关使用PHP进行WordPress开发的一些具体建议。
现代PHP最佳实践#1:目标PHP 7.0+使用现代PHP功能需要现代版本的PHP。完全没有理由支持低于7.0的PHP版本。甚至WordPress核心最早也要在2019年底使用PHP 7.0。
不过, 最好的做法是检查你的最低版本, 以避免在不兼容的环境中出现” 白屏死机” 。下面的代码片段显示了如何使用插件标头声明代码中带有保护条件的最低PHP版本。
< ?php /** * Plugin Name: My Awesome Plugin * Requires PHP: 7.0 */// bails if PHP version is lower than required if (version_compare(PHP_VERSION, '7.0.0', '< ')) { // add admin notice here return; }// the rest of the actual plugin here

现代PHP最佳实践2:采用PHP行业标准(PSR-2编码样式指南)PSR是PHP Framework Interop Group发布的建议。它们是任何现代PHP工作流程中的事实上的行业标准, 可以肯定地说, PHP社区总体上遵循这些标准。 PSR-2是描述编码风格的建议。流行的PHP框架(例如Symfony和Laravel)遵循PSR-2。
为什么要使用PSR-2而不是WordPress编码标准?主要是因为WordPress标准已过时, 并且未使用任何较新的语言功能。这是可以理解的, 因为WordPress核心必须遵循自己的标准。它直到最近才必须支持PHP 5.2, 并且PSR-2与PHP 5.2不兼容。
这可能并不明显, 但是除非你致力于核心, 否则不需要使用WordPress编码标准。将符合PSR-2标准的插件提交到WordPress插件目录不会有问题。实际上, 这样做有一些很好的理由。
现代PHP最佳实践#3:使用PHP模板引擎PHP不是模板引擎。它最初是一种语言, 但后来演变为功能齐全的编程语言, 没有理由继续使用它来进行模板制作。两个最流行的PHP模板引擎是Twig和Blade, 分别由Symfony和Laravel使用。本文将使用Twig作为示例模板引擎。但是, 刀片具有可比的特性和功能。我恳请你同时研究两者, 并自己决定最适合你的情况。
下面的示例比较了PHP模板及其对应的Twig模板。在PHP示例中, 显示和转义输出特别冗长:
foreach ( $options as $option ) { ?> < tr class="form-field"> < th scope="row"> < label for="< ?php echo esc_attr( $option-> option_name ); ?> "> < ?php echo esc_html( strtolower( $option-> option_name ) ); ?> < /label> < /th> < /tr> < ?php } // End foreach

在Twig中, 这更加简洁易读:
{% for option in options %} < tr class="form-field"> < th scope="row"> < label for="{{ option.option_name }}"> {{ option.option_name }} < /label> < /th> < /tr> {% endfor %}

Twig的主要优点是:
  • 简洁明了的语法
  • 自动输出转义
  • 通过继承和块扩展模板
在性能方面, Twig可以编译为PHP模板, 几乎没有任何开销。 Twig只有一部分PHP语言结构仅限于模板化。这迫使开发人员从模板中删除业务逻辑, 从而强制分离关注点。
甚至存在用于WordPress的Twig。它叫做Timber, 是开始创建更好的模板的好方法。 Timber入门主题是以OOP方式组织主题的完美示例。
现代PHP最佳实践4:使用ComposerComposer是PHP的依赖项管理器。它是一种工具, 它可以声明一个项目正在使用的库, 然后自动执行它们的下载, 安装和更新。然后, 你只需要包含Composer的自动加载文件vendor / autoload.php, 而不是手动要求每个库。
WordPress插件和主题通常不使用任何第三方库。部分原因是WordPress具有可满足几乎所有需求的广泛API, 部分原因是可能存在版本冲突。考虑两个需要相同PHP库但版本不同的插件。首先运行的插件获取正确的版本, 第二个插件也获取该版本。这很可能是另一种白屏死机的情况。
为避免冲突, 应在应用程序级别(即整个WordPress网站)上使用依赖项管理。这就是Roots(更确切地说, Bedrock)所做的。在软件包(插件或主题)级别使用时, Composer在使用第三方库时可能会导致冲突。这是一个已知问题。到目前为止, 唯一存在的解决方案是将外部库的名称空间重命名为唯一的名称, 而这并不是一件容易的事。
但是, Composer仍然有用途:自动加载自己的类。但是在进一步进行自动加载之前, 我们需要了解PHP名称空间。
现代PHP最佳实践#5:使用命名空间WordPress核心是一个遗留项目, 它使用全局命名空间, 或者换句话说, 根本没有命名空间。全局声明的任何类或函数(意味着不在另一个类或函数内)在整个代码库中的任何位置都可见。它们的名称不仅在你的代码库中必须是唯一的, 而且对于现在使用或将来可能使用的所有插件和主题都必须是唯一的。
命名冲突(例如, 使用已经存在的名称声明函数)通常会导致白屏死亡, 我们不希望这样。 WordPress Codex建议你为所有函数和类添加独特的前缀。因此, 我们没有像Order这样简单的类名, 而是得到了类似Akrte_Awesome_Plugin_Order的名称, 其中” Akrte” 是我刚刚编写的唯一前缀。
命名空间可以看作是组, 也可以看作是文件夹, 如果我们使用文件系统的类比的话, 则可以帮助组织代码并避免名称冲突。你可以使用以斜杠分隔的复杂名称空间, 就像嵌套文件夹一样。 (PHP名称空间特别使用反斜杠。)
这些名称空间部分称为子名称空间。如果使用名称空间, 则示例类Akrte_Awesome_Plugin_Order将为Akrte \ Awesome_Plugin \ Order。这里Akrte和Awesome_Plugin是名称空间部分(或子命名空间), 而Order是类名称。然后, 你可以添加一条use语句, 之后仅使用类名。它肯定看起来更好:
use Akrte\Awesome_Plugin\Order; $a = new Order;

显然, 名称空间应该是唯一的。因此, 我们应该给第一个” 根” 子命名空间一个唯一的名称, 通常是供应商名称。例如, 可以使用以下名称空间重做WooCommerce类WC_REST_Order_Notes_V2_Controller:
namespace WooCommerce\RestApi\V2\Controllers; class OrderNotes {}

如今, WooCommerce代码库确实使用名称空间。例如, 在WooCommerce REST API版本4中。
现代PHP最佳实践#6:使用自动加载器在大多数PHP工作流程中, 将PHP文件链接在一起的通常方法是使用require或include语句。随着项目的发展, 你在主插件文件中会获得数十个require语句。自动装带器可自动包括文件, 仅在需要时才这样做。从技术上讲, 它是一个函数, 在代码中首次遇到该函数时, 它需要一个包含类或函数的文件。不再需要手动添加任何require语句。
通常, 由于自动加载器仅加载特定请求中使用的模块, 因此通常还会显着提高性能。如果没有自动加载器, 即使请求仅使用你代码的10%, 也将包括整个代码库。
自动加载器功能需要知道你的类和函数位于哪些文件中。为此, 有一个PHP-FIG标准PSR-4。
它说名称空间的一部分(前缀)被指定为对应于基本文件夹。紧随其后的子命名空间对应于基本文件夹中的文件夹。最后, 类名对应于文件名。示例类Akrte \ AwesomePlugin \ Models \ Order将需要以下文件夹结构:
/awesome-plugin awesome-plugin.php /includes /Models Order.php

名称空间前缀Akrte \ AwesomePlugin \对应于在下面讨论的自动加载器配置中指定的includes文件夹。 Models子命名空间具有一个对应的Models文件夹, 并且Order类包含在Order.php中。
幸运的是, 你无需自己实现自动加载器功能。 Composer可以为你创建一个自动加载器:
  1. 安装作曲家
  2. 在项目的根文件夹中创建一个composer.json文件。它应该包含以下几行:
{ "name": "vendor-name/plugin-name", "require": {}, "autoload": { "psr-4": { "Akrte\\AwesomePlugin\\": "includes/" } } }

  1. 运行composer安装。
  2. 在主插件PHP文件顶部包含vendor / autoload.php, 如下所示:
< ?php /** * Plugin Name: My Awesome Plugin */defined('ABSPATH') || exit; require __DIR__ . '/vendor/autoload.php'; // do stuff

除名称空间外, WooCommerce的最新代码库还使用Composer自动加载器。
涵盖了这些PHP设计原则之后, 是时候将我们所有的PHP课程与WordPress后端自定义结合起来, 并提出最终建议。
现代WordPress开发最佳实践4:考虑使用根堆栈Roots是目前最全面的WordPress开发工作流程。但是, 我想说它不一定一定要在每个WordPress项目中使用, 因为:
  • 必须从一开始就使用根。确实, 这是最常见的原因。重构现有项目将耗资巨大。
  • 自以为是。同意时好, 反之则坏。例如, 你可能更喜欢其他组织主题的方式。固执己见的项目也需要时间来学习” 他们的方法” 。
  • 并非所有人都知道。在你维护使用Roots堆栈构建的网站之后, 将要跟进的开发人员可能不知道它是什么, 并且想知道WordPress文件夹发生了什么。我们应该考虑我们的WordPress开发人员。
总的来说, 你希望在致力于使用某个项目之前, 先充分了解该项目的所有利弊。
现代PHP和软件原理:使WordPress后端开发更可靠很明显, 没有一种真正的软件编写方法。诸如关注点分离之类的概念已有数十年历史。但是, 实际上它的含义一直受到争议。以CSS为例。开始时, 我们将其内联为HTML样式, 然后我们决定关注的分离是单独的CSS工作表。
快进十年:当今的JavaScript应用程序将组件用作关注点分离的实现。前端开发人员倾向于使用CSS-in-JS, 这基本上意味着要在HTML中再次内联CSS(嗯, 这不是那么简单, 但是你就知道了)。圈是完整的!
最佳实践一直是关于改善开发人员体验的:
必须编写程序供人们阅读, 并且只能偶然地使机器执行。 Abelson&Sussman, 计算机程序的结构和解释
此PHP WordPress教程中的一些实践可以在你的项目中快速便捷地实现。例如, 自动加载器:每个项目执行一次, 然后享受。另一方面, 新的软件体系结构构想需要时间, 实践和大量迭代才能变得方便和舒适。不过, 回报更大。你不仅会在做事上更有效率, 而且会喜欢做更多的事情。定期享受你为客户所做的工作也许是可持续的唯一途径。

    推荐阅读