大家好!今天咱们来聊聊 WordPress 小工具(Widgets)的核心部分:WP_Widget
类,尤其是它俩关键方法 form()
和 update()
。 别怕,咱们尽量用大白话,把这俩家伙扒个底朝天。
一、Widget 类:小工具的骨架
首先,WP_Widget
类是所有小工具的基类。 你要创建一个自定义小工具,就得继承它。 就像盖房子,WP_Widget
就是地基,form()
和 update()
就像是水电线路,缺了它们,房子就没法住人。
二、form()
方法:表单的舞台
form()
方法负责生成小工具在 WordPress 后台“外观 -> 小工具”页面显示的表单。 这个表单让你设置小工具的各种选项,比如标题、显示数量等等。
<?php
/**
* 自定义小工具类
*/
class My_Awesome_Widget extends WP_Widget {
/**
* 构造函数:设置小工具的基本信息
*/
function __construct() {
parent::__construct(
'my_awesome_widget', // Base ID
__('我的超棒小工具', 'textdomain'), // Name
array( 'description' => __( '一个超棒的小工具示例', 'textdomain' ), ) // Args
);
}
/**
* 表单方法:生成小工具设置表单
*
* @param array $instance 之前的选项值
*/
public function form( $instance ) {
// 设置默认值,防止未设置时出错
$title = ! empty( $instance['title'] ) ? $instance['title'] : __( '新标题', 'textdomain' );
$number = ! empty( $instance['number'] ) ? absint( $instance['number'] ) : 5; // 确保是整数
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( '标题:', 'textdomain' ); ?></label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>">
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'number' ) ); ?>"><?php _e( '显示数量:', 'textdomain' ); ?></label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'number' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'number' ) ); ?>" type="number" value="<?php echo esc_attr( $number ); ?>">
</p>
<?php
}
/**
* 小工具的显示方法(前端)
*/
public function widget( $args, $instance ) {
// 这里放小工具的具体显示代码,比如输出文章列表
echo $args['before_widget']; // 输出小工具的前置 HTML (比如 div 的开始标签)
if ( ! empty( $instance['title'] ) ) {
echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title']; // 输出小工具标题
}
echo '<p>显示 ' . $instance['number'] . ' 篇文章</p>'; // 简单示例
echo $args['after_widget']; // 输出小工具的后置 HTML (比如 div 的结束标签)
}
/**
* 更新方法:保存表单数据
*/
public function update( $new_instance, $old_instance ) {
$instance = array();
$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? sanitize_text_field( $new_instance['title'] ) : '';
$instance['number'] = ( ! empty( $new_instance['number'] ) ) ? absint( $new_instance['number'] ) : 5; // 确保是整数
return $instance;
}
}
/**
* 注册小工具
*/
function register_my_widget() {
register_widget( 'My_Awesome_Widget' );
}
add_action( 'widgets_init', 'register_my_widget' );
?>
代码解读:
-
__construct()
(构造函数): 这个函数在小工具被创建时调用。它设置了小工具的 ID、名称和描述。'my_awesome_widget'
:这是小工具的唯一ID,以后在代码里会用到。__('我的超棒小工具', 'textdomain')
:这是小工具的名称,显示在后台“外观 -> 小工具”页面。textdomain
用于国际化。array( 'description' => __( '一个超棒的小工具示例', 'textdomain' ) )
:小工具的描述,也会显示在后台。
-
form( $instance )
: 这个方法负责生成后台的表单。$instance
:这是一个数组,包含了之前保存的选项值。第一次使用时,它可能是空的。$title = ! empty( $instance['title'] ) ? $instance['title'] : __( '新标题', 'textdomain' );
:这行代码检查$instance
数组中是否存在title
键,并且不为空。如果存在,就使用之前保存的标题;否则,使用默认标题“新标题”。$this->get_field_id( 'title' )
:这个方法生成表单字段的id
属性。它的作用是确保每个小工具实例的 ID 都是唯一的,避免冲突。$this->get_field_name( 'title' )
:这个方法生成表单字段的name
属性。WordPress 会使用这个name
来保存表单数据。esc_attr()
:这个函数用于转义 HTML 属性,防止 XSS 攻击。_e()
:这是 WordPress 的国际化函数,用于翻译文本。
-
widget( $args, $instance )
: 这个方法负责在前端显示小工具。$args
:这是一个数组,包含了小工具容器的 HTML 代码,比如before_widget
(小工具容器的开始标签)、after_widget
(结束标签)、before_title
(标题的开始标签) 和after_title
(标题的结束标签)。$instance
:这还是包含了之前保存的选项值的数组。apply_filters( 'widget_title', $instance['title'] )
:这行代码允许其他插件或主题修改小工具的标题。
-
update( $new_instance, $old_instance )
: 这个方法负责保存表单数据。 -
register_my_widget()
和add_action( 'widgets_init', 'register_my_widget' )
: 这两行代码将小工具注册到 WordPress。widgets_init
是一个动作钩子,在 WordPress 初始化小工具时触发。
重点:
$this->get_field_id()
和$this->get_field_name()
非常重要! 它们确保每个小工具实例的 ID 和 name 都是唯一的,避免数据混乱。- 安全性! 使用
esc_attr()
转义 HTML 属性,使用sanitize_text_field()
清理用户输入,防止 XSS 攻击。 - 国际化! 使用
_e()
和__()
函数翻译文本,让你的小工具支持多种语言。
三、update()
方法:数据的守护神
update()
方法负责处理小工具表单提交的数据,进行验证、清理,然后保存到数据库。
/**
* 更新方法:保存表单数据
*
* @param array $new_instance 新提交的选项值
* @param array $old_instance 之前的选项值
* @return array 需要保存的选项值
*/
public function update( $new_instance, $old_instance ) {
$instance = array();
// 对 title 进行清理
$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? sanitize_text_field( $new_instance['title'] ) : '';
// 对 number 进行清理,并确保是整数
$instance['number'] = ( ! empty( $new_instance['number'] ) ) ? absint( $new_instance['number'] ) : 5; // 确保是整数
return $instance;
}
代码解读:
$new_instance
: 这个数组包含了用户提交的新数据。$old_instance
: 这个数组包含了之前保存的数据。$instance = array();
: 创建一个空数组,用于保存清理后的数据。sanitize_text_field( $new_instance['title'] )
: 这个函数用于清理文本字段,移除 HTML 标签和编码特殊字符,防止 XSS 攻击。absint( $new_instance['number'] )
: 这个函数用于将值转换为绝对整数。这可以确保number
字段的值始终是整数。return $instance;
: 返回需要保存的选项值。WordPress 会自动将这个数组保存到数据库。
重点:
- 数据清理!
update()
方法最重要的一点就是数据清理。你必须对用户提交的每个字段进行验证和清理,防止恶意代码注入。 sanitize_text_field()
、absint()
、esc_url_raw()
等等: WordPress 提供了很多数据清理函数,根据字段类型选择合适的函数。- 返回正确的数据!
update()
方法必须返回一个数组,包含了需要保存的选项值。
四、实例演示:一个更复杂的小工具
咱们来创建一个稍微复杂一点的小工具,包含下拉选择框、复选框和文本域。
<?php
class My_Advanced_Widget extends WP_Widget {
function __construct() {
parent::__construct(
'my_advanced_widget',
__('我的高级小工具', 'textdomain'),
array( 'description' => __( '一个包含各种表单元素的小工具', 'textdomain' ), )
);
}
public function form( $instance ) {
$title = ! empty( $instance['title'] ) ? $instance['title'] : __( '新标题', 'textdomain' );
$category = ! empty( $instance['category'] ) ? $instance['category'] : '';
$show_date = ! empty( $instance['show_date'] ) ? $instance['show_date'] : 0;
$content = ! empty( $instance['content'] ) ? $instance['content'] : '';
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( '标题:', 'textdomain' ); ?></label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>">
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'category' ) ); ?>"><?php _e( '分类:', 'textdomain' ); ?></label>
<select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'category' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'category' ) ); ?>">
<option value=""><?php _e( '选择分类', 'textdomain' ); ?></option>
<?php
$categories = get_categories();
foreach ( $categories as $cat ) {
$selected = ($category == $cat->term_id) ? 'selected' : '';
echo '<option value="' . esc_attr( $cat->term_id ) . '" ' . $selected . '>' . esc_html( $cat->name ) . '</option>';
}
?>
</select>
</p>
<p>
<input type="checkbox" id="<?php echo esc_attr( $this->get_field_id( 'show_date' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'show_date' ) ); ?>" value="1" <?php checked( $show_date, 1 ); ?>>
<label for="<?php echo esc_attr( $this->get_field_id( 'show_date' ) ); ?>"><?php _e( '显示日期', 'textdomain' ); ?></label>
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'content' ) ); ?>"><?php _e( '内容:', 'textdomain' ); ?></label>
<textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'content' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'content' ) ); ?>" rows="5"><?php echo esc_textarea( $content ); ?></textarea>
</p>
<?php
}
public function widget( $args, $instance ) {
echo $args['before_widget'];
if ( ! empty( $instance['title'] ) ) {
echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title'];
}
if ( ! empty( $instance['category'] ) ) {
$category = get_category( $instance['category'] );
if ( $category ) {
echo '<p>分类: ' . esc_html( $category->name ) . '</p>';
}
}
if ( ! empty( $instance['show_date'] ) ) {
echo '<p>显示日期: 是</p>';
} else {
echo '<p>显示日期: 否</p>';
}
if ( ! empty( $instance['content'] ) ) {
echo '<p>内容: ' . wp_kses_post( $instance['content'] ) . '</p>';
}
echo $args['after_widget'];
}
public function update( $new_instance, $old_instance ) {
$instance = array();
$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? sanitize_text_field( $new_instance['title'] ) : '';
$instance['category'] = ( ! empty( $new_instance['category'] ) ) ? absint( $new_instance['category'] ) : '';
$instance['show_date'] = isset( $new_instance['show_date'] ) ? 1 : 0; // checkbox 特殊处理
$instance['content'] = ( ! empty( $new_instance['content'] ) ) ? wp_kses_post( $new_instance['content'] ) : '';
return $instance;
}
}
function register_my_advanced_widget() {
register_widget( 'My_Advanced_Widget' );
}
add_action( 'widgets_init', 'register_my_advanced_widget' );
?>
代码解读:
- 下拉选择框: 使用
<select>
元素生成下拉选择框。从get_categories()
函数获取所有分类,并循环输出<option>
元素。 - 复选框: 使用
<input type="checkbox">
元素生成复选框。checked( $show_date, 1 )
函数用于在复选框被选中时添加checked
属性。 - 文本域: 使用
<textarea>
元素生成文本域。esc_textarea()
函数用于转义文本域中的内容,防止 HTML 注入。 update()
方法的 checkbox 处理: 由于 checkbox 只有在被选中时才会提交值,所以在update()
方法中,需要使用isset()
函数来判断 checkbox 是否被选中。wp_kses_post()
函数: 这个函数用于清理文本域中的 HTML 代码,只允许有限的 HTML 标签和属性。这是为了防止用户提交恶意 HTML 代码。
五、表单元素和数据清理函数对应表
表单元素 | 数据清理函数 | 说明 |
---|---|---|
文本框 | sanitize_text_field() |
移除 HTML 标签和编码特殊字符。 |
文本域 | wp_kses_post() |
允许有限的 HTML 标签和属性,移除其他 HTML 标签和属性。如果允许更多标签,可以使用 wp_kses() ,但需要小心配置允许的标签和属性。 |
数字输入框 | absint() |
转换为绝对整数。 |
浮点数输入框 | floatval() |
转换为浮点数。 |
URL 输入框 | esc_url_raw() |
验证 URL 格式,并移除危险字符。 |
Email 输入框 | sanitize_email() |
验证 Email 格式,并移除非法字符。 |
单选按钮/下拉框 | 根据实际情况选择 | 如果值是预定义的,可以进行白名单验证;如果值是用户输入的,需要进行相应的清理。 |
复选框 | isset() 判断是否选中 |
复选框只有在被选中时才会提交值,所以需要使用 isset() 函数来判断是否被选中。 |
六、总结
WP_Widget
类的 form()
和 update()
方法是创建 WordPress 小工具的核心。form()
方法负责生成后台的表单,update()
方法负责处理表单数据。在编写小工具时,一定要注意数据清理和安全性,防止 XSS 攻击。 记住,良好的用户体验和安全性是小工具成功的关键!
希望今天的讲解对你有所帮助。有问题随时提问!咱们下次再见!