Skip to content

Releases: CuyZ/Valinor

0.8.0

09 May 19:35
Compare
Choose a tag to compare

Notable changes

Float values handling

Allows the usage of float values, as follows:

class Foo
{
    /** @var 404.42|1337.42 */
    public readonly float $value;
}

Literal boolean true / false values handling

Thanks @danog for this feature!

Allows the usage of boolean values, as follows:

class Foo
{
    /** @var int|false */
    public readonly int|bool $value;
}

Class string of union of object handling

Allows to declare several class names in a class-string:

class Foo
{
    /** @var class-string<SomeClass|SomeOtherClass> */
    public readonly string $className;
}

Allow psalm and phpstan prefix in docblocks

Thanks @boesing for this feature!

The following annotations are now properly handled: @psalm-param, @phpstan-param, @psalm-return and @phpstan-return.

If one of those is found along with a basic @param or @return annotation, it will take precedence over the basic value.

Full list of changes

Features

  • Allow psalm and phpstan prefix in docblocks (64e0a2)
  • Handle class string of union of object (b7923b)
  • Handle filename in function definition (0b042b)
  • Handle float value type (790df8)
  • Handle literal boolean true / false types (afcedf)
  • Introduce composite types (892f38)

Bug Fixes

  • Call value altering function only if value is accepted (2f08e1)
  • Handle function definition cache invalidation when file is modified (511a0d)

Other

  • Add configuration for Composer allowed plugins (2f310c)
  • Add Psalm configuration file to .gitattributes (979272)
  • Bump dev-dependencies (844384)
  • Declare code type in docblocks (03c84a)
  • Ignore Polyfill coverage (c08fe5)
  • Remove symfony/polyfill-php80 dependency (368737)

0.7.0

24 Mar 13:41
Compare
Choose a tag to compare

Notable changes

Warning This release introduces a major breaking change that must be considered before updating

Constructor registration

The automatic named constructor discovery has been disabled. It is now mandatory to explicitly register custom constructors that can be used by the mapper.

This decision was made because of a security issue reported by @Ocramius and described in advisory GHSA-xhr8-mpwq-2rr2.

As a result, existing code must list all named constructors that were previously automatically used by the mapper, and registerer them using the method MapperBuilder::registerConstructor().

The method MapperBuilder::bind() has been deprecated in favor of the method above that should be used instead.

final class SomeClass
{
    public static function namedConstructor(string $foo): self
    {
        // …
    }
}

(new \CuyZ\Valinor\MapperBuilder())
    ->registerConstructor(
        SomeClass::namedConstructor(...),
        // …or for PHP < 8.1:
        [SomeClass::class, 'namedConstructor'],
    )
    ->mapper()
    ->map(SomeClass::class, [
        // …
    ]);

See documentation for more information.


Source builder

The Source class is a new entry point for sources that are not plain array or iterable. It allows accessing other features like camel-case keys or custom paths mapping in a convenient way.

It should be used as follows:

$source = \CuyZ\Valinor\Mapper\Source\Source::json($jsonString)
    ->camelCaseKeys()
    ->map([
        'towns' => 'cities',
        'towns.*.label' => 'name',
    ]);

$result = (new \CuyZ\Valinor\MapperBuilder())
    ->mapper()
    ->map(SomeClass::class, $source);

See documentation for more details about its usage.

Full list of changes

⚠ BREAKING CHANGES

  • Change Attributes::ofType return type to array (1a599b)
  • Introduce method to register constructors used during mapping (ecafba)

Features

  • Introduce a path-mapping source modifier (b7a7d2)
  • Introduce a source builder (ad5103)

Bug Fixes

  • Handle numeric key with camel case source key modifier (b8a18f)
  • Handle parameter default object value compilation (fdef93)
  • Handle variadic arguments in callable constructors (b646cc)
  • Properly handle alias types for function reflection (e5b515)

Other

  • Add Striker HTML report when running infection (79c7a4)
  • Handle class name in function definition (e2451d)
  • Introduce functions container to wrap definition handling (fd1117)

