石家庄网站制作:用现代PHP改进WordPress代码

2019.08.13 mf_web

90

WordPress诞生于十五年前,由于它历史上保留了向后兼容性,其新版本的代码无法充分利用新版PHP提供的最新功能。虽然最新版本的PHP是7.3.2,但WordPress仍然提供PHP 5.2.4支持。

但那些日子很快就会结束!WordPress将升级其最低PHP版本支持,在2019年4月达到PHP 5.6,在2019年12月达到PHP 7(如果一切按计划进行)。然后我们可以最终开始使用PHP的命令式编程功能,而不必担心破坏客户的网站。欢呼!

由于WordPress十五年的功能代码影响了开发人员使用WordPress构建的方式,因此我们的网站,主题和插件可能会出现不那么优秀的代码,可以很乐意接受升级。

本文由两部分组成:

  1. 最相关的新功能 PHP版本5.3,5.4,5.5,5.6和7.0中添加了更多功能(请注意,没有PHP 6),我们将探索最相关的功能。

  2. 构建更好的软件 我们将仔细研究这些功能以及它们如何帮助我们构建更好的软件。

石家庄网站制作让我们首先探索PHP的“新”功能。

类,OOP,实体和设计模式

类和对象被添加到PHP 5中,因此WordPress已经使用了这些功能,但不是非常广泛或全面:WordPress中的编码范例主要是函数式编程(通过调用缺少应用程序状态的函数来执行计算)而不是对象面向编程(OOP)(通过操纵对象的状态来执行计算)。因此,我还描述了类和对象以及如何通过OOP使用它们。

OOP是生成模块化应用程序的理想选择:类允许创建组件,每个组件都可以实现特定功能并与其他组件交互,并且可以通过其封装的属性和继承提供自定义,从而实现高度的代码可重用性。因此,应用程序的测试和维护成本更低,因为个别功能可以与应用程序隔离并自行处理; 由于开发人员可以使用已经开发的组件并避免为每个应用重新发明轮子,因此也提高了生产率。

类具有属性和函数,可以通过使用private(仅可从定义类中protected访问)(可从定义类及其祖先和继承类中public访问)和(可从任何位置访问)来获得可见性。在函数中,我们可以通过在名称前添加名称来访问类的属性$this->:

class Person {
  protected $name;
  public function __construct($name) {
    $this->name = $name;
  }
  public function getIntroduction() {
    return sprintf(
      __('My name is %s'),
      $this->name
    );
  }}

通过new关键字将类实例化为对象,之后我们可以通过->以下方式访问其属性和函数:

$person = new Person('Pedro');echo $person->getIntroduction();// This prints "My name is Pedro"

继承类可以覆盖其祖先类中的函数public和protected函数,并通过在它们前面加上来访问祖先函数parent:::

class WorkerPerson extends Person {
  protected $occupation;
  public function __construct($name, $occupation) {
    parent::__construct($name);
    $this->occupation = $occupation;
  }
  public function getIntroduction() {
    return sprintf(
      __('%s and my occupation is %s'),
      parent::getIntroduction(),
      $this->occupation
    );
  }}$worker = new WorkerPerson('Pedro', 'web development');echo $worker->getIntroduction();// This prints "My name is Pedro and my occupation is web development"

可以创建一个方法abstract,这意味着它必须由继承类实现。abstract必须自己创建一个包含方法的类abstract,这意味着它无法实例化; 只有实现抽象方法的类才能被实例化:

abstract class Person {
  abstract public function getName();
  public function getIntroduction() {
    return sprintf(
      __('My name is %s'),
      $this->getName()
    );
  }}// Person cannot be instantiatedclass Manuel extends Person {
  public function getName() {
    return 'Manuel';
  }}// Manuel can be instantiated$manuel = new Manuel();

类还可以定义static方法和属性,这些方法和属性位于类本身下,而不是作为对象的类的实例化。这些是通过self::类内部访问的,并通过类+的名称::从外部访问:

class Factory {
  protected static $instances = [];
  public static function registerInstance($handle, $instance) {
    self::$instances[$handle] = $instance;
  }
  public static function getInstance($handle) {
    return self::$instances[$handle];
  }}$engine = Factory::getInstance('Engine');

