MissingConstructorArgumentsExceptionMapper   A
last analyzed

Complexity

Total Complexity 3

Size/Duplication

Total Lines 28
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
c 1
b 0
f 0
dl 0
loc 28
rs 10
wmc 3

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __invoke() 0 17 2
A getType() 0 3 1
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
        $className = $matches['class'] ?? throw ExceptionMapperException::notMatchedGroup('class', $exception);
29
        $parameterName = $matches['parameter'] ?? throw ExceptionMapperException::notMatchedGroup('property', $exception);
30
        $parameter = ClassHelper::getMethodParameter($className, '__construct', $parameterName);
31
        $parameterTypeName = PropertyHelper::getTypeName($parameter);
32
        $classProperty = ClassHelper::getProperty($className, $parameterName);
33
        $propertyName = PropertyHelper::getName($classProperty);
34
        $propertyValue = HashMap::collect($payload)
35
            ->filter(static fn (Entry $entry) => str_contains($entry->key, $propertyName))
36
            ->toArrayList()
37
            ->firstElement()
38
            ->get()
39
        ;
40
41
        return new ValidationError(Type::class, "[{$parameterName}]", sprintf(self::MESSAGE_TEMPLATE, $parameterName, $className, $parameterTypeName, $propertyValue));
42
    }
43
44
    public static function getType(): string
45
    {
46
        return MissingConstructorArgumentsException::class;
47
    }
48
}
49