Completed
Push — develop ( efbf23...556e46 )
by Paul
02:08
created

Base::multiInputArgs()   D

Complexity

Conditions 14
Paths 336

Size

Total Lines 53
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 53
rs 4.6516
c 0
b 0
f 0
cc 14
eloc 31
nc 336
nop 3

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
namespace GeminiLabs\Castor\Forms\Fields;
4
5
use Exception;
6
use GeminiLabs\Castor\Services\Normalizer;
7
8
abstract class Base
9
{
10
	/**
11
	 * @var array
12
	 */
13
	protected $args;
14
15
	/**
16
	 * @var array
17
	 */
18
	protected $dependencies = [];
19
20
	/**
21
	 * Whether the field has multiple values
22
	 *
23
	 * @var bool
24
	 */
25
	protected $multi = false;
26
27
	/**
28
	 * Whether the field is rendered outside of the form table
29
	 *
30
	 * @var bool
31
	 */
32
	protected $outside = false;
33
34
	/**
35
	 * The field element tag (i.e. "input")
36
	 *
37
	 * @var string
38
	 */
39
	protected $element;
40
41
	public function __construct( array $args = [] )
42
	{
43
		$this->args = $args;
44
	}
45
46
	/**
47
	 * @param string $property
48
	 *
49
	 * @return mixed
50
	 * @throws Exception
51
	 */
52
	public function __get( $property )
53
	{
54
		switch( $property ) {
55
			case 'args';
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
56
			case 'dependencies';
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
57
			case 'element';
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
58
			case 'multi';
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
59
			case 'outside';
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
60
			return $this->$property;
61
		}
62
63
		throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $property );
64
	}
65
66
	/**
67
	 * Generate the field description
68
	 *
69
	 * @param bool $paragraph
70
	 *
71
	 * @return null|string
72
	 */
73
	public function generateDescription( $paragraph = true )
74
	{
75
		if( !isset( $this->args['desc'] ) || !$this->args['desc'] )return;
76
77
		$tag = ( !!$paragraph || $paragraph == 'p' ) ? 'p' : 'span';
78
79
		return sprintf( '<%1$s class="description">%2$s</%1$s>', $tag, $this->args['desc'] );
80
	}
81
82
	/**
83
	 * Generate the field label
84
	 *
85
	 * @return null|string
86
	 */
87
	public function generateLabel()
88
	{
89
		if( empty( $this->args['label'] ))return;
90
91
		$for = !!$this->args['id']
92
			? " for=\"{$this->args['id']}\""
93
			: '';
94
95
		return sprintf( '<label%s>%s</label>', $for, $this->args['label'] );
96
	}
97
98
	/**
99
	 * Render this field type
100
	 *
101
	 * @return string
102
	 */
103
	abstract public function render();
104
105
	/**
106
	 * Convert a value to camel case.
107
	 *
108
	 * @param string $value
109
	 *
110
	 * @return string
111
	 */
112
	protected function camelCase( $value )
113
	{
114
		$value = ucwords( str_replace( ['-', '_'], ' ', $value ));
115
116
		return lcfirst( str_replace( ' ', '', $value ));
117
	}
118
119
	/**
120
	 * Implode the field attributes
121
	 *
122
	 * @return array
123
	 */
124
	protected function implodeAttributes( $defaults = [] )
125
	{
126
		return $this->normalize( $defaults, 'implode' );
127
	}
128
129
	/**
130
	 * Implode multi-field items
131
	 *
132
	 * @return null|string
133
	 */
134
	protected function implodeOptions( $method = 'select_option', $default = null )
135
	{
136
		$this->args['default'] ?: $this->args['default'] = $default;
137
138
		$method = $this->camelCase( $method );
139
140
		$method = method_exists( $this, $method )
141
			? $method
142
			: 'selectOption';
143
144
		$i = 0;
145
146
		if( $method === 'singleInput' ) {
147
148
			if( !isset( $this->args['options'] ) || empty( $this->args['options'] ))return;
149
150
			// hack to make sure unset single checkbox values start at 1 instead of 0
151
			if( key( $this->args['options'] ) === 0 ) {
152
				$options = ['1' => $this->args['options'][0]];
153
				$this->args['options'] = $options;
154
			}
155
156
			return $this->singleInput();
157
		}
158
159
		return array_reduce( array_keys( $this->args['options'] ), function( $carry, $key ) use ( &$i, $method ) {
160
			return $carry .= $this->$method( $key, $i++ );
161
		});
162
	}
163
164
	/**
165
	 * Normalize attributes for this specific field type
166
	 *
167
	 * @param bool|string $implode
168
	 *
169
	 * @return array
170
	 */
171
	protected function normalize( array $defaults = [], $implode = false )
172
	{
173
		$args = $this->mergeAttributesWith( $defaults );
174
175
		$normalize = new Normalize;
176
177
		return ( $this->element && method_exists( $normalize, $this->element ))
178
			? $normalize->{$this->element}( $args, $implode )
179
			: ( !!$implode ? '' : [] );
180
	}
