| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * APIs to interact with global settings & styles. 4 * 5 * @package WordPress 6 */ 7 8 /** 9 * Gets the settings resulting of merging core, theme, and user data. 10 * 11 * @since 5.9.0 12 * 13 * @param array $path Path to the specific setting to retrieve. Optional. 14 * If empty, will return all settings. 15 * @param array $context { 16 * Metadata to know where to retrieve the $path from. Optional. 17 * 18 * @type string $block_name Which block to retrieve the settings from. 19 * If empty, it'll return the settings for the global context. 20 * @type string $origin Which origin to take data from. 21 * Valid values are 'all' (core, theme, and user) or 'base' (core and theme). 22 * If empty or unknown, 'all' is used. 23 * } 24 * @return mixed The settings array or individual setting value to retrieve. 25 */ 26 function wp_get_global_settings( $path = array(), $context = array() ) { 27 if ( ! empty( $context['block_name'] ) ) { 28 $new_path = array( 'blocks', $context['block_name'] ); 29 foreach ( $path as $subpath ) { 30 $new_path[] = $subpath; 31 } 32 $path = $new_path; 33 } 34 35 /* 36 * This is the default value when no origin is provided or when it is 'all'. 37 * 38 * The $origin is used as part of the cache key. Changes here need to account 39 * for clearing the cache appropriately. 40 */ 41 $origin = 'custom'; 42 if ( isset( $context['origin'] ) && 'base' === $context['origin'] ) { 43 $origin = 'theme'; 44 } 45 46 /* 47 * By using the 'theme_json' group, this data is marked to be non-persistent across requests. 48 * See `wp_cache_add_non_persistent_groups` in src/wp-includes/load.php and other places. 49 * 50 * The rationale for this is to make sure derived data from theme.json 51 * is always fresh from the potential modifications done via hooks 52 * that can use dynamic data (modify the stylesheet depending on some option, 53 * settings depending on user permissions, etc.). 54 * See some of the existing hooks to modify theme.json behavior: 55 * https://make.wordpress.org/core/2022/10/10/filters-for-theme-json-data/ 56 * 57 * A different alternative considered was to invalidate the cache upon certain 58 * events such as options add/update/delete, user meta, etc. 59 * It was judged not enough, hence this approach. 60 * See https://github.com/WordPress/gutenberg/pull/45372 61 */ 62 $cache_group = 'theme_json'; 63 $cache_key = 'wp_get_global_settings_' . $origin; 64 65 /* 66 * Ignore cache when the development mode is set to 'theme', so it doesn't interfere with the theme 67 * developer's workflow. 68 */ 69 $can_use_cached = ! wp_is_development_mode( 'theme' ); 70 71 $settings = false; 72 if ( $can_use_cached ) { 73 $settings = wp_cache_get( $cache_key, $cache_group ); 74 } 75 76 if ( false === $settings ) { 77 $settings = WP_Theme_JSON_Resolver::get_merged_data( $origin )->get_settings(); 78 if ( $can_use_cached ) { 79 wp_cache_set( $cache_key, $settings, $cache_group ); 80 } 81 } 82 83 return _wp_array_get( $settings, $path, $settings ); 84 } 85 86 /** 87 * Gets the styles resulting of merging core, theme, and user data. 88 * 89 * @since 5.9.0 90 * @since 6.3.0 the internal link format "var:preset|color|secondary" is resolved 91 * to "var(--wp--preset--font-size--small)" so consumers don't have to. 92 * @since 6.3.0 `transforms` is now usable in the `context` parameter. In case [`transforms`]['resolve_variables'] 93 * is defined, variables are resolved to their value in the styles. 94 * 95 * @param array $path Path to the specific style to retrieve. Optional. 96 * If empty, will return all styles. 97 * @param array $context { 98 * Metadata to know where to retrieve the $path from. Optional. 99 * 100 * @type string $block_name Which block to retrieve the styles from. 101 * If empty, it'll return the styles for the global context. 102 * @type string $origin Which origin to take data from. 103 * Valid values are 'all' (core, theme, and user) or 'base' (core and theme). 104 * If empty or unknown, 'all' is used. 105 * @type array $transforms Which transformation(s) to apply. 106 * Valid value is array( 'resolve-variables' ). 107 * If defined, variables are resolved to their value in the styles. 108 * } 109 * @return mixed The styles array or individual style value to retrieve. 110 */ 111 function wp_get_global_styles( $path = array(), $context = array() ) { 112 if ( ! empty( $context['block_name'] ) ) { 113 $path = array_merge( array( 'blocks', $context['block_name'] ), $path ); 114 } 115 116 $origin = 'custom'; 117 if ( isset( $context['origin'] ) && 'base' === $context['origin'] ) { 118 $origin = 'theme'; 119 } 120 121 $resolve_variables = isset( $context['transforms'] ) 122 && is_array( $context['transforms'] ) 123 && in_array( 'resolve-variables', $context['transforms'], true ); 124 125 $merged_data = WP_Theme_JSON_Resolver::get_merged_data( $origin ); 126 if ( $resolve_variables ) { 127 $merged_data = WP_Theme_JSON::resolve_variables( $merged_data ); 128 } 129 $styles = $merged_data->get_raw_data()['styles']; 130 return _wp_array_get( $styles, $path, $styles ); 131 } 132 133 134 /** 135 * Returns the stylesheet resulting of merging core, theme, and user data. 136 * 137 * @since 5.9.0 138 * @since 6.1.0 Added 'base-layout-styles' support. 139 * @since 6.6.0 Resolves relative paths in theme.json styles to theme absolute paths. 140 * @since 7.0.0 Deprecated 'base-layout-styles' type; classic themes now receive full styles 141 * with layout-specific alignment rules skipped via `base_layout_styles` option. 142 * 143 * @param array $types Optional. Types of styles to load. 144 * See {@see 'WP_Theme_JSON::get_stylesheet'} for all valid types. 145 * If empty, will load: 'variables', 'presets', 'styles'. 146 * @return string Stylesheet. 147 */ 148 function wp_get_global_stylesheet( $types = array() ) { 149 /* 150 * Ignore cache when the development mode is set to 'theme', so it doesn't interfere with the theme 151 * developer's workflow. 152 */ 153 $can_use_cached = empty( $types ) && ! wp_is_development_mode( 'theme' ); 154 155 /* 156 * By using the 'theme_json' group, this data is marked to be non-persistent across requests. 157 * @see `wp_cache_add_non_persistent_groups()`. 158 * 159 * The rationale for this is to make sure derived data from theme.json 160 * is always fresh from the potential modifications done via hooks 161 * that can use dynamic data (modify the stylesheet depending on some option, 162 * settings depending on user permissions, etc.). 163 * See some of the existing hooks to modify theme.json behavior: 164 * @see https://make.wordpress.org/core/2022/10/10/filters-for-theme-json-data/ 165 * 166 * A different alternative considered was to invalidate the cache upon certain 167 * events such as options add/update/delete, user meta, etc. 168 * It was judged not enough, hence this approach. 169 * @see https://github.com/WordPress/gutenberg/pull/45372 170 */ 171 $cache_group = 'theme_json'; 172 $cache_key = 'wp_get_global_stylesheet'; 173 if ( $can_use_cached ) { 174 $cached = wp_cache_get( $cache_key, $cache_group ); 175 if ( $cached ) { 176 return $cached; 177 } 178 } 179 180 $tree = WP_Theme_JSON_Resolver::resolve_theme_file_uris( WP_Theme_JSON_Resolver::get_merged_data() ); 181 182 if ( empty( $types ) ) { 183 $types = array( 'variables', 'styles', 'presets' ); 184 } 185 186 /* 187 * Enable base layout styles only mode for classic themes without theme.json. 188 * This skips alignment styles that target .wp-site-blocks which is only used by block themes. 189 */ 190 $options = array(); 191 if ( ! wp_is_block_theme() && ! wp_theme_has_theme_json() ) { 192 $options['base_layout_styles'] = true; 193 } 194 195 /* 196 * If variables are part of the stylesheet, then add them. 197 * This is so themes without a theme.json still work as before 5.9: 198 * they can override the default presets. 199 * See https://core.trac.wordpress.org/ticket/54782 200 */ 201 $styles_variables = ''; 202 if ( in_array( 'variables', $types, true ) ) { 203 /* 204 * Only use the default, theme, and custom origins. Why? 205 * Because styles for `blocks` origin are added at a later phase 206 * (i.e. in the render cycle). Here, only the ones in use are rendered. 207 * @see wp_add_global_styles_for_blocks 208 */ 209 $origins = array( 'default', 'theme', 'custom' ); 210 $styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins, $options ); 211 $types = array_diff( $types, array( 'variables' ) ); 212 } 213 214 /* 215 * For the remaining types (presets, styles), we do consider origins: 216 * 217 * - themes without theme.json: only the classes for the presets defined by core 218 * - themes with theme.json: the presets and styles classes, both from core and the theme 219 */ 220 $styles_rest = ''; 221 if ( ! empty( $types ) ) { 222 /* 223 * Only use the default, theme, and custom origins. Why? 224 * Because styles for `blocks` origin are added at a later phase 225 * (i.e. in the render cycle). Here, only the ones in use are rendered. 226 * @see wp_add_global_styles_for_blocks 227 */ 228 $origins = array( 'default', 'theme', 'custom' ); 229 $styles_rest = $tree->get_stylesheet( $types, $origins, $options ); 230 } 231 232 $stylesheet = $styles_variables . $styles_rest; 233 if ( $can_use_cached ) { 234 wp_cache_set( $cache_key, $stylesheet, $cache_group ); 235 } 236 237 return $stylesheet; 238 } 239 240 /** 241 * Adds global style rules to the inline style for each block. 242 * 243 * @since 6.1.0 244 * @since 6.7.0 Resolve relative paths in block styles. 245 * 246 * @global WP_Styles $wp_styles 247 */ 248 function wp_add_global_styles_for_blocks() { 249 global $wp_styles; 250 251 $tree = WP_Theme_JSON_Resolver::get_merged_data(); 252 $tree = WP_Theme_JSON_Resolver::resolve_theme_file_uris( $tree ); 253 $block_nodes = $tree->get_styles_block_nodes(); 254 255 $can_use_cached = ! wp_is_development_mode( 'theme' ); 256 $update_cache = false; 257 258 if ( $can_use_cached ) { 259 // Hash the merged WP_Theme_JSON data to bust cache on settings or styles change. 260 $cache_hash = md5( wp_json_encode( $tree->get_raw_data() ) ); 261 $cache_key = 'wp_styles_for_blocks'; 262 $cached = get_transient( $cache_key ); 263 264 // Reset the cached data if there is no value or if the hash has changed. 265 if ( ! is_array( $cached ) || $cached['hash'] !== $cache_hash ) { 266 $cached = array( 267 'hash' => $cache_hash, 268 'blocks' => array(), 269 ); 270 271 // Update the cache if the hash has changed. 272 $update_cache = true; 273 } 274 } 275 276 foreach ( $block_nodes as $metadata ) { 277 278 if ( $can_use_cached ) { 279 // Use the block name as the key for cached CSS data. Otherwise, use a hash of the metadata. 280 $cache_node_key = $metadata['name'] ?? md5( wp_json_encode( $metadata ) ); 281 282 if ( isset( $cached['blocks'][ $cache_node_key ] ) ) { 283 $block_css = $cached['blocks'][ $cache_node_key ]; 284 } else { 285 $block_css = $tree->get_styles_for_block( $metadata ); 286 $cached['blocks'][ $cache_node_key ] = $block_css; 287 288 // Update the cache if the cache contents have changed. 289 $update_cache = true; 290 } 291 } else { 292 $block_css = $tree->get_styles_for_block( $metadata ); 293 } 294 295 if ( ! wp_should_load_block_assets_on_demand() ) { 296 wp_add_inline_style( 'global-styles', $block_css ); 297 continue; 298 } 299 300 $stylesheet_handle = 'global-styles'; 301 302 /* 303 * When `wp_should_load_block_assets_on_demand()` is true, block styles are 304 * enqueued for each block on the page in class WP_Block's render function. 305 * This means there will be a handle in the styles queue for each of those blocks. 306 * Block-specific global styles should be attached to the global-styles handle, but 307 * only for blocks on the page, thus we check if the block's handle is in the queue 308 * before adding the inline style. 309 * This conditional loading only applies to core blocks. 310 * TODO: Explore how this could be expanded to third-party blocks as well. 311 */ 312 if ( isset( $metadata['name'] ) ) { 313 if ( str_starts_with( $metadata['name'], 'core/' ) ) { 314 $block_name = str_replace( 'core/', '', $metadata['name'] ); 315 $block_handle = 'wp-block-' . $block_name; 316 if ( in_array( $block_handle, $wp_styles->queue, true ) ) { 317 wp_add_inline_style( $stylesheet_handle, $block_css ); 318 } 319 } else { 320 wp_add_inline_style( $stylesheet_handle, $block_css ); 321 } 322 } 323 324 // The likes of block element styles from theme.json do not have $metadata['name'] set. 325 if ( ! isset( $metadata['name'] ) && ! empty( $metadata['path'] ) ) { 326 $block_name = wp_get_block_name_from_theme_json_path( $metadata['path'] ); 327 if ( $block_name ) { 328 if ( str_starts_with( $block_name, 'core/' ) ) { 329 $block_name = str_replace( 'core/', '', $block_name ); 330 $block_handle = 'wp-block-' . $block_name; 331 if ( in_array( $block_handle, $wp_styles->queue, true ) ) { 332 wp_add_inline_style( $stylesheet_handle, $block_css ); 333 } 334 } else { 335 wp_add_inline_style( $stylesheet_handle, $block_css ); 336 } 337 } 338 } 339 } 340 341 if ( $update_cache ) { 342 set_transient( $cache_key, $cached ); 343 } 344 } 345 346 /** 347 * Gets the block name from a given theme.json path. 348 * 349 * @since 6.3.0 350 * @access private 351 * 352 * @param array $path An array of keys describing the path to a property in theme.json. 353 * @return string Identified block name, or empty string if none found. 354 */ 355 function wp_get_block_name_from_theme_json_path( $path ) { 356 // Block name is expected to be the third item after 'styles' and 'blocks'. 357 if ( 358 count( $path ) >= 3 359 && 'styles' === $path[0] 360 && 'blocks' === $path[1] 361 && str_contains( $path[2], '/' ) 362 ) { 363 return $path[2]; 364 } 365 366 /* 367 * As fallback and for backward compatibility, allow any core block to be 368 * at any position. 369 */ 370 $result = array_values( 371 array_filter( 372 $path, 373 static function ( $item ) { 374 if ( str_contains( $item, 'core/' ) ) { 375 return true; 376 } 377 return false; 378 } 379 ) 380 ); 381 return $result[0] ?? ''; 382 } 383 384 /** 385 * Checks whether a theme or its parent has a theme.json file. 386 * 387 * @since 6.2.0 388 * 389 * @return bool Returns true if theme or its parent has a theme.json file, false otherwise. 390 */ 391 function wp_theme_has_theme_json() { 392 static $theme_has_support = array(); 393 394 $stylesheet = get_stylesheet(); 395 396 if ( 397 isset( $theme_has_support[ $stylesheet ] ) && 398 /* 399 * Ignore static cache when the development mode is set to 'theme', to avoid interfering with 400 * the theme developer's workflow. 401 */ 402 ! wp_is_development_mode( 'theme' ) 403 ) { 404 return $theme_has_support[ $stylesheet ]; 405 } 406 407 $stylesheet_directory = get_stylesheet_directory(); 408 $template_directory = get_template_directory(); 409 410 // This is the same as get_theme_file_path(), which isn't available in load-styles.php context 411 if ( $stylesheet_directory !== $template_directory && file_exists( $stylesheet_directory . '/theme.json' ) ) { 412 $path = $stylesheet_directory . '/theme.json'; 413 } else { 414 $path = $template_directory . '/theme.json'; 415 } 416 417 /** This filter is documented in wp-includes/link-template.php */ 418 $path = apply_filters( 'theme_file_path', $path, 'theme.json' ); 419 420 $theme_has_support[ $stylesheet ] = file_exists( $path ); 421 422 return $theme_has_support[ $stylesheet ]; 423 } 424 425 /** 426 * Cleans the caches under the theme_json group. 427 * 428 * @since 6.2.0 429 */ 430 function wp_clean_theme_json_cache() { 431 wp_cache_delete( 'wp_get_global_stylesheet', 'theme_json' ); 432 wp_cache_delete( 'wp_get_global_styles_svg_filters', 'theme_json' ); 433 wp_cache_delete( 'wp_get_global_settings_custom', 'theme_json' ); 434 wp_cache_delete( 'wp_get_global_settings_theme', 'theme_json' ); 435 wp_cache_delete( 'wp_get_global_styles_custom_css', 'theme_json' ); 436 wp_cache_delete( 'wp_get_theme_data_template_parts', 'theme_json' ); 437 WP_Theme_JSON_Resolver::clean_cached_data(); 438 } 439 440 /** 441 * Returns the current theme's wanted patterns (slugs) to be 442 * registered from Pattern Directory. 443 * 444 * @since 6.3.0 445 * 446 * @return string[] 447 */ 448 function wp_get_theme_directory_pattern_slugs() { 449 return WP_Theme_JSON_Resolver::get_theme_data( array(), array( 'with_supports' => false ) )->get_patterns(); 450 } 451 452 /** 453 * Returns the metadata for the custom templates defined by the theme via theme.json. 454 * 455 * @since 6.4.0 456 * 457 * @return array Associative array of `$template_name => $template_data` pairs, 458 * with `$template_data` having "title" and "postTypes" fields. 459 */ 460 function wp_get_theme_data_custom_templates() { 461 return WP_Theme_JSON_Resolver::get_theme_data( array(), array( 'with_supports' => false ) )->get_custom_templates(); 462 } 463 464 /** 465 * Returns the metadata for the template parts defined by the theme. 466 * 467 * @since 6.4.0 468 * 469 * @return array Associative array of `$part_name => $part_data` pairs, 470 * with `$part_data` having "title" and "area" fields. 471 */ 472 function wp_get_theme_data_template_parts() { 473 $cache_group = 'theme_json'; 474 $cache_key = 'wp_get_theme_data_template_parts'; 475 $can_use_cached = ! wp_is_development_mode( 'theme' ); 476 477 if ( $can_use_cached ) { 478 $metadata = wp_cache_get( $cache_key, $cache_group ); 479 if ( false !== $metadata ) { 480 return $metadata; 481 } 482 } 483 484 $metadata = WP_Theme_JSON_Resolver::get_theme_data( array(), array( 'with_supports' => false ) )->get_template_parts(); 485 if ( $can_use_cached ) { 486 wp_cache_set( $cache_key, $metadata, $cache_group ); 487 } 488 489 return $metadata; 490 } 491 492 /** 493 * Determines the CSS selector for the block type and property provided, 494 * returning it if available. 495 * 496 * @since 6.3.0 497 * 498 * @param WP_Block_Type $block_type The block's type. 499 * @param string|array $target The desired selector's target, `root` or array path. 500 * @param boolean $fallback Whether to fall back to broader selector. 501 * 502 * @return string|null CSS selector or `null` if no selector available. 503 */ 504 function wp_get_block_css_selector( $block_type, $target = 'root', $fallback = false ) { 505 if ( empty( $target ) ) { 506 return null; 507 } 508 509 $has_selectors = ! empty( $block_type->selectors ); 510 511 // Root Selector. 512 513 // Calculated before returning as it can be used as fallback for 514 // feature selectors later on. 515 $root_selector = null; 516 517 if ( $has_selectors && isset( $block_type->selectors['root'] ) ) { 518 // Use the selectors API if available. 519 $root_selector = $block_type->selectors['root']; 520 } elseif ( isset( $block_type->supports['__experimentalSelector'] ) && is_string( $block_type->supports['__experimentalSelector'] ) ) { 521 // Use the old experimental selector supports property if set. 522 $root_selector = $block_type->supports['__experimentalSelector']; 523 } else { 524 // If no root selector found, generate default block class selector. 525 $block_name = str_replace( '/', '-', str_replace( 'core/', '', $block_type->name ) ); 526 $root_selector = ".wp-block-{$block_name}"; 527 } 528 529 // Return selector if it's the root target we are looking for. 530 if ( 'root' === $target ) { 531 return $root_selector; 532 } 533 534 // If target is not `root` we have a feature or subfeature as the target. 535 // If the target is a string convert to an array. 536 if ( is_string( $target ) ) { 537 $target = explode( '.', $target ); 538 } 539 540 // Feature Selectors ( May fallback to root selector ). 541 if ( 1 === count( $target ) ) { 542 $fallback_selector = $fallback ? $root_selector : null; 543 544 // Prefer the selectors API if available. 545 if ( $has_selectors ) { 546 // Look for selector under `feature.root`. 547 $path = array( current( $target ), 'root' ); 548 $feature_selector = _wp_array_get( $block_type->selectors, $path, null ); 549 550 if ( $feature_selector ) { 551 return $feature_selector; 552 } 553 554 // Check if feature selector is set via shorthand. 555 $feature_selector = _wp_array_get( $block_type->selectors, $target, null ); 556 557 return is_string( $feature_selector ) ? $feature_selector : $fallback_selector; 558 } 559 560 // Try getting old experimental supports selector value. 561 $path = array( current( $target ), '__experimentalSelector' ); 562 $feature_selector = _wp_array_get( $block_type->supports, $path, null ); 563 564 // Nothing to work with, provide fallback or null. 565 if ( null === $feature_selector ) { 566 return $fallback_selector; 567 } 568 569 // Scope the feature selector by the block's root selector. 570 return WP_Theme_JSON::scope_selector( $root_selector, $feature_selector ); 571 } 572 573 // Subfeature selector 574 // This may fallback either to parent feature or root selector. 575 $subfeature_selector = null; 576 577 // Use selectors API if available. 578 if ( $has_selectors ) { 579 $subfeature_selector = _wp_array_get( $block_type->selectors, $target, null ); 580 } 581 582 // Only return if we have a subfeature selector. 583 if ( $subfeature_selector ) { 584 return $subfeature_selector; 585 } 586 587 // To this point we don't have a subfeature selector. If a fallback 588 // has been requested, remove subfeature from target path and return 589 // results of a call for the parent feature's selector. 590 if ( $fallback ) { 591 return wp_get_block_css_selector( $block_type, $target[0], $fallback ); 592 } 593 594 return null; 595 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue May 5 08:20:14 2026 | Cross-referenced by PHPXref |