Completed
Push — master ( ed922a...ce73d9 )
by Nikola
03:26
created

ClassLoader   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 98
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 12
lcom 1
cbo 7
dl 0
loc 98
ccs 0
cts 53
cp 0
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
C load() 0 57 11
1
<?php
2
/*
3
 * This file is part of the Abstract builder package, an RunOpenCode project.
4
 *
5
 * (c) 2017 RunOpenCode
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace RunOpenCode\AbstractBuilder\Ast;
11
12
use PhpParser\NodeTraverser;
13
use PhpParser\NodeVisitor\NameResolver;
14
use PhpParser\ParserFactory;
15
use RunOpenCode\AbstractBuilder\Ast\Visitor\ClassIntrospectionVisitor;
16
use RunOpenCode\AbstractBuilder\Exception\RuntimeException;
17
18
class ClassLoader
19
{
20
    /**
21
     * @var \PhpParser\Parser
22
     */
23
    private $parser;
24
25
    /**
26
     * @var \PhpParser\NodeTraverser
27
     */
28
    private $traverser;
29
30
    /**
31
     * @var ClassIntrospectionVisitor
32
     */
33
    private $introspector;
34
35
    /**
36
     * ClassLoader constructor.
37
     */
38
    public function __construct()
39
    {
40
        $this->parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP7);
41
        $this->traverser = new NodeTraverser();
42
        $this->introspector = new ClassIntrospectionVisitor();
43
44
        $this->traverser->addVisitor(new NameResolver());
45
        $this->traverser->addVisitor($this->introspector);
46
    }
47
48
    /**
49
     * Load class and build its metadata value object
50
     *
51
     * @param string $class Filename or full qualified class name
52
     *
53
     * @return ClassMetadata
54
     *
55
     * @throws \RunOpenCode\AbstractBuilder\Exception\RuntimeException
56
     * @throws \RunOpenCode\AbstractBuilder\Exception\InvalidArgumentException
57
     */
58
    public function load($class)
59
    {
60
        $filename = $class;
61
62
        if (class_exists($class, true)) {
63
            $filename = (new \ReflectionClass($class))->getFileName();
64
        }
65
66
        if (file_exists($filename)) {
67
68
            $this->traverser->traverse($this->parser->parse(file_get_contents($filename)));
0 ignored issues
show
Bug introduced by
It seems like $this->parser->parse(fil...et_contents($filename)) targeting PhpParser\Parser::parse() 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?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
69
            $classes = $this->introspector->getClasses();
70
71
            if (0 === count($classes)) {
72
                throw new RuntimeException(sprintf('Class definition does not exist in "%s".', $filename));
73
            }
74
75
            if ($filename === $class && count($classes) > 1) {
76
                throw new RuntimeException(sprintf('There are more than one class definition in "%s" and it can not be determined which class to use.', $filename));
77
            }
78
79
            if (1 === count($classes)) {
80
81
                return new ClassMetadata(
82
                    $classes[0]['ast'],
83
                    $classes[0]['namespace'],
84
                    $classes[0]['class'],
85
                    $filename,
86
                    $classes[0]['final'],
87
                    $classes[0]['abstract'],
88
                    $classes[0]['methods'],
89
                    null !== $classes[0]['parent'] ? $this->load($classes[0]['parent']) : null
90
                );
91
            }
92
93
            $class = trim('\\', $class);
94
95
            foreach ($classes as $metadata) {
96
97
                if ($class === $metadata['fqcn']) {
98
99
                    return new ClassMetadata(
100
                        $metadata['ast'],
101
                        $metadata['namespace'],
102
                        $metadata['class'],
103
                        $filename,
104
                        $metadata['final'],
105
                        $metadata['abstract'],
106
                        $metadata['methods'],
107
                        null !== $metadata['parent'] ? $this->load($metadata['parent']) : null
108
                    );
109
                }
110
            }
111
        }
112
113
        throw new RuntimeException(sprintf('Unable to load class from "%s".', $class));
114
    }
115
}
116