Completed
Pull Request — master (#298)
by Alexander
04:10
created

AbstractProxy   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 215
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 90.2%

Importance

Changes 0
Metric Value
wmc 37
lcom 1
cbo 0
dl 0
loc 215
ccs 46
cts 51
cp 0.902
rs 8.6
c 0
b 0
f 0

8 Methods

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