Commit Graph

240 Commits

Author SHA1 Message Date
6889f95fed cs-fix 2022-11-08 12:31:37 +01:00
ba4d54e214 Add basic test 2022-11-08 12:31:37 +01:00
ce715b4467 Add basic support for strict array tokens 2022-11-08 12:31:37 +01:00
Romain Canon
752ad9d12e fix: handle scalar value casting in union types only in flexible mode 2022-11-07 01:15:46 +01:00
Romain Canon
92a41a1564 misc: transform missing source value to null in flexible mode 2022-11-07 01:15:46 +01:00
Romain Canon
034f1c51e1 fix: allow missing and null value for array node in flexible mode 2022-11-07 01:15:46 +01:00
Romain Canon
08fb0e17ba fix: allow missing value for shaped array nullable node in flexible mode 2022-11-07 01:15:46 +01:00
Romain Canon
25ce2188bb test: add test to check mapping with missing value for nullable node 2022-11-07 01:15:46 +01:00
Tim Düsterhus
625ad1572b qa: add /docs/ export-ignore to .gitattributes 2022-10-25 19:52:50 +02:00
Tim Düsterhus
4085e6ab72 qa: add composer.lock export-ignore to .gitattributes
The lock file is not useful for downstream consumers and requires more than
200kB of data to be unnecessarily transmitted.
2022-10-25 19:52:50 +02:00
Tim Düsterhus
b81847839d misc: do not use uniqid()
The return value of `uniqid()` is not very random and easily guessable. While
this likely does not introduce any issues in CompiledPhpFileCache, the fact
that `uniqid()` is often mis-used in locations where actual randomness is a
hard requirement, makes this a red flag.

Replace the use of `uniqid()` to generate a random filename by hexadecimal
encoded `random_bytes(16)`, giving 128 Bits of randomness, making the value
unguessable in practice and preventing the `CompiledPhpFileCache` from showing
up when searching for `uniqid()`.
2022-10-25 19:49:01 +02:00
Romain Canon
027d2a43da release: version 0.16.0 2022-10-19 13:46:57 +02:00
Romain Canon
47ad4a1416 feat: add support for PHP 8.2 2022-10-19 13:44:19 +02:00
Romain Canon
bd5123390f misc: update dependencies 2022-10-19 13:44:19 +02:00
dependabot[bot]
4492ef73dc chore(deps): bump actions/cache from 3.0.10 to 3.0.11
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.10 to 3.0.11.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.10...v3.0.11)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-19 13:38:56 +02:00
Romain Canon
c71d6aef94 fix: properly handle quote char in type definition 2022-10-19 13:36:36 +02:00
Romain Canon
0d2205edd3 release: version 0.15.0 2022-10-06 13:49:08 +02:00
Nathan Boiron
0b8ca98a2c
misc: import namespace token parser inside library
The `TokenParser` is imported from the `doctrine/annotations` package in
order to reduce the coupling to this library, which will lead to its
removal from the dependencies in an upcoming version.
2022-10-06 13:40:11 +02:00
dependabot[bot]
833193e025 chore(deps): bump actions/cache from 3.0.8 to 3.0.10
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.8 to 3.0.10.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.8...v3.0.10)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-04 22:16:59 +02:00
Romain Canon
1244c2d68f feat: add support for class constant type
This notation is mainly useful when several cases in constants of a
class share a common prefix.

```php
final class SomeClassWithConstants
{
    public const FOO = 1337;

    public const BAR = 'bar';

    public const BAZ = 'baz';
}

$mapper = (new MapperBuilder())->mapper();

$mapper->map('SomeClassWithConstants::BA*', 1337); // error
$mapper->map('SomeClassWithConstants::BA*', 'bar'); // ok
$mapper->map('SomeClassWithConstants::BA*', 'baz'); // ok
```
2022-10-04 22:15:02 +02:00
Romain Canon
37f96f101d fix: correctly handle type inferring during mapping
Solves an issue where a type was inferred for a union but would be
marked as invalid.
2022-10-04 22:15:02 +02:00
Romain Canon
69ebd19ee8 feat: add support for wildcard in enumeration type
This notation can be used when several cases in an enum share a common
prefix.

