在 WordPress 插件开发中实现命名空间和编码标准
随着 WordPress 项目日益复杂,组织代码库变得至关重要。无论是构建插件、主题还是区块库,一个清晰且可扩展的架构都能让开发更快、上手更容易,长期维护也远不那么痛苦。
在重构多区块插件:更智能地构建、更清晰地注册、更轻松地扩展中,我介绍了如何为单个插件内管理多个静态、动态和交互式区块设置一个灵活的结构。该设置是本文指南的基础。
在这里,我们将更进一步,引入 PHP 命名空间、使用 Composer 的自动加载,并在 JavaScript、CSS 和 PHP 中强制执行一致的编码标准。这些不仅仅是最佳实践——它们是随着项目增长,保持代码质量和可扩展性的实际步骤。
我们将逐步介绍:
- 使用 Composer 设置 PSR-4 自动加载
- 将插件功能组织成可重用的、目的驱动的类
- 使用自动化的代码检查和格式化工具强制执行一致的样式
这个工作流程足够灵活,适合独立开发者,也足够健壮,可以支持大型项目的团队协作。
先决条件
本文建立在精炼多区块插件之上。如果你已经按照该指南搭建了一个包含静态、动态和交互式区块的插件,那么你就可以开始下一步了。
想跳过设置?本文构建的完整插件可在 GitHub 上获取,本文的每个部分都在一个分支中体现。
想跟着做但不想构建多区块起点?你可以克隆多区块仓库,运行 npm install 并从那里开始。
无论哪种方法,都会为你提供一个即用型的起点,其中已经配置好了命名空间、Composer 自动加载和代码检查。
命名空间和类
随着插件增长,保持代码组织有序和易于管理变得很重要。我使用 PHP 命名空间并将功能拆分到类中,这样插件的每个部分都有明确的目的。这有助于避免命名冲突,并使代码库更易于扩展和维护。
Composer 和自动加载
为了简化类的加载方式,我使用带有 PSR-4 自动加载的 Composer。这意味着我不需要手动包含文件,Composer 会根据我定义的命名空间和文件夹结构自动加载类。
在这个设置中,我使用命名空间 Advanced_Multi_Block 来分组所有与插件相关的类。这就像一个前缀,有助于避免命名冲突,并将所有内容保持在插件的范围内。
我首先在插件根目录创建一个 composer.json 文件并添加以下内容:
{
"name": "multi-block/namespacing-coding-standards",
"description": "An advanced multi block plugin with custom functionality.",
"type": "wordpress-plugin",
"license": "GPL-2.0-or-later",
"autoload": {
"psr-4": {
"Advanced_Multi_Block\": "Functions/"
}
}
}
然后我运行:composer install。这会生成 vendor 文件夹和一个我可以在主插件文件中包含的 autoloader。
文件和类
有了自动加载后,我将所有基于类的 PHP 文件组织到插件内的一个 Functions 文件夹中。这使所有内容集中在一处,并随着插件增长更易于管理。
每个类处理一个特定的功能片段,并在 Advanced_Multi_Block 命名空间下。这种结构有助于保持职责清晰,并避免整个代码库中的命名冲突。
插件路径
这个类提供了获取插件基本路径和 URL 的辅助方法。我在整个插件中使用它,以避免重复逻辑或依赖硬编码值。
在 Functions 目录中,我创建 Plugin_Paths.php 并粘贴以下代码:
<?php
namespace Advanced_Multi_Block;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class Plugin_Paths {
public static function plugin_url() {
return plugin_dir_url( dirname( __FILE__ ) );
}
public static function plugin_path() {
return plugin_dir_path( dirname( __FILE__ ) );
}
}
注册区块
这个类处理查找和注册区块的逻辑。我在构造函数中钩入 init 动作,并使用 Plugin_Paths 来获取插件的路径。
register_blocks() 方法遍历每个区块目录,检查是否存在 block.json 文件,并注册该区块。如果区块包含 viewScriptModule 字段,它会添加一个过滤器,以便 WordPress 为交互式区块加载所需的资源。
在 Functions 目录中,我创建 Register_Blocks.php 并粘贴以下代码:
<?php
namespace Advanced_Multi_Block;
use Advanced_Multi_BlockPlugin_Paths;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class Register_Blocks {
public function __construct() {
add_action( 'init', array( $this, 'register_blocks' ) );
}
public function register_blocks() {
if ( function_exists( 'wp_register_block_types_from_metadata_collection' ) ) {
wp_register_block_types_from_metadata_collection( Plugin_Paths::plugin_path() . 'build/blocks', Plugin_Paths::plugin_path() . '/build/blocks-manifest.php' );
return;
}
if ( function_exists( 'wp_register_block_metadata_collection' ) ) {
wp_register_block_metadata_collection( Plugin_Paths::plugin_path() . 'build/blocks', Plugin_Paths::plugin_path() . '/build/blocks-manifest.php' );
}
$manifest_data = include Plugin_Paths::plugin_path() . 'build/blocks-manifest.php';
foreach ( array_keys( $manifest_data ) as $block_type ) {
register_block_type( Plugin_Paths::plugin_path() . "build/blocks/{$block_type}" );
}
}
}
资源队列
这个类注册两个全局资源入口点,一个用于编辑器,一个用于前端。这些脚本与区块特定的脚本是分开的,对于区块变体、编辑器 UI 增强或跨多个区块的全局行为等功能非常有用。
每个脚本使用其对应的 .asset.php 文件来确保正确处理依赖关系和版本控制。我使用 Plugin_Paths 辅助函数来引用正确的路径和 URL。
在 Functions 目录中,我创建一个名为 Enqueues.php 的文件并添加以下代码:
<?php
namespace Advanced_Multi_Block;
use Advanced_Multi_BlockPlugin_Paths;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class Enqueues {
public function __construct() {
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_assets' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ) );
}
/**
* Enqueues the block assets for the editor
*/
public function enqueue_block_assets() {
$asset_file = include Plugin_Paths::plugin_path() . 'build/editor-script.asset.php';
wp_enqueue_script(
'editor-script-js',
Plugin_Paths::plugin_url() . 'build/editor-script.js',
$asset_file['dependencies'],
$asset_file['version'],
false
);
}
/**
* Enqueues the block assets for the frontend
*/
public function enqueue_frontend_assets() {
$asset_file = include Plugin_Paths::plugin_path() . 'build/frontend-script.asset.php';
wp_enqueue_script(
'frontend-script-js',
Plugin_Paths::plugin_url() . 'build/frontend-script.js',
$asset_file['dependencies'],
$asset_file['version'],
true
);
}
}
更新主插件文件
有了类和自动加载后,我更新主插件文件以正确加载所有内容。首先,我检查 Composer 自动加载文件并引入它。然后我实例化核心类,以便它们的功能在 WordPress 中注册。
这种设置使主文件保持简洁和专注,同时让每个类处理自己的职责。
<?php
if (! defined('ABSPATH') ) {
exit;
}
// Include Composer's autoload file.
if ( file_exists( plugin_dir_path( __FILE__ ) . 'vendor/autoload.php' ) ) {
require_once plugin_dir_path( __FILE__ ) . 'vendor/autoload.php';
} else {
wp_trigger_error( 'Advanced Multi Block Plugin: Composer autoload file not found. Please run `composer install`.', E_USER_ERROR );
return;
}
// Instantiate the classes.
$advanced_multi_block_classes = array(
Advanced_Multi_BlockPlugin_Paths::class,
Advanced_Multi_BlockRegister_Blocks::class,
Advanced_Multi_BlockEnqueues::class,
);
foreach ( $advanced_multi_block_classes as $advanced_multi_block_class ) {
new $advanced_multi_block_class();
}
编码标准
保持代码一致性是改善协作和长期可维护性的最简单方法之一,尤其是在大型或共享的代码库中。本节展示了我如何使用 WordPress 推荐的工具为此插件设置 JavaScript、CSS 和 PHP 的代码检查和格式化。
注意: 这并不意味着是一套严格的规则集。它是一个可用的起点,你可以按原样使用,或根据团队的偏好进行调整。
为 JS 添加代码检查
为了在我的 JavaScript 文件中强制执行一致的代码,我使用 WordPress 推荐的配置,通过 ESLint 设置代码检查,通过 Prettier 设置格式化。
我安装所需的开发依赖项:
npm install --save-dev @wordpress/eslint-plugin eslint-config-prettier @wordpress/prettier-config eslint-config-prettier
然后我在插件根目录创建一个 .eslintrc.json 文件,包含以下配置。它扩展了 WordPress ESLint 插件的推荐规则,并设置了一些环境和解析器选项:
{
"extends": ["plugin:@wordpress/eslint-plugin/recommended"],
"env": {
"browser": true,
"es6": true,
"jquery": true
},
"parserOptions": {
"requireConfigFile": false,
"ecmaVersion": 2021,
"sourceType": "module"
},
"rules": {
"@wordpress/no-global-active-element": "warn",
"@wordpress/no-unsafe-wp-apis": "warn"
}
}
我还添加一个 .eslintignore 文件以防止对编译文件和第三方文件进行检查:
/build/
/vendor/
/node_modules/
*.css
*.scss
为了处理代码格式化,我创建一个包含我首选样式规则的 .prettierrc 文件:
{
"tabWidth": 4,
"useTabs": true,
"printWidth": 100,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "avoid",
"semi": true,
"bracketSameLine": false,
"jsxSingleQuote": false,
"jsxBracketSameLine": false
}
我还创建一个 .prettierignore 文件以排除生成的目录和依赖目录:
build
node_modules
vendor
在 package.json 中,我添加以下脚本来检查和格式化 JS 文件。这些替换任何现有的 lint:js 或 format:js 条目:
"lint:js": "wp-scripts lint-js --max-warnings=0",
"format:js": "wp-scripts lint-js --fix",
我现在可以运行 npm run lint:js 或 npm run format:js 来检查和修复 JavaScript 文件。
为 CSS 添加代码检查
为了确保我的 SCSS 文件的一致性并捕获潜在问题,我使用 WordPress 推荐的 SCSS 配置来配置 Stylelint。
我安装必要的开发依赖项:
npm install --save-dev @wordpress/stylelint-config stylelint stylelint-scss
然后我在插件根目录创建一个 .stylelintrc.json 文件,包含以下配置。这扩展了 WordPress SCSS 规则,并禁用了几个规则以匹配我偏好的样式:
{
"extends": ["@wordpress/stylelint-config/scss"],
"rules": {
"at-rule-no-unknown": null,
"selector-class-pattern": null,
"scss/at-rule-no-unknown": true
}
}
我还创建一个 .stylelintignore 文件以排除编译文件和供应商文件:
build/
node_modules/
vendor/
*.min.css
*.min.scss
在 package.json 中,我添加以下脚本来检查和修复 SCSS 文件。这些替换任何现有的 lint:css 或 format:css 条目:
"lint:css": "stylelint "**/*.scss" --max-warnings=0",
"format:css": "stylelint "**/*.scss" --fix",
我现在可以运行 npm run lint:css 或 npm run format:css 来检查和修复 CSS 文件。
为 PHP 添加代码检查
为了在我的 PHP 文件中强制执行 WordPress 编码标准,我使用带有 WordPress 编码标准 (WPCS) 规则集的 PHP_CodeSniffer。这个设置有助于捕获常见问题并保持整个插件的一致性。
我首先在插件根目录创建一个 phpcs.xml.dist 文件,包含以下配置:
<?xml version="1.0"?>
<ruleset name="WordPress Plugin Coding Standards">
<description>A custom set of rules to check for a WordPress plugin</description>
<!-- What to scan -->
<file>.</file>
<exclude-pattern>/build/</exclude-pattern>
<exclude-pattern>/node_modules/</exclude-pattern>
<exclude-pattern>/vendor/</exclude-pattern>
<exclude-pattern>src/blocks-manifest.php</exclude-pattern>
<exclude-pattern>build/*.asset.php</exclude-pattern>
<!-- How to scan -->
<arg value="sp"/>
<arg name="basepath" value="."/>
<arg name="colors"/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="4"/>
<!-- Rules: WordPress Coding Standards -->
<config name="minimum_supported_wp_version" value="6.6"/>
<rule ref="WordPress">
<exclude name="Generic.Arrays.DisallowShortArraySyntax"/>
<exclude name="Generic.Functions.CallTimePassByReference"/>
<exclude name="WordPress.PHP.YodaConditions.NotYoda"/>
</rule>
<rule ref="WordPress.Arrays.MultipleStatementAlignment">
<properties>
<property name="maxColumn" value="80"/>
</properties>
</rule>
<rule ref="WordPress.NamingConventions.PrefixAllGlobals">
<properties>
<property name="prefixes" type="array">
<element value="Advanced_Multi_Block"/>
</property>
</properties>
</rule>
<rule ref="WordPress.WP.I18n">
<properties>
<property name="text_domain" type="array">
<element value="advanced-multi-block"/>
</property>
</properties>
</rule>
</ruleset>
这是基于官方 WordPress 标准的一个起点。如果你想进一步自定义,我建议查阅完整的WPCS 文档。
接下来,我更新我的 composer.json 文件以安装所需的依赖项,并定义用于代码检查和格式化的脚本:
{
"name": "wp-dev-blog/refactor-multi-block-plugin",
"description": "An advanced multi block plugin with custom functionality.",
"type": "wordpress-plugin",
"license": "GPL-2.0-or-later",
"autoload": {
"psr-4": {
"Advanced_Multi_Block\": "Functions/"
}
},
"require-dev": {
"wp-coding-standards/wpcs": "^3.1"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
},
"scripts": {
"format": "./vendor/bin/phpcbf --report-summary --report-source || true",
"lint": "./vendor/bin/phpcs"
}
}
然后我运行:composer update 来安装新的包。
在 package.json 中,我添加以下脚本来检查和修复 PHP 文件。这些替换任何现有的 lint:php 或 format:php 条目:
"lint:php": "composer run lint",
"format:php": "phpcbf --standard=phpcs.xml.dist -v",
我现在可以运行 npm run lint:php 或 npm run format:php 来检查和修复 PHP 文件。
全局命令
为了在所有环境中保持所有代码检查命令的一致性,我将以下内容添加到 package.json:
"lint": "npm run lint:js && npm run lint:php && npm run lint:css",
"format": "npm run format:js && npm run format:php && npm run format:css",
使用 Composer 自动加载
在 composer.json 中定义了 Composer 的 PSR-4 自动加载后,你就不需要为你的类手动编写 require 语句。主插件文件加载生成的 vendor/autoload.php,Composer 会处理其余部分。
每当你添加、移动或重命名类时,运行:composer dump-autoload
这一步会刷新类映射,以便发现新的或更新的文件。这是一个简单但重要的习惯,可以防止“类未找到”错误,并保持插件结构与命名空间同步。
结论
通过添加命名空间、Composer 自动加载和清晰的编码标准,你创建了一个经久耐用的基础。你的代码变得更易于理解、测试和扩展——并且随着你的插件或主题的增长,它也能保持这种状态。
此设置的每个组成部分都发挥着作用:
- 命名空间和类 分离关注点并减少冲突
- Composer 自动加载 消除了样板代码并无缝扩展
- 代码检查和格式化 工具使你的代码在整个技术栈中保持干净和一致
这种方法不仅仅是关于打磨,更是关于自信地构建,并避免随着代码库演变而减速。无论你是为客户、团队还是自己构建,这些实践都有助于确保项目保持可维护性、高性能和协作性。