/[drupal]/contributions/modules/rdf/rdf.api.inc
ViewVC logotype

Contents of /contributions/modules/rdf/rdf.api.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.40 - (show annotations) (download) (as text)
Sun Aug 16 00:17:41 2009 UTC (3 months, 1 week ago) by arto
Branch: MAIN
CVS Tags: HEAD
Changes since 1.39: +16 -4 lines
File MIME type: text/x-php
Implemented RSS feed support for extended datetime literals that include CCK datetime fields' time zone names.
1 <?php
2 // $Id: rdf.api.inc,v 1.39 2009/07/10 14:40:08 arto Exp $
3
4 //////////////////////////////////////////////////////////////////////////////
5 // RDF API settings
6
7 define('RDF_ARC2_PATH', drupal_get_path('module', 'rdf') . '/vendor/arc');
8 define('RDF_FORMAT', variable_get('rdf_format', 'rdf+json'));
9 define('RDF_FORMAT_RSS', variable_get('rdf_format_rss', 'rdf+xml'));
10 define('RDF_SITE_URI', url(NULL, array('absolute' => TRUE)));
11
12 //////////////////////////////////////////////////////////////////////////////
13 // RDF API repository selection
14
15 /**
16 * Limits future queries and operations to a particular repository.
17 */
18 function rdf_use_repository($name = NULL) {
19 if (is_null($name)) {
20 unset($GLOBALS['rdf_repository']);
21 }
22 else {
23 $GLOBALS['rdf_repository'] = $name;
24 }
25 }
26
27 //////////////////////////////////////////////////////////////////////////////
28 // RDF API namespace support
29
30 function rdf_define_vocabularies() {
31 $properties = rdf_rdf_properties();
32 foreach (rdf_rdf_namespaces() as $prefix => $base_uri) {
33 if (isset($properties[$prefix])) {
34 rdf_define_vocabulary($prefix, $base_uri, $properties[$prefix]);
35 }
36 }
37 }
38
39 /**
40 * Defines an RDF namespace class for predicate URI construction based on a
41 * given base URI and a set of properties.
42 */
43 function rdf_define_vocabulary($class_name, $base_uri, array $properties) {
44 if (!class_exists('RDF_Namespace')) {
45 eval('abstract class RDF_Namespace {}');
46 }
47
48 $code = array();
49 $code[] = "abstract class $class_name extends RDF_Namespace {";
50 foreach ($properties as $property) {
51 $code[] = "\tconst $property = '{$base_uri}{$property}';";
52 }
53 $code[] = "static function uriref(\$property) { return rdf_uri('{$base_uri}' . \$property); }";
54 $code[] = '}';
55 return eval(implode("\n", $code));
56 }
57
58 //////////////////////////////////////////////////////////////////////////////
59 // RDF API statement-centric queries
60
61 /**
62 * Determines whether a given statement exists.
63 *
64 * @param $subject
65 * @param $predicate
66 * @param $object
67 * @param $options
68 * @return
69 * TRUE if the statement exists, FALSE otherwise.
70 */
71 function rdf_exists($subject, $predicate = NULL, $object = NULL, array $options = array()) {
72 return rdf_count($subject, $predicate, $object, $options) > 0;
73 }
74
75 /**
76 * Returns the number of matching statements.
77 *
78 * @param $subject
79 * @param $predicate
80 * @param $object
81 * @param $options
82 * @return
83 */
84 function rdf_count($subject = NULL, $predicate = NULL, $object = NULL, array $options = array()) {
85 $results = rdf_query($subject, $predicate, $object, $options);
86 return count(is_object($results) ? $results->to_array() : $results);
87 }
88
89 /**
90 * @param $subject
91 * @param $predicate
92 * @param $default
93 * @param $options
94 */
95 function rdf_value($subject, $predicate, $default = NULL, array $options = array()) {
96 foreach (rdf_query($subject, $predicate, NULL, $options) as $statement) {
97 return $statement[2]; // object
98 }
99 return $default;
100 }
101
102 /**
103 * Finds all statements matching a given triple pattern.
104 *
105 * @param $subject
106 * @param $predicate
107 * @param $object
108 * @param $options
109 * @return
110 * An instance of RDF_QueryIterator, yielding denormalized statements.
111 */
112 function rdf_query($subject = NULL, $predicate = NULL, $object = NULL, array $options = array()) {
113 $subject = $subject ? _rdf_query_arg($subject) : $subject;
114 $predicate = $predicate ? _rdf_query_arg($predicate) : $predicate;
115 $repos = isset($options['repository']) ? $options['repository'] : NULL;
116
117 $results = new RDF_QueryIterator();
118 foreach (_rdf_get_callbacks('query', $repos) as $callback) {
119 list($callable, $callable_args) = $callback;
120 $args = array($subject, $predicate, $object, $options);
121 $args = !is_array($callable_args) ? $args : array_merge($callable_args, $args);
122 $results->append(new IteratorIterator(new RDF_QueryCallback($callable, $args)));
123 }
124 return $results;
125 }
126
127 //////////////////////////////////////////////////////////////////////////////
128 // RDF API statement-centric operations
129
130 /**
131 * Inserts multiple new statements.
132 *
133 * @param $statements
134 * @return
135 * TRUE if all statements were successfully inserted, FALSE otherwise.
136 */
137 function rdf_insert_all($statements, array $options = array()) {
138 $result = TRUE;
139 foreach ($statements as $statement) {
140 $result = call_user_func_array('rdf_insert', array_merge($statement, array($options))) && $result;
141 }
142 return $result;
143 }
144
145 /**
146 * Inserts a new statement.
147 *
148 * @param $subject
149 * @param $predicate
150 * @param $object
151 * @param $options
152 * @return
153 * TRUE, or a repository-specific non-NULL value, if the statement was
154 * successfully inserted; FALSE if an error occurred.
155 */
156 function rdf_insert($subject, $predicate, $object, array $options = array()) {
157 $subject = $subject ? _rdf_query_arg($subject) : $subject;
158 $predicate = $predicate ? _rdf_query_arg($predicate) : $predicate;
159 $repos = isset($options['repository']) ? $options['repository'] : NULL;
160
161 return _rdf_invoke_op('insert', array($subject, $predicate, $object, $options), $repos);
162 }
163
164 /**
165 * Deletes an existing statement.
166 *
167 * @param $subject
168 * @param $predicate
169 * @param $object
170 * @param $options
171 * @return
172 * TRUE, or a repository-specific non-NULL value, if the statement was
173 * successfully deleted; FALSE if an error occurred.
174 */
175 function rdf_delete($subject, $predicate, $object, array $options = array()) {
176 $subject = $subject ? _rdf_query_arg($subject) : $subject;
177 $predicate = $predicate ? _rdf_query_arg($predicate) : $predicate;
178 $repos = isset($options['repository']) ? $options['repository'] : NULL;
179
180 return _rdf_invoke_op('delete', array($subject, $predicate, $object, $options), $repos);
181 }
182
183 //////////////////////////////////////////////////////////////////////////////
184 // RDF API data constructors
185
186 function rdf_triple($subject, $predicate, $object) {
187 return func_get_args();
188 }
189
190 function rdf_mailto($email) {
191 return rdf_uri('mailto:' . $email);
192 }
193
194 function rdf_uri($uri) {
195 return RDF_URIRef::uri($uri);
196 }
197
198 function rdf_uriref($uri) { return rdf_uri($uri); } /* deprecated */
199 function r($uri) { return rdf_uri($uri); }
200
201 function rdf_bnode($id = NULL) {
202 return $id ? RDF_BNode::id($id) : RDF_BNode::generate();
203 }
204
205 function rdf_datetime($timestamp = NULL, $tz_offset = NULL, $tz_name = NULL, $datatype = 'xsd:dateTime') {
206 if (is_numeric($timestamp)) {
207 $timestamp = (int)$timestamp;
208 }
209 else if (is_string($timestamp)) {
210 $timestamp = strtotime($timestamp);
211 }
212 else if (is_null($timestamp)) {
213 $timestamp = gmmktime();
214 }
215
216 $datetime = format_date($timestamp, 'custom', 'Y-m-d\TH:i:sO', (int)$tz_offset);
217 $datetime = str_replace('+0000', 'Z', $datetime); // clean up format_date() result
218 $datetime = preg_replace('/([+-])(\d{2}):?(\d{2})$/', '\1\2:\3', $datetime);
219
220 // This is a non-standard extension for outputting the time zone name
221 // appended to the datetime literal value. If you don't know why you'd do
222 // this, then you don't need this. However, if you *do* need this, make
223 // sure to also specify another literal datatype than 'xsd:dateTime' in
224 // the $datatype argument in order to avoid overloading the XSD semantics.
225 // Example output: "2009-08-15T23:59:59-04:00 America/New_York"^^$datatype
226 if ($tz_name) {
227 $datetime .= ' ' . (string)$tz_name;
228 }
229
230 return rdf_literal($datetime, NULL, $datatype);
231 }
232
233 function rdf_literal($value, $language = NULL, $datatype = NULL) {
234 $language = is_object($language) && isset($language->language) ? $language->language : $language;
235 $language = ($language === TRUE) ? $GLOBALS['language']->language : $language; // for convenience
236 return empty($language) && empty($datatype) ? $value : new RDF_Literal($value, $language, $datatype);
237 }
238
239 function rdf_var($name) {
240 return new RDF_Variable($name);
241 }
242
243 function rdf_is_var($value) {
244 return is_object($value) && ($value instanceof RDF_Variable);
245 }
246
247 function rdf_seq() {
248 return new RDF_Seq(func_get_args());
249 }
250
251 function rdf_bag() {
252 return new RDF_Bag(func_get_args());
253 }
254
255 function rdf_alt() {
256 return new RDF_Alt(func_get_args());
257 }
258
259 function rdf_val_to_str($value) {
260 return (is_object($value) && $value instanceof RDF_Literal) ? $value->value : (string)$value;
261 }
262
263 function rdf_str_to_val($string) {
264 $value = $string;
265
266 // Attempt to cast untyped values to the correct RDF datatypes:
267 if (is_string($string)) {
268 if (rdf_is_valid_uri($string)) { // rdf:resource
269 $value = rdf_uri($string);
270 }
271 else if (rdf_is_valid_datetime($string)) { // xsd:dateTime
272 $tz = date_default_timezone_get();
273 date_default_timezone_set('UTC');
274 $value = rdf_datetime(strtotime($string));
275 date_default_timezone_set($tz);
276 }
277 else if (is_numeric($string)) { // xsd:numeric
278 // TODO: should we cast to numeric types? this can be ambiguous...
279 }
280 }
281
282 return $value;
283 }
284
285 //////////////////////////////////////////////////////////////////////////////
286 // RDF API helper functions
287
288 function rdf_is_local_uri($uri) {
289 $base_uri = $GLOBALS['base_url'] . '/'; // FIXME?
290 return (strpos($uri, $base_uri) === 0) ? substr($uri, strlen($base_uri)) : FALSE;
291 }
292
293 function rdf_is_valid_uri($uri) {
294 return rdf_is_valid_url($uri) || rdf_is_valid_urn($uri);
295 }
296
297 function rdf_is_valid_url($url) {
298 static $allowed_characters = '[a-z0-9\/:_\-_\.\?\$,;~=#&%\+]';
299 return preg_match("/^([a-z]+):\/\/" . $allowed_characters . "+$/i", (string)$url);
300 }
301
302 function rdf_is_valid_urn($urn) {
303 return preg_match('/^urn:/', $urn) || preg_match('/^mailto:/', $urn); // FIXME
304 }
305
306 function rdf_is_valid_curie($curie) {
307 return preg_match('/^\[?[\w\-]+:[\w\-]*\]?$/', (string)$curie); // FIXME
308 }
309
310 function rdf_is_valid_qname($qname) {
311 return preg_match('/^[\w\-]+:[\w\-]+$/', (string)$qname); // FIXME
312 }
313
314 function rdf_is_valid_prefix($qname) {
315 $namespaces = rdf_get_namespaces();
316 list($prefix,) = explode(':', $qname, 2);
317 return isset($namespaces[$prefix]);
318 }
319
320 function rdf_is_valid_datetime($string) {
321 return preg_match('/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):?(\d{2})?/', $string);
322 }
323
324 function rdf_qname_to_uri($qname) {
325 if (rdf_is_valid_qname($qname)) {
326 $namespaces = rdf_get_namespaces();
327 list($prefix, $local_part) = explode(':', $qname, 2);
328 if (isset($namespaces[$prefix])) {
329 return $namespaces[$prefix] . $local_part;
330 }
331 }
332 return $qname; // FIXME!
333 }
334
335 function rdf_qname_to_uriref($qname) {
336 return rdf_uri(rdf_qname_to_uri($qname)); // TODO: cache?
337 }
338
339 function rdf_uri_to_qname($uri, $gensym = TRUE, $fail = NULL) {
340 global $rdf_namespaces;
341 if (empty($rdf_namespaces)) {
342 rdf_get_namespaces();
343 }
344
345 if (!rdf_is_valid_uri($uri = (string)$uri)) {
346 return $uri;
347 }
348
349 $best_prefix = $best_match = '';
350
351 // Attempt to find the longest substring match
352 foreach ($rdf_namespaces as $prefix => $match) {
353 if (strpos($uri, $match) === 0 && strlen($match) > strlen($best_match)) {
354 $best_match = $match;
355 $best_prefix = $prefix;
356 }
357 }
358
359 // If successful, life is easy
360 if (!empty($best_prefix)) {
361 $local_part = substr($uri, strlen($best_match));
362 return implode(':', array($best_prefix, $local_part));
363 }
364
365 // No existing namespace prefix found, make one up
366 if ($gensym && preg_match('@([\w\d-_]+)$@', $uri, $matches)) {
367 static $gensym = 0;
368 $prefix = 'g.' . ++$gensym; // Good ol' Lisp tradition continues...
369 $local_part = $matches[1];
370 $rdf_namespaces[$prefix] = substr($uri, 0, -strlen($local_part));
371 return implode(':', array($prefix, $local_part));
372 }
373
374 //trigger_error('Could not convert URI ' . $uri . ' to QName', E_USER_WARNING);
375 return $fail;
376 }
377
378 //////////////////////////////////////////////////////////////////////////////
379 // ARC2 interoperability
380
381 function _rdf_deconstruct_arc2_triple($triple) {
382 $subject = _rdf_deconstruct_arc2_value($triple, 's');
383 $predicate = _rdf_deconstruct_arc2_value($triple, 'p');
384 $object = _rdf_deconstruct_arc2_value($triple, 'o');
385 return array($subject, $predicate, $object);
386 }
387
388 function _rdf_deconstruct_arc2_value($triple, $name) {
389 switch ($name == 'p' ? 'iri' : $triple[$name . '_type']) {
390 case 'uri':
391 case 'iri':
392 return rdf_uri($triple[$name]);
393 case 'bnode':
394 return rdf_bnode($triple[$name]);
395 case 'literal':
396 case 'literal1':
397 case 'literal2':
398 case 'literal_long1':
399 case 'literal_long2':
400 return rdf_literal($triple[$name], $triple[$name . '_lang'], $triple[$name . '_datatype']);
401 case 'var':
402 return rdf_var($triple[$name]);
403 }
404 }
405
406 //////////////////////////////////////////////////////////////////////////////
407 // RDF/PHP normalization & serialization
408
409 function rdf_normalize($input) {
410 $output = array();
411 foreach ($input as $triple) {
412 list($subject, $predicate, $object) = $triple;
413 $output[(string)$subject][(string)$predicate][] = $object;
414 }
415 return $output;
416 }
417
418 function rdf_denormalize($input) {
419 $output = array();
420 foreach ($input as $subject => $predicates) {
421 foreach ($predicates as $predicate => $objects) {
422 if (!is_array($objects)) {
423 $output[] = array($subject, $predicate, $objects);
424 }
425 else {
426 foreach ($objects as $object) {
427 $output[] = array($subject, $predicate, $object);
428 }
429 }
430 }
431 }
432 return $output;
433 }
434
435 function rdf_objectify($input) {
436 $output = array();
437 foreach ($input as $s => &$ps) {
438 foreach ($ps as $p => &$os) {
439 foreach ($os as &$o) {
440 $output[$s][$p][] = rdf_objectify_value($o);
441 }
442 }
443 }
444 return $output;
445 }
446
447 function rdf_objectify_value($value, &$bnodes = array()) {
448 switch ($value['type']) {
449 case 'bnode':
450 return rdf_uri($value['value']); // FIXME
451 case 'uri':
452 return rdf_uri($value['value']);
453 case 'literal':
454 return !isset($value['lang']) && !isset($value['datatype']) ? $value['value'] : rdf_literal($value['value'], $value['lang'], $value['datatype']);
455 }
456 }
457
458 function rdf_deobjectify($input) {
459 $output = array();
460 foreach ($input as $s => $ps) {
461 foreach ($ps as $p => $os) {
462 $os = is_array($os) ? $os : array($os);
463 foreach ($os as $o) {
464 $output[$s][$p][] = is_object($o) ? $o->to_array() : (!is_array($o) ? array('type' => 'literal', 'value' => (string)$o) : $o); // FIXME
465 }
466 }
467 }
468 return $output;
469 }
470
471 //////////////////////////////////////////////////////////////////////////////
472 // RDF/PHP serialization
473
474 function rdf_serialize($data, array $options = array()) {
475 $data = is_array($data) ? $data : rdf_normalize($data); // support RDF_QueryIterator
476 $formats = rdf_get_formats('info', 'w');
477 $format = isset($options['format']) ? $options['format'] : RDF_FORMAT;
478 if (!isset($formats[$format])) {
479 return FALSE;
480 }
481 if (isset($formats[$format]->file)) {
482 require_once './' . drupal_get_path('module', $formats[$format]->module) . '/' . $formats[$format]->file;
483 }
484 ob_start() && call_user_func($formats[$format]->serialize, $data, $options);
485 return ob_get_clean();
486 }
487
488 function rdf_unserialize($text, array $options = array()) {
489 $formats = rdf_get_formats('info', 'r');
490 $format = isset($options['format']) ? $options['format'] : RDF_FORMAT;
491 if (isset($formats[$format]->file)) {
492 require_once './' . drupal_get_path('module', $formats[$format]->module) . '/' . $formats[$format]->file;
493 }
494 return isset($formats[$format]) ? call_user_func($formats[$format]->unserialize, $text, $options) : FALSE;
495 }
496
497 /**
498 * @see http://n2.talis.com/wiki/RDF_JSON_Specification#rdf.2Fphp
499 */
500 function rdf_serialize_php($data, array $options = array()) {
501 print serialize(rdf_deobjectify($data));
502 }
503
504 /**
505 * @see http://n2.talis.com/wiki/RDF_JSON_Specification#rdf.2Fphp
506 */
507 function rdf_unserialize_php($text, array $options = array()) {
508 return rdf_objectify(unserialize((string)$text));
509 }
510
511 /**
512 * @see http://n2.talis.com/wiki/RDF_JSON_Specification
513 */
514 function rdf_serialize_json($data, array $options = array()) {
515 print drupal_to_js(rdf_deobjectify($data));
516 }
517
518 /**
519 * @see http://n2.talis.com/wiki/RDF_JSON_Specification
520 */
521 function rdf_unserialize_json($json, array $options = array()) {
522 return rdf_objectify(json_decode((string)$json, TRUE));
523 }
524
525 //////////////////////////////////////////////////////////////////////////////
526 // RDF API query selectors
527
528 function rdf_objects($objects) {
529 return is_array($objects) ? $objects : array($objects);
530 }
531
532 function rdf_select_resources($input) {
533 return array_keys($input);
534 }
535
536 function rdf_select_predicates($input) {
537 return !empty($input) ? array_unique(call_user_func_array('array_merge', rdf_select($input, FALSE, TRUE, FALSE))) : array();
538 }
539
540 function rdf_select_values($input) {
541 return !empty($input) ? array_unique(call_user_func_array('array_merge', rdf_select($input, FALSE, FALSE, TRUE))) : array();
542 }
543
544 function rdf_select($input, $subject = TRUE, $predicate = FALSE, $object = FALSE) {
545 $output = array();
546 foreach ($input as $s => $ps) {
547 foreach ($ps as $p => $os) {
548 foreach ($os as $o) {
549 $triple = array();
550 if ($subject) $triple[] = $s;
551 if ($predicate) $triple[] = $p;
552 if ($object) $triple[] = $o;
553 $output[] = $triple;
554 }
555 }
556 }
557 return $output;
558 }
559
560 function _rdf_filter($input, $subject = NULL, $predicate = NULL, $object = NULL, $options = array()) {
561 extract($options, EXTR_SKIP | EXTR_REFS);
562
563 $output = array();
564 foreach ($input as $s => $ps) {
565
566 foreach ($ps as $p => $os) {
567 foreach ($os as $o) {
568 if ((empty($callback) || $callback($s, $p, $o)) &&
569 (!$subject || $subject == $s) &&
570 (!$predicate || $predicate == $p) &&
571 (!$object || $object == $o)) { // FIXME: RDF_Literals
572 $output[$s][$p][] = $o;
573 }
574 }
575 }
576
577 if ($subject && $subject == $s)
578 break; // shortcut
579 }
580 return $output;
581 }
582
583 //////////////////////////////////////////////////////////////////////////////
584 // RDF/PHP handling
585
586 // TODO: rename to rdf_predicates()?
587 function rdf_expand_qnames(array $input, $remove_empty = TRUE) {
588 $output = array();
589 foreach ($input as $qname => $data) {
590 if (!empty($data)) {
591 $output[rdf_qname_to_uri($qname)] = is_array($data) ? $data : array($data);
592 }
593 }
594 return $output;
595 }
596
597 function rdf_get_prefixes(array $data, $namespaces = NULL) {
598 $result = array('rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
599 $namespaces = is_array($namespaces) ? $namespaces : rdf_get_namespaces();
600
601 foreach ($data as $subject => $predicates) {
602 foreach ($predicates as $predicate => $objects) {
603 $qnames = array($predicate);
604 foreach (is_array($objects) ? $objects : array($objects) as $object) {
605 if (rdf_is_valid_uri((string)$object)) {
606 $qnames[] = $object;
607 }
608 }
609 foreach ($qnames as $qname) {
610 list($prefix, ) = explode(':', rdf_uri_to_qname($qname));
611 if ($prefix != '_' && !isset($result[$prefix]) && isset($namespaces[$prefix])) {
612 $result[$prefix] = $namespaces[$prefix];
613 }
614 }
615 }
616 }
617 return $result;
618 }
619
620 //////////////////////////////////////////////////////////////////////////////
621 // RDF API functions
622
623 /**
624 * Adds an RDF auto-discovery <link> tag to the page's <head> element.
625 */
626 function rdf_add_autodiscovery_link($title, $path, $format = RDF_FORMAT, array $options = array()) {
627 $formats = rdf_get_formats();
628 drupal_add_link(array_merge(array('rel' => 'meta', 'type' => $formats[$format]->mime_type, 'title' => $title, 'href' => $path), $options));
629 }
630
631 function rdf_get_uuid() {
632 return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
633 mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff),
634 mt_rand(0, 0x0fff) | 0x4000,
635 mt_rand(0, 0x3fff) | 0x8000,
636 mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));
637 }
638
639 function rdf_get_formats($what = 'info', $mode = '') {
640 $formats = array();
641 foreach (module_implements('rdf_formats') as $module) {
642 if ($result = module_invoke($module, 'rdf_formats')) {
643 foreach ($result as $k => &$v) {
644 $formats[$k] = isset($formats[$k]) ? array_merge($formats[$k], $v) : $v;
645 $formats[$k]['name'] = $k;
646 $formats[$k]['module'] = $module;
647 }
648 }
649 }
650 ksort($formats);
651
652 foreach ($formats as $k => &$v) {
653 $formats[$k] = (object)$v;
654 }
655
656 if (preg_match('/^[rw]$/', $mode)) {
657 foreach ($formats as $k => &$v) {
658 if ((strpos($mode, 'r') !== FALSE && !isset($v->unserialize)) ||
659 (strpos($mode, 'w') !== FALSE && !isset($v->serialize))) {
660 unset($formats[$k]);
661 }
662 }
663 }
664
665 if ($what == 'names') {
666 foreach ($formats as $k => &$v) { $formats[$k] = $v->title; }
667 }
668
669 return $formats;
670 }
671
672 function rdf_get_repositories($what = 'info', $filters = array()) {
673 $repos = module_invoke_all('rdf_repositories');
674 if (!empty($filters)) {
675 foreach ($repos as $k => &$v) {
676 foreach ($filters as $filter => $value) {
677 if ($v[$filter] != $value) {
678 unset($repos[$k]);
679 continue 2;
680 }
681 }
682 }
683 }
684 if ($what == 'names') {
685 foreach ($repos as $k => &$v) {
686 $repos[$k] = $v['title'];
687 }
688 }
689 return $repos;
690 }
691
692 function rdf_register_namespace($prefix, $uri) {
693 rdf_get_namespaces();
694 $GLOBALS['rdf_namespaces'][$prefix] = $uri;
695 }
696
697 function rdf_get_namespaces($op = NULL) {
698 global $rdf_namespaces;
699 if (empty($rdf_namespaces)) {
700 $rdf_namespaces = module_invoke_all('rdf_namespaces');
701 ksort($rdf_namespaces);
702 }
703
704 switch ($op) {
705 case 'rdfa': // only RDFa-enabled namespaces
706 $defaults = array('rdf', 'dc', 'foaf');
707 $defaults = array_combine($defaults, $defaults);
708 return array_intersect_key($rdf_namespaces, array_filter(variable_get('rdf_rdfa_prefixes', $defaults), 'is_string'));
709 default: // all namespaces
710 return $rdf_namespaces;
711 }
712 }
713
714 function rdf_get_contexts() {
715 return module_invoke_all('rdf_contexts');
716 }
717
718 function rdf_get_predicates($prefix) {
719 $predicates = module_invoke_all('rdf_predicates');
720 return isset($predicates[$prefix]) ? $predicates[$prefix] : array();
721 }
722
723 function rdf_get_type($value) {
724 if (!is_object($value)) {
725 return 'string'; // plain literal
726 }
727 else if ($value instanceof RDF_Literal) {
728 return 'literal'; // language/datatype-tagged literal
729 }
730 else { // RDF_URIRef, or the like
731 return 'uri'; // TODO: bnode
732 }
733 }
734
735 function rdf_get_datatype($value) {
736 switch (gettype($value)) {
737 case 'object': return ($value instanceof RDF_Literal) ? $value->datatype : NULL;
738 case 'NULL': return NULL;
739 case 'boolean': return 'xsd:boolean';
740 case 'integer': return 'xsd:int';
741 case 'double': return 'xsd:double';
742 case 'string': //return 'xsd:string';
743 default: return NULL;
744 }
745 }
746
747 function rdf_load_site() {
748 return array(
749 dc::type => dcmitype::uriref('Service'),
750 dc::format => 'text/html',
751 dc::language => array_keys(language_list()),
752 dc::title => rdf_literal(variable_get('site_name', 'Drupal'), 'en'),
753 dc::description => variable_get('site_mission', ''),
754 dc::publisher => rdf_mailto(variable_get('site_mail', ini_get('sendmail_from'))),
755 dc::creator => rdf_uri('http://drupal.org/#' . DRUPAL_CORE_COMPATIBILITY),
756 );
757 }
758
759 function rdf_get_feed_info($feed_id = NULL, $enabled_only = FALSE) {
760 $feeds = module_invoke_all('rdf_feeds');
761 if ($feed_id) {
762 return isset($feeds[$feed_id]) ? (object)array_merge(array('id' => $feed_id), $feeds[$feed_id]) : NULL;
763 }
764 else {
765 foreach ($feeds as $feed_id => &$feed) {
766 $feeds[$feed_id] = $feed = (object)array_merge(array('id' => $feed_id), $feed);
767 if ($enabled_only && !module_exists($feed->module)) {
768 unset($feeds[$feed_id]);
769 }
770 }
771 return $feeds;
772 }
773 }
774
775 function rdf_get_feed_settings($feed) {
776 $defaults = array('description_from_mission' => '', 'description' => '', 'update_period' => '', 'update_frequency' => '', 'update_base' => '', 'format' => RDF_FORMAT_RSS);
777 if (($feed = is_object($feed) ? $feed : rdf_get_feed_info($feed))) {
778 return ($feed->module == 'views') ? // special case for Views feeds
779 rdf_views_get_feed_settings($feed->id, $defaults) :
780 variable_get('rdf_feed[' . $feed->id . ']', $defaults);
781 }
782 return $defaults;
783 }
784
785 function rdf_set_feed_settings($feed, $settings) {
786 if (($feed = is_object($feed) ? $feed : rdf_get_feed_info($feed))) {
787 if ($settings) {
788 variable_set('rdf_feed[' . $feed->id . ']', (array)$settings);
789 }
790 else {
791 variable_del('rdf_feed[' . $feed->id . ']');
792 }
793 }
794 }
795
796 //////////////////////////////////////////////////////////////////////////////
797 // Miscellaneous
798
799 function _rdf_query_arg($uri_or_qname) {
800 if (is_string($uri_or_qname) && preg_match('/^[\w]+:[\w]+$/i', $uri_or_qname))
801 return rdf_qname_to_uri($uri_or_qname);
802 return is_object($uri_or_qname) ? (string)$uri_or_qname : $uri_or_qname;
803 }
804
805 function _rdf_invoke_op($name, $args, $repos = NULL, $default = FALSE) {
806 call_user_func_array('module_invoke_all', array_merge(array('rdf'), $args)); // trigger hook_rdf()
807
808 foreach (_rdf_get_callbacks($name, $repos) as $callback) {
809 list($callable, $callable_args) = $callback;
810 if ($result = call_user_func_array($callable, array_merge($callable_args, $args))) {
811 return $result;
812 }
813 }
814 return $default;
815 }
816
817 function _rdf_get_callbacks($op, $repos = NULL) {
818 $callbacks = array();
819 $repos = !empty($repos) ? $repos : (isset($GLOBALS['rdf_repository']) ? $GLOBALS['rdf_repository'] : NULL);
820 $repos = !empty($repos) && !is_array($repos) ? array($repos) : $repos;
821 $repos = !empty($repos) ? array_intersect_key(rdf_get_repositories(), array_flip($repos)) : rdf_get_repositories();
822
823 foreach ($repos as $repo) {
824 if (isset($repo['callbacks'][$op])) {
825 $callback = $repo['callbacks'][$op]['function'];
826 if (is_callable($callback)) {
827 $args = is_array($repo['callbacks'][$op]['arguments']) ? $repo['callbacks'][$op]['arguments'] : array();
828 $callbacks[] = array($callback, $args);
829 }
830 }
831 }
832
833 return $callbacks;
834 }
835
836 //////////////////////////////////////////////////////////////////////////////
837 // RDF API classes
838
839 class RDF_Callback {
840 public function __construct($callback, array $arguments = array()) {
841 $this->callback = $callback;
842 $this->arguments = $arguments;
843 }
844
845 public function call() {
846 $arguments = func_get_args();
847 return call_user_func_array($this->callback, array_merge($this->arguments, $arguments));
848 }
849 }
850
851 /**
852 * @see http://www.php.net/~helly/php/ext/spl/interfaceIterator.html
853 */
854 class RDF_CallbackIterator implements Iterator {
855 public function __construct($callback, array $args_lhs, $args_iter, array $args_rhs = array()) {
856 $this->callback = $callback;
857 $this->args_lhs = $args_lhs;
858 $this->args_iter = $args_iter;
859 $this->args_rhs = $args_rhs;
860 $this->done = FALSE;
861 $this->key = NULL;
862 $this->value = NULL;
863 }
864
865 public function call($arg_iter) {
866 $result = call_user_func_array($this->callback, array_merge($this->args_lhs, array($arg_iter), $this->args_rhs));
867 list($this->key, $this->value) = each($result);
868 }
869
870 public function rewind() {
871 $this->done = FALSE;
872 reset($this->args_iter);
873 $this->next();
874 }
875
876 public function next() {
877 if (($kv = each($this->args_iter)) !== FALSE) {
878 list($key, $value) = $kv;
879 $this->call($value);
880 }
881 else {
882 $this->done = TRUE;
883 }
884 }
885
886 public function valid() { return !$this->done; }
887 public function current() { return $this->value; }
888 public function key() { return $this->key; }
889 }
890
891 /**
892 * @see http://www.php.net/~helly/php/ext/spl/interfaceIteratorAggregate.html
893 */
894 class RDF_QueryCallback extends RDF_Callback implements IteratorAggregate {
895 public function call() {
896 return call_user_func_array($this->callback, $this->arguments);
897 }
898
899 public function getIterator() {
900 $result = $this->call();
901 return is_object($result) ? $result : new ArrayIterator(is_array($result) ? $result : array());
902 }
903 }
904
905 /**
906 * @see http://www.php.net/~helly/php/ext/spl/classAppendIterator.html
907 */
908 class RDF_QueryIterator extends AppendIterator {
909 public function __construct() {
910 parent::__construct();
911 $this->rewind();
912 foreach (func_get_args() as $iterator) {
913 $this->append($iterator);
914 }
915 }
916
917 public function key() {
918 // By reindexing the aggregated results, we guarantee that e.g.
919 // iterator_to_array() will work correctly despite overlapping keys
920 // likely being returned by the inner iterators.
921 return $this->index++;
922 }
923
924 public function rewind() {
925 $this->index = 0;
926 return parent::rewind();
927 }
928
929 public function to_array() {
930 return iterator_to_array($this);
931 }
932 }
933
934 class RDF_Statement {
935 public $subject, $predicate, $object;
936
937 public function __construct($subject, $predicate, $object) {
938 foreach (get_defined_vars() as $k => $v) {
939 $this->$k = $v;
940 }
941 }
942
943 public function to_n3() {
944 // TODO
945 }
946 }
947
948 /**
949 * @see http://www.w3.org/TR/rdf-concepts/#section-Graph-URIref
950 */
951 class RDF_URIRef {
952 public static $resources = array();
953 public $uri;
954
955 public static function uri($uri, $class = __CLASS__) {
956 if (is_object($uri) && $uri instanceof RDF_URIRef) {
957 return $uri; // for convenience
958 }
959 if (!array_key_exists($uri, self::$resources)) {
960 $resource = new $class($uri);
961 self::$resources[$uri] = $resource;
962 return $resource;
963 }
964 return self::$resources[$uri];
965 }
966
967 public function qname() {
968 return rdf_uri_to_qname($this->uri);
969 }
970
971 public function to_array() {
972 return array('type' => 'uri', 'value' => $this->uri);
973 }
974
975 public function to_n3() {
976 return '<' . $this->uri . '>';
977 }
978
979 public function __toString() {
980 return $this->uri;
981 }
982
983 protected function __construct($uri) {
984 $this->uri = $uri;
985 }
986 }
987
988 /**
989 * @see http://www.w3.org/TR/rdf-concepts/#section-blank-nodes
990 */
991 class RDF_BNode extends RDF_URIRef {
992 public static function match($uri) {
993 return strpos($uri, 'http://bnode.net/') === 0;
994 }
995
996 public static function generate() {
997 return self::id(rdf_get_uuid());
998 }
999
1000 public static function id($id) {
1001 return self::uri('http://bnode.net/' . $id, __CLASS__);
1002 }
1003
1004 public function qname() {
1005 return '_:' . $id;
1006 }
1007
1008 public function to_array() {
1009 return array('type' => 'bnode', 'value' => $this->uri);
1010 }
1011
1012 public function to_n3() {
1013 return '_:' . substr($this->uri, strlen('http://bnode.net/')); // FIXME
1014 }
1015 }
1016
1017 /**
1018 * @see http://www.w3.org/TR/rdf-concepts/#section-Graph-Literal
1019 */
1020 class RDF_Literal {
1021 public $value, $language, $datatype;
1022
1023 public function __construct($value, $language = NULL, $datatype = NULL) {
1024 $this->value = $value;
1025 $this->language = $language ? strtolower($language) : NULL;
1026 $this->datatype = $datatype ? rdf_qname_to_uri($datatype) : NULL;
1027 }
1028
1029 public function qname() {
1030 return rdf_uri_to_qname($this->datatype);
1031 }
1032
1033 public function to_array() {
1034 $array = array('type' => 'literal', 'value' => $this->value);
1035 if ($this->language) {
1036 $array['lang'] = $this->language;
1037 }
1038 if ($this->datatype) {
1039 $array['datatype'] = $this->datatype;
1040 }
1041 return $array;
1042 }
1043
1044 public function to_n3() {
1045 return $this->__toString();
1046 }
1047
1048 public function __toString() {
1049 return '"' . $this->value . '"' . // FIXME
1050 ($this->language ? '@' . $this->language : '') .
1051 ($this->datatype ? '^^<' . $this->qname() . '>' : '');
1052 }
1053 }
1054
1055 /**
1056 * @see http://www.w3.org/TR/rdf-concepts/#section-XMLLiteral
1057 */
1058 class RDF_XMLLiteral {} // TODO
1059
1060 class RDF_Variable {
1061 public $name;
1062
1063 function __construct($name) {
1064 $this->name = $name;
1065 }
1066
1067 function to_n3() {
1068 return $this->__toString();
1069 }
1070
1071 function __toString() {
1072 return '?' . $this->name;
1073 }
1074 }
1075
1076 class RDF_Collection implements ArrayAccess {
1077 public $items, $uri;
1078
1079 function __construct(array $items = array()) {
1080 $this->items = $items;
1081 $this->uri = rdf_bnode();
1082 }
1083
1084 function offsetExists($offset) { return isset($this->items[$offset]); }
1085 function offsetGet($offset) { return $this->items[$offset]; }
1086 function offsetSet($offset, $value) { return is_null($offset) ? $this->items[] = $value : $this->items[$offset] = $value; }
1087 function offsetUnset($offset) { unset($this->items[$offset]); }
1088
1089 function uriref() { return $this->uri; }