```php
enum SomeEnum: string
{
    case FOO = 'foo';
    case BAR = 'bar';
    case BAZ = 'baz';
}

$mapper = (new MapperBuilder())->mapper();

$mapper->map('SomeEnum::BA*', 'foo'); // error
$mapper->map('SomeEnum::BA*', 'bar'); // ok
$mapper->map('SomeEnum::BA*', 'baz'); // ok
```
2022-10-04 20:44:55 +02:00
Romain Canon
b2889a3ba0 misc: remove unused code 2022-10-04 20:44:55 +02:00
Romain Canon
ad0f8fee17 misc: save type token symbols during lexing 2022-10-04 20:44:55 +02:00
d7dda9d261
qa: switch Psalm method from getChildNodes to getAtomicTypes
See vimeo/psalm#8525
2022-10-01 14:36:06 +02:00
Patrick Landolt
2b9d81cc45
doc: fix typo in code example 2022-09-30 13:12:12 +02:00
Romain Canon
212b77fd13 fix: improve scalar values casting
These changes lead to better performance, more precise casting in case
of "fixed types" and overall better error messages.
2022-09-29 13:52:06 +02:00
Romain Canon
cb87925aac feat: introduce utility class to build messages
The new class `\CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder` can be
used to easily create an instance of (error) message.

This new straightforward way of creating messages leads to the
depreciation of `\CuyZ\Valinor\Mapper\Tree\Message\ThrowableMessage`.

```php
$message = MessageBuilder::newError('Some message / {some_parameter}.')
    ->withCode('some_code')
    ->withParameter('some_parameter', 'some_value')
    ->build();
```
2022-09-29 13:52:06 +02:00
Romain Canon
de8aa9f440 misc: remove unused code 2022-09-29 13:52:06 +02:00
Romain Canon
86802daced qa: run Infection only on modified files 2022-09-26 22:31:04 +02:00
Romain Canon
e686c07f39 qa: set xdebug mode to off when running QA checks 2022-09-26 22:31:04 +02:00
Romain Canon
3ee526cb27 fix: fetch correct node value for children 2022-09-26 20:03:47 +02:00
Eduardo Dobay
c009ab98cc
fix: properly handle static anonymous functions
The `MethodObjectBuilder` was incorrectly used when a registered
constructor is a static anonymous functions — it was handled like a
static method closure `Class::method(...)` and would yield errors like
this:

```
Error: Call to undefined method 
stdClass::CuyZ\Valinor\Tests\Integration\Mapping\{closure}()
```

PHP Reflection does not provide any way of telling static functions and
closures of static methods apart, other than checking for the name
`{closure}`. We check that `{closure}` is actually the last part of the
fully-qualified name, instead of just checking that the string ends with
`{closure}`.
2022-09-24 20:01:53 +02:00
Lukáš Unger
0e8f12e5f7
fix: add return types for cache implementations
Fixes the following message reported by `symfony/error-handler`:

`User Deprecated: Method "Psr\SimpleCache\CacheInterface::get()" might
add "mixed" as a native return type declaration in the future. Do the
same in implementation "CuyZ\Valinor\Cache\..." now to avoid errors or
add an explicit @return annotation to suppress this message.`
2022-09-24 19:29:02 +02:00
Romain Canon
d8957c061d release: version 0.14.0 2022-09-01 12:51:11 +02:00
Romain Canon
48208c1ed1 fix: correctly fetch file system cache entries
Method `\CuyZ\Valinor\Cache\FileSystemCache::get()` was not properly
looping on all delegates, leading to the values not being fetched from
the cache files and resulting in `null` (the default value) being
returned in some cases. Because of the following algorithm, the cache
entry was populated again, so the cache was not really working here.

```php
if ($this->cache->has($key)) {
    $entry = $this->cache->get($key);

    if ($entry) {
        return $entry;
    }
}

$class = $this->delegate->for($type);

$this->cache->set($key, $class);

return $class;
```
2022-09-01 12:26:32 +02:00
Romain Canon
11a7ea7252 feat: introduce helper method to describe supported date formats
```php
(new \CuyZ\Valinor\MapperBuilder())
    // Both `Cookie` and `ATOM` formats will be accepted
    ->supportDateFormats(DATE_COOKIE, DATE_ATOM)
    ->mapper()
    ->map(DateTimeInterface::class, 'Monday, 08-Nov-1971 13:37:42 UTC');
```
2022-09-01 12:24:24 +02:00
Romain Canon
f232cc0636 feat!: introduce constructor for custom date formats
A new constructor can be registered to declare which format(s) are
supported during the mapping of a date object. By default, any valid
timestamp or ATOM-formatted value will be accepted.

