1 : <?php
2 :
3 : /**
4 : * XSLT Benchmarking
5 : * @link https://github.com/masicek/XSLT-Benchmarking
6 : * @author Viktor Mašíček <viktor@masicek.net>
7 : * @license "New" BSD License
8 : */
9 :
10 : namespace XSLTBenchmarking\TestsRunner;
11 :
12 : require_once ROOT . '/Exceptions.php';
13 : require_once LIBS . '/PhpPath/PhpPath.min.php';
14 :
15 : use PhpPath\P;
16 :
17 : /**
18 : * Class for control that two files has same content.
19 : * For XML, HTML, XHTML etc. do normalize both files before comapring.
20 : *
21 : * @author Viktor Mašíček <viktor@masicek.net>
22 : */
23 : class Controlor
24 : {
25 :
26 :
27 : /**
28 : * Check, if set files have same content.
29 : * For XML, HTML, XHTML etc. do normalize both files before comapring.
30 : *
31 : * @param string $filePath1 Path of the first file
32 : * @param string $filePath2 Path of the second file
33 : *
34 : * @return bool
35 : */
36 : public function isSame($filePath1, $filePath2)
37 : {
38 12 : P::mcf($filePath1);
39 11 : P::mcf($filePath2);
40 :
41 10 : $content1 = file_get_contents($filePath1);
42 10 : $content2 = file_get_contents($filePath2);
43 :
44 : // unify EOL
45 10 : $content1 = str_replace("\r\n", "\n", $content1);
46 10 : $content2 = str_replace("\r\n", "\n", $content2);
47 :
48 : // normalize
49 10 : $extension1 = strtolower(pathinfo($filePath1, PATHINFO_EXTENSION));
50 10 : if ($extension1 == 'xml' || $extension1 == 'html')
51 10 : {
52 8 : $content1 = $this->normalizeXml($content1, $extension1);
53 8 : }
54 : else
55 : {
56 : // unify possible declaration in "non XML files"
57 2 : $content1 = $this->unifyDeclaration($content1);
58 : }
59 :
60 10 : $extension2 = strtolower(pathinfo($filePath2, PATHINFO_EXTENSION));
61 10 : if ($extension2 == 'xml' || $extension2 == 'html')
62 10 : {
63 8 : $content2 = $this->normalizeXml($content2, $extension2);
64 8 : }
65 : else
66 : {
67 : // unify possible declaration in "non XML files"
68 2 : $content2 = $this->unifyDeclaration($content2);
69 : }
70 :
71 10 : return $content1 == $content2;
72 : }
73 :
74 :
75 : /**
76 : * If content is XML, then return normalized XML input content.
77 : * - remove insignificant whitespaces
78 : * - sort attributes alphabetical
79 : * If content is not XML, return input content.
80 : *
81 : * @param string $inputContent Normalizing content
82 : * @param string $extension Extension of file from withc is content
83 : *
84 : * @return string
85 : */
86 : private function normalizeXml($inputContent, $extension)
87 : {
88 : // add declaration, if not presented for XML
89 8 : if ($extension == 'xml')
90 8 : {
91 : // get begin with possible declaration
92 8 : $beginEndIdx = strpos($inputContent, '>');
93 8 : $begin = substr($inputContent, 0, $beginEndIdx + 1);
94 : // clean begin (e.g. for UTF-16)
95 8 : $begin = preg_replace('/[^ a-zA-Z0-9?"\'.=<>-]/', '', $begin);
96 8 : if (preg_match('/<\?xml[^?]+\?>/', $begin) === 0)
97 8 : {
98 0 : $inputContent = '<?xml version="1.0" encoding="UTF-8" ?>' . $inputContent;
99 0 : }
100 8 : }
101 :
102 : // remove insignificant whitespaces
103 : // simplier empty elements ('<el></el>' => '<el/>')
104 : // simplier attributes ('att = "aaa" att2="bbb"' => 'att="aaa" att2="bbb"')
105 8 : $dom = new \DOMDocument();
106 8 : $dom->preserveWhiteSpace = FALSE;
107 : // catch error/warning/notice during reading content as XML or HTML
108 8 : set_error_handler(array($this, 'errorHandler'));
109 : try
110 : {
111 8 : if ($extension == 'xml')
112 8 : {
113 8 : $dom->loadXml($inputContent);
114 8 : }
115 8 : if ($extension == 'html')
116 8 : {
117 0 : $dom->loadHTML($inputContent);
118 0 : $outputContent = $dom->saveXML();
119 0 : $dom->loadXml($outputContent);
120 0 : }
121 : }
122 8 : catch (\Exception $e)
123 : {
124 0 : restore_error_handler();
125 : // input is not XML or HTML
126 0 : return $inputContent;
127 : }
128 8 : restore_error_handler();
129 8 : $outputContent = $dom->saveXML();
130 :
131 : // normalize empty atribute ('att' => 'att=""')
132 : // TODO
133 :
134 : // sort attributes
135 8 : $outputContent = preg_replace_callback('#<[^/?][^>]*>#', array($this, 'sortAttributes'), $outputContent);
136 :
137 8 : $outputContent = $this->unifyDeclaration($outputContent);
138 :
139 8 : return $outputContent;
140 : }
141 :
142 :
143 : /**
144 : * Unify declaration of content. If any declaration are not in content, return unchanged it.
145 : *
146 : * @param string $content
147 : *
148 : * @return string
149 : */
150 : private function unifyDeclaration($content)
151 : {
152 0 : $outputContent = $content;
153 0 : $declarationLast = strpos($outputContent, '?>');
154 0 : if ($declarationLast !== FALSE)
155 0 : {
156 0 : $declaration = substr($outputContent, 0, $declarationLast);
157 0 : $declarationUnify = trim($declaration);
158 0 : $declarationUnify = strtolower($declarationUnify);
159 0 : $outputContent = $declarationUnify . substr($outputContent, $declarationLast);
160 0 : }
161 :
162 0 : return $outputContent;
163 : }
164 :
165 :
166 : /**
167 : * Normalize element
168 : * - remove insignificant whitespaces
169 : * - sort attributes alphabetical
170 : *
171 : * @param array $matches The first value is parsed element
172 : *
173 : * @return string
174 : */
175 : private function sortAttributes($matches)
176 : {
177 18 : $input = $matches[0];
178 :
179 : // begin
180 18 : $beginLast = strpos($input, ' ');
181 18 : $beginLast = $beginLast ?: strpos($input, '/>');
182 18 : $beginLast = $beginLast ?: strpos($input, '>');
183 18 : $begin = substr($input, 0, $beginLast);
184 :
185 : // end
186 18 : $endFirst = strrpos($input, '/>');
187 18 : $endFirst = $endFirst ?: strrpos($input, '>');
188 18 : $end = substr($input, $endFirst);
189 :
190 : // attributes
191 18 : $attributes = $input;
192 18 : $attributes = str_replace($begin, '', $attributes);
193 18 : $attributes = str_replace($end, '', $attributes);
194 : if ($attributes)
195 18 : {
196 16 : preg_match_all('/[^ ]+="[^"]*"/', $attributes, $matches);
197 16 : $attributes = $matches[0];
198 16 : $attributes = array_filter($attributes);
199 16 : sort($attributes);
200 16 : $attributes = implode(' ', $attributes);
201 16 : }
202 :
203 : if ($attributes)
204 18 : {
205 16 : $output = $begin . ' ' . $attributes . $end;
206 16 : }
207 : else
208 : {
209 10 : $output = $begin . $end;
210 : }
211 :
212 18 : return $output;
213 : }
214 :
215 :
216 : /**
217 : * Handling of error during reading XML or HTML file.
218 : *
219 : * @param type $errno
220 : * @param type $errstr
221 : *
222 : * @return void
223 : */
224 : public function errorHandler($errno, $errstr)
225 : {
226 0 : throw new \Exception('Error handler');
227 : }
228 :
229 :
230 : }
|