Passed
Push — develop ( fe986b...95ca01 )
by Aristeides
04:19
created

Kirki_Output::parse_output()   D

Complexity

Conditions 20
Paths 13

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 20
nc 13
nop 0
dl 0
loc 56
rs 4.1666
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Handles CSS output for fields.
4
 *
5
 * @package     Kirki
6
 * @subpackage  Controls
7
 * @copyright   Copyright (c) 2017, Aristeides Stathopoulos
8
 * @license     http://opensource.org/licenses/https://opensource.org/licenses/MIT
9
 * @since       2.2.0
10
 */
11
12
/**
13
 * Handles field CSS output.
14
 */
15
class Kirki_Output {
16
17
	/**
18
	 * The Kirki configuration used in the field.
19
	 *
20
	 * @access protected
21
	 * @var string
22
	 */
23
	protected $config_id = 'global';
24
25
	/**
26
	 * The field's `output` argument.
27
	 *
28
	 * @access protected
29
	 * @var array
30
	 */
31
	protected $output = array();
32
33
	/**
34
	 * An array of the generated styles.
35
	 *
36
	 * @access protected
37
	 * @var array
38
	 */
39
	protected $styles = array();
40
41
	/**
42
	 * The field.
43
	 *
44
	 * @access protected
45
	 * @var array
46
	 */
47
	protected $field = array();
48
49
	/**
50
	 * The value.
51
	 *
52
	 * @access protected
53
	 * @var string|array
54
	 */
55
	protected $value;
56
57
	/**
58
	 * The class constructor.
59
	 *
60
	 * @access public
61
	 * @param string       $config_id The config ID.
62
	 * @param array        $output    The output argument.
63
	 * @param string|array $value     The value.
64
	 * @param array        $field     The field.
65
	 */
66
	public function __construct( $config_id, $output, $value, $field ) {
67
68
		$this->config_id = $config_id;
69
		$this->value     = $value;
70
		$this->output    = $output;
71
		$this->field     = $field;
72
73
		$this->parse_output();
74
	}
75
76
	/**
77
	 * If we have a sanitize_callback defined, apply it to the value.
78
	 *
79
	 * @param array        $output The output args.
80
	 * @param string|array $value  The value.
81
	 *
82
	 * @return string|array
83
	 */
84
	protected function apply_sanitize_callback( $output, $value ) {
85
86
		if ( isset( $output['sanitize_callback'] ) && null !== $output['sanitize_callback'] ) {
87
88
			// If the sanitize_callback is invalid, return the value.
89
			if ( ! is_callable( $output['sanitize_callback'] ) ) {
90
				return $value;
91
			}
92
			return call_user_func( $output['sanitize_callback'], $this->value );
93
		}
94
95
		return $value;
96
97
	}
98
99
	/**
100
	 * If we have a value_pattern defined, apply it to the value.
101
	 *
102
	 * @param array        $output The output args.
103
	 * @param string|array $value  The value.
104
	 * @return string|array
105
	 */
106
	protected function apply_value_pattern( $output, $value ) {
107
108
		if ( isset( $output['value_pattern'] ) && ! empty( $output['value_pattern'] ) && is_string( $output['value_pattern'] ) ) {
109
			if ( ! is_array( $value ) ) {
110
				$value = str_replace( '$', $value, $output['value_pattern'] );
111
			}
112
			if ( is_array( $value ) ) {
113
				foreach ( array_keys( $value ) as $value_k ) {
114
					if ( ! is_string( $value[ $value_k ] ) ) {
115
						continue;
116
					}
117
					if ( isset( $output['choice'] ) ) {
118
						if ( $output['choice'] === $value_k ) {
119
							$value[ $output['choice'] ] = str_replace( '$', $value[ $output['choice'] ], $output['value_pattern'] );
120
						}
121
						continue;
122
					}
123
					$value[ $value_k ] = str_replace( '$', $value[ $value_k ], $output['value_pattern'] );
124
				}
125
			}
126
			$value = $this->apply_pattern_replace( $output, $value );
127
		} // End if().
128
		return $value;
129
	}
130
131
	/**
132
	 * If we have a value_pattern defined, apply it to the value.
133
	 *
134
	 * @param array        $output The output args.
135
	 * @param string|array $value  The value.
136
	 * @return string|array
137
	 */
138
	protected function apply_pattern_replace( $output, $value ) {
139
		if ( isset( $output['pattern_replace'] ) && is_array( $output['pattern_replace'] ) ) {
140
			$option_type = ( '' !== Kirki::get_config_param( $this->config_id, 'option_type' ) ) ? Kirki::get_config_param( $this->config_id, 'option_type' ) : 'theme_mod';
141
			$option_name = Kirki::get_config_param( $this->config_id, 'option_name' );
142
			$options     = array();
143
			if ( $option_name ) {
144
				$options = ( 'site_option' === $option_type ) ? get_site_option( $option_name ) : get_option( $option_name );
145
			}
146
			foreach ( $output['pattern_replace'] as $search => $replace ) {
147
				$replacement = '';
148
				switch ( $option_type ) {
149
					case 'option':
150
						if ( is_array( $options ) ) {
151
							if ( $option_name ) {
152
								$subkey      = str_replace( array( $option_name, '[', ']' ), '', $replace );
153
								$replacement = ( isset( $options[ $subkey ] ) ) ? $options[ $subkey ] : '';
154
								break;
155
							}
156
							$replacement = ( isset( $options[ $replace ] ) ) ? $options[ $replace ] : '';
157
							break;
158
						}
159
						$replacement = get_option( $replace );
160
						break;
161
					case 'site_option':
162
						$replacement = ( is_array( $options ) && isset( $options[ $replace ] ) ) ? $options[ $replace ] : get_site_option( $replace );
163
						break;
164
					case 'user_meta':
165
						$user_id = get_current_user_id();
166
						if ( $user_id ) {
167
							// @codingStandardsIgnoreLine
168
							$replacement = get_user_meta( $user_id, $replace, true );
169
						}
170
						break;
171
					default:
172
						$replacement = get_theme_mod( $replace );
173
				}
174
				$replacement = ( false === $replacement ) ? '' : $replacement;
175
				if ( is_array( $value ) ) {
176
					foreach ( $value as $k => $v ) {
177
						$_val        = ( isset( $value[ $v ] ) ) ? $value[ $v ] : $v;
178
						$value[ $k ] = str_replace( $search, $replacement, $_val );
179
					}
180
					return $value;
181
				}
182
				$value = str_replace( $search, $replacement, $value );
183
			} // End foreach().
184
		} // End if().
185
		return $value;
186
	}
187
188
	/**
189
	 * Parses the output arguments.
190
	 * Calls the process_output method for each of them.
191
	 *
192
	 * @access protected
193
	 */
194
	protected function parse_output() {
195
		foreach ( $this->output as $output ) {
196
			$skip = false;
197
198
			// Apply any sanitization callbacks defined.
199
			$value = $this->apply_sanitize_callback( $output, $this->value );
200
201
			// Skip if value is empty.
202
			if ( '' === $this->value ) {
203
				$skip = true;
204
			}
205
206
			// No need to proceed this if the current value is the same as in the "exclude" value.
207
			if ( isset( $output['exclude'] ) && is_array( $output['exclude'] ) ) {
208
				foreach ( $output['exclude'] as $exclude ) {
209
					if ( is_array( $value ) ) {
210
						if ( is_array( $exclude ) ) {
211
							$diff1 = array_diff( $value, $exclude );
212
							$diff2 = array_diff( $exclude, $value );
213
214
							if ( empty( $diff1 ) && empty( $diff2 ) ) {
215
								$skip = true;
216
							}
217
						}
218
						// If 'choice' is defined check for sub-values too.
219
						// Fixes https://github.com/aristath/kirki/issues/1416.
220
						// @codingStandardsIgnoreLine WordPress.PHP.StrictComparisons.LooseComparison
221
						if ( isset( $output['choice'] ) && isset( $value[ $output['choice'] ] ) && $exclude == $value[ $output['choice'] ] ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
222
							$skip = true;
223
						}
224
					}
225
					if ( $skip ) {
226
						continue;
227
					}
228
229
					// Skip if value is defined as excluded.
230
					if ( $exclude === $value || ( '' === $exclude && empty( $value ) ) ) {
231
						$skip = true;
232
					}
233
				}
234
			}
235
			if ( $skip ) {
236
				continue;
237
			}
238
239
			// Apply any value patterns defined.
240
			$value = $this->apply_value_pattern( $output, $value );
241
242
			if ( isset( $output['element'] ) && is_array( $output['element'] ) ) {
243
				$output['element'] = array_unique( $output['element'] );
244
				sort( $output['element'] );
245
				$output['element'] = implode( ',', $output['element'] );
246
			}
247
248
			$value = $this->process_value( $value, $output );
249
			$this->process_output( $output, $value );
250
		} // End foreach().
251
	}
252
253
	/**
254
	 * Parses an output and creates the styles array for it.
255
	 *
256
	 * @access protected
257
	 * @param array        $output The field output.
258
	 * @param string|array $value  The value.
259
	 *
260
	 * @return null
261
	 */
262
	protected function process_output( $output, $value ) {
263
		if ( ! isset( $output['element'] ) || ! isset( $output['property'] ) ) {
264
			return;
265
		}
266
		$output['media_query'] = ( isset( $output['media_query'] ) ) ? $output['media_query'] : 'global';
267
		$output['prefix']      = ( isset( $output['prefix'] ) ) ? $output['prefix'] : '';
268
		$output['units']       = ( isset( $output['units'] ) ) ? $output['units'] : '';
269
		$output['suffix']      = ( isset( $output['suffix'] ) ) ? $output['suffix'] : '';
270
271
		// Properties that can accept multiple values.
272
		// Useful for example for gradients where all browsers use the "background-image" property
273
		// and the browser prefixes go in the value_pattern arg.
274
		$accepts_multiple = array(
275
			'background-image',
276
			'background',
277
		);
278
		if ( in_array( $output['property'], $accepts_multiple, true ) ) {
279
			if ( isset( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) && ! is_array( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) ) {
280
				$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = (array) $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ];
281
			}
282
			$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ][] = $output['prefix'] . $value . $output['units'] . $output['suffix'];
0 ignored issues
show
Bug introduced by
Are you sure $value of type string|array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

282
			$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ][] = $output['prefix'] . /** @scrutinizer ignore-type */ $value . $output['units'] . $output['suffix'];
