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\Reports;
11 :
12 : require_once ROOT . '/Exceptions.php';
13 : require_once LIBS . '/PhpPath/PhpPath.min.php';
14 :
15 : use PhpPath\P;
16 :
17 : /**
18 : * Merging of more reported tests into one XML file.
19 : *
20 : * @author Viktor Mašíček <viktor@masicek.net>
21 : */
22 : class Merger
23 : {
24 :
25 :
26 : /**
27 : * List of reports for generating
28 : *
29 : * @var arry of \SimpleXML
30 : */
31 : private $reports;
32 :
33 :
34 : /**
35 : * Make merge of all added reports, save result into file and
36 : * return path of generated file
37 : *
38 : * @param string $outputDirPath Path of directory for generating mergered report
39 : *
40 : * @return string
41 : */
42 : public function merge($outputDirPath)
43 : {
44 : // make name of output
45 5 : P::cd($outputDirPath);
46 5 : $outputPath = P::m($outputDirPath, date('Y-m-d-H-i-s'). '-merge' . '.xml');
47 :
48 5 : $mergeredReports = $this->getMergeredReport();
49 5 : $this->saveMegeredReports($outputPath, $mergeredReports);
50 :
51 5 : return $outputPath;
52 : }
53 :
54 :
55 : /**
56 : * Add report into list of mergered reports
57 : *
58 : * @param string $path Path of XML file with reports
59 : *
60 : * @throws \XSLTBenchmarking\InvalidArgumentException Wrong format of file with params
61 : * @return void
62 : */
63 : public function addReportFile($path)
64 : {
65 4 : $path = P::mcf($path);
66 :
67 : // validate
68 3 : $dom = new \DOMDocument();
69 3 : $dom->load($path);
70 : try {
71 3 : $dom->schemaValidate(P::m(__DIR__, 'Report.xsd'));
72 3 : } catch (\Exception $e) {
73 1 : $error = libxml_get_last_error();
74 1 : throw new \XSLTBenchmarking\InvalidArgumentException(
75 1 : 'File "' . $path . '" has wrong format: ' . $error->message
76 1 : );
77 : }
78 :
79 : // make SimpleXML
80 2 : $report = new \SimpleXMLElement($path, 0, TRUE);
81 :
82 2 : $this->reports[] = $report;
83 2 : }
84 :
85 :
86 : /**
87 : * Merge reports into one SimpleXML object.
88 : *
89 : * @return \SimpleXMLElement
90 : */
91 : private function getMergeredReport()
92 : {
93 5 : $mergeEl = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><reports></reports>');
94 :
95 : // skeleton
96 5 : $globalEl = $mergeEl->addChild('global');
97 5 : $processorsListEl = $globalEl->addChild('processors');
98 5 : $testsListEl = $mergeEl->addChild('tests');
99 :
100 5 : foreach ($this->reports as $report)
101 : {
102 : // processors
103 5 : $processors = $report->xpath('//global/processors');
104 5 : $this->copyChilds($processors[0], $processorsListEl, array('processor|name'));
105 :
106 : // tests
107 5 : $tests = $report->xpath('//tests');
108 5 : $this->copyChilds($tests[0], $testsListEl, array('test|name', 'processor|name', 'input|input|expectedOutput'));
109 5 : }
110 :
111 5 : return $mergeEl;
112 : }
113 :
114 :
115 : /**
116 : * Copy children elements from source element into destination element.
117 : * Runction is called recursively if $childrenInfos have more than one value.
118 : *
119 : * @param \SimpleXMLElement $sourceElement Source XML element
120 : * @param \SimpleXMLElement $destinationElement Destionation XML element
121 : * @param array $childrenInfos Information for copy.
122 : * Each value is name of copied element and atributes for comapring (separated by '|').
123 : *
124 : * @return void
125 : */
126 : private function copyChilds(
127 : \SimpleXMLElement $sourceElement,
128 : \SimpleXMLElement $destinationElement,
129 : array $childrenInfos = array()
130 : )
131 : {
132 5 : $childInfo = array_shift($childrenInfos);
133 5 : $infos = explode('|', $childInfo);
134 5 : $childName = array_shift($infos);
135 :
136 5 : $childrenElements = $sourceElement->xpath($childName);
137 5 : foreach ($childrenElements as $childElement)
138 : {
139 5 : $checkRule = array();
140 5 : foreach ($infos as $info)
141 : {
142 5 : $checkRule[] = '@' . $info . '="' . (string)$childElement[$info] . '"';
143 5 : }
144 : if ($checkRule)
145 5 : {
146 5 : $checkRule = '[' . implode(' and ', $checkRule) . ']';
147 5 : }
148 :
149 5 : $destionationChildElements = $destinationElement->xpath($childName . $checkRule);
150 5 : if (count($destionationChildElements) == 0)
151 5 : {
152 5 : $addedElement = $destinationElement->addChild($childName);
153 5 : }
154 : else
155 : {
156 5 : $addedElement = $destionationChildElements[0];
157 : }
158 :
159 : // copy attribute - newer values are preferred
160 5 : $this->copyOrReplaceAttributes($childElement, $addedElement);
161 :
162 : // copy children
163 5 : if (count($childrenInfos) > 0)
164 5 : {
165 5 : $this->copyChilds($childElement, $addedElement, $childrenInfos);
166 5 : }
167 5 : }
168 5 : }
169 :
170 :
171 : /**
172 : * Copy element from source element into destination element.
173 : * If attribute existed in destination element, than its value is replaced by newer.
174 : *
175 : * @param \SimpleXMLElement $sourceElement Source XML element
176 : * @param \SimpleXMLElement $destinationElement Destionation XML element
177 : *
178 : * @return void
179 : */
180 : private function copyOrReplaceAttributes(\SimpleXMLElement $sourceElement, \SimpleXMLElement $destinationElement)
181 : {
182 5 : foreach ($sourceElement->attributes() as $name => $value)
183 : {
184 5 : $destinationElement[$name] = $value;
185 5 : }
186 5 : }
187 :
188 :
189 : /**
190 : * Save mergered reports into file. XML is indented.
191 : *
192 : * @param sring $outputPath Path of file
193 : * @param \SimpleXMLElement $mergeredReports Report for saving
194 : *
195 : * @return void
196 : */
197 : private function saveMegeredReports($outputPath, \SimpleXMLElement $mergeredReports)
198 : {
199 : // save + make indent
200 5 : $dom = dom_import_simplexml($mergeredReports)->ownerDocument;
201 5 : $dom->formatOutput = TRUE;
202 5 : $dom->save($outputPath);
203 5 : }
204 :
205 :
206 : }
|