Open Journal Systems  3.3.0
XSLTransformer.inc.php
1 <?php
2 
17 // The default character encoding
18 define('XSLT_PROCESSOR_ENCODING', Config::getVar('i18n', 'client_charset'));
19 
20 define('XSL_TRANSFORMER_DOCTYPE_STRING', 0x01);
21 define('XSL_TRANSFORMER_DOCTYPE_FILE', 0x02);
22 define('XSL_TRANSFORMER_DOCTYPE_DOM', 0x03);
23 
25 
27  static $processor;
28 
31 
34 
37 
40 
42  var $errors;
43 
49  function __construct() {
50  // Necessary to fetch configuration.
52 
53  $this->errors = array();
54  }
55 
60  static function checkSupport() {
61  self::$externalCommand = Config::getVar('cli', 'xslt_command');
62  self::$externalParameterSnippet = Config::getVar('cli', 'xslt_parameter_option');
63 
64  // Determine the appropriate XSLT processor for the system
65  if (self::$externalCommand) {
66  // check the external command to check for %xsl and %xml parameter substitution
67  if ( strpos(self::$externalCommand, '%xsl') === false ) return false;
68  if ( strpos(self::$externalCommand, '%xml') === false ) return false;
69  self::$processor = 'External';
70 
71  } elseif (extension_loaded('xsl') && extension_loaded('dom')) {
72  // XSL/DOM modules present
73  self::$processor = 'PHP';
74 
75  } else {
76  // no XSLT support
77  return false;
78  }
79  return true;
80  }
81 
82  //
83  // Getters and Setters
84  //
89  static function getProcessor() {
90  return self::$processor;
91  }
92 
97  function setParameters($parameters) {
98  $this->parameters = $parameters;
99  }
100 
105  function setRegisterPHPFunctions($flag) {
106  $this->registerPHPFunctions = $flag;
107  }
108  //
109  // Public methods
110  //
117  function transformFiles($xmlFile, $xslFile) {
118  return $this->transform($xmlFile, XSL_TRANSFORMER_DOCTYPE_FILE, $xslFile, XSL_TRANSFORMER_DOCTYPE_FILE, XSL_TRANSFORMER_DOCTYPE_STRING);
119  }
120 
127  function transformStrings($xml, $xsl) {
128  return $this->transform($xml, XSL_TRANSFORMER_DOCTYPE_STRING, $xsl, XSL_TRANSFORMER_DOCTYPE_STRING, XSL_TRANSFORMER_DOCTYPE_STRING);
129  }
130 
143  function transform($xml, $xmlType, $xsl, $xslType, $resultType) {
144  // If either XML or XSL file don't exist, then fail without trying to process XSLT
145  $fileManager = new FileManager();
146  if ($xmlType == XSL_TRANSFORMER_DOCTYPE_FILE) {
147  if (!$fileManager->fileExists($xml)) return false;
148  }
149  if ($xslType == XSL_TRANSFORMER_DOCTYPE_FILE) {
150  if (!$fileManager->fileExists($xsl)) return false;
151  }
152 
153  // The result type can only be string or DOM
154  assert($resultType != XSL_TRANSFORMER_DOCTYPE_FILE);
155 
156  switch (self::$processor) {
157  case 'External':
158  return $this->_transformExternal($xml, $xmlType, $xsl, $xslType, $resultType);
159 
160  case 'PHP':
161  return $this->_transformPHP($xml, $xmlType, $xsl, $xslType, $resultType);
162 
163  default:
164  // No XSLT processor available
165  return false;
166  }
167  }
168 
169  //
170  // Private helper methods
171  //
182  function _transformExternal($xml, $xmlType, $xsl, $xslType, $resultType) {
183 
184  // External transformation can only be done on files
185  if ($xmlType != XSL_TRANSFORMER_DOCTYPE_FILE || $xslType != XSL_TRANSFORMER_DOCTYPE_FILE) return false;
186 
187  // check the external command to check for %xsl and %xml parameter substitution
188  if ( strpos(self::$externalCommand, '%xsl') === false ) return false;
189  if ( strpos(self::$externalCommand, '%xml') === false ) return false;
190 
191  // Assemble the parameters to be supplied to the stylesheet
192  $parameterString = '';
193  foreach ($this->parameters as $name => $value) {
194  $parameterString .= str_replace(array('%n', '%v'), array($name, $value), self::$externalParameterSnippet);
195  }
196 
197  // perform %xsl and %xml replacements for fully-qualified shell command
198  $xsltCommand = str_replace(array('%xsl', '%xml', '%params'), array($xsl, $xml, $parameterString), self::$externalCommand);
199 
200  // check for safe mode and escape the shell command
201  if( !ini_get('safe_mode') ) $xsltCommand = escapeshellcmd($xsltCommand);
202 
203  // run the shell command and get the results
204  exec($xsltCommand . ' 2>&1', $contents, $status);
205 
206  // if there is an error state, copy result to error property
207  if ($status != false) {
208  if ($contents) {
209  $this->addError(implode("\n", $contents));
210  }
211  // completed with errors
212  return false;
213  }
214 
215  $resultXML = implode("\n", $contents);
216 
217  switch ($resultType) {
218  case XSL_TRANSFORMER_DOCTYPE_STRING:
219  // Directly return the XML string
220  return $resultXML;
221 
222  case XSL_TRANSFORMER_DOCTYPE_DOM:
223  // Instantiate and configure the result DOM
224  $resultDOM = new DOMDocument('1.0', XSLT_PROCESSOR_ENCODING);
225  $resultDOM->recover = true;
226  $resultDOM->substituteEntities = true;
227  $resultDOM->resolveExternals = true;
228 
229  // Load the XML and return the DOM
230  $resultDOM->loadXML($resultXML);
231  return $resultDOM;
232 
233  default:
234  assert(false);
235  }
236  }
237 
248  function _transformPHP($xml, $xmlType, $xsl, $xslType, $resultType) {
249  // Prepare the XML DOM
250  if ($xmlType == XSL_TRANSFORMER_DOCTYPE_DOM) {
251  // We already have a DOM document, no need to create one
252  assert(is_a($xml, 'DOMDocument'));
253  $xmlDOM = $xml;
254  } else {
255  // Instantiate and configure the XML DOM document
256  $xmlDOM = new DOMDocument('1.0', XSLT_PROCESSOR_ENCODING);
257 
258  // These are required for external entity resolution (eg. &nbsp;), but can slow processing
259  // substantially (20-100x), often up to 60s. This can be solved by use of local catalogs, ie.
260  // putenv("XML_CATALOG_FILES=/path/to/catalog.ent");
261  //
262  // see: http://www.whump.com/moreLikeThis/link/03815
263  $xmlDOM->recover = true;
264  $xmlDOM->substituteEntities = true;
265  $xmlDOM->resolveExternals = true;
266 
267  // Load the XML based on its type
268  switch ($xmlType) {
269  case XSL_TRANSFORMER_DOCTYPE_FILE:
270  $xmlDOM->load($xml);
271  break;
272 
273  case XSL_TRANSFORMER_DOCTYPE_STRING:
274  $xmlDOM->loadXML($xml);
275  break;
276 
277  default:
278  assert(false);
279  }
280  }
281 
282  // Prepare the XSL DOM
283  if ($xslType == XSL_TRANSFORMER_DOCTYPE_DOM) {
284  // We already have a DOM document, no need to create one
285  assert(is_a($xsl, 'DOMDocument'));
286  $xslDOM = $xsl;
287  } else {
288  // Instantiate the XSL DOM document
289  $xslDOM = new DOMDocument('1.0', XSLT_PROCESSOR_ENCODING);
290 
291  // Load the XSL based on its type
292  switch ($xslType) {
293  case XSL_TRANSFORMER_DOCTYPE_FILE:
294  $xslDOM->load($xsl);
295  break;
296 
297  case XSL_TRANSFORMER_DOCTYPE_STRING:
298  $xslDOM->loadXML($xsl);
299  break;
300 
301  default:
302  assert(false);
303  }
304  }
305 
306  // Create and configure the XSL processor
307  $processor = new XSLTProcessor();
308 
309  // Register PHP functions if requested.
310  // NB: This can open potential security issues; see FAQ/README
311  if ($this->registerPHPFunctions) {
312  $processor->registerPHPFunctions();
313  }
314 
315  // Set XSL parameters (if any)
316  if (is_array($this->parameters)) {
317  foreach ($this->parameters as $param => $value) {
318  $processor->setParameter(null, $param, $value);
319  }
320  }
321 
322  // Import the style sheet
323  $processor->importStylesheet($xslDOM);
324 
325  // Process depending on the requested result type
326  switch($resultType) {
327  case XSL_TRANSFORMER_DOCTYPE_STRING:
328  $resultXML = $processor->transformToXML($xmlDOM);
329  return $resultXML;
330 
331  case XSL_TRANSFORMER_DOCTYPE_DOM:
332  $resultDOM = $processor->transformToDoc($xmlDOM);
333  return $resultDOM;
334 
335  default:
336  assert(false);
337  }
338  }
339 
344  function addError($error) {
345  array_push($this->errors, $error);
346  }
347 }
348 
349 
XSLTransformer\checkSupport
static checkSupport()
Definition: XSLTransformer.inc.php:78
XSLTransformer\transformFiles
transformFiles($xmlFile, $xslFile)
Definition: XSLTransformer.inc.php:135
XSLTransformer\$registerPHPFunctions
$registerPHPFunctions
Definition: XSLTransformer.inc.php:54
XSLTransformer\getProcessor
static getProcessor()
Definition: XSLTransformer.inc.php:107
XSLTransformer\transformStrings
transformStrings($xml, $xsl)
Definition: XSLTransformer.inc.php:145
XSLTransformer
Wrapper class for running XSL transformations using PHP 4.x or 5.x.
Definition: XSLTransformer.inc.php:24
XSLTransformer\$parameters
$parameters
Definition: XSLTransformer.inc.php:48
XSLTransformer\$errors
$errors
Definition: XSLTransformer.inc.php:60
XSLTransformer\$externalCommand
static $externalCommand
Definition: XSLTransformer.inc.php:36
XSLTransformer\_transformPHP
_transformPHP($xml, $xmlType, $xsl, $xslType, $resultType)
Definition: XSLTransformer.inc.php:266
XSLTransformer\__construct
__construct()
Definition: XSLTransformer.inc.php:67
XSLTransformer\$processor
static $processor
Definition: XSLTransformer.inc.php:30
Config\getVar
static getVar($section, $key, $default=null)
Definition: Config.inc.php:35
XSLTransformer\$externalParameterSnippet
static $externalParameterSnippet
Definition: XSLTransformer.inc.php:42
XSLTransformer\_transformExternal
_transformExternal($xml, $xmlType, $xsl, $xslType, $resultType)
Definition: XSLTransformer.inc.php:200
XSLTransformer\addError
addError($error)
Definition: XSLTransformer.inc.php:362
XSLTransformer\setParameters
setParameters($parameters)
Definition: XSLTransformer.inc.php:115
FileManager
Class defining basic operations for file management.
Definition: FileManager.inc.php:35
XSLTransformer\transform
transform($xml, $xmlType, $xsl, $xslType, $resultType)
Definition: XSLTransformer.inc.php:161
XSLTransformer\setRegisterPHPFunctions
setRegisterPHPFunctions($flag)
Definition: XSLTransformer.inc.php:123