Completed
Pull Request — master (#393)
by Alexander
06:14
created

TraitProxyGenerator::__construct()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 33
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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