533 lines
16 KiB
PHP
533 lines
16 KiB
PHP
|
<?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 license@zend.com so we can send you a copy immediately.
|
||
|
*
|
||
|
* @category Zend
|
||
|
* @package Zend_Text_Table
|
||
|
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
|
||
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||
|
* @version $Id: Table.php 16209 2009-06-21 19:20:34Z thomas $
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Zend_Text_Table enables developers to create tables out of characters
|
||
|
*
|
||
|
* @category Zend
|
||
|
* @package Zend_Text_Table
|
||
|
* @copyright Copyright (c) 2005-2009 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';
|
||
|
|
||
|
$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);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|