: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Translation files are not inherited from the parent theme. TODO: If this fails for the
* child theme, it should probably try to load the parent theme's translations.
* @return bool True if the textdomain was successfully loaded or has already been loaded.
* False if no textdomain was specified in the file headers, or if the domain could not be loaded.
public function load_textdomain() {
if ( isset( $this->textdomain_loaded ) ) {
return $this->textdomain_loaded;
$textdomain = $this->get( 'TextDomain' );
$this->textdomain_loaded = false;
if ( is_textdomain_loaded( $textdomain ) ) {
$this->textdomain_loaded = true;
$path = $this->get_stylesheet_directory();
$domainpath = $this->get( 'DomainPath' );
$this->textdomain_loaded = load_theme_textdomain( $textdomain, $path );
return $this->textdomain_loaded;
* Determines whether the theme is allowed (multisite only).
* @param string $check Optional. Whether to check only the 'network'-wide settings, the 'site'
* settings, or 'both'. Defaults to 'both'.
* @param int $blog_id Optional. Ignored if only network-wide settings are checked. Defaults to current site.
* @return bool Whether the theme is allowed for the network. Returns true in single-site.
public function is_allowed( $check = 'both', $blog_id = null ) {
if ( ! is_multisite() ) {
if ( 'both' === $check || 'network' === $check ) {
$allowed = self::get_allowed_on_network();
if ( ! empty( $allowed[ $this->get_stylesheet() ] ) ) {
if ( 'both' === $check || 'site' === $check ) {
$allowed = self::get_allowed_on_site( $blog_id );
if ( ! empty( $allowed[ $this->get_stylesheet() ] ) ) {
* Returns whether this theme is a block-based theme or not.
public function is_block_theme() {
if ( isset( $this->block_theme ) ) {
return $this->block_theme;
$paths_to_index_block_template = array(
$this->get_file_path( '/templates/index.html' ),
$this->get_file_path( '/block-templates/index.html' ),
$this->block_theme = false;
foreach ( $paths_to_index_block_template as $path_to_index_block_template ) {
if ( is_file( $path_to_index_block_template ) && is_readable( $path_to_index_block_template ) ) {
$this->block_theme = true;
return $this->block_theme;
* Retrieves the path of a file in the theme.
* Searches in the stylesheet directory before the template directory so themes
* which inherit from a parent theme can just override one file.
* @param string $file Optional. File to search for in the stylesheet directory.
* @return string The path of the file.
public function get_file_path( $file = '' ) {
$file = ltrim( $file, '/' );
$stylesheet_directory = $this->get_stylesheet_directory();
$template_directory = $this->get_template_directory();
$path = $stylesheet_directory;
} elseif ( $stylesheet_directory !== $template_directory && file_exists( $stylesheet_directory . '/' . $file ) ) {
$path = $stylesheet_directory . '/' . $file;
$path = $template_directory . '/' . $file;
/** This filter is documented in wp-includes/link-template.php */
return apply_filters( 'theme_file_path', $path, $file );
* Determines the latest WordPress default theme that is installed.
* This hits the filesystem.
* @return WP_Theme|false Object, or false if no theme is installed, which would be bad.
public static function get_core_default_theme() {
foreach ( array_reverse( self::$default_themes ) as $slug => $name ) {
$theme = wp_get_theme( $slug );
if ( $theme->exists() ) {
* Returns array of stylesheet names of themes allowed on the site or network.
* @param int $blog_id Optional. ID of the site. Defaults to the current site.
* @return string[] Array of stylesheet names.
public static function get_allowed( $blog_id = null ) {
* Filters the array of themes allowed on the network.
* Site is provided as context so that a list of network allowed themes can
* @param string[] $allowed_themes An array of theme stylesheet names.
* @param int $blog_id ID of the site.
$network = (array) apply_filters( 'network_allowed_themes', self::get_allowed_on_network(), $blog_id );
return $network + self::get_allowed_on_site( $blog_id );
* Returns array of stylesheet names of themes allowed on the network.
* @return string[] Array of stylesheet names.
public static function get_allowed_on_network() {
if ( ! isset( $allowed_themes ) ) {
$allowed_themes = (array) get_site_option( 'allowedthemes' );
* Filters the array of themes allowed on the network.
* @param string[] $allowed_themes An array of theme stylesheet names.
$allowed_themes = apply_filters( 'allowed_themes', $allowed_themes );
* Returns array of stylesheet names of themes allowed on the site.
* @param int $blog_id Optional. ID of the site. Defaults to the current site.
* @return string[] Array of stylesheet names.
public static function get_allowed_on_site( $blog_id = null ) {
static $allowed_themes = array();
if ( ! $blog_id || ! is_multisite() ) {
$blog_id = get_current_blog_id();
if ( isset( $allowed_themes[ $blog_id ] ) ) {
* Filters the array of themes allowed on the site.
* @param string[] $allowed_themes An array of theme stylesheet names.
* @param int $blog_id ID of the site. Defaults to current site.
return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id );
$current = get_current_blog_id() === $blog_id;
$allowed_themes[ $blog_id ] = get_option( 'allowedthemes' );
switch_to_blog( $blog_id );
$allowed_themes[ $blog_id ] = get_option( 'allowedthemes' );
* This is all super old MU back compat joy.
* 'allowedthemes' keys things by stylesheet. 'allowed_themes' keyed things by name.
if ( false === $allowed_themes[ $blog_id ] ) {
$allowed_themes[ $blog_id ] = get_option( 'allowed_themes' );
switch_to_blog( $blog_id );
$allowed_themes[ $blog_id ] = get_option( 'allowed_themes' );
if ( ! is_array( $allowed_themes[ $blog_id ] ) || empty( $allowed_themes[ $blog_id ] ) ) {
$allowed_themes[ $blog_id ] = array();
$themes = wp_get_themes();
foreach ( $themes as $stylesheet => $theme_data ) {
if ( isset( $allowed_themes[ $blog_id ][ $theme_data->get( 'Name' ) ] ) ) {
$converted[ $stylesheet ] = true;
$allowed_themes[ $blog_id ] = $converted;
// Set the option so we never have to go through this pain again.
if ( is_admin() && $allowed_themes[ $blog_id ] ) {
update_option( 'allowedthemes', $allowed_themes[ $blog_id ] );
delete_option( 'allowed_themes' );
switch_to_blog( $blog_id );
update_option( 'allowedthemes', $allowed_themes[ $blog_id ] );
delete_option( 'allowed_themes' );
/** This filter is documented in wp-includes/class-wp-theme.php */
return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id );
* Returns the folder names of the block template directories.
* Folder names used by block themes.
* @type string $wp_template Theme-relative directory name for block templates.
* @type string $wp_template_part Theme-relative directory name for block template parts.
public function get_block_template_folders() {
// Return set/cached value if available.
if ( isset( $this->block_template_folders ) ) {
return $this->block_template_folders;
$this->block_template_folders = $this->default_template_folders;
$stylesheet_directory = $this->get_stylesheet_directory();
// If the theme uses deprecated block template folders.
if ( file_exists( $stylesheet_directory . '/block-templates' ) || file_exists( $stylesheet_directory . '/block-template-parts' ) ) {
$this->block_template_folders = array(
'wp_template' => 'block-templates',
'wp_template_part' => 'block-template-parts',
return $this->block_template_folders;
* Gets block pattern data for a specified theme.
* Each pattern is defined as a PHP file and defines
* its metadata using plugin-style headers. The minimum required definition is:
* * Slug: my-theme/my-pattern
* The output of the PHP source corresponds to the content of the pattern, e.g.:
* <main><p><?php echo "Hello"; ?></p></main>
* If applicable, this will collect from both parent and child theme.
* Other settable fields include:
* - Categories (comma-separated values)
* - Keywords (comma-separated values)
* - Block Types (comma-separated values)
* - Post Types (comma-separated values)
* - Template Types (comma-separated values)
* @return array Block pattern data.
public function get_block_patterns() {
$can_use_cached = ! wp_is_development_mode( 'theme' );
$pattern_data = $this->get_pattern_cache();
if ( is_array( $pattern_data ) ) {
// If in development mode, clear pattern cache.
$this->delete_pattern_cache();
$dirpath = $this->get_stylesheet_directory() . '/patterns/';
if ( ! file_exists( $dirpath ) ) {
$this->set_pattern_cache( $pattern_data );
$files = glob( $dirpath . '*.php' );
$this->set_pattern_cache( $pattern_data );
$default_headers = array(
'description' => 'Description',
'viewportWidth' => 'Viewport Width',
'inserter' => 'Inserter',
'categories' => 'Categories',
'keywords' => 'Keywords',
'blockTypes' => 'Block Types',
'postTypes' => 'Post Types',
'templateTypes' => 'Template Types',
$properties_to_parse = array(
foreach ( $files as $file ) {
$pattern = get_file_data( $file, $default_headers );
if ( empty( $pattern['slug'] ) ) {
/* translators: 1: file name. */
__( 'Could not register file "%s" as a block pattern ("Slug" field missing)' ),
if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern['slug'] ) ) {
/* translators: 1: file name; 2: slug value found. */
__( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")' ),
// Title is a required property.
if ( ! $pattern['title'] ) {
/* translators: 1: file name. */
__( 'Could not register file "%s" as a block pattern ("Title" field missing)' ),
// For properties of type array, parse data as comma-separated.
foreach ( $properties_to_parse as $property ) {
if ( ! empty( $pattern[ $property ] ) ) {
$pattern[ $property ] = array_filter( wp_parse_list( (string) $pattern[ $property ] ) );
unset( $pattern[ $property ] );
// Parse properties of type int.
$property = 'viewportWidth';
if ( ! empty( $pattern[ $property ] ) ) {
$pattern[ $property ] = (int) $pattern[ $property ];
unset( $pattern[ $property ] );
// Parse properties of type bool.
if ( ! empty( $pattern[ $property ] ) ) {
$pattern[ $property ] = in_array(
strtolower( $pattern[ $property ] ),
unset( $pattern[ $property ] );
$key = str_replace( $dirpath, '', $file );
$pattern_data[ $key ] = $pattern;
$this->set_pattern_cache( $pattern_data );
* Gets block pattern cache.
* @since 6.6.0 Uses transients to cache regardless of site environment.
* @return array|false Returns an array of patterns if cache is found, otherwise false.
private function get_pattern_cache() {
if ( ! $this->exists() ) {
$pattern_data = get_site_transient( 'wp_theme_files_patterns-' . $this->cache_hash );
if ( is_array( $pattern_data ) && $pattern_data['version'] === $this->get( 'Version' ) ) {
return $pattern_data['patterns'];
* Sets block pattern cache.
* @since 6.6.0 Uses transients to cache regardless of site environment.
* @param array $patterns Block patterns data to set in cache.
private function set_pattern_cache( array $patterns ) {