William Candillon has proposed merging lp:~zorba-coders/zorba/phpapi into 
lp:zorba.

Requested reviews:
  David Graf (davidagraf)
  Matthias Brantner (matthias-brantner)
  Rodolfo Ochoa (rodolfo-ochoa)

For more details, see:
https://code.launchpad.net/~zorba-coders/zorba/phpapi/+merge/87718

This merge adds the PHP API that was introduced at the PHP Tour 2011.
It contains a test for it (php2).

>From the last merge proposal, the following things have been done:
- Revert bogus change in  swig/php/generate_proxy.php.in (renaming of libPrefix 
to prefix).
- Introduce a STD Iterator for streaming results.
- Make the parseXML() method private.
- Improve importQueryFromURI

The two key tests are: 
- php1
- php2


-- 
https://code.launchpad.net/~zorba-coders/zorba/phpapi/+merge/87718
Your team Zorba Coders is subscribed to branch lp:zorba.
=== modified file 'doc/php/examples/CMakeLists.txt'
--- doc/php/examples/CMakeLists.txt	2011-08-19 00:03:31 +0000
+++ doc/php/examples/CMakeLists.txt	2012-01-06 10:17:29 +0000
@@ -20,14 +20,21 @@
   MESSAGE(STATUS "PHP Path:" ${phpPath})
   SET(phpExtensionPath ${CMAKE_BINARY_DIR}/swig/php)
   MESSAGE(STATUS "PHP Extension Path: " ${phpExtensionPath})
+  SET(phpIncludePath ${CMAKE_BINARY_DIR}/swig/php)
+  MESSAGE(STATUS "PHP Include Path: " ${phpExtensionPath})
   CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/php.ini.in ${CMAKE_CURRENT_BINARY_DIR}/php.ini)
   MESSAGE(STATUS "Configuration file: " ${CMAKE_CURRENT_BINARY_DIR}/php.ini)
   CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/simple.php.in ${CMAKE_CURRENT_BINARY_DIR}/simple.php)
   MESSAGE(STATUS "Simple configuration file: " ${CMAKE_CURRENT_BINARY_DIR}/simple.php)
-  ADD_TEST("php" ${PHP5_EXECUTABLE} -c ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/simple.php)
+  CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/test.php.in ${CMAKE_CURRENT_BINARY_DIR}/test.php)
+  MESSAGE(STATUS "PHP test  file configured: " ${CMAKE_CURRENT_BINARY_DIR}/test.php)
+  ADD_TEST("php1" ${PHP5_EXECUTABLE} -c ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/simple.php)
   MESSAGE(STATUS "Installing: " ${CMAKE_CURRENT_BINARY_DIR}/simple.php)
-  
-  INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/simple.php
+  ADD_TEST("php2" ${PHP5_EXECUTABLE} -c ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/test.php)
+  MESSAGE(STATUS "Installing: " ${CMAKE_CURRENT_BINARY_DIR}/test.php)
+  
+  
+  INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/simple.php ${CMAKE_CURRENT_BINARY_DIR}/test.php
           COMPONENT "php_examples"
           DESTINATION
           share/doc/zorba-${ZORBA_MAJOR_NUMBER}.${ZORBA_MINOR_NUMBER}.${ZORBA_PATCH_NUMBER}/php/examples)

=== modified file 'doc/php/examples/php.ini.in'
--- doc/php/examples/php.ini.in	2009-10-30 16:34:37 +0000
+++ doc/php/examples/php.ini.in	2012-01-06 10:17:29 +0000
@@ -1,2 +1,3 @@
 enable_dl=On
 extension_dir=@phpExtensionPath@
+include_path=".:@phpIncludePath@"
\ No newline at end of file