为了充分利用OOP,我们可以使用SOLID原则为应用程序建立一个声音且易于定制的基础,并设计模式以经过实践检验的方式解决特定问题。设计模式是标准化的,并且有很好的文档记录,使开发人员能够理解应用程序中不同组件之间的相互关系,并提供一种以有序方式构建应用程序的方法,这有助于避免使用global $wpdb污染了全局变量的全局变量(例如)全球环境。

命名空间

命名空间被添加到PHP 5.3中,因此它们目前从WordPress核心中完全丢失。

命名空间允许在结构上组织代码库,以避免在不同项具有相同名称时发生冲突 - 以类似于操作系统目录的方式允许具有相同名称的不同文件,只要它们存储在不同目录中即可。命名空间对PHP项(例如类,特征和接口)执行相同的封装技巧,以避免在不同项具有相同名称时通过将它们放在不同的命名空间上而发生冲突。

在与第三方库交互时必须使用命名空间,因为我们无法控制其项目的命名方式,因此在为我们的项目使用标准名称(如“文件”,“记录器”或“上传器”)时可能会发生冲突。此外,即使在单个项目中,名称空间也会阻止类名变得非常长,以避免与其他类冲突,这可能导致名称如“MyProject_Controller_FileUpload”。

命名空间是使用关键字定义的namespace(在打开后立即放置在行上<?php),并且可以跨越多个级别或子名称空间(类似于放置文件的多个子目录),它们使用以下命令分隔\:

<?phpnamespace CoolSoft\ImageResizer\Controllers;class ImageUpload {}

要访问上面的类,我们需要完全限定其名称,包括其名称空间(并从以下开始\):

$imageUpload = new \CoolSoft\ImageResizer\Controllers\ImageUpload();

或者我们也可以将类导入当前上下文,之后我们可以直接通过名称引用该类:

use CoolSoft\ImageResizer\Controllers\ImageUpload;$imageUpload = new ImageUpload();

通过按照既定惯例命名命名空间,我们可以获得额外的好处。例如,通过遵循PHP标准建议PSR-4,应用程序可以使用Composer的自动加载机制来加载文件,从而降低复杂性并在依赖项之间添加无摩擦的互操作性。此约定建立包括供应商名称(例如公司名称)作为顶级子名称空间,可选地后跟包名称,然后只有一个内部结构,其中每个子名称空间对应一个具有相同名称的目录。结果将1到1映射驱动器中文件的物理位置,并使用文件中定义的元素的命名空间。

性状

特性被添加到PHP 5.4中,因此它们目前在WordPress核心中完全丢失。

PHP支持单继承,因此子类是从单个父类派生的,而不是从多个父类派生的。因此,不相互扩展的类不能通过类继承重用代码。Traits是一种机制,可以实现行为的水平组合,从而可以在不同类层次结构中的类之间重用代码。

特征类似于类,但是它不能单独实例化。相反,在特征内定义的代码可以被认为是在编译时“并粘贴”到编写类中。

使用trait关键字定义特征,之后可以通过use关键字将其导入任何类。在下面的例子中,两个完全不相关的类Person和Shop可以重复使用通过的性状相同的代码Addressable:

trait Addressable {
  protected $address;
  public function getAddress() {
    return $this->address;
  }
  public function setAddress($address) {
    $this->address = $address;
  }}class Person {
  use Addressable;}class Shop {
  use Addressable;}$person = new Person('Juan Carlos');$person->setAddress('Obelisco, Buenos Aires');

一个类也可以组成多个特征:

trait Exportable {
  public class exportToCSV($filename) {
    // Iterate all properties and export them to a CSV file
  }}class Person {
  use Addressable, Exportable;}

特征还可以由其他特征组成,定义抽象方法,并且当两个或更多个组合特征具有相同的功能名称时提供冲突解决机制,以及其他特征。

接口

接口被添加到PHP 5中,因此WordPress已经使用了这个功能,但非常谨慎:核心总共包含不到10个接口!