0.6.0

24 Feb 13:37
Compare
Choose a tag to compare

⚠ BREAKING CHANGES

  • Improve interface inferring API (1eb6e6)
  • Improve object binding API (6d4270)

Features

  • Handle variadic parameters in constructors (b6b329)
  • Improve value altering API (422e6a)
  • Introduce a camel case source key modifier (d94652)
  • Introduce function definition repository (b49ebf)
  • Introduce method to get parameter by index (380961)

Bug Fixes

  • Change license in composer.json (6fdd62)
  • Ensure native mixed types remain valid (18ccbe)
  • Remove string keys when unpacking variadic parameter values (cbf4e1)
  • Transform exception thrown during object binding into a message (359e32)
  • Write temporary cache file inside cache subdirectory (1b80a1)

Other

  • Check value acceptance in separate node builder (30d447)
  • Narrow union types during node build (06e9de)

0.5.0

26 Jan 19:00
Compare
Choose a tag to compare

Features

  • Introduce automatic named constructor resolution (718d3c)
  • Set up dependabot for automated weekly dependency upgrades (23b611)
  • Simplify type signature of TreeMapper#map() (e28003)

Bug Fixes

  • Correct regex that detects @internal or @api annotations (39f0b7)
  • Improve type definitions to allow Psalm automatic inferring (f9b04c)
  • Return indexed list of attributes when filtering on type (66aa4d)

0.4.0

07 Jan 12:59
Compare
Choose a tag to compare

Notable changes

Allow mapping to any type

Previously, the method TreeMapper::map would allow mapping only to an object. It is now possible to map to any type handled by the library.

It is for instance possible to map to an array of objects:

$objects = (new MapperBuilder())->mapper()->map(
    'array<' . SomeClass::class . '>',
    [/* … */]
);

For simple use-cases, an array shape can be used:

$array = (new MapperBuilder())->mapper()->map(
    'array{foo: string, bar: int}',
    [/* … */]
);

echo $array['foo'];
echo $array['bar'] * 2;

This new feature changes the possible behaviour of the mapper, meaning static analysis tools need help to understand the types correctly. An extension for PHPStan and a plugin for Psalm are now provided and can be included in a project to automatically increase the type coverage.


Better handling of messages

When working with messages, it can sometimes be useful to customize the content of a message — for instance to translate it.

The helper class \CuyZ\Valinor\Mapper\Tree\Message\Formatter\MessageMapFormatter can be used to provide a list of new formats. It can be instantiated with an array where each key represents either:

  • The code of the message to be replaced
  • The content of the message to be replaced
  • The class name of the message to be replaced

If none of those is found, the content of the message will stay unchanged unless a default one is given to the class.

If one of these keys is found, the array entry will be used to replace the content of the message. This entry can be either a plain text or a callable that takes the message as a parameter and returns a string; it is for instance advised to use a callable in cases where a translation service is used — to avoid useless greedy operations.

In any case, the content can contain placeholders that will automatically be replaced by, in order:

  1. The original code of the message
  2. The original content of the message
  3. A string representation of the node type
  4. The name of the node
  5. The path of the node
try {
    (new \CuyZ\Valinor\MapperBuilder())
        ->mapper()
        ->map(SomeClass::class, [/* … */]);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
    $node = $error->node();
    $messages = new \CuyZ\Valinor\Mapper\Tree\Message\MessagesFlattener($node);

    $formatter = (new MessageMapFormatter([
        // Will match if the given message has this exact code
        'some_code' => 'new content / previous code was: %1$s',
    
        // Will match if the given message has this exact content
        'Some message content' => 'new content / previous message: %2$s',
    
        // Will match if the given message is an instance of `SomeError`
        SomeError::class => '
            - Original code of the message: %1$s
            - Original content of the message: %2$s
            - Node type: %3$s
            - Node name: %4$s
            - Node path: %5$s
        ',
    
        // A callback can be used to get access to the message instance
        OtherError::class => function (NodeMessage $message): string {
            if ((string)$message->type() === 'string|int') {
                // …
            }
    
            return 'Some message content';
        },
    
        // For greedy operation, it is advised to use a lazy-callback
        'bar' => fn () => $this->translator->translate('foo.bar'),
    ]))
        ->defaultsTo('some default message')
        // …or…
        ->defaultsTo(fn () => $this->translator->translate('default_message'));

    foreach ($messages as $message) {
        echo $formatter->format($message);    
    }
}

