| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Style Engine: WP_Style_Engine class 4 * 5 * @package WordPress 6 * @subpackage StyleEngine 7 * @since 6.1.0 8 */ 9 10 /** 11 * The main class integrating all other WP_Style_Engine_* classes. 12 * 13 * The Style Engine aims to provide a consistent API for rendering styling for blocks 14 * across both client-side and server-side applications. 15 * 16 * This class is final and should not be extended. 17 * 18 * This class is for internal Core usage and is not supposed to be used by extenders 19 * (plugins and/or themes). This is a low-level API that may need to do breaking changes. 20 * Please, use wp_style_engine_get_styles() instead. 21 * 22 * @access private 23 * @since 6.1.0 24 * @since 6.3.0 Added support for text-columns. 25 * @since 6.4.0 Added support for background.backgroundImage. 26 * @since 6.5.0 Added support for background.backgroundPosition, 27 * background.backgroundRepeat and dimensions.aspectRatio. 28 * @since 6.7.0 Added support for typography.writingMode. 29 * @since 7.0.0 Added support for typography.textIndent. 30 * @since 7.2.0 Added support for typography.textShadow. 31 * 32 * @phpstan-type Style_Definition array{ 33 * property_keys?: array{ default: string, individual?: string }, 34 * path: non-empty-list<string>, 35 * css_vars?: array<string, string>, 36 * classnames?: array<string, string|true>, 37 * value_func?: callable( mixed, array<string, mixed>, array<mixed> ): array<string, string>, 38 * } 39 */ 40 #[AllowDynamicProperties] 41 final class WP_Style_Engine { 42 /** 43 * The values `background-clip` accepts. 44 * 45 * @since 7.2.0 46 * @var string[] 47 */ 48 const VALID_BACKGROUND_CLIP_VALUES = array( 'border-box', 'padding-box', 'content-box', 'text' ); 49 50 /** 51 * Style definitions that contain the instructions to parse/output valid Gutenberg styles from a block's attributes. 52 * 53 * For every style definition, the following properties are valid: 54 * 55 * - classnames => (array) an array of classnames to be returned for block styles. The key is a classname or pattern. 56 * A value of `true` means the classname should be applied always. Otherwise, a valid CSS property (string) 57 * to match the incoming value, e.g., "color" to match var:preset|color|somePresetSlug. 58 * - css_vars => (array) an array of key value pairs used to generate CSS var values. 59 * The key should be the CSS property name that matches the second element of the preset string value, 60 * i.e., "color" in var:preset|color|somePresetSlug. The value is a CSS var pattern (e.g. `--wp--preset--color--$slug`), 61 * whose `$slug` fragment will be replaced with the preset slug, which is the third element of the preset string value, 62 * i.e., `somePresetSlug` in var:preset|color|somePresetSlug. 63 * - property_keys => (array) array of keys whose values represent a valid CSS property, e.g., "margin" or "border". 64 * - path => (array) a path that accesses the corresponding style value in the block style object. 65 * - value_func => (string) the name of a function to generate a CSS definition array for a particular style object. The output of this function should be `array( "$property" => "$value", ... )`. 66 * 67 * @since 6.1.0 68 * @since 7.1.0 Added `background.gradient` property. 69 * @since 7.2.0 Added `background.backgroundClip` property. 70 * @var array<string, array<string, array>> 71 * @phpstan-var array<string, array<string, Style_Definition>> 72 */ 73 const BLOCK_STYLE_DEFINITIONS_METADATA = array( 74 'background' => array( 75 'backgroundImage' => array( 76 'property_keys' => array( 77 'default' => 'background-image', 78 ), 79 'value_func' => array( self::class, 'get_url_or_value_css_declaration' ), 80 'path' => array( 'background', 'backgroundImage' ), 81 ), 82 'backgroundPosition' => array( 83 'property_keys' => array( 84 'default' => 'background-position', 85 ), 86 'path' => array( 'background', 'backgroundPosition' ), 87 ), 88 'backgroundRepeat' => array( 89 'property_keys' => array( 90 'default' => 'background-repeat', 91 ), 92 'path' => array( 'background', 'backgroundRepeat' ), 93 ), 94 'backgroundSize' => array( 95 'property_keys' => array( 96 'default' => 'background-size', 97 ), 98 'path' => array( 'background', 'backgroundSize' ), 99 ), 100 'backgroundAttachment' => array( 101 'property_keys' => array( 102 'default' => 'background-attachment', 103 ), 104 'path' => array( 'background', 'backgroundAttachment' ), 105 ), 106 'gradient' => array( 107 'property_keys' => array( 108 'default' => 'background-image', 109 ), 110 'css_vars' => array( 111 'gradient' => '--wp--preset--gradient--$slug', 112 ), 113 'path' => array( 'background', 'gradient' ), 114 'classnames' => array( 115 'has-background' => true, 116 ), 117 ), 118 'backgroundClip' => array( 119 'value_func' => array( self::class, 'get_background_clip_css_declarations' ), 120 'path' => array( 'background', 'backgroundClip' ), 121 ), 122 ), 123 'color' => array( 124 'text' => array( 125 'property_keys' => array( 126 'default' => 'color', 127 ), 128 'path' => array( 'color', 'text' ), 129 'css_vars' => array( 130 'color' => '--wp--preset--color--$slug', 131 ), 132 'classnames' => array( 133 'has-text-color' => true, 134 'has-$slug-color' => 'color', 135 ), 136 ), 137 'background' => array( 138 'property_keys' => array( 139 'default' => 'background-color', 140 ), 141 'path' => array( 'color', 'background' ), 142 'css_vars' => array( 143 'color' => '--wp--preset--color--$slug', 144 ), 145 'classnames' => array( 146 'has-background' => true, 147 'has-$slug-background-color' => 'color', 148 ), 149 ), 150 'gradient' => array( 151 'property_keys' => array( 152 'default' => 'background', 153 ), 154 'path' => array( 'color', 'gradient' ), 155 'css_vars' => array( 156 'gradient' => '--wp--preset--gradient--$slug', 157 ), 158 'classnames' => array( 159 'has-background' => true, 160 'has-$slug-gradient-background' => 'gradient', 161 ), 162 ), 163 ), 164 'border' => array( 165 'color' => array( 166 'property_keys' => array( 167 'default' => 'border-color', 168 'individual' => 'border-%s-color', 169 ), 170 'path' => array( 'border', 'color' ), 171 'classnames' => array( 172 'has-border-color' => true, 173 'has-$slug-border-color' => 'color', 174 ), 175 ), 176 'radius' => array( 177 'property_keys' => array( 178 'default' => 'border-radius', 179 'individual' => 'border-%s-radius', 180 ), 181 'path' => array( 'border', 'radius' ), 182 'css_vars' => array( 183 'border-radius' => '--wp--preset--border-radius--$slug', 184 ), 185 ), 186 'style' => array( 187 'property_keys' => array( 188 'default' => 'border-style', 189 'individual' => 'border-%s-style', 190 ), 191 'path' => array( 'border', 'style' ), 192 ), 193 'width' => array( 194 'property_keys' => array( 195 'default' => 'border-width', 196 'individual' => 'border-%s-width', 197 ), 198 'path' => array( 'border', 'width' ), 199 ), 200 'top' => array( 201 'value_func' => array( self::class, 'get_individual_property_css_declarations' ), 202 'path' => array( 'border', 'top' ), 203 'css_vars' => array( 204 'color' => '--wp--preset--color--$slug', 205 ), 206 ), 207 'right' => array( 208 'value_func' => array( self::class, 'get_individual_property_css_declarations' ), 209 'path' => array( 'border', 'right' ), 210 'css_vars' => array( 211 'color' => '--wp--preset--color--$slug', 212 ), 213 ), 214 'bottom' => array( 215 'value_func' => array( self::class, 'get_individual_property_css_declarations' ), 216 'path' => array( 'border', 'bottom' ), 217 'css_vars' => array( 218 'color' => '--wp--preset--color--$slug', 219 ), 220 ), 221 'left' => array( 222 'value_func' => array( self::class, 'get_individual_property_css_declarations' ), 223 'path' => array( 'border', 'left' ), 224 'css_vars' => array( 225 'color' => '--wp--preset--color--$slug', 226 ), 227 ), 228 ), 229 'shadow' => array( 230 'shadow' => array( 231 'property_keys' => array( 232 'default' => 'box-shadow', 233 ), 234 'path' => array( 'shadow' ), 235 'css_vars' => array( 236 'shadow' => '--wp--preset--shadow--$slug', 237 ), 238 ), 239 ), 240 'dimensions' => array( 241 'aspectRatio' => array( 242 'property_keys' => array( 243 'default' => 'aspect-ratio', 244 ), 245 'path' => array( 'dimensions', 'aspectRatio' ), 246 'classnames' => array( 247 'has-aspect-ratio' => true, 248 ), 249 ), 250 'height' => array( 251 'property_keys' => array( 252 'default' => 'height', 253 ), 254 'path' => array( 'dimensions', 'height' ), 255 'css_vars' => array( 256 'dimension' => '--wp--preset--dimension--$slug', 257 ), 258 ), 259 'minHeight' => array( 260 'property_keys' => array( 261 'default' => 'min-height', 262 ), 263 'path' => array( 'dimensions', 'minHeight' ), 264 'css_vars' => array( 265 'dimension' => '--wp--preset--dimension--$slug', 266 ), 267 ), 268 'minWidth' => array( 269 'property_keys' => array( 270 'default' => 'min-width', 271 ), 272 'path' => array( 'dimensions', 'minWidth' ), 273 'css_vars' => array( 274 'dimension' => '--wp--preset--dimension--$slug', 275 ), 276 ), 277 'objectFit' => array( 278 'property_keys' => array( 279 'default' => 'object-fit', 280 ), 281 'path' => array( 'dimensions', 'objectFit' ), 282 ), 283 'width' => array( 284 'property_keys' => array( 285 'default' => 'width', 286 ), 287 'path' => array( 'dimensions', 'width' ), 288 'css_vars' => array( 289 'dimension' => '--wp--preset--dimension--$slug', 290 ), 291 ), 292 ), 293 'spacing' => array( 294 'padding' => array( 295 'property_keys' => array( 296 'default' => 'padding', 297 'individual' => 'padding-%s', 298 ), 299 'path' => array( 'spacing', 'padding' ), 300 'css_vars' => array( 301 'spacing' => '--wp--preset--spacing--$slug', 302 ), 303 ), 304 'margin' => array( 305 'property_keys' => array( 306 'default' => 'margin', 307 'individual' => 'margin-%s', 308 ), 309 'path' => array( 'spacing', 'margin' ), 310 'css_vars' => array( 311 'spacing' => '--wp--preset--spacing--$slug', 312 ), 313 ), 314 ), 315 'typography' => array( 316 'fontSize' => array( 317 'property_keys' => array( 318 'default' => 'font-size', 319 ), 320 'path' => array( 'typography', 'fontSize' ), 321 'css_vars' => array( 322 'font-size' => '--wp--preset--font-size--$slug', 323 ), 324 'classnames' => array( 325 'has-$slug-font-size' => 'font-size', 326 ), 327 ), 328 'fontFamily' => array( 329 'property_keys' => array( 330 'default' => 'font-family', 331 ), 332 'css_vars' => array( 333 'font-family' => '--wp--preset--font-family--$slug', 334 ), 335 'path' => array( 'typography', 'fontFamily' ), 336 'classnames' => array( 337 'has-$slug-font-family' => 'font-family', 338 ), 339 ), 340 'fontStyle' => array( 341 'property_keys' => array( 342 'default' => 'font-style', 343 ), 344 'path' => array( 'typography', 'fontStyle' ), 345 ), 346 'fontWeight' => array( 347 'property_keys' => array( 348 'default' => 'font-weight', 349 ), 350 'path' => array( 'typography', 'fontWeight' ), 351 ), 352 'lineHeight' => array( 353 'property_keys' => array( 354 'default' => 'line-height', 355 ), 356 'path' => array( 'typography', 'lineHeight' ), 357 ), 358 'textColumns' => array( 359 'property_keys' => array( 360 'default' => 'column-count', 361 ), 362 'path' => array( 'typography', 'textColumns' ), 363 ), 364 'textDecoration' => array( 365 'property_keys' => array( 366 'default' => 'text-decoration', 367 ), 368 'path' => array( 'typography', 'textDecoration' ), 369 ), 370 'textIndent' => array( 371 'property_keys' => array( 372 'default' => 'text-indent', 373 ), 374 'path' => array( 'typography', 'textIndent' ), 375 ), 376 'textShadow' => array( 377 'property_keys' => array( 378 'default' => 'text-shadow', 379 ), 380 'css_vars' => array( 381 'text-shadow' => '--wp--preset--text-shadow--$slug', 382 ), 383 'path' => array( 'typography', 'textShadow' ), 384 'classnames' => array( 385 'has-$slug-text-shadow' => 'text-shadow', 386 ), 387 ), 388 'textTransform' => array( 389 'property_keys' => array( 390 'default' => 'text-transform', 391 ), 392 'path' => array( 'typography', 'textTransform' ), 393 ), 394 'letterSpacing' => array( 395 'property_keys' => array( 396 'default' => 'letter-spacing', 397 ), 398 'path' => array( 'typography', 'letterSpacing' ), 399 ), 400 'writingMode' => array( 401 'property_keys' => array( 402 'default' => 'writing-mode', 403 ), 404 'path' => array( 'typography', 'writingMode' ), 405 ), 406 ), 407 ); 408 409 /** 410 * Util: Extracts the slug in kebab case from a preset string, 411 * e.g. `heavenly-blue` from `var:preset|color|heavenlyBlue`. 412 * 413 * @since 6.1.0 414 * 415 * @param mixed $style_value A single CSS preset value. 416 * @param string $property_key The CSS property that is the second element of the preset string. 417 * Used for matching. 418 * @return string The slug, or empty string if not found. 419 */ 420 protected static function get_slug_from_preset_value( $style_value, $property_key ) { 421 if ( is_string( $style_value ) && is_string( $property_key ) 422 && str_contains( $style_value, "var:preset|{$property_key}|" ) 423 ) { 424 $index_to_splice = strrpos( $style_value, '|' ) + 1; 425 return _wp_to_kebab_case( substr( $style_value, $index_to_splice ) ); 426 } 427 return ''; 428 } 429 430 /** 431 * Util: Generates a CSS var string, e.g. `var(--wp--preset--color--background)` 432 * from a preset string such as `var:preset|space|50`. 433 * 434 * @since 6.1.0 435 * 436 * @param string $style_value A single CSS preset value. 437 * @param string[] $css_vars An associate array of CSS var patterns 438 * used to generate the var string. 439 * @return string The CSS var, or an empty string if no match for slug found. 440 */ 441 protected static function get_css_var_value( $style_value, $css_vars ) { 442 foreach ( $css_vars as $property_key => $css_var_pattern ) { 443 $slug = static::get_slug_from_preset_value( $style_value, $property_key ); 444 if ( static::is_valid_style_value( $slug ) ) { 445 $var = strtr( 446 $css_var_pattern, 447 array( '$slug' => $slug ) 448 ); 449 return "var($var)"; 450 } 451 } 452 return ''; 453 } 454 455 /** 456 * Util: Checks whether an incoming block style value is valid. 457 * 458 * @since 6.1.0 459 * 460 * @param mixed $style_value A single raw style value. 461 * @return bool 462 */ 463 protected static function is_valid_style_value( $style_value ) { 464 return '0' === $style_value || ! empty( $style_value ); 465 } 466 467 /** 468 * Stores a CSS rule using the provided CSS selector and CSS declarations. 469 * 470 * @since 6.1.0 471 * @since 6.6.0 Added the `$rules_group` parameter. 472 * @since 7.1.0 Extended `$css_declarations` parameter to accept `WP_Style_Engine_CSS_Declarations` object. 473 * 474 * @param string $store_name A valid store key. 475 * @param string $css_selector When a selector is passed, the function will return 476 * a full CSS rule `$selector { ...rules }` 477 * otherwise a concatenated string of properties and values. 478 * @param string[]|WP_Style_Engine_CSS_Declarations $css_declarations An associative array of CSS definitions, 479 * e.g. `array( "$property" => "$value", "$property" => "$value" )`, 480 * or a WP_Style_Engine_CSS_Declarations object. 481 * @param string $rules_group Optional. A parent CSS selector in the case of nested CSS, 482 * or a CSS nested @rule, such as `@media (min-width: 80rem)` 483 * or `@layer module`. 484 */ 485 public static function store_css_rule( $store_name, $css_selector, $css_declarations, $rules_group = '' ): void { 486 if ( empty( $store_name ) || empty( $css_selector ) || empty( $css_declarations ) ) { 487 return; 488 } 489 490 $store = static::get_store( $store_name ); 491 if ( ! $store ) { 492 return; 493 } 494 495 // The rule is null when the selector is only whitespace. 496 $rule = $store->add_rule( $css_selector, $rules_group ); 497 if ( $rule ) { 498 $rule->add_declarations( $css_declarations ); 499 } 500 } 501 502 /** 503 * Returns a store by store key. 504 * 505 * @since 6.1.0 506 * 507 * @param string $store_name A store key. 508 * @return WP_Style_Engine_CSS_Rules_Store|null 509 */ 510 public static function get_store( $store_name ) { 511 return WP_Style_Engine_CSS_Rules_Store::get_store( $store_name ); 512 } 513 514 /** 515 * Returns classnames and CSS based on the values in a styles object. 516 * 517 * Return values are parsed based on the instructions in {@see self::BLOCK_STYLE_DEFINITIONS_METADATA}. 518 * 519 * @since 6.1.0 520 * 521 * @param array<string, mixed> $block_styles The style object. 522 * @param array $options { 523 * Optional. An array of options. Default empty array. 524 * 525 * @type bool $convert_vars_to_classnames Whether to skip converting incoming CSS var patterns, 526 * e.g. `var:preset|<PRESET_TYPE>|<PRESET_SLUG>`, 527 * to `var( --wp--preset--* )` values. Default false. 528 * @type string $selector Optional. When a selector is passed, 529 * the value of `$css` in the return value will comprise 530 * a full CSS rule `$selector { ...$css_declarations }`, 531 * otherwise, the value will be a concatenated string 532 * of CSS declarations. 533 * } 534 * @return array { 535 * @type string[] $classnames Array of class names. 536 * @type string[] $declarations An associative array of CSS definitions, 537 * e.g. `array( "$property" => "$value", "$property" => "$value" )`. 538 * } 539 */ 540 public static function parse_block_styles( $block_styles, $options ) { 541 $parsed_styles = array( 542 'classnames' => array(), 543 'declarations' => array(), 544 ); 545 if ( empty( $block_styles ) || ! is_array( $block_styles ) ) { 546 return $parsed_styles; 547 } 548 549 // Collect CSS and classnames. 550 foreach ( static::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group_key => $definition_group_style ) { 551 if ( empty( $block_styles[ $definition_group_key ] ) ) { 552 continue; 553 } 554 foreach ( $definition_group_style as $style_definition ) { 555 $style_value = _wp_array_get( $block_styles, $style_definition['path'], null ); 556 557 if ( ! static::is_valid_style_value( $style_value ) ) { 558 continue; 559 } 560 561 $classnames = static::get_classnames( $style_value, $style_definition ); 562 if ( ! empty( $classnames ) ) { 563 $parsed_styles['classnames'] = array_merge( $parsed_styles['classnames'], $classnames ); 564 } 565 566 $css_declarations = static::get_css_declarations( $style_value, $style_definition, $options ); 567 if ( ! empty( $css_declarations ) ) { 568 /* 569 * Combine background gradient and background image into a single 570 * comma-separated background-image value, matching the JS style engine. 571 */ 572 if ( isset( $css_declarations['background-image'] ) && isset( $parsed_styles['declarations']['background-image'] ) ) { 573 $css_declarations['background-image'] = $css_declarations['background-image'] . ', ' . $parsed_styles['declarations']['background-image']; 574 } 575 $parsed_styles['declarations'] = array_merge( $parsed_styles['declarations'], $css_declarations ); 576 } 577 } 578 } 579 580 return $parsed_styles; 581 } 582 583 /** 584 * Returns classnames, and generates classname(s) from a CSS preset property pattern, 585 * e.g. `var:preset|<PRESET_TYPE>|<PRESET_SLUG>`. 586 * 587 * @since 6.1.0 588 * 589 * @param mixed $style_value A single raw style value or CSS preset property 590 * from the `$block_styles` array. 591 * @param array $style_definition A single style definition from {@see self::BLOCK_STYLE_DEFINITIONS_METADATA}. 592 * @return string[] An array of CSS classnames, or empty array if there are none. 593 * 594 * @phpstan-param Style_Definition $style_definition 595 * @phpstan-return list<string> 596 */ 597 protected static function get_classnames( $style_value, $style_definition ) { 598 if ( empty( $style_value ) ) { 599 return array(); 600 } 601 602 $classnames = array(); 603 if ( ! empty( $style_definition['classnames'] ) ) { 604 foreach ( $style_definition['classnames'] as $classname => $property_key ) { 605 if ( true === $property_key ) { 606 $classnames[] = $classname; 607 continue; 608 } 609 610 $slug = static::get_slug_from_preset_value( $style_value, $property_key ); 611 612 if ( $slug ) { 613 /* 614 * Right now we expect a classname pattern to be stored in self::BLOCK_STYLE_DEFINITIONS_METADATA. 615 * One day, if there are no stored schemata, we could allow custom patterns or 616 * generate classnames based on other properties 617 * such as a path or a value or a prefix passed in options. 618 */ 619 $classnames[] = strtr( $classname, array( '$slug' => $slug ) ); 620 } 621 } 622 } 623 624 return $classnames; 625 } 626 627 /** 628 * Returns an array of CSS declarations based on valid block style values. 629 * 630 * @since 6.1.0 631 * 632 * @param mixed $style_value A single raw style value from $block_styles array. 633 * @param array $style_definition A single style definition from {@see self::BLOCK_STYLE_DEFINITIONS_METADATA}. 634 * @param array $options { 635 * Optional. An array of options. Default empty array. 636 * 637 * @type bool $convert_vars_to_classnames Whether to skip converting incoming CSS var patterns, 638 * e.g. `var:preset|<PRESET_TYPE>|<PRESET_SLUG>`, 639 * to `var( --wp--preset--* )` values. Default false. 640 * } 641 * @return string[] An associative array of CSS definitions, e.g. `array( "$property" => "$value", "$property" => "$value" )`. 642 * 643 * @phpstan-param Style_Definition $style_definition 644 */ 645 protected static function get_css_declarations( $style_value, $style_definition, $options = array() ) { 646 if ( isset( $style_definition['value_func'] ) && is_callable( $style_definition['value_func'] ) ) { 647 return call_user_func( $style_definition['value_func'], $style_value, $style_definition, $options ); 648 } 649 650 if ( ! isset( $style_definition['property_keys'] ) ) { 651 return array(); 652 } 653 654 $css_declarations = array(); 655 $style_property_keys = $style_definition['property_keys']; 656 $should_skip_css_vars = isset( $options['convert_vars_to_classnames'] ) && true === $options['convert_vars_to_classnames']; 657 658 /* 659 * Build CSS var values from `var:preset|<PRESET_TYPE>|<PRESET_SLUG>` values, e.g, `var(--wp--css--rule-slug )`. 660 * Check if the value is a CSS preset and there's a corresponding css_var pattern in the style definition. 661 */ 662 if ( is_string( $style_value ) && str_contains( $style_value, 'var:' ) ) { 663 if ( ! $should_skip_css_vars && ! empty( $style_definition['css_vars'] ) ) { 664 $css_var = static::get_css_var_value( $style_value, $style_definition['css_vars'] ); 665 if ( static::is_valid_style_value( $css_var ) ) { 666 $css_declarations[ $style_property_keys['default'] ] = $css_var; 667 } 668 } 669 return $css_declarations; 670 } 671 672 /* 673 * Default rule builder. 674 * If the input contains an array, assume box model-like properties 675 * for styles such as margins and padding. 676 */ 677 if ( is_array( $style_value ) ) { 678 // Bail out early if the `'individual'` property is not defined. 679 if ( ! isset( $style_property_keys['individual'] ) ) { 680 return $css_declarations; 681 } 682 683 foreach ( $style_value as $key => $value ) { 684 if ( is_string( $value ) && str_contains( $value, 'var:' ) && ! $should_skip_css_vars && ! empty( $style_definition['css_vars'] ) ) { 685 $value = static::get_css_var_value( $value, $style_definition['css_vars'] ); 686 } 687 688 $individual_property = sprintf( $style_property_keys['individual'], _wp_to_kebab_case( $key ) ); 689 690 if ( $individual_property && is_string( $value ) && static::is_valid_style_value( $value ) ) { 691 $css_declarations[ $individual_property ] = $value; 692 } 693 } 694 695 return $css_declarations; 696 } 697 698 // CSS declarations only accept string values. 699 if ( is_string( $style_value ) ) { 700 $css_declarations[ $style_property_keys['default'] ] = $style_value; 701 } 702 return $css_declarations; 703 } 704 705 /** 706 * Style value parser that returns a CSS definition array comprising style properties 707 * that have keys representing individual style properties, otherwise known as longhand CSS properties. 708 * 709 * Example: 710 * 711 * "$style_property-$individual_feature: $value;" 712 * 713 * Which could represent the following: 714 * 715 * "border-{top|right|bottom|left}-{color|width|style}: {value};" 716 * 717 * or: 718 * 719 * "border-image-{outset|source|width|repeat|slice}: {value};" 720 * 721 * @since 6.1.0 722 * 723 * @param mixed $style_value A single raw style value from `$block_styles` array. 724 * @param array $individual_property_definition A single style definition from {@see self::BLOCK_STYLE_DEFINITIONS_METADATA} 725 * representing an individual property of a CSS property, 726 * e.g. 'top' in 'border-top'. 727 * @param array $options { 728 * Optional. An array of options. Default empty array. 729 * 730 * @type bool $convert_vars_to_classnames Whether to skip converting incoming CSS var patterns, 731 * e.g. `var:preset|<PRESET_TYPE>|<PRESET_SLUG>`, 732 * to `var( --wp--preset--* )` values. Default false. 733 * } 734 * @return string[] An associative array of CSS definitions, e.g. `array( "$property" => "$value", "$property" => "$value" )`. 735 * 736 * @phpstan-param Style_Definition $individual_property_definition 737 */ 738 protected static function get_individual_property_css_declarations( $style_value, $individual_property_definition, $options = array() ) { 739 if ( ! is_array( $style_value ) || empty( $style_value ) || empty( $individual_property_definition['path'] ) ) { 740 return array(); 741 } 742 743 /* 744 * The first item in $individual_property_definition['path'] array 745 * tells us the style property, e.g. "border". We use this to get a corresponding 746 * CSS style definition such as "color" or "width" from the same group. 747 * 748 * The second item in $individual_property_definition['path'] array 749 * refers to the individual property marker, e.g. "top". 750 */ 751 $definition_group_key = $individual_property_definition['path'][0]; 752 $individual_property_key = $individual_property_definition['path'][1]; 753 $should_skip_css_vars = isset( $options['convert_vars_to_classnames'] ) && true === $options['convert_vars_to_classnames']; 754 $css_declarations = array(); 755 756 foreach ( $style_value as $css_property => $value ) { 757 // CSS declarations only accept string values. 758 if ( empty( $value ) || ! is_string( $value ) ) { 759 continue; 760 } 761 762 // Get the corresponding style definition from the same group, e.g. `border.color`. 763 $style_definition = static::BLOCK_STYLE_DEFINITIONS_METADATA[ $definition_group_key ][ $css_property ] ?? null; 764 765 if ( $style_definition && isset( $style_definition['property_keys']['individual'] ) ) { 766 // Set a CSS var if there is a valid preset value. 767 if ( str_contains( $value, 'var:' ) 768 && ! $should_skip_css_vars && ! empty( $individual_property_definition['css_vars'] ) 769 ) { 770 $value = static::get_css_var_value( $value, $individual_property_definition['css_vars'] ); 771 } 772 773 $individual_css_property = sprintf( $style_definition['property_keys']['individual'], $individual_property_key ); 774 775 $css_declarations[ $individual_css_property ] = $value; 776 } 777 } 778 return $css_declarations; 779 } 780 781 /** 782 * Style value parser that constructs a CSS definition array comprising a single CSS property and value. 783 * If the provided value is an array containing a `url` property, the function will return a CSS definition array 784 * with a single property and value, with `url` escaped and injected into a CSS `url()` function, 785 * e.g., array( 'background-image' => "url( '...' )" ). 786 * 787 * @since 6.4.0 788 * 789 * @param mixed $style_value A single raw style value from $block_styles array. 790 * @param array $style_definition A single style definition from {@see self::BLOCK_STYLE_DEFINITIONS_METADATA}. 791 * @return string[] An associative array of CSS definitions, e.g., array( "$property" => "$value", "$property" => "$value" ). 792 * 793 * @phpstan-param Style_Definition $style_definition 794 * @phpstan-return array<string, string> 795 */ 796 protected static function get_url_or_value_css_declaration( $style_value, $style_definition ) { 797 if ( empty( $style_value ) ) { 798 return array(); 799 } 800 801 $css_declarations = array(); 802 803 if ( isset( $style_definition['property_keys']['default'] ) ) { 804 $value = null; 805 806 if ( is_array( $style_value ) && ! empty( $style_value['url'] ) && is_string( $style_value['url'] ) ) { 807 $value = "url('" . $style_value['url'] . "')"; 808 } elseif ( is_string( $style_value ) ) { 809 $value = $style_value; 810 } 811 812 if ( null !== $value ) { 813 $css_declarations[ $style_definition['property_keys']['default'] ] = $value; 814 } 815 } 816 817 return $css_declarations; 818 } 819 820 /** 821 * Style value parser that returns the CSS declarations for background clipping. 822 * 823 * For the `text` value the background is clipped to the block's text, which 824 * requires the vendor prefixed properties and a transparent fill color. 825 * 826 * @since 7.2.0 827 * 828 * @param mixed $style_value A single raw style value from $block_styles array. 829 * @param array $style_definition A single style definition from {@see self::BLOCK_STYLE_DEFINITIONS_METADATA}. 830 * @return array<string, string> An associative array of CSS definitions, mapping properties to values. 831 * 832 * @phpstan-param Style_Definition $style_definition 833 * @phpstan-return array<non-falsy-string, non-falsy-string> 834 */ 835 protected static function get_background_clip_css_declarations( $style_value, $style_definition ) { 836 if ( ! in_array( $style_value, static::VALID_BACKGROUND_CLIP_VALUES, true ) ) { 837 return array(); 838 } 839 840 $css_declarations = array( 841 'background-clip' => $style_value, 842 ); 843 844 if ( 'text' === $style_value ) { 845 $css_declarations['-webkit-background-clip'] = 'text'; 846 $css_declarations['-webkit-text-fill-color'] = 'transparent'; 847 } else { 848 /* 849 * Only the fill color is restored. `-webkit-background-clip` is an 850 * alias of `background-clip` in Chromium, so resetting it here would 851 * discard the value set above. The fill color is inherited, so it 852 * needs its initial value rather than `unset`, which would take a 853 * transparent fill from an ancestor clipping to text. 854 */ 855 $css_declarations['-webkit-text-fill-color'] = 'currentColor'; 856 } 857 858 return $css_declarations; 859 } 860 861 /** 862 * Returns compiled CSS from CSS declarations. 863 * 864 * @since 6.1.0 865 * 866 * @param string[] $css_declarations An associative array of CSS definitions, 867 * e.g. `array( "$property" => "$value", "$property" => "$value" )`. 868 * @param string $css_selector When a selector is passed, the function will return 869 * a full CSS rule `$selector { ...rules }`, 870 * otherwise a concatenated string of properties and values. 871 * @return string A compiled CSS string. 872 */ 873 public static function compile_css( $css_declarations, $css_selector ) { 874 if ( empty( $css_declarations ) || ! is_array( $css_declarations ) ) { 875 return ''; 876 } 877 878 // Return an entire rule if there is a selector. 879 if ( $css_selector ) { 880 $css_rule = new WP_Style_Engine_CSS_Rule( $css_selector, $css_declarations ); 881 return $css_rule->get_css(); 882 } 883 884 $css_declarations = new WP_Style_Engine_CSS_Declarations( $css_declarations ); 885 return $css_declarations->get_declarations_string(); 886 } 887 888 /** 889 * Returns a compiled stylesheet from stored CSS rules. 890 * 891 * @since 6.1.0 892 * 893 * @param WP_Style_Engine_CSS_Rule[] $css_rules An array of WP_Style_Engine_CSS_Rule objects 894 * from a store or otherwise. 895 * @param array $options { 896 * Optional. An array of options. Default empty array. 897 * 898 * @type string|null $context An identifier describing the origin of the style object, 899 * e.g. 'block-supports' or 'global-styles'. Default 'block-supports'. 900 * When set, the style engine will attempt to store the CSS rules. 901 * @type bool $optimize Whether to optimize the CSS output, e.g. combine rules. 902 * Default false. 903 * @type bool $prettify Whether to add new lines and indents to output. 904 * Defaults to whether the `SCRIPT_DEBUG` constant is defined. 905 * } 906 * @return string A compiled stylesheet from stored CSS rules. 907 */ 908 public static function compile_stylesheet_from_css_rules( $css_rules, $options = array() ) { 909 $processor = new WP_Style_Engine_Processor(); 910 $processor->add_rules( $css_rules ); 911 return $processor->get_css( $options ); 912 } 913 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Nov 14 08:20:01 2026 | Cross-referenced by PHPXref |