?? excelparser.php
字號:
<?php
//------------------------------------------------------------------------
// ABC Excel Parser Pro (ExcelFileParser class)
//
// PHP compatibility: 4.3.x, 5.0.2
// Copyright (c) Zakkis Technology, Inc.
// All rights reserved.
//
// This script parses a binary Excel file and store all data in an array.
// For more information see README.TXT file included in this distribution.
//
// 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
// REGENTS 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.
//
//------------------------------------------------------------------------
error_reporting (0); // E_ALL & ~E_NOTICE
define("LOG_2_BIG_BLOCK_SIZE",0x001e);
define("LOG_2_SMALL_BLOCK_SIZE",0x0020);
define("BAT_COUNT",0x002c);
/**
* Block index of the first block of the property table
*/
define("PROPERTIES_START",0x0030);
/**
* Block index of first big block containing the small block allocation table (SBAT)
*/
define("SBAT_START",0x003c);
/**
* Number of big blocks holding the SBAT
*/
define("SBAT_Block_Count",0x0040);
/**
* Block index of the first block in the Extended Block Allocation Table (XBAT)
*/
define("XBAT_START",0x0044);
/**
* Number of elements in the Extended Block Allocation Table (to be added to the BAT)
*/
define("XBAT_COUNT",0x48);
/**
* BAT_ARRAY
*/
define("BAT_ARRAY",0x4c);
require_once("debug.php");
require_once("exceldate.php");
require_once("excelfont.php");
require_once("dataprovider.php");
/**
* @package ExcelFileParser
* @version 4.2
*/
class ExcelFileParser {
var $dp = null;
var $max_blocks;
var $max_sblocks;
// Internal variables
var $fat;
var $sfat;
// Removed: var $sbd;
// Removed: var $syear;
var $formats;
var $xf;
var $fonts;
var $dbglog;
/**
* @var int
* Excel file BIFF version (7 = Excel 5-7, 8 = 2000, 10 = XP)
*/
var $biff_version;
/**
* Create Excel parser object
*
* Log type can be one of the following:
* ABC_CRITICAL
* ABC_ERROR
* ABC_ALERT
* ABC_WARNING
* ABC_NOTICE
* ABC_INFO
* ABC_DEBUG
* ABC_TRACE
* ABC_VAR_DUMP
* ABC_NO_LOG
*
* Bitwise combination of these flags is not allowed.
* You can use only one of them.
*
* @param string $logfilename - optional parameter (filename for debug logging). Default value - empty string, no logging.
* @param int $logtype log severity. Default value ABC_NO_LOG - disable logging.
*/
function ExcelFileParser($logfile="", $level = ABC_NO_LOG)
{
$this->dbglog = &DebugOut::getWriterSingleton($logfile,"",$level);
$this->dbglog->info("Logger started");
}
/**
*
* @return array list of formats
*/
function populateFormat()
{
$this->dbglog->trace(" populateFormat() function call");
$ret = array (
0=> "General",
1=> "0",
2=> "0.00",
3=> "#,##0",
4=> "#,##0.00",
5=> "($#,##0_);($#,##0)",
6=> "($#,##0_);[Red]($#,##0)",
7=> "($#,##0.00);($#,##0.00)",
8=> "($#,##0.00_);[Red]($#,##0.00)",
9=> "0%",
0xa=> "0.00%",
0xb=> "0.00E+00",
0xc=> "# ?/?",
0xd=> "# ??/??",
0xe=> "m/d/yy",
0xf=> "d-mmm-yy",
0x10=> "d-mmm",
0x11=> "mmm-yy",
0x12=> "h:mm AM/PM",
0x13=> "h:mm:ss AM/PM",
0x14=> "h:mm",
0x15=> "h:mm:ss",
0x16=> "m/d/yy h:mm",
// 0x17 - 0x24 reserved for international and undocumented
0x17=> "0x17",
0x18=> "0x18",
0x19=> "0x19",
0x1a=> "0x1a",
0x1b=> "0x1b",
0x1c=> "0x1c",
0x1d=> "0x1d",
0x1e=> "0x1e",
0x1f=> "0x1f",
0x20=> "0x20",
0x21=> "0x21",
0x22=> "0x22",
0x23=> "0x23",
0x24=> "0x24",
// 0x17 - 0x24 reserved for international and undocumented
0x25=> "(#,##0_);(#,##0)",
0x26=> "(#,##0_);[Red](#,##0)",
0x27=> "(#,##0.00_);(#,##0.00)",
0x28=> "(#,##0.00_);[Red](#,##0.00)",
0x29=> "_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)",
0x2a=> "_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)",
0x2b=> "_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)",
0x2c=> "_($*#,##0.00_);_($*(#,##0.00);_($*\"-\"??_);_(@_)",
0x2d=> "mm:ss",
0x2e=> "[h]:mm:ss",
0x2f=> "mm:ss.0",
0x30=> "##0.0E+0",
0x31=> "@");
$this->dbglog->dump($ret,"\$ret");
$this->dbglog->trace("populateFormat() function return");
return $ret;
}
/**
*
* @return int
*/
function xls2tstamp($date)
{
$date=$date>25568?$date:25569;
/*There was a bug if Converting date before 1-1-1970 (tstamp 0)*/
$ofs=(70 * 365 + 17+2) * 86400;
return ($date * 86400) - $ofs;
}
function getDateArray($date) {
return ExcelDateUtil::getDateArray($date);
}
/**
* @param array
* @return bool
*/
function isDateFormat($val)
{
$f_i=$this->xf['format'][$val];
if(preg_match("/[m|d|y]/i",$this->format[$f_i])!=0){
if(strrpos($this->format[$f_i],'[')!=FALSE) {
$tmp = preg_replace("/(\[\/?)(\w+)([^\]]*\])/","'\\1'.''.'\\3'",$this->format[$f_i]);
if (preg_match("/[m|d|y]/i", $tmp) != 0)
return TRUE;
else
return FALSE;
} else {
return TRUE;
}
} else
return FALSE;
}
function getUnicodeString($str,$ofs){
$size=0;
$i_ofs=0;
/* if (ord($str[$ofs])==255) {
$size=ord($str[$ofs])+ 256*(ord($str[$ofs+1]));
$i_ofs=2;
} else {*/
$size=ord($str[$ofs]);
$i_ofs=1;
/* }*/
$res=substr($str,$ofs+$i_ofs+2,$size);
//echo base_convert($ofs, 10, 16)."---$res<br>";
return $res;
}
/**
* Returns string from stream
*
* @deprecated
*/
function getByteString($str,$ofs){
$size=0;
$i_ofs=0;
// if (ord($str[$ofs])==255) {
// $size=ord($str[$ofs])+ 256*(ord($str[$ofs+1]));
// $i_ofs=2;
// } else {
$size=ord($str[$ofs]);
$i_ofs=1;
// }
return substr($str,$ofs+$i_ofs+1,$size);
}
/**
* Get blocks chain
*
* @param int
* @param bool
* @return mixed
*/
function get_blocks_chain($start,$small_fat=false) {
$this->dbglog->trace("get_blocks_chain(".var_export($start,true).",".var_export($small_fat,true).") function call ");
$chain = array();
$next_block = $start;
if( !$small_fat ) {
while( ($next_block!=0xfffffffe) &&
($next_block <= $this->max_blocks) &&
($next_block < count($this->fat)) )
{
$chain[] = $next_block;
$next_block = $this->fat[$next_block];
}
} else {
while( ($next_block!=0xfffffffe) &&
($next_block <= $this->max_sblocks) &&
($next_block < count($this->sfat)) )
{
$chain[] = $next_block;
$next_block = $this->sfat[$next_block];
}
}
if( $next_block != 0xfffffffe )
return false;
$this->dbglog->dump($chain,"\$chain");
$this->dbglog->trace("get_blocks_chain() function return");
return $chain;
}
/*
* Find stream by name
*
*/
function find_stream( $dir, $item_name,$item_num=0)
{
$this->dbglog->trace("find_stream(".var_export($dir,true).",".var_export($item_name,true).",".var_export($item_num,true).") function call ");
$dt = $dir->getOrd( $item_num * 0x80 + 0x42 );
$prev = $dir->getLong( $item_num * 0x80 + 0x44 );
$next = $dir->getLong( $item_num * 0x80 + 0x48 );
$dir_ = $dir->getLong( $item_num * 0x80 + 0x4c );
$curr_name = '';
if( ($dt==2) || ($dt==5) )
for( $i=0;
$i < ( $dir->getOrd( $item_num * 0x80 + 0x40 ) +
256 * $dir->getOrd( $item_num * 0x80 + 0x41 ) )/2-1;
$i++ )
$curr_name .= $dir->getByte( $item_num * 0x80 + $i * 2 );
if( (($dt==2) || ($dt==5)) && (strcmp($curr_name,$item_name)==0) ){
$this->dbglog->trace("find_stream() function return with ".var_export($item_num,true));
return $item_num;
}
if( $prev != 0xffffffff ) {
$i = $this->find_stream( $dir, $item_name, $prev);
if( $i>=0 ){
$this->dbglog->trace("find_stream() function return with ".var_export($i,true));
return $i;
}
}
if( $next != 0xffffffff ) {
$i = $this->find_stream( $dir, $item_name, $next);
if( $i>=0 ){
$this->dbglog->trace("find_stream() function return with ".var_export($i,true));
return $i;
}
}
if( $dir_ != 0xffffffff ) {
$i = $this->find_stream( $dir, $item_name, $dir_ );
if( $i>=0 ) {
$this->dbglog->trace("find_stream() function return with ".var_export($i,true));
return $i;
}
}
$this->dbglog->trace("find_stream() function return with -1");
return -2;
}
/**
* An RK value is an encoded integer or floating-point value. RK values have a size of 4 bytes and are used to decrease filesize for floating-point values.
*
* Bit Mask Contents
* 0 00000001H 0 = Value not changed 1 = Value is multiplied by 100
* 1 00000002H 0 = Floating-point value 1 = Signed integer value
* 31-2 FFFFFFFCH Encoded value
* If bit 1 is cleared, the encoded value represents the 30 most significant bits of an IEEE 754 floating-point value (64-bit
* double precision). The 34 least significant bits must be set to zero. If bit 1 is set, the encoded value represents a signed
* 30-bit integer value. To get the correct integer, the encoded value has to be shifted right arithmetically by 2 bits. If bit
* is set, the decoded value (both integer and floating-point) must be divided by 100 to get the final result.
*
* Examples:
* RK value Type Div 100 Encoded value Decoded value Result
* 3FF00000H float no 3FF00000H 3FF0000000000000H = 1.0 1.0
* 3FF00001H float yes 3FF00000H 3FF0000000000000H = 1.0 0.01
* 004B5646H integer no 004B5644H 0012D591H = 1234321 1234321
* 004B5647H integer yes 004B5644H 0012D591H = 1234321 12343.21
*
* @param int 32-bit value
* @return array
*/
function rk_decode($rk)
{
// $this->dbglog->trace("rk_decode(".var_export($rk,true).") function call");
$res = array();
// bit 1 integer value
if( $rk & 2 ) {
// integer
$val = ($rk & 0xfffffffc) >> 2;
if (((float)$val) == floor((float)$val)) {
$res['val'] = (int)$val;
$res['type'] = 1;
} else {
$res['val'] = (float)$val;
$res['type'] = 2;
}
} else {
// floating-point
$res['type'] = 2;
$frk = $rk;
$fexp = (($frk & 0x7ff00000) >> 20) - 1023;
$val = 1+(($frk & 0x000fffff) >> 2)/262144;
if( $fexp > 0 ) {
for( $i=0; $i<$fexp; $i++ )
$val *= 2;
} else {
if( $fexp==-1023 ) {
$val=0;
} else {
for( $i=0; $i<abs($fexp); $i++ )
$val /= 2;
}
}
if( $rk & 1 ) {
$val = $val / 100;
}
if($rk >> 31) {
$val = -($val);
}
$res['val'] = (float)$val;
}
// $this->dbglog->trace("rk_decode() function returns");
return $res;
}
/**
* Parse worksheet
*
* @param string
* @param int
*/
function parse_worksheet($ws, $_format_needed=1)
{
$this->dbglog->debug("parse_worksheet(DATA) function");
if( strlen($ws) <= 0 ){
$this->dbglog->trace("parse_worksheet() function returns 7 (Data not Found)");
return 7;
}
if( strlen($ws) < 4 ){
$this->dbglog->trace("parse_worksheet() function returns 6 (File Corrupted)");
return 6;
}
// parse workbook header
if( strlen($ws) < 256*ord($ws[3])+ord($ws[2]) ) return 6;
if( ord($ws[0]) != 0x09 ) return 6;
$vers = ord($ws[1]);
if( ($vers!=0) && ($vers!=2) && ($vers!=4) && ($vers!=8) )
return 8;
if( $vers!=8 ) {
$biff_ver = ($ver+4)/2;
} else {
if( strlen($ws) < 12 ) return 6;
switch( ord($ws[4])+256*ord($ws[5]) ) {
case 0x0500:
if( ord($ws[0x0a])+256*ord($ws[0x0b]) < 1994 ) {
$biff_ver = 5;
} else {
switch(ord( $ws[8])+256*ord($ws[9]) ) {
case 2412:
case 3218:
case 3321:
/*dbg*/ $this->dbglog->debug("Parsed BIFF version is 5");
$biff_ver = 5;
break;
default:
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -