CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
WordPress 开发入门/课时/PHP 架构
RawBlame
课时目录12 节

WordPress 开发入门

  1. 01WordPress 概览
  2. 02环境搭建
  3. 03主题开发
  4. 04区块编辑器
  5. 05插件生态
  6. 06Hooks 与 CPT
  7. 07PHP 架构
  8. 08数据库
  9. 09AJAX 与 Headless
  10. 10安全加固
  11. 11性能与运维
  12. 12综合练习

PHP 架构

OOP、Composer 与资源。

30 分钟

WordPress 里的 PHP 实战

这一课你将能做到

  1. 在模板里正确驱动主循环(The Loop),并说清它与主查询($wp_query)的关系
  2. 用 pre_get_posts 改主查询、用 WP_Query 开次级查询,且知道何时必须 wp_reset_postdata()
  3. 写出 WP 风格的 PHP 8.2 代码:Yoda 条件、命名空间、late escaping(输出时转义)

为什么要学

WP 本质是一个 PHP 应用。你学的 PHP 8.2 特性(只读属性、枚举、命名空间)在 WP 里全部可用,但 WP 有自己的「方言」:全局变量、The Loop、条件标签、转义函数家族。摸清这套方言,模板和插件才不会互相污染数据。

人话:The Loop 相当于一个游标。WP 根据 URL 先生成一次「主查询」存进全局 $wp_query,模板里 have_posts()/the_post() 就是推动游标逐条输出。你中途另开一个 WP_Query 查询会把「当前文章」这个全局指针挪走——不归位,后面的循环全部错乱,这是新手最高频的事故。

先建立图像

URL: /category/java/
   ↓ rewrite 解析 → 主查询 WP_Query( category_name=java ) 存入 $wp_query
   ↓ 模板层级选中 category.php
category.php:
   have_posts()? ──yes→ the_post() → $post 指向当前行 → the_title()/the_content()
        ↑                                        ↓
        └────────── while 循环直到 false ←──────┘
   ↓
wp_reset_postdata()(次级查询后必须调)

跟着做

步骤 1:标准主循环

在子主题 single.php 中:

<?php
get_header();
if ( have_posts() ) :
    while ( have_posts() ) :
        the_post();
        ?>
        <article>
            <h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
            <time><?php echo esc_html( get_the_date( 'Y-m-d' ) ); ?></time>
            <div class="content"><?php the_content(); ?></div>
        </article>
        <?php
    endwhile;
else :
    echo '<p>暂无内容</p>';
endif;
get_footer();

the_*() 系列直接输出(内部 echo),get_the_*() 返回值——需要转义、拼接时用后者。

步骤 2:条件标签分流

if ( is_front_page() ) {
    get_template_part( 'template-parts/hero', 'home' );
} elseif ( is_single() ) {
    get_template_part( 'template-parts/card', get_post_type() );
} elseif ( is_category( 'java' ) ) {
    echo '<div class="badge">Java 专栏</div>';
} elseif ( is_page( 'about' ) ) {
    get_template_part( 'template-parts/team' );
}

常用族:is_single/is_page/is_archive/is_category/is_tag/is_search/is_404/is_admin/is_paged。

步骤 3:改主查询的正确入口

需求:分类页每页显示 6 篇并排除置顶。不要在模板里写 query_posts(),挂 pre_get_posts:

add_action( 'pre_get_posts', function ( WP_Query $query ): void {
    if ( is_admin() || ! $query->is_main_query() ) {
        return;   // 只动前台主查询
    }
    if ( $query->is_category() ) {
        $query->set( 'posts_per_page', 6 );
        $query->set( 'ignore_sticky_posts', true );
    }
} );

这是 WP 面试的经典考点:改主查询用 pre_get_posts(查询发生前改 SQL),不要查询后再换内容。

步骤 4:次级查询与归位

侧边栏「最新课程」:

$courses = new WP_Query( array(
    'post_type'      => 'course',
    'posts_per_page' => 5,
    'no_found_rows'  => true,          // 不算总页数,更快
) );
if ( $courses->have_posts() ) :
    while ( $courses->have_posts() ) :
        $courses->the_post();          // 注意:把全局 $post 挪走了
        the_title();
    endwhile;
    wp_reset_postdata();               // 归位主查询
endif;

步骤 5:现代 PHP 落地到插件

<?php
namespace Qrocmq\Course;

enum Level: string {
    case Beginner = 'beginner';
    case Advanced = 'advanced';

    public function label(): string {
        return match ( $this ) {
            self::Beginner => '入门',
            self::Advanced => '进阶',
        };
    }
}

final class CourseMeta {
    public function __construct(
        public readonly string $title,
        public readonly Level $level,
        public readonly int $durationHours,
    ) {}
}

枚举、只读属性、match、命名空间与自动加载(Composer)在 WP 6.x + PHP 8.2 中可放心使用——类文件照常 require 或经 autoload 引入即可。

