咳咳,各位观众老爷们,晚上好!欢迎来到今天的“WordPress主题解剖”特别节目。今天咱们不聊八卦,不谈情怀,就来硬核地啃啃 WordPress 主题背后的“骨头”—— WP_Theme
类,特别是它如何从 style.css
这个小小的文件中,挖出主题的“身世”和“性格”。
准备好了吗?咱们这就开整!
一、 WP_Theme
类:主题的“户口本”
在 WordPress 的世界里,WP_Theme
类就像是每个主题的“户口本”,它记录了主题的各种基本信息,比如:
- 主题名称 (Theme Name)
- 主题版本 (Version)
- 作者 (Author)
- 描述 (Description)
- 授权方式 (License)
- 等等…
这些信息都藏在哪里呢?没错,就在主题根目录下的 style.css
文件里!
二、 style.css
:主题的“身份证”
style.css
文件可不只是用来写 CSS 样式那么简单,它还承担着“身份证”的重任,负责告诉 WordPress 这个主题是谁,从哪里来,要到哪里去(误)。
style.css
文件的头部注释部分,就是用来存放主题信息的关键区域。 WordPress 会读取这些注释,并将它们解析成 WP_Theme
对象的属性。
一个典型的 style.css
文件头部注释可能长这样:
/*
Theme Name: My Awesome Theme
Theme URI: https://example.com/my-awesome-theme/
Author: John Doe
Author URI: https://example.com/
Description: A super awesome theme for super awesome people.
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: my-awesome-theme
Tags: blog, one-column, custom-menu, featured-images
*/
注意,这些信息必须按照特定的格式写在注释里,而且必须放在文件的最开头,否则 WordPress 就识别不出来。
三、 WP_Theme
类的“寻宝之旅”:源码解析
好了,理论知识铺垫完毕,现在咱们深入 WP_Theme
类的源码,看看它是如何从 style.css
文件中“寻宝”的。
WP_Theme
类的核心方法之一是 get_stylesheet()
和 get_template()
。 它们分别返回主题的样式表目录(stylesheet directory)和模板目录(template directory)。 对于普通主题,这两个目录通常是相同的。 对于子主题,样式表目录是子主题的目录,而模板目录是父主题的目录。
<?php
/**
* Core class used to implement the Theme object.
*
* @since 3.4.0
*
* @property string $name Theme name.
* @property string $version Theme version.
* @property string $stylesheet Stylesheet name.
* @property string $template Template name.
*/
class WP_Theme {
/**
* Headers within the style sheet.
*
* @since 3.4.0
* @access private
* @var array
*/
private $headers = array();
/**
* Root directory of the theme.
*
* @since 3.4.0
* @access private
* @var string
*/
private $theme_root;
/**
* Root URI of the theme.
*
* @since 3.4.0
* @access private
* @var string
*/
private $theme_root_uri;
/**
* True, if the theme is installed in a subdirectory off of
* WP_CONTENT_DIR and WP_CONTENT_URL. Otherwise, false.
*
* @since 3.4.0
* @access private
* @var bool
*/
private $stylesheet_dir_name;
/**
* True, if the template is installed in a subdirectory off of
* WP_CONTENT_DIR and WP_CONTENT_URL. Otherwise, false.
*
* @since 3.4.0
* @access private
* @var bool
*/
private $template_dir_name;
/**
* The text domain of the theme.
*
* @since 3.4.0
* @access private
* @var string
*/
private $textdomain;
/**
* Constructor.
*
* @since 3.4.0
*
* @param string $stylesheet Stylesheet name.
* @param string $theme_root Theme root.
*/
public function __construct( $stylesheet, $theme_root ) {
$this->stylesheet = $stylesheet;
$this->theme_root = $theme_root;
$this->theme_root_uri = '';
$this->stylesheet_dir_name = '';
$this->template_dir_name = '';
$this->textdomain = '';
$this->headers = $this->get_theme_data();
}
/**
* Gets the data from a theme's style.css file.
*
* @since 3.4.0
* @access private
*
* @return array The contents of the style.css file.
*/
private function get_theme_data() {
$stylesheet = $this->get_stylesheet();
$theme_root = $this->get_theme_root();
$style_path = $theme_root . '/' . $stylesheet . '/style.css';
if ( ! is_readable( $style_path ) ) {
return array();
}
$theme_data = get_file_data( $style_path, $this->get_stylesheet_headers() );
if ( empty( $theme_data['TextDomain'] ) ) {
$theme_data['TextDomain'] = sanitize_title( $this->get( 'Name' ) );
}
return $theme_data;
}
/**
* Gets the headers from a theme's style.css file.
*
* @since 3.4.0
* @access private
*
* @return array The headers from the style.css file.
*/
private function get_stylesheet_headers() {
return array(
'Name' => 'Theme Name',
'ThemeURI' => 'Theme URI',
'Description' => 'Description',
'Author' => 'Author',
'AuthorURI' => 'Author URI',
'Version' => 'Version',
'Template' => 'Template',
'Status' => 'Status',
'Tags' => 'Tags',
'TextDomain' => 'Text Domain',
'License' => 'License',
'LicenseURI' => 'License URI',
);
}
/**
* Gets a theme's data.
*
* @since 3.4.0
*
* @param string $header Theme header.
* @return string|false The value of the header. False if not found.
*/
public function get( $header ) {
if ( isset( $this->headers[ $header ] ) ) {
return $this->headers[ $header ];
}
return false;
}
/**
* Gets the theme's stylesheet directory.
*
* @since 3.4.0
*
* @return string Stylesheet directory.
*/
public function get_stylesheet() {
return $this->stylesheet;
}
/**
* Gets the theme's template directory.
*
* @since 3.4.0
*
* @return string Template directory.
*/
public function get_template() {
if ( $this->has_parent() ) {
return $this->headers['Template'];
}
return $this->stylesheet;
}
/**
* Whether the theme has a parent.
*
* @since 3.4.0
*
* @return bool True, if the theme has a parent. False, if not.
*/
public function has_parent() {
return ! empty( $this->headers['Template'] );
}
/**
* Gets the name of the theme's text domain.
*
* @since 3.4.0
*
* @return string Text domain.
*/
public function get_template_directory() {
return $this->get_theme_root() . '/' . $this->get_template();
}
/**
* Gets the URI of the theme's template directory.
*
* @since 3.4.0
*
* @return string Template directory URI.
*/
public function get_template_directory_uri() {
return $this->get_theme_root_uri() . '/' . $this->get_template();
}
/**
* Gets the theme's stylesheet directory.
*
* @since 3.4.0
*
* @return string Stylesheet directory.
*/
public function get_stylesheet_directory() {
return $this->get_theme_root() . '/' . $this->get_stylesheet();
}
/**
* Gets the URI of the theme's stylesheet directory.
*
* @since 3.4.0
*
* @return string Stylesheet directory URI.
*/
public function get_stylesheet_directory_uri() {
return $this->get_theme_root_uri() . '/' . $this->get_stylesheet();
}
/**
* Gets the root directory of the theme.
*
* @since 3.4.0
*
* @return string Theme root.
*/
public function get_theme_root() {
return $this->theme_root;
}
/**
* Gets the URI of the root directory of the theme.
*
* @since 3.4.0
*
* @return string Theme root URI.
*/
public function get_theme_root_uri() {
if ( ! empty( $this->theme_root_uri ) ) {
return $this->theme_root_uri;
}
$theme_root = $this->get_theme_root();
/**
* Filters the URI of a theme's root directory.
*
* @since 3.4.0
*
* @param string $theme_root_uri The URI of the theme root directory.
* @param string $theme_root The absolute path to the theme root directory.
* @param string $stylesheet The theme's stylesheet.
*/
$this->theme_root_uri = apply_filters( 'theme_root_uri', get_theme_root_uri( $this->get_stylesheet(), $theme_root ), $theme_root, $this->get_stylesheet() );
return $this->theme_root_uri;
}
/**
* Gets the name of the theme's text domain.
*
* @since 3.4.0
*
* @return string Text domain.
*/
public function get_textdomain() {
return $this->textdomain;
}
/**
* Whether the theme is a child theme.
*
* @since 3.4.0
*
* @return bool True, if the theme is a child theme. False, if not.
*/
public function is_child_theme() {
return $this->has_parent();
}
/**
* Displays a contextual link to the theme.
*
* @since 3.4.0
*
* @param string $context Optional. Context of the link. Possible values are
* 'author', 'theme', and 'parent'. Default 'theme'.
*/
public function display( $context = 'theme' ) {
$stylesheet = $this->get_stylesheet();
$args = array(
'theme' => $stylesheet,
);
switch ( $context ) {
case 'author':
$author = $this->get( 'Author' );
if ( empty( $author ) ) {
break;
}
$author_uri = $this->get( 'AuthorURI' );
if ( empty( $author_uri ) ) {
echo esc_html( $author );
break;
}
echo '<a href="' . esc_url( $author_uri ) . '">' . esc_html( $author ) . '</a>';
break;
case 'parent':
$parent = $this->parent();
if ( ! $parent ) {
break;
}
$args['theme'] = $parent->get_stylesheet();
$name = $parent->get( 'Name' );
$name = translate( $name, $parent->get( 'TextDomain' ) );
echo '<a href="' . esc_url( wp_nonce_url( add_query_arg( $args, 'themes.php' ), 'switch-theme_' . $parent->get_stylesheet() ) ) . '">' . esc_html( $name ) . '</a>';
break;
case 'theme':
default:
$name = $this->get( 'Name' );
$name = translate( $name, $this->get( 'TextDomain' ) );
echo '<a href="' . esc_url( wp_nonce_url( add_query_arg( $args, 'themes.php' ), 'switch-theme_' . $stylesheet ) ) . '">' . esc_html( $name ) . '</a>';
break;
}
}
/**
* Returns the parent of the current theme.
*
* @since 3.4.0
*
* @return WP_Theme|false The parent of the current theme, or false if the
* theme does not have a parent.
*/
public function parent() {
if ( ! $this->has_parent() ) {
return false;
}
return wp_get_theme( $this->get( 'Template' ) );
}
}
?>
接下来,我们重点关注 __construct()
构造函数和 get_theme_data()
方法:
-
__construct( $stylesheet, $theme_root )
:- 这个构造函数接收两个参数:
$stylesheet
(样式表名称) 和$theme_root
(主题根目录)。 - 它会设置
$this->stylesheet
和$this->theme_root
属性。 - 最关键的是,它会调用
$this->get_theme_data()
方法,将从style.css
中提取的信息存储到$this->headers
属性中。
- 这个构造函数接收两个参数:
-
get_theme_data()
:- 这个方法负责从
style.css
文件中读取主题信息。 - 它首先构建
style.css
文件的完整路径:$style_path = $theme_root . '/' . $stylesheet . '/style.css';
- 然后,它会调用
get_file_data()
函数,这个函数是 WordPress 内置的,专门用来读取文件头部注释中的数据。 get_file_data()
函数需要两个参数:文件路径和要读取的头部信息列表。$this->get_stylesheet_headers()
方法就是用来定义要读取哪些头部信息的,比如 ‘Theme Name’, ‘Version’, ‘Author’ 等等。- 最后,
get_theme_data()
方法会将读取到的数据返回。
- 这个方法负责从
-
get_stylesheet_headers()
:返回一个数组,定义了需要从
style.css
文件中读取的头部信息。每个键值对的键表示要获取的属性名,值表示在style.css
文件中对应的头部名称。private function get_stylesheet_headers() { return array( 'Name' => 'Theme Name', 'ThemeURI' => 'Theme URI', 'Description' => 'Description', 'Author' => 'Author', 'AuthorURI' => 'Author URI', 'Version' => 'Version', 'Template' => 'Template', 'Status' => 'Status', 'Tags' => 'Tags', 'TextDomain' => 'Text Domain', 'License' => 'License', 'LicenseURI' => 'License URI', ); }
-
get( $header )
:这个方法用于获取特定头部信息的值。它接收一个
$header
参数,表示要获取的头部信息的名称。如果找到了对应的头部信息,则返回其值;否则返回false
。public function get( $header ) { if ( isset( $this->headers[ $header ] ) ) { return $this->headers[ $header ]; } return false; }
四、 get_file_data()
:幕后英雄
get_file_data()
函数是 WordPress 提供的核心函数,位于 wp-includes/functions.php
文件中。 它的作用是从文件中读取头部注释中的数据,并将其解析成一个数组。
<?php
/**
* Gets file data from a specific file.
*
* @since 2.9.0
*
* @param string $file Path to the file to open for reading.
* @param array $default_headers List of headers, in the format array('HeaderKey' => 'Header Name').
* @param string $context Optional. If specified adds filter hook {@see 'extra_theme_headers'} or
* {@see 'extra_plugin_headers'}. Use 'theme' for Theme files, 'plugin' for plugin files.
* Default null.
* @return array Array of file data.
*/
function get_file_data( $file, $default_headers, $context = null ) {
// We don't need to write to the file, so just use r.
$fp = fopen( $file, 'r' );
// Pull out the first 8kiB of the file.
$file_data = fread( $fp, 8192 );
// PHP will close file handle, but we are good citizens.
fclose( $fp );
// Make sure we catch CR-only line endings.
$file_data = str_replace( "r", "n", $file_data );
/**
* Filters extra file headers when parsing a file.
*
* The dynamic portion of the hook name, `$context`, refers to the context
* passed to `get_file_data()`.
*
* @since 2.9.0
*
* @param array $default_headers Array of default headers.
* @param string $file Full path to the file.
*/
if ( ! empty( $context ) ) {
$extra_headers = apply_filters( "extra_{$context}_headers", array(), $file );
$default_headers = array_merge( $extra_headers, $default_headers );
}
foreach ( $default_headers as $field => $regex ) {
// Pre-match to avoid perf loss.
if ( preg_match( '/^[ t/*#@]*' . preg_quote( $regex, '/' ) . ':(.*)$/mi', $file_data, $match ) && $match[1] ) {
$all_matches = preg_split( '/[rn]+/', $match[1] );
$value = trim( $all_matches[0] );
} else {
$value = '';
}
$all_headers[ $field ] = $value;
}
return $all_headers;
}
?>
简单来说,get_file_data()
函数会:
- 打开文件。
- 读取文件的前 8KB 内容(理论上主题信息应该都在这个范围内)。
- 遍历传入的头部信息列表(比如 ‘Theme Name’, ‘Version’ 等等)。
- 使用正则表达式从文件内容中匹配对应的头部信息。
- 将匹配到的信息存储到一个数组中并返回。
五、 实战演练:如何使用 WP_Theme
类获取主题信息
说了这么多,咱们来点实际的。 假设我们想要获取当前主题的名称和版本,可以这样做:
<?php
// 获取当前主题的 WP_Theme 对象
$theme = wp_get_theme();
// 获取主题名称
$theme_name = $theme->get( 'Name' );
// 获取主题版本
$theme_version = $theme->get( 'Version' );
// 输出主题信息
echo '主题名称:' . esc_html( $theme_name ) . '<br>';
echo '主题版本:' . esc_html( $theme_version ) . '<br>';
?>
这段代码首先使用 wp_get_theme()
函数获取当前主题的 WP_Theme
对象。 然后,使用 get()
方法分别获取主题的名称和版本。 最后,将主题信息输出到页面上。
六、 子主题的“特殊待遇”
对于子主题来说,WP_Theme
类的处理方式略有不同。 子主题的 style.css
文件中通常会包含一个 Template
头部,用来指定父主题的目录名。
/*
Theme Name: My Awesome Child Theme
Description: A child theme for My Awesome Theme.
Author: John Doe
Template: my-awesome-theme // 注意这里!
Version: 1.0.0
*/
当 WP_Theme
类检测到 Template
头部时,它会知道这是一个子主题,并会加载父主题的 WP_Theme
对象。 这样,子主题就可以继承父主题的信息,并且可以覆盖父主题的样式和功能。
WP_Theme
类中与子主题相关的关键方法是 has_parent()
和 parent()
:
has_parent()
:判断当前主题是否有父主题。parent()
:返回父主题的WP_Theme
对象。
七、 总结与展望
今天咱们一起深入剖析了 WordPress WP_Theme
类的源码,了解了它如何从 style.css
文件中提取主题信息。 掌握了这些知识,你就可以更加灵活地使用 WordPress 主题,并且可以开发出更加强大的主题相关功能。
方法/属性 | 作用 |
---|---|
__construct() |
构造函数,初始化 WP_Theme 对象,读取 style.css 信息。 |
get_theme_data() |
从 style.css 文件中读取主题数据。 |
get() |
获取指定头部信息的值。 |
get_stylesheet() |
获取主题的样式表目录名。 |
get_template() |
获取主题的模板目录名(对于子主题,返回父主题的目录名)。 |
has_parent() |
判断主题是否是子主题。 |
parent() |
获取父主题的 WP_Theme 对象。 |
当然,WP_Theme
类还有很多其他的细节和功能,比如主题的本地化、主题的截图等等,这些就留给各位观众老爷们自己去探索了。
希望今天的节目对大家有所帮助。 如果你觉得还不错,记得点赞、评论、转发三连哦!咱们下期再见!