amsify42/php-typestruct

PHP 数据验证包,用于验证数据是否符合定义的结构

1.1 2022-12-06 16:01 UTC

This package is not auto-updated.

Last update: 2024-09-25 00:48:59 UTC


README

PHP 数据验证包,用于验证数据是否符合定义的结构。

安装

$ composer require amsify42/php-typestruct

目录

  1. 简介
  2. 验证
  3. 数据
  4. 类验证
  5. 规则
  6. 自定义规则
  7. 复杂示例

1. 简介

这个 PHP 包的目的是使验证变得简单,并且定义的验证结构应该是可读的。传入的数据可以与定义的结构进行验证。

2. 验证

假设我们有以下数组的格式数据。

$data = [
    'id' => 42,
    'name' => 'amsify',
    'price' => 4.2
];

并且我们希望对这些数据进行严格验证。现在我们可以定义一个结构,以这个结构来验证数据。

namespace App\TypeStruct;

export typestruct Simple {
    id: int,
    name: string,
    price: float
}

注意我们定义的结构看起来不完全像 PHP 语法,但它将作为一个结构来验证数据。

$data = [
    'id' => 42,
    'name' => 'amsify',
    'price' => 4.2
];
$typeStruct = new Amsify42\TypeStruct\TypeStruct();
$typeStruct->setClass(App\TypeStruct\Simple::class);
$result = $typeStruct->validate($data);

注意我们正在创建一个 Amsify42\TypeStruct\TypeStruct 的新实例,传递类型结构类的完整类名 App\TypeStruct\Simple,并将数据传递给 validate() 方法。

validate 方法将返回信息,说明传入的数据是否已通过结构验证,并返回

array(2) {
  ["is_validated"]=>
  bool(true)
  ["messages"]=>
  array(0) {
  }
}

is_validated 将根据数据是否验证通过返回 truefalse,而 messages 将根据未验证的元素返回错误消息的层次结构。

辅助方法

我们还可以使用辅助方法来获取 Amsify42\TypeStruct\TypeStruct 的新实例。

/**
 * If we have direct of the typestruct file
 */
$typeStruct = get_typestruct('/path/to/Simple.php');
$result = $typeStruct->validate($data);
/**
 * For class, we need to pass full class name and 2nd param as 'class'
 */
$typeStruct = get_typestruct(App\TypeStruct\Simple::class, 'class');
$result = $typeStruct->validate($data);

自动加载

如果类型结构文件的名字和路径符合 psr-4 标准,则类型结构文件的自动加载将自动完成,否则您需要使用带有类型结构实例的 setPath() 方法,该方法期望直接路径的类型结构文件。

选项

使用类型结构实例,我们可以在调用 validate() 方法之前设置以下选项

/**
 * To tell the typestruct that data we are passing is of type object(stdClass)
 * default is false
 */
$typeStruct->isDataObject(true);
/**
 * If true, it will validate and collect all error messages else it will get the first error and exit
 * Default is true
 */
$typeStruct->validateFull(false);
/**
 * Default is empty string, you can either pass 'json' or 'xml' based on the type of data you are passing for validation.
 */
$typeStruct->contentType('json');
/**
 * Absolute path to the typestruct file
 */
$typeStruct->setPath('/path/to/Sample.php');
/**
 * Full class name of typestruct file
 */
$typeStruct->setClass(App\TypeStruct\Simple::class);

3. 数据

您可以传递以下数据进行验证

Array
Object(stdClass)
Json
XML

正如我们已经在数组示例中看到的那样,让我们看看其他示例

Object(stdClass)

$data        = new \stdClass();
$data->id    = 42;
$data->name  = 'amsify';
$data->price = 4.2;    

$typeStruct = new Amsify42\TypeStruct\TypeStruct();
$typeStruct->isDataObject(true)->setClass(App\TypeStruct\Simple::class);
$result = $typeStruct->validate($data);

