Completed
Pull Request — 2.x (#381)
by Alexander
03:09
created

AbstractProxy::getOverriddenFunction()   F

Complexity

Conditions 10
Paths 272

Size

Total Lines 35
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 10

Importance

Changes 0
Metric Value
dl 0
loc 35
ccs 26
cts 26
cp 1
rs 3.1304
c 0
b 0
f 0
cc 10
eloc 27
nc 272
nop 2
crap 10

How to fix   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
 * Go! AOP framework
4
 *
5
 * @copyright Copyright 2012, Lisachenko Alexander <[email protected]>
6
 *
7
 * This source file is subject to the license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace Go\Proxy;
12
13
use Reflection;
14
use ReflectionFunctionAbstract;
15
use ReflectionMethod;
16
use ReflectionParameter;
17
18
/**
19
 * Abstract class for building different proxies
20
 */
21
abstract class AbstractProxy
22
{
23
24
    /**
25
     * Indent for source code
26
     *
27
     * @var int
28
     */
29
    protected $indent = 4;
30
31
    /**
32
     * List of advices that are used for generation of child
33
     *
34
     * @var array
35
     */
36
    protected $advices = [];
37
38
    /**
39
     * PHP expression string for accessing LSB information
40
     *
41
     * @var string
42
     */
43
    protected static $staticLsbExpression = 'static::class';
44
45
    /**
46
     * Constructs an abstract proxy class
47
     *
48
     * @param array $advices List of advices
49
     */
50 4
    public function __construct(array $advices = [])
51
    {
52 4
        $this->advices = $this->flattenAdvices($advices);
53 4
    }
54
55
    /**
56
     * Returns text representation of class
57
     *
58
     * @return string
59
     */
60
    abstract public function __toString();
61
62
    /**
63
     * Indent block of code
64
     *
65
     * @param string $text Non-indented text
66
     *
67
     * @return string Indented text
68
     */
69 4
    protected function indent($text)
70
    {
71 4
        $pad   = str_pad('', $this->indent, ' ');
72 4
        $lines = array_map(function ($line) use ($pad) {
73 4
            return $pad . $line;
74 4
        }, explode("\n", $text));
75
76 4
        return implode("\n", $lines);
77
    }
78
79
    /**
80
     * Returns list of string representation of parameters
81
     *
82
     * @param array|ReflectionParameter[] $parameters List of parameters
83
     *
84
     * @return array
85
     */
86 4
    protected function getParameters(array $parameters)
87
    {
88 4
        $parameterDefinitions = [];
89 4
        foreach ($parameters as $parameter) {
90 2
            $parameterDefinitions[] = $this->getParameterCode($parameter);
91
        }
92
93 4
        return $parameterDefinitions;
94
    }
95
96
    /**
97
     * Return string representation of parameter
98
     *
99
     * @param ReflectionParameter $parameter Reflection parameter
100
     *
101
     * @return string
102
     */
103 2
    protected function getParameterCode(ReflectionParameter $parameter)
104
    {
105 2
        $type = '';
106 2
        if (PHP_VERSION_ID >= 70000) {
107 2
            $reflectionType = $parameter->getType();
108 2
            if ($reflectionType !== null) {
109 2
                $nullablePrefix = (PHP_VERSION_ID >= 70100 && $reflectionType->allowsNull()) ? '?' : '';
110 2
                $typeName       = (string) $reflectionType;
111 2
                $isBuiltin      = $reflectionType->isBuiltin();
112 2
                $isSpecialType  = $typeName === 'self' || $typeName === 'parent';
113 2
                $nsPrefix       = ($isBuiltin || $isSpecialType) ? '' : '\\';
114 2
                $type           = $nullablePrefix . $nsPrefix . $typeName;
115
            }
116
        } else {
117
            if ($parameter->isArray()) {
118
                $type = 'array';
119
            } elseif ($parameter->isCallable()) {
120
                $type = 'callable';
121
            } elseif ($parameter->getClass()) {
122
                $type = '\\' . $parameter->getClass()->name;
123
            }
124
        }
125 2
        $defaultValue = null;
126 2
        $isDefaultValueAvailable = $parameter->isDefaultValueAvailable();
127 2
        if ($isDefaultValueAvailable) {
128 1
            $defaultValue = var_export($parameter->getDefaultValue(), true);
129 2
        } elseif ($parameter->isOptional() && !$parameter->isVariadic()) {
130
            $defaultValue = 'null';
131
        }
132
        $code = (
133 2
            ($type ? "$type " : '') . // Typehint
134 2
            ($parameter->isPassedByReference() ? '&' : '') . // By reference sign
135 2
            ($parameter->isVariadic() ? '...' : '') . // Variadic symbol
136 2
            '$' . // Variable symbol
137 2
            $parameter->name . // Name of the argument
138 2
            ($defaultValue !== null ? (' = ' . $defaultValue) : '') // Default value if present
139
        );
140
141 2
        return $code;
142
    }
143
144
    /**
145
     * Replace concrete advices with list of ids
146
     *
147
     * @param array $advices
148
     *
149
     * @return array flatten list of advices
150
     */
151 4
    private function flattenAdvices(array $advices)
152
    {
153 4
        $flattenAdvices = [];
154 4
        foreach ($advices as $type => $typedAdvices) {
155 4
            foreach ($typedAdvices as $name => $concreteAdvices) {
156 4
                if (is_array($concreteAdvices)) {
157 4
                    $flattenAdvices[$type][$name] = array_keys($concreteAdvices);
158
                }
159
            }
160
        }
161
162 4
        return $flattenAdvices;
163
    }
164
165
    /**
166
     * Prepares a line with args from the method definition
167
     *
168
     * @param ReflectionFunctionAbstract $functionLike
169
     *
170
     * @return string
171
     */
172 4
    protected function prepareArgsLine(ReflectionFunctionAbstract $functionLike)
173
    {
174 4
        $argumentsPart = [];
175 4
        $arguments     = [];
176 4
        $hasOptionals  = false;
177
178 4
        foreach ($functionLike->getParameters() as $parameter) {
179 2
            $byReference  = ($parameter->isPassedByReference() && !$parameter->isVariadic()) ? '&' : '';
180 2
            $hasOptionals = $hasOptionals || $parameter->isOptional();
181
182 2
            $arguments[] = $byReference . '$' . $parameter->name;
183
        }
184
185 4
        $isVariadic = $functionLike->isVariadic();
186 4
        if ($isVariadic) {
187 1
            $argumentsPart[] = array_pop($arguments);
188
        }
189 4
        if (!empty($arguments)) {
190
            // Unshifting to keep correct order
191 2
            $argumentLine = '[' . implode(', ', $arguments) . ']';
192 2
            if ($hasOptionals) {
193 1
                $argumentLine = "\\array_slice($argumentLine, 0, \\func_num_args())";
194
            }
195 2
            array_unshift($argumentsPart, $argumentLine);
196
        }
197
198 4
        return implode(', ', $argumentsPart);
199
    }
200
201
    /**
202
     * Creates a function code from Reflection
203
     *
204
     * @param ReflectionFunctionAbstract $functionLike Reflection for method
205
     * @param string $body Body of method
206
     *
207
     * @return string
208
     */
209 4
    protected function getOverriddenFunction(ReflectionFunctionAbstract $functionLike, $body)
210
    {
211 4
        $reflectionReturnType = PHP_VERSION_ID >= 70000 ? $functionLike->getReturnType() : '';
212 4
        $modifiersLine        = '';
213 4
        if ($reflectionReturnType) {
214 1
            $nullablePrefix = $reflectionReturnType->allowsNull() ? '?' : '';
215 1
            $typeName       = (string) $reflectionReturnType;
216 1
            $isBuiltin      = $reflectionReturnType->isBuiltin();
217 1
            $isSpecialType  = $typeName === 'self' || $typeName === 'parent';
218 1
            $nsPrefix       = ($isBuiltin || $isSpecialType) ? '' : '\\';
219
220 1
            $reflectionReturnType = $nullablePrefix . $nsPrefix . $typeName;
221
        }
222 4
        if ($functionLike instanceof ReflectionMethod) {
223 4
            $modifiersLine = implode(' ', Reflection::getModifierNames($functionLike->getModifiers()));
224
        }
225
226
        $code = (
227 4
            preg_replace('/ {4}|\t/', '', $functionLike->getDocComment()) . "\n" . // Original Doc-block
228 4
            $modifiersLine . // List of modifiers (for methods)
229 4
            ' function ' . // 'function' keyword
230 4
            ($functionLike->returnsReference() ? '&' : '') . // By reference symbol
231 4
            $functionLike->name . // Name of the function
232 4
            '(' . // Start of parameters list
233 4
            implode(', ', $this->getParameters($functionLike->getParameters())) . // List of parameters
234 4
            ')' . // End of parameters list
235 4
            ($reflectionReturnType ? " : $reflectionReturnType" : '') . // Return type, if present
236 4
            "\n" .
237 4
            "{\n" . // Start of method body
238 4
            $this->indent($body) . "\n" . // Method body
239 4
            "}\n" // End of method body
240
        );
241
242 4
        return $code;
243
    }
244
}
245