Completed
Push — master ( e7d3d6...8ae888 )
by Rafael
03:45
created

AnnotationLoader::loadDefinitions()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 11
cp 0
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 9
nc 6
nop 1
crap 42
1
<?php
2
/*******************************************************************************
3
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Definition\Loader;
12
13
use Doctrine\Common\Annotations\Reader;
14
use Symfony\Component\Finder\Finder;
15
use Symfony\Component\HttpKernel\Kernel;
16
use Ynlo\GraphQLBundle\Definition\Loader\Annotation\AnnotationParserInterface;
17
use Ynlo\GraphQLBundle\Definition\Registry\Endpoint;
18
19
/**
20
 * Resolve and load definitions based on common annotations
21
 */
22
class AnnotationLoader implements DefinitionLoaderInterface
23
{
24
    /**
25
     * Folders inside bundles to locate definitions
26
     * TODO: allow add additional mapping on bundle config
27
     */
28
    private const DEFINITIONS_LOCATIONS = [
29
        'Model', //non persistent models like interfaces, or abstract classes
30
        'Entity', //doctrine entities
31
        'Mutation', //custom actions
32
        'Query', //custom actions
33
    ];
34
35
    /**
36
     * @var Kernel
37
     */
38
    protected $kernel;
39
40
    /**
41
     * @var Reader
42
     */
43
    protected $reader;
44
45
    /**
46
     * @var iterable|array|AnnotationParserInterface[]
47
     */
48
    protected $annotationParsers = [];
49
50
    /**
51
     * @param Kernel                               $kernel
52
     * @param Reader                               $reader
53
     * @param iterable|AnnotationParserInterface[] $annotationParsers
54
     */
55
    public function __construct(Kernel $kernel, Reader $reader, $annotationParsers = [])
56
    {
57
        $this->kernel = $kernel;
58
        $this->reader = $reader;
59
        $this->annotationParsers = $annotationParsers;
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65
    public function loadDefinitions(Endpoint $endpoint): void
66
    {
67
        $classesToLoad = $this->resolveClasses();
68
        foreach ($this->annotationParsers as $parser) {
69
            if ($parser instanceof AnnotationParserInterface) {
70
                foreach ($classesToLoad as $class) {
71
                    $refClass = new \ReflectionClass($class);
72
                    $annotations = $this->reader->getClassAnnotations($refClass);
73
                    foreach ($annotations as $annotation) {
74
                        if ($parser->supports($annotation)) {
75
                            $parser->parse($annotation, $refClass, $endpoint);
76
                        }
77
                    }
78
                }
79
            }
80
        }
81
    }
82
83
    /**
84
     * @return array
85
     */
86
    protected function resolveClasses(): array
87
    {
88
        $bundles = $this->kernel->getBundles();
89
        $classes = [];
90
        foreach (self::DEFINITIONS_LOCATIONS as $definitionLocation) {
91
            foreach ($bundles as $bundle) {
92
                $path = $bundle->getPath().'/'.$definitionLocation;
93
                if (file_exists($path)) {
94
                    $classes = array_merge($classes, $this->extractNamespaceClasses($path, $bundle->getNamespace(), $definitionLocation));
95
                }
96
            }
97
98
            if (Kernel::VERSION_ID >= 40000) {
99
                $path = $this->kernel->getRootDir().'/'.$definitionLocation;
100
                if (file_exists($path)) {
101
                    $classes = array_merge($classes, $this->extractNamespaceClasses($path, 'App', $definitionLocation));
102
                }
103
            }
104
105
        }
106
107
        return array_unique($classes);
108
    }
109
110
    /**
111
     * @param string $path
112
     * @param string $baseNamespace
113
     * @param string $baseLocation
114
     *
115
     * @return array
116
     */
117
    protected function extractNamespaceClasses($path, $baseNamespace, $baseLocation)
118
    {
119
        $classes = [];
120
        $finder = new Finder();
121
        foreach ($finder->in($path)->name('/.php$/')->getIterator() as $file) {
122
            $className = preg_replace('/.php$/', null, $file->getFilename());
123
            if ($file->getRelativePath()) {
124
                $subNamespace = str_replace('/', '\\', $file->getRelativePath());
125
                $fullyClassName = $baseNamespace.'\\'.$baseLocation.'\\'.$subNamespace.'\\'.$className;
126
            } else {
127
                $fullyClassName = $baseNamespace.'\\'.$baseLocation.'\\'.$className;
128
            }
129
            if (class_exists($fullyClassName) || interface_exists($fullyClassName)) {
130
                $classes[] = $fullyClassName;
131
            }
132
        }
133
134
        return $classes;
135
    }
136
}
137