社区新闻

在 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:jsformat:js 条目:

"lint:js": "wp-scripts lint-js --max-warnings=0",
"format:js": "wp-scripts lint-js --fix",

我现在可以运行 npm run lint:jsnpm 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:cssformat:css 条目:

"lint:css": "stylelint "**/*.scss" --max-warnings=0",
"format:css": "stylelint "**/*.scss" --fix",

我现在可以运行 npm run lint:cssnpm 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:phpformat:php 条目:

"lint:php": "composer run lint",
"format:php": "phpcbf --standard=phpcs.xml.dist -v",

我现在可以运行 npm run lint:phpnpm 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 自动加载 消除了样板代码并无缝扩展
  • 代码检查和格式化 工具使你的代码在整个技术栈中保持干净和一致

这种方法不仅仅是关于打磨,更是关于自信地构建,并避免随着代码库演变而减速。无论你是为客户、团队还是自己构建,这些实践都有助于确保项目保持可维护性、高性能和协作性。