Skip to content

Releases: CuyZ/Valinor

1.5.0

07 Aug 18:30
Compare
Choose a tag to compare

Features

  • Introduce method to get date formats supported during mapping (873961)

Bug Fixes

  • Allow filesystem cache to be cleared when directory does not exist (782408)
  • Allow negative timestamp to be mapped to a datetime (d358e8)
  • Allow overriding of supported datetime formats (1c70c2)
  • Correctly handle message formatting for long truncated UTF8 strings (0a8f37)
  • Make serialization of attributes possible (e8ca2f)
  • Remove exception inheritance from UnresolvableType (eaa128)
  • Remove previous exception from UnresolvableType (5c89c6)

Other

  • Avoid using unserialize when caching NULL default values (5e9b4c)
  • Catch json_encode exception to help identifying parsing errors (861c3b)
  • Update dependencies (c31e5c, 5fa107)

1.4.0

17 Apr 11:26
Compare
Choose a tag to compare

Notable changes

Exception thrown when source is invalid

JSON or YAML given to a source may be invalid, in which case an exception can
now be caught and manipulated.

try {
    $source = \CuyZ\Valinor\Mapper\Source\Source::json('invalid JSON');
} catch (\CuyZ\Valinor\Mapper\Source\Exception\InvalidSource $error) {
    // Let the application handle the exception in the desired way.
    // It is possible to get the original source with `$error->source()`
}

Features

  • Introduce InvalidSource thrown when using invalid JSON/YAML (0739d1)

Bug Fixes

  • Allow integer values in float types (c6df24)
  • Make array-key type match mixed (ccebf7)
  • Prevent infinite loop when class has parent class with same name (83eb05)

Other

  • Add previous exception in various custom exceptions (b9e381)

1.3.1

13 Feb 05:56
Compare
Choose a tag to compare

Bug Fixes

  • Check if temporary cache file exists before deletion (3177bf)
  • Display useful error message for invalid constructor return type (dc7f5c)
  • Keep input path when error occurs in single node (d70257)
  • Properly handle class static constructor for other class (d34974)
  • Properly handle union of null and objects (8f03a7)

Other

  • Update dependencies (f7e7f2)

1.3.0

08 Feb 09:40
Compare
Choose a tag to compare

Notable changes

Handle custom enum constructors registration

It is now possible to register custom constructors for enum, the same way it could be done for classes.

(new \CuyZ\Valinor\MapperBuilder())
    ->registerConstructor(
        // Allow the native constructor to be used
        SomeEnum::class,

        // Register a named constructor
        SomeEnum::fromMatrix(...)
    )
    ->mapper()
    ->map(SomeEnum::class, [
        'type' => 'FOO',
        'number' => 2,
    ]);

enum SomeEnum: string
{
    case CASE_A = 'FOO_VALUE_1';
    case CASE_B = 'FOO_VALUE_2';
    case CASE_C = 'BAR_VALUE_1';
    case CASE_D = 'BAR_VALUE_2';

    /**
     * @param 'FOO'|'BAR' $type
     * @param int<1, 2> $number
     * /
    public static function fromMatrix(string $type, int $number): self
    {
        return self::from("{$type}_VALUE_{$number}");
    }
}

An enum constructor can be for a specific pattern:

enum SomeEnum
{
    case FOO;
    case BAR;
    case BAZ;
}

(new \CuyZ\Valinor\MapperBuilder())
    ->registerConstructor(
        /**
         * This constructor will be called only when pattern
         * `SomeEnum::BA*` is requested during mapping.
         *
         * @return SomeEnum::BA*
         */
        fn (string $value): SomeEnum => /* Some custom domain logic */
    )
    ->mapper()
    ->map(SomeEnum::class . '::BA*', 'some custom value');

Note that this commit required heavy refactoring work, leading to a regression for union types containing enums and other types. As these cases are considered marginal, this change is considered non-breaking.

Features

  • Handle custom enum constructors registration (217e12)

Other

  • Handle enum type as class type (5a3caf)

0.17.1

18 Jan 09:52
Compare
Choose a tag to compare

Bug Fixes

  • Use PHP 8.0 Polyfill where needed (d90a95)

