Location: PHPKode > scripts > PHPExcel wrapper for populating XLS files with user data > printform-xls.php
<?PHP
/**
* @name printform-xls.php, contains class CPrintFormXls,
* for generating "on the fly" filled Excel document based on XLS template file,
* loaded configuration (config is XML file!) and provided user data.
* PHPExcel classes used for reading/writing XLS body, see http://www.codeplex.com/PHPExcel
* @version 1.01 build 0005 2010-07-06
* @Author Alexander Selifonov, <alex [at] selifan.ru>
* @Link: http://www.selifan.ru
*
* @license http://www.opensource.org/licenses/bsd-license.php    BSD
*
* To make this code working, don't forget to add these lines:
* require_once('PHPExcel.php');
* require_once('PHPExcel/IOFactory.php');
* require_once('PHPExcel/Reader/Excel5.php'); # Or other Excel formats if needed
* (if You'going to output to PDF, include respective modules !
**/
class CPrintFormXls {
    const USE_BUFFER_FILE = false; # set it to TRUE if 'php://output' writing fails for any reason...
    private $_data = array();
    private $_templatefile = '';
    private $_outname = '';
    private $_tofile  = false;
    private $_configfile = '';
#    private $_protection = false;
    private $_config = array();
    private $_sheetdefs = array();
    private $errormessage = '';
    private $_outfmt = 'Excel5'; # XLS output file format (anyone supported by PHPExcel, including 'pdf' !)
    private $_objReader = null;
    private $_objPHPExcel = null;
    private $_worksheet = null;
    public function CPrintFormXls($param='', $data=null, $outname='', $tofile=false) {
        if(is_array($param)) {
            if(isset($param['template'])) $this->_templatefile = $param['template'];
            if(isset($param['data']))     $this->_data   = $param['data'];
            if(isset($param['outname']))  $this->_outname= $param['outname'];
            if(isset($param['tofile']))    $this->_tofile  = $param['tofile'];
            if(isset($param['outfmt']))    $this->_outfmt  = $param['outfmt'];
            if(isset($param['configfile'])) {
               $this->_configfile  = $param['configfile'];
            }
        }
        elseif(is_scalar($param)) {
            $this->_configfile = $param;
            $this->_data = $data;
            $this->_out = $outname;
            $this->_tofile = $tofile;
        }
        if(!empty($this->_configfile)) $this->LoadConfig($this->_configfile);
    }
    /**
    * Loads configuration from prepared XML file, see it's format in docs and examples
    *
    * @param mixed $cfgname full path-filename to config XML file
    * @Returns true if configuration successfully loaded, false otherwise
    */
    public function LoadConfig($cfgname=null) {
        if(!$cfgname) $cfgname = $this->_configfile;
        else $this->_configfile = $cfgname;
        $ret = true;
        $this->_config = array('title'=>'','description'=>'', 'author'=>'','templatefile'=>''
           ,'stringcharset'=>'','protectsheets'=>0,'protectbook'=>0
           ,'dateformat'=>'dd/mm/yyyy');

        if(is_file($cfgname)) $xml = @simplexml_load_file($cfgname);
        elseif(substr($cfgname,0,5)=='<'.'?xml') $xml = @simplexml_load_string($cfgname);
        # $xp = $xml->xpath('//testformat');    echo '<pre>'; print_r($xp);echo '</pre>';
        if(!($xml) || !isset($xml->sheets)) {
            $this->_errormessage = 'Wrong XML file or no file, '.$cfgname;
            echo $this->_errormessage ;
            return false;
        }
        if(isset($xml->dateformat)) $this->_config['dateformat'] = (string)$xml->dateformat;

        if(isset($xml->protectsheets)) $this->_config['protectsheets'] = (int)$xml->protectsheets; # protect worksheets by password
        if(isset($xml->password)) $this->_config['password'] = (string)$xml->password; # password to protect with

        if(isset($xml->protectbook)) $this->_config['protectbook'] = (int)$xml->protectbook; # protect workbook
        if(isset($xml->bookpassword)) $this->_config['bookpassword'] = (int)$xml->bookpassword;

        if(isset($xml->title)) $this->_config['title'] = (string)$xml->title;
        if(isset($xml->description)) $this->_config['description'] = (string)$xml->description;
        if(isset($xml->author)) $this->_config['author'] = (string)$xml->author;

        if(isset($xml->stringcharset)) $this->_config['stringcharset'] = strtoupper($xml->stringcharset);
        # if not UTF-8 and not '', use iconv() to make UTF-8 !

        if(isset($xml->templatefile)) $this->_config['templatefile'] = $xml->templatefile;
        $fileversion = isset($xml->version) ? $xml->version : 1; # for future needs

        if(!empty($this->_config['templatefile'])) $this->_templatefile = $this->_config['templatefile'];

        $this->_sheetdefs = array();
        $isheet = 0;
        $fldcnt = 0;
        foreach($xml->sheets->children() as $key => $sheetitem) {
            $offset = isset($sheetitem['offset']) ? (int) $sheetitem['offset'] : $isheet;
            $this->_sheetdefs[$isheet] = array('offset'=>$offset, 'fields'=>array());

            # echo "[$key], off=$offset;"; print_r($sheetitem); echo '<hr />'; //debug
            foreach($sheetitem->children() as $key=>$item) {
                $fldname = isset($item['name'])? trim("{$item['name']}") : '';
                if(!$fldname) continue;
                $newar = array('col'=>0,'row'=>0);
                $newar['name'] = strtolower($fldname);
                $newar['col'] = isset($item['col']) ? intval($item['col']) : '0';
                $newar['row'] = isset($item['row']) ? intval($item['row']) : '0';
                $newar['type'] = isset($item['type']) ? strtolower(trim($item['type'])) : '';
                $newar['convert'] = isset($item['convert']) ? (string) $item['convert'] : ''; # user function to convert value
                $newar['scatter'] = isset($item['scatter']) ? (int)$item['scatter'] : 0; # scatter chars to adjacent cells, one by one
#                echo 'subs:'; print_r($newar); echo '<hr />';
                $this->_sheetdefs[$isheet]['fields'][] = $newar;
                $fldcnt++;
            }
            $isheet++;
        }
        if(!$fldcnt) {
            $this->_errormessage = 'No valid worksheet definitions found (no fields defined)!';
            $ret = false;
        }
        return $ret;
    }

