PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/symphony/lib/toolkit/class.xsltprocess.php

https://github.com/bauhouse/sym-designprojectx
PHP | 350 lines | 137 code | 47 blank | 166 comment | 23 complexity | fd682604c082d14cb6ed5e0aebbc69e3 MD5 | raw file
  1. <?php
  2. /**
  3. * @package toolkit
  4. */
  5. /**
  6. * The `XsltProcess` class is responsible for taking a chunk of XML
  7. * and applying an XSLT stylesheet to it. Custom error handlers are
  8. * used to capture any errors that occurred during this process, and
  9. * are exposed to the `ExceptionHandler`'s for display to the user.
  10. */
  11. class XsltProcess
  12. {
  13. /**
  14. * The XML for the transformation to be applied to
  15. * @var string
  16. */
  17. private $_xml;
  18. /**
  19. * The XSL for the transformation
  20. * @var string
  21. */
  22. private $_xsl;
  23. /**
  24. * Any errors that occur during the transformation are stored in this array.
  25. * @var array
  26. */
  27. private $_errors = array();
  28. /**
  29. * The `XsltProcess` constructor takes a two parameters for the
  30. * XML and the XSL and initialises the `$this->_xml` and `$this->_xsl` variables.
  31. * If an `XSLTProcessor` is not available, this function will return false
  32. *
  33. * @param string $xml
  34. * The XML for the transformation to be applied to
  35. * @param string $xsl
  36. * The XSL for the transformation
  37. */
  38. public function __construct($xml = null, $xsl = null)
  39. {
  40. $this->_xml = $xml;
  41. $this->_xsl = $xsl;
  42. }
  43. /**
  44. * Checks if there is an available `XSLTProcessor`
  45. *
  46. * @return boolean
  47. * true if there is an existing `XsltProcessor` class, false otherwise
  48. */
  49. public static function isXSLTProcessorAvailable()
  50. {
  51. return (class_exists('XsltProcessor') || function_exists('xslt_process'));
  52. }
  53. /**
  54. * This function will take a given XML file, a stylesheet and apply
  55. * the transformation. Any errors will call the error function to log
  56. * them into the `$_errors` array
  57. *
  58. * @see toolkit.XSLTProcess#__error()
  59. * @see toolkit.XSLTProcess#__process()
  60. * @param string $xml
  61. * The XML for the transformation to be applied to
  62. * @param string $xsl
  63. * The XSL for the transformation
  64. * @param array $parameters
  65. * An array of available parameters the XSL will have access to
  66. * @param array $register_functions
  67. * An array of available PHP functions that the XSL can use
  68. * @return string|boolean
  69. * The string of the resulting transform, or false if there was an error
  70. */
  71. public function process($xml = null, $xsl = null, array $parameters = array(), array $register_functions = array())
  72. {
  73. if ($xml) {
  74. $this->_xml = $xml;
  75. }
  76. if ($xsl) {
  77. $this->_xsl = $xsl;
  78. }
  79. // dont let process continue if no xsl functionality exists
  80. if (!XsltProcess::isXSLTProcessorAvailable()) {
  81. return false;
  82. }
  83. $XSLProc = new XsltProcessor;
  84. if (!empty($register_functions)) {
  85. $XSLProc->registerPHPFunctions($register_functions);
  86. }
  87. $result = @$this->__process(
  88. $XSLProc,
  89. $this->_xml,
  90. $this->_xsl,
  91. $parameters
  92. );
  93. unset($XSLProc);
  94. return $result;
  95. }
  96. /**
  97. * Uses `DomDocument` to transform the document. Any errors that
  98. * occur are trapped by custom error handlers, `trapXMLError` or
  99. * `trapXSLError`.
  100. *
  101. * @param XsltProcessor $XSLProc
  102. * An instance of `XsltProcessor`
  103. * @param string $xml
  104. * The XML for the transformation to be applied to
  105. * @param string $xsl
  106. * The XSL for the transformation
  107. * @param array $parameters
  108. * An array of available parameters the XSL will have access to
  109. * @return string
  110. */
  111. private function __process(XsltProcessor $XSLProc, $xml, $xsl, array $parameters = array())
  112. {
  113. // Create instances of the DomDocument class
  114. $xmlDoc = new DomDocument;
  115. $xslDoc= new DomDocument;
  116. // Set up error handling
  117. if (function_exists('ini_set')) {
  118. $ehOLD = ini_set('html_errors', false);
  119. }
  120. // Load the xml document
  121. set_error_handler(array($this, 'trapXMLError'));
  122. // Prevent remote entities from being loaded, RE: #1939
  123. $elOLD = libxml_disable_entity_loader(true);
  124. // Remove null bytes from XML
  125. $xml = str_replace(chr(0), '', $xml);
  126. $xmlDoc->loadXML($xml, LIBXML_NONET | LIBXML_DTDLOAD | LIBXML_DTDATTR | defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0);
  127. libxml_disable_entity_loader($elOLD);
  128. // Must restore the error handler to avoid problems
  129. restore_error_handler();
  130. // Load the xsl document
  131. set_error_handler(array($this, 'trapXSLError'));
  132. // Ensure that the XSLT can be loaded with `false`. RE: #1939
  133. // Note that `true` will cause `<xsl:import />` to fail.
  134. $elOLD = libxml_disable_entity_loader(false);
  135. $xslDoc->loadXML($xsl, LIBXML_NONET | LIBXML_DTDLOAD | LIBXML_DTDATTR | defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0);
  136. libxml_disable_entity_loader($elOLD);
  137. // Load the xsl template
  138. $XSLProc->importStyleSheet($xslDoc);
  139. // Set parameters when defined
  140. if (!empty($parameters)) {
  141. General::flattenArray($parameters);
  142. $XSLProc->setParameter('', $parameters);
  143. }
  144. // Must restore the error handler to avoid problems
  145. restore_error_handler();
  146. // Start the transformation
  147. set_error_handler(array($this, 'trapXMLError'));
  148. $processed = $XSLProc->transformToXML($xmlDoc);
  149. // Restore error handling
  150. if (function_exists('ini_set') && isset($ehOLD)) {
  151. ini_set('html_errors', $ehOLD);
  152. }
  153. // Must restore the error handler to avoid problems
  154. restore_error_handler();
  155. return $processed;
  156. }
  157. /**
  158. * That validate function takes an XSD to valid against `$this->_xml`
  159. * returning boolean. Optionally, a second parameter `$xml` can be
  160. * passed that will be used instead of `$this->_xml`.
  161. *
  162. * @since Symphony 2.3
  163. * @param string $xsd
  164. * The XSD to validate `$this->_xml` against
  165. * @param string $xml (optional)
  166. * If provided, this function will use this `$xml` instead of
  167. * `$this->_xml`.
  168. * @return boolean
  169. * Returns true if the `$xml` validates against `$xsd`, false otherwise.
  170. * If false is returned, the errors can be obtained with `XSLTProcess->getErrors()`
  171. */
  172. public function validate($xsd, $xml = null)
  173. {
  174. if (is_null($xml) && !is_null($this->_xml)) {
  175. $xml = $this->_xml;
  176. }
  177. if (is_null($xsd) || is_null($xml)) {
  178. return false;
  179. }
  180. // Create instances of the DomDocument class
  181. $xmlDoc = new DomDocument;
  182. // Set up error handling
  183. if (function_exists('ini_set')) {
  184. $ehOLD = ini_set('html_errors', false);
  185. }
  186. // Load the xml document
  187. set_error_handler(array($this, 'trapXMLError'));
  188. $elOLD = libxml_disable_entity_loader(true);
  189. $xmlDoc->loadXML($xml, LIBXML_NONET | LIBXML_DTDLOAD | LIBXML_DTDATTR | defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0);
  190. libxml_disable_entity_loader($elOLD);
  191. // Must restore the error handler to avoid problems
  192. restore_error_handler();
  193. // Validate the XML against the XSD
  194. set_error_handler(array($this, 'trapXSDError'));
  195. $result = $xmlDoc->schemaValidateSource($xsd);
  196. // Restore error handling
  197. if (function_exists('ini_set') && isset($ehOLD)) {
  198. ini_set('html_errors', $ehOLD);
  199. }
  200. // Must restore the error handler to avoid problems
  201. restore_error_handler();
  202. return $result;
  203. }
  204. /**
  205. * A custom error handler especially for XML errors.
  206. *
  207. * @link http://au.php.net/manual/en/function.set-error-handler.php
  208. * @param integer $errno
  209. * @param integer $errstr
  210. * @param integer $errfile
  211. * @param integer $errline
  212. */
  213. public function trapXMLError($errno, $errstr, $errfile, $errline)
  214. {
  215. $this->__error($errno, str_replace('DOMDocument::', null, $errstr), $errfile, $errline, 'xml');
  216. }
  217. /**
  218. * A custom error handler especially for XSL errors.
  219. *
  220. * @link http://au.php.net/manual/en/function.set-error-handler.php
  221. * @param integer $errno
  222. * @param integer $errstr
  223. * @param integer $errfile
  224. * @param integer $errline
  225. */
  226. public function trapXSLError($errno, $errstr, $errfile, $errline)
  227. {
  228. $this->__error($errno, str_replace('DOMDocument::', null, $errstr), $errfile, $errline, 'xsl');
  229. }
  230. /**
  231. * A custom error handler especially for XSD errors.
  232. *
  233. * @since Symphony 2.3
  234. * @link http://au.php.net/manual/en/function.set-error-handler.php
  235. * @param integer $errno
  236. * @param integer $errstr
  237. * @param integer $errfile
  238. * @param integer $errline
  239. */
  240. public function trapXSDError($errno, $errstr, $errfile, $errline)
  241. {
  242. $this->__error($errno, str_replace('DOMDocument::', null, $errstr), $errfile, $errline, 'xsd');
  243. }
  244. /**
  245. * Writes an error to the `$_errors` array, which contains the error information
  246. * and some basic debugging information.
  247. *
  248. * @link http://au.php.net/manual/en/function.set-error-handler.php
  249. * @param integer $number
  250. * @param string $message
  251. * @param string $file
  252. * @param string $line
  253. * @param string $type
  254. * Where the error occurred, can be either 'xml', 'xsl' or `xsd`
  255. */
  256. public function __error($number, $message, $file = null, $line = null, $type = null)
  257. {
  258. $context = null;
  259. if ($type == 'xml' || $type == 'xsd') {
  260. $context = $this->_xml;
  261. }
  262. if ($type == 'xsl') {
  263. $context = $this->_xsl;
  264. }
  265. $this->_errors[] = array(
  266. 'number' => $number,
  267. 'message' => $message,
  268. 'file' => $file,
  269. 'line' => $line,
  270. 'type' => $type,
  271. 'context' => $context
  272. );
  273. }
  274. /**
  275. * Returns boolean if any errors occurred during the transformation.
  276. *
  277. * @see getError
  278. * @return boolean
  279. */
  280. public function isErrors()
  281. {
  282. return (!empty($this->_errors) ? true : false);
  283. }
  284. /**
  285. * Provides an Iterator interface to return an error from the `$_errors`
  286. * array. Repeat calls to this function to get all errors
  287. *
  288. * @param boolean $all
  289. * If true, return all errors instead of one by one. Defaults to false
  290. * @param boolean $rewind
  291. * If rewind is true, resets the internal array pointer to the start of
  292. * the `$_errors` array. Defaults to false.
  293. * @return array
  294. * Either an array of error array's or just an error array
  295. */
  296. public function getError($all = false, $rewind = false)
  297. {
  298. if ($rewind) {
  299. reset($this->_errors);
  300. }
  301. return ($all ? $this->_errors : each($this->_errors));
  302. }
  303. }