跳至正文

更新日志 0.11.0 — 2022 年 6 月 23 日

重要变更

严格模式

映射器现在对类型更加敏感,并且在以下情况下会失败:

  • 当值与等待的标量类型不完全匹配时,例如给予等待整数的节点的字符串"42" 。

  • 当存在不必要的数组键时,例如将数组['foo' => …, 'bar' => …, 'baz' => …]映射到仅需要foo和bar对象。

  • 当遇到像mixed或object这样的宽容类型时。

通过启用灵活模式可以绕过这些限制:

(new \CuyZ\Valinor\MapperBuilder())
    ->flexible()
    ->mapper();
    ->map('array{foo: int, bar: bool}', [
        'foo' => '42', // Will be cast from `string` to `int`
        'bar' => 'true', // Will be cast from `string` to `bool`
        'baz' => '…', // Will be ignored
    ]);

当将此库用于提供程序应用程序时(例如可以使用 JSON 负载调用的 API 端点),建议使用严格模式。这可确保 API 的使用者提供准确的等待数据结构,并防止传递未知值。

当将此库用作外部源的使用者时,启用灵活模式是有意义的。例如,这允许将字符串数值转换为整数或忽略源中存在但应用程序中不需要的数据。

接口推断

现在必须列出映射器可以推断的所有可能的类类型。这一变化是库能够提供强大的新功能(例如编译映射器以获得更好的性能)的一步。

对MapperBuilder::infer的现有调用可能返回多个类名,现在必须向回调添加签名。不需要参数且始终返回相同类名的回调可以保持不变。

例如:

$builder = (new \CuyZ\Valinor\MapperBuilder())
    // Can remain unchanged
    ->infer(SomeInterface::class, fn () => SomeImplementation::class);
$builder = (new \CuyZ\Valinor\MapperBuilder())
    ->infer(
        SomeInterface::class,
        fn (string $type) => match($type) {
            'first' => ImplementationA::class,
            'second' => ImplementationB::class,
            default => throw new DomainException("Unhandled `$type`.")
        }
    )
    // …should be modified with:
    ->infer(
        SomeInterface::class,
        /** @return class-string<ImplementationA|ImplementationB> */
        fn (string $type) => match($type) {
            'first' => ImplementationA::class,
            'second' => ImplementationB::class,
            default => throw new DomainException("Unhandled `$type`.")
        }
    );

对象构造函数碰撞

所有这些更改都会导致对所有已注册的对象构造函数运行新的检查。如果发现多个具有相同签名(相同参数名称)的构造函数之间发生冲突,则会抛出异常。

final class SomeClass
{
    public static function constructorA(string $foo, string $bar): self
    {
        // …
    }

    public static function constructorB(string $foo, string $bar): self
    {
        // …
    }
}

(new \CuyZ\Valinor\MapperBuilder())
    ->registerConstructor(
        SomeClass::constructorA(...),
        SomeClass::constructorB(...),
    )
    ->mapper();
    ->map(SomeClass::class, [
        'foo' => 'foo',
        'bar' => 'bar',
    ]);

// Exception: A collision was detected […]

⚠ 重大变更

  • 处理接口推断的详尽列表( 1b0ff3 )
  • 使映射器更加严格并允许灵活模式( 90dc58 )

新功能

  • 改进缓存预热 ( 44c5f1 )