1.2.0

09 Jan 12:55
Compare
Choose a tag to compare

Notable changes

Handle single property/constructor argument with array input

It is now possible, again, to use an array for a single node (single class property or single constructor argument), if this array has one value with a key matching the argument/property name.

This is a revert of a change that was introduced in a previous commit: see hash 72cba32

Features

  • Allow usage of array input for single node during mapping (686186)

Bug Fixes

  • Do not re-validate single node with existing error (daaaac)

Other

  • Remove unneeded internal check (86cca5)
  • Remove unneeded internal checks and exceptions (157723)

1.1.0

20 Dec 22:09
Compare
Choose a tag to compare

Notable changes

Handle class generic types inheritance

It is now possible to use the @extends tag (already handled by PHPStan and Psalm) to declare the type of a parent class generic. This logic is recursively applied to all parents.

/**
 * @template FirstTemplate
 * @template SecondTemplate
 */
abstract class FirstClassWithGenerics
{
    /** @var FirstTemplate */
    public $valueA;

    /** @var SecondTemplate */
    public $valueB;
}

/**
 * @template FirstTemplate
 * @extends FirstClassWithGenerics<FirstTemplate, int>
 */
abstract class SecondClassWithGenerics extends FirstClassWithGenerics
{
    /** @var FirstTemplate */
    public $valueC;
}

/**
 * @extends SecondClassWithGenerics<string>
 */
final class ChildClass extends SecondClassWithGenerics
{
}

$object = (new \CuyZ\Valinor\MapperBuilder())
    ->mapper()
    ->map(ChildClass::class, [
        'valueA' => 'foo',
        'valueB' => 1337,
        'valueC' => 'bar',
    ]);

echo $object->valueA; // 'foo'
echo $object->valueB; // 1337
echo $object->valueC; // 'bar'

Added support for class inferring

It is now possible to infer abstract or parent classes the same way it can be done for interfaces.

Example with an abstract class:

abstract class SomeAbstractClass
{
    public string $foo;

    public string $bar;
}

final class SomeChildClass extends SomeAbstractClass
{
    public string $baz;
}

$result = (new \CuyZ\Valinor\MapperBuilder())
    ->infer(
        SomeAbstractClass::class,
        fn () => SomeChildClass::class
    )
    ->mapper()
    ->map(SomeAbstractClass::class, [
        'foo' => 'foo',
        'bar' => 'bar',
        'baz' => 'baz',
    ]);

assert($result instanceof SomeChildClass);
assert($result->foo === 'foo');
assert($result->bar === 'bar');
assert($result->baz === 'baz');

Features

  • Add support for class inferring (5a90ad)
  • Handle class generic types inheritance (6506b7)

Bug Fixes

  • Handle object return type in PHPStan extension (201728)
  • Import plugin class file in PHPStan configuration (58d540)
  • Keep nested errors when superfluous keys are detected (813b3b)

Other

  • Adapt code with PHP 8.0 syntax (3fac3e)
  • Add isAbstract flag in class definition (ad0c06)
  • Add isFinal flag in class definition (25da31)
  • Enhance TreeMapper::map() return type signature (dc32d3)
  • Improve return type signature for TreeMapper (c8f362)
  • Prevent multiple cache round-trip (13b620)

1.0.0

28 Nov 00:09
Compare
Choose a tag to compare

First stable version! 🥳 🎉

This release marks the end of the initial development phase. The library has been live for exactly one year at this date and is stable enough to start following the semantic versioning — it means that any backward incompatible change (aka breaking change) will lead to a bump of the major version.

This is the biggest milestone achieved by this project (yet™); I want to thank everyone who has been involved to make it possible, especially the contributors who submitted high-quality pull requests to improve the library.

There is also one person that I want to thank even more: my best friend Nathan, who has always been so supportive with my side-projects. Thanks, bro! 🙌

The last year marked a bigger investment of my time in OSS contributions; I've proven to myself that I am able to follow a stable way of managing my engagement to this community, and this is why I enabled sponsorship on my profile to allow people to ❤️ sponsor my work on GitHub — if you use this library in your applications, please consider offering me a 🍺 from time to time! 🤗

