Completed
Push — master ( 6cae08...ee02a6 )
by Rasmus
05:14
created

InputRenderer   C

Complexity

Total Complexity 61

Size/Duplication

Total Lines 686
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 99.37%

Importance

Changes 30
Bugs 0 Features 4
Metric Value
wmc 61
c 30
b 0
f 4
lcom 1
cbo 2
dl 0
loc 686
ccs 157
cts 158
cp 0.9937
rs 5.1727

28 Methods

Rating   Name   Duplication   Size   Complexity  
A getLabel() 0 6 2
A setLabel() 0 4 1
A getName() 0 7 2
A __construct() 0 10 3
A getPlaceholder() 0 6 2
A setPlaceholder() 0 4 1
A getId() 0 6 3
B getAttrs() 0 14 5
A isRequired() 0 6 2
A setRequired() 0 4 1
A render() 0 4 1
A renderGroup() 0 8 1
A renderDiv() 0 4 1
A mergeAttrs() 0 18 4
A escape() 0 4 1
A softEscape() 0 4 1
A tag() 0 6 3
A openTag() 0 4 1
D attrs() 0 30 9
A input() 0 14 1
A inputFor() 0 18 2
A group() 0 7 1
A groupFor() 0 7 1
A endGroup() 0 4 1
A divFor() 0 4 1
A label() 0 14 1
A labelFor() 0 18 4
B visit() 0 32 5

How to fix   Complexity   

Complex Class

Complex classes like InputRenderer 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 InputRenderer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace mindplay\kissform;
4
5
use mindplay\kissform\Facets\FieldInterface;
6
use RuntimeException;
7
8
/**
9
 * This class renders and populates input elements for use in forms,
10
 * by consuming property-information provided by {@see Field} objects,
11
 * and populating them with state from an {@see InputModel}.
12
 *
13
 * Conventions for method-names in this class:
14
 *
15
 *   * `get_()` and `is_()` methods provide raw information about Fields
16
 * 
17
 *   * `render_()` methods delegate rendering to {@see Field::renderInput} implementations.
18
 * 
19
 *   * `_For()` methods (such as `inputFor()`) render low-level HTML tags (with state) for Fields
20
 * 
21
 *   * Verb methods like `visit`, `merge` and `escape` perform various relevant actions
22
 * 
23
 *   * Noun methods like `tag`, `attrs` and `label` create low-level HTML tags
24
 *
25
 */
