Completed
Pull Request — develop (#14)
by Michael
03:08 queued 56s
created

CachedSchemaStorage::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 10
nc 1
nop 4
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
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
    public function __construct(
64
        array $configuredSchemas,
65
        FileLocatorInterface $fileLocator,
66
        $cacheFilePath,
67
        $environment
68
    ) {
69
        parent::__construct();
70
71
        $this->fileLocator = $fileLocator;
72
        $this->configuredSchemas = $configuredSchemas;
73
        $this->debugMode = $environment !== 'prod';
74
        $this->cacheFilePath = $cacheFilePath;
75
    }
76
77
    /**
78
     * Initializes the a config cache from the schemas configured in the sulu_validation.schemas parameter.
79
     */
80
    public function initializeCache()
81
    {
82
        if (!$this->isInitialized) {
83
            $schemaCache = new ConfigCache($this->cacheFilePath, $this->debugMode);
84
85
            if (!$schemaCache->isFresh()) {
86
                $resources = [];
87
                $processedSchemas = [];
88
89
                foreach ($this->configuredSchemas as $schemaPath) {
90
                    $this->processSchema($schemaPath, $processedSchemas, $resources);
91
                }
92
93
                $schemaCache->write(serialize($processedSchemas), $resources);
94
            }
95
96
            $this->schemas = unserialize(file_get_contents($schemaCache->getPath()));
97
            $this->isInitialized = true;
98
        }
99
    }
100
101
    /**
102
     * Returns a based on a given route id.
103
     *
104
     * @param string $routeId
105
     *
106
     * @return \stdClass
107
     *
108
     * @throws InvalidArgumentException
109
     */
110
    public function getSchemaByRoute($routeId)
111
    {
112
        $schemaFilePath = self::FILE_PREFIX . $this->fileLocator->locate($this->configuredSchemas[$routeId]);
113
114
        return $this->getSchema($schemaFilePath);
115
    }
116
117
    /**
118
     * Locates, validates and adds schema to the cache.
119
     *
120
     * @param string $schemaPath
121
     * @param array  $serializedSchemas
122
     * @param array  $resources
123
     *
124
     * @throws MalFormedJsonException
125
     * @throws InvalidArgumentException
126
     * @throws FileLocatorFileNotFoundException
127
     */
128
    protected function processSchema($schemaPath, array &$serializedSchemas, array &$resources)
129
    {
130
        $this->initializeCache();
131
132
        if (array_key_exists($schemaPath, $serializedSchemas)) {
133
            return;
134
        }
135
136
        $absoluteSchemaPath = $this->fileLocator->locate($schemaPath);
137
        $schema = json_decode(file_get_contents($absoluteSchemaPath));
138
139
        if (json_last_error() !== JSON_ERROR_NONE) {
140
            throw new MalFormedJsonException('Malformed json encountered in ' . $schemaPath);
141
        }
142
143
        if (strpos($absoluteSchemaPath, self::FILE_PREFIX) !== 0) {
144
            $absoluteSchemaPath = self::FILE_PREFIX . $absoluteSchemaPath;
145
        }
146
147
        $serializedSchemas[$absoluteSchemaPath] = $schema;
148
        $resources[] = new FileResource($absoluteSchemaPath);
0 ignored issues
show
Bug introduced by
It seems like $absoluteSchemaPath defined by $this->fileLocator->locate($schemaPath) on line 136 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...
149
        $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 136 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...
150
    }
151
152
    /**
153
     * Resolves references within a given schema and triggers the processing of the newly detected schemas.
154
     *
155
     * @param \stdClass $schema
156
     * @param string    $schemaFilePath
157
     * @param array     $serializedSchemas
158
     * @param array     $resources
159
     *
160
     * @throws MalFormedJsonException
161
     * @throws InvalidArgumentException
162
     * @throws FileLocatorFileNotFoundException
163
     */
164
    protected function processReferencesInSchema($schema, $schemaFilePath, array &$serializedSchemas, array &$resources)
165
    {
166
        $objectIterator = new ObjectIterator($schema);
167
        foreach ($objectIterator as $toResolveSchema) {
168
            if (property_exists($toResolveSchema, '$ref') && is_string($toResolveSchema->{'$ref'})) {
169
                $uri = $this->uriResolver->resolve($toResolveSchema->{'$ref'}, $schemaFilePath);
170
                $jsonPointer = new JsonPointer($uri);
171
                $toResolveSchema->{'$ref'} = (string) $jsonPointer;
172
                $this->processSchema($jsonPointer->getFilename(), $serializedSchemas, $resources);
173
            }
174
        }
175
    }
176
}
177