Changelog 2.0.0 — 27th of June 2025¶
First release of the v2 series! 🎉
This release introduces some new features but also backward compatibility breaks that are detailed in the upgrading chapter: it is strongly recommended to read it carefully before upgrading.
Notable new features¶
Mapper converters introduction
A mapper converter allows users to hook into the mapping process and apply custom logic to the input, by defining a callable signature that properly describes when it should be called:
- A first argument with a type matching the expected input being mapped
- A return type representing the targeted mapped type
These two types are enough for the library to know when to call the converter and can contain advanced type annotations for more specific use cases.
Below is a basic example of a converter that converts string inputs to uppercase:
(new \CuyZ\Valinor\MapperBuilder())
->registerConverter(
fn (string $value): string => strtoupper($value)
)
->mapper()
->map('string', 'hello world'); // 'HELLO WORLD'
Converters can be chained, allowing multiple transformations to be applied to a
value. A second callable parameter can be declared, allowing the current
converter to call the next one in the chain.
A priority can be given to a converter to control the order in which converters are applied. The higher the priority, the earlier the converter will be executed. The default priority is 0.
(new \CuyZ\Valinor\MapperBuilder())
->registerConverter(
function(string $value, callable $next): string {
return $next(strtoupper($value));
}
)
->registerConverter(
function(string $value, callable $next): string {
return $next($value . '!');
},
priority: -10,
)
->registerConverter(
function(string $value, callable $next): string {
return $next($value . '?');
},
priority: 10,
)
->mapper()
->map('string', 'hello world'); // 'HELLO WORLD?!'
More information can be found in the mapper converter chapter.
NormalizerBuilder introduction
The NormalizerBuilder class has been introduced and will now be the main entry
to instantiate normalizers. Therefore, the methods inMapperBuilder that used
to configure and return normalizers have been removed.
This decision aims to make a clear distinction between the mapper and the normalizer configuration API, where confusion could arise when using both.
The NormalizerBuilder can be used like this:
$normalizer = (new \CuyZ\Valinor\NormalizerBuilder())
->registerTransformer(
fn (\DateTimeInterface $date) => $date->format('Y/m/d')
)
->normalizer(\CuyZ\Valinor\Normalizer\Format::array())
->normalize($someData);
Changes to messages/errors handling
Some changes have been made to the way messages and errors are handled.
It is now easier to fetch messages when error(s) occur during mapping:
try {
(new \CuyZ\Valinor\MapperBuilder())->mapper()->map(/* … */);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
// Before (1.x):
$messages = \CuyZ\Valinor\Mapper\Tree\Message\Messages::flattenFromNode(
$error->node()
);
// After (2.x):
$messages = $error->messages();
}
Upgrading from 1.x to 2.x¶
⚠ BREAKING CHANGES¶
- Add purity markers in
MapperBuilderandNormalizerBuilder(123058) - Add type and source accessors to
MappingError(378141) - Change exposed error messages codes (15bb11)
- Introduce
NormalizerBuilderas the main entry for normalizers (f79ce2) - Introduce internal cache interface and remove PSR-16 dependency (dfdf40)
- Mark some class constructors as
@internal(7fe5fe) - Remove
MapperBuilder::alter()in favor of mapper converters (bee098) - Remove
MapperBuilder::enableFlexibleCasting()(f8f16d) - Remove unused class
PrioritizedList(0b8c89) - Remove unused interface
IdentifiableSource(aefb20) - Rename
MapperBuilder::warmup()method towarmupCacheFor()(963156) - Rework mapper node and messages handling (14d5ca)
Features¶
- Allow
MapperBuilderandNormalizerBuilderto clear cache (fe318c) - Introduce mapper converters to apply custom logic during mapping (46c823)
Bug Fixes¶
- Update file system cache entries permissions (6ffb0f)