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