PluginProbe
Packeta / 1.6.1
Packeta v1.6.1
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / schema / readme.md

readme.md in Packeta 1.6.1, at deps/nette/schema/readme.md

442 lines 12.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 Nette Schema
2 ************
3
4 [](https://packagist.org/packages/nette/schema![Downloads this Month](https://img.shields.io/packagist/dm/nette/schema.svg)](https://packagist.org/packages/nette/schema](https://packagist.org/packages/nette/schema)
5 [](https://github.com/nette/schema/actions![Tests](https://github.com/nette/schema/workflows/Tests/badge.svg?branch=master)](https://github.com/nette/schema/actions](https://github.com/nette/schema/actions)
6 [](https://coveralls.io/github/nette/schema?branch=master![Coverage Status](https://coveralls.io/repos/github/nette/schema/badge.svg?branch=master)](https://coveralls.io/github/nette/schema?branch=master](https://coveralls.io/github/nette/schema?branch=master)
7 [](https://github.com/nette/schema/releases![Latest Stable Version](https://poser.pugx.org/nette/schema/v/stable)](https://github.com/nette/schema/releases](https://github.com/nette/schema/releases)
8 [](https://github.com/nette/schema/blob/master/license.md![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/schema/blob/master/license.md](https://github.com/nette/schema/blob/master/license.md)
9
10
11 Introduction
12 ============
13
14 A practical library for validation and normalization of data structures against a given schema with a smart & easy-to-understand API.
15
16 Documentation can be found on the [](https://doc.nette.org/schemawebsite](https://doc.nette.org/schema](https://doc.nette.org/schema).
17
18 Installation:
19
20 ```shell
21 composer require nette/schema
22 ```
23
24 It requires PHP version 7.1 and supports PHP up to 8.2.
25
26
27 [](https://github.com/sponsors/dgSupport Me](https://github.com/sponsors/dg](https://github.com/sponsors/dg)
28 --------------------------------------------
29
30 Do you like Nette Schema? Are you looking forward to the new features?
31
32 [](https://github.com/sponsors/dg![Buy me a coffee](https://files.nette.org/icons/donation-3.svg)](https://github.com/sponsors/dg](https://github.com/sponsors/dg)
33
34 Thank you!
35
36
37 Basic Usage
38 -----------
39
40 In variable `$schema` we have a validation schema (what exactly this means and how to create it we will say later) and in variable `$data` we have a data structure that we want to validate and normalize. This can be, for example, data sent by the user through an API, configuration file, etc.
41
42 The task is handled by the [](https://api.nette.org/3.0/Nette/Schema/Processor.html\Packetery\Nette\Schema\Processor](https://api.nette.org/3.0/Nette/Schema/Processor.html](https://api.nette.org/3.0/Nette/Schema/Processor.html) class, which processes the input and either returns normalized data or throws an [](https://api.nette.org/3.0/Nette/Schema/ValidationException.html\Packetery\Nette\Schema\ValidationException](https://api.nette.org/3.0/Nette/Schema/ValidationException.html](https://api.nette.org/3.0/Nette/Schema/ValidationException.html) exception on error.
43
44 ```php
45 $processor = new \Packetery\Nette\Schema\Processor;
46
47 try {
48 $normalized = $processor->process($schema, $data);
49 } catch (\Packetery\Nette\Schema\ValidationException $e) {
50 echo 'Data is invalid: ' . $e->getMessage();
51 }
52 ```
53
54 Method `$e->getMessages()` returns array of all message strings and `$e->getMessageObjects()` return all messages as [](https://api.nette.org/3.1/Nette/Schema/Message.html\Packetery\Nette\Schema\Message](https://api.nette.org/3.1/Nette/Schema/Message.html](https://api.nette.org/3.1/Nette/Schema/Message.html) objects.
55
56
57 Defining Schema
58 ---------------
59
60 And now let's create a schema. The class [](https://api.nette.org/3.0/Nette/Schema/Expect.html\Packetery\Nette\Schema\Expect](https://api.nette.org/3.0/Nette/Schema/Expect.html](https://api.nette.org/3.0/Nette/Schema/Expect.html) is used to define it, we actually define expectations of what the data should look like. Let's say that the input data must be a structure (e.g. an array) containing elements `processRefund` of type bool and `refundAmount` of type int.
61
62 ```php
63 use \Packetery\Nette\Schema\Expect;
64
65 $schema = Expect::structure([
66 'processRefund' => Expect::bool(),
67 'refundAmount' => Expect::int(),
68 ]);
69 ```
70
71 We believe that the schema definition looks clear, even if you see it for the very first time.
72
73 Lets send the following data for validation:
74
75 ```php
76 $data = [
77 'processRefund' => true,
78 'refundAmount' => 17,
79 ];
80
81 $normalized = $processor->process($schema, $data); // OK, it passes
82 ```
83
84 The output, i.e. the value `$normalized`, is the object `stdClass`. If we want the output to be an array, we add a cast to schema `Expect::structure([...])->castTo('array')`.
85
86 All elements of the structure are optional and have a default value `null`. Example:
87
88 ```php
89 $data = [
90 'refundAmount' => 17,
91 ];
92
93 $normalized = $processor->process($schema, $data); // OK, it passes
94 // $normalized = {'processRefund' => null, 'refundAmount' => 17}
95 ```
96
97 The fact that the default value is `null` does not mean that it would be accepted in the input data `'processRefund' => null`. No, the input must be boolean, i.e. only `true` or `false`. We would have to explicitly allow `null` via `Expect::bool()->nullable()`.
98
99 An item can be made mandatory using `Expect::bool()->required()`. We change the default value to `false` using `Expect::bool()->default(false)` or shortly using `Expect::bool(false)`.
100
101 And what if we wanted to accept `1` and `0` besides booleans? Then we list the allowed values, which we will also normalize to boolean:
102
103 ```php
104 $schema = Expect::structure([
105 'processRefund' => Expect::anyOf(true, false, 1, 0)->castTo('bool'),
106 'refundAmount' => Expect::int(),
107 ]);
108
109 $normalized = $processor->process($schema, $data);
110 is_bool($normalized->processRefund); // true
111 ```
112
113 Now you know the basics of how the schema is defined and how the individual elements of the structure behave. We will now show what all the other elements can be used in defining a schema.
114
115
116
117 Data Types: type()
118 ------------------
119
120 All standard PHP data types can be listed in the schema:
121
122 ```php
123 Expect::string($default = null)
124 Expect::int($default = null)
125 Expect::float($default = null)
126 Expect::bool($default = null)
127 Expect::null()
128 Expect::array($default = [])
129 ```
130
131 And then all types [](https://doc.nette.org/validators#toc-validation-rulessupported by the Validators](https://doc.nette.org/validators#toc-validation-rules](https://doc.nette.org/validators#toc-validation-rules) via `Expect::type('scalar')` or abbreviated `Expect::scalar()`. Also class or interface names are accepted, e.g. `Expect::type('AddressEntity')`.
132
133 You can also use union notation:
134
135 ```php
136 Expect::type('bool|string|array')
137 ```
138
139 The default value is always `null` except for `array` and `list`, where it is an empty array. (A list is an array indexed in ascending order of numeric keys from zero, that is, a non-associative array).
140
141
142 Array of Values: arrayOf() listOf()
143 -----------------------------------
144
145 The array is too general structure, it is more useful to specify exactly what elements it can contain. For example, an array whose elements can only be strings:
146
147 ```php
148 $schema = Expect::arrayOf('string');
149
150 $processor->process($schema, ['hello', 'world']); // OK
151 $processor->process($schema, ['a' => 'hello', 'b' => 'world']); // OK
152 $processor->process($schema, ['key' => 123]); // ERROR: 123 is not a string
153 ```
154
155 The list is an indexed array:
156
157 ```php
158 $schema = Expect::listOf('string');
159
160 $processor->process($schema, ['a', 'b']); // OK
161 $processor->process($schema, ['a', 123]); // ERROR: 123 is not a string
162 $processor->process($schema, ['key' => 'a']); // ERROR: is not a list
163 $processor->process($schema, [1 => 'a', 0 => 'b']); // ERROR: is not a list
164 ```
165
166 The parameter can also be a schema, so we can write:
167
168 ```php
169 Expect::arrayOf(Expect::bool())
170 ```
171
172 The default value is an empty array. If you specify default value, it will be merged with the passed data. This can be disabled using `mergeDefaults(false)`.
173
174
175 Enumeration: anyOf()
176 --------------------
177
178 `anyOf()` is a set of values ​​or schemas that a value can be. Here's how to write an array of elements that can be either `'a'`, `true`, or `null`:
179
180 ```php
181 $schema = Expect::listOf(
182 Expect::anyOf('a', true, null)
183 );
184
185 $processor->process($schema, ['a', true, null, 'a']); // OK
186 $processor->process($schema, ['a', false]); // ERROR: false does not belong there
187 ```
188
189 The enumeration elements can also be schemas:
190
191 ```php
192 $schema = Expect::listOf(
193 Expect::anyOf(Expect::string(), true, null)
194 );
195
196 $processor->process($schema, ['foo', true, null, 'bar']); // OK
197 $processor->process($schema, [123]); // ERROR
198 ```
199
200 The default value is `null`.
201
202
203 Structures
204 ----------
205
206 Structures are objects with defined keys. Each of these key => value pairs is referred to as a "property":
207
208 Structures accept arrays and objects and return objects `stdClass` (unless you change it with `castTo('array')`, etc.).
209
210 By default, all properties are optional and have a default value of `null`. You can define mandatory properties using `required()`:
211
212 ```php
213 $schema = Expect::structure([
214 'required' => Expect::string()->required(),
215 'optional' => Expect::string(), // the default value is null
216 ]);
217
218 $processor->process($schema, ['optional' => '']);
219 // ERROR: item 'required' is missing
220
221 $processor->process($schema, ['required' => 'foo']);
222 // OK, returns {'required' => 'foo', 'optional' => null}
223 ```
224
225 Although `null` is the default value of the `optional` property, it is not allowed in the input data (the value must be a string). Properties accepting `null` are defined using `nullable()`:
226
227 ```php
228 $schema = Expect::structure([
229 'optional' => Expect::string(),
230 'nullable' => Expect::string()->nullable(),
231 ]);
232
233 $processor->process($schema, ['optional' => null]);
234 // ERROR: 'optional' expects to be string, null given.
235
236 $processor->process($schema, ['nullable' => null]);
237 // OK, returns {'optional' => null, 'nullable' => null}
238 ```
239
240 By default, there can be no extra items in the input data:
241
242 ```php
243 $schema = Expect::structure([
244 'key' => Expect::string(),
245 ]);
246
247 $processor->process($schema, ['additional' => 1]);
248 // ERROR: Unexpected item 'additional'
249 ```
250
251 Which we can change with `otherItems()`. As a parameter, we will specify the schema for each extra element:
252
253 ```php
254 $schema = Expect::structure([
255 'key' => Expect::string(),
256 ])->otherItems(Expect::int());
257
258 $processor->process($schema, ['additional' => 1]); // OK
259 $processor->process($schema, ['additional' => true]); // ERROR
260 ```
261
262 Deprecations
263 ------------
264
265 You can deprecate property using the `deprecated([string $message])` method. Deprecation notices are returned by `$processor->getWarnings()` (since v1.1):
266
267 ```php
268 $schema = Expect::structure([
269 'old' => Expect::int()->deprecated('The item %path% is deprecated'),
270 ]);
271
272 $processor->process($schema, ['old' => 1]); // OK
273 $processor->getWarnings(); // ["The item 'old' is deprecated"]
274 ```
275
276 Ranges: min() max()
277 -------------------
278
279 Use `min()` and `max()` to limit the number of elements for arrays:
280
281 ```php
282 // array, at least 10 items, maximum 20 items
283 Expect::array()->min(10)->max(20);
284 ```
285
286 For strings, limit their length:
287
288 ```php
289 // string, at least 10 characters long, maximum 20 characters
290 Expect::string()->min(10)->max(20);
291 ```
292
293 For numbers, limit their value:
294
295 ```php
296 // integer, between 10 and 20 inclusive
297 Expect::int()->min(10)->max(20);
298 ```
299
300 Of course, it is possible to mention only `min()`, or only `max()`:
301
302 ```php
303 // string, maximum 20 characters
304 Expect::string()->max(20);
305 ```
306
307
308 Regular Expressions: pattern()
309 ------------------------------
310
311 Using `pattern()`, you can specify a regular expression which the **whole** input string must match (i.e. as if it were wrapped in characters `^` a `$`):
312
313 ```php
314 // just 9 digits
315 Expect::string()->pattern('\d{9}');
316 ```
317
318
319 Custom Assertions: assert()
320 ---------------------------
321
322 You can add any other restrictions using `assert(callable $fn)`.
323
324 ```php
325 $countIsEven = function ($v) { return count($v) % 2 === 0; };
326
327 $schema = Expect::arrayOf('string')
328 ->assert($countIsEven); // the count must be even
329
330 $processor->process($schema, ['a', 'b']); // OK
331 $processor->process($schema, ['a', 'b', 'c']); // ERROR: 3 is not even
332 ```
333
334 Or
335
336 ```php
337 Expect::string()->assert('is_file'); // the file must exist
338 ```
339
340 You can add your own description for each assertions. It will be part of the error message.
341
342 ```php
343 $schema = Expect::arrayOf('string')
344 ->assert($countIsEven, 'Even items in array');
345
346 $processor->process($schema, ['a', 'b', 'c']);
347 // Failed assertion "Even items in array" for item with value array.
348 ```
349
350 The method can be called repeatedly to add more assertions.
351
352
353 Mapping to Objects: from()
354 --------------------------
355
356 You can generate structure schema from the class. Example:
357
358 ```php
359 class Config
360 {
361 /** @var string */
362 public $name;
363 /** @var string|null */
364 public $password;
365 /** @var bool */
366 public $admin = false;
367 }
368
369 $schema = Expect::from(new Config);
370
371 $data = [
372 'name' => 'jeff',
373 ];
374
375 $normalized = $processor->process($schema, $data);
376 // $normalized instanceof Config
377 // $normalized = {'name' => 'jeff', 'password' => null, 'admin' => false}
378 ```
379
380 If you are using PHP 7.4 or higher, you can use native types:
381
382 ```php
383 class Config
384 {
385 public string $name;
386 public ?string $password;
387 public bool $admin = false;
388 }
389
390 $schema = Expect::from(new Config);
391 ```
392
393 Anonymous classes are also supported:
394
395 ```php
396 $schema = Expect::from(new class {
397 public string $name;
398 public ?string $password;
399 public bool $admin = false;
400 });
401 ```
402
403 Because the information obtained from the class definition may not be sufficient, you can add a custom schema for the elements with the second parameter:
404
405 ```php
406 $schema = Expect::from(new Config, [
407 'name' => Expect::string()->pattern('\w:.*'),
408 ]);
409 ```
410
411
412 Casting: castTo()
413 -----------------
414
415 Successfully validated data can be cast:
416
417 ```php
418 Expect::scalar()->castTo('string');
419 ```
420
421 In addition to native PHP types, you can also cast to classes:
422
423 ```php
424 Expect::scalar()->castTo('AddressEntity');
425 ```
426
427
428 Normalization: before()
429 -----------------------
430
431 Prior to the validation itself, the data can be normalized using the method `before()`. As an example, let's have an element that must be an array of strings (eg `['a', 'b', 'c']`), but receives input in the form of a string `a b c`:
432
433 ```php
434 $explode = function ($v) { return explode(' ', $v); };
435
436 $schema = Expect::arrayOf('string')
437 ->before($explode);
438
439 $normalized = $processor->process($schema, 'a b c');
440 // OK, returns ['a', 'b', 'c']
441 ```
442