Completed
Pull Request — master (#339)
by Alexander
01:44
created

AbstractProxy::prepareArgsLine()   C

Complexity

Conditions 8
Paths 54

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 8

Importance

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