Location: PHPKode > scripts > metaForm > metaform/xmlForm.php
<?php
/*
xmlForm Class: Generate HTML forms from XML config file. HTML is generated using XSL transformations.
Version: 1.0
Copyright (C) 2002 Herman Veluwenkamp
Version: 2.0
Copyright (C) 2005 Yo Keller

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Copy of GNU Lesser General Public License at: http://www.gnu.org/copyleft/lesser.txt
Contact author at: hide@address.com
*/
class xmlForm {
	var $verb = 0;	// verbose flag
  /*
  Constructor initialising config file location
  */
  function xmlForm($conf) {
	//$conf = str_replace('&','&amp;',$conf);
	//$conf = str_replace('&','&amp;',$conf);
    $this->conf = $conf;
    $this->conf_validate($conf);
  }

  /*
  Validate $conf using rules defined in $conf
  */
  function conf_validate($conf='') {
    if (empty($conf)) $conf = $this->conf;
    $this->error           = '';
    $this->validationError = FALSE; // flag validation error
    $this->currentNode     = '';    // keep track of current node
    $this->newConf         = '';    // growing XML output string
    $this->var             = '';    // keeps track of VAR name we are looking at
    $this->type            = '';    // keeps track of VAR type we are looking at
    $this->regexp          = false; // regexp belonging to current VAR
    $this->exp             = false; // exp associated to current VAR
    $this->inputData       = ''; // input data to be validated against XML config
    $this->confValidation  = true;  // validation of xml configuration file
    $this->file       	   = '';    // keeps track of an associated file (e.g. selection list)

    $xmlParser = xml_parser_create();
    xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, false); // preserve case
    xml_set_element_handler($xmlParser, array(&$this,"startElement"), array(&$this,"endElement"));
    xml_set_character_data_handler($xmlParser, array(&$this,"characterData"));

    if (xml_parse($xmlParser, $conf)) $this->conf = $this->newConf; // if ok replace with new
    else $this->error = sprintf("(conf_validate)XML Error: %s at line %d",
                                 xml_error_string(xml_get_error_code($xmlParser)),
                                 xml_get_current_line_number($xmlParser));

