Completed
Push — master ( 9e3bdf...aed36c )
by Alexander
07:28 queued 04:20
created

AbstractProxy::__toString()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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