Nette Documentation Preview

syntax
Schema: Validierung von Daten
*****************************

.[perex]
Eine praktische Bibliothek zur Validierung und Normalisierung von Datenstrukturen anhand eines vorgegebenen Schemas mit einer intelligenten und leicht verständlichen API.

Installation:

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


Grundlegende Verwendung .[#toc-basic-usage]
-------------------------------------------

In der Variablen `$schema` haben wir ein Validierungsschema (was genau das bedeutet und wie man es erstellt, wird später erklärt) und in der Variablen `$data` haben wir eine Datenstruktur, die wir validieren und normalisieren wollen. Dies können z. B. Daten sein, die vom Benutzer über eine API, eine Konfigurationsdatei usw. gesendet werden.

Die Aufgabe wird von der Klasse [api:Nette\Schema\Processor] übernommen, die die Eingabe verarbeitet und entweder normalisierte Daten zurückgibt oder im Fehlerfall eine [api:Nette\Schema\ValidationException] Exception wirft.

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

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

Die Methode `$e->getMessages()` liefert ein Array mit allen Nachrichtenstrings und `$e->getMessageObjects()` liefert alle Nachrichten als "Nette\Schema\Message"-Objekte:https://api.nette.org/schema/master/Nette/Schema/Message.html zurück.


Schema definieren .[#toc-defining-schema]
-----------------------------------------

Und nun erstellen wir ein Schema. Die Klasse [api:Nette\Schema\Expect] wird dazu verwendet, um es zu definieren, d.h. wir definieren Erwartungen, wie die Daten aussehen sollen. Sagen wir, dass die Eingabedaten eine Struktur (z. B. ein Array) sein müssen, die Elemente `processRefund` vom Typ bool und `refundAmount` vom Typ int enthält.

```php
use Nette\Schema\Expect;

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

Wir glauben, dass die Schemadefinition klar aussieht, auch wenn Sie sie zum ersten Mal sehen.

Lassen Sie uns die folgenden Daten zur Validierung senden:

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

$normalized = $processor->process($schema, $data); // OK, es funktioniert
```

Die Ausgabe, d. h. der Wert `$normalized`, ist das Objekt `stdClass`. Wenn wir wollen, dass die Ausgabe ein Array ist, fügen wir einen Cast zu schema `Expect::structure([...])->castTo('array')`.

Alle Elemente der Struktur sind optional und haben einen Standardwert `null`. Beispiel:

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

$normalized = $processor->process($schema, $data); // OK, es funktioniert
// $normalized = {'processRefund' => null, 'refundAmount' => 17}
```

Die Tatsache, dass der Standardwert `null` ist, bedeutet nicht, dass er in den Eingabedaten `'processRefund' => null` akzeptiert würde. Nein, die Eingabe muss boolesch sein, d. h. nur `true` oder `false`. Wir müssten `null` über `Expect::bool()->nullable()` ausdrücklich zulassen.

Ein Element kann mit `Expect::bool()->required()` obligatorisch gemacht werden. Wir ändern den Standardwert auf `false` mit `Expect::bool()->default(false)` oder kurz mit `Expect::bool(false)`.

Und was, wenn wir neben Booleschen Werten auch `1` and `0` akzeptieren wollen? Dann listen wir die zulässigen Werte auf, die wir ebenfalls zu booleschen Werten normalisieren werden:

```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
```

Jetzt kennen Sie die Grundlagen, wie das Schema definiert ist und wie sich die einzelnen Elemente der Struktur verhalten. Wir werden nun zeigen, was alle anderen Elemente bei der Definition eines Schemas verwendet werden können.


Datentypen: type() .[#toc-data-types-type]
------------------------------------------

Alle Standard-PHP-Datentypen können im Schema aufgeführt werden:

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

Und dann alle Typen [, die von den Validatoren |utils:validators#Expected Types] über `Expect::type('scalar')` oder abgekürzt `Expect::scalar()`[unterstützt werden |utils:validators#Expected Types]. Auch Klassen- oder Schnittstellennamen werden akzeptiert, z.B. `Expect::type('AddressEntity')`.

Sie können auch die Union-Notation verwenden:

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

Der Standardwert ist immer `null`, außer bei `array` und `list`, wo es sich um ein leeres Array handelt. (Eine Liste ist ein Array, das in aufsteigender Reihenfolge der numerischen Schlüssel von Null an indiziert ist, also ein nicht-assoziatives Array).


Array von Werten: arrayOf() listOf() .[#toc-array-of-values-arrayof-listof]
---------------------------------------------------------------------------

Das Array ist eine zu allgemeine Struktur, es ist sinnvoller, genau anzugeben, welche Elemente es enthalten kann. Zum Beispiel, ein Array, dessen Elemente nur Strings sein können:

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

$processor->process($schema, ['hallo', 'welt']); // OK
$processor->process($schema, ['a' => 'hallo', 'b' => 'welt']); // OK
$processor->process($schema, ['key' => 123]); // FEHLER: 123 ist kein String
```

Der zweite Parameter kann zur Angabe von Schlüsseln verwendet werden (seit Version 1.2):

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

$processor->process($schema, ['hallo', 'welt']); // OK
$processor->process($schema, ['a' => 'hallo']); // ERROR: 'a' ist nicht int
```

Die Liste ist ein indiziertes Array:

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

$processor->process($schema, ['a', 'b']); // OK
$processor->process($schema, ['a', 123]); // FEHLER: 123 ist keine Zeichenkette
$processor->process($schema, ['key' => 'a']); // FEHLER: ist keine Liste
$processor->process($schema, [1 => 'a', 0 => 'b']); // FEHLER: ist keine Liste
```

Der Parameter kann auch ein Schema sein, so dass wir schreiben können:

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

Der Standardwert ist ein leeres Array. Wenn Sie einen Standardwert angeben, wird dieser mit den übergebenen Daten zusammengeführt. Dies kann mit `mergeDefaults(false)` deaktiviert werden (seit Version 1.1).


Aufzählung: anyOf() .[#toc-enumeration-anyof]
---------------------------------------------

`anyOf()` ist eine Menge von Werten oder Schemata, die ein Wert sein kann. So schreiben Sie ein Array von Elementen, die entweder `'a'`, `true` oder `null` sein können:

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

$processor->process($schema, ['a', true, null, 'a']); // OK
$processor->process($schema, ['a', false]); // ERROR: false gehört nicht dazu
```

Die Aufzählungselemente können auch Schemata sein:

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

$processor->process($schema, ['foo', true, null, 'bar']); // OK
$processor->process($schema, [123]); // FEHLER
```

Die Methode `anyOf()` akzeptiert Varianten als einzelne Parameter, nicht als Array. Um ihr ein Array von Werten zu übergeben, verwenden Sie den Entpackungsoperator `anyOf(...$variants)`.

Der Standardwert ist `null`. Verwenden Sie die Methode `firstIsDefault()`, um das erste Element zum Standardwert zu machen:

```php
// Standard ist 'hallo'
Expect::anyOf(Expect::string('hallo'), true, null)->firstIsDefault();
```


Strukturen .[#toc-structures]
-----------------------------

Strukturen sind Objekte mit definierten Schlüsseln. Jedes dieser Schlüssel => Wert-Paare wird als "Eigenschaft" bezeichnet:

Strukturen akzeptieren Arrays und Objekte und geben Objekte zurück `stdClass`.

Standardmäßig sind alle Eigenschaften optional und haben einen Standardwert von `null`. Obligatorische Eigenschaften können Sie mit `required()` definieren:

```php
$schema = Expect::structure([
	'required' => Expect::string()->required(),
	'optional' => Expect::string(), // der Standardwert ist null
]);

$processor->process($schema, ['optional' => '']);
// ERROR: option 'required' is missing

$processor->process($schema, ['required' => 'foo']);
// OK, liefert {'required' => 'foo', 'optional' => null}
```

Wenn Sie keine Eigenschaften mit nur einem Standardwert ausgeben wollen, verwenden Sie `skipDefaults()`:

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

$processor->process($schema, ['required' => 'foo']);
// OK, liefert {'required' => 'foo'}
```

Obwohl `null` der Standardwert der Eigenschaft `optional` ist, ist er in den Eingabedaten nicht erlaubt (der Wert muss ein String sein). Eigenschaften, die `null` akzeptieren, werden mit `nullable()` definiert:

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

$processor->process($schema, ['optional' => null]);
// FEHLER: 'optional' wird als String erwartet, null gegeben.

$processor->process($schema, ['nullable' => null]);
// OK, liefert {'optional' => null, 'nullable' => null}
```

Das Array mit allen Struktureigenschaften wird von der Methode `getShape()` zurückgegeben.

Standardmäßig können keine zusätzlichen Elemente in den Eingabedaten enthalten sein:

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

$processor->process($schema, ['additional' => 1]);
// ERROR: Unexpected item 'additional'
```

Das können wir mit `otherItems()` ändern. Als Parameter wird das Schema für jedes zusätzliche Element angegeben:

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

$processor->process($schema, ['additional' => 1]); // OK
$processor->process($schema, ['additional' => true]); // ERROR
```

Sie können eine neue Struktur erstellen, indem Sie mit `extend()` von einer anderen ableiten:

```php
$dog = Expect::structure([
	'name' => Expect::string(),
	'age' => Expect::int(),
]);

$dogWithBreed = $dog->extend([
	'breed' => Expect::string(),
]);
```


Array .[#toc-array]
-------------------

Ein Array mit definierten Schlüsseln. Es gelten die gleichen Regeln wie für [Strukturen |#structure].

```php
$schema = Expect::array([
	'required' => Expect::string()->required(),
	'optional' => Expect::string(), // default value is null
]);
```

Sie können auch ein indiziertes Array, ein sogenanntes Tupel, definieren:

```php
$schema = Expect::array([
	Expect::int(),
	Expect::string(),
	Expect::bool(),
]);

$processor->process($schema, [1, 'hello', true]); // OK
```


Verwerfungen .[#toc-deprecations]
---------------------------------

Sie können eine Eigenschaft mit der `deprecated([string $message])` Methode. Verwerfungshinweise werden von `$processor->getWarnings()` zurückgegeben:

```php
$schema = Expect::structure([
	'old' => Expect::int()->deprecated('Das Element %path% ist veraltet'),
]);

$processor->process($schema, ['old' => 1]); // OK
$processor->getWarnings(); // ["Das Element 'old' ist veraltet"]
```


Bereiche: min() max() .[#toc-ranges-min-max]
--------------------------------------------

Verwenden Sie `min()` und `max()`, um die Anzahl der Elemente für Arrays zu begrenzen:

```php
// Array, mindestens 10 Elemente, maximal 20 Elemente
Expect::array()->min(10)->max(20);
```

Bei Zeichenketten begrenzen Sie deren Länge:

```php
// String, mindestens 10 Zeichen lang, maximal 20 Zeichen
Expect::string()->min(10)->max(20);
```

Bei Zahlen begrenzen Sie ihren Wert:

```php
// Ganzzahl, zwischen 10 und 20 einschließlich
Expect::int()->min(10)->max(20);
```

Es ist natürlich möglich, nur `min()` oder nur `max()` zu nennen:

```php
// Zeichenkette, maximal 20 Zeichen
Expect::string()->max(20);
```


Reguläre Ausdrücke: pattern() .[#toc-regular-expressions-pattern]
-----------------------------------------------------------------

Mit `pattern()` können Sie einen regulären Ausdruck angeben, mit dem die **gesamte** Eingabezeichenkette übereinstimmen muss (d.h. als ob sie in Zeichen eingeschlossen wäre `^` a `$`):

```php
// nur 9 Ziffern
Expect::string()->pattern('\d{9}');
```


Benutzerdefinierte Assertions: assert() .[#toc-custom-assertions-assert]
------------------------------------------------------------------------

Sie können weitere Einschränkungen mit `assert(callable $fn)` hinzufügen.

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

$schema = Expect::arrayOf('string')
	->assert($countIsEven); // die Anzahl muss gerade sein

$processor->process($schema, ['a', 'b']); // OK
$processor->process($schema, ['a', 'b', 'c']); // ERROR: 3 ist nicht gerade
```

Oder

```php
Expect::string()->assert('is_file'); // die Datei muss existieren
```

Sie können Ihre eigene Beschreibung für jede Assertion hinzufügen. Sie wird Teil der Fehlermeldung sein.

```php
$schema = Expect::arrayOf('string')
	->assert($countIsEven, 'Gerade Elemente in Array');

$processor->process($schema, ['a', 'b', 'c']);
// Fehlgeschlagene Assertion "Gerade Elemente in Array" für Element mit Wert array.
```

Die Methode kann wiederholt aufgerufen werden, um mehrere Beschränkungen hinzuzufügen. Sie kann mit Aufrufen von `transform()` und `castTo()` vermischt werden.


Umwandlung: transform() .[#toc-transformation-transform]
--------------------------------------------------------

Erfolgreich überprüfte Daten können mit einer benutzerdefinierten Funktion geändert werden:

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

Die Methode kann wiederholt aufgerufen werden, um mehrere Transformationen hinzuzufügen. Sie kann mit Aufrufen von `assert()` und `castTo()` vermischt werden. Die Operationen werden in der Reihenfolge ausgeführt, in der sie deklariert sind:

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

Die Methode `transform()` kann den Wert gleichzeitig transformieren und validieren. Dies ist oft einfacher und weniger redundant als die Verkettung von `transform()` und `assert()`. Zu diesem Zweck erhält die Funktion ein [Context-Objekt |api:Nette\Schema\Context] mit einer `addError()` -Methode, die dazu verwendet werden kann, Informationen über Validierungsprobleme hinzuzufügen:

```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);
	});
```


Gießen: castTo() .[#toc-casting-castto]
---------------------------------------

Erfolgreich überprüfte Daten können gecastet werden:

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

Zusätzlich zu den nativen PHP-Typen können Sie auch auf Klassen casten. Es wird unterschieden, ob es sich um eine einfache Klasse ohne Konstruktor oder um eine Klasse mit Konstruktor handelt. Wenn die Klasse keinen Konstruktor hat, wird eine Instanz der Klasse erstellt und alle Elemente der Struktur werden in ihre Eigenschaften geschrieben:

```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
```

Wenn die Klasse einen Konstruktor hat, werden die Elemente der Struktur als benannte Parameter an den Konstruktor übergeben:

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

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

Casting in Verbindung mit einem skalaren Parameter erzeugt ein Objekt und übergibt den Wert als einzigen Parameter an den Konstruktor:

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


Normalisierung: before() .[#toc-normalization-before]
-----------------------------------------------------

Vor der eigentlichen Validierung können die Daten mit der Methode `before()` normalisiert werden. Als Beispiel nehmen wir ein Element an, das ein Array von Strings sein muss (z.B. `['a', 'b', 'c']`), aber eine Eingabe in Form einer Zeichenkette erhält `a b c`:

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

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

$normalized = $processor->process($schema, 'a b c');
// OK, liefert ['a', 'b', 'c']
```


Zuordnung zu Objekten: from() .[#toc-mapping-to-objects-from]
-------------------------------------------------------------

Sie können aus der Klasse ein Strukturschema erzeugen. Beispiel:

```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}
```

Anonyme Klassen werden ebenfalls unterstützt:

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

Da die aus der Klassendefinition gewonnenen Informationen möglicherweise nicht ausreichen, können Sie mit dem zweiten Parameter ein benutzerdefiniertes Schema für die Elemente hinzufügen:

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

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

Schema: Validierung von Daten

Eine praktische Bibliothek zur Validierung und Normalisierung von Datenstrukturen anhand eines vorgegebenen Schemas mit einer intelligenten und leicht verständlichen API.

Installation:

composer require nette/schema

Grundlegende Verwendung

In der Variablen $schema haben wir ein Validierungsschema (was genau das bedeutet und wie man es erstellt, wird später erklärt) und in der Variablen $data haben wir eine Datenstruktur, die wir validieren und normalisieren wollen. Dies können z. B. Daten sein, die vom Benutzer über eine API, eine Konfigurationsdatei usw. gesendet werden.

Die Aufgabe wird von der Klasse Nette\Schema\Processor übernommen, die die Eingabe verarbeitet und entweder normalisierte Daten zurückgibt oder im Fehlerfall eine Nette\Schema\ValidationException Exception wirft.

$processor = new Nette\Schema\Processor;

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

Die Methode $e->getMessages() liefert ein Array mit allen Nachrichtenstrings und $e->getMessageObjects() liefert alle Nachrichten als „Nette\Schema\Message“-Objekte:https://api.nette.org/…Message.html zurück.

Schema definieren

Und nun erstellen wir ein Schema. Die Klasse Nette\Schema\Expect wird dazu verwendet, um es zu definieren, d.h. wir definieren Erwartungen, wie die Daten aussehen sollen. Sagen wir, dass die Eingabedaten eine Struktur (z. B. ein Array) sein müssen, die Elemente processRefund vom Typ bool und refundAmount vom Typ int enthält.

use Nette\Schema\Expect;

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

Wir glauben, dass die Schemadefinition klar aussieht, auch wenn Sie sie zum ersten Mal sehen.

Lassen Sie uns die folgenden Daten zur Validierung senden:

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

$normalized = $processor->process($schema, $data); // OK, es funktioniert

Die Ausgabe, d. h. der Wert $normalized, ist das Objekt stdClass. Wenn wir wollen, dass die Ausgabe ein Array ist, fügen wir einen Cast zu schema Expect::structure([...])->castTo('array').

Alle Elemente der Struktur sind optional und haben einen Standardwert null. Beispiel:

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

$normalized = $processor->process($schema, $data); // OK, es funktioniert
// $normalized = {'processRefund' => null, 'refundAmount' => 17}

Die Tatsache, dass der Standardwert null ist, bedeutet nicht, dass er in den Eingabedaten 'processRefund' => null akzeptiert würde. Nein, die Eingabe muss boolesch sein, d. h. nur true oder false. Wir müssten null über Expect::bool()->nullable() ausdrücklich zulassen.

Ein Element kann mit Expect::bool()->required() obligatorisch gemacht werden. Wir ändern den Standardwert auf false mit Expect::bool()->default(false) oder kurz mit Expect::bool(false).

Und was, wenn wir neben Booleschen Werten auch 1 and 0 akzeptieren wollen? Dann listen wir die zulässigen Werte auf, die wir ebenfalls zu booleschen Werten normalisieren werden:

$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

Jetzt kennen Sie die Grundlagen, wie das Schema definiert ist und wie sich die einzelnen Elemente der Struktur verhalten. Wir werden nun zeigen, was alle anderen Elemente bei der Definition eines Schemas verwendet werden können.

Datentypen: type()

Alle Standard-PHP-Datentypen können im Schema aufgeführt werden:

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

Und dann alle Typen , die von den Validatoren über Expect::type('scalar') oder abgekürzt Expect::scalar()unterstützt werden. Auch Klassen- oder Schnittstellennamen werden akzeptiert, z.B. Expect::type('AddressEntity').

Sie können auch die Union-Notation verwenden:

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

Der Standardwert ist immer null, außer bei array und list, wo es sich um ein leeres Array handelt. (Eine Liste ist ein Array, das in aufsteigender Reihenfolge der numerischen Schlüssel von Null an indiziert ist, also ein nicht-assoziatives Array).

Array von Werten: arrayOf() listOf()

Das Array ist eine zu allgemeine Struktur, es ist sinnvoller, genau anzugeben, welche Elemente es enthalten kann. Zum Beispiel, ein Array, dessen Elemente nur Strings sein können:

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

$processor->process($schema, ['hallo', 'welt']); // OK
$processor->process($schema, ['a' => 'hallo', 'b' => 'welt']); // OK
$processor->process($schema, ['key' => 123]); // FEHLER: 123 ist kein String

Der zweite Parameter kann zur Angabe von Schlüsseln verwendet werden (seit Version 1.2):

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

$processor->process($schema, ['hallo', 'welt']); // OK
$processor->process($schema, ['a' => 'hallo']); // ERROR: 'a' ist nicht int

Die Liste ist ein indiziertes Array:

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

$processor->process($schema, ['a', 'b']); // OK
$processor->process($schema, ['a', 123]); // FEHLER: 123 ist keine Zeichenkette
$processor->process($schema, ['key' => 'a']); // FEHLER: ist keine Liste
$processor->process($schema, [1 => 'a', 0 => 'b']); // FEHLER: ist keine Liste

Der Parameter kann auch ein Schema sein, so dass wir schreiben können:

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

Der Standardwert ist ein leeres Array. Wenn Sie einen Standardwert angeben, wird dieser mit den übergebenen Daten zusammengeführt. Dies kann mit mergeDefaults(false) deaktiviert werden (seit Version 1.1).

Aufzählung: anyOf()

anyOf() ist eine Menge von Werten oder Schemata, die ein Wert sein kann. So schreiben Sie ein Array von Elementen, die entweder 'a', true oder null sein können:

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

$processor->process($schema, ['a', true, null, 'a']); // OK
$processor->process($schema, ['a', false]); // ERROR: false gehört nicht dazu

Die Aufzählungselemente können auch Schemata sein:

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

$processor->process($schema, ['foo', true, null, 'bar']); // OK
$processor->process($schema, [123]); // FEHLER

Die Methode anyOf() akzeptiert Varianten als einzelne Parameter, nicht als Array. Um ihr ein Array von Werten zu übergeben, verwenden Sie den Entpackungsoperator anyOf(...$variants).

Der Standardwert ist null. Verwenden Sie die Methode firstIsDefault(), um das erste Element zum Standardwert zu machen:

// Standard ist 'hallo'
Expect::anyOf(Expect::string('hallo'), true, null)->firstIsDefault();

Strukturen

Strukturen sind Objekte mit definierten Schlüsseln. Jedes dieser Schlüssel ⇒ Wert-Paare wird als „Eigenschaft“ bezeichnet:

Strukturen akzeptieren Arrays und Objekte und geben Objekte zurück stdClass.

Standardmäßig sind alle Eigenschaften optional und haben einen Standardwert von null. Obligatorische Eigenschaften können Sie mit required() definieren:

$schema = Expect::structure([
	'required' => Expect::string()->required(),
	'optional' => Expect::string(), // der Standardwert ist null
]);

$processor->process($schema, ['optional' => '']);
// ERROR: option 'required' is missing

$processor->process($schema, ['required' => 'foo']);
// OK, liefert {'required' => 'foo', 'optional' => null}

Wenn Sie keine Eigenschaften mit nur einem Standardwert ausgeben wollen, verwenden Sie skipDefaults():

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

$processor->process($schema, ['required' => 'foo']);
// OK, liefert {'required' => 'foo'}

Obwohl null der Standardwert der Eigenschaft optional ist, ist er in den Eingabedaten nicht erlaubt (der Wert muss ein String sein). Eigenschaften, die null akzeptieren, werden mit nullable() definiert:

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

$processor->process($schema, ['optional' => null]);
// FEHLER: 'optional' wird als String erwartet, null gegeben.

$processor->process($schema, ['nullable' => null]);
// OK, liefert {'optional' => null, 'nullable' => null}

Das Array mit allen Struktureigenschaften wird von der Methode getShape() zurückgegeben.

Standardmäßig können keine zusätzlichen Elemente in den Eingabedaten enthalten sein:

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

$processor->process($schema, ['additional' => 1]);
// ERROR: Unexpected item 'additional'

Das können wir mit otherItems() ändern. Als Parameter wird das Schema für jedes zusätzliche Element angegeben:

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

$processor->process($schema, ['additional' => 1]); // OK
$processor->process($schema, ['additional' => true]); // ERROR

Sie können eine neue Struktur erstellen, indem Sie mit extend() von einer anderen ableiten:

$dog = Expect::structure([
	'name' => Expect::string(),
	'age' => Expect::int(),
]);

$dogWithBreed = $dog->extend([
	'breed' => Expect::string(),
]);

Array

Ein Array mit definierten Schlüsseln. Es gelten die gleichen Regeln wie für Strukturen.

$schema = Expect::array([
	'required' => Expect::string()->required(),
	'optional' => Expect::string(), // default value is null
]);

Sie können auch ein indiziertes Array, ein sogenanntes Tupel, definieren:

$schema = Expect::array([
	Expect::int(),
	Expect::string(),
	Expect::bool(),
]);

$processor->process($schema, [1, 'hello', true]); // OK

Verwerfungen

Sie können eine Eigenschaft mit der deprecated([string $message]) Methode. Verwerfungshinweise werden von $processor->getWarnings() zurückgegeben:

$schema = Expect::structure([
	'old' => Expect::int()->deprecated('Das Element %path% ist veraltet'),
]);

$processor->process($schema, ['old' => 1]); // OK
$processor->getWarnings(); // ["Das Element 'old' ist veraltet"]

Bereiche: min() max()

Verwenden Sie min() und max(), um die Anzahl der Elemente für Arrays zu begrenzen:

// Array, mindestens 10 Elemente, maximal 20 Elemente
Expect::array()->min(10)->max(20);

Bei Zeichenketten begrenzen Sie deren Länge:

// String, mindestens 10 Zeichen lang, maximal 20 Zeichen
Expect::string()->min(10)->max(20);

Bei Zahlen begrenzen Sie ihren Wert:

// Ganzzahl, zwischen 10 und 20 einschließlich
Expect::int()->min(10)->max(20);

Es ist natürlich möglich, nur min() oder nur max() zu nennen:

// Zeichenkette, maximal 20 Zeichen
Expect::string()->max(20);

Reguläre Ausdrücke: pattern()

Mit pattern() können Sie einen regulären Ausdruck angeben, mit dem die gesamte Eingabezeichenkette übereinstimmen muss (d.h. als ob sie in Zeichen eingeschlossen wäre ^ a $):

// nur 9 Ziffern
Expect::string()->pattern('\d{9}');

Benutzerdefinierte Assertions: assert()

Sie können weitere Einschränkungen mit assert(callable $fn) hinzufügen.

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

$schema = Expect::arrayOf('string')
	->assert($countIsEven); // die Anzahl muss gerade sein

$processor->process($schema, ['a', 'b']); // OK
$processor->process($schema, ['a', 'b', 'c']); // ERROR: 3 ist nicht gerade

Oder

Expect::string()->assert('is_file'); // die Datei muss existieren

Sie können Ihre eigene Beschreibung für jede Assertion hinzufügen. Sie wird Teil der Fehlermeldung sein.

$schema = Expect::arrayOf('string')
	->assert($countIsEven, 'Gerade Elemente in Array');

$processor->process($schema, ['a', 'b', 'c']);
// Fehlgeschlagene Assertion "Gerade Elemente in Array" für Element mit Wert array.

Die Methode kann wiederholt aufgerufen werden, um mehrere Beschränkungen hinzuzufügen. Sie kann mit Aufrufen von transform() und castTo() vermischt werden.

Umwandlung: transform()

Erfolgreich überprüfte Daten können mit einer benutzerdefinierten Funktion geändert werden:

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

Die Methode kann wiederholt aufgerufen werden, um mehrere Transformationen hinzuzufügen. Sie kann mit Aufrufen von assert() und castTo() vermischt werden. Die Operationen werden in der Reihenfolge ausgeführt, in der sie deklariert sind:

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

Die Methode transform() kann den Wert gleichzeitig transformieren und validieren. Dies ist oft einfacher und weniger redundant als die Verkettung von transform() und assert(). Zu diesem Zweck erhält die Funktion ein Context-Objekt mit einer addError() -Methode, die dazu verwendet werden kann, Informationen über Validierungsprobleme hinzuzufügen:

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);
	});

Gießen: castTo()

Erfolgreich überprüfte Daten können gecastet werden:

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

Zusätzlich zu den nativen PHP-Typen können Sie auch auf Klassen casten. Es wird unterschieden, ob es sich um eine einfache Klasse ohne Konstruktor oder um eine Klasse mit Konstruktor handelt. Wenn die Klasse keinen Konstruktor hat, wird eine Instanz der Klasse erstellt und alle Elemente der Struktur werden in ihre Eigenschaften geschrieben:

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

Wenn die Klasse einen Konstruktor hat, werden die Elemente der Struktur als benannte Parameter an den Konstruktor übergeben:

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

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

Casting in Verbindung mit einem skalaren Parameter erzeugt ein Objekt und übergibt den Wert als einzigen Parameter an den Konstruktor:

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

Normalisierung: before()

Vor der eigentlichen Validierung können die Daten mit der Methode before() normalisiert werden. Als Beispiel nehmen wir ein Element an, das ein Array von Strings sein muss (z.B. ['a', 'b', 'c']), aber eine Eingabe in Form einer Zeichenkette erhält a b c:

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

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

$normalized = $processor->process($schema, 'a b c');
// OK, liefert ['a', 'b', 'c']

Zuordnung zu Objekten: from()

Sie können aus der Klasse ein Strukturschema erzeugen. Beispiel:

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}

Anonyme Klassen werden ebenfalls unterstützt:

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

Da die aus der Klassendefinition gewonnenen Informationen möglicherweise nicht ausreichen, können Sie mit dem zweiten Parameter ein benutzerdefiniertes Schema für die Elemente hinzufügen:

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