Passed
Push — master ( f3cc5e...8dc102 )
by Paul
03:59
created

Builder   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 320
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 121
dl 0
loc 320
ccs 0
cts 129
cp 0
rs 8.5599
c 0
b 0
f 0
wmc 48

21 Methods

Rating   Name   Duplication   Size   Complexity  
A buildFormInput() 0 11 4
A buildFieldDescription() 0 7 3
A normalize() 0 13 6
A buildCustomField() 0 7 2
A buildTag() 0 10 3
A buildFormLabel() 0 8 3
A buildFormSelect() 0 3 1
A buildFormTextarea() 0 3 1
A getCustomFieldClassName() 0 3 1
A buildFormInputMultiChoice() 0 18 2
A buildDefaultTag() 0 6 2
A mergeArgsWithRequiredDefaults() 0 10 2
A buildFormSelectOptions() 0 7 1
A setNameOrTextAttributeForTag() 0 6 2
A buildFormInputChoice() 0 8 2
A setTagFromMethod() 0 6 2
A raw() 0 4 1
A __call() 0 14 3
A getClosingTag() 0 4 2
A getOpeningTag() 0 5 2
A __set() 0 11 3

How to fix   Complexity   

Complex Class

Complex classes like Builder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Builder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules\Html;
4
5
use GeminiLabs\SiteReviews\Defaults\BuilderDefaults;
6
use GeminiLabs\SiteReviews\Helper;
7
use GeminiLabs\SiteReviews\Modules\Html\Attributes;
8
9
/**
10
 * @method string a( string|array ...$params )
11
 * @method string button( string|array ...$params )
12
 * @method string div( string|array ...$params )
13
 * @method string i( string|array ...$params )
14
 * @method string img( string|array ...$params )
15
 * @method string label( string|array ...$params )
16
 * @method string p( string|array ...$params )
17
 * @method string select( string|array ...$params )
18
 * @method string span( string|array ...$params )
19
 */
