comparison vendor/pear/console_table/Table.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 7a779792577d
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
1 <?php
2 /**
3 * Utility for printing tables from commandline scripts.
4 *
5 * PHP versions 5 and 7
6 *
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * o Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * o Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * o The names of the authors may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 * @category Console
33 * @package Console_Table
34 * @author Richard Heyes <richard@phpguru.org>
35 * @author Jan Schneider <jan@horde.org>
36 * @copyright 2002-2005 Richard Heyes
37 * @copyright 2006-2008 Jan Schneider
38 * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
39 * @version CVS: $Id$
40 * @link http://pear.php.net/package/Console_Table
41 */
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 /**
50 * The main class.
51 *
52 * @category Console
53 * @package Console_Table
54 * @author Jan Schneider <jan@horde.org>
55 * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
56 * @link http://pear.php.net/package/Console_Table
57 */
58 class Console_Table
59 {
60 /**
61 * The table headers.
62 *
63 * @var array
64 */
65 var $_headers = array();
66
67 /**
68 * The data of the table.
69 *
70 * @var array
71 */
72 var $_data = array();
73
74 /**
75 * The maximum number of columns in a row.
76 *
77 * @var integer
78 */
79 var $_max_cols = 0;
80
81 /**
82 * The maximum number of rows in the table.
83 *
84 * @var integer
85 */
86 var $_max_rows = 0;
87
88 /**
89 * Lengths of the columns, calculated when rows are added to the table.
90 *
91 * @var array
92 */
93 var $_cell_lengths = array();
94
95 /**
96 * Heights of the rows.
97 *
98 * @var array
99 */
100 var $_row_heights = array();
101
102 /**
103 * How many spaces to use to pad the table.
104 *
105 * @var integer
106 */
107 var $_padding = 1;
108
109 /**
110 * Column filters.
111 *
112 * @var array
113 */
114 var $_filters = array();
115
116 /**
117 * Columns to calculate totals for.
118 *
119 * @var array
120 */
121 var $_calculateTotals;
122
123 /**
124 * Alignment of the columns.
125 *
126 * @var array
127 */
128 var $_col_align = array();
129
130 /**
131 * Default alignment of columns.
132 *
133 * @var integer
134 */
135 var $_defaultAlign;
136
137 /**
138 * Character set of the data.
139 *
140 * @var string
141 */
142 var $_charset = 'utf-8';
143
144 /**
145 * Border characters.
146 * Allowed keys:
147 * - intersection - intersection ("+")
148 * - horizontal - horizontal rule character ("-")
149 * - vertical - vertical rule character ("|")
150 *
151 * @var array
152 */
153 var $_border = array(
154 'intersection' => '+',
155 'horizontal' => '-',
156 'vertical' => '|',
157 );
158
159 /**
160 * If borders are shown or not
161 * Allowed keys: top, right, bottom, left, inner: true and false
162 *
163 * @var array
164 */
165 var $_borderVisibility = array(
166 'top' => true,
167 'right' => true,
168 'bottom' => true,
169 'left' => true,
170 'inner' => true
171 );
172
173 /**
174 * Whether the data has ANSI colors.
175 *
176 * @var Console_Color2
177 */
178 var $_ansiColor = false;
179
180 /**
181 * Constructor.
182 *
183 * @param integer $align Default alignment. One of
184 * CONSOLE_TABLE_ALIGN_LEFT,
185 * CONSOLE_TABLE_ALIGN_CENTER or
186 * CONSOLE_TABLE_ALIGN_RIGHT.
187 * @param string $border The character used for table borders or
188 * CONSOLE_TABLE_BORDER_ASCII.
189 * @param integer $padding How many spaces to use to pad the table.
190 * @param string $charset A charset supported by the mbstring PHP
191 * extension.
192 * @param boolean $color Whether the data contains ansi color codes.
193 */
194 function __construct($align = CONSOLE_TABLE_ALIGN_LEFT,
195 $border = CONSOLE_TABLE_BORDER_ASCII, $padding = 1,
196 $charset = null, $color = false)
197 {
198 $this->_defaultAlign = $align;
199 $this->setBorder($border);
200 $this->_padding = $padding;
201 if ($color) {
202 if (!class_exists('Console_Color2')) {
203 include_once 'Console/Color2.php';
204 }
205 $this->_ansiColor = new Console_Color2();
206 }
207 if (!empty($charset)) {
208 $this->setCharset($charset);
209 }
210 }
211
212 /**
213 * Converts an array to a table.
214 *
215 * @param array $headers Headers for the table.
216 * @param array $data A two dimensional array with the table
217 * data.
218 * @param boolean $returnObject Whether to return the Console_Table object
219 * instead of the rendered table.
220 *
221 * @static
222 *
223 * @return Console_Table|string A Console_Table object or the generated
224 * table.
225 */
226 function fromArray($headers, $data, $returnObject = false)
227 {
228 if (!is_array($headers) || !is_array($data)) {
229 return false;
230 }
231
232 $table = new Console_Table();
233 $table->setHeaders($headers);
234
235 foreach ($data as $row) {
236 $table->addRow($row);
237 }
238
239 return $returnObject ? $table : $table->getTable();
240 }
241
242 /**
243 * Adds a filter to a column.
244 *
245 * Filters are standard PHP callbacks which are run on the data before
246 * table generation is performed. Filters are applied in the order they
247 * are added. The callback function must accept a single argument, which
248 * is a single table cell.
249 *
250 * @param integer $col Column to apply filter to.
251 * @param mixed &$callback PHP callback to apply.
252 *
253 * @return void
254 */
255 function addFilter($col, &$callback)
256 {
257 $this->_filters[] = array($col, &$callback);
258 }
259
260 /**
261 * Sets the charset of the provided table data.
262 *
263 * @param string $charset A charset supported by the mbstring PHP
264 * extension.
265 *
266 * @return void
267 */
268 function setCharset($charset)
269 {
270 $locale = setlocale(LC_CTYPE, 0);
271 setlocale(LC_CTYPE, 'en_US');
272 $this->_charset = strtolower($charset);
273 setlocale(LC_CTYPE, $locale);
274 }
275
276 /**
277 * Set the table border settings
278 *
279 * Border definition modes:
280 * - CONSOLE_TABLE_BORDER_ASCII: Default border with +, - and |
281 * - array with keys "intersection", "horizontal" and "vertical"
282 * - single character string that sets all three of the array keys
283 *
284 * @param mixed $border Border definition
285 *
286 * @return void
287 * @see $_border
288 */
289 function setBorder($border)
290 {
291 if ($border === CONSOLE_TABLE_BORDER_ASCII) {
292 $intersection = '+';
293 $horizontal = '-';
294 $vertical = '|';
295 } else if (is_string($border)) {
296 $intersection = $horizontal = $vertical = $border;
297 } else if ($border == '') {
298 $intersection = $horizontal = $vertical = '';
299 } else {
300 extract($border);
301 }
302
303 $this->_border = array(
304 'intersection' => $intersection,
305 'horizontal' => $horizontal,
306 'vertical' => $vertical,
307 );
308 }
309
310 /**
311 * Set which borders shall be shown.
312 *
313 * @param array $visibility Visibility settings.
314 * Allowed keys: left, right, top, bottom, inner
315 *
316 * @return void
317 * @see $_borderVisibility
318 */
319 function setBorderVisibility($visibility)
320 {
321 $this->_borderVisibility = array_merge(
322 $this->_borderVisibility,
323 array_intersect_key(
324 $visibility,
325 $this->_borderVisibility
326 )
327 );
328 }
329
330 /**
331 * Sets the alignment for the columns.
332 *
333 * @param integer $col_id The column number.
334 * @param integer $align Alignment to set for this column. One of
335 * CONSOLE_TABLE_ALIGN_LEFT
336 * CONSOLE_TABLE_ALIGN_CENTER
337 * CONSOLE_TABLE_ALIGN_RIGHT.
338 *
339 * @return void
340 */
341 function setAlign($col_id, $align = CONSOLE_TABLE_ALIGN_LEFT)
342 {
343 switch ($align) {
344 case CONSOLE_TABLE_ALIGN_CENTER:
345 $pad = STR_PAD_BOTH;
346 break;
347 case CONSOLE_TABLE_ALIGN_RIGHT:
348 $pad = STR_PAD_LEFT;
349 break;
350 default:
351 $pad = STR_PAD_RIGHT;
352 break;
353 }
354 $this->_col_align[$col_id] = $pad;
355 }
356
357 /**
358 * Specifies which columns are to have totals calculated for them and
359 * added as a new row at the bottom.
360 *
361 * @param array $cols Array of column numbers (starting with 0).
362 *
363 * @return void
364 */
365 function calculateTotalsFor($cols)
366 {
367 $this->_calculateTotals = $cols;
368 }
369
370 /**
371 * Sets the headers for the columns.
372 *
373 * @param array $headers The column headers.
374 *
375 * @return void
376 */
377 function setHeaders($headers)
378 {
379 $this->_headers = array(array_values($headers));
380 $this->_updateRowsCols($headers);
381 }
382
383 /**
384 * Adds a row to the table.
385 *
386 * @param array $row The row data to add.
387 * @param boolean $append Whether to append or prepend the row.
388 *
389 * @return void
390 */
391 function addRow($row, $append = true)
392 {
393 if ($append) {
394 $this->_data[] = array_values($row);
395 } else {
396 array_unshift($this->_data, array_values($row));
397 }
398
399 $this->_updateRowsCols($row);
400 }
401
402 /**
403 * Inserts a row after a given row number in the table.
404 *
405 * If $row_id is not given it will prepend the row.
406 *
407 * @param array $row The data to insert.
408 * @param integer $row_id Row number to insert before.
409 *
410 * @return void
411 */
412 function insertRow($row, $row_id = 0)
413 {
414 array_splice($this->_data, $row_id, 0, array($row));
415
416 $this->_updateRowsCols($row);
417 }
418
419 /**
420 * Adds a column to the table.
421 *
422 * @param array $col_data The data of the column.
423 * @param integer $col_id The column index to populate.
424 * @param integer $row_id If starting row is not zero, specify it here.
425 *
426 * @return void
427 */
428 function addCol($col_data, $col_id = 0, $row_id = 0)
429 {
430 foreach ($col_data as $col_cell) {
431 $this->_data[$row_id++][$col_id] = $col_cell;
432 }
433
434 $this->_updateRowsCols();
435 $this->_max_cols = max($this->_max_cols, $col_id + 1);
436 }
437
438 /**
439 * Adds data to the table.
440 *
441 * @param array $data A two dimensional array with the table data.
442 * @param integer $col_id Starting column number.
443 * @param integer $row_id Starting row number.
444 *
445 * @return void
446 */
447 function addData($data, $col_id = 0, $row_id = 0)
448 {
449 foreach ($data as $row) {
450 if ($row === CONSOLE_TABLE_HORIZONTAL_RULE) {
451 $this->_data[$row_id] = CONSOLE_TABLE_HORIZONTAL_RULE;
452 $row_id++;
453 continue;
454 }
455 $starting_col = $col_id;
456 foreach ($row as $cell) {
457 $this->_data[$row_id][$starting_col++] = $cell;
458 }
459 $this->_updateRowsCols();
460 $this->_max_cols = max($this->_max_cols, $starting_col);
461 $row_id++;
462 }
463 }
464
465 /**
466 * Adds a horizontal seperator to the table.
467 *
468 * @return void
469 */
470 function addSeparator()
471 {
472 $this->_data[] = CONSOLE_TABLE_HORIZONTAL_RULE;
473 }
474
475 /**
476 * Returns the generated table.
477 *
478 * @return string The generated table.
479 */
480 function getTable()
481 {
482 $this->_applyFilters();
483 $this->_calculateTotals();
484 $this->_validateTable();
485
486 return $this->_buildTable();
487 }
488
489 /**
490 * Calculates totals for columns.
491 *
492 * @return void
493 */
494 function _calculateTotals()
495 {
496 if (empty($this->_calculateTotals)) {
497 return;
498 }
499
500 $this->addSeparator();
501
502 $totals = array();
503 foreach ($this->_data as $row) {
504 if (is_array($row)) {
505 foreach ($this->_calculateTotals as $columnID) {
506 $totals[$columnID] += $row[$columnID];
507 }
508 }
509 }
510
511 $this->_data[] = $totals;
512 $this->_updateRowsCols();
513 }
514
515 /**
516 * Applies any column filters to the data.
517 *
518 * @return void
519 */
520 function _applyFilters()
521 {
522 if (empty($this->_filters)) {
523 return;
524 }
525
526 foreach ($this->_filters as $filter) {
527 $column = $filter[0];
528 $callback = $filter[1];
529
530 foreach ($this->_data as $row_id => $row_data) {
531 if ($row_data !== CONSOLE_TABLE_HORIZONTAL_RULE) {
532 $this->_data[$row_id][$column] =
533 call_user_func($callback, $row_data[$column]);
534 }
535 }
536 }
537 }
538
539 /**
540 * Ensures that column and row counts are correct.
541 *
542 * @return void
543 */
544 function _validateTable()
545 {
546 if (!empty($this->_headers)) {
547 $this->_calculateRowHeight(-1, $this->_headers[0]);
548 }
549
550 for ($i = 0; $i < $this->_max_rows; $i++) {
551 for ($j = 0; $j < $this->_max_cols; $j++) {
552 if (!isset($this->_data[$i][$j]) &&
553 (!isset($this->_data[$i]) ||
554 $this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE)) {
555 $this->_data[$i][$j] = '';
556 }
557
558 }
559 $this->_calculateRowHeight($i, $this->_data[$i]);
560
561 if ($this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE) {
562 ksort($this->_data[$i]);
563 }
564
565 }
566
567 $this->_splitMultilineRows();
568
569 // Update cell lengths.
570 for ($i = 0; $i < count($this->_headers); $i++) {
571 $this->_calculateCellLengths($this->_headers[$i]);
572 }
573 for ($i = 0; $i < $this->_max_rows; $i++) {
574 $this->_calculateCellLengths($this->_data[$i]);
575 }
576
577 ksort($this->_data);
578 }
579
580 /**
581 * Splits multiline rows into many smaller one-line rows.
582 *
583 * @return void
584 */
585 function _splitMultilineRows()
586 {
587 ksort($this->_data);
588 $sections = array(&$this->_headers, &$this->_data);
589 $max_rows = array(count($this->_headers), $this->_max_rows);
590 $row_height_offset = array(-1, 0);
591
592 for ($s = 0; $s <= 1; $s++) {
593 $inserted = 0;
594 $new_data = $sections[$s];
595
596 for ($i = 0; $i < $max_rows[$s]; $i++) {
597 // Process only rows that have many lines.
598 $height = $this->_row_heights[$i + $row_height_offset[$s]];
599 if ($height > 1) {
600 // Split column data into one-liners.
601 $split = array();
602 for ($j = 0; $j < $this->_max_cols; $j++) {
603 $split[$j] = preg_split('/\r?\n|\r/',
604 $sections[$s][$i][$j]);
605 }
606
607 $new_rows = array();
608 // Construct new 'virtual' rows - insert empty strings for
609 // columns that have less lines that the highest one.
610 for ($i2 = 0; $i2 < $height; $i2++) {
611 for ($j = 0; $j < $this->_max_cols; $j++) {
612 $new_rows[$i2][$j] = !isset($split[$j][$i2])
613 ? ''
614 : $split[$j][$i2];
615 }
616 }
617
618 // Replace current row with smaller rows. $inserted is
619 // used to take account of bigger array because of already
620 // inserted rows.
621 array_splice($new_data, $i + $inserted, 1, $new_rows);
622 $inserted += count($new_rows) - 1;
623 }
624 }
625
626 // Has the data been modified?
627 if ($inserted > 0) {
628 $sections[$s] = $new_data;
629 $this->_updateRowsCols();
630 }
631 }
632 }
633
634 /**
635 * Builds the table.
636 *
637 * @return string The generated table string.
638 */
639 function _buildTable()
640 {
641 if (!count($this->_data)) {
642 return '';
643 }
644
645 $vertical = $this->_border['vertical'];
646 $separator = $this->_getSeparator();
647
648 $return = array();
649 for ($i = 0; $i < count($this->_data); $i++) {
650 for ($j = 0; $j < count($this->_data[$i]); $j++) {
651 if ($this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE &&
652 $this->_strlen($this->_data[$i][$j]) <
653 $this->_cell_lengths[$j]) {
654 $this->_data[$i][$j] = $this->_strpad($this->_data[$i][$j],
655 $this->_cell_lengths[$j],
656 ' ',
657 $this->_col_align[$j]);
658 }
659 }
660
661 if ($this->_data[$i] !== CONSOLE_TABLE_HORIZONTAL_RULE) {
662 $row_begin = $this->_borderVisibility['left']
663 ? $vertical . str_repeat(' ', $this->_padding)
664 : '';
665 $row_end = $this->_borderVisibility['right']
666 ? str_repeat(' ', $this->_padding) . $vertical
667 : '';
668 $implode_char = str_repeat(' ', $this->_padding) . $vertical
669 . str_repeat(' ', $this->_padding);
670 $return[] = $row_begin
671 . implode($implode_char, $this->_data[$i]) . $row_end;
672 } elseif (!empty($separator)) {
673 $return[] = $separator;
674 }
675
676 }
677
678 $return = implode(PHP_EOL, $return);
679 if (!empty($separator)) {
680 if ($this->_borderVisibility['inner']) {
681 $return = $separator . PHP_EOL . $return;
682 }
683 if ($this->_borderVisibility['bottom']) {
684 $return .= PHP_EOL . $separator;
685 }
686 }
687 $return .= PHP_EOL;
688
689 if (!empty($this->_headers)) {
690 $return = $this->_getHeaderLine() . PHP_EOL . $return;
691 }
692
693 return $return;
694 }
695
696 /**
697 * Creates a horizontal separator for header separation and table
698 * start/end etc.
699 *
700 * @return string The horizontal separator.
701 */
702 function _getSeparator()
703 {
704 if (!$this->_border) {
705 return;
706 }
707
708 $horizontal = $this->_border['horizontal'];
709 $intersection = $this->_border['intersection'];
710
711 $return = array();
712 foreach ($this->_cell_lengths as $cl) {
713 $return[] = str_repeat($horizontal, $cl);
714 }
715
716 $row_begin = $this->_borderVisibility['left']
717 ? $intersection . str_repeat($horizontal, $this->_padding)
718 : '';
719 $row_end = $this->_borderVisibility['right']
720 ? str_repeat($horizontal, $this->_padding) . $intersection
721 : '';
722 $implode_char = str_repeat($horizontal, $this->_padding) . $intersection
723 . str_repeat($horizontal, $this->_padding);
724
725 return $row_begin . implode($implode_char, $return) . $row_end;
726 }
727
728 /**
729 * Returns the header line for the table.
730 *
731 * @return string The header line of the table.
732 */
733 function _getHeaderLine()
734 {
735 // Make sure column count is correct
736 for ($j = 0; $j < count($this->_headers); $j++) {
737 for ($i = 0; $i < $this->_max_cols; $i++) {
738 if (!isset($this->_headers[$j][$i])) {
739 $this->_headers[$j][$i] = '';
740 }
741 }
742 }
743
744 for ($j = 0; $j < count($this->_headers); $j++) {
745 for ($i = 0; $i < count($this->_headers[$j]); $i++) {
746 if ($this->_strlen($this->_headers[$j][$i]) <
747 $this->_cell_lengths[$i]) {
748 $this->_headers[$j][$i] =
749 $this->_strpad($this->_headers[$j][$i],
750 $this->_cell_lengths[$i],
751 ' ',
752 $this->_col_align[$i]);
753 }
754 }
755 }
756
757 $vertical = $this->_border['vertical'];
758 $row_begin = $this->_borderVisibility['left']
759 ? $vertical . str_repeat(' ', $this->_padding)
760 : '';
761 $row_end = $this->_borderVisibility['right']
762 ? str_repeat(' ', $this->_padding) . $vertical
763 : '';
764 $implode_char = str_repeat(' ', $this->_padding) . $vertical
765 . str_repeat(' ', $this->_padding);
766
767 $separator = $this->_getSeparator();
768 if (!empty($separator) && $this->_borderVisibility['top']) {
769 $return[] = $separator;
770 }
771 for ($j = 0; $j < count($this->_headers); $j++) {
772 $return[] = $row_begin
773 . implode($implode_char, $this->_headers[$j]) . $row_end;
774 }
775
776 return implode(PHP_EOL, $return);
777 }
778
779 /**
780 * Updates values for maximum columns and rows.
781 *
782 * @param array $rowdata Data array of a single row.
783 *
784 * @return void
785 */
786 function _updateRowsCols($rowdata = null)
787 {
788 // Update maximum columns.
789 $this->_max_cols = max($this->_max_cols, count($rowdata));
790
791 // Update maximum rows.
792 ksort($this->_data);
793 $keys = array_keys($this->_data);
794 $this->_max_rows = end($keys) + 1;
795
796 switch ($this->_defaultAlign) {
797 case CONSOLE_TABLE_ALIGN_CENTER:
798 $pad = STR_PAD_BOTH;
799 break;
800 case CONSOLE_TABLE_ALIGN_RIGHT:
801 $pad = STR_PAD_LEFT;
802 break;
803 default:
804 $pad = STR_PAD_RIGHT;
805 break;
806 }
807
808 // Set default column alignments
809 for ($i = 0; $i < $this->_max_cols; $i++) {
810 if (!isset($this->_col_align[$i])) {
811 $this->_col_align[$i] = $pad;
812 }
813 }
814 }
815
816 /**
817 * Calculates the maximum length for each column of a row.
818 *
819 * @param array $row The row data.
820 *
821 * @return void
822 */
823 function _calculateCellLengths($row)
824 {
825 for ($i = 0; $i < count($row); $i++) {
826 if (!isset($this->_cell_lengths[$i])) {
827 $this->_cell_lengths[$i] = 0;
828 }
829 $this->_cell_lengths[$i] = max($this->_cell_lengths[$i],
830 $this->_strlen($row[$i]));
831 }
832 }
833
834 /**
835 * Calculates the maximum height for all columns of a row.
836 *
837 * @param integer $row_number The row number.
838 * @param array $row The row data.
839 *
840 * @return void
841 */
842 function _calculateRowHeight($row_number, $row)
843 {
844 if (!isset($this->_row_heights[$row_number])) {
845 $this->_row_heights[$row_number] = 1;
846 }
847
848 // Do not process horizontal rule rows.
849 if ($row === CONSOLE_TABLE_HORIZONTAL_RULE) {
850 return;
851 }
852
853 for ($i = 0, $c = count($row); $i < $c; ++$i) {
854 $lines = preg_split('/\r?\n|\r/', $row[$i]);
855 $this->_row_heights[$row_number] = max($this->_row_heights[$row_number],
856 count($lines));
857 }
858 }
859
860 /**
861 * Returns the character length of a string.
862 *
863 * @param string $str A multibyte or singlebyte string.
864 *
865 * @return integer The string length.
866 */
867 function _strlen($str)
868 {
869 static $mbstring;
870
871 // Strip ANSI color codes if requested.
872 if ($this->_ansiColor) {
873 $str = $this->_ansiColor->strip($str);
874 }
875
876 // Cache expensive function_exists() calls.
877 if (!isset($mbstring)) {
878 $mbstring = function_exists('mb_strwidth');
879 }
880
881 if ($mbstring) {
882 return mb_strwidth($str, $this->_charset);
883 }
884
885 return strlen($str);
886 }
887
888 /**
889 * Returns part of a string.
890 *
891 * @param string $string The string to be converted.
892 * @param integer $start The part's start position, zero based.
893 * @param integer $length The part's length.
894 *
895 * @return string The string's part.
896 */
897 function _substr($string, $start, $length = null)
898 {
899 static $mbstring;
900
901 // Cache expensive function_exists() calls.
902 if (!isset($mbstring)) {
903 $mbstring = function_exists('mb_substr');
904 }
905
906 if (is_null($length)) {
907 $length = $this->_strlen($string);
908 }
909 if ($mbstring) {
910 $ret = @mb_substr($string, $start, $length, $this->_charset);
911 if (!empty($ret)) {
912 return $ret;
913 }
914 }
915 return substr($string, $start, $length);
916 }
917
918 /**
919 * Returns a string padded to a certain length with another string.
920 *
921 * This method behaves exactly like str_pad but is multibyte safe.
922 *
923 * @param string $input The string to be padded.
924 * @param integer $length The length of the resulting string.
925 * @param string $pad The string to pad the input string with. Must
926 * be in the same charset like the input string.
927 * @param const $type The padding type. One of STR_PAD_LEFT,
928 * STR_PAD_RIGHT, or STR_PAD_BOTH.
929 *
930 * @return string The padded string.
931 */
932 function _strpad($input, $length, $pad = ' ', $type = STR_PAD_RIGHT)
933 {
934 $mb_length = $this->_strlen($input);
935 $sb_length = strlen($input);
936 $pad_length = $this->_strlen($pad);
937
938 /* Return if we already have the length. */
939 if ($mb_length >= $length) {
940 return $input;
941 }
942
943 /* Shortcut for single byte strings. */
944 if ($mb_length == $sb_length && $pad_length == strlen($pad)) {
945 return str_pad($input, $length, $pad, $type);
946 }
947
948 switch ($type) {
949 case STR_PAD_LEFT:
950 $left = $length - $mb_length;
951 $output = $this->_substr(str_repeat($pad, ceil($left / $pad_length)),
952 0, $left, $this->_charset) . $input;
953 break;
954 case STR_PAD_BOTH:
955 $left = floor(($length - $mb_length) / 2);
956 $right = ceil(($length - $mb_length) / 2);
957 $output = $this->_substr(str_repeat($pad, ceil($left / $pad_length)),
958 0, $left, $this->_charset) .
959 $input .
960 $this->_substr(str_repeat($pad, ceil($right / $pad_length)),
961 0, $right, $this->_charset);
962 break;
963 case STR_PAD_RIGHT:
964 $right = $length - $mb_length;
965 $output = $input .
966 $this->_substr(str_repeat($pad, ceil($right / $pad_length)),
967 0, $right, $this->_charset);
968 break;
969 }
970
971 return $output;
972 }
973
974 }