Completed
Push — refactor ( d2218a )
by Akihito
01:47
created

CodeGenMethod::setParameterType()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 9
cts 9
cp 1
rs 8.8333
c 0
b 0
f 0
cc 7
nc 17
nop 2
crap 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Aop;
6
7
use PhpParser\BuilderFactory;
8
use PhpParser\Node;
9
use PhpParser\Node\Stmt\Class_;
10
use PhpParser\NodeTraverser;
11
use PhpParser\Parser;
12
use PhpParser\PrettyPrinter\Standard;
13
14
final class CodeGenMethod
15
{
16
    /**
17
     * @var \PhpParser\Parser
18
     */
19
    private $parser;
20
21
    /**
22
     * @var \PhpParser\BuilderFactory
23
     */
24
    private $factory;
25
26
    /**
27
     * @var \PhpParser\PrettyPrinter\Standard
28
     */
29
    private $printer;
30
31
    /**
32
     * @throws \Doctrine\Common\Annotations\AnnotationException
33
     */
34
    public function __construct(
35
        Parser $parser,
36
        BuilderFactory $factory,
37
        Standard $printer
38
    ) {
39
        $this->parser = $parser;
40
        $this->factory = $factory;
41
        $this->printer = $printer;
42
    }
43
44
    public function getMethods(\ReflectionClass $class, BindInterface $bind, CodeVisitor $code) : array
45
    {
46
        $bindingMethods = array_keys($bind->getBindings());
47
        $classMethods = $code->classMethod;
48
        $methods = [];
49
        foreach ($classMethods as $classMethod) {
50
            $methodName = $classMethod->name->name;
51
            $method = new \ReflectionMethod($class->name, $methodName);
52
            $isBindingMethod = in_array($methodName, $bindingMethods, true);
53
            /* @var $method \ReflectionMethod */
54
            $isPublic = $classMethod->flags === Class_::MODIFIER_PUBLIC;
55
            if ($isBindingMethod && $isPublic) {
56 33
                $methodInsideStatements = $this->getMethodInsideStatement($method);
57
                // replace statements in the method
58
                $classMethod->stmts = $methodInsideStatements;
59
                $methods[] = $classMethod;
60
            }
61 33
        }
62 33
63 33
        return $methods;
64 33
    }
65 33
66
    /**
67
     * @return Node\Stmt[]
68
     */
69
    private function getMethodInsideStatement(\ReflectionMethod $method) : array
70
    {
71
        $traverser = new NodeTraverser;
72
        $traverser->addVisitor(new AopTemplateConverter($method));
73 17
        $stmts = $this->getTemplateMethodNodeStmts();
74
75 17
        // traverse
76 17
        $stmts = $traverser->traverse($stmts);
77 17
        $result = [];
78 17
        foreach ($stmts as $stmt) {
79 16
            if ($stmt instanceof Node\Stmt) {
80 16
                $result[] = $stmt;
81
            }
82 16
        }
83 16
84
        return $result;
85
    }
86
87 17
    private function getTemplateMethodNodeStmts() : array
88
    {
89
        $code = $this->getTemplateCode();
90
        $node = $this->parser->parse($code)[0];
91
        if (! $node instanceof Class_) {
92
            throw new \LogicException; // @codeCoverageIgnore
93
        }
94
        $methodNode = $node->getMethods()[0];
95
        if ($methodNode->stmts === null) {
96
            throw new \LogicException; // @codeCoverageIgnore
97 15
        }
98
99 15
        return $methodNode->stmts;
100 15
    }
101 15
102 13
    /**
103
     * Return CodeGenTemplate string
104 15
     *
105 15
     * Compiler takes only the statements in the method. Then create new inherit code with interceptors.
106 5
     *
107
     * @see http://paul-m-jones.com/archives/182
108 15
     * @see http://stackoverflow.com/questions/8343399/calling-a-function-with-explicit-parameters-vs-call-user-func-array
109 15
     * @see http://stackoverflow.com/questions/1796100/what-is-faster-many-ifs-or-else-if
110
     * @see http://stackoverflow.com/questions/2401478/why-is-faster-than-in-php
111 15
     */
112
    private function getTemplateCode() : string
113
    {
114
        return <<<'EOT'
115
<?php
116
class AopTemplate extends \Ray\Aop\FakeMock implements Ray\Aop\WeavedInterface
117 13
{
118
    /**
119
     * @var array
120 13
     *
121
     * [$methodName => [$interceptorA[]][]
122 13
     */
123 13
    public $bindings;
124 13
125
    /**
126 13
     * @var bool
127
     */
128
    private $isAspect = true;
129 15
130
    /**
131 15
     * Method Template
132 15
     *
133 15
     * @param mixed $a
134 6
     */
135
    public function templateMethod($a, $b)
136
    {
137 15
        if (! $this->isAspect) {
138
            $this->isAspect = true;
139
140
            return parent::templateMethod($a, $b);
141
        }
142
143 15
        $this->isAspect = false;
144
        $result = (new Invocation($this, __FUNCTION__, [$a, $b], $this->bindings[__FUNCTION__]))->proceed();
145 15
        $this->isAspect = true;
146 15
147 15
        return $result;
148
    }
149
}
150 15
EOT;
151
    }
152
}
153