Completed
Push — master ( cbfed7...c39fb6 )
by Rafael
05:06
created

AnnotationLoader::loadDefinitions()   C

Complexity

Conditions 8
Paths 18

Size

Total Lines 39
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 26
nc 18
nop 1
dl 0
loc 39
ccs 28
cts 28
cp 1
crap 8
rs 5.3846
c 0
b 0
f 0
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\AnnotationReader;
14
use Symfony\Component\DependencyInjection\ContainerInterface;
15
use Symfony\Component\DependencyInjection\Definition;
16
use Symfony\Component\Finder\Finder;
17
use Ynlo\GraphQLBundle\Component\TaggedServices\TaggedServices;
18
use Ynlo\GraphQLBundle\Definition\Loader\Annotation\AnnotationParserInterface;
19
use Ynlo\GraphQLBundle\Definition\Registry\DefinitionManager;
20
21
/**
22
 * Resolve and load definitions based on common annotations
23
 */
24
class AnnotationLoader implements DefinitionLoaderInterface
25
{
26
    /**
27
     * Folders inside bundles to locate definitions
28
     * TODO: allow add additional mapping on bundle config
29
     */
30
    private const DEFINITIONS_LOCATIONS = [
31
        'Model', //non persistent models like interfaces, or abstract classes
32
        'Entity', //doctrine entities
33
        'Mutation', //custom actions
34
        'Query', //custom actions
35
    ];
36
37
    /**
38
     * @var ContainerInterface
39
     */
40
    protected $container;
41
42
    /**
43
     * @var AnnotationReader
44
     */
45
    protected $reader;
46
47
    /**
48
     * @param ContainerInterface $container
49
     */
50 1
    public function __construct(ContainerInterface $container)
51
    {
52 1
        $this->container = $container;
53 1
        $this->reader = $container->get('annotations.reader');
54 1
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59 1
    public function loadDefinitions(DefinitionManager $definitionManager): void
60
    {
61
        /** @var Definition $resolversServiceDefinition */
62 1
        $resolverDefinitions = $this->container
63 1
            ->get(TaggedServices::class)
64 1
            ->findTaggedServices('graphql.definition_resolver');
65
66 1
        $resolvers = [];
67 1
        foreach ($resolverDefinitions as $resolverDefinition) {
68 1
            $attr = $resolverDefinition->getAttributes();
69 1
            $priority = 0;
70 1
            if (isset($attr['priority'])) {
71 1
                $priority = $attr['priority'];
72
            }
73
74 1
            $resolvers[] = [$priority, $resolverDefinition->getService()];
75
        }
76
77
        //sort by priority
78 1
        usort(
79 1
            $resolvers,
80 1
            function ($service1, $service2) {
81 1
                list($priority1) = $service1;
82 1
                list($priority2) = $service2;
83
84 1
                return version_compare($priority2, $priority1);
85 1
            }
86
        );
87
88 1
        $classesToLoad = $this->resolveClasses();
89 1
        foreach ($resolvers as $resolver) {
90 1
            list(, $resolver) = $resolver;
91 1
            if ($resolver instanceof AnnotationParserInterface) {
92 1
                foreach ($classesToLoad as $class) {
93 1
                    $refClass = new \ReflectionClass($class);
94 1
                    $annotations = $this->reader->getClassAnnotations($refClass);
95 1
                    foreach ($annotations as $annotation) {
96 1
                        if ($resolver->supports($annotation)) {
97 1
                            $resolver->parse($annotation, $refClass, $definitionManager);
98
                        }
99
                    }
100
                }
101
            }
102
        }
103 1
    }
104
105
    /**
106
     * @return array
107
     */
108 1
    protected function resolveClasses(): array
109
    {
110 1
        $bundles = $this->container->get('kernel')->getBundles();
111 1
        $classes = [];
112 1
        foreach (self::DEFINITIONS_LOCATIONS as $definitionLocation) {
113 1
            foreach ($bundles as $bundle) {
114 1
                $path = $bundle->getPath().'/'.$definitionLocation;
115 1
                if (file_exists($path)) {
116 1
                    $finder = new Finder();
117 1
                    foreach ($finder->in($path)->name('/.php$/')->getIterator() as $file) {
118 1
                        $namespace = $bundle->getNamespace();
119 1
                        $className = preg_replace('/.php$/', null, $file->getFilename());
120
121 1
                        if ($file->getRelativePath()) {
122 1
                            $subNamespace = str_replace('/', '\\', $file->getRelativePath());
123 1
                            $fullyClassName = $namespace.'\\'.$definitionLocation.'\\'.$subNamespace.'\\'.$className;
124
                        } else {
125 1
                            $fullyClassName = $namespace.'\\'.$definitionLocation.'\\'.$className;
126
                        }
127
128 1
                        if (class_exists($fullyClassName) || interface_exists($fullyClassName)) {
129 1
                            $classes[] = $fullyClassName;
130
                        }
131
                    }
132
                }
133
            }
134
        }
135
136 1
        return array_unique($classes);
137
    }
138
}
139