Notable changes
Attribute converters
Note
Fetch common examples of mapping converters in the documentation.
Callable converters allow targeting any value during mapping, whereas attribute converters allow targeting a specific class or property for a more granular control.
To be detected by the mapper, an attribute class must be registered first by adding the AsConverter
attribute to it.
Attributes must declare a method named map
that follows the same rules as callable converters: a mandatory first parameter and an optional second callable
parameter.
Below is an example of an attribute converter that converts string inputs to boolean values based on specific string inputs:
namespace My\App;
#[\CuyZ\Valinor\Mapper\AsConverter]
#[\Attribute(\Attribute::TARGET_PROPERTY)]
final class CastToBool
{
/**
* @param callable(mixed): bool $next
*/
public function map(string $value, callable $next): bool
{
$value = match ($value) {
'yes', 'on' => true,
'no', 'off' => false,
default => $value,
};
return $next($value);
}
}
final class User
{
public string $name;
#[\My\App\CastToBool]
public bool $isActive;
}
$user = (new \CuyZ\Valinor\MapperBuilder())
->mapper()
->map(User::class, [
'name' => 'John Doe',
'isActive' => 'yes',
]);
$user->name === 'John Doe';
$user->isActive === true;
Attribute converters can also be used on function parameters when mapping arguments:
function someFunction(string $name, #[\My\App\CastToBool] bool $isActive) {
// …
};
$arguments = (new \CuyZ\Valinor\MapperBuilder())
->argumentsMapper()
->mapArguments(someFunction(...), [
'name' => 'John Doe',
'isActive' => 'yes',
]);
$arguments['name'] === 'John Doe';
$arguments['isActive'] === true;
When there is no control over the converter attribute class, it is possible to register it using the registerConverter
method.
(new \CuyZ\Valinor\MapperBuilder())
->registerConverter(\Some\External\ConverterAttribute::class)
->mapper()
->map(…);
It is also possible to register attributes that share a common interface by giving the interface name to the registration method.
namespace My\App;
interface SomeAttributeInterface {}
#[\Attribute]
final class SomeAttribute implements \My\App\SomeAttributeInterface {}
#[\Attribute]
final class SomeOtherAttribute implements \My\App\SomeAttributeInterface {}
(new \CuyZ\Valinor\MapperBuilder())
// Registers both `SomeAttribute` and `SomeOtherAttribute` attributes
->registerConverter(\My\App\SomeAttributeInterface::class)
->mapper()
->map(…);
Features
- Introduce attribute converters for granular control during mapping (0a8c0d)
Bug Fixes
- Properly detect invalid values returned by mapping converters (e80de7)
- Properly extract
=
token when reading types (9a511d) - Use polyfill for
array_find
(540741)
Other
- Mark exception as
@internal
(f3eace)