Completed
Pull Request — master (#309)
by Alexander
18:07 queued 16:03
created

AbstractProxy::getParameters()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
c 0
b 0
f 0
ccs 5
cts 5
cp 1
rs 9.6666
cc 2
eloc 5
nc 2
nop 1
crap 2
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 6
    public function __construct(array $advices = [])
51
    {
52 6
        $this->advices = $this->flattenAdvices($advices);
53 6
    }
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 6
    protected function indent($text)
70
    {
71 6
        $pad   = str_pad('', $this->indent, ' ');
72 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...
73 6
            return $pad . $line;
74 6
        }, explode("\n", $text));
75
76 6
        return join("\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 6
    protected function getParameters(array $parameters)
87
    {
88 6
        $parameterDefinitions = [];
89 6
        foreach ($parameters as $parameter) {
90 3
            $parameterDefinitions[] = $this->getParameterCode($parameter);
91
        }
92
93 6
        return $parameterDefinitions;
94
    }
95
96
    /**
97
     * Return string representation of parameter
98
     *
99
     * @param ReflectionParameter $parameter Reflection parameter
100
     *
101
     * @return string
102
     */
103 3
    protected function getParameterCode(ReflectionParameter $parameter)
104
    {
105 3
        $type = '';
106 3
        if (PHP_VERSION_ID >= 70000) {
107 3
            $reflectionType = $parameter->getType();
108 3
            if ($reflectionType) {
109 1
                $nullablePrefix = $reflectionType->allowsNull() ? '?' : '';
110 1
                $nsPrefix       = $reflectionType->isBuiltin() ? '' : '\\';
111 3
                $type           = $nullablePrefix . $nsPrefix . (string) $reflectionType;
112
            }
113
        } else {
114
            if ($parameter->isArray()) {
115
                $type = 'array';
116
            } elseif ($parameter->isCallable()) {
117
                $type = 'callable';
118
            } elseif ($parameter->getClass()) {
119
                $type = '\\' . $parameter->getClass()->name;
120
            }
121
        }
122 3
        $defaultValue = null;
123 3
        $isDefaultValueAvailable = $parameter->isDefaultValueAvailable();
124 3
        if ($isDefaultValueAvailable) {
125 2
            $defaultValue = var_export($parameter->getDefaultValue(), true);
126 3
        } elseif ($parameter->isOptional() && !$parameter->isVariadic()) {
127
            $defaultValue = 'null';
128
        }
129
        $code = (
130 3
            ($type ? "$type " : '') . // Typehint
131 3
            ($parameter->isPassedByReference() ? '&' : '') . // By reference sign
132 3
            ($parameter->isVariadic() ? '...' : '') . // Variadic symbol
133 3
            '$' . // Variable symbol
134 3
            ($parameter->name) . // Name of the argument
135 3
            ($defaultValue !== null ? (" = " . $defaultValue) : '') // Default value if present
136
        );
137
138 3
        return $code;
139
    }
140
141
    /**
142
     * Replace concrete advices with list of ids
143
     *
144
     * @param $advices
145
     *
146
     * @return array flatten list of advices
147
     */
148 6
    private function flattenAdvices($advices)
149
    {
150 6
        $flattenAdvices = [];
151 6
        foreach ($advices as $type => $typedAdvices) {
152 6
            foreach ($typedAdvices as $name => $concreteAdvices) {
153 6
                if (is_array($concreteAdvices)) {
154 6
                    $flattenAdvices[$type][$name] = array_keys($concreteAdvices);
155
                }
156
            }
157
        }
158
159 6
        return $flattenAdvices;
160
    }
161
162
    /**
163
     * Prepares a line with args from the method definition
164
     *
165
     * @param ReflectionFunctionAbstract $functionLike
166
     *
167
     * @return string
168
     */
169 6
    protected function prepareArgsLine(ReflectionFunctionAbstract $functionLike)
170
    {
171 6
        $argumentsPart = [];
172 6
        $arguments     = [];
173 6
        $hasOptionals  = false;
174
175 6
        foreach ($functionLike->getParameters() as $parameter) {
176 3
            $byReference  = ($parameter->isPassedByReference() && !$parameter->isVariadic()) ? '&' : '';
177 3
            $hasOptionals = $hasOptionals || $parameter->isOptional();
178
179 3
            $arguments[] = $byReference . '$' . $parameter->name;
180
        }
181
182 6
        $isVariadic = $functionLike->isVariadic();
183 6
        if ($isVariadic) {
184 1
            $argumentsPart[] = array_pop($arguments);
185
        }
186 6
        if (!empty($arguments)) {
187
            // Unshifting to keep correct order
188 3
            $argumentLine = '[' . join(', ', $arguments) . ']';
189 3
            if ($hasOptionals) {
190 2
                $argumentLine = "\\array_slice($argumentLine, 0, \\func_num_args())";
191
            }
192 3
            array_unshift($argumentsPart, $argumentLine);
193
        }
194
195 6
        return join(', ', $argumentsPart);
196
    }
197
198
    /**
199
     * Creates a function code from Reflection
200
     *
201
     * @param ReflectionFunctionAbstract $functionLike Reflection for method
202
     * @param string $body Body of method
203
     *
204
     * @return string
205
     */
206 6
    protected function getOverriddenFunction(ReflectionFunctionAbstract $functionLike, $body)
207
    {
208 6
        $reflectionReturnType = PHP_VERSION_ID >= 70000 ? $functionLike->getReturnType() : '';
209 6
        $modifiersLine        = '';
210 6
        if ($reflectionReturnType) {
211 1
            $nullablePrefix = $reflectionReturnType->allowsNull() ? '?' : '';
212 1
            $nsPrefix       = $reflectionReturnType->isBuiltin() ? '' : '\\';
213
214 1
            $reflectionReturnType = $nullablePrefix . $nsPrefix . (string) $reflectionReturnType;
215
        }
216 6
        if ($functionLike instanceof ReflectionMethod) {
217 6
            $modifiersLine = join(' ', Reflection::getModifierNames($functionLike->getModifiers()));
218
        }
219
220
        $code = (
221 6
            preg_replace('/ {4}|\t/', '', $functionLike->getDocComment()) . "\n" . // Original Doc-block
222 6
            $modifiersLine . // List of modifiers (for methods)
223 6
            ' function ' . // 'function' keyword
224 6
            ($functionLike->returnsReference() ? '&' : '') . // By reference symbol
225 6
            $functionLike->name . // Name of the function
226 6
            '(' . // Start of parameters list
227 6
            join(', ', $this->getParameters($functionLike->getParameters())) . // List of parameters
228 6
            ")" . // End of parameters list
229 6
            ($reflectionReturnType ? " : $reflectionReturnType" : '') . // Return type, if present
230 6
            "\n" .
231 6
            "{\n" . // Start of method body
232 6
            $this->indent($body) . "\n" . // Method body
233 6
            "}\n" // End of method body
234
        );
235
236 6
        return $code;
237
    }
238
}
239