接口允许创建代码,该代码指定必须实现哪些方法,而无需定义如何实际实现这些方法。它们对于定义组件之间的契约非常有用,这可以提高应用程序的模块性和可维护性:实现接口的类可以是一个黑盒代码,只要接口中函数的签名不会改变,代码可以随意升级而不会产生重大变化,这有助于防止技术债务的积累。此外,它们可以通过允许将某个接口的实现交换到不同供应商的接口来帮助减少供应商锁定。因此,必须针对接口而不是实现来编写应用程序(并定义哪些是实际的实现)依赖注入)。

接口是使用interface关键字定义的,并且必须仅列出其方法的签名(即没有定义其内容),这必须具有可见性public(默认情况下,不添加可见性关键字也使其公开):

interface FileStorage {
  function save($filename, $contents);
  function readContents($filename);}

类定义它通过implements关键字实现接口:

class LocalDriveFileStorage implements FileStorage {
  function save($filename, $contents) {
    // Implement logic
  }
  function readContents($filename) {
    // Implement logic
  }}

一个类可以实现多个接口,将它们分开,:

interface AWSService {
  function getRegion();}class S3FileStorage implements FileStorage, AWSService {
  function save($filename, $contents) {
    // Implement logic
  }
  function readContents($filename) {
    // Implement logic
  }
  function getRegion() {
    return 'us-east-1';
  }}

由于接口声明了组件应该执行的操作的意图,因此适当地命名接口非常重要。

关闭

闭包被添加到PHP 5.3中,因此它们目前在WordPress核心中完全丢失。

闭包是一种实现匿名函数的机制,它有助于将全局命名空间从单用(或很少使用)函数中整理出来。从技术上讲,封闭是阶级的实例Closure,然而,在实践中,我们很可能幸福地没有意识到这一事实而没有任何伤害。

在闭包之前,每当将函数作为参数传递给另一个函数时,我们必须事先定义函数并将其名称作为参数传递:

function duplicate($price) {
  return $price*2;}$touristPrices = array_map('duplicate', $localPrices);

使用闭包,匿名(即没有名称)函数可以直接作为参数传递:

$touristPrices = array_map(function($price) {
  return $price*2;}, $localPrices);

闭包可以通过use关键字将变量导入其上下文:

$factor = 2;$touristPrices = array_map(function($price) use($factor) {
  return $price*$factor;}, $localPrices);

发电机

生成器被添加到PHP 5.5中,因此它们目前在WordPress核心中完全丢失。

生成器提供了一种简单的方法来实现简单的迭代器。生成器允许编写foreach用于迭代一组数据的代码,而无需在内存中构建数组。生成器函数与普通函数相同,除了不是返回一次,它可以yield根据需要多次,以便提供要迭代的值。

function xrange($start, $limit, $step = 1) {
    for ($i = $start; $i <= $limit; $i += $step) {
        yield $i;
    }}foreach (xrange(1, 9, 2) as $number) {
    echo "$number ";}// This prints: 1 3 5 7 9

参数和返回类型声明

不同参数的类型声明在不同版本的PHP中引入:WordPress是已经能够声明接口和阵列(其不:我几乎发现声明数组作为核心参数,并且没有接口的功能的一个实例),并且将很快就可以声明callables(在PHP 5.4中添加)和标量类型:bool,float,int和string(在PHP 7.0中添加)。返回类型声明已添加到PHP 7.0。

参数类型声明允许函数声明参数必须具有哪种特定类型。验证在调用时执行,如果参数的类型不是声明的类型,则抛出异常。返回类型声明是相同的概念,但是,它们指定将从函数返回的值的类型。类型声明有助于使函数的意图更容易理解,并避免运行时错误接收或返回意外类型。

参数类型在参数变量名之前声明,返回类型在参数之后声明,前面是::

function foo(boolean $bar): int {}

标量参数类型声明有两个选项:强制和严格。在强制模式下,如果错误的类型作为参数传递,它将被转换为正确的类型。例如,为需要字符串的参数赋予整数的函数将获得string类型的变量。在严格模式下,只接受确切类型声明的变量。

强制模式是默认模式。要启用严格模式,我们必须添加与declare声明一起使用的语句strict_types:

declare(strict_types=1);function foo(boolean $bar) {}

新的语法和运算符

WordPress已经可以通过函数识别可变长度的参数列表func_num_args。从PHP 5.6开始,我们可以使用...令牌来表示函数接受可变数量的参数,并且这些参数将作为数组传递给给定变量:

