/symphony/lib/toolkit/class.xsltprocess.php
PHP | 388 lines | 323 code | 10 blank | 55 comment | 1 complexity | 3e75aac662c7add895d1c69062820f1e MD5 | raw file
- <?php
- /**
- * @package toolkit
- */
- /**
- * The `XSLTProcess` class is responsible for taking a chunk of XML
- * and applying an XSLT stylesheet to it. Custom error handlers are
- * used to capture any errors that occurred during this process, and
- * are exposed to the `ExceptionHandler`'s for display to the user.
- */
- class XSLTProcess
- {
- /**
- * An array of all the parameters to be made available during the XSLT
- * transform
- * @var array
- */
- protected $_param = array();
- /**
- * An array of the PHP functions to be made available during the XSLT
- * transform
- * @var array
- */
- protected $_registered_php_functions = array();
- /**
- * Any errors that occur during the transformation are stored in this array.
- * @var array
- */
- private $_errors = array();
- /**
- * The last context, i.e. xml data that the system uses right now.
- * Used when trapping errors, to be able to generate debug info.
- * @var string
- */
- private $_lastContext = null;
- /**
- * A path where the XSLTProc will write its profiling information.
- *
- * @var string
- */
- private $profiling = null;
- /**
- * Sets the parameters that will output with the resulting page
- * and be accessible in the XSLT. This function translates all ' into
- * `'`, with the tradeoff being that a <xsl:value-of select='$param' />
- * that has a ' will output `'` but the benefit that ' and " can be
- * in the params
- *
- * @link http://www.php.net/manual/en/xsltprocessor.setparameter.php#81077
- * @param array $param
- * An associative array of params for this page
- */
- public function setRuntimeParam(array $param)
- {
- $this->_param = str_replace("'", "'", $param);
- }
- /**
- * Allows the registration of PHP functions to be used on the Frontend
- * by passing the function name or an array of function names
- *
- * @param mixed $function
- * Either an array of function names, or just the function name as a
- * string
- */
- public function registerPHPFunction($function)
- {
- if (is_array($function)) {
- $this->_registered_php_functions = array_unique(
- array_merge($this->_registered_php_functions, $function)
- );
- } else {
- $this->_registered_php_functions[] = $function;
- }
- }
- /**
- * Checks if there is an available `XSLTProcessor`
- *
- * @return boolean
- * true if there is an existing `XSLTProcessor` class, false otherwise
- */
- public static function isXSLTProcessorAvailable()
- {
- return (class_exists('XSLTProcessor') || function_exists('xslt_process'));
- }
- /**
- * This function will take a given XML file, a stylesheet and apply
- * the transformation. Any errors will call the error function to log
- * them into the `$_errors` array
- *
- * @see toolkit.XSLTProcess#__error()
- * @see toolkit.XSLTProcess#__process()
- * @param string $xml
- * The XML for the transformation to be applied to
- * @param string $xsl
- * The XSL for the transformation
- * @return string|boolean
- * The string of the resulting transform, or false if there was an error
- */
- public function process($xml, $xsl)
- {
- // dont let process continue if no xsl functionality exists
- if (!XSLTProcess::isXSLTProcessorAvailable()) {
- return false;
- }
- $XSLProc = new XSLTProcessor;
- if (!empty($this->_registered_php_functions)) {
- $XSLProc->registerPHPFunctions($this->_registered_php_functions);
- }
- if (!empty($this->profiling)) {
- $XSLProc->setProfiling($this->profiling);
- }
- $result = $this->__process(
- $XSLProc,
- $xml,
- $xsl,
- $this->_param
- );
- unset($XSLProc);
- return $result;
- }
- /**
- * Uses `DOMDocument` to transform the document. Any errors that
- * occur are trapped by custom error handlers, `trapXMLError` or
- * `trapXSLError`.
- *
- * @param XSLTProcessor $XSLProc
- * An instance of `XSLTProcessor`
- * @param string $xml
- * The XML for the transformation to be applied to
- * @param string $xsl
- * The XSL for the transformation
- * @param array $parameters
- * An array of available parameters the XSL will have access to
- * @return string
- */
- private function __process(XSLTProcessor $XSLProc, $xml, $xsl, array $parameters = array())
- {
- // Create instances of the DOMDocument class
- $xmlDoc = new DOMDocument;
- $xslDoc = new DOMDocument;
- // Set up error handling
- if (function_exists('ini_set')) {
- $ehOLD = ini_set('html_errors', false);
- }
- // Load the xml document
- $this->_lastContext = $xml;
- set_error_handler(array($this, 'trapXMLError'));
- // Prevent remote entities from being loaded, RE: #1939
- $elOLD = libxml_disable_entity_loader(true);
- // Remove null bytes from XML
- $xml = str_replace(chr(0), '', $xml);
- $xmlDoc->loadXML($xml, LIBXML_NONET | LIBXML_DTDLOAD | LIBXML_DTDATTR | defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0);
- libxml_disable_entity_loader($elOLD);
- // Must restore the error handler to avoid problems
- restore_error_handler();
- // Load the xsl document
- $this->_lastContext = $xsl;
- set_error_handler(array($this, 'trapXSLError'));
- // Ensure that the XSLT can be loaded with `false`. RE: #1939
- // Note that `true` will cause `<xsl:import />` to fail.
- $elOLD = libxml_disable_entity_loader(false);
- $xslDoc->loadXML($xsl, LIBXML_NONET | LIBXML_DTDLOAD | LIBXML_DTDATTR | defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0);
- libxml_disable_entity_loader($elOLD);
- // Load the xsl template
- $XSLProc->importStyleSheet($xslDoc);
- // Set parameters when defined
- if (!empty($parameters)) {
- General::flattenArray($parameters);
- $XSLProc->setParameter('', $parameters);
- }
- // Must restore the error handler to avoid problems
- restore_error_handler();
- // Start the transformation
- set_error_handler(array($this, 'trapXMLError'));
- $processed = $XSLProc->transformToXML($xmlDoc);
- // Restore error handling
- if (function_exists('ini_set') && isset($ehOLD)) {
- ini_set('html_errors', $ehOLD);
- }
- // Must restore the error handler to avoid problems
- restore_error_handler();
- $this->_lastContext = null;
- return $processed;
- }
- /**
- * That validate function takes an XSD to valid against `$xml`
- * returning boolean.
- *
- * @since Symphony 2.3
- * @param string $xsd
- * The XSD to validate against
- * @param string $xml
- * The XML to validate
- * @return boolean
- * Returns true if the `$xml` validates against `$xsd`, false otherwise.
- * If false is returned, the errors can be obtained with `XSLTProcess->getErrors()`
- */
- public function validate($xsd, $xml)
- {
- if (is_null($xsd) || is_null($xml)) {
- return false;
- }
- // Create instances of the DOMDocument class
- $xmlDoc = new DOMDocument;
- // Set up error handling
- if (function_exists('ini_set')) {
- $ehOLD = ini_set('html_errors', false);
- }
- // Load the xml document
- $this->_lastContext = $xml;
- set_error_handler(array($this, 'trapXMLError'));
- $elOLD = libxml_disable_entity_loader(true);
- $xmlDoc->loadXML($xml, LIBXML_NONET | LIBXML_DTDLOAD | LIBXML_DTDATTR | defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0);
- libxml_disable_entity_loader($elOLD);
- // Must restore the error handler to avoid problems
- restore_error_handler();
- // Validate the XML against the XSD
- $this->_lastContext = $xsd;
- set_error_handler(array($this, 'trapXSDError'));
- $result = $xmlDoc->schemaValidateSource($xsd);
- // Restore error handling
- if (function_exists('ini_set') && isset($ehOLD)) {
- ini_set('html_errors', $ehOLD);
- }
- // Must restore the error handler to avoid problems
- restore_error_handler();
- $this->_lastContext = null;
- return $result;
- }
- /**
- * A custom error handler especially for XML errors.
- *
- * @link http://au.php.net/manual/en/function.set-error-handler.php
- * @param integer $errno
- * @param integer $errstr
- * @param integer $errfile
- * @param integer $errline
- */
- public function trapXMLError($errno, $errstr, $errfile, $errline)
- {
- $this->__error($errno, str_replace('DOMDocument::', null, $errstr), $errfile, $errline, 'xml');
- }
- /**
- * A custom error handler especially for XSL errors.
- *
- * @link http://au.php.net/manual/en/function.set-error-handler.php
- * @param integer $errno
- * @param integer $errstr
- * @param integer $errfile
- * @param integer $errline
- */
- public function trapXSLError($errno, $errstr, $errfile, $errline)
- {
- $this->__error($errno, str_replace('DOMDocument::', null, $errstr), $errfile, $errline, 'xsl');
- }
- /**
- * A custom error handler especially for XSD errors.
- *
- * @since Symphony 2.3
- * @link http://au.php.net/manual/en/function.set-error-handler.php
- * @param integer $errno
- * @param integer $errstr
- * @param integer $errfile
- * @param integer $errline
- */
- public function trapXSDError($errno, $errstr, $errfile, $errline)
- {
- $this->__error($errno, str_replace('DOMDocument::', null, $errstr), $errfile, $errline, 'xsd');
- }
- /**
- * Writes an error to the `$_errors` array, which contains the error information
- * and some basic debugging information.
- *
- * @link http://au.php.net/manual/en/function.set-error-handler.php
- * @param integer $number
- * @param string $message
- * @param string $file
- * @param string $line
- * @param string $type
- * Where the error occurred, can be either 'xml', 'xsl' or `xsd`
- */
- public function __error($number, $message, $file = null, $line = null, $type = null)
- {
- $this->_errors[] = array(
- 'number' => $number,
- 'message' => $message,
- 'file' => $file,
- 'line' => $line,
- 'type' => $type,
- 'context' => $this->_lastContext,
- );
- }
- /**
- * Returns boolean if any errors occurred during the transformation.
- *
- * @see getError
- * @return boolean
- */
- public function isErrors()
- {
- return (!empty($this->_errors) ? true : false);
- }
- /**
- * Provides an Iterator interface to return an error from the `$_errors`
- * array. Repeat calls to this function to get all errors
- *
- * @param boolean $all
- * If true, return all errors instead of one by one. Defaults to false
- * @param boolean $rewind
- * If rewind is true, resets the internal array pointer to the start of
- * the `$_errors` array. Defaults to false.
- * @return array
- * Either an array of error array's or just an error array
- */
- public function getError($all = false, $rewind = false)
- {
- if ($rewind) {
- reset($this->_errors);
- }
- return ($all ? $this->_errors : each($this->_errors));
- }
- /**
- * Gets the current profiling file path
- *
- * @return string
- */
- public function getProfiling()
- {
- return $this->profiling;
- }
- /**
- * Sets the current profiling file path
- *
- * @param $profiling string
- * The file path
- */
- public function setProfiling($profiling)
- {
- $this->profiling = $profiling;
- }
- }