Passed
Push — master ( 6268c8...c33117 )
by Paul
14:11 queued 01:11
created

Builder::buildDefaultElement()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules\Html;
4
5
use GeminiLabs\SiteReviews\Defaults\FieldDefaults;
6
use GeminiLabs\SiteReviews\Helper;
7
use GeminiLabs\SiteReviews\Helpers\Arr;
8
use GeminiLabs\SiteReviews\Helpers\Cast;
9
use GeminiLabs\SiteReviews\Helpers\Str;
10
11
/**
12
 * This class generates raw HTML tags without additional DOM markup.
13
 *
14
 * @method string a(string|array ...$params)
15
 * @method string button(string|array ...$params)
16
 * @method string div(string|array ...$params)
17
 * @method string i(string|array ...$params)
18
 * @method string img(string|array ...$params)
19
 * @method string input(string|array ...$params)
20
 * @method string label(string|array ...$params)
21
 * @method string option(string|array ...$params)
22
 * @method string p(string|array ...$params)
23
 * @method string select(string|array ...$params)
24
 * @method string small(string|array ...$params)
25
 * @method string span(string|array ...$params)
26
 * @method string textarea(string|array ...$params)
27
 */
28
class Builder
29
{
30
    const INPUT_TYPES = [
31
        'checkbox', 'date', 'datetime-local', 'email', 'file', 'hidden', 'image', 'month',
32
        'number', 'password', 'radio', 'range', 'reset', 'search', 'submit', 'tel', 'text', 'time',
33
        'url', 'week',
34
    ];
35
36
    const TAGS_FORM = [
37
        'input', 'select', 'textarea',
38
    ];
39
40
    const TAGS_SINGLE = [
41
        'img',
42
    ];
43
44
    const TAGS_STRUCTURE = [
45
        'div', 'form', 'nav', 'ol', 'section', 'ul',
46
    ];
47
48
    const TAGS_TEXT = [
49
        'a', 'button', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'i', 'label', 'li', 'option', 'optgroup',
50
        'p', 'pre', 'small', 'span',
51
    ];
52
53
    /**
54
     * @var \GeminiLabs\SiteReviews\Arguments
55
     */
56
    public $args;
57
58
    /**
59
     * @var bool
60
     */
61
    public $render = false;
62
63
    /**
64
     * @var string
65
     */
66
    public $tag;
67
68
    /**
69
     * @var string
70
     */
71
    public $type;
72
73
    /**
74
     * @param string $method
75
     * @param array $methodArgs
76
     * @return string|void
77
     */
78 7
    public function __call($method, $methodArgs)
79
    {
80 7
        $instance = new static();
81 7
        $args = call_user_func_array([$instance, 'prepareArgs'], $methodArgs);
82 7
        $tag = Str::dashCase($method);
83 7
        $result = $instance->build($tag, $args);
84 7
        if (!$instance->render) {
85 7
            return $result;
86
        }
87
        echo $result;
88
    }
89
90
    /**
91
     * @param string $property
92
     * @param mixed $value
93
     * @return void
94
     */
95
    public function __set($property, $value)
96
    {
97
        $method = Helper::buildMethodName($property, 'set');
98
        if (method_exists($this, $method)) {
99
            call_user_func([$this, $method], $value);
100
        }
101
    }
102
103
    /**
104
     * @return string
105
     */
106 7
    public function build($tag, array $args = [])
107
    {
108 7
        $this->setArgs($args, $tag);
109 7
        $this->setTag($tag);
110 7
        glsr()->action('builder', $this);
111 7
        $result = $this->isHtmlTag($this->tag)
112 7
            ? $this->buildElement()
113 7
            : $this->buildCustom($tag);
114 7
        return glsr()->filterString('builder/result', $result, $this);
115
    }
116
117
    /**
118
     * @return void|string
119
     */
120 7
    public function buildClosingTag()
121
    {
122 7
        return '</'.$this->tag.'>';
123
    }
124
125
    /**
126
     * @param string $tag
127
     * @return void|string
128
     */
129
    public function buildCustom($tag)
130
    {
131
        if (class_exists($className = $this->getFieldClassName($tag))) {
132
            return (new $className($this))->build();
133
        }
134
        glsr_log()->error("Field [$className] missing.");
135
    }
136
137
    /**
138
     * @return string
139
     */
140 7
    public function buildDefaultElement($text = '')
141
    {
142 7
        $text = Helper::ifEmpty($text, $this->args->text, $strict = true);
143 7
        return $this->buildOpeningTag().$text.$this->buildClosingTag();
144
    }
145
146
    /**
147
     * @return void|string
148
     */
149 7
    public function buildElement()
150
    {
151 7
        if (in_array($this->tag, static::TAGS_SINGLE)) {
152
            return $this->buildOpeningTag();
153
        }
154 7
        if (in_array($this->tag, static::TAGS_FORM)) {
155
            return $this->buildFormElement();
156
        }
157 7
        return $this->buildDefaultElement();
158
    }
159
160
    /**
161
     * @return void|string
162
     */
163
    public function buildFormElement()
164
    {
165
        $method = Helper::buildMethodName($this->tag, 'buildForm');
166
        return $this->$method();
167
    }
168
169
    /**
170
     * @return void|string
171
     */
172 7
    public function buildOpeningTag()
173
    {
174 7
        $attributes = glsr(Attributes::class)->{$this->tag}($this->args->toArray())->toString();
175 7
        return '<'.trim($this->tag.' '.$attributes).'>';
176
    }
177
178
    /**
179
     * @return string
180
     */
181
    public function raw(array $field)
182
    {
183
        unset($field['label']);
184
        return $this->{$field['type']}($field);
185
    }
186
187
    /**
188
     * @param array $args
189
     * @param string $type
190
     * @return void
191
     */
192 7
    public function setArgs($args = [], $type = '')
193
    {
194 7
        $args = Arr::consolidate($args);
195 7
        if (!empty($args)) {
196 7
            $args = $this->normalize($args, $type);
197 7
            $options = glsr()->args($args)->options;
198 7
            $args = glsr(FieldDefaults::class)->merge($args);
199 7
            if (is_array($options)) {
200
                // Merging reindexes the options array, this may not be desirable
201
                // if the array is indexed so here we restore the original options array.
202
                // It's a messy hack, but it will have to do for now.
203
                $args['options'] = $options;
204
            }
205
        }
206 7
        $args = glsr()->filterArray('builder/'.$type.'/args', $args, $this);
207 7
        $this->args = glsr()->args($args);
208 7
    }
209
210
    /**
211
     * @param bool $bool
212
     * @return void
213
     */
214
    public function setRender($bool)
215
    {
216
        $this->render = Cast::toBool($bool);
217
    }
218
219
    /**
220
     * @param string $tag
221
     * @return void
222
     */
223 7
    public function setTag($tag)
224
    {
225 7
        $tag = Cast::toString($tag);
226 7
        $this->tag = Helper::ifTrue(in_array($tag, static::INPUT_TYPES), 'input', $tag);
227 7
    }
228
229
    /**
230
     * @return string|void
231
     */
232
    protected function buildFormInput()
233
    {
234
        if (!in_array($this->args->type, ['checkbox', 'radio'])) {
235
            return $this->buildFormLabel().$this->buildOpeningTag();
236
        }
237
        return empty($this->args->options)
238
            ? $this->buildFormInputChoice()
239
            : $this->buildFormInputChoices();
240
    }
241
242
    /**
243
     * @return string|void
244
     */
245
    protected function buildFormInputChoice()
246
    {
247
        if ($label = Helper::ifEmpty($this->args->text, $this->args->label)) {
248
            return $this->buildFormLabel([
249
                'text' => $this->buildOpeningTag().' '.$label,
250
            ]);
251
        }
252
        return $this->buildOpeningTag();
253
    }
254
255
    /**
256
     * @return string|void
257
     */
258
    protected function buildFormInputChoices()
259
    {
260
        $index = 0;
261
        return array_reduce(array_keys($this->args->options), function ($carry, $value) use (&$index) {
262
            return $carry.$this->input([
263
                'checked' => in_array($value, $this->args->cast('value', 'array')),
264
                'class' => $this->args->class,
265
                'disabled' => $this->args->disabled,
266
                'id' => $this->indexedId(++$index),
267
                'label' => $this->args->options[$value],
268
                'name' => $this->args->name,
269
                'required' => $this->args->required,
270
                'tabindex' => $this->args->tabindex,
271
                'type' => $this->args->type,
272
                'value' => $value,
273
            ]);
274
        });
275
    }
276
277
    /**
278
     * @return void|string
279
     */
280
    protected function buildFormLabel(array $customArgs = [])
281
    {
282
        if (!empty($this->args->label) && 'hidden' !== $this->args->type) {
283
            return $this->label(wp_parse_args($customArgs, [
284
                'for' => $this->args->id,
285
                'text' => $this->args->label,
286
            ]));
287
        }
288
    }
289
290
    /**
291
     * @return string|void
292
     */
293
    protected function buildFormSelect()
294
    {
295
        return $this->buildFormLabel().$this->buildDefaultElement($this->buildFormSelectOptions());
296
    }
297
298
    /**
299
     * @return string|void
300
     */
301
    protected function buildFormSelectOptions()
302
    {
303
        $options = $this->args->cast('options', 'array');
304
        $optgroupEnabled = glsr()->filterBool('builder/enable/optgroup', false);
305
        if ($this->args->placeholder) {
306
            $options = Arr::prepend($options, $this->args->placeholder, '');
307
        }
308
        return array_reduce(array_keys($options), function ($carry, $key) use ($options, $optgroupEnabled) {
309
            if ($optgroupEnabled && is_array($options[$key])) {
310
                return $carry.$this->buildFormSelectOptGroup($options[$key], $key);
311
            }
312
            return $carry.$this->option([
313
                'selected' => $this->args->cast('value', 'string') === Cast::toString($key),
314
                'text' => $options[$key],
315
                'value' => $key,
316
            ]);
317
        });
318
    }
319
320
    /**
321
     * @return string
322
     */
323
    protected function buildFormSelectOptGroup($options, $label)
324
    {
325
        $children = array_reduce(array_keys($options), function ($carry, $key) use ($options) {
326
           return $carry.glsr(Builder::class)->option([
327
                'selected' => $this->args->cast('value', 'string') === Cast::toString($key),
328
                'text' => $options[$key],
329
                'value' => $key,
330
            ]);
331
        });
332
        return glsr(Builder::class)->optgroup([
333
            'label' => $label,
334
            'text' => $children,
335
        ]);
336
    }
337
338
    /**
339
     * @return string|void
340
     */
341
    protected function buildFormTextarea()
342
    {
343
        return $this->buildFormLabel().$this->buildDefaultElement($this->args->cast('value', 'string'));
344
    }
345
346
    /**
347
     * @return string
348
     */
349
    protected function indexedId($index)
350
    {
351
        return Helper::ifTrue(count($this->args->options) > 1,
352
            $this->args->id.'-'.$index,
353
            $this->args->id
354
        );
355
    }
356
357
    /**
358
     * @param string $tag
359
     * @return bool
360
     */
361 7
    protected function isHtmlTag($tag)
362
    {
363 7
        return in_array($tag, array_merge(
364 7
            static::TAGS_FORM,
365 7
            static::TAGS_SINGLE,
366 7
            static::TAGS_STRUCTURE,
367 7
            static::TAGS_TEXT
368
        ));
369
    }
370
371
    /**
372
     * @param string $tag
373
     * @return string
374
     */
375 7
    protected function getFieldClassName($tag)
376
    {
377 7
        $className = Helper::buildClassName($tag, __NAMESPACE__.'\Fields');
378 7
        return glsr()->filterString('builder/field/'.$tag, $className);
379
    }
380
381
    /**
382
     * @return array
383
     */
384 7
    protected function normalize(array $args, $type)
385
    {
386 7
        if (class_exists($className = $this->getFieldClassName($type))) {
387
            $args = $className::merge($args);
388
        }
389 7
        return $args;
390
    }
391
392
    /**
393
     * @param string|array ...$params
394
     * @return array
395
     */
396 7
    protected function prepareArgs(...$params)
397
    {
398 7
        if (is_array($parameter1 = array_shift($params))) {
399 7
            return $parameter1;
400
        }
401
        $parameter2 = Arr::consolidate(array_shift($params));
402
        if (is_scalar($parameter1)) {
403
            $parameter2['text'] = $parameter1;
404
        }
405
        return $parameter2;
406
    }
407
}
408