Minor dependency updates
[yaffs-website] / vendor / easyrdf / easyrdf / lib / EasyRdf / Sparql / Result.php
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 for returned for SPARQL SELECT and ASK query responses.
40  *
41  * @package    EasyRdf
42  * @copyright  Copyright (c) 2009-2013 Nicholas J Humfrey
43  * @license    http://www.opensource.org/licenses/bsd-license.php
44  */
45 class EasyRdf_Sparql_Result extends ArrayIterator
46 {
47     private $type = null;
48     private $boolean = null;
49
50     private $ordered = null;
51     private $distinct = null;
52     private $fields = array();
53
54     /** A constant for the SPARQL Query Results XML Format namespace */
55     const SPARQL_XML_RESULTS_NS = 'http://www.w3.org/2005/sparql-results#';
56
57     /** Create a new SPARQL Result object
58      *
59      * You should not normally need to create a SPARQL result
60      * object directly - it will be constructed automatically
61      * for you by EasyRdf_Sparql_Client.
62      *
63      * @param string $data      The SPARQL result body
64      * @param string $mimeType  The MIME type of the result
65      */
66     public function __construct($data, $mimeType)
67     {
68         if ($mimeType == 'application/sparql-results+xml') {
69             return $this->parseXml($data);
70         } elseif ($mimeType == 'application/sparql-results+json') {
71             return $this->parseJson($data);
72         } else {
73             throw new EasyRdf_Exception(
74                 "Unsupported SPARQL Query Results format: $mimeType"
75             );
76         }
77     }
78
79     /** Get the query result type (boolean/bindings)
80      *
81      * ASK queries return a result of type 'boolean'.
82      * SELECT query return a result of type 'bindings'.
83      *
84      * @return string The query result type.
85      */
86     public function getType()
87     {
88         return $this->type;
89     }
90
91     /** Return the boolean value of the query result
92      *
93      * If the query was of type boolean then this method will
94      * return either true or false. If the query was of some other
95      * type then this method will return null.
96      *
97      * @return boolean The result of the query.
98      */
99     public function getBoolean()
100     {
101         return $this->boolean;
102     }
103
104     /** Return true if the result of the query was true.
105      *
106      * @return boolean True if the query result was true.
107      */
108     public function isTrue()
109     {
110         return $this->boolean == true;
111     }
112
113     /** Return false if the result of the query was false.
114      *
115      * @return boolean True if the query result was false.
116      */
117     public function isFalse()
118     {
119         return $this->boolean == false;
120     }
121
122     /** Return the number of fields in a query result of type bindings.
123      *
124      * @return integer The number of fields.
125      */
126     public function numFields()
127     {
128         return count($this->fields);
129     }
130
131     /** Return the number of rows in a query result of type bindings.
132      *
133      * @return integer The number of rows.
134      */
135     public function numRows()
136     {
137         return count($this);
138     }
139
140     /** Get the field names in a query result of type bindings.
141      *
142      * @return array The names of the fields in the result.
143      */
144     public function getFields()
145     {
146         return $this->fields;
147     }
148
149     /** Return a human readable view of the query result.
150      *
151      * This method is intended to be a debugging aid and will
152      * return a pretty-print view of the query result.
153      *
154      * @param  string  $format  Either 'text' or 'html'
155      */
156     public function dump($format = 'html')
157     {
158         if ($this->type == 'bindings') {
159             $result = '';
160             if ($format == 'html') {
161                 $result .= "<table class='sparql-results' style='border-collapse:collapse'>";
162                 $result .= "<tr>";
163                 foreach ($this->fields as $field) {
164                     $result .= "<th style='border:solid 1px #000;padding:4px;".
165                                "vertical-align:top;background-color:#eee;'>".
166                                "?$field</th>";
167                 }
168                 $result .= "</tr>";
169                 foreach ($this as $row) {
170                     $result .= "<tr>";
171                     foreach ($this->fields as $field) {
172                         if (isset($row->$field)) {
173                             $result .= "<td style='border:solid 1px #000;padding:4px;".
174                                        "vertical-align:top'>".
175                                        $row->$field->dumpValue($format)."</td>";
176                         } else {
177                             $result .= "<td>&nbsp;</td>";
178                         }
179                     }
180                     $result .= "</tr>";
181                 }
182                 $result .= "</table>";
183             } else {
184                 // First calculate the width of each comment
185                 $colWidths = array();
186                 foreach ($this->fields as $field) {
187                     $colWidths[$field] = strlen($field);
188                 }
189
190                 $textData = array();
191                 foreach ($this as $row) {
192                     $textRow = array();
193                     foreach ($row as $k => $v) {
194                         $textRow[$k] = $v->dumpValue('text');
195                         $width = strlen($textRow[$k]);
196                         if ($colWidths[$k] < $width) {
197                             $colWidths[$k] = $width;
198                         }
199                     }
200                     $textData[] = $textRow;
201                 }
202
203                 // Create a horizontal rule
204                 $hr = "+";
205                 foreach ($colWidths as $k => $v) {
206                     $hr .= "-".str_repeat('-', $v).'-+';
207                 }
208
209                 // Output the field names
210                 $result .= "$hr\n|";
211                 foreach ($this->fields as $field) {
212                     $result .= ' '.str_pad("?$field", $colWidths[$field]).' |';
213                 }
214
215                 // Output each of the rows
216                 $result .= "\n$hr\n";
217                 foreach ($textData as $textRow) {
218                     $result .= '|';
219                     foreach ($textRow as $k => $v) {
220                         $result .= ' '.str_pad($v, $colWidths[$k]).' |';
221                     }
222                     $result .= "\n";
223                 }
224                 $result .= "$hr\n";
225
226             }
227             return $result;
228         } elseif ($this->type == 'boolean') {
229             $str = ($this->boolean ? 'true' : 'false');
230             if ($format == 'html') {
231                 return "<p>Result: <span style='font-weight:bold'>$str</span></p>";
232             } else {
233                 return "Result: $str";
234             }
235         } else {
236             throw new EasyRdf_Exception(
237                 "Failed to dump SPARQL Query Results format, unknown type: ". $this->type
238             );
239         }
240     }
241
242     /** Create a new EasyRdf_Resource or EasyRdf_Literal depending
243      *  on the type of data passed in.
244      *
245      * @ignore
246      */
247     protected function newTerm($data)
248     {
249         switch($data['type']) {
250             case 'bnode':
251                 return new EasyRdf_Resource('_:'.$data['value']);
252             case 'uri':
253                 return new EasyRdf_Resource($data['value']);
254             case 'literal':
255             case 'typed-literal':
256                 return EasyRdf_Literal::create($data);
257             default:
258                 throw new EasyRdf_Exception(
259                     "Failed to parse SPARQL Query Results format, unknown term type: ".
260                     $data['type']
261                 );
262         }
263     }
264
265     /** Parse a SPARQL result in the XML format into the object.
266      *
267      * @ignore
268      */
269     protected function parseXml($data)
270     {
271         $doc = new DOMDocument();
272         $doc->loadXML($data);
273
274         # Check for valid root node.
275         if ($doc->hasChildNodes() == false or
276             $doc->childNodes->length != 1 or
277             $doc->firstChild->nodeName != 'sparql' or
278             $doc->firstChild->namespaceURI != self::SPARQL_XML_RESULTS_NS) {
279             throw new EasyRdf_Exception(
280                 "Incorrect root node in SPARQL XML Query Results format"
281             );
282         }
283
284         # Is it the result of an ASK query?
285         $boolean = $doc->getElementsByTagName('boolean');
286         if ($boolean->length) {
287             $this->type = 'boolean';
288             $value = $boolean->item(0)->nodeValue;
289             $this->boolean = $value == 'true' ? true : false;
290             return;
291         }
292
293         # Get a list of variables from the header
294         $head = $doc->getElementsByTagName('head');
295         if ($head->length) {
296             $variables = $head->item(0)->getElementsByTagName('variable');
297             foreach ($variables as $variable) {
298                 $this->fields[] = $variable->getAttribute('name');
299             }
300         }
301
302         # Is it the result of a SELECT query?
303         $resultstag = $doc->getElementsByTagName('results');
304         if ($resultstag->length) {
305             $this->type = 'bindings';
306             $results = $resultstag->item(0)->getElementsByTagName('result');
307             foreach ($results as $result) {
308                 $bindings = $result->getElementsByTagName('binding');
309                 $t = new stdClass();
310                 foreach ($bindings as $binding) {
311                     $key = $binding->getAttribute('name');
312                     foreach ($binding->childNodes as $node) {
313                         if ($node->nodeType != XML_ELEMENT_NODE) {
314                             continue;
315                         }
316                         $t->$key = $this->newTerm(
317                             array(
318                                 'type' => $node->nodeName,
319                                 'value' => $node->nodeValue,
320                                 'lang' => $node->getAttribute('xml:lang'),
321                                 'datatype' => $node->getAttribute('datatype')
322                             )
323                         );
324                         break;
325                     }
326                 }
327                 $this[] = $t;
328             }
329             return $this;
330         }
331
332         throw new EasyRdf_Exception(
333             "Failed to parse SPARQL XML Query Results format"
334         );
335     }
336
337     /** Parse a SPARQL result in the JSON format into the object.
338      *
339      * @ignore
340      */
341     protected function parseJson($data)
342     {
343         // Decode JSON to an array
344         $data = json_decode($data, true);
345
346         if (isset($data['boolean'])) {
347             $this->type = 'boolean';
348             $this->boolean = $data['boolean'];
349         } elseif (isset($data['results'])) {
350             $this->type = 'bindings';
351             if (isset($data['head']['vars'])) {
352                 $this->fields = $data['head']['vars'];
353             }
354
355             foreach ($data['results']['bindings'] as $row) {
356                 $t = new stdClass();
357                 foreach ($row as $key => $value) {
358                     $t->$key = $this->newTerm($value);
359                 }
360                 $this[] = $t;
361             }
362         } else {
363             throw new EasyRdf_Exception(
364                 "Failed to parse SPARQL JSON Query Results format"
365             );
366         }
367     }
368
369     /** Magic method to return value of the result to string
370      *
371      * If this is a boolean result then it will return 'true' or 'false'.
372      * If it is a bindings type, then it will dump as a text based table.
373      *
374      * @return string A string representation of the result.
375      */
376     public function __toString()
377     {
378         if ($this->type == 'boolean') {
379             return $this->boolean ? 'true' : 'false';
380         } else {
381             return $this->dump('text');
382         }
383     }
384 }