Changelog 2.1.0 — 23rd of July 2025¶
Notable changes¶
Attribute converters
Info
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)