Add php files
This commit is contained in:
348
include/spreadsheet-reader-master/SpreadsheetReader.php
Normal file
348
include/spreadsheet-reader-master/SpreadsheetReader.php
Normal file
@@ -0,0 +1,348 @@
|
||||
<?php
|
||||
/**
|
||||
* Main class for spreadsheet reading
|
||||
*
|
||||
* @version 0.5.10
|
||||
* @author Martins Pilsetnieks
|
||||
*/
|
||||
class SpreadsheetReader implements SeekableIterator, Countable
|
||||
{
|
||||
const TYPE_XLSX = 'XLSX';
|
||||
const TYPE_XLS = 'XLS';
|
||||
const TYPE_CSV = 'CSV';
|
||||
const TYPE_ODS = 'ODS';
|
||||
|
||||
private $Options = array(
|
||||
'Delimiter' => '',
|
||||
'Enclosure' => '"'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var int Current row in the file
|
||||
*/
|
||||
private $Index = 0;
|
||||
|
||||
/**
|
||||
* @var SpreadsheetReader_* Handle for the reader object
|
||||
*/
|
||||
private $Handle = array();
|
||||
|
||||
/**
|
||||
* @var TYPE_* Type of the contained spreadsheet
|
||||
*/
|
||||
private $Type = false;
|
||||
|
||||
/**
|
||||
* @param string Path to file
|
||||
* @param string Original filename (in case of an uploaded file), used to determine file type, optional
|
||||
* @param string MIME type from an upload, used to determine file type, optional
|
||||
*/
|
||||
public function __construct($Filepath, $OriginalFilename = false, $MimeType = false)
|
||||
{
|
||||
if (!is_readable($Filepath))
|
||||
{
|
||||
throw new Exception('SpreadsheetReader: File ('.$Filepath.') not readable');
|
||||
}
|
||||
|
||||
// To avoid timezone warnings and exceptions for formatting dates retrieved from files
|
||||
$DefaultTZ = @date_default_timezone_get();
|
||||
if ($DefaultTZ)
|
||||
{
|
||||
date_default_timezone_set($DefaultTZ);
|
||||
}
|
||||
|
||||
// Checking the other parameters for correctness
|
||||
|
||||
// This should be a check for string but we're lenient
|
||||
if (!empty($OriginalFilename) && !is_scalar($OriginalFilename))
|
||||
{
|
||||
throw new Exception('SpreadsheetReader: Original file (2nd parameter) path is not a string or a scalar value.');
|
||||
}
|
||||
if (!empty($MimeType) && !is_scalar($MimeType))
|
||||
{
|
||||
throw new Exception('SpreadsheetReader: Mime type (3nd parameter) path is not a string or a scalar value.');
|
||||
}
|
||||
|
||||
// 1. Determine type
|
||||
if (!$OriginalFilename)
|
||||
{
|
||||
$OriginalFilename = $Filepath;
|
||||
}
|
||||
|
||||
$Extension = strtolower(pathinfo($OriginalFilename, PATHINFO_EXTENSION));
|
||||
|
||||
switch ($MimeType)
|
||||
{
|
||||
case 'text/csv':
|
||||
case 'text/comma-separated-values':
|
||||
case 'text/plain':
|
||||
$this -> Type = self::TYPE_CSV;
|
||||
break;
|
||||
case 'application/vnd.ms-excel':
|
||||
case 'application/msexcel':
|
||||
case 'application/x-msexcel':
|
||||
case 'application/x-ms-excel':
|
||||
case 'application/vnd.ms-excel':
|
||||
case 'application/x-excel':
|
||||
case 'application/x-dos_ms_excel':
|
||||
case 'application/xls':
|
||||
case 'application/xlt':
|
||||
case 'application/x-xls':
|
||||
// Excel does weird stuff
|
||||
if (in_array($Extension, array('csv', 'tsv', 'txt')))
|
||||
{
|
||||
$this -> Type = self::TYPE_CSV;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this -> Type = self::TYPE_XLS;
|
||||
}
|
||||
break;
|
||||
case 'application/vnd.oasis.opendocument.spreadsheet':
|
||||
case 'application/vnd.oasis.opendocument.spreadsheet-template':
|
||||
$this -> Type = self::TYPE_ODS;
|
||||
break;
|
||||
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
||||
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template':
|
||||
case 'application/xlsx':
|
||||
case 'application/xltx':
|
||||
$this -> Type = self::TYPE_XLSX;
|
||||
break;
|
||||
case 'application/xml':
|
||||
// Excel 2004 xml format uses this
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$this -> Type)
|
||||
{
|
||||
switch ($Extension)
|
||||
{
|
||||
case 'xlsx':
|
||||
case 'xltx': // XLSX template
|
||||
case 'xlsm': // Macro-enabled XLSX
|
||||
case 'xltm': // Macro-enabled XLSX template
|
||||
$this -> Type = self::TYPE_XLSX;
|
||||
break;
|
||||
case 'xls':
|
||||
case 'xlt':
|
||||
$this -> Type = self::TYPE_XLS;
|
||||
break;
|
||||
case 'ods':
|
||||
case 'odt':
|
||||
$this -> Type = self::TYPE_ODS;
|
||||
break;
|
||||
default:
|
||||
$this -> Type = self::TYPE_CSV;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-checking XLS files, in case they are renamed CSV or XLSX files
|
||||
if ($this -> Type == self::TYPE_XLS)
|
||||
{
|
||||
self::Load(self::TYPE_XLS);
|
||||
$this -> Handle = new SpreadsheetReader_XLS($Filepath);
|
||||
if ($this -> Handle -> Error)
|
||||
{
|
||||
$this -> Handle -> __destruct();
|
||||
|
||||
if (is_resource($ZipHandle = zip_open($Filepath)))
|
||||
{
|
||||
$this -> Type = self::TYPE_XLSX;
|
||||
zip_close($ZipHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this -> Type = self::TYPE_CSV;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Create handle
|
||||
switch ($this -> Type)
|
||||
{
|
||||
case self::TYPE_XLSX:
|
||||
self::Load(self::TYPE_XLSX);
|
||||
$this -> Handle = new SpreadsheetReader_XLSX($Filepath);
|
||||
break;
|
||||
case self::TYPE_CSV:
|
||||
self::Load(self::TYPE_CSV);
|
||||
$this -> Handle = new SpreadsheetReader_CSV($Filepath, $this -> Options);
|
||||
break;
|
||||
case self::TYPE_XLS:
|
||||
// Everything already happens above
|
||||
break;
|
||||
case self::TYPE_ODS:
|
||||
self::Load(self::TYPE_ODS);
|
||||
$this -> Handle = new SpreadsheetReader_ODS($Filepath, $this -> Options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about separate sheets in the given file
|
||||
*
|
||||
* @return array Associative array where key is sheet index and value is sheet name
|
||||
*/
|
||||
public function Sheets()
|
||||
{
|
||||
return $this -> Handle -> Sheets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the current sheet to another from the file.
|
||||
* Note that changing the sheet will rewind the file to the beginning, even if
|
||||
* the current sheet index is provided.
|
||||
*
|
||||
* @param int Sheet index
|
||||
*
|
||||
* @return bool True if sheet could be changed to the specified one,
|
||||
* false if not (for example, if incorrect index was provided.
|
||||
*/
|
||||
public function ChangeSheet($Index)
|
||||
{
|
||||
return $this -> Handle -> ChangeSheet($Index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoloads the required class for the particular spreadsheet type
|
||||
*
|
||||
* @param TYPE_* Spreadsheet type, one of TYPE_* constants of this class
|
||||
*/
|
||||
private static function Load($Type)
|
||||
{
|
||||
if (!in_array($Type, array(self::TYPE_XLSX, self::TYPE_XLS, self::TYPE_CSV, self::TYPE_ODS)))
|
||||
{
|
||||
throw new Exception('SpreadsheetReader: Invalid type ('.$Type.')');
|
||||
}
|
||||
|
||||
// 2nd parameter is to prevent autoloading for the class.
|
||||
// If autoload works, the require line is unnecessary, if it doesn't, it ends badly.
|
||||
if (!class_exists('SpreadsheetReader_'.$Type, false))
|
||||
{
|
||||
require(dirname(__FILE__).DIRECTORY_SEPARATOR.'SpreadsheetReader_'.$Type.'.php');
|
||||
}
|
||||
}
|
||||
|
||||
// !Iterator interface methods
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element.
|
||||
* Similar to the reset() function for arrays in PHP
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this -> Index = 0;
|
||||
if ($this -> Handle)
|
||||
{
|
||||
$this -> Handle -> rewind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element.
|
||||
* Similar to the current() function for arrays in PHP
|
||||
*
|
||||
* @return mixed current element from the collection
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
if ($this -> Handle)
|
||||
{
|
||||
return $this -> Handle -> current();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element.
|
||||
* Similar to the next() function for arrays in PHP
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
if ($this -> Handle)
|
||||
{
|
||||
$this -> Index++;
|
||||
|
||||
return $this -> Handle -> next();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the identifying key of the current element.
|
||||
* Similar to the key() function for arrays in PHP
|
||||
*
|
||||
* @return mixed either an integer or a string
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
if ($this -> Handle)
|
||||
{
|
||||
return $this -> Handle -> key();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is a current element after calls to rewind() or next().
|
||||
* Used to check if we've iterated to the end of the collection
|
||||
*
|
||||
* @return boolean FALSE if there's nothing more to iterate over
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
if ($this -> Handle)
|
||||
{
|
||||
return $this -> Handle -> valid();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// !Countable interface method
|
||||
public function count()
|
||||
{
|
||||
if ($this -> Handle)
|
||||
{
|
||||
return $this -> Handle -> count();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for SeekableIterator interface. Takes a posiiton and traverses the file to that position
|
||||
* The value can be retrieved with a `current()` call afterwards.
|
||||
*
|
||||
* @param int Position in file
|
||||
*/
|
||||
public function seek($Position)
|
||||
{
|
||||
if (!$this -> Handle)
|
||||
{
|
||||
throw new OutOfBoundsException('SpreadsheetReader: No file opened');
|
||||
}
|
||||
|
||||
$CurrentIndex = $this -> Handle -> key();
|
||||
|
||||
if ($CurrentIndex != $Position)
|
||||
{
|
||||
if ($Position < $CurrentIndex || is_null($CurrentIndex) || $Position == 0)
|
||||
{
|
||||
$this -> rewind();
|
||||
}
|
||||
|
||||
while ($this -> Handle -> valid() && ($Position > $this -> Handle -> key()))
|
||||
{
|
||||
$this -> Handle -> next();
|
||||
}
|
||||
|
||||
if (!$this -> Handle -> valid())
|
||||
{
|
||||
throw new OutOfBoundsException('SpreadsheetError: Position '.$Position.' not found');
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
?>
|
||||
277
include/spreadsheet-reader-master/SpreadsheetReader_CSV.php
Normal file
277
include/spreadsheet-reader-master/SpreadsheetReader_CSV.php
Normal file
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
/**
|
||||
* Class for parsing CSV files
|
||||
*
|
||||
* @author Martins Pilsetnieks
|
||||
*/
|
||||
class SpreadsheetReader_CSV implements Iterator, Countable
|
||||
{
|
||||
/**
|
||||
* @var array Options array, pre-populated with the default values.
|
||||
*/
|
||||
private $Options = array(
|
||||
'Delimiter' => ';',
|
||||
'Enclosure' => '"'
|
||||
);
|
||||
|
||||
private $Encoding = 'UTF-8';
|
||||
private $BOMLength = 0;
|
||||
|
||||
/**
|
||||
* @var resource File handle
|
||||
*/
|
||||
private $Handle = false;
|
||||
|
||||
private $Filepath = '';
|
||||
|
||||
private $Index = 0;
|
||||
|
||||
private $CurrentRow = null;
|
||||
|
||||
/**
|
||||
* @param string Path to file
|
||||
* @param array Options:
|
||||
* Enclosure => string CSV enclosure
|
||||
* Separator => string CSV separator
|
||||
*/
|
||||
public function __construct($Filepath, array $Options = null)
|
||||
{
|
||||
$this -> Filepath = $Filepath;
|
||||
|
||||
if (!is_readable($Filepath))
|
||||
{
|
||||
throw new Exception('SpreadsheetReader_CSV: File not readable ('.$Filepath.')');
|
||||
}
|
||||
|
||||
// For safety's sake
|
||||
@ini_set('auto_detect_line_endings', true);
|
||||
|
||||
$this -> Options = array_merge($this -> Options, $Options);
|
||||
$this -> Handle = fopen($Filepath, 'r');
|
||||
|
||||
// Checking the file for byte-order mark to determine encoding
|
||||
$BOM16 = bin2hex(fread($this -> Handle, 2));
|
||||
if ($BOM16 == 'fffe')
|
||||
{
|
||||
$this -> Encoding = 'UTF-16LE';
|
||||
//$this -> Encoding = 'UTF-16';
|
||||
$this -> BOMLength = 2;
|
||||
}
|
||||
elseif ($BOM16 == 'feff')
|
||||
{
|
||||
$this -> Encoding = 'UTF-16BE';
|
||||
//$this -> Encoding = 'UTF-16';
|
||||
$this -> BOMLength = 2;
|
||||
}
|
||||
|
||||
if (!$this -> BOMLength)
|
||||
{
|
||||
fseek($this -> Handle, 0);
|
||||
$BOM32 = bin2hex(fread($this -> Handle, 4));
|
||||
if ($BOM32 == '0000feff')
|
||||
{
|
||||
//$this -> Encoding = 'UTF-32BE';
|
||||
$this -> Encoding = 'UTF-32';
|
||||
$this -> BOMLength = 4;
|
||||
}
|
||||
elseif ($BOM32 == 'fffe0000')
|
||||
{
|
||||
//$this -> Encoding = 'UTF-32LE';
|
||||
$this -> Encoding = 'UTF-32';
|
||||
$this -> BOMLength = 4;
|
||||
}
|
||||
}
|
||||
|
||||
fseek($this -> Handle, 0);
|
||||
$BOM8 = bin2hex(fread($this -> Handle, 3));
|
||||
if ($BOM8 == 'efbbbf')
|
||||
{
|
||||
$this -> Encoding = 'UTF-8';
|
||||
$this -> BOMLength = 3;
|
||||
}
|
||||
|
||||
// Seeking the place right after BOM as the start of the real content
|
||||
if ($this -> BOMLength)
|
||||
{
|
||||
fseek($this -> Handle, $this -> BOMLength);
|
||||
}
|
||||
|
||||
// Checking for the delimiter if it should be determined automatically
|
||||
if (!$this -> Options['Delimiter'])
|
||||
{
|
||||
// fgetcsv needs single-byte separators
|
||||
$Semicolon = ';';
|
||||
$Tab = "\t";
|
||||
$Comma = ',';
|
||||
|
||||
// Reading the first row and checking if a specific separator character
|
||||
// has more columns than others (it means that most likely that is the delimiter).
|
||||
$SemicolonCount = count(fgetcsv($this -> Handle, null, $Semicolon));
|
||||
fseek($this -> Handle, $this -> BOMLength);
|
||||
$TabCount = count(fgetcsv($this -> Handle, null, $Tab));
|
||||
fseek($this -> Handle, $this -> BOMLength);
|
||||
$CommaCount = count(fgetcsv($this -> Handle, null, $Comma));
|
||||
fseek($this -> Handle, $this -> BOMLength);
|
||||
|
||||
$Delimiter = $Semicolon;
|
||||
if ($TabCount > $SemicolonCount || $CommaCount > $SemicolonCount)
|
||||
{
|
||||
$Delimiter = $CommaCount > $TabCount ? $Comma : $Tab;
|
||||
}
|
||||
|
||||
$this -> Options['Delimiter'] = $Delimiter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about sheets in the file.
|
||||
* Because CSV doesn't have any, it's just a single entry.
|
||||
*
|
||||
* @return array Sheet data
|
||||
*/
|
||||
public function Sheets()
|
||||
{
|
||||
return array(0 => basename($this -> Filepath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes sheet to another. Because CSV doesn't have any sheets
|
||||
* it just rewinds the file so the behaviour is compatible with other
|
||||
* sheet readers. (If an invalid index is given, it doesn't do anything.)
|
||||
*
|
||||
* @param bool Status
|
||||
*/
|
||||
public function ChangeSheet($Index)
|
||||
{
|
||||
if ($Index == 0)
|
||||
{
|
||||
$this -> rewind();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// !Iterator interface methods
|
||||
/**
|
||||
* Rewind the Iterator to the first element.
|
||||
* Similar to the reset() function for arrays in PHP
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
fseek($this -> Handle, $this -> BOMLength);
|
||||
$this -> CurrentRow = null;
|
||||
$this -> Index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element.
|
||||
* Similar to the current() function for arrays in PHP
|
||||
*
|
||||
* @return mixed current element from the collection
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
if ($this -> Index == 0 && is_null($this -> CurrentRow))
|
||||
{
|
||||
$this -> next();
|
||||
$this -> Index--;
|
||||
}
|
||||
return $this -> CurrentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element.
|
||||
* Similar to the next() function for arrays in PHP
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this -> CurrentRow = array();
|
||||
|
||||
// Finding the place the next line starts for UTF-16 encoded files
|
||||
// Line breaks could be 0x0D 0x00 0x0A 0x00 and PHP could split lines on the
|
||||
// first or the second linebreak leaving unnecessary \0 characters that mess up
|
||||
// the output.
|
||||
if ($this -> Encoding == 'UTF-16LE' || $this -> Encoding == 'UTF-16BE')
|
||||
{
|
||||
while (!feof($this -> Handle))
|
||||
{
|
||||
// While bytes are insignificant whitespace, do nothing
|
||||
$Char = ord(fgetc($this -> Handle));
|
||||
if (!$Char || $Char == 10 || $Char == 13)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When significant bytes are found, step back to the last place before them
|
||||
if ($this -> Encoding == 'UTF-16LE')
|
||||
{
|
||||
fseek($this -> Handle, ftell($this -> Handle) - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
fseek($this -> Handle, ftell($this -> Handle) - 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this -> Index++;
|
||||
$this -> CurrentRow = fgetcsv($this -> Handle, null, $this -> Options['Delimiter'], $this -> Options['Enclosure']);
|
||||
|
||||
if ($this -> CurrentRow)
|
||||
{
|
||||
// Converting multi-byte unicode strings
|
||||
// and trimming enclosure symbols off of them because those aren't recognized
|
||||
// in the relevan encodings.
|
||||
if ($this -> Encoding != 'ASCII' && $this -> Encoding != 'UTF-8')
|
||||
{
|
||||
$Encoding = $this -> Encoding;
|
||||
foreach ($this -> CurrentRow as $Key => $Value)
|
||||
{
|
||||
$this -> CurrentRow[$Key] = trim(trim(
|
||||
mb_convert_encoding($Value, 'UTF-8', $this -> Encoding),
|
||||
$this -> Options['Enclosure']
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $this -> CurrentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the identifying key of the current element.
|
||||
* Similar to the key() function for arrays in PHP
|
||||
*
|
||||
* @return mixed either an integer or a string
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this -> Index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is a current element after calls to rewind() or next().
|
||||
* Used to check if we've iterated to the end of the collection
|
||||
*
|
||||
* @return boolean FALSE if there's nothing more to iterate over
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return ($this -> CurrentRow || !feof($this -> Handle));
|
||||
}
|
||||
|
||||
// !Countable interface method
|
||||
/**
|
||||
* Ostensibly should return the count of the contained items but this just returns the number
|
||||
* of rows read so far. It's not really correct but at least coherent.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this -> Index + 1;
|
||||
}
|
||||
}
|
||||
?>
|
||||
339
include/spreadsheet-reader-master/SpreadsheetReader_ODS.php
Normal file
339
include/spreadsheet-reader-master/SpreadsheetReader_ODS.php
Normal file
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
/**
|
||||
* Class for parsing ODS files
|
||||
*
|
||||
* @author Martins Pilsetnieks
|
||||
*/
|
||||
class SpreadsheetReader_ODS implements Iterator, Countable
|
||||
{
|
||||
private $Options = array(
|
||||
'TempDir' => '',
|
||||
'ReturnDateTimeObjects' => false
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string Path to temporary content file
|
||||
*/
|
||||
private $ContentPath = '';
|
||||
/**
|
||||
* @var XMLReader XML reader object
|
||||
*/
|
||||
private $Content = false;
|
||||
|
||||
/**
|
||||
* @var array Data about separate sheets in the file
|
||||
*/
|
||||
private $Sheets = false;
|
||||
|
||||
private $CurrentRow = null;
|
||||
|
||||
/**
|
||||
* @var int Number of the sheet we're currently reading
|
||||
*/
|
||||
private $CurrentSheet = 0;
|
||||
|
||||
private $Index = 0;
|
||||
|
||||
private $TableOpen = false;
|
||||
private $RowOpen = false;
|
||||
|
||||
/**
|
||||
* @param string Path to file
|
||||
* @param array Options:
|
||||
* TempDir => string Temporary directory path
|
||||
* ReturnDateTimeObjects => bool True => dates and times will be returned as PHP DateTime objects, false => as strings
|
||||
*/
|
||||
public function __construct($Filepath, array $Options = null)
|
||||
{
|
||||
if (!is_readable($Filepath))
|
||||
{
|
||||
throw new Exception('SpreadsheetReader_ODS: File not readable ('.$Filepath.')');
|
||||
}
|
||||
|
||||
$this -> TempDir = isset($Options['TempDir']) && is_writable($Options['TempDir']) ?
|
||||
$Options['TempDir'] :
|
||||
sys_get_temp_dir();
|
||||
|
||||
$this -> TempDir = rtrim($this -> TempDir, DIRECTORY_SEPARATOR);
|
||||
$this -> TempDir = $this -> TempDir.DIRECTORY_SEPARATOR.uniqid().DIRECTORY_SEPARATOR;
|
||||
|
||||
$Zip = new ZipArchive;
|
||||
$Status = $Zip -> open($Filepath);
|
||||
|
||||
if ($Status !== true)
|
||||
{
|
||||
throw new Exception('SpreadsheetReader_ODS: File not readable ('.$Filepath.') (Error '.$Status.')');
|
||||
}
|
||||
|
||||
if ($Zip -> locateName('content.xml') !== false)
|
||||
{
|
||||
$Zip -> extractTo($this -> TempDir, 'content.xml');
|
||||
$this -> ContentPath = $this -> TempDir.'content.xml';
|
||||
}
|
||||
|
||||
$Zip -> close();
|
||||
|
||||
if ($this -> ContentPath && is_readable($this -> ContentPath))
|
||||
{
|
||||
$this -> Content = new XMLReader;
|
||||
$this -> Content -> open($this -> ContentPath);
|
||||
$this -> Valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor, destroys all that remains (closes and deletes temp files)
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this -> Content && $this -> Content instanceof XMLReader)
|
||||
{
|
||||
$this -> Content -> close();
|
||||
unset($this -> Content);
|
||||
}
|
||||
if (file_exists($this -> ContentPath))
|
||||
{
|
||||
@unlink($this -> ContentPath);
|
||||
unset($this -> ContentPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an array with information about sheets in the current file
|
||||
*
|
||||
* @return array List of sheets (key is sheet index, value is name)
|
||||
*/
|
||||
public function Sheets()
|
||||
{
|
||||
if ($this -> Sheets === false)
|
||||
{
|
||||
$this -> Sheets = array();
|
||||
|
||||
if ($this -> Valid)
|
||||
{
|
||||
$this -> SheetReader = new XMLReader;
|
||||
$this -> SheetReader -> open($this -> ContentPath);
|
||||
|
||||
while ($this -> SheetReader -> read())
|
||||
{
|
||||
if ($this -> SheetReader -> name == 'table:table')
|
||||
{
|
||||
$this -> Sheets[] = $this -> SheetReader -> getAttribute('table:name');
|
||||
$this -> SheetReader -> next();
|
||||
}
|
||||
}
|
||||
|
||||
$this -> SheetReader -> close();
|
||||
}
|
||||
}
|
||||
return $this -> Sheets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the current sheet in the file to another
|
||||
*
|
||||
* @param int Sheet index
|
||||
*
|
||||
* @return bool True if sheet was successfully changed, false otherwise.
|
||||
*/
|
||||
public function ChangeSheet($Index)
|
||||
{
|
||||
$Index = (int)$Index;
|
||||
|
||||
$Sheets = $this -> Sheets();
|
||||
if (isset($Sheets[$Index]))
|
||||
{
|
||||
$this -> CurrentSheet = $Index;
|
||||
$this -> rewind();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// !Iterator interface methods
|
||||
/**
|
||||
* Rewind the Iterator to the first element.
|
||||
* Similar to the reset() function for arrays in PHP
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
if ($this -> Index > 0)
|
||||
{
|
||||
// If the worksheet was already iterated, XML file is reopened.
|
||||
// Otherwise it should be at the beginning anyway
|
||||
$this -> Content -> close();
|
||||
$this -> Content -> open($this -> ContentPath);
|
||||
$this -> Valid = true;
|
||||
|
||||
$this -> TableOpen = false;
|
||||
$this -> RowOpen = false;
|
||||
|
||||
$this -> CurrentRow = null;
|
||||
}
|
||||
|
||||
$this -> Index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element.
|
||||
* Similar to the current() function for arrays in PHP
|
||||
*
|
||||
* @return mixed current element from the collection
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
if ($this -> Index == 0 && is_null($this -> CurrentRow))
|
||||
{
|
||||
$this -> next();
|
||||
$this -> Index--;
|
||||
}
|
||||
return $this -> CurrentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element.
|
||||
* Similar to the next() function for arrays in PHP
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this -> Index++;
|
||||
|
||||
$this -> CurrentRow = array();
|
||||
|
||||
if (!$this -> TableOpen)
|
||||
{
|
||||
$TableCounter = 0;
|
||||
$SkipRead = false;
|
||||
|
||||
while ($this -> Valid = ($SkipRead || $this -> Content -> read()))
|
||||
{
|
||||
if ($SkipRead)
|
||||
{
|
||||
$SkipRead = false;
|
||||
}
|
||||
|
||||
if ($this -> Content -> name == 'table:table' && $this -> Content -> nodeType != XMLReader::END_ELEMENT)
|
||||
{
|
||||
if ($TableCounter == $this -> CurrentSheet)
|
||||
{
|
||||
$this -> TableOpen = true;
|
||||
break;
|
||||
}
|
||||
|
||||
$TableCounter++;
|
||||
$this -> Content -> next();
|
||||
$SkipRead = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this -> TableOpen && !$this -> RowOpen)
|
||||
{
|
||||
while ($this -> Valid = $this -> Content -> read())
|
||||
{
|
||||
switch ($this -> Content -> name)
|
||||
{
|
||||
case 'table:table':
|
||||
$this -> TableOpen = false;
|
||||
$this -> Content -> next('office:document-content');
|
||||
$this -> Valid = false;
|
||||
break 2;
|
||||
case 'table:table-row':
|
||||
if ($this -> Content -> nodeType != XMLReader::END_ELEMENT)
|
||||
{
|
||||
$this -> RowOpen = true;
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this -> RowOpen)
|
||||
{
|
||||
$LastCellContent = '';
|
||||
|
||||
while ($this -> Valid = $this -> Content -> read())
|
||||
{
|
||||
switch ($this -> Content -> name)
|
||||
{
|
||||
case 'table:table-cell':
|
||||
if ($this -> Content -> nodeType == XMLReader::END_ELEMENT || $this -> Content -> isEmptyElement)
|
||||
{
|
||||
if ($this -> Content -> nodeType == XMLReader::END_ELEMENT)
|
||||
{
|
||||
$CellValue = $LastCellContent;
|
||||
}
|
||||
elseif ($this -> Content -> isEmptyElement)
|
||||
{
|
||||
$LastCellContent = '';
|
||||
$CellValue = $LastCellContent;
|
||||
}
|
||||
|
||||
$this -> CurrentRow[] = $LastCellContent;
|
||||
|
||||
if ($this -> Content -> getAttribute('table:number-columns-repeated') !== null)
|
||||
{
|
||||
$RepeatedColumnCount = $this -> Content -> getAttribute('table:number-columns-repeated');
|
||||
// Checking if larger than one because the value is already added to the row once before
|
||||
if ($RepeatedColumnCount > 1)
|
||||
{
|
||||
$this -> CurrentRow = array_pad($this -> CurrentRow, count($this -> CurrentRow) + $RepeatedColumnCount - 1, $LastCellContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$LastCellContent = '';
|
||||
}
|
||||
case 'text:p':
|
||||
if ($this -> Content -> nodeType != XMLReader::END_ELEMENT)
|
||||
{
|
||||
$LastCellContent = $this -> Content -> readString();
|
||||
}
|
||||
break;
|
||||
case 'table:table-row':
|
||||
$this -> RowOpen = false;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this -> CurrentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the identifying key of the current element.
|
||||
* Similar to the key() function for arrays in PHP
|
||||
*
|
||||
* @return mixed either an integer or a string
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this -> Index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is a current element after calls to rewind() or next().
|
||||
* Used to check if we've iterated to the end of the collection
|
||||
*
|
||||
* @return boolean FALSE if there's nothing more to iterate over
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this -> Valid;
|
||||
}
|
||||
|
||||
// !Countable interface method
|
||||
/**
|
||||
* Ostensibly should return the count of the contained items but this just returns the number
|
||||
* of rows read so far. It's not really correct but at least coherent.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this -> Index + 1;
|
||||
}
|
||||
}
|
||||
?>
|
||||
266
include/spreadsheet-reader-master/SpreadsheetReader_XLS.php
Normal file
266
include/spreadsheet-reader-master/SpreadsheetReader_XLS.php
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
/**
|
||||
* Class for parsing XLS files
|
||||
*
|
||||
* @author Martins Pilsetnieks
|
||||
*/
|
||||
class SpreadsheetReader_XLS implements Iterator, Countable
|
||||
{
|
||||
/**
|
||||
* @var array Options array, pre-populated with the default values.
|
||||
*/
|
||||
private $Options = array(
|
||||
);
|
||||
|
||||
/**
|
||||
* @var resource File handle
|
||||
*/
|
||||
private $Handle = false;
|
||||
|
||||
private $Index = 0;
|
||||
|
||||
private $Error = false;
|
||||
|
||||
/**
|
||||
* @var array Sheet information
|
||||
*/
|
||||
private $Sheets = false;
|
||||
private $SheetIndexes = array();
|
||||
|
||||
/**
|
||||
* @var int Current sheet index
|
||||
*/
|
||||
private $CurrentSheet = 0;
|
||||
|
||||
/**
|
||||
* @var array Content of the current row
|
||||
*/
|
||||
private $CurrentRow = array();
|
||||
|
||||
/**
|
||||
* @var int Column count in the sheet
|
||||
*/
|
||||
private $ColumnCount = 0;
|
||||
/**
|
||||
* @var int Row count in the sheet
|
||||
*/
|
||||
private $RowCount = 0;
|
||||
|
||||
/**
|
||||
* @var array Template to use for empty rows. Retrieved rows are merged
|
||||
* with this so that empty cells are added, too
|
||||
*/
|
||||
private $EmptyRow = array();
|
||||
|
||||
/**
|
||||
* @param string Path to file
|
||||
* @param array Options
|
||||
*/
|
||||
public function __construct($Filepath, array $Options = null)
|
||||
{
|
||||
if (!is_readable($Filepath))
|
||||
{
|
||||
throw new Exception('SpreadsheetReader_XLS: File not readable ('.$Filepath.')');
|
||||
}
|
||||
|
||||
if (!class_exists('Spreadsheet_Excel_Reader'))
|
||||
{
|
||||
throw new Exception('SpreadsheetReader_XLS: Spreadsheet_Excel_Reader class not available');
|
||||
}
|
||||
|
||||
$this -> Handle = new Spreadsheet_Excel_Reader($Filepath, false, 'UTF-8');
|
||||
|
||||
if (function_exists('mb_convert_encoding'))
|
||||
{
|
||||
$this -> Handle -> setUTFEncoder('mb');
|
||||
}
|
||||
|
||||
if (empty($this -> Handle -> sheets))
|
||||
{
|
||||
$this -> Error = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
$this -> ChangeSheet(0);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
unset($this -> Handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an array with information about sheets in the current file
|
||||
*
|
||||
* @return array List of sheets (key is sheet index, value is name)
|
||||
*/
|
||||
public function Sheets()
|
||||
{
|
||||
if ($this -> Sheets === false)
|
||||
{
|
||||
$this -> Sheets = array();
|
||||
$this -> SheetIndexes = array_keys($this -> Handle -> sheets);
|
||||
|
||||
foreach ($this -> SheetIndexes as $SheetIndex)
|
||||
{
|
||||
$this -> Sheets[] = $this -> Handle -> boundsheets[$SheetIndex]['name'];
|
||||
}
|
||||
}
|
||||
return $this -> Sheets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the current sheet in the file to another
|
||||
*
|
||||
* @param int Sheet index
|
||||
*
|
||||
* @return bool True if sheet was successfully changed, false otherwise.
|
||||
*/
|
||||
public function ChangeSheet($Index)
|
||||
{
|
||||
$Index = (int)$Index;
|
||||
$Sheets = $this -> Sheets();
|
||||
|
||||
if (isset($this -> Sheets[$Index]))
|
||||
{
|
||||
$this -> rewind();
|
||||
$this -> CurrentSheet = $this -> SheetIndexes[$Index];
|
||||
|
||||
$this -> ColumnCount = $this -> Handle -> sheets[$this -> CurrentSheet]['numCols'];
|
||||
$this -> RowCount = $this -> Handle -> sheets[$this -> CurrentSheet]['numRows'];
|
||||
|
||||
// For the case when Spreadsheet_Excel_Reader doesn't have the row count set correctly.
|
||||
if (!$this -> RowCount && count($this -> Handle -> sheets[$this -> CurrentSheet]['cells']))
|
||||
{
|
||||
end($this -> Handle -> sheets[$this -> CurrentSheet]['cells']);
|
||||
$this -> RowCount = (int)key($this -> Handle -> sheets[$this -> CurrentSheet]['cells']);
|
||||
}
|
||||
|
||||
if ($this -> ColumnCount)
|
||||
{
|
||||
$this -> EmptyRow = array_fill(1, $this -> ColumnCount, '');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this -> EmptyRow = array();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function __get($Name)
|
||||
{
|
||||
switch ($Name)
|
||||
{
|
||||
case 'Error':
|
||||
return $this -> Error;
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// !Iterator interface methods
|
||||
/**
|
||||
* Rewind the Iterator to the first element.
|
||||
* Similar to the reset() function for arrays in PHP
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this -> Index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element.
|
||||
* Similar to the current() function for arrays in PHP
|
||||
*
|
||||
* @return mixed current element from the collection
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
if ($this -> Index == 0)
|
||||
{
|
||||
$this -> next();
|
||||
}
|
||||
|
||||
return $this -> CurrentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element.
|
||||
* Similar to the next() function for arrays in PHP
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
// Internal counter is advanced here instead of the if statement
|
||||
// because apparently it's fully possible that an empty row will not be
|
||||
// present at all
|
||||
$this -> Index++;
|
||||
|
||||
if ($this -> Error)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
elseif (isset($this -> Handle -> sheets[$this -> CurrentSheet]['cells'][$this -> Index]))
|
||||
{
|
||||
$this -> CurrentRow = $this -> Handle -> sheets[$this -> CurrentSheet]['cells'][$this -> Index];
|
||||
if (!$this -> CurrentRow)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$this -> CurrentRow = $this -> CurrentRow + $this -> EmptyRow;
|
||||
ksort($this -> CurrentRow);
|
||||
|
||||
$this -> CurrentRow = array_values($this -> CurrentRow);
|
||||
return $this -> CurrentRow;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this -> CurrentRow = $this -> EmptyRow;
|
||||
return $this -> CurrentRow;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the identifying key of the current element.
|
||||
* Similar to the key() function for arrays in PHP
|
||||
*
|
||||
* @return mixed either an integer or a string
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this -> Index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is a current element after calls to rewind() or next().
|
||||
* Used to check if we've iterated to the end of the collection
|
||||
*
|
||||
* @return boolean FALSE if there's nothing more to iterate over
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
if ($this -> Error)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ($this -> Index <= $this -> RowCount);
|
||||
}
|
||||
|
||||
// !Countable interface method
|
||||
/**
|
||||
* Ostensibly should return the count of the contained items but this just returns the number
|
||||
* of rows read so far. It's not really correct but at least coherent.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
if ($this -> Error)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this -> RowCount;
|
||||
}
|
||||
}
|
||||
?>
|
||||
1211
include/spreadsheet-reader-master/SpreadsheetReader_XLSX.php
Normal file
1211
include/spreadsheet-reader-master/SpreadsheetReader_XLSX.php
Normal file
File diff suppressed because it is too large
Load Diff
1737
include/spreadsheet-reader-master/php-excel-reader/excel_reader2.php
Normal file
1737
include/spreadsheet-reader-master/php-excel-reader/excel_reader2.php
Normal file
File diff suppressed because it is too large
Load Diff
100
include/spreadsheet-reader-master/test.php
Normal file
100
include/spreadsheet-reader-master/test.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* XLS parsing uses php-excel-reader from http://code.google.com/p/php-excel-reader/
|
||||
*/
|
||||
header('Content-Type: text/plain');
|
||||
|
||||
if (isset($argv[1]))
|
||||
{
|
||||
$Filepath = $argv[1];
|
||||
}
|
||||
elseif (isset($_GET['File']))
|
||||
{
|
||||
$Filepath = $_GET['File'];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (php_sapi_name() == 'cli')
|
||||
{
|
||||
echo 'Please specify filename as the first argument'.PHP_EOL;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo 'Please specify filename as a HTTP GET parameter "File", e.g., "/test.php?File=test.xlsx"';
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// Excel reader from http://code.google.com/p/php-excel-reader/
|
||||
require('php-excel-reader/excel_reader2.php');
|
||||
require('SpreadsheetReader.php');
|
||||
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
$StartMem = memory_get_usage();
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
echo 'Starting memory: '.$StartMem.PHP_EOL;
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
|
||||
try
|
||||
{
|
||||
$Spreadsheet = new SpreadsheetReader($Filepath);
|
||||
$BaseMem = memory_get_usage();
|
||||
|
||||
$Sheets = $Spreadsheet -> Sheets();
|
||||
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
echo 'Spreadsheets:'.PHP_EOL;
|
||||
print_r($Sheets);
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
|
||||
foreach ($Sheets as $Index => $Name)
|
||||
{
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
echo '*** Sheet '.$Name.' ***'.PHP_EOL;
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
|
||||
$Time = microtime(true);
|
||||
|
||||
$Spreadsheet -> ChangeSheet($Index);
|
||||
|
||||
foreach ($Spreadsheet as $Key => $Row)
|
||||
{
|
||||
echo $Key.': ';
|
||||
if ($Row)
|
||||
{
|
||||
print_r($Row);
|
||||
}
|
||||
else
|
||||
{
|
||||
var_dump($Row);
|
||||
}
|
||||
$CurrentMem = memory_get_usage();
|
||||
|
||||
echo 'Memory: '.($CurrentMem - $BaseMem).' current, '.$CurrentMem.' base'.PHP_EOL;
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
|
||||
if ($Key && ($Key % 500 == 0))
|
||||
{
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
echo 'Time: '.(microtime(true) - $Time);
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
echo PHP_EOL.'---------------------------------'.PHP_EOL;
|
||||
echo 'Time: '.(microtime(true) - $Time);
|
||||
echo PHP_EOL;
|
||||
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
echo '*** End of sheet '.$Name.' ***'.PHP_EOL;
|
||||
echo '---------------------------------'.PHP_EOL;
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception $E)
|
||||
{
|
||||
echo $E -> getMessage();
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user