ZK now speaks Php

From Documentation
ZK now speaks Php

Author
Edwin Estévez Parra, Information Technology Engineer, University of Holguín province, Cuba
Date
July 06, 2009
Version
Applicable to ZK 3.0 and later. Applicable to Quercus 4.0.3 and later.


Introduction

Since 2.3, ZK allows developers to write the scripting codes in their favorite scripting language, not just Java. The built-in support includes Java(BeanShell), JavaScript(Rhino), Ruby(JRuby) , Groovy[1], Pyton(Jyton) and lately Php(Quercus). This article shows how to write scripting codes in Php using the ZK Ajax Framework. Some developers ask themselves how to write scripting codes in Php using ZK, when the latter is built on the Java plataform. All this resulting from the Php interpreter, Quercus.


The Interpreter

Quercus is Caucho Technology's fast, open-source, 100% Java implementation of the Php language. Quercus is a feature of Caucho Technology's Resin Application Server and is built into Resin. Developers using Resin can launch Php projects without having to install the standard Php interpreter (http://www.php.net) as Quercus takes on the role of the Php engine.

Quercus is pioneering a new mixed Java/Php approach to web applications and services. On Quercus, Java and Php is tightly integrated with each other - Php applications can choose to use Java libraries and technologies like JMS, EJB, SOA frameworks, Hibernate, and Spring. This revolutionary capability is made possible because 1) Php code is interpreted/compiled into Java and 2) Quercus and its libraries are written entirely in Java. This lets Php applications and Java libraries to talk directly with one another at the program level. To facilitate this new Java/Php architecture, Quercus provides an API and interface to expose Java libraries to Php[2].


Registering the interpreter

For registering the Php interpreter[3] the following lines must be added to the ZK configuration file (zk.xml) which is located in WEB-INF/ directory of the web application.

  <zscript-config>
    <language-name>php</language-name>
    <interpreter-class>scripting.php.PhpInterpreter</interpreter-class>
  </zscript-config>


Besides the PhpInterpreter and SimpleQuercusScriptEngine classes, and also the resin library which can be downloaded from caucho.com site must be added.


Any Servlet/JSP Container can be used, although Resin Application Server is recommended because Php applications can take advantage of Resin’s advanced features like connection pooling, distributed sessions, load balancing, and proxy caching.


For the front-end design, ZK Studio which is a plugin for the Eclipse IDE was used. A simple application that uses the DBMS MySql[4] was developed as an example.


Product list.JPG


Understanding the code

The Php interpreter implementation for ZK allows to access the visual components from the script codes, and also it permits to call the functions declared in the script codes from the view using the language name as a prefix.

Next, a ZK typical zul file used for the front end is shown.

When analysing the line 6, where a listbox component is created for visualizing several products, the onSelect event is registered, and when it is triggered, it invokes the setValues function (declared in fns.php file) using the php prefix.

viewlist.zul

   <?page id="pageid" title="ZK-PHP" contentType="text/html;charset=UTF-8"?>
   <zk>
	<window id="win" title="Product List" width="400px" border="normal">
	    <vbox spacing="1">
		<hbox>
			 <listbox id="listbx" mold="paging" pageSize="10"
				onSelect="php:setValues();"></listbox>
		</hbox>
		
		<hbox>
                     Name:  <textbox id="tb" constraint="no empty" />
	             Price:    <decimalbox id="db" constraint="no negative, no zero" />
		</hbox>
		<hbox>
			<button id="add" label="Add" width="40px" height="24px" onClick="php:doInsert();" />
			<button id="update" label="Update" width="60px" height="24px" onClick="php:doUpdate();" />
			<button id="delete" label="Delete" width="55px" height="24px" onClick="php:doDelete();" />
		</hbox>
	    </vbox>                       
                     <!—scripting codes-->
          </window>
   </zk>

Alternatively <?page zscript-language="php"?> directive can be used to call php functions directly


   <?page id="pageid" title="ZK-PHP" zscript-language="php" contentType="text/html;charset=UTF-8"?>
   <zk>
	...
		<hbox>
			<button id="add" label="Add" width="40px" height="24px" onClick="doInsert();" />
			<button id="update" label="Update" width="60px" height="24px" onClick="doUpdate();" />
			<button id="delete" label="Delete" width="55px" height="24px" onClick="doDelete();" />
		</hbox>
        ...
    </zk>