注意: 我们将 true 传递给 isDataObject() 方法来告诉 TypeStruct 我们传递的数据是 Object(stdClass) 类型。

Json

$jsonData = '{"id":42,"name":"amsify","price":4.2}';
$typeStruct = new TypeStruct();
$typeStruct->contentType('json')->setClass(App\TypeStruct\Simple::class);
$result = $typeStruct->validate($jsonData);

XML

$xmlData = '<?xml version="1.0" encoding="UTF-8" ?> <root> <id>42</id> <name>amsify</name> <price>4.2</price> </root>';
$typeStruct = new TypeStruct();
$typeStruct->contentType('xml')->setClass(App\TypeStruct\Simple::class);
$result = $typeStruct->validate($xmlData);

注意: 我们调用 contentType() 方法来设置其类型,无论是 Json 还是 XML

4. 类验证

我们可以通过创建类并扩展它到 Amsify42\TypeStruct\Validator 来进行验证

<?php

namespace App\Validators;

use Amsify42\TypeStruct\Validator;

class Sample extends Validator
{
    protected $tsClass = \App\TypeStruct\Simple::class;

    protected $data = [
                        'id'    => 42,
                        'name'  => 'amsify',
                        'price' => 4.2
                    ];                 
}

由于我们已经设置了 TypeStruct 类名和 dataprotected 属性中。我们可以直接创建这个类的实例并验证

$sample = new \Amsify42\Validators\Sample();
$result = $sample->validate();

您也可以像这样在验证类型结构之前设置数据

$sample = new \Amsify42\Validators\Sample();
$sample->setData(['id' => 42, 'name' => 'amsify']);
$result = $sample->validate();

并且我们还可以使用这些扩展 Amsify42\TypeStruct\Validator 的受保护属性

/**
 * Instead of setting typestruct class name, we can also set direct path of that typestruct file
 */
protected $tsPath;
/**
 * This will decide whether validation will stop at first error itself or when completing all validation errors. Default is true
 */
protected $validateFull;
/**
 * You can set to json or xml, default is empty string
 */
protected $contentType;
/**
 * Tells the typestruct whether the data we setting/passing is of type Object(stdClass)
 */
protected $isDataObject;

5. 规则

基本

这些是我们可以用作元素的类型。它将检查键是否存在以及其类型。

export typestruct Sample {
    id: int,
    name: string,
    price: float,
    points: numeric,
    is_active: boolean,
    is_public: tinyInt,
    items: array
    some: any
}

numeric 类似于 PHP 的 is_numeric() 方法,允许数字甚至带有引号。 tinyInt 期望值为 01,而 any 意味着元素值可以是任何类型。

可选

要使元素可选,我们只需用问号 ? 预先加在其前面即可

export typestruct Sample {
    id: int,
    name: string,
    email: ?string
}

可选也可以应用于子字典

export typestruct Sample {
    id: int,
    name: string,
    email: ?string,
    details : ?{
        address: string,
        pincode: ?int
    }
}

长度

我们还可以设置这些类型的长度限制,如下所示

export typestruct Sample {
    id: int(5),
    name: string(20),
    price: float(5.2),
    is_active: boolean,
    items: [5]
}

数组

以下是我们可以使用的数组类型

items: int[] 
items: string[]
items: float[]
items: numeric[]
items: boolean[]
items: tinyInt[]

作为子元素的扩展

我们还可以使用其他外部 TypeStruct 文件作为元素

namespace App\TypeStruct;
export typestruct Category {
    id: int,
    name: string
}

现在我们可以这样使用 Category 作为类型

namespace App\TypeStruct;
use App\TypeStruct\Category;
export typestruct Product {
    id: int,
    name: string,
    price: float,
    active: boolean,
    category: Category
}

或者作为这种类型的数组

namespace App\TypeStruct;
use App\TypeStruct\Category;
export typestruct Product {
    id: int,
    name: string,
    price: float,
    active: boolean,
    categories: Category[]
}

更多规则