    xml_parser_free($xmlParser);
    return !$this->validationError;
  }
  

  /*
  Validate $data using rules defined in $conf
  */
  function validate($data) {
    $this->error           = '';
    $this->validationError = FALSE; // flag validation error
    $this->currentNode     = '';    // keep track of current node
    $this->newConf         = '';    // growing XML output string
    $this->var             = '';    // keeps track of VAR name we are looking at
    $this->type            = '';    // keeps track of VAR type we are looking at
    $this->regexp          = false; // regexp belonging to current VAR
    $this->exp             = false; // exp associated to current VAR
    $this->inputData       = $data; // input data to be validated against XML config
    $this->confValidation  = false; // no validation of xml configuration file
    $this->file       	   = '';    // keeps track of an associated file (e.g. selection list)

    if (!empty($data['formAction'])){
    	switch($data['formAction']){
    		case 'cancel':
    		return !$this->validationError;
    		break;
    		
    		case 'ok':
    		$this->newConf .= "<formAction><act>".$data['formAction']."</act>\n";
    		break;
    		case 'apply':
    		$this->newConf .= "<formAction><act>".$data['formAction']."</act>\n";
    		break;
    		
    		default:
    		$this->newConf .= "<formAction><act>".$data['formAction']."</act>\n";
    		//break;
    	}
    }

    $xmlParser = xml_parser_create();
    xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, false); // preserve case
    xml_set_element_handler($xmlParser, array(&$this,"startElement"), array(&$this,"endElement"));
    xml_set_character_data_handler($xmlParser, array(&$this,"characterData"));

    if (xml_parse($xmlParser, $this->conf)) $this->conf = $this->newConf; // if ok replace with new
    else $this->error = sprintf("(validate)XML Error: %s at line %d",
                                 xml_error_string(xml_get_error_code($xmlParser)),
                                 xml_get_current_line_number($xmlParser));
    /*
    print "<h2>Input data</h2>\n";
    print_r($data);
    print "<hr />";
    */
    if (!empty($data['formAction'])){
    	switch($data['formAction']){
    		case 'ok':
    		$this->conf .= "</formAction>\n";
 		$this->newConf .= "</formAction>\n";
    		break;
    		case 'apply':
    		$this->conf .= "</formAction>\n";
 		$this->newConf .= "</formAction>\n";
    		break;
    		default:
    		$this->conf .= "</formAction>\n";
    		$this->newConf .= "</formAction>\n";
    	}
    }
    xml_parser_free($xmlParser);
    if (!empty($this->error)){
    	print "<h2>XML Parse error:</h2>\n".$this->error."<hr />\n";
    	//print "<h2>generated data:</h2>\n".$this->newConf."<hr />\n";
    	//$this->conf = $this->newConf;
    }
    return !$this->validationError;
  }
  
  /*
  Handler when start of element is encountered
  */
  function startElement($parser, $name, $attributes) {
    $this->currentNode = $name; // keep track of current node
    switch($name) {
      case 'value':
      	if (!$this->confValidation) return; // ignore VALUE nodes
      	break;
      case 'var': // save var 'NAME' and 'TYPE' attributes
        $this->var  = $attributes['name'];
        $this->type = $attributes['type'];
        break;
    }
    $attributeString = ''; // concatenate attributes;
    foreach ($attributes as $key => $value) $attributeString .= $key."=\"".$value."\" ";
    $this->newConf .= "<$name $attributeString>";
  }

  /*
  Handler when end of element is encountered
  */
  function endElement($parser, $name) {
    $this->currentNode = ''; // reset current node
    switch($name) {
      case 'value':
      	if (!$this->confValidation) return; // ignore VALUE nodes
      	break; 
      case 'var': // end of VAR element     
        
        if ($this->confValidation){
        	if ($this->type == "select-file" || $this->type == "multiselect-file" ){
        		if (empty($this->file) || !file_exists($this->file)){
			  $this->newConf .= "<validation-error>cannot find selection file".$this->file." </validation-error/>\n";
			  $this->validationError = TRUE; // flag validation error
        		}
        		// read the file and generate the option list
        		$fseln = fopen($this->file,'r');
        		$contents = fread($fseln, filesize($this->file));
        		fclose($fseln);
        		// do some cleanup
        		$contents = str_replace("\r","",$contents);
			$contents = htmlentities($contents,ENT_QUOTES);
			$contents = str_replace('&','&amp;',$contents);
			// ... and more cleanup
			$sels = array();
			$sels =  explode("\n", $contents);
			for($j=0;$j<sizeof($sels);$j++)
				$sels[$j] =  trim($sels[$j]);
			$sels = array_unique($sels);
			// ... and generate the option list
			//foreach ($sels as $seln)
			for($j=0;$j<sizeof($sels);$j++)
			{
				$jp = $j+1;	// use values > 0! otherwise the 0-valued items are ignored ...
				$this->newConf .= "<option value=\"".$jp."\">".$sels[$j]."</option>\n";
			}

        	} // end if ($this->type == "select-file" || $this->type == "multiselect-file" )
        } // end if ($this->confValidation)
        else {
        
		if (!$this->isVarValid()) {
		  $this->newConf .= "<validation-error/>\n";
		  $this->validationError = TRUE; // flag validation error
		}

		$data = $this->inputData[$this->var];
		if (is_array($data)){
			foreach($data as $k => $elt){
				$data[$k] = htmlentities($elt,ENT_QUOTES);// 01-03-2005-10:59
				$data[$k] = str_replace('&','&amp;',$data[$k]);// 01-03-2005-10:59
			}
		}
		else {
			$data = htmlentities($data,ENT_QUOTES);// 01-03-2005-10:59
			$data = str_replace('&','&amp;',$data);// 01-03-2005-10:59
		}
		if (is_array($data)) $data = '<value>'.implode("</value>/n<value>", $data).'</value>';
		else $data = "<value>$data</value>";
		$this->newConf .= $data."\n";
        }
        break;
    }
    $this->newConf .= "</$name>\n";
  }

  /*
  Handler for when character data is encounered
  */
  function characterData($parser, $data) {
    if ($this->currentNode == '') return; // ignore whitespace
    if (!$this->confValidation && $this->currentNode == 'value') return; // ignore VALUE nodes
    if ($this->currentNode == 'validation-regexp') $this->regexp = $data; // regular expression for validation
    if ($this->currentNode == 'validation-exp') $this->exp = $data; // expression for validation
    if ($this->currentNode == 'file') $this->file = $data; // keep the associated param file
    $this->newConf .= $data;	//str_replace('&amp;','&',$data);	//$data;	//
 if ($this->verb && $this->currentNode == 'validation-exp' && !empty($data))
 	echo "<h3>validation-exp: ".$this->exp." data: ".$data."</h3>\n";
 }
  
  /*
  Validate current $var
  */
  function isVarValid() {
    $type   = $this->type;
    $var    = $this->var;
    $regexp = $this->regexp;
    $exp    = $this->exp;
    $data   = $this->inputData[$var];

    switch ($type) {
      case 'text':
        $test_value = $data;       
        break;
       
      case 'textarea':
        $test_value = $data;
        break;

      case 'select':
        $test_value = sizeof($data);
        break;
       
      case 'multiselect':
      case 'multiselect-file':
        $test_value = sizeof($data);
        $value = @implode(',', $data);
        break;
       
      case 'radio':
        $test_value = sizeof($data);
        break;
       
      case 'checkbox':
        $test_value = sizeof($data);
        $value = @implode(',', $data);
        break;
        
      case 'select-file':
        $test_value = sizeof($data);
        break;
      
      default:
        $test_value = $data;
    }
    $matches = array();
    if (!preg_match($regexp, $test_value,$matches))return FAlSE;
    elseif (empty($exp))return TRUE;
    else
    {
    	if($this->verb)echo "<h2>isVarValid-0: exp: ".$exp." </h2>\n";
    	$srch = array('matches','.lt.','.gt.','.eq.','.diff.');
    	$repl = array('\$matches','<','>','==','!='); // recover lt and gt signs etc - insert '$' in front of 'matches[i]'
    	$exp = str_replace($srch,$repl,$exp);	// insert '$' in front of 'matches[i]'
    	if($this->verb)echo "<h2>isVarValid-1: exp: ".$exp." </h2>\n";
    	$res = eval("return (".$exp.");");
    	if($this->verb)echo "<h2>isVarValid-2: resultat: $res</h2>\n";
    	if($this->verb)print_r($matches);
    	if($this->verb)echo "<br />\n";
    	return $res;
    }
  }
  
  /*
  Perform XSL transformation on $conf passing in $parameters
  */
  function xslTransform($xsl, $parameters) {
    $result       = '';
    $hasError     = FALSE;
    $this->error  = '';
    $this->output = '';
        
    $xh = xslt_create();
    $conf = str_replace('&','&amp;',$this->conf);	//01-03-05 11:37
    $args = array('/_xml' => $conf, '/_xsl' => $xsl);
    $result = xslt_process($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $args, $parameters);
    if ($result) $this->output = str_replace('&amp;','&',str_replace('&amp;','&',$result));	//str_replace('&amp;','&',$result);	//	//$result;	//01-03-05 11:14
    else {
      $this->error = "(xslTransform)XML Error: ".xslt_error($xh).". Code is ".xslt_errno($xh);
      $hasError = TRUE;
    }    
    xslt_free($xh);  
    return !$hasError;
  }
}
?>
Return current item: metaForm