|
1 <?php |
|
2 /** |
|
3 * Style Engine: WP_Style_Engine_Processor class |
|
4 * |
|
5 * @package WordPress |
|
6 * @subpackage StyleEngine |
|
7 * @since 6.1.0 |
|
8 */ |
|
9 |
|
10 /** |
|
11 * Core class used to compile styles from stores or collection of CSS rules. |
|
12 * |
|
13 * @since 6.1.0 |
|
14 */ |
|
15 #[AllowDynamicProperties] |
|
16 class WP_Style_Engine_Processor { |
|
17 |
|
18 /** |
|
19 * A collection of Style Engine Store objects. |
|
20 * |
|
21 * @since 6.1.0 |
|
22 * @var WP_Style_Engine_CSS_Rules_Store[] |
|
23 */ |
|
24 protected $stores = array(); |
|
25 |
|
26 /** |
|
27 * The set of CSS rules that this processor will work on. |
|
28 * |
|
29 * @since 6.1.0 |
|
30 * @var WP_Style_Engine_CSS_Rule[] |
|
31 */ |
|
32 protected $css_rules = array(); |
|
33 |
|
34 /** |
|
35 * Adds a store to the processor. |
|
36 * |
|
37 * @since 6.1.0 |
|
38 * |
|
39 * @param WP_Style_Engine_CSS_Rules_Store $store The store to add. |
|
40 * @return WP_Style_Engine_Processor Returns the object to allow chaining methods. |
|
41 */ |
|
42 public function add_store( $store ) { |
|
43 if ( ! $store instanceof WP_Style_Engine_CSS_Rules_Store ) { |
|
44 _doing_it_wrong( |
|
45 __METHOD__, |
|
46 __( '$store must be an instance of WP_Style_Engine_CSS_Rules_Store' ), |
|
47 '6.1.0' |
|
48 ); |
|
49 return $this; |
|
50 } |
|
51 |
|
52 $this->stores[ $store->get_name() ] = $store; |
|
53 |
|
54 return $this; |
|
55 } |
|
56 |
|
57 /** |
|
58 * Adds rules to be processed. |
|
59 * |
|
60 * @since 6.1.0 |
|
61 * @since 6.6.0 Added support for rules_group. |
|
62 * |
|
63 * @param WP_Style_Engine_CSS_Rule|WP_Style_Engine_CSS_Rule[] $css_rules A single, or an array of, |
|
64 * WP_Style_Engine_CSS_Rule objects |
|
65 * from a store or otherwise. |
|
66 * @return WP_Style_Engine_Processor Returns the object to allow chaining methods. |
|
67 */ |
|
68 public function add_rules( $css_rules ) { |
|
69 if ( ! is_array( $css_rules ) ) { |
|
70 $css_rules = array( $css_rules ); |
|
71 } |
|
72 |
|
73 foreach ( $css_rules as $rule ) { |
|
74 $selector = $rule->get_selector(); |
|
75 $rules_group = $rule->get_rules_group(); |
|
76 |
|
77 /** |
|
78 * If there is a rules_group and it already exists in the css_rules array, |
|
79 * add the rule to it. |
|
80 * Otherwise, create a new entry for the rules_group. |
|
81 */ |
|
82 if ( ! empty( $rules_group ) ) { |
|
83 if ( isset( $this->css_rules[ "$rules_group $selector" ] ) ) { |
|
84 $this->css_rules[ "$rules_group $selector" ]->add_declarations( $rule->get_declarations() ); |
|
85 continue; |
|
86 } |
|
87 $this->css_rules[ "$rules_group $selector" ] = $rule; |
|
88 continue; |
|
89 } |
|
90 |
|
91 // If the selector already exists, add the declarations to it. |
|
92 if ( isset( $this->css_rules[ $selector ] ) ) { |
|
93 $this->css_rules[ $selector ]->add_declarations( $rule->get_declarations() ); |
|
94 continue; |
|
95 } |
|
96 $this->css_rules[ $rule->get_selector() ] = $rule; |
|
97 } |
|
98 |
|
99 return $this; |
|
100 } |
|
101 |
|
102 /** |
|
103 * Gets the CSS rules as a string. |
|
104 * |
|
105 * @since 6.1.0 |
|
106 * @since 6.4.0 The Optimization is no longer the default. |
|
107 * |
|
108 * @param array $options { |
|
109 * Optional. An array of options. Default empty array. |
|
110 * |
|
111 * @type bool $optimize Whether to optimize the CSS output, e.g. combine rules. |
|
112 * Default false. |
|
113 * @type bool $prettify Whether to add new lines and indents to output. |
|
114 * Defaults to whether the `SCRIPT_DEBUG` constant is defined. |
|
115 * } |
|
116 * @return string The computed CSS. |
|
117 */ |
|
118 public function get_css( $options = array() ) { |
|
119 $defaults = array( |
|
120 'optimize' => false, |
|
121 'prettify' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG, |
|
122 ); |
|
123 $options = wp_parse_args( $options, $defaults ); |
|
124 |
|
125 // If we have stores, get the rules from them. |
|
126 foreach ( $this->stores as $store ) { |
|
127 $this->add_rules( $store->get_all_rules() ); |
|
128 } |
|
129 |
|
130 // Combine CSS selectors that have identical declarations. |
|
131 if ( true === $options['optimize'] ) { |
|
132 $this->combine_rules_selectors(); |
|
133 } |
|
134 |
|
135 // Build the CSS. |
|
136 $css = ''; |
|
137 foreach ( $this->css_rules as $rule ) { |
|
138 // See class WP_Style_Engine_CSS_Rule for the get_css method. |
|
139 $css .= $rule->get_css( $options['prettify'] ); |
|
140 $css .= $options['prettify'] ? "\n" : ''; |
|
141 } |
|
142 return $css; |
|
143 } |
|
144 |
|
145 /** |
|
146 * Combines selectors from the rules store when they have the same styles. |
|
147 * |
|
148 * @since 6.1.0 |
|
149 */ |
|
150 private function combine_rules_selectors() { |
|
151 // Build an array of selectors along with the JSON-ified styles to make comparisons easier. |
|
152 $selectors_json = array(); |
|
153 foreach ( $this->css_rules as $rule ) { |
|
154 $declarations = $rule->get_declarations()->get_declarations(); |
|
155 ksort( $declarations ); |
|
156 $selectors_json[ $rule->get_selector() ] = wp_json_encode( $declarations ); |
|
157 } |
|
158 |
|
159 // Combine selectors that have the same styles. |
|
160 foreach ( $selectors_json as $selector => $json ) { |
|
161 // Get selectors that use the same styles. |
|
162 $duplicates = array_keys( $selectors_json, $json, true ); |
|
163 // Skip if there are no duplicates. |
|
164 if ( 1 >= count( $duplicates ) ) { |
|
165 continue; |
|
166 } |
|
167 |
|
168 $declarations = $this->css_rules[ $selector ]->get_declarations(); |
|
169 |
|
170 foreach ( $duplicates as $key ) { |
|
171 // Unset the duplicates from the $selectors_json array to avoid looping through them as well. |
|
172 unset( $selectors_json[ $key ] ); |
|
173 // Remove the rules from the rules collection. |
|
174 unset( $this->css_rules[ $key ] ); |
|
175 } |
|
176 // Create a new rule with the combined selectors. |
|
177 $duplicate_selectors = implode( ',', $duplicates ); |
|
178 $this->css_rules[ $duplicate_selectors ] = new WP_Style_Engine_CSS_Rule( $duplicate_selectors, $declarations ); |
|
179 } |
|
180 } |
|
181 } |