Location: PHPKode > scripts > XML Sapiens Processor > xml-sapiens-processor/mvc/view/sapiprocessor.php
<?PHP
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * PHP XML Sapiens Processor Library
 *
 * Library contains PHP classes, which you can use for XML Sapiens 
 * transformation of source data document
 * XML Sapiens transformation extends source data file on the grounds 
 * of specified logic model. Later on you can do a XSL-transformation 
 * with target data file. Thus you get separated data, UI-model and 
 * representation layers. 
 * In order to learn more about XML Sapiens technology 
 * reference to http://www.xmlsapiens.org
 *
 * PHP versions 4 
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
 * the PHP License and are unable to obtain it through the web, please
 * send a note to hide@address.com so we can mail you a copy immediately.
 *
 * @category   Processor
 * @package    SAPIPROCESSOR
 * @author     Max Baryshnikov <hide@address.com>
 * @author     Dmitry Sheiko <hide@address.com>
 * @copyright  2004-2005 XML Sapiens Team
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id:$
 * @link       http://pear.php.net/package/PackageName
 * @see        nothing
 * @since      File available since Release 1.0.0
 */


/**
* SAPI Core class. Needs to be extended with DAL.
* Parses XML Sapiens objects in the given template.
* @package kernel
* @author Max Baryshnikov aka Mephius<hide@address.com>
* @version 1.2 , 07.12.2004
* @since SAPID v.0.9
* @copyright (c) by Mephius
*/

// {{{ sapi_core
/**
 * PHP XML Sapiens Processor Class
 *
 * Needs to be extended with DAL.
 * Parses XML Sapiens objects in the given template.
 *
 * @category   Processor
 * @package    SAPIPROCESSOR
 * @author     Max Baryshnikov <hide@address.com>
 * @author     Dmitry Sheiko <hide@address.com>
 * @copyright  2004-2005 XML Sapiens Team
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version    CVS: $Id:$
 * @link       http://pear.php.net/package/PackageName
 * @see        nothing
 * @since      File available since Release 1.0.0
 */



class sapi_core{
	/**
	* Current value of the template
	* @var string (may be string or filepath)
	*/
	var $template="";

	/**
	* Current site access mode
	* @var string 
	*/
	var $access_mode="delivery";

	/**
	* Query containers parsing passed flag
	* @var boolean
	*/
	var $qc_passed=false;

	/**
	* Processing instructions parsing passed flag
	* @var boolean
	*/
	var $pi_passed=false;

	/**
	* Static Data Containers parsing passed flag
	* @var boolean
	*/
	var $sdc_passed=false;

	/**
	* Dynamic Data Containers parsing passed flag
	* @var boolean
	*/
	var $ddc_passed=false;

	/**
	* Value parsing passed flag
	* @var boolean
	*/
	var $val_passed=false;

	/**
	* Parser Environment
	* @var array
	*/
	var $env=array();

	/**
	* Parser Objects Content
	* @var array
	*/
	var $objects=array();

	/**
	* Found SAPI objects
	* @var array
	*/
	var $sapi=array();

	/**
	* Temporary array of parsed <sapi:code> instructions
	* @var array
	*/
	var $code=array();

	/**
	* Current element (used by XML Parser)
	* @var string
	*/
	var $element="";

	/**
	* Counter
	* @var integer
	*/
	var $count=0;

	/**
	* Code Array
	* @var array
	*/
	var $codearr=array();

	/**
	* Parser Stack value
	* @var array
	*/
	var $stack=array();

	/**
	* Current handler
	* @var string
	*/
	var $handler="";

	/**
	* Temporary Parser Environment
	* @var string
	*/
	var $thandler="";

	/**
	* Parser Mode
	* @var integer
	*/
	var $mode=0;

	/**
	* Errors' stack pointer
	* @var string
	*/
	var $error_pointer = 0;

	/**
	* Contains TRUE if reached end of errors list
	* @var string
	*/	
	var $eoel;

	/**
	* Errors' stack
	* @var array
	*/
	var $errors_stack = array();

	/**
	* Error code=>message sequences
	* @var array
	*/
	var $errors_msg = array(
	001 => "Can't open the file for reading",
	002 => "The CMS application not loaded",
	003 => "Source DATA-file not found",
	);


