Source for file Renderer.php
Documentation is available at Renderer.php
* Base class of all Renderer drivers
* Copyright (c) 1997-2007, Andrew Nagy <asnagy@webitecture.org>,
* Olivier Guilyardi <olivier@samalyse.com>,
* Mark Wiesemann <wiesemann@php.net>
* Sascha Grossenbacher <saschagros@bluewin.ch>
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The names of the authors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* CSV file id: $Id: Renderer.php,v 1.85 2007/12/05 12:42:32 olivierg Exp $
* @version $Revision: 1.85 $
* @package Structures_DataGrid
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* Base class of all Renderer drivers
* - buildHeader: (bool) Whether to build the header.
* - buildFooter: (bool) Whether to build the footer.
* - fillWithEmptyRows: (bool) Ensures that all pages have the same number of
* - numberAlign: (bool) Whether to right-align numeric values.
* - defaultCellValue: (string) What value to put by default into empty cells.
* - defaultColumnValues: (array) Per-column default cell value. This is an array
* of the form: array(fieldName => value, ...).
* - hideColumnLinks: (array) By default sorting links are enabled on all
* columns. With this option it is possible to
* disable sorting links on specific columns. This
* is an array of the form: array(fieldName, ...).
* This option only affects drivers that support
* - encoding: (string) The content encoding. If the mbstring extension
* is present the default value is set from
* mb_internal_encoding(), otherwise it is ISO-8859-1.
* - extraVars: (array) Variables to be added to the generated HTTP
* - excludeVars: (array) Variables to be removed from the generated
* - columnAttributes: (array) Column cells attributes. This is an array of
* array(fieldName => array(attribute => value, ...) ...)
* This option is only used by XML/HTML based
* - onMove: (string) Name of a Javascript function to call on
* onClick/onSubmit events when the user is either paging
* or sorting the data. This function
* receives a single object argument of the
* form: { page: <page>, sort: [{field: <field>,
* direction: <direction>}, ...],
* data: <user_data> }. Remark: setting this
* option doesn't remove the href attribute,
* you should return false from your handler
* function to void it (eg: for AJAX, etc..).
* - onMoveData: (string) User data passed in the "data" member of the
* object argument passed to onMove. No JSON
* serialization is performed, this is assigned
* as a raw string to the "data" attribute.
* It's up to you to add quotes, slashes, etc...
* --- DRIVER INTERFACE ---
* Methods (none required):
* - defaultCellFormatter()
* - getPaging() (deprecated)
* Properties (all read-only):
* Options that drivers may handle:
* @version $Revision: 1.85 $
* @author Olivier Guilyardi <olivier@samalyse.com>
* @author Mark Wiesemann <wiesemann@php.net>
* @author Sascha Grossenbacher <saschagros@bluewin.ch>
* @package Structures_DataGrid
* Columns' fields names and labels
* Drivers can read the content of this property but must not change it.
* array(<columnIndex> => array(field => <fieldName>,
* Where <columnIndex> is zero-based
* Drivers can read the content of this property but must not change it.
* <columnIndex> => array(<cellValue>, ...),
* Where <rowIndex> and <columnIndex> are zero-based
* Fields/directions the data is currently sorted by
* Drivers can read the content of this property but must not change it.
* @var array Structure: array(fieldName => direction, ....)
* Whether the backend support sorting by multiple fields
* Drivers can read the content of this property but must not change it.
* Drivers can read the content of this property but must not change it.
* Number of records in the current page
* Drivers can read the content of this property but must not change it.
* Total number of records as reported by the datasource
* Drivers can read the content of this property but must not change it.
* First record number (starting from 1), in the current page
* Drivers can read the content of this property but must not change it.
* Last record number (starting from 1), in the current page
* Drivers can read the content of this property but must not change it.
* Page number starting from 1.
* Drivers can read the content of this property but must not change it.
* Number of records per page
* Drivers can read the content of this property but must not change it.
* Drivers can read the content of this property but must not change it.
* GET/POST/Cookie parameters prefix
* Drivers can read the content of this property but must not change it.
* Which fields the datagrid may be sorted by
* Drivers can read the content of this property but must not change it.
* The default directions to sort by
* Drivers can read the content of this property but must not change it.
* @var array Structure: array(field => ASC|DESC, ...)
* Common and driver-specific options
* Drivers can read the content of this property but must not change it.
* @see Structures_DataGrid_Renderer::setOption()
* @see Structures_DataGrid_Renderer::_addDefaultOptions()
* Special driver features
* Beware: this is a private property, it is not meant to be accessed
* by drivers. Use the $_columns property instead
* @see Structures_DataGrid_Renderer::_columns
var $_columnObjects = array ();
* Whether the datagrid has been built or not
* @see Structures_DataGrid_Renderer::isBuilt()
* Cache for the GET parameters that are common to all sorting http queries
* @see Structures_DataGrid_Renderer::_buildSortingHttpQuery()
var $_sortingHttpQueryCommon = null;
* Whether streaming is enabled or not
var $_streamingEnabled = false;
* URL mapper instance, if provided
* @var object Net_URL_Mapper
* Instantiate the driver and set default options and features
* Drivers may overload this method in order to change/add default options.
* @see Structures_DataGrid_Renderer::_addDefaultOptions()
/* Options that the drivers may/should handle */
'encoding' => 'ISO-8859-1',
'fillWithEmptyRows' => false ,
'excludeVars' => array (),
'columnAttributes' => array (),
/* Options that must not be accessed by drivers */
'defaultCellValue' => null ,
'defaultColumnValues' => array (),
'hideColumnLinks' => array (),
'outputBuffering' => false ,
'objectPreserving' => false ,
$encoding = mb_internal_encoding ();
if ($encoding != 'pass') {
$this->_options['encoding'] = $encoding;
* Adds some default options.
* This method is meant to be called by drivers. It allows adding some
* @param array $options An associative array of the from:
* array(optionName => optionValue, ...)
* @see Structures_DataGrid_Renderer::setOption()
* Add special driver features
* This method is meant to be called by drivers. It allows specifying
* the special features that are supported by the current driver.
* @param array $features An associative array of the form:
* array(feature => true|false, ...)
* @param mixed $options An associative array of the form:
* array("option_name" => "option_value",...)
* @param string $name Option name
* @param mixed $value Option value
* This method is supposed to be called ONLY by the code that loads the
* driver. In most cases, that'll be the Structures_DataGrid class.
* @param array $columns Array of Structures_DataGrid_Column objects
$this->_columnObjects = &$columns;
* Specify how the datagrid is currently sorted
* This method is supposed to be called ONLY by the code that loads the
* driver. In most cases, that'll be the Structures_DataGrid class.
* The multiSort capabilities is related to the multiSort DataSource
* feature. In short : the DataGrid checks if the DataSource supports
* multiSort and informs the Renderer about it.
* @param array $currentSort Structure:
* array(fieldName => direction, ....)
* @param bool $multiSortCapable Whether the backend support sorting by
* Specify page and row limits
* This method is supposed to be called ONLY by the code that loads the
* driver. In most cases, that'll be the Structures_DataGrid class.
* @param int $currentPage Current page number
* @param int $rowsPerPage Maximum number of rows per page
* @param int $totalRowNum Total number of data rows
function setLimit($currentPage, $rowsPerPage, $totalRowNum) {
$this->_page = $currentPage;
1 : ceil($totalRowNum / $rowsPerPage);
$this->_firstRecord = ($currentPage - 1 ) * $rowsPerPage + 1;
* Tell the renderer whether streaming is enabled or not
* This method is supposed to be called ONLY by the code that loads the
* driver. In most cases, that'll be the Structures_DataGrid class.
* @param int $status Whether streaming is enabled or not
$this->_streamingEnabled = (boolean) $status;
* Attach a container object
* Drivers that provide support for the Structures_DataGrid::fill() method
* must implement this method.
* @param object Container of the class supported by the driver
* @return mixed True or PEAR_Error
* Return the container used by the driver
* Drivers should implement this method when they have some kind of support
* for rendering containers.
* @return object Container of the class supported by the driver
* Create or/and prepare the container
* Drivers may optionally implement this method for any pre-build()
* For the container support, it is responsible for creating the
* container if it has not already been provided by the user with
* the setContainer() method. It is where preliminary container
* setup should also be done.
* Drivers may optionally implement this method.
* @param array $columns Columns' fields names and labels (This is a
* convenient reference to the $_columns protected
* @return void or PEAR_Error
* Stream a chunk of records
* @param array $records 2D array of records
* @param integer $startRow Starting row number
* @param boolean $eof Whether the current chunk is the last chunk
* @return void or PEAR_Error
function streamBody($records, $startRow, $eof = false )
$rowNum = count($records);
for ($row = 0; $row < $rowNum; $row++ ) {
$result = $this->buildRow($row + $startRow, $records[$row]);
if (PEAR ::isError ($result)) {
if (PEAR ::isError ($result)) {
* Drivers may overload() this method, if buildRow() and buildEmptyRow()
* are not flexible enough.
* @return void or PEAR_Error
if (PEAR ::isError ($result)) {
if (PEAR ::isError ($result)) {
* This is a very simple method for drivers to build a row.
* For more flexibility, drivers should overload buildBody()
* @param int $index Row index (zero-based)
* @param array $data Record data.
* Structure: array(0 => <value0>, 1 => <value1>, ...)
* @return void or PEAR_Error
* Drivers must overload this method if they need to do something with
* empty rows that remain at the end of the body.
* This method will only be called if the "fillWithEmptyRows" option is
* @param int $index Row index (zero-based)
* @return void or PEAR_Error
* Drivers may optionally implement this method.
* @return void or PEAR_Error
* Finish building the datagrid.
* Drivers may optionally implement this method for any post-build()
* @return void or PEAR_Error
* Retrieve output from the container object
* Drivers may optionally implement this method.
* This method is meant to retrieve final output from the container.
* Usually the container is an object (ex: HTMLTable instance),
* and the final output a string.
* The driver knows how to retrieve such final output from a given
* container (ex: HTMLTable::toHTML()), and this is where to do it.
* Sometimes the container may not be an object, but the final output
* itself. In this case, this method should simply return the container.
* This method mustn't output anything directly to the standard output.
* Default formatter for all cells
* Drivers may optionally implement this method.
* @param string Cell value
* @return string Formatted cell value
* Drivers must not overload this method. Pre and post-build operations
* can be performed in init() and finalize()
* @param array $chunk 2D array of records
* @param integer $startRow Starting row number of current chunk
* @param boolean $eof Whether the current chunk is the last chunk
function build($chunk, $startRow, $eof = false )
// on first call of build(): initialize the columns and prepare the header
foreach ($this->_columnObjects as $index => $column) {
$field = $column->orderBy;
} else if (!is_null($column->fieldName )) {
$field = $column->fieldName;
$field = $column->columnName;
$label = $column->columnName;
if (isset ($this->_options['defaultColumnValues'][$field])) {
$column->setAutoFillValue ($this->_options['defaultColumnValues'][$field]);
$column->setAutoFillValue ($this->_options['defaultCellValue']);
$this->_options['columnAttributes'][$field] = array ();
$this->_options['columnAttributes'][$field] =
if (PEAR ::isError ($result)) {
if (PEAR ::isError ($result)) {
$chunkSize = count($chunk);
for ($rec = 0; $rec < $chunkSize; $rec++ ) {
// Currently, no formatting is performed on object records.
// These are not converted to indexed arrays, so that some
// renderer drivers might fail to process them.
foreach ($this->_columnObjects as $column) {
$content[] = $this->recordToCell ($column, $chunk[$rec],
$result = $this->streamBody($chunk, $startRow, $eof);
if (PEAR ::isError ($result)) {
// if this is the last chunk, do some final operations
if (PEAR ::isError ($result)) {
if (PEAR ::isError ($result)) {
if (PEAR ::isError ($result)) {
if (PEAR ::isError ($result)) {
* Returns the output from the renderer (e.g. HTML table, XLS object, ...)
* Drivers must not overload this method. Output generation has to be
* implemented in flatten().
* @return mixed The output from the renderer
if ($this->_streamingEnabled) {
return PEAR ::raiseError ('getOutput() cannot be used together with ' .
* Render to the standard output
* This method may be overloaded by renderer drivers in order to prepare
* writing to the standard output (like calling header(), etc...).
* @return void or object PEAR_Error
$result = $this->build(array (), 0 );
if (PEAR ::isError ($result)) {
* Return an error related to an unsupported public method
* When a given public method is not implemented/supported by the driver
* it must return a PEAR_Error object with code DATAGRID_ERROR_UNSUPPORTED.
* This is a helper method for generating such PEAR_Error objects.
* function anUnsupportedMethod()
* return $this->_noSupport(__FUNCTION__);
* @param string $method The name of the unsupported method
* @return object PEAR_Error with code DATAGRID_ERROR_UNSUPPORTED
return PEAR ::raiseError ("The renderer driver class \"" . get_class($this).
" \" does not support the $method() method" ,
* Sets the rendered status. This can be used to "flush the cache" in case
* you need to render the datagrid twice with the second time having changes
* This is quite an obsolete method...
* @param bool $status The rendered status of the DataGrid
/* What are we supposed to do with $status = true ? */
* Set the HTTP Request prefix
* @param string $prefix The prefix string
* Perform record/column to cell intersection and formatting
* @param object $column The column object
* @param array $record Array of record values
* @param int $row The row number of the cell
* @param int $col The column number of the cell
* @return string Formatted cell value
function recordToCell (&$column, $record, $row = null , $col = null )
if (isset ($column->formatter ) and !empty ($column->formatter )) {
$value = $column->formatter ($record, $row, $col);
} else if (isset ($column->fieldName )) {
$record = (array) $record; // record might be an object
if (isset ($record[$column->fieldName ])) {
if (empty ($value) and !is_null($column->autoFillValue )) {
$value = $column->autoFillValue;
* Query the grid build status
* @return bool Whether the grid has already been built or not
* Build an HTTP query for sorting a given column
* This is a handy method that most drivers can use in order to build
* the HTTP queries that are used to sort columns.
* It takes the global "extraVars", "excludeVars" options as well as the
* $_requestPrefix property into account and can also convert the ampersand
* to XML/HTML entities according to the "encoding" option.
* @param string $field Sort field name
* @param string $direction Sort direction
* @param bool $convertAmpersand Whether to convert ampersands to
* XML/HTML compliant entities
* @param array $extraParameters Optional extra HTTP parameters
* @return string Query string of the
$extraParameters = array ())
if (is_null($this->_sortingHttpQueryCommon)) {
// Build and cache the list of common get parameters
$this->_sortingHttpQueryCommon = $this->_options['extraVars'];
$ignore = $this->_options['excludeVars'];
$ignore[] = $prefix . 'orderBy';
$ignore[] = $prefix . 'direction';
foreach ($extraParameters as $var => $value) {
$ignore[] = $prefix . $var;
foreach ($_GET as $key => $val) {
$this->_sortingHttpQueryCommon[$key] = $val;
// Build list of GET variables
$get[$prefix . 'orderBy'] = $field;
$get[$prefix . 'direction'] = $direction;
foreach ($extraParameters as $var => $value) {
$get[$prefix . $var] = $value;
// Merge common and column-specific GET variables
$get = array_merge($this->_sortingHttpQueryCommon, $get);
if ($convertAmpersand and ini_get('arg_separator.output') == '&') {
* Builds a HTTP URL for sorting and paging.
* It uses NUM and optionally adds a query string with extraVars/GET
* @param string $field Sort field name
* @param string $direction Sort direction
* @param int $page Pager index
* @return string generated HTTP URL
function _buildMapperURL ($field, $direction, $page = 1 )
if (!empty ($direction)) {
$params = array ('page' => $page,
'direction' => $direction);
if (is_null($this->_sortingHttpQueryCommon)) {
// Build and cache the list of common get parameters
$this->_sortingHttpQueryCommon = $this->_options['extraVars'];
$ignore = $this->_options['excludeVars'];
$ignore[] = $prefix . 'orderBy';
$ignore[] = $prefix . 'direction';
foreach ($_GET as $key => $val) {
$this->_sortingHttpQueryCommon[$key] = $val;
return $this->_urlMapper->generate ($params, $this->_sortingHttpQueryCommon);
* Build a Javascript handler call for a given page and sorting spec
* @param string $page Page number (can also be "%d" for replacement
* @param mixed $sortSpec Array of fields and directions, or raw
* @return string JS function string, semi-colon included
foreach ($sortSpec as $field => $direction) {
$sort[] = "{field: '" . addslashes($field) . "', " .
" direction:'$direction'}";
$sort = "[" . join(',', $sort) . "]";
$data = $this->_options['onMoveData'] or $data = "''";
" ({ page: $page, sort: $sort, data: $data });";
* List special driver features
* @return array Of the form: array(feature => true|false, etc...)
* Tell if the driver as a specific feature
* @param string $name Feature name
* @param object $instance Net_URL_Mapper instance
* @return object Net_URL_Mapper instance or null
// This function is here because we can't depend on PHP_Compat
// If $formdata is an object, convert it to an array
// Check we have an array to work with
user_error('http_build_query() Parameter 1 expected to be Array or Object. Incorrect value given.',
// If the array is empty, return null
$separator = ini_get('arg_separator.output');
if (strlen($separator) == 0 ) {
// Start building the query
foreach ($formdata as $key => $val) {
if (is_integer($key) && $numeric_prefix != null ) {
$key = $numeric_prefix . $key;
// If the value is an array, recursively parse it
// The value is a resource
foreach ($array as $key => $value) {
$separator = ini_get('arg_separator.output');
if (strlen($separator) == 0 ) {
/* vim: set expandtab tabstop=4 shiftwidth=4: */
Documentation generated on Tue, 18 Dec 2007 11:30:15 -0500 by phpDocumentor 1.4.0. PEAR Logo Copyright © PHP Group 2004.
|