1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Utils;
9:
10: use Nette;
11:
12:
13: 14: 15:
16: class Validators
17: {
18: use Nette\StaticClass;
19:
20: protected static $validators = [
21: 'bool' => 'is_bool',
22: 'boolean' => 'is_bool',
23: 'int' => 'is_int',
24: 'integer' => 'is_int',
25: 'float' => 'is_float',
26: 'number' => [__CLASS__, 'isNumber'],
27: 'numeric' => [__CLASS__, 'isNumeric'],
28: 'numericint' => [__CLASS__, 'isNumericInt'],
29: 'string' => 'is_string',
30: 'unicode' => [__CLASS__, 'isUnicode'],
31: 'array' => 'is_array',
32: 'list' => [Arrays::class, 'isList'],
33: 'object' => 'is_object',
34: 'resource' => 'is_resource',
35: 'scalar' => 'is_scalar',
36: 'callable' => [__CLASS__, 'isCallable'],
37: 'null' => 'is_null',
38: 'email' => [__CLASS__, 'isEmail'],
39: 'url' => [__CLASS__, 'isUrl'],
40: 'uri' => [__CLASS__, 'isUri'],
41: 'none' => [__CLASS__, 'isNone'],
42: 'type' => [__CLASS__, 'isType'],
43: 'identifier' => [__CLASS__, 'isPhpIdentifier'],
44: 'pattern' => null,
45: 'alnum' => 'ctype_alnum',
46: 'alpha' => 'ctype_alpha',
47: 'digit' => 'ctype_digit',
48: 'lower' => 'ctype_lower',
49: 'upper' => 'ctype_upper',
50: 'space' => 'ctype_space',
51: 'xdigit' => 'ctype_xdigit',
52: 'iterable' => [__CLASS__, 'isIterable'],
53: ];
54:
55: protected static $counters = [
56: 'string' => 'strlen',
57: 'unicode' => [Strings::class, 'length'],
58: 'array' => 'count',
59: 'list' => 'count',
60: 'alnum' => 'strlen',
61: 'alpha' => 'strlen',
62: 'digit' => 'strlen',
63: 'lower' => 'strlen',
64: 'space' => 'strlen',
65: 'upper' => 'strlen',
66: 'xdigit' => 'strlen',
67: ];
68:
69:
70: 71: 72: 73: 74: 75: 76:
77: public static function assert($value, $expected, $label = 'variable')
78: {
79: if (!static::is($value, $expected)) {
80: $expected = str_replace(['|', ':'], [' or ', ' in range '], $expected);
81: if (is_array($value)) {
82: $type = 'array(' . count($value) . ')';
83: } elseif (is_object($value)) {
84: $type = 'object ' . get_class($value);
85: } elseif (is_string($value) && strlen($value) < 40) {
86: $type = "string '$value'";
87: } else {
88: $type = gettype($value);
89: }
90: throw new AssertionException("The $label expects to be $expected, $type given.");
91: }
92: }
93:
94:
95: 96: 97: 98: 99: 100: 101: 102:
103: public static function assertField($arr, $field, $expected = null, $label = "item '%' in array")
104: {
105: self::assert($arr, 'array', 'first argument');
106: if (!array_key_exists($field, $arr)) {
107: throw new AssertionException('Missing ' . str_replace('%', $field, $label) . '.');
108:
109: } elseif ($expected) {
110: static::assert($arr[$field], $expected, str_replace('%', $field, $label));
111: }
112: }
113:
114:
115: 116: 117: 118: 119: 120:
121: public static function is($value, $expected)
122: {
123: foreach (explode('|', $expected) as $item) {
124: if (substr($item, -2) === '[]') {
125: if (self::everyIs($value, substr($item, 0, -2))) {
126: return true;
127: }
128: continue;
129: }
130:
131: list($type) = $item = explode(':', $item, 2);
132: if (isset(static::$validators[$type])) {
133: if (!call_user_func(static::$validators[$type], $value)) {
134: continue;
135: }
136: } elseif ($type === 'pattern') {
137: if (preg_match('|^' . (isset($item[1]) ? $item[1] : '') . '\z|', $value)) {
138: return true;
139: }
140: continue;
141: } elseif (!$value instanceof $type) {
142: continue;
143: }
144:
145: if (isset($item[1])) {
146: $length = $value;
147: if (isset(static::$counters[$type])) {
148: $length = call_user_func(static::$counters[$type], $value);
149: }
150: $range = explode('..', $item[1]);
151: if (!isset($range[1])) {
152: $range[1] = $range[0];
153: }
154: if (($range[0] !== '' && $length < $range[0]) || ($range[1] !== '' && $length > $range[1])) {
155: continue;
156: }
157: }
158: return true;
159: }
160: return false;
161: }
162:
163:
164: 165: 166: 167: 168: 169:
170: public static function everyIs($values, $expected)
171: {
172: if (!self::isIterable($values)) {
173: return false;
174: }
175: foreach ($values as $value) {
176: if (!static::is($value, $expected)) {
177: return false;
178: }
179: }
180: return true;
181: }
182:
183:
184: 185: 186: 187:
188: public static function isNumber($value)
189: {
190: return is_int($value) || is_float($value);
191: }
192:
193:
194: 195: 196: 197:
198: public static function isNumericInt($value)
199: {
200: return is_int($value) || is_string($value) && preg_match('#^-?[0-9]+\z#', $value);
201: }
202:
203:
204: 205: 206: 207:
208: public static function isNumeric($value)
209: {
210: return is_float($value) || is_int($value) || is_string($value) && preg_match('#^-?[0-9]*[.]?[0-9]+\z#', $value);
211: }
212:
213:
214: 215: 216: 217:
218: public static function isCallable($value)
219: {
220: return $value && is_callable($value, true);
221: }
222:
223:
224: 225: 226: 227: 228:
229: public static function isUnicode($value)
230: {
231: return is_string($value) && preg_match('##u', $value);
232: }
233:
234:
235: 236: 237: 238:
239: public static function isNone($value)
240: {
241: return $value == null;
242: }
243:
244:
245: 246: 247: 248: 249:
250: public static function isList($value)
251: {
252: return Arrays::isList($value);
253: }
254:
255:
256: 257: 258: 259: 260: 261:
262: public static function isInRange($value, $range)
263: {
264: if ($value === null || !(isset($range[0]) || isset($range[1]))) {
265: return false;
266: }
267: $limit = isset($range[0]) ? $range[0] : $range[1];
268: if (is_string($limit)) {
269: $value = (string) $value;
270: } elseif ($limit instanceof \DateTimeInterface) {
271: if (!$value instanceof \DateTimeInterface) {
272: return false;
273: }
274: } elseif (is_numeric($value)) {
275: $value *= 1;
276: } else {
277: return false;
278: }
279: return (!isset($range[0]) || ($value >= $range[0])) && (!isset($range[1]) || ($value <= $range[1]));
280: }
281:
282:
283: 284: 285: 286: 287:
288: public static function isEmail($value)
289: {
290: $atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]";
291: $alpha = "a-z\x80-\xFF";
292: return (bool) preg_match("(^
293: (\"([ !#-[\\]-~]*|\\\\[ -~])+\"|$atom+(\\.$atom+)*) # quoted or unquoted
294: @
295: ([0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)+ # domain - RFC 1034
296: [$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
297: \\z)ix", $value);
298: }
299:
300:
301: 302: 303: 304: 305:
306: public static function isUrl($value)
307: {
308: $alpha = "a-z\x80-\xFF";
309: return (bool) preg_match("(^
310: https?://(
311: (([-_0-9$alpha]+\\.)* # subdomain
312: [0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)? # domain
313: [$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
314: |\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3} # IPv4
315: |\[[0-9a-f:]{3,39}\] # IPv6
316: )(:\\d{1,5})? # port
317: (/\\S*)? # path
318: \\z)ix", $value);
319: }
320:
321:
322: 323: 324: 325: 326:
327: public static function isUri($value)
328: {
329: return (bool) preg_match('#^[a-z\d+\.-]+:\S+\z#i', $value);
330: }
331:
332:
333: 334: 335: 336: 337:
338: public static function isType($type)
339: {
340: return class_exists($type) || interface_exists($type) || trait_exists($type);
341: }
342:
343:
344: 345: 346: 347:
348: public static function isPhpIdentifier($value)
349: {
350: return is_string($value) && preg_match('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\z#', $value);
351: }
352:
353:
354: 355: 356: 357:
358: private static function isIterable($value)
359: {
360: return is_array($value) || $value instanceof \Traversable;
361: }
362: }
363: