Test Failed
Push — main ( 450f80...a11e57 )
by Fractal
02:32
created

__invoke()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 13
nc 1
nop 2
dl 0
loc 18
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace FRZB\Component\RequestMapper\ExceptionMapper\Mapper;
6
7
use Fp\Collections\Entry;
8
use Fp\Collections\HashMap;
9
use FRZB\Component\DependencyInjection\Attribute\AsService;
10
use FRZB\Component\DependencyInjection\Attribute\AsTagged;
11
use FRZB\Component\RequestMapper\Data\ErrorInterface;
12
use FRZB\Component\RequestMapper\Data\ValidationError;
13
use FRZB\Component\RequestMapper\Exception\ExceptionMapperException;
14
use FRZB\Component\RequestMapper\Helper\ClassHelper;
15
use FRZB\Component\RequestMapper\Helper\PropertyHelper;
16
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
17
use Symfony\Component\Validator\Constraints\Type;
18
19
#[AsService, AsTagged(ExceptionMapperInterface::class)]
20
class MissingConstructorArgumentsExceptionMapper implements ExceptionMapperInterface
21
{
22
    private const MESSAGE_TEMPLATE = 'Class property "%s:%s" must be of type "%s", given value "%s"';
23
    private const REGEX = '/Cannot create an instance of "(?<class>(?:\\w+\\\\)*(?:\\w+))" from serialized data because its constructor requires parameter "(?<parameter>(\\w+))" to be present/';
24
25
    public function __invoke(MissingConstructorArgumentsException $exception, array $payload): ErrorInterface
26
    {
27
        preg_match(self::REGEX, $exception->getMessage(), $matches) ?: throw new \TypeError($exception->getMessage(), previous: $exception);
28
29
        $className = $matches['class'] ?? throw ExceptionMapperException::notMatchedGroup('class', $exception);
30
        $parameterName = $matches['parameter'] ?? throw ExceptionMapperException::notMatchedGroup('property', $exception);
31
        $parameter = ClassHelper::getMethodParameter($className, '__construct', $parameterName);
32
        $parameterTypeName = PropertyHelper::getTypeName($parameter);
33
        $classProperty = ClassHelper::getProperty($className, $parameterName);
34
        $propertyName = PropertyHelper::getName($classProperty);
35
        $propertyValue = HashMap::collect($payload)
36
            ->filter(static fn (Entry $entry) => str_contains($entry->key, $propertyName))
37
            ->toArrayList()
38
            ->firstElement()
39
            ->get()
40
        ;
41
42
        return new ValidationError(Type::class, "[{$parameterName}]", sprintf(self::MESSAGE_TEMPLATE, $parameterName, $className, $parameterTypeName, $propertyValue));
43
    }
44
45
    public static function getType(): string
46
    {
47
        return MissingConstructorArgumentsException::class;
48
    }
49
}
50