	/**
	* Current object name
	* @var string
	*/
	var $object_name = false;

	/**
	* Current object source
	* @var string
	*/
	var $object_source = false;




	/**
	* Class constructor
	* @param mixed $template initial value of template to parse
	* @param array $env initial environmant array
	* @param string $handler this instance handler	
	* @param string $name this instance name
	* @return object
	*/
	// {{{ sapi_core
	function sapi_core($xml_data= false, $template=false, $env=false, $handler="", $name="sapi_env"){

		if(!$env) $this->env=&$GLOBALS["env"]; else $this->env = $env;

		if(file_exists($xml_data)) $xml_data = $this->filecontent($xml_data);
		if(file_exists($template)) $template = $this->filecontent($template);

		$this->template=$template;
		if(preg_match("/rel=[\"\']sapi[\"\']/is", $this->template)) {
			preg_match_all("/<link\s+rel=[\"\']sapi[\"\']\s+href=[\"\']([a-zA-Z_\d\.\/\?\&]+)[\"\'].*?\/>/is", $this->template, $matches);
			if (count($matches[1])!=0) {

				// Load included XML Sapiens files
				foreach($matches[1] as $layer => $filename) {
					$data = $this->filecontent($filename);
					// Order XML Sapiens objects

					// Search for SDCs
					preg_match_all("/<sapi:sdc\s+name=[\"\']([a-zA-Z_\d\.\/\?\&]+)[\"\'][^>]*?>.*?<\/sapi:sdc>/is", $data, $lmatches);
					if($lmatches[1]) {
						foreach($lmatches[1] as $key => $tname) {
							$this->objects["sdc"][$tname] = '<sapi version="1.1" xmlns:sapi="http://www.xmlsapiens.org/spec/sapi.dtd">'.$lmatches[0][$key]."</sapi>";
						}
					}
					unset($lmatches);

					// Search for DDCs
					preg_match_all("/<sapi:ddc\s+name=[\"\']([a-zA-Z_\d\.\/\?\&]+)[\"\'][^>]*?>.*?<\/sapi:ddc>/is", $data, $lmatches);
					if($lmatches[1]) {
						foreach($lmatches[1] as $key => $tname) {
							$this->objects["ddc"][$tname] = '<sapi version="1.1" xmlns:sapi="http://www.xmlsapiens.org/spec/sapi.dtd">'.$lmatches[0][$key]."</sapi>";
						}
					}
					unset($lmatches);

					// Search for QCs
					preg_match_all("/<sapi:qc\s+name=[\"\']([a-zA-Z_\d\.\/\?\&]+)[\"\'][^>]*?>(.*?)<\/sapi:qc>/is", $data, $lmatches);
					if($lmatches[1]) {
						foreach($lmatches[1] as $key => $tname) {
							$this->objects["interface"][$tname] = $lmatches[2][$key];
						}
					}
					unset($lmatches);

					$this->template = str_replace($matches[0][$layer], "", $this->template);
				}
			}

		}


		if($name=="sapi_env") {
			$this->env=array_merge($this->init_stack($this->env), $this->env);
		}


		// Receiving source DATA-file
		if($xml_data) {
			//$this->define_error(003, $xml_data);
			$qp=xml_parser_create();
			xml_set_element_handler($qp, array(&$this, 'startElement'), array(&$this, 'endElement'));
			xml_set_character_data_handler($qp, array(&$this, 'characterData'));
			xml_parser_set_option($qp, XML_OPTION_SKIP_WHITE, 1);
			if(!xml_parse($qp, trim($xml_data))) {
				$this->define_error( xml_get_current_line_number($qp), $xml_data, xml_error_string(xml_get_error_code($qp)));
			}
			xml_parser_free($qp);
		}


		$this->handler=$handler;
		$this->name=$name;

		return $this;
	} // }}} sapi_core