function sum(...$numbers) {
  $sum = 0;
  foreach ($numbers as $number) {
    $sum += $number;
  }
  return $sum;}

从PHP 5.6开始,常量可能涉及涉及数字和字符串文字的标量表达式,而不仅仅是静态值,还有数组:

const SUM = 37 + 2; // A scalar expressionconst LETTERS = ['a', 'b', 'c']; // An array

从PHP 7.0开始,还可以使用define以下命令定义数组:

define('LETTERS', ['a', 'b', 'c']);

PHP 7.0添加了几个新的运算符:Null合并运算符(??)和Spaceship运算符(<=>)。

Null合并运算符??是需要与isset()一起使用三元数的常见情况的语法糖。它返回第一个操作数(如果存在且不为NULL); 否则,它返回其第二个操作数。

$username = $_GET['user'] ?? 'nobody';// This is equivalent to:// $username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

Spaceship运算符<=>用于比较两个表达式,当第一个操作数分别小于,等于或大于第二个操作数时,返回-1,0或1。

echo 1 <=> 2; // returns -1echo 1 <=> 1; // returns 0echo 2 <=> 1; // returns 1

这些是添加到跨越5.3到7.0版本的PHP的最重要的新功能。可以通过浏览有关从版本迁移到版本的PHP文档来获取本文中未列出的其他新功能的列表。

接下来,我们将分析如何充分利用所有这些新功能,以及从Web开发的最新趋势,以生成更好的软件。

PHP标准建议

在PHP标准的建议是由一批来自流行的框架和库PHP开发人员创建的,试图建立规范,使不同的项目可以更加无缝地集成和不同的团队可以相互更好地工作。建议不是一成不变的:现有的建议可能会被弃用,而新建的建议可能会取而代之,而新建议则会不断发布。

目前的建议如下:

建议描述
编码样式
标准化格式化可以减少读取其他作者代码时的认知摩擦
PSR-1基本编码标准
PSR-2编码风格指南
自动
加载自动加载器通过将名称空间映射到文件系统路径来消除包含文件的复杂性
PSR-4改进的自动加载
接口
接口通过遵循预期的合同简化了项目之间的代码共享
PSR-3记录器接口
PSR-6缓存接口
PSR-11容器接口
PSR-13超媒体链接
PSR-16简单缓存
HTTP
互操作标准和接口,在客户端和服务器端具有处理HTTP请求和响应的不可知方法
PSR-7HTTP消息接口
PSR-15HTTP处理程序
PSR-17HTTP工厂
PSR-18HTTP客户端

在组件中思考和编码

组件使得可以使用框架中的最佳功能而无需锁定框架本身。例如,Symfony已作为一组可重用的PHP组件发布,可以独立于Symfony框架安装; Laravel是另一个PHP框架,它使用了几个Symfony组件,并发布了自己的可重用组件集,可供任何PHP项目使用。

所有这些组件都发布在Packagist中,这是一个公共PHP包的存储库,可以通过Composer轻松添加到任何PHP项目,Composer是一个非常流行的PHP依赖管理器。

WordPress应该是这种良性发展周期的一部分。不幸的是,WordPress核心本身不是使用组件构建的(几乎完全没有接口就证明了这一点),此外,它甚至没有通过Composer安装WordPress所需的composer.json文件。这是因为WordPress社区尚未同意 WordPress是否是网站的依赖关系(在这种情况下通过Composer安装它是合理的)或者它是否是网站本身(在这种情况下,Composer可能不是该工作的正确工具) 。

在我看来,如果我们期望WordPress在接下来的十五年中保持相关性(至少WordPress作为后端CMS),那么必须将WordPress识别为站点的依赖关系并通过Composer进行安装。原因很简单:在终端中只有一个命令,Composer可以从Packagist中发布的数千个包中声明和安装项目的依赖项,从而可以立即创建功能非常强大的PHP应用程序,开发人员喜欢这样工作。如果WordPress不适应这种模式,它可能会失去开发社区的支持并且被遗忘,因为在引入基于Git的部署之后FTP不再受欢迎。

