Passed
Push — master ( 429b06...f0c7c5 )
by Rafael
08:46
created

DefinitionRegistry::initialize()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 9.4285
cc 3
eloc 8
nc 3
nop 0
crap 3
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\Registry;
12
13
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
14
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
15
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
16
use Symfony\Component\Config\Definition\Processor;
17
use Ynlo\GraphQLBundle\Component\TaggedServices\TaggedServices;
18
use Ynlo\GraphQLBundle\Component\TaggedServices\TagSpecification;
19
use Ynlo\GraphQLBundle\Definition\DefinitionInterface;
20
use Ynlo\GraphQLBundle\Definition\Extension\DefinitionExtensionInterface;
21
use Ynlo\GraphQLBundle\Definition\Extension\DefinitionExtensionManager;
22
use Ynlo\GraphQLBundle\Definition\FieldsAwareDefinitionInterface;
23
use Ynlo\GraphQLBundle\Definition\Loader\DefinitionLoaderInterface;
24
use Ynlo\GraphQLBundle\Definition\MetaAwareInterface;
25
26
/**
27
 * DefinitionRegistry
28
 */
29
class DefinitionRegistry
30
{
31
    /**
32
     * @var TaggedServices
33
     */
34
    private $taggedServices;
35
36
    /**
37
     * @var DefinitionExtensionManager
38
     */
39
    private $extensionManager;
40
41
    /**
42
     * @var Endpoint
43
     */
44
    private static $endpoint;
45
46
    /**
47
     * @var string
48
     */
49
    private $cacheDir;
50
51
    /**
52
     * DefinitionRegistry constructor.
53
     *
54
     * @param TaggedServices             $taggedServices
55
     * @param DefinitionExtensionManager $extensionManager
56
     * @param null|string                $cacheDir
57
     */
58
    public function __construct(TaggedServices $taggedServices, DefinitionExtensionManager $extensionManager, ?string $cacheDir = null)
59
    {
60
        $this->taggedServices = $taggedServices;
61
        $this->extensionManager = $extensionManager;
62
        $this->cacheDir = $cacheDir;
63
    }
64 21
65
    /**
66 21
     * @return Endpoint
67 21
     */
68 21
    public function getEndpoint(): Endpoint
69 21
    {
70 21
        //use first static cache
71
        if (self::$endpoint) {
72
            return self::$endpoint;
73
        }
74
75 21
        $this->loadCache();
76
77
        //use file cache
78
        if (self::$endpoint) {
79 21
            return self::$endpoint;
80 21
        }
81
82
        $this->initialize();
83 1
84
        return self::$endpoint;
85 1
    }
86 1
87 1
    /**
88 1
     * remove the specification cache
89 1
     */
90
    public function clearCache()
91
    {
92
        @unlink($this->cacheFileName());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

92
        /** @scrutinizer ignore-unhandled */ @unlink($this->cacheFileName());

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
93 1
        $this->initialize();
94
    }
95 1
96
    /**
97
     * Initialize endpoint
98
     */
99
    protected function initialize()
100
    {
101
        self::$endpoint = new Endpoint();
102
103 1
        $specifications = $this->getTaggedServices('graphql.definition_loader');
104
        foreach ($specifications as $specification) {
105
            $resolver = $specification->getService();
106 1
            if ($resolver instanceof DefinitionLoaderInterface) {
107 1
                $resolver->loadDefinitions(self::$endpoint);
108 1
            }
109 1
        }
110 1
111 1
        $this->compile(self::$endpoint);
112 1
        $this->saveCache();
113 1
    }
114
115
    /**
116
     * @return string
117
     */
118
    protected function cacheFileName()
119 1
    {
120 1
        return $this->cacheDir.DIRECTORY_SEPARATOR.'graphql.registry_definitions.meta';
121 1
    }
122 1
123
    /**
124
     * Load cache
125 1
     */
126 1
    protected function loadCache()
127 1
    {
128 1
        if (file_exists($this->cacheFileName())) {
129
            $content = @file_get_contents($this->cacheFileName());
130
            if ($content) {
131
                self::$endpoint = unserialize($content);
132 1
            }
133
        }
134 1
    }
135
136
    /**
137
     * Save cache
138
     */
139
    protected function saveCache()
140
    {
141 1
        file_put_contents($this->cacheFileName(), serialize(self::$endpoint));
142
    }
143 1
144 1
    /**
145 1
     * Verify endpoint definitions and do some tasks to prepare the endpoint
146
     *
147 1
     * @param Endpoint $endpoint
148 1
     */
149
    protected function compile(Endpoint $endpoint)
150 1
    {
151 1
        //run all extensions for each definition
152 1
        foreach ($this->extensionManager->getExtensions() as $extension) {
153
            foreach ($endpoint->allTypes() as $type) {
154
                $this->configureDefinition($extension, $type, $endpoint);
155 1
                if ($type instanceof FieldsAwareDefinitionInterface) {
156 1
                    foreach ($type->getFields() as $field) {
157
                        $this->configureDefinition($extension, $field, $endpoint);
158
                        foreach ($field->getArguments() as $argument) {
159
                            $this->configureDefinition($extension, $argument, $endpoint);
160
                        }
161
                    }
162
                }
163 1
            }
164 1
165
            foreach ($endpoint->allQueries() as $query) {
166
                $this->configureDefinition($extension, $query, $endpoint);
167
                foreach ($query->getArguments() as $argument) {
168
                    $this->configureDefinition($extension, $argument, $endpoint);
169
                }
170
            }
171
            foreach ($endpoint->allMutations() as $mutation) {
172 1
                $this->configureDefinition($extension, $mutation, $endpoint);
173
                foreach ($mutation->getArguments() as $argument) {
174 1
                    $this->configureDefinition($extension, $argument, $endpoint);
175
                }
176
            }
177
178
            $extension->configureEndpoint($endpoint);
179
        }
180
    }
181
182
    /**
183
     * @param DefinitionExtensionInterface $extension
184
     * @param DefinitionInterface          $definition
185
     * @param Endpoint                     $endpoint
186
     */
187
    protected function configureDefinition(DefinitionExtensionInterface $extension, DefinitionInterface $definition, Endpoint $endpoint)
188
    {
189
        $config = [];
190
        if ($definition instanceof MetaAwareInterface) {
191
            $treeBuilder = new TreeBuilder();
192
            /** @var NodeBuilder $root */
193
            $root = $treeBuilder->root($extension->getName());
194
            $extension->buildConfig($root);
0 ignored issues
show
Bug introduced by
$root of type Symfony\Component\Config...ion\Builder\NodeBuilder is incompatible with the type Symfony\Component\Config...der\ArrayNodeDefinition expected by parameter $root of Ynlo\GraphQLBundle\Defin...nterface::buildConfig(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

194
            $extension->buildConfig(/** @scrutinizer ignore-type */ $root);
Loading history...
195
196
            if ($definition->hasMeta($extension->getName())) {
197
                $options = $definition->getMeta($extension->getName());
198
                $processor = new Processor();
199
200
                try {
201
                    $options = $extension->normalizeConfig($definition, $options);
202
                    $config = $processor->process($treeBuilder->buildTree(), [$options]);
203
                } catch (InvalidConfigurationException $exception) {
204
                    $error = sprintf('Error compiling schema definition "%s", %s', $definition->getName(), $exception->getMessage());
205
                    throw new \RuntimeException($error, 0, $exception);
206
                }
207
            }
208
        }
209
        $extension->configure($definition, $endpoint, $config);
210
    }
211
212
213
    /**
214
     * @param string $tag
215
     *
216
     * @return array|TagSpecification[]
217
     */
218
    private function getTaggedServices($tag): array
219
    {
220
        return $this->taggedServices->findTaggedServices($tag);
221
    }
222
}
223