Passed
Pull Request — master (#46)
by
unknown
05:40 queued 03:35
created

ClassConfigFactory   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Test Coverage

Coverage 98.53%

Importance

Changes 4
Bugs 1 Features 0
Metric Value
eloc 65
c 4
b 1
f 0
dl 0
loc 170
ccs 67
cts 68
cp 0.9853
rs 10
wmc 20

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getMethodParameterConfigs() 0 8 2
A getUnionType() 0 8 1
A getClassConfig() 0 17 2
A getMethodConfig() 0 7 1
A getMethodConfigs() 0 8 2
A getMethodParameterTypeConfig() 0 17 3
A getMethodParameterConfig() 0 15 4
A getMethodReturnTypeConfig() 0 18 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Proxy;
6
7
use InvalidArgumentException;
8
use Reflection;
9
use ReflectionClass;
10
use ReflectionException;
11
use ReflectionMethod;
12
use ReflectionNamedType;
13
use ReflectionParameter;
14
use ReflectionUnionType;
15
use Yiisoft\Proxy\Config\ClassConfig;
16
use Yiisoft\Proxy\Config\MethodConfig;
17
use Yiisoft\Proxy\Config\ParameterConfig;
18
use Yiisoft\Proxy\Config\TypeConfig;
19
20
/**
21
 * A factory for creating class configs ({@see ClassConfig}). Uses PHP `Reflection` to get the necessary metadata.
22
 *
23
 * @link https://www.php.net/manual/en/book.reflection.php
24
 */
25
final class ClassConfigFactory
26
{
27
    /**
28
     * Gets single class config based for individual class.
29
     *
30
     * @param string $className Full class or interface name (including namespace).
31
     *
32
     * @throws InvalidArgumentException In case class or interface does not exist.
33
     *
34
     * @return ClassConfig Class config with all related configs (methods, parameters, types) linked.
35
     */
36 9
    public function getClassConfig(string $className): ClassConfig
37
    {
38
        try {
39 9
            $reflection = new ReflectionClass($className);
40 1
        } catch (ReflectionException) {
41 1
            throw new InvalidArgumentException("$className must exist.");
42
        }
43
44 8
        return new ClassConfig(
45 8
            isInterface: $reflection->isInterface(),
46 8
            namespace: $reflection->getNamespaceName(),
47 8
            modifiers: Reflection::getModifierNames($reflection->getModifiers()),
48 8
            name: $reflection->getName(),
49 8
            shortName: $reflection->getShortName(),
50 8
            parent: (string) $reflection->getParentClass(),
51 8
            interfaces: $reflection->getInterfaceNames(),
52 8
            methods: $this->getMethodConfigs($reflection),
53
        );
54
    }
55
56
    /**
57
     * Gets the complete set of method configs for a given class reflection.
58
     *
59
     * @param ReflectionClass $class Reflection of a class.
60
     *
61
     * @return MethodConfig[] List of method configs. The order is maintained.
62
     */
63 8
    private function getMethodConfigs(ReflectionClass $class): array
64
    {
65 8
        $methods = [];
66 8
        foreach ($class->getMethods() as $method) {
67 8
            $methods[$method->getName()] = $this->getMethodConfig($method);
68
        }
69
70 8
        return $methods;
71
    }
72
73
    /**
74
     * Gets single method config for individual method reflection.
75
     *
76
     * @param ReflectionMethod $method Reflection of a method.
77
     *
78
     * @return MethodConfig Single method config.
79
     */
80 8
    private function getMethodConfig(ReflectionMethod $method): MethodConfig
81
    {
82 8
        return new MethodConfig(
83 8
            modifiers: Reflection::getModifierNames($method->getModifiers()),
84 8
            name: $method->getName(),
85 8
            parameters: $this->getMethodParameterConfigs($method),
86 8
            returnType: $this->getMethodReturnTypeConfig($method),
87
        );
88
    }
89
90
    /**
91
     * Gets the complete set of parameter configs for a given method reflection.
92
     *
93
     * @param ReflectionMethod $method Reflection of a method.
94
     *
95
     * @return ParameterConfig[] List of parameter configs. The order is maintained.
96
     */
97 8
    private function getMethodParameterConfigs(ReflectionMethod $method): array
98
    {
99 8
        $parameters = [];
100 8
        foreach ($method->getParameters() as $param) {
101 5
            $parameters[$param->getName()] = $this->getMethodParameterConfig($param);
102
        }
103
104 8
        return $parameters;
105
    }
106
107
    /**
108
     * Gets single parameter config for individual method's parameter reflection.
109
     *
110
     * @param ReflectionParameter $param Reflection of a method's parameter.
111
     *
112
     * @return ParameterConfig Single parameter config.
113
     */
114 5
    private function getMethodParameterConfig(ReflectionParameter $param): ParameterConfig
115
    {
116 5
        return new ParameterConfig(
117 5
            type: $this->getMethodParameterTypeConfig($param),
118 5
            name: $param->getName(),
119 5
            isDefaultValueAvailable: $param->isDefaultValueAvailable(),
120 5
            isDefaultValueConstant: $param->isDefaultValueAvailable()
121 2
                ? $param->isDefaultValueConstant()
122 5
                : null,
123 5
            defaultValueConstantName: $param->isOptional()
124 2
                ? $param->getDefaultValueConstantName()
125 5
                : null,
126 5
            defaultValue: $param->isOptional()
127 2
                ? $param->getDefaultValue()
128 5
                : null,
129
        );
130
    }
131
132
    /**
133
     * Gets single type config for individual method's parameter reflection.
134
     *
135
     * @param ReflectionParameter $param Reflection pf a method's parameter.
136
     *
137
     * @return TypeConfig|null Single type config. `null` is returned when type is not specified.
138
     */
139 5
    private function getMethodParameterTypeConfig(ReflectionParameter $param): ?TypeConfig
140
    {
141 5
        $type = $param->getType();
142 5
        if (!$type) {
143 2
            return null;
144
        }
145
146 5
        if ($type instanceof ReflectionUnionType) {
147 2
            $name = $this->getUnionType($type);
148
        } else {
149
            /** @var ReflectionNamedType $type */
150 5
            $name = $type->getName();
151
        }
152
153 5
        return new TypeConfig(
154
            name: $name,
155 5
            allowsNull: $type->allowsNull(),
156
        );
157
    }
158
159
    /**
160
     * Gets single return type config for individual method reflection.
161
     *
162
     * @param ReflectionMethod $method Reflection of a method.
163
     *
164
     * @return TypeConfig|null Single type config. `null` is returned when return type is not specified.
165
     */
166 8
    private function getMethodReturnTypeConfig(ReflectionMethod $method): ?TypeConfig
167
    {
168 8
        $returnType = $method->getReturnType();
169 8
        if (!$returnType && method_exists($method, 'getTentativeReturnType')) {
170
            $returnType = $method->getTentativeReturnType();
171
        }
172
173 8
        if (!$returnType) {
174 3
            return null;
175
        }
176
177 7
        $name = $returnType instanceof ReflectionUnionType
178 3
            ? $this->getUnionType($returnType)
179 7
            : $returnType->getName();
180
181 7
        return new TypeConfig(
182
            name: $name,
183 7
            allowsNull: $returnType->allowsNull(),
184
        );
185
    }
186
187 5
    private function getUnionType(ReflectionUnionType $type): string
188
    {
189 5
        $types = array_map(
190 5
            static fn (ReflectionNamedType $namedType) => $namedType->getName(),
191 5
            $type->getTypes()
192
        );
193
194 5
        return implode('|', $types);
195
    }
196
}
197