Passed
Pull Request — master (#1045)
by Aleksei
09:49
created

ProxyClassRenderer::renderMethod()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 12
ccs 11
cts 11
cp 1
rs 9.9332
cc 4
nc 1
nop 2
crap 4
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 6
    public static function renderClass(\ReflectionClass $type, $className): string
13
    {
14 6
        if (\str_contains($className, '\\')) {
15 4
            $classShortName = \substr($className, \strrpos($className, '\\') + 1);
16 4
            $classNamespaceStr = 'namespace ' . \substr($className, 0, \strrpos($className, '\\')) . ';';
17
        } else {
18 2
            $classShortName = $className;
19 2
            $classNamespaceStr = '';
20
        }
21
22 6
        $interface = $type->getName();
23 6
        $classBody = [];
24 6
        foreach ($type->getMethods() as $method) {
25 6
            if ($method->isConstructor()) {
26 2
                continue;
27
            }
28
29 4
            $hasRefs = false;
30 4
            $return = $method->hasReturnType() && (string)$method->getReturnType() === 'void' ? '' : 'return ';
31 4
            $call = ($method->isStatic() ? '::' : '->') . $method->getName();
32 4
            $context = $method->isStatic() ? 'null' : '$this->__container_proxy_context';
33
34 4
            $args = [];
35 4
            foreach ($method->getParameters() as $param) {
36 2
                $hasRefs = $hasRefs || $param->isPassedByReference();
37 2
                $args[] = ($param->isVariadic() ? '...' : '') . '$'. $param->getName();
38
            }
39
40 4
            if (!$hasRefs && !$method->isVariadic()) {
41 4
                $classBody[] = self::renderMethod($method, <<<PHP
42 4
                    {$return}\\Spiral\\Core\\Internal\\Proxy\\Resolver::resolve(
43 4
                        '{$interface}',
44 4
                        $context,
45 4
                    ){$call}(...\\func_get_args());
46 4
                PHP);
47 4
                continue;
48
            }
49
50 1
            $argsStr = \implode(', ', $args);
51
52 1
            if ($method->isVariadic()) {
53 1
                $classBody[] = self::renderMethod($method, <<<PHP
54 1
                    {$return}\\Spiral\\Core\\Internal\\Proxy\\Resolver::resolve(
55 1
                        '{$interface}',
56 1
                        $context,
57 1
                    ){$call}($argsStr);
58 1
                PHP);
59 1
                continue;
60
            }
61
62 1
            $classBody[] = self::renderMethod($method, <<<PHP
63 1
                {$return}\\Spiral\\Core\\Internal\\Proxy\\Resolver::resolve(
64 1
                        '{$interface}',
65 1
                        $context,
66 1
                    ){$call}($argsStr, ...\\array_slice(\\func_get_args(), {$method->getNumberOfParameters()}));
67 1
            PHP);
68
        }
69 6
        $bodyStr = \implode("\n\n", $classBody);
70
71 6
        return <<<PHP
72 6
            $classNamespaceStr
73
74 6
            final class $classShortName implements \\$interface {
75
                use \Spiral\Core\Internal\Proxy\ProxyTrait;
76
77 6
            $bodyStr
78
            }
79 6
            PHP;
80
    }
81
82 9
    public static function renderMethod(\ReflectionMethod $m, string $body = ''): string
83
    {
84 9
        return \sprintf(
85 9
            "public%s function %s%s(%s)%s {\n%s\n}",
86 9
            $m->isStatic() ? ' static' : '',
87 9
            $m->returnsReference() ? '&' : '',
88 9
            $m->getName(),
89 9
            \implode(', ', \array_map([self::class, 'renderParameter'], $m->getParameters())),
90 9
            $m->hasReturnType()
91 7
                ? ': ' . self::renderParameterTypes($m->getReturnType(), $m->getDeclaringClass())
92 9
                : '',
93 9
            $body,
94 9
        );
95
    }
96
97 27
    public static function renderParameter(\ReflectionParameter $param): string
98
    {
99 27
        return \ltrim(\sprintf(
100 27
            '%s %s%s%s%s',
101 27
            $param->hasType() ? self::renderParameterTypes($param->getType(), $param->getDeclaringClass()) : '',
102 27
            $param->isPassedByReference() ? '&' : '',
103 27
            $param->isVariadic() ? '...' : '',
104 27
            '$' . $param->getName(),
105 27
            $param->isOptional() && !$param->isVariadic() ? ' = ' . self::renderDefaultValue($param) : '',
106 27
        ), ' ');
107
    }
108
109 28
    public static function renderParameterTypes(\ReflectionType $types, \ReflectionClass $class): string
110
    {
111 28
        if ($types instanceof \ReflectionNamedType) {
112 21
            return ($types->allowsNull() && $types->getName() !== 'mixed' ? '?' : '') . ($types->isBuiltin()
113 18
                ? $types->getName()
114 21
                : self::normalizeClassType($types, $class));
115
        }
116
117 8
        [$separator, $types] = match (true) {
118 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

118
            $types instanceof \ReflectionUnionType => ['|', $types->/** @scrutinizer ignore-call */ getTypes()],
Loading history...
119 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...
120 8
            default => throw new \Exception('Unknown type.'),
121 8
        };
122
123 8
        $result = [];
124 8
        foreach ($types as $type) {
125 8
            $result[] = $type->isBuiltin()
126 7
                ? $type->getName()
127 3
                : self::normalizeClassType($type, $class);
128
        }
129
130 8
        return \implode($separator, $result);
131
    }
132
133 1
    public static function renderDefaultValue(\ReflectionParameter $param): string
134
    {
135 1
        if ($param->isDefaultValueConstant()) {
136
            $result = $param->getDefaultValueConstantName();
137
138
            return \explode('::', $result)[0] === 'self'
139
                ? $result
140
                : '\\' . $result;
141
        }
142
143 1
        $cut = self::cutDefaultValue($param);
144
145 1
        return \str_starts_with($cut, 'new ')
146
            ? $cut
147 1
            : \var_export($param->getDefaultValue(), true);
148
    }
149
150 1
    public static function normalizeClassType(\ReflectionNamedType $type, \ReflectionClass $class): string
151
    {
152 1
        return '\\' . ($type->getName() === 'self' ? $class->getName() : $type->getName());
153
    }
154
155 1
    private static function cutDefaultValue(\ReflectionParameter $param): string
156
    {
157 1
        $string = (string)$param;
158
159 1
        return \trim(\substr($string, \strpos($string, '=') + 1, -1));
160
    }
161
}
162