PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.1.1
Kirki – Freeform Page Builder, Website Builder & Customizer v6.1.1
6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / customizer / packages / modules / css / src / CSS / Output.php
kirki / customizer / packages / modules / css / src / CSS Last commit date
Property 5 months ago Generator.php 5 months ago Output.php 5 months ago Property.php 5 months ago
Output.php
378 lines
1 <?php
2 /**
3 * Handles CSS output for fields.
4 *
5 * @package Kirki
6 * @subpackage Controls
7 * @copyright Copyright (c) 2023, Themeum
8 * @license https://opensource.org/licenses/MIT
9 * @since 2.2.0
10 */
11
12 namespace Kirki\Module\CSS;
13
14 use Kirki\Compatibility\Values;
15 use Kirki\Compatibility\Kirki;
16
17 /**
18 * Handles field CSS output.
19 */
20 class Output {
21
22 /**
23 * The field's `output` argument.
24 *
25 * @access protected
26 * @var array
27 */
28 protected $output = [];
29
30 /**
31 * An array of the generated styles.
32 *
33 * @access protected
34 * @var array
35 */
36 protected $styles = [];
37
38 /**
39 * The field.
40 *
41 * @access protected
42 * @var array
43 */
44 protected $field = [];
45
46 /**
47 * The value.
48 *
49 * @access protected
50 * @var string|array
51 */
52 protected $value;
53
54 /**
55 * The class constructor.
56 *
57 * @access public
58 * @param string $config_id The config ID.
59 * @param array $output The output argument.
60 * @param string|array $value The value.
61 * @param array $field The field.
62 */
63 public function __construct( $config_id, $output, $value, $field ) {
64 $this->value = $value;
65 $this->output = $output;
66 $this->field = $field;
67
68 $this->parse_output();
69 }
70
71 /**
72 * If we have a sanitize_callback defined, apply it to the value.
73 *
74 * @param array $output The output args.
75 * @param string|array $value The value.
76 *
77 * @return string|array
78 */
79 protected function apply_sanitize_callback( $output, $value ) {
80 if ( isset( $output['sanitize_callback'] ) && null !== $output['sanitize_callback'] ) {
81
82 // If the sanitize_callback is invalid, return the value.
83 if ( ! is_callable( $output['sanitize_callback'] ) ) {
84 return $value;
85 }
86 return call_user_func( $output['sanitize_callback'], $this->value );
87 }
88 return $value;
89 }
90
91 /**
92 * If we have a value_pattern defined, apply it to the value.
93 *
94 * @param array $output The output args.
95 * @param string|array $value The value.
96 * @return string|array
97 */
98 protected function apply_value_pattern( $output, $value ) {
99 if ( isset( $output['value_pattern'] ) && is_string( $output['value_pattern'] ) ) {
100 if ( ! is_array( $value ) ) {
101 $value = str_replace( '$', $value, $output['value_pattern'] );
102 }
103 if ( is_array( $value ) ) {
104 foreach ( array_keys( $value ) as $value_k ) {
105 if ( is_array( $value[ $value_k ] ) ) {
106 continue;
107 }
108 if ( isset( $output['choice'] ) ) {
109 if ( $output['choice'] === $value_k ) {
110 $value[ $output['choice'] ] = str_replace( '$', $value[ $output['choice'] ], $output['value_pattern'] );
111 }
112 continue;
113 }
114 $value[ $value_k ] = str_replace( '$', $value[ $value_k ], $output['value_pattern'] );
115 }
116 }
117 $value = $this->apply_pattern_replace( $output, $value );
118 }
119 return $value;
120 }
121
122 /**
123 * If we have a value_pattern defined, apply it to the value.
124 *
125 * @param array $output The output args.
126 * @param string|array $value The value.
127 * @return string|array
128 */
129 protected function apply_pattern_replace( $output, $value ) {
130 if ( isset( $output['pattern_replace'] ) && is_array( $output['pattern_replace'] ) ) {
131 $option_type = ( isset( $this->field['option_type'] ) ) ? $this->field['option_type'] : 'theme_mod';
132 $option_name = ( isset( $this->field['option_name'] ) ) ? $this->field['option_name'] : '';
133 $options = [];
134 if ( $option_name ) {
135 $options = ( 'site_option' === $option_type ) ? get_site_option( $option_name ) : get_option( $option_name );
136 }
137 foreach ( $output['pattern_replace'] as $search => $replace ) {
138 $replacement = '';
139 switch ( $option_type ) {
140 case 'option':
141 if ( is_array( $options ) ) {
142 if ( $option_name ) {
143 $subkey = str_replace( [ $option_name, '[', ']' ], '', $replace );
144 $replacement = ( isset( $options[ $subkey ] ) ) ? $options[ $subkey ] : '';
145 break;
146 }
147 $replacement = ( isset( $options[ $replace ] ) ) ? $options[ $replace ] : '';
148 break;
149 }
150 $replacement = get_option( $replace );
151 break;
152 case 'site_option':
153 $replacement = ( is_array( $options ) && isset( $options[ $replace ] ) ) ? $options[ $replace ] : get_site_option( $replace );
154 break;
155 case 'user_meta':
156 $user_id = get_current_user_id();
157 if ( $user_id ) {
158 $replacement = get_user_meta( $user_id, $replace, true );
159 }
160 break;
161 default:
162 $replacement = get_theme_mod( $replace );
163 if ( ! $replacement ) {
164 $replacement = Values::get_value( $this->field['kirki_config'], $replace );
165 }
166 }
167 $replacement = ( false === $replacement ) ? '' : $replacement;
168 if ( is_array( $value ) ) {
169 foreach ( $value as $k => $v ) {
170 $_val = ( isset( $value[ $v ] ) ) ? $value[ $v ] : $v;
171 $value[ $k ] = str_replace( $search, $replacement, $_val );
172 }
173 return $value;
174 }
175 $value = str_replace( $search, $replacement, $value );
176 }
177 }
178 return $value;
179 }
180
181 /**
182 * Parses the output arguments.
183 * Calls the process_output method for each of them.
184 *
185 * @access protected
186 */
187 protected function parse_output() {
188 foreach ( $this->output as $output ) {
189 $skip = false;
190
191 // Apply any sanitization callbacks defined.
192 $value = $this->apply_sanitize_callback( $output, $this->value );
193
194 // Skip if value is empty.
195 if ( '' === $this->value ) {
196 $skip = true;
197 }
198
199 // No need to proceed this if the current value is the same as in the "exclude" value.
200 if ( isset( $output['exclude'] ) && is_array( $output['exclude'] ) ) {
201 foreach ( $output['exclude'] as $exclude ) {
202 if ( is_array( $value ) ) {
203 if ( is_array( $exclude ) ) {
204 $diff1 = array_diff( $value, $exclude );
205 $diff2 = array_diff( $exclude, $value );
206
207 if ( empty( $diff1 ) && empty( $diff2 ) ) {
208 $skip = true;
209 }
210 }
211 // If 'choice' is defined check for sub-values too.
212 // Fixes https://github.com/aristath/kirki/issues/1416.
213 if ( isset( $output['choice'] ) && isset( $value[ $output['choice'] ] ) && $exclude == $value[ $output['choice'] ] ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
214 $skip = true;
215 }
216 }
217 if ( $skip ) {
218 continue;
219 }
220
221 // Skip if value is defined as excluded.
222 if ( $exclude === $value || ( '' === $exclude && empty( $value ) ) ) {
223 $skip = true;
224 }
225 }
226 }
227 if ( $skip ) {
228 continue;
229 }
230
231 // Apply any value patterns defined.
232 $value = $this->apply_value_pattern( $output, $value );
233
234 if ( isset( $output['element'] ) && is_array( $output['element'] ) ) {
235 $output['element'] = array_unique( $output['element'] );
236 sort( $output['element'] );
237 $output['element'] = implode( ',', $output['element'] );
238 }
239
240 $value = $this->process_value( $value, $output );
241
242 if ( is_admin() ) {
243 // In admin area, only output kirki-styles/kirki-inline-styles inside editing screen.
244 if ( ! isset( $_GET['editor'] ) || 1 !== (int) $_GET['editor'] ) { // phpcs:ignore WordPress.Security.NonceVerification
245 continue;
246 }
247 } else {
248 // Check if this is a frontend style.
249 if ( isset( $output['context'] ) && ! in_array( 'front', $output['context'], true ) ) {
250 continue;
251 }
252 }
253
254 /**
255 * Inside gutenberg editing screen, prepend `.editor-styles-wrapper` to the element
256 * so that it doesn't pollute elements other than inside the editing content.
257 */
258 if ( isset( $_GET['editor'] ) && 1 === (int) $_GET['editor'] ) {
259 if ( isset( $output['element'] ) && ! empty( $output['element'] ) ) {
260
261 if ( -1 < strpos( $output['element'], ',' ) ) {
262 $elms = explode( ',', $output['element'] );
263
264 foreach ( $elms as $index => $elm ) {
265 if ( ! empty( $elm ) ) {
266 $elms[ $index ] = '.editor-styles-wrapper ' . $elm;
267 $elms[ $index ] = str_ireplace( '.editor-styles-wrapper :root', '.editor-styles-wrapper', $elms[ $index ] );
268 }
269 }
270
271 $output['element'] = implode( ',', $elms );
272 } else {
273 $output['element'] = '.editor-styles-wrapper ' . $output['element'];
274 $output['element'] = str_ireplace( '.editor-styles-wrapper :root', '.editor-styles-wrapper', $output['element'] );
275 }
276 }
277 }
278
279 $this->process_output( $output, $value );
280 }
281 }
282
283 /**
284 * Parses an output and creates the styles array for it.
285 *
286 * @access protected
287 * @param array $output The field output.
288 * @param string|array $value The value.
289 *
290 * @return null
291 */
292 protected function process_output( $output, $value ) {
293 $output = apply_filters( 'kirki_output_item_args', $output, $value, $this->output, $this->field );
294
295 if ( ! isset( $output['element'] ) || ! isset( $output['property'] ) ) {
296 return;
297 }
298
299 $output['media_query'] = ( isset( $output['media_query'] ) ) ? $output['media_query'] : 'global';
300 $output['prefix'] = ( isset( $output['prefix'] ) ) ? $output['prefix'] : '';
301 $output['units'] = ( isset( $output['units'] ) ) ? $output['units'] : '';
302 $output['suffix'] = ( isset( $output['suffix'] ) ) ? $output['suffix'] : '';
303
304 // Properties that can accept multiple values.
305 // Useful for example for gradients where all browsers use the "background-image" property
306 // and the browser prefixes go in the value_pattern arg.
307 $accepts_multiple = [
308 'background-image',
309 'background',
310 ];
311
312 if ( in_array( $output['property'], $accepts_multiple, true ) ) {
313 if ( isset( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) && ! is_array( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) ) {
314 $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = (array) $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ];
315 }
316
317 $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ][] = $output['prefix'] . $value . $output['units'] . $output['suffix'];
318 return;
319 }
320
321 if ( is_string( $value ) || is_numeric( $value ) ) {
322 $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value ) . $output['units'] . $output['suffix'];
323 }
324 }
325
326 /**
327 * Some CSS properties are unique.
328 * We need to tweak the value to make everything works as expected.
329 *
330 * @access protected
331 * @param string $property The CSS property.
332 * @param string|array $value The value.
333 *
334 * @return array
335 */
336 protected function process_property_value( $property, $value ) {
337 $properties = apply_filters(
338 'kirki_output_property_classnames',
339 [
340 'font-family' => '\Kirki\Module\CSS\Property\Font_Family',
341 'background-image' => '\Kirki\Module\CSS\Property\Background_Image',
342 'background-position' => '\Kirki\Module\CSS\Property\Background_Position',
343 ]
344 );
345 if ( array_key_exists( $property, $properties ) ) {
346 $classname = $properties[ $property ];
347 $obj = new $classname( $property, $value );
348 return $obj->get_value();
349 }
350 return $value;
351 }
352
353 /**
354 * Returns the value.
355 *
356 * @access protected
357 * @param string|array $value The value.
358 * @param array $output The field "output".
359 * @return string|array
360 */
361 protected function process_value( $value, $output ) {
362 if ( isset( $output['property'] ) ) {
363 return $this->process_property_value( $output['property'], $value );
364 }
365 return $value;
366 }
367
368 /**
369 * Exploses the private $styles property to the world
370 *
371 * @access protected
372 * @return array
373 */
374 public function get_styles() {
375 return $this->styles;
376 }
377 }
378