Completed
Push — master ( a594de...325643 )
by Mark
29s queued 10s
created

TableHelper::_cellWidth()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 1
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11
 * @since         3.1.0
12
 * @license       https://opensource.org/licenses/mit-license.php MIT License
13
 */
14
namespace Cake\Shell\Helper;
15
16
use Cake\Console\Helper;
17
18
/**
19
 * Create a visually pleasing ASCII art table
20
 * from 2 dimensional array data.
21
 */
22
class TableHelper extends Helper
23
{
24
25
    /**
26
     * Default config for this helper.
27
     *
28
     * @var array
29
     */
30
    protected $_defaultConfig = [
31
        'headers' => true,
32
        'rowSeparator' => false,
33
        'headerStyle' => 'info',
34
    ];
35
36
    /**
37
     * Calculate the column widths
38
     *
39
     * @param array $rows The rows on which the columns width will be calculated on.
40
     * @return array
41
     */
42
    protected function _calculateWidths($rows)
43
    {
44
        $widths = [];
45
        foreach ($rows as $line) {
46
            foreach (array_values($line) as $k => $v) {
47
                $columnLength = $this->_cellWidth($v);
48
                if ($columnLength >= (isset($widths[$k]) ? $widths[$k] : 0)) {
49
                    $widths[$k] = $columnLength;
50
                }
51
            }
52
        }
53
54
        return $widths;
55
    }
56
57
    /**
58
     * Get the width of a cell exclusive of style tags.
59
     *
60
     * @param string $text The text to calculate a width for.
61
     * @return int The width of the textual content in visible characters.
62
     */
63
    protected function _cellWidth($text)
64
    {
65
        if (strpos($text, '<') === false && strpos($text, '>') === false) {
66
            return mb_strwidth($text);
67
        }
68
        $styles = array_keys($this->_io->styles());
69
        $tags = implode('|', $styles);
70
        $text = preg_replace('#</?(?:' . $tags . ')>#', '', $text);
71
72
        return mb_strwidth($text);
73
    }
74
75
    /**
76
     * Output a row separator.
77
     *
78
     * @param array $widths The widths of each column to output.
79
     * @return void
80
     */
81
    protected function _rowSeparator($widths)
82
    {
83
        $out = '';
84
        foreach ($widths as $column) {
85
            $out .= '+' . str_repeat('-', $column + 2);
86
        }
87
        $out .= '+';
88
        $this->_io->out($out);
89
    }
90
91
    /**
92
     * Output a row.
93
     *
94
     * @param array $row The row to output.
95
     * @param array $widths The widths of each column to output.
96
     * @param array $options Options to be passed.
97
     * @return void
98
     */
99
    protected function _render(array $row, $widths, $options = [])
100
    {
101
        if (count($row) === 0) {
102
            return;
103
        }
104
105
        $out = '';
106
        foreach (array_values($row) as $i => $column) {
107
            $pad = $widths[$i] - $this->_cellWidth($column);
108
            if (!empty($options['style'])) {
109
                $column = $this->_addStyle($column, $options['style']);
110
            }
111
            $out .= '| ' . $column . str_repeat(' ', $pad) . ' ';
112
        }
113
        $out .= '|';
114
        $this->_io->out($out);
115
    }
116
117
    /**
118
     * Output a table.
119
     *
120
     * Data will be output based on the order of the values
121
     * in the array. The keys will not be used to align data.
122
     *
123
     * @param array $rows The data to render out.
124
     * @return void
125
     */
126
    public function output($rows)
127
    {
128
        if (!is_array($rows) || count($rows) === 0) {
129
            return;
130
        }
131
132
        $config = $this->getConfig();
133
        $widths = $this->_calculateWidths($rows);
134
135
        $this->_rowSeparator($widths);
136
        if ($config['headers'] === true) {
137
            $this->_render(array_shift($rows), $widths, ['style' => $config['headerStyle']]);
138
            $this->_rowSeparator($widths);
139
        }
140
141
        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...
142
            return;
143
        }
144
145
        foreach ($rows as $line) {
146
            $this->_render($line, $widths);
147
            if ($config['rowSeparator'] === true) {
148
                $this->_rowSeparator($widths);
149
            }
150
        }
151
        if ($config['rowSeparator'] !== true) {
152
            $this->_rowSeparator($widths);
153
        }
154
    }
155
156
    /**
157
     * Add style tags
158
     *
159
     * @param string $text The text to be surrounded
160
     * @param string $style The style to be applied
161
     * @return string
162
     */
163
    protected function _addStyle($text, $style)
164
    {
165
        return '<' . $style . '>' . $text . '</' . $style . '>';
166
    }
167
}
168