Completed
Push — develop ( 44a93a...3e4b25 )
by Aristeides
03:30
created

Kirki_Output::apply_pattern_replace()   C

Complexity

Conditions 22
Paths 199

Size

Total Lines 54
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 41
nc 199
nop 2
dl 0
loc 54
rs 5.945
c 0
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 value.
43
	 *
44
	 * @access protected
45
	 * @var string|array
46
	 */
47
	protected $value;
48
49
	/**
50
	 * The class constructor.
51
	 *
52
	 * @access public
53
	 * @param string       $config_id The config ID.
54
	 * @param array        $output    The output argument.
55
	 * @param string|array $value     The value.
56
	 */
57
	public function __construct( $config_id, $output, $value ) {
58
59
		$this->config_id = $config_id;
60
		$this->value     = $value;
61
		$this->output    = $output;
62
63
		$this->parse_output();
64
	}
65
66
	/**
67
	 * If we have a sanitize_callback defined, apply it to the value.
68
	 *
69
	 * @param array        $output The output args.
70
	 * @param string|array $value  The value.
71
	 *
72
	 * @return string|array
73
	 */
74
	protected function apply_sanitize_callback( $output, $value ) {
75
76
		if ( isset( $output['sanitize_callback'] ) && null !== $output['sanitize_callback'] ) {
77
78
			// If the sanitize_callback is invalid, return the value.
79
			if ( ! is_callable( $output['sanitize_callback'] ) ) {
80
				return $value;
81
			}
82
			return call_user_func( $output['sanitize_callback'], $this->value );
83
		}
84
85
		return $value;
86
87
	}
88
89
	/**
90
	 * If we have a value_pattern defined, apply it to the value.
91
	 *
92
	 * @param array        $output The output args.
93
	 * @param string|array $value  The value.
94
	 * @return string|array
95
	 */
96
	protected function apply_value_pattern( $output, $value ) {
97
98
		if ( isset( $output['value_pattern'] ) && ! empty( $output['value_pattern'] ) && is_string( $output['value_pattern'] ) ) {
99
			if ( ! is_array( $value ) ) {
100
				$value = str_replace( '$', $value, $output['value_pattern'] );
101
			}
102
			if ( is_array( $value ) ) {
103
				foreach ( array_keys( $value ) as $value_k ) {
104
					if ( ! is_string( $value[ $value_k ] ) ) {
105
						continue;
106
					}
107
					if ( isset( $output['choice'] ) ) {
108
						if ( $output['choice'] === $value_k ) {
109
							$value[ $output['choice'] ] = str_replace( '$', $value[ $output['choice'] ], $output['value_pattern'] );
110
						}
111
						continue;
112
					}
113
					$value[ $value_k ] = str_replace( '$', $value[ $value_k ], $output['value_pattern'] );
114
				}
115
			}
116
			$value = $this->apply_pattern_replace( $output, $value );
117
		} // End if().
118
		return $value;
119
	}
120
121
	/**
122
	 * If we have a value_pattern defined, apply it to the value.
123
	 *
124
	 * @param array        $output The output args.
125
	 * @param string|array $value  The value.
126
	 * @return string|array
127
	 */
128
	protected function apply_pattern_replace( $output, $value ) {
129
		if ( isset( $output['pattern_replace'] ) && is_array( $output['pattern_replace'] ) ) {
130
			$option_type = ( '' !== Kirki::get_config_param( $this->config_id, 'option_type' ) ) ? Kirki::get_config_param( $this->config_id, 'option_type' ) : 'theme_mod';
131
			$option_name = Kirki::get_config_param( $this->config_id, 'option_name' );
132
			$options     = array();
133
			if ( $option_name ) {
134
				$options = ( 'site_option' === $option_type ) ? get_site_option( $option_name ) : get_option( $option_name );
135
			}
136
			foreach ( $output['pattern_replace'] as $search => $replace ) {
137
				$replacement = '';
138
				switch ( $option_type ) {
139
					case 'option':
140
						if ( is_array( $options ) ) {
141
							if ( $option_name ) {
142
								$subkey = str_replace( array( $option_name, '[', ']' ), '', $replace );
143
								$replacement = ( isset( $options[ $subkey ] ) ) ? $options[ $subkey ] : '';
144
								break;
145
							}
146
							$replacement = ( isset( $options[ $replace ] ) ) ? $options[ $replace ] : '';
147
							break;
148
						}
149
						$replacement = get_option( $replace );
150
						break;
151
					case 'site_option':
152
						$replacement = ( is_array( $options ) && isset( $options[ $replace ] ) ) ? $options[ $replace ] : get_site_option( $replace );
153
						break;
154
					case 'user_meta':
155
						$user_id = get_current_user_id();
156
						if ( $user_id ) {
157
							// @codingStandardsIgnoreLine
158
							$replacement = get_user_meta( $user_id, $replace, true );
159
						}
160
						break;
161
					default:
162
						$replacement = get_theme_mod( $replace );
163
				}
164
				$replacement = ( false === $replacement ) ? '' : $replacement;
165
				if ( is_array( $value ) ) {
166
					foreach ( $value as $k => $v ) {
167
						if ( isset( $output['choice'] ) ) {
168
							if ( $k === $output['choice'] ) {
169
								$value[ $k ] = str_replace( $search, $replacement, $v );
170
							}
171
							continue;
172
						}
173
						$value[ $k ] = str_replace( $search, $replacement, $v );
174
					}
175
					return $value;
176
				}
177
				$value = str_replace( $search, $replacement, $value );
178
			} // End foreach().
179
		} // End if().
180
		return $value;
181
	}
