WordPress 主题模式中 PHP 的运用与实践
WordPress 块主题的 Patterns(模式)系统为开发者提供了一个独特的 PHP 执行环境。与传统的模板文件不同,Patterns 在注册阶段(init 钩子)就已经编译并保存为 HTML 块标记,这使得许多常见的 WordPress 函数(如 is_page()、get_post())在 Patterns 中无法正常工作。本文深入探讨了这一技术限制背后的原理,并详细讲解如何正确地在 Patterns 中使用 PHP 实现文本国际化、动态资源加载以及循环输出等功能。无论你是刚开始接触块主题开发,还是从经典主题过渡而来,这份指南都能帮你避开常见的坑,写出更优雅的 Pattern 代码。
理解 Patterns 的执行时机
在块主题开发中,Patterns 和普通模板文件大的区别在于执行时机的不同。普通模板文件是在页面加载时才执行 PHP 代码,而 Patterns 则是在 WordPress 初始化阶段(init 钩子)就被处理了。
具体来说,当你注册一个 Pattern 时,WordPress 会立即解析其中的 PHP 代码,并将生成的 HTML 块标记保存下来。这意味着:
- 注册时执行:Pattern 中的 PHP 代码在
init钩子触发时运行 - 渲染时展示:实际展示给用户的是早已生成好的静态 HTML
这个特性带来一个重要限制:在 Pattern 注册时,WordPress 的全局数据(如当前查询对象、文章数据等)还没有准备好。所以像下面这样的代码是无法工作的:
php
<?php if ( is_page() ) : ?>
<!-- wp:paragraph -->
<p><?php the_title(); ?></p>
<!-- /wp:paragraph -->
<?php endif; ?>
is_page() 和 the_title() 都依赖于当前页面的查询数据,但在 Pattern 注册阶段这些数据根本不存在,所以这段代码无论放在哪里都不会按预期执行。
如果你之前做过经典主题开发,这个特点确实需要适应。在经典主题中,模板文件是在页面请求时才被加载执行的,所有全局数据都已经就绪。而 Patterns 更像是”提前编译好的模板片段”,在编译时就要确定所有内容。
从实际案例开始
为了更好地理解 Patterns 中 PHP 的用法,我们从一个简单的 Hero(英雄区域)Pattern 开始。创建一个 patterns/hero.php 文件,放入以下代码:
php
<?php
/**
* Title: Hero
* Slug: themeslug/hero
* Categories: featured
*/
?>
<!-- wp:cover {"overlayColor":"contrast","isUserOverlayColor":true,"align":"full"} -->
<div class="wp-block-cover alignfull">
<span aria-hidden="true" class="wp-block-cover__background has-contrast-background-color has-background-dim-100 has-background-dim"></span>
<div class="wp-block-cover__inner-container">
<!-- wp:heading {"textAlign":"center"} -->
<h2 class="wp-block-heading has-text-align-center">欢迎来到我的网站</h2>
<!-- /wp:heading -->
<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">这是我的温馨小窝。</p>
<!-- /wp:paragraph -->
<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons">
<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button">按钮 A</a></div>
<!-- /wp:button -->
<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button">按钮 B</a></div>
<!-- /wp:button -->
</div>
<!-- /wp:buttons -->
</div>
</div>
<!-- /wp:cover -->
这个 Pattern 在编辑器中会呈现为一个带有标题、描述文字和两个按钮的封面区域。接下来,我们将逐步为它添加 PHP 动态功能。
文本国际化处理
Patterns 大的优势之一就是可以轻松处理文本国际化。在模板或模板部件中,如果你需要翻译文本,的办法就是把内容迁移到 Pattern 中,因为 Patterns 支持 PHP 的国际化函数。
WordPress 提供了一套完整的国际化函数:
| 函数 | 用途 |
|---|---|
__() | 返回翻译后的字符串 |
_e() | 直接输出翻译后的字符串 |
esc_html__() | 返回经过转义的翻译字符串 |
esc_html_e() | 输出经过转义的翻译字符串 |
以我们 Hero Pattern 中的标题为例,原先的写法是硬编码的:
html
<h2 class="wp-block-heading has-text-align-center">欢迎来到我的网站</h2>
这种写法无法被翻译工具识别。改成使用 esc_html_e() 后:
html
<h2 class="wp-block-heading has-text-align-center"><?php esc_html_e( '欢迎来到我的网站', 'themeslug' ); ?></h2>
同样的方法应用到段落和两个按钮上。修改后的完整代码如下:
php
<?php
/**
* Title: Hero
* Slug: themeslug/hero
* Categories: featured
*/
?>
<!-- wp:cover {"overlayColor":"contrast","isUserOverlayColor":true,"align":"full"} -->
<div class="wp-block-cover alignfull">
<span aria-hidden="true" class="wp-block-cover__background has-contrast-background-color has-background-dim-100 has-background-dim"></span>
<div class="wp-block-cover__inner-container">
<!-- wp:heading {"textAlign":"center"} -->
<h2 class="wp-block-heading has-text-align-center"><?php esc_html_e( '欢迎来到我的网站', 'themeslug' ); ?></h2>
<!-- /wp:heading -->
<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center"><?php esc_html_e( '这是我的温馨小窝。', 'themeslug' ); ?></p>
<!-- /wp:paragraph -->
<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons">
<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( '按钮 A', 'themeslug' ); ?></a></div>
<!-- /wp:button -->
<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( '按钮 B', 'themeslug' ); ?></a></div>
<!-- /wp:button -->
</div>
<!-- /wp:buttons -->
</div>
</div>
<!-- /wp:cover -->
注意 themeslug 是文本域(Text Domain),它必须与主题中声明的文本域一致,否则翻译文件无法正确加载。
动态资源引用
Patterns 中另一个常见需求是引用主题内的资源文件,比如图片、字体或 CSS 文件。在硬编码的情况下,图片 URL 可能是这样的:
text
http://localhost/wp-content/uploads/2023/10/hero-background.jpg
这种写法有两个问题:
- 不可移植:迁移到其他环境时 URL 会失效
- 依赖用户上传:图片必须被导入媒体库
更好的做法是使用 get_theme_file_uri() 函数,它返回主题目录的 URL:
php
<?php echo esc_url( get_theme_file_uri( 'assets/images/hero-background.jpg' ) ); ?>
将这个应用到 Cover 块的背景图片上。假设你从 WordPress 图片目录下载了一张图片,保存到主题的 /assets/images/hero-background.jpg,那么修改后的 Cover 块代码如下:
php
<!-- wp:cover {"url":"<?php echo esc_url( get_theme_file_uri( 'assets/images/hero-background.jpg' ) ); ?>","dimRatio":50,"overlayColor":"contrast","align":"full"} -->
<div class="wp-block-cover alignfull">
<span aria-hidden="true" class="wp-block-cover__background has-contrast-background-color has-background-dim"></span>
<img class="wp-block-cover__image-background" alt="" src="<?php echo esc_url( get_theme_file_uri( 'assets/images/hero-background.jpg' ) ); ?>" data-object-fit="cover"/>
<div class="wp-block-cover__inner-container">
<!-- 内容省略 -->
</div>
</div>
<!-- /wp:cover -->
esc_url() 用于对 URL 进行转义,防止潜在的安全问题。这是一个必须养成的好习惯。
使用循环来避免重复代码
在软件开发中,”不要重复自己”(DRY)是一个重要的原则。观察我们的 Hero Pattern,两个按钮的结构完全相同,只是文字不同:
php
<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( '按钮 A', 'themeslug' ); ?></a></div>
<!-- /wp:button -->
<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( '按钮 B', 'themeslug' ); ?></a></div>
<!-- /wp:button -->
使用 foreach 循环可以优雅地解决这个问题。首先在 Pattern 文件顶部定义按钮数据:
php
$buttons = array(
__( '创业故事', 'themeslug' ),
__( '加入我们', 'themeslug' )
);
然后替换原有的两个按钮块:
php
<?php foreach ( $buttons as $button ) : ?>
<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php echo esc_html( $button ); ?></a></div>
<!-- /wp:button -->
<?php endforeach; ?>
终的完整 Pattern 代码:
php
<?php
/**
* Title: Hero
* Slug: themeslug/hero
* Categories: featured
*/
$buttons = array(
__( '创业故事', 'themeslug' ),
__( '加入我们', 'themeslug' )
);
?>
<!-- wp:cover {"url":"<?php echo esc_url( get_theme_file_uri( 'assets/images/hero-background.jpg' ) ); ?>","dimRatio":50,"overlayColor":"contrast","align":"full"} -->
<div class="wp-block-cover alignfull">
<span aria-hidden="true" class="wp-block-cover__background has-contrast-background-color has-background-dim"></span>
<img class="wp-block-cover__image-background" alt="" src="<?php echo esc_url( get_theme_file_uri( 'assets/images/hero-background.jpg' ) ); ?>" data-object-fit="cover"/>
<div class="wp-block-cover__inner-container">
<!-- wp:heading {"textAlign":"center"} -->
<h2 class="wp-block-heading has-text-align-center"><?php esc_html_e( '欢迎来到我的网站', 'themeslug' ); ?></h2>
<!-- /wp:heading -->
<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center"><?php esc_html_e( '这是我的温馨小窝。', 'themeslug' ); ?></p>
<!-- /wp:paragraph -->
<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons">
<?php foreach ( $buttons as $button ) : ?>
<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php echo esc_html( $button ); ?></a></div>
<!-- /wp:button -->
<?php endforeach; ?>
</div>
<!-- /wp:buttons -->
</div>
</div>
<!-- /wp:cover -->
这样做的好处显而易见:
- 易于维护:修改按钮文字只需改数组
- 易于扩展:添加新按钮只需在数组中新增一项
- 代码更简洁:减少了重复的 HTML 结构
实战小结
Patterns 中的 PHP 使用有其独特的规则和限制:
你可以在 Patterns 中做的:
- 使用国际化函数(
__()、_e()、esc_html_e()等) - 使用
get_theme_file_uri()等资源路径函数 - 使用 PHP 的控制结构(循环、条件判断)
- 定义和使用变量、数组
- 调用任何不依赖全局查询数据的 WordPress 函数
你不能在 Patterns 中做的:
- 使用
is_page()、is_single()、is_home()等条件标签 - 使用
get_post()、get_queried_object()等获取当前文章数据的函数 - 直接访问
$post、$wp_query等全局变量
这个限制的本质在于 Patterns 的”编译时”与”运行时”分离。一旦你理解了这一点,就能在设计 Pattern 时做出正确的判断。
对于更复杂的动态需求(比如需要在渲染时获取实时数据),可以考虑使用 WordPress 的 Block Bindings API 或自定义块。但这些属于更高级的话题,超出了 Patterns 本身的能力范围。
掌握好 Patterns 中 PHP 的使用,你的块主题开发效率会有显著提升。从文本国际化到资源管理,再到代码复用,PHP 为 Patterns 带来的可能性远不止于此。