Completed
Push — 1.x ( ef8475...bb88ff )
by Akihito
17s queued 14s
created

ClassParam   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 91
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 39
c 7
b 0
f 0
dl 0
loc 91
rs 10
wmc 14

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 2
B __invoke() 0 36 7
A getProps() 0 15 3
A enum() 0 12 2
1
<?php
2
3
namespace BEAR\Resource;
4
5
use BackedEnum;
6
use BEAR\Resource\Exception\ParameterException;
7
use Ray\Di\InjectorInterface;
8
use ReflectionClass;
9
use ReflectionEnum;
0 ignored issues
show
Bug introduced by
The type ReflectionEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use ReflectionNamedType;
11
use ReflectionParameter;
12
13
use function assert;
14
use function class_exists;
15
use function enum_exists;
16
use function is_a;
17
use function is_iterable;
18
use function ltrim;
19
use function preg_replace;
20
use function strtolower;
21
22
use const PHP_VERSION_ID;
23
24
final class ClassParam implements ParamInterface
25
{
26
    private string $type;
27
    private bool $isDefaultAvailable;
28
    private mixed $defaultValue;
29
30
    public function __construct(
31
        ReflectionNamedType $type,
32
        ReflectionParameter $parameter,
33
    ) {
34
        $this->type = $type->getName();
35
        $this->isDefaultAvailable = $parameter->isDefaultValueAvailable();
36
        if (! $this->isDefaultAvailable) {
37
            return;
38
        }
39
40
        $this->defaultValue = $parameter->getDefaultValue();
41
    }
42
43
    /**
44
     * {@inheritDoc}
45
     */
46
    public function __invoke(string $varName, array $query, InjectorInterface $injector)
47
    {
48
        try {
49
            /** @psalm-suppress MixedAssignment */
50
            $props = $this->getProps($varName, $query, $injector);
51
        } catch (ParameterException $e) {
52
            if ($this->isDefaultAvailable) {
53
                return $this->defaultValue;
54
            }
55
56
            throw $e;
57
        }
58
59
        assert(class_exists($this->type));
60
        $refClass = (new ReflectionClass($this->type));
61
62
        if (PHP_VERSION_ID >= 80100 && $refClass->isEnum()) {
0 ignored issues
show
Bug introduced by
The method isEnum() does not exist on ReflectionClass. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

62
        if (PHP_VERSION_ID >= 80100 && $refClass->/** @scrutinizer ignore-call */ isEnum()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
63
            return $this->enum($this->type, $props);
64
        }
65
66
        assert(is_iterable($props));
67
68
        $hasConstructor = (bool) $refClass->getConstructor();
69
        if ($hasConstructor) {
70
            /** @psalm-suppress MixedMethodCall */
71
            return new $this->type(...$props);
72
        }
73
74
        /** @psalm-suppress MixedMethodCall */
75
        $obj = new $this->type();
76
        /** @psalm-suppress MixedAssignment */
77
        foreach ($props as $propName => $propValue) {
78
            $obj->{$propName} = $propValue;
79
        }
80
81
        return $obj;
82
    }
83
84
    /** @param array<string, mixed> $query */
85
    private function getProps(string $varName, array $query, InjectorInterface $injector): mixed
86
    {
87
        if (isset($query[$varName])) {
88
            return $query[$varName];
89
        }
90
91
        // try camelCase variable name
92
        $snakeName = ltrim(strtolower((string) preg_replace('/[A-Z]/', '_\0', $varName)), '_');
93
        if (isset($query[$snakeName])) {
94
            return $query[$snakeName];
95
        }
96
97
        unset($injector);
98
99
        throw new ParameterException($varName);
100
    }
101
102
    /** @psalm-suppress MixedArgument */
103
    private function enum(string $type, mixed $props): mixed
104
    {
105
        $refEnum = new ReflectionEnum($type);
106
        assert(enum_exists($type));
107
108
        if (! $refEnum->isBacked()) {
109
            throw new NotBackedEnumException($type);
110
        }
111
112
        assert(is_a($type, BackedEnum::class, true));
113
114
        return $type::from($props); // @phpstan-ignore-line
115
    }
116
}
117