182
183
	/**
184
	 * Parses the output arguments.
185
	 * Calls the process_output method for each of them.
186
	 *
187
	 * @access protected
188
	 */
189
	protected function parse_output() {
190
		foreach ( $this->output as $output ) {
191
			$skip = false;
192
193
			// Apply any sanitization callbacks defined.
194
			$value = $this->apply_sanitize_callback( $output, $this->value );
195
196
			// Skip if value is empty.
197
			if ( '' === $this->value ) {
198
				$skip = true;
199
			}
200
201
			// No need to proceed this if the current value is the same as in the "exclude" value.
202
			if ( isset( $output['exclude'] ) && is_array( $output['exclude'] ) ) {
203
				foreach ( $output['exclude'] as $exclude ) {
204
					if ( is_array( $value ) ) {
205
						if ( is_array( $exclude ) ) {
206
							$diff1 = array_diff( $value, $exclude );
207
							$diff2 = array_diff( $exclude, $value );
208
209
							if ( empty( $diff1 ) && empty( $diff2 ) ) {
210
								$skip = true;
211
							}
212
						}
213
						// If 'choice' is defined check for sub-values too.
214
						// Fixes https://github.com/aristath/kirki/issues/1416.
215
						if ( isset( $output['choice'] ) && isset( $value[ $output['choice'] ] ) && $exclude == $value[ $output['choice'] ] ) {
216
							$skip = true;
217
						}
218
					}
219
					if ( $skip ) {
220
						continue;
221
					}
222
223
					// Skip if value is defined as excluded.
224
					if ( $exclude === $value || ( '' === $exclude && empty( $value ) ) ) {
225
						$skip = true;
226
					}
227
				}
228
			}
229
			if ( $skip ) {
230
				continue;
231
			}
232
233
			// Apply any value patterns defined.
234
			$value = $this->apply_value_pattern( $output, $value );
235
236
			if ( isset( $output['element'] ) && is_array( $output['element'] ) ) {
237
				$output['element'] = array_unique( $output['element'] );
238
				sort( $output['element'] );
239
				$output['element'] = implode( ',', $output['element'] );
240
			}
241
242
			$value = $this->process_value( $value, $output );
243
			$this->process_output( $output, $value );
244
		} // End foreach().
245
	}
246
247
	/**
248
	 * Parses an output and creates the styles array for it.
249
	 *
250
	 * @access protected
251
	 * @param array        $output The field output.
252
	 * @param string|array $value  The value.
253
	 *
254
	 * @return void
255
	 */
256
	protected function process_output( $output, $value ) {
257
		if ( ! isset( $output['element'] ) || ! isset( $output['property'] ) ) {
258
			return;
259
		}
260
		$output['media_query'] = ( isset( $output['media_query'] ) ) ? $output['media_query'] : 'global';
261
		$output['prefix']      = ( isset( $output['prefix'] ) ) ? $output['prefix'] : '';
262
		$output['units']       = ( isset( $output['units'] ) ) ? $output['units'] : '';
263
		$output['suffix']      = ( isset( $output['suffix'] ) ) ? $output['suffix'] : '';
264
265
		// Properties that can accept multiple values.
266
		// Useful for example for gradients where all browsers use the "background-image" property
267
		// and the browser prefixes go in the value_pattern arg.
268
		$accepts_multiple = array(
269
			'background-image',
270
			'background',
271
		);
272
		if ( in_array( $output['property'], $accepts_multiple, true ) ) {
273
			if ( isset( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) && ! is_array( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) ) {
274
				$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = (array) $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ];
275
			}
276
			$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ][] = $output['prefix'] . $value . $output['units'] . $output['suffix'];
277
			return;
278
		}
279
		$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value ) . $output['units'] . $output['suffix'];
280
	}
281
282
	/**
283
	 * Some CSS properties are unique.
284
	 * We need to tweak the value to make everything works as expected.
285
	 *
286
	 * @access protected
287
	 * @param string       $property The CSS property.
288
	 * @param string|array $value    The value.
289
	 *
290
	 * @return array
291
	 */
292
	protected function process_property_value( $property, $value ) {
293
		$properties = apply_filters( "kirki/{$this->config_id}/output/property-classnames", array(
294
			'font-family'         => 'Kirki_Output_Property_Font_Family',
295
			'background-image'    => 'Kirki_Output_Property_Background_Image',
296
			'background-position' => 'Kirki_Output_Property_Background_Position',
297
		) );
298
		if ( array_key_exists( $property, $properties ) ) {
299
			$classname = $properties[ $property ];
300
			$obj = new $classname( $property, $value );
301
			return $obj->get_value();
302
		}
303
		return $value;
304
	}
305
306
	/**
307
	 * Returns the value.
308
	 *
309
	 * @access protected
310
	 * @param string|array $value The value.
311
	 * @param array        $output The field "output".
312
	 * @return string|array
313
	 */
314
	protected function process_value( $value, $output ) {
315
		if ( isset( $output['property'] ) ) {
316
			return $this->process_property_value( $output['property'], $value );
317
		}
318
		return $value;
319
	}
320
321
	/**
322
	 * Exploses the private $styles property to the world
323
	 *
324
	 * @access protected
325
	 * @return array
326
	 */
327
	public function get_styles() {
328
		return $this->styles;
329
	}
330
}
331