Nette Documentation Preview

syntax
Schema: валідація даних
***********************

.[perex]
Практична бібліотека для перевірки та нормалізації структур даних за заданою схемою з інтелектуальним і простим у розумінні API.

Встановлення:

```shell
composer require nette/schema
```


Використання .[#toc-ispol-zovanie]
----------------------------------

У змінній `$schema` у нас є схема валідації (що саме це значить і як її створити, ми розповімо пізніше), а у змінній `$data` у нас є структура даних, яку ми хочемо валідувати та нормалізувати. Це можуть бути, наприклад, дані, надіслані користувачем через API, конфігураційний файл тощо.

Завданням займається клас [api:Nette\Schema\Processor], який обробляє вхідні дані та або повертає нормалізовані дані, або викидає виняток [api:Nette\Schema\ValidationException] у разі помилки.

```php
$processor = new Nette\Schema\Processor;

try {
	$normalized = $processor->process($schema, $data);
} catch (Nette\Schema\ValidationException $e) {
	echo 'Data is invalid: ' . $e->getMessage();
}
```

Метод `$e->getMessages()` повертає масив усіх рядків повідомлень, а `$e->getMessageObjects()` повертає всі повідомлення у вигляді об'єктів "Nette\Schema\Message":https://api.nette.org/schema/master/Nette/Schema/Message.html.


Визначення схеми .[#toc-opredelenie-shemy]
------------------------------------------

А тепер давайте створимо схему. За допомогою класу [api:Nette\Schema\Expect] ми фактично визначаємо, як мають виглядати дані. Припустимо, що вхідні дані повинні являти собою структуру (наприклад, масив), що містить елементи `processRefund` типу bool і `refundAmount` типу int.

```php
use Nette\Schema\Expect;

$schema = Expect::structure([
	'processRefund' => Expect::bool(),
	'refundAmount' => Expect::int(),
]);
```

Ми вважаємо, що визначення схеми виглядає зрозумілим, навіть якщо ви бачите його вперше.

Відправимо такі дані для перевірки:

```php
$data = [
	'processRefund' => true,
	'refundAmount' => 17,
];

$normalized = $processor->process($schema, $data); // ОК, проходить
```

Вихід, тобто значенням `$normalized`, є об'єкт `stdClass`. Якщо ми хочемо, щоб результатом був масив, ми додаємо приведення до схеми `Expect::structure([...])->castTo('array')`.

Усі елементи структури є необов'язковими і мають значення за замовчуванням `null`. Приклад:

```php
$data = [
	'refundAmount' => 17,
];

$normalized = $processor->process($schema, $data); // ОК, проходить
// $normalized = {'processRefund' => null, 'refundAmount' => 17}.
```

Той факт, що значенням за замовчуванням є `null`, не означає, що воно буде прийнято у вхідних даних `'processRefund' => null`. Ні, вхідні дані повинні бути булевими, тобто тільки `true` або `false`. Нам довелося б явно дозволити `null` через `Expect::bool()->nullable()`.

Елемент можна зробити обов'язковим, використовуючи `Expect::bool()->required()`. Ми змінюємо значення за замовчуванням на `false`, використовуючи `Expect::bool()->default(false)` або коротко `Expect::bool(false)`.

А що якщо ми захочемо приймати `1` и `0` крім булевих чисел? Перелічимо допустимі значення, які ми також нормалізуємо в boolean:

```php
$schema = Expect::structure([
	'processRefund' => Expect::anyOf(true, false, 1, 0)->castTo('bool'),
	'refundAmount' => Expect::int(),
]);

$normalized = $processor->process($schema, $data);
is_bool($normalized->processRefund); // true
```

Тепер ви знаєте основи того, як визначається схема і як поводяться окремі елементи структури. Тепер ми покажемо, які ще елементи можуть бути використані при визначенні схеми.


Типи даних: type() .[#toc-tipy-dannyh-type]
-------------------------------------------

Усі стандартні типи даних PHP можуть бути перераховані в схемі:

```php
Expect::string($default = null)
Expect::int($default = null)
Expect::float($default = null)
Expect::bool($default = null)
Expect::null()
Expect::array($default = [])
```

А потім всі типи, [підтримувані валідаторами |utils:validators#Expected-Types] через `Expect::type('scalar')` або скорочено `Expect::scalar()`. Також приймаються імена класів або інтерфейсів, наприклад: `Expect::type('AddressEntity')`.

Ви також можете використовувати нотацію об'єднання:

```php
Expect::type('bool|string|array')
```

Значення за замовчуванням завжди `null`, за винятком `array` і `list`, де це порожній масив. (Список - це масив, індексований у порядку зростання числових ключів від нуля, тобто неасоціативний масив).


Масив значень: arrayOf() listOf() .[#toc-massiv-znacenij-arrayof-listof]
------------------------------------------------------------------------

Масив - занадто загальна структура, корисніше вказати, які саме елементи він може містити. Наприклад, масив, елементами якого можуть бути тільки рядки:

```php
$schema = Expect::arrayOf('string');

$processor->process($schema, ['hello', 'world']); // OK
$processor->process($schema, ['a' => 'hello', 'b' => 'world']); // OK
$processor->process($schema, ['key' => 123]); // ПОМИЛКА: 123 не рядок
```

Другий параметр може використовуватися для вказівки ключів (починаючи з версії 1.2):

```php
$schema = Expect::arrayOf('string', 'int');

$processor->process($schema, ['hello', 'world']); // ОК
$processor->process($schema, ['a' => 'hello']); // ПОМИЛКА: 'a' не int
```

Список являє собою індексований масив:

```php
$schema = Expect::listOf('string');

$processor->process($schema, ['a', 'b']); // OK
$processor->process($schema, ['a', 123]); // ERROR: 123 не є рядком
$processor->process($schema, ['key' => 'a']); // ERROR: не список
$processor->process($schema, [1 => 'a', 0 => 'b']); // ERROR: не список
```

Параметр також може бути схемою, тому ми можемо написати:

```php
Expect::arrayOf(Expect::bool())
```

Значення за замовчуванням - порожній масив. Якщо ви вкажете значення за замовчуванням, воно буде об'єднано з переданими даними. Це можна відключити за допомогою `mergeDefaults(false)` (починаючи з версії 1.1).


Перерахування: anyOf() .[#toc-perecislenie-anyof]
-------------------------------------------------

`anyOf()` - це набір значень або схем, якими може бути значення. Ось як записати масив елементів, які можуть бути або `'a'`, або `true`, або `null`:

```php
$schema = Expect::listOf(
	Expect::anyOf('a', true, null),
);

$processor->process($schema, ['a', true, null, 'a']); // ОК
$processor->process($schema, ['a', false]); // ПОМИЛКА: false тут не місце
```

Елементи перерахування також можуть бути схемами:

```php
$schema = Expect::listOf(
	Expect::anyOf(Expect::string(), true, null),
);

$processor->process($schema, ['foo', true, null, 'bar']); // ОК
$processor->process($schema, [123]); // ПОМИЛКА
```

Метод `anyOf()` приймає варіанти як окремі параметри, а не як масив. Щоб передати йому масив значень, використовуйте оператор розпакування `anyOf(...$variants)`.

Значення за замовчуванням - `null`. Використовуйте метод `firstIsDefault()`, щоб зробити перший елемент елементом за замовчуванням:

```php
// за замовчуванням 'hello'
Expect::anyOf(Expect::string('hello'), true, null)->firstIsDefault();
```


Структури .[#toc-struktury]
---------------------------

Структури - це об'єкти з певними ключами. Кожна з цих пар ключ => значення називається "властивістю":

Структури приймають масиви та об'єкти і повертають об'єкти `stdClass` (якщо ви не зміните його за допомогою `castTo('array')` тощо).

За замовчуванням усі властивості є необов'язковими і мають значення за замовчуванням `null`. Ви можете визначити обов'язкові властивості, використовуючи `required()`:

```php
$schema = Expect::structure([
	'required' => Expect::string()->required(),
	'optional' => Expect::string(), // значення за замовчуванням - null
]);

$processor->process($schema, ['optional' => '']);
// ПОМИЛКА: опція 'required' відсутня

$processor->process($schema, ['required' => 'foo']);
// OK, повертає {'required' => 'foo', 'optional' => null}
```

Якщо ви не хочете виводити властивості тільки зі значенням за замовчуванням, використовуйте `skipDefaults()`:

```php
$schema = Expect::structure([
	'required' => Expect::string()->required(),
	'optional' => Expect::string(),
])->skipDefaults();

$processor->process($schema, ['required' => 'foo']);
// OK, повертає {'required' => 'foo'}
```

Хоча `null` є значенням за замовчуванням властивості `optional`, воно не допускається у вхідних даних (значення має бути рядком). Властивості, що приймають значення `null`, визначаються за допомогою `nullable()`:

```php
$schema = Expect::structure([
	'optional' => Expect::string(),
	'nullable' => Expect::string()->nullable(),
]);

$processor->process($schema, ['optional' => null]);
// ПОМИЛКА: 'optional' очікується як рядок, а надається null.

$processor->process($schema, ['nullable' => null]);
// OK, повертає {'optional' => null, 'nullable' => null}
```

За замовчуванням, у вхідних даних не може бути зайвих елементів:

```php
$schema = Expect::structure([
	'key' => Expect::string(),
]);

$processor->process($schema, ['additional' => 1]);
// ПОМИЛКА: Несподіваний елемент 'additional'
```

Подібні елементи змінити за допомогою `otherItems()`. Як параметр ми вкажемо схему для кожного додаткового елемента:

```php
$schema = Expect::structure([
	'key' => Expect::string(),
])->otherItems(Expect::int());

$processor->process($schema, ['additional' => 1]); // OK
$processor->process($schema, ['additional' => true]); // ПОМИЛКА
```


Застарілі елементи .[#toc-ustarevsie-elementy]
----------------------------------------------

Ви можете оголосити властивість застарілою, використовуючи метод `deprecated([string $message])`. Повідомлення про застарівання повертаються за допомогою `$processor->getWarnings()`:

```php
$schema = Expect::structure([
	'old' => Expect::int()->deprecated('Елемент %path% застарів'),
]);

$processor->process($schema, ['old' => 1]); // OK
$processor->getWarnings(); // ["Елемент 'old' застарів"]
```


Діапазони: min() max() .[#toc-diapazony-min-max]
------------------------------------------------

Використовуйте `min()` і `max()` для обмеження кількості елементів у масивах:

```php
// масив, мінімум 10 елементів, максимум 20 елементів
Expect::array()->min(10)->max(20);
```

Для рядків обмежує їхню довжину:

```php
// рядок, довжиною не менше 10 символів, максимум 20 символів
Expect::string()->min(10)->max(20);
```

Для чисел обмежує їхнє значення:

```php
// ціле число, від 10 до 20 включно
Expect::int()->min(10)->max(20);
```

Звичайно, можна згадати тільки `min()`, або тільки `max()`:

```php
// рядок, максимум 20 символів
Expect::string()->max(20);
```


Регулярні вирази: pattern() .[#toc-regulyarnye-vyrazeniya-pattern]
------------------------------------------------------------------

Використовуючи `pattern()`, ви можете вказати регулярний вираз, якому має відповідати **всівся** вхідний рядок (тобто так, ніби він був загорнутий у символи `^` a `$`):

```php
// только 9 цифр
Expect::string()->pattern('\d{9}');
```


Користувацькі твердження: assert() .[#toc-pol-zovatel-skie-utverzdeniya-assert]
-------------------------------------------------------------------------------

Ви можете додати будь-які інші обмеження, використовуючи `assert(callable $fn)`.

```php
$countIsEven = fn($v) => count($v) % 2 === 0;

$schema = Expect::arrayOf('string')
	->assert($countIsEven); // число має бути парним

$processor->process($schema, ['a', 'b']); // OK
$processor->process($schema, ['a', 'b', 'c']); // ПОМИЛКА: 3 - непарне число
```

Або

```php
Expect::string()->assert('is_file'); // файл повинен існувати
```

Ви можете додати власний опис для кожного твердження. Він буде частиною повідомлення про помилку.

```php
$schema = Expect::arrayOf('string')
	->assert($countIsEven, 'Парні елементи в масиві');

$processor->process($schema, ['a', 'b', 'c']);
// Невдале твердження "Парні елементи в масиві" для елемента з масивом значень.
```

Метод можна викликати багаторазово, щоб додати кілька обмежень. Його можна змішувати з викликами `transform()` та `castTo()`.


Перетворення: transform() .[#toc-transformation-transform]
----------------------------------------------------------

Успішно перевірені дані можна змінити за допомогою спеціальної функції:

```php
// conversion to uppercase:
Expect::string()->transform(fn(string $s) => strtoupper($s));
```

Метод можна викликати багаторазово, щоб додати кілька перетворень. Його можна змішувати з викликами `assert()` та `castTo()`. Операції будуть виконані в тому порядку, в якому вони були оголошені:

```php
Expect::type('string|int')
	->castTo('string')
	->assert('ctype_lower', 'All characters must be lowercased')
	->transform(fn(string $s) => strtoupper($s)); // conversion to uppercase
```

Метод `transform()` може одночасно перетворювати і перевіряти значення. Часто це простіше і менш надлишково, ніж ланцюжок `transform()` і `assert()`. Для цього функція отримує об'єкт [Context |api:Nette\Schema\Context] з методом `addError()`, який можна використовувати для додавання інформації про проблеми з валідацією:

```php
Expect::string()
	->transform(function (string $s, Nette\Schema\Context $context) {
		if (!ctype_lower($s)) {
			$context->addError('All characters must be lowercased', 'my.case.error');
			return null;
		}

		return strtoupper($s);
	});
```


Кастинг: castTo() .[#toc-casting-castto]
----------------------------------------

Успішно перевірені дані можуть бути закинуті:

```php
Expect::scalar()->castTo('string');
```

На додаток до власних типів PHP, ви також можете виконувати приведення до класів. При цьому розрізняється, чи це простий клас без конструктора, чи клас з конструктором. Якщо клас не має конструктора, створюється його екземпляр і всі елементи структури записуються в його властивості:

```php
class Info
{
	public bool $processRefund;
	public int $refundAmount;
}

Expect::structure([
	'processRefund' => Expect::bool(),
	'refundAmount' => Expect::int(),
])->castTo(Info::class);

// creates '$obj = new Info' and writes to $obj->processRefund and $obj->refundAmount
```

Якщо клас має конструктор, то елементи структури передаються як іменовані параметри до конструктора:

```php
class Info
{
	public function __construct(
		public bool $processRefund,
		public int $refundAmount,
	) {
	}
}

// creates $obj = new Info(processRefund: ..., refundAmount: ...)
```

Приведення у поєднанні зі скалярним параметром створює об'єкт і передає значення як єдиний параметр до конструктора:

```php
Expect::string()->castTo(DateTime::class);
// creates new DateTime(...)
```


Нормалізація: before() .[#toc-normalizaciya-before]
---------------------------------------------------

Перед самою перевіркою дані можуть бути нормалізовані за допомогою методу `before()`. Як приклад, нехай є елемент, який має бути масивом рядків (наприклад, `['a', 'b', 'c']`), але отримує вхідні дані у вигляді рядка `a b c`:

```php
$explode = fn($v) => explode(' ', $v);

$schema = Expect::arrayOf('string')
	->before($explode);

$normalized = $processor->process($schema, 'a b c');
// OK, повертає ['a', 'b', 'c']
```


Відображення на об'єкти: from() .[#toc-mapping-to-objects-from]
---------------------------------------------------------------

Ви можете згенерувати схему структури з класу. Приклад:

```php
class Config
{
	public string $name;
	public string|null $password;
	public bool $admin = false;
}

$schema = Expect::from(new Config);

$data = [
	'name' => 'jeff',
];

$normalized = $processor->process($schema, $data);
// $normalized instanceof Config
// $normalized = {'name' => 'jeff', 'password' => null, 'admin' => false}
```

Також підтримуються анонімні класи:

```php
$schema = Expect::from(new class {
	public string $name;
	public ?string $password;
	public bool $admin = false;
});
```

Оскільки інформації, отриманої з визначення класу, може бути недостатньо, ви можете додати власну схему для елементів за допомогою другого параметра:

```php
$schema = Expect::from(new Config, [
	'name' => Expect::string()->pattern('\w:.*'),
]);
```

{{leftbar: nette:@menu-topics}}

Schema: валідація даних

Практична бібліотека для перевірки та нормалізації структур даних за заданою схемою з інтелектуальним і простим у розумінні API.

Встановлення:

composer require nette/schema

Використання

У змінній $schema у нас є схема валідації (що саме це значить і як її створити, ми розповімо пізніше), а у змінній $data у нас є структура даних, яку ми хочемо валідувати та нормалізувати. Це можуть бути, наприклад, дані, надіслані користувачем через API, конфігураційний файл тощо.

Завданням займається клас Nette\Schema\Processor, який обробляє вхідні дані та або повертає нормалізовані дані, або викидає виняток Nette\Schema\ValidationException у разі помилки.

$processor = new Nette\Schema\Processor;

try {
	$normalized = $processor->process($schema, $data);
} catch (Nette\Schema\ValidationException $e) {
	echo 'Data is invalid: ' . $e->getMessage();
}

Метод $e->getMessages() повертає масив усіх рядків повідомлень, а $e->getMessageObjects() повертає всі повідомлення у вигляді об'єктів Nette\Schema\Message.

Визначення схеми

А тепер давайте створимо схему. За допомогою класу Nette\Schema\Expect ми фактично визначаємо, як мають виглядати дані. Припустимо, що вхідні дані повинні являти собою структуру (наприклад, масив), що містить елементи processRefund типу bool і refundAmount типу int.

use Nette\Schema\Expect;

$schema = Expect::structure([
	'processRefund' => Expect::bool(),
	'refundAmount' => Expect::int(),
]);

Ми вважаємо, що визначення схеми виглядає зрозумілим, навіть якщо ви бачите його вперше.

Відправимо такі дані для перевірки:

$data = [
	'processRefund' => true,
	'refundAmount' => 17,
];

$normalized = $processor->process($schema, $data); // ОК, проходить

Вихід, тобто значенням $normalized, є об'єкт stdClass. Якщо ми хочемо, щоб результатом був масив, ми додаємо приведення до схеми Expect::structure([...])->castTo('array').

Усі елементи структури є необов'язковими і мають значення за замовчуванням null. Приклад:

$data = [
	'refundAmount' => 17,
];

$normalized = $processor->process($schema, $data); // ОК, проходить
// $normalized = {'processRefund' => null, 'refundAmount' => 17}.

Той факт, що значенням за замовчуванням є null, не означає, що воно буде прийнято у вхідних даних 'processRefund' => null. Ні, вхідні дані повинні бути булевими, тобто тільки true або false. Нам довелося б явно дозволити null через Expect::bool()->nullable().

Елемент можна зробити обов'язковим, використовуючи Expect::bool()->required(). Ми змінюємо значення за замовчуванням на false, використовуючи Expect::bool()->default(false) або коротко Expect::bool(false).

А що якщо ми захочемо приймати 1 и 0 крім булевих чисел? Перелічимо допустимі значення, які ми також нормалізуємо в boolean:

$schema = Expect::structure([
	'processRefund' => Expect::anyOf(true, false, 1, 0)->castTo('bool'),
	'refundAmount' => Expect::int(),
]);

$normalized = $processor->process($schema, $data);
is_bool($normalized->processRefund); // true

Тепер ви знаєте основи того, як визначається схема і як поводяться окремі елементи структури. Тепер ми покажемо, які ще елементи можуть бути використані при визначенні схеми.

Типи даних: type()

Усі стандартні типи даних PHP можуть бути перераховані в схемі:

Expect::string($default = null)
Expect::int($default = null)
Expect::float($default = null)
Expect::bool($default = null)
Expect::null()
Expect::array($default = [])

А потім всі типи, підтримувані валідаторами через Expect::type('scalar') або скорочено Expect::scalar(). Також приймаються імена класів або інтерфейсів, наприклад: Expect::type('AddressEntity').

Ви також можете використовувати нотацію об'єднання:

Expect::type('bool|string|array')

Значення за замовчуванням завжди null, за винятком array і list, де це порожній масив. (Список – це масив, індексований у порядку зростання числових ключів від нуля, тобто неасоціативний масив).

Масив значень: arrayOf() listOf()

Масив – занадто загальна структура, корисніше вказати, які саме елементи він може містити. Наприклад, масив, елементами якого можуть бути тільки рядки:

$schema = Expect::arrayOf('string');

$processor->process($schema, ['hello', 'world']); // OK
$processor->process($schema, ['a' => 'hello', 'b' => 'world']); // OK
$processor->process($schema, ['key' => 123]); // ПОМИЛКА: 123 не рядок

Другий параметр може використовуватися для вказівки ключів (починаючи з версії 1.2):

$schema = Expect::arrayOf('string', 'int');

$processor->process($schema, ['hello', 'world']); // ОК
$processor->process($schema, ['a' => 'hello']); // ПОМИЛКА: 'a' не int

Список являє собою індексований масив:

$schema = Expect::listOf('string');

$processor->process($schema, ['a', 'b']); // OK
$processor->process($schema, ['a', 123]); // ERROR: 123 не є рядком
$processor->process($schema, ['key' => 'a']); // ERROR: не список
$processor->process($schema, [1 => 'a', 0 => 'b']); // ERROR: не список

Параметр також може бути схемою, тому ми можемо написати:

Expect::arrayOf(Expect::bool())

Значення за замовчуванням – порожній масив. Якщо ви вкажете значення за замовчуванням, воно буде об'єднано з переданими даними. Це можна відключити за допомогою mergeDefaults(false) (починаючи з версії 1.1).

Перерахування: anyOf()

anyOf() – це набір значень або схем, якими може бути значення. Ось як записати масив елементів, які можуть бути або 'a', або true, або null:

$schema = Expect::listOf(
	Expect::anyOf('a', true, null),
);

$processor->process($schema, ['a', true, null, 'a']); // ОК
$processor->process($schema, ['a', false]); // ПОМИЛКА: false тут не місце

Елементи перерахування також можуть бути схемами:

$schema = Expect::listOf(
	Expect::anyOf(Expect::string(), true, null),
);

$processor->process($schema, ['foo', true, null, 'bar']); // ОК
$processor->process($schema, [123]); // ПОМИЛКА

Метод anyOf() приймає варіанти як окремі параметри, а не як масив. Щоб передати йому масив значень, використовуйте оператор розпакування anyOf(...$variants).

Значення за замовчуванням – null. Використовуйте метод firstIsDefault(), щоб зробити перший елемент елементом за замовчуванням:

// за замовчуванням 'hello'
Expect::anyOf(Expect::string('hello'), true, null)->firstIsDefault();

Структури

Структури – це об'єкти з певними ключами. Кожна з цих пар ключ ⇒ значення називається „властивістю“:

Структури приймають масиви та об'єкти і повертають об'єкти stdClass (якщо ви не зміните його за допомогою castTo('array') тощо).

За замовчуванням усі властивості є необов'язковими і мають значення за замовчуванням null. Ви можете визначити обов'язкові властивості, використовуючи required():

$schema = Expect::structure([
	'required' => Expect::string()->required(),
	'optional' => Expect::string(), // значення за замовчуванням - null
]);

$processor->process($schema, ['optional' => '']);
// ПОМИЛКА: опція 'required' відсутня

$processor->process($schema, ['required' => 'foo']);
// OK, повертає {'required' => 'foo', 'optional' => null}

Якщо ви не хочете виводити властивості тільки зі значенням за замовчуванням, використовуйте skipDefaults():

$schema = Expect::structure([
	'required' => Expect::string()->required(),
	'optional' => Expect::string(),
])->skipDefaults();

$processor->process($schema, ['required' => 'foo']);
// OK, повертає {'required' => 'foo'}

Хоча null є значенням за замовчуванням властивості optional, воно не допускається у вхідних даних (значення має бути рядком). Властивості, що приймають значення null, визначаються за допомогою nullable():

$schema = Expect::structure([
	'optional' => Expect::string(),
	'nullable' => Expect::string()->nullable(),
]);

$processor->process($schema, ['optional' => null]);
// ПОМИЛКА: 'optional' очікується як рядок, а надається null.

$processor->process($schema, ['nullable' => null]);
// OK, повертає {'optional' => null, 'nullable' => null}

За замовчуванням, у вхідних даних не може бути зайвих елементів:

$schema = Expect::structure([
	'key' => Expect::string(),
]);

$processor->process($schema, ['additional' => 1]);
// ПОМИЛКА: Несподіваний елемент 'additional'

Подібні елементи змінити за допомогою otherItems(). Як параметр ми вкажемо схему для кожного додаткового елемента:

$schema = Expect::structure([
	'key' => Expect::string(),
])->otherItems(Expect::int());

$processor->process($schema, ['additional' => 1]); // OK
$processor->process($schema, ['additional' => true]); // ПОМИЛКА

Застарілі елементи

Ви можете оголосити властивість застарілою, використовуючи метод deprecated([string $message]). Повідомлення про застарівання повертаються за допомогою $processor->getWarnings():

$schema = Expect::structure([
	'old' => Expect::int()->deprecated('Елемент %path% застарів'),
]);

$processor->process($schema, ['old' => 1]); // OK
$processor->getWarnings(); // ["Елемент 'old' застарів"]

Діапазони: min() max()

Використовуйте min() і max() для обмеження кількості елементів у масивах:

// масив, мінімум 10 елементів, максимум 20 елементів
Expect::array()->min(10)->max(20);

Для рядків обмежує їхню довжину:

// рядок, довжиною не менше 10 символів, максимум 20 символів
Expect::string()->min(10)->max(20);

Для чисел обмежує їхнє значення:

// ціле число, від 10 до 20 включно
Expect::int()->min(10)->max(20);

Звичайно, можна згадати тільки min(), або тільки max():

// рядок, максимум 20 символів
Expect::string()->max(20);

Регулярні вирази: pattern()

Використовуючи pattern(), ви можете вказати регулярний вираз, якому має відповідати всівся вхідний рядок (тобто так, ніби він був загорнутий у символи ^ a $):

// только 9 цифр
Expect::string()->pattern('\d{9}');

Користувацькі твердження: assert()

Ви можете додати будь-які інші обмеження, використовуючи assert(callable $fn).

$countIsEven = fn($v) => count($v) % 2 === 0;

$schema = Expect::arrayOf('string')
	->assert($countIsEven); // число має бути парним

$processor->process($schema, ['a', 'b']); // OK
$processor->process($schema, ['a', 'b', 'c']); // ПОМИЛКА: 3 - непарне число

Або

Expect::string()->assert('is_file'); // файл повинен існувати

Ви можете додати власний опис для кожного твердження. Він буде частиною повідомлення про помилку.

$schema = Expect::arrayOf('string')
	->assert($countIsEven, 'Парні елементи в масиві');

$processor->process($schema, ['a', 'b', 'c']);
// Невдале твердження "Парні елементи в масиві" для елемента з масивом значень.

Метод можна викликати багаторазово, щоб додати кілька обмежень. Його можна змішувати з викликами transform() та castTo().

Перетворення: transform()

Успішно перевірені дані можна змінити за допомогою спеціальної функції:

// conversion to uppercase:
Expect::string()->transform(fn(string $s) => strtoupper($s));

Метод можна викликати багаторазово, щоб додати кілька перетворень. Його можна змішувати з викликами assert() та castTo(). Операції будуть виконані в тому порядку, в якому вони були оголошені:

Expect::type('string|int')
	->castTo('string')
	->assert('ctype_lower', 'All characters must be lowercased')
	->transform(fn(string $s) => strtoupper($s)); // conversion to uppercase

Метод transform() може одночасно перетворювати і перевіряти значення. Часто це простіше і менш надлишково, ніж ланцюжок transform() і assert(). Для цього функція отримує об'єкт Context з методом addError(), який можна використовувати для додавання інформації про проблеми з валідацією:

Expect::string()
	->transform(function (string $s, Nette\Schema\Context $context) {
		if (!ctype_lower($s)) {
			$context->addError('All characters must be lowercased', 'my.case.error');
			return null;
		}

		return strtoupper($s);
	});

Кастинг: castTo()

Успішно перевірені дані можуть бути закинуті:

Expect::scalar()->castTo('string');

На додаток до власних типів PHP, ви також можете виконувати приведення до класів. При цьому розрізняється, чи це простий клас без конструктора, чи клас з конструктором. Якщо клас не має конструктора, створюється його екземпляр і всі елементи структури записуються в його властивості:

class Info
{
	public bool $processRefund;
	public int $refundAmount;
}

Expect::structure([
	'processRefund' => Expect::bool(),
	'refundAmount' => Expect::int(),
])->castTo(Info::class);

// creates '$obj = new Info' and writes to $obj->processRefund and $obj->refundAmount

Якщо клас має конструктор, то елементи структури передаються як іменовані параметри до конструктора:

class Info
{
	public function __construct(
		public bool $processRefund,
		public int $refundAmount,
	) {
	}
}

// creates $obj = new Info(processRefund: ..., refundAmount: ...)

Приведення у поєднанні зі скалярним параметром створює об'єкт і передає значення як єдиний параметр до конструктора:

Expect::string()->castTo(DateTime::class);
// creates new DateTime(...)

Нормалізація: before()

Перед самою перевіркою дані можуть бути нормалізовані за допомогою методу before(). Як приклад, нехай є елемент, який має бути масивом рядків (наприклад, ['a', 'b', 'c']), але отримує вхідні дані у вигляді рядка a b c:

$explode = fn($v) => explode(' ', $v);

$schema = Expect::arrayOf('string')
	->before($explode);

$normalized = $processor->process($schema, 'a b c');
// OK, повертає ['a', 'b', 'c']

Відображення на об'єкти: from()

Ви можете згенерувати схему структури з класу. Приклад:

class Config
{
	public string $name;
	public string|null $password;
	public bool $admin = false;
}

$schema = Expect::from(new Config);

$data = [
	'name' => 'jeff',
];

$normalized = $processor->process($schema, $data);
// $normalized instanceof Config
// $normalized = {'name' => 'jeff', 'password' => null, 'admin' => false}

Також підтримуються анонімні класи:

$schema = Expect::from(new class {
	public string $name;
	public ?string $password;
	public bool $admin = false;
});

Оскільки інформації, отриманої з визначення класу, може бути недостатньо, ви можете додати власну схему для елементів за допомогою другого параметра:

$schema = Expect::from(new Config, [
	'name' => Expect::string()->pattern('\w:.*'),
]);