8 * Copyright (c) 2009-2014 Nicholas J Humfrey. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * 3. The name of the author 'Nicholas J Humfrey" may be used to endorse or
18 * promote products derived from this software without specific prior
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
34 * @copyright Copyright (c) 2009-2014 Nicholas J Humfrey
35 * @license http://www.opensource.org/licenses/bsd-license.php
39 * A namespace registry and manipulation class.
42 * @copyright Copyright (c) 2009-2014 Nicholas J Humfrey
43 * @license http://www.opensource.org/licenses/bsd-license.php
45 class EasyRdf_Namespace
47 /** Namespace registry
49 * List of default namespaces come from:
50 * - http://www.w3.org/2011/rdfa-context/rdfa-1.1
52 * With a few extras added.
55 private static $initial_namespaces = array(
56 'bibo' => 'http://purl.org/ontology/bibo/',
57 'cc' => 'http://creativecommons.org/ns#',
58 'cert' => 'http://www.w3.org/ns/auth/cert#',
59 'ctag' => 'http://commontag.org/ns#',
60 'dc' => 'http://purl.org/dc/terms/',
61 'dc11' => 'http://purl.org/dc/elements/1.1/',
62 'dcat' => 'http://www.w3.org/ns/dcat#',
63 'dcterms' => 'http://purl.org/dc/terms/',
64 'doap' => 'http://usefulinc.com/ns/doap#',
65 'exif' => 'http://www.w3.org/2003/12/exif/ns#',
66 'foaf' => 'http://xmlns.com/foaf/0.1/',
67 'geo' => 'http://www.w3.org/2003/01/geo/wgs84_pos#',
68 'gr' => 'http://purl.org/goodrelations/v1#',
69 'grddl' => 'http://www.w3.org/2003/g/data-view#',
70 'ical' => 'http://www.w3.org/2002/12/cal/icaltzd#',
71 'ma' => 'http://www.w3.org/ns/ma-ont#',
72 'og' => 'http://ogp.me/ns#',
73 'org' => 'http://www.w3.org/ns/org#',
74 'owl' => 'http://www.w3.org/2002/07/owl#',
75 'prov' => 'http://www.w3.org/ns/prov#',
76 'qb' => 'http://purl.org/linked-data/cube#',
77 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
78 'rdfa' => 'http://www.w3.org/ns/rdfa#',
79 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
80 'rev' => 'http://purl.org/stuff/rev#',
81 'rif' => 'http://www.w3.org/2007/rif#',
82 'rr' => 'http://www.w3.org/ns/r2rml#',
83 'rss' => 'http://purl.org/rss/1.0/',
84 'schema' => 'http://schema.org/',
85 'sd' => 'http://www.w3.org/ns/sparql-service-description#',
86 'sioc' => 'http://rdfs.org/sioc/ns#',
87 'skos' => 'http://www.w3.org/2004/02/skos/core#',
88 'skosxl' => 'http://www.w3.org/2008/05/skos-xl#',
89 'synd' => 'http://purl.org/rss/1.0/modules/syndication/',
90 'v' => 'http://rdf.data-vocabulary.org/#',
91 'vcard' => 'http://www.w3.org/2006/vcard/ns#',
92 'void' => 'http://rdfs.org/ns/void#',
93 'wdr' => 'http://www.w3.org/2007/05/powder#',
94 'wdrs' => 'http://www.w3.org/2007/05/powder-s#',
95 'wot' => 'http://xmlns.com/wot/0.1/',
96 'xhv' => 'http://www.w3.org/1999/xhtml/vocab#',
97 'xml' => 'http://www.w3.org/XML/1998/namespace',
98 'xsd' => 'http://www.w3.org/2001/XMLSchema#',
101 private static $namespaces = null;
103 private static $default = null;
105 /** Counter for numbering anonymous namespaces */
106 private static $anonymousNamespaceCount = 0;
109 * Return all the namespaces registered
111 * @return array Associative array of all the namespaces.
113 public static function namespaces()
115 if (self::$namespaces === null) {
116 self::resetNamespaces();
119 return self::$namespaces;
123 * Resets list of namespaces to the one, which is provided by EasyRDF
124 * useful for tests, among other things
126 public static function resetNamespaces()
128 self::$namespaces = self::$initial_namespaces;
132 * Return a namespace given its prefix.
134 * @param string $prefix The namespace prefix (eg 'foaf')
135 * @return string The namespace URI (eg 'http://xmlns.com/foaf/0.1/')
137 public static function get($prefix)
139 if (!is_string($prefix) or $prefix === null) {
140 throw new InvalidArgumentException(
141 "\$prefix should be a string and cannot be null or empty"
145 if (preg_match('/\W/', $prefix)) {
146 throw new InvalidArgumentException(
147 "\$prefix should only contain alpha-numeric characters"
151 $prefix = strtolower($prefix);
152 $namespaces = self::namespaces();
154 if (array_key_exists($prefix, $namespaces)) {
155 return $namespaces[$prefix];
162 * Register a new namespace.
164 * @param string $prefix The namespace prefix (eg 'foaf')
165 * @param string $long The namespace URI (eg 'http://xmlns.com/foaf/0.1/')
167 public static function set($prefix, $long)
169 if (!is_string($prefix) or $prefix === null) {
170 throw new InvalidArgumentException(
171 "\$prefix should be a string and cannot be null or empty"
175 if ($prefix !== '') {
176 // prefix ::= Name minus ":" // see: http://www.w3.org/TR/REC-xml-names/#NT-NCName
177 // Name ::= NameStartChar (NameChar)* // see: http://www.w3.org/TR/REC-xml/#NT-Name
178 // NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] |
179 // [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] |
180 // [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
181 // NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
184 'A-Z_a-z\xc0-\xD6\xd8-\xf6\xf8-\xff\x{0100}-\x{02ff}\x{0370}-\x{037d}' .
185 '\x{037F}-\x{1FFF}\x{200C}-\x{200D}\x{2070}-\x{218F}\x{2C00}-\x{2FEF}\x{3001}-\x{D7FF}' .
186 '\x{F900}-\x{FDCF}\x{FDF0}-\x{FFFD}\x{10000}-\x{EFFFF}';
190 '\-.0-9\xb7\x{0300}-\x{036f}\x{203f}-\x{2040}';
192 $regex = "#^[{$_name_start_char}]{1}[{$_name_char}]{0,}$#u";
194 $match_result = preg_match($regex, $prefix);
196 if ($match_result === false) {
197 throw new LogicException('regexp error');
200 if ($match_result === 0) {
201 throw new InvalidArgumentException(
202 "\$prefix should match RDFXML-QName specification. got: {$prefix}"
207 if (!is_string($long) or $long === null or $long === '') {
208 throw new InvalidArgumentException(
209 "\$long should be a string and cannot be null or empty"
213 $prefix = strtolower($prefix);
215 $namespaces = self::namespaces();
216 $namespaces[$prefix] = $long;
218 self::$namespaces = $namespaces;
222 * Get the default namespace
224 * Returns the URI of the default namespace or null
225 * if no default namespace is defined.
227 * @return string The URI of the default namespace
229 public static function getDefault()
231 return self::$default;
235 * Set the default namespace
237 * Set the default namespace to either a URI or the prefix of
238 * an already defined namespace.
241 * EasyRdf_Namespace::setDefault('http://schema.org/');
243 * @param string $namespace The URI or prefix of a namespace (eg 'og')
245 public static function setDefault($namespace)
247 if (is_null($namespace) or $namespace === '') {
248 self::$default = null;
249 } elseif (preg_match('/^\w+$/', $namespace)) {
250 $namespaces = self::namespaces();
252 if (!isset($namespaces[$namespace])) {
253 throw new InvalidArgumentException(
254 "Unable to set default namespace to unknown prefix: $namespace"
258 self::$default = $namespaces[$namespace];
260 self::$default = $namespace;
265 * Delete an existing namespace.
267 * @param string $prefix The namespace prefix (eg 'foaf')
269 public static function delete($prefix)
271 if (!is_string($prefix) or $prefix === null or $prefix === '') {
272 throw new InvalidArgumentException(
273 "\$prefix should be a string and cannot be null or empty"
277 $prefix = strtolower($prefix);
278 self::namespaces(); // make sure, that self::$namespaces is initialized
279 if (isset(self::$namespaces[$prefix])) {
280 unset(self::$namespaces[$prefix]);
285 * Delete the anonymous namespaces and reset the counter to 0
287 public static function reset()
289 while (self::$anonymousNamespaceCount > 0) {
290 self::delete('ns'.(self::$anonymousNamespaceCount-1));
291 self::$anonymousNamespaceCount--;
296 * Try and breakup a URI into a prefix and local part
298 * If $createNamespace is true, and the URI isn't part of an existing
299 * namespace, then EasyRdf will attempt to create a new namespace and
300 * return the name of the new prefix (for example 'ns0', 'term').
302 * If it isn't possible to split the URI, then null will be returned.
304 * @param string $uri The full URI (eg 'http://xmlns.com/foaf/0.1/name')
305 * @param bool $createNamespace If true, a new namespace will be created
306 * @return array The split URI (eg 'foaf', 'name') or null
308 public static function splitUri($uri, $createNamespace = false)
310 if ($uri === null or $uri === '') {
311 throw new InvalidArgumentException(
312 "\$uri cannot be null or empty"
316 if (is_object($uri) and ($uri instanceof EasyRdf_Resource)) {
317 $uri = $uri->getUri();
318 } elseif (!is_string($uri)) {
319 throw new InvalidArgumentException(
320 "\$uri should be a string or EasyRdf_Resource"
324 foreach (self::namespaces() as $prefix => $long) {
325 if (substr($uri, 0, strlen($long)) !== $long) {
329 $local_part = substr($uri, strlen($long));
331 if (strpos($local_part, '/') !== false) {
332 // we can't have '/' in local part
336 return array($prefix, $local_part);
339 if ($createNamespace) {
340 // Try and create a new namespace
341 # FIXME: check the valid characters for an XML element name
342 if (preg_match('/^(.+?)([\w\-]+)$/', $uri, $matches)) {
343 $prefix = "ns".(self::$anonymousNamespaceCount++);
344 self::set($prefix, $matches[1]);
345 return array($prefix, $matches[2]);
353 * Return the prefix namespace that a URI belongs to.
355 * @param string $uri A full URI (eg 'http://xmlns.com/foaf/0.1/name')
356 * @return string The prefix namespace that it is a part of(eg 'foaf')
358 public static function prefixOfUri($uri)
360 if ($parts = self::splitUri($uri)) {
366 * Shorten a URI by substituting in the namespace prefix.
368 * If $createNamespace is true, and the URI isn't part of an existing
369 * namespace, then EasyRdf will attempt to create a new namespace and
370 * use that namespace to shorten the URI (for example ns0:term).
372 * If it isn't possible to shorten the URI, then null will be returned.
374 * @param string $uri The full URI (eg 'http://xmlns.com/foaf/0.1/name')
375 * @param bool $createNamespace If true, a new namespace will be created
376 * @return string The shortened URI (eg 'foaf:name') or null
378 public static function shorten($uri, $createNamespace = false)
380 if ($parts = self::splitUri($uri, $createNamespace)) {
381 return implode(':', $parts);
386 * Expand a shortened URI (qname) back into a full URI.
388 * If it isn't possible to expand the qname, for example if the namespace
389 * isn't registered, then the original string will be returned.
391 * @param string $shortUri The short URI (eg 'foaf:name')
392 * @return string The full URI (eg 'http://xmlns.com/foaf/0.1/name')
394 public static function expand($shortUri)
396 if (!is_string($shortUri) or $shortUri === '') {
397 throw new InvalidArgumentException(
398 "\$shortUri should be a string and cannot be null or empty"
402 if ($shortUri === 'a') {
403 $namespaces = self::namespaces();
404 return $namespaces['rdf'] . 'type';
405 } elseif (preg_match('/^(\w+?):([\w\-]+)$/', $shortUri, $matches)) {
406 $long = self::get($matches[1]);
408 return $long . $matches[2];
410 } elseif (preg_match('/^(\w+)$/', $shortUri) and isset(self::$default)) {
411 return self::$default . $shortUri;