1<?php 2 3/** 4 * EasyRdf 5 * 6 * LICENSE 7 * 8 * Copyright (c) 2009-2013 Nicholas J Humfrey. All rights reserved. 9 * 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 19 * written permission. 20 * 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. 32 * 33 * @package EasyRdf 34 * @copyright Copyright (c) 2009-2013 Nicholas J Humfrey 35 * @license http://www.opensource.org/licenses/bsd-license.php 36 */ 37 38/** 39 * Container for collection of EasyRdf_Resources. 40 * 41 * @package EasyRdf 42 * @copyright Copyright (c) 2009-2013 Nicholas J Humfrey 43 * @license http://www.opensource.org/licenses/bsd-license.php 44 */ 45class EasyRdf_Graph 46{ 47 /** The URI of the graph */ 48 private $uri = null; 49 private $parsedUri = null; 50 51 /** Array of resources contained in the graph */ 52 private $resources = array(); 53 54 private $index = array(); 55 private $revIndex = array(); 56 57 /** Counter for the number of bnodes */ 58 private $bNodeCount = 0; 59 60 /** Array of URLs that have been loaded into the graph */ 61 private $loaded = array(); 62 63 private $maxRedirects = 10; 64 65 66 /** 67 * Constructor 68 * 69 * If no URI is given then an unnamed graph is created. 70 * 71 * The $data parameter is optional and will be parsed into 72 * the graph if given. 73 * 74 * The data format is optional and should be specified if it 75 * can't be guessed by EasyRdf. 76 * 77 * @param string $uri The URI of the graph 78 * @param string $data Data for the graph 79 * @param string $format The document type of the data (e.g. rdfxml) 80 * @return object EasyRdf_Graph 81 */ 82 public function __construct($uri = null, $data = null, $format = null) 83 { 84 $this->checkResourceParam($uri, true); 85 86 if ($uri) { 87 $this->uri = $uri; 88 $this->parsedUri = new EasyRdf_ParsedUri($uri); 89 if ($data) { 90 $this->parse($data, $format, $this->uri); 91 } 92 } 93 } 94 95 /** 96 * Create a new graph and load RDF data from a URI into it 97 * 98 * This static function is shorthand for: 99 * $graph = new EasyRdf_Graph($uri); 100 * $graph->load($uri, $format); 101 * 102 * The document type is optional but should be specified if it 103 * can't be guessed or got from the HTTP headers. 104 * 105 * @param string $uri The URI of the data to load 106 * @param string $format Optional format of the data (eg. rdfxml) 107 * @return object EasyRdf_Graph The new the graph object 108 */ 109 public static function newAndLoad($uri, $format = null) 110 { 111 $graph = new self($uri); 112 $graph->load($uri, $format); 113 return $graph; 114 } 115 116 /** Get or create a resource stored in a graph 117 * 118 * If the resource did not previously exist, then a new resource will 119 * be created. If you provide an RDF type and that type is registered 120 * with the EasyRdf_TypeMapper, then the resource will be an instance 121 * of the class registered. 122 * 123 * If URI is null, then the URI of the graph is used. 124 * 125 * @param string $uri The URI of the resource 126 * @param mixed $types RDF type of a new resource (e.g. foaf:Person) 127 * @return object EasyRdf_Resource 128 */ 129 public function resource($uri = null, $types = array()) 130 { 131 $this->checkResourceParam($uri, true); 132 if (!$uri) { 133 throw new InvalidArgumentException( 134 '$uri is null and EasyRdf_Graph object has no URI either.' 135 ); 136 } 137 138 // Resolve relative URIs 139 if ($this->parsedUri) { 140 $uri = $this->parsedUri->resolve($uri)->toString(); 141 } 142 143 // Add the types 144 $this->addType($uri, $types); 145 146 // Create resource object if it doesn't already exist 147 if (!isset($this->resources[$uri])) { 148 $resClass = $this->classForResource($uri); 149 $this->resources[$uri] = new $resClass($uri, $this); 150 } 151 152 return $this->resources[$uri]; 153 } 154 155 /** Work out the class to instantiate a resource as 156 * @ignore 157 */ 158 protected function classForResource($uri) 159 { 160 $rdfType = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; 161 if (isset($this->index[$uri][$rdfType])) { 162 foreach ($this->index[$uri][$rdfType] as $type) { 163 if ($type['type'] == 'uri' or $type['type'] == 'bnode') { 164 $class = EasyRdf_TypeMapper::get($type['value']); 165 if ($class != null) { 166 return $class; 167 } 168 } 169 } 170 } 171 172 // Parsers don't typically add a rdf:type to rdf:List, so we have to 173 // do a bit of 'inference' here using properties. 174 if ($uri == 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil' or 175 isset($this->index[$uri]['http://www.w3.org/1999/02/22-rdf-syntax-ns#first']) or 176 isset($this->index[$uri]['http://www.w3.org/1999/02/22-rdf-syntax-ns#rest']) 177 ) { 178 return 'EasyRdf_Collection'; 179 } 180 return 'EasyRdf_Resource'; 181 } 182 183 /** 184 * Create a new blank node in the graph and return it. 185 * 186 * If you provide an RDF type and that type is registered 187 * with the EasyRdf_TypeMapper, then the resource will be an instance 188 * of the class registered. 189 * 190 * @param mixed $types RDF type of a new blank node (e.g. foaf:Person) 191 * @return object EasyRdf_Resource The new blank node 192 */ 193 public function newBNode($types = array()) 194 { 195 return $this->resource($this->newBNodeId(), $types); 196 } 197 198 /** 199 * Create a new unique blank node identifier and return it. 200 * 201 * @return string The new blank node identifier (e.g. _:genid1) 202 */ 203 public function newBNodeId() 204 { 205 return "_:genid".(++$this->bNodeCount); 206 } 207 208 /** 209 * Parse some RDF data into the graph object. 210 * 211 * @param string $data Data to parse for the graph 212 * @param string $format Optional format of the data 213 * @param string $uri The URI of the data to load 214 * @return integer The number of triples added to the graph 215 */ 216 public function parse($data, $format = null, $uri = null) 217 { 218 $this->checkResourceParam($uri, true); 219 220 if (empty($format) or $format == 'guess') { 221 // Guess the format if it is Unknown 222 $format = EasyRdf_Format::guessFormat($data, $uri); 223 } else { 224 $format = EasyRdf_Format::getFormat($format); 225 } 226 227 if (!$format) { 228 throw new EasyRdf_Exception( 229 "Unable to parse data of an unknown format." 230 ); 231 } 232 233 $parser = $format->newParser(); 234 return $parser->parse($this, $data, $format, $uri); 235 } 236 237 /** 238 * Parse a file containing RDF data into the graph object. 239 * 240 * @param string $filename The path of the file to load 241 * @param string $format Optional format of the file 242 * @param string $uri The URI of the file to load 243 * @return integer The number of triples added to the graph 244 */ 245 public function parseFile($filename, $format = null, $uri = null) 246 { 247 if ($uri === null) { 248 $uri = "file://$filename"; 249 } 250 251 return $this->parse( 252 file_get_contents($filename), 253 $format, 254 $uri 255 ); 256 } 257 258 /** 259 * Load RDF data into the graph from a URI. 260 * 261 * If no URI is given, then the URI of the graph will be used. 262 * 263 * The document type is optional but should be specified if it 264 * can't be guessed or got from the HTTP headers. 265 * 266 * @param string $uri The URI of the data to load 267 * @param string $format Optional format of the data (eg. rdfxml) 268 * @return integer The number of triples added to the graph 269 */ 270 public function load($uri = null, $format = null) 271 { 272 $this->checkResourceParam($uri, true); 273 274 if (!$uri) { 275 throw new EasyRdf_Exception( 276 "No URI given to load() and the graph does not have a URI." 277 ); 278 } 279 280 // Setup the HTTP client 281 $client = EasyRdf_Http::getDefaultHttpClient(); 282 $client->resetParameters(true); 283 $client->setConfig(array('maxredirects' => 0)); 284 $client->setMethod('GET'); 285 $client->setHeaders('Accept', EasyRdf_Format::getHttpAcceptHeader()); 286 287 $requestUrl = $uri; 288 $response = null; 289 $redirectCounter = 0; 290 do { 291 // Have we already loaded it into the graph? 292 $requestUrl = EasyRdf_Utils::removeFragmentFromUri($requestUrl); 293 if (in_array($requestUrl, $this->loaded)) { 294 return 0; 295 } 296 297 // Make the HTTP request 298 $client->setHeaders('host', null); 299 $client->setUri($requestUrl); 300 $response = $client->request(); 301 302 // Add the URL to the list of URLs loaded 303 $this->loaded[] = $requestUrl; 304 305 if ($response->isRedirect() and $location = $response->getHeader('location')) { 306 // Avoid problems with buggy servers that add whitespace 307 $location = trim($location); 308 309 // Some servers return relative URLs in the location header 310 // resolve it in relation to previous request 311 $baseUri = new EasyRdf_ParsedUri($requestUrl); 312 $requestUrl = $baseUri->resolve($location)->toString(); 313 $requestUrl = EasyRdf_Utils::removeFragmentFromUri($requestUrl); 314 315 // If it is a 303 then drop the parameters 316 if ($response->getStatus() == 303) { 317 $client->resetParameters(); 318 } 319 320 ++$redirectCounter; 321 } elseif ($response->isSuccessful()) { 322 // If we didn't get any location, stop redirecting 323 break; 324 } else { 325 throw new EasyRdf_Http_Exception( 326 "HTTP request for {$requestUrl} failed: ".$response->getMessage(), 327 $response->getStatus(), 328 null, 329 $response->getBody() 330 ); 331 } 332 } while ($redirectCounter < $this->maxRedirects); 333 334 if (!$format or $format == 'guess') { 335 list($format, $params) = EasyRdf_Utils::parseMimeType( 336 $response->getHeader('Content-Type') 337 ); 338 } 339 340 // Parse the data 341 return $this->parse($response->getBody(), $format, $uri); 342 } 343 344 /** Get an associative array of all the resources stored in the graph. 345 * The keys of the array is the URI of the EasyRdf_Resource. 346 * 347 * @return array Array of EasyRdf_Resource 348 */ 349 public function resources() 350 { 351 foreach ($this->index as $subject => $properties) { 352 if (!isset($this->resources[$subject])) { 353 $this->resource($subject); 354 } 355 } 356 357 foreach ($this->revIndex as $object => $properties) { 358 if (!isset($this->resources[$object])) { 359 $this->resource($object); 360 } 361 } 362 363 return $this->resources; 364 } 365 366 /** Get an arry of resources matching a certain property and optional value. 367 * 368 * For example this routine could be used as a way of getting 369 * everyone who has name: 370 * $people = $graph->resourcesMatching('foaf:name') 371 * 372 * Or everyone who is male: 373 * $people = $graph->resourcesMatching('foaf:gender', 'male'); 374 * 375 * Or all homepages: 376 * $people = $graph->resourcesMatching('^foaf:homepage'); 377 * 378 * @param string $property The property to check. 379 * @param mixed $value Optional, the value of the propery to check for. 380 * @return array Array of EasyRdf_Resource 381 */ 382 public function resourcesMatching($property, $value = null) 383 { 384 $this->checkSinglePropertyParam($property, $inverse); 385 $this->checkValueParam($value); 386 387 // Use the reverse index if it is an inverse property 388 if ($inverse) { 389 $index = &$this->revIndex; 390 } else { 391 $index = &$this->index; 392 } 393 394 $matched = array(); 395 foreach ($index as $subject => $props) { 396 if (isset($index[$subject][$property])) { 397 if (isset($value)) { 398 foreach ($this->index[$subject][$property] as $v) { 399 if ($v['type'] == $value['type'] and 400 $v['value'] == $value['value']) { 401 $matched[] = $this->resource($subject); 402 break; 403 } 404 } 405 } else { 406 $matched[] = $this->resource($subject); 407 } 408 } 409 } 410 return $matched; 411 } 412 413 /** Get the URI of the graph 414 * 415 * @return string The URI of the graph 416 */ 417 public function getUri() 418 { 419 return $this->uri; 420 } 421 422 /** Check that a URI/resource parameter is valid, and convert it to a string 423 * @ignore 424 */ 425 protected function checkResourceParam(&$resource, $allowNull = false) 426 { 427 if ($allowNull == true) { 428 if ($resource === null) { 429 if ($this->uri) { 430 $resource = $this->uri; 431 } else { 432 return; 433 } 434 } 435 } elseif ($resource === null) { 436 throw new InvalidArgumentException( 437 "\$resource should be either IRI, blank-node identifier or EasyRdf_Resource. got null" 438 ); 439 } 440 441 if (is_object($resource) and $resource instanceof EasyRdf_Resource) { 442 $resource = $resource->getUri(); 443 } elseif (is_object($resource) and $resource instanceof EasyRdf_ParsedUri) { 444 $resource = strval($resource); 445 } elseif (is_string($resource)) { 446 if ($resource == '') { 447 throw new InvalidArgumentException( 448 "\$resource should be either IRI, blank-node identifier or EasyRdf_Resource. got empty string" 449 ); 450 } elseif (preg_match("|^<(.+)>$|", $resource, $matches)) { 451 $resource = $matches[1]; 452 } else { 453 $resource = EasyRdf_Namespace::expand($resource); 454 } 455 } else { 456 throw new InvalidArgumentException( 457 "\$resource should be either IRI, blank-node identifier or EasyRdf_Resource" 458 ); 459 } 460 } 461 462 /** Check that a single URI/property parameter (not a property path) 463 * is valid, and expand it if required 464 * @ignore 465 */ 466 protected function checkSinglePropertyParam(&$property, &$inverse) 467 { 468 if (is_object($property) and $property instanceof EasyRdf_Resource) { 469 $property = $property->getUri(); 470 } elseif (is_object($property) and $property instanceof EasyRdf_ParsedUri) { 471 $property = strval($property); 472 } elseif (is_string($property)) { 473 if ($property == '') { 474 throw new InvalidArgumentException( 475 "\$property cannot be an empty string" 476 ); 477 } elseif (substr($property, 0, 1) == '^') { 478 $inverse = true; 479 $property = EasyRdf_Namespace::expand(substr($property, 1)); 480 } elseif (substr($property, 0, 2) == '_:') { 481 throw new InvalidArgumentException( 482 "\$property cannot be a blank node" 483 ); 484 } else { 485 $inverse = false; 486 $property = EasyRdf_Namespace::expand($property); 487 } 488 } 489 490 if ($property === null or !is_string($property)) { 491 throw new InvalidArgumentException( 492 "\$property should be a string or EasyRdf_Resource and cannot be null" 493 ); 494 } 495 } 496 497 /** Check that a value parameter is valid, and convert it to an associative array if needed 498 * @ignore 499 */ 500 protected function checkValueParam(&$value) 501 { 502 if (isset($value)) { 503 if (is_object($value)) { 504 if (!method_exists($value, 'toRdfPhp')) { 505 // Convert to a literal object 506 $value = EasyRdf_Literal::create($value); 507 } 508 $value = $value->toRdfPhp(); 509 } elseif (is_array($value)) { 510 if (!isset($value['type'])) { 511 throw new InvalidArgumentException( 512 "\$value is missing a 'type' key" 513 ); 514 } 515 516 if (!isset($value['value'])) { 517 throw new InvalidArgumentException( 518 "\$value is missing a 'value' key" 519 ); 520 } 521 522 // Fix ordering and remove unknown keys 523 $value = array( 524 'type' => strval($value['type']), 525 'value' => strval($value['value']), 526 'lang' => isset($value['lang']) ? strval($value['lang']) : null, 527 'datatype' => isset($value['datatype']) ? strval($value['datatype']) : null 528 ); 529 } else { 530 $value = array( 531 'type' => 'literal', 532 'value' => strval($value), 533 'datatype' => EasyRdf_Literal::getDatatypeForValue($value) 534 ); 535 } 536 if (!in_array($value['type'], array('uri', 'bnode', 'literal'), true)) { 537 throw new InvalidArgumentException( 538 "\$value does not have a valid type (".$value['type'].")" 539 ); 540 } 541 if (empty($value['datatype'])) { 542 unset($value['datatype']); 543 } 544 if (empty($value['lang'])) { 545 unset($value['lang']); 546 } 547 if (isset($value['lang']) and isset($value['datatype'])) { 548 throw new InvalidArgumentException( 549 "\$value cannot have both and language and a datatype" 550 ); 551 } 552 } 553 } 554 555 /** Get a single value for a property of a resource 556 * 557 * If multiple values are set for a property then the value returned 558 * may be arbitrary. 559 * 560 * If $property is an array, then the first item in the array that matches 561 * a property that exists is returned. 562 * 563 * This method will return null if the property does not exist. 564 * 565 * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) 566 * @param string $propertyPath A valid property path 567 * @param string $type The type of value to filter by (e.g. literal or resource) 568 * @param string $lang The language to filter by (e.g. en) 569 * @return mixed A value associated with the property 570 */ 571 public function get($resource, $propertyPath, $type = null, $lang = null) 572 { 573 $this->checkResourceParam($resource); 574 575 if (is_object($propertyPath) and $propertyPath instanceof EasyRdf_Resource) { 576 return $this->getSingleProperty($resource, $propertyPath->getUri(), $type, $lang); 577 } elseif (is_string($propertyPath) and preg_match('|^(\^?)<(.+)>|', $propertyPath, $matches)) { 578 return $this->getSingleProperty($resource, "$matches[1]$matches[2]", $type, $lang); 579 } elseif ($propertyPath === null or !is_string($propertyPath)) { 580 throw new InvalidArgumentException( 581 "\$propertyPath should be a string or EasyRdf_Resource and cannot be null" 582 ); 583 } elseif ($propertyPath === '') { 584 throw new InvalidArgumentException( 585 "\$propertyPath cannot be an empty string" 586 ); 587 } 588 589 // Loop through each component in the path 590 foreach (explode('/', $propertyPath) as $part) { 591 // Stop if we come to a literal 592 if ($resource instanceof EasyRdf_Literal) { 593 return null; 594 } 595 596 // Try each of the alternative paths 597 foreach (explode('|', $part) as $p) { 598 $res = $this->getSingleProperty($resource, $p, $type, $lang); 599 if ($res) { 600 break; 601 } 602 } 603 604 // Stop if nothing was found 605 $resource = $res; 606 if (!$resource) { 607 break; 608 } 609 } 610 611 return $resource; 612 } 613 614 /** Get a single value for a property of a resource 615 * 616 * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) 617 * @param string $property The name of the property (e.g. foaf:name) 618 * @param string $type The type of value to filter by (e.g. literal or resource) 619 * @param string $lang The language to filter by (e.g. en) 620 * @return mixed A value associated with the property 621 * 622 * @ignore 623 */ 624 protected function getSingleProperty($resource, $property, $type = null, $lang = null) 625 { 626 $this->checkResourceParam($resource); 627 $this->checkSinglePropertyParam($property, $inverse); 628 629 // Get an array of values for the property 630 $values = $this->propertyValuesArray($resource, $property, $inverse); 631 if (!isset($values)) { 632 return null; 633 } 634 635 // Filter the results 636 $result = null; 637 if ($type) { 638 foreach ($values as $value) { 639 if ($type == 'literal' and $value['type'] == 'literal') { 640 if ($lang == null or (isset($value['lang']) and $value['lang'] == $lang)) { 641 $result = $value; 642 break; 643 } 644 } elseif ($type == 'resource') { 645 if ($value['type'] == 'uri' or $value['type'] == 'bnode') { 646 $result = $value; 647 break; 648 } 649 } 650 } 651 } else { 652 $result = $values[0]; 653 } 654 655 // Convert the internal data structure into a PHP object 656 return $this->arrayToObject($result); 657 } 658 659 /** Get a single literal value for a property of a resource 660 * 661 * If multiple values are set for a property then the value returned 662 * may be arbitrary. 663 * 664 * This method will return null if there is not literal value for the 665 * property. 666 * 667 * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) 668 * @param string|array $property The name of the property (e.g. foaf:name) 669 * @param string $lang The language to filter by (e.g. en) 670 * @return object EasyRdf_Literal Literal value associated with the property 671 */ 672 public function getLiteral($resource, $property, $lang = null) 673 { 674 return $this->get($resource, $property, 'literal', $lang); 675 } 676 677 /** Get a single resource value for a property of a resource 678 * 679 * If multiple values are set for a property then the value returned 680 * may be arbitrary. 681 * 682 * This method will return null if there is not resource for the 683 * property. 684 * 685 * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) 686 * @param string|array $property The name of the property (e.g. foaf:name) 687 * @return object EasyRdf_Resource Resource associated with the property 688 */ 689 public function getResource($resource, $property) 690 { 691 return $this->get($resource, $property, 'resource'); 692 } 693 694 /** Return all the values for a particular property of a resource 695 * @ignore 696 */ 697 protected function propertyValuesArray($resource, $property, $inverse = false) 698 { 699 // Is an inverse property being requested? 700 if ($inverse) { 701 if (isset($this->revIndex[$resource])) { 702 $properties = &$this->revIndex[$resource]; 703 } 704 } else { 705 if (isset($this->index[$resource])) { 706 $properties = &$this->index[$resource]; 707 } 708 } 709 710 if (isset($properties[$property])) { 711 return $properties[$property]; 712 } else { 713 return null; 714 } 715 } 716 717 /** Get an EasyRdf_Resource or EasyRdf_Literal object from an associative array. 718 * @ignore 719 */ 720 protected function arrayToObject($data) 721 { 722 if ($data) { 723 if ($data['type'] == 'uri' or $data['type'] == 'bnode') { 724 return $this->resource($data['value']); 725 } else { 726 return EasyRdf_Literal::create($data); 727 } 728 } else { 729 return null; 730 } 731 } 732 733 /** Get all values for a property path 734 * 735 * This method will return an empty array if the property does not exist. 736 * 737 * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) 738 * @param string $propertyPath A valid property path 739 * @param string $type The type of value to filter by (e.g. literal) 740 * @param string $lang The language to filter by (e.g. en) 741 * @return array An array of values associated with the property 742 */ 743 public function all($resource, $propertyPath, $type = null, $lang = null) 744 { 745 $this->checkResourceParam($resource); 746 747 if (is_object($propertyPath) and $propertyPath instanceof EasyRdf_Resource) { 748 return $this->allForSingleProperty($resource, $propertyPath->getUri(), $type, $lang); 749 } elseif (is_string($propertyPath) and preg_match('|^(\^?)<(.+)>|', $propertyPath, $matches)) { 750 return $this->allForSingleProperty($resource, "$matches[1]$matches[2]", $type, $lang); 751 } elseif ($propertyPath === null or !is_string($propertyPath)) { 752 throw new InvalidArgumentException( 753 "\$propertyPath should be a string or EasyRdf_Resource and cannot be null" 754 ); 755 } elseif ($propertyPath === '') { 756 throw new InvalidArgumentException( 757 "\$propertyPath cannot be an empty string" 758 ); 759 } 760 761 $objects = array($resource); 762 763 // Loop through each component in the path 764 foreach (explode('/', $propertyPath) as $part) { 765 766 $results = array(); 767 foreach (explode('|', $part) as $p) { 768 foreach ($objects as $o) { 769 // Ignore literals found earlier in path 770 if ($o instanceof EasyRdf_Literal) { 771 continue; 772 } 773 774 $results = array_merge( 775 $results, 776 $this->allForSingleProperty($o, $p, $type, $lang) 777 ); 778 } 779 } 780 781 // Stop if we don't have anything 782 if (empty($objects)) { 783 break; 784 } 785 786 // Use the results as the input to the next iteration 787 $objects = $results; 788 } 789 790 return $results; 791 } 792 793 /** Get all values for a single property of a resource 794 * 795 * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) 796 * @param string $property The name of the property (e.g. foaf:name) 797 * @param string $type The type of value to filter by (e.g. literal) 798 * @param string $lang The language to filter by (e.g. en) 799 * @return array An array of values associated with the property 800 * 801 * @ignore 802 */ 803 protected function allForSingleProperty($resource, $property, $type = null, $lang = null) 804 { 805 $this->checkResourceParam($resource); 806 $this->checkSinglePropertyParam($property, $inverse); 807 808 // Get an array of values for the property 809 $values = $this->propertyValuesArray($resource, $property, $inverse); 810 if (!isset($values)) { 811 return array(); 812 } 813 814 $objects = array(); 815 if ($type) { 816 foreach ($values as $value) { 817 if ($type == 'literal' and $value['type'] == 'literal') { 818 if ($lang == null or (isset($value['lang']) and $value['lang'] == $lang)) { 819 $objects[] = $this->arrayToObject($value); 820 } 821 } elseif ($type == 'resource') { 822 if ($value['type'] == 'uri' or $value['type'] == 'bnode') { 823 $objects[] = $this->arrayToObject($value); 824 } 825 } 826 } 827 } else { 828 foreach ($values as $value) { 829 $objects[] = $this->arrayToObject($value); 830 } 831 } 832 return $objects; 833 } 834 835 /** Get all literal values for a property of a resource 836 * 837 * This method will return an empty array if the resource does not 838 * has any literal values for that property. 839 * 840 * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) 841 * @param string $property The name of the property (e.g. foaf:name) 842 * @param string $lang The language to filter by (e.g. en) 843 * @return array An array of values associated with the property 844 */ 845 public function allLiterals($resource, $property, $lang = null) 846 { 847 return $this->all($resource, $property, 'literal', $lang); 848 } 849 850 /** Get all resources for a property of a resource 851 * 852 * This method will return an empty array if the resource does not 853 * has any resources for that property. 854 * 855 * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) 856 * @param string $property The name of the property (e.g. foaf:name) 857 * @return array An array of values associated with the property 858 */ 859 public function allResources($resource, $property) 860 { 861 return $this->all($resource, $property, 'resource'); 862 } 863 864 /** Get all the resources in the graph of a certain type 865 * 866 * If no resources of the type are available and empty 867 * array is returned. 868 * 869 * @param string $type The type of the resource (e.g. foaf:Person) 870 * @return array The array of resources 871 */ 872 public function allOfType($type) 873 { 874 return $this->all($type, '^rdf:type'); 875 } 876 877 /** Count the number of values for a property of a resource 878 * 879 * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) 880 * @param string $property The name of the property (e.g. foaf:name) 881 * @param string $type The type of value to filter by (e.g. literal) 882 * @param string $lang The language to filter by (e.g. en) 883 * @return integer The number of values for this property 884 */ 885 public function countValues($resource, $property, $type = null, $lang = null) 886 { 887 return count($this->all($resource, $property, $type, $lang)); 888 } 889 890 /** Concatenate all values for a property of a resource into a string. 891 * 892 * The default is to join the values together with a space character. 893 * This method will return an empty string if the property does not exist. 894 * 895 * @param mixed $resource The resource to get the property on 896 * @param string $property The name of the property (e.g. foaf:name) 897 * @param string $glue The string to glue the values together with. 898 * @param string $lang The language to filter by (e.g. en) 899 * @return string Concatenation of all the values. 900 */ 901 public function join($resource, $property, $glue = ' ', $lang = null) 902 { 903 return join($glue, $this->all($resource, $property, 'literal', $lang)); 904 } 905 906 /** Add data to the graph 907 * 908 * The resource can either be a resource or the URI of a resource. 909 * 910 * Example: 911 * $graph->add("http://www.example.com", 'dc:title', 'Title of Page'); 912 * 913 * @param mixed $resource The resource to add data to 914 * @param mixed $property The property name 915 * @param mixed $value The new value for the property 916 * @return integer The number of values added (1 or 0) 917 */ 918 public function add($resource, $property, $value) 919 { 920 $this->checkResourceParam($resource); 921 $this->checkSinglePropertyParam($property, $inverse); 922 $this->checkValueParam($value); 923 924 // No value given? 925 if ($value === null) { 926 return 0; 927 } 928 929 // Check that the value doesn't already exist 930 if (isset($this->index[$resource][$property])) { 931 foreach ($this->index[$resource][$property] as $v) { 932 if ($v == $value) { 933 return 0; 934 } 935 } 936 } 937 $this->index[$resource][$property][] = $value; 938 939 // Add to the reverse index if it is a resource 940 if ($value['type'] == 'uri' or $value['type'] == 'bnode') { 941 $uri = $value['value']; 942 $this->revIndex[$uri][$property][] = array( 943 'type' => substr($resource, 0, 2) == '_:' ? 'bnode' : 'uri', 944 'value' => $resource 945 ); 946 } 947 948 // Success 949 return 1; 950 } 951 952 /** Add a literal value as a property of a resource 953 * 954 * The resource can either be a resource or the URI of a resource. 955 * The value can either be a single value or an array of values. 956 * 957 * Example: 958 * $graph->add("http://www.example.com", 'dc:title', 'Title of Page'); 959 * 960 * @param mixed $resource The resource to add data to 961 * @param mixed $property The property name 962 * @param mixed $value The value or values for the property 963 * @param string $lang The language of the literal 964 * @return integer The number of values added 965 */ 966 public function addLiteral($resource, $property, $value, $lang = null) 967 { 968 $this->checkResourceParam($resource); 969 $this->checkSinglePropertyParam($property, $inverse); 970 971 if (is_array($value)) { 972 $added = 0; 973 foreach ($value as $v) { 974 $added += $this->addLiteral($resource, $property, $v, $lang); 975 } 976 return $added; 977 } elseif (!is_object($value) or !$value instanceof EasyRdf_Literal) { 978 $value = EasyRdf_Literal::create($value, $lang); 979 } 980 return $this->add($resource, $property, $value); 981 } 982 983 /** Add a resource as a property of another resource 984 * 985 * The resource can either be a resource or the URI of a resource. 986 * 987 * Example: 988 * $graph->add("http://example.com/bob", 'foaf:knows', 'http://example.com/alice'); 989 * 990 * @param mixed $resource The resource to add data to 991 * @param mixed $property The property name 992 * @param mixed $resource2 The resource to be value of the property 993 * @return integer The number of values added 994 */ 995 public function addResource($resource, $property, $resource2) 996 { 997 $this->checkResourceParam($resource); 998 $this->checkSinglePropertyParam($property, $inverse); 999 $this->checkResourceParam($resource2); 1000 1001 return $this->add( 1002 $resource, 1003 $property, 1004 array( 1005 'type' => substr($resource2, 0, 2) == '_:' ? 'bnode' : 'uri', 1006 'value' => $resource2 1007 ) 1008 ); 1009 } 1010 1011 /** Set a value for a property 1012 * 1013 * The new value will replace the existing values for the property. 1014 * 1015 * @param string $resource The resource to set the property on 1016 * @param string $property The name of the property (e.g. foaf:name) 1017 * @param mixed $value The value for the property 1018 * @return integer The number of values added (1 or 0) 1019 */ 1020 public function set($resource, $property, $value) 1021 { 1022 $this->checkResourceParam($resource); 1023 $this->checkSinglePropertyParam($property, $inverse); 1024 $this->checkValueParam($value); 1025 1026 // Delete the old values 1027 $this->delete($resource, $property); 1028 1029 // Add the new values 1030 return $this->add($resource, $property, $value); 1031 } 1032 1033 /** Delete a property (or optionally just a specific value) 1034 * 1035 * @param mixed $resource The resource to delete the property from 1036 * @param string $property The name of the property (e.g. foaf:name) 1037 * @param mixed $value The value to delete (null to delete all values) 1038 * @return integer The number of values deleted 1039 */ 1040 public function delete($resource, $property, $value = null) 1041 { 1042 $this->checkResourceParam($resource); 1043 1044 if (is_object($property) and $property instanceof EasyRdf_Resource) { 1045 return $this->deleteSingleProperty($resource, $property->getUri(), $value); 1046 } elseif (is_string($property) and preg_match('|^(\^?)<(.+)>|', $property, $matches)) { 1047 return $this->deleteSingleProperty($resource, "$matches[1]$matches[2]", $value); 1048 } elseif ($property === null or !is_string($property)) { 1049 throw new InvalidArgumentException( 1050 "\$property should be a string or EasyRdf_Resource and cannot be null" 1051 ); 1052 } elseif ($property === '') { 1053 throw new InvalidArgumentException( 1054 "\$property cannot be an empty string" 1055 ); 1056 } 1057 1058 // FIXME: finish implementing property paths for delete 1059 return $this->deleteSingleProperty($resource, $property, $value); 1060 } 1061 1062 1063 /** Delete a property (or optionally just a specific value) 1064 * 1065 * @param mixed $resource The resource to delete the property from 1066 * @param string $property The name of the property (e.g. foaf:name) 1067 * @param mixed $value The value to delete (null to delete all values) 1068 * @return integer The number of values deleted 1069 * 1070 * @ignore 1071 */ 1072 public function deleteSingleProperty($resource, $property, $value = null) 1073 { 1074 $this->checkResourceParam($resource); 1075 $this->checkSinglePropertyParam($property, $inverse); 1076 $this->checkValueParam($value); 1077 1078 $count = 0; 1079 if (isset($this->index[$resource][$property])) { 1080 foreach ($this->index[$resource][$property] as $k => $v) { 1081 if (!$value or $v == $value) { 1082 unset($this->index[$resource][$property][$k]); 1083 $count++; 1084 if ($v['type'] == 'uri' or $v['type'] == 'bnode') { 1085 $this->deleteInverse($v['value'], $property, $resource); 1086 } 1087 } 1088 } 1089 1090 // Clean up the indexes - remove empty properties and resources 1091 if ($count) { 1092 if (count($this->index[$resource][$property]) == 0) { 1093 unset($this->index[$resource][$property]); 1094 } 1095 if (count($this->index[$resource]) == 0) { 1096 unset($this->index[$resource]); 1097 } 1098 } 1099 } 1100 1101 return $count; 1102 } 1103 1104 /** Delete a resource from a property of another resource 1105 * 1106 * The resource can either be a resource or the URI of a resource. 1107 * 1108 * Example: 1109 * $graph->delete("http://example.com/bob", 'foaf:knows', 'http://example.com/alice'); 1110 * 1111 * @param mixed $resource The resource to delete data from 1112 * @param mixed $property The property name 1113 * @param mixed $resource2 The resource value of the property to be deleted 1114 */ 1115 public function deleteResource($resource, $property, $resource2) 1116 { 1117 $this->checkResourceParam($resource); 1118 $this->checkSinglePropertyParam($property, $inverse); 1119 $this->checkResourceParam($resource2); 1120 1121 return $this->delete( 1122 $resource, 1123 $property, 1124 array( 1125 'type' => substr($resource2, 0, 2) == '_:' ? 'bnode' : 'uri', 1126 'value' => $resource2 1127 ) 1128 ); 1129 } 1130 1131 /** Delete a literal value from a property of a resource 1132 * 1133 * Example: 1134 * $graph->delete("http://www.example.com", 'dc:title', 'Title of Page'); 1135 * 1136 * @param mixed $resource The resource to add data to 1137 * @param mixed $property The property name 1138 * @param mixed $value The value of the property 1139 * @param string $lang The language of the literal 1140 */ 1141 public function deleteLiteral($resource, $property, $value, $lang = null) 1142 { 1143 $this->checkResourceParam($resource); 1144 $this->checkSinglePropertyParam($property, $inverse); 1145 $this->checkValueParam($value); 1146 1147 if ($lang) { 1148 $value['lang'] = $lang; 1149 } 1150 1151 return $this->delete($resource, $property, $value); 1152 } 1153 1154 /** This function is for internal use only. 1155 * 1156 * Deletes an inverse property from a resource. 1157 * 1158 * @ignore 1159 */ 1160 protected function deleteInverse($resource, $property, $value) 1161 { 1162 if (isset($this->revIndex[$resource])) { 1163 foreach ($this->revIndex[$resource][$property] as $k => $v) { 1164 if ($v['value'] === $value) { 1165 unset($this->revIndex[$resource][$property][$k]); 1166 } 1167 } 1168 if (count($this->revIndex[$resource][$property]) == 0) { 1169 unset($this->revIndex[$resource][$property]); 1170 } 1171 if (count($this->revIndex[$resource]) == 0) { 1172 unset($this->revIndex[$resource]); 1173 } 1174 } 1175 } 1176 1177 /** Check if the graph contains any statements 1178 * 1179 * @return boolean True if the graph contains no statements 1180 */ 1181 public function isEmpty() 1182 { 1183 return count($this->index) == 0; 1184 } 1185 1186 /** Get a list of all the shortened property names (qnames) for a resource. 1187 * 1188 * This method will return an empty array if the resource has no properties. 1189 * 1190 * @return array Array of shortened URIs 1191 */ 1192 public function properties($resource) 1193 { 1194 $this->checkResourceParam($resource); 1195 1196 $properties = array(); 1197 if (isset($this->index[$resource])) { 1198 foreach ($this->index[$resource] as $property => $value) { 1199 $short = EasyRdf_Namespace::shorten($property); 1200 if ($short) { 1201 $properties[] = $short; 1202 } 1203 } 1204 } 1205 return $properties; 1206 } 1207 1208 /** Get a list of the full URIs for the properties of a resource. 1209 * 1210 * This method will return an empty array if the resource has no properties. 1211 * 1212 * @return array Array of full URIs 1213 */ 1214 public function propertyUris($resource) 1215 { 1216 $this->checkResourceParam($resource); 1217 1218 if (isset($this->index[$resource])) { 1219 return array_keys($this->index[$resource]); 1220 } else { 1221 return array(); 1222 } 1223 } 1224 1225 /** Get a list of the full URIs for the properties that point to a resource. 1226 * 1227 * @return array Array of full property URIs 1228 */ 1229 public function reversePropertyUris($resource) 1230 { 1231 $this->checkResourceParam($resource); 1232 1233 if (isset($this->revIndex[$resource])) { 1234 return array_keys($this->revIndex[$resource]); 1235 } else { 1236 return array(); 1237 } 1238 } 1239 1240 /** Check to see if a property exists for a resource. 1241 * 1242 * This method will return true if the property exists. 1243 * If the value parameter is given, then it will only return true 1244 * if the value also exists for that property. 1245 * 1246 * By providing a value parameter you can use this function to check 1247 * to see if a triple exists in the graph. 1248 * 1249 * @param mixed $resource The resource to check 1250 * @param string $property The name of the property (e.g. foaf:name) 1251 * @param mixed $value An optional value of the property 1252 * @return boolean True if value the property exists. 1253 */ 1254 public function hasProperty($resource, $property, $value = null) 1255 { 1256 $this->checkResourceParam($resource); 1257 $this->checkSinglePropertyParam($property, $inverse); 1258 $this->checkValueParam($value); 1259 1260 // Use the reverse index if it is an inverse property 1261 if ($inverse) { 1262 $index = &$this->revIndex; 1263 } else { 1264 $index = &$this->index; 1265 } 1266 1267 if (isset($index[$resource][$property])) { 1268 if (is_null($value)) { 1269 return true; 1270 } else { 1271 foreach ($index[$resource][$property] as $v) { 1272 if ($v == $value) { 1273 return true; 1274 } 1275 } 1276 } 1277 } 1278 1279 return false; 1280 } 1281 1282 /** Serialise the graph into RDF 1283 * 1284 * The $format parameter can be an EasyRdf_Format object, a 1285 * format name, a mime type or a file extension. 1286 * 1287 * Example: 1288 * $turtle = $graph->serialise('turtle'); 1289 * 1290 * @param mixed $format The format to serialise to 1291 * @param array $options Serialiser-specific options, for fine-tuning the output 1292 * @return mixed The serialised graph 1293 */ 1294 public function serialise($format, array $options = array()) 1295 { 1296 if (!$format instanceof EasyRdf_Format) { 1297 $format = EasyRdf_Format::getFormat($format); 1298 } 1299 $serialiser = $format->newSerialiser(); 1300 return $serialiser->serialise($this, $format->getName(), $options); 1301 } 1302 1303 /** Return a human readable view of all the resources in the graph 1304 * 1305 * This method is intended to be a debugging aid and will 1306 * return a pretty-print view of all the resources and their 1307 * properties. 1308 * 1309 * @param string $format Either 'html' or 'text' 1310 * @return string 1311 */ 1312 public function dump($format = 'html') 1313 { 1314 $result = ''; 1315 if ($format == 'html') { 1316 $result .= "<div style='font-family:arial; font-weight: bold; padding:0.5em; ". 1317 "color: black; background-color:lightgrey;border:dashed 1px grey;'>". 1318 "Graph: ". $this->uri . "</div>\n"; 1319 } else { 1320 $result .= "Graph: ". $this->uri . "\n"; 1321 } 1322 1323 foreach ($this->index as $resource => $properties) { 1324 $result .= $this->dumpResource($resource, $format); 1325 } 1326 return $result; 1327 } 1328 1329 /** Return a human readable view of a resource and its properties 1330 * 1331 * This method is intended to be a debugging aid and will 1332 * print a resource and its properties. 1333 * 1334 * @param mixed $resource The resource to dump 1335 * @param string $format Either 'html' or 'text' 1336 * @return string 1337 */ 1338 public function dumpResource($resource, $format = 'html') 1339 { 1340 $this->checkResourceParam($resource, true); 1341 1342 if (isset($this->index[$resource])) { 1343 $properties = $this->index[$resource]; 1344 } else { 1345 return ''; 1346 } 1347 1348 $plist = array(); 1349 foreach ($properties as $property => $values) { 1350 $olist = array(); 1351 foreach ($values as $value) { 1352 if ($value['type'] == 'literal') { 1353 $olist []= EasyRdf_Utils::dumpLiteralValue($value, $format, 'black'); 1354 } else { 1355 $olist []= EasyRdf_Utils::dumpResourceValue($value['value'], $format, 'blue'); 1356 } 1357 } 1358 1359 $pstr = EasyRdf_Namespace::shorten($property); 1360 if ($pstr == null) { 1361 $pstr = $property; 1362 } 1363 if ($format == 'html') { 1364 $plist []= "<span style='font-size:130%'>→</span> ". 1365 "<span style='text-decoration:none;color:green'>". 1366 htmlentities($pstr) . "</span> ". 1367 "<span style='font-size:130%'>→</span> ". 1368 join(", ", $olist); 1369 } else { 1370 $plist []= " -> $pstr -> " . join(", ", $olist); 1371 } 1372 } 1373 1374 if ($format == 'html') { 1375 return "<div id='".htmlentities($resource, ENT_QUOTES)."' " . 1376 "style='font-family:arial; padding:0.5em; ". 1377 "background-color:lightgrey;border:dashed 1px grey;'>\n". 1378 "<div>".EasyRdf_Utils::dumpResourceValue($resource, $format, 'blue')." ". 1379 "<span style='font-size: 0.8em'>(". 1380 $this->classForResource($resource).")</span></div>\n". 1381 "<div style='padding-left: 3em'>\n". 1382 "<div>".join("</div>\n<div>", $plist)."</div>". 1383 "</div></div>\n"; 1384 } else { 1385 return $resource." (".$this->classForResource($resource).")\n" . 1386 join("\n", $plist) . "\n\n"; 1387 } 1388 } 1389 1390 /** Get the resource type of the graph 1391 * 1392 * The type will be a shortened URI as a string. 1393 * If the graph has multiple types then the type returned 1394 * may be arbitrary. 1395 * This method will return null if the resource has no type. 1396 * 1397 * @return string A type assocated with the resource (e.g. foaf:Document) 1398 */ 1399 public function type($resource = null) 1400 { 1401 $type = $this->typeAsResource($resource); 1402 1403 if ($type) { 1404 return EasyRdf_Namespace::shorten($type); 1405 } 1406 1407 return null; 1408 } 1409 1410 /** Get the resource type of the graph as a EasyRdf_Resource 1411 * 1412 * If the graph has multiple types then the type returned 1413 * may be arbitrary. 1414 * This method will return null if the resource has no type. 1415 * 1416 * @return object EasyRdf_Resource A type assocated with the resource 1417 */ 1418 public function typeAsResource($resource = null) 1419 { 1420 $this->checkResourceParam($resource, true); 1421 1422 if ($resource) { 1423 return $this->get($resource, 'rdf:type', 'resource'); 1424 } 1425 1426 return null; 1427 } 1428 1429 /** Get a list of types for a resource 1430 * 1431 * The types will each be a shortened URI as a string. 1432 * This method will return an empty array if the resource has no types. 1433 * 1434 * If $resource is null, then it will get the types for the URI of the graph. 1435 * 1436 * @return array All types assocated with the resource (e.g. foaf:Person) 1437 */ 1438 public function types($resource = null) 1439 { 1440 $resources = $this->typesAsResources($resource); 1441 1442 $types = array(); 1443 foreach ($resources as $type) { 1444 $types[] = EasyRdf_Namespace::shorten($type); 1445 } 1446 1447 return $types; 1448 } 1449 1450 /** 1451 * Get the resource types of the graph as a EasyRdf_Resource 1452 * 1453 * @return EasyRdf_Resource[] 1454 */ 1455 public function typesAsResources($resource = null) 1456 { 1457 $this->checkResourceParam($resource, true); 1458 1459 if ($resource) { 1460 return $this->all($resource, 'rdf:type', 'resource'); 1461 } 1462 1463 return array(); 1464 } 1465 1466 /** Check if a resource is of the specified type 1467 * 1468 * @param string $resource The resource to check the type of 1469 * @param string $type The type to check (e.g. foaf:Person) 1470 * @return boolean True if resource is of specified type 1471 */ 1472 public function isA($resource, $type) 1473 { 1474 $this->checkResourceParam($resource, true); 1475 1476 $type = EasyRdf_Namespace::expand($type); 1477 foreach ($this->all($resource, 'rdf:type', 'resource') as $t) { 1478 if ($t->getUri() == $type) { 1479 return true; 1480 } 1481 } 1482 return false; 1483 } 1484 1485 /** Add one or more rdf:type properties to a resource 1486 * 1487 * @param string $resource The resource to add the type to 1488 * @param string $types One or more types to add (e.g. foaf:Person) 1489 * @return integer The number of types added 1490 */ 1491 public function addType($resource, $types) 1492 { 1493 $this->checkResourceParam($resource, true); 1494 1495 if (!is_array($types)) { 1496 $types = array($types); 1497 } 1498 1499 $count = 0; 1500 foreach ($types as $type) { 1501 $type = EasyRdf_Namespace::expand($type); 1502 $count += $this->add($resource, 'rdf:type', array('type' => 'uri', 'value' => $type)); 1503 } 1504 1505 return $count; 1506 } 1507 1508 /** Change the rdf:type property for a resource 1509 * 1510 * Note that if the resource object has already previously 1511 * been created, then the PHP class of the resource will not change. 1512 * 1513 * @param string $resource The resource to change the type of 1514 * @param string $type The new type (e.g. foaf:Person) 1515 * @return integer The number of types added 1516 */ 1517 public function setType($resource, $type) 1518 { 1519 $this->checkResourceParam($resource, true); 1520 1521 $this->delete($resource, 'rdf:type'); 1522 return $this->addType($resource, $type); 1523 } 1524 1525 /** Get a human readable label for a resource 1526 * 1527 * This method will check a number of properties for a resource 1528 * (in the order: skos:prefLabel, rdfs:label, foaf:name, dc:title) 1529 * and return an approriate first that is available. If no label 1530 * is available then it will return null. 1531 * 1532 * @return string A label for the resource. 1533 */ 1534 public function label($resource = null, $lang = null) 1535 { 1536 $this->checkResourceParam($resource, true); 1537 1538 if ($resource) { 1539 return $this->get( 1540 $resource, 1541 'skos:prefLabel|rdfs:label|foaf:name|rss:title|dc:title|dc11:title', 1542 'literal', 1543 $lang 1544 ); 1545 } else { 1546 return null; 1547 } 1548 } 1549 1550 /** Get the primary topic of the graph 1551 * 1552 * @return EasyRdf_Resource The primary topic of the document. 1553 */ 1554 public function primaryTopic($resource = null) 1555 { 1556 $this->checkResourceParam($resource, true); 1557 1558 if ($resource) { 1559 return $this->get( 1560 $resource, 1561 'foaf:primaryTopic|^foaf:isPrimaryTopicOf', 1562 'resource' 1563 ); 1564 } else { 1565 return null; 1566 } 1567 } 1568 1569 /** Returns the graph as a RDF/PHP associative array 1570 * 1571 * @return array The contents of the graph as an array. 1572 */ 1573 public function toRdfPhp() 1574 { 1575 return $this->index; 1576 } 1577 1578 /** Calculates the number of triples in the graph 1579 * 1580 * @return integer The number of triples in the graph. 1581 */ 1582 public function countTriples() 1583 { 1584 $count = 0; 1585 foreach ($this->index as $resource) { 1586 foreach ($resource as $property => $values) { 1587 $count += count($values); 1588 } 1589 } 1590 return $count; 1591 } 1592 1593 /** Magic method to return URI of resource when casted to string 1594 * 1595 * @return string The URI of the resource 1596 */ 1597 public function __toString() 1598 { 1599 return $this->uri == null ? '' : $this->uri; 1600 } 1601 1602 /** Magic method to get a property of the graph 1603 * 1604 * Note that only properties in the default namespace can be accessed in this way. 1605 * 1606 * Example: 1607 * $value = $graph->title; 1608 * 1609 * @see EasyRdf_Namespace::setDefault() 1610 * @param string $name The name of the property 1611 * @return string A single value for the named property 1612 */ 1613 public function __get($name) 1614 { 1615 return $this->get($this->uri, $name); 1616 } 1617 1618 /** Magic method to set the value for a property of the graph 1619 * 1620 * Note that only properties in the default namespace can be accessed in this way. 1621 * 1622 * Example: 1623 * $graph->title = 'Title'; 1624 * 1625 * @see EasyRdf_Namespace::setDefault() 1626 * @param string $name The name of the property 1627 * @param string $value The value for the property 1628 */ 1629 public function __set($name, $value) 1630 { 1631 return $this->set($this->uri, $name, $value); 1632 } 1633 1634 /** Magic method to check if a property exists 1635 * 1636 * Note that only properties in the default namespace can be accessed in this way. 1637 * 1638 * Example: 1639 * if (isset($graph->title)) { blah(); } 1640 * 1641 * @see EasyRdf_Namespace::setDefault() 1642 * @param string $name The name of the property 1643 */ 1644 public function __isset($name) 1645 { 1646 return $this->hasProperty($this->uri, $name); 1647 } 1648 1649 /** Magic method to delete a property of the graph 1650 * 1651 * Note that only properties in the default namespace can be accessed in this way. 1652 * 1653 * Example: 1654 * unset($graph->title); 1655 * 1656 * @see EasyRdf_Namespace::setDefault() 1657 * @param string $name The name of the property 1658 */ 1659 public function __unset($name) 1660 { 1661 return $this->delete($this->uri, $name); 1662 } 1663} 1664