20
class Builder
21
{
22
	const INPUT_TYPES = [
23
		'checkbox', 'date', 'datetime-local', 'email', 'file', 'hidden', 'image', 'month',
24
		'number', 'password', 'radio', 'range', 'reset', 'search', 'submit', 'tel', 'text', 'time',
25
		'url', 'week',
26
	];
27
28
	const TAGS_FORM = [
29
		'input', 'select', 'textarea',
30
	];
31
32
	const TAGS_SINGLE = [
33
		'img',
34
	];
35
36
	const TAGS_STRUCTURE = [
37
		'div', 'form', 'nav', 'ol', 'section', 'ul',
38
	];
39
40
	const TAGS_TEXT = [
41
		'a', 'button', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'i', 'label', 'li', 'option', 'p', 'pre', 'small',
42
		'span',
43
	];
44
45
	/**
46
	 * @var array
47
	 */
48
	public $args = [];
49
50
	/**
51
	 * @var bool
52
	 */
53
	public $render = false;
54
55
	/**
56
	 * @var string
57
	 */
58
	public $tag;
59
60
	/**
61
	 * @param string $method
62
	 * @param array $args
63
	 * @return string|void
64
	 */
65
	public function __call( $method, $args )
66
	{
67
		$instance = new static;
68
		$instance->setTagFromMethod( $method );
69
		call_user_func_array( [$instance, 'normalize'], $args += ['',''] );
70
		$tags = array_merge( static::TAGS_FORM, static::TAGS_SINGLE, static::TAGS_STRUCTURE, static::TAGS_TEXT );
71
		do_action_ref_array( 'site-reviews/builder', [&$instance] );
72
		$generatedTag = in_array( $instance->tag, $tags )
73
			? $instance->buildTag()
74
			: $instance->buildCustomField();
75
		if( !$this->render ) {
76
			return $generatedTag;
77
		}
78
		echo $generatedTag;
79
	}
80
81
	/**
82
	 * @param string $property
83
	 * @param mixed $value
84
	 * @return void
85
	 */
86
	public function __set( $property, $value )
87
	{
88
		$properties = [
89
			'args' => 'is_array',
90
			'render' => 'is_bool',
91
			'tag' => 'is_string',
92
		];
93
		if( !isset( $properties[$property] )
94
			|| empty( array_filter( [$value], $properties[$property] ))
95
		)return;
96
		$this->$property = $value;
97
	}
98
99
	/**
100
	 * @return void|string
101
	 */
102
	public function getClosingTag()
103
	{
104
		if( empty( $this->tag ))return;
105
		return '</'.$this->tag.'>';
106
	}
107
108
	/**
109
	 * @return void|string
110
	 */
111
	public function getOpeningTag()
112
	{
113
		if( empty( $this->tag ))return;
114
		$attributes = glsr( Attributes::class )->{$this->tag}( $this->args )->toString();
115
		return '<'.trim( $this->tag.' '.$attributes ).'>';
116
	}
117
118
	/**
119
	 * @return string
120
	 */
121
	public function raw( array $field )
122
	{
123
		unset( $field['label'] );
124
		return $this->{$field['type']}( $field );
125
	}
126
127
	/**
128
	 * @return string|void
129
	 */
130
	protected function buildCustomField()
131
	{
132
		$className = $this->getCustomFieldClassName();
133
		if( class_exists( $className )) {
134
			return (new $className( $this ))->build();
135
		}
136
		glsr_log()->error( 'Field missing: '.$className );
137
	}
138
139
	/**
140
	 * @return string|void
141
	 */
142
	protected function buildDefaultTag( $text = '' )
143
	{
144
		if( empty( $text )) {
145
			$text = $this->args['text'];
146
		}
147
		return $this->getOpeningTag().$text.$this->getClosingTag();
148
	}
149
150
	/**
151
	 * @return string|void
152
	 */
153
	protected function buildFieldDescription()
154
	{
155
		if( empty( $this->args['description'] ))return;
156
		if( $this->args['is_widget'] ) {
157
			return $this->small( $this->args['description'] );
158
		}
159
		return $this->p( $this->args['description'], ['class' => 'description'] );
160
	}
161
162
	/**
163
	 * @return string|void
164
	 */
165
	protected function buildFormInput()
166
	{
167
		if( !in_array( $this->args['type'], ['checkbox', 'radio'] )) {
168
			if( isset( $this->args['multiple'] )) {
169
				$this->args['name'].= '[]';
170
			}
171
			return $this->buildFormLabel().$this->getOpeningTag();
172
		}
173
		return empty( $this->args['options'] )
174
			? $this->buildFormInputChoice()
175
			: $this->buildFormInputMultiChoice();
176
	}
177
178
	/**
179
	 * @return string|void
180
	 */
181
	protected function buildFormInputChoice()
182
	{
183
		if( !empty( $this->args['text'] )) {
184
			$this->args['label'] = $this->args['text'];
185
		}
186
		return $this->getOpeningTag().$this->buildFormLabel([
187
			'class' => 'glsr-'.$this->args['type'].'-label',
188
			'text' => $this->args['label'].'<span></span>',
189
		]);
190
	}
191
192
	/**
193
	 * @return string|void
194
	 */
195
	protected function buildFormInputMultiChoice()
196
	{
197
		if( $this->args['type'] == 'checkbox' ) {
198
			$this->args['name'].= '[]';
199
		}
200
		$index = 0;
201
		$options = array_reduce( array_keys( $this->args['options'] ), function( $carry, $key ) use( &$index ) {
202
			return $carry.$this->li( $this->{$this->args['type']}([
203
				'checked' => in_array( $key, (array)$this->args['value'] ),
204
				'id' => $this->args['id'].'-'.$index++,
205
				'name' => $this->args['name'],
206
				'text' => $this->args['options'][$key],
207
				'value' => $key,
208
			]));
209
		});
210
		return $this->ul( $options, [
211
			'class' => $this->args['class'],
212
			'id' => $this->args['id'],
213
		]);
214
	}
215
216
	/**
217
	 * @return void|string
218
	 */
219
	protected function buildFormLabel( array $customArgs = [] )
220
	{
221
		if( empty( $this->args['label'] ) || $this->args['type'] == 'hidden' )return;
222
		return $this->label( wp_parse_args( $customArgs, [
223
			'for' => $this->args['id'],
224
			'is_public' => $this->args['is_public'],
225
			'text' => $this->args['label'],
226
			'type' => $this->args['type'],
227
		]));
228
	}
229
230
	/**
231
	 * @return string|void
232
	 */
233
	protected function buildFormSelect()
234
	{
235
		return $this->buildFormLabel().$this->buildDefaultTag( $this->buildFormSelectOptions() );
236
	}
237
238
	/**
239
	 * @return string|void
240
	 */
241
	protected function buildFormSelectOptions()
242
	{
243
		return array_reduce( array_keys( $this->args['options'] ), function( $carry, $key ) {
244
			return $carry.$this->option([
245
				'selected' => $this->args['value'] == $key,
246
				'text' => $this->args['options'][$key],
247
				'value' => $key,
248
			]);
249
		});
250
	}
251
252
	/**
253
	 * @return string|void
254
	 */
255
	protected function buildFormTextarea()
256
	{
257
		return $this->buildFormLabel().$this->buildDefaultTag( $this->args['value'] );
258
	}
259
260
	/**
261
	 * @return string|void
262
	 */
263
	protected function buildTag()
264
	{
265
		$this->mergeArgsWithRequiredDefaults();
266
		if( in_array( $this->tag, static::TAGS_SINGLE )) {
267
			return $this->getOpeningTag();
268
		}
269
		if( !in_array( $this->tag, static::TAGS_FORM )) {
270
			return $this->buildDefaultTag();
271
		}
272
		return call_user_func( [$this, 'buildForm'.ucfirst( $this->tag )] ).$this->buildFieldDescription();
273
	}
274
275
	/**
276
	 * @return string
277
	 */
278
	protected function getCustomFieldClassName()
279
	{
280
		return glsr( Helper::class )->buildClassName( $this->tag, __NAMESPACE__.'\Fields' );
281
	}
282
283
	/**
284
	 * @return void
285
	 */
286
	protected function mergeArgsWithRequiredDefaults()
287
	{
288
		$className = $this->getCustomFieldClassName();
289
		if( class_exists( $className )) {
290
			$this->args = array_merge(
291
				wp_parse_args( $this->args, $className::defaults() ),
292
				$className::required()
293
			);
294
		}
295
		$this->args = glsr( BuilderDefaults::class )->merge( $this->args );
296
	}
297
298
	/**
299
	 * @param string|array ...$params
300
	 * @return void
301
	 */
302
	protected function normalize( ...$params )
303
	{
304
		if( is_string( $params[0] ) || is_numeric( $params[0] )) {
305
			$this->setNameOrTextAttributeForTag( $params[0] );
306
		}
307
		if( is_array( $params[0] )) {
308
			$this->args += $params[0];
309
		}
310
		else if( is_array( $params[1] )) {
311
			$this->args += $params[1];
312
		}
313
		if( !isset( $this->args['is_public'] )) {
314
			$this->args['is_public'] = false;
315
		}
316
	}
317
318
	/**
319
	 * @param string $value
320
	 * @return void
321
	 */
322
	protected function setNameOrTextAttributeForTag( $value )
323
	{
324
		$attribute = in_array( $this->tag, static::TAGS_FORM )
325
			? 'name'
326
			: 'text';
327
		$this->args[$attribute] = $value;
328
	}
329
330
	/**
331
	 * @param string $method
332
	 * @return void
333
	 */
334
	protected function setTagFromMethod( $method )
335
	{
336
		$this->tag = strtolower( $method );
337
		if( in_array( $this->tag, static::INPUT_TYPES )) {
338
			$this->args['type'] = $this->tag;
339
			$this->tag = 'input';
340
		}
341
	}
342
}
343