=== modified file 'doc/php/examples/simple.php.in'
--- doc/php/examples/simple.php.in	2011-08-04 02:14:56 +0000
+++ doc/php/examples/simple.php.in	2012-01-06 10:17:29 +0000
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-require '@phpPath@/zorba_api_wrapper.php';
+require '@phpPath@/Zorba/zorba_api_wrapper.php';
 
 function example_1(Zorba $aZorba)
 {

=== added file 'doc/php/examples/test.php.in'
--- doc/php/examples/test.php.in	1970-01-01 00:00:00 +0000
+++ doc/php/examples/test.php.in	2012-01-06 10:17:29 +0000
@@ -0,0 +1,122 @@
+<?php
+/*
+ * Copyright 2006-2008 The FLWOR Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+require '@phpPath@/Zorba/XQueryProcessor.php';
+
+function omitXMLDecl($xml)
+{
+  $xml = str_replace('<?xml version="1.0" encoding="UTF-8"?>', '', $xml);
+  $xml = trim($xml);
+  return $xml;
+}
+
+function assertEquality($test, $reference, $label)
+{
+  $test = omitXMLDecl($test);
+  echo "=====================================\n";
+  var_dump($test);
+  var_dump($reference);
+  if($test != $reference) {
+    throw new Exception(
+       "Test "
+     . $label
+     . " failed. Result:\n"
+     . $test
+     . "\nDoesn't match reference:\n"
+     . $reference
+    );
+  }
+}
+/* Test 1 */
+$xquery = new XQueryProcessor();
+$xquery->importQuery('1+1');
+$result = $xquery->execute();
+assertEquality($result, '2', "1+1");
+
+/* Test 2 */
+$query = <<<'XQ'
+declare variable $foo as xs:string external;
+declare variable $bar as xs:integer external;
+declare variable $doc1 as document-node() external;
+declare variable $doc2-str as xs:string external;
+declare variable $doc2 as document-node() := parse-xml($doc2-str);
+
+$foo, $bar, $doc1 , $doc2
+XQ;
+
+$xquery->importQuery($query);
+
+$xquery->setVariable("foo", "bar");
+$xquery->setVariable("bar", 3);
+
+$doc = simplexml_load_string('<root />');
+$xquery->setVariable("doc1", $doc);
+
+$doc = "<root />";
+$xquery->setVariable("doc2-str", $doc);
+
+$result = trim($xquery->execute());
+assertEquality($result,  "bar 3<root/><root/>", "Test PHP Types Mapping");
+
+/* Test 3 */
+$xquery->importQuery("(1, 2, <foo bar='bar' />)");
+$it = $xquery->getIterator();
+
+foreach($it as $pos => $value) {
+  switch($pos) {
+    case 0:
+      assertEquality($value, "1", "Consume the item at position {$pos}");
+    break;
+    case 1:
+      assertEquality($value, "2", "Consume the item at position {$pos}");
+    break;
+    case 3:
+      assertEquality($value, '<foo bar="bar"/>', "Consume the item at position {$pos}");
+    break;
+  }
+}
+
+//Let's do it again
+$it = $xquery->getIterator();
+
+foreach($it as $pos => $value) {
+  switch($pos) {
+    case 0:
+      assertEquality($value, "1", "Consume the item at position {$pos}");
+    break;
+    case 1:
+      assertEquality($value, "2", "Consume the item at position {$pos}");
+    break;
+    case 3:
+      assertEquality($value, '<foo bar="bar"/>', "Consume the item at position {$pos}");
+    break;
+  }
+}
+
+//Try the same query with execute
+$result = $xquery->execute();
+assertEquality($result, "1 2<foo bar=\"bar\"/>", "Execute To URI");
+
+/* Test 4 */
+$xquery->importQueryFromURI("@phpPath@/test.xq");
+$result = $xquery->execute();
+assertEquality($result, "2", "Test importQueryFromURI");
+
+/* Test 5 */
+//$xquery->importQueryFromURI("http://zorba.s3.amazonaws.com/test.xq";);
+//$result = $xquery->execute();
+//assertEquality($result, "2", "Test importQueryFromURI");
+?>

=== modified file 'swig/php/CMakeLists.txt'
--- swig/php/CMakeLists.txt	2011-12-21 14:40:33 +0000
+++ swig/php/CMakeLists.txt	2012-01-06 10:17:29 +0000
@@ -38,6 +38,9 @@
     SWIG_LINK_LIBRARIES (zorba_api ${PHP5_LIBRARY})
   ENDIF (WIN32)
 
+  #Copy test to the build folder
+  CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/test.xq" "${CMAKE_CURRENT_BINARY_DIR}/test.xq")
+
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/../StaticContext.h" "${CMAKE_CURRENT_BINARY_DIR}/StaticContext.h")
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/../ItemFactory.h" "${CMAKE_CURRENT_BINARY_DIR}/ItemFactory.h")
 
@@ -55,7 +58,8 @@
 
     INSTALL (
       FILES
-        ${CMAKE_CURRENT_BINARY_DIR}/zorba_api_wrapper.php
+        ${CMAKE_CURRENT_BINARY_DIR}/Zorba/XQueryProcessor.php
+        ${CMAKE_CURRENT_BINARY_DIR}/Zorba/zorba_api_wrapper.php
         ${CMAKE_CURRENT_BINARY_DIR}/${ZORBA_SWIG_LIB_PREFIX}zorba_api.so
       DESTINATION ${PHP5_INSTALL_PATH}
     )
@@ -77,6 +81,7 @@
 
   ### Start PHP proxy generation
   # Configure the test file
+  SET (phpLibPrefix ${ZORBA_SWIG_LIB_PREFIX})
   SET (phpAPIPath ${CMAKE_CURRENT_BINARY_DIR})
   SET (phpLibPrefix ${ZORBA_SWIG_LIB_PREFIX})
   CONFIGURE_FILE (
@@ -84,8 +89,13 @@
     ${CMAKE_CURRENT_BINARY_DIR}/generate_proxy.php
   )
 
+  CONFIGURE_FILE (
+    ${CMAKE_CURRENT_SOURCE_DIR}/XQueryProcessor.php
+    ${CMAKE_CURRENT_BINARY_DIR}/Zorba/XQueryProcessor.php
+  )
+
   ADD_CUSTOM_COMMAND (
-    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/zorba_api_wrapper.php
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Zorba/zorba_api_wrapper.php
     # the following addes a dependency to the *.cxx file that is generated by swig
     DEPENDS ${swig_generated_file_fullname}
     DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generate_proxy.php.in
@@ -94,7 +104,8 @@
   )
   ADD_CUSTOM_TARGET (
     Api_PHP_Wrapper ALL
-    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/zorba_api_wrapper.php
+    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Zorba/zorba_api_wrapper.php
+    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Zorba/XQueryProcessor.php
   )
   ### End PHP proxy generation
 

=== added file 'swig/php/XQueryProcessor.php'
--- swig/php/XQueryProcessor.php	1970-01-01 00:00:00 +0000
+++ swig/php/XQueryProcessor.php	2012-01-06 10:17:29 +0000
@@ -0,0 +1,353 @@
+<?php
+require_once 'Zorba/zorba_api_wrapper.php';
+
+class XQueryCompilerException extends Exception{}
+class XQueryProcessorException extends Exception{}
+
+/**
+ * Iterate over an instance of the XML Data Model (i.e, a sequence of items).
+ * This class implements the SPL Iterator interface.
+ *
+ * The following code snippet iterates over a small sequence of items.
+ * <code>
+ * <?php
+ * require_once 'Zorba/XQueryProcessor.php';
+ *
+ * $xquery = new XQueryProcessor();
+ * $xquery->importQuery('(1, 2, 3)');
+ * 
+ * $iterator = $xquery->getIterator();
+ * foreach($it as $key => $value) {
+ *   echo $value."\n";
+ * }
+ * ?>
+ * </code>
+ */
+class XQueryIterator implements Iterator {
+
+  private $xquery   = null;
+  private $iterator = null;
+  private $item     = null;
+  private $position = 0;
+  private $valid    = false;
+
+  public function __construct(XQuery $xquery)
+  {
+	  $this->xquery = $xquery;
+    $this->item = Item::createEmptyItem();
+  }
+
+  public function __destruct()
+  {
+    $this->xquery->destroy();
+  }
+
+  public function rewind()
+  {
+    if ($this->iterator != null) {
+      $this->iterator->close();
+      $this->iterator->destroy();
+    }
+    
+    $this->position = 0;
+    $this->iterator = $this->xquery->iterator(); 
+    $this->iterator->open();
+    $this->valid    = $this->iterator->next($this->item);
+  }
+
+  public function current()
+  {
+    return $this->item->serialize();
+  }
+
+  public function key()
+  {
+    return $this->position;
+  }
+
+  public function next()
+  {
+    ++$this->position;
+    $this->valid = $this->iterator->next($this->item);
+  }
+
+  public function valid()
+  {
+    return $this->valid;
+  }
+}
+
+/**
+ * The XQueryProcessor class allows to invoke
+ * <a href="http://www.zorba-xquery.com";>Zorba XQuery Processor</a>.  
+ *
+ * Instruction to install the extension can be found at <a href=""></a>.
+ *
+ * The following code snippet executes a small <em>Hello World</em> program:
+ * <code>
+ * <?php
+ * require_once 'ZorbaXQueryProcessor.php';
+ *
+ * $xquery = new XQueryProcessor();
+ *
+ * $query = <<<'XQ'
+ * declare variable $name external;
+ *
+ * <h1>Hello {$name}</h1>
+ * XQ;
+ *
+ * $xquery->importQuery($query);
+ *
+ * $xquery->setVariable('name', 'World');
+ *
+ * echo $xquery->execute();
+ * ?>
+ * </code>
+ */
+class XQueryProcessor {
+
+  private $store = null;
+  private $zorba = null;
+  private $query = null;
+  private $variables = array();
+
+  /**
+   * Creates an XQueryProcessor instance.
+   */
+  public function __construct(){
+    $this->store = InMemoryStore::getInstance();
+    $this->zorba = Zorba::getInstance($this->store);
+  }
+
+  /**
+   * Shutdowns the XQueryProcessor instance.
+   */
+  public function __destruct() {
+    $this->zorba->shutdown();
+    InMemoryStore::shutdown($this->store);
+  }
+
+  /**
+   * Import a query to execute.
+   * For instance, the following code snippets imports and executes the query '1+1':
+   * <code>
+   * $xquery = new ZorbaXQueryProcessor();
+   *
+   * $xquery->importQuery('1+1');
+   * 
+   * echo $xquery->execute() . '\n';
+   * </code>
+   * In the following code snippet, the following code snippets imports and execute an <em>Hello World</em> query:
+   * <code>
+   * <?php
+   * $xquery = new XQueryProcessor();
+   *
+   * $query = <<<'XQ'
+   * let $world := 'World'
+   * return <h1>Hello {$world}</h1>
+   * XQ;
+   *
+   * $xquery->importQuery($query);
+   *
+   * echo $xquery->execute() . '\n';
+   * ?>
+   * </code>
+   *  
+   * @param $query Query to execute.
+   * @return ZorbaXQueryProcessor instance.
+   */ 
+  public function importQuery($query) {
+	if(!is_string($query)) {
+		throw new XQueryProcessorException('The query parameter must be a string. For instance: XQueryProcessor->importQuery("1+1")');
+	}
+    $this->query = $query;
+    return $this;
+  }
+
+  /**
+   * Import a query to execute from its filename.
+   * For instance, the following code snippet imports the query file named <em>hello_word.xq</em>:
+   * <code>
+   * $xquery = new ZorbaXQueryProcessor();
+   *
+   * $xquery->importQueryiFromURI('hello_world.xq');
+   * 
+   * echo $xquery->execute() . '\n';
+   * </code>
+   *
+   * @param  $filename Filename of the query to execute.
+   * @return ZorbaXQueryProcessor instance.
+   */
+  public function importQueryFromURI($filename) {
+    $ctx = null;
+    if(func_num_args() == 2) {
+      $ctx = func_get_arg(1);
+    }
+    $query = file_get_contents($filename, FILE_USE_INCLUDE_PATH, $ctx);
+    $this->importQuery($query);
+    return $this;
+  }
+
+  /**
+   * Set value for an external variable.
+   *
+   * The following code snippet sets the value of the variable
+   * <em>$i</em> with <em>1</em>.
+   * <code>
+   * $xquery = new ZorbaXQueryProcessor();
+   * 
+   * $query = <<<'XQ'
+   * declare variable $i as xs:integer external;
+   *
+   * $i + 1
+   * 'XQ';
+   *
+   * $xquery->importQuery($query);
+   * $xquery->setVariable($i, 1);
+   *
+   * echo $xquery->execute() . '\n';
+   * </code>
+   *
+   * The following code snippet sets the value of the variable <em>$i</em> in
+   * the local namespace with the value <em>1</em>.
+   * <code>
+   * $xquery = new ZorbaXQueryProcessor();
+   * 
+   * $query = <<<'XQ'
+   * declare variable $local:i as xs:integer external;
+   *
+   * $i + 1
+   * 'XQ';
+   *
+   * $xquery->importQuery($query);
+   * $xquery->setVariable("http://www.w3.org/2005/xquery-local-functions";, $i, 1);
+   *
+   * echo $xquery->execute() . '\n';
+   * </code>
+   *
+   * PHP types are converted to the following XML types:
+   * - <b>string</b>: xs:string
+   * - <b>float</b>: xs:float
+   * - <b>long</b>: xs:long
+   * - <b>integer</b>: xs:integer
+   * - <b>boolean</b>: xs:boolean
+   * - <b>DOMDocument</b>: document-node()    
+   * 
+   * @param string $namespace optional Namespace URI of the external variable.
+   * @param string $name Local name of the external variable.
+   * @param mixed  $value of the external variable.
+   *
+   * return ZorbaXQueryProcessor instance. 
+   */
+  public function setVariable($arg1, $arg2) {
+    $count = func_num_args();
+    if($count == 2) {
+      $name  = func_get_arg(0);
+      $value = func_get_arg(1);
+      $this->variables['_'][$name] = $value;
+    } else {
+      $ns  = func_get_arg(0);
+      $name  = func_get_arg(1);
+      $value = func_get_arg(2);
+      $this->variables[$ns][$name] = $value;
+    }
+    return $this;
+  } 
+  
+  /**
+   * Execute the Query.
+   *
+   * @return Query result.
+   */
+  public function execute() {
+    //Execute
+    $query = $this->compile();
+    $result = $query->execute();
+    $query->destroy();
+    return $result;
+  }
+
+  public function getIterator() {
+    return new XQueryIterator($this->compile());
+  }  
+	/**
+	 * 
+	 */
+	private function compile()
+	{
+	  if(!is_string($this->query)) {
+		throw new XQueryCompilerException('No Query Imported. Use XQueryProcessor->importQuery($query).');
+	  }
+	  //Compile Query
+	  $query = $this->zorba->compileQuery($this->query);
+	  //Set Variables
+	  $dctx = $query->getDynamicContext();
+	  foreach($this->variables as $ns => $variables){
+        foreach($variables as $name => $value) {
+          if($ns == "_") $ns = "";
+          $param = $this->zorba->compileQuery(".");
+          $value = $this->getItem($value);
+          $param->getDynamicContext()->setContextItem($value);
+          $dctx->setVariable($ns, $name, $param->iterator());
+	    }
+      }
+	  return $query;
+	}
+
+  /*
+   * Converts a PHP value to an XQuery Item.
+   * - DOMDocument & SimpleXMLElement: document-node()
+   * - String: xs:string
+   * - Integer: xs:integer
+   * - Boolean: xs:boolean
+   * - Float: xs:float
+   * - Long: xs:long
+   */  
+	private function getItem($value) {
+      $itemFactory = $this->zorba->getItemFactory();
+
+      if($value instanceof DOMDocument or $value instanceof SimpleXMLElement) { 
+        $value = $this->parseXML($value->saveXML());
+      } else if(is_string($value)) {
+        $value = $itemFactory->createString($value);
+      } else if(is_int($value)) {
+        $value = $itemFactory->createInteger($value);
+      } else if(is_bool($value)) {
+        $value = $itemFactory->createBoolean($value);
+      } else if(is_float($value)) {
+        $value = $itemFactory->createFloat($value);
+      } else if(is_long($value)) {
+        $value = $itemFactory->createLong($value);
+      } else  {
+        throw new XQueryCompilerException("Unsupported variable type: ".gettype($value));
+      }
+
+      assert($value instanceof Item);
+
+      return $value;
+    }
+
+  /**
+   * Parse an XML string to an XQuery Item.
+   * This function is used internally only. 
+   * @param $xml string XML string to parse.
+   *
+   * @return Item instance.
+   */
+  private function parseXML($xml)
+  {
+    $lDataManager = $this->zorba->getXmlDataManager();
+    $lDocMgr = $lDataManager->getDocumentManager();
+    $iter = $lDataManager->parseXML($xml);
+    
+    $iter->open();
+    $doc = Item::createEmptyItem();
+    
+    $iter->next($doc);
+    $iter->close();
+    $iter->destroy();
+    
+    return $doc;  
+  }
+}
+?>

=== modified file 'swig/php/generate_proxy.php.in'
--- swig/php/generate_proxy.php.in	2011-12-21 14:40:33 +0000
+++ swig/php/generate_proxy.php.in	2012-01-06 10:17:29 +0000
@@ -164,7 +164,7 @@
 	}
 }
 
-file_put_contents('zorba_api_wrapper.php', $wrapper);
+file_put_contents('Zorba/zorba_api_wrapper.php', $wrapper);
 
 function getRefParameters(ReflectionMethod $method, array $params) {
 	$result = "";

=== added file 'swig/php/test.xq'
--- swig/php/test.xq	1970-01-01 00:00:00 +0000
+++ swig/php/test.xq	2012-01-06 10:17:29 +0000
@@ -0,0 +1,1 @@
+1+1

-- 
Mailing list: https://launchpad.net/~zorba-coders
Post to     : zorba-coders@lists.launchpad.net
Unsubscribe : https://launchpad.net/~zorba-coders
More help   : https://help.launchpad.net/ListHelp

Reply via email to