    public function SetTitle($strg) { $this->_config['title'] = $strg; }
    public function SetDescription($strg) { $this->_config['description'] = $strg; }
    public function SetAuthor($strg) { $this->_config['author'] = $strg; }

    public function ProtectSheets($protect, $password=null, $options = false) {
        $this->_config['protectsheets'] = $protect;
        if($password!==null) $this->_config['password'] = $password;
        $this->_config['protectoptions'] = $options;
    }
    /**
    * sets workbook protection
    *
    * @param mixed $protect true means "set protecting"
    * @param mixed $password
    */
    public function ProtectBook($protect, $password=null) {
        $this->_config['protectbook'] = $protect;
        if($password!==null ) $this->_config['bookpassword'] = $password;
    }
    /**
    * Adding data to be populated in template
    *
    * @param mixed $param must be an associative array or string - variable name (in that case second parameter should be passed)
    */
    public function AddData($param, $pval=null) {
        if(is_array($param)) $this->_data = array_merge($this->_data, $param);
        elseif(is_string($param)) $this->_data[$param] = $pval;
    }
    /**
    * Populates and sends/saves final Xls document body
    *
    * @param mixed $dest if output filename passed, result will be saved into it,
    * otherwise will be sent to client stream
    * @return true if XLS file generated/echoed, false if some errors
    */
    public function Render() {
        ini_set('max_execution_time', 600);
        if(count($this->_sheetdefs)<1) {
            $this->errormessage = 'Configuration not loaded, Rendering impossible !';
            return false;
        }

        if(empty($this->_outname)) {
            $this->_outname = $this->_templatefile;
            $off = max(strrpos($this->_outname, '/'), strrpos($this->_outname, '\\'));
            if($off!==false) $this->_outname = substr($this->_outname, $off+1);
        }

        if(!$this->CreateXlsObject()) return false;

        $creator = empty($this->_config['author']) ? 'CPrintFormXls, PHPExcel wrapper class' : $this->_config['author'];
        $this->_objPHPExcel->getProperties()->setCreator($creator);

        if(!empty($this->_config['title'])) $this->_objPHPExcel->getProperties()->setDescription($this->_config['title']);
        if(!empty($this->_config['description'])) $this->_objPHPExcel->getProperties()->setTitle($this->_config['description']);

        # Populating with data...
        foreach($this->_sheetdefs as $sheetdef) {

            $this->_worksheet = $this->_objPHPExcel->getSheet($sheetdef['offset']);
            foreach($sheetdef['fields'] as $no=>$fcfg) {
                $fldname = $fcfg['name'];
                if(!isset($this->_data[$fldname])) continue;
                if(!is_scalar($this->_data[$fldname])) continue;
                $strval = $this->_data[$fldname];
                if(!empty($fcfg['convert']) && function_exists($fcfg['convert'])) # user converter function
                   $strval = call_user_func($fcfg['convert'], $strval);
                switch($fcfg['type']) {
                    case 'date':
                        $this->XlsSetDate($fcfg['row'],$fcfg['col'],$strval);
                        break;
                    case 'bool':
                    case 'logical':
                        $strval = $strval ? 'X' : '';
                        $this->_worksheet->setCellValueByColumnAndRow($fcfg['col'], $fcfg['row'], $strval);
                        break;
                    case 'bool-invert':
                    case 'logical-invert':
                        $strval = $strval ? '' : 'X';
                        $this->_worksheet->setCellValueByColumnAndRow($fcfg['col'], $fcfg['row'], $strval);
                        break;
                    default:
                        if(is_string($strval)) {
                            $strval = $this->ConvertCset($strval);
                        }
                        $this->_worksheet->setCellValueByColumnAndRow($fcfg['col'], $fcfg['row'], $strval);
                        break;
                }
            }
            if($this->_config['protectsheets']) {
                $pwd = isset($this->_config['password']) ? $this->_config['password'] : '';
                $this->_worksheet->getProtection()->setPassword($pwd);
                $this->_worksheet->getProtection()->setSheet(true);
                $this->_worksheet->getProtection()->setSort(true);
                $this->_worksheet->getProtection()->setInsertRows(true);
                $this->_worksheet->getProtection()->setFormatCells(true);
            }
            if($this->_config['protectbook']) { # whole workbook protecting
                $pwd = isset($this->_config['bookpassword']) ? (string)$this->_config['bookpassword'] : '';
                $this->_objPHPExcel->getSecurity()->setLockWindows(true)->setLockStructure(true)->setWorkbookPassword($pwd);
#                $this->_objPHPExcel->getSecurity()->setLockStructure(true);
#                $this->_objPHPExcel->getSecurity()->setWorkbookPassword($pwd);
            }
        }
        if($this->_outfmt == 'pdf') {
            $objWriter = new PHPExcel_Writer_PDF($this->_objPHPExcel);
        }
        else {
            $objWriter = @PHPExcel_IOFactory::createWriter($this->_objPHPExcel, $this->_outfmt);
            if(!$objWriter) {
                $this->errormessage = 'PHPExcel: Unsupported writer for format '.$this->_outfmt;
                return false;
            }
        }
        if(self::USE_BUFFER_FILE && !$this->_tofile) {
            @mkdir('./tmp');
            $tmpname = './tmp/'.mt_rand(10000000000,99999999999).'.$$$';
            $objWriter->save($tmpname);
        }
        if($this->_tofile) { # just save to specified file
            if(strtolower($this->_templatefile)==strtolower($this->_outname)) $this->_outname .= '.xls'; # protect accidental template overwriting !
            $objWriter->save($this->_outname);
        }
        else {
            @Header('Pragma: no-cache');
            @Header('Pragma: public');
            @Header('Cache-Control: must-revalidate, post-check=0, pre-check=0');

            if($this->_outfmt == 'pdf')
               @Header('Content-Type: application/pdf');
            else
               @Header('Content-Type: application/vnd.ms-excel');

            @Header("Content-Disposition: attachment; filename=\"{$this->_outname}\"");
            @header('Cache-Control: max-age=0');
            if(self::USE_BUFFER_FILE) {
                $xlbody = file_get_contents($tmpname);
                @Header('Content-Length: '.strlen($xlbody));
                echo($xlbody);
                unlink($tmpname);
            }
            else $objWriter->save('php://output'); # direct output instead of reading from saved temp.file
        }
        unset($this->_worksheet, $this->_objPHPExcel, $objWriter);
        return true;
    }

