wp/wp-includes/class-wp-theme.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
     4  *
     4  *
     5  * @package WordPress
     5  * @package WordPress
     6  * @subpackage Theme
     6  * @subpackage Theme
     7  * @since 3.4.0
     7  * @since 3.4.0
     8  */
     8  */
       
     9 #[AllowDynamicProperties]
     9 final class WP_Theme implements ArrayAccess {
    10 final class WP_Theme implements ArrayAccess {
    10 
    11 
    11 	/**
    12 	/**
    12 	 * Whether the theme has been marked as updateable.
    13 	 * Whether the theme has been marked as updateable.
    13 	 *
    14 	 *
    21 	/**
    22 	/**
    22 	 * Headers for style.css files.
    23 	 * Headers for style.css files.
    23 	 *
    24 	 *
    24 	 * @since 3.4.0
    25 	 * @since 3.4.0
    25 	 * @since 5.4.0 Added `Requires at least` and `Requires PHP` headers.
    26 	 * @since 5.4.0 Added `Requires at least` and `Requires PHP` headers.
       
    27 	 * @since 6.1.0 Added `Update URI` header.
    26 	 * @var string[]
    28 	 * @var string[]
    27 	 */
    29 	 */
    28 	private static $file_headers = array(
    30 	private static $file_headers = array(
    29 		'Name'        => 'Theme Name',
    31 		'Name'        => 'Theme Name',
    30 		'ThemeURI'    => 'Theme URI',
    32 		'ThemeURI'    => 'Theme URI',
    37 		'Tags'        => 'Tags',
    39 		'Tags'        => 'Tags',
    38 		'TextDomain'  => 'Text Domain',
    40 		'TextDomain'  => 'Text Domain',
    39 		'DomainPath'  => 'Domain Path',
    41 		'DomainPath'  => 'Domain Path',
    40 		'RequiresWP'  => 'Requires at least',
    42 		'RequiresWP'  => 'Requires at least',
    41 		'RequiresPHP' => 'Requires PHP',
    43 		'RequiresPHP' => 'Requires PHP',
       
    44 		'UpdateURI'   => 'Update URI',
    42 	);
    45 	);
    43 
    46 
    44 	/**
    47 	/**
    45 	 * Default themes.
    48 	 * Default themes.
    46 	 *
    49 	 *
    53 	 * @since 4.7.0 Added the Twenty Seventeen theme.
    56 	 * @since 4.7.0 Added the Twenty Seventeen theme.
    54 	 * @since 5.0.0 Added the Twenty Nineteen theme.
    57 	 * @since 5.0.0 Added the Twenty Nineteen theme.
    55 	 * @since 5.3.0 Added the Twenty Twenty theme.
    58 	 * @since 5.3.0 Added the Twenty Twenty theme.
    56 	 * @since 5.6.0 Added the Twenty Twenty-One theme.
    59 	 * @since 5.6.0 Added the Twenty Twenty-One theme.
    57 	 * @since 5.9.0 Added the Twenty Twenty-Two theme.
    60 	 * @since 5.9.0 Added the Twenty Twenty-Two theme.
       
    61 	 * @since 6.1.0 Added the Twenty Twenty-Three theme.
       
    62 	 * @since 6.4.0 Added the Twenty Twenty-Four theme.
    58 	 * @var string[]
    63 	 * @var string[]
    59 	 */
    64 	 */
    60 	private static $default_themes = array(
    65 	private static $default_themes = array(
    61 		'classic'         => 'WordPress Classic',
    66 		'classic'           => 'WordPress Classic',
    62 		'default'         => 'WordPress Default',
    67 		'default'           => 'WordPress Default',
    63 		'twentyten'       => 'Twenty Ten',
    68 		'twentyten'         => 'Twenty Ten',
    64 		'twentyeleven'    => 'Twenty Eleven',
    69 		'twentyeleven'      => 'Twenty Eleven',
    65 		'twentytwelve'    => 'Twenty Twelve',
    70 		'twentytwelve'      => 'Twenty Twelve',
    66 		'twentythirteen'  => 'Twenty Thirteen',
    71 		'twentythirteen'    => 'Twenty Thirteen',
    67 		'twentyfourteen'  => 'Twenty Fourteen',
    72 		'twentyfourteen'    => 'Twenty Fourteen',
    68 		'twentyfifteen'   => 'Twenty Fifteen',
    73 		'twentyfifteen'     => 'Twenty Fifteen',
    69 		'twentysixteen'   => 'Twenty Sixteen',
    74 		'twentysixteen'     => 'Twenty Sixteen',
    70 		'twentyseventeen' => 'Twenty Seventeen',
    75 		'twentyseventeen'   => 'Twenty Seventeen',
    71 		'twentynineteen'  => 'Twenty Nineteen',
    76 		'twentynineteen'    => 'Twenty Nineteen',
    72 		'twentytwenty'    => 'Twenty Twenty',
    77 		'twentytwenty'      => 'Twenty Twenty',
    73 		'twentytwentyone' => 'Twenty Twenty-One',
    78 		'twentytwentyone'   => 'Twenty Twenty-One',
    74 		'twentytwentytwo' => 'Twenty Twenty-Two',
    79 		'twentytwentytwo'   => 'Twenty Twenty-Two',
       
    80 		'twentytwentythree' => 'Twenty Twenty-Three',
       
    81 		'twentytwentyfour'  => 'Twenty Twenty-Four',
    75 	);
    82 	);
    76 
    83 
    77 	/**
    84 	/**
    78 	 * Renamed theme tags.
    85 	 * Renamed theme tags.
    79 	 *
    86 	 *
   108 	 * @var array
   115 	 * @var array
   109 	 */
   116 	 */
   110 	private $headers_sanitized;
   117 	private $headers_sanitized;
   111 
   118 
   112 	/**
   119 	/**
       
   120 	 * Is this theme a block theme.
       
   121 	 *
       
   122 	 * @since 6.2.0
       
   123 	 * @var bool
       
   124 	 */
       
   125 	private $block_theme;
       
   126 
       
   127 	/**
   113 	 * Header name from the theme's style.css after being translated.
   128 	 * Header name from the theme's style.css after being translated.
   114 	 *
   129 	 *
   115 	 * Cached due to sorting functions running over the translated name.
   130 	 * Cached due to sorting functions running over the translated name.
   116 	 *
   131 	 *
   117 	 * @since 3.4.0
   132 	 * @since 3.4.0
   178 	 *
   193 	 *
   179 	 * @since 3.4.0
   194 	 * @since 3.4.0
   180 	 * @var string
   195 	 * @var string
   181 	 */
   196 	 */
   182 	private $cache_hash;
   197 	private $cache_hash;
       
   198 
       
   199 	/**
       
   200 	 * Block template folders.
       
   201 	 *
       
   202 	 * @since 6.4.0
       
   203 	 * @var string[]
       
   204 	 */
       
   205 	private $block_template_folders;
       
   206 
       
   207 	/**
       
   208 	 * Default values for template folders.
       
   209 	 *
       
   210 	 * @since 6.4.0
       
   211 	 * @var string[]
       
   212 	 */
       
   213 	private $default_template_folders = array(
       
   214 		'wp_template'      => 'templates',
       
   215 		'wp_template_part' => 'parts',
       
   216 	);
   183 
   217 
   184 	/**
   218 	/**
   185 	 * Flag for whether the themes cache bucket should be persistently cached.
   219 	 * Flag for whether the themes cache bucket should be persistently cached.
   186 	 *
   220 	 *
   187 	 * Default is false. Can be set with the {@see 'wp_cache_themes_persistently'} filter.
   221 	 * Default is false. Can be set with the {@see 'wp_cache_themes_persistently'} filter.
   227 			} else {
   261 			} else {
   228 				wp_cache_add_non_persistent_groups( 'themes' );
   262 				wp_cache_add_non_persistent_groups( 'themes' );
   229 			}
   263 			}
   230 		}
   264 		}
   231 
   265 
       
   266 		// Handle a numeric theme directory as a string.
       
   267 		$theme_dir = (string) $theme_dir;
       
   268 
   232 		$this->theme_root = $theme_root;
   269 		$this->theme_root = $theme_root;
   233 		$this->stylesheet = $theme_dir;
   270 		$this->stylesheet = $theme_dir;
   234 
   271 
   235 		// Correct a situation where the theme is 'some-directory/some-theme' but 'some-directory' was passed in as part of the theme root instead.
   272 		// Correct a situation where the theme is 'some-directory/some-theme' but 'some-directory' was passed in as part of the theme root instead.
   236 		if ( ! in_array( $theme_root, (array) $wp_theme_directories, true )
   273 		if ( ! in_array( $theme_root, (array) $wp_theme_directories, true )
   244 		$theme_file       = $this->stylesheet . '/style.css';
   281 		$theme_file       = $this->stylesheet . '/style.css';
   245 
   282 
   246 		$cache = $this->cache_get( 'theme' );
   283 		$cache = $this->cache_get( 'theme' );
   247 
   284 
   248 		if ( is_array( $cache ) ) {
   285 		if ( is_array( $cache ) ) {
   249 			foreach ( array( 'errors', 'headers', 'template' ) as $key ) {
   286 			foreach ( array( 'block_template_folders', 'block_theme', 'errors', 'headers', 'template' ) as $key ) {
   250 				if ( isset( $cache[ $key ] ) ) {
   287 				if ( isset( $cache[ $key ] ) ) {
   251 					$this->$key = $cache[ $key ];
   288 					$this->$key = $cache[ $key ];
   252 				}
   289 				}
   253 			}
   290 			}
   254 			if ( $this->errors ) {
   291 			if ( $this->errors ) {
   269 					)
   306 					)
   270 				);
   307 				);
   271 			} else {
   308 			} else {
   272 				$this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) );
   309 				$this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) );
   273 			}
   310 			}
   274 			$this->template = $this->stylesheet;
   311 			$this->template               = $this->stylesheet;
       
   312 			$this->block_theme            = false;
       
   313 			$this->block_template_folders = $this->default_template_folders;
   275 			$this->cache_add(
   314 			$this->cache_add(
   276 				'theme',
   315 				'theme',
   277 				array(
   316 				array(
   278 					'headers'    => $this->headers,
   317 					'block_template_folders' => $this->block_template_folders,
   279 					'errors'     => $this->errors,
   318 					'block_theme'            => $this->block_theme,
   280 					'stylesheet' => $this->stylesheet,
   319 					'headers'                => $this->headers,
   281 					'template'   => $this->template,
   320 					'errors'                 => $this->errors,
       
   321 					'stylesheet'             => $this->stylesheet,
       
   322 					'template'               => $this->template,
   282 				)
   323 				)
   283 			);
   324 			);
   284 			if ( ! file_exists( $this->theme_root ) ) { // Don't cache this one.
   325 			if ( ! file_exists( $this->theme_root ) ) { // Don't cache this one.
   285 				$this->errors->add( 'theme_root_missing', __( '<strong>Error</strong>: The themes directory is either empty or does not exist. Please check your installation.' ) );
   326 				$this->errors->add( 'theme_root_missing', __( '<strong>Error:</strong> The themes directory is either empty or does not exist. Please check your installation.' ) );
   286 			}
   327 			}
   287 			return;
   328 			return;
   288 		} elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) {
   329 		} elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) {
   289 			$this->headers['Name'] = $this->stylesheet;
   330 			$this->headers['Name']        = $this->stylesheet;
   290 			$this->errors          = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) );
   331 			$this->errors                 = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) );
   291 			$this->template        = $this->stylesheet;
   332 			$this->template               = $this->stylesheet;
       
   333 			$this->block_theme            = false;
       
   334 			$this->block_template_folders = $this->default_template_folders;
   292 			$this->cache_add(
   335 			$this->cache_add(
   293 				'theme',
   336 				'theme',
   294 				array(
   337 				array(
   295 					'headers'    => $this->headers,
   338 					'block_template_folders' => $this->block_template_folders,
   296 					'errors'     => $this->errors,
   339 					'block_theme'            => $this->block_theme,
   297 					'stylesheet' => $this->stylesheet,
   340 					'headers'                => $this->headers,
   298 					'template'   => $this->template,
   341 					'errors'                 => $this->errors,
       
   342 					'stylesheet'             => $this->stylesheet,
       
   343 					'template'               => $this->template,
   299 				)
   344 				)
   300 			);
   345 			);
   301 			return;
   346 			return;
   302 		} else {
   347 		} else {
   303 			$this->headers = get_file_data( $this->theme_root . '/' . $theme_file, self::$file_headers, 'theme' );
   348 			$this->headers = get_file_data( $this->theme_root . '/' . $theme_file, self::$file_headers, 'theme' );
   304 			// Default themes always trump their pretenders.
   349 			/*
   305 			// Properly identify default themes that are inside a directory within wp-content/themes.
   350 			 * Default themes always trump their pretenders.
       
   351 			 * Properly identify default themes that are inside a directory within wp-content/themes.
       
   352 			 */
   306 			$default_theme_slug = array_search( $this->headers['Name'], self::$default_themes, true );
   353 			$default_theme_slug = array_search( $this->headers['Name'], self::$default_themes, true );
   307 			if ( $default_theme_slug ) {
   354 			if ( $default_theme_slug ) {
   308 				if ( basename( $this->stylesheet ) != $default_theme_slug ) {
   355 				if ( basename( $this->stylesheet ) !== $default_theme_slug ) {
   309 					$this->headers['Name'] .= '/' . $this->stylesheet;
   356 					$this->headers['Name'] .= '/' . $this->stylesheet;
   310 				}
   357 				}
   311 			}
   358 			}
   312 		}
   359 		}
   313 
   360 
   321 				)
   368 				)
   322 			);
   369 			);
   323 			$this->cache_add(
   370 			$this->cache_add(
   324 				'theme',
   371 				'theme',
   325 				array(
   372 				array(
   326 					'headers'    => $this->headers,
   373 					'block_template_folders' => $this->get_block_template_folders(),
   327 					'errors'     => $this->errors,
   374 					'block_theme'            => $this->is_block_theme(),
   328 					'stylesheet' => $this->stylesheet,
   375 					'headers'                => $this->headers,
       
   376 					'errors'                 => $this->errors,
       
   377 					'stylesheet'             => $this->stylesheet,
   329 				)
   378 				)
   330 			);
   379 			);
   331 
   380 
   332 			return;
   381 			return;
   333 		}
   382 		}
   339 
   388 
   340 		if ( ! $this->template ) {
   389 		if ( ! $this->template ) {
   341 			$this->template = $this->stylesheet;
   390 			$this->template = $this->stylesheet;
   342 			$theme_path     = $this->theme_root . '/' . $this->stylesheet;
   391 			$theme_path     = $this->theme_root . '/' . $this->stylesheet;
   343 
   392 
   344 			if (
   393 			if ( ! $this->is_block_theme() && ! file_exists( $theme_path . '/index.php' ) ) {
   345 				! file_exists( $theme_path . '/templates/index.html' )
       
   346 				&& ! file_exists( $theme_path . '/block-templates/index.html' ) // Deprecated path support since 5.9.0.
       
   347 				&& ! file_exists( $theme_path . '/index.php' )
       
   348 			) {
       
   349 				$error_message = sprintf(
   394 				$error_message = sprintf(
   350 					/* translators: 1: templates/index.html, 2: index.php, 3: Documentation URL, 4: Template, 5: style.css */
   395 					/* translators: 1: templates/index.html, 2: index.php, 3: Documentation URL, 4: Template, 5: style.css */
   351 					__( 'Template is missing. Standalone themes need to have a %1$s or %2$s template file. <a href="%3$s">Child themes</a> need to have a %4$s header in the %5$s stylesheet.' ),
   396 					__( 'Template is missing. Standalone themes need to have a %1$s or %2$s template file. <a href="%3$s">Child themes</a> need to have a %4$s header in the %5$s stylesheet.' ),
   352 					'<code>templates/index.html</code>',
   397 					'<code>templates/index.html</code>',
   353 					'<code>index.php</code>',
   398 					'<code>index.php</code>',
   357 				);
   402 				);
   358 				$this->errors = new WP_Error( 'theme_no_index', $error_message );
   403 				$this->errors = new WP_Error( 'theme_no_index', $error_message );
   359 				$this->cache_add(
   404 				$this->cache_add(
   360 					'theme',
   405 					'theme',
   361 					array(
   406 					array(
   362 						'headers'    => $this->headers,
   407 						'block_template_folders' => $this->get_block_template_folders(),
   363 						'errors'     => $this->errors,
   408 						'block_theme'            => $this->block_theme,
   364 						'stylesheet' => $this->stylesheet,
   409 						'headers'                => $this->headers,
   365 						'template'   => $this->template,
   410 						'errors'                 => $this->errors,
       
   411 						'stylesheet'             => $this->stylesheet,
       
   412 						'template'               => $this->template,
   366 					)
   413 					)
   367 				);
   414 				);
   368 				return;
   415 				return;
   369 			}
   416 			}
   370 		}
   417 		}
   371 
   418 
   372 		// If we got our data from cache, we can assume that 'template' is pointing to the right place.
   419 		// If we got our data from cache, we can assume that 'template' is pointing to the right place.
   373 		if ( ! is_array( $cache ) && $this->template != $this->stylesheet && ! file_exists( $this->theme_root . '/' . $this->template . '/index.php' ) ) {
   420 		if ( ! is_array( $cache )
   374 			// If we're in a directory of themes inside /themes, look for the parent nearby.
   421 			&& $this->template !== $this->stylesheet
   375 			// wp-content/themes/directory-of-themes/*
   422 			&& ! file_exists( $this->theme_root . '/' . $this->template . '/index.php' )
       
   423 		) {
       
   424 			/*
       
   425 			 * If we're in a directory of themes inside /themes, look for the parent nearby.
       
   426 			 * wp-content/themes/directory-of-themes/*
       
   427 			 */
   376 			$parent_dir  = dirname( $this->stylesheet );
   428 			$parent_dir  = dirname( $this->stylesheet );
   377 			$directories = search_theme_directories();
   429 			$directories = search_theme_directories();
   378 
   430 
   379 			if ( '.' !== $parent_dir && file_exists( $this->theme_root . '/' . $parent_dir . '/' . $this->template . '/index.php' ) ) {
   431 			if ( '.' !== $parent_dir
       
   432 				&& file_exists( $this->theme_root . '/' . $parent_dir . '/' . $this->template . '/index.php' )
       
   433 			) {
   380 				$this->template = $parent_dir . '/' . $this->template;
   434 				$this->template = $parent_dir . '/' . $this->template;
   381 			} elseif ( $directories && isset( $directories[ $this->template ] ) ) {
   435 			} elseif ( $directories && isset( $directories[ $this->template ] ) ) {
   382 				// Look for the template in the search_theme_directories() results, in case it is in another theme root.
   436 				/*
   383 				// We don't look into directories of themes, just the theme root.
   437 				 * Look for the template in the search_theme_directories() results, in case it is in another theme root.
       
   438 				 * We don't look into directories of themes, just the theme root.
       
   439 				 */
   384 				$theme_root_template = $directories[ $this->template ]['theme_root'];
   440 				$theme_root_template = $directories[ $this->template ]['theme_root'];
   385 			} else {
   441 			} else {
   386 				// Parent theme is missing.
   442 				// Parent theme is missing.
   387 				$this->errors = new WP_Error(
   443 				$this->errors = new WP_Error(
   388 					'theme_no_parent',
   444 					'theme_no_parent',
   393 					)
   449 					)
   394 				);
   450 				);
   395 				$this->cache_add(
   451 				$this->cache_add(
   396 					'theme',
   452 					'theme',
   397 					array(
   453 					array(
   398 						'headers'    => $this->headers,
   454 						'block_template_folders' => $this->get_block_template_folders(),
   399 						'errors'     => $this->errors,
   455 						'block_theme'            => $this->is_block_theme(),
   400 						'stylesheet' => $this->stylesheet,
   456 						'headers'                => $this->headers,
   401 						'template'   => $this->template,
   457 						'errors'                 => $this->errors,
       
   458 						'stylesheet'             => $this->stylesheet,
       
   459 						'template'               => $this->template,
   402 					)
   460 					)
   403 				);
   461 				);
   404 				$this->parent = new WP_Theme( $this->template, $this->theme_root, $this );
   462 				$this->parent = new WP_Theme( $this->template, $this->theme_root, $this );
   405 				return;
   463 				return;
   406 			}
   464 			}
   407 		}
   465 		}
   408 
   466 
   409 		// Set the parent, if we're a child theme.
   467 		// Set the parent, if we're a child theme.
   410 		if ( $this->template != $this->stylesheet ) {
   468 		if ( $this->template !== $this->stylesheet ) {
   411 			// If we are a parent, then there is a problem. Only two generations allowed! Cancel things out.
   469 			// If we are a parent, then there is a problem. Only two generations allowed! Cancel things out.
   412 			if ( $_child instanceof WP_Theme && $_child->template == $this->stylesheet ) {
   470 			if ( $_child instanceof WP_Theme && $_child->template === $this->stylesheet ) {
   413 				$_child->parent = null;
   471 				$_child->parent = null;
   414 				$_child->errors = new WP_Error(
   472 				$_child->errors = new WP_Error(
   415 					'theme_parent_invalid',
   473 					'theme_parent_invalid',
   416 					sprintf(
   474 					sprintf(
   417 						/* translators: %s: Theme directory name. */
   475 						/* translators: %s: Theme directory name. */
   420 					)
   478 					)
   421 				);
   479 				);
   422 				$_child->cache_add(
   480 				$_child->cache_add(
   423 					'theme',
   481 					'theme',
   424 					array(
   482 					array(
   425 						'headers'    => $_child->headers,
   483 						'block_template_folders' => $_child->get_block_template_folders(),
   426 						'errors'     => $_child->errors,
   484 						'block_theme'            => $_child->is_block_theme(),
   427 						'stylesheet' => $_child->stylesheet,
   485 						'headers'                => $_child->headers,
   428 						'template'   => $_child->template,
   486 						'errors'                 => $_child->errors,
       
   487 						'stylesheet'             => $_child->stylesheet,
       
   488 						'template'               => $_child->template,
   429 					)
   489 					)
   430 				);
   490 				);
   431 				// The two themes actually reference each other with the Template header.
   491 				// The two themes actually reference each other with the Template header.
   432 				if ( $_child->stylesheet == $this->template ) {
   492 				if ( $_child->stylesheet === $this->template ) {
   433 					$this->errors = new WP_Error(
   493 					$this->errors = new WP_Error(
   434 						'theme_parent_invalid',
   494 						'theme_parent_invalid',
   435 						sprintf(
   495 						sprintf(
   436 							/* translators: %s: Theme directory name. */
   496 							/* translators: %s: Theme directory name. */
   437 							__( 'The "%s" theme is not a valid parent theme.' ),
   497 							__( 'The "%s" theme is not a valid parent theme.' ),
   439 						)
   499 						)
   440 					);
   500 					);
   441 					$this->cache_add(
   501 					$this->cache_add(
   442 						'theme',
   502 						'theme',
   443 						array(
   503 						array(
   444 							'headers'    => $this->headers,
   504 							'block_template_folders' => $this->get_block_template_folders(),
   445 							'errors'     => $this->errors,
   505 							'block_theme'            => $this->is_block_theme(),
   446 							'stylesheet' => $this->stylesheet,
   506 							'headers'                => $this->headers,
   447 							'template'   => $this->template,
   507 							'errors'                 => $this->errors,
       
   508 							'stylesheet'             => $this->stylesheet,
       
   509 							'template'               => $this->template,
   448 						)
   510 						)
   449 					);
   511 					);
   450 				}
   512 				}
   451 				return;
   513 				return;
   452 			}
   514 			}
   453 			// Set the parent. Pass the current instance so we can do the crazy checks above and assess errors.
   515 			// Set the parent. Pass the current instance so we can do the checks above and assess errors.
   454 			$this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this );
   516 			$this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this );
   455 		}
   517 		}
   456 
   518 
   457 		if ( wp_paused_themes()->get( $this->stylesheet ) && ( ! is_wp_error( $this->errors ) || ! isset( $this->errors->errors['theme_paused'] ) ) ) {
   519 		if ( wp_paused_themes()->get( $this->stylesheet ) && ( ! is_wp_error( $this->errors ) || ! isset( $this->errors->errors['theme_paused'] ) ) ) {
   458 			$this->errors = new WP_Error( 'theme_paused', __( 'This theme failed to load properly and was paused within the admin backend.' ) );
   520 			$this->errors = new WP_Error( 'theme_paused', __( 'This theme failed to load properly and was paused within the admin backend.' ) );
   459 		}
   521 		}
   460 
   522 
   461 		// We're good. If we didn't retrieve from cache, set it.
   523 		// We're good. If we didn't retrieve from cache, set it.
   462 		if ( ! is_array( $cache ) ) {
   524 		if ( ! is_array( $cache ) ) {
   463 			$cache = array(
   525 			$cache = array(
   464 				'headers'    => $this->headers,
   526 				'block_theme'            => $this->is_block_theme(),
   465 				'errors'     => $this->errors,
   527 				'block_template_folders' => $this->get_block_template_folders(),
   466 				'stylesheet' => $this->stylesheet,
   528 				'headers'                => $this->headers,
   467 				'template'   => $this->template,
   529 				'errors'                 => $this->errors,
       
   530 				'stylesheet'             => $this->stylesheet,
       
   531 				'template'               => $this->template,
   468 			);
   532 			);
   469 			// If the parent theme is in another root, we'll want to cache this. Avoids an entire branch of filesystem calls above.
   533 			// If the parent theme is in another root, we'll want to cache this. Avoids an entire branch of filesystem calls above.
   470 			if ( isset( $theme_root_template ) ) {
   534 			if ( isset( $theme_root_template ) ) {
   471 				$cache['theme_root_template'] = $theme_root_template;
   535 				$cache['theme_root_template'] = $theme_root_template;
   472 			}
   536 			}
   712 	public function parent() {
   776 	public function parent() {
   713 		return isset( $this->parent ) ? $this->parent : false;
   777 		return isset( $this->parent ) ? $this->parent : false;
   714 	}
   778 	}
   715 
   779 
   716 	/**
   780 	/**
       
   781 	 * Perform reinitialization tasks.
       
   782 	 *
       
   783 	 * Prevents a callback from being injected during unserialization of an object.
       
   784 	 */
       
   785 	public function __wakeup() {
       
   786 		if ( $this->parent && ! $this->parent instanceof self ) {
       
   787 			throw new UnexpectedValueException();
       
   788 		}
       
   789 		if ( $this->headers && ! is_array( $this->headers ) ) {
       
   790 			throw new UnexpectedValueException();
       
   791 		}
       
   792 		foreach ( $this->headers as $value ) {
       
   793 			if ( ! is_string( $value ) ) {
       
   794 				throw new UnexpectedValueException();
       
   795 			}
       
   796 		}
       
   797 		$this->headers_sanitized = array();
       
   798 	}
       
   799 
       
   800 	/**
   717 	 * Adds theme data to cache.
   801 	 * Adds theme data to cache.
   718 	 *
   802 	 *
   719 	 * Cache entries keyed by the theme and the type of data.
   803 	 * Cache entries keyed by the theme and the type of data.
   720 	 *
   804 	 *
   721 	 * @since 3.4.0
   805 	 * @since 3.4.0
   749 	 */
   833 	 */
   750 	public function cache_delete() {
   834 	public function cache_delete() {
   751 		foreach ( array( 'theme', 'screenshot', 'headers', 'post_templates' ) as $key ) {
   835 		foreach ( array( 'theme', 'screenshot', 'headers', 'post_templates' ) as $key ) {
   752 			wp_cache_delete( $key . '-' . $this->cache_hash, 'themes' );
   836 			wp_cache_delete( $key . '-' . $this->cache_hash, 'themes' );
   753 		}
   837 		}
   754 		$this->template          = null;
   838 		$this->template               = null;
   755 		$this->textdomain_loaded = null;
   839 		$this->textdomain_loaded      = null;
   756 		$this->theme_root_uri    = null;
   840 		$this->theme_root_uri         = null;
   757 		$this->parent            = null;
   841 		$this->parent                 = null;
   758 		$this->errors            = null;
   842 		$this->errors                 = null;
   759 		$this->headers_sanitized = null;
   843 		$this->headers_sanitized      = null;
   760 		$this->name_translated   = null;
   844 		$this->name_translated        = null;
   761 		$this->headers           = array();
   845 		$this->block_theme            = null;
       
   846 		$this->block_template_folders = null;
       
   847 		$this->headers                = array();
   762 		$this->__construct( $this->stylesheet, $this->theme_root );
   848 		$this->__construct( $this->stylesheet, $this->theme_root );
       
   849 		$this->delete_pattern_cache();
   763 	}
   850 	}
   764 
   851 
   765 	/**
   852 	/**
   766 	 * Gets a raw, unformatted theme header.
   853 	 * Gets a raw, unformatted theme header.
   767 	 *
   854 	 *
   842 	/**
   929 	/**
   843 	 * Sanitizes a theme header.
   930 	 * Sanitizes a theme header.
   844 	 *
   931 	 *
   845 	 * @since 3.4.0
   932 	 * @since 3.4.0
   846 	 * @since 5.4.0 Added support for `Requires at least` and `Requires PHP` headers.
   933 	 * @since 5.4.0 Added support for `Requires at least` and `Requires PHP` headers.
       
   934 	 * @since 6.1.0 Added support for `Update URI` header.
   847 	 *
   935 	 *
   848 	 * @param string $header Theme header. Accepts 'Name', 'Description', 'Author', 'Version',
   936 	 * @param string $header Theme header. Accepts 'Name', 'Description', 'Author', 'Version',
   849 	 *                       'ThemeURI', 'AuthorURI', 'Status', 'Tags', 'RequiresWP', 'RequiresPHP'.
   937 	 *                       'ThemeURI', 'AuthorURI', 'Status', 'Tags', 'RequiresWP', 'RequiresPHP',
       
   938 	 *                       'UpdateURI'.
   850 	 * @param string $value  Value to sanitize.
   939 	 * @param string $value  Value to sanitize.
   851 	 * @return string|array An array for Tags header, string otherwise.
   940 	 * @return string|array An array for Tags header, string otherwise.
   852 	 */
   941 	 */
   853 	private function sanitize_header( $header, $value ) {
   942 	private function sanitize_header( $header, $value ) {
   854 		switch ( $header ) {
   943 		switch ( $header ) {
   886 
   975 
   887 				$value = wp_kses( $value, $header_tags_with_a );
   976 				$value = wp_kses( $value, $header_tags_with_a );
   888 				break;
   977 				break;
   889 			case 'ThemeURI':
   978 			case 'ThemeURI':
   890 			case 'AuthorURI':
   979 			case 'AuthorURI':
   891 				$value = esc_url_raw( $value );
   980 				$value = sanitize_url( $value );
   892 				break;
   981 				break;
   893 			case 'Tags':
   982 			case 'Tags':
   894 				$value = array_filter( array_map( 'trim', explode( ',', strip_tags( $value ) ) ) );
   983 				$value = array_filter( array_map( 'trim', explode( ',', strip_tags( $value ) ) ) );
   895 				break;
   984 				break;
   896 			case 'Version':
   985 			case 'Version':
   897 			case 'RequiresWP':
   986 			case 'RequiresWP':
   898 			case 'RequiresPHP':
   987 			case 'RequiresPHP':
       
   988 			case 'UpdateURI':
   899 				$value = strip_tags( $value );
   989 				$value = strip_tags( $value );
   900 				break;
   990 				break;
   901 		}
   991 		}
   902 
   992 
   903 		return $value;
   993 		return $value;
  1176 			return $this->get_stylesheet_directory_uri() . '/' . $screenshot;
  1266 			return $this->get_stylesheet_directory_uri() . '/' . $screenshot;
  1177 		} elseif ( 0 === $screenshot ) {
  1267 		} elseif ( 0 === $screenshot ) {
  1178 			return false;
  1268 			return false;
  1179 		}
  1269 		}
  1180 
  1270 
  1181 		foreach ( array( 'png', 'gif', 'jpg', 'jpeg', 'webp' ) as $ext ) {
  1271 		foreach ( array( 'png', 'gif', 'jpg', 'jpeg', 'webp', 'avif' ) as $ext ) {
  1182 			if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) {
  1272 			if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) {
  1183 				$this->cache_add( 'screenshot', 'screenshot.' . $ext );
  1273 				$this->cache_add( 'screenshot', 'screenshot.' . $ext );
  1184 				if ( 'relative' === $uri ) {
  1274 				if ( 'relative' === $uri ) {
  1185 					return 'screenshot.' . $ext;
  1275 					return 'screenshot.' . $ext;
  1186 				}
  1276 				}
  1255 
  1345 
  1256 					$post_templates[ $type ][ $file ] = _cleanup_header_comment( $header[1] );
  1346 					$post_templates[ $type ][ $file ] = _cleanup_header_comment( $header[1] );
  1257 				}
  1347 				}
  1258 			}
  1348 			}
  1259 
  1349 
  1260 			if ( current_theme_supports( 'block-templates' ) ) {
  1350 			$this->cache_add( 'post_templates', $post_templates );
  1261 				$block_templates = get_block_templates( array(), 'wp_template' );
  1351 		}
  1262 				foreach ( get_post_types( array( 'public' => true ) ) as $type ) {
  1352 
  1263 					foreach ( $block_templates as $block_template ) {
  1353 		if ( current_theme_supports( 'block-templates' ) ) {
  1264 						if ( ! $block_template->is_custom ) {
  1354 			$block_templates = get_block_templates( array(), 'wp_template' );
  1265 							continue;
  1355 			foreach ( get_post_types( array( 'public' => true ) ) as $type ) {
  1266 						}
  1356 				foreach ( $block_templates as $block_template ) {
  1267 
  1357 					if ( ! $block_template->is_custom ) {
  1268 						if ( isset( $block_template->post_types ) && ! in_array( $type, $block_template->post_types, true ) ) {
  1358 						continue;
  1269 							continue;
       
  1270 						}
       
  1271 
       
  1272 						$post_templates[ $type ][ $block_template->slug ] = $block_template->title;
       
  1273 					}
  1359 					}
       
  1360 
       
  1361 					if ( isset( $block_template->post_types ) && ! in_array( $type, $block_template->post_types, true ) ) {
       
  1362 						continue;
       
  1363 					}
       
  1364 
       
  1365 					$post_templates[ $type ][ $block_template->slug ] = $block_template->title;
  1274 				}
  1366 				}
  1275 			}
  1367 			}
  1276 
       
  1277 			$this->cache_add( 'post_templates', $post_templates );
       
  1278 		}
  1368 		}
  1279 
  1369 
  1280 		if ( $this->load_textdomain() ) {
  1370 		if ( $this->load_textdomain() ) {
  1281 			foreach ( $post_templates as &$post_type ) {
  1371 			foreach ( $post_templates as &$post_type ) {
  1282 				foreach ( $post_type as &$post_template ) {
  1372 				foreach ( $post_type as &$post_template ) {
  1482 	 * @since 5.9.0
  1572 	 * @since 5.9.0
  1483 	 *
  1573 	 *
  1484 	 * @return bool
  1574 	 * @return bool
  1485 	 */
  1575 	 */
  1486 	public function is_block_theme() {
  1576 	public function is_block_theme() {
       
  1577 		if ( isset( $this->block_theme ) ) {
       
  1578 			return $this->block_theme;
       
  1579 		}
       
  1580 
  1487 		$paths_to_index_block_template = array(
  1581 		$paths_to_index_block_template = array(
       
  1582 			$this->get_file_path( '/templates/index.html' ),
  1488 			$this->get_file_path( '/block-templates/index.html' ),
  1583 			$this->get_file_path( '/block-templates/index.html' ),
  1489 			$this->get_file_path( '/templates/index.html' ),
       
  1490 		);
  1584 		);
       
  1585 
       
  1586 		$this->block_theme = false;
  1491 
  1587 
  1492 		foreach ( $paths_to_index_block_template as $path_to_index_block_template ) {
  1588 		foreach ( $paths_to_index_block_template as $path_to_index_block_template ) {
  1493 			if ( is_file( $path_to_index_block_template ) && is_readable( $path_to_index_block_template ) ) {
  1589 			if ( is_file( $path_to_index_block_template ) && is_readable( $path_to_index_block_template ) ) {
  1494 				return true;
  1590 				$this->block_theme = true;
  1495 			}
  1591 				break;
  1496 		}
  1592 			}
  1497 
  1593 		}
  1498 		return false;
  1594 
       
  1595 		return $this->block_theme;
  1499 	}
  1596 	}
  1500 
  1597 
  1501 	/**
  1598 	/**
  1502 	 * Retrieves the path of a file in the theme.
  1599 	 * Retrieves the path of a file in the theme.
  1503 	 *
  1600 	 *
  1515 		$stylesheet_directory = $this->get_stylesheet_directory();
  1612 		$stylesheet_directory = $this->get_stylesheet_directory();
  1516 		$template_directory   = $this->get_template_directory();
  1613 		$template_directory   = $this->get_template_directory();
  1517 
  1614 
  1518 		if ( empty( $file ) ) {
  1615 		if ( empty( $file ) ) {
  1519 			$path = $stylesheet_directory;
  1616 			$path = $stylesheet_directory;
  1520 		} elseif ( file_exists( $stylesheet_directory . '/' . $file ) ) {
  1617 		} elseif ( $stylesheet_directory !== $template_directory && file_exists( $stylesheet_directory . '/' . $file ) ) {
  1521 			$path = $stylesheet_directory . '/' . $file;
  1618 			$path = $stylesheet_directory . '/' . $file;
  1522 		} else {
  1619 		} else {
  1523 			$path = $template_directory . '/' . $file;
  1620 			$path = $template_directory . '/' . $file;
  1524 		}
  1621 		}
  1525 
  1622 
  1620 			 * @param int      $blog_id        ID of the site. Defaults to current site.
  1717 			 * @param int      $blog_id        ID of the site. Defaults to current site.
  1621 			 */
  1718 			 */
  1622 			return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id );
  1719 			return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id );
  1623 		}
  1720 		}
  1624 
  1721 
  1625 		$current = get_current_blog_id() == $blog_id;
  1722 		$current = get_current_blog_id() === $blog_id;
  1626 
  1723 
  1627 		if ( $current ) {
  1724 		if ( $current ) {
  1628 			$allowed_themes[ $blog_id ] = get_option( 'allowedthemes' );
  1725 			$allowed_themes[ $blog_id ] = get_option( 'allowedthemes' );
  1629 		} else {
  1726 		} else {
  1630 			switch_to_blog( $blog_id );
  1727 			switch_to_blog( $blog_id );
  1631 			$allowed_themes[ $blog_id ] = get_option( 'allowedthemes' );
  1728 			$allowed_themes[ $blog_id ] = get_option( 'allowedthemes' );
  1632 			restore_current_blog();
  1729 			restore_current_blog();
  1633 		}
  1730 		}
  1634 
  1731 
  1635 		// This is all super old MU back compat joy.
  1732 		/*
  1636 		// 'allowedthemes' keys things by stylesheet. 'allowed_themes' keyed things by name.
  1733 		 * This is all super old MU back compat joy.
       
  1734 		 * 'allowedthemes' keys things by stylesheet. 'allowed_themes' keyed things by name.
       
  1735 		 */
  1637 		if ( false === $allowed_themes[ $blog_id ] ) {
  1736 		if ( false === $allowed_themes[ $blog_id ] ) {
  1638 			if ( $current ) {
  1737 			if ( $current ) {
  1639 				$allowed_themes[ $blog_id ] = get_option( 'allowed_themes' );
  1738 				$allowed_themes[ $blog_id ] = get_option( 'allowed_themes' );
  1640 			} else {
  1739 			} else {
  1641 				switch_to_blog( $blog_id );
  1740 				switch_to_blog( $blog_id );
  1672 		/** This filter is documented in wp-includes/class-wp-theme.php */
  1771 		/** This filter is documented in wp-includes/class-wp-theme.php */
  1673 		return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id );
  1772 		return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id );
  1674 	}
  1773 	}
  1675 
  1774 
  1676 	/**
  1775 	/**
       
  1776 	 * Returns the folder names of the block template directories.
       
  1777 	 *
       
  1778 	 * @since 6.4.0
       
  1779 	 *
       
  1780 	 * @return string[] {
       
  1781 	 *     Folder names used by block themes.
       
  1782 	 *
       
  1783 	 *     @type string $wp_template      Theme-relative directory name for block templates.
       
  1784 	 *     @type string $wp_template_part Theme-relative directory name for block template parts.
       
  1785 	 * }
       
  1786 	 */
       
  1787 	public function get_block_template_folders() {
       
  1788 		// Return set/cached value if available.
       
  1789 		if ( isset( $this->block_template_folders ) ) {
       
  1790 			return $this->block_template_folders;
       
  1791 		}
       
  1792 
       
  1793 		$this->block_template_folders = $this->default_template_folders;
       
  1794 
       
  1795 		$stylesheet_directory = $this->get_stylesheet_directory();
       
  1796 		// If the theme uses deprecated block template folders.
       
  1797 		if ( file_exists( $stylesheet_directory . '/block-templates' ) || file_exists( $stylesheet_directory . '/block-template-parts' ) ) {
       
  1798 			$this->block_template_folders = array(
       
  1799 				'wp_template'      => 'block-templates',
       
  1800 				'wp_template_part' => 'block-template-parts',
       
  1801 			);
       
  1802 		}
       
  1803 		return $this->block_template_folders;
       
  1804 	}
       
  1805 
       
  1806 	/**
       
  1807 	 * Gets block pattern data for a specified theme.
       
  1808 	 * Each pattern is defined as a PHP file and defines
       
  1809 	 * its metadata using plugin-style headers. The minimum required definition is:
       
  1810 	 *
       
  1811 	 *     /**
       
  1812 	 *      * Title: My Pattern
       
  1813 	 *      * Slug: my-theme/my-pattern
       
  1814 	 *      *
       
  1815 	 *
       
  1816 	 * The output of the PHP source corresponds to the content of the pattern, e.g.:
       
  1817 	 *
       
  1818 	 *     <main><p><?php echo "Hello"; ?></p></main>
       
  1819 	 *
       
  1820 	 * If applicable, this will collect from both parent and child theme.
       
  1821 	 *
       
  1822 	 * Other settable fields include:
       
  1823 	 *
       
  1824 	 *     - Description
       
  1825 	 *     - Viewport Width
       
  1826 	 *     - Inserter         (yes/no)
       
  1827 	 *     - Categories       (comma-separated values)
       
  1828 	 *     - Keywords         (comma-separated values)
       
  1829 	 *     - Block Types      (comma-separated values)
       
  1830 	 *     - Post Types       (comma-separated values)
       
  1831 	 *     - Template Types   (comma-separated values)
       
  1832 	 *
       
  1833 	 * @since 6.4.0
       
  1834 	 *
       
  1835 	 * @return array Block pattern data.
       
  1836 	 */
       
  1837 	public function get_block_patterns() {
       
  1838 		$can_use_cached = ! wp_is_development_mode( 'theme' );
       
  1839 
       
  1840 		$pattern_data = $this->get_pattern_cache();
       
  1841 		if ( is_array( $pattern_data ) ) {
       
  1842 			if ( $can_use_cached ) {
       
  1843 				return $pattern_data;
       
  1844 			}
       
  1845 			// If in development mode, clear pattern cache.
       
  1846 			$this->delete_pattern_cache();
       
  1847 		}
       
  1848 
       
  1849 		$dirpath      = $this->get_stylesheet_directory() . '/patterns/';
       
  1850 		$pattern_data = array();
       
  1851 
       
  1852 		if ( ! file_exists( $dirpath ) ) {
       
  1853 			if ( $can_use_cached ) {
       
  1854 				$this->set_pattern_cache( $pattern_data );
       
  1855 			}
       
  1856 			return $pattern_data;
       
  1857 		}
       
  1858 		$files = glob( $dirpath . '*.php' );
       
  1859 		if ( ! $files ) {
       
  1860 			if ( $can_use_cached ) {
       
  1861 				$this->set_pattern_cache( $pattern_data );
       
  1862 			}
       
  1863 			return $pattern_data;
       
  1864 		}
       
  1865 
       
  1866 		$default_headers = array(
       
  1867 			'title'         => 'Title',
       
  1868 			'slug'          => 'Slug',
       
  1869 			'description'   => 'Description',
       
  1870 			'viewportWidth' => 'Viewport Width',
       
  1871 			'inserter'      => 'Inserter',
       
  1872 			'categories'    => 'Categories',
       
  1873 			'keywords'      => 'Keywords',
       
  1874 			'blockTypes'    => 'Block Types',
       
  1875 			'postTypes'     => 'Post Types',
       
  1876 			'templateTypes' => 'Template Types',
       
  1877 		);
       
  1878 
       
  1879 		$properties_to_parse = array(
       
  1880 			'categories',
       
  1881 			'keywords',
       
  1882 			'blockTypes',
       
  1883 			'postTypes',
       
  1884 			'templateTypes',
       
  1885 		);
       
  1886 
       
  1887 		foreach ( $files as $file ) {
       
  1888 			$pattern = get_file_data( $file, $default_headers );
       
  1889 
       
  1890 			if ( empty( $pattern['slug'] ) ) {
       
  1891 				_doing_it_wrong(
       
  1892 					__FUNCTION__,
       
  1893 					sprintf(
       
  1894 						/* translators: 1: file name. */
       
  1895 						__( 'Could not register file "%s" as a block pattern ("Slug" field missing)' ),
       
  1896 						$file
       
  1897 					),
       
  1898 					'6.0.0'
       
  1899 				);
       
  1900 				continue;
       
  1901 			}
       
  1902 
       
  1903 			if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern['slug'] ) ) {
       
  1904 				_doing_it_wrong(
       
  1905 					__FUNCTION__,
       
  1906 					sprintf(
       
  1907 						/* translators: 1: file name; 2: slug value found. */
       
  1908 						__( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")' ),
       
  1909 						$file,
       
  1910 						$pattern['slug']
       
  1911 					),
       
  1912 					'6.0.0'
       
  1913 				);
       
  1914 			}
       
  1915 
       
  1916 			// Title is a required property.
       
  1917 			if ( ! $pattern['title'] ) {
       
  1918 				_doing_it_wrong(
       
  1919 					__FUNCTION__,
       
  1920 					sprintf(
       
  1921 						/* translators: 1: file name. */
       
  1922 						__( 'Could not register file "%s" as a block pattern ("Title" field missing)' ),
       
  1923 						$file
       
  1924 					),
       
  1925 					'6.0.0'
       
  1926 				);
       
  1927 				continue;
       
  1928 			}
       
  1929 
       
  1930 			// For properties of type array, parse data as comma-separated.
       
  1931 			foreach ( $properties_to_parse as $property ) {
       
  1932 				if ( ! empty( $pattern[ $property ] ) ) {
       
  1933 					$pattern[ $property ] = array_filter( wp_parse_list( (string) $pattern[ $property ] ) );
       
  1934 				} else {
       
  1935 					unset( $pattern[ $property ] );
       
  1936 				}
       
  1937 			}
       
  1938 
       
  1939 			// Parse properties of type int.
       
  1940 			$property = 'viewportWidth';
       
  1941 			if ( ! empty( $pattern[ $property ] ) ) {
       
  1942 				$pattern[ $property ] = (int) $pattern[ $property ];
       
  1943 			} else {
       
  1944 				unset( $pattern[ $property ] );
       
  1945 			}
       
  1946 
       
  1947 			// Parse properties of type bool.
       
  1948 			$property = 'inserter';
       
  1949 			if ( ! empty( $pattern[ $property ] ) ) {
       
  1950 				$pattern[ $property ] = in_array(
       
  1951 					strtolower( $pattern[ $property ] ),
       
  1952 					array( 'yes', 'true' ),
       
  1953 					true
       
  1954 				);
       
  1955 			} else {
       
  1956 				unset( $pattern[ $property ] );
       
  1957 			}
       
  1958 
       
  1959 			$key = str_replace( $dirpath, '', $file );
       
  1960 
       
  1961 			$pattern_data[ $key ] = $pattern;
       
  1962 		}
       
  1963 
       
  1964 		if ( $can_use_cached ) {
       
  1965 			$this->set_pattern_cache( $pattern_data );
       
  1966 		}
       
  1967 
       
  1968 		return $pattern_data;
       
  1969 	}
       
  1970 
       
  1971 	/**
       
  1972 	 * Gets block pattern cache.
       
  1973 	 *
       
  1974 	 * @since 6.4.0
       
  1975 	 * @since 6.6.0 Uses transients to cache regardless of site environment.
       
  1976 	 *
       
  1977 	 * @return array|false Returns an array of patterns if cache is found, otherwise false.
       
  1978 	 */
       
  1979 	private function get_pattern_cache() {
       
  1980 		if ( ! $this->exists() ) {
       
  1981 			return false;
       
  1982 		}
       
  1983 
       
  1984 		$pattern_data = get_site_transient( 'wp_theme_files_patterns-' . $this->cache_hash );
       
  1985 
       
  1986 		if ( is_array( $pattern_data ) && $pattern_data['version'] === $this->get( 'Version' ) ) {
       
  1987 			return $pattern_data['patterns'];
       
  1988 		}
       
  1989 		return false;
       
  1990 	}
       
  1991 
       
  1992 	/**
       
  1993 	 * Sets block pattern cache.
       
  1994 	 *
       
  1995 	 * @since 6.4.0
       
  1996 	 * @since 6.6.0 Uses transients to cache regardless of site environment.
       
  1997 	 *
       
  1998 	 * @param array $patterns Block patterns data to set in cache.
       
  1999 	 */
       
  2000 	private function set_pattern_cache( array $patterns ) {
       
  2001 		$pattern_data = array(
       
  2002 			'version'  => $this->get( 'Version' ),
       
  2003 			'patterns' => $patterns,
       
  2004 		);
       
  2005 
       
  2006 		/**
       
  2007 		 * Filters the cache expiration time for theme files.
       
  2008 		 *
       
  2009 		 * @since 6.6.0
       
  2010 		 *
       
  2011 		 * @param int    $cache_expiration Cache expiration time in seconds.
       
  2012 		 * @param string $cache_type       Type of cache being set.
       
  2013 		 */
       
  2014 		$cache_expiration = (int) apply_filters( 'wp_theme_files_cache_ttl', self::$cache_expiration, 'theme_block_patterns' );
       
  2015 
       
  2016 		// We don't want to cache patterns infinitely.
       
  2017 		if ( $cache_expiration <= 0 ) {
       
  2018 			_doing_it_wrong(
       
  2019 				__METHOD__,
       
  2020 				sprintf(
       
  2021 					/* translators: %1$s: The filter name.*/
       
  2022 					__( 'The %1$s filter must return an integer value greater than 0.' ),
       
  2023 					'<code>wp_theme_files_cache_ttl</code>'
       
  2024 				),
       
  2025 				'6.6.0'
       
  2026 			);
       
  2027 
       
  2028 			$cache_expiration = self::$cache_expiration;
       
  2029 		}
       
  2030 
       
  2031 		set_site_transient( 'wp_theme_files_patterns-' . $this->cache_hash, $pattern_data, $cache_expiration );
       
  2032 	}
       
  2033 
       
  2034 	/**
       
  2035 	 * Clears block pattern cache.
       
  2036 	 *
       
  2037 	 * @since 6.4.0
       
  2038 	 * @since 6.6.0 Uses transients to cache regardless of site environment.
       
  2039 	 */
       
  2040 	public function delete_pattern_cache() {
       
  2041 		delete_site_transient( 'wp_theme_files_patterns-' . $this->cache_hash );
       
  2042 	}
       
  2043 
       
  2044 	/**
  1677 	 * Enables a theme for all sites on the current network.
  2045 	 * Enables a theme for all sites on the current network.
  1678 	 *
  2046 	 *
  1679 	 * @since 4.6.0
  2047 	 * @since 4.6.0
  1680 	 *
  2048 	 *
  1681 	 * @param string|string[] $stylesheets Stylesheet name or array of stylesheet names.
  2049 	 * @param string|string[] $stylesheets Stylesheet name or array of stylesheet names.
  1729 	 * @since 3.4.0
  2097 	 * @since 3.4.0
  1730 	 *
  2098 	 *
  1731 	 * @param WP_Theme[] $themes Array of theme objects to sort (passed by reference).
  2099 	 * @param WP_Theme[] $themes Array of theme objects to sort (passed by reference).
  1732 	 */
  2100 	 */
  1733 	public static function sort_by_name( &$themes ) {
  2101 	public static function sort_by_name( &$themes ) {
  1734 		if ( 0 === strpos( get_user_locale(), 'en_' ) ) {
  2102 		if ( str_starts_with( get_user_locale(), 'en_' ) ) {
  1735 			uasort( $themes, array( 'WP_Theme', '_name_sort' ) );
  2103 			uasort( $themes, array( 'WP_Theme', '_name_sort' ) );
  1736 		} else {
  2104 		} else {
  1737 			foreach ( $themes as $key => $theme ) {
  2105 			foreach ( $themes as $key => $theme ) {
  1738 				$theme->translate_header( 'Name', $theme->headers['Name'] );
  2106 				$theme->translate_header( 'Name', $theme->headers['Name'] );
  1739 			}
  2107 			}
  1769 	 *             Greater than 0 if `$a` falls higher in the natural order than `$b`. Used with usort().
  2137 	 *             Greater than 0 if `$a` falls higher in the natural order than `$b`. Used with usort().
  1770 	 */
  2138 	 */
  1771 	private static function _name_sort_i18n( $a, $b ) {
  2139 	private static function _name_sort_i18n( $a, $b ) {
  1772 		return strnatcasecmp( $a->name_translated, $b->name_translated );
  2140 		return strnatcasecmp( $a->name_translated, $b->name_translated );
  1773 	}
  2141 	}
       
  2142 
       
  2143 	private static function _check_headers_property_has_correct_type( $headers ) {
       
  2144 		if ( ! is_array( $headers ) ) {
       
  2145 			return false;
       
  2146 		}
       
  2147 		foreach ( $headers as $key => $value ) {
       
  2148 			if ( ! is_string( $key ) || ! is_string( $value ) ) {
       
  2149 				return false;
       
  2150 			}
       
  2151 		}
       
  2152 		return true;
       
  2153 	}
  1774 }
  2154 }