PluginProbe ʕ •ᴥ•ʔ
FAPI Member / trunk
FAPI Member vtrunk
2.2.33 2.2.32 trunk 1.9.47 2.1.18 2.2.24 2.2.25 2.2.26 2.2.28 2.2.29 2.2.30 2.2.31
fapi-member / libs / nette / utils / src / Utils / Validators.php
fapi-member / libs / nette / utils / src / Utils Last commit date
ArrayHash.php 2 years ago ArrayList.php 2 years ago Arrays.php 2 years ago Callback.php 2 years ago DateTime.php 2 years ago FileSystem.php 2 years ago Floats.php 2 years ago Helpers.php 2 years ago Html.php 2 years ago Image.php 2 years ago Json.php 2 years ago ObjectHelpers.php 2 years ago ObjectMixin.php 2 years ago Paginator.php 2 years ago Random.php 2 years ago Reflection.php 2 years ago Strings.php 2 years ago Type.php 2 years ago Validators.php 2 years ago exceptions.php 2 years ago
Validators.php
339 lines
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 declare (strict_types=1);
8 namespace FapiMember\Library\Nette\Utils;
9
10 use FapiMember\Library\Nette;
11 /**
12 * Validation utilities.
13 */
14 class Validators
15 {
16 use Nette\StaticClass;
17 private const BuiltinTypes = ['string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1, 'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1, 'false' => 1, 'never' => 1, 'true' => 1];
18 /** @var array<string,?callable> */
19 protected static $validators = [
20 // PHP types
21 'array' => 'is_array',
22 'bool' => 'is_bool',
23 'boolean' => 'is_bool',
24 'float' => 'is_float',
25 'int' => 'is_int',
26 'integer' => 'is_int',
27 'null' => 'is_null',
28 'object' => 'is_object',
29 'resource' => 'is_resource',
30 'scalar' => 'is_scalar',
31 'string' => 'is_string',
32 // pseudo-types
33 'callable' => [self::class, 'isCallable'],
34 'iterable' => 'is_iterable',
35 'list' => [Arrays::class, 'isList'],
36 'mixed' => [self::class, 'isMixed'],
37 'none' => [self::class, 'isNone'],
38 'number' => [self::class, 'isNumber'],
39 'numeric' => [self::class, 'isNumeric'],
40 'numericint' => [self::class, 'isNumericInt'],
41 // string patterns
42 'alnum' => 'ctype_alnum',
43 'alpha' => 'ctype_alpha',
44 'digit' => 'ctype_digit',
45 'lower' => 'ctype_lower',
46 'pattern' => null,
47 'space' => 'ctype_space',
48 'unicode' => [self::class, 'isUnicode'],
49 'upper' => 'ctype_upper',
50 'xdigit' => 'ctype_xdigit',
51 // syntax validation
52 'email' => [self::class, 'isEmail'],
53 'identifier' => [self::class, 'isPhpIdentifier'],
54 'uri' => [self::class, 'isUri'],
55 'url' => [self::class, 'isUrl'],
56 // environment validation
57 'class' => 'class_exists',
58 'interface' => 'interface_exists',
59 'directory' => 'is_dir',
60 'file' => 'is_file',
61 'type' => [self::class, 'isType'],
62 ];
63 /** @var array<string,callable> */
64 protected static $counters = ['string' => 'strlen', 'unicode' => [Strings::class, 'length'], 'array' => 'count', 'list' => 'count', 'alnum' => 'strlen', 'alpha' => 'strlen', 'digit' => 'strlen', 'lower' => 'strlen', 'space' => 'strlen', 'upper' => 'strlen', 'xdigit' => 'strlen'];
65 /**
66 * Verifies that the value is of expected types separated by pipe.
67 * @param mixed $value
68 * @throws AssertionException
69 */
70 public static function assert($value, string $expected, string $label = 'variable'): void
71 {
72 if (!static::is($value, $expected)) {
73 $expected = str_replace(['|', ':'], [' or ', ' in range '], $expected);
74 $translate = ['boolean' => 'bool', 'integer' => 'int', 'double' => 'float', 'NULL' => 'null'];
75 $type = $translate[gettype($value)] ?? gettype($value);
76 if (is_int($value) || is_float($value) || is_string($value) && strlen($value) < 40) {
77 $type .= ' ' . var_export($value, \true);
78 } elseif (is_object($value)) {
79 $type .= ' ' . get_class($value);
80 }
81 throw new AssertionException("The {$label} expects to be {$expected}, {$type} given.");
82 }
83 }
84 /**
85 * Verifies that element $key in array is of expected types separated by pipe.
86 * @param mixed[] $array
87 * @param int|string $key
88 * @throws AssertionException
89 */
90 public static function assertField(array $array, $key, ?string $expected = null, string $label = "item '%' in array"): void
91 {
92 if (!array_key_exists($key, $array)) {
93 throw new AssertionException('Missing ' . str_replace('%', $key, $label) . '.');
94 } elseif ($expected) {
95 static::assert($array[$key], $expected, str_replace('%', $key, $label));
96 }
97 }
98 /**
99 * Verifies that the value is of expected types separated by pipe.
100 * @param mixed $value
101 */
102 public static function is($value, string $expected): bool
103 {
104 foreach (explode('|', $expected) as $item) {
105 if (substr($item, -2) === '[]') {
106 if (is_iterable($value) && self::everyIs($value, substr($item, 0, -2))) {
107 return \true;
108 }
109 continue;
110 } elseif (substr($item, 0, 1) === '?') {
111 $item = substr($item, 1);
112 if ($value === null) {
113 return \true;
114 }
115 }
116 [$type] = $item = explode(':', $item, 2);
117 if (isset(static::$validators[$type])) {
118 try {
119 if (!static::$validators[$type]($value)) {
120 continue;
121 }
122 } catch (\TypeError $e) {
123 continue;
124 }
125 } elseif ($type === 'pattern') {
126 if (Strings::match($value, '|^' . ($item[1] ?? '') . '$|D')) {
127 return \true;
128 }
129 continue;
130 } elseif (!$value instanceof $type) {
131 continue;
132 }
133 if (isset($item[1])) {
134 $length = $value;
135 if (isset(static::$counters[$type])) {
136 $length = static::$counters[$type]($value);
137 }
138 $range = explode('..', $item[1]);
139 if (!isset($range[1])) {
140 $range[1] = $range[0];
141 }
142 if ($range[0] !== '' && $length < $range[0] || $range[1] !== '' && $length > $range[1]) {
143 continue;
144 }
145 }
146 return \true;
147 }
148 return \false;
149 }
150 /**
151 * Finds whether all values are of expected types separated by pipe.
152 * @param mixed[] $values
153 */
154 public static function everyIs(iterable $values, string $expected): bool
155 {
156 foreach ($values as $value) {
157 if (!static::is($value, $expected)) {
158 return \false;
159 }
160 }
161 return \true;
162 }
163 /**
164 * Checks if the value is an integer or a float.
165 * @param mixed $value
166 */
167 public static function isNumber($value): bool
168 {
169 return is_int($value) || is_float($value);
170 }
171 /**
172 * Checks if the value is an integer or a integer written in a string.
173 * @param mixed $value
174 */
175 public static function isNumericInt($value): bool
176 {
177 return is_int($value) || is_string($value) && preg_match('#^[+-]?[0-9]+$#D', $value);
178 }
179 /**
180 * Checks if the value is a number or a number written in a string.
181 * @param mixed $value
182 */
183 public static function isNumeric($value): bool
184 {
185 return is_float($value) || is_int($value) || is_string($value) && preg_match('#^[+-]?([0-9]++\.?[0-9]*|\.[0-9]+)$#D', $value);
186 }
187 /**
188 * Checks if the value is a syntactically correct callback.
189 * @param mixed $value
190 */
191 public static function isCallable($value): bool
192 {
193 return $value && is_callable($value, \true);
194 }
195 /**
196 * Checks if the value is a valid UTF-8 string.
197 * @param mixed $value
198 */
199 public static function isUnicode($value): bool
200 {
201 return is_string($value) && preg_match('##u', $value);
202 }
203 /**
204 * Checks if the value is 0, '', false or null.
205 * @param mixed $value
206 */
207 public static function isNone($value): bool
208 {
209 return $value == null;
210 // intentionally ==
211 }
212 /** @internal */
213 public static function isMixed(): bool
214 {
215 return \true;
216 }
217 /**
218 * Checks if a variable is a zero-based integer indexed array.
219 * @param mixed $value
220 * @deprecated use Nette\Utils\Arrays::isList
221 * @return ($value is list ? true : false)
222 */
223 public static function isList($value): bool
224 {
225 return Arrays::isList($value);
226 }
227 /**
228 * Checks if the value is in the given range [min, max], where the upper or lower limit can be omitted (null).
229 * Numbers, strings and DateTime objects can be compared.
230 * @param mixed $value
231 */
232 public static function isInRange($value, array $range): bool
233 {
234 if ($value === null || !(isset($range[0]) || isset($range[1]))) {
235 return \false;
236 }
237 $limit = $range[0] ?? $range[1];
238 if (is_string($limit)) {
239 $value = (string) $value;
240 } elseif ($limit instanceof \DateTimeInterface) {
241 if (!$value instanceof \DateTimeInterface) {
242 return \false;
243 }
244 } elseif (is_numeric($value)) {
245 $value *= 1;
246 } else {
247 return \false;
248 }
249 return (!isset($range[0]) || $value >= $range[0]) && (!isset($range[1]) || $value <= $range[1]);
250 }
251 /**
252 * Checks if the value is a valid email address. It does not verify that the domain actually exists, only the syntax is verified.
253 */
254 public static function isEmail(string $value): bool
255 {
256 $atom = "[-a-z0-9!#\$%&'*+/=?^_`{|}~]";
257 // RFC 5322 unquoted characters in local-part
258 $alpha = "a-z\x80-\xff";
259 // superset of IDN
260 return (bool) preg_match(<<<XX
261 \t\t(^
262 \t\t\t("([ !#-[\\]-~]*|\\\\[ -~])+"|{$atom}+(\\.{$atom}+)*) # quoted or unquoted
263 \t\t\t@
264 \t\t\t([0-9{$alpha}]([-0-9{$alpha}]{0,61}[0-9{$alpha}])?\\.)+ # domain - RFC 1034
265 \t\t\t[{$alpha}]([-0-9{$alpha}]{0,17}[{$alpha}])? # top domain
266 \t\t\$)Dix
267 XX, $value);
268 }
269 /**
270 * Checks if the value is a valid URL address.
271 */
272 public static function isUrl(string $value): bool
273 {
274 $alpha = "a-z\x80-\xff";
275 return (bool) preg_match(<<<XX
276 \t\t(^
277 \t\t\thttps?://(
278 \t\t\t\t(([-_0-9{$alpha}]+\\.)* # subdomain
279 \t\t\t\t\t[0-9{$alpha}]([-0-9{$alpha}]{0,61}[0-9{$alpha}])?\\.)? # domain
280 \t\t\t\t\t[{$alpha}]([-0-9{$alpha}]{0,17}[{$alpha}])? # top domain
281 \t\t\t\t|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3} # IPv4
282 \t\t\t\t|\\[[0-9a-f:]{3,39}\\] # IPv6
283 \t\t\t)(:\\d{1,5})? # port
284 \t\t\t(/\\S*)? # path
285 \t\t\t(\\?\\S*)? # query
286 \t\t\t(\\#\\S*)? # fragment
287 \t\t\$)Dix
288 XX, $value);
289 }
290 /**
291 * Checks if the value is a valid URI address, that is, actually a string beginning with a syntactically valid schema.
292 */
293 public static function isUri(string $value): bool
294 {
295 return (bool) preg_match('#^[a-z\d+\.-]+:\S+$#Di', $value);
296 }
297 /**
298 * Checks whether the input is a class, interface or trait.
299 */
300 public static function isType(string $type): bool
301 {
302 return class_exists($type) || interface_exists($type) || trait_exists($type);
303 }
304 /**
305 * Checks whether the input is a valid PHP identifier.
306 */
307 public static function isPhpIdentifier(string $value): bool
308 {
309 return preg_match('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#D', $value) === 1;
310 }
311 /**
312 * Determines if type is PHP built-in type. Otherwise, it is the class name.
313 */
314 public static function isBuiltinType(string $type): bool
315 {
316 return isset(self::BuiltinTypes[strtolower($type)]);
317 }
318 /**
319 * Determines if type is special class name self/parent/static.
320 */
321 public static function isClassKeyword(string $name): bool
322 {
323 return (bool) preg_match('#^(self|parent|static)$#Di', $name);
324 }
325 /**
326 * Checks whether the given type declaration is syntactically valid.
327 */
328 public static function isTypeDeclaration(string $type): bool
329 {
330 return (bool) preg_match(<<<'XX'
331 ~(
332 \?? (?<type> \\? (?<name> [a-zA-Z_\x7f-\xff][\w\x7f-\xff]*) (\\ (?&name))* ) |
333 (?<intersection> (?&type) (& (?&type))+ ) |
334 (?<upart> (?&type) | \( (?&intersection) \) ) (\| (?&upart))+
335 )$~xAD
336 XX, $type);
337 }
338 }
339