分析 WordPress `wp_localize_script()` 函数的源码:如何安全地将 PHP 变量传递给 JavaScript。

好嘞,各位朋友们,今天咱们来聊聊 WordPress 里一个相当实用,但又容易被忽略的小可爱:wp_localize_script()。这玩意儿就像个秘密通道,能安全地把 PHP 世界里的宝贝数据偷偷运到 JavaScript 地盘,让你的前端代码也能享受到后端数据的滋润。

为什么要用 wp_localize_script()

直接在 JavaScript 里硬编码 PHP 变量,或者用 AJAX 疯狂请求数据?这听起来就让人头皮发麻。前者修改起来麻烦,后者则会增加服务器负担。wp_localize_script() 提供了一个更优雅、更安全的方式,帮你解决这个问题。

wp_localize_script() 的工作原理

简单来说,wp_localize_script() 做了这么几件事:

  1. 登记脚本: 你需要先用 wp_register_script()wp_enqueue_script() 注册或引入你的 JavaScript 文件。这是前提。
  2. 准备数据: 你要准备好一个 PHP 数组,这个数组里包含了你想要传递给 JavaScript 的数据。
  3. 本地化: wp_localize_script() 会把你的 PHP 数组转换成一个 JavaScript 对象,并且把它附加到你指定的 JavaScript 脚本上。
  4. 访问数据: 在 JavaScript 里,你可以通过一个全局变量来访问这些数据。

源码剖析:wp_localize_script() 的庐山真面目

咱们先来看看 wp-includes/functions.wp-scripts.php 文件里 wp_localize_script() 函数的源码(简化版,去掉了部分错误处理和兼容性代码):

function wp_localize_script( $handle, $object_name, $l10n ) {
    global $wp_scripts;

    if ( ! is_a( $wp_scripts, 'WP_Scripts' ) ) {
        return false;
    }

    $wp_scripts->localize( $handle, $object_name, $l10n );

    return true;
}

可以看到,wp_localize_script() 本身只是一个简单的包装函数,它把任务委托给了 $wp_scripts 对象(WP_Scripts 类的实例)的 localize() 方法。

接下来,我们深入 WP_Scripts 类的 localize() 方法:

public function localize( $handle, $object_name, $l10n ) {
    if ( ! is_scalar( $handle ) ) {
        return false;
    }

    if ( isset( $this->registered[ $handle ] ) ) {
        $this->registered[ $handle ]->add_data( 'data', sprintf(
            'var %s = %s;',
            $object_name,
            wp_json_encode( $l10n )
        ) );
    }

    return true;
}

这个方法才是真正的干活者,它做了以下事情:

  1. 验证 $handle: 确保 $handle 是一个标量值(字符串或数字),也就是脚本的句柄。
  2. 检查脚本是否已注册: 确认你想要本地化的脚本已经被注册了(通过 wp_register_script()wp_enqueue_script())。
  3. 添加数据: 这是关键一步。它使用 sprintf() 函数构建了一段 JavaScript 代码:var %s = %s;
    • %s 会被替换成 $object_namewp_json_encode( $l10n )
    • $object_name 是你指定的 JavaScript 对象的名字,比如 'my_ajax_object'
    • wp_json_encode( $l10n ) 是一个非常重要的函数,它把你的 PHP 数组 $l10n 转换成 JSON 字符串。
  4. 使用 add_data(): add_data() 方法把这段 JavaScript 代码添加到脚本的 data 属性中。 这个 data 属性最终会被 WordPress 在生成 HTML 的时候插入到 <script> 标签里。

代码示例:从 PHP 到 JavaScript 的数据旅行

假设你有一个 my-script.js 文件,你想把一些 PHP 数据传递给它。

  1. 注册/引入 JavaScript 文件 (functions.php):

    function my_enqueue_scripts() {
        wp_register_script( 'my-script', get_template_directory_uri() . '/js/my-script.js', array( 'jquery' ), '1.0', true );
        wp_enqueue_script( 'my-script' );
    }
    add_action( 'wp_enqueue_scripts', 'my_enqueue_scripts' );

    这里我们先用 wp_register_script() 注册了 my-script.js,依赖于 jQuery,版本号是 1.0,并且指定在页面的 <footer> 之前加载 (true)。然后用 wp_enqueue_script() 引入它。

  2. 准备 PHP 数据并本地化 (functions.php):

    function my_localize_script() {
        $translation_array = array(
            'ajax_url' => admin_url( 'admin-ajax.php' ),
            'nonce'    => wp_create_nonce( 'my_ajax_nonce' ),
            'some_data' => 'Hello from PHP!',
            'numbers' => array(1, 2, 3),
            'is_admin' => is_admin()
        );
        wp_localize_script( 'my-script', 'my_ajax_object', $translation_array );
    }
    add_action( 'wp_enqueue_scripts', 'my_localize_script' );

    这里我们创建了一个 PHP 数组 $translation_array,包含了我们需要传递给 JavaScript 的数据。

    • ajax_url: WordPress 的 AJAX 端点。
    • nonce: 一个安全令牌,用于验证 AJAX 请求的来源。
    • some_data: 一个简单的字符串。
    • numbers: 一个数字数组。
    • is_admin: 一个布尔值,表示当前是否在 WordPress 后台。

    然后,我们用 wp_localize_script() 把这个数组传递给 my-script.js,并指定 JavaScript 对象的名字为 my_ajax_object

  3. 在 JavaScript 中访问数据 (my-script.js):

    jQuery(document).ready(function($) {
        console.log(my_ajax_object.ajax_url);
        console.log(my_ajax_object.nonce);
        console.log(my_ajax_object.some_data);
        console.log(my_ajax_object.numbers[0]); // 输出 1
        console.log(my_ajax_object.is_admin);
    
        // 使用 AJAX
        $.ajax({
            url: my_ajax_object.ajax_url,
            type: 'POST',
            data: {
                action: 'my_ajax_action',
                security: my_ajax_object.nonce,
                some_data: 'Hello from JavaScript!'
            },
            success: function(response) {
                console.log(response);
            }
        });
    });

    在 JavaScript 中,我们可以通过 my_ajax_object 这个全局变量来访问 PHP 传递过来的数据。 注意,这个变量名必须和 wp_localize_script() 中指定的 $object_name 一致。