Notable changes

End of PHP 7.4 support

PHP 7.4 security support has ended on the 28th of November 2022; the minimum version supported by this library is now PHP 8.0.

New mapper to map arguments of a callable

This new mapper can be used to ensure a source has the right shape before calling a function/method.

The mapper builder can be configured the same way it would be with a tree mapper, for instance to customize the type strictness.

$someFunction = function(string $foo, int $bar): string {
    return "$foo / $bar";
};

try {
    $arguments = (new \CuyZ\Valinor\MapperBuilder())
        ->argumentsMapper()
        ->mapArguments($someFunction, [
            'foo' => 'some value',
            'bar' => 42,
        ]);

    // some value / 42
    echo $someFunction(...$arguments);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
    // Do something…
}

Support for TimeZone objects

Native TimeZone objects construction is now supported with a proper error handling.

try {
    (new \CuyZ\Valinor\MapperBuilder())
        ->mapper()
        ->map(DateTimeZone::class, 'Jupiter/Europa');
} catch (MappingError $exception) {
    $error = $exception->node()->messages()[0];

    // Value 'Jupiter/Europa' is not a valid timezone.
    echo $error->toString();
}

Mapping object with one property

When a class needs only one value, the source given to the mapper must match the type of the single property/parameter.

This change aims to bring consistency on how the mapper behaves when mapping an object that needs one argument. Before this change, the source could either match the needed type, or be an array with a single entry and a key named after the argument.

See example below:

final class Identifier
{
    public readonly string $value;
}

final class SomeClass
{
    public readonly Identifier $identifier;

    public readonly string $description;
}

(new \CuyZ\Valinor\MapperBuilder())->mapper()->map(SomeClass::class, [
    'identifier' => ['value' => 'some-identifier'], // ❌
    'description' => 'Lorem ipsum…',
]);

(new \CuyZ\Valinor\MapperBuilder())->mapper()->map(SomeClass::class, [
    'identifier' => 'some-identifier', // ✅
    'description' => 'Lorem ipsum…',
]);

Upgrading from 0.x to 1.0

As this is a major release, all deprecated features have been removed, leading to an important number of breaking changes.

You can click on the entries below to get advice on available replacements.

Doctrine annotations support removal

Doctrine annotations cannot be used anymore, PHP attributes must be used.

`BackwardCompatibilityDateTimeConstructor` class removal

You must use the method available in the mapper builder, see dealing with dates chapter.

Mapper builder `flexible` method removal

The flexible has been splitted in three disctint modes, see type strictness & flexibility chapter.

Mapper builder `withCacheDir` method removal

You must now register a cache instance directly, see performance & caching chapter.

`StaticMethodConstructor` class removal

You must now register the constructors using the mapper builder, see custom object constructors chapter.

Mapper builder `bind` method removal

You must now register the constructors using the mapper builder, see custom object constructors chapter.

`ThrowableMessage` class removal

You must now use the MessageBuilder class, see error handling chapter.

`MessagesFlattener` class removal

You must now use the Messages class, see error handling chapter.

`TranslatableMessage` class removal

You must now use the HasParameters class, see custom exception chapter.

Message methods removal

The following methods have been removed:

  • \CuyZ\Valinor\Mapper\Tree\Message\NodeMessage::name()
  • \CuyZ\Valinor\Mapper\Tree\Message\NodeMessage::path()
  • \CuyZ\Valinor\Mapper\Tree\Message\NodeMessage::type()
  • \CuyZ\Valinor\Mapper\Tree\Message\NodeMessage::value()
  • \CuyZ\Valinor\Mapper\Tree\Node::value()

It is still possible to get the wanted values using the method \CuyZ\Valinor\Mapper\Tree\Message\NodeMessage::node().

The placeholder {original_value} has also been removed, the same value can be fetched with {source_value}.

`PlaceHolderMessageFormatter` class removal

Other features are available to format message, see error messages customization chapter.

`Identifier` attribute removal

This feature has been part of the library since its first public release, but it was never documented because it did not fit one of the library's main philosophy which is to be almost entirely decoupled from an application's domain layer.

The feature is entirely removed and not planned to be replaced by an alternative, unless the community really feels like there is a need for something alike.

