facile-it/php-codec

PHP中对io-ts的部分移植

0.0.3 2022-01-16 17:51 UTC

README

PHP-codec是io-ts在PHP中的部分移植。

Minimum PHP Version

CI Static analysis codecov Join the chat at https://gitter.im/php-codec/community

现在就安装它。它只需要PHP >= 7.4。

composer require facile-it/php-codec

免责声明

该项目正在积极开发中:它是不稳定的,且文档记录不完整。API可能会多次更改,并且不久将准备好用于生产。

该项目遵循语义版本控制

简介

该项目是将出色的io-ts库部分移植到TypeScript的项目。所有内容都围绕着解码器、编码器和编解码器的概念。

解码器可以将值从一种类型转换为另一种类型。这种转换可能会失败。

use Facile\PhpCodec\Validation\Validation;

/**
 * @psalm-template I
 * @psalm-template A
 */
interface Decoder {
     /**
     * @psalm-param I $i
     * @psalm-return Validation<A>
     */
    public function decode($i): Validation;
    
    /** ... */
}

编码器执行类似的转换,但类型之间不能失败。

/**
 * @psalm-template A
 * @psalm-template O
 */
interface Encoder
{
    /**
     * @psalm-param A $a
     * @psalm-return O
     */
    public function encode($a);
}

编解码器是解码器和编码器的组合,结合了它们的功能。

我建议阅读io-ts文档中的理念部分。它从一个关于编解码器的美丽描述开始。

Type<A, O, I>类型的值(称为"编解码器")是静态类型A的运行时表示。

入门指南

composer require facile-it/php-codec

解码器

解码器是具有解码能力的对象。类型为Decoder<I, A>的解码器接收类型为I的输入并构建类型为Validation<A>的结果。

Facile\PhpCodec\Decoders提供了内置解码器和组合器的工厂方法。

如何使用解码器

use Facile\PhpCodec\Decoders;
use Facile\PhpCodec\Decoder;
use Facile\PhpCodec\Validation\Validation;
use Facile\PhpCodec\Validation\ValidationFailures;
use Facile\PhpCodec\Validation\ValidationSuccess;

/** @var Decoder<string, int> $decoder */
$decoder = Decoders::intFromString();

/** @var Validation<int> $v1 */
$v1 = $decoder->decode('123');
// Since '123' is a numeric string which represents an integer,
// then we can expect the decoding to be successful.
// Hence, $v1 will be an instance of ValidationSuccess

if($v1 instanceof ValidationSuccess) {
    var_dump($v1->getValue());
}

/** @var Validation<int> $v2 */
$v2 = $decoder->decode('hello');
// Similarly, since 'hello' is not a numeric string, we expect 
// the decoding fail. $v2 will be an instance of ValidationError

if($v2 instanceof ValidationFailures) {
    var_dump($v2->getErrors());
}

处理验证结果

我们可以使用Validation::fold在任何情况下提供有效结果的同时解构验证结果。

use Facile\PhpCodec\Decoders;
use Facile\PhpCodec\Decoder;
use Facile\PhpCodec\Validation\Validation;

/** @var Decoder<string, int> $decoder */
$decoder = Decoders::intFromString();

Validation::fold(
    function (\Facile\PhpCodec\Validation\ValidationFailures $failures): int {
        // I may not care about the error.
        // Here I want to give a default value when the deconding fails.
        return 0;
    },
    function (\Facile\PhpCodec\Validation\ValidationSuccess $success): int {
        return $success->getValue();
    },
    $decoder->decode($input)
);

您可以使用路径报告器为失败构建格式良好的错误消息。

use Facile\PhpCodec\Decoders;

$decoder = Decoders::intFromString();
$v = $decoder->decode('hello');
$msgs = \Facile\PhpCodec\Reporters::path()->report($v);

var_dump($msgs);
/* This will print 
array(1) {
  [0] =>
  string(49) "Invalid value "hello" supplied to : IntFromString"
}
*/

示例

查看示例文件夹。

报告器

报告器从Validation对象创建报告。一般来说,报告器是实现Reporter<T>接口的对象,其中T是生成的报告的类型。

一组有趣的报告器是验证错误报告器组。它们实现了Reporter<list<string>>。因此,给定的一个Validation对象,它们为每个验证错误生成一个错误消息列表。

PHP-Codec附带两个错误报告器

  • PathReporter,它是io-ts的PathReporter的直接移植。
  • SimplePathReporter,它是PathReporter的简化版(即:消息更短)。
$d = Decoders::arrayProps([
  'a' => Decoders::arrayProps([
    'a1' => Decoders::int(),
    'a2' => Decoders::string(),
  ]),
  'b' => Decoders::arrayProps(['b1' => Decoders::bool()])
]);
$v = $d->decode(['a'=> ['a1' => 'str', 'a2' => 1], 'b' => 2]);

$x = \Facile\PhpCodec\Reporters::path()->report($v);
// $x will be
// ['Invalid value "str" supplied to : {a: {a1: int, a2: string}, b: {b1: bool}}/a: {a1: int, a2: string}/a1: int',
//  'Invalid value 1 supplied to : {a: {a1: int, a2: string}, b: {b1: bool}}/a: {a1: int, a2: string}/a2: string',
//  'Invalid value undefined supplied to : {a: {a1: int, a2: string}, b: {b1: bool}}/b: {b1: bool}/b1: bool']

$y = \Facile\PhpCodec\Reporters::simplePath()->report($v);
// $y will be
// ['/a/a1: Invalid value "str" supplied to decoder "int"',
//  '/a/a2: Invalid value 1 supplied to decoder "string"',
//  '/b/b1: Invalid value undefined supplied to decoder "bool"']