• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / forms / 15763260878

19 Jun 2025 05:19PM UTC coverage: 93.011%. Remained the same
15763260878

push

github

dg
netteForms: restructured package, includes UMD and ESM (BC break)

2076 of 2232 relevant lines covered (93.01%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.67
/src/Forms/Helpers.php
1
<?php
2

3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7

8
declare(strict_types=1);
9

10
namespace Nette\Forms;
11

12
use Nette;
13
use Nette\Utils\Html;
14
use Nette\Utils\Image;
15
use Nette\Utils\Strings;
16
use function array_fill_keys, array_map, array_values, explode, html_entity_decode, htmlspecialchars, in_array, ini_get, is_a, is_array, is_numeric, is_scalar, is_string, str_ends_with, str_replace, strip_tags, strpos, strtolower, strtr, substr, substr_replace;
17
use const ENT_HTML5, ENT_NOQUOTES, ENT_QUOTES;
18

19

20
/**
21
 * Forms helpers.
22
 */
23
final class Helpers
24
{
25
        use Nette\StaticClass;
26

27
        private const UnsafeNames = [
28
                'attributes', 'children', 'elements', 'focus', 'length', 'reset', 'style', 'submit', 'onsubmit', 'form',
29
                'presenter', 'action',
30
        ];
31

32

33
        /**
34
         * Extracts and sanitizes submitted form data for single control.
35
         * @param  int  $type  type Form::DataText, DataLine, DataFile, DataKeys
36
         * @internal
37
         */
38
        public static function extractHttpData(
1✔
39
                array $data,
40
                string $htmlName,
41
                int $type,
42
        ): string|array|Nette\Http\FileUpload|null
43
        {
44
                $name = explode('[', str_replace(['[]', ']', '.'], ['', '', '_'], $htmlName));
1✔
45
                $data = Nette\Utils\Arrays::get($data, $name, null);
1✔
46
                $itype = $type & ~Form::DataKeys;
1✔
47

48
                if (str_ends_with($htmlName, '[]')) {
1✔
49
                        if (!is_array($data)) {
1✔
50
                                return [];
1✔
51
                        }
52

53
                        foreach ($data as $k => $v) {
1✔
54
                                $data[$k] = $v = static::sanitize($itype, $v);
1✔
55
                                if ($v === null) {
1✔
56
                                        unset($data[$k]);
1✔
57
                                }
58
                        }
59

60
                        if ($type & Form::DataKeys) {
1✔
61
                                return $data;
1✔
62
                        }
63

64
                        return array_values($data);
1✔
65
                } else {
66
                        return static::sanitize($itype, $data);
1✔
67
                }
68
        }
69

70

71
        private static function sanitize(int $type, $value): string|array|Nette\Http\FileUpload|null
1✔
72
        {
73
                if ($type === Form::DataText) {
1✔
74
                        return is_scalar($value)
1✔
75
                                ? Strings::unixNewLines((string) $value)
1✔
76
                                : null;
1✔
77

78
                } elseif ($type === Form::DataLine) {
1✔
79
                        return is_scalar($value)
1✔
80
                                ? Strings::trim(strtr((string) $value, "\r\n", '  '))
1✔
81
                                : null;
1✔
82

83
                } elseif ($type === Form::DataFile) {
1✔
84
                        return $value instanceof Nette\Http\FileUpload ? $value : null;
1✔
85

86
                } else {
87
                        throw new Nette\InvalidArgumentException('Unknown data type');
×
88
                }
89
        }
90

91

92
        /**
93
         * Converts control name to HTML name.
94
         */
95
        public static function generateHtmlName(string $id): string
1✔
96
        {
97
                $name = str_replace(Nette\ComponentModel\IComponent::NAME_SEPARATOR, '][', $id, $count);
1✔
98
                if ($count) {
1✔
99
                        $name = substr_replace($name, '', strpos($name, ']'), 1) . ']';
1✔
100
                }
101

102
                if (is_numeric($name) || in_array($name, self::UnsafeNames, strict: true)) {
1✔
103
                        $name = '_' . $name;
1✔
104
                }
105

106
                return $name;
1✔
107
        }
108

109

110
        public static function exportRules(Rules $rules): array
1✔
111
        {
112
                $payload = [];
1✔
113
                foreach ($rules as $rule) {
1✔
114
                        if (!$rule->canExport()) {
1✔
115
                                if ($rule->branch) {
1✔
116
                                        continue;
1✔
117
                                }
118

119
                                break;
1✔
120
                        }
121

122
                        $op = $rule->validator;
1✔
123
                        if (!is_string($op)) {
1✔
124
                                $op = Nette\Utils\Callback::toString($op);
×
125
                        }
126

127
                        if ($rule->branch) {
1✔
128
                                $item = [
1✔
129
                                        'op' => ($rule->isNegative ? '~' : '') . $op,
1✔
130
                                        'rules' => static::exportRules($rule->branch),
1✔
131
                                        'control' => $rule->control->getHtmlName(),
1✔
132
                                ];
133
                                if ($rule->branch->getToggles()) {
1✔
134
                                        $item['toggle'] = $rule->branch->getToggles();
1✔
135
                                } elseif (!$item['rules']) {
1✔
136
                                        continue;
1✔
137
                                }
138
                        } else {
139
                                $msg = Validator::formatMessage($rule, withValue: false);
1✔
140
                                if ($msg instanceof Nette\HtmlStringable) {
1✔
141
                                        $msg = html_entity_decode(strip_tags((string) $msg), ENT_QUOTES | ENT_HTML5, 'UTF-8');
×
142
                                }
143

144
                                $item = ['op' => ($rule->isNegative ? '~' : '') . $op, 'msg' => $msg];
1✔
145
                        }
146

147
                        if (is_array($rule->arg)) {
1✔
148
                                $item['arg'] = [];
1✔
149
                                foreach ($rule->arg as $key => $value) {
1✔
150
                                        $item['arg'][$key] = self::exportArgument($value, $rule->control);
1✔
151
                                }
152
                        } elseif ($rule->arg !== null) {
1✔
153
                                $item['arg'] = self::exportArgument($rule->arg, $rule->control);
1✔
154
                        }
155

156
                        $payload[] = $item;
1✔
157
                }
158

159
                return $payload;
1✔
160
        }
161

162

163
        private static function exportArgument(mixed $value, Control $control): mixed
1✔
164
        {
165
                if ($value instanceof Control) {
1✔
166
                        return ['control' => $value->getHtmlName()];
1✔
167
                } elseif ($control instanceof Controls\DateTimeControl) {
1✔
168
                        return $control->formatHtmlValue($value);
1✔
169
                } else {
170
                        return $value;
1✔
171
                }
172
        }
173

174

175
        public static function createInputList(
1✔
176
                array $items,
177
                ?array $inputAttrs = null,
178
                ?array $labelAttrs = null,
179
                $wrapper = null,
180
        ): string
181
        {
182
                [$inputAttrs, $inputTag] = self::prepareAttrs($inputAttrs, 'input');
1✔
183
                [$labelAttrs, $labelTag] = self::prepareAttrs($labelAttrs, 'label');
1✔
184
                $res = '';
1✔
185
                $input = Html::el();
1✔
186
                $label = Html::el();
1✔
187
                [$wrapper, $wrapperEnd] = $wrapper instanceof Html ? [$wrapper->startTag(), $wrapper->endTag()] : [(string) $wrapper, ''];
1✔
188

189
                foreach ($items as $value => $caption) {
1✔
190
                        foreach ($inputAttrs as $k => $v) {
1✔
191
                                $input->attrs[$k] = $v[$value] ?? null;
1✔
192
                        }
193

194
                        foreach ($labelAttrs as $k => $v) {
1✔
195
                                $label->attrs[$k] = $v[$value] ?? null;
1✔
196
                        }
197

198
                        $input->value = $value;
1✔
199
                        $res .= ($res === '' && $wrapperEnd === '' ? '' : $wrapper)
1✔
200
                                . $labelTag . $label->attributes() . '>'
1✔
201
                                . $inputTag . $input->attributes() . '>'
1✔
202
                                . ($caption instanceof Nette\HtmlStringable ? $caption : htmlspecialchars((string) $caption, ENT_NOQUOTES, 'UTF-8'))
1✔
203
                                . '</label>'
1✔
204
                                . $wrapperEnd;
1✔
205
                }
206

207
                return $res;
1✔
208
        }
209

210

211
        public static function createSelectBox(array $items, ?array $optionAttrs = null, $selected = null): Html
1✔
212
        {
213
                if ($selected !== null) {
1✔
214
                        $optionAttrs['selected?'] = $selected;
1✔
215
                }
216

217
                [$optionAttrs, $optionTag] = self::prepareAttrs($optionAttrs, 'option');
1✔
218
                $option = Html::el();
1✔
219
                $res = $tmp = '';
1✔
220
                foreach ($items as $group => $subitems) {
1✔
221
                        if (is_array($subitems)) {
1✔
222
                                $res .= Html::el('optgroup')->label($group)->startTag();
1✔
223
                                $tmp = '</optgroup>';
1✔
224
                        } else {
225
                                $subitems = [$group => $subitems];
1✔
226
                        }
227

228
                        foreach ($subitems as $value => $caption) {
1✔
229
                                $option->value = $value;
1✔
230
                                foreach ($optionAttrs as $k => $v) {
1✔
231
                                        $option->attrs[$k] = $v[$value] ?? null;
1✔
232
                                }
233

234
                                if ($caption instanceof Html) {
1✔
235
                                        $caption = clone $caption;
1✔
236
                                        $res .= $caption->setName('option')->addAttributes($option->attrs);
1✔
237
                                } else {
238
                                        $res .= $optionTag . $option->attributes() . '>'
1✔
239
                                                . htmlspecialchars((string) $caption, ENT_NOQUOTES, 'UTF-8')
1✔
240
                                                . '</option>';
1✔
241
                                }
242

243
                                if ($selected === $value) {
1✔
244
                                        unset($optionAttrs['selected'], $option->attrs['selected']);
1✔
245
                                }
246
                        }
247

248
                        $res .= $tmp;
1✔
249
                        $tmp = '';
1✔
250
                }
251

252
                return Html::el('select')->setHtml($res);
1✔
253
        }
254

255

256
        private static function prepareAttrs(?array $attrs, string $name): array
1✔
257
        {
258
                $dynamic = [];
1✔
259
                foreach ((array) $attrs as $k => $v) {
1✔
260
                        if ($k[-1] === '?' || $k[-1] === ':') {
1✔
261
                                $p = substr($k, 0, -1);
1✔
262
                                unset($attrs[$k], $attrs[$p]);
1✔
263
                                if ($k[-1] === '?') {
1✔
264
                                        $dynamic[$p] = array_fill_keys((array) $v, value: true);
1✔
265
                                } elseif (is_array($v) && $v) {
1✔
266
                                        $dynamic[$p] = $v;
1✔
267
                                } else {
268
                                        $attrs[$p] = $v;
1✔
269
                                }
270
                        }
271
                }
272

273
                return [$dynamic, '<' . $name . Html::el(null, $attrs)->attributes()];
1✔
274
        }
275

276

277
        /** @internal */
278
        public static function iniGetSize(string $name): int
1✔
279
        {
280
                $value = ini_get($name);
1✔
281
                $units = ['k' => 10, 'm' => 20, 'g' => 30];
1✔
282
                return isset($units[$ch = strtolower(substr($value, -1))])
1✔
283
                        ? (int) $value << $units[$ch]
1✔
284
                        : (int) $value;
1✔
285
        }
286

287

288
        /** @internal */
289
        public static function getSingleType($reflection): ?string
290
        {
291
                $type = Nette\Utils\Type::fromReflection($reflection);
1✔
292
                if (!$type) {
1✔
293
                        return null;
1✔
294
                } elseif ($res = $type->getSingleName()) {
1✔
295
                        return $res;
1✔
296
                } else {
297
                        throw new Nette\InvalidStateException(
×
298
                                Nette\Utils\Reflection::toString($reflection) . " has unsupported type '$type'.",
×
299
                        );
300
                }
301
        }
302

303

304
        /** @internal */
305
        public static function tryEnumConversion(mixed $value, $reflection): mixed
1✔
306
        {
307
                if ($value !== null
1✔
308
                        && $reflection
1✔
309
                        && ($type = Nette\Utils\Type::fromReflection($reflection)?->getSingleName())
1✔
310
                        && is_a($type, \BackedEnum::class, allow_string: true)
1✔
311
                ) {
312
                        return $type::from($value);
1✔
313
                }
314

315
                return $value;
1✔
316
        }
317

318

319
        /** @internal */
320
        public static function getSupportedImages(): array
321
        {
322
                return array_values(array_map(fn($type) => Image::typeToMimeType($type), Image::getSupportedTypes()));
1✔
323
        }
324
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc