Commit Graph

33 Commits

Author SHA1 Message Date
Romain Canon
b2e810e3ce feat!: 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:

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

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

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

echo strtolower($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.
2022-01-02 00:48:01 +01:00
Romain Canon
33167d28d0 test: fix cache directory removal 2022-01-02 00:48:01 +01:00
Romain Canon
0144bf084a misc: raise PHPStan version
This enables full PHP 8.1 support 🎉
2021-12-31 13:30:14 +01:00
Aurimas Niekis
d2795bc6b9 fix: handle nested attributes compilation 2021-12-27 20:57:38 +01:00
Romain Canon
54f608e5b1 feat!: add access to root node when error occurs during mapping
When an error occurs during mapping, the root instance of `Node` can now
be accessed from the exception. This recursive object allows retrieving
all needed information through the whole mapping tree: path, values,
types and messages, including the issues that caused the exception.

It can be used like the following:

```php
try {
   (new \CuyZ\Valinor\MapperBuilder())
       ->mapper()
       ->map(SomeClass::class, [/* ... */]);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
    // Do something with `$error->node()`
    // See README for more information
}
```

This change removes the method `MappingError::describe()` which provided
a flattened view of messages of all the errors that were encountered
during the mapping. The same behaviour can still be retrieved, see the
example below:

```php
use CuyZ\Valinor\Mapper\Tree\Message\Message;
use CuyZ\Valinor\Mapper\Tree\Node;

/**
 * @implements \IteratorAggregate<string, array<\Throwable&Message>>
 */
final class MappingErrorList implements \IteratorAggregate
{
    private Node $node;

    public function __construct(Node $node)
    {
        $this->node = $node;
    }

    /**
     * @return \Traversable<string, array<\Throwable&Message>>
     */
    public function getIterator(): \Traversable
    {
        yield from $this->errors($this->node);
    }

    /**
     * @return \Traversable<string, array<\Throwable&Message>>
     */
    private function errors(Node $node): \Traversable
    {
        $errors = array_filter(
            $node->messages(),
            static fn (Message $m) => $m instanceof \Throwable
        );

        if (! empty($errors)) {
            yield $node->path() => array_values($errors);
        }

        foreach ($node->children() as $child) {
            yield from $this->errors($child);
        }
    }
}

try {
   (new \CuyZ\Valinor\MapperBuilder())
       ->mapper()
       ->map(SomeClass::class, [/* ... */]);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
    $errors = iterator_to_array(new MappingErrorList($error->node()));
}
```

The class `CannotMapObject` is deleted, as it does not provide any
value; this means that `MappingError` which was previously an interface
becomes a class.
2021-12-27 13:52:36 +01:00
Romain Canon
f12ef39e2f release: version 0.3.0 2021-12-18 22:40:24 +01:00
Brandon Savage
179ba3df29
feat: handle common database datetime formats (#40) 2021-12-17 17:55:17 +01:00
Romain Canon
e5ccbe201b misc: raise version of friendsofphp/php-cs-fixer
Grants PHP 8.1 support.
2021-12-15 13:19:24 +01:00
Romain Canon
0b507c9b33 misc: change Composer scripts calls 2021-12-15 13:19:24 +01:00
Romain Canon
231c276021 release: version 0.2.0 2021-12-07 18:59:46 +01:00
Romain Canon
ce3dfb0ced misc: use marcocesarato/php-conventional-changelog for changelog 2021-12-07 18:59:46 +01:00
Romain Canon
9f99a2a1ef feat: handle integer range type
Integer range can be used as follows:

```php
final class SomeClass
{
    /** @var int<42, 1337> */
    public int $intRange; // accepts any int between 42 and 1337

    /** @var int<-1337, 1337> */
    public int $negativeIntRange; // also works with negative values

    /** @var int<min, 1337> */
    public int $minIntRange; // `min` can be used…

    /** @var int<0, max> */
    public int $maxIntRange; // …as well as `max`
}
```

Note that `min` and `max` will check the range with PHP's internal
constants `PHP_INT_MIN` and `PHP_INT_MAX`.
2021-12-07 18:20:25 +01:00
Romain Canon
185edf6053 misc: move exceptions to more specific folder 2021-12-07 18:20:25 +01:00
Romain Canon
9ee2cc471e fix: handle integer value match properly 2021-12-07 18:20:25 +01:00
Romain Canon
fa3ce50dfb feat: handle type alias import in class definition
Type aliases can now be imported from another class definition.

Both PHPStan and Psalm syntax are handled.

```php
/**
 * @phpstan-type SomeTypeAlias = array{foo: string}
 */
final class SomeClass
{
    /** @var SomeTypeAlias */
    public array $someTypeAlias;
}

/**
 * @phpstan-import-type SomeTypeAlias from SomeClass
 */
final class SomeOtherClass
{
    /** @var SomeTypeAlias */
    public array $someTypeAlias;
}
```
2021-12-07 18:20:04 +01:00
Romain Canon
56142dea5b feat: handle local type aliasing in class definition
Type aliases can now be added to a class definition.

Both PHPStan and Psalm syntax are handled.

```php
/**
 * @phpstan-type SomeTypeAlias = array{foo: string}
 * @psalm-type SomeOtherTypeAlias = array{bar: int}
 */
final class SomeClass
{
    /** @var SomeTypeAlias */
    public array $someTypeAlias;

    /** @var SomeOtherTypeAlias */
    public array $someOtherTypeAlias;
}
```
2021-12-07 18:20:04 +01:00
Romain Canon
99b4f4f7aa refactor: extract reflection type resolving to class 2021-12-07 18:20:04 +01:00
Romain Canon
680941687b misc: rename GenericAssignerLexer to TypeAliasLexer 2021-12-07 18:20:04 +01:00
Romain Canon
4f561290b1 misc: delete commented code 2021-12-02 22:35:34 +01:00
Romain Canon
5a578ea4c2 fix: do not accept shaped array with excessive key(s)
The mapper will now delete the excessive keys given as input to a shaped
 array.
2021-12-02 22:35:34 +01:00
Romain Canon
710c7a925c release: version 0.1.1 2021-12-01 13:28:25 +01:00
Romain Canon
d99c59dfb5 feat: handle multiline type declaration
The following type will now be handled:

```php
/**
 * @var array{
 *     foo: string,
 *     bar: int
 * }
 */
public array $foo;
```
2021-12-01 09:45:33 +01:00
Romain Canon
dd4624c5e0 fix: handle correctly iterable source during mapping 2021-11-30 13:04:09 +01:00
Romain Canon
a77b28c5c2 misc!: change license from GPL 3 to MIT
See https://twitter.com/binEinfachNurDa/status/1465282745563856901
2021-11-30 13:01:20 +01:00
Romain Canon
e3e169fb3c misc: delete unwanted code 2021-11-30 12:57:31 +01:00
Viktor Szépe
ea842e2efb
ci: fix CI config (#10) 2021-11-29 13:24:11 +01:00
Romain Canon
1f754a7e77 misc: use composer runtime API 2021-11-29 12:36:00 +01:00
Romain Canon
5561d018ab fix: handle shaped array integer key 2021-11-29 12:17:19 +01:00
Viktor Szépe
9ea95f43f3
misc: syntax highlight stub files (#9) 2021-11-29 12:00:21 +01:00
Romain Canon
8fc6af283c misc: change PHPStan stub file extension 2021-11-29 11:59:29 +01:00
Romain Canon
1c628b6675 fix: resolve single/double quotes when parsing doc-block type 2021-11-29 11:36:11 +01:00
Romain Canon
6cdea31bc2 fix: filter type symbols with strict string comparison
Previously, `array_filter` would remove the integer value `0` from the
array.

See #1
2021-11-29 11:27:29 +01:00
Romain Canon
396f64a524 feat: initial release
🎉
2021-11-28 18:21:56 +01:00