#native_company# #native_desc#
#native_cta#

XSLTransform

By Melvyn Sopacua
on May 6, 2001

Version: 1.3.1

Type: Class

Category: HTML

License: GNU General Public License

Description: A modification of Justin Grant’s class to be compatible with the new API, in php 4.0.3 and higher.

Tested on WinNT 4SP6 and BSD unix, both with Sablotron 0.52 and PHP 4.0.4pl1.

Usage example is included in the comments section.

<?php
/******************************************************************************
Date:			Tue, 01 May 2001
Author:		 	Melvyn Sopacua
Description:	XSL Transformation using the 4.0.3+ API.
License:		GPL
Version:		1.4 (2001/05/05)
Comments:		Based on work done by Justin Grant @ phpbuilder.com.
Revisions:
	1.0		- Changed some function calls in Justin's files, for the new API.
	1.1		- Added CSV style logging in the error_handler.
	1.2		- Finally figured out how xslt_set_error_handler passes arguments.
	1.3		- Added 'XmlStrip' property and method to allow stripping of the
			  XML declaration at the start of the XSL transformation output,
			  so that files can be written as php includes.
	1.3.1	- Fixed resetting of the $errlog_opened variable
			- Added documentation about xsl:output method set to HTML
******************************************************************************/

/* 
	Usage example:
require('xsl_transform.class.php');
$xml_root="d:/server/xml-data";
$html_path=".";
$xml_file="$xml_root/wubba.xml";
$xsl_file="$xml_root/rsstest.xsl";
$html_file="$html_path/wubba.php";
$x=new XSLTransformer;
$ok=$x->setXml($xml_file);
if(!$ok)
{
	echo $x->getError();
	$x->destroy();
	exit;
}
$ok=$x->setXsl($xsl_file);
if(!$ok)
{
	echo $x->getError();
	$x->destroy();
	exit;
}	
$x->setXmlStrip();
$ok=$x->transform();
if(!$ok)
{
	echo $x->getError();
	$x->destroy();
	exit;
}
$html=$x->getOutput();
$x->destroy();
if(!($fp=fopen($html_file, "w")))
{
	$x->destroy();
	die("Error opening output file $html_file for writing. Check permissions.");
}
fwrite($fp, $html);
fclose($fp);
echo "OK";
*/



//___________________Since errors are trapped by reference we need this external
//_____________________________________________________________________function.

//___________Use variables if you're a change-it-all-around-all-the-time person.
//_________________You'll then need to globalize them in the xsl_error function.

define(XSL_ERR_LOG, "d:/server/logs/xslt_errors.log");
define(XSL_USE_ERR_LOG, 1);

set_time_limit(300);

$fp_errlog='';
$errlog_opened=0;

function xsl_error($errstr, $errno, $arr_msg)
{
	global $fp_errlog, $errlog_opened;
	if(!XSL_USE_ERR_LOG)
	{
		return true;
	}
	
	//_______________________________________________________We should log this.
	if(!$fp_errlog)
	{
		if(!$errlog_opened)
		{
			$fp_errlog=fopen(XSL_ERR_LOG, "a");
			$errlog_opened=1;
		}
		else
		{
			//__The only thing that comes to mind, is 1 not being equal to true.
			die("Error in logic or file-pointer conflicts.");
		}
	}
	//______At this point, returning false or true, does not make a difference,
	//_________at all, but since this is not expected behavior, this bit is made
	//________________________________________________to be forwards-compatible.
	//________One can argue, if a return false in this function, should generate
	//__________________________a php fatal error, since it is an error handler.
	//______On the other hand, one can argue, that the outcome of this function,
	//________________should determin the outcome of the transformation process.
	//____________At present the return value does neither, which is unexpected.
	if($arr_msg["msgtype"] != "error")
	{
		$line=sprintf(""%s","%s","%d","%s"n", date("d-m-Y H:i:s", time()), "warning", $arr_msg["line"], $arr_msg["msg"]);
		fwrite($fp_errlog, $line);
		return true;
	}
	else
	{
		$line=sprintf(""%s","%s","%d","%s"n", date("d-m-Y H:i:s", time()), "fatal", $arr_msg["line"], $arr_msg["msg"]);
		fwrite($fp_errlog, $line);
		return false;
	}
}


