Source of: Report.php


<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: *
// +----------------------------------------------------------------------+
// | PHP version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Sebastiao Rocha A Neto <rocha@i-node.com.br>                |
// +----------------------------------------------------------------------+
//
// $Id: Report.php,v 1.18 2004/01/12 13:35:39 rocha Exp $
/**
 * PHP Report:
 * 
 * Generates a html report page from a result set
 * array. That usually came from a sql query
 * and isnt well formatted for display. This class
 * 'prettyfy' the result using a template class, either
 * IT,ITX or PHPLIB template
 * 
 * Ex.: 
 * 
 * Inicial result set:
 * ----------------------------------------------
 * title    subtitle    value1    value2    value2
 * ----------------------------------------------
 * titulo1    subtitulo1    valor1    valor2    valor3
 * titulo1    subtitulo1    valor1    valor2    valor3
 * titulo1    subtitulo1    total1    total2    total3
 * titulo1    subtitulo2    valor1    valor2    valor3
 * titulo1    subtitulo2    valor1    valor2    valor3
 * titulo1    subtitulo2    total1    total2    total3
 * titulo2    subtitulo1    valor1    valor2    valor3
 * titulo2    subtitulo1    valor1    valor2    valor3
 * titulo2    subtitulo1    total1    total2    total3
 * titulo2    subtitulo2    valor1    valor2    valor3
 * titulo2    subtitulo2    total1    total2    total3
 *
 * 
 * Report output
 * ============================================
 * titulo1
 * ---------------------------------------------
 *       subtitulo1
 *       ----------------------------
 *           valor1 valor2 valor3
 *           valor1 valor2 valor3
 *       subtitulo2
 *       ----------------------------
 *           valor1 valor2 valor3
 *           valor1 valor2 valor3
 * ============================================
 * titulo2
 * ---------------------------------------------
 *       subtitulo1
 *       ----------------------------
 *             valor1 valor2 valor3
 *             valor1 valor2 valor3
 *       subtitulo2
 *       ----------------------------
 *           valor1 valor2 valor3
 *           valor1 valor2 valor3
 * ---------------------------------------------- 
 *
 * The format is done on the template file like:
 *
 * <!-- BEGIN title -->
 * {title}
 * ---------------------------------------------
 * <!-- BEGIN subtitle -->
 * {subtitle}
 * ----------------------------
 *     <!-- BEGIN values -->
 *     {value1} {value2} {value3}
 *     <!-- END values -->
 *   <!-- BEGIN totals -->
 *     {total1} {total2} {total3}
 *     <!-- END totals -->
 *
 * <!-- END subtitle -->
 * ----------------------------------------------
 * <!-- END title -->
 * 
 **/
