Completed
Push — v2 ( b41681...2062ad )
by Joschi
04:41
created

VocabularyCache::isPrefix()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 3
crap 2
1
<?php
2
3
/**
4
 * micrometa
5
 *
6
 * @category Jkphl
7
 * @package Jkphl\Rdfalite
8
 * @subpackage Jkphl\Micrometa\Infrastructure
9
 * @author Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright Copyright © 2017 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2017 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Jkphl\Micrometa\Infrastructure\Parser\JsonLD;
38
39
use Jkphl\Micrometa\Ports\Cache;
40
use ML\JsonLD\RemoteDocument;
41
42
/**
43
 * Vocabulary cache
44
 *
45
 * @package Jkphl\Rdfalite
46
 * @subpackage Jkphl\Micrometa\Infrastructure
47
 */
48
class VocabularyCache
49
{
50
    /**
51
     * Documents
52
     *
53
     * @var RemoteDocument[]
54
     */
55
    protected $documents = [];
56
    /**
57
     * Vocabularies
58
     *
59
     * @var array
60
     */
61
    protected $vocabularies = [];
62
    /**
63
     * Vocabulary prefices
64
     *
65
     * @var array
66
     */
67
    protected $prefices = [];
68
    /**
69
     * Document cache slot
70
     *
71
     * @var string
72
     */
73
    const SLOT_DOC = 'jsonld.doc';
74
    /**
75
     * Vocabulary cache slot
76
     *
77
     * @var string
78
     */
79
    const SLOT_VOCABS = 'jsonld.vocabs';
80
81
    /**
82
     * Return a cached document
83
     *
84
     * @param string $url URL
85
     * @return RemoteDocument|null Cached document
86
     */
87 1
    public function getDocument($url)
88
    {
89 1
        $urlHash = $this->getCacheHash($url, self::SLOT_DOC);
90
91
        // Try to retrieve the document from the cache
92 1
        if (Cache::getAdapter()->hasItem($urlHash)) {
93 1
            return Cache::getAdapter()->getItem($urlHash)->get();
94
        }
95
96 1
        return null;
97
    }
98
99
    /**
100
     * Cache a document
101
     *
102
     * @param string $url URL
103
     * @param RemoteDocument $document Document
104
     * @return RemoteDocument Document
105
     */
106 1
    public function setDocument($url, RemoteDocument $document)
107
    {
108
        // Process the context
109 1
        if (isset($document->document->{'@context'}) && is_object($document->document->{'@context'})) {
110 1
            $this->processContext((array)$document->document->{'@context'});
111
        }
112
113
        // Save the document to the cache
114 1
        $docUrlHash = $this->getCacheHash($url, self::SLOT_DOC);
115 1
        $cachedDocument = Cache::getAdapter()->getItem($docUrlHash);
116 1
        $cachedDocument->set($document);
117 1
        Cache::getAdapter()->save($cachedDocument);
118
119
        // Return the document
120 1
        return $document;
121
    }
122
123
    /**
124
     * Process a context vocabulary
125
     *
126
     * @param array $context Context
127
     */
128 1
    protected function processContext(array $context)
129
    {
130 1
        $prefices = [];
131 1
        $vocabularyCache = Cache::getAdapter()->getItem(self::SLOT_VOCABS);
132 1
        $vocabularies = $vocabularyCache->isHit() ? $vocabularyCache->get() : [];
133
134
        // Run through all vocabulary terms
135 1
        foreach ($context as $name => $definition) {
136
            // Skip JSON-LD reserved terms
137 1
            if ($this->isReservedTokens($name, $definition)) {
138 1
                continue;
139
            }
140
141
            // Process this prefix / vocabulary term
142 1
            $this->processPrefixVocabularyTerm($name, $definition, $prefices, $vocabularies);
143
        }
144
145 1
        $vocabularyCache->set($vocabularies);
146 1
        Cache::getAdapter()->save($vocabularyCache);
147 1
    }
148
149
    /**
150
     * Test if a vocabulary name or definition is a reserved term
151
     *
152
     * @param string $name Name
153
     * @param string $definition Definition
154
     * @return boolean Is reserved term
155
     */
156 1
    protected function isReservedTokens($name, $definition)
157
    {
158 1
        return !strncmp('@', $name, 1) || (is_string($definition) && !strncmp('@', $definition, 1));
159
    }
160
161
    /**
162
     * Process a prefix / vocabulary term
163
     *
164
     * @param string $name Prefix name
165
     * @param string|\stdClass $definition Definition
166
     * @param array $prefices Prefix register
167
     * @param array $vocabularies Vocabulary register
168
     */
169 1
    protected function processPrefixVocabularyTerm($name, $definition, array &$prefices, array &$vocabularies)
170
    {
171
        // Register a prefix (and vocabulary)
172 1
        if ($this->isPrefix($name, $definition, $prefices)) {
173 1
            $this->processPrefix($name, $definition, $prefices, $vocabularies);
0 ignored issues
show
Bug introduced by
It seems like $definition defined by parameter $definition on line 169 can also be of type object<stdClass>; however, Jkphl\Micrometa\Infrastr...yCache::processPrefix() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
174
175
            // Else: Register vocabulary term
176 1
        } elseif ($this->isTerm($definition)) {
177 1
            $this->processVocabularyTerm($definition, $prefices, $vocabularies);
0 ignored issues
show
Bug introduced by
It seems like $definition defined by parameter $definition on line 169 can also be of type string; however, Jkphl\Micrometa\Infrastr...processVocabularyTerm() does only seem to accept object<stdClass>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
178
        }
179 1
    }
180
181
    /**
182
     * Test whether this is a prefix and vocabulary definition
183
     *
184
     * @param string $name Prefix name
185
     * @param string|\stdClass $definition Definition
186
     * @param array $prefices Prefix register
187
     * @return bool Is a prefix and vocabulary definition
188
     */
189 1
    protected function isPrefix($name, $definition, array &$prefices)
190
    {
191 1
        return is_string($definition) && !isset($prefices[$name]);
192
    }
193
194
    /**
195
     * Test whether this is a term definition
196
     *
197
     * @param string|\stdClass $definition Definition
198
     * @return bool Is a term definition
199
     */
200 1
    protected function isTerm($definition)
201
    {
202 1
        return is_object($definition) && isset($definition->{'@id'});
203
    }
204
205
    /**
206
     * Process a vocabulary prefix
207
     *
208
     * @param string $name Prefix name
209
     * @param string $definition Prefix definition
210
     * @param array $prefices Prefix register
211
     * @param array $vocabularies Vocabulary register
212
     */
213 1
    protected function processPrefix($name, $definition, array &$prefices, array &$vocabularies)
214
    {
215 1
        $prefices[$name] = $definition;
216
217
        // Register the vocabulary
218 1
        if (!isset($vocabularies[$definition])) {
219 1
            $vocabularies[$definition] = [];
220
        }
221 1
    }
222
223
    /**
224
     * Process a vocabulary term
225
     *
226
     * @param \stdClass $definition Term definition
227
     * @param array $prefices Prefix register
228
     * @param array $vocabularies Vocabulary register
229
     */
230 1
    protected function processVocabularyTerm($definition, array &$prefices, array &$vocabularies)
231
    {
232 1
        $prefixName = explode(':', $definition->{'@id'}, 2);
233 1
        if (count($prefixName) == 2) {
234 1
            if (isset($prefices[$prefixName[0]])) {
235 1
                $vocabularies[$prefices[$prefixName[0]]][$prefixName[1]] = true;
236
            }
237
        }
238 1
    }
239
240
    /**
241
     * Create an IRI from a name considering the known vocabularies
242
     *
243
     * @param string $name Name
244
     * @return \stdClass IRI
245
     */
246 2
    public function expandIRI($name)
247
    {
248 2
        $iri = (object)['name' => $name, 'profile' => ''];
249 2
        $vocabularies = Cache::getAdapter()->getItem(self::SLOT_VOCABS);
250
251
        // Run through all vocabularies
252 2
        if ($vocabularies->isHit()) {
253 1
            $this->matchVocabularies($name, $vocabularies->get(), $iri);
254
        }
255
256 2
        return $iri;
257
    }
258
259
    /**
260
     * Match a name with the known vocabularies
261
     *
262
     * @param string $name Name
263
     * @param array $vocabularies Vocabularies
264
     * @param \stdClass $iri IRI
265
     */
266 1
    protected function matchVocabularies($name, array $vocabularies, &$iri)
267
    {
268
        // Run through all vocabularies
269 1
        foreach ($vocabularies as $profile => $terms) {
270 1
            $profileLength = strlen($profile);
271
272
            // If the name matches the profile and the remaining string is a registered term
273 1
            if (!strncasecmp($profile, $name, $profileLength) && !empty($terms[substr($name, $profileLength)])) {
274 1
                $iri->profile = $profile;
275 1
                $iri->name = substr($name, $profileLength);
276 1
                return;
277
            }
278
        }
279 1
    }
280
281
    /**
282
     * Create a cache hash
283
     *
284
     * @param string $str String
285
     * @param string $slot Slot
286
     * @return string URL hash
287
     */
288 1
    protected function getCacheHash($str, $slot)
289
    {
290 1
        return $slot.'.'.md5($str);
291
    }
292
}
293