我认为Gutenberg的发布已经证明WordPress是一个站点依赖而不是站点本身:Gutenberg将WordPress视为无头CMS,并且可以与其他后端系统一起运行,正如Drupal Gutenberg所说明的那样。因此,Gutenberg明确指出,为站点供电的CMS可以交换,因此应将其视为依赖。此外,Gutenberg本身的目的是基于通过npm发布的JavaScript组件(由核心提交者Adam Silverstein解释),因此如果希望WordPress客户端通过npm包管理器管理其JavaScript包,那么为什么不将此逻辑扩展到后端是为了通过Composer管理PHP依赖项?

现在好消息:没有必要等待解决这个问题,因为已经可以将WordPress视为站点的依赖关系并通过Composer安装它。约翰·P·布洛赫已经反映WordPress的核心在Git中,增加了一个composer.json文件,并且它在Packagist发布,以及罗茨基岩提供了一个包,用现代的开发工具支持,并增强安全性的自定义文件夹结构安装WordPress。主题和插件也包括在内; 只要它们已被列在WordPress 主题和插件目录中,它们就可以在WordPress Packagist下使用。

因此,创建WordPress代码而不考虑主题和插件是一个明智的选择,但是考虑组件,使它们可以通过Packagist使用以供任何PHP项目使用,并且另外打包和发布为主题和用于WordPress特定用途的插件。如果组件需要与WordPress API交互,则可以在接口后面抽象这些API ,如果需要,也可以为其他CMS实现。

添加模板引擎以改善视图层

如果我们遵循组件中思考和编码的建议,并将WordPress视为站点本身以外的站点依赖关系,那么我们的项目可以摆脱WordPress强加的边界,并导入从其他框架中获取的想法和工具。

在服务器端呈现HTML内容就是一个很好的例子,它通过普通的PHP模板完成。这个视图层可以通过模板引擎Twig(由Symfony)和Blade(由Laravel)增强,它们提供了非常简洁的语法和强大的功能,使其优于普通的PHP模板。特别是,Gutenberg的动态块可以轻松地从这些模板引擎中受益,因为它们在服务器端呈现块的HTML的过程与WordPress的模板层次结构分离。

建筑师一般用途的申请

对接口进行编码,并根据组件进行思考,这使我们能够构建一个通用的应用程序,并根据我们需要提供的特定用途对其进行自定义,而不是仅针对我们拥有的每个项目的特定用途进行编码。尽管这种方法在短期内成本更高(涉及额外的工作),但从长期来看,只需定制一般用途的应用程序,只需付出较少的努力即可获得额外的项目。

要使此方法有效,必须考虑以下因素:

避免固定依赖(尽可能多)

几年前,jQuery和Bootstrap(或者Foundation,或者<-insert你最喜欢的库 - >)可能已经被认为是必备品了,然而,他们一直在不断地对抗vanilla JS和更新的原生CSS功能。因此,五年前编码的一般用途项目依赖于这些库,现在可能不再合适了。因此,作为一般经验法则,对第三方库的固定依赖关系的数量越少,它就会被证明是长期的最新版本。

逐步增强功能

WordPress是一个完整的CMS系统,包括用户管理,因此开箱即用支持用户管理。但是,并非每个WordPress站点都需要用户管理。因此,我们的应用程序应考虑到这一点,并在每个方案中以最佳方式工作:在需要时支持用户管理,但不要在不支持时加载相应的资产。这种方法也可以逐步运行:假设客户需要实现联系我们表格但没有预算,所以我们使用功能有限的免费插件对其进行编码,另一个客户有预算从商业插件产品中购买许可证更好的功能。然后,我们可以将我们的功能编码为默认的基本功能,并且越来越多地使用系统中最具功能的插件中的功能。

连续代码和文档审查

通过定期审查我们以前编写的代码及其文档,我们可以验证它是否是关于新公约和技术的最新信息,如果不是,则在技术债务变得太昂贵而无法克服之前采取措施进行升级我们需要从头开始重新编写代码。

推荐阅读:注意:PHP和WordPress功能,可以使您的网站不安全

尝试尽量减少问题,但在发生问题时做好准备

没有软件是100%完美的:错误总是在那里,我们还没有找到它们。因此,我们需要确保一旦问题出现,就很容易解决。

简单

