我最近整理了一系列文章,其中介绍了WordPress中的名称空间和自动加载 。 如果您不熟悉以上任何一个术语,那么我建议您阅读该系列文章。
您可以预期学习的要点如下:
在本系列文章中,我们将确切地了解什么是PHP名称空间,为什么有益,以及如何使用它们。 然后,我们将研究如何使用自动加载器自动加载所需的文件,而不必在代码中手动加载它们。
在编写该系列,特别是自动装带器的系列时,我忍不住意识到与我共享代码时引入的许多代码味道。
这并不是说自动装带器是坏的或不起作用。 如果您下载了该插件,运行了该插件,或者紧随其后并编写了自己的自动加载器,那么您知道它确实可以工作。
但是在一个专注于名称空间的系列中(这是面向对象编程的一部分),我忍不住感到不舒服,在系列结束时让自动加载器处于最终状态。
不要误解我的意思:我仍然支持该系列影片,所涵盖的内容以及我们制作的作品的最终结果。 但是从面向对象的角度来看,还有更多工作可以完成。 因此,在此后续系列中,我们将从面向对象编程的角度重新审视自动加载器的概念。
具体来说,我们将讨论以下概念:
- 介面
- 接口实现
- 单一责任原则
- 以及面向对象编程的核心其他原则和思想
我希望,当我们完成本系列文章时,我们不仅可以将自动加载器重构为更易于维护和易于阅读的东西,而且还可以遵循更大的面向对象的实践。
话虽如此,让我们开始吧。
入门
就像我写的几乎每一篇文章一样,我喜欢尝试做两件事:
- 定义前进的路线图。
- 为您提供启动机器并运行所需的一切。
在开始编写任何代码之前,让我们现在开始。
我们的路线图
在接下来的两篇文章中,我们将研究一些面向对象的概念,这些概念将使我们能够改进上一个系列中构建的插件。
如果您没有该插件的副本,则可以下载该插件的副本 ; 但是,我将在每个教程中共享完整的代码示例,注释和解释。
本系列文章假设您对我们将要讨论的任何概念一无所知 ,因此我们将从头开始。 您需要做的就是在计算机上安装足够的软件来启动和运行WordPress副本,以及一个可以在其中编辑代码的编辑器。
你需要什么
首先,您需要以下工具:
- 一个本地开发环境, 至少包括PHP 5.6.20 , Apache Web服务器和MySQL数据库服务器 。 MAMP 4非常适合此功能。
- WordPress 4.6.1所在的目录。
- 您很喜欢使用文本编辑器或IDE来编写插件。
- WordPress插件API的使用知识。
在所有这些都准备好之后(我知道它看起来很多,但实际上并不需要很长的时间来设置),您需要安装上面链接的插件的副本。
完成后,我们准备开始讨论界面和单一责任原则。
定义的接口
根据软件的使用背景,当您听到“界面”一词时,您可能最终会想到用户在屏幕上实际看到的内容。 您知道: 用户界面 。
但是,当涉及到面向对象设计时,这根本不是我们要谈论的内容。 相反,我们在谈论类接口。 这通常可以描述为类以及它公开给其他类与之通信的公共方法。
有更正式的定义吗? 当然。 维基百科提供了一个:
在计算中,接口是共享边界,计算机系统的两个独立组件将在该边界上交换信息。
实际上,这还不错。 它足够通用,几乎可以应用于任何编程语言,而且技术上也不至于使我们无法理解。
再说一次,我们正在使用PHP。 那么,PHP手册就该主题提供了什么?
对象接口使您可以创建代码,该代码指定类必须实现的方法,而不必定义如何处理这些方法。
我认为这是一个很好的定义。 这很简单。 (就我所知)它与语言无关,并且在大多数(如果不是全部)面向对象的语言中都可以很好地工作。 该手册甚至说:
接口的定义方式与类相同,但是用interface关键字代替了class关键字,并且没有定义任何方法的内容。
接口中声明的所有方法必须是公共的。 这就是界面的本质。
如果要实现自己的接口,尤其是涉及此插件时,我们必须记住两点。 即,我们需要记住以下几点:
- 我们像定义类一样定义接口,但是我们使用
interface
关键字。 - 接口中定义的方法必须是公共的(而不是
protected
或private
),因为这可以保证其他类可以访问的功能。
在我们进行下一步之前,WordPress项目中的界面会是什么样? 这是我一直在从事的项目的示例:
<?php
/**
* Defines a common set of functions that any class responsible for loading
* stylesheets, JavaScript, or other assets should implement.
*/
interface Assets_Interface {
public function init();
public function enqueue();
}
上面的代码应明确其用途,尤其是考虑到位于界面上方的注释。
众所周知,WordPress可以注册和排队两种资产:样式表和JavaScript文件。
由于这两个都是资产,因此当我们为样式表管理或JavaScript管理创建类时,我们有理由将其概括为资产接口,对吗?
此外,我们知道我们想使用init方法初始化文件,以便我们可以将指定的入队功能与适当的WordPress API函数挂钩。 另外,您可能还要做其他一些工作,如果是这种情况,那么您可能要向接口添加另一个方法签名。
无论哪种情况,实现此接口的任何类都必须为以下方法提供功能。 那么实现此接口的类是什么样的呢?
这是一个非常简单的类示例,该类将样式表添加到WordPress的管理区域:
<?php
class CSS_Loader implements Assets_Interface {
/**
* Registers the 'enqueue' function with the proper WordPress hook for
* registering stylesheets.
*/
public function init() {
add_action(
'admin_enqueue_scripts',
array( $this, 'enqueue' )
);
}
/**
* Defines the functionality responsible for loading the file.
*/
public function enqueue() {
wp_enqueue_style(
'tutsplus-namespace-demo',
plugins_url( 'assets/css/admin.css', dirname( __FILE__ ) ),
array(),
filemtime( plugin_dir_path( dirname( __FILE__ ) ) . 'assets/css/admin.css' )
);
}
}
现在, 如何通过PHP实例化和执行此操作超出了本教程的范围。 当我们开始重构自动装带器时,将会看到很多东西。
但是我想说明的一点是,接口定义了类必须实现的公共方法。 它没有定义实现,但是它保证了一定的功能集将存在并且可以被第三方类公开访问。
单一责任原则
谈论单一责任原则的挑战之一是,它常常被误解为以下含义:
类(或函数或例程)应该只做一件事。
但这有点误导了,不是吗? 我的意思是,即使是简单的for循环也可以做很多事情:它初始化一个值,与值进行比较,然后在循环体完成时迭代该值。
相反,该原则规定如下:
一个班级只有一个改变的理由。
由于我们中有很多开发人员利用Google来帮助我们进行日常工作,因此,我认为了解这一想法的来源很重要。 就是说,这是由众所周知的鲍勃·马丁叔叔(Robert Bob Martin)或罗伯特·马丁(Robert Martin)所著,他曾撰写过许多顶级编程书籍 。
一类只具有一个改变理由的观念带有很多含义,不是吗? 这是当今自动装带器想到的一个例子。
让我们回顾一下代码(我知道它不是一个类,它是一个函数,但是原理是适用的):
<?php
/**
* Dynamically loads the class attempting to be instantiated elsewhere in the
* plugin.
*
* @package Tutsplus_Namespace_Demo\Inc
*/
spl_autoload_register( 'tutsplus_namespace_demo_autoload' );
/**
* Dynamically loads the class attempting to be instantiated elsewhere in the
* plugin by looking at the $class_name parameter being passed as an argument.
*
* The argument should be in the form: TutsPlus_Namespace_Demo\Namespace. The
* function will then break the fully-qualified class name into its pieces and
* will then build a file to the path based on the namespace.
*
* The namespaces in this plugin map to the paths in the directory structure.
*
* @param string $class_name The fully-qualified name of the class to load.
*/
function tutsplus_namespace_demo_autoload( $class_name ) {
// If the specified $class_name does not include our namespace, duck out.
if ( false === strpos( $class_name, 'Tutsplus_Namespace_Demo' ) ) {
return;
}
// Split the class name into an array to read the namespace and class.
$file_parts = explode( '\\', $class_name );
// Do a reverse loop through $file_parts to build the path to the file.
$namespace = '';
for ( $i = count( $file_parts ) - 1; $i > 0; $i-- ) {
// Read the current component of the file part.
$current = strtolower( $file_parts[ $i ] );
$current = str_ireplace( '_', '-', $current );
// If we're at the first entry, then we're at the filename.
if ( count( $file_parts ) - 1 === $i ) {
/* If 'interface' is contained in the parts of the file name, then
* define the $file_name differently so that it's properly loaded.
* Otherwise, just set the $file_name equal to that of the class
* filename structure.
*/
if ( strpos( strtolower( $file_parts[ count( $file_parts ) - 1 ] ), 'interface' ) ) {
// Grab the name of the interface from its qualified name.
$interface_name = explode( '_', $file_parts[ count( $file_parts ) - 1 ] );
$interface_name = $interface_name[0];
$file_name = "interface-$interface_name.php";
} else {
$file_name = "class-$current.php";
}
} else {
$namespace = '/' . $current . $namespace;
}
}
// Now build a path to the file using mapping to the file location.
$filepath = trailingslashit( dirname( dirname( __FILE__ ) ) . $namespace );
$filepath .= $file_name;
// If the file exists in the specified path, then include it.
if ( file_exists( $filepath ) ) {
include_once( $filepath );
} else {
wp_die(
esc_html( "The file attempting to be loaded at $filepath does not exist." )
);
}
}
此功能中发生了很多事情。 只是从较高的角度看,我们可以看到它正在执行以下操作:
- 它确定PHP是否正在尝试调用此函数中的代码。
- 该函数确定我们是否正在加载接口或类。
- 然后,自动加载器尝试包含该文件,否则将引发错误。
如果一个类只应该有一个改变的理由,那么上述单个功能可以改变的原因有三个(以上只是一个较高的层次)。 此外,代码也可以更清晰。
我不是要回避代码注释的人,但是上面的代码中发生了很多解释。 当您刚开始编写自动装带器时很好,但是当您进入像我们这样的更高级的领域时,那将无法承受更严格的体系结构。
结合在一起
在这里,接口和单一责任原则可以携手并进。
正如接口为实现者提供的功能提供一组功能签名(或合同)一样,它可以确保实现该接口的任何类都严格遵守其定义。
但这提出了一个有趣的问题:我们应该有多个接口吗? 答案是,这取决于您要创建的解决方案的性质。
就我们而言,我认为这是有道理的。
毕竟,我们希望检查传入的类名称,并确定它是接口还是类,或者是否值得抛出错误。 此外,我们正在努力确保系统的其余部分中包含正确的文件。
但这超出了本教程的主题,在编写更多代码时,我们将不得不更深入地探讨。
结论
至此,我们已经涵盖了必要的概念,以便我们可以开始重构自动装带器。 也就是说,我们将引入一个接口,确保我们的代码遵守该接口,然后确保我们的一个或多个类及其各自的方法遵循单一职责原则。
此外,我们将确保它在插件的上下文中继续正常运行,已正确记录并遵循WordPress编码标准。
同时,如果您有兴趣阅读有关WordPress上下文中的面向对象编程的更多信息,可以在我的个人资料页面上找到我以前的所有教程。 请随时在我的博客上关注我,或在Twitter上关注我,我经常谈论这两者。
与往常一样,如果您正在寻找其他实用程序来帮助您构建不断增长的WordPress工具集,或者例如要学习和精通WordPress的代码,请不要忘记看看我们在Envato Market中提供的功能 。
话虽如此,本系列的下一篇教程将更加实用。 也就是说,我们将编写代码,重构现有代码,并应用在本教程中学到的所有知识。 在此之前,请不要在评论中留下任何反馈。
翻译自: https://code.tutsplus.com/tutorials/object-oriented-autoloading-in-wordpress-part-1--cms-27381