Completed
Pull Request — 2.x (#43)
by jake
02:49 queued 50s
created

Select   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 362
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 65.88%

Importance

Changes 11
Bugs 1 Features 2
Metric Value
wmc 29
c 11
b 1
f 2
lcom 1
cbo 2
dl 0
loc 362
ccs 56
cts 85
cp 0.6588
rs 10

15 Methods

Rating   Name   Duplication   Size   Complexity  
A selected() 0 5 1
A placeholder() 0 5 1
A strict() 0 5 1
A __invoke() 0 11 2
A __toString() 0 18 1
A attribs() 0 13 3
A option() 0 5 1
A options() 0 17 3
A optgroup() 0 9 2
B buildSelect() 0 15 5
A buildOptionPlaceholder() 0 10 2
A buildOptions() 0 16 3
A buildOption() 0 19 2
A beginOptgroup() 0 8 1
A endOptgroup() 0 5 1
1
<?php
2
/**
3
 *
4
 * This file is part of Aura for PHP.
5
 *
6
 * @license http://opensource.org/licenses/bsd-license.php BSD
7
 *
8
 */
9
namespace Aura\Html\Helper\Input;
10
11
/**
12
 *
13
 * An HTML select input.
14
 *
15
 * @package Aura.Html
16
 *
17
 */
18
class Select extends AbstractInput
19
{
20
    /**
21
     *
22
     * A stack of HTML pieces for the select.
23
     *
24
     * @var array
25
     *
26
     */
27
    protected $stack = array();
28
29
    /**
30
     *
31
     * Are we currently processing an optgroup?
32
     *
33
     * @var bool
34
     *
35
     */
36
    protected $optgroup = false;
37
38
    /**
39
     *
40
     * The current option indent level.
41
     *
42
     * @var int
43
     *
44
     */
45
    protected $optlevel = 1;
46
47
    /**
48
     *
49
     * The value of the 'placeholder' pseudo-attribute.
50
     *
51
     * @var mixed
52
     *
53
     */
54
    protected $placeholder;
55
56
    /**
57
     *
58
     * Use strict equality when matching selected option values?
59
     *
60
     * @var bool
61
     *
62
     */
63
    protected $strict = false;
64
65
    /**
66
     *
67
     * If a $spec is passed, returns a full select tag with options; if no $spec
68
     * is passed, returns this helper object itself.
69
     *
70
     * @param array $spec A select input specfication.
71
     *
72
     * @return string|self
73
     *
74
     */
75 3
    public function __invoke(array $spec = null)
76
    {
77 3
        if ($spec !== null) {
78
            $this->prep($spec);
79
            $this->attribs($this->attribs);
80
            $this->options($this->options);
81
            $this->selected($this->value);
82
        }
83
84 3
        return $this;
85 3
    }
86
87
    /**
88
     *
89
     * Returns a select tag with options.
90
     *
91
     * @return string
92
     *
93
     */
94 3
    public function __toString()
95
    {
96
        // build the html
97 3
        $html = $this->buildSelect()
98
              . $this->buildOptionPlaceholder()
99
              . $this->buildOptions()
100
              . $this->indent(0, '</select>');
101
102
        // reset for next time
103 3
        $this->stack = array();
104 3
        $this->optgroup = false;
105 3
        $this->optlevel = 1;
106 3
        $this->placeholder = null;
107 3
        $this->strict = false;
108
109
        // done!
110 3
        return $html;
111
    }
112
113
    /**
114
     *
115
     * Sets the HTML attributes for the select tag.
116
     *
117
     * @param array $attribs The attribues to set.
118
     *
119
     * @return string
120
     *
121
     */
122 3
    public function attribs(array $attribs)
123
    {
124 3
        $this->attribs = $attribs;
125 3
        if (isset($this->attribs['placeholder'])) {
126
            $this->placeholder($this->attribs['placeholder']);
127 1
            unset($this->attribs['placeholder']);
128
        }
129 3
        if (isset($this->attribs['strict'])) {
130
            $this->strict($this->attribs['strict']);
131 1
            unset($this->attribs['strict']);
132
        }
133 3
        return $this;
134 3
    }
135
136
    /**
137
     *
138
     * Adds a single option to the stack.
139
     *
140
     * @param string $value The option value.
141
     *
142
     * @param string $label The option label.
143
     *
144
     * @param array $attribs Attributes for the option.
145
     *
146
     * @return self
147
     *
148
     */
149
    public function option($value, $label, array $attribs = array())
150
    {
151
        $this->stack[] = array('buildOption', $value, $label, $attribs);
152
        return $this;
153
    }
154
155
    /**
156
     *
157
     * Adds multiple options to the stack.
158
     *
159
     * @param array $options An array of options where the key is the option
160
     * value, and the value is the option label.  If the value is an array,
161
     * the key is treated as a label for an optgroup, and the value is
162
     * a sub-array of options.
163
     *
164
     * @param array $attribs Attributes to be used on each option.
165
     *
166
     * @return self
167
     *
168
     */
169 1
    public function options(array $options, array $attribs = array())
170
    {
171
        // set the options and optgroups
172
        foreach ($options as $key => $val) {
173
            if (is_array($val)) {
174
                // the key is an optgroup label
175
                $this->optgroup($key);
176
                // recursively descend into the array
177
                $this->options($val, $attribs);
178
            } else {
179
                // the key is an option value and the val is an option label
180
                $this->option($key, $val, $attribs);
181
            }
182
        }
183
184 1
        return $this;
185 1
    }
186
187
    /**
188
     *
189
     * Adds an optgroup input to the stack.
190
     *
191
     * @param string $label The optgroup label.
192
     *
193
     * @param array $attribs Attributes for the optgroup.
194
     *
195
     * @return self
196
     *
197
     */
198 2
    public function optgroup($label, array $attribs = array())
199
    {
200
        if ($this->optgroup) {
201 2
            $this->stack[] = array('endOptgroup');
202
        }
203
        $this->stack[] = array('beginOptgroup', $label, $attribs);
204
        $this->optgroup = true;
205
        return $this;
206
    }
207
208
    /**
209
     *
210
     * Sets the selected value(s).
211
     *
212
     * @param mixed $selected The selected value(s).
213
     *
214
     * @return self
215
     *
216
     */
217 3
    public function selected($selected)
218
    {
219 3
        $this->value = (array) $selected;
220 3
        return $this;
221
    }
222
223
    /**
224
     *
225
     * Sets the text for a placeholder option.
226
     *
227
     * @param string $placeholder The placeholder text.
228
     *
229
     * @return self
230
     *
231
     */
232 1
    public function placeholder($placeholder)
233
    {
234 1
        $this->placeholder = $placeholder;
235 1
        return $this;
236
    }
237
238
    /**
239
     *
240
     * Use strict equality when matching selected option values?
241
     *
242
     * @param bool $strict True for strict equality, false for loose equality.
243
     *
244
     * @return self
245
     *
246
     * @see buildOption()
247
     *
248
     */
249 1
    public function strict($strict = true)
250
    {
251 1
        $this->strict = (bool) $strict;
252 1
        return $this;
253
    }
254
255
    /**
256
     *
257
     * Builds the opening select tag.
258
     *
259
     * @return string
260
     *
261
     */
262 3
    protected function buildSelect()
263
    {
264 3
        $append_brackets = isset($this->attribs['multiple'])
265 1
                        && $this->attribs['multiple']
266 1
                        && isset($this->attribs['name'])
267 2
                        && substr($this->attribs['name'], -2) != '[]';
268
269
        // if this is a multiple select, the name needs to end in "[]"
270 3
        if ($append_brackets) {
271 1
            $this->attribs['name'] .= '[]';
272
        }
273
274
        $attr = $this->escaper->attr($this->attribs);
275
        return $this->indent(0, "<select {$attr}>");
276
    }
277
278
    /**
279
     *
280
     * Builds the 'placeholder' option (if any).
281
     *
282
     * @return string
283
     *
284
     */
285 3
    protected function buildOptionPlaceholder()
286
    {
287 3
        if ($this->placeholder) {
288 1
            return $this->buildOption(array(
289 1
                '',
290 1
                $this->placeholder,
291 1
                array('disabled' => true),
292
            ));
293
        }
294 2
    }
295
296
    /**
297
     *
298
     * Builds the collection of option tags.
299
     *
300
     * @return string
301
     *
302
     */
303 3
    protected function buildOptions()
304
    {
305 3
        $html = '';
306
307 3
        foreach ($this->stack as $info) {
308
            $method = array_shift($info);
309
            $html .= $this->$method($info);
310
        }
311
312
        // close any optgroup tags
313 3
        if ($this->optgroup) {
314
            $html .= $this->endOptgroup();
315
        }
316
317 3
        return $html;
318
    }
319
320
    /**
321
     *
322
     * Builds the HTML for a single option.
323
     *
324
     * @param array $info The option info.
325
     *
326
     * @return string
327
     *
328
     */
329 2
    protected function buildOption($info)
330
    {
331
        list($value, $label, $attribs) = $info;
332
333
        // set the option value into the attribs
334
        $attribs['value'] = $value;
335
336
        // is the value selected?
337
        if (in_array($value, $this->value, $this->strict)) {
338 2
            $attribs['selected'] = true;
339
        } else {
340
            unset($attribs['selected']);
341 2
        }
342
343
        // build attributes and return option tag with label text
344
        $attribs = $this->escaper->attr($attribs);
345
        $label = $this->escaper->html($label);
346
        return $this->indent($this->optlevel, "<option {$attribs}>{$label}</option>");
347
    }
348
349
    /**
350
     *
351
     * Builds the HTML to begin an optgroup.
352
     *
353
     * @param array $info The optgroup info.
354
     *
355
     * @return null
356
     *
357
     */
358
    protected function beginOptgroup($info)
359
    {
360
        list($label, $attribs) = $info;
361
        $this->optlevel += 1;
362
        $attribs['label'] = $label;
363
        $attribs = $this->escaper->attr($attribs);
364
        return $this->indent(1, "<optgroup {$attribs}>");
365
    }
366
367
    /**
368
     *
369
     * Builds the HTML to end an optgroup.
370
     *
371
     * @return null
372
     *
373
     */
374
    protected function endOptgroup()
375
    {
376
        $this->optlevel -= 1;
377
        return $this->indent(1, "</optgroup>");
378
    }
379
}
380