Overview
Namespaces
Classes
- AMemoryUsageDriver
- AProcessorDriver
- Controlor
- Libxslt1123phpProcessorDriver
- Libxslt1126phpProcessorDriver
- LinuxMemoryUsageDriver
- MemoryUsage
- MSXML30ProcessorDriver
- MSXML60ProcessorDriver
- Params
- Processor
- Runner
- Sablotron103cmdProcessorDriver
- Saxon655ProcessorDriver
- SaxonHE9402ProcessorDriver
- Test
- TestRunner
- WindowsMemoryUsageDriver
- Xalan271ProcessorDriver
- XmlParamsDriver
- Xsltproc1123ProcessorDriver
- Xsltproc1126ProcessorDriver
- XT20051206ProcessorDriver
Interfaces
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 ROOT . '/Microtime.php';
14: require_once LIBS . '/PhpPath/PhpPath.min.php';
15:
16: use XSLTBenchmarking\Microtime;
17: use PhpPath\P;
18:
19:
20: /**
21: * Class for parse one template in one processor
22: *
23: * @author Viktor Mašíček <viktor@masicek.net>
24: */
25: class Processor
26: {
27:
28:
29: /**
30: * Path of dirctory containing processors drivers
31: *
32: * @var string
33: */
34: private $driversDir;
35:
36: /**
37: * Namespace of processors drivers
38: *
39: * @var string
40: */
41: private $driversNamespace;
42:
43: /**
44: * Path of temporary directory
45: *
46: * @var string
47: */
48: private $tmpDir;
49:
50: /**
51: * Class for measure memory usage of run command
52: *
53: * @var MemoryUsage
54: */
55: private $memoryUsage;
56:
57: /**
58: * List of available names of processors
59: *
60: * @var array
61: */
62: private $available = NULL;
63:
64: /**
65: * List of informations about processors
66: *
67: * @var array
68: */
69: private $informations = NULL;
70:
71:
72: /**
73: * Configure object
74: *
75: * @param string $tmpDir Path of temporary directory
76: * @param MemoryUsage $memoryUsage Class for measure memory usage of run command
77: * @param string $driversDir Path of dirctory containing processor drivers
78: * @param string $driversNamespace Namespace of processors drivers
79: */
80: public function __construct(
81: $tmpDir,
82: MemoryUsage $memoryUsage,
83: $driversDir = NULL,
84: $driversNamespace = '\XSLTBenchmarking\TestsRunner\\'
85: )
86: {
87: if (is_null($driversDir))
88: {
89: $driversDir = P::m(__DIR__, 'Drivers');
90: }
91:
92: $this->tmpDir = P::mcd($tmpDir);
93: $this->memoryUsage = $memoryUsage;
94: $this->driversDir = P::mcd($driversDir);
95: $this->driversNamespace = $driversNamespace;
96: }
97:
98:
99: /**
100: * Return list of available names of processors
101: *
102: * @return array ([name] => AProcessorDriver)
103: */
104: public function getAvailable()
105: {
106: if (!$this->available)
107: {
108: $this->available = $this->detectAvailable();
109: }
110: return $this->available;
111: }
112:
113:
114: /**
115: * Return information about processors
116: *
117: * @return array
118: */
119: public function getInformations()
120: {
121: if (!$this->informations)
122: {
123: $this->informations = $this->readInformations();
124: }
125: return $this->informations;
126: }
127:
128:
129: /**
130: * Run one XSLT transformation in the processor
131: *
132: * @param string $processorName Name of used processor
133: * @param string $templatePath Path of XSLT template
134: * @param string $xmlInputPath Path of XML input file
135: * @param string $outputPath Path of generated output file
136: *
137: * @return array|string List of spend times on transformation|Error message
138: */
139: public function run($processorName, $templatePath, $xmlInputPath, $outputPath, $repeating)
140: {
141: $processors = $this->getAvailable();
142: if (!isset($processors[$processorName]))
143: {
144: throw new \XSLTBenchmarking\InvalidArgumentException('Unknown processor "' . $processorName . '"');
145: }
146:
147: $processor = $processors[$processorName];
148:
149: P::mcf($templatePath);
150: P::mcf($xmlInputPath);
151:
152: // stylesheet for transformation have to be set in input XML file
153: if ($processor->isTemplateSetInInput())
154: {
155: $xmlInputPath = $this->makeInputWithTemplatePath($xmlInputPath, $templatePath);
156: }
157:
158: $errorPath = P::m($this->tmpDir, 'transformation.err');
159:
160: $beforeCommand = $this->getCommand($processor->getBeforeCommandTemplate(), $templatePath, $xmlInputPath, $outputPath, $errorPath);
161: $commandBase = $this->getCommand($processor->getCommandTemplate(), $templatePath, $xmlInputPath, $outputPath, $errorPath);
162: $afterCommand = $this->getCommand($processor->getAfterCommandTemplate(), $templatePath, $xmlInputPath, $outputPath, $errorPath);
163:
164: $times = array();
165: $memoryList = array();
166: for ($repeatingIdx = 0; $repeatingIdx < $repeating; $repeatingIdx++)
167: {
168: if (is_file($errorPath))
169: {
170: throw new \XSLTBenchmarking\InvalidStateException('Error tmp file should not exist "' . $errorPath . '"');
171: }
172:
173: $command = $commandBase;
174:
175: // each transformation generate into separate file
176: if ($repeatingIdx != 0)
177: {
178: $outputPathNew = preg_replace('/[.]([^.]*)$/', '-' . $repeatingIdx . '.$1', $outputPath);
179: $command = str_replace($outputPath, $outputPathNew, $command);
180: }
181:
182: // preparing command
183: if ($beforeCommand)
184: {
185: exec($beforeCommand);
186: }
187:
188: // memore usage - run
189: $command = $this->memoryUsage->run($command);
190:
191: // transformation command
192: $timeStart = Microtime::now();
193: exec($command);
194: $timeEnd = Microtime::now();
195:
196: // memore usage - get
197: $memoryList[] = (string)$this->memoryUsage->get();
198:
199: // concluding comand
200: if ($afterCommand)
201: {
202: exec($afterCommand);
203: }
204:
205: // check exitence of generated file
206: if ($repeatingIdx == 0 && !is_file($outputPath))
207: {
208: $error = 'Output file was not be generated (' . $outputPath . ')';
209: // detect generated error
210: if (is_file($errorPath))
211: {
212: $error .= '; ' . file_get_contents($errorPath);
213: unlink($errorPath);
214: }
215:
216: break;
217: }
218: if ($repeatingIdx != 0 && !is_file($outputPathNew))
219: {
220: $error = 'Output file (repeated) was not be generated (' . $outputPathNew . ')';
221: // detect generated error
222: if (is_file($errorPath))
223: {
224: $error .= '; ' . file_get_contents($errorPath);
225: unlink($errorPath);
226: }
227:
228: break;
229: }
230:
231: // difference between two same transformation
232: if ($repeatingIdx != 0)
233: {
234: if (file_get_contents($outputPath) == file_get_contents($outputPathNew))
235: {
236: unlink($outputPathNew);
237: }
238: else
239: {
240: $error = 'Difference between two same transformation: (' . $outputPath . ') != (' . $outputPathNew . ')';
241: break;
242: }
243: }
244:
245: // detect error
246: if (is_file($errorPath))
247: {
248: $error = file_get_contents($errorPath);
249: unlink($errorPath);
250: }
251: else
252: {
253: $error = '';
254: }
255:
256: if ($error)
257: {
258: break;
259: }
260:
261: // spend time
262: $times[] = Microtime::substract($timeEnd, $timeStart);
263: }
264:
265: if ($error)
266: {
267: return $error;
268: }
269: else
270: {
271: return array(
272: 'times' => $times,
273: 'memory' => $memoryList
274: );
275: }
276: }
277:
278:
279: // --- PRIVATE FUNCTIONS ---
280:
281:
282: /**
283: * Detect list of available names of processors
284: *
285: * @return array ([name] => AProcessorDriver)
286: */
287: private function detectAvailable()
288: {
289: $driversFiles = scandir($this->driversDir);
290:
291: $drivers = array();
292: foreach ($driversFiles as $driverFile)
293: {
294: if (in_array($driverFile, array('AProcessorDriver.php', '.', '..')))
295: {
296: continue;
297: }
298:
299: // driver have to be php file
300: if (substr($driverFile, -4) !== '.php')
301: {
302: continue;
303: }
304:
305: require_once P::m($this->driversDir, $driverFile);
306: $className = $this->driversNamespace . substr($driverFile, 0, -4);
307: $driver = new $className;
308:
309: // driver have to be instance of AProcessorDriver
310: if (($driver instanceof AProcessorDriver) && ($driver->isAvailable()))
311: {
312: $drivers[$driver->getName()] = $driver;
313: }
314: }
315:
316: return $drivers;
317: }
318:
319:
320: /**
321: * Read information about processors
322: *
323: * @return array
324: */
325: private function readInformations()
326: {
327: $informations = array();
328: foreach ($this->getAvailable() as $name => $processorDriver)
329: {
330: $informations[$name] = $processorDriver->getInformations();
331: }
332:
333: return $informations;
334: }
335:
336:
337: /**
338: * Make command from template
339: *
340: * Templates substitutions:
341: * [XSLT] = path of XSLT template for transformation
342: * [INPUT] = path of input XML file
343: * [OUTPUT] = path of generated output XML file
344: * [ERROR] = path of file for eventual generated error message
345: * [PROCESSORS] = path of directory containing XSLT processors (libraries, command-line program etc.)
346: * [LIBS] = path of Libs directory
347: *
348: * @param string $commandTemplate Template of command
349: * @param string $templatePath Path of XSLT template for transformation
350: * @param string $xmlInputPath Path of input XML file
351: * @param string $outputPath Path of generated output XML file
352: * @param string $errorPath Path of file for eventual generated error message
353: *
354: * @return string
355: */
356: private function getCommand($commandTemplate, $templatePath, $xmlInputPath, $outputPath, $errorPath)
357: {
358: $command = $commandTemplate;
359:
360: // replace substitutions
361: $command = str_replace('[XSLT]', $templatePath, $command);
362: $command = str_replace('[INPUT]', $xmlInputPath, $command);
363: $command = str_replace('[OUTPUT]', $outputPath, $command);
364: $command = str_replace('[ERROR]', $errorPath, $command);
365: $command = str_replace('[PROCESSORS]', P::m(LIBS, 'Processors'), $command);
366: $command = str_replace('[LIBS]', P::m(LIBS), $command);
367:
368: return $command;
369: }
370:
371:
372: /**
373: * Add into input XML path of template by directive "<?xml-stylesheet href="[XSLT]" type="text/xml" ..."
374: * and return path of generated file (in temporary directory).
375: *
376: * @param string $xmlInputPath Path of input XML
377: * @param string $templatePath Path of template
378: *
379: * @return string
380: */
381: private function makeInputWithTemplatePath($xmlInputPath, $templatePath)
382: {
383: $content = file_get_contents($xmlInputPath);
384:
385: // add template path
386: $content = str_replace(
387: '<?xml version="1.0" encoding="UTF-8"?>',
388: '<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="' . $templatePath . '" ?>',
389: $content
390: );
391:
392: $xmlInputPath = P::m($this->tmpDir, basename($xmlInputPath));
393: file_put_contents($xmlInputPath, $content);
394:
395: return $xmlInputPath;
396: }
397:
398:
399: }
400: