Table::result()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 8
cts 8
cp 1
rs 9.7666
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
namespace League\CLImate\TerminalObject\Basic;
4
5
use League\CLImate\Exceptions\InvalidArgumentException;
6
use League\CLImate\TerminalObject\Helper\StringLength;
7
use function get_object_vars;
8
use function is_array;
9
use function is_object;
10
11
class Table extends BasicTerminalObject
12
{
13
    use StringLength;
14
15
    /**
16
     * The data for the table, an array of (arrays|objects)
17
     *
18
     * @var array $data
19
     */
20
    protected $data           = [];
21
22
    /**
23
     * An array of the widths of each column in the table
24
     *
25
     * @var array $column_widths
26
     */
27
    protected $column_widths  = [];
28
29
    /**
30
     * The width of the table
31
     *
32
     * @var integer $table_width
33
     */
34
    protected $table_width    = 0;
35
36
    /**
37
     * The divider between table cells
38
     *
39
     * @var string $column_divider
40
     */
41
    protected $column_divider = ' | ';
42
43
    /**
44
     * The border to divide each row of the table
45
     *
46
     * @var string $border
47
     */
48
    protected $border;
49
50
    /**
51
     * The array of rows that will ultimately be returned
52
     *
53 28
     * @var array $rows
54
     */
55 28
    protected $rows           = [];
56 28
57
    /**
58
     * @var string $pregix A string to be output before each row is output.
59
     */
60
    private $prefix = "";
61
62
63 28
    public function __construct(array $data, $prefix = "")
64
    {
65 28
        $this->data = $this->getData($data);
66 28
        $this->prefix = $prefix;
67 28
    }
68
69 28
70
    /**
71 28
     * @param array $input
72 28
     *
73 28
     * @return array
74 28
     */
75
    private function getData(array $input)
76 28
    {
77
        $output = [];
78
79
        foreach ($input as $item) {
80
            if (is_object($item)) {
81
                $item = get_object_vars($item);
82
            }
83
84 28
            if (!is_array($item)) {
85
                throw new InvalidArgumentException("Invalid table data, you must pass an array of arrays or objects");
86 28
            }
87 28
88
            $output[] = $item;
89 28
        }
90
91
        return $output;
92
    }
93
94
95 28
    /**
96
     * Return the built rows
97 28
     *
98
     * @return array
99
     */
100
    public function result()
101
    {
102
        $this->column_widths = $this->getColumnWidths();
103
        $this->table_width   = $this->getWidth();
104 28
        $this->border        = $this->getBorder();
105
106 28
        $this->buildHeaderRow();
107
108 28
        foreach ($this->data as $columns) {
109 24
            $this->addLine($this->buildRow($columns));
110 24
            $this->addLine($this->border);
111 24
        }
112 24
113 4
        return $this->rows;
114
    }
115 28
116
    /**
117
     * Append a line to the output.
118
     *
119
     * @param string $line The line to output
120
     *
121
     * @return void
122
     */
123
    private function addLine($line)
124 28
    {
125
        $this->rows[] = $this->prefix . $line;
126 28
    }
127
128 28
129 28
    /**
130 28
     * Determine the width of the table
131
     *
132 28
     * @return integer
133
     */
134 28
    protected function getWidth()
135
    {
136
        $first_row = reset($this->data);
137
        $first_row = $this->buildRow($first_row);
138
139
        return $this->lengthWithoutTags($first_row);
140
    }
141
142
    /**
143
     * Get the border for each row based on the table width
144
     */
145 28
    protected function getBorder()
146
    {
147 28
        return (new Border())->length($this->table_width)->result();
148
    }
149
150
    /**
151
     * Check for a header row (if it's an array of associative arrays or objects),
152
     * if there is one, tack it onto the front of the rows array
153
     */
154
    protected function buildHeaderRow()
155 28
    {
156
        $this->addLine($this->border);
157 28
158
        $header_row = $this->getHeaderRow();
159 28
        if ($header_row) {
160 4
            $this->addLine($this->buildRow($header_row));
161 4
            $this->addLine((new Border)->char('=')->length($this->table_width)->result());
162
        }
163 28
    }
164 28
165
    /**
166
     * Get table row
167 28
     *
168 24
     * @param  mixed  $columns
169
     *
170
     * @return string
171 4
     */
172
    protected function buildRow($columns)
173
    {
174
        $row = [];
175
176
        foreach ($columns as $key => $column) {
177
            $row[] = $this->buildCell($key, $column);
178
        }
179 28
180
        $row = implode($this->column_divider, $row);
181 28
182
        return trim($this->column_divider . $row . $this->column_divider);
183 28
    }
184 4
185 4
    /**
186
     * Build the string for this particular table cell
187
     *
188 28
     * @param  mixed  $key
189
     * @param  string $column
190 28
     *
191 28
     * @return string
192 28
     */
193 28
    protected function buildCell($key, $column)
194 28
    {
195
        return  $this->pad($column, $this->column_widths[$key]);
196 28
    }
197
198
    /**
199
     * Get the header row for the table if it's an associative array or object
200
     *
201
     * @return mixed
202
     */
203
    protected function getHeaderRow()
204
    {
205
        $first_item = reset($this->data);
206 28
207
        $keys       = array_keys($first_item);
208 28
        $first_key  = reset($keys);
209
210 28
        // We have an associative array (probably), let's have a header row
211
        if (!is_int($first_key)) {
212
            return array_combine($keys, $keys);
213
        }
214
215
        return false;
216
    }
217
218
    /**
219
     * Determine the width of each column
220
     *
221 28
     * @return array
222
     */
223 28
    protected function getColumnWidths()
224
    {
225
        $first_row = reset($this->data);
226
227
        // Create an array with the columns as keys and values of zero
228
        $column_widths = $this->getDefaultColumnWidths($first_row);
229
230
        foreach ($this->data as $columns) {
231
            foreach ($columns as $key => $column) {
232
                $column_widths[$key] = $this->getCellWidth($column_widths[$key], $column);
233
            }
234
        }
235
236
        return $column_widths;
237
    }
238
239
    /**
240
     * Set up an array of default column widths
241
     *
242
     * @param array $columns
243
     *
244
     * @return array
245
     */
246
    protected function getDefaultColumnWidths(array $columns)
247
    {
248
        $widths = $this->arrayOfStrLens(array_keys($columns));
249
250
        return array_combine(array_keys($columns), $widths);
251
    }
252
253
    /**
254
     * Determine the width of the columns without tags
255
     *
256
     * @param array  $current_width
257
     * @param string $str
258
     *
259
     * @return integer
260
     */
261
    protected function getCellWidth($current_width, $str)
262
    {
263
        return max($current_width, $this->lengthWithoutTags($str));
264
    }
265
}
266