1<?php 2require_once 'Zorba/zorba_api_wrapper.php'; 3 4class XQueryCompilerException extends Exception{} 5class XQueryProcessorException extends Exception{} 6 7/** 8 * Iterate over a sequence of XQuery items. 9 * This class implements the SPL Iterator interface. 10 * 11 * The following code snippet iterates over a small sequence of items. 12 * <code> 13 * <?php 14 * require_once 'Zorba/XQueryProcessor.php'; 15 * 16 * $xquery = new XQueryProcessor(); 17 * $xquery->importQuery('(1, 2, 3)'); 18 * 19 * $iterator = $xquery->getIterator(); 20 * foreach($it as $key => $value) { 21 * echo $value."\n"; 22 * } 23 * ?> 24 * </code> 25 */ 26class XQueryIterator implements Iterator { 27 28 private $xquery = null; 29 private $iterator = null; 30 private $item = null; 31 private $position = 0; 32 private $valid = false; 33 34 /** 35 * XQueryIterator constructor. 36 * This constructor is used internally only. 37 * 38 * @param XQuery XQuery object. 39 * 40 * @see XQueryProcessor::getIterator() 41 */ 42 public function __construct(XQuery $xquery) 43 { 44 $this->xquery = $xquery; 45 $this->item = Item::createEmptyItem(); 46 } 47 48 49 public function __destruct() 50 { 51 $this->xquery->destroy(); 52 } 53 54 /** 55 * Rewinds back to the first item of the Iterator. 56 */ 57 public function rewind() 58 { 59 if ($this->iterator != null) { 60 $this->iterator->close(); 61 $this->iterator->destroy(); 62 } 63 64 $this->position = 0; 65 $this->iterator = $this->xquery->iterator(); 66 $this->iterator->open(); 67 $this->valid = $this->iterator->next($this->item); 68 } 69 70 /** 71 * Return the current item serialized as string. 72 * 73 * @return string The current item serialized as string. 74 * 75 */ 76 public function current() 77 { 78 return $this->item->serialize(); 79 } 80 81 /** 82 * Return the position of current item. 83 * 84 * @return interger the position of the current item. 85 */ 86 public function key() 87 { 88 return $this->position; 89 } 90 91 /** 92 * Move forward to the next item 93 * 94 */ 95 public function next() 96 { 97 ++$this->position; 98 $this->valid = $this->iterator->next($this->item); 99 } 100 101 /** 102 * Checks if there are still items to process. 103 * 104 * @return boolean Returns true if the iterator has still items to process or false otherwise. 105 */ 106 public function valid() 107 { 108 return $this->valid; 109 } 110} 111 112/** 113 * The XQueryProcessor class allows to invoke the 114 * <a href="http://www.zorba-xquery.com">Zorba XQuery Processor</a>. 115 * 116 * Instructions to install the extension can be found at <a href="http://www.zorba-xquery.com/site2/html/php.html">http://www.zorba-xquery.com/site2/html/php.html</a>. 117 * 118 * The following code snippet executes a small <em>Hello World</em> program: 119 * <code> 120 * <?php 121 * require_once 'ZorbaXQueryProcessor.php'; 122 * 123 * $xquery = new XQueryProcessor(); 124 * 125 * $query = <<<'XQ' 126 * declare variable $name external; 127 * 128 * <h1>Hello {$name}</h1> 129 * XQ; 130 * 131 * $xquery->importQuery($query); 132 * 133 * $xquery->setVariable('name', 'World'); 134 * 135 * echo $xquery->execute(); 136 * ?> 137 * </code> 138 */ 139class XQueryProcessor implements IteratorAggregate { 140 141 private $store = null; 142 private $zorba = null; 143 private $query = null; 144 private $variables = array(); 145 146 /** 147 * Creates an XQueryProcessor instance. 148 */ 149 public function __construct(){ 150 $this->store = InMemoryStore::getInstance(); 151 $this->zorba = Zorba::getInstance($this->store); 152 } 153 154 /** 155 * Shuts down the XQueryProcessor instance. 156 */ 157 public function __destruct() { 158 $this->zorba->shutdown(); 159 InMemoryStore::shutdown($this->store); 160 } 161 162 /** 163 * Import a query to execute. 164 * For instance, the following code snippets imports and executes the query '1+1': 165 * <code> 166 * $xquery = new ZorbaXQueryProcessor(); 167 * 168 * $xquery->importQuery('1+1'); 169 * 170 * echo $xquery->execute() . '\n'; 171 * </code> 172 * The following code snippets imports and executes an <em>Hello World</em> query: 173 * <code> 174 * <?php 175 * $xquery = new XQueryProcessor(); 176 * 177 * $query = <<<'XQ' 178 * let $world := 'World' 179 * return <h1>Hello {$world}</h1> 180 * XQ; 181 * 182 * $xquery->importQuery($query); 183 * 184 * echo $xquery->execute() . '\n'; 185 * ?> 186 * </code> 187 * 188 * @param $query Query to execute. 189 * @return ZorbaXQueryProcessor instance. 190 */ 191 public function importQuery($query) { 192 if(!is_string($query)) { 193 throw new XQueryProcessorException('The query parameter must be a string. For instance: XQueryProcessor->importQuery("1+1")'); 194 } 195 $this->query = $query; 196 return $this; 197 } 198 199 /** 200 * Import a query to execute from a file with the given name. 201 * For instance, the following code snippet imports the query file named <em>hello_word.xq</em>: 202 * <code> 203 * $xquery = new ZorbaXQueryProcessor(); 204 * 205 * $xquery->importQueryFromURI('hello_world.xq'); 206 * 207 * echo $xquery->execute() . '\n'; 208 * </code> 209 * 210 * @param $filename Filename containing the query to execute. 211 * @return ZorbaXQueryProcessor instance. 212 */ 213 public function importQueryFromURI($filename) { 214 $ctx = null; 215 if(func_num_args() == 2) { 216 $ctx = func_get_arg(1); 217 } 218 $query = file_get_contents($filename, FILE_USE_INCLUDE_PATH, $ctx); 219 $this->importQuery($query); 220 return $this; 221 } 222 223 /** 224 * Set a value for an external variable. 225 * 226 * The following code snippet sets the value of the variable 227 * <em>$i</em> with <em>1</em> with type xs:integer. 228 * <code> 229 * $xquery = new ZorbaXQueryProcessor(); 230 * 231 * $query = <<<'XQ' 232 * declare variable $i as xs:integer external; 233 * 234 * $i + 1 235 * 'XQ'; 236 * 237 * $xquery->importQuery($query); 238 * $xquery->setVariable($i, 1); 239 * 240 * echo $xquery->execute() . '\n'; 241 * </code> 242 * 243 * The following code snippet sets the value of the variable <em>$i</em> in 244 * the local namespace to the value <em>1</em>. 245 * <code> 246 * $xquery = new ZorbaXQueryProcessor(); 247 * 248 * $query = <<<'XQ' 249 * declare variable $local:i as xs:integer external; 250 * 251 * $i + 1 252 * 'XQ'; 253 * 254 * $xquery->importQuery($query); 255 * $xquery->setVariable("http://www.w3.org/2005/xquery-local-functions", $i, 1); 256 * 257 * echo $xquery->execute() . '\n'; 258 * </code> 259 * 260 * PHP types are converted to the following XML types: 261 * - <b>DOMDocument</b> & <b>SimpleXMLElement</b>: document-node() 262 * - <b>string</b>: xs:string 263 * - <b>float</b>: xs:float 264 * - <b>long</b>: xs:long 265 * - <b>integer</b>: xs:integer 266 * - <b>boolean</b>: xs:boolean 267 * - <b>DOMDocument</b>: document-node() 268 * 269 * @param string $namespace optional Namespace URI of the external variable. 270 * @param string $name Local name of the external variable. 271 * @param mixed $value of the external variable. 272 * 273 * return ZorbaXQueryProcessor instance. 274 */ 275 public function setVariable($arg1, $arg2) { 276 $count = func_num_args(); 277 if($count == 2) { 278 $name = func_get_arg(0); 279 $value = func_get_arg(1); 280 $this->variables['_'][$name] = $value; 281 } else { 282 $ns = func_get_arg(0); 283 $name = func_get_arg(1); 284 $value = func_get_arg(2); 285 $this->variables[$ns][$name] = $value; 286 } 287 return $this; 288 } 289 290 /** 291 * Execute the Query. 292 * 293 * @return Query result. 294 */ 295 public function execute() { 296 //Execute 297 $query = $this->compile(); 298 $result = $query->execute(); 299 $query->destroy(); 300 return $result; 301 } 302 303 /** 304 * Provide an intance of the SPL iterator to iterator over the 305 * sequence of items produced by the query result. 306 * 307 * @return XQueryIterator 308 */ 309 public function getIterator() { 310 return new XQueryIterator($this->compile()); 311 } 312 313 /** 314 * Internal method that creates an instance of the 315 * XQuery class from the input parameters (importQuery and setVariable). 316 * 317 * @return XQuery compiled query. 318 */ 319 private function compile() 320 { 321 //You need at least to import a query in order to compile it. 322 if(!is_string($this->query)) { 323 throw new XQueryCompilerException('No Query Imported. Use XQueryProcessor->importQuery($query).'); 324 } 325 326 //Compile Query 327 $query = $this->zorba->compileQuery($this->query); 328 329 //Set Variables 330 $dctx = $query->getDynamicContext(); 331 foreach($this->variables as $ns => $variables){ 332 foreach($variables as $name => $value) { 333 if($ns == "_") $ns = ""; 334 $param = $this->zorba->compileQuery("."); 335 $value = $this->getItem($value); 336 $param->getDynamicContext()->setContextItem($value); 337 $dctx->setVariable($ns, $name, $param->iterator()); 338 } 339 } 340 //Returns an instance of the XQuery class 341 return $query; 342 } 343 344 /** 345 * Converts a PHP value to an XQuery Item. 346 * The mapping between PHP and XQuery types in {@link setVariable}. 347 * 348 * @see setVariable() 349 */ 350 private function getItem($value) { 351 $itemFactory = $this->zorba->getItemFactory(); 352 353 if($value instanceof DOMDocument or $value instanceof SimpleXMLElement) { 354 $value = $this->parseXML($value->saveXML()); 355 } else if(is_string($value)) { 356 $value = $itemFactory->createString($value); 357 } else if(is_int($value)) { 358 $value = $itemFactory->createInteger($value); 359 } else if(is_bool($value)) { 360 $value = $itemFactory->createBoolean($value); 361 } else if(is_float($value)) { 362 $value = $itemFactory->createFloat($value); 363 } else if(is_long($value)) { 364 $value = $itemFactory->createLong($value); 365 } else { 366 throw new XQueryCompilerException("Unsupported variable type: ".gettype($value)); 367 } 368 369 assert($value instanceof Item); 370 371 return $value; 372 } 373 374 /** 375 * Parse an XML string to an XQuery Item. 376 * This function is used internally only. 377 * @param $xml string XML string to parse. 378 * 379 * @return Item instance. 380 */ 381 private function parseXML($xml) 382 { 383 $lDataManager = $this->zorba->getXmlDataManager(); 384 $lDocMgr = $lDataManager->getDocumentManager(); 385 $iter = $lDataManager->parseXML($xml); 386 387 $iter->open(); 388 $doc = Item::createEmptyItem(); 389 390 $iter->next($doc); 391 $iter->close(); 392 $iter->destroy(); 393 394 return $doc; 395 } 396} 397?> 398