Scripting codes

The zscript element is a special element to define the scripting codes that will be evaluated when a ZUML page is rendered. Typical use includes initialization and declaring global variables and methods[5].

It’s necessary to include the java object references in the array GLOBALS so they can be visible inside the Php functions.

viewlist.zul

 <!-- mainscript -->
<zscript language="php"> 
    
            # Including php files
		
            require_once "php/fns.php";
   	    require_once "php/product.php";
	    require_once "php/database_fns.php";
		
	    # adding java objects to global array so they can be referenced inside php functions          
             

            $GLOBALS['listbx']=$listbx;
	    $GLOBALS['tb']=$tb;
	    $GLOBALS['db']=$db;
	    
             
	    $listmodel = getProductList( );    # function declared in database_fns.php  
	    drawListbox( $listmodel );           # function declared in fns.php
		
	    $GLOBALS['listmodel']=$listmodel;
    
</zscript>


Also Php files can be included as follows, but they can´t contain the <?php ?> block.

    <zscript language="php" src="mainscript.php" />

To reference a ZK component inside a Php function, the static method getComponent from the org.zkoss.zk.ui.Path java class can be used as the following code shows.


fns.php

function setValues(){
    global $tb, $db, $listmodel;
    $listbx=Path::getComponent("//pageid/win/listbx");
    $index = $listbx->getSelectedIndex();
    $product=$listmodel->get( $index );
    $tb->value=$product->NAME;
    $db->value=$product->PRICE;
  }


Conclusion

With the Php language inclusion in the ZK Ajax Framework, developers can take advantages that offers ZK to build the front end of their applications more easily and the built-in of Quercus with the java technology.

Download

Download [ZkPhp] war file.

Edwin Estévez is an Information Technology Engineer at the University of Holguín province, Cuba. He has experience in the java programming language and almost 3 years using the Zk Ajax Framework. e-mail: [email protected]


Notes

  1. Teach ZK a New Language
  2. Quercus documentation
  3. UnLike the ZK BSHInterpreter implementation, PhpInterpreter doesn’t supports the hierarchical scopes.
  4. The MySql driver mysql-connector-java-5.1.5-bin.jar must be placed in the classpath.
  5. The ZK Dev Guide

Example files

Eclipse Project

Webcontent.JPG

PhpInterpreter.java

package scripting.php;

import java.util.HashMap;

import javax.script.ScriptException;

import org.zkoss.zk.scripting.util.GenericInterpreter;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.UiException;

import com.caucho.quercus.env.Env;
import com.caucho.quercus.env.EnvVar;
import com.caucho.quercus.env.EnvVarImpl;
import com.caucho.quercus.env.Value;
import com.caucho.quercus.program.Function;

/**
 * The PHP interpreter based on <a href="www.quercus.caucho.com/">Quercus</a>.
 * 
 * <p> </p>
 * @author Edwin Estevez Parra
 * @since 3.6.x
*/

public class PhpInterpreter extends GenericInterpreter {
	
	private SimpleQuercusScriptEngine _ip;

    public PhpInterpreter() {
	}
	
	/** Returns the native interpreter, or null if it is not initialized
	 * or destroyed.
	 * From application's standpoint, it never returns null, and the returned
	 * object must be an instance of {@link scripting.php.SimpleQuercusScriptEngine}
	 * 
	 */
	public Object getNativeInterpreter() {
		return _ip;
	}
	
	protected void exec(String script) {
		try {
			_ip.eval( script ); 
		} catch (ScriptException ex) {
			throw UiException.Aide.wrap(ex);
		}
	}
	
	protected boolean contains(String name) {
		return _ip.getGlobal().containsKey(name);
	}
	
	protected Object get(String name) {
		EnvVar value = _ip.getGlobal().get(name);
		return phpToJava( value ); 
	}
	protected void set(String name, Object value) {
		_ip.getGlobal().put(name, javaToPhp(value) );
	}
	protected void unset(String name) {
		_ip.getGlobal().remove(name);
	}