/*
	XSLTranformer -- Class to transform XML files using	
	XSL with the Sablotron libraries.Justin Grant (2000-07-30)
	Thanks to Bill Humphries for the original examples on	
	using the Sablotron module.
	Modified for new API by Melvyn Sopacua.
*/

class XSLTransformer
{
	var $xsl, $xml, $output, $error, $XmlStrip;

	function XSLTransformer()
	{
		$this->processor = xslt_create();
		$this->XmlStrip=false;
		xslt_set_error_handler("xsl_error");
	}
 /* Destructor */
	function destroy()
	{
		global $fp_errlog, $errlog_opened;
		
		//______________________________________Remember to close the error log!
		//____________________________________________And to reset the variable!
		if($errlog_opened)
		{
			fclose($fp_errlog);
			$errlog_opened=false;
		}
		xslt_free($this->processor);
	}
 /* output methods */
	function setOutput($string) 
	{
		$this->output = $string;
	}
	
	function getOutput() 
	{
		if($this->XmlStrip)
		{
			$this->output=ereg_replace("<?xml[^>]+>", "", $this->output);
		}
		return $this->output;
	}
 /* set methods */
	function setXml($uri) 
	{
		if($doc = new docReader($uri)) 
		{
			$this->xml = $doc->getString();
			return true;
		}
		else 
		{
			$this->setError("Could not open $xml");
			return false;
		}
	}
	function setXsl($uri) 
	{
		if($doc = new docReader($uri)) 
		{
			$this->xsl = $doc->getString();
			return true;
		}
		else 
		{
			$this->setError("Could not open $uri");
			return false;
		}
	}

	/*
		NOTE:
			the XmlStrip function provides a way to strip the XML declaration,
			so files written can be used as php includes, and php short-tags may
			still be used.
			Alternatively, you may use:
			<xsl:output omit-xml-declaration="yes"/>
			in your Xsl stylesheet. This however, would require two different,
			stylesheets for output as an xml file, or as a php include, while,
			the rest may be the same.
		
		NOTE 2:
			If you use processing instructions, like I do, to call PHP code,
			you cannot set the xsl output method to HTML.
			Setting the output method to HTML, will result in the closing tag
			for processing instructions to be '>' instead of '? >' (without the
			space). This will create parser errors in PHP.
			Example:
			<xsl:processing-instruction name="php">echo "This document: $REQ_URI";</xsl:processing-instruction>
	*/
	function setXmlStrip($boolean=true)
	{
		//__________Since the default is set to false, when calling this method, 
		//_______________________________people probably want to set it to true.
		$this->XmlStrip=$boolean;
	}
	
/* transform method */
	function transform() 
	{
		$return=@xslt_process($this->xsl, $this->xml, $outcome);
		$this->setOutput($outcome);
		return $return;
	}
/* Error Handling */
	//________________________________Does not handle the transformation errors.
	function setError($string) 
	{
		$this->error = $string;
	}
	function getError() 
	{
		return $this->error;
	}
}



/* docReader -- read a file or URL as a string */
/* test */
/*
	$docUri = new docReader('http://www.someurl.com/doc.html');
	echo $docUri->getString();
*/
class docReader {
	var $string; // public string representation of file
	var $type; // private URI type: 'file','url'
	var $bignum = 1000000;
 /* public constructor */
	function docReader($uri) { // returns integer
		$this->setUri($uri);
		$this->setType();
		$fp = fopen($this->getUri(),"r");
		if($fp)
		{ // get length
			if ($this->getType() == 'file')
			{
				$length = filesize($this->getUri());
			}
			else
			{
				$length = $this->bignum;
			}
				$this->setString(fread($fp,$length));
				return 1;
		}
		else 
		{
			return 0;
		}
	}
 /* determine if a URI is a filename or URL */
	function isFile($uri) { // returns boolean
		if (strstr($uri,'http://') == $uri) 
		{
			return false;
		}
		else 
		{
			return true;
		}
	}
 /* set and get methods */
	function setUri($string) 
	{
		$this->uri = $string;
	}
	function getUri() 
	{
		return $this->uri;
	}
	function setString($string) 
	{
		$this->string = $string;
	}
	function getString() 
	{
		return $this->string;
	}
	function setType() 
	{
		if ($this->isFile($this->uri)) 
		{
		 $this->type = 'file';
		}
		else 
		{
		 $this->type = 'url';
		}
	}
	function getType() 
	{
		return $this->type;
	}
}
?>