WordPress Version: 6.3
/**
* Searches all registered theme directories for complete and valid themes.
*
* @since 2.9.0
*
* @global array $wp_theme_directories
*
* @param bool $force Optional. Whether to force a new directory scan. Default false.
* @return array|false Valid themes found on success, false on failure.
*/
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]);
}
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) {
trigger_error("{$theme_root} is not readable", E_USER_NOTICE);
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) {
trigger_error("{$theme_root}/{$dir} is not readable", E_USER_NOTICE);
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;
}