Automatic union of objects inferring during mapping

When the mapper needs to map a source to a union of objects, it will try to guess which object it will map to, based on the needed arguments of the objects, and the values contained in the source.

final class UnionOfObjects
{
    public readonly SomeFooObject|SomeBarObject $object;
}

final class SomeFooObject
{
    public readonly string $foo;
}

final class SomeBarObject
{
    public readonly string $bar;
}

// Will map to an instance of `SomeFooObject`
(new \CuyZ\Valinor\MapperBuilder())
    ->mapper()
    ->map(UnionOfObjects::class, ['foo' => 'foo']);

// Will map to an instance of `SomeBarObject`
(new \CuyZ\Valinor\MapperBuilder())
    ->mapper()
    ->map(UnionOfObjects::class, ['bar' => 'bar']);

Full list of changes

⚠ BREAKING CHANGES

  • Add access to root node when error occurs during mapping (54f608)
  • Allow mapping to any type (b2e810)
  • Allow object builder to yield arguments without source (8a7414)
  • Wrap node messages in proper class (a805ba)

Features

  • Introduce automatic union of objects inferring during mapping (79d7c2)
  • Introduce helper class MessageMapFormatter (ddf69e)
  • Introduce helper class MessagesFlattener (a97b40)
  • Introduce helper NodeTraverser for recursive operations on nodes (cc1bc6)

Bug Fixes

  • Handle nested attributes compilation (d2795b)
  • Treat forbidden mixed type as invalid type (36bd36)
  • Treat union type resolving error as message (e834cd)
  • Use locked package versions for quality assurance workflow (626f13)

Other

  • Ignore changelog configuration file in git export (85a6a4)
  • Raise PHPStan version (0144bf)

0.3.0

18 Dec 21:42
Compare
Choose a tag to compare

Features

  • Handle common database datetime formats (#40) (179ba3)

Other

  • Change Composer scripts calls (0b507c)
  • Raise version of friendsofphp/php-cs-fixer (e5ccbe)

0.2.0

07 Dec 18:10
Compare
Choose a tag to compare

Features

  • Handle integer range type (9f99a2)
  • Handle local type aliasing in class definition (56142d)
  • Handle type alias import in class definition (fa3ce5)

Bug Fixes

  • Do not accept shaped array with excessive key(s) (5a578e)
  • Handle integer value match properly (9ee2cc)

Other

  • Delete commented code (4f5612)
  • Move exceptions to more specific folder (185edf)
  • Rename GenericAssignerLexer to TypeAliasLexer (680941)
  • Use marcocesarato/php-conventional-changelog for changelog (178aa9)

0.1.1

01 Dec 12:30
Compare
Choose a tag to compare

⚠ BREAKING CHANGES

  • Change license from GPL 3 to MIT (a77b28)

Features

  • Handle multiline type declaration (d99c59)

Bug Fixes

  • Filter type symbols with strict string comparison (6cdea3)
  • Handle correctly iterable source during mapping (dd4624)
  • Handle shaped array integer key (5561d0)
  • Resolve single/double quotes when parsing doc-block type (1c628b)

Other

  • Change PHPStan stub file extension (8fc6af)
  • Delete unwanted code (e3e169)
  • Syntax highlight stub files (#9) (9ea95f)
  • Use composer runtime API (1f754a)

0.1.0

28 Nov 17:42
Compare
Choose a tag to compare

Initial release 🎉