函数文档

search_theme_directories()

💡 云策文档标注

概述

search_theme_directories() 函数用于扫描所有已注册的主题目录,查找完整且有效的主题。它支持缓存机制以提高性能,并返回主题信息数组。

关键要点

  • 函数参数:$force(可选布尔值,默认为 false),强制重新扫描目录,忽略缓存。
  • 返回值:成功时返回包含有效主题的数组,失败时返回 false。
  • 工作原理:遍历 $wp_theme_directories 全局变量中的目录,检查每个目录下是否存在 style.css 文件以识别主题。
  • 缓存处理:使用 wp_cache_themes_persistently 过滤器控制缓存,默认缓存过期时间为 30 分钟,通过 site_transient 存储 theme_roots。
  • 错误处理:使用 wp_trigger_error() 记录不可读目录的警告。
  • 主题结构:支持主题文件位于根目录或直接子目录中,与官方文档描述略有不同。

代码示例

// 基本用法:扫描主题目录
$themes = search_theme_directories();
if ( $themes ) {
    foreach ( $themes as $theme_dir => $theme_data ) {
        echo "主题: " . $theme_dir . ",文件: " . $theme_data['theme_file'] . "n";
    }
}

// 强制重新扫描
$themes = search_theme_directories( true );

注意事项

  • 函数依赖于全局变量 $wp_theme_directories,如果为空则返回 false。
  • 缓存机制可提高性能,但强制扫描($force = true)会绕过缓存。
  • 主题识别基于 style.css 文件的存在,即使其他文件缺失,也可能被包含在结果中。
  • 使用 wp_trigger_error() 记录目录读取错误,但不会中断函数执行。

📄 原文内容

Searches all registered theme directories for complete and valid themes.

Parameters

$forcebooloptional
Whether to force a new directory scan.

Default:false

Return

array|false Valid themes found on success, false on failure.

Source

function search_theme_directories( $force = false ) {
	global $wp_theme_directories;
	static $found_themes = null;

	if ( empty( $wp_theme_directories ) ) {
		return false;
	}

	if ( ! $force && isset( $found_themes ) ) {
		return $found_themes;
	}

	$found_themes = array();

	$wp_theme_directories = (array) $wp_theme_directories;
	$relative_theme_roots = array();

	/*
	 * Set up maybe-relative, maybe-absolute array of theme directories.
	 * We always want to return absolute, but we need to cache relative
	 * to use in get_theme_root().
	 */
	foreach ( $wp_theme_directories as $theme_root ) {
		if ( str_starts_with( $theme_root, WP_CONTENT_DIR ) ) {
			$relative_theme_roots[ str_replace( WP_CONTENT_DIR, '', $theme_root ) ] = $theme_root;
		} else {
			$relative_theme_roots[ $theme_root ] = $theme_root;
		}
	}

	/**
	 * Filters whether to get the cache of the registered theme directories.
	 *
	 * @since 3.4.0
	 *
	 * @param bool   $cache_expiration Whether to get the cache of the theme directories. Default false.
	 * @param string $context          The class or function name calling the filter.
	 */
	$cache_expiration = apply_filters( 'wp_cache_themes_persistently', false, 'search_theme_directories' );

	if ( $cache_expiration ) {
		$cached_roots = get_site_transient( 'theme_roots' );
		if ( is_array( $cached_roots ) ) {
			foreach ( $cached_roots as $theme_dir => $theme_root ) {
				// A cached theme root is no longer around, so skip it.
				if ( ! isset( $relative_theme_roots[ $theme_root ] ) ) {
					continue;
				}
				$found_themes[ $theme_dir ] = array(
					'theme_file' => $theme_dir . '/style.css',
					'theme_root' => $relative_theme_roots[ $theme_root ], // Convert relative to absolute.
				);
			}
			return $found_themes;
		}
		if ( ! is_int( $cache_expiration ) ) {
			$cache_expiration = 30 * MINUTE_IN_SECONDS;
		}
	} else {
		$cache_expiration = 30 * MINUTE_IN_SECONDS;
	}

	/* Loop the registered theme directories and extract all themes */
	foreach ( $wp_theme_directories as $theme_root ) {

		// Start with directories in the root of the active theme directory.
		$dirs = @ scandir( $theme_root );
		if ( ! $dirs ) {
			wp_trigger_error( __FUNCTION__, "$theme_root is not readable" );
			continue;
		}
		foreach ( $dirs as $dir ) {
			if ( ! is_dir( $theme_root . '/' . $dir ) || '.' === $dir[0] || 'CVS' === $dir ) {
				continue;
			}
			if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) {
				/*
				 * wp-content/themes/a-single-theme
				 * wp-content/themes is $theme_root, a-single-theme is $dir.
				 */
				$found_themes[ $dir ] = array(
					'theme_file' => $dir . '/style.css',
					'theme_root' => $theme_root,
				);
			} else {
				$found_theme = false;
				/*
				 * wp-content/themes/a-folder-of-themes/*
				 * wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs.
				 */
				$sub_dirs = @ scandir( $theme_root . '/' . $dir );
				if ( ! $sub_dirs ) {
					wp_trigger_error( __FUNCTION__, "$theme_root/$dir is not readable" );
					continue;
				}
				foreach ( $sub_dirs as $sub_dir ) {
					if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || '.' === $dir[0] || 'CVS' === $dir ) {
						continue;
					}
					if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) ) {
						continue;
					}
					$found_themes[ $dir . '/' . $sub_dir ] = array(
						'theme_file' => $dir . '/' . $sub_dir . '/style.css',
						'theme_root' => $theme_root,
					);
					$found_theme                           = true;
				}
				/*
				 * Never mind the above, it's just a theme missing a style.css.
				 * Return it; WP_Theme will catch the error.
				 */
				if ( ! $found_theme ) {
					$found_themes[ $dir ] = array(
						'theme_file' => $dir . '/style.css',
						'theme_root' => $theme_root,
					);
				}
			}
		}
	}

	asort( $found_themes );

	$theme_roots          = array();
	$relative_theme_roots = array_flip( $relative_theme_roots );

	foreach ( $found_themes as $theme_dir => $theme_data ) {
		$theme_roots[ $theme_dir ] = $relative_theme_roots[ $theme_data['theme_root'] ]; // Convert absolute to relative.
	}

	if ( get_site_transient( 'theme_roots' ) !== $theme_roots ) {
		set_site_transient( 'theme_roots', $theme_roots, $cache_expiration );
	}

	return $found_themes;
}

Hooks

apply_filters( ‘wp_cache_themes_persistently’, bool $cache_expiration, string $context )

Filters whether to get the cache of the registered theme directories.

Changelog

Version Description
2.9.0 Introduced.

User Contributed Notes

  1. Skip to note 2 content

    The official documentation states that “the style.css file must be located in the root directory of the theme, not in a subdirectory.”. However, looking at the implementation of this function, we can see that this is not true. You can place the style.css and functions.php files in one of direct subdirectories of the root directory. The core will find those files and recognize the subdirectory as the theme’s root directory.