Erebot  latest
A modular IRC bot for PHP 5.3+
DOM.php
1 <?php
2 /*
3  This file is part of Erebot, a modular IRC bot written in PHP.
4 
5  Copyright © 2010 François Poirotte
6 
7  Erebot is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  Erebot is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with Erebot. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 namespace Erebot;
22 
23 if (!defined('XML_PARSE_BIG_LINES')) {
24  define('XML_PARSE_BIG_LINES', 1 << 22);
25 }
26 
41 class DOM extends \DomDocument
42 {
44  protected $errors;
45 
55  public function __construct($version = null, $encoding = null)
56  {
57  $this->clearErrors();
58  if ($version === null && $encoding === null) {
59  parent::__construct();
60  } elseif ($encoding === null) {
61  parent::__construct($version);
62  } else {
63  parent::__construct($version, $encoding);
64  }
65  }
66 
83  public function relaxNGValidate($filename, $schematron = true)
84  {
85  $success = parent::relaxNGValidate($filename);
86  return $this->schematronValidation(
87  'file',
88  $filename,
89  'RNG',
90  $success,
91  $schematron
92  );
93  }
94 
111  public function relaxNGValidateSource($source, $schematron = true)
112  {
113  $success = parent::relaxNGValidateSource($source);
114  return $this->schematronValidation(
115  'string',
116  $source,
117  'RNG',
118  $success,
119  $schematron
120  );
121  }
122 
139  public function schemaValidate($filename, $schematron = true)
140  {
141  $success = parent::schemaValidate($filename);
142  return $this->schematronValidation(
143  'file',
144  $filename,
145  'XSD',
146  $success,
147  $schematron
148  );
149  }
150 
167  public function schemaValidateSource($source, $schematron = true)
168  {
169  $success = parent::schemaValidateSource($source);
170  return $this->schematronValidation(
171  'string',
172  $source,
173  'XSD',
174  $success,
175  $schematron
176  );
177  }
178 
214  protected function schematronValidation(
215  $source,
216  $data,
217  $schemaSource,
218  $success,
219  $schematron
220  ) {
221  try {
222  $base = dirname(__DIR__) .
223  DIRECTORY_SEPARATOR . 'data' .
224  DIRECTORY_SEPARATOR;
225  $xsl1 = $base . 'ExtractSchFrom' . $schemaSource . '.xsl';
226  $skeleton = $base . 'iso_schematron_skeleton_for_xslt1.xsl';
227  } catch (\Exception $e) {
228  return false;
229  }
230 
231  $quiet = !libxml_use_internal_errors();
232  if (!$quiet) {
233  $this->errors = array_merge($this->errors, libxml_get_errors());
234  libxml_clear_errors();
235  }
236  if (!$success || !$schematron) {
237  return $success;
238  }
239 
240  $schema = new \DomDocument();
241  if ($source === 'file') {
242  $success = $schema->load($data, XML_PARSE_BIG_LINES);
243  } else {
244  $success = $schema->loadXML($data, XML_PARSE_BIG_LINES);
245  }
246 
247  if (!$quiet) {
248  $this->errors = array_merge($this->errors, libxml_get_errors());
249  libxml_clear_errors();
250  }
251  if (!$success) {
252  return false;
253  }
254 
255  // Load the extracting stylesheet.
256  $extractor = new \DomDocument();
257  $success = $extractor->loadXML(file_get_contents($xsl1), XML_PARSE_BIG_LINES);
258  if (!$quiet) {
259  $this->errors = array_merge($this->errors, libxml_get_errors());
260  libxml_clear_errors();
261  }
262  if (!$success) {
263  return false;
264  }
265 
266  // Generate a Schematron schema from the rules
267  // embedded in the RNG or XSD schema.
268  $processor = new \XSLTProcessor();
269  $processor->importStylesheet($extractor);
270  $result = $processor->transformToDoc($schema);
271  if ($result === false) {
272  return false;
273  }
274 
275  // Load the Schematron skeleton.
276  $validator = new \DomDocument();
277  $success = $validator->loadXML(file_get_contents($skeleton), XML_PARSE_BIG_LINES);
278  if (!$quiet) {
279  $this->errors = array_merge($this->errors, libxml_get_errors());
280  libxml_clear_errors();
281  }
282  if (!$success) {
283  return false;
284  }
285 
286  // Generate the validation XML stylesheet
287  // using the Schematron schema & skeleton.
288  $processor->importStylesheet($validator);
289  $result = $processor->transformToDoc($result);
290  if ($result === false) {
291  return false;
292  }
293 
294  // Apply the validation stylesheet to the document.
295  $processor = new \XSLTProcessor();
296  $processor->registerPHPFunctions('\\Erebot\\DOM::getNodeLineNo');
297  $processor->importStylesheet($result);
298  $result = $processor->transformToDoc($this);
299  if ($result === false) {
300  return false;
301  }
302 
303  $valid = true;
304  foreach ($result->documentElement->childNodes as $child) {
305  if ($child->nodeType !== XML_ELEMENT_NODE) {
306  continue;
307  }
308 
309  if ($child->localName !== 'assertionFailure') {
310  continue;
311  }
312 
313  $valid = false;
314 
315  // If running in quiet mode, don't report errors.
316  if ($quiet) {
317  continue;
318  }
319 
320  $error = new \LibXMLError();
321  $error->level = LIBXML_ERR_ERROR;
322  $error->code = 0;
323  $error->column = 0;
324  $error->message = '';
325  $error->file = $this->documentURI;
326  $error->line = 0;
327  $error->path = '';
328 
329  foreach ($child->childNodes as $subchild) {
330  if ($subchild->nodeType !== XML_ELEMENT_NODE) {
331  continue;
332  }
333  if ($subchild->localName === 'description' && $error->message === '') {
334  $error->message = $subchild->textContent;
335  } elseif ($subchild->localName === 'location' && $error->path === '') {
336  $error->path = $subchild->textContent;
337  } elseif ($subchild->localName === 'line') {
338  $error->line = (int) $subchild->textContent;
339  }
340  }
341  $this->errors[] = $error;
342  }
343  return $valid;
344  }
345 
370  public function validate($schematron = false)
371  {
372  $success = parent::validate();
373  $quiet = !libxml_use_internal_errors();
374  if (!$quiet) {
375  $this->errors = array_merge($this->errors, libxml_get_errors());
376  libxml_clear_errors();
377  }
378  return $success;
379  }
380 
387  public function getErrors()
388  {
389  return $this->errors;
390  }
391 
395  public function clearErrors()
396  {
397  $this->errors = array();
398  }
399 
400  public static function getNodeLineNo(array $nodes)
401  {
402  if (count($nodes) !== 1 || !($nodes[0] instanceof \DOMNode )) {
403  throw new \Exception();
404  }
405  return $nodes[0]->getLineNo();
406  }
407 }
clearErrors()
Definition: DOM.php:395
Base class for other (Erebot-related) exceptions.
Definition: Exception.php:27
schemaValidate($filename, $schematron=true)
Definition: DOM.php:139
Definition: CLI.php:21
schemaValidateSource($source, $schematron=true)
Definition: DOM.php:167
relaxNGValidate($filename, $schematron=true)
Definition: DOM.php:83
schematronValidation($source, $data, $schemaSource, $success, $schematron)
Definition: DOM.php:214
validate($schematron=false)
Definition: DOM.php:370
An extension of PHP&#39;s DomDocument class that implements Schematron validation on top of RelaxNG/XML S...
Definition: DOM.php:41
$errors
Stores the LibXMLError errors raised during validation.
Definition: DOM.php:44
relaxNGValidateSource($source, $schematron=true)
Definition: DOM.php:111
__construct($version=null, $encoding=null)
Definition: DOM.php:55
getErrors()
Definition: DOM.php:387