Issues (101)

src/di/Name.php (6 issues)

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 get_class;
18
use function is_string;
19
use function preg_match;
20
use function substr;
21
use function trim;
22
23
/**
24
 * @psalm-import-type ParameterNameMapping from Types
25
 */
26
final class Name
27
{
28
    /**
29
     * 'Unnamed' name
30
     */
31
    public const ANY = '';
32
33
    /** @var string */
34
    private $name = '';
35
36
    /**
37
     * Named database
38
     *
39
     * format: array<varName, NamedName>
40
     *
41
     * @var ParameterNameMapping
0 ignored issues
show
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...
42
     */
43
    private $names;
44
45
    /**
46
     * @param string|ParameterNameMapping|null $name
47
     */
48
    public function __construct($name = null)
49
    {
50
        if ($name === null) {
51
            return;
52
        }
53
54
        if (is_string($name)) {
55
            $this->setName($name);
56
57
            return;
58
        }
59
60
        $this->names = $name;
61
    }
62
63
    /**
64
     * Create instance from PHP8 attributes
65
     *
66
     * psalm does not know ReflectionAttribute?? PHPStan produces no type error here.
67
     */
68
    public static function withAttributes(ReflectionMethod $method): ?self
69
    {
70
        $params = $method->getParameters();
71
        $names = [];
72
        foreach ($params as $param) {
73
            /** @var array<ReflectionAttribute> $attributes */
74
            $attributes = $param->getAttributes();
75
            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...
76
                $name = self::getName($attributes);
77
                $names[$param->name] = $name;
78
            }
79
        }
80
81
        if ($names) {
82
            return new self($names);
0 ignored issues
show
$names of type array is incompatible with the type Ray\Di\ParameterNameMapping|null|string expected by parameter $name of Ray\Di\Name::__construct(). ( Ignorable by Annotation )

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

82
            return new self(/** @scrutinizer ignore-type */ $names);
Loading history...
83
        }
84
85
        return null;
86
    }
87
88
    /**
89
     * @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...
90
     *
91
     * @throws ReflectionException
92
     *
93
     * @psalm-suppress MixedAssignment
94
     * @psalm-suppress MixedArgument
95
     */
96
    private static function getName(array $attributes): string
97
    {
98
        $refAttribute = $attributes[0];
99
        $attribute = $refAttribute->newInstance();
100
        if ($attribute instanceof Named) {
101
            return $attribute->value;
102
        }
103
104
        $isQualifier = (bool) (new ReflectionClass($attribute))->getAttributes(Qualifier::class);
105
        if ($isQualifier) {
106
            return get_class($attribute);
107
        }
108
109
        return '';
110
    }
111
112
    public function __invoke(ReflectionParameter $parameter): string
113
    {
114
        // single variable named binding
115
        if ($this->name) {
116
            return $this->name;
117
        }
118
119
        $parameterName = $parameter->name;
120
121
        // multiple variable named binding
122
        return $this->names[$parameterName] ?? $this->names[self::ANY] ?? self::ANY;
123
    }
124
125
    private function setName(string $name): void
126
    {
127
        // annotation
128
        if (class_exists($name, false)) {
129
            $this->name = $name;
130
131
            return;
132
        }
133
134
        // single name
135
        // @Named(name)
136
        if ($name === self::ANY || preg_match('/^\w+$/', $name)) {
137
            $this->name = $name;
138
139
            return;
140
        }
141
142
        // name list
143
        // @Named(varName1=name1, varName2=name2)]
144
        $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...
145
    }
146
147
    /**
148
     * @return ParameterNameMapping
149
     *
150
     * @psalm-pure
151
     */
152
    private function parseName(string $name): array
153
    {
154
        $names = [];
155
        $keyValues = explode(',', $name);
156
        foreach ($keyValues as $keyValue) {
157
            $exploded = explode('=', $keyValue);
158
            if (isset($exploded[1])) {
159
                [$key, $value] = $exploded;
160
                if (isset($key[0]) && $key[0] === '$') {
161
                    $key = substr($key, 1);
162
                }
163
164
                $trimedKey = trim((string) $key);
165
166
                $names[$trimedKey] = trim($value);
167
            }
168
        }
169
170
        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...
171
    }
172
}
173