Sparql.php
Go to the documentation of this file.
00001 <?php 00002 00005 00026 class Sparql extends WebService 00027 { 00029 private $db; 00030 00032 private $conneg; 00033 00035 private $dtdURL; 00036 00038 private $query = ""; 00039 00041 private $dataset = ""; 00042 00044 private $requester_ip = ""; 00045 00047 private $limit = ""; 00048 00050 private $offset = ""; 00051 00053 private $registered_ip = ""; 00054 00056 private $sparqlContent = ""; 00057 00059 private $instanceRecordsObjectLiteral = array(); 00060 00062 private $instanceRecordsObjectResource = array(); 00063 00065 private $namespaces = 00066 array ("http://www.w3.org/2002/07/owl#" => "owl", "http://www.w3.org/1999/02/22-rdf-syntax-ns#" => "rdf", 00067 "http://www.w3.org/2000/01/rdf-schema#" => "rdfs", "http://purl.org/ontology/wsf#" => "wsf"); 00068 00070 private $isConstructQuery = FALSE; 00071 00073 private $isDescribeQuery = FALSE; 00074 00076 public static $supportedSerializations = 00077 array ("application/rdf+json", "text/rdf+n3", "application/json", "text/xml", "application/sparql-results+xml", "application/sparql-results+json", 00078 "text/html", "application/rdf+xml", "application/rdf+n3", "application/*", "text/plain", "text/*", "*/*"); 00079 00081 private $errorMessenger = 00082 '{ 00083 "ws": "/ws/sparql/", 00084 "_200": { 00085 "id": "WS-SPARQL-200", 00086 "level": "Warning", 00087 "name": "No query specified for this request", 00088 "description": "No query specified for this request" 00089 }, 00090 "_201": { 00091 "id": "WS-SPARQL-201", 00092 "level": "Warning", 00093 "name": "No dataset specified for this request", 00094 "description": "No dataset specified for this request" 00095 }, 00096 "_202": { 00097 "id": "WS-SPARQL-202", 00098 "level": "Warning", 00099 "name": "The maximum number of records returned within the same slice is 2000. Use multiple queries with the OFFSET parameter to build-up the entire resultset.", 00100 "description": "The maximum number of records returned within the same slice is 2000. Use multiple queries with the OFFSET parameter to build-up the entire resultset." 00101 }, 00102 "_203": { 00103 "id": "WS-SPARQL-203", 00104 "level": "Warning", 00105 "name": "SPARUL not permitted.", 00106 "description": "No SPARUL queries are permitted for this sparql endpoint." 00107 }, 00108 "_204": { 00109 "id": "WS-SPARQL-204", 00110 "level": "Warning", 00111 "name": "CONSTRUCT not permitted.", 00112 "description": "The SPARQL CONSTRUCT clause is not permitted for this sparql endpoint. Please change you mime type if you want to get the resultset in a specific format." 00113 }, 00114 "_205": { 00115 "id": "WS-SPARQL-205", 00116 "level": "Warning", 00117 "name": "GRAPH not permitted without FROM NAMED clauses.", 00118 "description": "The SPARQL GRAPH clause is not permitted for this sparql endpoint. GRAPH clauses are only permitted when you bound your SPARQL query using one, or a series of FROM NAMED clauses." 00119 }, 00120 "_206": { 00121 "id": "WS-SPARQL-206", 00122 "level": "Warning", 00123 "name": "Dataset not accessible.", 00124 "description": "You don\' have access to the dataset URI you specified in the dataset parameter of this query." 00125 }, 00126 "_300": { 00127 "id": "WS-SPARQL-300", 00128 "level": "Warning", 00129 "name": "Connection to the sparql endpoint failed", 00130 "description": "Connection to the sparql endpoint failed" 00131 }, 00132 "_301": { 00133 "id": "WS-SPARQL-301", 00134 "level": "Notice", 00135 "name": "No instance records found", 00136 "description": "No instance records found for this query" 00137 } 00138 }'; 00139 00140 00159 function __construct($query, $dataset, $limit, $offset, $registered_ip, $requester_ip) 00160 { 00161 parent::__construct(); 00162 00163 $this->db = new DB_Virtuoso($this->db_username, $this->db_password, $this->db_dsn, $this->db_host); 00164 00165 $this->query = $query; 00166 $this->limit = $limit; 00167 $this->offset = $offset; 00168 $this->dataset = $dataset; 00169 $this->requester_ip = $requester_ip; 00170 00171 if($registered_ip == "") 00172 { 00173 $this->registered_ip = $requester_ip; 00174 } 00175 else 00176 { 00177 $this->registered_ip = $registered_ip; 00178 } 00179 00180 if(strtolower(substr($this->registered_ip, 0, 4)) == "self") 00181 { 00182 $pos = strpos($this->registered_ip, "::"); 00183 00184 if($pos !== FALSE) 00185 { 00186 $account = substr($this->registered_ip, $pos + 2, strlen($this->registered_ip) - ($pos + 2)); 00187 00188 $this->registered_ip = $requester_ip . "::" . $account; 00189 } 00190 else 00191 { 00192 $this->registered_ip = $requester_ip; 00193 } 00194 } 00195 00196 $this->uri = $this->wsf_base_url . "/wsf/ws/sparql/"; 00197 $this->title = "Sparql Web Service"; 00198 $this->crud_usage = new CrudUsage(FALSE, TRUE, FALSE, FALSE); 00199 $this->endpoint = $this->wsf_base_url . "/ws/sparql/"; 00200 00201 $this->dtdURL = "sparql/sparql.dtd"; 00202 00203 $this->errorMessenger = json_decode($this->errorMessenger); 00204 } 00205 00206 function __destruct() 00207 { 00208 parent::__destruct(); 00209 00210 if(isset($this->db)) 00211 { 00212 @$this->db->close(); 00213 } 00214 } 00215 00228 protected function validateQuery() 00229 { 00230 // Validating the access of the dataset specified as input parameter if defined. 00231 if($this->dataset != "") 00232 { 00233 $ws_av = new AuthValidator($this->registered_ip, $this->dataset, $this->uri); 00234 00235 $ws_av->pipeline_conneg("*/*", $this->conneg->getAcceptCharset(), $this->conneg->getAcceptEncoding(), 00236 $this->conneg->getAcceptLanguage()); 00237 00238 $ws_av->process(); 00239 00240 if($ws_av->pipeline_getResponseHeaderStatus() != 200) 00241 { 00242 $this->conneg->setStatus($ws_av->pipeline_getResponseHeaderStatus()); 00243 $this->conneg->setStatusMsg($ws_av->pipeline_getResponseHeaderStatusMsg()); 00244 $this->conneg->setStatusMsgExt($ws_av->pipeline_getResponseHeaderStatusMsgExt()); 00245 $this->conneg->setError($ws_av->pipeline_getError()->id, $ws_av->pipeline_getError()->webservice, 00246 $ws_av->pipeline_getError()->name, $ws_av->pipeline_getError()->description, 00247 $ws_av->pipeline_getError()->debugInfo, $ws_av->pipeline_getError()->level); 00248 00249 return; 00250 } 00251 } 00252 } 00253 00264 public function pipeline_getError() { return ($this->conneg->error); } 00265 00266 00277 public function pipeline_getResultset() 00278 { 00279 $labelProperties = 00280 array (Namespaces::$dcterms . "title", Namespaces::$foaf . "name", Namespaces::$foaf . "givenName", 00281 Namespaces::$foaf . "family_name", Namespaces::$rdfs . "label", Namespaces::$skos_2004 . "prefLabel", 00282 Namespaces::$skos_2004 . "altLabel", Namespaces::$skos_2008 . "prefLabel", 00283 Namespaces::$skos_2008 . "altLabel"); 00284 00285 $xml = new ProcessorXML(); 00286 00287 // Creation of the RESULTSET 00288 $resultset = $xml->createResultset(); 00289 00290 // Creation of the prefixes elements. 00291 $void = $xml->createPrefix("owl", "http://www.w3.org/2002/07/owl#"); 00292 $resultset->appendChild($void); 00293 $rdf = $xml->createPrefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); 00294 $resultset->appendChild($rdf); 00295 $dcterms = $xml->createPrefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#"); 00296 $resultset->appendChild($dcterms); 00297 $dcterms = $xml->createPrefix("wsf", "http://purl.org/ontology/wsf#"); 00298 $resultset->appendChild($dcterms); 00299 00300 $subject; 00301 00302 foreach($this->instanceRecordsObjectResource as $uri => $result) 00303 { 00304 // Assigning types 00305 if(isset($result["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"])) 00306 { 00307 foreach($result["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"] as $key => $type) 00308 { 00309 if($key > 0) 00310 { 00311 $pred = $xml->createPredicate("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"); 00312 $object = $xml->createObject("", $type); 00313 $pred->appendChild($object); 00314 $subject->appendChild($pred); 00315 } 00316 else 00317 { 00318 $subject = $xml->createSubject($type, $uri); 00319 } 00320 } 00321 } 00322 else 00323 { 00324 $subject = $xml->createSubject("http://www.w3.org/2002/07/owl#Thing", $uri); 00325 } 00326 00327 // Assigning object resource properties 00328 foreach($result as $property => $values) 00329 { 00330 if($property != "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") 00331 { 00332 foreach($values as $value) 00333 { 00334 $label = ""; 00335 00336 foreach($labelProperties as $labelProperty) 00337 { 00338 if($this->instanceRecordsObjectLiteral[$value]) 00339 { 00340 // The object resource is part of the resultset 00341 // This mainly occurs when we export complete datasets 00342 00343 if(isset($this->instanceRecordsObjectLiteral[$value][$labelProperty])) 00344 { 00345 $label = $this->instanceRecordsObjectLiteral[$value][$labelProperty][0]; 00346 break; 00347 } 00348 } 00349 else 00350 { 00351 // The object resource is not part of the resultset 00352 // In the future, we can send another sparql query to get its label. 00353 } 00354 } 00355 00356 $pred = $xml->createPredicate($property); 00357 $object = $xml->createObject("", $value, ($label != "" ? $label : "")); 00358 $pred->appendChild($object); 00359 00360 $subject->appendChild($pred); 00361 } 00362 } 00363 } 00364 00365 // Assigning object literal properties 00366 if(isset($this->instanceRecordsObjectLiteral[$uri])) 00367 { 00368 foreach($this->instanceRecordsObjectLiteral[$uri] as $property => $values) 00369 { 00370 if($property != "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") 00371 { 00372 foreach($values as $value) 00373 { 00374 $pred = $xml->createPredicate($property); 00375 $object = $xml->createObjectContent($value); 00376 $pred->appendChild($object); 00377 $subject->appendChild($pred); 00378 } 00379 } 00380 } 00381 } 00382 00383 $resultset->appendChild($subject); 00384 } 00385 00386 return ($this->injectDoctype($xml->saveXML($resultset))); 00387 00388 } 00389 00402 public function injectDoctype($xmlDoc) 00403 { 00404 $posHeader = strpos($xmlDoc, '"?>') + 3; 00405 $xmlDoc = substr($xmlDoc, 0, $posHeader) 00406 . "\n<!DOCTYPE resultset PUBLIC \"-//Structured Dynamics LLC//SPARQL DTD 0.1//EN\" \"" . $this->dtdBaseURL 00407 . $this->dtdURL . "\">" . substr($xmlDoc, $posHeader, strlen($xmlDoc) - $posHeader); 00408 00409 return ($xmlDoc); 00410 } 00411 00430 public function ws_conneg($accept, $accept_charset, $accept_encoding, $accept_language) 00431 { 00432 $this->conneg = 00433 new Conneg($accept, $accept_charset, $accept_encoding, $accept_language, Sparql::$supportedSerializations); 00434 00435 // Validate query 00436 $this->validateQuery(); 00437 00438 // If the query is still valid 00439 if($this->conneg->getStatus() == 200) 00440 { 00441 // Check for errors 00442 if($this->query == "") 00443 { 00444 $this->conneg->setStatus(400); 00445 $this->conneg->setStatusMsg("Bad Request"); 00446 $this->conneg->setStatusMsgExt($this->errorMessenger->_200->name); 00447 $this->conneg->setError($this->errorMessenger->_200->id, $this->errorMessenger->ws, 00448 $this->errorMessenger->_200->name, $this->errorMessenger->_200->description, "", 00449 $this->errorMessenger->_200->level); 00450 00451 return; 00452 } 00453 00454 if($this->limit > 2000) 00455 { 00456 $this->conneg->setStatus(400); 00457 $this->conneg->setStatusMsg("Bad Request"); 00458 $this->conneg->setStatusMsgExt($this->errorMessenger->_202->name); 00459 $this->conneg->setError($this->errorMessenger->_202->id, $this->errorMessenger->ws, 00460 $this->errorMessenger->_202->name, $this->errorMessenger->_202->description, "", 00461 $this->errorMessenger->_202->level); 00462 00463 return; 00464 } 00465 } 00466 } 00467 00486 public function pipeline_conneg($accept, $accept_charset, $accept_encoding, $accept_language) 00487 { $this->ws_conneg($accept, $accept_charset, $accept_encoding, $accept_language); } 00488 00499 public function pipeline_getResponseHeaderStatus() { return $this->conneg->getStatus(); } 00500 00511 public function pipeline_getResponseHeaderStatusMsg() { return $this->conneg->getStatusMsg(); } 00512 00525 public function pipeline_getResponseHeaderStatusMsgExt() { return $this->conneg->getStatusMsgExt(); } 00526 00539 private function getNamespace($uri) 00540 { 00541 $pos = strrpos($uri, "#"); 00542 00543 if($pos !== FALSE) 00544 { 00545 return array (substr($uri, 0, $pos) . "#", substr($uri, $pos + 1, strlen($uri) - ($pos + 1))); 00546 } 00547 else 00548 { 00549 $pos = strrpos($uri, "/"); 00550 00551 if($pos !== FALSE) 00552 { 00553 return array (substr($uri, 0, $pos) . "/", substr($uri, $pos + 1, strlen($uri) - ($pos + 1))); 00554 } 00555 else 00556 { 00557 $pos = strpos($uri, ":"); 00558 00559 if($pos !== FALSE) 00560 { 00561 $nsUri = explode(":", $uri, 2); 00562 00563 foreach($this->namespaces as $uri2 => $prefix2) 00564 { 00565 $uri2 = urldecode($uri2); 00566 00567 if($prefix2 == $nsUri[0]) 00568 { 00569 return (array ($uri2, $nsUri[1])); 00570 } 00571 } 00572 00573 return explode(":", $uri, 2); 00574 } 00575 } 00576 } 00577 00578 return (FALSE); 00579 } 00580 00581 00592 public function pipeline_serialize() 00593 { 00594 $rdf_part = ""; 00595 00596 switch($this->conneg->getMime()) 00597 { 00598 case "application/json": 00599 $json_part = ""; 00600 $xml = new ProcessorXML(); 00601 $xml->loadXML($this->pipeline_getResultset()); 00602 00603 $subjects = $xml->getSubjects(); 00604 00605 $nsId = 0; 00606 00607 foreach($subjects as $subject) 00608 { 00609 $subjectURI = $xml->getURI($subject); 00610 $subjectType = $xml->getType($subject); 00611 00612 $ns = $this->getNamespace($subjectType); 00613 00614 if(!isset($this->namespaces[$ns[0]])) 00615 { 00616 $this->namespaces[$ns[0]] = "ns" . $nsId; 00617 $nsId++; 00618 } 00619 00620 $json_part .= " { \n"; 00621 $json_part .= " \"uri\": \"" . parent::jsonEncode($subjectURI) . "\", \n"; 00622 $json_part .= " \"type\": \"" . parent::jsonEncode($this->namespaces[$ns[0]] . ":" . $ns[1]) 00623 . "\", \n"; 00624 00625 $predicates = $xml->getPredicates($subject); 00626 00627 $nbPredicates = 0; 00628 00629 foreach($predicates as $predicate) 00630 { 00631 $objects = $xml->getObjects($predicate); 00632 00633 foreach($objects as $object) 00634 { 00635 $nbPredicates++; 00636 00637 if($nbPredicates == 1) 00638 { 00639 $json_part .= " \"predicates\": [ \n"; 00640 } 00641 00642 $objectType = $xml->getType($object); 00643 $predicateType = $xml->getType($predicate); 00644 00645 if($objectType == "rdfs:Literal") 00646 { 00647 $objectValue = $xml->getContent($object); 00648 00649 $ns = $this->getNamespace($predicateType); 00650 00651 if(!isset($this->namespaces[$ns[0]])) 00652 { 00653 $this->namespaces[$ns[0]] = "ns" . $nsId; 00654 $nsId++; 00655 } 00656 00657 $json_part .= " { \n"; 00658 $json_part .= " \"" . parent::jsonEncode($this->namespaces[$ns[0]] . ":" . $ns[1]) . "\": \"" 00659 . parent::jsonEncode($objectValue) . "\" \n"; 00660 $json_part .= " },\n"; 00661 } 00662 else 00663 { 00664 $objectURI = $xml->getURI($object); 00665 00666 $ns = $this->getNamespace($predicateType); 00667 00668 if(!isset($this->namespaces[$ns[0]])) 00669 { 00670 $this->namespaces[$ns[0]] = "ns" . $nsId; 00671 $nsId++; 00672 } 00673 00674 $json_part .= " { \n"; 00675 $json_part .= " \"" . parent::jsonEncode($this->namespaces[$ns[0]] . ":" . $ns[1]) 00676 . "\": { \n"; 00677 $json_part .= " \"uri\": \"" . parent::jsonEncode($objectURI) . "\",\n"; 00678 00679 // Check if there is a reification statement for this object. 00680 $reifies = $xml->getReificationStatementsByType($object, "wsf:objectLabel"); 00681 00682 $nbReification = 0; 00683 00684 foreach($reifies as $reify) 00685 { 00686 $nbReification++; 00687 00688 if($nbReification > 0) 00689 { 00690 $json_part .= " \"reifies\": [\n"; 00691 } 00692 00693 $json_part .= " { \n"; 00694 $json_part .= " \"type\": \"wsf:objectLabel\", \n"; 00695 $json_part .= " \"value\": \"" . parent::jsonEncode($xml->getValue($reify)) 00696 . "\" \n"; 00697 $json_part .= " },\n"; 00698 } 00699 00700 if($nbReification > 0) 00701 { 00702 $json_part = substr($json_part, 0, strlen($json_part) - 2) . "\n"; 00703 00704 $json_part .= " ]\n"; 00705 } 00706 else 00707 { 00708 $json_part = substr($json_part, 0, strlen($json_part) - 2) . "\n"; 00709 } 00710 00711 $json_part .= " } \n"; 00712 $json_part .= " },\n"; 00713 } 00714 } 00715 } 00716 00717 if(strlen($json_part) > 0) 00718 { 00719 $json_part = substr($json_part, 0, strlen($json_part) - 2) . "\n"; 00720 } 00721 00722 if($nbPredicates > 0) 00723 { 00724 $json_part .= " ]\n"; 00725 } 00726 00727 $json_part .= " },\n"; 00728 } 00729 00730 if(strlen($json_part) > 0) 00731 { 00732 $json_part = substr($json_part, 0, strlen($json_part) - 2) . "\n"; 00733 } 00734 00735 $json_header .= " \"prefixes\": [ \n"; 00736 $json_header .= " {\n"; 00737 00738 foreach($this->namespaces as $ns => $prefix) 00739 { 00740 $json_header .= " \"$prefix\": \"$ns\",\n"; 00741 } 00742 00743 if(strlen($json_header) > 0) 00744 { 00745 $json_header = substr($json_header, 0, strlen($json_header) - 2) . "\n"; 00746 } 00747 00748 $json_header .= " } \n"; 00749 $json_header .= " ],\n"; 00750 $json_header .= " \"resultset\": {\n"; 00751 $json_header .= " \"subject\": [\n"; 00752 $json_header .= $json_part; 00753 $json_header .= " ]\n"; 00754 $json_header .= " }\n"; 00755 00756 return ($json_header); 00757 break; 00758 00759 case "application/rdf+n3": 00760 00761 $xml = new ProcessorXML(); 00762 $xml->loadXML($this->pipeline_getResultset()); 00763 00764 $subjects = $xml->getSubjects(); 00765 00766 foreach($subjects as $subject) 00767 { 00768 $subjectURI = $xml->getURI($subject); 00769 $subjectType = $xml->getType($subject, FALSE); 00770 00771 $rdf_part .= "\n <$subjectURI> a <$subjectType> ;\n"; 00772 00773 $predicates = $xml->getPredicates($subject); 00774 00775 foreach($predicates as $predicate) 00776 { 00777 $objects = $xml->getObjects($predicate); 00778 00779 foreach($objects as $object) 00780 { 00781 $objectType = $xml->getType($object); 00782 $predicateType = $xml->getType($predicate, FALSE); 00783 $objectContent = $xml->getContent($object); 00784 00785 if($objectType == "rdfs:Literal") 00786 { 00787 $objectValue = $xml->getContent($object); 00788 $rdf_part .= " <$predicateType> \"\"\"" . str_replace(array( "\\" ), "\\\\", $objectValue) 00789 . "\"\"\" ;\n"; 00790 } 00791 else 00792 { 00793 $objectURI = $xml->getURI($object); 00794 $rdf_part .= " <$predicateType> <$objectURI> ;\n"; 00795 } 00796 } 00797 } 00798 00799 if(strlen($rdf_part) > 0) 00800 { 00801 $rdf_part = substr($rdf_part, 0, strlen($rdf_part) - 2) . ".\n"; 00802 } 00803 } 00804 00805 return ($rdf_part); 00806 break; 00807 00808 case "application/rdf+xml": 00809 $xml = new ProcessorXML(); 00810 $xml->loadXML($this->pipeline_getResultset()); 00811 00812 $subjects = $xml->getSubjects(); 00813 00814 $nsId = 0; 00815 00816 foreach($subjects as $subject) 00817 { 00818 $subjectURI = $xml->getURI($subject); 00819 $subjectType = $xml->getType($subject); 00820 00821 $ns1 = $this->getNamespace($subjectType); 00822 00823 if(!isset($this->namespaces[$ns1[0]])) 00824 { 00825 $this->namespaces[$ns1[0]] = "ns" . $nsId; 00826 $nsId++; 00827 } 00828 00829 $rdf_part .= "\n <" . $this->namespaces[$ns1[0]] . ":" . $ns1[1] . " rdf:about=\"". 00830 $this->xmlEncode($subjectURI)."\">\n"; 00831 00832 $predicates = $xml->getPredicates($subject); 00833 00834 foreach($predicates as $predicate) 00835 { 00836 $objects = $xml->getObjects($predicate); 00837 00838 foreach($objects as $object) 00839 { 00840 $objectType = $xml->getType($object); 00841 $predicateType = $xml->getType($predicate); 00842 00843 if($objectType == "rdfs:Literal") 00844 { 00845 $objectValue = $xml->getContent($object); 00846 00847 $ns = $this->getNamespace($predicateType); 00848 00849 if(!isset($this->namespaces[$ns[0]])) 00850 { 00851 $this->namespaces[$ns[0]] = "ns" . $nsId; 00852 $nsId++; 00853 } 00854 00855 $rdf_part .= " <" . $this->namespaces[$ns[0]] . ":" . $ns[1] . ">" 00856 . $this->xmlEncode($objectValue) . "</" . $this->namespaces[$ns[0]] . ":" . $ns[1] . ">\n"; 00857 } 00858 else 00859 { 00860 $objectURI = $xml->getURI($object); 00861 00862 $ns = $this->getNamespace($predicateType); 00863 00864 if(!isset($this->namespaces[$ns[0]])) 00865 { 00866 $this->namespaces[$ns[0]] = "ns" . $nsId; 00867 $nsId++; 00868 } 00869 00870 $rdf_part .= " <" . $this->namespaces[$ns[0]] . ":" . $ns[1] 00871 . " rdf:resource=\"".$this->xmlEncode($objectURI)."\" />\n"; 00872 } 00873 } 00874 } 00875 00876 $rdf_part .= " </" . $this->namespaces[$ns1[0]] . ":" . $ns1[1] . ">\n"; 00877 } 00878 00879 $rdf_header = "<rdf:RDF "; 00880 00881 foreach($this->namespaces as $ns => $prefix) 00882 { 00883 $rdf_header .= " xmlns:$prefix=\"$ns\""; 00884 } 00885 00886 $rdf_header .= ">\n\n"; 00887 00888 $rdf_part = $rdf_header . $rdf_part; 00889 00890 return ($rdf_part); 00891 break; 00892 00893 case "text/xml": 00894 case "application/sparql-results+xml": 00895 case "application/sparql-results+json": 00896 return $this->pipeline_getResultset(); 00897 break; 00898 } 00899 } 00900 00909 public function pipeline_serialize_reification() { } 00910 00921 public function ws_serialize() 00922 { 00923 if($this->conneg->getMime() == "application/sparql-results+xml" 00924 || $this->conneg->getMime() == "application/sparql-results+json" 00925 || $this->isDescribeQuery === TRUE 00926 || $this->isConstructQuery === TRUE) 00927 { 00928 return $this->sparqlContent; 00929 } 00930 else 00931 { 00932 switch($this->conneg->getMime()) 00933 { 00934 case "application/rdf+n3": 00935 $rdf_document = ""; 00936 $rdf_document .= "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"; 00937 $rdf_document .= "@prefix wsf: <http://purl.org/ontology/wsf#> .\n"; 00938 00939 $rdf_document .= $this->pipeline_serialize(); 00940 00941 $rdf_document .= $this->pipeline_serialize_reification(); 00942 00943 return $rdf_document; 00944 break; 00945 00946 case "application/rdf+xml": 00947 $rdf_document = ""; 00948 $rdf_document .= "<?xml version=\"1.0\"?>\n"; 00949 00950 $rdf_document .= $this->pipeline_serialize(); 00951 00952 $rdf_document .= $this->pipeline_serialize_reification(); 00953 00954 $rdf_document .= "</rdf:RDF>"; 00955 00956 return $rdf_document; 00957 break; 00958 00959 case "application/json": 00960 $json_document = ""; 00961 $json_document .= "{\n"; 00962 $json_document .= $this->pipeline_serialize(); 00963 $json_document .= "}"; 00964 00965 return ($json_document); 00966 break; 00967 00968 default: 00969 return $this->pipeline_getResultset(); 00970 break; 00971 } 00972 } 00973 } 00974 00987 public function ws_respond($content) 00988 { 00989 // First send the header of the request 00990 $this->conneg->respond(); 00991 00992 // second, send the content of the request 00993 00994 // Make sure there is no error. 00995 if($this->conneg->getStatus() == 200) 00996 { 00997 echo $content; 00998 } 00999 01000 $this->__destruct(); 01001 } 01002 01003 01012 public function process() 01013 { 01014 // Make sure there was no conneg error prior to this process call 01015 if($this->conneg->getStatus() == 200) 01016 { 01017 $ch = curl_init(); 01018 01019 // Normalize the query to remove the return carriers and line feeds 01020 // This is performed to help matching the regular expressions patterns. 01021 $this->query = str_replace(array("\r", "\n"), " ", $this->query); 01022 01023 // remove the possible starting "sparql" 01024 $this->query = preg_replace("/^[\s\t]*sparql[\s\t]*/Uim", "", $this->query); 01025 01026 // Check if there is a prolog to this SPARQL query. 01027 01028 // First check if there is a "base" declaration 01029 01030 preg_match("/^[\s\t]*base[\s\t]*<.*>/Uim", $this->query, $matches, PREG_OFFSET_CAPTURE); 01031 01032 $baseOffset = -1; 01033 if(count($matches) > 0) 01034 { 01035 $baseOffset = $matches[0][1] + strlen($matches[0][0]); 01036 } 01037 01038 // Second check for all possible "prefix" clauses 01039 preg_match_all("/[\s\t]*prefix[\s\t]*.*:.*<.*>/Uim", $this->query, $matches, PREG_OFFSET_CAPTURE); 01040 01041 $lastPrefixOffset = -1; 01042 01043 if(count($matches) > 0) 01044 { 01045 $lastPrefixOffset = $matches[0][count($matches[0]) - 1][1] + strlen($matches[0][count($matches[0]) - 1][0]); 01046 } 01047 01048 $prologEndOffset = -1; 01049 01050 if($lastPrefixOffset > -1) 01051 { 01052 $prologEndOffset = $lastPrefixOffset; 01053 } 01054 elseif($baseOffset > -1) 01055 { 01056 $prologEndOffset = $baseOffset; 01057 } 01058 01059 $noPrologQuery = $this->query; 01060 if($prologEndOffset != -1) 01061 { 01062 $noPrologQuery = substr($this->query, $prologEndOffset); 01063 } 01064 01065 // Now extract prefixes references 01066 $prefixes = array(); 01067 preg_match_all("/[\s\t]*prefix[\s\t]*(.*):(.*)<(.*)>/Uim", $this->query, $matches, PREG_OFFSET_CAPTURE); 01068 01069 if(count($matches[0]) > 0) 01070 { 01071 for($i = 0; $i < count($matches[1]); $i++) 01072 { 01073 $p = str_replace(array(" ", " "), "", $matches[1][$i][0]).":".str_replace(array(" ", " "), "", $matches[2][$i][0]); 01074 $iri = $matches[3][$i][0]; 01075 01076 $prefixes[$p] = $iri; 01077 } 01078 } 01079 01080 // Drop any SPARUL queries 01081 // Reference: http://www.w3.org/Submission/SPARQL-Update/ 01082 if(preg_match_all("/^[\s\t]*modify[\s\t]*/Uim",$noPrologQuery , $matches) > 0 || 01083 preg_match_all("/^[\s\t]*delete[\s\t]*/Uim", $noPrologQuery, $matches) > 0 || 01084 preg_match_all("/^[\s\t]*insert[\s\t]*/Uim", $noPrologQuery, $matches) > 0 || 01085 preg_match_all("/^[\s\t]*load[\s\t]*/Uim", $noPrologQuery, $matches) > 0 || 01086 preg_match_all("/^[\s\t]*clear[\s\t]*/Uim", $noPrologQuery, $matches) > 0 || 01087 preg_match_all("/^[\s\t]*create[\s\t]*/Uim", $noPrologQuery, $matches) > 0 || 01088 preg_match_all("/^[\s\t]*drop[\s\t]*/Uim", $noPrologQuery, $matches) > 0) 01089 { 01090 $this->conneg->setStatus(400); 01091 $this->conneg->setStatusMsg("Bad Request"); 01092 $this->conneg->setStatusMsgExt($this->errorMessenger->_203->name); 01093 $this->conneg->setError($this->errorMessenger->_203->id, $this->errorMessenger->ws, 01094 $this->errorMessenger->_203->name, $this->errorMessenger->_203->description, "", 01095 $this->errorMessenger->_203->level); 01096 01097 return; 01098 } 01099 01100 // Detect any CONSTRUCT clause 01101 $this->isConstructQuery = FALSE; 01102 if(preg_match_all("/^[\s\t]*construct[\s\t]*/Uim", $noPrologQuery, $matches) > 0) 01103 { 01104 $this->isConstructQuery = TRUE; 01105 /* 01106 $this->conneg->setStatus(400); 01107 $this->conneg->setStatusMsg("Bad Request"); 01108 $this->conneg->setStatusMsgExt($this->errorMessenger->_204->name); 01109 $this->conneg->setError($this->errorMessenger->_204->id, $this->errorMessenger->ws, 01110 $this->errorMessenger->_204->name, $this->errorMessenger->_204->description, "", 01111 $this->errorMessenger->_204->level); 01112 01113 return; 01114 */ 01115 } 01116 01117 // Drop any SPARQL query with a GRAPH clause which are not bound by one, or a series, of FROM NAMED clauses 01118 01119 if((preg_match_all("/[\s\t]*graph[\s\t]*</Uim", $noPrologQuery, $matches) > 0 || 01120 preg_match_all("/[\s\t]*graph[\s\t]*\?/Uim", $noPrologQuery, $matches) > 0 || 01121 preg_match_all("/[\s\t]*graph[\s\t]*\$/Uim", $noPrologQuery, $matches) > 0 || 01122 preg_match_all("/[\s\t]*graph[\s\t]*[a-zA-Z0-9\-_]*:/Uim", $noPrologQuery, $matches) > 0) && 01123 (preg_match_all("/([\s\t]*from[\s\t]*named[\s\t]*<(.*)>[\s\t]*)/Uim", $noPrologQuery, $matches) <= 0 && 01124 preg_match_all("/[\s\t]*(from[\s\t]*named)[\s\t]*([^\s\t<]*):(.*)[\s\t]*/Uim", $noPrologQuery, $matches) <= 0)) 01125 { 01126 $this->conneg->setStatus(400); 01127 $this->conneg->setStatusMsg("Bad Request"); 01128 $this->conneg->setStatusMsgExt($this->errorMessenger->_205->name); 01129 $this->conneg->setError($this->errorMessenger->_205->id, $this->errorMessenger->ws, 01130 $this->errorMessenger->_205->name, $this->errorMessenger->_205->description, "", 01131 $this->errorMessenger->_205->level); 01132 01133 return; 01134 } 01135 01136 $graphs = array(); 01137 01138 // Validate DESCRIBE query. 01139 // The only thing we have to check here, is to get the graph IRI if the DESCRIBE is immediately using 01140 // IRIRef clause. Possibilities are: 01141 // "DESCRIBE <test>" -- IRI_REF 01142 // "DESCRIBE a:" -- PrefixedName 01143 01144 $this->isDescribeQuery = FALSE; 01145 if(preg_match("/^[\s\t]*describe[\s\t]*/Uim", $noPrologQuery, $matches) > 0) 01146 { 01147 $this->isDescribeQuery = TRUE; 01148 } 01149 01150 preg_match_all("/^[\s\t]*describe[\s\t]*<(.*)>/Uim", $noPrologQuery, $matches); 01151 01152 if(count($matches[0]) > 0) 01153 { 01154 array_push($graphs, $matches[1][0]); 01155 } 01156 01157 preg_match_all("/^[\s\t]*describe[\s\t]*([^<\s\t]*):(.*)[\s\t]*/Uim", $noPrologQuery, $matches); 01158 01159 if(count($matches[0]) > 0) 01160 { 01161 for($i = 0; $i < count($matches[0]); $i++) 01162 { 01163 $p = $matches[1][$i].":"; 01164 01165 if(isset($prefixes[$p])) 01166 { 01167 $d = $prefixes[$p].$matches[2][$i]; 01168 array_push($graphs, $d); 01169 } 01170 } 01171 } 01172 01173 01174 // Get all the "from" and "from named" clauses so that we validate if the user has access to them. 01175 01176 // Check for the clauses that uses direct IRI_REF 01177 preg_match_all("/([\s\t]*from[\s\t]*<(.*)>[\s\t]*)/Uim", $noPrologQuery, $matches); 01178 01179 foreach($matches[2] as $match) 01180 { 01181 array_push($graphs, $match); 01182 } 01183 01184 preg_match_all("/([\s\t]*from[\s\t]*named[\s\t]*<(.*)>[\s\t]*)/Uim", $noPrologQuery, $matches); 01185 01186 foreach($matches[2] as $match) 01187 { 01188 array_push($graphs, $match); 01189 } 01190 01191 // Check for the clauses that uses PrefixedName 01192 01193 preg_match_all("/[\s\t]*(from|from[\s\t]*named)[\s\t]*([^\s\t<]*):(.*)[\s\t]*/Uim", $noPrologQuery, $matches); 01194 01195 if(count($matches[0]) > 0) 01196 { 01197 for($i = 0; $i < count($matches[0]); $i++) 01198 { 01199 $p = $matches[2][$i].":"; 01200 01201 if(isset($prefixes[$p])) 01202 { 01203 $d = $prefixes[$p].$matches[3][$i]; 01204 array_push($graphs, $d); 01205 } 01206 } 01207 } 01208 01209 01210 if($this->dataset == "" && count($graphs) <= 0) 01211 { 01212 $this->conneg->setStatus(400); 01213 $this->conneg->setStatusMsg("Bad Request"); 01214 $this->conneg->setStatusMsgExt($this->errorMessenger->_201->name); 01215 $this->conneg->setError($this->errorMessenger->_201->id, $this->errorMessenger->ws, 01216 $this->errorMessenger->_201->name, $this->errorMessenger->_201->description, "", 01217 $this->errorMessenger->_201->level); 01218 01219 return; 01220 } 01221 01222 // Validate all graphs of the query. If one of the graph is not accessible to the user, we just return 01223 // and error for this SPARQL query. 01224 foreach($graphs as $graph) 01225 { 01226 if(substr($graph, strlen($graph) - 12, 12) == "reification/") 01227 { 01228 $graph = substr($graph, 0, strlen($graph) - 12); 01229 } 01230 01231 $ws_av = new AuthValidator($this->registered_ip, $graph, $this->uri); 01232 01233 $ws_av->pipeline_conneg("*/*", $this->conneg->getAcceptCharset(), $this->conneg->getAcceptEncoding(), 01234 $this->conneg->getAcceptLanguage()); 01235 01236 $ws_av->process(); 01237 01238 if($ws_av->pipeline_getResponseHeaderStatus() != 200) 01239 { 01240 $this->conneg->setStatus($ws_av->pipeline_getResponseHeaderStatus()); 01241 $this->conneg->setStatusMsg($ws_av->pipeline_getResponseHeaderStatusMsg()); 01242 $this->conneg->setStatusMsgExt($ws_av->pipeline_getResponseHeaderStatusMsgExt()); 01243 $this->conneg->setError($ws_av->pipeline_getError()->id, $ws_av->pipeline_getError()->webservice, 01244 $ws_av->pipeline_getError()->name, $ws_av->pipeline_getError()->description, 01245 $ws_av->pipeline_getError()->debugInfo, $ws_av->pipeline_getError()->level); 01246 01247 return; 01248 } 01249 } 01250 01251 /* 01252 if registered_ip != requester_ip, this means that the query is sent by a registered system 01253 on the behalf of someone else. In this case, we want to make sure that that system 01254 (the one that send the actual query) has access to the same datasets. Otherwise, it means that 01255 it tries to personificate that registered_ip user. 01256 01257 Validate all graphs of the query. If one of the graph is not accessible to the syste, we just return 01258 and error for this SPARQL query. 01259 */ 01260 foreach($graphs as $graph) 01261 { 01262 if(substr($graph, strlen($graph) - 12, 12) == "reification/") 01263 { 01264 $graph = substr($graph, 0, strlen($graph) - 12); 01265 } 01266 01267 $ws_av = new AuthValidator($this->requester_ip, $graph, $this->uri); 01268 01269 $ws_av->pipeline_conneg("*/*", $this->conneg->getAcceptCharset(), $this->conneg->getAcceptEncoding(), 01270 $this->conneg->getAcceptLanguage()); 01271 01272 $ws_av->process(); 01273 01274 if($ws_av->pipeline_getResponseHeaderStatus() != 200) 01275 { 01276 $this->conneg->setStatus($ws_av->pipeline_getResponseHeaderStatus()); 01277 $this->conneg->setStatusMsg($ws_av->pipeline_getResponseHeaderStatusMsg()); 01278 $this->conneg->setStatusMsgExt($ws_av->pipeline_getResponseHeaderStatusMsgExt()); 01279 $this->conneg->setError($ws_av->pipeline_getError()->id, $ws_av->pipeline_getError()->webservice, 01280 $ws_av->pipeline_getError()->name, $ws_av->pipeline_getError()->description, 01281 $ws_av->pipeline_getError()->debugInfo, $ws_av->pipeline_getError()->level); 01282 01283 return; 01284 } 01285 } 01286 01287 // Determine the query format 01288 $queryFormat = ""; 01289 01290 if($this->conneg->getMime() == "application/sparql-results+json" || 01291 $this->conneg->getMime() == "application/sparql-results+xml" || 01292 $this->conneg->getMime() == "text/html" || 01293 $this->isDescribeQuery === TRUE || 01294 $this->isConstructQuery === TRUE) 01295 { 01296 $queryFormat = $this->conneg->getMime(); 01297 } 01298 elseif($this->conneg->getMime() == "text/xml" || $this->conneg->getMime() == "application/json" 01299 || $this->conneg->getMime() == "application/rdf+xml" || $this->conneg->getMime() == "application/rdf+n3") 01300 { 01301 $queryFormat = "application/sparql-results+xml"; 01302 } 01303 01304 01305 01306 // Add a limit to the query 01307 01308 // Disable limits and offset for now until we figure out what to do (not limit on triples, but resources) 01309 // $this->query .= " limit ".$this->limit." offset ".$this->offset; 01310 01311 curl_setopt($ch, CURLOPT_URL, 01312 $this->db_host . ":" . $this->triplestore_port . "/sparql?default-graph-uri=" . urlencode($this->dataset) . "&query=" 01313 . urlencode($this->query) . "&format=" . urlencode($queryFormat)); 01314 01315 //curl_setopt($ch, CURLOPT_HTTPHEADER, array( "Accept: " . $queryFormat )); 01316 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 01317 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 01318 curl_setopt($ch, CURLOPT_HEADER, TRUE); 01319 01320 $xml_data = curl_exec($ch); 01321 01322 $header = substr($xml_data, 0, strpos($xml_data, "\r\n\r\n")); 01323 01324 $data = 01325 substr($xml_data, strpos($xml_data, "\r\n\r\n") + 4, strlen($xml_data) - (strpos($xml_data, "\r\n\r\n") - 4)); 01326 01327 curl_close($ch); 01328 01329 // check returned message 01330 01331 $httpMsgNum = substr($header, 9, 3); 01332 $httpMsg = substr($header, 13, strpos($header, "\r\n") - 13); 01333 01334 if($httpMsgNum == "200") 01335 { 01336 $this->sparqlContent = $data; 01337 } 01338 else 01339 { 01340 $this->conneg->setStatus($httpMsgNum); 01341 $this->conneg->setStatusMsg($httpMsg); 01342 $this->conneg->setStatusMsgExt($this->errorMessenger->_300->name); 01343 $this->conneg->setError($this->errorMessenger->_300->id, $this->errorMessenger->ws, 01344 $this->errorMessenger->_300 > name, $this->errorMessenger->_300->description, $data, 01345 $this->errorMessenger->_300->level); 01346 01347 $this->sparqlContent = ""; 01348 return; 01349 } 01350 01351 // If a DESCRIBE query as been requested by the user, then we simply returns what is returned by 01352 // the triple store. We don't have any convertion to do here. 01353 if($this->isDescribeQuery === TRUE) 01354 { 01355 return; 01356 } 01357 01358 // If a CONSTRUCT query as been requested by the user, then we simply returns what is returned by 01359 // the triple store. We don't have any convertion to do here. 01360 if($this->isConstructQuery === TRUE) 01361 { 01362 return; 01363 } 01364 01365 if($this->conneg->getMime() == "text/xml" || $this->conneg->getMime() == "application/rdf+n3" 01366 || $this->conneg->getMime() == "application/rdf+xml" || $this->conneg->getMime() == "application/json") 01367 { 01368 // Read the XML file and populate the recordInstances variables 01369 01370 $xml = $this->xml2ary($this->sparqlContent); 01371 01372 if(isset($xml["sparql"]["_c"]["results"]["_c"]["result"])) 01373 { 01374 foreach($xml["sparql"]["_c"]["results"]["_c"]["result"] as $result) 01375 { 01376 $s = ""; 01377 $p = ""; 01378 $o = ""; 01379 01380 foreach($result["_c"]["binding"] as $binding) 01381 { 01382 $boundVariable = $binding["_a"]["name"]; 01383 01384 $keys = array_keys($binding["_c"]); 01385 01386 $boundType = $keys[0]; 01387 $boundValue = $binding["_c"][$boundType]["_v"]; 01388 01389 switch($boundVariable) 01390 { 01391 case "s": 01392 $s = $boundValue; 01393 break; 01394 01395 case "p": 01396 $p = $boundValue; 01397 break; 01398 01399 case "o": 01400 $o = $boundValue; 01401 break; 01402 } 01403 } 01404 01405 // process URI 01406 if($boundType == "uri") 01407 { 01408 if(!isset($this->instanceRecordsObjectResource[$s][$p])) 01409 { 01410 $this->instanceRecordsObjectResource[$s][$p] = array( $o ); 01411 } 01412 else 01413 { 01414 array_push($this->instanceRecordsObjectResource[$s][$p], $o); 01415 } 01416 } 01417 01418 // Process Literal 01419 if($boundType == "literal") 01420 { 01421 if(!isset($this->instanceRecordsObjectLiteral[$s][$p])) 01422 { 01423 $this->instanceRecordsObjectLiteral[$s][$p] = array( $o ); 01424 } 01425 else 01426 { 01427 array_push($this->instanceRecordsObjectLiteral[$s][$p], $o); 01428 } 01429 } 01430 01431 // Process BNode 01432 if($boundType == "bnode") 01433 { 01434 if(!isset($this->instanceRecordsObjectResource[$s][$p])) 01435 { 01436 $this->instanceRecordsObjectResource[$s][$p] = array( $o ); 01437 } 01438 else 01439 { 01440 array_push($this->instanceRecordsObjectResource[$s][$p], $o); 01441 } 01442 } 01443 } 01444 } 01445 01446 if(count($this->instanceRecordsObjectResource) <= 0) 01447 { 01448 $this->conneg->setStatus(400); 01449 $this->conneg->setStatusMsg("Bad Request"); 01450 $this->conneg->setStatusMsgExt($this->errorMessenger->_301->name); 01451 $this->conneg->setError($this->errorMessenger->_301->id, $this->errorMessenger->ws, 01452 $this->errorMessenger->_301->name, $this->errorMessenger->_301->description, "", 01453 $this->errorMessenger->_301->level); 01454 } 01455 } 01456 } 01457 } 01458 01459 /* 01460 Working with XML. Usage: 01461 $xml=xml2ary(file_get_contents('1.xml')); 01462 $link=&$xml['ddd']['_c']; 01463 $link['twomore']=$link['onemore']; 01464 // ins2ary(); // dot not insert a link, and arrays with links inside! 01465 echo ary2xml($xml); 01466 01467 from: http://mysrc.blogspot.com/2007/02/php-xml-to-array-and-backwards.html 01468 */ 01469 01470 // XML to Array 01471 private function xml2ary(&$string) 01472 { 01473 $parser = xml_parser_create(); 01474 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); 01475 xml_parse_into_struct($parser, $string, $vals, $index); 01476 xml_parser_free($parser); 01477 01478 $mnary = array(); 01479 $ary = &$mnary; 01480 01481 foreach($vals as $r) 01482 { 01483 $t = $r['tag']; 01484 01485 if($r['type'] == 'open') 01486 { 01487 if(isset($ary[$t])) 01488 { 01489 if(isset($ary[$t][0]))$ary[$t][] = array(); 01490 else $ary[$t] = array ($ary[$t], array()); 01491 $cv = &$ary[$t][count($ary[$t]) - 1]; 01492 } 01493 else $cv = &$ary[$t]; 01494 01495 if(isset($r['attributes'])) 01496 { 01497 foreach($r['attributes'] as $k => $v)$cv['_a'][$k] = $v; 01498 } 01499 $cv['_c'] = array(); 01500 $cv['_c']['_p'] = &$ary; 01501 $ary = &$cv['_c']; 01502 } 01503 elseif($r['type'] == 'complete') 01504 { 01505 if(isset($ary[$t])) 01506 { // same as open 01507 if(isset($ary[$t][0]))$ary[$t][] = array(); 01508 else $ary[$t] = array ($ary[$t], array()); 01509 $cv = &$ary[$t][count($ary[$t]) - 1]; 01510 } 01511 else $cv = &$ary[$t]; 01512 01513 if(isset($r['attributes'])) 01514 { 01515 foreach($r['attributes'] as $k => $v)$cv['_a'][$k] = $v; 01516 } 01517 $cv['_v'] = (isset($r['value']) ? $r['value'] : ''); 01518 } 01519 elseif($r['type'] == 'close') 01520 { 01521 $ary = &$ary['_p']; 01522 } 01523 } 01524 01525 $this->_del_p($mnary); 01526 return $mnary; 01527 } 01528 01529 // _Internal: Remove recursion in result array 01530 private function _del_p(&$ary) 01531 { 01532 foreach($ary as $k => $v) 01533 { 01534 if($k === '_p')unset($ary[$k]); 01535 elseif(is_array($ary[$k]))$this->_del_p($ary[$k]); 01536 } 01537 } 01538 01539 // Array to XML 01540 private function ary2xml($cary, $d = 0, $forcetag = '') 01541 { 01542 $res = array(); 01543 01544 foreach($cary as $tag => $r) 01545 { 01546 if(isset($r[0])) 01547 { 01548 $res[] = ary2xml($r, $d, $tag); 01549 } 01550 else 01551 { 01552 if($forcetag)$tag = $forcetag; 01553 $sp = str_repeat("\t", $d); 01554 $res[] = "$sp<$tag"; 01555 01556 if(isset($r['_a'])) 01557 { 01558 foreach($r['_a'] as $at => $av)$res[] = " $at=\"$av\""; 01559 } 01560 $res[] = ">" . ((isset($r['_c'])) ? "\n" : ''); 01561 01562 if(isset($r['_c']))$res[] = ary2xml($r['_c'], $d + 1); 01563 elseif(isset($r['_v']))$res[] = $r['_v']; 01564 $res[] = (isset($r['_c']) ? $sp : '') . "</$tag>\n"; 01565 } 01566 } 01567 return implode('', $res); 01568 } 01569 01570 // Insert element into array 01571 private function ins2ary(&$ary, $element, $pos) 01572 { 01573 $ar1 = array_slice($ary, 0, $pos); 01574 $ar1[] = $element; 01575 $ary = array_merge($ar1, array_slice($ary, $pos)); 01576 } 01577 } 01578 01579 01581 01582 ?>