	/**
	 * Appends to an array it's serialized copy
	 * $arr["one"]["two"] will be accessable as $arr["one.two"]
	 *
	 * @param array $arr array to serialize
	 * @return array
	 */
	function init_stack($arr){
		$flag_end=0;
		while ($flag_end==0){
			$flag_end=1;
			if($arr)
			foreach ($arr as $key=>$value) {
				if (is_array($value)) {
					$flag_end=0;
					unset($arr[$key]);
					foreach ($value as $k=>$v) $arr[$key . "." . $k]=$v;
				}
			}
		}
		return $arr;
	} // }}} init_stack


	/**
	 * Errors functions section
	 */


	/**
	 * End of Errors List Trigger
	 * @param void
	 * @return boolen
	 */
	function errors_eol() {
		return $this->eoel;
	}

	/**
	* Amount of errors
	* @param void
	* @return int
	*/
	function errors() {
		return count($this->errors_stack);
	}

	/**
	* Move error's pointer back
	* @param void
	* @return int
	*/
	function previous_error() {
		if($this->error_pointer>0) $this->error_pointer--;
		return $this->error_pointer;
	}

	/**
	* Move error's pointer forward
	* @param void
	* @return int
	*/
	function next_error() {
		if($this->error_pointer<$this->errors()-1) return ++$this->error_pointer;
		else { $this->eoel = true; return false; }
	}

	/**
	* Return name of file with error
	* @param void
	* @return int
	*/
	function error_file() {
		return $this->errors_stack[$this->error_pointer]["error_file"];
	}

	/**
	* Return number of string with error
	* @param void
	* @return int
	*/
	function error_string() {
		return $this->errors_stack[$this->error_pointer]["error_string"];
	}

	/**
	* Return error message
	* @param void
	* @return int
	*/
	function error() {
		return $this->errors_stack[$this->error_pointer]["error"];
	}

	/**
	* Define error
	* @param int/string $errorno - error number or error message
	* @param string $error_filename
	* @param string $error_string - number of string with error
	* @return void
	*/

	function define_error($errorno, $error_file = "Source", $error_string = 0) {
		if(is_string($errorno)) $error = $errorno;
		else $error = $this->errors_msg[$errorno];
		if(!$error) $error = "Undefined error";
		$this->errors_stack[] =  array(
		"error" => $error,
		"error_string" => (int)$error_string,
		"error_file" => $error_file,
		);
	}


	/**
	* Getting of file content by file name
	* @params string
	* @return string
	*/

	function filecontent($filename) {
		$fp=@fopen($filename, "r");
		if (!$fp) $this->define_error(001, $filenam, 0);
		else {
			$size=filesize($filename);
			$content=fread($fp, $size);
			fclose($fp);
		}
		return $content;
	}




	/**
	 * XML Parser functions
	 * @return $this->objects["qc"]
	 */	

	function startElement($parser, $name, $attrs)  {
		global $depth, $XMLTag;
		if(!isset($depth[$parser])) $depth[$parser] = 0;
		$XMLTag = $name; $depth[$parser]++;
	}

	function endElement($parser, $name) {
		global $depth, $XMLTag;
		$XMLTag = false; $depth[$parser]--;
	}

	function characterData($parser, $data) {
		global $depth, $XMLTag;
		if(trim($data)) {
			$this->objects["qc"][strtolower($XMLTag)]=$data;
			$this->objects["qc"][$XMLTag]=$data;
		}
	}



