1<?php 2/** 3 * File containing the ezcConsoleTable class. 4 * 5 * @package ConsoleTools 6 * @version 1.6.1 7 * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved. 8 * @license http://ez.no/licenses/new_bsd New BSD License 9 * @filesource 10 */ 11 12/** 13 * Creating tables to be printed to the console. 14 * 15 * Every ezcConsoleTable object can be accessed as if it was a multidimensional, 16 * numerically indexed array. The first dimension represents the rows of the 17 * table, so $table[0] gives you access to the first row of the table, which is 18 * represented by a {@link ezcConsoleTableRow} object. You can access its 19 * properties directly, using e.g. $table[0]->format. The second dimension gives 20 * you direct access to the cells of your table, like $table[0][0] accesses the 21 * first cell in the first row of your table. You can access its properties 22 * diretly here, too. This works like e.g. $table[0][0]->format. Table row and 23 * cell objects are created on the fly, when you access them for the first time. 24 * You can also create them as if you simply create new array elements. E.g. 25 * $table[] creates a new row in the table. 26 * 27 * <code> 28 * // Initialize the console output handler 29 * $out = new ezcConsoleOutput(); 30 * // Define a new format "headline" 31 * $out->formats->headline->color = 'red'; 32 * $out->formats->headline->style = array( 'bold' ); 33 * // Define a new format "sum" 34 * $out->formats->sum->color = 'blue'; 35 * $out->formats->sum->style = array( 'negative' ); 36 * 37 * // Create a new table 38 * $table = new ezcConsoleTable( $out, 60 ); 39 * 40 * // Create first row and in it the first cell 41 * $table[0][0]->content = 'Headline 1'; 42 * 43 * // Create 3 more cells in row 0 44 * for ( $i = 2; $i < 5; $i++ ) 45 * { 46 * $table[0][]->content = "Headline $i"; 47 * } 48 * 49 * $data = array( 1, 2, 3, 4 ); 50 * 51 * // Create some more data in the table... 52 * foreach ( $data as $value ) 53 * { 54 * // Create a new row each time and set it's contents to the actual value 55 * $table[][0]->content = $value; 56 * } 57 * 58 * // Set another border format for our headline row 59 * $table[0]->borderFormat = 'headline'; 60 * 61 * // Set the content format for all cells of the 3rd row to "sum" 62 * $table[2]->format = 'sum'; 63 * 64 * $table->outputTable(); 65 * </code> 66 * 67 * @property ezcConsoleTableOptions $options 68 * Contains the options for this class. 69 * @property int $width 70 * Width of the table. 71 * 72 * @see ezcConsoleOutput 73 * @package ConsoleTools 74 * @version 1.6.1 75 * @mainclass 76 */ 77class ezcConsoleTable implements Countable, Iterator, ArrayAccess 78{ 79 /** 80 * Automatically wrap text to fit into a column. 81 * @see ezcConsoleTable::$options 82 */ 83 const WRAP_AUTO = 1; 84 85 /** 86 * Do not wrap text. Columns will be extended to fit the largest text. 87 * ATTENTION: This is risky! 88 * @see ezcConsoleTable::$options 89 */ 90 const WRAP_NONE = 2; 91 92 /** 93 * Text will be cut to fit into a column. 94 * @see ezcConsoleTable::$options 95 */ 96 const WRAP_CUT = 3; 97 98 /** 99 * Align text in the default direction. 100 */ 101 const ALIGN_DEFAULT = -1; 102 /** 103 * Align text in cells to the right. 104 */ 105 const ALIGN_LEFT = STR_PAD_RIGHT; 106 /** 107 * Align text in cells to the left. 108 */ 109 const ALIGN_RIGHT = STR_PAD_LEFT; 110 /** 111 * Align text in cells to the center. 112 */ 113 const ALIGN_CENTER = STR_PAD_BOTH; 114 115 /** 116 * The width given by settings must be used even if the data allows it smaller. 117 */ 118 const WIDTH_FIXED = 1; 119 /** 120 * The width given by settings is a maximum value, if data allows it, the table gets smaller. 121 */ 122 const WIDTH_MAX = 2; 123 124 /** 125 * Container to hold the properties 126 * 127 * @var array(string=>mixed) 128 */ 129 protected $properties; 130 131 /** 132 * The ezcConsoleOutput object to use. 133 * 134 * @var ezcConsoleOutput 135 */ 136 protected $outputHandler; 137 138 /** 139 * Collection of the rows that are contained in the table. 140 * 141 * @var array(ezcConsoleTableRow) 142 */ 143 protected $rows; 144 145 /** 146 * Tool object for multi-byte encoding safe string operations. 147 * 148 * @var ezcConsoleStringTool 149 */ 150 private $stringTool; 151 152 /** 153 * Creates a new table. 154 * 155 * @param ezcConsoleOutput $outHandler Output handler to utilize 156 * @param int $width Overall width of the table (chars). 157 * @param array $options Options 158 * 159 * @see ezcConsoleTable::$options 160 * 161 * @throws ezcBaseValueException On an invalid setting. 162 */ 163 public function __construct( ezcConsoleOutput $outHandler, $width, $options = array() ) 164 { 165 $this->rows = array(); 166 $this->outputHandler = $outHandler; 167 $this->stringTool = new ezcConsoleStringTool(); 168 169 $this->__set( 'width', $width ); 170 if ( $options instanceof ezcConsoleTableOptions ) 171 { 172 $this->properties['options'] = $options; 173 } 174 else if ( is_array( $options ) ) 175 { 176 $this->properties['options'] = new ezcConsoleTableOptions( $options ); 177 } 178 else 179 { 180 throw new ezcBaseValueException( "options", $options, "array" ); 181 } 182 } 183 184 /** 185 * Set new options. 186 * This method allows you to change the options of the table. 187 * 188 * @param ezcConsoleTableOptions $options The options to set. 189 * 190 * @throws ezcBaseSettingNotFoundException 191 * If you tried to set a non-existent option value. 192 * @throws ezcBaseSettingValueException 193 * If the value is not valid for the desired option. 194 * @throws ezcBaseValueException 195 * If you submit neither an array nor an instance of 196 * ezcConsoleTableOptions. 197 */ 198 public function setOptions( $options = array() ) 199 { 200 if ( is_array( $options ) ) 201 { 202 $this->properties['options']->merge( $options ); 203 } 204 else if ( $options instanceof ezcConsoleTableOptions ) 205 { 206 $this->properties['options'] = $options; 207 } 208 else 209 { 210 throw new ezcBaseValueException( "options", $options, "instance of ezcConsoleTableOptions" ); 211 } 212 } 213 214 /** 215 * Returns the current options. 216 * Returns the options currently set for this table. 217 * 218 * @return ezcConsoleTableOptions The current options. 219 */ 220 public function getOptions() 221 { 222 return $this->properties['options']; 223 } 224 225 /** 226 * Returns the table in an array. 227 * 228 * Returns the entire table as an array of printable lines. Each element of 229 * the array represents a physical line of the drawn table, including all 230 * borders and stuff, so you can simply print the table using 231 * <code> 232 * echo implode( "\n" , $table->getTable() ): 233 * </code> 234 * which is basically what {@link ezcConsoleTable::outputTable()} does. 235 * 236 * @return array An array representation of the table. 237 */ 238 public function getTable() 239 { 240 return $this->generateTable(); 241 } 242 243 /** 244 * Output the table. 245 * Prints the complete table to the console. 246 * 247 * @return void 248 */ 249 public function outputTable() 250 { 251 echo implode( PHP_EOL, $this->generateTable() ); 252 } 253 254 /** 255 * Returns the table in a string. 256 * 257 * @return string 258 */ 259 public function __toString() 260 { 261 return implode( PHP_EOL, $this->generateTable() ); 262 } 263 264 /** 265 * Returns if the given offset exists. 266 * This method is part of the ArrayAccess interface to allow access to the 267 * data of this object as if it was an array. 268 * 269 * @param int $offset The offset to check. 270 * @return bool True when the offset exists, otherwise false. 271 * 272 * @throws ezcBaseValueException 273 * If a non numeric row ID is requested. 274 */ 275 public function offsetExists( $offset ) 276 { 277 if ( !is_int( $offset ) || $offset < 0 ) 278 { 279 throw new ezcBaseValueException( 'offset', $offset, 'int >= 0' ); 280 } 281 return isset( $this->rows[$offset] ); 282 } 283 284 // From here only interface method implementations follow, which are not intended for direct usage 285 286 /** 287 * Returns the element with the given offset. 288 * This method is part of the ArrayAccess interface to allow access to the 289 * data of this object as if it was an array. In case of the 290 * ezcConsoleTable class this method always returns a valid row object 291 * since it creates them on the fly, if a given item does not exist. 292 * 293 * @param int $offset The offset to check. 294 * @return ezcConsoleTableCell 295 * 296 * @throws ezcBaseValueException 297 * If a non numeric row ID is requested. 298 */ 299 public function offsetGet( $offset ) 300 { 301 $offset = ( $offset === null ) ? count( $this->rows ) : $offset; 302 if ( !is_int( $offset ) || $offset < 0 ) 303 { 304 throw new ezcBaseValueException( 'offset', $offset, 'int >= 0 or null' ); 305 } 306 if ( !isset( $this->rows[$offset] ) ) 307 { 308 $this->rows[$offset] = new ezcConsoleTableRow(); 309 } 310 return $this->rows[$offset]; 311 } 312 313 /** 314 * Set the element with the given offset. 315 * This method is part of the ArrayAccess interface to allow access to the 316 * data of this object as if it was an array. 317 * 318 * @param int $offset The offset to assign an item to. 319 * @param ezcConsoleTableRow $value The row to assign. 320 * @return void 321 * 322 * @throws ezcBaseValueException 323 * If a non numeric row ID is requested. 324 * @throws ezcBaseValueException 325 * If the provided value is not of type {@link ezcConsoleTableRow}. 326 */ 327 public function offsetSet( $offset, $value ) 328 { 329 if ( !( $value instanceof ezcConsoleTableRow ) ) 330 { 331 throw new ezcBaseValueException( 'value', $value, 'ezcConsoleTableRow' ); 332 } 333 if ( !isset( $offset ) ) 334 { 335 $offset = count( $this ); 336 } 337 if ( !is_int( $offset ) || $offset < 0 ) 338 { 339 throw new ezcBaseValueException( 'offset', $offset, 'int >= 0' ); 340 } 341 $this->rows[$offset] = $value; 342 } 343 344 /** 345 * Unset the element with the given offset. 346 * This method is part of the ArrayAccess interface to allow access to the 347 * data of this object as if it was an array. 348 * 349 * @param int $offset The offset to unset the value for. 350 * @return void 351 * 352 * @throws ezcBaseValueException 353 * If a non numeric row ID is requested. 354 */ 355 public function offsetUnset( $offset ) 356 { 357 if ( !is_int( $offset ) || $offset < 0 ) 358 { 359 throw new ezcBaseValueException( 'offset', $offset, 'int >= 0' ); 360 } 361 if ( isset( $this->rows[$offset] ) ) 362 { 363 unset( $this->rows[$offset] ); 364 } 365 } 366 367 /** 368 * Returns the number of cells in the row. 369 * This method is part of the Countable interface to allow the usage of 370 * PHP's count() function to check how many cells this row has. 371 * 372 * @return int Number of cells in this row. 373 */ 374 public function count() 375 { 376 $keys = array_keys( $this->rows ); 377 return count( $keys ) > 0 ? ( end( $keys ) + 1 ) : 0; 378 } 379 380 /** 381 * Returns the currently selected cell. 382 * This method is part of the Iterator interface to allow access to the 383 * cells of this row by iterating over it like an array (e.g. using 384 * foreach). 385 * 386 * @return ezcConsoleTableCell The currently selected cell. 387 */ 388 public function current() 389 { 390 return current( $this->rows ); 391 } 392 393 /** 394 * Returns the key of the currently selected cell. 395 * This method is part of the Iterator interface to allow access to the 396 * cells of this row by iterating over it like an array (e.g. using 397 * foreach). 398 * 399 * @return int The key of the currently selected cell. 400 */ 401 public function key() 402 { 403 return key( $this->rows ); 404 } 405 406 /** 407 * Returns the next cell and selects it or false on the last cell. 408 * This method is part of the Iterator interface to allow access to the 409 * cells of this row by iterating over it like an array (e.g. using 410 * foreach). 411 * 412 * @return mixed ezcConsoleTableCell if the next cell exists, or false. 413 */ 414 public function next() 415 { 416 return next( $this->rows ); 417 } 418 419 /** 420 * Selects the very first cell and returns it. 421 * This method is part of the Iterator interface to allow access to the 422 * cells of this row by iterating over it like an array (e.g. using 423 * foreach). 424 * 425 * @return ezcConsoleTableCell The very first cell of this row. 426 */ 427 public function rewind() 428 { 429 return reset( $this->rows ); 430 } 431 432 /** 433 * Returns if the current cell is valid. 434 * This method is part of the Iterator interface to allow access to the 435 * cells of this row by iterating over it like an array (e.g. using 436 * foreach). 437 * 438 * @return ezcConsoleTableCell The very first cell of this row. 439 */ 440 public function valid() 441 { 442 return current( $this->rows ) !== false; 443 } 444 445 /** 446 * Property read access. 447 * 448 * @param string $key Name of the property. 449 * @return mixed Value of the property or null. 450 * 451 * @throws ezcBasePropertyNotFoundException 452 * If the the desired property is not found. 453 * @ignore 454 */ 455 public function __get( $key ) 456 { 457 switch ( $key ) 458 { 459 case 'options': 460 case 'width': 461 return $this->properties[$key]; 462 default: 463 break; 464 } 465 throw new ezcBasePropertyNotFoundException( $key ); 466 } 467 468 /** 469 * Property write access. 470 * 471 * @param string $key Name of the property. 472 * @param mixed $val The value for the property. 473 * 474 * @throws ezcBasePropertyNotFoundException 475 * If a the value for the property options is not an instance of 476 * @throws ezcBaseValueException 477 * If a the value for a property is out of range. 478 * @ignore 479 */ 480 public function __set( $key, $val ) 481 { 482 switch ( $key ) 483 { 484 case 'options': 485 if ( !( $val instanceof ezcConsoleTableOptions ) ) 486 { 487 throw new ezcBaseValueException( $key, $val, 'ezcConsoleTableOptions' ); 488 } 489 $this->properties['options'] = $val; 490 return; 491 case 'width': 492 if ( $val < 1 ) 493 { 494 throw new ezcBaseValueException( $key, $val, 'int > 0' ); 495 } 496 $this->properties[$key] = $val; 497 return; 498 default: 499 break; 500 } 501 throw new ezcBasePropertyNotFoundException( $key ); 502 } 503 504 /** 505 * Property isset access. 506 * 507 * @param string $key Name of the property. 508 * @return bool True is the property is set, otherwise false. 509 * @ignore 510 */ 511 public function __isset( $key ) 512 { 513 switch ( $key ) 514 { 515 case 'options': 516 case 'width': 517 case 'cols': 518 return true; 519 } 520 return false; 521 } 522 523 /** 524 * Generate the complete table as an array. 525 * 526 * @return array(string) The table. 527 */ 528 private function generateTable() 529 { 530 $colWidth = $this->getColWidths(); 531 $table = array(); 532 533 if ( $this->options->lineVertical !== null ) 534 { 535 $table[] = $this->generateBorder( 536 $colWidth, 537 ( isset( $this[0] ) ? $this[0]->borderFormat : 'default' ) 538 ); 539 } 540 541 // Rows submitted by the user 542 for ( $i = 0; $i < count( $this->rows ); $i++ ) 543 { 544 // Auto broken rows 545 foreach ( $this->breakRows( $this->rows[$i], $colWidth ) as $brkRow => $brkCells ) 546 { 547 $table[] = $this->generateRow( $brkCells, $colWidth, $this->rows[$i] ); 548 } 549 $afterBorderFormat = isset( $this->rows[$i + 1] ) && $this->rows[$i + 1]->borderFormat != 'default' ? $this->rows[$i + 1]->borderFormat : $this->rows[$i]->borderFormat; 550 if ( $this->options->lineVertical !== null ) 551 { 552 $table[] = $this->generateBorder( $colWidth, $afterBorderFormat ); 553 } 554 } 555 556 // Empty tables need closing border 557 if ( $this->options->lineVertical !== null && count( $this->rows ) == null ) 558 { 559 $table[] = $this->generateBorder( $colWidth, 'default' ); 560 } 561 562 return $table; 563 } 564 565 /** 566 * Generate top/bottom borders of rows. 567 * 568 * @param array(int) $colWidth Array of column width. 569 * @param string $format Format name. 570 * @return string The Border string. 571 */ 572 private function generateBorder( $colWidth, $format ) 573 { 574 $border = ''; 575 foreach ( $colWidth as $col => $width ) 576 { 577 $border .= ( $this->options->lineHorizontal !== null ? $this->properties['options']->corner : '' ) 578 . str_repeat( 579 $this->properties['options']->lineVertical, 580 $width + ( 581 2 * iconv_strlen( $this->properties['options']->colPadding, 'UTF-8' ) 582 ) 583 ); 584 } 585 $border .= ( $this->options->lineHorizontal !== null ? $this->properties['options']->corner : '' ); 586 587 return $this->formatText( $border, $format ); 588 } 589 590 /** 591 * Generate a single physical row. 592 * This method generates the string for a single physical table row. 593 * 594 * @param array(string) $cells Cells of the row. 595 * @param array(int) $colWidth Calculated columns widths. 596 * @param ezcConsoleTableRow $row The row to generate. 597 * @return string The row. 598 */ 599 private function generateRow( $cells, $colWidth, $row ) 600 { 601 $rowData = ''; 602 for ( $cell = 0; $cell < count( $colWidth ); $cell++ ) 603 { 604 $align = $this->determineAlign( $row, $cell ); 605 $format = $this->determineFormat( $row, $cell ); 606 $borderFormat = $this->determineBorderFormat( $row ); 607 608 $data = isset( $cells[$cell] ) ? $cells[$cell] : ''; 609 $rowData .= $this->formatText( 610 $this->properties['options']->lineHorizontal, 611 $borderFormat 612 ); 613 $rowData .= $this->properties['options']->colPadding; 614 $rowData .= $this->formatText( 615 $this->stringTool->strPad( $data, $colWidth[$cell], ' ', $align ), 616 $format 617 ); 618 $rowData .= $this->properties['options']->colPadding; 619 } 620 $rowData .= $this->formatText( $this->properties['options']->lineHorizontal, $row->borderFormat ); 621 return $rowData; 622 } 623 624 /** 625 * Determine the alignment of a cell. 626 * Walks the inheritance path upwards to determine the alignment of a 627 * cell. Checks first, if the cell has it's own alignment (apart from 628 * ezcConsoleTable::ALIGN_DEFAULT). If not, checks the row for an 629 * alignment setting and uses the default alignment if not found. 630 * 631 * @param ezcConsoleTableRow $row The row this cell belongs to. 632 * @param int $cellId Index of the desired cell. 633 * @return int An alignement constant (ezcConsoleTable::ALIGN_*). 634 */ 635 private function determineAlign( $row, $cellId = 0 ) 636 { 637 return ( $row[$cellId]->align !== ezcConsoleTable::ALIGN_DEFAULT 638 ? $row[$cellId]->align 639 : ( $row->align !== ezcConsoleTable::ALIGN_DEFAULT 640 ? $row->align 641 : ( $this->properties['options']->defaultAlign !== ezcConsoleTable::ALIGN_DEFAULT 642 ? $this->properties['options']->defaultAlign 643 : ezcConsoleTable::ALIGN_LEFT ) ) ); 644 } 645 646 /** 647 * Determine the format of a cells content. 648 * Walks the inheritance path upwards to determine the format of a 649 * cells content. Checks first, if the cell has it's own format (apart 650 * from 'default'). If not, checks the row for a format setting and 651 * uses the default format if not found. 652 * 653 * @param ezcConsoleTableRow $row The row this cell belongs to. 654 * @param int $cellId Index of the desired cell. 655 * @return string A format name. 656 */ 657 private function determineFormat( $row, $cellId ) 658 { 659 return ( $row[$cellId]->format != 'default' 660 ? $row[$cellId]->format 661 : ( $row->format !== 'default' 662 ? $row->format 663 : $this->properties['options']->defaultFormat ) ); 664 } 665 666 /** 667 * Determine the format of a rows border. 668 * Walks the inheritance path upwards to determine the format of a 669 * rows border. Checks first, if the row has it's own format (apart 670 * from 'default'). If not, uses the default format. 671 * 672 * @param ezcConsoleTableRow $row The row this cell belongs to. 673 * @return string A format name. 674 */ 675 private function determineBorderFormat( $row ) 676 { 677 return $row->borderFormat !== 'default' 678 ? $row->borderFormat 679 : $this->properties['options']->defaultBorderFormat; 680 } 681 682 /** 683 * Returns auto broken rows from an array of cells. 684 * The data provided by a user may not fit into a cell calculated by the 685 * class. In this case, the data can be automatically wrapped. The table 686 * row then spans over multiple physical console lines. 687 * 688 * @param array(string) $cells Array of cells in one row. 689 * @param array(int) $colWidth Columns widths array. 690 * @return array(string) Physical rows generated out of this row. 691 */ 692 private function breakRows( $cells, $colWidth ) 693 { 694 $rows = array(); 695 // Iterate through cells of the row 696 foreach ( $colWidth as $cell => $width ) 697 { 698 $data = $cells[$cell]->content; 699 // Physical row id, start with 0 for each row 700 $row = 0; 701 // Split into multiple physical rows if manual breaks exist 702 $dataLines = explode( "\n", $data ); 703 foreach ( $dataLines as $dataLine ) 704 { 705 // Does the physical row fit? 706 if ( iconv_strlen( $dataLine, 'UTF-8' ) > ( $colWidth[$cell] ) ) 707 { 708 switch ( $this->properties['options']->colWrap ) 709 { 710 case ezcConsoleTable::WRAP_AUTO: 711 $subLines = explode( 712 "\n", 713 $this->stringTool->wordwrap( $dataLine, $colWidth[$cell], "\n", true ) 714 ); 715 foreach ( $subLines as $lineNo => $line ) 716 { 717 $rows[$row++][$cell] = $line; 718 } 719 break; 720 case ezcConsoleTable::WRAP_CUT: 721 $rows[$row++][$cell] = iconv_substr( $dataLine, 0, $colWidth[$cell], 'UTF-8' ); 722 break; 723 case ezcConsoleTable::WRAP_NONE: 724 default: 725 $rows[$row++][$cell] = $dataLine; 726 break; 727 } 728 } 729 else 730 { 731 $rows[$row++][$cell] = $dataLine; 732 } 733 } 734 } 735 return $rows; 736 } 737 738 /** 739 * Determine width of each single column. 740 * 741 * @return void 742 */ 743 private function getColWidths() 744 { 745 if ( is_array( $this->properties['options']->colWidth ) ) 746 { 747 return $this->properties['options']->colWidth; 748 } 749 750 // Determine number of columns: 751 $colCount = 0; 752 foreach ( $this->rows as $row ) 753 { 754 $colCount = max( sizeof( $row ), $colCount ); 755 } 756 757 if ( $colCount === 0 ) 758 { 759 return array( $this->width ); 760 } 761 762 $borderWidth = iconv_strlen( 763 $this->properties['options']->lineHorizontal, 764 'UTF-8' 765 ); 766 767 // Subtract border and padding chars from global width 768 $globalWidth = $this->width 769 - ( 770 // Per column: 2 * border padding + 1 border 771 $colCount * ( 772 2 * iconv_strlen( $this->properties['options']->colPadding, 'UTF-8' ) 773 + $borderWidth 774 ) 775 // 1 Additional border 776 ) - $borderWidth; 777 778 // Width of a column if each is made equal 779 $colNormWidth = round( $globalWidth / $colCount ); 780 $colMaxWidth = array(); 781 782 // Determine the longest data for each column 783 foreach ( $this->rows as $row => $cells ) 784 { 785 foreach ( $cells as $col => $cell ) 786 { 787 $contentLength = 0; 788 foreach ( explode( PHP_EOL, $cell->content ) as $contentRow ) 789 { 790 $contentLength = max( 791 $contentLength, 792 iconv_strlen( $contentRow, 'UTF-8' ) 793 ); 794 } 795 $colMaxWidth[$col] = isset( $colMaxWidth[$col] ) ? max( $colMaxWidth[$col], $contentLength ) : $contentLength; 796 } 797 } 798 $colWidth = array(); 799 $colWidthOverflow = array(); 800 $spareWidth = 0; 801 802 // Make columns best fit 803 foreach ( $colMaxWidth as $col => $maxWidth ) 804 { 805 // Does the largest data of the column fit into the average size 806 // + what we have in spare from earlier columns? 807 if ( $maxWidth <= ( $colNormWidth + $spareWidth ) ) 808 { 809 // We fit in, make the column as large as necessary 810 $colWidth[$col] = $maxWidth; 811 $spareWidth += ( $colNormWidth - $maxWidth ); 812 } 813 else 814 { 815 // Does not fit, use maximal possible width 816 $colWidth[$col] = $colNormWidth + $spareWidth; 817 $spareWidth = 0; 818 // Store overflow for second processing step 819 $colWidthOverflow[$col] = $maxWidth - $colWidth[$col]; 820 } 821 } 822 823 // Do we have spare to give to the columns again? 824 if ( $spareWidth > 0 ) 825 { 826 // Second processing step 827 if ( count( $colWidthOverflow ) > 0 ) 828 { 829 $overflowSum = array_sum( $colWidthOverflow ); 830 foreach ( $colWidthOverflow as $col => $overflow ); 831 { 832 $colWidth[$col] += floor( $overflow / $overflowSum * $spareWidth ); 833 } 834 } 835 elseif ( $this->properties['options']->widthType === ezcConsoleTable::WIDTH_FIXED ) 836 { 837 $widthSum = array_sum( $colWidth ); 838 foreach ( $colWidth as $col => $width ) 839 { 840 $colWidth[$col] += floor( $width / $widthSum * $spareWidth ); 841 } 842 } 843 } 844 845 // Finally sanitize values from rounding issues, if necessary 846 if ( ( $colSum = array_sum( $colWidth ) ) != $globalWidth && $this->properties['options']->widthType === ezcConsoleTable::WIDTH_FIXED ) 847 { 848 $colWidth[count( $colWidth ) - 1] -= $colSum - $globalWidth; 849 } 850 return $colWidth; 851 } 852 853 /** 854 * Returns the given $text formatted with $format. 855 * 856 * In case $useFormats is set to false in the output handler, the text is 857 * returned as given, without any formatting. 858 * 859 * @param string $text 860 * @param string $format 861 * @return string 862 */ 863 private function formatText( $text, $format ) 864 { 865 if ( $this->outputHandler->options->useFormats ) 866 { 867 return $this->outputHandler->formatText( $text, $format ); 868 } 869 else 870 { 871 return $text; 872 } 873 } 874} 875?> 876