步骤 6:late escaping 输出

// 差:入库时净化就以为安全了(数据来源可能变化)
// 好:输出点转义,语义明确
echo esc_html( $post->post_title );          // 纯文本
echo esc_url( home_url( '/courses' ) );      // URL
echo esc_attr( $meta['level'] );             // 属性值
echo wp_kses_post( get_the_content() );      // 允许富文本白名单

讲清楚要点

  1. 主查询一生只有一次:由 URL 决定、before 模板渲染执行;改它用 pre_get_posts,不要在模板里再跑一遍同条件的 WP_Query 造成双倍 SQL。
  2. the_ 与 get_the_ 的分工**:前者输出(多数内部已转义),后者返回值交你处理——处理就别忘了 esc_*。
  3. 全局变量污染链:WP_Query→the_post() 改写全局 $post;任何「嵌套循环输出错位」的幽灵问题,先查 reset。
  4. WP 方言:函数名下划线小写、双引号、Yoda 条件('publish' === $status 防止误赋值)、_e()/__() 包裹可翻译字符串。
  5. PHP 8.2 的使用边界:枚举/readonly 随便用;动态属性已废弃(8.2 弃用警告),别再依赖 WP 老代码里 \stdClass 随手加属性的习惯。

你可能会懵的地方

  • have_posts() 循环里调用 the_post() 到底干了什么? 把游标后移一位并重置全局 $post 为当前文章——之后所有模板函数(the_title 等)都在操作这个 $post。

  • the_content() 和 get_the_content() 的实际区别? the_content() 应用了完整过滤器链(wpautop 段落化、短代码渲染、区块序列化转换),get_the_content() 是原始存储内容,直接输出会少掉全部加工。

  • get_posts() 和 new WP_Query 选哪个? 两者底层一样,get_posts 默认 no_found_rows=true 更轻,适合「拿几条就完事」;需要分页/计算总页数用 WP_Query。

  • 为什么我的页面出现两套循环内容重复? 主查询已渲染过一次,你在模板里又用相同参数跑了一遍次级查询。改主查询(pre_get_posts)而不是再查。

  • PHP 8.2 报 Deprecated: Creation of dynamic property? 老插件/老代码在运行时给对象动态加属性。定位到哪个插件(Query Monitor/日志),升级或替换;自己新代码用类属性或 #[\AllowDynamicProperties]。

错法对照

错法问题正解
query_posts( ... ) 改列表抛弃主查询、双份 SQL、破坏分页pre_get_posts + is_main_query
次级查询后不 wp_reset_postdata()全局 $post 污染,后续循环错乱每次次级查询结束立即归位
echo $post->post_titleXSS(标题可含 HTML 实体注入)echo esc_html(...)
用 == 比较 'publish' 和 0松散比较 PHP8 下触发意外Yoda 严格比较 ===
在 Loop 外调 the_title()没有当前文章,输出异常get_the_title( $post_id ) 带 ID

卡点急救

  1. 列表页输出重复文章:主查询被二次执行或循环没 break——检查是否误用 WP_Query 替代主查询。
  2. 循环内容全部错位:找最近一次未 reset 的次级查询,补 wp_reset_postdata()。
  3. 升级 PHP 8.2 后页面报 Deprecated 刷屏:先看日志定位插件,临时在开发环境保留警告逐个修,生产关闭 DISPLAY。

面试常问

  • pre_get_posts 和 WP_Query 分别在什么场景用? pre_get_posts 修改主查询(归档页数量、首页排除分类),发生在 SQL 生成前;WP_Query 用于模板内全新的次级查询(侧栏、相关文章),两者不可混用。

  • the_content() 的过滤器链说明什么? 输出经过 wpautop、do_shortcode、区块转换等多层 filter,这就是 filter 体系的价值:内容加工被拆成可插拔环节,插件可以在任意环节介入。

  • 如何在 WP 中安全输出一段用户提交的富文本? 输出时用 wp_kses_post 白名单过滤;纯文本用 esc_html;属性用 esc_attr;URL 用 esc_url——late escaping 保证无论数据源头如何变化,输出点是最后一道闸。

自测三问

  1. 主查询和次级查询的本质区别是什么?各自正确的修改/使用入口?
  2. wp_reset_postdata() 归位的是哪个变量?不归位的典型症状?
  3. 写出 four 个 esc_* 函数及其适用场景。

小结一句话

The Loop 是主查询的迭代器,pre_get_posts 是改主查询的唯一正门,次级查询用完必须归位;配合 WP 方言与 late escaping,你就能写出与核心逻辑和平共处的现代 PHP。

延伸

  • The Loop 官方文档
  • WP_Query 类参考
  • 下一课:数据库——$wpdb、表结构与 meta 查询的性能真相。
加载本节练习…
← 上一课Hooks 与 CPT下一课 →数据库