把自定义样式从主题目录搬到主题选项,升级不再丢

原来主题里有个 custom/ 目录(custom.css / custom.js / custom.php),
是给用户随手加样式的入口。问题是它位于主题目录内,而 WordPress 升级
主题的做法是把整个主题目录删掉再解压(class-theme-upgrader.php 里
clear_destination = true,随后 delete_old_theme() 递归删除),
所以每次升级这些文件都会丢。custom.php 还会被 require,
等于在主题里留了一个执行任意 PHP 的口子。

改成后台选项:
- 主题选项新增「自定义代码」组,含自定义 CSS / 自定义 JS 两个输入框
- 内容存在数据库的 kratos 选项里,与主题文件解耦,升级、重装都不影响
- 分别在主题样式和主题脚本之后加载,优先级最高

为此新增了 code 字段类型,没有复用 textarea:textarea 的 sanitizer
走 wp_kses(),CSS 的子选择器 > 和 JS 的 < 会被当成 HTML 标签剥掉,
样式会静默失效。code 类型只去掉 PHP 标签、打断 </style> 和 </script>,
其余原样保留。渲染时也不走公共的 stripslashes —— options.php 已经
wp_unslash 过,再剥一层会把 CSS 的 \f101、JS 的 \n 啃坏。

自定义 CSS 的默认值就是原来 custom.css 的内容,逐字节一致(354 字节),
所以升级瞬间站点外观没有任何变化。清空输入框保存即可彻底去掉。

验证:真实 WP 7.1.3 / PHP 8.5.12 环境 20 条断言全过(含 DB 往返、
子选择器 / 反斜杠保真、危险内容过滤),生产选项指纹前后一致;
反向注入「默认值改空」能立刻被抓到。
This commit is contained in:
2026-10-23 10:35:56 +08:00
parent 8a55457210
commit eaabc7717f
11 changed files with 180 additions and 54 deletions
+11 -2
View File
@@ -63,14 +63,23 @@ function theme_autoload()
wp_add_inline_style('kratos', $admin_bar_css);
}
}
wp_enqueue_style('custom', get_template_directory_uri() . '/custom/custom.css', array(), THEME_VERSION);
// 自定义样式:存在主题选项里(数据库),排在主题样式之后,优先级最高。
// 放选项而非主题目录,是为了让它在主题升级(升级会清空主题目录)后依然存在。
$custom_css = yang_custom_css();
if (trim($custom_css) !== '') {
wp_add_inline_style('kratos', $custom_css);
}
// js
wp_deregister_script('jquery');
wp_enqueue_script('jquery', ASSET_PATH . '/assets/js/jquery.min.js', array(), '3.4.1', false);
wp_enqueue_script('bootstrap-bundle', ASSET_PATH . '/assets/js/bootstrap.bundle.min.js', array(), '4.5.0', true);
wp_enqueue_script('layer', ASSET_PATH . '/assets/js/layer.min.js', array(), '3.1.1', true);
wp_enqueue_script('kratos', ASSET_PATH . '/assets/js/kratos.min.js', array(), THEME_VERSION, true);
wp_enqueue_script('custom', get_template_directory_uri() . '/custom/custom.js', array(), THEME_VERSION, true);
// 自定义脚本:同样存在主题选项里,挂在 kratos 之后(页脚输出)。
$custom_js = yang_custom_js();
if (trim($custom_js) !== '') {
wp_add_inline_script('kratos', $custom_js);
}
$data = array(
'site' => home_url(),