Issues (55)

src/ClassParam.php (9 issues)

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

64
        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...
65
            return $this->enum($this->type, $props, $varName);
66
        }
67
68
        assert(is_iterable($props));
69
70
        $hasConstructor = (bool) $refClass->getConstructor();
71
        if ($hasConstructor) {
72
            /** @psalm-suppress MixedMethodCall */
73
            return new $this->type(...$props);
74
        }
75
76
        /** @psalm-suppress MixedMethodCall */
77
        $obj = new $this->type();
78
        /** @psalm-suppress MixedAssignment */
79
        foreach ($props as $propName => $propValue) {
80
            $obj->{$propName} = $propValue;
81
        }
82
83
        return $obj;
84
    }
85
86
    /**
87
     * @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...
88
     *
89
     * @psalm-suppress MixedArgument
90
     */
91
    private function enum(string $type, mixed $props, string $varName): mixed
92
    {
93
        /** @var class-string<UnitEnum> $type */
94
        $refEnum = new ReflectionEnum($type);
95
        assert(enum_exists($type));
0 ignored issues
show
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

95
        assert(/** @scrutinizer ignore-call */ enum_exists($type));
Loading history...
96
97
        if (! $refEnum->isBacked()) {
98
            throw new NotBackedEnumException($type);
99
        }
100
101
        assert(is_a($type, BackedEnum::class, true));
102
        if (! (is_int($props) || is_string($props))) {
103
            throw new ParameterEnumTypeException($varName);
104
        }
105
106
        /**  @psalm-suppress MixedAssignment */
107
        $value = $type::tryFrom($props);
108
        if ($value === null) {
109
            throw new ParameterInvalidEnumException($varName);
110
        }
111
112
        return $value;
113
    }
114
}
115