26
class InputRenderer
27
{
28
    /**
29
     * @var string HTML encoding charset
30
     */
31
    public $encoding = 'UTF-8';
32
33
    /**
34
     * @var bool if true, use long form XHTML for value-less attributes (e.g. disabled="disabled")
35
     *
36
     * @see attrs()
37
     */
38
    public $xhtml = false;
39
40
    /**
41
     * @var InputModel input model
42
     */
43
    public $model;
44
45
    /**
46
     * @var string|string[]|null form element collection name(s)
47
     */
48
    public $collection_name;
49
50
    /**
51
     * @var string|null form element id-attribute prefix (or null, to bypass id-attribute generation)
52
     */
53
    public $id_prefix;
54
55
    /**
56
     * @var string CSS class name applied to all form controls
57
     *
58
     * @see inputFor()
59
     */
60
    public $input_class = 'form-control';
61
62
    /**
63
     * @var string CSS class name added to labels
64
     *
65
     * @see labelFor()
66
     */
67
    public $label_class = 'control-label';
68
69
    /**
70
     * @var string suffix to append to all labels (e.g. ":")
71
     *
72
     * @see labelFor()
73
     */
74
    public $label_suffix = '';
75
76
    /**
77
     * @var string CSS class-name added to required fields
78
     *
79
     * @see groupFor()
80
     */
81
    public $required_class = 'required';
82
83
    /**
84
     * @var string CSS class-name added to fields with error state
85
     *
86
     * @see groupFor()
87
     */
88
    public $error_class = 'has-error';
89
90
    /**
91
     * @var string group tag name (e.g. "div", "fieldset", etc.; defaults to "div")
92
     *
93
     * @see groupFor()
94
     * @see endGroup()
95
     */
96
    public $group_tag = 'div';
97
98
    /**
99
     * @var array default attributes to be added to opening control-group tags
100
     *
101
     * @see groupFor()
102
     */
103
    public $group_attrs = ['class' => 'form-group'];
104
105
    /**
106
     * @var string[] map where Field name => label override
107
     */
108
    protected $labels = [];
109
110
    /**
111
     * @var string[] map where Field name => placeholder override
112
     */
113
    protected $placeholders = [];
114
115
    /**
116
     * @var bool[] map where Field name => required flag
117
     */
118
    protected $required = [];
119
120
    /**
121
     * @var string[] list of void elements
122
     *
123
     * @see tag()
124
     *
125
     * @link http://www.w3.org/TR/html-markup/syntax.html#void-elements
126
     */
127
    private static $void_elements = [
128
        'area'    => true,
129
        'base'    => true,
130
        'br'      => true,
131
        'col'     => true,
132
        'command' => true,
133
        'embed'   => true,
134
        'hr'      => true,
135
        'img'     => true,
136
        'input'   => true,
137
        'keygen'  => true,
138
        'link'    => true,
139
        'meta'    => true,
140
        'param'   => true,
141
        'source'  => true,
142
        'track'   => true,
143
        'wbr'     => true,
144
    ];
145
146
    /**
147
     * @param InputModel|array|null $model           input model, or (possibly nested) input array (e.g. $_GET or $_POST)
148
     * @param string|string[]|null  $collection_name collection name(s) for inputs, e.g. 'myform' or ['myform', '123'] etc.
149
     * @param string|null           $id_prefix       base id for inputs, e.g. 'myform' or 'myform-123', etc.
150
     */
151 25
    public function __construct($model = null, $collection_name = null, $id_prefix = "form")
152
    {
153 25
        $this->model = InputModel::create($model);
154 25
        $this->collection_name = $collection_name;
155 25
        $this->id_prefix = $id_prefix === null
156 1
            ? ($collection_name === null
157 1
                ? null
158 1
                : implode('-', (array) $this->collection_name))
159 25
            : $id_prefix;
160 25
    }
161
162
    /**
163
     * @param FieldInterface $field
164
     *
165
     * @return string
166
     *
167
     * @see Field::getLabel()
168
     */
169 3
    public function getLabel(FieldInterface $field)
170
    {
171 3
        return array_key_exists($field->getName(), $this->labels)
172 1
            ? $this->labels[$field->getName()]
173 3
            : $field->getLabel();
174
    }
175
176
    /**
177
     * Override the label defined by the Field
178
     *
179
     * @param FieldInterface $field
180
     * @param string         $label
181
     */
182 1
    public function setLabel(FieldInterface $field, $label)
183
    {
184 1
        $this->labels[$field->getName()] = $label;
185 1
    }
186
187
    /**
188
     * @param FieldInterface $field
189
     *
190
     * @return string
191
     *
192
     * @see Field::getPlaceholder()
193
     */
194 12
    public function getPlaceholder(FieldInterface $field)
195
    {
196 12
        return array_key_exists($field->getName(), $this->placeholders)
197 1
            ? $this->placeholders[$field->getName()]
198 12
            : $field->getPlaceholder();
199
    }
200
201
    /**
202
     * Override the placeholder label defined by the Field
203
     *
204
     * @param FieldInterface $field
205
     * @param string         $placeholder
206
     */
207 1
    public function setPlaceholder(FieldInterface $field, $placeholder)
208
    {
209 1
        $this->placeholders[$field->getName()] = $placeholder;
210 1
    }
211
212
    /**
213
     * @param FieldInterface $field
214
     *
215
     * @return string|null computed name-attribute
216
     */
217 18
    public function getName(FieldInterface $field)
218
    {
219 18
        $names = (array) $this->collection_name;
220 18
        $names[] = $field->getName();
221
222 18
        return $names[0] . (count($names) > 1 ? '[' . implode('][', array_slice($names, 1)) . ']' : '');
223
    }
224
225
    /**
226
     * @param FieldInterface $field
227
     * @param string|null    $suffix
228
     *
229
     * @return string|null computed id-attribute
230
     */
231 17
    public function getId(FieldInterface $field, $suffix = null)
232
    {
233 17
        return $this->id_prefix
234 17
            ? $this->id_prefix . '-' . $field->getName() . ($suffix ? "-{$suffix}" : '')
235 17
            : null;
236
    }
237
238
    /**
239
     * Conditionally create (or add) CSS class-names for Field status, e.g.
240
     * {@see $required_class} for {@see Field::$required} and {@see $error_class}
241
     * if the {@see $model} contains an error.
242
     *
243
     * @param FieldInterface $field
244
     *
245
     * @return array map of HTML attributes (with additonial classes)
246
     *
247
     * @see $required_class
248
     * @see $error_class
249
     */
250 4
    public function getAttrs(FieldInterface $field)
251
    {
252 4
        $classes = [];
253
254 4
        if ($this->required_class !== null && $this->isRequired($field)) {
255 3
            $classes[] = $this->required_class;
256
        }
257
258 4
        if ($this->error_class !== null && $this->model->hasError($field)) {
259 2
            $classes[] = $this->error_class;
260
        }
261
262 4
        return ['class' => $classes];
263
    }
264
265
    /**
266
     * @param FieldInterface $field
267
     *
268
     * @return bool true, if the given Field is required
269
     */
270 12
    public function isRequired(FieldInterface $field)
271
    {
272 12
        return array_key_exists($field->getName(), $this->required)
273 1
            ? $this->required[$field->getName()]
274 12
            : $field->isRequired();
275
    }
276
277
    /**
278
     * Override the required flag defined by the Field
279
     *
280
     * @param FieldInterface $field
281
     * @param bool           $required
282
     */
283 1
    public function setRequired(FieldInterface $field, $required = true)
284
    {
285 1
        $this->required[$field->getName()] = (bool) $required;
286 1
    }
287
288
    /**
289
     * Build an HTML input for a given Field.
290
     *
291
     * @param FieldInterface $field
292
     * @param array          $attr
293
     *
294
     * @return string
295
     *
296
     * @throws RuntimeException if the given Field cannot be rendered
297
     */
298 18
    public function render(FieldInterface $field, array $attr = [])
299
    {
300 18
        return $field->renderInput($this, $this->model, $attr);
301
    }
302
303
    /**
304
     * Builds an HTML group containing a label and rendered input for a given Field.
305
     *
306
     * @param FieldInterface $field
307
     * @param string|null    $label      label text (optional)
308
     * @param array          $input_attr map of HTML attributes for the input (optional)
309
     * @param array          $group_attr map of HTML attributes for the group (optional)
310
     *
311
     * @return string
312
     */
313 1
    public function renderGroup(FieldInterface $field, $label = null, array $input_attr = [], $group_attr = [])
314
    {
315
        return
316 1
            $this->groupFor($field, $group_attr)
317 1
            . $this->labelFor($field, $label)
318 1
            . $this->render($field, $input_attr)
319 1
            . $this->endGroup();
320
    }
321
322
    /**
323
     * Builds an HTML div with state-classes, containing a rendered input for a given Field.
324
     *
325
     * @param FieldInterface $field
326
     * @param array          $input_attr attributes for the generated input
327
     * @param array          $div_attr   attributes for the wrapper div
328
     *
329
     * @return string HTML
330
     */
331 2
    public function renderDiv(FieldInterface $field, array $input_attr = [], $div_attr = [])
332
    {
333 2
        return $this->divFor($field, $this->render($field, $input_attr), $div_attr);
334
    }
335
336
    /**
337
     * Visit a given Field - temporarily swaps out {@see $model}, {@see $name_prefix}
338
     * and {@see $id_prefix} and merges any changes made to the model while calling
339
     * the given function.
340
     *
341
     * @param FieldInterface|int|string $field Field instance, or an integer index, or string key
342
     * @param callable                  $func  function (InputModel $model): mixed
343
     *
344
     * @return mixed
345
     */
346 2
    public function visit($field, $func)
347
    {
348 2
        $model = $this->model;
349 2
        $name_prefix = $this->collection_name;
350 2
        $id_prefix = $this->id_prefix;
351
352 2
        $key = $field instanceof FieldInterface
353 2
            ? $field->getName()
354 2
            : (string) $field;
355
356 2
        $this->model = InputModel::create(@$model->input[$key], $model->getError($key));
0 ignored issues
show
Bug introduced by
It seems like $model->getError($key) targeting mindplay\kissform\InputModel::getError() can also be of type string; however, mindplay\kissform\InputModel::create() does only seem to accept array<integer,string>|null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
357 2
        $this->collection_name = array_merge((array) $this->collection_name, [$key]);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge((array) $thi...tion_name, array($key)) of type array is incompatible with the declared type string|array<integer,string>|null of property $collection_name.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
358 2
        $this->id_prefix = $this->id_prefix
359 2
            ? $this->id_prefix . '-' . $key
360
            : null;
361
362 2
        call_user_func($func, $this->model);
363
364 2
        if ($this->model->input !== []) {
365 2
            $model->input[$key] = $this->model->input;
366
        } else {
367 2
            unset($model->input[$key]);
368
        }
369
370 2
        if ($this->model->hasErrors()) {
371 1
            $model->setError($key, $this->model->getErrors());
372
        }
373
374 2
        $this->model = $model;
375 2
        $this->collection_name = $name_prefix;
376 2
        $this->id_prefix = $id_prefix;
377 2
    }
378
379
    /**
380
     * Merge any number of attribute maps, with the latter overwriting the first, and
381
     * with special handling for the class-attribute.
382
     *
383
     * @param array ...$attr
384
     *
385
     * @return array
386
     */
387 19
    public function mergeAttrs()
388
    {
389 19
        $maps = func_get_args();
390
391 19
        $result = [];
392
393 19
        foreach ($maps as $map) {
394 19
            if (isset($map['class'])) {
395 17
                if (isset($result['class'])) {
396 7
                    $map['class'] = array_merge((array) $result['class'], (array) $map['class']);
397
                }
398
            }
399
400 19
            $result = array_merge($result, $map);
401
        }
402
403 19
        return $result;
404
    }
405
406
    /**
407
     * Encode plain text as HTML
408
     *
409
     * @param string $text  plain text
410
     * @param int    $flags encoding flags (optional, see htmlspecialchars)
411
     *
412
     * @return string escaped HTML
413
     *
414
     * @see softEscape()
415
     */
416 23
    public function escape($text, $flags = ENT_COMPAT)
417
    {
418 23
        return htmlspecialchars($text, $flags, $this->encoding, true);
419
    }
420
421
    /**
422
     * Encode plain text as HTML, while attempting to avoid double-encoding
423
     *
424
     * @param string $text  plain text
425
     * @param int    $flags encoding flags (optional, see htmlspecialchars)
426
     *
427
     * @return string escaped HTML
428
     *
429
     * @see escape()
430
     */
431 5
    public function softEscape($text, $flags = ENT_COMPAT)
432
    {
433 5
        return htmlspecialchars($text, $flags, $this->encoding, false);
434
    }
435
436
    /**
437
     * Build an opening and closing HTML tag (or a self-closing tag) - examples:
438
     *
439
     *     echo $renderer->tag('input', array('type' => 'text'));  => <input type="text"/>
440
     *
441
     *     echo $renderer->tag('div', array(), 'Foo &amp; Bar');   => <div>Foo &amp; Bar</div>
442
     *
443
     *     echo $renderer->tag('script', array(), '');             => <script></script>
444
     *
445
     * @param string      $name HTML tag name
446
     * @param array       $attr map of HTML attributes
447
     * @param string|null $html inner HTML, or NULL to build a self-closing tag
448
     *
449
     * @return string
450
     *
451
     * @see openTag()
452
     */
453 22
    public function tag($name, array $attr = [], $html = null)
454
    {
455 22
        return $html === null && isset(self::$void_elements[$name])
456 17
            ? '<' . $name . $this->attrs($attr) . '/>'
457 22
            : '<' . $name . $this->attrs($attr) . '>' . $html . '</' . $name . '>';
458
    }
459
460
    /**
461
     * Build an open HTML tag; remember to close the tag.
462
     *
463
     * Note that there is no closeTag() equivalent, as this wouldn't help with anything
464
     * and would actually require more code than e.g. a simple literal `</div>`
465
     *
466
     * @param string $name HTML tag name
467
     * @param array  $attr map of HTML attributes
468
     *
469
     * @return string
470
     *
471
     * @see tag()
472
     */
473 3
    public function openTag($name, array $attr = [])
474
    {
475 3
        return '<' . $name . $this->attrs($attr) . '>';
476
    }
477
478
    /**
479
     * Build HTML attributes for use inside an HTML (or XML) tag.
480
     *
481
     * Includes a leading space, since this is usually used inside a tag, e.g.:
482
     *
483
     *     <div<?= $form->attrs(array('class' => 'foo')) ?>>...</div>
484
     *
485
     * Accepts strings, or arrays of strings, as attribute-values - arrays will
486
     * be folded using space as a separator, e.g. useful for the class-attribute.
487
     *
488
     * Attributes containing NULL, FALSE or an empty array() are ignored.
489
     *
490
     * Attributes containing TRUE are rendered as value-less attributes.
491
     *
492
     * @param array $attr map where attribute-name => attribute value(s)
493
     * @param bool  $sort true, to sort attributes by name; otherwise false (sorting is enabled by default)
494
     *
495
     * @return string
496
     */
497 23
    public function attrs(array $attr, $sort = true)
498
    {
499 23
        if ($sort) {
500 23
            ksort($attr);
501
        }
502
503 23
        $html = '';
504
505 23
        foreach ($attr as $name => $value) {
506 23
            if (is_array($value)) {
507 8
                $value = count($value)
508 7
                    ? implode(' ', $value) // fold multi-value attribute (e.g. class-names)
509 8
                    : null; // filter empty array
510
            }
511
512 23
            if ($value === null || $value === false) {
513 17
                continue; // skip NULL and FALSE attributes
514
            }
515
516 23
            if ($value === true) {
517 8
                $html .= $this->xhtml ?
518 1
                    ' ' . $name . '="' . $name . '"' // e.g. disabled="disabled" (as required for XHTML)
519 8
                    : ' ' . $name; // value-less HTML attribute
520
            } else {
521 23
                $html .= ' ' . $name . '="' . $this->escape($value) . '"';
522
            }
523
        }
524
525 23
        return $html;
526
    }
527
528
    /**
529
     * Builds an HTML <input> tag
530
     *
531
     * @param string $type
532
     * @param string $name
0 ignored issues
show
Documentation introduced by
Should the type for parameter $name not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
533
     * @param string $value
0 ignored issues
show
Documentation introduced by
Should the type for parameter $value not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
534
     * @param array  $attr map of HTML attributes
535
     *
536
     * @return string
537
     *
538
     * @see inputFor()
539
     */
540 1
    public function input($type, $name = null, $value= null, $attr = [])
541
    {
542 1
        return $this->tag(
543 1
            'input',
544 1
            $this->mergeAttrs(
545
                [
546 1
                    'type'  => $type,
547 1
                    'name'  => $name,
548 1
                    'value' => $value,
549
                ],
550
                $attr
551
            )
552
        );
553
    }
554
555
    /**
556
     * Build an HTML5 input-tag for a given Field
557
     *
558
     * @param FieldInterface $field
559
     * @param string         $type HTML5 input type-attribute (e.g. "text", "password", etc.)
560
     * @param array          $attr map of HTML attributes
561
     *
562
     * @return string
563
     */
564 11
    public function inputFor(FieldInterface $field, $type, array $attr = [])
565
    {
566 11
        return $this->tag(
567 11
            'input',
568 11
            $this->mergeAttrs(
569
                [
570 11
                    'name'        => $this->getName($field),
571 11
                    'id'          => $this->getId($field),
572 11
                    'class'       => $this->input_class,
573 11
                    'value'       => $this->model->getInput($field),
574 11
                    'type'        => $type,
575 11
                    'required'    => $this->isRequired($field),
576 11
                    'placeholder' => @$attr['placeholder'] ?: $this->getPlaceholder($field),
577
                ],
578
                $attr
579
            )
580
        );
581
    }
582
583
    /**
584
     * Build an HTML opening tag for an input group
585
     *
586
     * Call {@see endGroup()} to create the matching closing tag.
587
     *
588
     * @param array $attr optional map of HTML attributes
589
     *
590
     * @return string
591
     *
592
     * @see groupFor()
593
     */
594 1
    public function group($attr = [])
595
    {
596 1
        return $this->openTag(
597 1
            $this->group_tag,
598 1
            $this->mergeAttrs($this->group_attrs, $attr)
599
        );
600
    }
601
602
    /**
603
     * Build an HTML opening tag for an input group, with CSS classes added for
604
     * {@see Field::$required} and error state, as needed.
605
     *
606
     * Call {@see endGroup()} to create the matching closing tag.
607
     *
608
     * @param FieldInterface $field
609
     * @param array          $attr map of HTML attributes (optional)
610
     *
611
     * @return string
612
     *
613
     * @see $group_tag
614
     * @see $group_attrs
615
     * @see $required_class
616
     * @see $error_class
617
     * @see endGroup()
618
     */
619 2
    public function groupFor(FieldInterface $field, array $attr = [])
620
    {
621 2
        return $this->openTag(
622 2
            $this->group_tag,
623 2
            $this->mergeAttrs($this->group_attrs, $this->getAttrs($field), $attr)
624
        );
625
    }
626
627
    /**
628
     * Returns the matching closing tag for a {@see group()} or {@see groupFor()} tag.
629
     *
630
     * @return string
631
     *
632
     * @see groupFor()
633
     * @see $group_tag
634
     */
635 2
    public function endGroup()
636
    {
637 2
        return "</{$this->group_tag}>";
638
    }
639
640
    /**
641
     * Builds an HTML div with state-classes, containing the given HTML.
642
     *
643
     * @param FieldInterface $field
644
     * @param string         $html inner HTML for the generated div
645
     * @param array          $attr additional attributes for the div
646
     *
647
     * @return string HTML
648
     */
649 2
    public function divFor(FieldInterface $field, $html, array $attr = [])
650
    {
651 2
        return $this->tag('div', $this->mergeAttrs($this->getAttrs($field), $attr), $html);
652
    }
653
654
    /**
655
     * Build a `<label for="id" />` tag
656
     *
657
     * @param string $for   target element ID
658
     * @param string $label label text
659
     * @param array  $attr  map of HTML attributes
660
     *
661
     * @return string
662
     *
663
     * @see labelFor()
664
     */
665 3
    public function label($for, $label, $attr = [])
666
    {
667 3
        return $this->tag(
668 3
            'label',
669 3
            $this->mergeAttrs(
670
                [
671 3
                    'for' => $for,
672 3
                    'class' => $this->label_class
673
                ],
674
                $attr
675
            ),
676 3
            $this->softEscape($label . $this->label_suffix)
677
        );
678
    }
679
680
    /**
681
     * Build an HTML `<label for="id" />` tag
682
     *
683
     * @param FieldInterface $field
684
     * @param string|null    $label label text (optional)
685
     * @param array          $attr  map of HTML attributes
686
     *
687
     * @return string
688
     *
689
     * @see Field::getLabel()
690
     *
691
     * @throws RuntimeException if a label cannot be produced
692
     */
693 2
    public function labelFor(FieldInterface $field, $label = null, array $attr = [])
0 ignored issues
show
Unused Code introduced by
The parameter $attr is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
694
    {
695 2
        $id = $this->getId($field);
696
697 2
        if ($id === null) {
698 1
            throw new RuntimeException("cannot produce a label when FormHelper::\$id_prefix is NULL");
699
        }
700
701 2
        if ($label === null) {
702 2
            $label = $this->getLabel($field);
703
704 2
            if ($label === null) {
705 1
                throw new RuntimeException("the given Field has no defined label");
706
            }
707
        }
708
709 2
        return $this->label($id, $label);
710
    }
711
}
712