    private function XlsSetDate($row,$col, $value) {
        if(is_string($value)) { # try to convert 'dddd-mm-yy' or 'dd-mm-yyyy' to 'EXCEL' integer
            $elm = preg_split("/[\s-.\/]+/", $value);
            if(count($elm)<3) return;
            $yy = intval($elm[0]); $mm = intval($elm[1]); $dd = intval($elm[2]); # default fmt is yyyy-mm-dd
            if($elm[2]>90) { # ok, it's must be mm/dd/yyyy OR dd.mm.yyyy
                $yy = intval($elm[2]);
                $mm = intval($elm[0]); $dd = intval($elm[1]);
            }
            if($mm>12) { # swap if wrong month (too big, may be it's day no.)
                $tmp = $dd; $mm = $dd; $dd=$mm; $mm=$tmp;
            }
            $value = floor(mktime(0,0,1,$mm,$dd,$yy) / 86400) + 25570; # 25570 - correcting from mktime to Excel date number
        }
        $this->_worksheet->setCellValueByColumnAndRow($col, $row, $value);
        $k1 = floor($col/26);
        $k2 = intval($col % 26);
        $colname = (($k1>0)? chr(64+$k1):'') .chr(65+$k2).$row; # col,row number to "AZ5" notation
        $this->_worksheet->getStyle($colname)->getNumberFormat()->setFormatCode($this->_config['dateformat']);
    }

    # returns last error message
    public function GetErrorMessage() { return $this->errormessage; }
    /**
    * @return PHPExcel object to manupulate in user's code
    */
    private function CreateXlsObject() {
        if(is_object($this->_objPHPExcel)) return true;
        $this->_objReader = @PHPExcel_IOFactory::createReader('Excel5'); # TODO : investigate support of reading other XLS[x]
        if(!$this->_objReader) {
            $this->errormessage = 'PHPExcel classes not included !';
            return false;
        }
        try {  $this->_objPHPExcel = $this->_objReader->load($this->_templatefile); }
        catch (Exception $e) {
            $this->errormessage = 'Loading XLS file error, error message is: '.$e->getMessage();
             return false;
        }
        return true;
    }
    public function GetXlsObject() {
        if(!$this->_objPHPExcel) $this->CreateXlsObject();
        return $this->_objPHPExcel;
    }
    private function ConvertCset($strval) {
        $ret = ($this->_config['stringcharset']!='' && $this->_config['stringcharset']!='UTF-8') ?
         iconv($this->_config['stringcharset'],'UTF-8',$strval) : $strval;
        return $ret;
    }
} # class CPrintFormXls definition end
Return current item: PHPExcel wrapper for populating XLS files with user data