ClassParam::getProps()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 3
dl 0
loc 15
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BEAR\Resource;
4
5
use BackedEnum;
0 ignored issues
show
Bug introduced by
The type BackedEnum 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...
6
use BEAR\Resource\Exception\ParameterEnumTypeException;
7
use BEAR\Resource\Exception\ParameterException;
8
use BEAR\Resource\Exception\ParameterInvalidEnumException;
9
use Override;
10
use Ray\Di\InjectorInterface;
11
use ReflectionClass;
12
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...
13
use ReflectionNamedType;
14
use ReflectionParameter;
15
use UnitEnum;
0 ignored issues
show
Bug introduced by
The type UnitEnum 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...
16
17
use function assert;
18
use function class_exists;
19
use function enum_exists;
0 ignored issues
show
introduced by
The function enum_exists was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
20
use function is_a;
21
use function is_int;
22
use function is_iterable;
23
use function is_string;
24
use function ltrim;
25
use function preg_replace;
26
use function strtolower;
27
28
/** @psalm-import-type Query from Types */
29
final class ClassParam implements ParamInterface
30
{
31
    private readonly string $type;
32
    private readonly bool $isDefaultAvailable;
33
    private readonly mixed $defaultValue; // @phpstan-ignore-line
34
35
    public function __construct(
36
        ReflectionNamedType $type,
37
        ReflectionParameter $parameter,
38
    ) {
39
        $this->type = $type->getName();
0 ignored issues
show
Bug introduced by
The property type is declared read-only in BEAR\Resource\ClassParam.
Loading history...
40
        $this->isDefaultAvailable = $parameter->isDefaultValueAvailable();
0 ignored issues
show
Bug introduced by
The property isDefaultAvailable is declared read-only in BEAR\Resource\ClassParam.
Loading history...
41
        if (! $this->isDefaultAvailable) {
42
            return;
43
        }
44
45
        $this->defaultValue = $parameter->getDefaultValue();
0 ignored issues
show
Bug introduced by
The property defaultValue is declared read-only in BEAR\Resource\ClassParam.
Loading history...
46
    }
47
48
    /**
49
     * {@inheritDoc}
50
     */
51
    #[Override]
52
    public function __invoke(string $varName, array $query, InjectorInterface $injector)
53
    {
54
        try {
55
            /** @psalm-suppress MixedAssignment */
56
            $props = $this->getProps($varName, $query, $injector);
57
        } catch (ParameterException $e) {
58
            if ($this->isDefaultAvailable) {
59
                return $this->defaultValue;
60
            }
61
62
            throw $e;
63
        }
64
65
        assert(class_exists($this->type));
66
        $refClass = new ReflectionClass($this->type);
67
68
        if ($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

68
        if ($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...
69
            return $this->enum($this->type, $props, $varName);
70
        }
71
72
        assert(is_iterable($props));
73
74
        $hasConstructor = (bool) $refClass->getConstructor();
75
        if ($hasConstructor) {
76
            /** @psalm-suppress MixedMethodCall */
77
            return new $this->type(...$props);
78
        }
79
80
        /** @psalm-suppress MixedMethodCall */
81
        $obj = new $this->type();
82
        /** @psalm-suppress MixedAssignment */
83
        foreach ($props as $propName => $propValue) {
84
            $obj->{$propName} = $propValue;
85
        }
86
87
        return $obj;
88
    }
89
90
    /** @param Query $query */
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\Query 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...
91
    private function getProps(string $varName, array $query, InjectorInterface $injector): mixed
92
    {
93
        if (isset($query[$varName])) {
94
            return $query[$varName];
95
        }
96
97
        // try camelCase variable name
98
        $snakeName = ltrim(strtolower((string) preg_replace('/[A-Z]/', '_\0', $varName)), '_');
99
        if (isset($query[$snakeName])) {
100
            return $query[$snakeName];
101
        }
102
103
        unset($injector);
104
105
        throw new ParameterException($varName);
106
    }
107
108
    /**
109
     * @param class-string $type
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
110
     *
111
     * @psalm-suppress MixedArgument
112
     */
113
    private function enum(string $type, mixed $props, string $varName): mixed
114
    {
115
        /** @var class-string<UnitEnum> $type */
116
        $refEnum = new ReflectionEnum($type);
117
        assert(enum_exists($type));
0 ignored issues
show
Bug introduced by
The function enum_exists was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

117
        assert(/** @scrutinizer ignore-call */ enum_exists($type));
Loading history...
118
119
        if (! $refEnum->isBacked()) {
120
            throw new NotBackedEnumException($type);
121
        }
122
123
        assert(is_a($type, BackedEnum::class, true));
124
        if (! (is_int($props) || is_string($props))) {
125
            throw new ParameterEnumTypeException($varName);
126
        }
127
128
        /**  @psalm-suppress MixedAssignment */
129
        $value = $type::tryFrom($props);
130
        if ($value === null) {
131
            throw new ParameterInvalidEnumException($varName);
132
        }
133
134
        return $value;
135
    }
136
}
137