Loading history...
283
			return;
284
		}
285
		if ( is_string( $value ) || is_numeric( $value ) ) {
286
			$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value ) . $output['units'] . $output['suffix'];
0 ignored issues
show
Bug introduced by
Are you sure $this->process_property_...ut['property'], $value) of type array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

286
			$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . /** @scrutinizer ignore-type */ $this->process_property_value( $output['property'], $value ) . $output['units'] . $output['suffix'];
Loading history...
287
		}
288
	}
289
290
	/**
291
	 * Some CSS properties are unique.
292
	 * We need to tweak the value to make everything works as expected.
293
	 *
294
	 * @access protected
295
	 * @param string       $property The CSS property.
296
	 * @param string|array $value    The value.
297
	 *
298
	 * @return array
299
	 */
300
	protected function process_property_value( $property, $value ) {
301
		$properties = apply_filters(
302
			"kirki_{$this->config_id}_output_property_classnames", array(
303
				'font-family'         => 'Kirki_Output_Property_Font_Family',
304
				'background-image'    => 'Kirki_Output_Property_Background_Image',
305
				'background-position' => 'Kirki_Output_Property_Background_Position',
306
			)
307
		);
308
		if ( array_key_exists( $property, $properties ) ) {
309
			$classname = $properties[ $property ];
310
			$obj       = new $classname( $property, $value );
311
			return $obj->get_value();
312
		}
313
		return $value;
314
	}
315
316
	/**
317
	 * Returns the value.
318
	 *
319
	 * @access protected
320
	 * @param string|array $value The value.
321
	 * @param array        $output The field "output".
322
	 * @return string|array
323
	 */
324
	protected function process_value( $value, $output ) {
325
		if ( isset( $output['property'] ) ) {
326
			return $this->process_property_value( $output['property'], $value );
327
		}
328
		return $value;
329
	}
330
331
	/**
332
	 * Exploses the private $styles property to the world
333
	 *
334
	 * @access protected
335
	 * @return array
336
	 */
337
	public function get_styles() {
338
		return $this->styles;
339
	}
340
}
341