1<?php
2/**
3 * Copyright (c) 2008-2009, Laurent Laville <pear@laurent-laville.org>
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 *     * Redistributions of source code must retain the above copyright
12 *       notice, this list of conditions and the following disclaimer.
13 *     * Redistributions in binary form must reproduce the above copyright
14 *       notice, this list of conditions and the following disclaimer in the
15 *       documentation and/or other materials provided with the distribution.
16 *     * Neither the name of the authors nor the names of its contributors
17 *       may be used to endorse or promote products derived from this software
18 *       without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 * PHP versions 4 and 5
33 *
34 * @category PHP
35 * @package  PHP_CompatInfo
36 * @author   Laurent Laville <pear@laurent-laville.org>
37 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD
38 * @version  CVS: $Id: Xml.php,v 1.13 2009/01/02 10:18:47 farell Exp $
39 * @link     http://pear.php.net/package/PHP_CompatInfo
40 * @since    File available since Release 1.8.0b2
41 */
42
43require_once 'XML/Util.php';
44
45/**
46 * Array renderer for PHP_CompatInfo component.
47 *
48 * The PHP_CompatInfo_Renderer_Xml class is a concrete implementation
49 * of PHP_CompatInfo_Renderer abstract class. It simply display results as
50 * an XML stream.
51 *
52 * @category PHP
53 * @package  PHP_CompatInfo
54 * @author   Laurent Laville <pear@laurent-laville.org>
55 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD
56 * @version  Release: 1.9.0
57 * @link     http://pear.php.net/package/PHP_CompatInfo
58 * @since    Class available since Release 1.8.0b2
59 */
60class PHP_CompatInfo_Renderer_Xml extends PHP_CompatInfo_Renderer
61{
62    /**
63     * Xml Renderer Class constructor (ZE1) for PHP4
64     *
65     * @param object &$parser Instance of the parser (model of MVC pattern)
66     * @param array  $conf    A hash containing any additional configuration
67     *
68     * @access public
69     * @since  version 1.8.0b2 (2008-06-03)
70     */
71    function PHP_CompatInfo_Renderer_Xml(&$parser, $conf)
72    {
73        $this->__construct($parser, $conf);
74    }
75
76    /**
77     * Xml Renderer Class constructor (ZE2) for PHP5+
78     *
79     * @param object &$parser Instance of the parser (model of MVC pattern)
80     * @param array  $conf    A hash containing any additional configuration
81     *
82     * @access public
83     * @since  version 1.8.0b2 (2008-06-03)
84     */
85    function __construct(&$parser, $conf)
86    {
87        $defaults = array('use-beautifier' => 'auto');
88        $conf     = array_merge($defaults, $conf);
89
90        parent::PHP_CompatInfo_Renderer($parser, $conf);
91    }
92
93    /**
94     * Display final results
95     *
96     * Display final results, when data source parsing is over.
97     *
98     * @access public
99     * @return void
100     * @since  version 1.8.0b2 (2008-06-03)
101     */
102    function display()
103    {
104        if ($this->parseData === false) {
105            // invalid data source
106            return;
107        }
108
109        $version    = isset($this->conf['xml']['version'])
110                    ? $this->conf['xml']['version'] : '1.0';
111        $encoding   = isset($this->conf['xml']['encoding'])
112                    ? $this->conf['xml']['encoding'] : 'UTF-8';
113        $standalone = isset($this->conf['xml']['standalone'])
114                    ? $this->conf['xml']['standalone'] : null;
115
116        $msg  = XML_Util::getXMLDeclaration($version, $encoding, $standalone);
117        $msg .= PHP_EOL;
118        $msg .= XML_Util::createStartElement('pci',
119                                       array('version' => '1.9.0'));
120
121        $o = $this->args['output-level'];
122        $v = $this->args['verbose'];
123
124        $dataSource = $this->_parser->dataSource['dataSource'];
125        $dataType   = $this->_parser->dataSource['dataType'];
126        $options    = $this->_parser->options;
127
128        if ($dataType == 'directory'
129            || $dataType == 'array'
130            || $dataType == 'file') {
131            // parsing a directory or a list of files, chunks of code
132
133            if ($options['is_string'] == false) {
134                if ($dataType == 'directory') {
135                    // print <dir> tag
136                    $tag = array('qname' => 'dir',
137                                 'content' => dirname($dataSource[0]));
138                } else {
139                    // print <file> tag
140                    $tag = array('qname' => 'file',
141                                 'content' => $dataSource[0]);
142                }
143                $msg .= XML_Util::createTagFromArray($tag);
144                $msg .= PHP_EOL;
145            }
146
147            // print global <version> tag
148            if ($o & 16) {
149                if (empty($this->parseData['max_version'])) {
150                    $attr = array();
151                } else {
152                    $attr = array('max' => $this->parseData['max_version']);
153                }
154                $tag  = array('qname' => 'version',
155                              'attributes' => $attr,
156                              'content' => $this->parseData['version']);
157                $msg .= XML_Util::createTagFromArray($tag);
158                $msg .= PHP_EOL;
159            }
160
161            // print global <conditions> tag group
162            if ($o & 1) {
163                $msg .= $this->_printTagList($this->parseData['cond_code'],
164                                             'condition');
165            }
166            // print global <extensions> tag group
167            if ($o & 2) {
168                $msg .= $this->_printTagList($this->parseData['extensions'],
169                                             'extension');
170            }
171            // print global <constants> tag group
172            if ($o & 4) {
173                $msg .= $this->_printTagList($this->parseData['constants'],
174                                             'constant');
175            }
176            // print global <tokens> tag group
177            if ($o & 8) {
178                $msg .= $this->_printTagList($this->parseData['tokens'],
179                                             'token');
180            }
181
182            // print global <ignored> tag group
183            $msg .= XML_Util::createStartElement('ignored');
184            $msg .= PHP_EOL;
185            // with children groups <files>, <functions>, <extensions>, <constants>
186            $ignored = array('file' => $this->parseData['ignored_files'],
187                             'function' => $this->parseData['ignored_functions'],
188                             'extension' => $this->parseData['ignored_extensions'],
189                             'constant' => $this->parseData['ignored_constants']);
190            foreach ($ignored as $tag => $data) {
191                $msg .= $this->_printTagList($data, $tag);
192            }
193            $msg .= XML_Util::createEndElement('ignored');
194            $msg .= PHP_EOL;
195
196            // remove summary data
197            unset($this->parseData['ignored_files']);
198            unset($this->parseData['ignored_functions']);
199            unset($this->parseData['ignored_extensions']);
200            unset($this->parseData['ignored_constants']);
201            unset($this->parseData['max_version']);
202            unset($this->parseData['version']);
203            unset($this->parseData['classes']);
204            unset($this->parseData['functions']);
205            unset($this->parseData['extensions']);
206            unset($this->parseData['constants']);
207            unset($this->parseData['tokens']);
208            unset($this->parseData['cond_code']);
209
210            if ($v & 4 || $options['debug'] == true) {
211                // print local <functions> tag group
212                $msg .= $this->_printTagList($this->parseData, 'function');
213
214                $entries = array_keys($this->parseData);
215                foreach ($entries as $k) {
216                    if (is_numeric($k{0})) {
217                        unset($this->parseData[$k]);
218                    }
219                }
220            }
221
222            if ($dataType == 'file') {
223                // parsing a single file
224                $files = array($dataSource[0] => $this->parseData);
225            } else {
226                $files = $this->parseData;
227            }
228        } else {
229            // ... or a chunk of code (string)
230            $files = array($this->parseData);
231        }
232
233        if ($this->args['summarize'] === false
234            && count($files) > 1) {
235
236            if ($options['is_string'] == false) {
237                // print <files> tag group
238                $msg .= XML_Util::createStartElement('files',
239                                               array('count' => count($files)));
240                $msg .= PHP_EOL;
241            }
242
243            foreach ($files as $file => $this->parseData) {
244                if ($options['is_string'] == true) {
245                    $msg .= XML_Util::createStartElement('string',
246                                                   array('name' => $file));
247                } else {
248                    // print local <file> tag
249                    $msg .= XML_Util::createStartElement('file',
250                                                   array('name' => $file));
251                }
252                $msg .= PHP_EOL;
253
254                // print local <version> tag
255                if ($o & 16) {
256                    if (empty($this->parseData['max_version'])) {
257                        $attr = array();
258                    } else {
259                        $attr = array('max' => $this->parseData['max_version']);
260                    }
261                    $tag  = array('qname' => 'version',
262                                  'attributes' => $attr,
263                                  'content' => $this->parseData['version']);
264                    $msg .= XML_Util::createTagFromArray($tag);
265                    $msg .= PHP_EOL;
266                }
267
268                // print local <conditions> tag group
269                if ($o & 1) {
270                    $msg .= $this->_printTagList($this->parseData['cond_code'],
271                                                 'condition');
272                }
273                // print local <extensions> tag group
274                if ($o & 2) {
275                    $msg .= $this->_printTagList($this->parseData['extensions'],
276                                                 'extension');
277                }
278                // print local <constants> tag group
279                if ($o & 4) {
280                    $msg .= $this->_printTagList($this->parseData['constants'],
281                                                 'constant');
282                }
283                // print local <tokens> tag group
284                if ($o & 8) {
285                    $msg .= $this->_printTagList($this->parseData['tokens'],
286                                                 'token');
287                }
288
289                // print local <ignored> tag group
290                $msg .= XML_Util::createStartElement('ignored');
291                $msg .= PHP_EOL;
292                // with children groups <functions>, <extensions>, <constants>
293                $ignored = array(
294                    'function' => $this->parseData['ignored_functions'],
295                    'extension' => $this->parseData['ignored_extensions'],
296                    'constant' => $this->parseData['ignored_constants']
297                    );
298                foreach ($ignored as $tag => $data) {
299                    $msg .= $this->_printTagList($data, $tag);
300                }
301                $msg .= XML_Util::createEndElement('ignored');
302                $msg .= PHP_EOL;
303
304                // extra information only if verbose mode >= 4
305                if ($v & 4 || $options['debug'] == true) {
306                    unset($this->parseData['ignored_files']);
307                    unset($this->parseData['ignored_functions']);
308                    unset($this->parseData['ignored_extensions']);
309                    unset($this->parseData['ignored_constants']);
310                    unset($this->parseData['max_version']);
311                    unset($this->parseData['version']);
312                    unset($this->parseData['classes']);
313                    unset($this->parseData['functions']);
314                    unset($this->parseData['extensions']);
315                    unset($this->parseData['constants']);
316                    unset($this->parseData['tokens']);
317                    unset($this->parseData['cond_code']);
318
319                    // print local <functions> tag group
320                    $msg .= $this->_printTagList($this->parseData, 'function');
321                }
322
323                if ($options['is_string'] == true) {
324                    $msg .= XML_Util::createEndElement('string');
325                } else {
326                    $msg .= XML_Util::createEndElement('file');
327                }
328                $msg .= PHP_EOL;
329            }
330            if ($options['is_string'] == false) {
331                $msg .= XML_Util::createEndElement('files');
332                $msg .= PHP_EOL;
333            }
334        }
335        $msg .= XML_Util::createEndElement('pci');
336        $msg .= PHP_EOL;
337
338        if (strtolower($this->conf['use-beautifier']) != 'no') {
339            // try to see if we can improve XML render
340            $beautifier = 'XML/Beautifier.php';
341            if (PHP_CompatInfo_Renderer::isIncludable($beautifier)) {
342                include_once $beautifier;
343                $def = array();
344                $opt = isset($this->conf['beautifier'])
345                     ? $this->conf['beautifier'] : $def;
346                $fmt = new XML_Beautifier($opt);
347                $msg = $fmt->formatString($msg);
348            }
349        }
350
351        echo $msg;
352    }
353
354    /**
355     * Print a group of same tag in the XML report.
356     *
357     * Groups list are : extension(s), constant(s), token(s)
358     *
359     * @param array  $dataSrc Data source
360     * @param string $tagName Name of the XML tag
361     *
362     * @return string
363     * @access private
364     * @since  version 1.7.0b4 (2008-04-03)
365     */
366    function _printTagList($dataSrc, $tagName)
367    {
368        $msg = '';
369
370        if ($tagName == 'function') {
371            $c = 0;
372            foreach ($dataSrc as $version => $functions) {
373                $c += count($functions);
374            }
375            $attributes = array('count' => $c);
376        } elseif ($tagName == 'condition') {
377            if ($this->_parser->options['debug'] === true) {
378                $c = 0;
379                foreach ($dataSrc[1] as $cond => $elements) {
380                    $c += count($elements);
381                }
382                $attributes = array('count' => $c, 'level' => $dataSrc[0]);
383            } else {
384                $attributes = array('level' => $dataSrc[0]);
385            }
386        } else {
387            $attributes = array('count' => count($dataSrc));
388        }
389
390        $msg .= XML_Util::createStartElement($tagName.'s', $attributes);
391        $msg .= PHP_EOL;
392
393        if ($tagName == 'function') {
394            foreach ($dataSrc as $version => $functions) {
395                foreach ($functions as $data) {
396                    $attr = array('version' => $version);
397                    if (!empty($data['extension'])) {
398                        $attr['extension'] = $data['extension'];
399                        $attr['pecl']      = $data['pecl'] === true ?
400                                                'true' : 'false';
401                    }
402                    $tag  = array('qname' => $tagName,
403                                  'attributes' => $attr,
404                                  'content' => $data['function']);
405                    $msg .= XML_Util::createTagFromArray($tag);
406                    $msg .= PHP_EOL;
407                }
408            }
409        } elseif ($tagName == 'condition') {
410            if ($this->_parser->options['debug'] == true) {
411                foreach ($dataSrc[1] as $cond => $elements) {
412                    $cond = ($cond == 0) ? 1 : ($cond * 2);
413                    foreach ($elements as $data) {
414                        $tag  = array('qname' => $tagName,
415                                      'attributes' => array('level' => $cond),
416                                      'content' => $data);
417                        $msg .= XML_Util::createTagFromArray($tag);
418                        $msg .= PHP_EOL;
419                    }
420                }
421            }
422        } else {
423            foreach ($dataSrc as $data) {
424                $tag  = array('qname' => $tagName,
425                              'attributes' => array(),
426                              'content' => $data);
427                $msg .= XML_Util::createTagFromArray($tag);
428                $msg .= PHP_EOL;
429            }
430        }
431
432        $msg .= XML_Util::createEndElement($tagName.'s');
433        $msg .= PHP_EOL;
434
435        return $msg;
436    }
437}
438?>