[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/style-engine/ -> class-wp-style-engine.php (source)

   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   */
  30  #[AllowDynamicProperties]
  31  final class WP_Style_Engine {
  32      /**
  33       * Style definitions that contain the instructions to parse/output valid Gutenberg styles from a block's attributes.
  34       *
  35       * For every style definition, the following properties are valid:
  36       *
  37       *  - classnames    => (array) an array of classnames to be returned for block styles. The key is a classname or pattern.
  38       *                    A value of `true` means the classname should be applied always. Otherwise, a valid CSS property (string)
  39       *                    to match the incoming value, e.g., "color" to match var:preset|color|somePresetSlug.
  40       *  - css_vars      => (array) an array of key value pairs used to generate CSS var values.
  41       *                     The key should be the CSS property name that matches the second element of the preset string value,
  42       *                     i.e., "color" in var:preset|color|somePresetSlug. The value is a CSS var pattern (e.g. `--wp--preset--color--$slug`),
  43       *                     whose `$slug` fragment will be replaced with the preset slug, which is the third element of the preset string value,
  44       *                     i.e., `somePresetSlug` in var:preset|color|somePresetSlug.
  45       *  - property_keys => (array) array of keys whose values represent a valid CSS property, e.g., "margin" or "border".
  46       *  - path          => (array) a path that accesses the corresponding style value in the block style object.
  47       *  - 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", ... )`.
  48       *
  49       * @since 6.1.0
  50       * @var array
  51       */
  52      const BLOCK_STYLE_DEFINITIONS_METADATA = array(
  53          'background' => array(
  54              'backgroundImage'      => array(
  55                  'property_keys' => array(
  56                      'default' => 'background-image',
  57                  ),
  58                  'value_func'    => array( self::class, 'get_url_or_value_css_declaration' ),
  59                  'path'          => array( 'background', 'backgroundImage' ),
  60              ),
  61              'backgroundPosition'   => array(
  62                  'property_keys' => array(
  63                      'default' => 'background-position',
  64                  ),
  65                  'path'          => array( 'background', 'backgroundPosition' ),
  66              ),
  67              'backgroundRepeat'     => array(
  68                  'property_keys' => array(
  69                      'default' => 'background-repeat',
  70                  ),
  71                  'path'          => array( 'background', 'backgroundRepeat' ),
  72              ),
  73              'backgroundSize'       => array(
  74                  'property_keys' => array(
  75                      'default' => 'background-size',
  76                  ),
  77                  'path'          => array( 'background', 'backgroundSize' ),
  78              ),
  79              'backgroundAttachment' => array(
  80                  'property_keys' => array(
  81                      'default' => 'background-attachment',
  82                  ),
  83                  'path'          => array( 'background', 'backgroundAttachment' ),
  84              ),
  85          ),
  86          'color'      => array(
  87              'text'       => array(
  88                  'property_keys' => array(
  89                      'default' => 'color',
  90                  ),
  91                  'path'          => array( 'color', 'text' ),
  92                  'css_vars'      => array(
  93                      'color' => '--wp--preset--color--$slug',
  94                  ),
  95                  'classnames'    => array(
  96                      'has-text-color'  => true,
  97                      'has-$slug-color' => 'color',
  98                  ),
  99              ),
 100              'background' => array(
 101                  'property_keys' => array(
 102                      'default' => 'background-color',
 103                  ),
 104                  'path'          => array( 'color', 'background' ),
 105                  'css_vars'      => array(
 106                      'color' => '--wp--preset--color--$slug',
 107                  ),
 108                  'classnames'    => array(
 109                      'has-background'             => true,
 110                      'has-$slug-background-color' => 'color',
 111                  ),
 112              ),
 113              'gradient'   => array(
 114                  'property_keys' => array(
 115                      'default' => 'background',
 116                  ),
 117                  'path'          => array( 'color', 'gradient' ),
 118                  'css_vars'      => array(
 119                      'gradient' => '--wp--preset--gradient--$slug',
 120                  ),
 121                  'classnames'    => array(
 122                      'has-background'                => true,
 123                      'has-$slug-gradient-background' => 'gradient',
 124                  ),
 125              ),
 126          ),
 127          'border'     => array(
 128              'color'  => array(
 129                  'property_keys' => array(
 130                      'default'    => 'border-color',
 131                      'individual' => 'border-%s-color',
 132                  ),
 133                  'path'          => array( 'border', 'color' ),
 134                  'classnames'    => array(
 135                      'has-border-color'       => true,
 136                      'has-$slug-border-color' => 'color',
 137                  ),
 138              ),
 139              'radius' => array(
 140                  'property_keys' => array(
 141                      'default'    => 'border-radius',
 142                      'individual' => 'border-%s-radius',
 143                  ),
 144                  'path'          => array( 'border', 'radius' ),
 145                  'css_vars'      => array(
 146                      'border-radius' => '--wp--preset--border-radius--$slug',
 147                  ),
 148              ),
 149              'style'  => array(
 150                  'property_keys' => array(
 151                      'default'    => 'border-style',
 152                      'individual' => 'border-%s-style',
 153                  ),
 154                  'path'          => array( 'border', 'style' ),
 155              ),
 156              'width'  => array(
 157                  'property_keys' => array(
 158                      'default'    => 'border-width',
 159                      'individual' => 'border-%s-width',
 160                  ),
 161                  'path'          => array( 'border', 'width' ),
 162              ),
 163              'top'    => array(
 164                  'value_func' => array( self::class, 'get_individual_property_css_declarations' ),
 165                  'path'       => array( 'border', 'top' ),
 166                  'css_vars'   => array(
 167                      'color' => '--wp--preset--color--$slug',
 168                  ),
 169              ),
 170              'right'  => array(
 171                  'value_func' => array( self::class, 'get_individual_property_css_declarations' ),
 172                  'path'       => array( 'border', 'right' ),
 173                  'css_vars'   => array(
 174                      'color' => '--wp--preset--color--$slug',
 175                  ),
 176              ),
 177              'bottom' => array(
 178                  'value_func' => array( self::class, 'get_individual_property_css_declarations' ),
 179                  'path'       => array( 'border', 'bottom' ),
 180                  'css_vars'   => array(
 181                      'color' => '--wp--preset--color--$slug',
 182                  ),
 183              ),
 184              'left'   => array(
 185                  'value_func' => array( self::class, 'get_individual_property_css_declarations' ),
 186                  'path'       => array( 'border', 'left' ),
 187                  'css_vars'   => array(
 188                      'color' => '--wp--preset--color--$slug',
 189                  ),
 190              ),
 191          ),
 192          'shadow'     => array(
 193              'shadow' => array(
 194                  'property_keys' => array(
 195                      'default' => 'box-shadow',
 196                  ),
 197                  'path'          => array( 'shadow' ),
 198                  'css_vars'      => array(
 199                      'shadow' => '--wp--preset--shadow--$slug',
 200                  ),
 201              ),
 202          ),
 203          'dimensions' => array(
 204              'aspectRatio' => array(
 205                  'property_keys' => array(
 206                      'default' => 'aspect-ratio',
 207                  ),
 208                  'path'          => array( 'dimensions', 'aspectRatio' ),
 209                  'classnames'    => array(
 210                      'has-aspect-ratio' => true,
 211                  ),
 212              ),
 213              'minHeight'   => array(
 214                  'property_keys' => array(
 215                      'default' => 'min-height',
 216                  ),
 217                  'path'          => array( 'dimensions', 'minHeight' ),
 218                  'css_vars'      => array(
 219                      'spacing' => '--wp--preset--spacing--$slug',
 220                  ),
 221              ),
 222          ),
 223          'spacing'    => array(
 224              'padding' => array(
 225                  'property_keys' => array(
 226                      'default'    => 'padding',
 227                      'individual' => 'padding-%s',
 228                  ),
 229                  'path'          => array( 'spacing', 'padding' ),
 230                  'css_vars'      => array(
 231                      'spacing' => '--wp--preset--spacing--$slug',
 232                  ),
 233              ),
 234              'margin'  => array(
 235                  'property_keys' => array(
 236                      'default'    => 'margin',
 237                      'individual' => 'margin-%s',
 238                  ),
 239                  'path'          => array( 'spacing', 'margin' ),
 240                  'css_vars'      => array(
 241                      'spacing' => '--wp--preset--spacing--$slug',
 242                  ),
 243              ),
 244          ),
 245          'typography' => array(
 246              'fontSize'       => array(
 247                  'property_keys' => array(
 248                      'default' => 'font-size',
 249                  ),
 250                  'path'          => array( 'typography', 'fontSize' ),
 251                  'css_vars'      => array(
 252                      'font-size' => '--wp--preset--font-size--$slug',
 253                  ),
 254                  'classnames'    => array(
 255                      'has-$slug-font-size' => 'font-size',
 256                  ),
 257              ),
 258              'fontFamily'     => array(
 259                  'property_keys' => array(
 260                      'default' => 'font-family',
 261                  ),
 262                  'css_vars'      => array(
 263                      'font-family' => '--wp--preset--font-family--$slug',
 264                  ),
 265                  'path'          => array( 'typography', 'fontFamily' ),
 266                  'classnames'    => array(
 267                      'has-$slug-font-family' => 'font-family',
 268                  ),
 269              ),
 270              'fontStyle'      => array(
 271                  'property_keys' => array(
 272                      'default' => 'font-style',
 273                  ),
 274                  'path'          => array( 'typography', 'fontStyle' ),
 275              ),
 276              'fontWeight'     => array(
 277                  'property_keys' => array(
 278                      'default' => 'font-weight',
 279                  ),
 280                  'path'          => array( 'typography', 'fontWeight' ),
 281              ),
 282              'lineHeight'     => array(
 283                  'property_keys' => array(
 284                      'default' => 'line-height',
 285                  ),
 286                  'path'          => array( 'typography', 'lineHeight' ),
 287              ),
 288              'textColumns'    => array(
 289                  'property_keys' => array(
 290                      'default' => 'column-count',
 291                  ),
 292                  'path'          => array( 'typography', 'textColumns' ),
 293              ),
 294              'textDecoration' => array(
 295                  'property_keys' => array(
 296                      'default' => 'text-decoration',
 297                  ),
 298                  'path'          => array( 'typography', 'textDecoration' ),
 299              ),
 300              'textTransform'  => array(
 301                  'property_keys' => array(
 302                      'default' => 'text-transform',
 303                  ),
 304                  'path'          => array( 'typography', 'textTransform' ),
 305              ),
 306              'letterSpacing'  => array(
 307                  'property_keys' => array(
 308                      'default' => 'letter-spacing',
 309                  ),
 310                  'path'          => array( 'typography', 'letterSpacing' ),
 311              ),
 312              'writingMode'    => array(
 313                  'property_keys' => array(
 314                      'default' => 'writing-mode',
 315                  ),
 316                  'path'          => array( 'typography', 'writingMode' ),
 317              ),
 318          ),
 319      );
 320  
 321      /**
 322       * Util: Extracts the slug in kebab case from a preset string,
 323       * e.g. `heavenly-blue` from `var:preset|color|heavenlyBlue`.
 324       *
 325       * @since 6.1.0
 326       *
 327       * @param string $style_value  A single CSS preset value.
 328       * @param string $property_key The CSS property that is the second element of the preset string.
 329       *                             Used for matching.
 330       * @return string The slug, or empty string if not found.
 331       */
 332  	protected static function get_slug_from_preset_value( $style_value, $property_key ) {
 333          if ( is_string( $style_value ) && is_string( $property_key )
 334              && str_contains( $style_value, "var:preset|{$property_key}|" )
 335          ) {
 336              $index_to_splice = strrpos( $style_value, '|' ) + 1;
 337              return _wp_to_kebab_case( substr( $style_value, $index_to_splice ) );
 338          }
 339          return '';
 340      }
 341  
 342      /**
 343       * Util: Generates a CSS var string, e.g. `var(--wp--preset--color--background)`
 344       * from a preset string such as `var:preset|space|50`.
 345       *
 346       * @since 6.1.0
 347       *
 348       * @param string   $style_value  A single CSS preset value.
 349       * @param string[] $css_vars     An associate array of CSS var patterns
 350       *                               used to generate the var string.
 351       * @return string The CSS var, or an empty string if no match for slug found.
 352       */
 353  	protected static function get_css_var_value( $style_value, $css_vars ) {
 354          foreach ( $css_vars as $property_key => $css_var_pattern ) {
 355              $slug = static::get_slug_from_preset_value( $style_value, $property_key );
 356              if ( static::is_valid_style_value( $slug ) ) {
 357                  $var = strtr(
 358                      $css_var_pattern,
 359                      array( '$slug' => $slug )
 360                  );
 361                  return "var($var)";
 362              }
 363          }
 364          return '';
 365      }
 366  
 367      /**
 368       * Util: Checks whether an incoming block style value is valid.
 369       *
 370       * @since 6.1.0
 371       *
 372       * @param string $style_value A single CSS preset value.
 373       * @return bool
 374       */
 375  	protected static function is_valid_style_value( $style_value ) {
 376          return '0' === $style_value || ! empty( $style_value );
 377      }
 378  
 379      /**
 380       * Stores a CSS rule using the provided CSS selector and CSS declarations.
 381       *
 382       * @since 6.1.0
 383       * @since 6.6.0 Added the `$rules_group` parameter.
 384       *
 385       * @param string   $store_name       A valid store key.
 386       * @param string   $css_selector     When a selector is passed, the function will return
 387       *                                   a full CSS rule `$selector { ...rules }`
 388       *                                   otherwise a concatenated string of properties and values.
 389       * @param string[] $css_declarations An associative array of CSS definitions,
 390       *                                   e.g. `array( "$property" => "$value", "$property" => "$value" )`.
 391       * @param string $rules_group        Optional. A parent CSS selector in the case of nested CSS, or a CSS nested @rule,
 392       *                                   such as `@media (min-width: 80rem)` or `@layer module`.
 393       */
 394  	public static function store_css_rule( $store_name, $css_selector, $css_declarations, $rules_group = '' ) {
 395          if ( empty( $store_name ) || empty( $css_selector ) || empty( $css_declarations ) ) {
 396              return;
 397          }
 398          static::get_store( $store_name )->add_rule( $css_selector, $rules_group )->add_declarations( $css_declarations );
 399      }
 400  
 401      /**
 402       * Returns a store by store key.
 403       *
 404       * @since 6.1.0
 405       *
 406       * @param string $store_name A store key.
 407       * @return WP_Style_Engine_CSS_Rules_Store|null
 408       */
 409  	public static function get_store( $store_name ) {
 410          return WP_Style_Engine_CSS_Rules_Store::get_store( $store_name );
 411      }
 412  
 413      /**
 414       * Returns classnames and CSS based on the values in a styles object.
 415       *
 416       * Return values are parsed based on the instructions in BLOCK_STYLE_DEFINITIONS_METADATA.
 417       *
 418       * @since 6.1.0
 419       *
 420       * @param array $block_styles The style object.
 421       * @param array $options      {
 422       *     Optional. An array of options. Default empty array.
 423       *
 424       *     @type bool        $convert_vars_to_classnames Whether to skip converting incoming CSS var patterns,
 425       *                                                   e.g. `var:preset|<PRESET_TYPE>|<PRESET_SLUG>`,
 426       *                                                   to `var( --wp--preset--* )` values. Default false.
 427       *     @type string      $selector                   Optional. When a selector is passed,
 428       *                                                   the value of `$css` in the return value will comprise
 429       *                                                   a full CSS rule `$selector { ...$css_declarations }`,
 430       *                                                   otherwise, the value will be a concatenated string
 431       *                                                   of CSS declarations.
 432       * }
 433       * @return array {
 434       *     @type string[] $classnames   Array of class names.
 435       *     @type string[] $declarations An associative array of CSS definitions,
 436       *                                  e.g. `array( "$property" => "$value", "$property" => "$value" )`.
 437       * }
 438       */
 439  	public static function parse_block_styles( $block_styles, $options ) {
 440          $parsed_styles = array(
 441              'classnames'   => array(),
 442              'declarations' => array(),
 443          );
 444          if ( empty( $block_styles ) || ! is_array( $block_styles ) ) {
 445              return $parsed_styles;
 446          }
 447  
 448          // Collect CSS and classnames.
 449          foreach ( static::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group_key => $definition_group_style ) {
 450              if ( empty( $block_styles[ $definition_group_key ] ) ) {
 451                  continue;
 452              }
 453              foreach ( $definition_group_style as $style_definition ) {
 454                  $style_value = _wp_array_get( $block_styles, $style_definition['path'], null );
 455  
 456                  if ( ! static::is_valid_style_value( $style_value ) ) {
 457                      continue;
 458                  }
 459  
 460                  $classnames = static::get_classnames( $style_value, $style_definition );
 461                  if ( ! empty( $classnames ) ) {
 462                      $parsed_styles['classnames'] = array_merge( $parsed_styles['classnames'], $classnames );
 463                  }
 464  
 465                  $css_declarations = static::get_css_declarations( $style_value, $style_definition, $options );
 466                  if ( ! empty( $css_declarations ) ) {
 467                      $parsed_styles['declarations'] = array_merge( $parsed_styles['declarations'], $css_declarations );
 468                  }
 469              }
 470          }
 471  
 472          return $parsed_styles;
 473      }
 474  
 475      /**
 476       * Returns classnames, and generates classname(s) from a CSS preset property pattern,
 477       * e.g. `var:preset|<PRESET_TYPE>|<PRESET_SLUG>`.
 478       *
 479       * @since 6.1.0
 480       *
 481       * @param string $style_value      A single raw style value or CSS preset property
 482       *                                 from the `$block_styles` array.
 483       * @param array  $style_definition A single style definition from BLOCK_STYLE_DEFINITIONS_METADATA.
 484       * @return string[] An array of CSS classnames, or empty array if there are none.
 485       */
 486  	protected static function get_classnames( $style_value, $style_definition ) {
 487          if ( empty( $style_value ) ) {
 488              return array();
 489          }
 490  
 491          $classnames = array();
 492          if ( ! empty( $style_definition['classnames'] ) ) {
 493              foreach ( $style_definition['classnames'] as $classname => $property_key ) {
 494                  if ( true === $property_key ) {
 495                      $classnames[] = $classname;
 496                      continue;
 497                  }
 498  
 499                  $slug = static::get_slug_from_preset_value( $style_value, $property_key );
 500  
 501                  if ( $slug ) {
 502                      /*
 503                       * Right now we expect a classname pattern to be stored in BLOCK_STYLE_DEFINITIONS_METADATA.
 504                       * One day, if there are no stored schemata, we could allow custom patterns or
 505                       * generate classnames based on other properties
 506                       * such as a path or a value or a prefix passed in options.
 507                       */
 508                      $classnames[] = strtr( $classname, array( '$slug' => $slug ) );
 509                  }
 510              }
 511          }
 512  
 513          return $classnames;
 514      }
 515  
 516      /**
 517       * Returns an array of CSS declarations based on valid block style values.
 518       *
 519       * @since 6.1.0
 520       *
 521       * @param mixed $style_value      A single raw style value from $block_styles array.
 522       * @param array $style_definition A single style definition from BLOCK_STYLE_DEFINITIONS_METADATA.
 523       * @param array $options          {
 524       *     Optional. An array of options. Default empty array.
 525       *
 526       *     @type bool $convert_vars_to_classnames Whether to skip converting incoming CSS var patterns,
 527       *                                            e.g. `var:preset|<PRESET_TYPE>|<PRESET_SLUG>`,
 528       *                                            to `var( --wp--preset--* )` values. Default false.
 529       * }
 530       * @return string[] An associative array of CSS definitions, e.g. `array( "$property" => "$value", "$property" => "$value" )`.
 531       */
 532  	protected static function get_css_declarations( $style_value, $style_definition, $options = array() ) {
 533          if ( isset( $style_definition['value_func'] ) && is_callable( $style_definition['value_func'] ) ) {
 534              return call_user_func( $style_definition['value_func'], $style_value, $style_definition, $options );
 535          }
 536  
 537          $css_declarations     = array();
 538          $style_property_keys  = $style_definition['property_keys'];
 539          $should_skip_css_vars = isset( $options['convert_vars_to_classnames'] ) && true === $options['convert_vars_to_classnames'];
 540  
 541          /*
 542           * Build CSS var values from `var:preset|<PRESET_TYPE>|<PRESET_SLUG>` values, e.g, `var(--wp--css--rule-slug )`.
 543           * Check if the value is a CSS preset and there's a corresponding css_var pattern in the style definition.
 544           */
 545          if ( is_string( $style_value ) && str_contains( $style_value, 'var:' ) ) {
 546              if ( ! $should_skip_css_vars && ! empty( $style_definition['css_vars'] ) ) {
 547                  $css_var = static::get_css_var_value( $style_value, $style_definition['css_vars'] );
 548                  if ( static::is_valid_style_value( $css_var ) ) {
 549                      $css_declarations[ $style_property_keys['default'] ] = $css_var;
 550                  }
 551              }
 552              return $css_declarations;
 553          }
 554  
 555          /*
 556           * Default rule builder.
 557           * If the input contains an array, assume box model-like properties
 558           * for styles such as margins and padding.
 559           */
 560          if ( is_array( $style_value ) ) {
 561              // Bail out early if the `'individual'` property is not defined.
 562              if ( ! isset( $style_property_keys['individual'] ) ) {
 563                  return $css_declarations;
 564              }
 565  
 566              foreach ( $style_value as $key => $value ) {
 567                  if ( is_string( $value ) && str_contains( $value, 'var:' ) && ! $should_skip_css_vars && ! empty( $style_definition['css_vars'] ) ) {
 568                      $value = static::get_css_var_value( $value, $style_definition['css_vars'] );
 569                  }
 570  
 571                  $individual_property = sprintf( $style_property_keys['individual'], _wp_to_kebab_case( $key ) );
 572  
 573                  if ( $individual_property && static::is_valid_style_value( $value ) ) {
 574                      $css_declarations[ $individual_property ] = $value;
 575                  }
 576              }
 577  
 578              return $css_declarations;
 579          }
 580  
 581          $css_declarations[ $style_property_keys['default'] ] = $style_value;
 582          return $css_declarations;
 583      }
 584  
 585      /**
 586       * Style value parser that returns a CSS definition array comprising style properties
 587       * that have keys representing individual style properties, otherwise known as longhand CSS properties.
 588       *
 589       * Example:
 590       *
 591       *     "$style_property-$individual_feature: $value;"
 592       *
 593       * Which could represent the following:
 594       *
 595       *     "border-{top|right|bottom|left}-{color|width|style}: {value};"
 596       *
 597       * or:
 598       *
 599       *     "border-image-{outset|source|width|repeat|slice}: {value};"
 600       *
 601       * @since 6.1.0
 602       *
 603       * @param array $style_value                    A single raw style value from `$block_styles` array.
 604       * @param array $individual_property_definition A single style definition from BLOCK_STYLE_DEFINITIONS_METADATA
 605       *                                              representing an individual property of a CSS property,
 606       *                                              e.g. 'top' in 'border-top'.
 607       * @param array $options                        {
 608       *     Optional. An array of options. Default empty array.
 609       *
 610       *     @type bool $convert_vars_to_classnames Whether to skip converting incoming CSS var patterns,
 611       *                                            e.g. `var:preset|<PRESET_TYPE>|<PRESET_SLUG>`,
 612       *                                            to `var( --wp--preset--* )` values. Default false.
 613       * }
 614       * @return string[] An associative array of CSS definitions, e.g. `array( "$property" => "$value", "$property" => "$value" )`.
 615       */
 616  	protected static function get_individual_property_css_declarations( $style_value, $individual_property_definition, $options = array() ) {
 617          if ( ! is_array( $style_value ) || empty( $style_value ) || empty( $individual_property_definition['path'] ) ) {
 618              return array();
 619          }
 620  
 621          /*
 622           * The first item in $individual_property_definition['path'] array
 623           * tells us the style property, e.g. "border". We use this to get a corresponding
 624           * CSS style definition such as "color" or "width" from the same group.
 625           *
 626           * The second item in $individual_property_definition['path'] array
 627           * refers to the individual property marker, e.g. "top".
 628           */
 629          $definition_group_key    = $individual_property_definition['path'][0];
 630          $individual_property_key = $individual_property_definition['path'][1];
 631          $should_skip_css_vars    = isset( $options['convert_vars_to_classnames'] ) && true === $options['convert_vars_to_classnames'];
 632          $css_declarations        = array();
 633  
 634          foreach ( $style_value as $css_property => $value ) {
 635              if ( empty( $value ) ) {
 636                  continue;
 637              }
 638  
 639              // Build a path to the individual rules in definitions.
 640              $style_definition_path = array( $definition_group_key, $css_property );
 641              $style_definition      = _wp_array_get( static::BLOCK_STYLE_DEFINITIONS_METADATA, $style_definition_path, null );
 642  
 643              if ( $style_definition && isset( $style_definition['property_keys']['individual'] ) ) {
 644                  // Set a CSS var if there is a valid preset value.
 645                  if ( is_string( $value ) && str_contains( $value, 'var:' )
 646                      && ! $should_skip_css_vars && ! empty( $individual_property_definition['css_vars'] )
 647                  ) {
 648                      $value = static::get_css_var_value( $value, $individual_property_definition['css_vars'] );
 649                  }
 650  
 651                  $individual_css_property = sprintf( $style_definition['property_keys']['individual'], $individual_property_key );
 652  
 653                  $css_declarations[ $individual_css_property ] = $value;
 654              }
 655          }
 656          return $css_declarations;
 657      }
 658  
 659      /**
 660       * Style value parser that constructs a CSS definition array comprising a single CSS property and value.
 661       * If the provided value is an array containing a `url` property, the function will return a CSS definition array
 662       * with a single property and value, with `url` escaped and injected into a CSS `url()` function,
 663       * e.g., array( 'background-image' => "url( '...' )" ).
 664       *
 665       * @since 6.4.0
 666       *
 667       * @param array $style_value      A single raw style value from $block_styles array.
 668       * @param array $style_definition A single style definition from BLOCK_STYLE_DEFINITIONS_METADATA.
 669       * @return string[] An associative array of CSS definitions, e.g., array( "$property" => "$value", "$property" => "$value" ).
 670       */
 671  	protected static function get_url_or_value_css_declaration( $style_value, $style_definition ) {
 672          if ( empty( $style_value ) ) {
 673              return array();
 674          }
 675  
 676          $css_declarations = array();
 677  
 678          if ( isset( $style_definition['property_keys']['default'] ) ) {
 679              $value = null;
 680  
 681              if ( ! empty( $style_value['url'] ) ) {
 682                  $value = "url('" . $style_value['url'] . "')";
 683              } elseif ( is_string( $style_value ) ) {
 684                  $value = $style_value;
 685              }
 686  
 687              if ( null !== $value ) {
 688                  $css_declarations[ $style_definition['property_keys']['default'] ] = $value;
 689              }
 690          }
 691  
 692          return $css_declarations;
 693      }
 694  
 695      /**
 696       * Returns compiled CSS from CSS declarations.
 697       *
 698       * @since 6.1.0
 699       *
 700       * @param string[] $css_declarations An associative array of CSS definitions,
 701       *                                   e.g. `array( "$property" => "$value", "$property" => "$value" )`.
 702       * @param string   $css_selector     When a selector is passed, the function will return
 703       *                                   a full CSS rule `$selector { ...rules }`,
 704       *                                   otherwise a concatenated string of properties and values.
 705       * @return string A compiled CSS string.
 706       */
 707  	public static function compile_css( $css_declarations, $css_selector ) {
 708          if ( empty( $css_declarations ) || ! is_array( $css_declarations ) ) {
 709              return '';
 710          }
 711  
 712          // Return an entire rule if there is a selector.
 713          if ( $css_selector ) {
 714              $css_rule = new WP_Style_Engine_CSS_Rule( $css_selector, $css_declarations );
 715              return $css_rule->get_css();
 716          }
 717  
 718          $css_declarations = new WP_Style_Engine_CSS_Declarations( $css_declarations );
 719          return $css_declarations->get_declarations_string();
 720      }
 721  
 722      /**
 723       * Returns a compiled stylesheet from stored CSS rules.
 724       *
 725       * @since 6.1.0
 726       *
 727       * @param WP_Style_Engine_CSS_Rule[] $css_rules An array of WP_Style_Engine_CSS_Rule objects
 728       *                                              from a store or otherwise.
 729       * @param array                      $options   {
 730       *     Optional. An array of options. Default empty array.
 731       *
 732       *     @type string|null $context  An identifier describing the origin of the style object,
 733       *                                 e.g. 'block-supports' or 'global-styles'. Default 'block-supports'.
 734       *                                 When set, the style engine will attempt to store the CSS rules.
 735       *     @type bool        $optimize Whether to optimize the CSS output, e.g. combine rules.
 736       *                                 Default false.
 737       *     @type bool        $prettify Whether to add new lines and indents to output.
 738       *                                 Defaults to whether the `SCRIPT_DEBUG` constant is defined.
 739       * }
 740       * @return string A compiled stylesheet from stored CSS rules.
 741       */
 742  	public static function compile_stylesheet_from_css_rules( $css_rules, $options = array() ) {
 743          $processor = new WP_Style_Engine_Processor();
 744          $processor->add_rules( $css_rules );
 745          return $processor->get_css( $options );
 746      }
 747  }


Generated : Fri Oct 10 08:20:03 2025 Cross-referenced by PHPXref