Completed
Push — master ( bd5921...d9293e )
by Christian
17:54 queued 08:59
created

Util/Console/Helper/Table/Renderer/XmlRenderer.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/*
3
 * @author Tom Klingenberg <[email protected]>
4
 */
5
6
namespace N98\Util\Console\Helper\Table\Renderer;
7
8
use DOMDocument;
9
use DOMElement;
10
use DOMException;
11
use RuntimeException;
12
use Symfony\Component\Console\Output\OutputInterface;
13
14
/**
15
 * Class XmlRenderer
16
 *
17
 * @package N98\Util\Console\Helper\Table\Renderer
18
 */
19
class XmlRenderer implements RendererInterface
20
{
21
    const NAME_ROOT = 'table';
22
    const NAME_ROW  = 'row';
23
24
    private $headers;
25
26
    /**
27
     * {@inheritdoc}
28
     */
29
    public function render(OutputInterface $output, array $rows)
30
    {
31
        $dom = new DOMDocument('1.0', 'UTF-8');
32
        $dom->formatOutput = true;
33
34
        $rows && $this->setHeadersFrom($rows);
0 ignored issues
show
Bug Best Practice introduced by
The expression $rows of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
35
36
        $table = $dom->createElement(self::NAME_ROOT);
37
38
        /** @var DOMElement $table */
39
        $table = $dom->appendChild($table);
40
41
        $this->appendHeaders($table, $this->headers);
42
        $this->appendRows($table, $rows);
43
44
        /** @var $output \Symfony\Component\Console\Output\StreamOutput */
45
        $output->write($dom->saveXML(), false, $output::OUTPUT_RAW);
46
    }
47
48
    private function appendRows(DOMElement $parent, array $rows)
49
    {
50
        $doc = $parent->ownerDocument;
51
52
        if (!$rows) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rows of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
53
            $parent->appendChild($doc->createComment('intentionally left blank, the table is empty'));
54
55
            return;
56
        }
57
58
        foreach ($rows as $fields) {
59
            /** @var DOMElement $row */
60
            $row = $parent->appendChild($doc->createElement(self::NAME_ROW));
61
            $this->appendRowFields($row, $fields);
62
        }
63
    }
64
65
    /**
66
     * @param DOMElement $row
67
     * @param array      $fields
68
     */
69
    private function appendRowFields(DOMElement $row, array $fields)
70
    {
71
        $index = 0;
72
        foreach ($fields as $key => $value) {
73
            $header  = $this->getHeader($index++, $key);
74
            $element = $this->createField($row->ownerDocument, $header, $value);
75
            $row->appendChild($element);
76
        }
77
    }
78
79
    /**
80
     * @param DOMElement $parent
81
     * @param array      $headers
82
     */
83
    private function appendHeaders(DOMElement $parent, array $headers = null)
84
    {
85
        if (!$headers) {
86
            return;
87
        }
88
89
        $doc = $parent->ownerDocument;
90
91
        $parent = $parent->appendChild($doc->createElement('headers'));
92
93
        foreach ($headers as $header) {
94
            $parent->appendChild($doc->createElement('header', $header));
95
        }
96
    }
97
98
    /**
99
     * create a DOMElement containing the data
100
     *
101
     * @param DOMDocument $doc
102
     * @param string      $key
103
     * @param string      $value
104
     *
105
     * @return DOMElement
106
     */
107
    private function createField(DOMDocument $doc, $key, $value)
108
    {
109
        $name = $this->getName($key);
110
111
        $base64 = !preg_match('//u', $value) || preg_match('/[\x0-\x8\xB-\xC\xE-\x1F]/', $value);
112
113
        $node = $doc->createElement($name, $base64 ? base64_encode($value) : $value);
114
115
        if ($base64) {
116
            $node->setAttribute('encoding', 'base64');
117
        }
118
119
        return $node;
120
    }
121
122
    /**
123
     * @param string $string
124
     *
125
     * @return string valid XML element name
126
     *
127
     * @throws DOMException if no valid XML Name can be generated
128
     * @throws RuntimeException if character encoding is not US-ASCII or UTF-8
129
     */
130
    private function getName($string)
131
    {
132
        $name = preg_replace("/[^a-z0-9]/ui", '_', $string);
133
        if (null === $name) {
134
            throw new RuntimeException(
135
                sprintf('Encoding error, only US-ASCII and UTF-8 supported, can not process %s', var_export($string, true))
0 ignored issues
show
This line exceeds maximum limit of 120 characters; contains 123 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
136
            );
137
        }
138
139
        try {
140
            new DOMElement("$name");
141
        } catch (DOMException $e) {
142
            throw new DOMException(sprintf('Invalid name %s', var_export($name, true)));
143
        }
144
145
        return $name;
146
    }
147
148
    /**
149
     * @param int   $index zero-based
150
     * @param mixed $default
151
     *
152
     * @return string
153
     */
154
    private function getHeader($index, $default = null)
155
    {
156
        if (!isset($this->headers[$index])) {
157
            return $default;
158
        }
159
160
        return $this->headers[$index];
161
    }
162
163
    /**
164
     * @param array $rows
165
     *
166
     * @return void
167
     */
168
    private function setHeadersFrom(array $rows)
169
    {
170
        $first = reset($rows);
171
172
        if (is_array($first)) {
173
            $this->headers = array_keys($first);
174
        }
175
    }
176
}
177