This repository was archived by the owner on Nov 21, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathSchematronValidator.php
More file actions
120 lines (95 loc) · 3.84 KB
/
SchematronValidator.php
File metadata and controls
120 lines (95 loc) · 3.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<?php
declare(strict_types=1);
namespace Libero\XmlValidator;
use DOMDocument;
use DOMElement;
use DOMXPath;
use UnexpectedValueException;
use XSLTProcessor;
use function array_map;
use function iterator_to_array;
use function sprintf;
use function trim;
use const LIBXML_BIGLINES;
final class SchematronValidator implements XmlValidator
{
private const LIB_PATH = __DIR__.'/../lib/schematron';
private const SVRL_URI = 'http://purl.oclc.org/dsdl/svrl';
private const SCHEMATRON_DOCUMENT_ELEMENT = '{http://purl.oclc.org/dsdl/schematron}schema';
private const RELAX_NG_DOCUMENT_ELEMENT = '{http://relaxng.org/ns/structure/1.0}grammar';
private const XML_SCHEMA_DOCUMENT_ELEMENT = '{http://www.w3.org/2001/XMLSchema}schema';
private $schema;
public function __construct(string $schema)
{
$this->schema = $schema;
}
/**
* @throws ValidationFailed
*/
public function validate(DOMDocument $document) : void
{
$schema = new DOMDocument();
$foundSchema = @$schema->load($this->schema, LIBXML_BIGLINES);
if (!$foundSchema) {
throw new ValidationFailed([new Failure("Failed to load '{$this->schema}'")]);
}
$processor = new XSLTProcessor();
$schematron = $this->toSchematron($processor, $schema);
$validator = new DOMDocument();
$validator->load(self::LIB_PATH.'/iso_svrl_for_xslt1.xsl', LIBXML_BIGLINES);
$processor->importStylesheet($validator);
$xslt = $processor->transformToDoc($schematron);
$processor->importStylesheet($xslt);
$result = $processor->transformToDoc($document);
$resultXpath = new DOMXPath($result);
$resultXpath->registerNamespace('svrl', self::SVRL_URI);
$documentXpath = new DOMXPath($document);
$failures = array_map(
function (DOMElement $failedAssert) use ($documentXpath, $resultXpath) {
$message = trim($resultXpath->query('svrl:text[1]', $failedAssert)->item(0)->textContent ?? '');
$location = $documentXpath->query($failedAssert->getAttribute('location'))->item(0);
return new Failure($message, $location ? $location->getLineNo() : null, $location);
},
iterator_to_array($resultXpath->query('/svrl:schematron-output/svrl:failed-assert'))
);
if (!empty($failures)) {
throw new ValidationFailed($failures);
}
}
private function toSchematron(XSLTProcessor $processor, DOMDocument $schema) : DOMDocument
{
$documentElement = sprintf(
'{%s}%s',
$schema->documentElement->namespaceURI,
$schema->documentElement->nodeName
);
if (self::SCHEMATRON_DOCUMENT_ELEMENT === $documentElement) {
return $schema;
}
try {
$extractorXslt = $this->getExtractor($documentElement);
} catch (UnexpectedValueException $e) {
throw new UnexpectedValueException(
"Schema is not Schematron, nor can it be extracted: document element is {$documentElement}",
0,
$e
);
}
$extractor = new DOMDocument();
$extractor->load($extractorXslt, LIBXML_BIGLINES);
$processor->importStylesheet($extractor);
return $processor->transformToDoc($schema);
}
private function getExtractor(string $documentElement) : string
{
switch ($documentElement) {
case self::RELAX_NG_DOCUMENT_ELEMENT:
return self::LIB_PATH.'/ExtractSchFromRNG.xsl';
case self::XML_SCHEMA_DOCUMENT_ELEMENT:
return self::LIB_PATH.'/ExtractSchFromXSD.xsl';
}
throw new UnexpectedValueException(
"Schema appears not to be an XSD nor RNG: document element is {$documentElement}"
);
}
}