Completed
Push — develop ( 718564...9c3bcc )
by Wachter
06:08
created

CachedSchemaStorage::getSchema()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php
2
3
/*
4
 * This file is part of Sulu.
5
 *
6
 * (c) MASSIVE ART WebServices GmbH
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Sulu\Bundle\ValidationBundle\JsonSchema;
13
14
use InvalidArgumentException;
15
use JsonSchema\Entity\JsonPointer;
16
use JsonSchema\Iterator\ObjectIterator;
17
use JsonSchema\SchemaStorage;
18
use Sulu\Bundle\ValidationBundle\Exceptions\MalFormedJsonException;
19
use Symfony\Component\Config\ConfigCache;
20
use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
21
use Symfony\Component\Config\FileLocatorInterface;
22
use Symfony\Component\Config\Resource\FileResource;
23
24
/**
25
 * This schema storage makes use of a config cache to prevent the same schemas multiple times. The cache is build on
26
 * base of the values in the sulu_validation.schemas parameter.
27
 */
28
class CachedSchemaStorage extends SchemaStorage implements CachedSchemaStorageInterface
29
{
30
    const FILE_PREFIX = 'file://';
31
32
    /**
33
     * @var FileLocatorInterface
34
     */
35
    private $fileLocator;
36
37
    /**
38
     * @var array
39
     */
40
    private $configuredSchemas;
41
42
    /**
43
     * @var bool
44
     */
45
    private $debugMode;
46
47
    /**
48
     * @var string
49
     */
50
    private $cacheFilePath;
51
52
    /**
53
     * @var bool
54
     */
55
    private $isInitialized = false;
56
57
    /**
58
     * @param array                $configuredSchemas array containing all file paths to configured schemas
59
     * @param FileLocatorInterface $fileLocator
60
     * @param string               $cacheFilePath
61
     * @param string               $environment
62
     */
63 10
    public function __construct(
64
        array $configuredSchemas,
65
        FileLocatorInterface $fileLocator,
66
        $cacheFilePath,
67
        $environment
68
    ) {
69 10
        parent::__construct();
70
71 10
        $this->fileLocator = $fileLocator;
72 10
        $this->configuredSchemas = $configuredSchemas;
73 10
        $this->debugMode = $environment !== 'prod';
74 10
        $this->cacheFilePath = $cacheFilePath;
75 10
    }
76
77
    /**
78
     * Initializes the a config cache from the schemas configured in the sulu_validation.schemas parameter.
79
     */
80 9
    public function initializeCache()
81
    {
82 9
        if ($this->isInitialized) {
83 9
            return;
84
        }
85
86 9
        $schemaCache = new ConfigCache($this->cacheFilePath, $this->debugMode);
87
88 9
        if (!$schemaCache->isFresh()) {
89 1
            $resources = [];
90 1
            $processedSchemas = [];
91
92 1
            foreach ($this->configuredSchemas as $schemaPath) {
93 1
                $this->processSchema($schemaPath, $processedSchemas, $resources);
94
            }
95
96 1
            $schemaCache->write(serialize($processedSchemas), $resources);
97
        }
98
99 9
        $this->schemas = unserialize(file_get_contents($schemaCache->getPath()));
100 9
        $this->isInitialized = true;
101 9
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106 8
    public function getSchemaByRoute($routeId)
107
    {
108 8
        if (!$this->initializeCache()) {
109 8
            $this->initializeCache();
110
        }
111
112 8
        $schemaFilePath = self::FILE_PREFIX . $this->fileLocator->locate($this->configuredSchemas[$routeId]);
113
114 8
        return $this->getSchema($schemaFilePath);
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 9
    public function getSchema($id)
121
    {
122 9
        if (!$this->initializeCache()) {
123 9
            $this->initializeCache();
124
        }
125
126 9
        return parent::getSchema($id);
127
    }
128
129
    /**
130
     * Locates, validates and adds schema to the cache.
131
     *
132
     * @param string $schemaPath
133
     * @param array  $serializedSchemas
134
     * @param array  $resources
135
     *
136
     * @throws MalFormedJsonException
137
     * @throws InvalidArgumentException
138
     * @throws FileLocatorFileNotFoundException
139
     */
140 1
    protected function processSchema($schemaPath, array &$serializedSchemas, array &$resources)
141
    {
142 1
        if (array_key_exists($schemaPath, $serializedSchemas)) {
143 1
            return;
144
        }
145
146 1
        $absoluteSchemaPath = $this->fileLocator->locate($schemaPath);
147 1
        $schema = json_decode(file_get_contents($absoluteSchemaPath));
148
149 1
        if (json_last_error() !== JSON_ERROR_NONE) {
150
            throw new MalFormedJsonException('Malformed json encountered in ' . $schemaPath);
151
        }
152
153 1
        if (strpos($absoluteSchemaPath, self::FILE_PREFIX) !== 0) {
154 1
            $absoluteSchemaPath = self::FILE_PREFIX . $absoluteSchemaPath;
155
        }
156
157 1
        $serializedSchemas[$absoluteSchemaPath] = $schema;
158 1
        $resources[] = new FileResource($absoluteSchemaPath);
0 ignored issues
show
Bug introduced by
It seems like $absoluteSchemaPath defined by $this->fileLocator->locate($schemaPath) on line 146 can also be of type array; however, Symfony\Component\Config...Resource::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
159 1
        $this->processReferencesInSchema($schema, $absoluteSchemaPath, $serializedSchemas, $resources);
0 ignored issues
show
Bug introduced by
It seems like $absoluteSchemaPath defined by $this->fileLocator->locate($schemaPath) on line 146 can also be of type array; however, Sulu\Bundle\ValidationBu...essReferencesInSchema() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
160 1
    }
161
162
    /**
163
     * Resolves references within a given schema and triggers the processing of the newly detected schemas.
164
     *
165
     * @param \stdClass $schema
166
     * @param string    $schemaFilePath
167
     * @param array     $serializedSchemas
168
     * @param array     $resources
169
     *
170
     * @throws MalFormedJsonException
171
     * @throws InvalidArgumentException
172
     * @throws FileLocatorFileNotFoundException
173
     */
174 1
    protected function processReferencesInSchema($schema, $schemaFilePath, array &$serializedSchemas, array &$resources)
175
    {
176 1
        $objectIterator = new ObjectIterator($schema);
177 1
        foreach ($objectIterator as $toResolveSchema) {
178 1
            if (property_exists($toResolveSchema, '$ref') && is_string($toResolveSchema->{'$ref'})) {
179 1
                $uri = $this->uriResolver->resolve($toResolveSchema->{'$ref'}, $schemaFilePath);
180 1
                $jsonPointer = new JsonPointer($uri);
181 1
                $toResolveSchema->{'$ref'} = (string) $jsonPointer;
182 1
                $this->processSchema($jsonPointer->getFilename(), $serializedSchemas, $resources);
183
            }
184
        }
185 1
    }
186
}
187