Completed
Push — master ( 8f80d1...9927eb )
by Alexander
11s
created

TraitProxyGenerator::getJoinpointInvocationBody()   B

Complexity

Conditions 6
Paths 24

Size

Total Lines 32
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 32
ccs 0
cts 27
cp 0
rs 8.439
c 0
b 0
f 0
cc 6
eloc 21
nc 24
nop 1
crap 42
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 Go\Aop\Intercept\MethodInvocation;
15
use Go\Core\AspectContainer;
16
use Go\Core\AspectKernel;
17
use Go\Proxy\Part\FunctionCallArgumentListGenerator;
18
use ReflectionClass;
19
use ReflectionMethod;
20
use Zend\Code\Generator\DocBlockGenerator;
21
use Zend\Code\Generator\TraitGenerator;
22
use Zend\Code\Generator\ValueGenerator;
23
use Zend\Code\Reflection\DocBlockReflection;
24
25
/**
26
 * Trait proxy builder that is used to generate a trait from the list of joinpoints
27
 */
28
class TraitProxyGenerator extends ClassProxyGenerator
29
{
30
31
    /**
32
     * Generates an child code by original class reflection and joinpoints for it
33
     *
34
     * @param ReflectionClass $originalTrait        Original class reflection
35
     * @param string          $parentTraitName      Parent trait name to use
36
     * @param string[][]      $traitAdvices         List of advices for class
37
     * @param bool            $useParameterWidening Enables usage of parameter widening feature
38
     */
39
    public function __construct(
40
        ReflectionClass $originalTrait,
41
        string $parentTraitName,
42
        array $traitAdvices,
43
        bool $useParameterWidening
44
    ) {
45
        $this->advices        = $traitAdvices;
46
        $dynamicMethodAdvices = $traitAdvices[AspectContainer::METHOD_PREFIX] ?? [];
47
        $staticMethodAdvices  = $traitAdvices[AspectContainer::STATIC_METHOD_PREFIX] ?? [];
48
        $interceptedMethods   = array_keys($dynamicMethodAdvices + $staticMethodAdvices);
49
        $generatedMethods     = $this->interceptMethods($originalTrait, $interceptedMethods);
50
51
        $this->generator = new TraitGenerator(
52
            $originalTrait->getShortName(),
53
            $originalTrait->getNamespaceName(),
54
            null,
55
            null,
56
            [],
57
            [],
58
            $generatedMethods,
59
            DocBlockGenerator::fromReflection(new DocBlockReflection($originalTrait->getDocComment()))
60
        );
61
62
        // Normalize FQDN
63
        $namespaceParts       = explode('\\', $parentTraitName);
64
        $parentNormalizedName = end($namespaceParts);
65
        $this->generator->addTrait($parentNormalizedName);
66
67
        foreach ($interceptedMethods as $methodName) {
68
            $fullName = $parentNormalizedName . '::' . $methodName;
69
            $this->generator->addTraitAlias($fullName, $methodName . '➩', ReflectionMethod::IS_PROTECTED);
70
        }
71
    }
72
73
    /**
74
     * Returns a method invocation for the specific trait method
75
     *
76
     * @param array $advices List of advices for this trait method
77
     */
78
    public static function getJoinPoint(
79
        string $className,
80
        string $joinPointType,
81
        string $methodName,
82
        array $advices
83
    ): MethodInvocation {
84
        static $accessor;
85
86
        if ($accessor === null) {
87
            $aspectKernel = AspectKernel::getInstance();
88
            $accessor     = $aspectKernel->getContainer()->get('aspect.advisor.accessor');
89
        }
90
91
        $filledAdvices = [];
92
        foreach ($advices as $advisorName) {
93
            $filledAdvices[] = $accessor->$advisorName;
94
        }
95
96
        $joinPoint = new self::$invocationClassMap[$joinPointType]($className, $methodName . '➩', $filledAdvices);
97
98
        return $joinPoint;
99
    }
100
101
    /**
102
     * Creates string definition for trait method body by method reflection
103
     */
104
    protected function getJoinpointInvocationBody(ReflectionMethod $method): string
105
    {
106
        $isStatic = $method->isStatic();
107
        $class    = '\\' . __CLASS__;
108
        $scope    = $isStatic ? 'static::class' : '$this';
109
        $prefix   = $isStatic ? AspectContainer::STATIC_METHOD_PREFIX : AspectContainer::METHOD_PREFIX;
110
111
        $argumentList = new FunctionCallArgumentListGenerator($method);
112
        $argumentCode = $argumentList->generate();
113
        $argumentCode = $scope . ($argumentCode ? ", $argumentCode" : '');
114
115
        $return = 'return ';
116
        if ($method->hasReturnType()) {
117
            $returnType = (string) $method->getReturnType();
118
            if ($returnType === 'void') {
119
                // void return types should not return anything
120
                $return = '';
121
            }
122
        }
123
124
        $advicesArray = new ValueGenerator($this->advices[$prefix][$method->name], ValueGenerator::TYPE_ARRAY_SHORT);
125
        $advicesArray->setArrayDepth(1);
126
        $advicesCode = $advicesArray->generate();
127
128
        return <<<BODY
129
static \$__joinPoint;
130
if (\$__joinPoint === null) {
131
    \$__joinPoint = {$class}::getJoinPoint(__CLASS__, '{$prefix}', '{$method->name}', {$advicesCode});
132
}
133
{$return}\$__joinPoint->__invoke($argumentCode);
134
BODY;
135
    }
136
137
    /**
138
     * {@inheritDoc}
139
     */
140
    public function generate(): string
141
    {
142
        return $this->generator->generate();
143
    }
144
}
145