大家好,今天咱们聊聊WordPress主题定制器的幕后大佬:WP_Customize_Manager
嗨,各位技术宅们!今天咱们不聊八卦,来点硬核的:WordPress 主题定制器,以及驱动它的核心类 WP_Customize_Manager
。 这玩意儿可不简单,它就像一个精密的指挥中心,协调着各种设置、控件,让用户能够轻松定制主题,而不用碰一行代码。
咱们这次就来扒一扒它的源码,看看它是如何运作的。准备好了吗?Let’s dive in!
1. WP_Customize_Manager
:主题定制器的总指挥
WP_Customize_Manager
类是主题定制器的核心,负责初始化、注册设置和控件、处理用户交互等。 简单来说,它就是个大管家,管理着主题定制的所有事务。
1.1 初始化 (Constructor)
首先,我们来看看它的构造函数 __construct()
:
public function __construct() {
// 确保只实例化一次
if ( self::$instance ) {
_doing_it_wrong( __CLASS__, sprintf( __( '%s should not be instantiated more than once.' ), __CLASS__ ), '4.0.0' );
return;
}
self::$instance = $this;
// 设置一些必要的属性
$this->settings = array();
$this->controls = array();
$this->sections = array();
$this->panels = array();
$this->nav_menus = array();
// 注册动作钩子
add_action( 'admin_menu', array( $this, 'register_admin_menu' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'wp_ajax_customize_save', array( $this, 'save' ) );
add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) );
add_action( 'wp_ajax_customize_ms_loaded', array( $this, 'ms_loaded' ) );
add_action( 'customize_register', array( $this, 'register_controls' ), 11 ); // 优先级11,确保在主题的register_controls之后执行
add_action( 'customize_controls_print_styles', array( $this, 'controls_print_styles' ) );
add_action( 'customize_controls_print_scripts', array( $this, 'controls_print_scripts' ) );
add_action( 'customize_controls_enqueue_scripts', array( $this, 'controls_enqueue_scripts' ) );
add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) );
add_action( 'customize_preview_print_styles', array( $this, 'customize_preview_print_styles' ) );
add_action( 'customize_preview_print_scripts', array( $this, 'customize_preview_print_scripts' ) );
add_action( 'customize_save', array( $this, 'save_nav_menus' ) );
add_action( 'customize_update_nav_menu', array( $this, 'update_nav_menu' ), 10, 3 );
add_action( 'customize_update_nav_menu_item', array( $this, 'update_nav_menu_item' ), 10, 3 );
add_action( 'wp_loaded', array( $this, 'wp_loaded' ) );
add_action( 'shutdown', array( $this, 'shutdown' ), 0 );
add_action( 'wp_die_handler', array( $this, '_wp_die_handler' ) );
// 其他初始化逻辑...
}
- 单例模式: 确保
WP_Customize_Manager
只被实例化一次,避免冲突。就像一个国家只能有一个总统一样。 - 属性初始化: 创建了
settings
、controls
、sections
、panels
等数组,用于存储各种定制项。这些数组就像一个个抽屉,存放着不同类型的定制信息。 - 动作钩子 (Action Hooks): 注册了一堆动作钩子,这些钩子会在特定的时机被触发,执行相应的操作。 比如
admin_menu
钩子会在后台菜单加载时触发,customize_save
钩子会在用户保存定制时触发。 就像埋伏好的特工,等待时机行动。
1.2 关键属性
属性名 | 类型 | 描述 |
---|---|---|
$settings |
array | 存储所有注册的 WP_Customize_Setting 对象。这些是实际存储定制值的地方。 |
$controls |
array | 存储所有注册的 WP_Customize_Control 对象。这些是用户界面上的控件。 |
$sections |
array | 存储所有注册的 WP_Customize_Section 对象。用于组织控件。 |
$panels |
array | 存储所有注册的 WP_Customize_Panel 对象。用于组织Section。 |
$nav_menus |
array | 存储导航菜单相关信息。 |
2. 设置 (Settings): 定制项的存储仓库
WP_Customize_Setting
类代表一个可定制的设置项,比如主题颜色、logo 图片等。它负责验证、清理和存储定制值。
2.1 注册设置 (add_setting()
方法)
public function add_setting( $id, $args = array() ) {
if ( ! is_string( $id ) || '' === $id ) {
_doing_it_wrong( __FUNCTION__, __( 'Setting ID must be a non-empty string.' ), '4.2.0' );
return null;
}
if ( isset( $this->settings[ $id ] ) ) {
_doing_it_wrong( __FUNCTION__, sprintf( __( 'Setting %s is already registered.' ), $id ), '4.0.0' );
return null;
}
// 实例化 WP_Customize_Setting 对象
$class = 'WP_Customize_Setting'; // 默认的设置类
if ( isset( $args['type'] ) ) {
$class = $args['type'];
}
if ( ! class_exists( $class ) ) {
_doing_it_wrong( __FUNCTION__, sprintf( __( 'Setting type %s does not exist.' ), $class ), '4.0.0' );
return null;
}
$setting = new $class( $this, $id, $args );
$this->settings[ $id ] = $setting;
return $setting;
}
这个方法负责注册一个新的设置。
- 参数校验: 确保设置 ID 是一个非空字符串,并且没有被重复注册。
- 实例化
WP_Customize_Setting
: 根据$args['type']
参数,实例化对应的WP_Customize_Setting
子类。 如果没有指定type
,则使用默认的WP_Customize_Setting
类。 - 存储设置: 将新创建的
WP_Customize_Setting
对象存储到$this->settings
数组中。
2.2 WP_Customize_Setting
的子类
WordPress 提供了几个 WP_Customize_Setting
的子类,用于处理不同类型的设置:
WP_Customize_Theme_Option
:用于存储主题选项的值。WP_Customize_Nav_Menu_Setting
:用于存储导航菜单的设置。WP_Customize_Nav_Menu_Item_Setting
:用于存储导航菜单项的设置。
2.3 获取设置值 (get_setting()
方法)
public function get_setting( $id ) {
if ( ! isset( $this->settings[ $id ] ) ) {
return false;
}
return $this->settings[ $id ];
}
这个方法用于根据 ID 获取已注册的 WP_Customize_Setting
对象。
2.4 设置值的存储机制
WP_Customize_Setting
对象最终会将定制值存储到 WordPress 的 options 表中,或者使用主题 mod。 具体使用哪种方式取决于设置的 transport
属性。
theme_mod
:定制值存储为主题 mod。这是推荐的方式,因为它与主题相关联,当主题切换时,定制值也会随之改变。option
:定制值存储为 WordPress option。这种方式不推荐,因为它与主题无关,当主题切换时,定制值不会改变。
3. 控件 (Controls): 用户交互的界面元素
WP_Customize_Control
类代表一个用户界面控件,比如文本框、下拉列表、颜色选择器等。它负责显示设置项,并允许用户修改它的值。
3.1 注册控件 (add_control()
方法)
public function add_control( $id, $args = array() ) {
if ( ! is_string( $id ) || '' === $id ) {
_doing_it_wrong( __FUNCTION__, __( 'Control ID must be a non-empty string.' ), '4.2.0' );
return null;
}
if ( isset( $this->controls[ $id ] ) ) {
_doing_it_wrong( __FUNCTION__, sprintf( __( 'Control %s is already registered.' ), $id ), '4.0.0' );
return null;
}
// 实例化 WP_Customize_Control 对象
$class = 'WP_Customize_Control';
if ( isset( $args['type'] ) ) {
$class = $args['type'];
}
if ( ! class_exists( $class ) ) {
_doing_it_wrong( __FUNCTION__, sprintf( __( 'Control type %s does not exist.' ), $class ), '4.0.0' );
return null;
}
$control = new $class( $this, $id, $args );
$this->controls[ $id ] = $control;
return $control;
}
这个方法与 add_setting()
非常相似,负责注册一个新的控件。
- 参数校验: 确保控件 ID 是一个非空字符串,并且没有被重复注册。
- 实例化
WP_Customize_Control
: 根据$args['type']
参数,实例化对应的WP_Customize_Control
子类。 如果没有指定type
,则使用默认的WP_Customize_Control
类。 - 存储控件: 将新创建的
WP_Customize_Control
对象存储到$this->controls
数组中。
3.2 WP_Customize_Control
的子类
WordPress 提供了丰富的 WP_Customize_Control
子类,涵盖了常见的用户界面控件:
控件类 | 描述 |
---|---|
WP_Customize_Color_Control |
颜色选择器 |
WP_Customize_Image_Control |
图片上传控件 |
WP_Customize_Upload_Control |
文件上传控件 |
WP_Customize_Media_Control |
多媒体上传控件(图片、视频、音频) |
WP_Customize_Cropped_Image_Control |
可裁剪的图片上传控件 |
WP_Customize_Background_Image_Control |
背景图片控件,集成了图片选择、裁剪、平铺等功能 |
WP_Customize_Header_Image_Control |
头部图片控件,集成了图片选择、裁剪等功能 |
WP_Customize_Nav_Menu_Control |
导航菜单选择控件 |
WP_Customize_Nav_Menu_Location_Control |
导航菜单位置控件,用于指定菜单在主题中的显示位置 |
WP_Customize_Nav_Menu_Item_Control |
导航菜单项控件,用于编辑菜单项的属性,例如标题、链接、目标等 |
WP_Customize_Dropdown_Pages_Control |
下拉列表选择页面控件 |
WP_Customize_TextArea_Control |
多行文本框 |
WP_Customize_Code_Editor_Control |
代码编辑器控件,用于编辑代码片段 |
如果你觉得这些还不够用,还可以自定义 WP_Customize_Control
的子类,创建自己的控件。
3.3 控件与设置的关联
每个 WP_Customize_Control
对象都必须与一个 WP_Customize_Setting
对象相关联。 通过 control
的 setting
属性,可以指定它所关联的设置。
$wp_customize->add_control( 'blogname', array(
'label' => __( 'Site Title' ),
'section' => 'title_tagline',
'settings' => 'blogname', // 与 blogname 设置相关联
) );
3.4 控件的渲染 (render_content()
方法)
每个 WP_Customize_Control
子类都需要实现 render_content()
方法,用于渲染控件的内容。 这个方法会生成 HTML 代码,显示控件的界面元素。
例如,WP_Customize_Color_Control
的 render_content()
方法会生成一个颜色选择器:
protected function render_content() {
?>
<label>
<?php if ( ! empty( $this->label ) ) : ?>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<?php endif; ?>
<?php if ( ! empty( $this->description ) ) : ?>
<span class="description customize-control-description"><?php echo $this->description; ?></span>
<?php endif; ?>
</label>
<input class="color-picker-hex" type="text" data-palette="<?php echo esc_attr( wp_json_encode( $this->palette ) ); ?>" data-default-color="<?php echo esc_attr( $this->default ); ?>" value="<?php echo esc_attr( $this->value() ); ?>" />
<?php
}
4. 区块 (Sections) 和 面板 (Panels): 定制项的组织者
为了更好地组织控件,WordPress 提供了区块 (Sections) 和 面板 (Panels) 的概念。
- 区块 (Sections): 用于将相关的控件组织在一起。 比如,可以将 "网站标题"、"网站副标题"、"网站 Logo" 等控件放在 "网站身份" 区块中。
- 面板 (Panels): 用于将相关的区块组织在一起。 比如,可以将 "网站身份" 区块、"主题颜色" 区块、"页眉图片" 区块放在 "外观" 面板中。
4.1 注册区块 (add_section()
方法)
public function add_section( $id, $args = array() ) {
if ( ! is_string( $id ) || '' === $id ) {
_doing_it_wrong( __FUNCTION__, __( 'Section ID must be a non-empty string.' ), '4.2.0' );
return null;
}
if ( isset( $this->sections[ $id ] ) ) {
_doing_it_wrong( __FUNCTION__, sprintf( __( 'Section %s is already registered.' ), $id ), '4.0.0' );
return null;
}
$class = 'WP_Customize_Section';
if ( isset( $args['type'] ) ) {
$class = $args['type'];
}
if ( ! class_exists( $class ) ) {
_doing_it_wrong( __FUNCTION__, sprintf( __( 'Section type %s does not exist.' ), $class ), '4.0.0' );
return null;
}
$section = new $class( $this, $id, $args );
$this->sections[ $id ] = $section;
return $section;
}
4.2 注册面板 (add_panel()
方法)
public function add_panel( $id, $args = array() ) {
if ( ! is_string( $id ) || '' === $id ) {
_doing_it_wrong( __FUNCTION__, __( 'Panel ID must be a non-empty string.' ), '4.2.0' );
return null;
}
if ( isset( $this->panels[ $id ] ) ) {
_doing_it_wrong( __FUNCTION__, sprintf( __( 'Panel %s is already registered.' ), $id ), '4.0.0' );
return null;
}
$class = 'WP_Customize_Panel';
if ( isset( $args['type'] ) ) {
$class = $args['type'];
}
if ( ! class_exists( $class ) ) {
_doing_it_wrong( __FUNCTION__, sprintf( __( 'Panel type %s does not exist.' ), $class ), '4.0.0' );
return null;
}
$panel = new $class( $this, $id, $args );
$this->panels[ $id ] = $panel;
return $panel;
}
4.3 WP_Customize_Section
和 WP_Customize_Panel
的子类
WordPress 也提供了一些 WP_Customize_Section
和 WP_Customize_Panel
的子类,用于实现更高级的功能。
WP_Customize_Sidebar_Section
:用于定制侧边栏。WP_Customize_Nav_Menu_Panel
:用于定制导航菜单。
4.4 控件与区块的关联
在注册控件时,可以使用 section
属性将控件与一个区块关联起来。
$wp_customize->add_control( 'blogname', array(
'label' => __( 'Site Title' ),
'section' => 'title_tagline', // 与 title_tagline 区块相关联
'settings' => 'blogname',
) );
5. 主题定制器的预览
主题定制器的一个重要功能是实时预览。 当用户修改一个设置的值时,预览窗口会立即更新,显示修改后的效果。
5.1 customize_preview_init
动作钩子
WP_Customize_Manager
类在 customize_preview_init
动作钩子中注册了一个回调函数,用于初始化预览窗口。
add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) );
public function customize_preview_init() {
// 加载主题定制器的预览脚本
wp_enqueue_script( 'customize-preview' );
// 其他初始化逻辑...
}
5.2 预览脚本 (customize-preview.js
)
customize-preview.js
是一个 JavaScript 文件,负责与主题定制器进行通信,并更新预览窗口的内容。
它会监听 wp.customize
对象的变化,当一个设置的值发生改变时,它会向预览窗口发送消息,告诉它更新相应的元素。
5.3 postMessage
API
主题定制器和预览窗口运行在不同的域名下,它们之间使用 postMessage
API 进行通信。 postMessage
API 允许跨域名的 JavaScript 代码安全地进行通信。
6. 保存定制
当用户点击 "保存并发布" 按钮时,主题定制器会将所有修改后的设置值保存到数据库中。
6.1 customize_save
动作钩子
WP_Customize_Manager
类在 customize_save
动作钩子中注册了一个回调函数,用于保存定制值。
add_action( 'customize_save', array( $this, 'save' ) );
public function save() {
// 验证用户权限
if ( ! current_user_can( 'customize' ) ) {
wp_die( -1 );
}
// 循环遍历所有设置,并保存它们的值
foreach ( $this->settings as $setting ) {
$setting->save();
}
// 其他保存逻辑...
}
6.2 WP_Customize_Setting::save()
方法
每个 WP_Customize_Setting
对象都有一个 save()
方法,用于将设置的值保存到数据库中。
public function save() {
if ( ! $this->check_capabilities() ) {
return false;
}
$value = $this->post_value();
if ( is_null( $value ) ) {
return false;
}
return $this->update( $value );
}
7. 总结
今天我们深入探讨了 WordPress WP_Customize_Manager
类的源码,了解了它如何管理主题定制器的设置、控件、区块和面板,以及如何实现实时预览和保存定制值。
WP_Customize_Manager
类是主题定制器的核心,理解它的运作机制对于开发自定义主题和插件非常重要。
希望这次的分享对大家有所帮助! 下次有机会再聊!