	public void init(Page owner, String zslang) {
		super.init(owner, zslang);
		String path = owner.getDesktop().getWebApp().getRealPath("/");
		_ip = new SimpleQuercusScriptEngine(new Variables(), path ); 
	
	}
	
	public void destroy() {
		_ip.closeEnv();
		_ip = null;
		super.destroy();
	}
	
	/** Returns the method. */
	public org.zkoss.xel.Function getFunction(String name, Class[] argTypes) {
        final Object val = _ip.getEnv().findFunction(name);
        if (!(val instanceof Function))
            return null;
        return new QuercusFunction((Function) val, _ip.getEnv());
    }

	//supporting class//
	/** Extends Binding to support ZK namespaces.
	 */
	private class Variables extends HashMap{
        public Object get(Object key) {
			Object val = super.get(key);
			
			if (val != null || containsKey(key) || !(key instanceof String))
				return val;
			val = getFromNamespace((String) key);
			
			/** if Object with this key doesn't exist, this method needs to 
			 * return null for Quercus enviroment to create the new variable
			 */
			return ( val != UNDEFINED)? javaToPhp(val) : null;   
		}
		
	}

	private static class QuercusFunction implements org.zkoss.xel.Function {
        private final Function _func;
        private final Env _env;

        private QuercusFunction(Function func, Env env) {
            _func = func;
            _env = env;
        }

        public Class<?>[] getParameterTypes() {
           	return new Class[0];
        }

        public Class<Object> getReturnType() {
            return Object.class;
        }

        public Object invoke(Object obj, Object[] args) throws Exception {
            if (args == null)
                return _func.call(_env).toJavaObject();
            else{
                 Value[] phpargs = new Value[args.length];
                  for(int i=0; i< args.length; i++) {
                    Value phparg = (Value)_env.wrapJava( args[i] );
                    phpargs[i] = phparg;
                  }
              return _func.call(_env, phpargs).toJavaObject();
            }
        }

        public java.lang.reflect.Method toMethod() {
            return null;
        }

    }
	
	//utilities//
	private Object phpToJava(EnvVar value){
		return (value!=null)? value.getRef().toJavaObject(): null;
	}
	
	private EnvVar javaToPhp(Object value){
		return new EnvVarImpl( _ip.getEnv().wrapJava( value ).toVar() );
	}
	
}


SimpleQuercusScriptEngine.java

package scripting.php;

import java.io.Reader;
import java.io.Writer;
import java.util.Map;

import javax.script.AbstractScriptEngine;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;

import com.caucho.quercus.Quercus;
import com.caucho.quercus.QuercusExitException;
import com.caucho.quercus.env.Env;
import com.caucho.quercus.env.EnvVar;
import com.caucho.quercus.env.Value;
import com.caucho.quercus.page.InterpretedPage;
import com.caucho.quercus.page.QuercusPage;
import com.caucho.quercus.program.QuercusProgram;
import com.caucho.vfs.FilePath;
import com.caucho.vfs.NullWriteStream;
import com.caucho.vfs.WriteStream;
import com.caucho.vfs.WriterStreamImpl;

/**
 * The PHP script engine based on 
 * {@link com.caucho.quercus.script.QuercusScriptEngine}.
 * 
 * @author Edwin Estevez Parra
 * @since 3.6.x
*/


public class SimpleQuercusScriptEngine extends AbstractScriptEngine {
    
    private Quercus _quercus = null;
	private Env _env = null;
	private Map<String, EnvVar> _map;
		
	public SimpleQuercusScriptEngine(Map <String, EnvVar> map) {
		
		_quercus = new Quercus();
		_quercus.init(); // Load quercus classes and modules.
		_map=map;
		
	} 
	
	public SimpleQuercusScriptEngine(Map <String, EnvVar> map, String path) {
		_quercus = new Quercus();
		_quercus.setPwd(new FilePath( path ) );
		_quercus.init(); // Load quercus classes and modules.
		_map=map;
	}

