文派素格 (WPSlug) 修复 ACF Pro 兼容性问题

·1 次浏览
  • 作者
    帖子
  • wenpai@wenpai
    楼主

    问题描述

    近期收到用户反馈,文派素格 (WPSlug) 插件与 Advanced Custom Fields Pro (ACF Pro) 同时启用时,会出现以下兼容性问题:

    1. ACF 字段 key 被篡改:field_abc_123 被转换为 field-abc-123(下划线变连字符),导致 ACF 自定义字段无法正常保存和读取。这是最严重的问题。
    2. ACF 字段组的 post_name 被干扰:WPSlug 尝试对 ACF 内部文章类型的 slug 进行拼音转换。
    3. ACF 字段组的 post_excerpt 被转拼音:ACF 内部调用 sanitize_title() 生成的摘要被 WPSlug 拦截处理。

    根因分析

    WPSlug 通过 sanitize_title(priority 9) 、 wp_insert_post_data(priority 10) 、 wp_unique_post_slug(priority 10) 、 transition_post_status(priority 10) 等钩子拦截 slug 生成。

    ACF Pro 注册了 5 个内部文章类型:acf-field-group 、 acf-field 、 acf-post-type 、 acf-taxonomy 、 acf-ui-options-page 。当 ACF 保存字段数据时,会经过 wp_insert_post → sanitize_title 链路,WPSlug 在这个链路上的过滤器会把 ACF 的 field key 中的下划线转成连字符。

    ACF 自身在 wp_unique_post_slug(priority 999) 有修复逻辑 return $original_slug,但此时 $original_slug 已经被 sanitize_title 污染,修复无效。

    WPSlug 的 isPostTypeEnabled() 在启用列表为空时默认允许所有文章类型 (空=全部),没有排除 ACF 的内部类型。

    修复方案

    1. 排除 ACF 内部文章类型

    在 WPSlug_Core 中新增排除列表,在 processPostData 、 processUniquePostSlug 、 handlePostStatusTransition 三个方法中增加前置守卫:

    private static $excluded_post_types = array(
        'acf-field-group',
        'acf-field',
        'acf-post-type',
        'acf-taxonomy',
        'acf-ui-options-page',
    );
    

    2. 调用栈检测保护 sanitize_title

    processSanitizeTitle 过滤器无法直接获取 post_type 信息,通过 isAcfInternalRequest() 方法检测调用栈中是否存在 ACF 相关的类或函数:

    • ACF_Internal_Post_Type 类
    • ACF_Field_Group 类
    • acf_update_field 、 acf_import_field_group 等函数

    3. 纵深防御

    在 WPSlug_Settings::isPostTypeEnabled() 中也加入 ACF 内部类型排除,双重保障。

    测试验证

    测试项 修复前 修复后
    ACF 字段 key field_abc_123 field-abc-123 :x: field_abc_123 :white_check_mark:
    ACF 字段组 post_name 被转拼音 保持 group_xxx key :white_check_mark:
    ACF 字段组 post_excerpt 被转拼音 保持原文 :white_check_mark:
    acf_get_field() 查找 找不到 :x: 正常找到 :white_check_mark:
    普通文章中文 slug 正常转拼音 正常转拼音 :white_check_mark:
    页面中文 slug 正常转拼音 正常转拼音 :white_check_mark:
    自定义文章类型 正常转拼音 正常转拼音 :white_check_mark:
    自定义分类法 正常转拼音 正常转拼音 :white_check_mark:
    草稿→发布转换 正常 正常 :white_check_mark:
    带 ACF 字段的文章保存 字段值丢失 字段值正常保持 :white_check_mark:

    更新方式

    已推送到 feicode 仓库,使用文派更新器的用户会自动收到更新推送。

    如果你在使用中遇到其他插件的兼容性问题,欢迎在此帖反馈。

    系统

    你的分析很全面,修复方案遵循了 :electric_plug: 插件开发的最佳实践。针对你的方案,补充几点纵深防御和性能优化的建议。

    1. 优化排除列表定义

    建议将排除列表定义为类常量,提高可读性和性能。

    class WPSlug_Core {
        const EXCLUDED_POST_TYPES = array(
            'acf-field-group',
            'acf-field',
            'acf-post-type',
            'acf-taxonomy',
            'acf-ui-options-page',
        );
    }
    

    2. 优化调用栈检测

    在 processSanitizeTitle 中,使用 debug_backtrace() 开销较大。建议结合 $_POST 或 $_REQUEST 数据中的 action 参数进行初步过滤,仅在必要时进行调用栈分析。

    private function isAcfInternalRequest() {
        // 快速检查:ACF 的 AJAX 请求通常有特定的 action
        if ( isset( $_POST['action'] ) && strpos( $_POST['action'], 'acf/' ) === 0 ) {
            return true;
        }
        // 深度检查:仅在必要时分析调用栈
        $trace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 8 );
        foreach ( $trace as $item ) {
            if ( isset( $item['class'] ) && strpos( $item['class'], 'ACF_' ) === 0 ) {
                return true;
            }
            if ( isset( $item['function'] ) && strpos( $item['function'], 'acf_' ) === 0 ) {
                return true;
            }
        }
        return false;
    }
    

    3. 使用 WordPress 核心的 doing_filter() 进行精确拦截

    在 sanitize_title 过滤器中,可以检查是否处于特定的 ACF 内部处理流程中。

    add_filter( 'sanitize_title', array( $this, 'processSanitizeTitle' ), 9, 3 );
    public function processSanitizeTitle( $title, $raw_title, $context ) {
        // 排除 ACF 的内部处理
        if ( $this->isAcfInternalRequest() ) {
            return $title; // 直接返回,不进行任何处理
        }
        // ... 原有逻辑
    }
    

    4. 考虑未来扩展性

    建议将排除逻辑抽象为一个公共方法,方便其他插件通过过滤器进行扩展。

    public function isExcludedPostType( $post_type ) {
        $excluded = apply_filters( 'wpslug_excluded_post_types', self::EXCLUDED_POST_TYPES );
        return in_array( $post_type, $excluded, true );
    }
    

    5. 插件审核注意事项

    如果计划将插件提交到官方目录,确保所有用户输入都经过验证和清理。你的修复方案主要涉及内部逻辑,但任何从设置页面接收的配置 (如排除列表的扩展) 都需要使用 sanitize_text_field() 或类似的函数进行处理。

    更多关于插件兼容性的讨论,请访问 /c/wordpress 。对于涉及用户数据或权限的安全问题,建议咨询 @security 。

Cyberforums · 原文 · Markdown

想回复或继续讨论?请到 Cyberforums 赛博论坛参与。

参与讨论 / 回复