1<?php
2
3namespace dokuwiki\Form;
4
5/**
6 * Class InputElement
7 *
8 * Base class for all input elements. Uses a wrapping label when label
9 * text is given.
10 *
11 * @todo figure out how to make wrapping or related label configurable
12 * @package dokuwiki\Form
13 */
14class InputElement extends Element
15{
16    /**
17     * @var LabelElement
18     */
19    protected $label;
20
21    /**
22     * @var bool if the element should reflect posted values
23     */
24    protected $useInput = true;
25
26    /**
27     * @param string $type The type of this element
28     * @param string $name The name of this form element
29     * @param string $label The label text for this element (will be autoescaped)
30     */
31    public function __construct($type, $name, $label = '')
32    {
33        parent::__construct($type, ['name' => $name]);
34        $this->attr('name', $name);
35        $this->attr('type', $type);
36        if ($label) $this->label = new LabelElement($label);
37    }
38
39    /**
40     * Returns the label element if there's one set
41     *
42     * @return LabelElement|null
43     */
44    public function getLabel()
45    {
46        return $this->label;
47    }
48
49    /**
50     * Should the user sent input be used to initialize the input field
51     *
52     * The default is true. Any set values will be overwritten by the INPUT
53     * provided values.
54     *
55     * @param bool $useinput
56     * @return $this
57     */
58    public function useInput($useinput)
59    {
60        $this->useInput = (bool) $useinput;
61        return $this;
62    }
63
64    /**
65     * Get or set the element's ID
66     *
67     * @param null|string $id
68     * @return string|$this
69     */
70    public function id($id = null)
71    {
72        if ($this->label) $this->label->attr('for', $id);
73        return parent::id($id);
74    }
75
76    /**
77     * Adds a class to the class attribute
78     *
79     * This is the preferred method of setting the element's class
80     *
81     * @param string $class the new class to add
82     * @return $this
83     */
84    public function addClass($class)
85    {
86        if ($this->label) $this->label->addClass($class);
87        return parent::addClass($class);
88    }
89
90    /**
91     * Figures out how to access the value for this field from INPUT data
92     *
93     * The element's name could have been given as a simple string ('foo')
94     * or in array notation ('foo[bar]').
95     *
96     * Note: this function only handles one level of arrays. If your data
97     * is nested deeper, you should call useInput(false) and set the
98     * correct value yourself
99     *
100     * @return array name and array key (null if not an array)
101     */
102    protected function getInputName()
103    {
104        $name = $this->attr('name');
105        parse_str("$name=1", $parsed);
106
107        $name = array_keys($parsed);
108        $name = array_shift($name);
109
110        if (isset($parsed[$name]) && is_array($parsed[$name])) {
111            $key = array_keys($parsed[$name]);
112            $key = array_shift($key);
113        } else {
114            $key = null;
115        }
116
117        return [$name, $key];
118    }
119
120    /**
121     * Handles the useInput flag and set the value attribute accordingly
122     */
123    protected function prefillInput()
124    {
125        global $INPUT;
126
127        [$name, $key] = $this->getInputName();
128        if (!$INPUT->has($name)) return;
129
130        if ($key === null) {
131            $value = $INPUT->str($name);
132        } else {
133            $value = $INPUT->arr($name);
134            if (isset($value[$key])) {
135                $value = $value[$key];
136            } else {
137                $value = '';
138            }
139        }
140        $this->val($value);
141    }
142
143    /**
144     * The HTML representation of this element
145     *
146     * @return string
147     */
148    protected function mainElementHTML()
149    {
150        if ($this->useInput) $this->prefillInput();
151        return '<input ' . buildAttributes($this->attrs()) . ' />';
152    }
153
154    /**
155     * The HTML representation of this element wrapped in a label
156     *
157     * @return string
158     */
159    public function toHTML()
160    {
161        if ($this->label) {
162            return '<label ' . buildAttributes($this->label->attrs()) . '>' . DOKU_LF
163                . '<span>' . hsc($this->label->val()) . '</span>' . DOKU_LF
164                . $this->mainElementHTML() . DOKU_LF
165                . '</label>';
166        } else {
167            return $this->mainElementHTML();
168        }
169    }
170}
171