WordPress函数:shortcode_atts 安全处理短代码属性
编辑文章简介
shortcode_atts 是 WordPress 中用于处理短代码属性的核心函数,它能将用户输入的属性与默认值安全合并,并过滤未定义的属性,确保短代码安全稳定运行。
语法
shortcode_atts( array $defaults, array $provided, string $shortcode = '' )
位置: wp-includes/shortcodes.php
参数说明:
| 参数 | 类型 | 描述 |
|---|---|---|
$defaults |
数组 | 短代码支持的属性及其默认值 |
$provided |
数组 | 用户实际提供的属性(通常从 $atts 获取) |
$shortcode |
字符串 | 短代码名称(可选,用于过滤钩子) |
返回值: 合并后的属性数组,已过滤无效属性并应用了类型转换。
用法
在短代码回调函数中,首先使用 shortcode_atts 处理属性:
function my_shortcode_callback( $atts, $content = null ) {
// 定义默认值并合并用户输入
$atts = shortcode_atts(
array(
'title' => '默认标题',
'count' => 5,
'type' => 'post'
),
$atts,
'my_shortcode'
);
// 现在可以安全使用 $atts['title'], $atts['count'] 等
return '<h2>' . esc_html( $atts['title'] ) . '</h2>';
}
add_shortcode( 'my_shortcode', 'my_shortcode_callback' );
易错点
直接使用未处理的 $atts
错误做法:
function bad_shortcode( $atts ) {
// 直接访问可能不存在的数组键
$title = $atts['title']; // 可能触发 PHP 警告
return $title;
}
原因: 用户可能不提供 title 属性,导致未定义索引错误,且无法控制默认值。
忽略类型转换
错误做法:
$atts = shortcode_atts(
array( 'count' => '5' ),
$atts
);
$query_count = $atts['count'] * 2; // 字符串运算可能产生意外结果
原因: 短代码属性始终是字符串,数学运算前需要类型转换。
忘记转义输出
错误做法:
$atts = shortcode_atts( array( 'title' => '' ), $atts );
echo "<div>$atts[title]</div>"; // XSS 漏洞!
原因: 用户输入的属性可能包含恶意脚本,直接输出会导致跨站脚本攻击。
误解第三个参数
错误理解: 认为 $shortcode 参数只是用于调试的标识。
实际情况: 该参数会生成 shortcode_atts_{$shortcode} 过滤钩子,是扩展短代码属性的重要机制。
最佳实践
安全性强化
输入验证与清理
在 shortcode_atts 处理后进行额外的数据验证:
function secure_shortcode( $atts ) {
$defaults = array(
'user_id' => 0,
'status' => 'publish',
'meta_key' => ''
);
$atts = shortcode_atts( $defaults, $atts, 'secure_example' );
// 额外验证
$atts['user_id'] = absint( $atts['user_id'] );
// 限制可选值
$allowed_statuses = array( 'publish', 'draft', 'private' );
if ( ! in_array( $atts['status'], $allowed_statuses, true ) ) {
$atts['status'] = $defaults['status'];
}
// 清理字符串
$atts['meta_key'] = sanitize_key( $atts['meta_key'] );
// 数据库查询时使用 prepare
global $wpdb;
$query = $wpdb->prepare(
"SELECT * FROM $wpdb->posts WHERE post_author = %d AND post_status = %s",
$atts['user_id'],
$atts['status']
);
// ... 其余逻辑
}
输出转义
根据输出上下文使用正确的转义函数:
function output_shortcode( $atts ) {
$atts = shortcode_atts( array(
'title' => '',
'description' => '',
'url' => ''
), $atts, 'output_example' );
ob_start();
?>
<div class="shortcode-output">
<!-- 普通文本转义 -->
<h2><?php echo esc_html( $atts['title'] ); ?></h2>
<!-- 允许少量 HTML -->
<p><?php echo wp_kses_post( $atts['description'] ); ?></p>
<!-- URL 属性转义 -->
<a href="<?php echo esc_url( $atts['url'] ); ?>">链接</a>
<!-- 在 HTML 属性中使用 -->
<div data-config="<?php echo esc_attr( wp_json_encode( $atts ) ); ?>">
内容
</div>
</div>
<?php
return ob_get_clean();
}
性能优化
合理设置默认值
避免在默认值中执行昂贵操作:
// 不推荐 - 每次短代码调用都会执行查询
$defaults = array(
'category' => get_queried_object_id() // 每次都会执行
);
// 推荐 - 使用回调或条件逻辑
function optimized_shortcode( $atts ) {
// 延迟获取动态默认值
$category_id = isset( $atts['category'] ) ? $atts['category'] : get_queried_object_id();
$atts = shortcode_atts(
array(
'category' => 0, // 使用占位值
'count' => 5
),
$atts,
'optimized'
);
// 动态替换占位值
if ( 0 === $atts['category'] ) {
$atts['category'] = $category_id;
}
}
使用过滤钩子扩展
利用 shortcode_atts_{$shortcode} 钩子让其他开发者可以修改属性:
function extendable_shortcode( $atts ) {
$defaults = array(
'color' => 'blue',
'size' => 'medium',
'style' => 'default'
);
// 基础合并
$atts = shortcode_atts( $defaults, $atts, 'extendable' );
// 应用过滤钩子(第三方可以通过此钩子修改属性)
$atts = apply_filters( 'extendable_shortcode_atts', $atts );
// 确保过滤后仍然有必需的值
$atts = wp_parse_args( $atts, $defaults );
// ... 短代码逻辑
}
代码可维护性
统一属性前缀
为插件/主题的短代码属性添加前缀,避免冲突:
function prefixed_shortcode( $atts ) {
$defaults = array(
'myplugin_title' => '',
'myplugin_layout' => 'grid',
'myplugin_columns' => 3
);
$atts = shortcode_atts( $defaults, $atts, 'myplugin_widget' );
// 使用更友好的内部变量名
$title = $atts['myplugin_title'];
$layout = $atts['myplugin_layout'];
$columns = $atts['myplugin_columns'];
// ... 逻辑处理
}
属性文档与类型提示
为复杂短代码添加详细的属性文档:
/**
* 产品展示短代码
*
* 属性:
* @param array $atts {
* 短代码属性配置
*
* @type int $product_id 产品ID,默认0(显示最新产品)
* @type string $display_type 显示类型:'full'|'compact'|'thumb',默认'full'
* @type bool $show_price 显示价格:1|0,默认1
* @type string $align 对齐方式:'left'|'center'|'right',默认'left'
* }
* @param string $content 短代码内容(本短代码不支持内容)
*/
function product_display_shortcode( $atts, $content = null ) {
// 属性定义与处理
$atts = shortcode_atts(
array(
'product_id' => 0,
'display_type' => 'full',
'show_price' => 1,
'align' => 'left'
),
$atts,
'product_display'
);
// 类型转换与验证
$atts['product_id'] = absint( $atts['product_id'] );
$atts['show_price'] = (bool) $atts['show_price'];
$allowed_types = array( 'full', 'compact', 'thumb' );
if ( ! in_array( $atts['display_type'], $allowed_types, true ) ) {
$atts['display_type'] = 'full';
}
// ... 实现逻辑
}
与现代 WordPress 结合
与区块编辑器结合
为短代码创建对应的区块:
/**
* 为短代码注册区块类型
*/
function register_shortcode_block() {
register_block_type( 'my-plugin/shortcode-block', array(
'attributes' => array(
'title' => array(
'type' => 'string',
'default' => '默认标题'
),
'count' => array(
'type' => 'number',
'default' => 5
)
),
'render_callback' => function( $attributes ) {
// 将区块属性转换为短代码属性格式
$shortcode_atts = array();
foreach ( $attributes as $key => $value ) {
$shortcode_atts[ $key ] = $value;
}
// 调用短代码函数
return my_shortcode_callback( $shortcode_atts );
}
) );
}
add_action( 'init', 'register_shortcode_block' );
REST API 端点中的属性处理
在自定义 REST API 端点中使用相似的逻辑:
register_rest_route( 'myplugin/v1', '/shortcode-render', array(
'methods' => 'POST',
'callback' => function( WP_REST_Request $request ) {
// 从请求中获取参数
$provided_params = $request->get_params();
// 使用类似 shortcode_atts 的逻辑
$defaults = array(
'title' => '',
'count' => 10,
'type' => 'post'
);
// 合并并过滤参数
$atts = array_intersect_key( $provided_params, $defaults );
$atts = wp_parse_args( $atts, $defaults );
// 验证和清理
$atts['count'] = absint( $atts['count'] );
$atts['title'] = sanitize_text_field( $atts['title'] );
// 调用短代码渲染
return array(
'html' => my_shortcode_callback( $atts ),
'atts' => $atts
);
},
'permission_callback' => '__return_true'
) );
通过遵循这些最佳实践,你可以创建出安全、高效且易于维护的 WordPress 短代码,同时为未来的扩展和与现代 WordPress 功能的集成做好准备。