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