Completed
Pull Request — master (#256)
by Alexander
06:46
created

FunctionProxy::getOverriddenFunction()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12
Metric Value
dl 0
loc 26
ccs 0
cts 21
cp 0
rs 8.8571
cc 3
eloc 18
nc 3
nop 2
crap 12
1
<?php
2
/*
3
 * Go! AOP framework
4
 *
5
 * @copyright Copyright 2013, 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 Go\Aop\Advice;
14
use Go\Aop\Framework\ReflectionFunctionInvocation;
15
use Go\Aop\Intercept\FunctionInvocation;
16
use Go\Core\AspectContainer;
17
use Go\Core\AspectKernel;
18
use Go\Core\LazyAdvisorAccessor;
19
use Go\ParserReflection\ReflectionFileNamespace;
20
use ReflectionFunction;
21
use ReflectionFunctionAbstract;
22
23
/**
24
 * Function proxy builder that is used to generate a proxy-function from the list of joinpoints
25
 */
26
class FunctionProxy extends AbstractProxy
27
{
28
29
    /**
30
     * List of advices for functions
31
     *
32
     * @var array
33
     */
34
    protected static $functionAdvices = [];
35
36
    /**
37
     * Name for the current namespace
38
     *
39
     * @var string
40
     */
41
    protected $namespace = '';
42
43
    /**
44
     * Source code for functions
45
     *
46
     * @var array Name of the function => source code for it
47
     */
48
    protected $functionsCode = [];
49
50
    /**
51
     * Constructs functions stub class from namespace Reflection
52
     *
53
     * @param ReflectionFileNamespace $namespace Reflection of namespace
54
     * @param array $advices List of function advices
55
     *
56
     * @throws \InvalidArgumentException for invalid classes
57
     */
58
    public function __construct(ReflectionFileNamespace $namespace, array $advices = [])
59
    {
60
        parent::__construct($advices);
61
        $this->namespace = $namespace;
0 ignored issues
show
Documentation Bug introduced by
It seems like $namespace of type object<Go\ParserReflecti...eflectionFileNamespace> is incompatible with the declared type string of property $namespace.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
62
63
        if (empty($advices[AspectContainer::FUNCTION_PREFIX])) {
64
            return;
65
        }
66
67
        foreach ($advices[AspectContainer::FUNCTION_PREFIX] as $pointName => $value) {
68
            $function = new ReflectionFunction($pointName);
69
            $this->override($function, $this->getJoinpointInvocationBody($function));
70
        }
71
    }
72
73
    /**
74
     * Returns a joinpoint for specific function in the namespace
75
     *
76
     * @param string $joinPointName Special joinpoint name
77
     * @param string $namespace Name of the namespace
78
     *
79
     * @return FunctionInvocation
80
     */
81
    public static function getJoinPoint($joinPointName, $namespace)
82
    {
83
        /** @var LazyAdvisorAccessor $accessor */
84
        static $accessor = null;
85
86
        if (!$accessor) {
87
            $accessor = AspectKernel::getInstance()->getContainer()->get('aspect.advisor.accessor');
88
        }
89
90
        $advices = self::$functionAdvices[$namespace][AspectContainer::FUNCTION_PREFIX][$joinPointName];
91
92
        $filledAdvices = [];
93
        foreach ($advices as $advisorName) {
94
            $filledAdvices[] = $accessor->$advisorName;
95
        }
96
97
        return new ReflectionFunctionInvocation($joinPointName, $filledAdvices);
98
    }
99
100
    /**
101
     * Inject advices for given trait
102
     *
103
     * NB This method will be used as a callback during source code evaluation to inject joinpoints
104
     *
105
     * @param string $namespace Aop child proxy class
106
     * @param array|Advice[] $advices List of advices to inject into class
107
     *
108
     * @return void
109
     */
110
    public static function injectJoinPoints($namespace, array $advices = [])
111
    {
112
        self::$functionAdvices[$namespace] = $advices;
113
    }
114
115
    /**
116
     * Override function with new body
117
     *
118
     * @param ReflectionFunction $function Function reflection
119
     * @param string $body New body for function
120
     *
121
     * @return $this
122
     */
123
    public function override(ReflectionFunction $function, $body)
124
    {
125
        $this->functionsCode[$function->name] = $this->getOverriddenFunction($function, $body);
126
127
        return $this;
128
    }
129
130
    /**
131
     * {@inheritDoc}
132
     */
133
    public function __toString()
134
    {
135
        $functionsCode = (
136
            "<?php\n" . // Start of file header
137
            $this->namespace->getDocComment() . "\n" . // Doc-comment for file
0 ignored issues
show
Bug introduced by
The method getDocComment cannot be called on $this->namespace (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
138
            'namespace ' . // 'namespace' keyword
139
            $this->namespace->getName() . // Name
0 ignored issues
show
Bug introduced by
The method getName cannot be called on $this->namespace (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
140
            ";\n" . // End of namespace name
141
            join("\n", $this->functionsCode) // Function definitions
142
        );
143
144
        return $functionsCode
145
            // Inject advices on call
146
            . PHP_EOL
147
            . '\\' . __CLASS__ . "::injectJoinPoints('"
148
                . $this->namespace->getName() . "',"
0 ignored issues
show
Bug introduced by
The method getName cannot be called on $this->namespace (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
149
                . var_export($this->advices, true) . ");";
150
    }
151
152
    /**
153
     * Creates definition for trait method body
154
     *
155
     * @param ReflectionFunction $function Method reflection
156
     *
157
     * @return string new method body
158
     */
159
    protected function getJoinpointInvocationBody(ReflectionFunction $function)
160
    {
161
        $class = '\\' . __CLASS__;
162
163
        $args = $this->prepareArgsLine($function);
164
165
        return <<<BODY
166
static \$__joinPoint = null;
167
if (!\$__joinPoint) {
168
    \$__joinPoint = {$class}::getJoinPoint('{$function->name}', __NAMESPACE__);
169
}
170
return \$__joinPoint->__invoke($args);
171
BODY;
172
    }
173
174
}
175