Passed
Push — feature/restore-toConstructor-... ( f15eed )
by Akihito
02:43
created

Name::withAttributes()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 10
c 2
b 0
f 0
dl 0
loc 18
rs 9.9332
cc 4
nc 6
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Di;
6
7
use Ray\Di\Di\Named;
8
use Ray\Di\Di\Qualifier;
9
use ReflectionAttribute;
10
use ReflectionClass;
11
use ReflectionException;
12
use ReflectionMethod;
13
use ReflectionParameter;
14
15
use function class_exists;
16
use function explode;
17
use function is_string;
18
use function preg_match;
19
use function substr;
20
use function trim;
21
22
/**
23
 * @psalm-import-type ParameterNameMapping from Types
24
 */
25
final class Name
26
{
27
    /**
28
     * 'Unnamed' name
29
     */
30
    public const ANY = '';
31
32
    private string $name = '';
33
34
    /**
35
     * Named database
36
     *
37
     * format: array<varName, NamedName>
38
     *
39
     * @var ParameterNameMapping
0 ignored issues
show
Bug introduced by
The type Ray\Di\ParameterNameMapping 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...
40
     */
41
    private array $names = [];
42
43
    /**
44
     * @param string|ParameterNameMapping $name
45
     */
46
    public function __construct(string|array $name)
47
    {
48
        if (is_string($name)) {
0 ignored issues
show
introduced by
The condition is_string($name) is always false.
Loading history...
49
            $this->setName($name);
50
51
            return;
52
        }
53
54
        $this->names = $name;
0 ignored issues
show
Documentation Bug introduced by
It seems like $name of type array is incompatible with the declared type Ray\Di\ParameterNameMapping of property $names.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
55
    }
56
57
    /**
58
     * Create instance from PHP8 attributes
59
     *
60
     * psalm does not know ReflectionAttribute?? PHPStan produces no type error here.
61
     */
62
    public static function withAttributes(ReflectionMethod $method): ?self
63
    {
64
        $params = $method->getParameters();
65
        $names = [];
66
        foreach ($params as $param) {
67
            /** @var array<ReflectionAttribute> $attributes */
68
            $attributes = $param->getAttributes();
69
            if ($attributes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $attributes of type ReflectionAttribute[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
70
                $name = self::getName($attributes);
71
                $names[$param->name] = $name;
72
            }
73
        }
74
75
        if ($names !== []) {
76
            return new self($names);
77
        }
78
79
        return null;
80
    }
81
82
    /**
83
     * @param non-empty-array<ReflectionAttribute> $attributes
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-array<ReflectionAttribute> at position 0 could not be parsed: Unknown type name 'non-empty-array' at position 0 in non-empty-array<ReflectionAttribute>.
Loading history...
84
     *
85
     * @throws ReflectionException
86
     *
87
     * @psalm-suppress MixedAssignment
88
     * @psalm-suppress MixedArgument
89
     */
90
    private static function getName(array $attributes): string
91
    {
92
        $refAttribute = $attributes[0];
93
        $attribute = $refAttribute->newInstance();
94
        if ($attribute instanceof Named) {
95
            return $attribute->value;
96
        }
97
98
        $isQualifier = (bool) (new ReflectionClass($attribute))->getAttributes(Qualifier::class);
99
        if ($isQualifier) {
100
            return $attribute::class;
101
        }
102
103
        return '';
104
    }
105
106
    public function __invoke(ReflectionParameter $parameter): string
107
    {
108
        // single variable named binding
109
        if ($this->name) {
110
            return $this->name;
111
        }
112
113
        $parameterName = $parameter->name;
114
115
        // multiple variable named binding
116
        return $this->names[$parameterName] ?? $this->names[self::ANY] ?? self::ANY;
117
    }
118
119
    private function setName(string $name): void
120
    {
121
        // annotation
122
        if (class_exists($name, false)) {
123
            $this->name = $name;
124
125
            return;
126
        }
127
128
        // single name
129
        // @Named(name)
130
        if ($name === self::ANY || preg_match('/^\w+$/', $name)) {
131
            $this->name = $name;
132
133
            return;
134
        }
135
136
        // name list (backward compatibility)
137
        // @Named(varName1=name1, varName2=name2) or toConstructor string format
138
        $this->names = $this->parseName($name);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->parseName($name) of type array or array is incompatible with the declared type Ray\Di\ParameterNameMapping of property $names.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
139
    }
140
141
    /**
142
     * Parse "key=value,key=value" format (backward compatibility)
143
     *
144
     * @return ParameterNameMapping
145
     *
146
     * @psalm-pure
147
     */
148
    private function parseName(string $name): array
149
    {
150
        $names = [];
151
        $keyValues = explode(',', $name);
152
        foreach ($keyValues as $keyValue) {
153
            $exploded = explode('=', $keyValue);
154
            if (isset($exploded[1])) {
155
                [$key, $value] = $exploded;
156
                if (isset($key[0]) && $key[0] === '$') {
157
                    $key = substr($key, 1);
158
                }
159
160
                $trimedKey = trim($key);
161
162
                $names[$trimedKey] = trim($value);
163
            }
164
        }
165
166
        return $names;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $names returns the type array which is incompatible with the documented return type Ray\Di\ParameterNameMapping.
Loading history...
167
    }
168
}
169