	@Override
	public Object eval(String script, ScriptContext cxt)
			throws ScriptException {
		try {
			QuercusProgram program = _quercus.parseCode( script );
			
			Writer writer = cxt.getWriter();
			WriteStream out;
			if (writer != null) {
				WriterStreamImpl s = new WriterStreamImpl();
				s.setWriter(writer);
				WriteStream os = new WriteStream(s);

				os.setNewlineString("\n");

				try {
					os.setEncoding("iso-8859-1");
				} catch (Exception e) {
				}
				out = os;
			} else
				out = new NullWriteStream();

			QuercusPage page = new InterpretedPage(program);
			
			if(_env == null){
				_env = new Env(_quercus, page, out, null, null);
				_env.pushEnv( _map );
				_env.start(); 
			}
			
            Object result = null;
			try {
		        Value value = program.execute(_env);
		        
		        if (value != null)
		          result = value.toJavaObject();
		      }
		      catch (QuercusExitException e) {
		        //php/2148
		      }
			out.flushBuffer();
			out.free();
			writer.flush();

			return result;
		} catch (RuntimeException e) {
			throw e;
		} catch (Exception e) {
			throw new ScriptException(e);
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
		
	}
    
	public Env getEnv() {
		return _env;
	}
	
	public void closeEnv(){
		_env.close(); _env = null;
		_quercus.close(); _quercus = null;
	}
	
	public Map<String, EnvVar> getGlobal(){
		return _env.getEnv();
	}
	
	public Quercus getQuercus(){
		return _quercus;
	}
	
	@Override
	public Object eval(Reader reader, ScriptContext context)
			throws ScriptException {
		return null;
	}
	
	@Override
	public Bindings createBindings() {
		return null;
	}
	
	@Override
	public ScriptEngineFactory getFactory() {
		return null;
	}
	
}


database_fns.php

 
 <?php

import java.util.ArrayList;
import com.caucho.quercus.lib.db.Mysqli;

$mysql = new Mysqli( 'localhost','root','admin','DB');

$GLOBALS['mysql']=$mysql;

function getProductList(){
	  global $mysql;
	  
	  $list = new ArrayList();
	  $sql =  "select * from product order by id";
	  $rs = $mysql->query( $sql )->resultSet;
		
		while($rs->next()){
		 $id = $rs->getObject( 'id' );
		 $name = $rs->getObject( 'name' );
		 $price = $rs->getObject( 'price' );
		 
		 $list->add( new Product( $id, $name, $price ) );
	    }
	  return $list;
	}
	
	function insert($name, $price){
	  global $mysql;
	  $sql = "insert into product(name, price) values('$name',$price)";
	  $mysql->query( $sql );
	}
	
	function delete($id){
	  global $mysql;
	  $sql = "delete from product where id=$id";
	  $mysql->query( $sql );
	}
	
	function update($id, $name, $price){
	  global $mysql;
	  $sql = "update product set name='$name',price=$price where id=$id";
	  $mysql->query( $sql );
	}
	
	?>

fns.php

 
 <?php
 
        import org.zkoss.zul.*;  
		import org.zkoss.zk.ui.*;
   	    import java.lang.*;
   	    
 function drawListbox($listmodel){
 
   global $listbx, $count;
   
   $listbx->items->clear(); $count=0;
   foreach ($listmodel as $product){
	 $item = new Listitem();
     render($item, $product);
     
     $listbx->appendChild( $item );
   }
 }
 
 $count=0;
  function render($item, $data){
  global $count;
    (new Listcell(++$count))->setParent($item);
    (new Listcell($data->NAME))->setParent($item);
    (new Listcell($data->PRICE))->setParent($item);
  }
  
  function setValues(){
    global $tb,$db,$listmodel;
    
    $listbx=Path::getComponent("//pageid/win/listbx");
    $index = $listbx->getSelectedIndex();
    $product=$listmodel->get( $index );
    $tb->value=$product->NAME;
    $db->value=$product->PRICE;
  }

 function doInsert(  ){
	    	
	    	global $tb,$db,$listmodel, $listbx;
	    	
	    	if($db->value == NULL) return;
	    	$sql = insert($tb->value, new Double($db->value->doubleValue()) );
	    	
	    	$listmodel = getProductList( );     
	    	drawListbox( $listmodel );
            	    	
	    	#Messagebox::show( $sql  );
 }
 
