Completed
Push — 2.x ( f14446...eb6a38 )
by Akihito
02:29
created

CodeGen::generate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 13
ccs 0
cts 12
cp 0
rs 9.4286
cc 1
eloc 10
nc 1
nop 3
crap 2
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 PhpParser\BuilderFactory;
10
use PhpParser\Comment\Doc;
11
use PhpParser\Node\Stmt;
12
use PhpParser\Node\Stmt\Class_;
13
use PhpParser\NodeTraverser;
14
use PhpParser\Parser;
15
use PhpParser\PrettyPrinter\Standard;
16
17
final class CodeGen implements CodeGenInterface
18
{
19
    /**
20
     * @var \PHPParser\Parser
21
     */
22
    private $parser;
23
24
    /**
25
     * @var \PHPParser\BuilderFactory
26
     */
27
    private $factory;
28
29
    /**
30
     * @var \PHPParser\PrettyPrinter\Standard
31
     */
32
    private $printer;
33
34
    /**
35
     * @var CodeGenMethod
36
     */
37
    private $codeGenMethod;
38
39
    /**
40
     * @param \PHPParser\Parser                 $parser
41
     * @param \PHPParser\BuilderFactory         $factory
42
     * @param \PHPParser\PrettyPrinter\Standard $printer
43
     */
44
    public function __construct(
45
        Parser $parser,
46
        BuilderFactory $factory,
47
        Standard $printer
48
    ) {
49
        $this->parser = $parser;
50
        $this->factory = $factory;
51
        $this->printer = $printer;
52
        $this->codeGenMethod = new CodeGenMethod($parser, $factory, $printer);
53
    }
54
55
    /**
56
     * @param string           $class
57
     * @param \ReflectionClass $sourceClass
58
     *
59
     * @return string
60
     */
61
    public function generate($class, \ReflectionClass $sourceClass, BindInterface $bind)
62
    {
63
        $methods = $this->codeGenMethod->getMethods($sourceClass, $bind);
64
        $stmt = $this
65
            ->getClass($class, $sourceClass)
66
            ->addStmts($methods)
67
            ->getNode();
68
        $stmt = $this->addClassDocComment($stmt, $sourceClass);
69
        $code = $this->printer->prettyPrint([$stmt]);
70
        $statements = $this->getUseStatements($sourceClass);
71
72
        return $statements . $code;
73
    }
74
75
    /**
76
     * @param \ReflectionClass $class
77
     *
78
     * @return string
79
     */
80
    private function getUseStatements(\ReflectionClass $class)
81
    {
82
        $traverser = new NodeTraverser();
83
        $useStmtsVisitor = new CodeGenVisitor();
84
        $traverser->addVisitor($useStmtsVisitor);
85
        // parse
86
        $stmts = $this->parser->parse(file_get_contents($class->getFileName()));
87
        // traverse
88
        $traverser->traverse($stmts);
0 ignored issues
show
Bug introduced by
It seems like $stmts defined by $this->parser->parse(fil...$class->getFileName())) on line 86 can also be of type null; however, PhpParser\NodeTraverser::traverse() does only seem to accept array<integer,object<PhpParser\Node>>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
89
        // pretty print
90
        $code = $this->printer->prettyPrint($useStmtsVisitor());
91
92
        return (string) $code;
93
    }
94
95
    /**
96
     * Return class statement
97
     *
98
     * @param string           $newClassName
99
     * @param \ReflectionClass $class
100
     *
101
     * @return \PhpParser\Builder\Class_
102
     */
103
    private function getClass($newClassName, \ReflectionClass $class)
104
    {
105
        $parentClass = $class->name;
106
        $builder = $this->factory
107
            ->class($newClassName)
108
            ->extend($parentClass)
109
            ->implement('Ray\Aop\WeavedInterface')
110
            ->addStmt(
111
                $this->factory->property('isIntercepting')->makePrivate()->setDefault(true)
112
            )->addStmt(
113
                $this->factory->property('bind')->makePublic()
114
            );
115
116
        return $builder;
117
    }
118
119
    /**
120
     * Add class doc comment
121
     *
122
     * @param Class_           $node
123
     * @param \ReflectionClass $class
124
     *
125
     * @return \PHPParser\Node\Stmt\Class_
126
     */
127
    private function addClassDocComment(Class_ $node, \ReflectionClass $class)
128
    {
129
        $docComment = $class->getDocComment();
130
        if ($docComment) {
131
            $node->setAttribute('comments', [new Doc($docComment)]);
132
        }
133
134
        return $node;
135
    }
136
}
137