Open
Conversation
cfd5457 to
23ffe46
Compare
049200b to
e53d51a
Compare
8e42656 to
2c5cf92
Compare
2c5cf92 to
9b7c208
Compare
f95a62e to
6ecd49e
Compare
2f9c09f to
ac5dea8
Compare
798d51d to
453ef0c
Compare
453ef0c to
fdf704f
Compare
4baf1cc to
0a95f23
Compare
7f9d051 to
12f4be5
Compare
d4a4c2e to
01135c8
Compare
e4d9fc4 to
95c7fed
Compare
8b37747 to
f05538e
Compare
| datasource | package | from | to | | ---------- | ------------ | ------ | ----- | | packagist | cuyz/valinor | 1.17.0 | 2.4.0 |
f05538e to
498eea7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
1.17.0→2.4.0Release Notes
CuyZ/Valinor (cuyz/valinor)
v2.4.0Compare Source
Notable changes
This release brings a whole set of new features to the library:
Enjoy! 🎉
HTTP request mapping support
This library now provides a way to map an HTTP request to controller action parameters or object properties. Parameters can be mapped from route, query and body values.
Three attributes are available to explicitly bind a parameter to a single source, ensuring the value is never resolved from the wrong source:
#[FromRoute]— for parameters extracted from the URL path by router#[FromQuery]— for query string parameters#[FromBody]— for request body valuesThose attributes can be omitted entirely if the parameter is not bound to a specific source, in which case a collision error is raised if the same key is found in more than one source.
This gives controllers a clean, type-safe signature without coupling to a framework's request object, while benefiting from the library's validation and error handling.
Normal mapping rules apply there: parameters are required unless they have a default value.
Route and query parameter values coming from an HTTP request are typically strings. The mapper automatically handles scalar value casting for these parameters: a string
"42"will be properly mapped to anintparameter.Mapping a request using attributes
Consider an API that lists articles for a given author. The author identifier comes from the URL path, while filtering and pagination come from the query string.
Mapping a request without using attributes
When it is unnecessary to distinguish which source a parameter comes from, the attribute can be omitted entirely — the mapper will resolve each parameter from whichever source contains the matching key.
Mapping all parameters at once
Instead of mapping individual query parameters or body values to separate parameters, the
asRootoption can be used to map all of them at once to a single parameter. This is useful when working with complex data structures or when the number of parameters is large.The same approach works with
#[FromBody(asRoot: true)]for body values.Mapping to an object
Instead of mapping to a callable's arguments, an
HttpRequestcan be mapped directly to an object. The attributes work the same way on constructor parameters or promoted properties.Using PSR-7 requests
An
HttpRequestinstance can be built directly from a PSR-7ServerRequestInterface. This is the recommended approach when integrating with frameworks that use PSR-7.The factory method extracts query parameters from
getQueryParams()and body values fromgetParsedBody(). It also passes the original PSR-7 request object through, so it can be injected into controller parameters if needed (see below).Accessing the original request object
When building an
HttpRequest, an original request object can be provided. If a controller parameter's type matches this object, it will be injected automatically; no attribute is needed.Error handling
When the mapping fails — for instance because a required query parameter is missing or a body value has the wrong type — a
MappingErroris thrown, just like with regular mapping.Read the validation and error handling chapter for more information.
Mapper/Normalizer configurators support
Introduce
MapperBuilderConfiguratorandNormalizerBuilderConfiguratorinterfaces along with aconfigureWith()method on both builders.A configurator is a reusable piece of configuration logic that can be applied to a
MapperBuilderor aNormalizerBuilderinstance. This is useful when the same configuration needs to be applied in multiple places across an application, or when configuration logic needs to be distributed as a package.In the example below, we apply two configuration settings to a
MapperBuilderinside a single class, but this could contain any number of customizations, depending on the needs of the application.This configurator can be registered within the
MapperBuilderinstance:Composing multiple configurators
Multiple configurators can be combined to compose the final configuration. Each configurator is applied in order, allowing layered and modular configuration.
This approach keeps each configurator focused on a single concern, making them easier to test and reuse independently.
Using
NormalizerBuilderConfiguratorThe same configurator logic can be applied on
NormalizerBuilder:CamelCase/snake_case keys conversion support
Two configurators are available to convert the keys of input data before mapping them to object properties or shaped array keys. This allows accepting data with a different naming convention than the one used in the PHP codebase.
ConvertKeysToCamelCasefirst_name→firstNameFirstName→firstNamefirst-name→firstNameConvertKeysToSnakeCasefirstName→first_nameFirstName→first_namefirst-name→first_nameThis configurator can be combined with a key restriction configurator to both validate and convert keys in a single step. The restriction configurator must be registered before the conversion so that the validation runs on the original input keys.
Keys case restriction support
Four configurators restrict which key case is accepted when mapping input data to objects or shaped arrays. If a key does not match the expected case, a mapping error will be raised.
This is useful, for instance, to enforce a consistent naming convention across an API's input to ensure that a JSON payload only contains
camelCase,snake_case,PascalCaseorkebab-casekeys.Available configurators:
new RestrictKeysToCamelCase()firstNamenew RestrictKeysToPascalCase()FirstNamenew RestrictKeysToSnakeCase()first_namenew RestrictKeysToKebabCase()first-nameFeatures
Bug Fixes
FileWatchingCache(d445e4)Internal
Deps
v2.3.2Compare Source
Notable changes
End of PHP 8.1 support
PHP 8.1 security support has ended on the 31st of December 2025.
See: https://www.php.net/supported-versions.php
Removal of
composer-runtime-apipackage dependencyUsing the
composer-runtime-apilibrary leads to unnecessary IO everytime the library is used; therefore, we prefer to use a basic constant that contains the package version.This change slightly increases performance and makes the package completely dependency free. 🎉
Bug Fixes
Cache
Internal
composer-runtime-apirequirement by PHP constant usage (8152be)Other
v2.3.1Compare Source
Bug Fixes
v2.3.0Compare Source
Notable new features
PHP 8.5 support 🐘
Enjoy the upcoming PHP 8.5 version before it is even officially released!
Performance improvements
The awesome @staabm has identified some performance bottlenecks in the codebase, leading to changes that improved the execution time of the mapper by ~50% in his case (and probably some of yours)!
Incoming HTTP request mapping
There is an ongoing discussion to add support for HTTP request mapping, if that's something you're interested in, please join the discussion!
Features
Other
Internal
ShapedArrayType::toString()(4fcfb6)v2.2.2Compare Source
Bug Fixes
v2.2.1Compare Source
This release contains a lot of internal refactorings that were needed to fix an important bug regarding converters. Although we made our best to provide a stable release, bugs can have slipped through the cracks. If that's the case, please open an issue describing the issue and we will try to fix it as soon as possible.
The commit d9e3cf0 is the result of a long journey whose goal was to fix a very upsetting bug that would make mapper converters being called when they shouldn't be. This could result in unexpected behaviors and could even lead to invalid data being mapped.
Take the following example below:
We register a converter that will return null if the string length is lower than 5. For this converter to be called, the target type should match the
string|nulltype, because that is what the converter can return.In this example, we want to map a value to
string, which is not matched by the converter return type because it does not containnull. This means that the converter should never be called, because it could return an invalid value (nullwill never be a validstring).Before this commit, the converter would be called and return
null, which would raise an unexpected error:This error was caused by the following line:
It should have been:
Easy fix, isn't it?
Well… actually no. Because changing this completely modifies the behavior of the converters, and the library is now missing a lot of information to properly infer the return type of the converter.
In some cases this change was enough, but in some more complex cases we now would need more information.
For instance, let's take the
CamelCaseKeysexample as it was written in the documentation before this commit:There is a big issue in the types signature of this converter: the
objectreturn type means that the converter can return anything, as long as this is an object. This breaks the type matching contract and the converter should never be called. But it was.This is the new way of writing this converter:
Now, the type matching contract is respected because of the
@templateannotation, and the converter is called when mapping to any object.To be able to properly infer the return type of the converter, we needed to:
@templateannotations inside functionsThis was a huge amount of work, which required several small changes during the last month, as well as b7f3e5f and d9e3cf0. A lot of work for an error in a single line of code, right? T_T
The good news is: the library is now more powerful than ever, as it is now able to statically infer generic types, which could bring new possibilities in the future.
Now the bad news is: this commit can break backwards compatibility promise in some cases. But as this is still a (huge) bug fix, we will not release a new major version, although it can break some existing code. Instead, converters should be adapted to use proper type signatures.
To help with that, here are the list of the diff that should be applied to converter examples that were written in the documentation:
CamelCaseKeys
RenameKeys
Explode
ArrayToList
JsonDecode
Bug Fixes
Internal
INFconstant to detect default converter value (72079b)Other
callabletype parsing (2563a3)v2.2.0Compare Source
Notable new features
Mapping error messages improvements
Feedback has been improved in mapping error messages, especially the expected signature of the failing nodes.
This gets rid of the infamous
?that was used whenever an object was present in a type, leading to incomplete and misleading messages.Example of a new message:
Features
Bug Fixes
non-empty-list(9739cd)Other
iterabletype the same way it is done witharray(6291a7)unknown(c8ef49)v2.1.2Compare Source
Bug Fixes
Other
@pureannotations (c3871f)v2.1.1Compare Source
Bug Fixes
v2.1.0Compare Source
Notable changes
Attribute converters
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
AsConverterattribute to it.Attributes must declare a method named
mapthat follows the same rules as callable converters: a mandatory first parameter and an optional secondcallableparameter.Below is an example of an attribute converter that converts string inputs to boolean values based on specific string inputs:
Attribute converters can also be used on function parameters when mapping arguments:
When there is no control over the converter attribute class, it is possible to register it using the
registerConvertermethod.It is also possible to register attributes that share a common interface by giving the interface name to the registration method.
Features
Bug Fixes
=token when reading types (9a511d)array_find(540741)Other
@internal(f3eace)v2.0.0Compare Source
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:
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:
Converters can be chained, allowing multiple transformations to be applied to a value. A second
callableparameter 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.
More information can be found in the mapper converter chapter.
NormalizerBuilderintroductionThe
NormalizerBuilderclass has been introduced and will now be the main entry to instantiate normalizers. Therefore, the methods inMapperBuilderthat 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
NormalizerBuildercan be used like this: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:
Upgrading from 1.x to 2.x
See the upgrading chapter.
⚠ BREAKING CHANGES
MapperBuilderandNormalizerBuilder(123058)MappingError(378141)NormalizerBuilderas the main entry for normalizers (f79ce2)@internal(7fe5fe)MapperBuilder::alter()in favor of mapper converters (bee098)MapperBuilder::enableFlexibleCasting()(f8f16d)PrioritizedList(0b8c89)IdentifiableSource(aefb20)MapperBuilder::warmup()method towarmupCacheFor()(963156)Features
MapperBuilderandNormalizerBuilderto clear cache (fe318c)Bug Fixes
Other
Throwableinheritance fromErrorMessage(dbd731)Configuration
📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.