@vituum/vite-plugin-latte 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/latte/PlaceholderFunction.php +2 -2
- package/package.json +1 -1
- package/vendor/autoload.php +1 -1
- package/vendor/composer/autoload_classmap.php +51 -0
- package/vendor/composer/autoload_real.php +4 -4
- package/vendor/composer/autoload_static.php +53 -2
- package/vendor/composer/installed.json +96 -7
- package/vendor/composer/installed.php +14 -5
- package/vendor/latte/latte/src/Latte/Compiler/Escaper.php +11 -2
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/FilterNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php +2 -2
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NameNode.php +11 -21
- package/vendor/latte/latte/src/Latte/Compiler/PrintContext.php +1 -1
- package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +2 -2
- package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +275 -280
- package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +4 -4
- package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +10 -2
- package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +6 -2
- package/vendor/latte/latte/src/Latte/Engine.php +109 -97
- package/vendor/latte/latte/src/Latte/Essential/AuxiliaryIterator.php +46 -0
- package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +42 -25
- package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +76 -72
- package/vendor/latte/latte/src/Latte/Essential/Filters.php +95 -37
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ContentTypeNode.php +7 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ForeachNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplatePrintNode.php +25 -3
- package/vendor/latte/latte/src/Latte/Essential/Nodes/VarPrintNode.php +9 -2
- package/vendor/latte/latte/src/Latte/Essential/Passes.php +10 -50
- package/vendor/latte/latte/src/Latte/Helpers.php +3 -1
- package/vendor/latte/latte/src/Latte/Loader.php +1 -0
- package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +1 -2
- package/vendor/latte/latte/src/Latte/Runtime/Filters.php +1 -4
- package/vendor/latte/latte/src/Latte/Runtime/FunctionExecutor.php +68 -0
- package/vendor/latte/latte/src/Latte/Runtime/Template.php +1 -3
- package/vendor/nette/utils/.phpstorm.meta.php +13 -0
- package/vendor/nette/utils/composer.json +51 -0
- package/vendor/nette/utils/license.md +60 -0
- package/vendor/nette/utils/readme.md +56 -0
- package/vendor/nette/utils/src/HtmlStringable.php +22 -0
- package/vendor/nette/utils/src/Iterators/CachingIterator.php +164 -0
- package/vendor/nette/utils/src/Iterators/Mapper.php +34 -0
- package/vendor/nette/utils/src/SmartObject.php +140 -0
- package/vendor/nette/utils/src/StaticClass.php +34 -0
- package/vendor/nette/utils/src/Translator.php +25 -0
- package/vendor/nette/utils/src/Utils/ArrayHash.php +106 -0
- package/vendor/nette/utils/src/Utils/ArrayList.php +136 -0
- package/vendor/nette/utils/src/Utils/Arrays.php +522 -0
- package/vendor/nette/utils/src/Utils/Callback.php +137 -0
- package/vendor/nette/utils/src/Utils/DateTime.php +140 -0
- package/vendor/nette/utils/src/Utils/FileInfo.php +69 -0
- package/vendor/nette/utils/src/Utils/FileSystem.php +326 -0
- package/vendor/nette/utils/src/Utils/Finder.php +510 -0
- package/vendor/nette/utils/src/Utils/Floats.php +107 -0
- package/vendor/nette/utils/src/Utils/Helpers.php +104 -0
- package/vendor/nette/utils/src/Utils/Html.php +839 -0
- package/vendor/nette/utils/src/Utils/Image.php +829 -0
- package/vendor/nette/utils/src/Utils/ImageColor.php +75 -0
- package/vendor/nette/utils/src/Utils/ImageType.php +25 -0
- package/vendor/nette/utils/src/Utils/Iterables.php +159 -0
- package/vendor/nette/utils/src/Utils/Json.php +84 -0
- package/vendor/nette/utils/src/Utils/ObjectHelpers.php +229 -0
- package/vendor/nette/utils/src/Utils/Paginator.php +245 -0
- package/vendor/nette/utils/src/Utils/Random.php +52 -0
- package/vendor/nette/utils/src/Utils/Reflection.php +320 -0
- package/vendor/nette/utils/src/Utils/ReflectionMethod.php +36 -0
- package/vendor/nette/utils/src/Utils/Strings.php +708 -0
- package/vendor/nette/utils/src/Utils/Type.php +267 -0
- package/vendor/nette/utils/src/Utils/Validators.php +416 -0
- package/vendor/nette/utils/src/Utils/exceptions.php +50 -0
- package/vendor/nette/utils/src/compatibility.php +32 -0
- package/vendor/nette/utils/src/exceptions.php +109 -0
|
@@ -0,0 +1,267 @@
|
|
|
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\Utils;
|
|
11
|
+
|
|
12
|
+
use Nette;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* PHP type reflection.
|
|
17
|
+
*/
|
|
18
|
+
final class Type
|
|
19
|
+
{
|
|
20
|
+
/** @var array<int, string|self> */
|
|
21
|
+
private array $types;
|
|
22
|
+
private bool $simple;
|
|
23
|
+
private string $kind; // | &
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Creates a Type object based on reflection. Resolves self, static and parent to the actual class name.
|
|
28
|
+
* If the subject has no type, it returns null.
|
|
29
|
+
*/
|
|
30
|
+
public static function fromReflection(
|
|
31
|
+
\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection,
|
|
32
|
+
): ?self
|
|
33
|
+
{
|
|
34
|
+
$type = $reflection instanceof \ReflectionFunctionAbstract
|
|
35
|
+
? $reflection->getReturnType() ?? (PHP_VERSION_ID >= 80100 && $reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null)
|
|
36
|
+
: $reflection->getType();
|
|
37
|
+
|
|
38
|
+
return $type ? self::fromReflectionType($type, $reflection, asObject: true) : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
private static function fromReflectionType(\ReflectionType $type, $of, bool $asObject): self|string
|
|
43
|
+
{
|
|
44
|
+
if ($type instanceof \ReflectionNamedType) {
|
|
45
|
+
$name = self::resolve($type->getName(), $of);
|
|
46
|
+
return $asObject
|
|
47
|
+
? new self($type->allowsNull() && $name !== 'mixed' ? [$name, 'null'] : [$name])
|
|
48
|
+
: $name;
|
|
49
|
+
|
|
50
|
+
} elseif ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) {
|
|
51
|
+
return new self(
|
|
52
|
+
array_map(fn($t) => self::fromReflectionType($t, $of, asObject: false), $type->getTypes()),
|
|
53
|
+
$type instanceof \ReflectionUnionType ? '|' : '&',
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
} else {
|
|
57
|
+
throw new Nette\InvalidStateException('Unexpected type of ' . Reflection::toString($of));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates the Type object according to the text notation.
|
|
64
|
+
*/
|
|
65
|
+
public static function fromString(string $type): self
|
|
66
|
+
{
|
|
67
|
+
if (!Validators::isTypeDeclaration($type)) {
|
|
68
|
+
throw new Nette\InvalidArgumentException("Invalid type '$type'.");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if ($type[0] === '?') {
|
|
72
|
+
return new self([substr($type, 1), 'null']);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
$unions = [];
|
|
76
|
+
foreach (explode('|', $type) as $part) {
|
|
77
|
+
$part = explode('&', trim($part, '()'));
|
|
78
|
+
$unions[] = count($part) === 1 ? $part[0] : new self($part, '&');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return count($unions) === 1 && $unions[0] instanceof self
|
|
82
|
+
? $unions[0]
|
|
83
|
+
: new self($unions);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resolves 'self', 'static' and 'parent' to the actual class name.
|
|
89
|
+
*/
|
|
90
|
+
public static function resolve(
|
|
91
|
+
string $type,
|
|
92
|
+
\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $of,
|
|
93
|
+
): string
|
|
94
|
+
{
|
|
95
|
+
$lower = strtolower($type);
|
|
96
|
+
if ($of instanceof \ReflectionFunction) {
|
|
97
|
+
return $type;
|
|
98
|
+
} elseif ($lower === 'self') {
|
|
99
|
+
return $of->getDeclaringClass()->name;
|
|
100
|
+
} elseif ($lower === 'static') {
|
|
101
|
+
return ($of instanceof ReflectionMethod ? $of->getOriginalClass() : $of->getDeclaringClass())->name;
|
|
102
|
+
} elseif ($lower === 'parent' && $of->getDeclaringClass()->getParentClass()) {
|
|
103
|
+
return $of->getDeclaringClass()->getParentClass()->name;
|
|
104
|
+
} else {
|
|
105
|
+
return $type;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
private function __construct(array $types, string $kind = '|')
|
|
111
|
+
{
|
|
112
|
+
$o = array_search('null', $types, strict: true);
|
|
113
|
+
if ($o !== false) { // null as last
|
|
114
|
+
array_splice($types, $o, 1);
|
|
115
|
+
$types[] = 'null';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
$this->types = $types;
|
|
119
|
+
$this->simple = is_string($types[0]) && ($types[1] ?? 'null') === 'null';
|
|
120
|
+
$this->kind = count($types) > 1 ? $kind : '';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
public function __toString(): string
|
|
125
|
+
{
|
|
126
|
+
$multi = count($this->types) > 1;
|
|
127
|
+
if ($this->simple) {
|
|
128
|
+
return ($multi ? '?' : '') . $this->types[0];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
$res = [];
|
|
132
|
+
foreach ($this->types as $type) {
|
|
133
|
+
$res[] = $type instanceof self && $multi ? "($type)" : $type;
|
|
134
|
+
}
|
|
135
|
+
return implode($this->kind, $res);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Returns the array of subtypes that make up the compound type as strings.
|
|
141
|
+
* @return array<int, string|string[]>
|
|
142
|
+
*/
|
|
143
|
+
public function getNames(): array
|
|
144
|
+
{
|
|
145
|
+
return array_map(fn($t) => $t instanceof self ? $t->getNames() : $t, $this->types);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Returns the array of subtypes that make up the compound type as Type objects:
|
|
151
|
+
* @return self[]
|
|
152
|
+
*/
|
|
153
|
+
public function getTypes(): array
|
|
154
|
+
{
|
|
155
|
+
return array_map(fn($t) => $t instanceof self ? $t : new self([$t]), $this->types);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Returns the type name for simple types, otherwise null.
|
|
161
|
+
*/
|
|
162
|
+
public function getSingleName(): ?string
|
|
163
|
+
{
|
|
164
|
+
return $this->simple
|
|
165
|
+
? $this->types[0]
|
|
166
|
+
: null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Returns true whether it is a union type.
|
|
172
|
+
*/
|
|
173
|
+
public function isUnion(): bool
|
|
174
|
+
{
|
|
175
|
+
return $this->kind === '|';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Returns true whether it is an intersection type.
|
|
181
|
+
*/
|
|
182
|
+
public function isIntersection(): bool
|
|
183
|
+
{
|
|
184
|
+
return $this->kind === '&';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Returns true whether it is a simple type. Single nullable types are also considered to be simple types.
|
|
190
|
+
*/
|
|
191
|
+
public function isSimple(): bool
|
|
192
|
+
{
|
|
193
|
+
return $this->simple;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
/** @deprecated use isSimple() */
|
|
198
|
+
public function isSingle(): bool
|
|
199
|
+
{
|
|
200
|
+
return $this->simple;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Returns true whether the type is both a simple and a PHP built-in type.
|
|
206
|
+
*/
|
|
207
|
+
public function isBuiltin(): bool
|
|
208
|
+
{
|
|
209
|
+
return $this->simple && Validators::isBuiltinType($this->types[0]);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Returns true whether the type is both a simple and a class name.
|
|
215
|
+
*/
|
|
216
|
+
public function isClass(): bool
|
|
217
|
+
{
|
|
218
|
+
return $this->simple && !Validators::isBuiltinType($this->types[0]);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Determines if type is special class name self/parent/static.
|
|
224
|
+
*/
|
|
225
|
+
public function isClassKeyword(): bool
|
|
226
|
+
{
|
|
227
|
+
return $this->simple && Validators::isClassKeyword($this->types[0]);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Verifies type compatibility. For example, it checks if a value of a certain type could be passed as a parameter.
|
|
233
|
+
*/
|
|
234
|
+
public function allows(string $subtype): bool
|
|
235
|
+
{
|
|
236
|
+
if ($this->types === ['mixed']) {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
$subtype = self::fromString($subtype);
|
|
241
|
+
return $subtype->isUnion()
|
|
242
|
+
? Arrays::every($subtype->types, fn($t) => $this->allows2($t instanceof self ? $t->types : [$t]))
|
|
243
|
+
: $this->allows2($subtype->types);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
private function allows2(array $subtypes): bool
|
|
248
|
+
{
|
|
249
|
+
return $this->isUnion()
|
|
250
|
+
? Arrays::some($this->types, fn($t) => $this->allows3($t instanceof self ? $t->types : [$t], $subtypes))
|
|
251
|
+
: $this->allows3($this->types, $subtypes);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
private function allows3(array $types, array $subtypes): bool
|
|
256
|
+
{
|
|
257
|
+
return Arrays::every(
|
|
258
|
+
$types,
|
|
259
|
+
fn($type) => Arrays::some(
|
|
260
|
+
$subtypes,
|
|
261
|
+
fn($subtype) => Validators::isBuiltinType($type)
|
|
262
|
+
? strcasecmp($type, $subtype) === 0
|
|
263
|
+
: is_a($subtype, $type, allow_string: true)
|
|
264
|
+
)
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -0,0 +1,416 @@
|
|
|
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\Utils;
|
|
11
|
+
|
|
12
|
+
use Nette;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Validation utilities.
|
|
17
|
+
*/
|
|
18
|
+
class Validators
|
|
19
|
+
{
|
|
20
|
+
use Nette\StaticClass;
|
|
21
|
+
|
|
22
|
+
private const BuiltinTypes = [
|
|
23
|
+
'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1,
|
|
24
|
+
'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1, 'false' => 1,
|
|
25
|
+
'never' => 1, 'true' => 1,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
/** @var array<string,?callable> */
|
|
29
|
+
protected static $validators = [
|
|
30
|
+
// PHP types
|
|
31
|
+
'array' => 'is_array',
|
|
32
|
+
'bool' => 'is_bool',
|
|
33
|
+
'boolean' => 'is_bool',
|
|
34
|
+
'float' => 'is_float',
|
|
35
|
+
'int' => 'is_int',
|
|
36
|
+
'integer' => 'is_int',
|
|
37
|
+
'null' => 'is_null',
|
|
38
|
+
'object' => 'is_object',
|
|
39
|
+
'resource' => 'is_resource',
|
|
40
|
+
'scalar' => 'is_scalar',
|
|
41
|
+
'string' => 'is_string',
|
|
42
|
+
|
|
43
|
+
// pseudo-types
|
|
44
|
+
'callable' => [self::class, 'isCallable'],
|
|
45
|
+
'iterable' => 'is_iterable',
|
|
46
|
+
'list' => [Arrays::class, 'isList'],
|
|
47
|
+
'mixed' => [self::class, 'isMixed'],
|
|
48
|
+
'none' => [self::class, 'isNone'],
|
|
49
|
+
'number' => [self::class, 'isNumber'],
|
|
50
|
+
'numeric' => [self::class, 'isNumeric'],
|
|
51
|
+
'numericint' => [self::class, 'isNumericInt'],
|
|
52
|
+
|
|
53
|
+
// string patterns
|
|
54
|
+
'alnum' => 'ctype_alnum',
|
|
55
|
+
'alpha' => 'ctype_alpha',
|
|
56
|
+
'digit' => 'ctype_digit',
|
|
57
|
+
'lower' => 'ctype_lower',
|
|
58
|
+
'pattern' => null,
|
|
59
|
+
'space' => 'ctype_space',
|
|
60
|
+
'unicode' => [self::class, 'isUnicode'],
|
|
61
|
+
'upper' => 'ctype_upper',
|
|
62
|
+
'xdigit' => 'ctype_xdigit',
|
|
63
|
+
|
|
64
|
+
// syntax validation
|
|
65
|
+
'email' => [self::class, 'isEmail'],
|
|
66
|
+
'identifier' => [self::class, 'isPhpIdentifier'],
|
|
67
|
+
'uri' => [self::class, 'isUri'],
|
|
68
|
+
'url' => [self::class, 'isUrl'],
|
|
69
|
+
|
|
70
|
+
// environment validation
|
|
71
|
+
'class' => 'class_exists',
|
|
72
|
+
'interface' => 'interface_exists',
|
|
73
|
+
'directory' => 'is_dir',
|
|
74
|
+
'file' => 'is_file',
|
|
75
|
+
'type' => [self::class, 'isType'],
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
/** @var array<string,callable> */
|
|
79
|
+
protected static $counters = [
|
|
80
|
+
'string' => 'strlen',
|
|
81
|
+
'unicode' => [Strings::class, 'length'],
|
|
82
|
+
'array' => 'count',
|
|
83
|
+
'list' => 'count',
|
|
84
|
+
'alnum' => 'strlen',
|
|
85
|
+
'alpha' => 'strlen',
|
|
86
|
+
'digit' => 'strlen',
|
|
87
|
+
'lower' => 'strlen',
|
|
88
|
+
'space' => 'strlen',
|
|
89
|
+
'upper' => 'strlen',
|
|
90
|
+
'xdigit' => 'strlen',
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Verifies that the value is of expected types separated by pipe.
|
|
96
|
+
* @throws AssertionException
|
|
97
|
+
*/
|
|
98
|
+
public static function assert(mixed $value, string $expected, string $label = 'variable'): void
|
|
99
|
+
{
|
|
100
|
+
if (!static::is($value, $expected)) {
|
|
101
|
+
$expected = str_replace(['|', ':'], [' or ', ' in range '], $expected);
|
|
102
|
+
$translate = ['boolean' => 'bool', 'integer' => 'int', 'double' => 'float', 'NULL' => 'null'];
|
|
103
|
+
$type = $translate[gettype($value)] ?? gettype($value);
|
|
104
|
+
if (is_int($value) || is_float($value) || (is_string($value) && strlen($value) < 40)) {
|
|
105
|
+
$type .= ' ' . var_export($value, return: true);
|
|
106
|
+
} elseif (is_object($value)) {
|
|
107
|
+
$type .= ' ' . $value::class;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
throw new AssertionException("The $label expects to be $expected, $type given.");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Verifies that element $key in array is of expected types separated by pipe.
|
|
117
|
+
* @param mixed[] $array
|
|
118
|
+
* @throws AssertionException
|
|
119
|
+
*/
|
|
120
|
+
public static function assertField(
|
|
121
|
+
array $array,
|
|
122
|
+
$key,
|
|
123
|
+
?string $expected = null,
|
|
124
|
+
string $label = "item '%' in array",
|
|
125
|
+
): void
|
|
126
|
+
{
|
|
127
|
+
if (!array_key_exists($key, $array)) {
|
|
128
|
+
throw new AssertionException('Missing ' . str_replace('%', $key, $label) . '.');
|
|
129
|
+
|
|
130
|
+
} elseif ($expected) {
|
|
131
|
+
static::assert($array[$key], $expected, str_replace('%', $key, $label));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Verifies that the value is of expected types separated by pipe.
|
|
138
|
+
*/
|
|
139
|
+
public static function is(mixed $value, string $expected): bool
|
|
140
|
+
{
|
|
141
|
+
foreach (explode('|', $expected) as $item) {
|
|
142
|
+
if (str_ends_with($item, '[]')) {
|
|
143
|
+
if (is_iterable($value) && self::everyIs($value, substr($item, 0, -2))) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
continue;
|
|
148
|
+
} elseif (str_starts_with($item, '?')) {
|
|
149
|
+
$item = substr($item, 1);
|
|
150
|
+
if ($value === null) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
[$type] = $item = explode(':', $item, 2);
|
|
156
|
+
if (isset(static::$validators[$type])) {
|
|
157
|
+
try {
|
|
158
|
+
if (!static::$validators[$type]($value)) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
} catch (\TypeError $e) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
} elseif ($type === 'pattern') {
|
|
165
|
+
if (Strings::match($value, '|^' . ($item[1] ?? '') . '$|D')) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
continue;
|
|
170
|
+
} elseif (!$value instanceof $type) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (isset($item[1])) {
|
|
175
|
+
$length = $value;
|
|
176
|
+
if (isset(static::$counters[$type])) {
|
|
177
|
+
$length = static::$counters[$type]($value);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
$range = explode('..', $item[1]);
|
|
181
|
+
if (!isset($range[1])) {
|
|
182
|
+
$range[1] = $range[0];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (($range[0] !== '' && $length < $range[0]) || ($range[1] !== '' && $length > $range[1])) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Finds whether all values are of expected types separated by pipe.
|
|
199
|
+
* @param mixed[] $values
|
|
200
|
+
*/
|
|
201
|
+
public static function everyIs(iterable $values, string $expected): bool
|
|
202
|
+
{
|
|
203
|
+
foreach ($values as $value) {
|
|
204
|
+
if (!static::is($value, $expected)) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Checks if the value is an integer or a float.
|
|
215
|
+
* @return ($value is int|float ? true : false)
|
|
216
|
+
*/
|
|
217
|
+
public static function isNumber(mixed $value): bool
|
|
218
|
+
{
|
|
219
|
+
return is_int($value) || is_float($value);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Checks if the value is an integer or a integer written in a string.
|
|
225
|
+
* @return ($value is non-empty-string ? bool : ($value is int ? true : false))
|
|
226
|
+
*/
|
|
227
|
+
public static function isNumericInt(mixed $value): bool
|
|
228
|
+
{
|
|
229
|
+
return is_int($value) || (is_string($value) && preg_match('#^[+-]?[0-9]+$#D', $value));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Checks if the value is a number or a number written in a string.
|
|
235
|
+
* @return ($value is non-empty-string ? bool : ($value is int|float ? true : false))
|
|
236
|
+
*/
|
|
237
|
+
public static function isNumeric(mixed $value): bool
|
|
238
|
+
{
|
|
239
|
+
return is_float($value) || is_int($value) || (is_string($value) && preg_match('#^[+-]?([0-9]++\.?[0-9]*|\.[0-9]+)$#D', $value));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Checks if the value is a syntactically correct callback.
|
|
245
|
+
*/
|
|
246
|
+
public static function isCallable(mixed $value): bool
|
|
247
|
+
{
|
|
248
|
+
return $value && is_callable($value, syntax_only: true);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Checks if the value is a valid UTF-8 string.
|
|
254
|
+
*/
|
|
255
|
+
public static function isUnicode(mixed $value): bool
|
|
256
|
+
{
|
|
257
|
+
return is_string($value) && preg_match('##u', $value);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Checks if the value is 0, '', false or null.
|
|
263
|
+
* @return ($value is 0|''|false|null ? true : false)
|
|
264
|
+
*/
|
|
265
|
+
public static function isNone(mixed $value): bool
|
|
266
|
+
{
|
|
267
|
+
return $value == null; // intentionally ==
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
/** @internal */
|
|
272
|
+
public static function isMixed(): bool
|
|
273
|
+
{
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Checks if a variable is a zero-based integer indexed array.
|
|
280
|
+
* @deprecated use Nette\Utils\Arrays::isList
|
|
281
|
+
* @return ($value is list ? true : false)
|
|
282
|
+
*/
|
|
283
|
+
public static function isList(mixed $value): bool
|
|
284
|
+
{
|
|
285
|
+
return Arrays::isList($value);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Checks if the value is in the given range [min, max], where the upper or lower limit can be omitted (null).
|
|
291
|
+
* Numbers, strings and DateTime objects can be compared.
|
|
292
|
+
*/
|
|
293
|
+
public static function isInRange(mixed $value, array $range): bool
|
|
294
|
+
{
|
|
295
|
+
if ($value === null || !(isset($range[0]) || isset($range[1]))) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
$limit = $range[0] ?? $range[1];
|
|
300
|
+
if (is_string($limit)) {
|
|
301
|
+
$value = (string) $value;
|
|
302
|
+
} elseif ($limit instanceof \DateTimeInterface) {
|
|
303
|
+
if (!$value instanceof \DateTimeInterface) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
} elseif (is_numeric($value)) {
|
|
307
|
+
$value *= 1;
|
|
308
|
+
} else {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return (!isset($range[0]) || ($value >= $range[0])) && (!isset($range[1]) || ($value <= $range[1]));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Checks if the value is a valid email address. It does not verify that the domain actually exists, only the syntax is verified.
|
|
318
|
+
*/
|
|
319
|
+
public static function isEmail(string $value): bool
|
|
320
|
+
{
|
|
321
|
+
$atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part
|
|
322
|
+
$alpha = "a-z\x80-\xFF"; // superset of IDN
|
|
323
|
+
return (bool) preg_match(<<<XX
|
|
324
|
+
(^(?n)
|
|
325
|
+
("([ !#-[\\]-~]*|\\\\[ -~])+"|$atom+(\\.$atom+)*) # quoted or unquoted
|
|
326
|
+
@
|
|
327
|
+
([0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)+ # domain - RFC 1034
|
|
328
|
+
[$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
|
|
329
|
+
$)Dix
|
|
330
|
+
XX, $value);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Checks if the value is a valid URL address.
|
|
336
|
+
*/
|
|
337
|
+
public static function isUrl(string $value): bool
|
|
338
|
+
{
|
|
339
|
+
$alpha = "a-z\x80-\xFF";
|
|
340
|
+
return (bool) preg_match(<<<XX
|
|
341
|
+
(^(?n)
|
|
342
|
+
https?://(
|
|
343
|
+
(([-_0-9$alpha]+\\.)* # subdomain
|
|
344
|
+
[0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)? # domain
|
|
345
|
+
[$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
|
|
346
|
+
|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3} # IPv4
|
|
347
|
+
|\\[[0-9a-f:]{3,39}\\] # IPv6
|
|
348
|
+
)(:\\d{1,5})? # port
|
|
349
|
+
(/\\S*)? # path
|
|
350
|
+
(\\?\\S*)? # query
|
|
351
|
+
(\\#\\S*)? # fragment
|
|
352
|
+
$)Dix
|
|
353
|
+
XX, $value);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Checks if the value is a valid URI address, that is, actually a string beginning with a syntactically valid schema.
|
|
359
|
+
*/
|
|
360
|
+
public static function isUri(string $value): bool
|
|
361
|
+
{
|
|
362
|
+
return (bool) preg_match('#^[a-z\d+\.-]+:\S+$#Di', $value);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Checks whether the input is a class, interface or trait.
|
|
368
|
+
* @deprecated
|
|
369
|
+
*/
|
|
370
|
+
public static function isType(string $type): bool
|
|
371
|
+
{
|
|
372
|
+
return class_exists($type) || interface_exists($type) || trait_exists($type);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Checks whether the input is a valid PHP identifier.
|
|
378
|
+
*/
|
|
379
|
+
public static function isPhpIdentifier(string $value): bool
|
|
380
|
+
{
|
|
381
|
+
return preg_match('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#D', $value) === 1;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Determines if type is PHP built-in type. Otherwise, it is the class name.
|
|
387
|
+
*/
|
|
388
|
+
public static function isBuiltinType(string $type): bool
|
|
389
|
+
{
|
|
390
|
+
return isset(self::BuiltinTypes[strtolower($type)]);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Determines if type is special class name self/parent/static.
|
|
396
|
+
*/
|
|
397
|
+
public static function isClassKeyword(string $name): bool
|
|
398
|
+
{
|
|
399
|
+
return (bool) preg_match('#^(self|parent|static)$#Di', $name);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Checks whether the given type declaration is syntactically valid.
|
|
405
|
+
*/
|
|
406
|
+
public static function isTypeDeclaration(string $type): bool
|
|
407
|
+
{
|
|
408
|
+
return (bool) preg_match(<<<'XX'
|
|
409
|
+
~((?n)
|
|
410
|
+
\?? (?<type> \\? (?<name> [a-zA-Z_\x7f-\xff][\w\x7f-\xff]*) (\\ (?&name))* ) |
|
|
411
|
+
(?<intersection> (?&type) (& (?&type))+ ) |
|
|
412
|
+
(?<upart> (?&type) | \( (?&intersection) \) ) (\| (?&upart))+
|
|
413
|
+
)$~xAD
|
|
414
|
+
XX, $type);
|
|
415
|
+
}
|
|
416
|
+
}
|