[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WP_Theme_JSON_Resolver class 4 * 5 * @package WordPress 6 * @subpackage Theme 7 * @since 5.8.0 8 */ 9 10 /** 11 * Class that abstracts the processing of the different data sources 12 * for site-level config and offers an API to work with them. 13 * 14 * This class is for internal core usage and is not supposed to be used by extenders (plugins and/or themes). 15 * This is a low-level API that may need to do breaking changes. Please, 16 * use get_global_settings(), get_global_styles(), and get_global_stylesheet() instead. 17 * 18 * @access private 19 */ 20 #[AllowDynamicProperties] 21 class WP_Theme_JSON_Resolver { 22 23 /** 24 * Container for keep track of registered blocks. 25 * 26 * @since 6.1.0 27 * @var array 28 */ 29 protected static $blocks_cache = array( 30 'core' => array(), 31 'blocks' => array(), 32 'theme' => array(), 33 'user' => array(), 34 ); 35 36 /** 37 * Container for data coming from core. 38 * 39 * @since 5.8.0 40 * @var WP_Theme_JSON 41 */ 42 protected static $core = null; 43 44 /** 45 * Container for data coming from the blocks. 46 * 47 * @since 6.1.0 48 * @var WP_Theme_JSON 49 */ 50 protected static $blocks = null; 51 52 /** 53 * Container for data coming from the theme. 54 * 55 * @since 5.8.0 56 * @var WP_Theme_JSON 57 */ 58 protected static $theme = null; 59 60 /** 61 * Container for data coming from the user. 62 * 63 * @since 5.9.0 64 * @var WP_Theme_JSON 65 */ 66 protected static $user = null; 67 68 /** 69 * Stores the ID of the custom post type 70 * that holds the user data. 71 * 72 * @since 5.9.0 73 * @var int 74 */ 75 protected static $user_custom_post_type_id = null; 76 77 /** 78 * Container to keep loaded i18n schema for `theme.json`. 79 * 80 * @since 5.8.0 As `$theme_json_i18n`. 81 * @since 5.9.0 Renamed from `$theme_json_i18n` to `$i18n_schema`. 82 * @var array 83 */ 84 protected static $i18n_schema = null; 85 86 /** 87 * `theme.json` file cache. 88 * 89 * @since 6.1.0 90 * @var array 91 */ 92 protected static $theme_json_file_cache = array(); 93 94 /** 95 * Processes a file that adheres to the theme.json schema 96 * and returns an array with its contents, or a void array if none found. 97 * 98 * @since 5.8.0 99 * @since 6.1.0 Added caching. 100 * 101 * @param string $file_path Path to file. Empty if no file. 102 * @return array Contents that adhere to the theme.json schema. 103 */ 104 protected static function read_json_file( $file_path ) { 105 if ( $file_path ) { 106 if ( array_key_exists( $file_path, static::$theme_json_file_cache ) ) { 107 return static::$theme_json_file_cache[ $file_path ]; 108 } 109 110 $decoded_file = wp_json_file_decode( $file_path, array( 'associative' => true ) ); 111 if ( is_array( $decoded_file ) ) { 112 static::$theme_json_file_cache[ $file_path ] = $decoded_file; 113 return static::$theme_json_file_cache[ $file_path ]; 114 } 115 } 116 117 return array(); 118 } 119 120 /** 121 * Returns a data structure used in theme.json translation. 122 * 123 * @since 5.8.0 124 * @deprecated 5.9.0 125 * 126 * @return array An array of theme.json fields that are translatable and the keys that are translatable. 127 */ 128 public static function get_fields_to_translate() { 129 _deprecated_function( __METHOD__, '5.9.0' ); 130 return array(); 131 } 132 133 /** 134 * Given a theme.json structure modifies it in place to update certain values 135 * by its translated strings according to the language set by the user. 136 * 137 * @since 5.8.0 138 * 139 * @param array $theme_json The theme.json to translate. 140 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 141 * Default 'default'. 142 * @return array Returns the modified $theme_json_structure. 143 */ 144 protected static function translate( $theme_json, $domain = 'default' ) { 145 if ( null === static::$i18n_schema ) { 146 $i18n_schema = wp_json_file_decode( __DIR__ . '/theme-i18n.json' ); 147 static::$i18n_schema = null === $i18n_schema ? array() : $i18n_schema; 148 } 149 150 return translate_settings_using_i18n_schema( static::$i18n_schema, $theme_json, $domain ); 151 } 152 153 /** 154 * Returns core's origin config. 155 * 156 * @since 5.8.0 157 * 158 * @return WP_Theme_JSON Entity that holds core data. 159 */ 160 public static function get_core_data() { 161 if ( null !== static::$core && static::has_same_registered_blocks( 'core' ) ) { 162 return static::$core; 163 } 164 165 $config = static::read_json_file( __DIR__ . '/theme.json' ); 166 $config = static::translate( $config ); 167 168 /** 169 * Filters the default data provided by WordPress for global styles & settings. 170 * 171 * @since 6.1.0 172 * 173 * @param WP_Theme_JSON_Data $theme_json Class to access and update the underlying data. 174 */ 175 $theme_json = apply_filters( 'wp_theme_json_data_default', new WP_Theme_JSON_Data( $config, 'default' ) ); 176 $config = $theme_json->get_data(); 177 static::$core = new WP_Theme_JSON( $config, 'default' ); 178 179 return static::$core; 180 } 181 182 /** 183 * Checks whether the registered blocks were already processed for this origin. 184 * 185 * @since 6.1.0 186 * 187 * @param string $origin Data source for which to cache the blocks. 188 * Valid values are 'core', 'blocks', 'theme', and 'user'. 189 * @return bool True on success, false otherwise. 190 */ 191 protected static function has_same_registered_blocks( $origin ) { 192 // Bail out if the origin is invalid. 193 if ( ! isset( static::$blocks_cache[ $origin ] ) ) { 194 return false; 195 } 196 197 $registry = WP_Block_Type_Registry::get_instance(); 198 $blocks = $registry->get_all_registered(); 199 200 // Is there metadata for all currently registered blocks? 201 $block_diff = array_diff_key( $blocks, static::$blocks_cache[ $origin ] ); 202 if ( empty( $block_diff ) ) { 203 return true; 204 } 205 206 foreach ( $blocks as $block_name => $block_type ) { 207 static::$blocks_cache[ $origin ][ $block_name ] = true; 208 } 209 210 return false; 211 } 212 213 /** 214 * Returns the theme's data. 215 * 216 * Data from theme.json will be backfilled from existing 217 * theme supports, if any. Note that if the same data 218 * is present in theme.json and in theme supports, 219 * the theme.json takes precedence. 220 * 221 * @since 5.8.0 222 * @since 5.9.0 Theme supports have been inlined and the `$theme_support_data` argument removed. 223 * @since 6.0.0 Added an `$options` parameter to allow the theme data to be returned without theme supports. 224 * 225 * @param array $deprecated Deprecated. Not used. 226 * @param array $options { 227 * Options arguments. 228 * 229 * @type bool $with_supports Whether to include theme supports in the data. Default true. 230 * } 231 * @return WP_Theme_JSON Entity that holds theme data. 232 */ 233 public static function get_theme_data( $deprecated = array(), $options = array() ) { 234 if ( ! empty( $deprecated ) ) { 235 _deprecated_argument( __METHOD__, '5.9.0' ); 236 } 237 238 $options = wp_parse_args( $options, array( 'with_supports' => true ) ); 239 240 if ( null === static::$theme || ! static::has_same_registered_blocks( 'theme' ) ) { 241 $wp_theme = wp_get_theme(); 242 $theme_json_file = $wp_theme->get_file_path( 'theme.json' ); 243 if ( is_readable( $theme_json_file ) ) { 244 $theme_json_data = static::read_json_file( $theme_json_file ); 245 $theme_json_data = static::translate( $theme_json_data, $wp_theme->get( 'TextDomain' ) ); 246 } else { 247 $theme_json_data = array(); 248 } 249 250 /** 251 * Filters the data provided by the theme for global styles and settings. 252 * 253 * @since 6.1.0 254 * 255 * @param WP_Theme_JSON_Data $theme_json Class to access and update the underlying data. 256 */ 257 $theme_json = apply_filters( 'wp_theme_json_data_theme', new WP_Theme_JSON_Data( $theme_json_data, 'theme' ) ); 258 $theme_json_data = $theme_json->get_data(); 259 static::$theme = new WP_Theme_JSON( $theme_json_data ); 260 261 if ( $wp_theme->parent() ) { 262 // Get parent theme.json. 263 $parent_theme_json_file = $wp_theme->parent()->get_file_path( 'theme.json' ); 264 if ( $theme_json_file !== $parent_theme_json_file && is_readable( $parent_theme_json_file ) ) { 265 $parent_theme_json_data = static::read_json_file( $parent_theme_json_file ); 266 $parent_theme_json_data = static::translate( $parent_theme_json_data, $wp_theme->parent()->get( 'TextDomain' ) ); 267 $parent_theme = new WP_Theme_JSON( $parent_theme_json_data ); 268 269 /* 270 * Merge the child theme.json into the parent theme.json. 271 * The child theme takes precedence over the parent. 272 */ 273 $parent_theme->merge( static::$theme ); 274 static::$theme = $parent_theme; 275 } 276 } 277 } 278 279 if ( ! $options['with_supports'] ) { 280 return static::$theme; 281 } 282 283 /* 284 * We want the presets and settings declared in theme.json 285 * to override the ones declared via theme supports. 286 * So we take theme supports, transform it to theme.json shape 287 * and merge the static::$theme upon that. 288 */ 289 $theme_support_data = WP_Theme_JSON::get_from_editor_settings( get_classic_theme_supports_block_editor_settings() ); 290 if ( ! wp_theme_has_theme_json() ) { 291 if ( ! isset( $theme_support_data['settings']['color'] ) ) { 292 $theme_support_data['settings']['color'] = array(); 293 } 294 295 $default_palette = false; 296 if ( current_theme_supports( 'default-color-palette' ) ) { 297 $default_palette = true; 298 } 299 if ( ! isset( $theme_support_data['settings']['color']['palette'] ) ) { 300 // If the theme does not have any palette, we still want to show the core one. 301 $default_palette = true; 302 } 303 $theme_support_data['settings']['color']['defaultPalette'] = $default_palette; 304 305 $default_gradients = false; 306 if ( current_theme_supports( 'default-gradient-presets' ) ) { 307 $default_gradients = true; 308 } 309 if ( ! isset( $theme_support_data['settings']['color']['gradients'] ) ) { 310 // If the theme does not have any gradients, we still want to show the core ones. 311 $default_gradients = true; 312 } 313 $theme_support_data['settings']['color']['defaultGradients'] = $default_gradients; 314 315 if ( ! isset( $theme_support_data['settings']['shadow'] ) ) { 316 $theme_support_data['settings']['shadow'] = array(); 317 } 318 /* 319 * Shadow presets are explicitly disabled for classic themes until a 320 * decision is made for whether the default presets should match the 321 * other presets or if they should be disabled by default in classic 322 * themes. See https://github.com/WordPress/gutenberg/issues/59989. 323 */ 324 $theme_support_data['settings']['shadow']['defaultPresets'] = false; 325 326 // Allow themes to enable link color setting via theme_support. 327 if ( current_theme_supports( 'link-color' ) ) { 328 $theme_support_data['settings']['color']['link'] = true; 329 } 330 331 // Allow themes to enable all border settings via theme_support. 332 if ( current_theme_supports( 'border' ) ) { 333 $theme_support_data['settings']['border']['color'] = true; 334 $theme_support_data['settings']['border']['radius'] = true; 335 $theme_support_data['settings']['border']['style'] = true; 336 $theme_support_data['settings']['border']['width'] = true; 337 } 338 339 // Allow themes to enable appearance tools via theme_support. 340 if ( current_theme_supports( 'appearance-tools' ) ) { 341 $theme_support_data['settings']['appearanceTools'] = true; 342 } 343 } 344 $with_theme_supports = new WP_Theme_JSON( $theme_support_data ); 345 $with_theme_supports->merge( static::$theme ); 346 return $with_theme_supports; 347 } 348 349 /** 350 * Gets the styles for blocks from the block.json file. 351 * 352 * @since 6.1.0 353 * 354 * @return WP_Theme_JSON 355 */ 356 public static function get_block_data() { 357 $registry = WP_Block_Type_Registry::get_instance(); 358 $blocks = $registry->get_all_registered(); 359 360 if ( null !== static::$blocks && static::has_same_registered_blocks( 'blocks' ) ) { 361 return static::$blocks; 362 } 363 364 $config = array( 'version' => 2 ); 365 foreach ( $blocks as $block_name => $block_type ) { 366 if ( isset( $block_type->supports['__experimentalStyle'] ) ) { 367 $config['styles']['blocks'][ $block_name ] = static::remove_json_comments( $block_type->supports['__experimentalStyle'] ); 368 } 369 370 if ( 371 isset( $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ) && 372 ! isset( $config['styles']['blocks'][ $block_name ]['spacing']['blockGap'] ) 373 ) { 374 /* 375 * Ensure an empty placeholder value exists for the block, if it provides a default blockGap value. 376 * The real blockGap value to be used will be determined when the styles are rendered for output. 377 */ 378 $config['styles']['blocks'][ $block_name ]['spacing']['blockGap'] = null; 379 } 380 } 381 382 /** 383 * Filters the data provided by the blocks for global styles & settings. 384 * 385 * @since 6.1.0 386 * 387 * @param WP_Theme_JSON_Data $theme_json Class to access and update the underlying data. 388 */ 389 $theme_json = apply_filters( 'wp_theme_json_data_blocks', new WP_Theme_JSON_Data( $config, 'blocks' ) ); 390 $config = $theme_json->get_data(); 391 392 static::$blocks = new WP_Theme_JSON( $config, 'blocks' ); 393 return static::$blocks; 394 } 395 396 /** 397 * When given an array, this will remove any keys with the name `//`. 398 * 399 * @since 6.1.0 400 * 401 * @param array $input_array The array to filter. 402 * @return array The filtered array. 403 */ 404 private static function remove_json_comments( $input_array ) { 405 unset( $input_array['//'] ); 406 foreach ( $input_array as $k => $v ) { 407 if ( is_array( $v ) ) { 408 $input_array[ $k ] = static::remove_json_comments( $v ); 409 } 410 } 411 412 return $input_array; 413 } 414 415 /** 416 * Returns the custom post type that contains the user's origin config 417 * for the active theme or an empty array if none are found. 418 * 419 * This can also create and return a new draft custom post type. 420 * 421 * @since 5.9.0 422 * 423 * @param WP_Theme $theme The theme object. If empty, it 424 * defaults to the active theme. 425 * @param bool $create_post Optional. Whether a new custom post 426 * type should be created if none are 427 * found. Default false. 428 * @param array $post_status_filter Optional. Filter custom post type by 429 * post status. Default `array( 'publish' )`, 430 * so it only fetches published posts. 431 * @return array Custom Post Type for the user's origin config. 432 */ 433 public static function get_user_data_from_wp_global_styles( $theme, $create_post = false, $post_status_filter = array( 'publish' ) ) { 434 if ( ! $theme instanceof WP_Theme ) { 435 $theme = wp_get_theme(); 436 } 437 438 /* 439 * Bail early if the theme does not support a theme.json. 440 * 441 * Since wp_theme_has_theme_json() only supports the active 442 * theme, the extra condition for whether $theme is the active theme is 443 * present here. 444 */ 445 if ( $theme->get_stylesheet() === get_stylesheet() && ! wp_theme_has_theme_json() ) { 446 return array(); 447 } 448 449 $user_cpt = array(); 450 $post_type_filter = 'wp_global_styles'; 451 $stylesheet = $theme->get_stylesheet(); 452 $args = array( 453 'posts_per_page' => 1, 454 'orderby' => 'date', 455 'order' => 'desc', 456 'post_type' => $post_type_filter, 457 'post_status' => $post_status_filter, 458 'ignore_sticky_posts' => true, 459 'no_found_rows' => true, 460 'update_post_meta_cache' => false, 461 'update_post_term_cache' => false, 462 'tax_query' => array( 463 array( 464 'taxonomy' => 'wp_theme', 465 'field' => 'name', 466 'terms' => $stylesheet, 467 ), 468 ), 469 ); 470 471 $global_style_query = new WP_Query(); 472 $recent_posts = $global_style_query->query( $args ); 473 if ( count( $recent_posts ) === 1 ) { 474 $user_cpt = get_object_vars( $recent_posts[0] ); 475 } elseif ( $create_post ) { 476 $cpt_post_id = wp_insert_post( 477 array( 478 'post_content' => '{"version": ' . WP_Theme_JSON::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', 479 'post_status' => 'publish', 480 'post_title' => 'Custom Styles', // Do not make string translatable, see https://core.trac.wordpress.org/ticket/54518. 481 'post_type' => $post_type_filter, 482 'post_name' => sprintf( 'wp-global-styles-%s', urlencode( $stylesheet ) ), 483 'tax_input' => array( 484 'wp_theme' => array( $stylesheet ), 485 ), 486 ), 487 true 488 ); 489 if ( ! is_wp_error( $cpt_post_id ) ) { 490 $user_cpt = get_object_vars( get_post( $cpt_post_id ) ); 491 } 492 } 493 494 return $user_cpt; 495 } 496 497 /** 498 * Returns the user's origin config. 499 * 500 * @since 5.9.0 501 * 502 * @return WP_Theme_JSON Entity that holds styles for user data. 503 */ 504 public static function get_user_data() { 505 if ( null !== static::$user && static::has_same_registered_blocks( 'user' ) ) { 506 return static::$user; 507 } 508 509 $config = array(); 510 $user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme() ); 511 512 if ( array_key_exists( 'post_content', $user_cpt ) ) { 513 $decoded_data = json_decode( $user_cpt['post_content'], true ); 514 515 $json_decoding_error = json_last_error(); 516 if ( JSON_ERROR_NONE !== $json_decoding_error ) { 517 trigger_error( 'Error when decoding a theme.json schema for user data. ' . json_last_error_msg() ); 518 /** 519 * Filters the data provided by the user for global styles & settings. 520 * 521 * @since 6.1.0 522 * 523 * @param WP_Theme_JSON_Data $theme_json Class to access and update the underlying data. 524 */ 525 $theme_json = apply_filters( 'wp_theme_json_data_user', new WP_Theme_JSON_Data( $config, 'custom' ) ); 526 $config = $theme_json->get_data(); 527 return new WP_Theme_JSON( $config, 'custom' ); 528 } 529 530 /* 531 * Very important to verify that the flag isGlobalStylesUserThemeJSON is true. 532 * If it's not true then the content was not escaped and is not safe. 533 */ 534 if ( 535 is_array( $decoded_data ) && 536 isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) && 537 $decoded_data['isGlobalStylesUserThemeJSON'] 538 ) { 539 unset( $decoded_data['isGlobalStylesUserThemeJSON'] ); 540 $config = $decoded_data; 541 } 542 } 543 544 /** This filter is documented in wp-includes/class-wp-theme-json-resolver.php */ 545 $theme_json = apply_filters( 'wp_theme_json_data_user', new WP_Theme_JSON_Data( $config, 'custom' ) ); 546 $config = $theme_json->get_data(); 547 static::$user = new WP_Theme_JSON( $config, 'custom' ); 548 549 return static::$user; 550 } 551 552 /** 553 * Returns the data merged from multiple origins. 554 * 555 * There are four sources of data (origins) for a site: 556 * 557 * - default => WordPress 558 * - blocks => each one of the blocks provides data for itself 559 * - theme => the active theme 560 * - custom => data provided by the user 561 * 562 * The custom's has higher priority than the theme's, the theme's higher than blocks', 563 * and block's higher than default's. 564 * 565 * Unlike the getters 566 * {@link https://developer.wordpress.org/reference/classes/wp_theme_json_resolver/get_core_data/ get_core_data}, 567 * {@link https://developer.wordpress.org/reference/classes/wp_theme_json_resolver/get_theme_data/ get_theme_data}, 568 * and {@link https://developer.wordpress.org/reference/classes/wp_theme_json_resolver/get_user_data/ get_user_data}, 569 * this method returns data after it has been merged with the previous origins. 570 * This means that if the same piece of data is declared in different origins 571 * (default, blocks, theme, custom), the last origin overrides the previous. 572 * 573 * For example, if the user has set a background color 574 * for the paragraph block, and the theme has done it as well, 575 * the user preference wins. 576 * 577 * @since 5.8.0 578 * @since 5.9.0 Added user data, removed the `$settings` parameter, 579 * added the `$origin` parameter. 580 * @since 6.1.0 Added block data and generation of spacingSizes array. 581 * @since 6.2.0 Changed ' $origin' parameter values to 'default', 'blocks', 'theme' or 'custom'. 582 * 583 * @param string $origin Optional. To what level should we merge data: 'default', 'blocks', 'theme' or 'custom'. 584 * 'custom' is used as default value as well as fallback value if the origin is unknown. 585 * @return WP_Theme_JSON 586 */ 587 public static function get_merged_data( $origin = 'custom' ) { 588 if ( is_array( $origin ) ) { 589 _deprecated_argument( __FUNCTION__, '5.9.0' ); 590 } 591 592 $result = new WP_Theme_JSON(); 593 $result->merge( static::get_core_data() ); 594 if ( 'default' === $origin ) { 595 $result->set_spacing_sizes(); 596 return $result; 597 } 598 599 $result->merge( static::get_block_data() ); 600 if ( 'blocks' === $origin ) { 601 return $result; 602 } 603 604 $result->merge( static::get_theme_data() ); 605 if ( 'theme' === $origin ) { 606 $result->set_spacing_sizes(); 607 return $result; 608 } 609 610 $result->merge( static::get_user_data() ); 611 $result->set_spacing_sizes(); 612 613 return $result; 614 } 615 616 /** 617 * Returns the ID of the custom post type 618 * that stores user data. 619 * 620 * @since 5.9.0 621 * 622 * @return integer|null 623 */ 624 public static function get_user_global_styles_post_id() { 625 if ( null !== static::$user_custom_post_type_id ) { 626 return static::$user_custom_post_type_id; 627 } 628 629 $user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme(), true ); 630 631 if ( array_key_exists( 'ID', $user_cpt ) ) { 632 static::$user_custom_post_type_id = $user_cpt['ID']; 633 } 634 635 return static::$user_custom_post_type_id; 636 } 637 638 /** 639 * Determines whether the active theme has a theme.json file. 640 * 641 * @since 5.8.0 642 * @since 5.9.0 Added a check in the parent theme. 643 * @deprecated 6.2.0 Use wp_theme_has_theme_json() instead. 644 * 645 * @return bool 646 */ 647 public static function theme_has_support() { 648 _deprecated_function( __METHOD__, '6.2.0', 'wp_theme_has_theme_json()' ); 649 650 return wp_theme_has_theme_json(); 651 } 652 653 /** 654 * Builds the path to the given file and checks that it is readable. 655 * 656 * If it isn't, returns an empty string, otherwise returns the whole file path. 657 * 658 * @since 5.8.0 659 * @since 5.9.0 Adapted to work with child themes, added the `$template` argument. 660 * 661 * @param string $file_name Name of the file. 662 * @param bool $template Optional. Use template theme directory. Default false. 663 * @return string The whole file path or empty if the file doesn't exist. 664 */ 665 protected static function get_file_path_from_theme( $file_name, $template = false ) { 666 $path = $template ? get_template_directory() : get_stylesheet_directory(); 667 $candidate = $path . '/' . $file_name; 668 669 return is_readable( $candidate ) ? $candidate : ''; 670 } 671 672 /** 673 * Cleans the cached data so it can be recalculated. 674 * 675 * @since 5.8.0 676 * @since 5.9.0 Added the `$user`, `$user_custom_post_type_id`, 677 * and `$i18n_schema` variables to reset. 678 * @since 6.1.0 Added the `$blocks` and `$blocks_cache` variables 679 * to reset. 680 */ 681 public static function clean_cached_data() { 682 static::$core = null; 683 static::$blocks = null; 684 static::$blocks_cache = array( 685 'core' => array(), 686 'blocks' => array(), 687 'theme' => array(), 688 'user' => array(), 689 ); 690 static::$theme = null; 691 static::$user = null; 692 static::$user_custom_post_type_id = null; 693 static::$i18n_schema = null; 694 } 695 696 /** 697 * Returns an array of all nested JSON files within a given directory. 698 * 699 * @since 6.2.0 700 * 701 * @param string $dir The directory to recursively iterate and list files of. 702 * @return array The merged array. 703 */ 704 private static function recursively_iterate_json( $dir ) { 705 $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ) ); 706 $nested_json_files = iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ); 707 return $nested_json_files; 708 } 709 710 711 /** 712 * Returns the style variations defined by the theme. 713 * 714 * @since 6.0.0 715 * @since 6.2.0 Returns parent theme variations if theme is a child. 716 * 717 * @return array 718 */ 719 public static function get_style_variations() { 720 $variation_files = array(); 721 $variations = array(); 722 $base_directory = get_stylesheet_directory() . '/styles'; 723 $template_directory = get_template_directory() . '/styles'; 724 if ( is_dir( $base_directory ) ) { 725 $variation_files = static::recursively_iterate_json( $base_directory ); 726 } 727 if ( is_dir( $template_directory ) && $template_directory !== $base_directory ) { 728 $variation_files_parent = static::recursively_iterate_json( $template_directory ); 729 // If the child and parent variation file basename are the same, only include the child theme's. 730 foreach ( $variation_files_parent as $parent_path => $parent ) { 731 foreach ( $variation_files as $child_path => $child ) { 732 if ( basename( $parent_path ) === basename( $child_path ) ) { 733 unset( $variation_files_parent[ $parent_path ] ); 734 } 735 } 736 } 737 $variation_files = array_merge( $variation_files, $variation_files_parent ); 738 } 739 ksort( $variation_files ); 740 foreach ( $variation_files as $path => $file ) { 741 $decoded_file = wp_json_file_decode( $path, array( 'associative' => true ) ); 742 if ( is_array( $decoded_file ) ) { 743 $translated = static::translate( $decoded_file, wp_get_theme()->get( 'TextDomain' ) ); 744 $variation = ( new WP_Theme_JSON( $translated ) )->get_raw_data(); 745 if ( empty( $variation['title'] ) ) { 746 $variation['title'] = basename( $path, '.json' ); 747 } 748 $variations[] = $variation; 749 } 750 } 751 return $variations; 752 } 753 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Fri Apr 26 08:20:02 2024 | Cross-referenced by PHPXref |