Completed
Branch 2.x (867c01)
by Akihito
09:30
created

CodeGenMethod::getMethods()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 16
ccs 12
cts 12
cp 1
rs 9.2
cc 4
eloc 10
nc 3
nop 2
crap 4
1
<?php
2
/**
3
 * This file is part of the Ray.Aop package
4
 *
5
 * @license http://opensource.org/licenses/bsd-license.php BSD
6
 */
7
namespace Ray\Aop;
8
9
use Doctrine\Common\Annotations\AnnotationReader;
10
use PHPParser\Builder\Method;
11
use PhpParser\Builder\Param;
12
use PhpParser\BuilderFactory;
13
use PhpParser\Comment\Doc;
14
use PhpParser\Parser;
15
use PhpParser\PrettyPrinter\Standard;
16
use Ray\Aop\Annotation\AbstractAssisted;
17
18
final class CodeGenMethod
19
{
20
    /**
21
     * @var \PHPParser\Parser
22
     */
23
    private $parser;
24
25
    /**
26
     * @var \PHPParser\BuilderFactory
27
     */
28
    private $factory;
29
30
    /**
31
     * @var \PHPParser\PrettyPrinter\Standard
32
     */
33
    private $printer;
34
35
    private $reader;
36
37
    /**
38
     * @var AbstractAssisted
39
     */
40
    private $assisted = [];
41
42
    /**
43
     * @param \PHPParser\Parser                 $parser
44
     * @param \PHPParser\BuilderFactory         $factory
45
     * @param \PHPParser\PrettyPrinter\Standard $printer
46
     */
47 24
    public function __construct(
48
        Parser $parser,
49
        BuilderFactory $factory,
50
        Standard $printer
51
    ) {
52 24
        $this->parser = $parser;
53 24
        $this->factory = $factory;
54 24
        $this->printer = $printer;
55 24
        $this->reader = new AnnotationReader;
56 24
    }
57
58
    /**
59
     * @param \ReflectionClass $class
60
     *
61
     * @return array
62
     */
63 9
    public function getMethods(\ReflectionClass $class, BindInterface $bind)
64
    {
65 9
        $bindingMethods = array_keys($bind->getBindings());
66 9
        $stmts = [];
67 9
        $methods = $class->getMethods();
68 9
        foreach ($methods as $method) {
69 9
            $this->assisted = $this->reader->getMethodAnnotation($method, AbstractAssisted::class);
70 9
            $isBindingMethod = in_array($method->getName(), $bindingMethods);
71
            /* @var $method \ReflectionMethod */
72 9
            if ($isBindingMethod && $method->isPublic()) {
73 8
                $stmts[] = $this->getMethod($method);
74 8
            }
75 9
        }
76
77 9
        return $stmts;
78
    }
79
80
    /**
81
     * Return method statement
82
     *
83
     * @param \ReflectionMethod $method
84
     *
85
     * @return \PhpParser\Node\Stmt\ClassMethod
86
     */
87 8
    private function getMethod(\ReflectionMethod $method)
88
    {
89 8
        $methodStmt = $this->factory->method($method->name);
90 8
        $params = $method->getParameters();
91 8
        foreach ($params as $param) {
92 8
            $methodStmt = $this->getMethodStatement($param, $methodStmt);
93 8
        }
94 8
        $methodInsideStatements = $this->getMethodInsideStatement();
95 8
        $methodStmt->addStmts($methodInsideStatements);
96 8
        $node = $this->addMethodDocComment($methodStmt, $method);
97
98 8
        return $node;
99
    }
100
101
    /**
102
     * Return parameter reflection
103
     *
104
     * @param \ReflectionParameter      $param
105
     * @param \PHPParser\Builder\Method $methodStmt
106
     *
107
     * @return \PHPParser\Builder\Method
108
     */
109 8
    private function getMethodStatement(\ReflectionParameter $param, Method $methodStmt)
110
    {
111
        /** @var $paramStmt Param */
112 8
        $paramStmt = $this->factory->param($param->name);
113
        /* @var $param \ReflectionParameter */
114 8
        $typeHint = $param->getClass();
115 8
        $this->setTypeHint($param, $paramStmt, $typeHint);
116 8
        $this->setDefault($param, $paramStmt);
117 8
        $methodStmt->addParam($paramStmt);
118
119 8
        return $methodStmt;
120
    }
121
122
    /**
123
     * @param Method            $methodStmt
124
     * @param \ReflectionMethod $method
125
     *
126
     * @return \PhpParser\Node\Stmt\ClassMethod
127
     */
128 8
    private function addMethodDocComment(Method $methodStmt, \ReflectionMethod $method)
129
    {
130 8
        $node = $methodStmt->getNode();
131 8
        $docComment = $method->getDocComment();
132 8
        if ($docComment) {
133 5
            $node->setAttribute('comments', [new Doc($docComment)]);
134 5
        }
135
136 8
        return $node;
137
    }
138
139
    /**
140
     * @return \PHPParser\Node[]
141
     */
142 8
    private function getMethodInsideStatement()
143
    {
144 8
        $code = file_get_contents(dirname(__DIR__) . '/src-data/CodeGenTemplate.php');
145 8
        $node = $this->parser->parse($code)[0];
146
        /** @var $node \PHPParser\Node\Stmt\Class_ */
147 8
        $node = $node->getMethods()[0];
148
149 8
        return $node->stmts;
150
    }
151
152
    /**
153
     * @param \ReflectionParameter $param
154
     * @param Param                $paramStmt
155
     * @param \ReflectionClass     $typeHint
156
     */
157 8
    private function setTypeHint(\ReflectionParameter $param, Param $paramStmt, \ReflectionClass $typeHint = null)
158
    {
159 8
        if ($typeHint) {
160 2
            $paramStmt->setTypeHint($typeHint->name);
161 2
        }
162 8
        if ($param->isArray()) {
163 1
            $paramStmt->setTypeHint('array');
164 1
        }
165 8
        if ($param->isCallable()) {
166 1
            $paramStmt->setTypeHint('callable');
167 1
        }
168 8
    }
169
170
    /**
171
     * @param \ReflectionParameter $param
172
     * @param Param                $paramStmt
173
     */
174 8
    private function setDefault(\ReflectionParameter $param, $paramStmt)
175
    {
176 8
        if ($param->isDefaultValueAvailable()) {
177 2
            $paramStmt->setDefault($param->getDefaultValue());
178
179 2
            return;
180
        }
181 8
        if ($this->assisted && in_array($param->getName(), $this->assisted->values)) {
182 1
            $paramStmt->setDefault(null);
183 1
        }
184 8
    }
185
}
186