require_once("Report_template.php");
//require_once("Report_IT.php");
class Report extends ReportTemplate {
    
/**
     * Array with columns names used for parse values '0' as null
     * @access private
     * @var array
     **/
    
var $_nullforzero = array();

    
/**
     * Raw result array
     * @access private
     * @var array 
     **/
    
var $_resultset = array();

    
/**
     * Array with columns names used for (sub)titles
     * @access private 
     * @var array 
     **/
    
var $_title = array();

    
/**
     * Number of subtitle levels
     *
     * @access private
     * @var integer 
     **/
    
var $_lastlevel = -1;

    
/**
     * New array with the new rewritten array
     * 
     * @access private 
     * @var array
     **/
    
var $_result = array();

    
/**
     * Boolean showtotals, if true the last line of 
     * block values is total, and have a template block
     * apart to be parsed.
     * 
     * @access private
     * @var boolean 
     **/
    
var $_showtotals false;

    
/**
     *
     * @access private
     * @var string
     **/
    
var $_blockvaluename 'values';

    
/**
     * @access private
     * @var string
     **/
    
var $_blocktotalname 'totals';

    
/**
     * Thousand separator (pt_BR stile)
     * 
     * @access private
     * @var string
     **/
    
var $_thousand_sep ',';
    
/**
     * Decimal point (pt_BR style)
     *
     * @access private
     * @var string
     **/
    
var $_dec_point '.';
        
    
/**
     * Currency symbol (Brazilian real)
     *
     * @access private
     * @var string
     **/
    
var $_cur_symbol 'R$';

    
/**
     * HTML style to show negative values
     * 
     * @access private
     * @var string
     **/
    
var $_negativestyle '<font color="#FF0000">(%s)</font>';

    
/**
     * Variable type for parsing befor show.
     * Types: numeric, money, date
     * 
     * @access private
     * @var array
     **/
    
var $_variabletype = array();

    
/**
     * Number of decimals
     *
     * @access private
     * @var integer
     **/
    
var $_decimals 2;

    
/**
     *
     * @access private
     * @var array
     **/
    
var $_totals = array();

    
/**
     * Date regex, use to extract fields from database columns
     * @access private
     * @var string
     **/
    
var $_dateregex "([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})";

    
/**
     *
     * @access private
     * @var string
     **/
    
var $_datetimeregex "([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})\s+([^\s]+)";

    
/**
     * Use european style for days (DD/MM/YYYY) or (MM/DD/YYY)?
     * @access public
     * @var string
     **/
    
var $europeanstyle true;
        
    
/**
     * Report::Report()
     *
     * Constructor
     * @access protected 
     * @param  $result
     **/
function Report(&$resultset$dir$file)
{
    
$this->setResultSet($resultset);
    
$this->debug false;
    
$this->_file $file;
    
$this->_dir  $dir;
    
$this->setup();
}

/**
 * Report::setTitle()
 *
 * Template independent function.
 * Set the name of columns to use as title/subtitles. $level tells the
 * position the title in the hierachie. Accepts an array or a pair (title, level)
 *
 * @param  $title
 * @param unknown $level
 * @return void
 **/
function setTitle($title$level null)
{
    if ( isset(
$level)) {
        
$this->_title[$level] = $title;
        
$this->_lastlevel $level;
    } else {
        if ( 
is_array($title)) {
            foreach (
$title as $level => $name) {
                
$this->_title[$level] = $name;
                
$this->_lastlevel $level;
            }
        } else {
            
/**
             * Raise error
             **/
            
return new PEAR_Error("Unable to set title, unknown args.", -1);
        }
    }
}

/**
 * Report::rewriteArray()
 *
 * Template independent function.
 * Remove one column from the array and returns a new array
 *
 * Needs a array:
 *                     title    value1  value2
 *                     ------------------------
 * array ( '1' => array(titulo1,  valor1,  valor2),
 *         '2' => array(titulo1,  valor1,  valor2),
 *         '3' => array(titulo1,  valor1,  valor2)
 * )
 *
 * Returns
 * array (
 *       'titulo1' => array( '1' => array( valor1,  valor2),
 *                           '2' => array( valor1,  valor2),
 *                           '3' => array( valor1,  valor2)
 *                         )
 *       )
 * @param  $title
 * @param  $oldarray
 * @return
 **/
function &rewriteArray($colname, &$oldarray)
{
    
$part = array();
    foreach (
$oldarray as $idx => $list) {
        
$name $list[$colname];
        if ( isset(
$count[$name])) {
            
$count[$name]++;
        } else {
            
$count[$name] = 0;
        }
        unset(
$list[$colname]);
        
$part[$name][$count[$name]] = $list;
    }
    
// print_r($part);
    
return $part;
}

/**
 * Report::rewriteResultSet()
 *
 * Template independent function.
 * Recursive function to write the final array as
 * $result[$title][$subtitle]...[] = array ( value1 => 'valor1', ...etc);
 *
 * @param string $list
 * @return mixed array with parcial result written.
 **/
function &rewriteResultSet($list "")
{
    static 
$level 0;

    
$title $this->_title[$level];
    if (!
$level ) {
        if (!
$list) {
            
$list =& $this->getResult(); // (i) => array()
        
}
        
$list =& $this->rewriteArray($title$list); //  (name)(i) => array()
        
$level++;
        foreach (
$list as $titlevalue => $newlist) {
            
$reslist[$titlevalue] = $this->rewriteResultSet(&$newlist);
        }
        
$level--; // back one level
        
return $reslist;
    } elseif  (
$level <= $this->_lastlevel) {
        
$list =& $this->rewriteArray($title$list);
        
$level++;
        foreach (
$list as $titlevalue => $newlist) {
            
$reslist[$titlevalue] = $this->rewriteResultSet(&$newlist);
        }
        
$level--; // back one level
        
return $reslist;
    } else {
        return 
$list;
    }
}

/**
 * Report::makeReport()
 *
 * Recursive function to create the report
 * from the rewritten array
 *
 * @param string $list
 * @return
 **/
function makeReport($list "")
{
    static 
$showlevel 0;

    if (!
$list && isset($this->_title[$showlevel]) ) {
        
$list $this->rewriteResultSet();
    } elseif(!
$list) {
        
$list $this->getResult(); // (i) => array()
    
}
    if (!
$showlevel && isset($this->_title[$showlevel])) {
        
$titlename $this->_title[$showlevel];
        
$showlevel++;
        foreach(
$list as $title => $newlist) {
            
$this->_titlevalues[$titlename]=$title;
            
$this->makeReport($newlist);
            
$this->blockParser($titlename$title);
            
$this->cleanBlock($this->_title[$showlevel]); // Only used in phplib template.inc
        
}
        
// Finishing the recursion, show the gran total if we were asked to.
        
if($this->_blockgrantotalname) {
            
$this->parseFinal($this->_grantotals$this->_blockgrantotalname);
        }
        
$showlevel--;
    } elseif  (isset(
$this->_lastlevel) && ($showlevel <= $this->_lastlevel)) {
        
$titlename $this->_title[$showlevel];
        
$showlevel++;
        foreach(
$list as $title => $newlist) {
            
$this->_titlevalues[$titlename]=$title;
            
$this->makeReport($newlist);
            
$this->blockParser($titlename$title);
            
$this->cleanBlock($this->_title[$showlevel]); // Only used in phplib template.inc
        
}
        
$showlevel--;
    } else {
        
/**
         * End of line, parse the values
         * Set variables values
         **/
        
$this->parseVariables(&$list);
    }
}
/**
*
*/
function setGranTotalBlock($val)
{
 
$this->_blockgrantotalname $val;
}
/**
 * @access public
 * @return array
 **/
function setNullForZero($zeroValue)
{
    if (
is_array($zeroValue)) {
        foreach(
$zeroValue as $zero) {
            
$this->_nullforzero[$zero] = true;
        }
    } elseif (
is_string($zeroValue)) {
        
$this->_nullforzero[$zeroValue] = true;
    }
}

/**
 * Get the current result array
 *
 * @access public
 * @return void
 **/
function & getResult()
{
    if (!
$this->_result) {
        return 
$this->_resultset;
    } else {
        return 
$this->_result;
    }
}

/**
 * @access public
 * @return array
 **/
function setResult($newValue)
{
    
$this->_result $newValue;
}

/**
 * Get the raw resultset array
 *
 * @access public
 * @return array
 **/
function getResultSet()
{
    return 
$this->_resultset;
}

/**
 * Set the raw result set array
 * @access public
 * @return array
 **/
function setResultSet($newValue)
{
    
/**
     * Since we can have result columns coming
     * from evaluation of expressions, if they exists we insert then
     * in the resultset. The value here doesnt matter yet.
     **/
    
$temp $newValue;
    if( 
is_array($this->_expression) ) {
        foreach(
$this->_expression as $name => $value ){
            
//echo "Set result: $name = $value <br>";
            
foreach($temp as $i => $dummy ) {
                
$newValue[$i][$name] = '';
            }
        }
    }
    
$this->_resultset =& $newValue;
}

/**
 * Get the number of subtitles
 * @access public
 * @return integer
 **/
function getLastLevel()
{
    return 
$this->_lastlevel;
}

/**
 * Set the number of subtitles
 * @access public
 * @return void
 **/
function setLastLevel($newValue)
{
    
$this->_lastlevel $newValue;
}

/**
 * @access public
 * @return boolean
 **/
function getShowTotals()
{
    return 
$this->_showtotals;
}

/**
 * @access public
 * @return void
 **/
function setShowTotals($newValue)
{
    
$this->_showtotals $newValue;
}
/**
 *
 * @access public
 * @return string
 **/
function getBlockValueName(){
    return 
$this->_blockvaluename;
}

/**
 *
 * @access public
 * @return void
 **/
function setBlockValueName($newValue){
    
$this->_blockvaluename $newValue;
}

/**
 *
 * @access public
 * @return string
 **/
function getBlockTotalName(){
    return 
$this->_blocktotalname;
}

/**
 *
 * @access public
 * @return void
 **/
function setBlockTotalName($newValue){
    
$this->_blocktotalname $newValue;
}

/**
 * Report::setVar()
 *
 *  Wrapper for setVariable's IT method.
 *  It does some formatting before set parse template
 *  variables. Also accumlate totals if that was set.
 *
 * @param $variable
 * @param string $value
 * @return
 **/
function setVar($variable$value="")
{
    if (
is_array($variable)) {
        
$this->_currentlist $variable;
        foreach (
$variable as $name => $value) {
            
/**
             * See if we are collecting totals
             *
             **/
            
if( $this->_totalizing ){
                
/**
                 * See if we have a expression to eval.
                 */
                
if( $this->_expression[$name] ) {
                    
$value $this->evaluateExpression($name);
                }
                if( 
$this->_functions[$name] ) {
                    
$value $this->evaluateFunction($name$value);
                }
                
/**
                 * See if we have to do some accounting.
                 */
                
if( $this->doTotals[$name] ) {
                    
$this->_totals[$name]     += $value;
                    
$this->_grantotals[$name] += $value;
                }
            }
            
/**
             * Parse the value.
             */
            
$newvalue $this->parseValue($name$value);
            
$this->replaceVar($name$newvalue);
        }
    } else {
        
/**
         * See if we have to do some accounting.
         */
        
if( $this->doTotals[$variable] ) {
            
$this->_totals[$variable] += $value;
        }
        
/**
         * See if we have to do some accounting.
         */
        
if( $this->_functions[$variable] ) {
              
$value $this->evaluateFunction($variable$value);
        }
        
/**
         * See if we have a expression to eval.
         */
        
if( $this->_expression[$variable] ) {
            
$value $this->evaluateExpression($variable);
        }
        
/**
        * Parse the value.
        */
        
$newvalue $this->parseValue($variable$value);
        
$this->replaceVar($variable$newvalue);
    }
}
/**
 *
 * @access public
 * @return string
 **/
function getTotals(){
    return 
$this->_totals;
}

/**
 *
 * @access public
 * @return array
 **/
function setTotals($newValue)
{
    if(isset(
$newValue)) {
        
$this->_showtotals $newValue;
    }
    
$this->doTotals $newValue;
}

/**
 * Report::evaluateFunction()
 *
 * @param $name
 * @return
 **/
function evaluateFunction($name$value)
{
    
$function $this->getFunction($name);
    
$val      $function($value);
    return 
$val;
}

/**
 * Report::getFunction()
 *
 * @param $name
 * @return
 **/
function getFunction($name)
{
    return 
$this->_functions[$name];
}

/**
 * Report::setFunctions()
 *
 * @param $name
 * @return
 **/
function setFunctions($f)
{
    
$this->_functions $f;
}

/**
 * Report::evaluateExpression()
 *
 * @param $name
 * @return
 **/
function evaluateExpression($name)
{
    
$expression $this->getExpression($name);
    foreach (
$this->_currentlist as $var => $value) {
        if (!isset(
$value)) {
            
$value "0";
        }
        
$expression str_replace("{".$var."}"$value$expression);
    }
    
// Some var can be at the title array, we dont want do miss them.
    
if ($this->_titlevalues) {
        
reset($this->_titlevalues);
        while (list(
$var$value) = each($this->_titlevalues)) {
            
$expression str_replace("{".$var."}"$value$expression);
        }
        
reset($this->_titlevalues);
    }

    eval(
"\$expression = $expression;");
    
// Since we added the new column for the expression without a value now we load it.
    
$this->_currentlist[$name] = $expression;
    return 
$expression;
}

/**
 * Report::getExpression()
 *
 * @param $name
 * @return
 **/
function getExpression($name)
{
    return 
$this->_expression[$name];
}

/**
 * Report::setExpression()
 *
 * @param $name
 * @param $expression
 * @return
 **/
function setExpression($name$expression$type="numeric")
{
    
$this->_expression[$name]   = $expression;
    
$this->_variabletype[$name] = $type;
    
// Add a new 'virtual' column to the list
    
foreach($this->_resultset as $i => $nada) {
        
$this->_resultset[$i][$name] = '';
    }
}

/**
 * Report::parseValue()
 *
 * @param $variable
 * @param $value
 * @return
 **/
function parseValue($variable$value)
{
    
$type $this->getVariableType($variable);
    switch (
$type) {
        case 
"date":
                
$regex $this->getDateRegex();
                if (
eregi($regex$value$regs)) {
                    if(
$this->europeanstyle) {
                        
$newvalue sprintf("%s/%s/%s",$regs[3],$regs[2],$regs[1]);
                    } else {
                        
$newvalue sprintf("%s/%s/%s",$regs[2],$regs[3],$regs[1]);
                    }
                } else {
                    
$newvalue =  $value;
                }
            break;

        case 
"datetime":
                
$regex $this->_datetimeregex;
                if (
eregi($regex$value$regs)) {
                    
$newvalue =  sprintf("%s/%s/%s %s",$regs[3],$regs[2],$regs[1], $regs[4]);
                } else {
                    
$newvalue =  $value;
                }
            break;

        case 
"money":
            
$thousand $this->getThousandSeparator();
            
$decimals 2;
            
$decpoint $this->getDecimalPoint();
            if (
$this->_negativestyle && $value 0) {
                
$value  = -$value;
                
$value  =  number_format($value$decimals$thousand$decpoint);
                
$value  $this->getNegativeStyle($value);
                
$value  =  $this->getCurrencySymbol() . " " $value;
            } else {
                
$value =  number_format($value$decimals$thousand$decpoint);
                
$value =  $this->getCurrencySymbol() . " " $value;
            }
            
$newvalue =  $value;
            break;

        case 
"select":
            
$combo $this->combos[$variable];
            
$i=0;
            while (
$combo[$i]) {
                if (
trim($combo[$i]["value"]) == trim($value)) {
                    if (
ereg"^\s*$"$value)) {
                        
$newvalue =  "";
                    } else {
                        
$newvalue =  $combo[$i]["label"];
                    }
                }
                
$i++;
            }
            break;

        case 
"numeric":
            
$thousand $this->getThousandSeparator();
            
$decimals $this->getDecimals();
            
$decpoint $this->getDecimalPoint();
            if (
$this->_negativestyle && $value 0) {
                
$value = -$value;
                
$value =  number_format($value$decimals$thousand$decpoint);
                
$value $this->getNegativeStyle($value);
            } else {
                
$value =  number_format($value$decimals$thousand$decpoint);
            }
            
$newvalue =  $value;
            break;

        case 
"percent":
            if (
$this->_nullforzero[$variable] && !$value) {
                
$newvalue =  "";
            }
            
$thousand $this->getThousandSeparator();
            
$decimals $this->getDecimals();
            
$decpoint $this->getDecimalPoint();
            if (
$this->_negativestyle && $value 0) {
                
$value = -$value;
                
$value =  number_format($value$decimals$thousand$decpoint) . "%";
                
$value $this->getNegativeStyle($value);
            } else {
                
$value =  number_format($value$decimals$thousand$decpoint) . "%";
            }
            
$newvalue =  $value;

        default:
            
$newvalue =  $value;
    } 
// switch
    
return $this->ifForm($variable$newvalue);
}

