扩展规范化器¶
该库提供了一个开箱即用的规范化器,可以按原样使用,也可以扩展以添加自定义逻辑。为此,必须在NormalizerBuilder中注册变换器。
变换器可以是可调用的(函数、闭包或实现__invoke()方法的类),也可以是可以用于类或属性的特性。
可调用变换器¶
可调用变换器必须声明至少一个参数,该参数的类型将确定在规范化期间何时使用它。在下面的示例中,全局变换器用于格式化规范化器找到的任何日期。
(new \CuyZ\Valinor\NormalizerBuilder())
->registerTransformer(
fn (\DateTimeInterface $date) => $date->format('Y/m/d')
)
->normalizer(\CuyZ\Valinor\Normalizer\Format::array())
->normalize(
new \My\App\Event(
eventName: 'Release of legendary album',
date: new \DateTimeImmutable('1971-11-08'),
)
);
// [
// 'eventName' => 'Release of legendary album',
// 'date' => '1971/11/08',
// ]
变换器可以串联起来。为此,必须在变换器中声明callable类型的第二个参数。这个参数(按照惯例命名为$next )可以在变换器逻辑中任何需要的时候使用。
(new \CuyZ\Valinor\NormalizerBuilder())
// The type of the first parameter of the transformer will determine when it
// is used during normalization.
->registerTransformer(
fn (string $value, callable $next) => strtoupper($next())
)
// Transformers can be chained, the last registered one will take precedence
// over the previous ones, which can be called using the `$next` parameter.
->registerTransformer(
/**
* Advanced type annotations like `non-empty-string` can be used to
* target a more specific type.
*
* @param non-empty-string $value
*/
fn (string $value, callable $next) => $next() . '!'
)
// A priority can be given to a transformer, to make sure it is called
// before or after another one. The higher priority, the sooner the
// transformer will be called. The default priority is 0.
->registerTransformer(
/**
* @param non-empty-string $value
*/
fn (string $value, callable $next) => $next() . '?',
priority: 100
)
->normalizer(\CuyZ\Valinor\Normalizer\Format::array())
->normalize('Hello world'); // HELLO WORLD!?
特性变换器¶
可调用变换器允许在规范化期间定位任何值,而属性变换器允许定位特定类或属性以进行更精细的控制。
为了被规范化器检测到,必须首先通过添加AsTransformer特性来注册特性类。
特性必须声明一个名为normalize方法,该方法遵循与可调用变换器相同的规则:强制的第一个参数和可选的第二个callable参数。
namespace My\App;
#[\CuyZ\Valinor\Normalizer\AsTransformer]
#[\Attribute(\Attribute::TARGET_PROPERTY)]
final class Uppercase
{
public function normalize(string $value, callable $next): string
{
return strtoupper($next());
}
}
final readonly class City
{
public function __construct(
public string $zipCode,
#[\My\App\Uppercase]
public string $name,
#[\My\App\Uppercase]
public string $country,
) {}
}
(new \CuyZ\Valinor\NormalizerBuilder())
->normalizer(\CuyZ\Valinor\Normalizer\Format::array())
->normalize(
new \My\App\City(
zipCode: 'NW1 6XE',
name: 'London',
country: 'United Kingdom',
)
);
// [
// 'zipCode' => 'NW1 6XE',
// 'name' => 'LONDON',
// 'country' => 'UNITED KINGDOM',
// ]
如果特性需要转换属性的键,则需要声明一个名为normalizeKey的方法。
namespace My\App;
#[\CuyZ\Valinor\Normalizer\AsTransformer]
#[\Attribute(\Attribute::TARGET_PROPERTY)]
final class PrefixedWith
{
public function __construct(private string $prefix) {}
public function normalizeKey(string $value): string
{
return $this->prefix . $value;
}
}
final readonly class Address
{
public function __construct(
#[\My\App\PrefixedWith('address_')]
public string $road,
#[\My\App\PrefixedWith('address_')]
public string $zipCode,
#[\My\App\PrefixedWith('address_')]
public string $city,
) {}
}
(new \CuyZ\Valinor\NormalizerBuilder())
->normalizer(\CuyZ\Valinor\Normalizer\Format::array())
->normalize(
new \My\App\Address(
road: '221B Baker Street',
zipCode: 'NW1 6XE',
city: 'London',
)
);
// [
// 'address_road' => '221B Baker Street',
// 'address_zipCode' => 'NW1 6XE',
// 'address_city' => 'London',
// ]
当无法控制变换器特性类时,可以使用registerTransformer方法来注册它。
(new \CuyZ\Valinor\NormalizerBuilder())
->registerTransformer(\Some\External\TransformerAttribute::class)
->normalizer(\CuyZ\Valinor\Normalizer\Format::array())
->normalize(…);
还可以通过向注册方法提供接口名称来注册共享公共接口的特性。
namespace My\App;
interface SomeAttributeInterface {}
#[\Attribute]
final class SomeAttribute implements \My\App\SomeAttributeInterface {}
#[\Attribute]
final class SomeOtherAttribute implements \My\App\SomeAttributeInterface {}
(new \CuyZ\Valinor\NormalizerBuilder())
// Registers both `SomeAttribute` and `SomeOtherAttribute` attributes
->registerTransformer(\My\App\SomeAttributeInterface::class)
->normalizer(\CuyZ\Valinor\Normalizer\Format::array())
->normalize(…);