Test Failed
Pull Request — master (#1045)
by Aleksei
06:19
created

ProxyClassRenderer::renderClass()   C

Complexity

Conditions 12
Paths 122

Size

Total Lines 54
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 37
c 1
b 0
f 0
dl 0
loc 54
rs 6.7833
cc 12
nc 122
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Core\Internal\Proxy;
6
7
/**
8
 * @internal
9
 */
10
final class ProxyClassRenderer
11
{
12
    public static function renderClass(\ReflectionClass $type, $className): string
13
    {
14
        $classShortName = \substr($className, \strrpos($className, '\\') + 1);
15
        $classNamespace = \substr($className, 0, \strrpos($className, '\\'));
16
17
        $interface = $type->getName();
18
        $classBody = [];
19
        foreach ($type->getMethods() as $method) {
20
            if ($method->isConstructor()) {
21
                continue;
22
            }
23
24
            $hasRefs = false;
25
            $return = $method->hasReturnType() && (string)$method->getReturnType() === 'void' ? '' : 'return ';
26
            $call = ($method->isStatic() ? '::' : '->') . $method->getName();
27
28
            $args = [];
29
            foreach ($method->getParameters() as $param) {
30
                $hasRefs = $hasRefs || $param->isPassedByReference();
31
                $args[] = ($param->isVariadic() ? '...' : '') . '$'. $param->getName();
32
            }
33
34
            if (!$hasRefs && !$method->isVariadic()) {
35
                $classBody[] = self::renderMethod($method, <<<PHP
36
                    {$return}\\Spiral\\Core\\Internal\\Proxy\\Resolver::resolve('{$interface}')
37
                        {$call}(...\\func_get_args());
38
                PHP);
39
                continue;
40
            }
41
42
            $argsStr = \implode(', ', $args);
43
44
            if ($method->isVariadic()) {
45
                $classBody[] = self::renderMethod($method, <<<PHP
46
                    {$return}\\Spiral\\Core\\Internal\\Proxy\\Resolver::resolve('{$interface}')
47
                        {$call}($argsStr);
48
                PHP);
49
                continue;
50
            }
51
52
            $classBody[] = self::renderMethod($method, <<<PHP
53
                {$return}\\Spiral\\Core\\Internal\\Proxy\\Resolver::resolve('{$interface}')
54
                    {$call}($argsStr, ...\\array_slice(\\func_get_args(), {$method->getNumberOfParameters()}));
55
            PHP);
56
        }
57
        $bodyStr = \implode("\n\n", $classBody);
58
59
        return <<<PHP
60
            namespace $classNamespace;
61
62
            final class $classShortName implements \\$interface {
63
                use \Spiral\Core\Internal\Proxy\ProxyTrait;
64
65
            $bodyStr
66
            }
67
            PHP;
68
    }
69
70
    public static function renderMethod(\ReflectionMethod $m, string $body = ''): string
71
    {
72
        return \sprintf(
73
            "public%s function %s%s(%s)%s {\n%s\n}",
74
            $m->isStatic() ? ' static' : '',
75
            $m->returnsReference() ? '&' : '',
76
            $m->getName(),
77
            \implode(', ', \array_map([self::class, 'renderParameter'], $m->getParameters())),
78
            $m->hasReturnType()
79
                ? ': ' . self::renderParameterTypes($m->getReturnType(), $m->getDeclaringClass())
80
                : '',
81
            $body,
82
        );
83
    }
84
85
    public static function renderParameter(\ReflectionParameter $param): string
86
    {
87
        return \ltrim(\sprintf(
88
            '%s %s%s%s%s',
89
            $param->hasType() ? self::renderParameterTypes($param->getType(), $param->getDeclaringClass()) : '',
90
            $param->isPassedByReference() ? '&' : '',
91
            $param->isVariadic() ? '...' : '',
92
            '$' . $param->getName(),
93
            $param->isOptional() && !$param->isVariadic() ? ' = ' . self::renderDefaultValue($param) : '',
94
        ), ' ');
95
    }
96
97
    public static function renderParameterTypes(\ReflectionType $types, \ReflectionClass $class): string
98
    {
99
        if ($types instanceof \ReflectionNamedType) {
100
            return ($types->allowsNull() && $types->getName() !== 'mixed' ? '?' : '') . ($types->isBuiltin()
101
                ? $types->getName()
102
                : self::normalizeClassType($types, $class));
103
        }
104
105
        [$separator, $types] = match (true) {
106
            $types instanceof \ReflectionUnionType => ['|', $types->getTypes()],
0 ignored issues
show
Bug introduced by
The method getTypes() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionUnionType. ( Ignorable by Annotation )

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

106
            $types instanceof \ReflectionUnionType => ['|', $types->/** @scrutinizer ignore-call */ getTypes()],
Loading history...
107
            $types instanceof \ReflectionIntersectionType => ['&', $types->getTypes()],
0 ignored issues
show
Bug introduced by
The type ReflectionIntersectionType 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...
108
            default => throw new \Exception('Unknown type.'),
109
        };
110
111
        $result = [];
112
        foreach ($types as $type) {
113
            $result[] = $type->isBuiltin()
114
                ? $type->getName()
115
                : self::normalizeClassType($type, $class);
116
        }
117
118
        return \implode($separator, $result);
119
    }
120
121
    public static function renderDefaultValue(\ReflectionParameter $param): string
122
    {
123
        if ($param->isDefaultValueConstant()) {
124
            $result = $param->getDefaultValueConstantName();
125
126
            return \explode('::', $result)[0] === 'self'
127
                ? $result
128
                : '\\' . $result;
129
        }
130
131
        $cut = self::cutDefaultValue($param);
132
133
        return \str_starts_with($cut, 'new ')
134
            ? $cut
135
            : \var_export($param->getDefaultValue(), true);
136
    }
137
138
    public static function normalizeClassType(\ReflectionNamedType $type, \ReflectionClass $class): string
139
    {
140
        return '\\' . ($type->getName() === 'self' ? $class->getName() : $type->getName());
141
    }
142
143
    private static function cutDefaultValue(\ReflectionParameter $param): string
144
    {
145
        $string = (string)$param;
146
147
        return \trim(\substr($string, \strpos($string, '=') + 1, -1));
148
    }
149
}
150