/**
 * Se o campo pertence a um form rederiza o tag com o valor
 * já formatado.
 */
function ifForm($var$val)
{
     if( 
$this->formfield[$var] ) {
         
$this->form->elements[$this->_namespace.$var]['ob']->value $GLOBALS[$this->_namespace.$var];
         return 
$this->form->ge($this->_namespace.$var$val);
     }
     return 
$val;
}
/**
 *
 * @access public
 * @return string
 **/
function getDateRegex(){
    return 
$this->_dateregex;
}

/**
 *
 * @access public
 * @return void
 **/
function setDateRegex($newValue){
    
$this->_dateregex $newValue;
}

/**
 *
 * @access public
 * @return integer
 **/
function getDecimals(){
    return 
$this->_decimals;
}

/**
 *
 * @access public
 * @return void
 **/
function setDecimals($newValue){
    
$this->_decimals $newValue;
}

/**
 *
 * @access public
 * @return string
 **/
function getVariableType($name){
    return 
$this->_variabletype[$name];
}

/**
 *
 * @access public
 * @return array
 **/
function setVariableType($newValue){
    
$this->_variabletype $newValue;
}

/**
 *
 * @access public
 * @return string
 **/
function getNegativeStyle($value){
    
$string $this->_negativestyle;
    
$string sprintf($string$value);
    return 
$string;
}

/**
 *
 * @access public
 * @return void
 **/
function setNegativeStyle($newValue){
    
$this->_negativestyle $newValue;
}

/**
 *
 * @access public
 * @return string
 **/
function getCurrencySymbol(){
    return 
$this->_cur_symbol;
}

/**
 *
 * @access public
 * @return void
 **/
function setCurrencySymbol($newValue){
    
$this->_cur_symbol $newValue;
}

/**
 *
 * @access public
 * @return string
 **/
function getThousandSeparator(){
    return 
$this->_thousand_sep;
}

/**
 *
 * @access public
 * @return void
 **/
function setThousandSeparator($newValue){
    
$this->_thousand_sep $newValue;
}
/**
 *
 * @access public
 * @return string
 **/
function getDecimalPoint(){
    return 
$this->_dec_point;
}

/**
 *
 * @access public
 * @return void
 **/
function setDecimalPoint($newValue){
    
$this->_dec_point $newValue;
}

}

?>

Processed: 2010/Sep/08 11:56:27