雪软 / schema
📐 Nette Schema: 对给定的 Schema 进行数据结构验证。
Requires
- php: >=8.0
- nette/utils: ^4.0
Requires (Dev)
- nette/tester: ^2.4
- phpstan/phpstan-nette: ^1.0
- tracy/tracy: ^2.8
This package is auto-updated.
Last update: 2024-08-30 01:49:53 UTC
README
Nette Schema
介绍
一个用于对给定的 Schema 进行数据结构验证和规范化的实用库,具有智能且易于理解的 API。
文档可以在网站上找到。
安装
composer require nette/schema
它需要 PHP 版本 8.0,并支持 PHP 8.0 到 8.2。
支持我
你喜欢 Nette Schema 吗?你在期待新功能吗?
谢谢!
基本用法
在变量 $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 对象。
定义 Schema
现在我们来创建一个 Schema。使用类 Nette\Schema\Expect 来定义它,我们实际上定义了数据的期望外观。假设输入数据必须是一个包含元素 processRefund
的结构(例如数组)类型为 bool 和 refundAmount
类型为 int。
use Nette\Schema\Expect; $schema = Expect::structure([ 'processRefund' => Expect::bool(), 'refundAmount' => Expect::int(), ]);
我们认为 Schema 定义看起来很清晰,即使是你第一次看到它。
让我们发送以下数据进行验证
$data = [ 'processRefund' => true, 'refundAmount' => 17, ]; $normalized = $processor->process($schema, $data); // OK, it passes
输出,即值 $normalized
,是 stdClass
对象。如果我们想让输出是一个数组,我们向 Schema 添加一个类型转换 Expect::structure([...])->castTo('array')
。
结构中的所有元素都是可选的,并具有默认值 null
。示例
$data = [ 'refundAmount' => 17, ]; $normalized = $processor->process($schema, $data); // OK, it passes // $normalized = {'processRefund' => null, 'refundAmount' => 17}
默认值是 null
并不意味着它会被接受在输入数据 'processRefund' => null
中。不,输入必须是布尔值,即只有 true
或 false
。我们必须通过 Expect::bool()->nullable()
明确允许 null
。
可以使用 Expect::bool()->required()
将项目变为必填。我们使用 Expect::bool()->default(false)
或简短地使用 Expect::bool(false)
将默认值更改为 false
。
如果我们还想接受布尔值以外的 1
和 0
,我们可以列出允许的值,这些值也将被规范化为布尔值
$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
现在你已经了解了如何定义 Schema 以及结构中各个元素的行为。我们现在将展示所有其他元素在定义 Schema 时的用途。
数据类型:type()
所有标准的 PHP 数据类型都可以在 Schema 中列出
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()
支持的 Validators 类型。也接受类或接口名称,例如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]); // ERROR: 123 is not a string
列表是索引数组
$schema = Expect::listOf('string'); $processor->process($schema, ['a', 'b']); // OK $processor->process($schema, ['a', 123]); // ERROR: 123 is not a string $processor->process($schema, ['key' => 'a']); // ERROR: is not a list $processor->process($schema, [1 => 'a', 0 => 'b']); // ERROR: is not a list
参数也可以是一个模式,因此我们可以这样写
Expect::arrayOf(Expect::bool())
默认值为空数组。如果您指定默认值并调用mergeDefaults()
,则它将与传递的数据合并。
枚举:anyOf()
anyOf()
是一组值或模式,值可以是这些之一。以下是如何编写一个可以包含'a'
、true
或null
的元素数组的示例
$schema = Expect::listOf( Expect::anyOf('a', true, null) ); $processor->process($schema, ['a', true, null, 'a']); // OK $processor->process($schema, ['a', false]); // ERROR: false does not belong there
枚举元素也可以是模式
$schema = Expect::listOf( Expect::anyOf(Expect::string(), true, null) ); $processor->process($schema, ['foo', true, null, 'bar']); // OK $processor->process($schema, [123]); // ERROR
默认值为null
。
结构
结构是具有定义键的对象。每个键 => 值对都称为“属性”
结构接受数组和对象,并返回stdClass
对象(除非您使用castTo('array')
等更改它)。
默认情况下,所有属性都是可选的,默认值为null
。您可以使用required()
定义必需属性
$schema = Expect::structure([ 'required' => Expect::string()->required(), 'optional' => Expect::string(), // the default value is null ]); $processor->process($schema, ['optional' => '']); // ERROR: item 'required' is missing $processor->process($schema, ['required' => 'foo']); // OK, returns {'required' => 'foo', 'optional' => null}
尽管null
是可选属性的默认值,但在输入数据中不允许使用(值必须是字符串)。接受null
的属性使用nullable()
定义
$schema = Expect::structure([ 'optional' => Expect::string(), 'nullable' => Expect::string()->nullable(), ]); $processor->process($schema, ['optional' => null]); // ERROR: 'optional' expects to be string, null given. $processor->process($schema, ['nullable' => null]); // OK, returns {'optional' => null, 'nullable' => null}
默认情况下,输入数据中不允许有额外的项
$schema = Expect::structure([ 'key' => Expect::string(), ]); $processor->process($schema, ['additional' => 1]); // ERROR: Unexpected item 'additional'
我们可以使用otherItems()
更改这一点。作为参数,我们将指定每个额外元素的架构
$schema = Expect::structure([ 'key' => Expect::string(), ])->otherItems(Expect::int()); $processor->process($schema, ['additional' => 1]); // OK $processor->process($schema, ['additional' => true]); // ERROR
弃用
您可以使用deprecated([string $message])
方法弃用属性。自v1.1以来,弃用警告由$processor->getWarnings()
返回
$schema = Expect::structure([ 'old' => Expect::int()->deprecated('The item %path% is deprecated'), ]); $processor->process($schema, ['old' => 1]); // OK $processor->getWarnings(); // ["The item 'old' is deprecated"]
范围:min() max()
使用min()
和max()
限制数组的元素数量
// array, at least 10 items, maximum 20 items Expect::array()->min(10)->max(20);
对于字符串,限制它们的长度
// string, at least 10 characters long, maximum 20 characters Expect::string()->min(10)->max(20);
对于数字,限制它们的值
// integer, between 10 and 20 inclusive Expect::int()->min(10)->max(20);
当然,您可以只提及min()
或只提及max()
// string, maximum 20 characters Expect::string()->max(20);
正则表达式:pattern()
使用pattern()
,您可以指定一个正则表达式,整个输入字符串必须匹配(即,就像它被字符^
和$
包裹一样)
// just 9 digits Expect::string()->pattern('\d{9}');
自定义断言:assert()
您可以使用assert(callable $fn)
添加任何其他限制。
$countIsEven = function ($v) { return count($v) % 2 === 0; }; $schema = Expect::arrayOf('string') ->assert($countIsEven); // the count must be even $processor->process($schema, ['a', 'b']); // OK $processor->process($schema, ['a', 'b', 'c']); // ERROR: 3 is not even
或者
Expect::string()->assert('is_file'); // the file must exist
您可以为每个断言添加自己的描述。它将是错误消息的一部分。
$schema = Expect::arrayOf('string') ->assert($countIsEven, 'Even items in array'); $processor->process($schema, ['a', 'b', 'c']); // Failed assertion "Even items in array" for item with value array.
该方法可以重复调用以添加更多断言。
映射到对象:from()
您可以从类生成结构架构。示例
class Config { /** @var string */ public $name; /** @var string|null */ public $password; /** @var bool */ public $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 7.4或更高版本,您可以使用原生类型
class Config { public string $name; public ?string $password; public bool $admin = false; } $schema = Expect::from(new Config);
匿名类也受支持
$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:.*'), ]);
转换:castTo()
成功验证的数据可以转换
Expect::scalar()->castTo('string');
除了原生PHP类型之外,您还可以将数据转换到类
Expect::scalar()->castTo('AddressEntity');
规范化:before()
在验证之前,可以使用before()
方法对数据进行规范化。以下是一个示例,元素必须是一个字符串数组(例如['a', 'b', 'c']
),但接收到的输入是字符串a b c
$explode = function ($v) { return explode(' ', $v); }; $schema = Expect::arrayOf('string') ->before($explode); $normalized = $processor->process($schema, 'a b c'); // OK, returns ['a', 'b', 'c']