⚠ BREAKING CHANGES

  • Disallow array when mapping to object with one argument (72cba3)
  • Mark tree mapper and arguments mapper as @pure (0d9855)
  • Remove deprecated backward compatibility datetime constructor (a65e8d)
  • Remove deprecated class ThrowableMessage (d36ca9)
  • Remove deprecated class to flatten messages (f9ed93)
  • Remove deprecated interface TranslatableMessage (ceb197)
  • Remove deprecated message methods (e6557d)
  • Remove deprecated method constructor attribute (d76467)
  • Remove deprecated method to enable flexible mode (a2bef3)
  • Remove deprecated method to set cache directory (b0d6d2)
  • Remove deprecated method used to bind a callback (b79ed8)
  • Remove deprecated placeholder message formatter (c2723d)
  • Remove Doctrine annotations support (66c182)
  • Remove identifier attribute (8a7486)
  • Remove PHP 7.4 support (5f5a50)
  • Remove support for strict-array type (22c3b4)

Features

  • Add constructor for DateTimeZone with error support ([a0a4d6](a0a4d63...
Read more

0.17.0

08 Nov 12:30
Compare
Choose a tag to compare

Notable changes

The main feature introduced in this release is the split of the flexible mode in three distinct modes:

  1. The flexible casting

    Changes the behaviours explained below:

    $flexibleMapper = (new \CuyZ\Valinor\MapperBuilder())
        ->enableFlexibleCasting()
        ->mapper();
    
    // ---
    // Scalar types will accept non-strict values; for instance an
    // integer type will accept any valid numeric value like the
    // *string* "42".
    
    $flexibleMapper->map('int', '42');
    // => 42
    
    // ---
    // List type will accept non-incremental keys.
    
    $flexibleMapper->map('list<int>', ['foo' => 42, 'bar' => 1337]);
    // => [0 => 42, 1 => 1338]
    
    // ---
    // If a value is missing in a source for a node that accepts `null`,
    // the node will be filled with `null`.
    
    $flexibleMapper->map(
        'array{foo: string, bar: null|string}',
        ['foo' => 'foo'] // `bar` is missing
    );
    // => ['foo' => 'foo', 'bar' => null]
    
    // ---
    // Array and list types will convert `null` or missing values to an
    // empty array.
    
    $flexibleMapper->map(
        'array{foo: string, bar: array<string>}',
        ['foo' => 'foo'] // `bar` is missing
    );
    // => ['foo' => 'foo', 'bar' => []]
  2. The superfluous keys

    Superfluous keys in source arrays will be allowed, preventing errors when a value is not bound to any object property/parameter or shaped array element.

    (new \CuyZ\Valinor\MapperBuilder())
        ->allowSuperfluousKeys()
        ->mapper()
        ->map(
            'array{foo: string, bar: int}',
            [
                'foo' => 'foo',
                'bar' => 42,
                'baz' => 1337.404, // `baz` will be ignored
            ]
        );
  3. The permissive types

    Allows permissive types mixed and object to be used during mapping.

    (new \CuyZ\Valinor\MapperBuilder())
        ->allowPermissiveTypes()
        ->mapper()
        ->map(
            'array{foo: string, bar: mixed}',
            [
                'foo' => 'foo',
                'bar' => 42, // Could be any value
            ]
        );

Full list of changes

Features

  • Add support for strict-array type (d456eb)
  • Introduce new callback message formatter (93f898)
  • Introduce new helper class to list messages (513827)
  • Split mapper flexible mode in three distinct modes (549e5f)

Bug Fixes

  • Allow missing and null value for array node in flexible mode (034f1c)
  • Allow missing value for shaped array nullable node in flexible mode (08fb0e)
  • Handle scalar value casting in union types only in flexible mode (752ad9)

Other

  • Do not use uniqid() (b81847)
  • Transform missing source value to null in flexible mode (92a41a)

0.16.0

19 Oct 11:47
Compare
Choose a tag to compare

Features

  • Add support for PHP 8.2 (a92360)

Bug Fixes

  • Properly handle quote char in type definition (c71d6a)

Other

  • Update dependencies (c2de32)