各位观众老爷,今天咱就来扒一扒 WordPress 里面的 wp_editor()
这个老家伙,看看它是怎么变着法儿地伺候 Gutenberg 编辑器,又怎么周到地照顾着经典编辑器。准备好了吗?咱们这就开始!
开场白:这 wp_editor()
到底是个啥玩意儿?
简单来说,wp_editor()
就是 WordPress 提供的一个方便的函数,它能帮你生成一个文本编辑器。以前是 TinyMCE(经典编辑器),现在嘛,自然也得支持 Gutenberg(块编辑器)。 关键是,它还得能根据用户的设置,决定到底加载哪个。
第一幕:wp_editor()
的骨架结构
咱们先来看看 wp_editor()
函数的基本结构,摸清楚它的脉络:
function wp_editor( $content, $editor_id, $settings = array() ) {
global $tinymce, $wp_version, $concatenate_scripts;
// 1. 处理默认参数和用户传入的参数
$editor_id = sanitize_html_class( $editor_id ); // 清理 editor_id
$settings = wp_parse_args( $settings, array(
'wpautop' => true,
'media_buttons' => true,
'textarea_name' => $editor_id,
'textarea_rows' => 20,
'tabindex' => '',
'editor_class' => '',
'editor_css' => '',
'teeny' => false,
'dfw' => false,
'tinymce' => true, // 关键:是否启用 TinyMCE (经典编辑器)
'quicktags' => true
) );
// 2. 决定使用哪个编辑器
if ( ! empty( $_GET['classic-editor'] ) ) { // 强制使用经典编辑器
$use_wp_editor = true;
} elseif ( ! empty( $_GET['classic-editor__forget'] ) ) { // 强制不使用经典编辑器
$use_wp_editor = false;
} else {
$use_wp_editor = ( 'classic' === get_option( 'classic-editor-replace' ) ) || ( ! function_exists( 'the_block_editor_meta_boxes' ) );
}
// 3. 加载相应的编辑器
if ( $use_wp_editor ) {
// 加载经典编辑器 (TinyMCE)
_WP_Editors::editor( $editor_id, $content, $settings );
} else {
// 加载 Gutenberg 编辑器
_WP_Editors::block_editor( $editor_id, $content, $settings );
}
}
看到没?wp_editor()
接收三个参数:
$content
: 编辑器的初始内容。$editor_id
: 编辑器的 ID,非常重要,用于 JavaScript 和 CSS 的关联。$settings
: 一个数组,包含各种配置选项,比如是否启用 TinyMCE,高度,CSS等等。
第二幕:参数的处理,决定命运的时刻
wp_parse_args()
函数在这里起到了关键作用。它将用户传入的 $settings
和默认设置合并,确保所有必要的选项都有值。注意 tinymce
这个参数,它控制着是否启用经典编辑器。默认情况下,它是 true
,也就是说,默认启用 TinyMCE。
接下来,就是判断到底该用哪个编辑器了。WordPress 会检查 URL 参数 classic-editor
和 classic-editor__forget
,如果存在,就强制使用或不使用经典编辑器。 如果URL参数不存在,它会检查 classic-editor-replace
选项,并检查the_block_editor_meta_boxes
函数是否存在。
classic-editor-replace
选项:如果设置为'classic'
,则始终使用经典编辑器。the_block_editor_meta_boxes
函数:如果不存在,说明主题或插件禁用了 Gutenberg,那么就使用经典编辑器。
第三幕:经典编辑器的华丽登场(TinyMCE)
如果 $use_wp_editor
为 true
,就意味着要加载经典编辑器了。 这个时候,_WP_Editors::editor()
方法会被调用。这个方法负责初始化 TinyMCE 编辑器。
class _WP_Editors {
// ... 其他方法 ...
public static function editor( $editor_id, $content, $settings = array() ) {
global $tinymce, $concatenate_scripts, $wp_styles, $wp_scripts;
// 1. 准备 TinyMCE 的配置
$mce_settings = self::mce_settings( $editor_id, $settings );
// 2. 加载 TinyMCE 的 JavaScript 和 CSS
wp_enqueue_script( 'wp-tinymce' );
wp_enqueue_style( 'editor-buttons' );
// 3. 输出 HTML 结构
echo "<div id='wp-$editor_id-wrap' class='wp-core-ui wp-editor-wrap " . ( $settings['teeny'] ? 'teeny' : 'wp-editor-expand' ) . "'>";
// 3.1 Visual 和 Text 模式切换标签
echo "<div id='wp-$editor_id-editor-tools' class='wp-editor-tools hide-if-no-js'>";
echo "<div class='wp-media-buttons'>";
if ( $settings['media_buttons'] ) {
do_action( 'media_buttons', $editor_id );
}
echo "</div>";
echo "<div class='wp-editor-tabs'>";
echo "<button type='button' id='$editor_id-tmce' class='wp-switch-editor switch-tmce' data-wp-editor-id='$editor_id'>" . __( 'Visual' ) . "</button>";
echo "<button type='button' id='$editor_id-html' class='wp-switch-editor switch-html' data-wp-editor-id='$editor_id'>" . __( 'Text' ) . "</button>";
echo "</div>";
echo "</div>";
// 3.2 TinyMCE 编辑器本体
echo "<div id='wp-$editor_id-editor-container' class='wp-editor-container'>";
echo "<textarea class='wp-editor-area' rows='" . absint( $settings['textarea_rows'] ) . "' cols='40' name='" . esc_attr( $settings['textarea_name'] ) . "' id='$editor_id'" . ( $settings['tabindex'] ? ' tabindex="' . absint( $settings['tabindex'] ) . '"' : '' ) . ">" . esc_textarea( $content ) . "</textarea>";
echo "</div>";
echo "</div>";
// 4. 启动 TinyMCE (通过 JavaScript)
self::enqueue_scripts(); // 确保脚本已经加载
self::editor_js( $editor_id, $settings );
}
// ... 其他方法 ...
}
这个方法主要做了这几件事:
- 准备 TinyMCE 的配置:
self::mce_settings()
方法会根据$settings
数组生成 TinyMCE 的配置参数,包括工具栏按钮、插件等等。 - 加载 TinyMCE 的 JavaScript 和 CSS:
wp_enqueue_script()
和wp_enqueue_style()
函数负责加载 TinyMCE 的必要资源。 - 输出 HTML 结构: 这个部分负责生成编辑器界面的 HTML 代码,包括 Visual 和 Text 模式切换标签,以及 TinyMCE 编辑器的
textarea
。 - 启动 TinyMCE (通过 JavaScript):
self::editor_js()
方法会输出 JavaScript 代码,用于初始化 TinyMCE 编辑器。
重点来了:self::mce_settings()
方法
这个方法非常重要,它决定了 TinyMCE 编辑器的外观和功能。 它会根据 $settings
数组中的选项,生成一个 JavaScript 对象,作为 TinyMCE 的初始化参数。
public static function mce_settings( $editor_id, $settings ) {
global $tinymce, $wp_version, $concatenate_scripts;
$mce_css = apply_filters( 'mce_css', '' );
if ( ! empty( $mce_css ) ) {
$mce_css = ', ' . $mce_css;
}
$baseurl = includes_url( 'js/tinymce' );
$plugins = array( 'charmap', 'hr', 'media', 'paste', 'tabfocus', 'textcolor', 'fullscreen', 'wordpress', 'wpautoresize', 'wpeditimage', 'wpemoji', 'wpgallery', 'wplink', 'wpdialogs', 'wptextpattern' );
if ( ! $settings['teeny'] ) {
$plugins[] = 'image';
}
$plugins = apply_filters( 'mce_plugins', $plugins, $editor_id );
$plugins = implode( ',', array_unique( $plugins ) );
$rel_url = false;
if ( isset( $settings['relative_urls'] ) && $settings['relative_urls'] ) {
$rel_url = true;
}
$remove_script_host = false;
if ( isset( $settings['remove_script_host'] ) && $settings['remove_script_host'] ) {
$remove_script_host = true;
}
$mceInit = array(
'selector' => "#$editor_id",
'wp_autoresize_on' => ! $settings['fixed_height'],
'wp_keep_scroll_position' => true,
'body_class' => $editor_id,
'skin' => 'lightgray',
'toolbar1' => 'formatselect,bold,italic,bullist,numlist,blockquote,alignleft,aligncenter,alignright,link,unlink,wp_more,spellchecker,fullscreen,wp_adv',
'toolbar2' => 'strikethrough,hr,forecolor,pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help',
'toolbar3' => '',
'toolbar4' => '',
'plugins' => $plugins,
'content_css' => includes_url( 'css/dashicons.min.css' ) . ',' . includes_url( 'css/editor-style.css?ver=' . $wp_version ) . $mce_css,
'menubar' => false,
'wpautop' => $settings['wpautop'],
'indent' => ! $settings['wpautop'],
'toolbar_items_size' => 'small',
'browser_spellcheck' => true,
'relative_urls' => $rel_url,
'remove_script_host' => $remove_script_host,
'convert_urls' => false,
'remove_trailing_brs' => true,
'verify_html' => false,
'apply_source_formatting' => true,
'directionality' => is_rtl() ? 'rtl' : 'ltr',
'wp_lang_attr' => get_language_attributes(),
'end_container_on_empty_block' => true,
'add_unload_trigger' => false
);
if ( $settings['teeny'] ) {
$mceInit['toolbar1'] = 'bold,italic,underline,separator,bullist,numlist,separator,link,unlink,separator,undo,redo';
unset( $mceInit['toolbar2'], $mceInit['toolbar3'], $mceInit['toolbar4'] );
}
if ( ! empty( $settings['editor_css'] ) ) {
$mceInit['content_css'] .= ',' . $settings['editor_css'];
}
$mceInit = apply_filters( 'tiny_mce_before_init', $mceInit, $editor_id );
$mceInit['selector'] = sprintf( '#%s', $editor_id );
return $mceInit;
}
这个方法会设置 TinyMCE 的各种选项,比如:
plugins
: 启用的 TinyMCE 插件,比如charmap
(特殊字符),media
(媒体插入)等等。toolbar1
、toolbar2
: 工具栏按钮的排列。content_css
: 编辑器的 CSS 样式。wpautop
: 是否自动添加<p>
标签。
第四幕:Gutenberg 编辑器的异军突起
如果 $use_wp_editor
为 false
,就意味着要加载 Gutenberg 编辑器了。 这个时候,_WP_Editors::block_editor()
方法会被调用。
public static function block_editor( $editor_id, $content, $settings = array() ) {
global $post;
// 1. 准备数据
$block_editor_context = [
'editor_styles' => [],
'settings' => $settings,
'post' => $post
];
// 2. 允许插件修改 block editor 的 context
$block_editor_context = apply_filters( 'block_editor_settings', $block_editor_context );
// 3. 渲染 block editor
$render_block_editor = function() use ( $editor_id, $content, $block_editor_context ) {
$settings = $block_editor_context['settings'];
$editor_settings = wp_json_encode( $settings );
?>
<div id="wp-<?php echo esc_attr( $editor_id ); ?>-wrap" class="wp-core-ui wp-block-editor-container <?php echo esc_attr( $settings['editor_class'] ); ?>">
<input type="hidden" id="wp-block-editor-initial-content" value="<?php echo esc_attr( $content ); ?>">
<div id="<?php echo esc_attr( $editor_id ); ?>" class="wp-block-editor"></div>
<script type="text/javascript">
( function() {
var mountNode = document.getElementById( '<?php echo esc_attr( $editor_id ); ?>' );
if ( mountNode ) {
wp.editPost.initialize( '<?php echo esc_attr( $editor_id ); ?>', 'post', <?php echo $editor_settings; ?> );
}
} )();
</script>
</div>
<?php
};
$render_block_editor();
}
这个方法相对简单,主要做了这几件事:
- 准备数据: 创建一个
$block_editor_context
数组,包含编辑器样式、设置和当前文章对象。 - 允许插件修改 block editor 的 context: 通过
apply_filters( 'block_editor_settings', $block_editor_context )
允许插件修改$block_editor_context
数组,从而自定义 Gutenberg 编辑器的行为。 - 渲染 block editor: 输出 HTML 结构,包括一个
<div>
作为 Gutenberg 编辑器的挂载点,以及一段 JavaScript 代码来初始化 Gutenberg 编辑器。
关键点:Gutenberg 的初始化
注意这段 JavaScript 代码:
( function() {
var mountNode = document.getElementById( '<?php echo esc_attr( $editor_id ); ?>' );
if ( mountNode ) {
wp.editPost.initialize( '<?php echo esc_attr( $editor_id ); ?>', 'post', <?php echo $editor_settings; ?> );
}
} )();
它使用 wp.editPost.initialize()
函数来初始化 Gutenberg 编辑器。这个函数是 Gutenberg 编辑器的核心,它负责加载编辑器界面,处理用户输入,并将内容保存到数据库。
第五幕:兼容模式的秘密武器
wp_editor()
函数通过检查 URL 参数 classic-editor
和 classic-editor__forget
,以及 classic-editor-replace
选项,实现了对经典编辑器的兼容模式。 这意味着,即使你的 WordPress 版本已经升级到 Gutenberg,你仍然可以通过这些方式来强制使用经典编辑器。
总结陈词:wp_editor()
的角色定位
wp_editor()
函数就像一个万能的管家,它能根据你的需求,为你提供合适的编辑器。 无论是经典编辑器还是 Gutenberg 编辑器,它都能完美地支持。
表格总结:
功能点 | 经典编辑器 (TinyMCE) | Gutenberg 编辑器 (Block Editor) |
---|---|---|
加载方式 | _WP_Editors::editor() |
_WP_Editors::block_editor() |
初始化 | TinyMCE JavaScript API | wp.editPost.initialize() |
配置参数 | self::mce_settings() 生成的 JavaScript 对象 |
$block_editor_context 数组 |
兼容模式 | 通过 URL 参数 classic-editor 和 classic-editor__forget 以及 classic-editor-replace 选项支持 |
N/A |
可扩展性 | mce_plugins 和 tiny_mce_before_init 过滤器 |
block_editor_settings 过滤器 |
最后,留个小问题:
你觉得 wp_editor()
函数还有哪些可以改进的地方? 欢迎在评论区留言,咱们一起探讨!
今天的讲座就到这里,谢谢大家! 别忘了点赞、收藏、加关注哦! 下次再见!