TraitProxyGenerator::getJoinpointInvocationBody()   B
last analyzed

Complexity

Conditions 7
Paths 24

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 35
c 0
b 0
f 0
ccs 0
cts 26
cp 0
rs 8.4266
cc 7
nc 24
nop 1
crap 56
1
<?php
2
3
declare(strict_types = 1);
4
/*
5
 * Go! AOP framework
6
 *
7
 * @copyright Copyright 2012, Lisachenko Alexander <[email protected]>
8
 *
9
 * This source file is subject to the license that is bundled
10
 * with this source code in the file LICENSE.
11
 */
12
13
namespace Go\Proxy;
14
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 Laminas\Code\Generator\DocBlockGenerator;
22
use Laminas\Code\Generator\TraitGenerator;
23
use Laminas\Code\Generator\ValueGenerator;
24
use Laminas\Code\Reflection\DocBlockReflection;
25
use ReflectionNamedType;
26
27
/**
28
 * Trait proxy builder that is used to generate a trait from the list of joinpoints
29
 */
30
class TraitProxyGenerator extends ClassProxyGenerator
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[][]      $traitAdviceNames List of advices for class
38
     */
39
    public function __construct(
40
        ReflectionClass $originalTrait,
41
        string $parentTraitName,
42
        array $traitAdviceNames,
43
        bool $useParameterWidening
44
    ) {
45
        $this->adviceNames          = $traitAdviceNames;
46
        $this->useParameterWidening = $useParameterWidening;
47
48
        $dynamicMethodAdvices = $traitAdviceNames[AspectContainer::METHOD_PREFIX] ?? [];
49
        $staticMethodAdvices  = $traitAdviceNames[AspectContainer::STATIC_METHOD_PREFIX] ?? [];
50
        $interceptedMethods   = array_keys($dynamicMethodAdvices + $staticMethodAdvices);
51
        $generatedMethods     = $this->interceptMethods($originalTrait, $interceptedMethods);
52
53
        $this->generator = new TraitGenerator(
54
            $originalTrait->getShortName(),
55
            $originalTrait->getNamespaceName(),
56
            null,
57
            null,
58
            [],
59
            [],
60
            $generatedMethods,
61
            DocBlockGenerator::fromReflection(new DocBlockReflection($originalTrait->getDocComment()))
62
        );
63
64
        // Normalize FQDN
65
        $namespaceParts       = explode('\\', $parentTraitName);
66
        $parentNormalizedName = end($namespaceParts);
67
        $this->generator->addTrait($parentNormalizedName);
68
69
        foreach ($interceptedMethods as $methodName) {
70
            $fullName = $parentNormalizedName . '::' . $methodName;
71
            $this->generator->addTraitAlias($fullName, $methodName . '➩', ReflectionMethod::IS_PROTECTED);
72
        }
73
    }
74
75
    /**
76
     * Returns a method invocation for the specific trait method
77
     *
78
     * @param array $adviceNames List of advices for this trait method
79
     */
80
    public static function getJoinPoint(
81
        string $className,
82
        string $joinPointType,
83
        string $methodName,
84
        array $adviceNames
85
    ): MethodInvocation {
86
        static $accessor;
87
88
        if ($accessor === null) {
89
            $aspectKernel = AspectKernel::getInstance();
90
            $accessor     = $aspectKernel->getContainer()->get('aspect.advisor.accessor');
91
        }
92
93
        $filledAdvices = [];
94
        foreach ($adviceNames as $advisorName) {
95
            $filledAdvices[] = $accessor->$advisorName;
96
        }
97
98
        $joinPoint = new self::$invocationClassMap[$joinPointType]($className, $methodName . '➩', $filledAdvices);
99
100
        return $joinPoint;
101
    }
102
103
    /**
104
     * Creates string definition for trait method body by method reflection
105
     */
106
    protected function getJoinpointInvocationBody(ReflectionMethod $method): string
107
    {
108
        $isStatic = $method->isStatic();
109
        $class    = '\\' . self::class;
110
        $scope    = $isStatic ? 'static::class' : '$this';
111
        $prefix   = $isStatic ? AspectContainer::STATIC_METHOD_PREFIX : AspectContainer::METHOD_PREFIX;
112
113
        $argumentList = new FunctionCallArgumentListGenerator($method);
114
        $argumentCode = $argumentList->generate();
115
        $argumentCode = $scope . ($argumentCode !== '' ? ", $argumentCode" : '');
116
117
        $return = 'return ';
118
        if ($method->hasReturnType()) {
119
            $returnType = $method->getReturnType();
120
            if ($returnType instanceof ReflectionNamedType && $returnType->getName() === 'void') {
0 ignored issues
show
Bug introduced by
The class ReflectionNamedType does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
121
                // void return types should not return anything
122
                $return = '';
123
            }
124
        }
125
126
        $advicesArrayValue = new ValueGenerator(
127
            $this->adviceNames[$prefix][$method->name],
128
            ValueGenerator::TYPE_ARRAY_SHORT
129
        );
130
        $advicesArrayValue->setArrayDepth(1);
131
        $advicesCode = $advicesArrayValue->generate();
132
133
        return <<<BODY
134
static \$__joinPoint;
135
if (\$__joinPoint === null) {
136
    \$__joinPoint = {$class}::getJoinPoint(__CLASS__, '{$prefix}', '{$method->name}', {$advicesCode});
137
}
138
{$return}\$__joinPoint->__invoke($argumentCode);
139
BODY;
140
    }
141
142
    /**
143
     * {@inheritDoc}
144
     */
145
    public function generate(): string
146
    {
147
        return $this->generator->generate();
148
    }
149
}
150