Mercurial > hg > isophonics-drupal-site
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 } |