console_table::_substr()   A
last analyzed

Complexity

Conditions 5
Paths 12

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 12
nop 3
dl 0
loc 17
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Utility for printing tables from commandline scripts.
5
 *
6
 * PHP versions 5 and 7
7
 *
8
 * All rights reserved.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions are met:
12
 *
13
 * o Redistributions of source code must retain the above copyright notice,
14
 *   this list of conditions and the following disclaimer.
15
 * o Redistributions in binary form must reproduce the above copyright notice,
16
 *   this list of conditions and the following disclaimer in the documentation
17
 *   and/or other materials provided with the distribution.
18
 * o The names of the authors may not be used to endorse or promote products
19
 *   derived from this software without specific prior written permission.
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
 * POSSIBILITY OF SUCH DAMAGE.
32
 *
33
 * @category  Console
34
 * @package   Console_Table
35
 * @author    Richard Heyes <[email protected]>
36
 * @author    Jan Schneider <[email protected]>
37
 * @copyright 2002-2005 Richard Heyes
38
 * @copyright 2006-2008 Jan Schneider
39
 * @license   http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
40
 * @version   CVS: $Id$
41
 * @link      http://pear.php.net/package/Console_Table
42
 */
43
define('CONSOLE_TABLE_HORIZONTAL_RULE', 1);
44
define('CONSOLE_TABLE_ALIGN_LEFT', -1);
45
define('CONSOLE_TABLE_ALIGN_CENTER', 0);
46
define('CONSOLE_TABLE_ALIGN_RIGHT', 1);
47
define('CONSOLE_TABLE_BORDER_ASCII', -1);
48
/**
49
 * The main class.
50
 *
51
 * @category Console
52
 * @package  Console_Table
53
 * @author   Jan Schneider <[email protected]>
54
 * @license  http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
55
 * @link     http://pear.php.net/package/Console_Table
56
 */
