Passed
Pull Request — master (#1045)
by
unknown
19:48 queued 09:50
created

ProxyClassRenderer::renderClass()   F

Complexity

Conditions 16
Paths 1936

Size

Total Lines 96
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 70
CRAP Score 16

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 64
c 1
b 0
f 0
dl 0
loc 96
ccs 70
cts 70
cp 1
rs 1.4
cc 16
nc 1936
nop 4
crap 16

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
    /**
13
     * @param \ReflectionClass $type Interface reflection.
14
     * @param string $className Class name to use in the generated code.
15
     * @param bool $defineOverload Define __call() and __callStatic() methods.
16
     * @param bool $attachContainer Attach container to the proxy.
17
     *
18
     * @return non-empty-string PHP code
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
19
     */
20 6
    public static function renderClass(
21
        \ReflectionClass $type,
22
        string $className,
23
        bool $defineOverload = false,
24
        bool $attachContainer = false,
25
    ): string {
26 6
        $traits = $defineOverload ? [
27 6
            MagicCallTrait::class,
28 6
        ] : [];
29
30 6
        if (\str_contains($className, '\\')) {
31 4
            $classShortName = \substr($className, \strrpos($className, '\\') + 1);
32 4
            $classNamespaceStr = 'namespace ' . \substr($className, 0, \strrpos($className, '\\')) . ';';
33
        } else {
34 2
            $classShortName = $className;
35 2
            $classNamespaceStr = '';
36
        }
37
38 6
        $interface = $type->getName();
39 6
        $classBody = [];
40 6
        foreach ($type->getMethods() as $method) {
41 6
            if ($method->isConstructor()) {
42 2
                continue;
43
            }
44
45 4
            $hasRefs = false;
46 4
            $return = $method->hasReturnType() && (string)$method->getReturnType() === 'void' ? '' : 'return ';
47 4
            $call = ($method->isStatic() ? '::' : '->') . $method->getName();
48 4
            $context = $method->isStatic() ? 'null' : '$this->__container_proxy_context';
49 4
            $containerStr = match (false) {
50 4
                $attachContainer => 'null',
51
                /** @see \Spiral\Core\Internal\Proxy\ProxyTrait::__container_proxy_container */
52 4
                $method->isStatic() => '$this->__container_proxy_container',
53 4
                default => \sprintf(
54 4
                    'throw new \Spiral\Core\Exception\Container\ContainerException(\'%s\')',
55 4
                    'Static method call is not allowed on a Proxy that was created without dynamic scope.',
56 4
                ),
57 4
            };
58 4
            $resolveStr = <<<PHP
59 4
                \\Spiral\\Core\\Internal\\Proxy\\Resolver::resolve(
60 4
                    '{$interface}',
61 4
                    {$context},
62 4
                    {$containerStr},
63
                )
64 4
                PHP;
65
66 4
            $args = [];
67 4
            foreach ($method->getParameters() as $param) {
68 2
                $hasRefs = $hasRefs || $param->isPassedByReference();
69 2
                $args[] = ($param->isVariadic() ? '...' : '') . '$' . $param->getName();
70
            }
71
72 4
            if (!$hasRefs && !$method->isVariadic()) {
73 4
                $classBody[] = self::renderMethod(
74 4
                    $method,
75 4
                    <<<PHP
76 4
                    {$return}{$resolveStr}{$call}(...\\func_get_args());
77 4
                PHP
78 4
                );
79 4
                continue;
80
            }
81
82 1
            $argsStr = \implode(', ', $args);
83
84 1
            if ($method->isVariadic()) {
85 1
                $classBody[] = self::renderMethod(
86 1
                    $method,
87 1
                    <<<PHP
88 1
                    {$return}{$resolveStr}{$call}($argsStr);
89 1
                PHP
90 1
                );
91 1
                continue;
92
            }
93
94 1
            $countParams = $method->getNumberOfParameters();
95 1
            $classBody[] = self::renderMethod(
96 1
                $method,
97 1
                <<<PHP
98 1
                {$return}{$resolveStr}{$call}($argsStr, ...\\array_slice(\\func_get_args(), {$countParams}));
99 1
            PHP
100 1
            );
101
        }
102 6
        $bodyStr = \implode("\n\n", $classBody);
103
104 6
        $traitsStr = $traits === [] ? '' : \implode(
105 6
            "\n    ",
106 6
            \array_map(fn (string $trait): string => 'use \\' . \ltrim($trait, '\\') . ';', $traits)
107 6
        );
108 6
        return <<<PHP
109 6
            $classNamespaceStr
110
111 6
            final class $classShortName implements \\$interface {
112
                use \Spiral\Core\Internal\Proxy\ProxyTrait;
113 6
                $traitsStr
114
115 6
            $bodyStr
116
            }
117 6
            PHP;
118
    }
119
120 9
    public static function renderMethod(\ReflectionMethod $m, string $body = ''): string
121
    {
122 9
        return \sprintf(
123 9
            "public%s function %s%s(%s)%s {\n%s\n}",
124 9
            $m->isStatic() ? ' static' : '',
125 9
            $m->returnsReference() ? '&' : '',
126 9
            $m->getName(),
127 9
            \implode(', ', \array_map([self::class, 'renderParameter'], $m->getParameters())),
128 9
            $m->hasReturnType()
129 7
                ? ': ' . self::renderParameterTypes($m->getReturnType(), $m->getDeclaringClass())
130 9
                : '',
131 9
            $body,
132 9
        );
133
    }
134
135 27
    public static function renderParameter(\ReflectionParameter $param): string
136
    {
137 27
        return \ltrim(
138 27
            \sprintf(
139 27
                '%s %s%s%s%s',
140 27
                $param->hasType() ? self::renderParameterTypes($param->getType(), $param->getDeclaringClass()) : '',
141 27
                $param->isPassedByReference() ? '&' : '',
142 27
                $param->isVariadic() ? '...' : '',
143 27
                '$' . $param->getName(),
144 27
                $param->isOptional() && !$param->isVariadic() ? ' = ' . self::renderDefaultValue($param) : '',
145 27
            ),
146 27
            ' '
147 27
        );
148
    }
149
150 28
    public static function renderParameterTypes(\ReflectionType $types, \ReflectionClass $class): string
151
    {
152 28
        if ($types instanceof \ReflectionNamedType) {
153 21
            return ($types->allowsNull() && $types->getName() !== 'mixed' ? '?' : '') . ($types->isBuiltin()
154 18
                    ? $types->getName()
155 21
                    : self::normalizeClassType($types, $class));
156
        }
157
158 8
        [$separator, $types] = match (true) {
159 8
            $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

159
            $types instanceof \ReflectionUnionType => ['|', $types->/** @scrutinizer ignore-call */ getTypes()],
Loading history...
160 8
            $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...
161 8
            default => throw new \Exception('Unknown type.'),
162 8
        };
163
164 8
        $result = [];
165 8
        foreach ($types as $type) {
166 8
            $result[] = $type->isBuiltin()
167 7
                ? $type->getName()
168 3
                : self::normalizeClassType($type, $class);
169
        }
170
171 8
        return \implode($separator, $result);
172
    }
173
174 1
    public static function renderDefaultValue(\ReflectionParameter $param): string
175
    {
176 1
        if ($param->isDefaultValueConstant()) {
177
            $result = $param->getDefaultValueConstantName();
178
179
            return \explode('::', $result)[0] === 'self'
180
                ? $result
181
                : '\\' . $result;
182
        }
183
184 1
        $cut = self::cutDefaultValue($param);
185
186 1
        return \str_starts_with($cut, 'new ')
187
            ? $cut
188 1
            : \var_export($param->getDefaultValue(), true);
189
    }
190
191 1
    public static function normalizeClassType(\ReflectionNamedType $type, \ReflectionClass $class): string
192
    {
193 1
        return '\\' . ($type->getName() === 'self' ? $class->getName() : $type->getName());
194
    }
195
196 1
    private static function cutDefaultValue(\ReflectionParameter $param): string
197
    {
198 1
        $string = (string)$param;
199
200 1
        return \trim(\substr($string, \strpos($string, '=') + 1, -1));
201
    }
202
}
203