Completed
Push — develop ( 67e161...8b0772 )
by Paul
05:50
created

PhpFileParser::parseNodes()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 2
nop 2
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace PhpUnitGen\Parser;
4
5
use PhpParser\Error;
6
use PhpParser\Node;
7
use PhpParser\Parser;
8
use PhpUnitGen\Exception\ParsingException;
9
use PhpUnitGen\Model\ModelInterface\PhpFileModelInterface;
10
use PhpUnitGen\Model\PhpFileModel;
11
use PhpUnitGen\Model\PropertyInterface\NodeInterface;
12
use PhpUnitGen\Parser\NodeParser\InterfaceNodeParser;
13
use PhpUnitGen\Parser\NodeParser\NamespaceNodeParser;
14
use PhpUnitGen\Parser\NodeParser\NodeParserInterface;
15
use PhpUnitGen\Parser\NodeParser\UseNodeParser;
16
use PhpUnitGen\Parser\ParserInterface\PhpFileParserInterface;
17
use Respect\Validation\Validator;
18
19
/**
20
 * Class PhpFileParser.
21
 *
22
 * @author     Paul Thébaud <[email protected]>.
23
 * @copyright  2017-2018 Paul Thébaud <[email protected]>.
24
 * @license    https://opensource.org/licenses/MIT The MIT license.
25
 * @link       https://github.com/paul-thebaud/phpunit-generator
26
 * @since      Class available since Release 2.0.0.
27
 */
28
class PhpFileParser implements PhpFileParserInterface
29
{
30
    /**
31
     * @var Parser $phpParser A parser to parse php code as a string.
32
     */
33
    private $phpParser;
34
35
    /**
36
     * @var NodeParserInterface[] $nodeParsers Mapping array between node class and node parser.
37
     */
38
    private $nodeParsers = [];
39
40
    /**
41
     * @var string[] $nodeChildrenParsingNeeded Array containing node type that need .
42
     */
43
    private $nodeChildrenParsingNeeded = [];
44
45
    /**
46
     * PhpFileParser constructor.
47
     *
48
     * @param Parser $phpParser
49
     */
50
    public function __construct(
51
        Parser $phpParser
52
    ) {
53
        $this->phpParser = $phpParser;
54
55
        $this->nodeParsers = [
56
            Node\Stmt\Namespace_::class => new NamespaceNodeParser(),
57
            Node\Stmt\Use_::class       => new UseNodeParser(),
58
            Node\Stmt\Function_::class  => null,
59
            Node\Stmt\Interface_::class => new InterfaceNodeParser(),
60
            Node\Stmt\Trait_::class     => null,
61
            Node\Stmt\Class_::class     => null
62
        ];
63
        $this->nodeChildrenParsingNeeded = [
64
            Node\Stmt\Namespace_::class,
65
            Node\Stmt\Interface_::class,
66
            Node\Stmt\Trait_::class,
67
            Node\Stmt\Class_::class
68
        ];
69
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74
    public function parse(string $code): PhpFileModelInterface
75
    {
76
        try {
77
            $nodes = $this->phpParser->parse($code);
78
        } catch (Error $error) {
79
            throw new ParsingException("Unable to parse given php code (maybe your code contains errors).");
80
        }
81
82
        /** @var PhpFileModelInterface $phpFileModel */
83
        $phpFileModel = new PhpFileModel();
84
        $phpFileModel = $this->parseNodes($nodes, $phpFileModel);
85
86
        return $phpFileModel;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $phpFileModel could return the type PhpUnitGen\Model\PropertyInterface\NodeInterface which includes types incompatible with the type-hinted return PhpUnitGen\Model\ModelIn...e\PhpFileModelInterface. Consider adding an additional type-check to rule them out.
Loading history...
87
    }
88
89
    /**
90
     * Parse nodes recursively if $nodes is validate as an array.
91
     *
92
     * @param mixed         $nodesToParse Nodes to parse.
93
     * @param NodeInterface $node         The node to inject parsed information in.
94
     *
95
     * @return NodeInterface The modified php file model.
96
     */
97
    private function parseNodes($nodesToParse, NodeInterface $node): NodeInterface
98
    {
99
        if (Validator::arrayType()->length(1, null)->validate($nodesToParse)) {
100
            foreach ($nodesToParse as $nodeToParse) {
101
                $node = $this->parseNode($nodeToParse, $node);
102
            }
103
        }
104
        return $node;
105
    }
106
107
    /**
108
     * Parse a node and statements recursively and inject information if possible.
109
     *
110
     * @param Node          $nodeToParse The node to parse.
111
     * @param NodeInterface $node        The node to inject parsed information in.
112
     *
113
     * @return NodeInterface The modified php file model.
114
     */
115
    private function parseNode(Node $nodeToParse, NodeInterface $node): NodeInterface
116
    {
117
        $children = [];
118
119
        $resultingNode = $node;
120
121
        // Find the type of the node
122
        $class = get_class($nodeToParse);
123
124
        if (Validator::key($class, Validator::instance(NodeParserInterface::class))
125
            ->validate($this->nodeParsers)
126
        ) {
127
            $resultingNode = ($this->nodeParsers[$class])->parse($nodeToParse, $resultingNode);
128
            // Check if we need to parse children of the node.
129
            if (Validator::contains($class)->validate($this->nodeChildrenParsingNeeded)) {
130
                return $this->parseNodes($children, $resultingNode);
131
            }
132
        }
133
134
        return $resultingNode;
135
    }
136
}
137