安全性考量:wp_json_encode()wp_create_nonce()

  • wp_json_encode() 的重要性: wp_json_encode() 函数至关重要,它负责把 PHP 数据转换成 JSON 字符串。 JSON 是一种通用的数据交换格式,JavaScript 可以很容易地解析它。 wp_json_encode() 会自动处理特殊字符的转义,防止 XSS 攻击。 永远不要手动构建 JSON 字符串,一定要用 wp_json_encode()!

  • wp_create_nonce() 的作用: 在上面的例子中,我们使用 wp_create_nonce() 创建了一个 nonce(Number used Once,只用一次的数字),用于验证 AJAX 请求的来源。 Nonce 可以防止 CSRF(Cross-Site Request Forgery,跨站请求伪造)攻击。 在 AJAX 请求中,我们需要把 nonce 传递给服务器,然后在服务器端验证它。

一个更完整的 AJAX 例子(PHP 端):

// 在 functions.php 中

// 处理 AJAX 请求
add_action( 'wp_ajax_my_ajax_action', 'my_ajax_callback' );
add_action( 'wp_ajax_nopriv_my_ajax_action', 'my_ajax_callback' ); // 如果需要未登录用户也能访问

function my_ajax_callback() {
    // 验证 nonce
    check_ajax_referer( 'my_ajax_nonce', 'security' );

    // 获取 JavaScript 传递过来的数据
    $some_data = $_POST['some_data'];

    // 做一些处理
    $response = 'You sent: ' . $some_data . '.  The server says hello!';

    // 返回 JSON 响应
    wp_send_json_success( $response );

    // 始终要用 wp_die() 结束 AJAX 处理函数
    wp_die();
}

wp_localize_script() 的优势总结

优点 描述
安全性 使用 wp_json_encode()wp_create_nonce() 可以有效地防止 XSS 和 CSRF 攻击。
代码组织性 把 PHP 数据和 JavaScript 代码分离,提高了代码的可维护性和可读性。
性能 避免了频繁的 AJAX 请求,减少了服务器的负担。
方便性 可以方便地把 PHP 变量传递给 JavaScript,无需手动构建 URL 或传递参数。
国际化 (I18n) 支持 wp_localize_script() 最初的设计目的是为了方便 JavaScript 代码的国际化。虽然现在主要用于传递数据,但它仍然可以用来传递翻译字符串。

注意事项和最佳实践

  • 只传递必要的数据: 不要传递过多的数据,只传递 JavaScript 真正需要的数据。
  • 数据类型: wp_json_encode() 可以处理字符串、数字、布尔值、数组和对象。 确保你的 PHP 数组只包含这些类型的数据。
  • 对象命名冲突: 选择一个不太可能与其他 JavaScript 库或插件冲突的 $object_name。可以使用前缀,例如 myplugin_ajax_object
  • 调试: 如果数据没有正确传递,可以使用浏览器的开发者工具来检查 JavaScript 对象的内容。
  • 替代方案: 对于更复杂的数据交互,可以考虑使用 WordPress REST API。
  • Enqueue 时机: 确保在 wp_enqueue_scripts 动作中同时 enqueue script 和 localize script。 如果localize script 的动作早于 enqueue script, 那么localize 将不会生效。
  • Nonce 的作用域: 为每个 AJAX 请求使用不同的 nonce,或者定期刷新 nonce。
  • 验证数据: 在 PHP 端,验证从 JavaScript 接收到的数据,防止恶意数据。

总结

wp_localize_script() 是一个简单而强大的工具,可以安全地把 PHP 变量传递给 JavaScript。 通过合理地使用它,可以提高 WordPress 主题和插件的开发效率,并增强安全性。 记住,要始终关注安全性,并遵循最佳实践。

希望今天的讲解对大家有所帮助! 下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注