Completed
Push — 2.x ( 711074...57aa77 )
by Akihito
17s queued 12s
created

CodeGenMethod::getMethods()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 6
cts 6
cp 1
rs 9.568
c 0
b 0
f 0
cc 4
nc 3
nop 3
crap 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Aop;
6
7
use PhpParser\BuilderFactory;
8
use PhpParser\Node\Identifier;
9
use PhpParser\Node\Stmt;
10
use PhpParser\Node\Stmt\Class_;
11
use PhpParser\Node\Stmt\ClassMethod;
12
use PhpParser\NodeAbstract;
13
use PhpParser\Parser;
14
use ReflectionClass;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Ray\Aop\ReflectionClass.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
15
use ReflectionMethod;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Ray\Aop\ReflectionMethod.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
16
17
final class CodeGenMethod
18
{
19
    /**
20
     * @var \PhpParser\Parser
21
     */
22
    private $parser;
23
24
    /**
25
     * @var \PhpParser\BuilderFactory
26
     */
27
    private $factory;
28
29
    /**
30
     * @throws \Doctrine\Common\Annotations\AnnotationException
31
     */
32
    public function __construct(
33
        Parser $parser,
34
        BuilderFactory $factory
35
    ) {
36
        $this->parser = $parser;
37
        $this->factory = $factory;
38
    }
39
40
    /**
41
     * @param ReflectionClass<object> $class
0 ignored issues
show
Documentation introduced by
The doc-type ReflectionClass<object> could not be parsed: Expected "|" or "end of type", but got "<" at position 15. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
42
     *
43
     * @return ClassMethod[]
44
     */
45
    public function getMethods(ReflectionClass $class, BindInterface $bind, CodeVisitor $code) : array
46
    {
47
        $bindingMethods = array_keys($bind->getBindings());
48
        $classMethods = $code->classMethod;
49
        $methods = [];
50
        foreach ($classMethods as $classMethod) {
51
            $methodName = $classMethod->name->name;
52
            $method = new ReflectionMethod($class->name, $methodName);
0 ignored issues
show
Unused Code introduced by
$method is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
53
            $isBindingMethod = in_array($methodName, $bindingMethods, true);
54
            $isPublic = $classMethod->flags === Class_::MODIFIER_PUBLIC;
55
            if ($isBindingMethod && $isPublic) {
56 33
                $methodInsideStatements = $this->getTemplateMethodNodeStmts(
57
                    $classMethod->getReturnType()
58
                );
59
                // replace statements in the method
60
                $classMethod->stmts = $methodInsideStatements;
61 33
                $methods[] = $classMethod;
62 33
            }
63 33
        }
64 33
65 33
        return $methods;
66
    }
67
68
    /**
69
     * @return Stmt[]
70
     */
71
    private function getTemplateMethodNodeStmts(?NodeAbstract $returnType) : array
72
    {
73 17
        $code = $this->isReturnVoid($returnType) ? AopTemplate::RETURN_VOID : AopTemplate::RETURN;
74
        $parts = $this->parser->parse($code);
75 17
        assert(isset($parts[0]));
76 17
        $node = $parts[0];
77 17
        assert($node instanceof Class_);
78 17
        $methodNode = $node->getMethods()[0];
79 16
        assert($methodNode->stmts !== null);
80 16
81
        return $methodNode->stmts;
82 16
    }
83 16
84
    private function isReturnVoid(?NodeAbstract $returnType) : bool
85
    {
86
        return $returnType instanceof Identifier && $returnType->name === 'void';
87 17
    }
88
}
89