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 * Class that represents an RDF resource
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_Resource
46{
47    /** The URI for this resource */
48    protected $uri = null;
49
50    /** The Graph that this resource belongs to */
51    protected $graph = null;
52
53
54    /** Constructor
55     *
56     * * Please do not call new EasyRdf_Resource() directly *
57     *
58     * To create a new resource use the get method in a graph:
59     * $resource = $graph->resource('http://www.example.com/');
60     *
61     */
62    public function __construct($uri, $graph = null)
63    {
64        if (!is_string($uri) or $uri == null or $uri == '') {
65            throw new InvalidArgumentException(
66                "\$uri should be a string and cannot be null or empty"
67            );
68        }
69
70        $this->uri = $uri;
71
72        # Check that $graph is an EasyRdf_Graph object
73        if (is_object($graph) and $graph instanceof EasyRdf_Graph) {
74            $this->graph = $graph;
75        } elseif (!is_null($graph)) {
76            throw new InvalidArgumentException(
77                "\$graph should be an EasyRdf_Graph object"
78            );
79        }
80    }
81
82    /**
83     * Return the graph that this resource belongs to
84     *
85     * @return EasyRdf_Graph
86     */
87    public function getGraph()
88    {
89        return $this->graph;
90    }
91
92    /** Returns the URI for the resource.
93     *
94     * @return string  URI of this resource.
95     */
96    public function getUri()
97    {
98        return $this->uri;
99    }
100
101    /** Check to see if a resource is a blank node.
102     *
103     * @return bool True if this resource is a blank node.
104     */
105    public function isBNode()
106    {
107        if (substr($this->uri, 0, 2) == '_:') {
108            return true;
109        } else {
110            return false;
111        }
112    }
113
114    /** Get the identifier for a blank node
115     *
116     * Returns null if the resource is not a blank node.
117     *
118     * @return string The identifer for the bnode
119     */
120    public function getBNodeId()
121    {
122        if (substr($this->uri, 0, 2) == '_:') {
123            return substr($this->uri, 2);
124        } else {
125            return null;
126        }
127    }
128
129    /** Get a the prefix of the namespace that this resource is part of
130     *
131     * This method will return null the resource isn't part of any
132     * registered namespace.
133     *
134     * @return string The namespace prefix of the resource (e.g. foaf)
135     */
136    public function prefix()
137    {
138        return EasyRdf_Namespace::prefixOfUri($this->uri);
139    }
140
141    /** Get a shortened version of the resources URI.
142     *
143     * This method will return the full URI if the resource isn't part of any
144     * registered namespace.
145     *
146     * @return string The shortened URI of this resource (e.g. foaf:name)
147     */
148    public function shorten()
149    {
150        return EasyRdf_Namespace::shorten($this->uri);
151    }
152
153    /** Gets the local name of the URI of this resource
154     *
155     * The local name is defined as the part of the URI string
156     * after the last occurrence of the '#', ':' or '/' character.
157     *
158     * @return string The local name
159     */
160    public function localName()
161    {
162        if (preg_match("|([^#:/]+)$|", $this->uri, $matches)) {
163            return $matches[1];
164        }
165    }
166
167    /** Parse the URI of the resource and return as a ParsedUri object
168     *
169     * @return EasyRdf_ParsedUri
170     */
171    public function parseUri()
172    {
173        return new EasyRdf_ParsedUri($this->uri);
174    }
175
176    /** Generates an HTML anchor tag, linking to this resource.
177     *
178     * If no text is given, then the URI also uses as the link text.
179     *
180     * @param  string  $text    Text for the link.
181     * @param  array   $options Associative array of attributes for the anchor tag
182     * @return string  The HTML link string
183     */
184    public function htmlLink($text = null, $options = array())
185    {
186        $options = array_merge(array('href' => $this->uri), $options);
187        if ($text === null) {
188            $text = $this->uri;
189        }
190
191        $html = "<a";
192        foreach ($options as $key => $value) {
193            if (!preg_match('/^[-\w]+$/', $key)) {
194                throw new InvalidArgumentException(
195                    "\$options should use valid attribute names as keys"
196                );
197            }
198
199            $html .= " ".htmlspecialchars($key)."=\"".
200                         htmlspecialchars($value)."\"";
201        }
202        $html .= ">".htmlspecialchars($text)."</a>";
203
204        return $html;
205    }
206
207    /** Returns the properties of the resource as an RDF/PHP associative array
208     *
209     * For example:
210     * array('type' => 'uri', 'value' => 'http://www.example.com/')
211     *
212     * @return array  The properties of the resource
213     */
214    public function toRdfPhp()
215    {
216        if ($this->isBNode()) {
217            return array('type' => 'bnode', 'value' => $this->uri);
218        } else {
219            return array('type' => 'uri', 'value' => $this->uri);
220        }
221    }
222
223    /** Return pretty-print view of the resource
224     *
225     * @param  string $format Either 'html' or 'text'
226     * @param  string $color The colour of the text
227     * @return string
228     */
229    public function dumpValue($format = 'html', $color = 'blue')
230    {
231        return EasyRdf_Utils::dumpResourceValue($this, $format, $color);
232    }
233
234    /** Magic method to return URI of resource when casted to string
235     *
236     * @return string The URI of the resource
237     */
238    public function __toString()
239    {
240        return $this->uri;
241    }
242
243
244
245    /** Throw can exception if the resource does not belong to a graph
246     *  @ignore
247     */
248    protected function checkHasGraph()
249    {
250        if (!$this->graph) {
251            throw new EasyRdf_Exception(
252                "EasyRdf_Resource is not part of a graph."
253            );
254        }
255    }
256
257    /** Perform a load (download of remote URI) of the resource into the graph
258     *
259     * The document type is optional but should be specified if it
260     * can't be guessed or got from the HTTP headers.
261     *
262     * @param  string  $format  Optional format of the data (eg. rdfxml)
263     */
264    public function load($format = null)
265    {
266        $this->checkHasGraph();
267        return $this->graph->load($this->uri, $format);
268    }
269
270    /** Delete a property (or optionally just a specific value)
271     *
272     * @param  string  $property The name of the property (e.g. foaf:name)
273     * @param  object  $value The value to delete (null to delete all values)
274     * @return null
275     */
276    public function delete($property, $value = null)
277    {
278        $this->checkHasGraph();
279        return $this->graph->delete($this->uri, $property, $value);
280    }
281
282    /** Add values to for a property of the resource
283     *
284     * Example:
285     *   $resource->add('prefix:property', 'value');
286     *
287     * @param  mixed $property   The property name
288     * @param  mixed $value      The value for the property
289     * @return integer           The number of values added (1 or 0)
290     */
291    public function add($property, $value)
292    {
293        $this->checkHasGraph();
294        return $this->graph->add($this->uri, $property, $value);
295    }
296
297    /** Add a literal value as a property of the resource
298     *
299     * The value can either be a single value or an array of values.
300     *
301     * Example:
302     *   $resource->add('dc:title', 'Title of Page');
303     *
304     * @param  mixed  $property  The property name
305     * @param  mixed  $values    The value or values for the property
306     * @param  string $lang      The language of the literal
307     * @return integer           The number of values added
308     */
309    public function addLiteral($property, $values, $lang = null)
310    {
311        $this->checkHasGraph();
312        return $this->graph->addLiteral($this->uri, $property, $values, $lang);
313    }
314
315    /** Add a resource as a property of the resource
316     *
317     * Example:
318     *   $bob->add('foaf:knows', 'http://example.com/alice');
319     *
320     * @param  mixed $property   The property name
321     * @param  mixed $resource2  The resource to be the value of the property
322     * @return integer           The number of values added (1 or 0)
323     */
324    public function addResource($property, $resource2)
325    {
326        $this->checkHasGraph();
327        return $this->graph->addResource($this->uri, $property, $resource2);
328    }
329
330    /** Set value for a property
331     *
332     * The new value(s) will replace the existing values for the property.
333     * The name of the property should be a string.
334     * If you set a property to null or an empty array, then the property
335     * will be deleted.
336     *
337     * @param  string  $property The name of the property (e.g. foaf:name)
338     * @param  mixed   $value    The value for the property.
339     * @return integer           The number of values added (1 or 0)
340     */
341    public function set($property, $value)
342    {
343        $this->checkHasGraph();
344        return $this->graph->set($this->uri, $property, $value);
345    }
346
347    /** Get a single value for a property
348     *
349     * If multiple values are set for a property then the value returned
350     * may be arbitrary.
351     *
352     * If $property is an array, then the first item in the array that matches
353     * a property that exists is returned.
354     *
355     * This method will return null if the property does not exist.
356     *
357     * @param  string|array $property The name of the property (e.g. foaf:name)
358     * @param  string       $type     The type of value to filter by (e.g. literal or resource)
359     * @param  string       $lang     The language to filter by (e.g. en)
360     * @return mixed                  A value associated with the property
361     */
362    public function get($property, $type = null, $lang = null)
363    {
364        $this->checkHasGraph();
365        return $this->graph->get($this->uri, $property, $type, $lang);
366    }
367
368    /** Get a single literal value for a property of the resource
369     *
370     * If multiple values are set for a property then the value returned
371     * may be arbitrary.
372     *
373     * This method will return null if there is not literal value for the
374     * property.
375     *
376     * @param  string|array $property The name of the property (e.g. foaf:name)
377     * @param  string       $lang     The language to filter by (e.g. en)
378     * @return object EasyRdf_Literal Literal value associated with the property
379     */
380    public function getLiteral($property, $lang = null)
381    {
382        $this->checkHasGraph();
383        return $this->graph->get($this->uri, $property, 'literal', $lang);
384    }
385
386    /** Get a single resource value for a property of the resource
387     *
388     * If multiple values are set for a property then the value returned
389     * may be arbitrary.
390     *
391     * This method will return null if there is not resource for the
392     * property.
393     *
394     * @param  string|array $property The name of the property (e.g. foaf:name)
395     * @return object EasyRdf_Resource Resource associated with the property
396     */
397    public function getResource($property)
398    {
399        $this->checkHasGraph();
400        return $this->graph->get($this->uri, $property, 'resource');
401    }
402
403    /** Get all values for a property
404     *
405     * This method will return an empty array if the property does not exist.
406     *
407     * @param  string  $property The name of the property (e.g. foaf:name)
408     * @param  string  $type     The type of value to filter by (e.g. literal)
409     * @param  string  $lang     The language to filter by (e.g. en)
410     * @return array             An array of values associated with the property
411     */
412    public function all($property, $type = null, $lang = null)
413    {
414        $this->checkHasGraph();
415        return $this->graph->all($this->uri, $property, $type, $lang);
416    }
417
418    /** Get all literal values for a property of the resource
419     *
420     * This method will return an empty array if the resource does not
421     * has any literal values for that property.
422     *
423     * @param  string  $property The name of the property (e.g. foaf:name)
424     * @param  string  $lang     The language to filter by (e.g. en)
425     * @return array             An array of values associated with the property
426     */
427    public function allLiterals($property, $lang = null)
428    {
429        $this->checkHasGraph();
430        return $this->graph->all($this->uri, $property, 'literal', $lang);
431    }
432
433    /** Get all resources for a property of the resource
434     *
435     * This method will return an empty array if the resource does not
436     * has any resources for that property.
437     *
438     * @param  string  $property The name of the property (e.g. foaf:name)
439     * @return array             An array of values associated with the property
440     */
441    public function allResources($property)
442    {
443        $this->checkHasGraph();
444        return $this->graph->all($this->uri, $property, 'resource');
445    }
446
447    /** Count the number of values for a property of a resource
448     *
449     * This method will return 0 if the property does not exist.
450     *
451     * @param  string  $property The name of the property (e.g. foaf:name)
452     * @param  string  $type     The type of value to filter by (e.g. literal)
453     * @param  string  $lang     The language to filter by (e.g. en)
454     * @return integer           The number of values associated with the property
455     */
456    public function countValues($property, $type = null, $lang = null)
457    {
458        $this->checkHasGraph();
459        return $this->graph->countValues($this->uri, $property, $type, $lang);
460    }
461
462    /** Concatenate all values for a property into a string.
463     *
464     * The default is to join the values together with a space character.
465     * This method will return an empty string if the property does not exist.
466     *
467     * @param  string  $property The name of the property (e.g. foaf:name)
468     * @param  string  $glue     The string to glue the values together with.
469     * @param  string  $lang     The language to filter by (e.g. en)
470     * @return string            Concatenation of all the values.
471     */
472    public function join($property, $glue = ' ', $lang = null)
473    {
474        $this->checkHasGraph();
475        return $this->graph->join($this->uri, $property, $glue, $lang);
476    }
477
478    /** Get a list of the full URIs for the properties of this resource.
479     *
480     * This method will return an empty array if the resource has no properties.
481     *
482     * @return array            Array of full URIs
483     */
484    public function propertyUris()
485    {
486        $this->checkHasGraph();
487        return $this->graph->propertyUris($this->uri);
488    }
489
490    /** Get a list of all the shortened property names (qnames) for a resource.
491     *
492     * This method will return an empty array if the resource has no properties.
493     *
494     * @return array            Array of shortened URIs
495     */
496    public function properties()
497    {
498        $this->checkHasGraph();
499        return $this->graph->properties($this->uri);
500    }
501
502    /** Get a list of the full URIs for the properties that point to this resource.
503     *
504     * @return array   Array of full property URIs
505     */
506    public function reversePropertyUris()
507    {
508        $this->checkHasGraph();
509        return $this->graph->reversePropertyUris($this->uri);
510    }
511
512    /** Check to see if a property exists for this resource.
513     *
514     * This method will return true if the property exists.
515     * If the value parameter is given, then it will only return true
516     * if the value also exists for that property.
517     *
518     * @param  string  $property The name of the property (e.g. foaf:name)
519     * @param  mixed   $value    An optional value of the property
520     * @return bool              True if value the property exists.
521     */
522    public function hasProperty($property, $value = null)
523    {
524        $this->checkHasGraph();
525        return $this->graph->hasProperty($this->uri, $property, $value);
526    }
527
528    /** Get a list of types for a resource.
529     *
530     * The types will each be a shortened URI as a string.
531     * This method will return an empty array if the resource has no types.
532     *
533     * @return array All types assocated with the resource (e.g. foaf:Person)
534     */
535    public function types()
536    {
537        $this->checkHasGraph();
538        return $this->graph->types($this->uri);
539    }
540
541    /** Get a single type for a resource.
542     *
543     * The type will be a shortened URI as a string.
544     * If the resource has multiple types then the type returned
545     * may be arbitrary.
546     * This method will return null if the resource has no type.
547     *
548     * @return string A type assocated with the resource (e.g. foaf:Person)
549     */
550    public function type()
551    {
552        $this->checkHasGraph();
553        return $this->graph->type($this->uri);
554    }
555
556    /** Get a single type for a resource, as a resource.
557     *
558     * The type will be returned as an EasyRdf_Resource.
559     * If the resource has multiple types then the type returned
560     * may be arbitrary.
561     * This method will return null if the resource has no type.
562     *
563     * @return EasyRdf_Resource A type assocated with the resource.
564     */
565    public function typeAsResource()
566    {
567        $this->checkHasGraph();
568        return $this->graph->typeAsResource($this->uri);
569    }
570
571    /**
572     * Get a list of types for a resource, as Resources.
573     *
574     * @return EasyRdf_Resource[]
575     * @throws EasyRdf_Exception
576     */
577    public function typesAsResources()
578    {
579        $this->checkHasGraph();
580        return $this->graph->typesAsResources($this->uri);
581    }
582
583    /** Check if a resource is of the specified type
584     *
585     * @param  string  $type The type to check (e.g. foaf:Person)
586     * @return boolean       True if resource is of specified type.
587     */
588    public function isA($type)
589    {
590        $this->checkHasGraph();
591        return $this->graph->isA($this->uri, $type);
592    }
593
594    /** Add one or more rdf:type properties to the resource
595     *
596     * @param  string  $types    One or more types to add (e.g. foaf:Person)
597     * @return integer           The number of types added
598     */
599    public function addType($types)
600    {
601        $this->checkHasGraph();
602        return $this->graph->addType($this->uri, $types);
603    }
604
605    /** Change the rdf:type property for the resource
606     *
607     * Note that the PHP class of the resource will not change.
608     *
609     * @param  string  $type     The new type (e.g. foaf:Person)
610     * @return integer           The number of types added
611     */
612    public function setType($type)
613    {
614        $this->checkHasGraph();
615        return $this->graph->setType($this->uri, $type);
616    }
617
618    /** Get the primary topic of this resource.
619     *
620     * Returns null if no primary topic is available.
621     *
622     * @return EasyRdf_Resource The primary topic of this resource.
623     */
624    public function primaryTopic()
625    {
626        $this->checkHasGraph();
627        return $this->graph->primaryTopic($this->uri);
628    }
629
630    /** Get a human readable label for this resource
631     *
632     * This method will check a number of properties for the resource
633     * (in the order: skos:prefLabel, rdfs:label, foaf:name, dc:title)
634     * and return an approriate first that is available. If no label
635     * is available then it will return null.
636     *
637     * @return string A label for the resource.
638     */
639    public function label($lang = null)
640    {
641        $this->checkHasGraph();
642        return $this->graph->label($this->uri, $lang);
643    }
644
645    /** Return a human readable view of the resource and its properties
646     *
647     * This method is intended to be a debugging aid and will
648     * print a resource and its properties.
649     *
650     * @param  string $format   Either 'html' or 'text'
651     * @return string
652     */
653    public function dump($format = 'html')
654    {
655        $this->checkHasGraph();
656        return $this->graph->dumpResource($this->uri, $format);
657    }
658
659    /** Magic method to get a property of a resource
660     *
661     * Note that only properties in the default namespace can be accessed in this way.
662     *
663     * Example:
664     *   $value = $resource->title;
665     *
666     * @see EasyRdf_Namespace::setDefault()
667     * @param  string $name The name of the property
668     * @return string       A single value for the named property
669     */
670    public function __get($name)
671    {
672        return $this->graph->get($this->uri, $name);
673    }
674
675    /** Magic method to set the value for a property of a resource
676     *
677     * Note that only properties in the default namespace can be accessed in this way.
678     *
679     * Example:
680     *   $resource->title = 'Title';
681     *
682     * @see EasyRdf_Namespace::setDefault()
683     * @param  string $name The name of the property
684     * @param  string $value The value for the property
685     */
686    public function __set($name, $value)
687    {
688        return $this->graph->set($this->uri, $name, $value);
689    }
690
691    /** Magic method to check if a property exists
692     *
693     * Note that only properties in the default namespace can be accessed in this way.
694     *
695     * Example:
696     *   if (isset($resource->title)) { blah(); }
697     *
698     * @see EasyRdf_Namespace::setDefault()
699     * @param string $name The name of the property
700     */
701    public function __isset($name)
702    {
703        return $this->graph->hasProperty($this->uri, $name);
704    }
705
706    /** Magic method to delete a property of the resource
707     *
708     * Note that only properties in the default namespace can be accessed in this way.
709     *
710     * Example:
711     *   unset($resource->title);
712     *
713     * @see EasyRdf_Namespace::setDefault()
714     * @param string $name The name of the property
715     */
716    public function __unset($name)
717    {
718        return $this->graph->delete($this->uri, $name);
719    }
720}
721