WordPress函数:wp_parse_str 解析查询字符串

编辑文章

简介

wp_parse_str 函数将 URL 格式的查询字符串解析并转换为一个 PHP 关联数组,常用于处理 URL 参数或表单序列化数据。

语法

此函数定义于 WordPress 核心文件 wp-includes/formatting.php 中。它内部封装了 PHP 的原生 parse_str() 函数,但额外处理了 WordPress 历史版本中的“魔术引号”问题,确保了在不同服务器环境下的解析一致性。

wp_parse_str( string $input_string, array &$output_array ): void
参数 类型 必填 说明
$input_string string 需要被解析的查询字符串(例如:'name=John&age=25')。
&$output_array array 解析后的参数将被填充到这个变量中。注意这是一个引用传递的参数。
返回值 void 此函数无返回值,结果通过修改第二个参数 &$output_array 来传递。

重要历史提示:在 WordPress 3.4 之前,wp_parse_str() 的参数顺序与 PHP 原生的 parse_str() 相反。现在的顺序(字符串在前,数组在后)与 PHP 保持一致。了解这一点有助于阅读遗留代码。

用法

基础用法

最基础的场景是解析一个格式良好的查询字符串,并将其转换为一个关联数组,便于在代码中访问各个参数。

// 示例:解析一个简单的查询字符串
$query_string = 'post_type=page&posts_per_page=5&orderby=date';
$args = array(); // 必须先声明一个数组变量

wp_parse_str( $query_string, $args );

// 现在 $args 数组包含了解析后的参数
var_dump( $args );
/*
输出:
array(3) {
  ["post_type"]=>
  string(4) "page"
  ["posts_per_page"]=>
  string(1) "5"
  ["orderby"]=>
  string(4) "date"
}
*/

// 你可以像使用普通数组一样使用这些值
if ( isset( $args['post_type'] ) ) {
    echo '查询的文章类型是:' . esc_html( $args['post_type'] );
}

进阶用法

一个典型的进阶场景是从当前页面的 URL 中获取查询参数,并将其用于构建自定义查询。这在创建自定义筛选功能或处理插件设置页面时非常有用。

// 示例:从当前URL获取参数,并用于 WP_Query
// 假设当前URL是:https://example.com/?category=news&featured=1

// 获取当前URL中的查询字符串部分
$current_query_string = $_SERVER['QUERY_STRING']; // ‘category=news&featured=1’

$query_args = array(); // 初始化数组,用于存放解析后的参数
wp_parse_str( $current_query_string, $query_args );

// 现在将解析出的参数安全地合并到 WP_Query 参数中
$custom_query = new WP_Query(
    array(
        'post_type'      => 'post',
        'posts_per_page' => 10,
        'category_name'  => isset( $query_args['category'] ) ? sanitize_title( $query_args['category'] ) : '',
        'meta_query'     => array(
            // 注意:这里仅作示例,实际中需要更严格地验证 ‘featured’ 的值
            array(
                'key'   => '_featured',
                'value' => isset( $query_args['featured'] ) ? '1' : '',
            ),
        ),
    )
);

// ... 使用 $custom_query 进行循环输出 ...

易错点

混淆参数顺序

这是最常见的错误。错误地将数组作为第一个参数传入,导致期望解析的字符串未被处理,而传入的数组却被清空。

// 错误示例
$my_array = array( 'some_data' => 'value' );
wp_parse_str( $my_array, $query_string ); // 参数顺序错误!

// 正确顺序
wp_parse_str( $query_string, $my_array );

误解返回值

试图直接获取函数的返回值会导致错误,因为此函数没有返回值。必须通过第二个参数(引用)来获取结果。

// 错误示例
$result = wp_parse_str( 'key=value', $array ); // $result 为 null

// 正确做法
$array = array();
wp_parse_str( 'key=value', $array );
// 现在 $array 是 array( 'key' => 'value' )

忽略输入来源的安全性

wp_parse_str 本身只负责解析字符串结构,并不对解析出的内容进行任何安全过滤或验证。直接使用来自用户(如 $_GET$_POST)的原始字符串是极其危险的,可能导致安全漏洞。

// 危险示例:直接解析不可信的输入
wp_parse_str( $_GET['user_input'], $params );
// 如果 $_GET['user_input'] 是 `evil_code=<script>alert('xss')</script>`,
// 那么 $params['evil_code'] 就包含恶意脚本。

// 相对安全的做法是,在特定、受控的上下文中使用,或先对输入字符串进行初步验证。
// 更好的做法见下方‘最佳实践’。

最佳实践

安全地使用解析结果

鉴于 wp_parse_str 不处理数据安全,对解析后得到的数据进行验证、清理和转义是开发者的责任。永远不要信任解析后的数据,尤其是在进行数据库操作或前端输出时。

// 示例:安全地处理并输出解析后的数据
$raw_input = $_SERVER['QUERY_STRING']; // 来自URL,不可信
$params = array();
wp_parse_str( $raw_input, $params );

// 1. 验证:检查必须的参数是否存在、类型是否正确
$user_id = isset( $params['user_id'] ) ? absint( $params['user_id'] ) : 0; // 转换为非负整数
if ( ! $user_id ) {
    wp_die( '无效的用户ID' );
}

// 2. 清理:用于数据库查询时,使用 prepare 语句
global $wpdb;
$user = $wpdb->get_row(
    $wpdb->prepare(
        "SELECT * FROM {$wpdb->users} WHERE ID = %d",
        $user_id
    )
);

// 3. 转义:在HTML上下文中输出前,必须转义
if ( $user ) {
    echo '<h2>用户:' . esc_html( $user->display_name ) . '</h2>'; // 输出时转义
}

预处理输入字符串

在调用 wp_parse_str 之前,可以先对输入字符串进行一些预处理,例如使用 sanitize_text_field 移除不必要的空格和非法字符,或者使用 wp_strip_all_tags 剥离所有HTML/PHP标签,这能为后续处理提供一个更干净的基础。

// 示例:在解析前清理输入字符串
$dirty_string = $_POST['form_data']; // 可能包含多余空格或换行
$clean_string = sanitize_text_field( $dirty_string ); // 清理字符串

$form_data = array();
wp_parse_str( $clean_string, $form_data );
// 现在 $form_data 中的数据已经过初步清理

保持代码清晰与可维护

当解析的字符串结构复杂,或解析逻辑在多处使用时,应将逻辑封装成有明确命名和注释的函数或方法。这有助于其他开发者(或未来的你)理解代码意图。

// 示例:封装解析逻辑
function my_plugin_parse_filter_params( $query_string ) {
    $params = array();
    wp_parse_str( $query_string, $params );

    // 定义我们期望并允许的参数及其默认值、清理方式
    $allowed = array(
        'category' => array(
            'default' => '',
            'sanitize' => 'sanitize_title',
        ),
        'page' => array(
            'default' => 1,
            'sanitize' => 'absint',
        ),
        // ... 更多参数
    );

    $clean_params = array();
    foreach ( $allowed as $key => $rules ) {
        $value = isset( $params[ $key ] ) ? $params[ $key ] : $rules['default'];
        $clean_params[ $key ] = call_user_func( $rules['sanitize'], $value );
    }

    return $clean_params;
}

// 在代码中使用
$filters = my_plugin_parse_filter_params( $_SERVER['QUERY_STRING'] );
// $filters 现在是一个完全验证和清理过的安全数组