57
class console_table
58
{
59
    /**
60
     * The table headers.
61
     *
62
     * @var array
63
     */
64
    var $_headers = array();
65
    /**
66
     * The data of the table.
67
     *
68
     * @var array
69
     */
70
    var $_data = array();
71
    /**
72
     * The maximum number of columns in a row.
73
     *
74
     * @var integer
75
     */
76
    var $_max_cols = 0;
77
    /**
78
     * The maximum number of rows in the table.
79
     *
80
     * @var integer
81
     */
82
    var $_max_rows = 0;
83
    /**
84
     * Lengths of the columns, calculated when rows are added to the table.
85
     *
86
     * @var array
87
     */
88
    var $_cell_lengths = array();
89
    /**
90
     * Heights of the rows.
91
     *
92
     * @var array
93
     */
94
    var $_row_heights = array();
95
    /**
96
     * How many spaces to use to pad the table.
97
     *
98
     * @var integer
99
     */
100
    var $_padding = 1;
101
    /**
102
     * Column filters.
103
     *
104
     * @var array
105
     */
106
    var $_filters = array();
107
    /**
108
     * Columns to calculate totals for.
109
     *
110
     * @var array
111
     */
112
    var $_calculateTotals;
113
    /**
114
     * Alignment of the columns.
115
     *
116
     * @var array
117
     */
118
    var $_col_align = array();
119
    /**
120
     * Default alignment of columns.
121
     *
122
     * @var integer
123
     */
124
    var $_defaultAlign;
125
    /**
126
     * Character set of the data.
127
     *
128
     * @var string
129
     */
130
    var $_charset = 'utf-8';
131
    /**
132
     * Border characters.
133
     * Allowed keys:
134
     * - intersection - intersection ("+")
135
     * - horizontal - horizontal rule character ("-")
136
     * - vertical - vertical rule character ("|")
137
     *
138
     * @var array
139
     */
140
    var $_border = array(
141
        'intersection' => '+',
142
        'horizontal' => '-',
143
        'vertical' => '|',
144
    );
145
    /**
146
     * If borders are shown or not
147
     * Allowed keys: top, right, bottom, left, inner: true and false
148
     *
149
     * @var array
150
     */
151
    var $_borderVisibility = array(
152
        'top'    => true,
153
        'right'  => true,
154
        'bottom' => true,
155
        'left'   => true,
156
        'inner'  => true
157
    );
158
    /**
159
     * Whether the data has ANSI colors.
160
     *
161
     * @var Console_Color2
0 ignored issues
show
Bug introduced by
The type Console_Color2 was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
162
     */
163
    var $_ansiColor = false;
164
    /**
165
     * Constructor.
166
     *
167
     * @param integer $align   Default alignment. One of
168
     *                         CONSOLE_TABLE_ALIGN_LEFT,
169
     *                         CONSOLE_TABLE_ALIGN_CENTER or
170
     *                         CONSOLE_TABLE_ALIGN_RIGHT.
171
     * @param string  $border  The character used for table borders or
172
     *                         CONSOLE_TABLE_BORDER_ASCII.
173
     * @param integer $padding How many spaces to use to pad the table.
174
     * @param string  $charset A charset supported by the mbstring PHP
175
     *                         extension.
176
     * @param boolean $color   Whether the data contains ansi color codes.
177
     */
178
    function __construct($align = CONSOLE_TABLE_ALIGN_LEFT,
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
179
        $border = CONSOLE_TABLE_BORDER_ASCII, $padding = 1,
180
        $charset = null, $color = false)
181
    {
182
        $this->_defaultAlign = $align;
183
        $this->setBorder($border);
184
        $this->_padding      = $padding;
185
        if ($color) {
186
            if (!class_exists('Console_Color2')) {
187
                include_once 'Console/Color2.php';
188
            }
189
            $this->_ansiColor = new Console_Color2();
190
        }
191
        if (!empty($charset)) {
192
            $this->setCharset($charset);
193
        }
194
    }
195
    /**
196
     * Converts an array to a table.
197
     *
198
     * @param array   $headers      Headers for the table.
199
     * @param array   $data         A two dimensional array with the table
200
     *                              data.
201
     * @param boolean $returnObject Whether to return the Console_Table object
202
     *                              instead of the rendered table.
203
     *
204
     * @static
205
     *
206
     * @return Console_Table|string  A Console_Table object or the generated
207
     *                               table.
208
     */
209
    function fromArray($headers, $data, $returnObject = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
210
    {
211
        if (!is_array($headers) || !is_array($data)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always true.
Loading history...
introduced by
The condition is_array($headers) is always true.
Loading history...
212
            return false;
213
        }
214
        $table = new Console_Table();
215
        $table->setHeaders($headers);
216
        foreach ($data as $row) {
217
            $table->addRow($row);
218
        }
219
        return $returnObject ? $table : $table->getTable();
220
    }
221
    /**
222
     * Adds a filter to a column.
223
     *
224
     * Filters are standard PHP callbacks which are run on the data before
225
     * table generation is performed. Filters are applied in the order they
226
     * are added. The callback function must accept a single argument, which
227
     * is a single table cell.
228
     *
229
     * @param integer $col       Column to apply filter to.
230
     * @param mixed   &$callback PHP callback to apply.
231
     *
232
     * @return void
233
     */
234
    function addFilter($col, &$callback)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
235
    {
236
        $this->_filters[] = array($col, &$callback);
237
    }
238
    /**
239
     * Sets the charset of the provided table data.
240
     *
241
     * @param string $charset A charset supported by the mbstring PHP
242
     *                        extension.
243
     *
244
     * @return void
245
     */
246
    function setCharset($charset)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
247
    {
248
        $locale = setlocale(LC_CTYPE, 0);
249
        setlocale(LC_CTYPE, 'en_US');
250
        $this->_charset = strtolower($charset);
251
        setlocale(LC_CTYPE, $locale);
252
    }
253
    /**
254
     * Set the table border settings
255
     *
256
     * Border definition modes:
257
     * - CONSOLE_TABLE_BORDER_ASCII: Default border with +, - and |
258
     * - array with keys "intersection", "horizontal" and "vertical"
259
     * - single character string that sets all three of the array keys
260
     *
261
     * @param mixed $border Border definition
262
     *
263
     * @return void
264
     * @see $_border
265
     */
266
    function setBorder($border)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
267
    {
268
        if ($border === CONSOLE_TABLE_BORDER_ASCII) {
269
            $intersection = '+';
270
            $horizontal = '-';
271
            $vertical = '|';
272
        } else if (is_string($border)) {
273
            $intersection = $horizontal = $vertical = $border;
274
        } else if ($border == '') {
275
            $intersection = $horizontal = $vertical = '';
276
        } else {
277
            extract($border);
278
        }
279
        $this->_border = array(
280
            'intersection' => $intersection,
281
            'horizontal' => $horizontal,
282
            'vertical' => $vertical,
283
        );
284
    }
285
    /**
286
     * Set which borders shall be shown.
287
     *
288
     * @param array $visibility Visibility settings.
289
     *                          Allowed keys: left, right, top, bottom, inner
290
     *
291
     * @return void
292
     * @see    $_borderVisibility
293
     */
294
    function setBorderVisibility($visibility)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
295
    {
296
        $this->_borderVisibility = array_merge(
297
            $this->_borderVisibility,
298
            array_intersect_key(
299
                $visibility,
300
                $this->_borderVisibility
301
            )
302
        );
303
    }
304
    /**
305
     * Sets the alignment for the columns.
306
     *
307
     * @param integer $col_id The column number.
308
     * @param integer $align  Alignment to set for this column. One of
309
     *                        CONSOLE_TABLE_ALIGN_LEFT
310
     *                        CONSOLE_TABLE_ALIGN_CENTER
311
     *                        CONSOLE_TABLE_ALIGN_RIGHT.
312
     *
313
     * @return void
314
     */
315
    function setAlign($col_id, $align = CONSOLE_TABLE_ALIGN_LEFT)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
316
    {
317
        switch ($align) {
318
            case CONSOLE_TABLE_ALIGN_CENTER:
319
                $pad = STR_PAD_BOTH;
320
                break;
321
            case CONSOLE_TABLE_ALIGN_RIGHT:
322
                $pad = STR_PAD_LEFT;
323
                break;
324
            default:
325
                $pad = STR_PAD_RIGHT;
326
                break;
327
        }
328
        $this->_col_align[$col_id] = $pad;
329
    }
330
    /**
331
     * Specifies which columns are to have totals calculated for them and
332
     * added as a new row at the bottom.
333
     *
334
     * @param array $cols Array of column numbers (starting with 0).
335
     *
336
     * @return void
337
     */
338
    function calculateTotalsFor($cols)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
339
    {
340
        $this->_calculateTotals = $cols;
341
    }
342
    /**
343
     * Sets the headers for the columns.
344
     *
345
     * @param array $headers The column headers.
346
     *
347
     * @return void
348
     */
349
    function setHeaders($headers)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
350
    {
351
        $this->_headers = array(array_values($headers));
352
        $this->_updateRowsCols($headers);
353
    }
354
    /**
355
     * Adds a row to the table.
356
     *
357
     * @param array   $row    The row data to add.
358
     * @param boolean $append Whether to append or prepend the row.
359
     *
360
     * @return void
361
     */
362
    function addRow($row, $append = true)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
363
    {
364
        if ($append) {
365
            $this->_data[] = array_values($row);
366
        } else {
367
            array_unshift($this->_data, array_values($row));
368
        }
369
        $this->_updateRowsCols($row);
370
    }
371
    /**
372
     * Inserts a row after a given row number in the table.
373
     *
374
     * If $row_id is not given it will prepend the row.
375
     *
376
     * @param array   $row    The data to insert.
377
     * @param integer $row_id Row number to insert before.
378
     *
379
     * @return void
380
     */
381
    function insertRow($row, $row_id = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
382
    {
383
        array_splice($this->_data, $row_id, 0, array($row));
384
        $this->_updateRowsCols($row);
385
    }
386
    /**
387
     * Adds a column to the table.
388
     *
389
     * @param array   $col_data The data of the column.
390
     * @param integer $col_id   The column index to populate.
391
     * @param integer $row_id   If starting row is not zero, specify it here.
392
     *
393
     * @return void
394
     */
395
    function addCol($col_data, $col_id = 0, $row_id = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
396
    {
397
        foreach ($col_data as $col_cell) {
398
            $this->_data[$row_id++][$col_id] = $col_cell;
399
        }
400
        $this->_updateRowsCols();
401
        $this->_max_cols = max($this->_max_cols, $col_id + 1);
402
    }
403
    /**
404
     * Adds data to the table.
405
     *
406
     * @param array   $data   A two dimensional array with the table data.
407
     * @param integer $col_id Starting column number.
408
     * @param integer $row_id Starting row number.
409
     *
410
     * @return void
411
     */
412
    function addData($data, $col_id = 0, $row_id = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
413
    {
414
        foreach ($data as $row) {
415
            if ($row === CONSOLE_TABLE_HORIZONTAL_RULE) {
416
                $this->_data[$row_id] = CONSOLE_TABLE_HORIZONTAL_RULE;
417
                $row_id++;
418
                continue;
419
            }
420
            $starting_col = $col_id;
421
            foreach ($row as $cell) {
422
                $this->_data[$row_id][$starting_col++] = $cell;
423
            }
424
            $this->_updateRowsCols();
425
            $this->_max_cols = max($this->_max_cols, $starting_col);
426
            $row_id++;
427
        }
428
    }
429
    /**
430
     * Adds a horizontal seperator to the table.
431
     *
432
     * @return void
433
     */
434
    function addSeparator()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
435
    {
436
        $this->_data[] = CONSOLE_TABLE_HORIZONTAL_RULE;
437
    }
438
    /**
439
     * Returns the generated table.
440
     *
441
     * @return string  The generated table.
442
     */
443
    function getTable()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
444
    {
445
        $this->_applyFilters();
446
        $this->_calculateTotals();
447
        $this->_validateTable();
448
        return $this->_buildTable();
449
    }
450
    /**
451
     * Calculates totals for columns.
452
     *
453
     * @return void
454
     */
455
    function _calculateTotals()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
456
    {
457
        if (empty($this->_calculateTotals)) {
458
            return;
459
        }
460
        $this->addSeparator();
461
        $totals = array();
462
        foreach ($this->_data as $row) {
463
            if (is_array($row)) {
464
                foreach ($this->_calculateTotals as $columnID) {
465
                    $totals[$columnID] += $row[$columnID];
466
                }
467
            }
468
        }
469
        $this->_data[] = $totals;
470
        $this->_updateRowsCols();
471
    }
472
    /**
473
     * Applies any column filters to the data.
474
     *
475
     * @return void
476
     */
477
    function _applyFilters()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
478
    {
479
        if (empty($this->_filters)) {
480
            return;
481
        }
482
        foreach ($this->_filters as $filter) {
483
            $column   = $filter[0];
484
            $callback = $filter[1];
485
            foreach ($this->_data as $row_id => $row_data) {
486
                if ($row_data !== CONSOLE_TABLE_HORIZONTAL_RULE) {
487
                    $this->_data[$row_id][$column] =
488
                        call_user_func($callback, $row_data[$column]);
489
                }
490
            }
491
        }
492
    }
493
    /**
494
     * Ensures that column and row counts are correct.
495
     *
496
     * @return void
497
     */
498
    function _validateTable()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
499
    {
500
        if (!empty($this->_headers)) {
501
            $this->_calculateRowHeight(-1, $this->_headers[0]);
502
        }
503
        for ($i = 0; $i < $this->_max_rows; $i++) {
504
            for ($j = 0; $j < $this->_max_cols; $j++) {
505
                if (!isset($this->_data[$i][$j]) &&
506
                    (!isset($this->_data[$i]) ||
507
                        $this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE)) {
508
                    $this->_data[$i][$j] = '';
509
                }
510
            }
511
            $this->_calculateRowHeight($i, $this->_data[$i]);
512
            if ($this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE) {
513
                ksort($this->_data[$i]);
514
            }
515
        }
516
        $this->_splitMultilineRows();
517
        // Update cell lengths.
518
        for ($i = 0; $i < count($this->_headers); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
519
            $this->_calculateCellLengths($this->_headers[$i]);
520
        }
521
        for ($i = 0; $i < $this->_max_rows; $i++) {
522
            $this->_calculateCellLengths($this->_data[$i]);
523
        }
524
        ksort($this->_data);
525
    }
526
    /**
527
     * Splits multiline rows into many smaller one-line rows.
528
     *
529
     * @return void
530
     */
531
    function _splitMultilineRows()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
532
    {
533
        ksort($this->_data);
534
        $sections          = array(&$this->_headers, &$this->_data);
535
        $max_rows          = array(count($this->_headers), $this->_max_rows);
536
        $row_height_offset = array(-1, 0);
537
        for ($s = 0; $s <= 1; $s++) {
538
            $inserted = 0;
539
            $new_data = $sections[$s];
540
            for ($i = 0; $i < $max_rows[$s]; $i++) {
541
                // Process only rows that have many lines.
542
                $height = $this->_row_heights[$i + $row_height_offset[$s]];
543
                if ($height > 1) {
544
                    // Split column data into one-liners.
545
                    $split = array();
546
                    for ($j = 0; $j < $this->_max_cols; $j++) {
547
                        $split[$j] = preg_split('/\r?\n|\r/',
548
                            $sections[$s][$i][$j]);
549
                    }
550
                    $new_rows = array();
551
                    // Construct new 'virtual' rows - insert empty strings for
552
                    // columns that have less lines that the highest one.
553
                    for ($i2 = 0; $i2 < $height; $i2++) {
554
                        for ($j = 0; $j < $this->_max_cols; $j++) {
555
                            $new_rows[$i2][$j] = !isset($split[$j][$i2])
556
                                ? ''
557
                                : $split[$j][$i2];
558
                        }
559
                    }
560
                    // Replace current row with smaller rows.  $inserted is
561
                    // used to take account of bigger array because of already
562
                    // inserted rows.
563
                    array_splice($new_data, $i + $inserted, 1, $new_rows);
564
                    $inserted += count($new_rows) - 1;
565
                }
566
            }
567
            // Has the data been modified?
568
            if ($inserted > 0) {
569
                $sections[$s] = $new_data;
570
                $this->_updateRowsCols();
571
            }
572
        }
573
    }
574
    /**
575
     * Builds the table.
576
     *
577
     * @return string  The generated table string.
578
     */
579
    function _buildTable()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
580
    {
581
        if (!count($this->_data)) {
582
            return '';
583
        }
584
        $vertical = $this->_border['vertical'];
585
        $separator = $this->_getSeparator();
586
        $return = array();
587
        for ($i = 0; $i < count($this->_data); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
588
            for ($j = 0; $j < count($this->_data[$i]); $j++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
589
                if ($this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE &&
590
                    $this->_strlen($this->_data[$i][$j]) <
591
                    $this->_cell_lengths[$j]) {
592
                    $this->_data[$i][$j] = $this->_strpad($this->_data[$i][$j],
593
                        $this->_cell_lengths[$j],
594
                        ' ',
595
                        $this->_col_align[$j]);
596
                }
597
            }
598
            if ($this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE) {
599
                $row_begin = $this->_borderVisibility['left']
600
                    ? $vertical . str_repeat(' ', $this->_padding)
601
                    : '';
602
                $row_end = $this->_borderVisibility['right']
603
                    ? str_repeat(' ', $this->_padding) . $vertical
604
                    : '';
605
                $implode_char = str_repeat(' ', $this->_padding) . $vertical
606
                    . str_repeat(' ', $this->_padding);
607
                $return[]     = $row_begin
608
                    . implode($implode_char, $this->_data[$i]) . $row_end;
609
            } elseif (!empty($separator)) {
610
                $return[] = $separator;
611
            }
612
        }
613
        $return = implode(PHP_EOL, $return);
614
        if (!empty($separator)) {
615
            if ($this->_borderVisibility['inner']) {
616
                $return = $separator . PHP_EOL . $return;
617
            }
618
            if ($this->_borderVisibility['bottom']) {
619
                $return .= PHP_EOL . $separator;
620
            }
621
        }
622
        $return .= PHP_EOL;
623
        if (!empty($this->_headers)) {
624
            $return = $this->_getHeaderLine() .  PHP_EOL . $return;
625
        }
626
        return $return;
627
    }
628
    /**
629
     * Creates a horizontal separator for header separation and table
630
     * start/end etc.
631
     *
632
     * @return string  The horizontal separator.
633
     */
634
    function _getSeparator()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
635
    {
636
        if (!$this->_border) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_border 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...
637
            return;
638
        }
639
        $horizontal = $this->_border['horizontal'];
640
        $intersection = $this->_border['intersection'];
641
        $return = array();
642
        foreach ($this->_cell_lengths as $cl) {
643
            $return[] = str_repeat($horizontal, $cl);
644
        }
645
        $row_begin = $this->_borderVisibility['left']
646
            ? $intersection . str_repeat($horizontal, $this->_padding)
647
            : '';
648
        $row_end = $this->_borderVisibility['right']
649
            ? str_repeat($horizontal, $this->_padding) . $intersection
650
            : '';
651
        $implode_char = str_repeat($horizontal, $this->_padding) . $intersection
652
            . str_repeat($horizontal, $this->_padding);
653
        return $row_begin . implode($implode_char, $return) . $row_end;
654
    }
655
    /**
656
     * Returns the header line for the table.
657
     *
658
     * @return string  The header line of the table.
659
     */
660
    function _getHeaderLine()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
661
    {
662
        // Make sure column count is correct
663
        for ($j = 0; $j < count($this->_headers); $j++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
664
            for ($i = 0; $i < $this->_max_cols; $i++) {
665
                if (!isset($this->_headers[$j][$i])) {
666
                    $this->_headers[$j][$i] = '';
667
                }
668
            }
669
        }
670
        for ($j = 0; $j < count($this->_headers); $j++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
671
            for ($i = 0; $i < count($this->_headers[$j]); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
672
                if ($this->_strlen($this->_headers[$j][$i]) <
673
                    $this->_cell_lengths[$i]) {
674
                    $this->_headers[$j][$i] =
675
                        $this->_strpad($this->_headers[$j][$i],
676
                            $this->_cell_lengths[$i],
677
                            ' ',
678
                            $this->_col_align[$i]);
679
                }
680
            }
681
        }
682
        $vertical = $this->_border['vertical'];
683
        $row_begin = $this->_borderVisibility['left']
684
            ? $vertical . str_repeat(' ', $this->_padding)
685
            : '';
686
        $row_end = $this->_borderVisibility['right']
687
            ? str_repeat(' ', $this->_padding) . $vertical
688
            : '';
689
        $implode_char = str_repeat(' ', $this->_padding) . $vertical
690
            . str_repeat(' ', $this->_padding);
691
        $separator = $this->_getSeparator();
692
        if (!empty($separator) && $this->_borderVisibility['top']) {
693
            $return[] = $separator;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.
Loading history...
694
        }
695
        for ($j = 0; $j < count($this->_headers); $j++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
696
            $return[] = $row_begin
697
                . implode($implode_char, $this->_headers[$j]) . $row_end;
698
        }
699
        return implode(PHP_EOL, $return);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $return does not seem to be defined for all execution paths leading up to this point.
Loading history...
700
    }
701
    /**
702
     * Updates values for maximum columns and rows.
703
     *
704
     * @param array $rowdata Data array of a single row.
705
     *
706
     * @return void
707
     */
708
    function _updateRowsCols($rowdata = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
709
    {
710
        // Update maximum columns.
711
        $this->_max_cols = max($this->_max_cols, count($rowdata));
0 ignored issues
show
Bug introduced by
It seems like $rowdata can also be of type null; however, parameter $value of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

711
        $this->_max_cols = max($this->_max_cols, count(/** @scrutinizer ignore-type */ $rowdata));
Loading history...
712
        // Update maximum rows.
713
        ksort($this->_data);
714
        $keys            = array_keys($this->_data);
715
        $this->_max_rows = end($keys) + 1;
716
        switch ($this->_defaultAlign) {
717
            case CONSOLE_TABLE_ALIGN_CENTER:
718
                $pad = STR_PAD_BOTH;
719
                break;
720
            case CONSOLE_TABLE_ALIGN_RIGHT:
721
                $pad = STR_PAD_LEFT;
722
                break;
723
            default:
724
                $pad = STR_PAD_RIGHT;
725
                break;
726
        }
727
        // Set default column alignments
728
        for ($i = 0; $i < $this->_max_cols; $i++) {
729
            if (!isset($this->_col_align[$i])) {
730
                $this->_col_align[$i] = $pad;
731
            }
732
        }
733
    }
734
    /**
735
     * Calculates the maximum length for each column of a row.
736
     *
737
     * @param array $row The row data.
738
     *
739
     * @return void
740
     */
741
    function _calculateCellLengths($row)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
742
    {
743
        for ($i = 0; $i < count($row); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
744
            if (!isset($this->_cell_lengths[$i])) {
745
                $this->_cell_lengths[$i] = 0;
746
            }
747
            $this->_cell_lengths[$i] = max($this->_cell_lengths[$i],
748
                $this->_strlen($row[$i]));
749
        }
750
    }
751
    /**
752
     * Calculates the maximum height for all columns of a row.
753
     *
754
     * @param integer $row_number The row number.
755
     * @param array   $row        The row data.
756
     *
757
     * @return void
758
     */
759
    function _calculateRowHeight($row_number, $row)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
760
    {
761
        if (!isset($this->_row_heights[$row_number])) {
762
            $this->_row_heights[$row_number] = 1;
763
        }
764
        // Do not process horizontal rule rows.
765
        if ($row === CONSOLE_TABLE_HORIZONTAL_RULE) {
0 ignored issues
show
introduced by
The condition $row === CONSOLE_TABLE_HORIZONTAL_RULE is always false.
Loading history...
766
            return;
767
        }
768
        for ($i = 0, $c = count($row); $i < $c; ++$i) {
769
            $lines                           = preg_split('/\r?\n|\r/', $row[$i]);
770
            $this->_row_heights[$row_number] = max($this->_row_heights[$row_number],
771
                count($lines));
772
        }
773
    }
774
    /**
775
     * Returns the character length of a string.
776
     *
777
     * @param string $str A multibyte or singlebyte string.
778
     *
779
     * @return integer  The string length.
780
     */
781
    function _strlen($str)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
782
    {
783
        static $mbstring;
784
        // Strip ANSI color codes if requested.
785
        if ($this->_ansiColor) {
786
            $str = $this->_ansiColor->strip($str);
787
        }
788
        // Cache expensive function_exists() calls.
789
        if (!isset($mbstring)) {
790
            $mbstring = function_exists('mb_strwidth');
791
        }
792
        if ($mbstring) {
793
            return mb_strwidth($str, $this->_charset);
794
        }
795
        return strlen($str);
796
    }
797
    /**
798
     * Returns part of a string.
799
     *
800
     * @param string  $string The string to be converted.
801
     * @param integer $start  The part's start position, zero based.
802
     * @param integer $length The part's length.
803
     *
804
     * @return string  The string's part.
805
     */
806
    function _substr($string, $start, $length = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
807
    {
808
        static $mbstring;
809
        // Cache expensive function_exists() calls.
810
        if (!isset($mbstring)) {
811
            $mbstring = function_exists('mb_substr');
812
        }
813
        if (is_null($length)) {
814
            $length = $this->_strlen($string);
815
        }
816
        if ($mbstring) {
817
            $ret = @mb_substr($string, $start, $length, $this->_charset);
818
            if (!empty($ret)) {
819
                return $ret;
820
            }
821
        }
822
        return substr($string, $start, $length);
823
    }
824
    /**
825
     * Returns a string padded to a certain length with another string.
826
     *
827
     * This method behaves exactly like str_pad but is multibyte safe.
828
     *
829
     * @param string  $input  The string to be padded.
830
     * @param integer $length The length of the resulting string.
831
     * @param string  $pad    The string to pad the input string with. Must
832
     *                        be in the same charset like the input string.
833
     * @param const   $type   The padding type. One of STR_PAD_LEFT,
0 ignored issues
show
Bug introduced by
The type const was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
834
     *                        STR_PAD_RIGHT, or STR_PAD_BOTH.
835
     *
836
     * @return string  The padded string.
837
     */
838
    function _strpad($input, $length, $pad = ' ', $type = STR_PAD_RIGHT)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
839
    {
840
        $mb_length  = $this->_strlen($input);
841
        $sb_length  = strlen($input);
842
        $pad_length = $this->_strlen($pad);
843
        /* Return if we already have the length. */
844
        if ($mb_length >= $length) {
845
            return $input;
846
        }
847
        /* Shortcut for single byte strings. */
848
        if ($mb_length == $sb_length && $pad_length == strlen($pad)) {
849
            return str_pad($input, $length, $pad, $type);
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type const; however, parameter $pad_type of str_pad() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

849
            return str_pad($input, $length, $pad, /** @scrutinizer ignore-type */ $type);
Loading history...
850
        }
851
        switch ($type) {
852
            case STR_PAD_LEFT:
853
                $left   = $length - $mb_length;
854
                $output = $this->_substr(str_repeat($pad, ceil($left / $pad_length)),
0 ignored issues
show
Bug introduced by
ceil($left / $pad_length) of type double is incompatible with the type integer expected by parameter $times of str_repeat(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

854
                $output = $this->_substr(str_repeat($pad, /** @scrutinizer ignore-type */ ceil($left / $pad_length)),
Loading history...
855
                        0, $left, $this->_charset) . $input;
0 ignored issues
show
Unused Code introduced by
The call to console_table::_substr() has too many arguments starting with $this->_charset. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

855
                $output = $this->/** @scrutinizer ignore-call */ _substr(str_repeat($pad, ceil($left / $pad_length)),

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
856
                break;
857
            case STR_PAD_BOTH:
858
                $left   = floor(($length - $mb_length) / 2);
859
                $right  = ceil(($length - $mb_length) / 2);
860
                $output = $this->_substr(str_repeat($pad, ceil($left / $pad_length)),
861
                        0, $left, $this->_charset) .
0 ignored issues
show
Bug introduced by
$left of type double is incompatible with the type integer expected by parameter $length of console_table::_substr(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

861
                        0, /** @scrutinizer ignore-type */ $left, $this->_charset) .
Loading history...
862
                    $input .
863
                    $this->_substr(str_repeat($pad, ceil($right / $pad_length)),
864
                        0, $right, $this->_charset);
865
                break;
866
            case STR_PAD_RIGHT:
867
                $right  = $length - $mb_length;
868
                $output = $input .
869
                    $this->_substr(str_repeat($pad, ceil($right / $pad_length)),
870
                        0, $right, $this->_charset);
871
                break;
872
        }
873
        return $output;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $output does not seem to be defined for all execution paths leading up to this point.
Loading history...
874
    }
875
}