	/**
	 * XML Parser start element handler
	 *
	 * @param object $parser XML Parser Resourse ID
	 * @param string $name element name
	 * @param array $attribs element attributes
	 */
	function sapi_start($parser, $name, $attribs)
	{
		$this->element=$name;
		if ($name=="SAPI:WHEN" and !isset($attribs["EXP"])) {
			$this->code.='if ';
		}elseif ($name=="SAPI:WHEN" and $attribs["EXP"]){
			$data=preg_replace("/([a-zA-Z_\d\.]+)\.value/is", "\$this->env[\"\\1\"]", $attribs["EXP"]);
			$this->code.="if (".preg_replace("/(([a-z\.]{1,})(\[(\d+)\])?|\s)\.value/is", "\$this->env[\"\\2.\\4\"]", $data).") {\n";
		}elseif ($name=="SAPI:FOR-EACH"){

			if(!function_exists(preg_replace("/(.*)\(.*\)$/isU", "\\1", $attribs["SELECT"]))) $this->define_error(002, $this->object_source, 0);

			$this->code.="
			if(!function_exists(\"".preg_replace("/(.*)\(.*\)$/isU", "\\1", $attribs["SELECT"])."\")) return false;
			\$" . $attribs["NAME"] . "=" . $attribs["SELECT"] . ";
			if(is_array(\$".$attribs["NAME"]."))
			foreach (\$".$attribs["NAME"]." as \$key=>\$value){
				if(!strpos(\"".$attribs["NAME"]."\", \$this->handler)) {
					\$th=\$this->handler;
					\$this->handler.=\".".$attribs["NAME"]."\";
					\$this->thandler.=\".this\";
				}

				\$tenv[\$this->handler]=\$value;
				\$tenv[\$this->thandler]=\$value;
				\$arr[\$th][\"".$attribs["NAME"]."\"]=\$value;
			
				foreach(\$value as \$k=>\$v) {
					\$cl_key[\$th][\"".$attribs["NAME"]."\"][\$k]=\"\";
					\$cl_key[\$this->handler . \".\" . \$k]=\"\";
					\$cl_key[\$this->thandler . \".\" . \$k]=\"\";
				}
				
				\$this->env=array_merge(\$this->env, \$cl_key);
				\$newarr=array_merge_recursive(\$this->init_stack(\$tenv), \$arr);
				
				\$this->env=array_merge(\$this->env, \$newarr);\n
				unset(\$newarr);
				unset(\$arr);";
		}elseif ($name=="SAPI:CODE"){
			$this->code.="
		ob_start(); 
		echo '" . addcslashes(stripcslashes($this->codearr[$this->count]), "'") . "';
		\$iter_stack=ob_get_contents();\nob_end_clean();
		\$tmp_sapi_obj =& new sapi_core(false, stripslashes(\$iter_stack), \$this->env, \$this->handler, \"tmp_sapi_env\");
		\$this->stack.=\$tmp_sapi_obj->process();";
			$this->count++;
		}
	} // }}} sapi_start

	/**
	 * XML Parser end element handler
	 *
	 * @param object $parser XML Parser Resourse ID
	 * @param string $name element name
	 */
	function sapi_end($parser, $name)
	{
		if ($name=="SAPI:FOR-EACH") $this->code .= "\$this->handler=preg_replace(\"/(.*)(\.[a-zA-z\d]+)$/\", \"\\\\1\", \$this->handler);
		\$this->thandler=preg_replace(\"/\.this$/\", \"\", \$this->thandler);";
		if ($name=="SAPI:FOR-EACH" or $name=="SAPI:WHEN") {
			$this->code.="\n}\n";
		}
	} // }}} sapi_end

	/**
	 * XML Parser character data handler
	 *
	 * @param object $parser XML Parser Resourse ID
	 * @param string $name element name
	 */
	function sapi_data($parser, $data)
	{
		if ($this->element=="SAPI:EXP" and trim($data)!="") {
			$data=preg_replace("/([a-zA-Z_\d\.]+)\.value/is", "\$this->env[\"\\1\"]", $data);
			$this->code.="(".preg_replace("/(([a-z\.]{1,})(\[(\d+)\])?|\s)\.value/is", "\$this->env[\"\\2.\\4\"]", $data).") {\n";
		}
	} // }}} sapi_data


	/**
	 * Initializes sapi containers from file
	 *
	 * @param string $filename
	 */
	function sapi_init($name, $type){
		if( !isset( $this->objects[$type][$name] ) ) { $this->define_error("Object $type.$name.value is not found"); return false; }
		$this->object_name = $name;
		$this->object_source = false;
		// Choose operations of certain object type
		switch($type) {
			case "sdc":
			case "ddc":
			if(preg_match("/<sapi\s/is", $this->objects[$type][$name])) {
				$sapi = $this->objects[$type][$name];
			} else {
				$sapi = $this->filecontent($this->objects[$type][$name]);
				$this->object_source = $this->objects[$type][$name];
			}
			break;
			default:
			$sapi = $this->objects[$type][$name];
		}
		if(!$this->object_source) $this->object_source = "The '".$name."' object";

		preg_match_all("/<sapi:sdc (.*)>(.*)<\/sapi:sdc>/isU", $sapi, $tsdc, PREG_PATTERN_ORDER);
		preg_match_all("/<sapi:ddc (.*)>(.*)<\/sapi:ddc>/isU", $sapi, $tddc, PREG_PATTERN_ORDER);
		unset($sapi);
		$sapi_containers=array_merge(/*$tqc,*/ $tsdc, $tddc);
		unset($tqc);
		unset($sdc);
		unset($ddc);

		if (is_array($sapi_containers))
		foreach ($sapi_containers as $k=>$value){
			foreach ($value as $key=>$container) {
				$p = xml_parser_create();
				xml_parser_set_option($p, XML_OPTION_CASE_FOLDING, 0);
				xml_parser_set_option($p, XML_OPTION_SKIP_WHITE, 1);
				xml_parse_into_struct($p, $container, $values, $tags);
				xml_parser_free($p);
				foreach ($tags as $tag=>$index) {

					if ($tag=="sapi:sdc") {
						$data=$values[$index[0]]["attributes"];
						$data["value"]=$sapi_containers[$k+2][$key];
						$this->sapi["sdc"][$data["name"]]=$data;
					}
					if ($tag=="sapi:ddc") {
						$data=$values[$index[0]]["attributes"];
						preg_match("/<sapi:name>(.*)<\/sapi:name>/isU", $sapi_containers[$k+2][$key], $name);
						preg_match("/<sapi:email>(.*)<\/sapi:email>/isU", $sapi_containers[$k+2][$key], $email);
						preg_match("/<sapi:url>(.*)<\/sapi:url>/isU", $sapi_containers[$k+2][$key], $url);
						preg_match("/<sapi:comments>(.*)<\/sapi:comments>/isU", $sapi_containers[$k+2][$key], $comment);
						$sapi_containers[$k+2][$key]=preg_replace("/<sapi:author>(.*)<\/sapi:author>/isU", "", $sapi_containers[$k+2][$key]);
						$sapi_containers[$k+2][$key]=preg_replace("/<sapi:comments>(.*)<\/sapi:comments>/isU", "", $sapi_containers[$k+2][$key]);
						$data["author"]["name"]=$name[1];
						$data["author"]["email"]=$email[1];
						$data["author"]["url"]=$url[1];
						$data["comments"]=$comment[1];
						$ddc_body=$sapi_containers[$k+2][$key];
						preg_match_all("/<sapi:code.*>(.*)<\/sapi:code>/isU", $ddc_body, $tcode, PREG_PATTERN_ORDER);
						$this->codearr=$tcode[1];
						$ddc_body=preg_replace("/<sapi:code(.*)>/isU", "<sapi:code\\1><![CDATA[", $ddc_body);
						$ddc_body=str_replace("</sapi:code>", "]]></sapi:code>", $ddc_body);
						$sp=xml_parser_create();
						xml_set_element_handler($sp, array(&$this, 'sapi_start'), array(&$this, 'sapi_end'));
						xml_set_character_data_handler($sp, array(&$this, 'sapi_data'));
						xml_parser_set_option($sp, XML_OPTION_SKIP_WHITE, 1);
						$this->count=0;
						$this->code="";
						if(!xml_parse($sp, trim($ddc_body))) {
							if(DEBUG){
								$ddc_body=str_replace("<![CDATA[", "", $ddc_body);
								$ddc_body=str_replace("]]>", "", $ddc_body);
								$arr=explode("\n", trim($ddc_body));
								$code='<span style="color: red;"><b>Error:</b>&nbsp;' . xml_error_string(xml_get_error_code($sp)) . " at line ".xml_get_current_line_number($sp)."</span><br /><br />";
								foreach ($arr as $key=>$line) {
									if($key==xml_get_current_line_number($sp)-1) $code.='<div style="background-color: #FF9797"><div style="float: left; clear: left; width: 20px; background-color: #dfdfdf;">'.($key+1).".</div>".str_replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;", htmlentities($line))."</div>"; else $code.='<div style="float: left; clear: left; width: 20px; background-color: #dfdfdf;">'.($key+1).".</div>".str_replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;", htmlentities($line))."<br />";
								}
								$this->env["sapi_error_cnt"]++;
								$data["exec"]="\$error=<<<EOC\n<div style=\"border: 1px solid red; color: white; background-color: red; font-weight: bold; cursor: pointer;\" align=\"center\" onclick=\"if(document.getElementById('sapi_error_".$this->env["sapi_error_cnt"]."').style.display!='block') document.getElementById('sapi_error_".$this->env["sapi_error_cnt"]."').style.display='block'; else document.getElementById('sapi_error_".$this->env["sapi_error_cnt"]."').style.display='none';\">SAPI Error #".$this->env["sapi_error_cnt"]."</div><div id=\"sapi_error_".$this->env["sapi_error_cnt"]."\" style=\"display: none;margin-top: 2px; position: absolute; background-color: #efefef; width: 700px; border: 1px solid Gray; padding: 5px;\">" . $code."</div>\nEOC;\n \$this->stack=\$error;";
							}else $data["exec"]="";
						}else{
							$data["exec"]=trim($this->code);
						}
						xml_parser_free($sp);

						$this->sapi["ddc"][$data["name"]]=$data;

					}
				}
			}
		}
		unset($sapi_containers);
	} // }}} sapi_init


	/**
	 * Loads query container data
	 *
	 * @param string $name
	 * @param string $query_type
	 * @return string
	 */
	function load_qc_data($name){
		$this->object_name = $name;
		$this->object_source = false;
		$data = (isset($this->objects["qc"][$name])?$this->objects["qc"][$name]:false);
		if($this->access_mode!="delivery" and $this->handler!="interface_trans" and isset($this->objects["interface"][$name])) {
			$tmp_sapi_obj =& new sapi_core("<$name><![CDATA[$data]]></$name>", $this->objects["interface"][$name], false, "interface_trans" );
			$data = $tmp_sapi_obj->process();
			unset($tmp_sapi_obj);
		}

		if(isset($data)) return trim($data); else  return "";
	} // }}} load_qc_data



	/**
	 * Checks current value of template for incapsulated Query Containers
	 *
	 */
	function check_qc(){
		$this->qc_passed=0;
		while ($this->qc_passed==0) {
			$this->qc_passed=1;
			preg_match_all("/<sapi:apply\s+name=\"(qc\.[a-zA-Z\.\d\-_]+)\"\s*?\/>/isU", $this->template, $matches);
			if (count($matches[1])!=0) {
				$qc=$matches[1];
				$this->qc_passed=0;
				foreach ($qc as $c=>$name) {
					$env_name=preg_replace("/qc\.(.*)\.value/", "\\1", trim($name));
					$data=$this->load_qc_data($env_name);
					$this->template=preg_replace("/<sapi:apply\s+name=\"$name\"\s*?\/>/isU", $data, $this->template);
					$GLOBALS["qc"][$name]=1;
				}
			}
			unset($matches);
		}

	} // }}} check_qc

	/**
	 * Checks current value of template for incapsulated Processing Instructions
	 *
	 */
	function check_pi(){
		while ($this->pi_passed==0) {
			$this->pi_passed=1;
			preg_match_all("/(<\?SYSTEM(.*)\?>)/isU", $this->template, $matches, PREG_PATTERN_ORDER);
			if (count($matches[0])!=0) {
				$pi=$matches[1];
				unset($matches);
				$this->pi_passed=0;
				$this->qc_passed=0;
				unset($patterns);
				foreach ($pi as $eval_code) {
					$patterns[]=$eval_code;
					$code=preg_replace("/(<\?SYSTEM)(.*)(\?>)/isU", "\\2", $eval_code);
					ob_start();
					if(@eval($code . " return true;")) $replacements[]=ob_get_contents(); else $replacements[]=$this->compile_error($code);
					ob_end_clean();
				}
				foreach ($patterns as $key=>$pattern) {
					$this->template=str_replace($pattern, $replacements[$key], $this->template);
				}
			}
		}
	} // }}} check_pi

	/**
	 * Checks current value of template for incapsulated Static Data Containers
	 *
	 */
	function check_sdc(){
		$this->sdc_passed=0;
		while ($this->sdc_passed==0) {
			$this->sdc_passed=1;
			preg_match_all("/<sapi:apply\s+name=\"(sdc\.[a-zA-Z\.\d\-_]+)\"\s+\/>/isU", $this->template, $matches);
			if (count($matches[1])!=0) {
				$sdc=$matches[1];
				unset($matches);
				$this->sdc_passed=0;
				$this->qc_passed=0;
				$this->pi_passed=0;
				foreach ($sdc as $c=>$name) {
					$name=preg_replace("/sdc\.(.*)\.value/i", "\\1", trim($name));
					//Load container
					$this->sapi_init($name, "sdc");
					if(isset($this->sapi["sdc"][$name]["value"]))
					$data=$this->sapi["sdc"][$name]["value"];
					else
					$data = false;
					if ($this->mode==1 and !$GLOBALS["sdc"][$name]) {//Only if this container isn't processed yet
					$this->names[]=$name;
					$this->sapi["sdc"][$name]["passed"]=1;
					$type="sdc";
					ob_start();
					include($GLOBALS["root_path"] . "usr/system/editor.php");
					$data=ob_get_contents();
					ob_end_clean();
					}
					$this->template=preg_replace("/<sapi:apply\s+name=\"sdc.$name.value\"\s+\/>/isU", $data, $this->template);
					$GLOBALS["sdc"][$name]=1;
				}
			}
		}
	} // }}} check_sdc

	/**
	 * Checks current value of template for Dynamic Data Containers
	 *
	 */
	function check_ddc(){
		$this->ddc_passed=0;
		while ($this->ddc_passed==0) {
			$this->ddc_passed=1;
			preg_match_all("/<sapi:apply\s+name=\"(ddc\.[a-zA-Z\.\d_\-]+)\"\s*(redirect=\"([a-zA-Z\d_]+)\")?\s*(\/>|>\s*(.*)\s*<\/sapi:apply>){1}/isU", $this->template, $matches);
			if (count($matches[1])!=0) {
				$ddc=$matches[1];
				$params=$matches[5];
				$redirect=$matches[3];
				unset($matches);
				$this->ddc_passed=0;
				foreach ($ddc as $c=>$name) {
					$env_name=preg_replace("/ddc\.(.*)\.value/i", "\\1", trim($name));
					//Load container
					$this->sapi_init($env_name, "ddc");
					$this->handler=$env_name;
					$this->thandler="this";
					$this->stack="";
					if($params[$c]){
						preg_match_all("/<sapi:param\s+name=\"([a-zA-Z\d_]+)\">(.*)<\/sapi:param>/isU", $params[$c], $matches);
						if(is_array($matches[1]))
						foreach ($matches[1] as $kp=>$param) $this->env[$this->handler.".param.".$param]=$this->env[$this->thandler.".param.".$param]=$matches[2][$kp];
					}


					eval("\n " . $this->sapi["ddc"][$env_name]["exec"] . "");

					unset($this->sapi["ddc"][$env_name]["exec"]);
					if(is_array($tenv)){
						foreach ($tenv as $key=>$entry)
						if (is_array($entry)) foreach ($entry as $key2=>$value)
						unset($this->env[$key.".".$key2]);
					}
					unset($this->env[$this->handler]);
					if($redirect[$c]) {
						$this->env[$redirect[$c]]=$this->stack;
						$this->stack="";
					}
					$this->template=preg_replace("/<sapi:apply\s+name=\"$name\"\s*(redirect=\".*\")?\s*(\/>|>.*<\/sapi:apply>)/isU", $this->stack, $this->template);
				}
			}
		}
	} // }}} check_ddc

	/**
	 * Checks current value of template for Environment Variables
	 *
	 */
	function check_val(){
		$this->val_passed=0;
		while ($this->val_passed==0) {
			$this->val_passed=1;
			preg_match_all("/<sapi:apply\s+name=\"([a-zA-Z_\d\.\-]+)\.value\"\s+\/>/is", $this->template, $matches);
			foreach ($matches[1] as $key=>$value) if (strpos($value, "ddc.")!==false or strpos($value, "sdc.")!==false or strpos($value, "qc.")!==false) unset($matches[1][$key]);
			if (count($matches[1])!=0) {
				$val=$matches[1];
				unset($matches);
				$this->qc_passed=0;
				$this->pi_passed=0;
				$this->sdc_passed=0;
				$this->ddc_passed=0;
				$this->val_passed=0;
				foreach ($val as $c=>$name) {
					if(!isset($this->env[$name])) $this->env[$name]="";
					$this->template=preg_replace("/<sapi:apply\s+name=\"$name\.value\"\s+\/>/isU", $this->env[$name], $this->template);
				}

			}

			$res=preg_match_all("/sapi:([a-zA-Z]+)=\"([a-zA-Z_\d\.\-\(\),'\/\s]+)\"/isU", $this->template, $matches);
			if(is_array($matches[2]) and $res!=0)	{
				foreach ($matches[2] as $k=>$aexp) {
					if (preg_match("/lt\s?\(|leq\s?\(|gt\s?\(|geq\s?\(|eq\s?\(|neq\s?\(|add\s?\(|substract\s?\(multiply\s?\(|divide\s?\(/isU", $aexp)) {
						preg_match_all("/([a-zA-Z\d\.\-_]+)\.value/is", $aexp, $m);
						if(is_array($m[1])){
							foreach ($m[1] as $key=>$value) {
								$patterns[$key]=$m[0][$key];
								$replacements[$key]=$this->env[$m[1][$key]];
							}
							$aexp=str_replace($patterns, $replacements, $aexp);
						}
						@eval("\$rr=" . $aexp . ";");

						$this->template=str_replace($matches[0][$k], $matches[1][$k] . '="'.$rr.'"', $this->template);
						unset($matches[0][$k]);
						unset($matches[1][$k]);
						unset($matches[2][$k]);
					}
				}


				$patterns=array();
				$replacements=array();
				if (is_array($matches[1]))
				foreach ($matches[1] as $key=>$value) {
					$patterns[$key]=$matches[0][$key];
					$tvar = strtoupper(str_replace(array("qc.", ".value"), "", $matches[2][$key]));

					if(strstr($matches[2][$key], "qc")) $replacements[$key]=$value . '="'.str_replace("\"", "&quot;", (isset($this->env["page.".$tvar])?$this->env["page.".$tvar]:$this->load_qc_data($tvar))).'"';
					else
					$replacements[$key]=$value . '="'.$this->env[str_replace(".value", "", $matches[2][$key])].'"';
				}
				$this->template=str_replace($patterns, $replacements, $this->template);
			}
		}
	} //\\check_val


	/**
	 * Builds document contents from template and sapi description
	 * 
	 * @return string
	 */
	function process(){

		while (!$this->qc_passed or !$this->pi_passed or !$this->sdc_passed or !$this->ddc_passed or !$this->val_passed) {
			$this->check_qc();
			$this->check_pi();
			if ($this->qc_passed and $this->pi_passed) {
				$this->check_sdc();
			}
			if ($this->qc_passed and $this->pi_passed and $this->sdc_passed) {
				$this->check_ddc();
			}
			if ($this->qc_passed and $this->pi_passed and $this->sdc_passed and $this->ddc_passed) {
				$this->check_val();
			}
		}
		return $this->template;
	} // }}} process

	/**
	 * Processes evaluation error
	 *
	 * @param $code string
	 * @return string
	 */
	function compile_error($code){
		$this->env["error_cnt"]++;
		if(DEBUG)
		return '<div style="border: 1px solid red; color: white; background-color: red; font-weight: bold; cursor: pointer;" align="center" onclick="if(document.getElementById(\'error_'.$this->env["error_cnt"].'\').style.display!=\'block\') document.getElementById(\'error_'.$this->env["error_cnt"].'\').style.display=\'block\'; else document.getElementById(\'error_'.$this->env["error_cnt"].'\').style.display=\'none\';">PHP error #'.$this->env["error_cnt"].'</div><div id="error_'.$this->env["error_cnt"].'" style="position: absolute; width: 700px; background-color: #dfdfdf; border: 1px solid red; padding: 3px; display: none; margin-top: 2px;"><b>Eval\'ed code:</b><br /><br />'.highlight_string("<?\n".$code."\n?>", true).'</div>';
		else return "";
	} // }}} compile_error


} // }}} sapi_core

?>
Return current item: XML Sapiens Processor