181
182
	/**
183
	 * Merge and overwrite empty $this->args values with $defaults
184
	 *
185
	 * @return array
186
	 */
187
	protected function mergeAttributesWith( array $defaults )
188
	{
189
		// similar to array_merge except overwrite empty values
190
		foreach( $defaults as $key => $value ) {
191
			if( isset( $this->args[ $key ] ) && !empty( $this->args[ $key ] ))continue;
192
			$this->args[ $key ] = $value;
193
		}
194
195
		$attributes = $this->args['attributes'];
196
197
		// prioritize $attributes over $this->args, don't worry about duplicates
198
		return array_merge( $this->args, $attributes );
199
	}
200
201
	/**
202
	 * Generate checkboxes and radios
203
	 *
204
	 * @param string $optionKey
205
	 * @param string $number
206
	 * @param string $type
207
	 *
208
	 * @return null|string
209
	 */
210
	protected function multiInput( $optionKey, $number, $type = 'radio' )
211
	{
212
		$args = $this->multiInputArgs( $type, $optionKey, $number );
213
214
		if( !$args )return;
215
216
		$attributes = '';
217
218
		foreach( $args['attributes'] as $key => $val ) {
219
			$attributes .= sprintf( '%s="%s" ', $key, $val );
220
		}
221
222
		return sprintf( '<li><label for="%s"><input %s%s/> %s</label></li>',
223
			$args['attributes']['id'],
224
			$attributes,
225
			checked( $args['value'], $args['attributes']['value'], false ),
226
			$args['label']
227
		);
228
	}
229
230
	/**
231
	 * Build the checkbox/radio args
232
	 *
233
	 * @param string $type
234
	 * @param string $optionName
235
	 * @param string $number
236
	 *
237
	 * @return array|null
238
	 */
239
	protected function multiInputArgs( $type, $optionName, $number )
240
	{
241
		$defaults = [
242
			'class' => '',
243
			'name'  => '',
244
			'type'  => $type,
245
			'value' => '',
246
		];
247
248
		$args = [];
249
250
		$value = $this->args['options'][ $optionName ];
251
252
		if( is_array( $value )) {
253
			$args = $value;
254
		}
255
256
		if( is_string( $value )) {
257
			$label = $value;
258
		}
259
260
		isset( $args['name'] ) ?: $args['name'] = $optionName;
261
		isset( $args['value'] ) ?: $args['value'] = $optionName;
262
263
		$args = wp_parse_args( $args, $defaults );
264
265
		if( !isset( $label ) || $args['name'] === '' )return;
266
267
		$args['id']   = $this->args['id'] . "-{$number}";
268
		$args['name'] = $this->args['name'] . ( $type === 'checkbox' && $this->multi ? '[]' : '' );
269
270
		$args = array_filter( $args, function( $value ) {
271
			return $value !== '';
272
		});
273
274
		if( is_array( $this->args['value'] )) {
275
			if( in_array( $args['value'], $this->args['value'] )) {
276
				$this->args['default'] = $args['value'];
277
			}
278
		}
279
		else if( $this->args['value'] ) {
280
			$this->args['default'] = $this->args['value'];
281
		}
282
		else if( $type == 'radio' && !$this->args['default'] ) {
283
			$this->args['default'] = 0;
284
		}
285
286
		return [
287
			'attributes' => $args,
288
			'label'      => $label,
289
			'value'      => $this->args['default'],
290
		];
291
	}
292
293
	/**
294
	 * Generate checkboxes
295
	 *
296
	 * @param string $optionKey
297
	 * @param string $number
298
	 *
299
	 * @return null|string
300
	 */
301
	protected function multiInputCheckbox( $optionKey, $number )
302
	{
303
		return $this->multiInput( $optionKey, $number, 'checkbox' );
304
	}
305
306
	/**
307
	 * Generate select options
308
	 *
309
	 * @param string $optionKey
310
	 *
311
	 * @return string
312
	 */
313
	protected function selectOption( $optionKey )
314
	{
315
		return sprintf( '<option value="%s"%s>%s</option>',
316
			$optionKey,
317
			selected( $this->args['value'], $optionKey, false ),
318
			$this->args['options'][ $optionKey ]
319
		);
320
	}
321
322
	/**
323
	 * Generate a single checkbox
324
	 *
325
	 * @param string $type
326
	 *
327
	 * @return null|string
328
	 */
329
	protected function singleInput( $type = 'checkbox' )
330
	{
331
		$optionKey = key( $this->args['options'] );
332
333
		$args = $this->multiInputArgs( $type, $optionKey, 1 );
334
335
		if( !$args )return;
336
337
		$atts = $this->normalize();
338
		$atts = wp_parse_args( $args['attributes'], $atts );
339
340
		$attributes = '';
341
342
		foreach( $atts as $key => $val ) {
343
			$attributes .= sprintf( '%s="%s" ', $key, $val );
344
		}
345
346
		return sprintf( '<label for="%s"><input %s%s/> %s</label>',
347
			$atts['id'],
348
			$attributes,
349
			checked( $args['value'], $atts['value'], false ),
350
			$args['label']
351
		);
352
	}
353
}
354