 function doDelete(  ){
  global $listmodel, $listbx;
    
    $index = $listbx->getSelectedIndex();
    
    if ( $index != -1 )  {
	    $product=$listmodel->get( $index );
	    $sql=delete( $product->ID );
	    
	    $listmodel = getProductList( );     
		drawListbox( $listmodel );
	}else showError(); 
 }
 
 function doUpdate(  ){
  global $tb,$db,$listmodel;
    
    $listbx=Path::getComponent("//pageid/win/listbx");
    $index = $listbx->getSelectedIndex();
    
    if ( $index != -1 )  {
     
     $product=$listmodel->get( $index );
     $sql=update( $product->ID, $tb->value, new Double($db->value->doubleValue()) );
     $listmodel = getProductList( );     
	 drawListbox( $listmodel );
	
	}else showError(); 
 }
 
 function showError(){
   Messagebox::show('An element from the list must be selected');
 }
 
 ?>

product.php

 
 <?php
      class Product {
	var $ID;
	var $NAME;
	var $PRICE;
		
	function Product($id, $name, $price) {
	$this->ID = $id;
	$this->NAME = $name;
	$this->PRICE = $price;
	}
		
      }
		
  ?>

MySql_script.sql

  -- Database name DB

CREATE TABLE product (
	id BIGINT NOT NULL AUTO_INCREMENT,
	name VARCHAR(50) NOT NULL,
	price DOUBLE NOT NULL,
	PRIMARY KEY (id)
);

viewlist.zul

<?page id="pageid" title="ZK-PHP" zscript-language="php" contentType="text/html;charset=UTF-8"?>
<zk>
	<window id="win" title="Product List" width="400px"
		border="normal">
		 <vbox spacing="1">
		    <hbox>
			 <listbox id="listbx" mold="paging" pageSize="10"
				onSelect="setValues();">
				<listhead>
					<listheader label="#" width="10%" />
					<listheader label="Name" width="40%" />
					<listheader label="Price" width="50%" />
				</listhead>
			 </listbox>
		    </hbox>
		
			<hbox>
				Name:  <textbox id="tb" constraint="no empty" />
				Price: <decimalbox id="db" constraint="no negative,no zero" />
			</hbox>
			<hbox>
				<button id="add" label="Add" width="40px" height="24px"
					onClick="doInsert();" />
				<button id="update" label="Update" width="60px"
					height="24px" onClick="doUpdate();" />
				<button id="delete" label="Delete" width="55px"
					height="24px" onClick="doDelete();" />
			</hbox>
		</vbox>
<!--   # php files can be included as following, but they can´t contain the <?php ?> block
		<zscript language="php" src="database_fns.php" />
		<zscript language="php" src="fns.php" /> 
		<zscript language="php" src="product.php" />
-->

	<zscript><![CDATA[ 
		# Including php files
		require_once "php/fns.php";
   		require_once "php/product.php";
		require_once "php/database_fns.php";
		
		# adding java objects to global array so they can be referenced 
        # inside php functions

        $GLOBALS['listbx']=$listbx;
	    $GLOBALS['tb']=$tb;
	    $GLOBALS['db']=$db;
	    
	    $listmodel = getProductList( ); # function declared in database_fns.php
	    
	    drawListbox( $listmodel ); # function declared in fns.php
		
	    $GLOBALS['listmodel']=$listmodel;
	    
	]]></zscript>
                              
	</window>
</zk>


Libraries

  1. The Zk Ajax Framework libs from http://zkoss.org/ site
  2. The MySql Driver mysql-connector-java-5.1.5-bin.jar
  3. resin.jar and javamail-141.jar, inject-16.jar for more functionality
    To download it, do the following instructions:
    * Download Quercus Application Demo from http://caucho.com/download/quercus-4.0.3.war
    * Unpack quercus-4.0.3.war and get the .jar files at /lib directory.

Edwin Estevez is an Information Technology Engineer at the University of Holguín province, Cuba. He has experience in the java programming language and almost 3 years using the Zk Ajax Framework. e-mail: [email protected]



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.