Completed
Pull Request — master (#160)
by
unknown
34:50
created

Table::getHeaderRow()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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