Completed
Push — 2.x ( 867c01...cbcabc )
by Akihito
07:06
created

CodeGenMethod::setParameterType()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 2
Bugs 1 Features 0
Metric Value
dl 0
loc 12
c 2
b 1
f 0
ccs 0
cts 0
cp 0
rs 9.4285
cc 3
eloc 7
nc 3
nop 4
crap 12
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
        $isOverPhp7 = version_compare(PHP_VERSION, '7.0.0') >= 0;
112 8
        /** @var $paramStmt Param */
113
        $paramStmt = $this->factory->param($param->name);
114 8
        /* @var $param \ReflectionParameter */
115 8
        $typeHint = $param->getClass();
116 8
        $this->setParameterType($param, $paramStmt, $isOverPhp7, $typeHint);
117 8
        $this->setDefault($param, $paramStmt);
118
        if ($isOverPhp7) {
119 8
            $this->setReturnType($param, $methodStmt, $isOverPhp7);
0 ignored issues
show
Unused Code introduced by
The call to CodeGenMethod::setReturnType() has too many arguments starting with $isOverPhp7.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
120
        }
121
        $methodStmt->addParam($paramStmt);
122
123
        return $methodStmt;
124
    }
125
126
    /**
127
     * @param Method            $methodStmt
128 8
     * @param \ReflectionMethod $method
129
     *
130 8
     * @return \PhpParser\Node\Stmt\ClassMethod
131 8
     */
132 8
    private function addMethodDocComment(Method $methodStmt, \ReflectionMethod $method)
133 5
    {
134 5
        $node = $methodStmt->getNode();
135
        $docComment = $method->getDocComment();
136 8
        if ($docComment) {
137
            $node->setAttribute('comments', [new Doc($docComment)]);
138
        }
139
140
        return $node;
141
    }
142 8
143
    /**
144 8
     * @return \PHPParser\Node[]
145 8
     */
146
    private function getMethodInsideStatement()
147 8
    {
148
        $code = file_get_contents(dirname(__DIR__) . '/src-data/CodeGenTemplate.php');
149 8
        $node = $this->parser->parse($code)[0];
150
        /** @var $node \PHPParser\Node\Stmt\Class_ */
151
        $node = $node->getMethods()[0];
152
153
        return $node->stmts;
154
    }
155
156
    /**
157 8
     * @param \ReflectionParameter $param
158
     * @param Param                $paramStmt
159 8
     * @param \ReflectionClass     $typeHint
160 2
     *
161 2
     * @codeCoverageIgnore
162 8
     */
163 1
    private function setTypeHint(\ReflectionParameter $param, Param $paramStmt, \ReflectionClass $typeHint = null)
164 1
    {
165 8
        if ($typeHint) {
166 1
            $paramStmt->setTypeHint($typeHint->name);
167 1
        }
168 8
        if ($param->isArray()) {
169
            $paramStmt->setTypeHint('array');
170
        }
171
        if ($param->isCallable()) {
172
            $paramStmt->setTypeHint('callable');
173
        }
174 8
    }
175
176 8
    /**
177 2
     * @param \ReflectionParameter $param
178
     * @param Param                $paramStmt
179 2
     */
180
    private function setDefault(\ReflectionParameter $param, $paramStmt)
181 8
    {
182 1
        if ($param->isDefaultValueAvailable()) {
183 1
            $paramStmt->setDefault($param->getDefaultValue());
184 8
185
            return;
186
        }
187
        if ($this->assisted && in_array($param->getName(), $this->assisted->values)) {
188
            $paramStmt->setDefault(null);
189
        }
190
    }
191
192
    /**
193
     * @param \ReflectionParameter $param
194
     * @param Param                $paramStmt
195
     * @param bool                 $isOverPhp7
196
     * @param \ReflectionClass     $typeHint
197
     */
198
    private function setParameterType(\ReflectionParameter $param, Param $paramStmt, $isOverPhp7, \ReflectionClass $typeHint = null)
199
    {
200
        if (! $isOverPhp7) {
201
            $this->setTypeHint($param, $paramStmt, $typeHint); // @codeCoverageIgnore
202
203
            return; // @codeCoverageIgnore
204
        }
205
        $type = $param->getType();
206
        if ($type) {
207
            $paramStmt->setTypeHint((string) $type);
208
        }
209
    }
210
211
    /**
212
     * @param \ReflectionParameter $param
213
     * @param Method $methodStmt
0 ignored issues
show
Coding Style introduced by
Expected 15 spaces after parameter type; 1 found
Loading history...
214
     */
215
    private function setReturnType(\ReflectionParameter $param, Method $methodStmt)
216
    {
217
        $returnType = $param->getDeclaringFunction()->getReturnType();
218
        if ($returnType && method_exists($methodStmt, 'setReturnType')) {
219
            $methodStmt->setReturnType((string)$returnType); // @codeCoverageIgnore
220
        }
221
    }
222
}
223