您还可以这样附加更多规则到输入上

namespace App\TypeStruct;
export typestruct User {
    id: int,
    name: string,
    email: string<email>
}

如您所见,我们已将规则 email 添加到电子邮件元素中,用于检查有效的电子邮件地址。您可以在元素中添加更多规则,并用点 . 分隔,如下所示

namespace App\TypeStruct;
export typestruct User {
    id: int,
    url: string<url.checkHost>
}

以下是您可以使用的预定义规则

nonEmpty - Check for non empty value just like php method empty() checks
email - Check for valid email
url - Check if string is a valid url
date - Check if string is a valid date

6. 自定义规则

我们还可以编写方法执行自定义验证,但只能在创建类并扩展到 Amsify42\TypeStruct\Validator 时实现

namespace App\TypeStruct;

export typestruct Simple {
    id: int,
    name: string<checkName>,
    price: float
}

现在我们可以在验证器类中编写 checkName 方法,如下所示

<?php

namespace App\Validators;

use Amsify42\TypeStruct\Validator;
use App\TypeStruct\Simple;

class Sample extends Validator
{
    protected $tsClass = Simple::class;

    protected $data = [
                        'id'    => 42,
                        'name'  => 'amsify',
                        'price' => 4.2
                    ];

    public function checkName()
    {
        if($this->value() !== 'amsify')
        {
            return 'Name should be amsify';
        }
        return true;
    }                                 
}

我们可以使用 $this->name() 来获取当前元素的名称,使用 $this->value() 来获取当前元素的值,该值适用于规则。要获取其他元素的值,我们已经可以从这些自定义规则方法中访问 $this->data
如果您想更轻松地访问自定义方法中的数据,您还可以使用 $this->path() 方法,它将直接从多级路径获取元素。

class Sample extends Validator
{
    ...
    protected $data = [
                        'id'    => 42,
                        'detail' => [
                            'more' => [
                                'location' => 'City'
                            ]
                        ]
                    ];

    public function checkCustom()
    {
        echo $this->path('detail.more.location'); /* It will print `City` */
    }                                 
} 

注意: $this->path 预期参数为点(如果多个键)分隔的键名,如果键不存在,则返回 NULL,或者返回目标键值。

7. 复杂示例

namespace App\TypeStruct;

use App\TypeStruct\User;

export typestruct Sample {
    name: string,
    email: string,
    is_test: tinyInt,
    id: int,
    address: {
        door: string,
        zip: int
    },
    items: [],
    user : User,
    someEl: {
        key1: string,
        key2: int,
        key12: array,
        records: \App\TypeStruct\Record[],
        someChild: {
            key3: boolean,
            key4: float,
            someAgainChild: {
                key5: string,
                key6: float,
                key56: boolean[]
            }
        }
    }
}
<?php

namespace App\TypeStruct;

export typestruct User {
    id: int,
    name: string,
    email: string<email>
}
<?php

namespace App\TypeStruct;

export typestruct Record {
    id: int,
    name: string
}

上述复杂的多级类型结构示例文件将使用以下数据进行验证

[
    'name' => 'amsify',
    'is_test' => '1',
    'user' => [
        'id' => 1,
        'name' => 'some',
        'email' => 'some@site.com'
    ],
    'address' => [
        'door' => '12-3-534',
        'zip' => 600035
    ],
    'url' => 'https://www.site.com/page.html',
    'items' => [1,2,3,4,5,6,7],
    'someEl' => [
        'key1' => 'val1',
        'key2' => 2,
        'key12' => [1,2,12],
        'records' => [
            [
                'id' => 1,
                'name' => 'r1'
            ],
            [
                'id' => 2,
                'name' => 'r2'
            ]
        ],
        'someChild' => [
            'key3' => true,
            'key4' => 4.01,
            'someAgainChild' => [
                'key5' => 'val5',
                'key6' => 6.4,
                'key56' => [true,false,true]
            ]
        ]
    ]
]