<?php /** * Zend Framework * * LICENSE * * This source file is subject to the new BSD license that is bundled * with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://framework.zend.com/license/new-bsd * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to [email protected] so we can send you a copy immediately. * * @category Zend * @package Zend_Text_Table * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @version $Id$ */ /** * Zend_Text_Table enables developers to create tables out of characters * * @category Zend * @package Zend_Text_Table * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Text_Table { /** * Auto seperator settings */ const AUTO_SEPARATE_NONE = 0x0; const AUTO_SEPARATE_HEADER = 0x1; const AUTO_SEPARATE_FOOTER = 0x2; const AUTO_SEPARATE_ALL = 0x4; /** * Decorator used for the table borders * * @var Zend_Text_Table_Decorator_Interface */ protected $_decorator = null; /** * List of all column widths * * @var array */ protected $_columnWidths = null; /** * Rows of the table * * @var array */ protected $_rows = array(); /** * Auto separation mode * * @var integer */ protected $_autoSeparate = self::AUTO_SEPARATE_ALL; /** * Padding for columns * * @var integer */ protected $_padding = 0; /** * Default column aligns for rows created by appendRow(array $data) * * @var array */ protected $_defaultColumnAligns = array(); /** * Plugin loader for decorators * * @var string */ protected $_pluginLoader = null; /** * Charset which is used for input by default * * @var string */ protected static $_inputCharset = 'utf-8'; /** * Charset which is used internally * * @var string */ protected static $_outputCharset = 'utf-8'; /** * Option keys to skip when calling setOptions() * * @var array */ protected $_skipOptions = array( 'options', 'config', 'defaultColumnAlign', ); /** * Create a basic table object * * @param array $columnsWidths List of all column widths * @param Zend_Config|array $options Configuration options * @throws Zend_Text_Table_Exception When no columns widths were set */ public function __construct($options = null) { // Set options if (is_array($options)) { $this->setOptions($options); } else if ($options instanceof Zend_Config) { $this->setConfig($options); } // Check if column widths were set // @todo When column widths were not set, assume auto-sizing if ($this->_columnWidths === null) { require_once 'Zend/Text/Table/Exception.php'; throw new Zend_Text_Table_Exception('You must define the column widths'); } // If no decorator was given, use default unicode decorator if ($this->_decorator === null) { if (self::getOutputCharset() === 'utf-8') { $this->setDecorator('unicode'); } else { $this->setDecorator('ascii'); } } } /** * Set options from array * * @param array $options Configuration for Zend_Text_Table * @return Zend_Text_Table */ public function setOptions(array $options) { foreach ($options as $key => $value) { if (in_array(strtolower($key), $this->_skipOptions)) { continue; } $method = 'set' . ucfirst($key); if (method_exists($this, $method)) { $this->$method($value); } } return $this; } /** * Set options from config object * * @param Zend_Config $config Configuration for Zend_Text_Table * @return Zend_Text_Table */ public function setConfig(Zend_Config $config) { return $this->setOptions($config->toArray()); } /** * Set column widths * * @param array $columnWidths Widths of all columns * @throws Zend_Text_Table_Exception When no columns were supplied * @throws Zend_Text_Table_Exception When a column has an invalid width * @return Zend_Text_Table */ public function setColumnWidths(array $columnWidths) { if (count($columnWidths) === 0) { require_once 'Zend/Text/Table/Exception.php'; throw new Zend_Text_Table_Exception('You must supply at least one column'); } foreach ($columnWidths as $columnNum => $columnWidth) { if (is_int($columnWidth) === false or $columnWidth < 1) { require_once 'Zend/Text/Table/Exception.php'; throw new Zend_Text_Table_Exception('Column ' . $columnNum . ' has an invalid' . ' column width'); } } $this->_columnWidths = $columnWidths; return $this; } /** * Set auto separation mode * * @param integer $autoSeparate Auto separation mode * @return Zend_Text_Table */ public function setAutoSeparate($autoSeparate) { $this->_autoSeparate = (int) $autoSeparate; return $this; } /** * Set decorator * * @param Zend_Text_Table_Decorator_Interface|string $decorator Decorator to use * @return Zend_Text_Table */ public function setDecorator($decorator) { if ($decorator instanceof Zend_Text_Table_Decorator_Interface) { $this->_decorator = $decorator; } else { $classname = $this->getPluginLoader()->load($decorator); $this->_decorator = new $classname; } return $this; } /** * Set the column padding * * @param integer $padding The padding for the columns * @return Zend_Text_Table */ public function setPadding($padding) { $this->_padding = max(0, (int) $padding); return $this; } /** * Get the plugin loader for decorators * * @return Zend_Loader_PluginLoader */ public function getPluginLoader() { if ($this->_pluginLoader === null) { $prefix = 'Zend_Text_Table_Decorator_'; $pathPrefix = 'Zend/Text/Table/Decorator/'; require_once 'Zend/Loader/PluginLoader.php'; $this->_pluginLoader = new Zend_Loader_PluginLoader(array($prefix => $pathPrefix)); } return $this->_pluginLoader; } /** * Set default column align for rows created by appendRow(array $data) * * @param integer $columnNum * @param string $align * @return Zend_Text_Table */ public function setDefaultColumnAlign($columnNum, $align) { $this->_defaultColumnAligns[$columnNum] = $align; return $this; } /** * Set the input charset for column contents * * @param string $charset */ public static function setInputCharset($charset) { self::$_inputCharset = strtolower($charset); } /** * Get the input charset for column contents * * @param string $charset */ public static function getInputCharset() { return self::$_inputCharset; } /** * Set the output charset for column contents * * @param string $charset */ public static function setOutputCharset($charset) { self::$_outputCharset = strtolower($charset); } /** * Get the output charset for column contents * * @param string $charset */ public static function getOutputCharset() { return self::$_outputCharset; } /** * Append a row to the table * * @param array|Zend_Text_Table_Row $row The row to append to the table * @throws Zend_Text_Table_Exception When $row is neither an array nor Zend_Zext_Table_Row * @throws Zend_Text_Table_Exception When a row contains too many columns * @return Zend_Text_Table */ public function appendRow($row) { if (!is_array($row) && !($row instanceof Zend_Text_Table_Row)) { require_once 'Zend/Text/Table/Exception.php'; throw new Zend_Text_Table_Exception('$row must be an array or instance of Zend_Text_Table_Row'); } if (is_array($row)) { if (count($row) > count($this->_columnWidths)) { require_once 'Zend/Text/Table/Exception.php'; throw new Zend_Text_Table_Exception('Row contains too many columns'); } require_once 'Zend/Text/Table/Row.php'; require_once 'Zend/Text/Table/Column.php'; $data = $row; $row = new Zend_Text_Table_Row(); $colNum = 0; foreach ($data as $columnData) { if (isset($this->_defaultColumnAligns[$colNum])) { $align = $this->_defaultColumnAligns[$colNum]; } else { $align = null; } $row->appendColumn(new Zend_Text_Table_Column($columnData, $align)); $colNum++; } } $this->_rows[] = $row; return $this; } /** * Render the table * * @throws Zend_Text_Table_Exception When no rows were added to the table * @return string */ public function render() { // There should be at least one row if (count($this->_rows) === 0) { require_once 'Zend/Text/Table/Exception.php'; throw new Zend_Text_Table_Exception('No rows were added to the table yet'); } // Initiate the result variable $result = ''; // Count total columns $totalNumColumns = count($this->_columnWidths); // Now render all rows, starting from the first one $numRows = count($this->_rows); foreach ($this->_rows as $rowNum => $row) { // Get all column widths if (isset($columnWidths) === true) { $lastColumnWidths = $columnWidths; } $renderedRow = $row->render($this->_columnWidths, $this->_decorator, $this->_padding); $columnWidths = $row->getColumnWidths(); $numColumns = count($columnWidths); // Check what we have to draw if ($rowNum === 0) { // If this is the first row, draw the table top $result .= $this->_decorator->getTopLeft(); foreach ($columnWidths as $columnNum => $columnWidth) { $result .= str_repeat($this->_decorator->getHorizontal(), $columnWidth); if (($columnNum + 1) === $numColumns) { $result .= $this->_decorator->getTopRight(); } else { $result .= $this->_decorator->getHorizontalDown(); } } $result .= "\n"; } else { // Else check if we have to draw the row separator if ($this->_autoSeparate & self::AUTO_SEPARATE_ALL) { $drawSeparator = true; } else if ($rowNum === 1 && $this->_autoSeparate & self::AUTO_SEPARATE_HEADER) { $drawSeparator = true; } else if ($rowNum === ($numRows - 1) && $this->_autoSeparate & self::AUTO_SEPARATE_FOOTER) { $drawSeparator = true; } else { $drawSeparator = false; } if ($drawSeparator) { $result .= $this->_decorator->getVerticalRight(); $currentUpperColumn = 0; $currentLowerColumn = 0; $currentUpperWidth = 0; $currentLowerWidth = 0; // Loop through all column widths foreach ($this->_columnWidths as $columnNum => $columnWidth) { // Add the horizontal line $result .= str_repeat($this->_decorator->getHorizontal(), $columnWidth); // If this is the last line, break out if (($columnNum + 1) === $totalNumColumns) { break; } // Else check, which connector style has to be used $connector = 0x0; $currentUpperWidth += $columnWidth; $currentLowerWidth += $columnWidth; if ($lastColumnWidths[$currentUpperColumn] === $currentUpperWidth) { $connector |= 0x1; $currentUpperColumn += 1; $currentUpperWidth = 0; } else { $currentUpperWidth += 1; } if ($columnWidths[$currentLowerColumn] === $currentLowerWidth) { $connector |= 0x2; $currentLowerColumn += 1; $currentLowerWidth = 0; } else { $currentLowerWidth += 1; } switch ($connector) { case 0x0: $result .= $this->_decorator->getHorizontal(); break; case 0x1: $result .= $this->_decorator->getHorizontalUp(); break; case 0x2: $result .= $this->_decorator->getHorizontalDown(); break; case 0x3: $result .= $this->_decorator->getCross(); break; default: // This can never happen, but the CS tells I have to have it ... break; } } $result .= $this->_decorator->getVerticalLeft() . "\n"; } } // Add the rendered row to the result $result .= $renderedRow; // If this is the last row, draw the table bottom if (($rowNum + 1) === $numRows) { $result .= $this->_decorator->getBottomLeft(); foreach ($columnWidths as $columnNum => $columnWidth) { $result .= str_repeat($this->_decorator->getHorizontal(), $columnWidth); if (($columnNum + 1) === $numColumns) { $result .= $this->_decorator->getBottomRight(); } else { $result .= $this->_decorator->getHorizontalUp(); } } $result .= "\n"; } } return $result; } /** * Magic method which returns the rendered table * * @return string */ public function __toString() { try { return $this->render(); } catch (Exception $e) { trigger_error($e->getMessage(), E_USER_ERROR); } } }