Completed
Pull Request — master (#1592)
by Aristeides
05:37 queued 03:31
created

Kirki_Output::apply_pattern_replace()   D

Complexity

Conditions 20
Paths 103

Size

Total Lines 48
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 37
nc 103
nop 2
dl 0
loc 48
rs 4.8444
c 0
b 0
f 0

How to fix   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
						$value[ $k ] = str_replace( $search, $replacement, $value[ $v ] );
168
					}
169
					return $value;
170
				}
171
				$value = str_replace( $search, $replacement, $value );
172
			} // End foreach().
173
		} // End if().
174
		return $value;
175
	}
176
177
	/**
178
	 * Parses the output arguments.
179
	 * Calls the process_output method for each of them.
180
	 *
181
	 * @access protected
182
	 */
183
	protected function parse_output() {
184
		foreach ( $this->output as $output ) {
185
			$skip = false;
186
187
			// Apply any sanitization callbacks defined.
188
			$value = $this->apply_sanitize_callback( $output, $this->value );
189
190
			// Skip if value is empty.
191
			if ( '' === $this->value ) {
192
				$skip = true;
193
			}
194
195
			// No need to proceed this if the current value is the same as in the "exclude" value.
196
			if ( isset( $output['exclude'] ) && is_array( $output['exclude'] ) ) {
197
				foreach ( $output['exclude'] as $exclude ) {
198
					if ( is_array( $value ) ) {
199
						if ( is_array( $exclude ) ) {
200
							$diff1 = array_diff( $value, $exclude );
201
							$diff2 = array_diff( $exclude, $value );
202
203
							if ( empty( $diff1 ) && empty( $diff2 ) ) {
204
								$skip = true;
205
							}
206
						}
207
						// If 'choice' is defined check for sub-values too.
208
						// Fixes https://github.com/aristath/kirki/issues/1416.
209
						if ( isset( $output['choice'] ) && isset( $value[ $output['choice'] ] ) && $exclude == $value[ $output['choice'] ] ) {
210
							$skip = true;
211
						}
212
					}
213
					if ( $skip ) {
214
						continue;
215
					}
216
217
					// Skip if value is defined as excluded.
218
					if ( $exclude === $value || ( '' === $exclude && empty( $value ) ) ) {
219
						$skip = true;
220
					}
221
				}
222
			}
223
			if ( $skip ) {
224
				continue;
225
			}
226
227
			// Apply any value patterns defined.
228
			$value = $this->apply_value_pattern( $output, $value );
229
230
			if ( isset( $output['element'] ) && is_array( $output['element'] ) ) {
231
				$output['element'] = array_unique( $output['element'] );
232
				sort( $output['element'] );
233
				$output['element'] = implode( ',', $output['element'] );
234
			}
235
236
			$value = $this->process_value( $value, $output );
237
			$this->process_output( $output, $value );
238
		} // End foreach().
239
	}
240
241
	/**
242
	 * Parses an output and creates the styles array for it.
243
	 *
244
	 * @access protected
245
	 * @param array        $output The field output.
246
	 * @param string|array $value  The value.
247
	 *
248
	 * @return void
249
	 */
250
	protected function process_output( $output, $value ) {
251
		if ( ! isset( $output['element'] ) || ! isset( $output['property'] ) ) {
252
			return;
253
		}
254
		$output['media_query'] = ( isset( $output['media_query'] ) ) ? $output['media_query'] : 'global';
255
		$output['prefix']      = ( isset( $output['prefix'] ) ) ? $output['prefix'] : '';
256
		$output['units']       = ( isset( $output['units'] ) ) ? $output['units'] : '';
257
		$output['suffix']      = ( isset( $output['suffix'] ) ) ? $output['suffix'] : '';
258
259
		// Properties that can accept multiple values.
260
		// Useful for example for gradients where all browsers use the "background-image" property
261
		// and the browser prefixes go in the value_pattern arg.
262
		$accepts_multiple = array(
263
			'background-image',
264
			'background',
265
		);
266
		if ( in_array( $output['property'], $accepts_multiple, true ) ) {
267
			if ( isset( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) && ! is_array( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) ) {
268
				$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = (array) $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ];
269
			}
270
			$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ][] = $output['prefix'] . $value . $output['units'] . $output['suffix'];
271
			return;
272
		}
273
		$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value ) . $output['units'] . $output['suffix'];
274
	}
275
276
	/**
277
	 * Some CSS properties are unique.
278
	 * We need to tweak the value to make everything works as expected.
279
	 *
280
	 * @access protected
281
	 * @param string       $property The CSS property.
282
	 * @param string|array $value    The value.
283
	 *
284
	 * @return array
285
	 */
286
	protected function process_property_value( $property, $value ) {
287
		$properties = apply_filters( "kirki/{$this->config_id}/output/property-classnames", array(
288
			'font-family'         => 'Kirki_Output_Property_Font_Family',
289
			'background-image'    => 'Kirki_Output_Property_Background_Image',
290
			'background-position' => 'Kirki_Output_Property_Background_Position',
291
		) );
292
		if ( array_key_exists( $property, $properties ) ) {
293
			$classname = $properties[ $property ];
294
			$obj = new $classname( $property, $value );
295
			return $obj->get_value();
296
		}
297
		return $value;
298
	}
299
300
	/**
301
	 * Returns the value.
302
	 *
303
	 * @access protected
304
	 * @param string|array $value The value.
305
	 * @param array        $output The field "output".
306
	 * @return string|array
307
	 */
308
	protected function process_value( $value, $output ) {
309
		if ( isset( $output['property'] ) ) {
310
			return $this->process_property_value( $output['property'], $value );
311
		}
312
		return $value;
313
	}
314
315
	/**
316
	 * Exploses the private $styles property to the world
317
	 *
318
	 * @access protected
319
	 * @return array
320
	 */
321
	public function get_styles() {
322
		return $this->styles;
323
	}
324
}
325