复杂的软件无法长期维护:不仅仅因为其他团队成员可能不理解它,而且因为编写它的人可能在未来几年内无法理解他/她自己的复杂代码。因此,生产简单的软件必须是一个优先事项,因为只有简单的软件才能正确和快速。

编译时失败比运行时更好

如果可以在编译时或运行时验证一段代码以防止错误,那么我们应该优先考虑编译时解决方案,因此可能会出现错误并在开发阶段和应用程序到达生产之前处理。例如,既const和define用于定义常量,但是,而const在编译时有效的,define在运行时验证。因此,只要有可能,使用const优先于define。

遵循此建议,可以通过将实际类作为参数而不是具有类名的字符串传递来增强挂钩类中包含的WordPress函数。在下面的示例中,如果Foo重命名了类,而第二个钩子将产生编译错误,则第一个钩子将在运行时失败,因此第二个钩子更好:

class Foo {
  public static function bar() {
  }}add_action('init', ['Foo', 'bar']); // Not so goodadd_action('init', [Foo::class, 'bar']); // Much better

出于与上述相同的原因,我们应该避免使用全局变量(例如global $wpdb):这些变量不仅会污染全局上下文并且不容易跟踪它们的来源,而且如果它们被重命名,则会产生错误。运行。作为解决方案,我们可以使用依赖注入容器来获取所需对象的实例。

处理错误/例外

我们可以创建一个Exception对象体系结构,以便应用程序可以根据每个特定问题做出适当的反应,以便在可能的情况下从中恢复,或者在没有时向用户显示有用的错误消息,并且通常为管理员记录错误解决问题。并始终保护您的用户免受死亡的白屏:所有未被捕获的Errors和Exceptions都可以通过功能拦截,set_exception_handler以在屏幕上打印不可怕的错误消息。

采用构建工具

构建工具可以通过自动执行手动执行非常繁琐的任务来节省大量时间。WordPress不提供与任何特定构建工具的集成,因此将这些工具合并到项目中的任务将完全落在开发人员身上。

有不同的工具可以实现不同的目的。例如,有一些构建工具可用于执行压缩和调整图像大小的任务,缩小JS和CSS文件,以及将文件到用于生成发布的目录,例如Webpack,Grunt和Gulp ; 其他工具有助于创建项目的脚手架,这有助于为我们的主题或插件(如Yeoman)生成文件夹结构。事实上,有了这么多工具,浏览文章比较不同的可用工具将有助于找到最适合我们需求的工具。

但是,在某些情况下,没有构建工具可以完全满足我们项目的需求,因此我们可能需要将自己的构建工具编码为项目本身的扩展。例如,我这样做是为了生成service-worker.js文件,以便在WordPress中添加对Service Workers的支持。

结论

石家庄网站制作由于强调保持向后兼容性,甚至扩展到PHP 5.2.4,WordPress还无法从PHP中添加的最新功能中受益,而这一事实使得WordPress成为一个不太令人兴奋的代码平台对于许多开发人员。

幸运的是,这些令人沮丧的日子可能很快就会结束,WordPress可能会成为一个闪亮而令人兴奋的平台再次编码:从2019年12月开始,PHP 7.0+的需求将提供大量的PHP功能,使开发人员能够生成更强大的功能。更高性能的软件。在本文中,我们回顾了最重要的最新PHP功能以及如何充分利用它们。

最近发布的Gutenberg可能是未来美好时光的标志:即使Gutenberg本身尚未被社区完全接受,至少它表明愿意将最新技术(如React和Webpack)纳入核心。这一系列事件让我想知道:如果前端可以进行这样的修改,为什么不将它扩展到后端呢?一旦WordPress需要至少PHP 7.0,现代工具和方法的升级可以加速:尽管npm成为首选的JavaScript包管理器,为什么不让Composer成为正式的PHP依赖管理器?如果块是在前端构建站点的新单元,为什么不使用PHP组件作为将功能合并到后端的单元?最后,如果Gutenberg将WordPress视为可交换的后端CMS,为什么还没有认识到WordPress是网站依赖,而不是网站本身?我将留下这些未解决的问题供你思考和思考。


最新案例

寒枫总监

来电咨询

18868949445

微信咨询

寒枫总监

TOP