```php
(new \CuyZ\Valinor\MapperBuilder())
    // Both COOKIE and ATOM formats will be accepted
    ->registerConstructor(
        new \CuyZ\Valinor\Mapper\Object\DateTimeFormatConstructor(DATE_COOKIE, DATE_ATOM)
    )
    ->mapper()
    ->map(DateTimeInterface::class, 'Monday, 08-Nov-1971 13:37:42 UTC');
```

The previously very opinionated behaviour has been removed, but can be
temporarily used to help with the migration.

```php
(new \CuyZ\Valinor\MapperBuilder())
    ->registerConstructor(
        new \CuyZ\Valinor\Mapper\Object\BackwardCompatibilityDateTimeConstructor()
    )
    ->mapper()
    ->map(DateTimeInterface::class, 'Monday, 08-Nov-1971 13:37:42 UTC');
```
2022-09-01 12:24:24 +02:00
Romain Canon
bf7af18d37 qa: improve cache and performances of Github actions 2022-08-31 09:36:22 +02:00
Romain Canon
3c4d29901a fix: prevent illegal characters in PSR-16 cache keys
@see https://www.php-fig.org/psr/psr-16/#12-definitions
2022-08-31 01:01:16 +02:00
Romain Canon
546fa5c6cf test: add missing test assertion 2022-08-31 01:01:16 +02:00
mtouellette
fd39aea2a7
fix: handle concurrent cache file creation 2022-08-30 22:06:53 +02:00
Romain Canon
bf445b5364 fix: allow trailing comma in shaped array
Allows the following syntax:

```php
/**
 * @var array{
 * 	   foo: string,
 *     bar: int,
 * }
 */
 ```
2022-08-30 21:20:28 +02:00
Romain Canon
8c7568c94a qa: change Psalm cache directory 2022-08-30 20:55:20 +02:00
dependabot[bot]
f8615b5220 chore(deps): bump actions/cache from 3.0.5 to 3.0.8
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.5 to 3.0.8.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.5...v3.0.8)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-30 15:31:19 +02:00
Romain Canon
e437d9405c feat: introduce attribute DynamicConstructor
In some situations the type handled by a constructor is only known at
runtime, in which case the constructor needs to know what class must be
used to instantiate the object.

For instance, an interface may declare a static constructor that is then
implemented by several child classes. One solution would be to register
the constructor for each child class, which leads to a lot of
boilerplate code and would require a new registration each time a new
child is created. Another way is to use the attribute
`\CuyZ\Valinor\Mapper\Object\DynamicConstructor`.

When a constructor uses this attribute, its first parameter must be a
string and will be filled with the name of the actual class that the
mapper needs to build when the constructor is called. Other arguments
may be added and will be mapped normally, depending on the source given
to the mapper.

```php
interface InterfaceWithStaticConstructor
{
    public static function from(string $value): self;
}

final class ClassWithInheritedStaticConstructor implements InterfaceWithStaticConstructor
{
    private function __construct(private SomeValueObject $value) {}

    public static function from(string $value): self
    {
        return new self(new SomeValueObject($value));
    }
}

(new \CuyZ\Valinor\MapperBuilder())
    ->registerConstructor(
        #[\CuyZ\Valinor\Attribute\DynamicConstructor]
        function (string $className, string $value): InterfaceWithStaticConstructor {
            return $className::from($value);
        }
    )
    ->mapper()
    ->map(ClassWithInheritedStaticConstructor::class, 'foo');
```
2022-08-30 15:15:41 +02:00
Romain Canon
4bc50e3e42 misc: add singleton usage of ClassStringType 2022-08-29 23:09:15 +02:00
Romain Canon
ec494cec48 misc: fetch attributes for function definition 2022-08-29 23:09:15 +02:00
Romain Canon
c37ac1e259 feat: handle abstract constructor registration
It is now possible to register a static method constructor that can be
inherited by a child class. The constructor will then be used correctly
to map the child class.

```php
abstract class ClassWithStaticConstructor
{
    public string $value;

    final private function __construct(string $value)
    {
        $this->value = $value;
    }

    public static function from(string $value): static
    {
        return new static($value);
    }
}

final class ChildClass extends ClassWithStaticConstructor {}

(new MapperBuilder())
    // The constructor can be used for every child of the parent class
    ->registerConstructor(ClassWithStaticConstructor::from(...))
    ->mapper()
    ->map(ChildClass::class, 'foo');
```
2022-08-29 23:09:15 +02:00
Romain Canon
73b62241b6 fix: handle inherited private constructor in class definition 2022-08-29 23:09:15 +02:00