diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1bc28be --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +/Build export-ignore +/Documentation export-ignore +/Tests export-ignore +README.md export-ignore diff --git a/Classes/PHPExcel.php b/Classes/PHPExcel.php index 5c66cf4..19213c2 100644 --- a/Classes/PHPExcel.php +++ b/Classes/PHPExcel.php @@ -42,6 +42,13 @@ if (!defined('PHPEXCEL_ROOT')) { */ class PHPExcel { + /** + * Unique ID + * + * @var string + */ + private $_uniqueID; + /** * Document properties * @@ -63,6 +70,13 @@ class PHPExcel */ private $_workSheetCollection = array(); + /** + * Calculation Engine + * + * @var PHPExcel_Calculation + */ + private $_calculationEngine = NULL; + /** * Active sheet index * @@ -103,6 +117,9 @@ class PHPExcel */ public function __construct() { + $this->_uniqueID = uniqid(); + $this->_calculationEngine = PHPExcel_Calculation::getInstance($this); + // Initialise worksheet collection and add one worksheet $this->_workSheetCollection = array(); $this->_workSheetCollection[] = new PHPExcel_Worksheet($this); @@ -126,13 +143,23 @@ class PHPExcel $this->addCellStyleXf(new PHPExcel_Style); } + /** + * Code to execute when this worksheet is unset() + * + */ + public function __destruct() { + PHPExcel_Calculation::unsetInstance($this); + $this->disconnectWorksheets(); + } // function __destruct() /** * Disconnect all worksheets from this PHPExcel workbook object, * typically so that the PHPExcel object can be unset * */ - public function disconnectWorksheets() { + public function disconnectWorksheets() + { + $worksheet = NULL; foreach($this->_workSheetCollection as $k => &$worksheet) { $worksheet->disconnectCells(); $this->_workSheetCollection[$k] = null; @@ -141,6 +168,16 @@ class PHPExcel $this->_workSheetCollection = array(); } + /** + * Return the calculation engine for this worksheet + * + * @return PHPExcel_Calculation + */ + public function getCalculationEngine() + { + return $this->_calculationEngine; + } // function getCellCacheController() + /** * Get properties * @@ -840,12 +877,14 @@ class PHPExcel foreach ($sheet->getColumnDimensions() as $columnDimension) { $columnDimension->setXfIndex( $map[$columnDimension->getXfIndex()] ); } - } - // also do garbage collection for all the sheets - foreach ($this->getWorksheetIterator() as $sheet) { + // also do garbage collection for all the sheets $sheet->garbageCollect(); } } + public function getID() { + return $this->_uniqueID; + } + } diff --git a/Classes/PHPExcel/CachedObjectStorage/APC.php b/Classes/PHPExcel/CachedObjectStorage/APC.php index 8f1e931..2e61f03 100644 --- a/Classes/PHPExcel/CachedObjectStorage/APC.php +++ b/Classes/PHPExcel/CachedObjectStorage/APC.php @@ -154,8 +154,8 @@ class PHPExcel_CachedObjectStorage_APC extends PHPExcel_CachedObjectStorage_Cach // Set current entry to the requested entry $this->_currentObjectID = $pCoord; $this->_currentObject = unserialize($obj); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); + // Re-attach this as the cell's parent + $this->_currentObject->attach($this); // Return requested entry return $this->_currentObject; diff --git a/Classes/PHPExcel/CachedObjectStorage/CacheBase.php b/Classes/PHPExcel/CachedObjectStorage/CacheBase.php index 924b4b4..c4b5781 100644 --- a/Classes/PHPExcel/CachedObjectStorage/CacheBase.php +++ b/Classes/PHPExcel/CachedObjectStorage/CacheBase.php @@ -86,6 +86,11 @@ abstract class PHPExcel_CachedObjectStorage_CacheBase { } // function __construct() + public function getParent() + { + return $this->_parent; + } + /** * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? * @@ -101,6 +106,27 @@ abstract class PHPExcel_CachedObjectStorage_CacheBase { } // function isDataSet() + /** + * Move a cell object from one address to another + * + * @param string $fromAddress Current address of the cell to move + * @param string $toAddress Destination address of the cell to move + * @return boolean + */ + public function moveCell($fromAddress, $toAddress) { + if ($fromAddress === $this->_currentObjectID) { + $this->_currentObjectID = $toAddress; + } + $this->_currentCellIsDirty = true; + if (isset($this->_cellCache[$fromAddress])) { + $this->_cellCache[$toAddress] = &$this->_cellCache[$fromAddress]; + unset($this->_cellCache[$fromAddress]); + } + + return TRUE; + } // function moveCell() + + /** * Add or Update a cell in cache * @@ -151,7 +177,7 @@ abstract class PHPExcel_CachedObjectStorage_CacheBase { public function getSortedCellList() { $sortKeys = array(); foreach ($this->getCellList() as $coord) { - list($column,$row) = sscanf($coord,'%[A-Z]%d'); + sscanf($coord,'%[A-Z]%d', $column, $row); $sortKeys[sprintf('%09d%3s',$row,$column)] = $coord; } ksort($sortKeys); @@ -172,7 +198,7 @@ abstract class PHPExcel_CachedObjectStorage_CacheBase { $col = array('A' => '1A'); $row = array(1); foreach ($this->getCellList() as $coord) { - list($c,$r) = sscanf($coord,'%[A-Z]%d'); + sscanf($coord,'%[A-Z]%d', $c, $r); $row[$r] = $r; $col[$c] = strlen($c).$c; } @@ -188,6 +214,23 @@ abstract class PHPExcel_CachedObjectStorage_CacheBase { } + public function getCurrentAddress() + { + return $this->_currentObjectID; + } + + public function getCurrentColumn() + { + sscanf($this->_currentObjectID, '%[A-Z]%d', $column, $row); + return $column; + } + + public function getCurrentRow() + { + sscanf($this->_currentObjectID, '%[A-Z]%d', $column, $row); + return $row; + } + /** * Get highest worksheet column * @@ -237,7 +280,7 @@ abstract class PHPExcel_CachedObjectStorage_CacheBase { $this->_parent = $parent; if (($this->_currentObject !== NULL) && (is_object($this->_currentObject))) { - $this->_currentObject->attach($parent); + $this->_currentObject->attach($this); } } // function copyCellCollection() diff --git a/Classes/PHPExcel/CachedObjectStorage/DiscISAM.php b/Classes/PHPExcel/CachedObjectStorage/DiscISAM.php index 3fde385..956fd56 100644 --- a/Classes/PHPExcel/CachedObjectStorage/DiscISAM.php +++ b/Classes/PHPExcel/CachedObjectStorage/DiscISAM.php @@ -124,8 +124,8 @@ class PHPExcel_CachedObjectStorage_DiscISAM extends PHPExcel_CachedObjectStorage $this->_currentObjectID = $pCoord; fseek($this->_fileHandle,$this->_cellCache[$pCoord]['ptr']); $this->_currentObject = unserialize(fread($this->_fileHandle,$this->_cellCache[$pCoord]['sz'])); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); + // Re-attach this as the cell's parent + $this->_currentObject->attach($this); // Return requested entry return $this->_currentObject; diff --git a/Classes/PHPExcel/CachedObjectStorage/Igbinary.php b/Classes/PHPExcel/CachedObjectStorage/Igbinary.php index 6e33468..7e7d1df 100644 --- a/Classes/PHPExcel/CachedObjectStorage/Igbinary.php +++ b/Classes/PHPExcel/CachedObjectStorage/Igbinary.php @@ -96,8 +96,8 @@ class PHPExcel_CachedObjectStorage_Igbinary extends PHPExcel_CachedObjectStorage // Set current entry to the requested entry $this->_currentObjectID = $pCoord; $this->_currentObject = igbinary_unserialize($this->_cellCache[$pCoord]); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); + // Re-attach this as the cell's parent + $this->_currentObject->attach($this); // Return requested entry return $this->_currentObject; diff --git a/Classes/PHPExcel/CachedObjectStorage/Memcache.php b/Classes/PHPExcel/CachedObjectStorage/Memcache.php index 76ff909..a544059 100644 --- a/Classes/PHPExcel/CachedObjectStorage/Memcache.php +++ b/Classes/PHPExcel/CachedObjectStorage/Memcache.php @@ -158,8 +158,8 @@ class PHPExcel_CachedObjectStorage_Memcache extends PHPExcel_CachedObjectStorage // Set current entry to the requested entry $this->_currentObjectID = $pCoord; $this->_currentObject = unserialize($obj); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); + // Re-attach this as the cell's parent + $this->_currentObject->attach($this); // Return requested entry return $this->_currentObject; diff --git a/Classes/PHPExcel/CachedObjectStorage/Memory.php b/Classes/PHPExcel/CachedObjectStorage/Memory.php index 416b1e4..1e05b06 100644 --- a/Classes/PHPExcel/CachedObjectStorage/Memory.php +++ b/Classes/PHPExcel/CachedObjectStorage/Memory.php @@ -48,11 +48,15 @@ class PHPExcel_CachedObjectStorage_Memory extends PHPExcel_CachedObjectStorage_C * * @param string $pCoord Coordinate address of the cell to update * @param PHPExcel_Cell $cell Cell to update - * @return void + * @return PHPExcel_Cell * @throws PHPExcel_Exception */ public function addCacheData($pCoord, PHPExcel_Cell $cell) { $this->_cellCache[$pCoord] = $cell; + + // Set current entry to the new/updated entry + $this->_currentObjectID = $pCoord; + return $cell; } // function addCacheData() @@ -67,10 +71,14 @@ class PHPExcel_CachedObjectStorage_Memory extends PHPExcel_CachedObjectStorage_C public function getCacheData($pCoord) { // Check if the entry that has been requested actually exists if (!isset($this->_cellCache[$pCoord])) { + $this->_currentObjectID = NULL; // Return null if requested entry doesn't exist in cache return null; } + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + // Return requested entry return $this->_cellCache[$pCoord]; } // function getCacheData() diff --git a/Classes/PHPExcel/CachedObjectStorage/MemoryGZip.php b/Classes/PHPExcel/CachedObjectStorage/MemoryGZip.php index ecb29b8..43ed104 100644 --- a/Classes/PHPExcel/CachedObjectStorage/MemoryGZip.php +++ b/Classes/PHPExcel/CachedObjectStorage/MemoryGZip.php @@ -96,8 +96,8 @@ class PHPExcel_CachedObjectStorage_MemoryGZip extends PHPExcel_CachedObjectStora // Set current entry to the requested entry $this->_currentObjectID = $pCoord; $this->_currentObject = unserialize(gzinflate($this->_cellCache[$pCoord])); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); + // Re-attach this as the cell's parent + $this->_currentObject->attach($this); // Return requested entry return $this->_currentObject; diff --git a/Classes/PHPExcel/CachedObjectStorage/MemorySerialized.php b/Classes/PHPExcel/CachedObjectStorage/MemorySerialized.php index 3feb5fa..5e1f838 100644 --- a/Classes/PHPExcel/CachedObjectStorage/MemorySerialized.php +++ b/Classes/PHPExcel/CachedObjectStorage/MemorySerialized.php @@ -96,8 +96,8 @@ class PHPExcel_CachedObjectStorage_MemorySerialized extends PHPExcel_CachedObjec // Set current entry to the requested entry $this->_currentObjectID = $pCoord; $this->_currentObject = unserialize($this->_cellCache[$pCoord]); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); + // Re-attach this as the cell's parent + $this->_currentObject->attach($this); // Return requested entry return $this->_currentObject; diff --git a/Classes/PHPExcel/CachedObjectStorage/PHPTemp.php b/Classes/PHPExcel/CachedObjectStorage/PHPTemp.php index e6eafb7..5b13e66 100644 --- a/Classes/PHPExcel/CachedObjectStorage/PHPTemp.php +++ b/Classes/PHPExcel/CachedObjectStorage/PHPTemp.php @@ -116,8 +116,8 @@ class PHPExcel_CachedObjectStorage_PHPTemp extends PHPExcel_CachedObjectStorage_ $this->_currentObjectID = $pCoord; fseek($this->_fileHandle,$this->_cellCache[$pCoord]['ptr']); $this->_currentObject = unserialize(fread($this->_fileHandle,$this->_cellCache[$pCoord]['sz'])); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); + // Re-attach this as the cell's parent + $this->_currentObject->attach($this); // Return requested entry return $this->_currentObject; diff --git a/Classes/PHPExcel/CachedObjectStorage/SQLite.php b/Classes/PHPExcel/CachedObjectStorage/SQLite.php index 915d21d..5e82c08 100644 --- a/Classes/PHPExcel/CachedObjectStorage/SQLite.php +++ b/Classes/PHPExcel/CachedObjectStorage/SQLite.php @@ -116,8 +116,8 @@ class PHPExcel_CachedObjectStorage_SQLite extends PHPExcel_CachedObjectStorage_C $cellResult = $cellResultSet->fetchSingle(); $this->_currentObject = unserialize($cellResult); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); + // Re-attach this as the cell's parent + $this->_currentObject->attach($this); // Return requested entry return $this->_currentObject; @@ -169,6 +169,32 @@ class PHPExcel_CachedObjectStorage_SQLite extends PHPExcel_CachedObjectStorage_C } // function deleteCacheData() + /** + * Move a cell object from one address to another + * + * @param string $fromAddress Current address of the cell to move + * @param string $toAddress Destination address of the cell to move + * @return boolean + */ + public function moveCell($fromAddress, $toAddress) { + if ($fromAddress === $this->_currentObjectID) { + $this->_currentObjectID = $toAddress; + } + + $query = "DELETE FROM kvp_".$this->_TableName." WHERE id='".$toAddress."'"; + $result = $this->_DBHandle->exec($query); + if ($result === false) + throw new PHPExcel_Exception($this->_DBHandle->lastErrorMsg()); + + $query = "UPDATE kvp_".$this->_TableName." SET id='".$toAddress."' WHERE id='".$fromAddress."'"; + $result = $this->_DBHandle->exec($query); + if ($result === false) + throw new PHPExcel_Exception($this->_DBHandle->lastErrorMsg()); + + return TRUE; + } // function moveCell() + + /** * Get a list of all cell addresses currently held in cache * @@ -256,6 +282,10 @@ class PHPExcel_CachedObjectStorage_SQLite extends PHPExcel_CachedObjectStorage_C * Destroy this cell collection */ public function __destruct() { + if (!is_null($this->_DBHandle)) { + $this->_DBHandle->queryExec('DROP TABLE kvp_'.$this->_TableName); + $this->_DBHandle->close(); + } $this->_DBHandle = null; } // function __destruct() diff --git a/Classes/PHPExcel/CachedObjectStorage/SQLite3.php b/Classes/PHPExcel/CachedObjectStorage/SQLite3.php index f9ca246..ecd8029 100644 --- a/Classes/PHPExcel/CachedObjectStorage/SQLite3.php +++ b/Classes/PHPExcel/CachedObjectStorage/SQLite3.php @@ -49,6 +49,11 @@ class PHPExcel_CachedObjectStorage_SQLite3 extends PHPExcel_CachedObjectStorage_ */ private $_DBHandle = null; + private $_selectQuery; + private $_insertQuery; + private $_updateQuery; + private $_deleteQuery; + /** * Store cell data in cache for the current cell object if it's "dirty", * and the 'nullify' the current cell object @@ -60,10 +65,9 @@ class PHPExcel_CachedObjectStorage_SQLite3 extends PHPExcel_CachedObjectStorage_ if ($this->_currentCellIsDirty) { $this->_currentObject->detach(); - $query = $this->_DBHandle->prepare("INSERT OR REPLACE INTO kvp_".$this->_TableName." VALUES(:id,:data)"); - $query->bindValue('id',$this->_currentObjectID,SQLITE3_TEXT); - $query->bindValue('data',serialize($this->_currentObject),SQLITE3_BLOB); - $result = $query->execute(); + $this->_insertQuery->bindValue('id',$this->_currentObjectID,SQLITE3_TEXT); + $this->_insertQuery->bindValue('data',serialize($this->_currentObject),SQLITE3_BLOB); + $result = $this->_insertQuery->execute(); if ($result === false) throw new PHPExcel_Exception($this->_DBHandle->lastErrorMsg()); $this->_currentCellIsDirty = false; @@ -106,21 +110,23 @@ class PHPExcel_CachedObjectStorage_SQLite3 extends PHPExcel_CachedObjectStorage_ } $this->_storeData(); - $query = "SELECT value FROM kvp_".$this->_TableName." WHERE id='".$pCoord."'"; - $cellResult = $this->_DBHandle->querySingle($query); - if ($cellResult === false) { + $this->_selectQuery->bindValue('id',$pCoord,SQLITE3_TEXT); + $cellResult = $this->_selectQuery->execute(); + if ($cellResult === FALSE) { throw new PHPExcel_Exception($this->_DBHandle->lastErrorMsg()); - } elseif (is_null($cellResult)) { + } + $cellData = $cellResult->fetchArray(SQLITE3_ASSOC); + if ($cellData === FALSE) { // Return null if requested entry doesn't exist in cache - return null; + return NULL; } // Set current entry to the requested entry $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize($cellResult); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); + $this->_currentObject = unserialize($cellData['value']); + // Re-attach this as the cell's parent + $this->_currentObject->attach($this); // Return requested entry return $this->_currentObject; @@ -135,19 +141,18 @@ class PHPExcel_CachedObjectStorage_SQLite3 extends PHPExcel_CachedObjectStorage_ */ public function isDataSet($pCoord) { if ($pCoord === $this->_currentObjectID) { - return true; + return TRUE; } // Check if the requested entry exists in the cache - $query = "SELECT id FROM kvp_".$this->_TableName." WHERE id='".$pCoord."'"; - $cellResult = $this->_DBHandle->querySingle($query); - if ($cellResult === false) { + $this->_selectQuery->bindValue('id',$pCoord,SQLITE3_TEXT); + $cellResult = $this->_selectQuery->execute(); + if ($cellResult === FALSE) { throw new PHPExcel_Exception($this->_DBHandle->lastErrorMsg()); - } elseif (is_null($cellResult)) { - // Return null if requested entry doesn't exist in cache - return false; } - return true; + $cellData = $cellResult->fetchArray(SQLITE3_ASSOC); + + return ($cellData === FALSE) ? FALSE : TRUE; } // function isDataSet() @@ -160,17 +165,44 @@ class PHPExcel_CachedObjectStorage_SQLite3 extends PHPExcel_CachedObjectStorage_ public function deleteCacheData($pCoord) { if ($pCoord === $this->_currentObjectID) { $this->_currentObject->detach(); - $this->_currentObjectID = $this->_currentObject = null; + $this->_currentObjectID = $this->_currentObject = NULL; } // Check if the requested entry exists in the cache - $query = "DELETE FROM kvp_".$this->_TableName." WHERE id='".$pCoord."'"; - $result = $this->_DBHandle->exec($query); + $this->_deleteQuery->bindValue('id',$pCoord,SQLITE3_TEXT); + $result = $this->_deleteQuery->execute(); + if ($result === FALSE) + throw new PHPExcel_Exception($this->_DBHandle->lastErrorMsg()); + + $this->_currentCellIsDirty = FALSE; + } // function deleteCacheData() + + + /** + * Move a cell object from one address to another + * + * @param string $fromAddress Current address of the cell to move + * @param string $toAddress Destination address of the cell to move + * @return boolean + */ + public function moveCell($fromAddress, $toAddress) { + if ($fromAddress === $this->_currentObjectID) { + $this->_currentObjectID = $toAddress; + } + + $this->_deleteQuery->bindValue('id',$toAddress,SQLITE3_TEXT); + $result = $this->_deleteQuery->execute(); if ($result === false) throw new PHPExcel_Exception($this->_DBHandle->lastErrorMsg()); - $this->_currentCellIsDirty = false; - } // function deleteCacheData() + $this->_updateQuery->bindValue('toid',$toAddress,SQLITE3_TEXT); + $this->_updateQuery->bindValue('fromid',$fromAddress,SQLITE3_TEXT); + $result = $this->_updateQuery->execute(); + if ($result === false) + throw new PHPExcel_Exception($this->_DBHandle->lastErrorMsg()); + + return TRUE; + } // function moveCell() /** @@ -253,6 +285,11 @@ class PHPExcel_CachedObjectStorage_SQLite3 extends PHPExcel_CachedObjectStorage_ if (!$this->_DBHandle->exec('CREATE TABLE kvp_'.$this->_TableName.' (id VARCHAR(12) PRIMARY KEY, value BLOB)')) throw new PHPExcel_Exception($this->_DBHandle->lastErrorMsg()); } + + $this->_selectQuery = $this->_DBHandle->prepare("SELECT value FROM kvp_".$this->_TableName." WHERE id = :id"); + $this->_insertQuery = $this->_DBHandle->prepare("INSERT OR REPLACE INTO kvp_".$this->_TableName." VALUES(:id,:data)"); + $this->_updateQuery = $this->_DBHandle->prepare("UPDATE kvp_".$this->_TableName." SET id=:toId WHERE id=:fromId"); + $this->_deleteQuery = $this->_DBHandle->prepare("DELETE FROM kvp_".$this->_TableName." WHERE id = :id"); } // function __construct() @@ -261,6 +298,7 @@ class PHPExcel_CachedObjectStorage_SQLite3 extends PHPExcel_CachedObjectStorage_ */ public function __destruct() { if (!is_null($this->_DBHandle)) { + $this->_DBHandle->exec('DROP TABLE kvp_'.$this->_TableName); $this->_DBHandle->close(); } $this->_DBHandle = null; diff --git a/Classes/PHPExcel/CachedObjectStorage/Wincache.php b/Classes/PHPExcel/CachedObjectStorage/Wincache.php index 38d6116..97b1639 100644 --- a/Classes/PHPExcel/CachedObjectStorage/Wincache.php +++ b/Classes/PHPExcel/CachedObjectStorage/Wincache.php @@ -158,8 +158,8 @@ class PHPExcel_CachedObjectStorage_Wincache extends PHPExcel_CachedObjectStorage // Set current entry to the requested entry $this->_currentObjectID = $pCoord; $this->_currentObject = unserialize($obj); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); + // Re-attach this as the cell's parent + $this->_currentObject->attach($this); // Return requested entry return $this->_currentObject; diff --git a/Classes/PHPExcel/CalcEngine/CyclicReferenceStack.php b/Classes/PHPExcel/CalcEngine/CyclicReferenceStack.php new file mode 100644 index 0000000..e64b468 --- /dev/null +++ b/Classes/PHPExcel/CalcEngine/CyclicReferenceStack.php @@ -0,0 +1,58 @@ +_stack); + } + + public function push($value) { + $this->_stack[] = $value; + } // function push() + + public function pop() { + return array_pop($this->_stack); + } // function pop() + + public function onStack($value) { + return in_array($value,$this->_stack); + } + + public function clear() { + $this->_stack = array(); + } // function push() + + public function showStack() { + return $this->_stack; + } + +} // class PHPExcel_CalcEngine_CyclicReferenceStack diff --git a/Classes/PHPExcel/CalcEngine/Logger.php b/Classes/PHPExcel/CalcEngine/Logger.php new file mode 100644 index 0000000..250f841 --- /dev/null +++ b/Classes/PHPExcel/CalcEngine/Logger.php @@ -0,0 +1,121 @@ +_cellStack = $stack; + } + + public function setWriteDebugLog($pValue = FALSE) { + $this->_writeDebugLog = $pValue; + } + + public function getWriteDebugLog() { + return $this->_writeDebugLog; + } + + public function setEchoDebugLog($pValue = FALSE) { + $this->_echoDebugLog = $pValue; + } + + public function getEchoDebugLog() { + return $this->_echoDebugLog; + } + + public function writeDebugLog() { + // Only write the debug log if logging is enabled + if ($this->_writeDebugLog) { + $message = implode(func_get_args()); + $cellReference = implode(' -> ', $this->_cellStack->showStack()); + if ($this->_echoDebugLog) { + echo $cellReference, + ($this->_cellStack->count() > 0 ? ' => ' : ''), + $message, + PHP_EOL; + } + $this->_debugLog[] = $cellReference . + ($this->_cellStack->count() > 0 ? ' => ' : '') . + $message; + } + } // function _writeDebug() + + public function clearLog() { + $this->_debugLog = array(); + } // function flushLogger() + + public function getLog() { + return $this->_debugLog; + } // function flushLogger() + +} // class PHPExcel_CalcEngine_Logger + diff --git a/Classes/PHPExcel/Calculation.php b/Classes/PHPExcel/Calculation.php index 254a4e9..93f77d7 100644 --- a/Classes/PHPExcel/Calculation.php +++ b/Classes/PHPExcel/Calculation.php @@ -96,13 +96,29 @@ class PHPExcel_Calculation { private static $_instance; + /** + * Instance of the workbook this Calculation Engine is using + * + * @access private + * @var PHPExcel + */ + private $_workbook; + + /** + * List of instances of the calculation engine that we've instantiated + * + * @access private + * @var PHPExcel_Calculation[] + */ + private static $_workbookSets; + /** * Calculation cache * * @access private * @var array */ - private static $_calculationCache = array (); + private $_calculationCache = array (); /** @@ -111,7 +127,7 @@ class PHPExcel_Calculation { * @access private * @var boolean */ - private static $_calculationCacheEnabled = true; + private $_calculationCacheEnabled = TRUE; /** @@ -121,10 +137,10 @@ class PHPExcel_Calculation { * @access private * @var array */ - private static $_operators = array('+' => true, '-' => true, '*' => true, '/' => true, - '^' => true, '&' => true, '%' => false, '~' => false, - '>' => true, '<' => true, '=' => true, '>=' => true, - '<=' => true, '<>' => true, '|' => true, ':' => true + private static $_operators = array('+' => TRUE, '-' => TRUE, '*' => TRUE, '/' => TRUE, + '^' => TRUE, '&' => TRUE, '%' => FALSE, '~' => FALSE, + '>' => TRUE, '<' => TRUE, '=' => TRUE, '>=' => TRUE, + '<=' => TRUE, '<>' => TRUE, '|' => TRUE, ':' => TRUE ); @@ -134,12 +150,21 @@ class PHPExcel_Calculation { * @access private * @var array */ - private static $_binaryOperators = array('+' => true, '-' => true, '*' => true, '/' => true, - '^' => true, '&' => true, '>' => true, '<' => true, - '=' => true, '>=' => true, '<=' => true, '<>' => true, - '|' => true, ':' => true + private static $_binaryOperators = array('+' => TRUE, '-' => TRUE, '*' => TRUE, '/' => TRUE, + '^' => TRUE, '&' => TRUE, '>' => TRUE, '<' => TRUE, + '=' => TRUE, '>=' => TRUE, '<=' => TRUE, '<>' => TRUE, + '|' => TRUE, ':' => TRUE ); + /** + * The debug log generated by the calculation engine + * + * @access private + * @var PHPExcel_CalcEngine_Logger + * + */ + private $debugLog; + /** * Flag to determine how formula errors should be handled * If true, then a user error will be triggered @@ -149,7 +174,7 @@ class PHPExcel_Calculation { * @var boolean * */ - public $suppressFormulaErrors = false; + public $suppressFormulaErrors = FALSE; /** * Error message for any error that was raised/thrown by the calculation engine @@ -158,31 +183,7 @@ class PHPExcel_Calculation { * @var string * */ - public $formulaError = null; - - /** - * Flag to determine whether a debug log should be generated by the calculation engine - * If true, then a debug log will be generated - * If false, then a debug log will not be generated - * - * @access public - * @var boolean - * - */ - public $writeDebugLog = false; - - /** - * Flag to determine whether a debug log should be echoed by the calculation engine - * If true, then a debug log will be echoed - * If false, then a debug log will not be echoed - * A debug log can only be echoed if it is generated - * - * @access public - * @var boolean - * - */ - public $echoDebugLog = false; - + public $formulaError = NULL; /** * An array of the nested cell references accessed by the calculation engine, used for the debug log @@ -191,22 +192,14 @@ class PHPExcel_Calculation { * @var array of string * */ - private $debugLogStack = array(); + private $_cyclicReferenceStack; - /** - * The debug log generated by the calculation engine - * - * @access public - * @var array of string - * - */ - public $debugLog = array(); private $_cyclicFormulaCount = 0; private $_cyclicFormulaCell = ''; public $cyclicFormulaCount = 0; - private $_savedPrecision = 12; + private $_savedPrecision = 14; private static $_localeLanguage = 'en_us'; // US English (default locale) @@ -221,9 +214,9 @@ class PHPExcel_Calculation { // Constant conversion from text name/value to actual (datatyped) value - private static $_ExcelConstants = array('TRUE' => true, - 'FALSE' => false, - 'NULL' => null + private static $_ExcelConstants = array('TRUE' => TRUE, + 'FALSE' => FALSE, + 'NULL' => NULL ); // PHPExcel functions @@ -395,7 +388,7 @@ class PHPExcel_Calculation { 'COLUMN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => 'PHPExcel_Calculation_LookupRef::COLUMN', 'argumentCount' => '-1', - 'passByReference' => array(true) + 'passByReference' => array(TRUE) ), 'COLUMNS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => 'PHPExcel_Calculation_LookupRef::COLUMNS', @@ -804,7 +797,7 @@ class PHPExcel_Calculation { 'HYPERLINK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => 'PHPExcel_Calculation_LookupRef::HYPERLINK', 'argumentCount' => '1,2', - 'passCellReference'=> true + 'passCellReference'=> TRUE ), 'HYPGEOMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL, 'functionCall' => 'PHPExcel_Calculation_Statistical::HYPGEOMDIST', @@ -893,7 +886,7 @@ class PHPExcel_Calculation { 'INDIRECT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => 'PHPExcel_Calculation_LookupRef::INDIRECT', 'argumentCount' => '1,2', - 'passCellReference'=> true + 'passCellReference'=> TRUE ), 'INFO' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION, 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY', @@ -1210,8 +1203,8 @@ class PHPExcel_Calculation { 'OFFSET' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => 'PHPExcel_Calculation_LookupRef::OFFSET', 'argumentCount' => '3,5', - 'passCellReference'=> true, - 'passByReference' => array(true) + 'passCellReference'=> TRUE, + 'passByReference' => array(TRUE) ), 'OR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL, 'functionCall' => 'PHPExcel_Calculation_Logical::LOGICAL_OR', @@ -1356,7 +1349,7 @@ class PHPExcel_Calculation { 'ROW' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => 'PHPExcel_Calculation_LookupRef::ROW', 'argumentCount' => '-1', - 'passByReference' => array(true) + 'passByReference' => array(TRUE) ), 'ROWS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => 'PHPExcel_Calculation_LookupRef::ROWS', @@ -1671,12 +1664,20 @@ class PHPExcel_Calculation { - private function __construct() { - $setPrecision = (PHP_INT_SIZE == 4) ? 12 : 16; + private function __construct(PHPExcel $workbook = NULL) { + $setPrecision = (PHP_INT_SIZE == 4) ? 14 : 16; $this->_savedPrecision = ini_get('precision'); if ($this->_savedPrecision < $setPrecision) { ini_set('precision',$setPrecision); } + + if ($workbook !== NULL) { + self::$_workbookSets[$workbook->getID()] = $this; + } + + $this->_workbook = $workbook; + $this->_cyclicReferenceStack = new PHPExcel_CalcEngine_CyclicReferenceStack(); + $this->_debugLog = new PHPExcel_CalcEngine_Logger($this->_cyclicReferenceStack); } // function __construct() @@ -1702,7 +1703,14 @@ class PHPExcel_Calculation { * @access public * @return PHPExcel_Calculation */ - public static function getInstance() { + public static function getInstance(PHPExcel $workbook = NULL) { + if ($workbook !== NULL) { + if (isset(self::$_workbookSets[$workbook->getID()])) { + return self::$_workbookSets[$workbook->getID()]; + } + return new PHPExcel_Calculation($workbook); + } + if (!isset(self::$_instance) || (self::$_instance === NULL)) { self::$_instance = new PHPExcel_Calculation(); } @@ -1710,6 +1718,13 @@ class PHPExcel_Calculation { return self::$_instance; } // function getInstance() + public static function unsetInstance(PHPExcel $workbook = NULL) { + if ($workbook !== NULL) { + if (isset(self::$_workbookSets[$workbook->getID()])) { + unset(self::$_workbookSets[$workbook->getID()]); + } + } + } /** * Flush the calculation cache for any existing instance of this class @@ -1718,13 +1733,15 @@ class PHPExcel_Calculation { * @access public * @return null */ - public static function flushInstance() { - if (isset(self::$_instance) && (self::$_instance !== NULL)) { - self::$_instance->clearCalculationCache(); - } + public function flushInstance() { + $this->clearCalculationCache(); } // function flushInstance() + public function getDebugLog() { + return $this->_debugLog; + } + /** * __clone implementation. Cloning should not be allowed in a Singleton! * @@ -1732,7 +1749,7 @@ class PHPExcel_Calculation { * @throws PHPExcel_Calculation_Exception */ public final function __clone() { - throw new PHPExcel_Calculation_Exception ('Cloning a Singleton is not allowed!'); + throw new PHPExcel_Calculation_Exception ('Cloning the calculation engine is not allowed!'); } // function __clone() @@ -1768,10 +1785,10 @@ class PHPExcel_Calculation { ($returnType == self::RETURN_ARRAY_AS_ERROR) || ($returnType == self::RETURN_ARRAY_AS_ARRAY)) { self::$returnArrayAsType = $returnType; - return true; + return TRUE; } - return false; - } // function setExcelCalendar() + return FALSE; + } // function setArrayReturnType() /** @@ -1782,7 +1799,7 @@ class PHPExcel_Calculation { */ public static function getArrayReturnType() { return self::$returnArrayAsType; - } // function getExcelCalendar() + } // function getArrayReturnType() /** @@ -1792,18 +1809,17 @@ class PHPExcel_Calculation { * @return boolean */ public function getCalculationCacheEnabled() { - return self::$_calculationCacheEnabled; + return $this->_calculationCacheEnabled; } // function getCalculationCacheEnabled() - /** * Enable/disable calculation cache * * @access public * @param boolean $pValue */ - public function setCalculationCacheEnabled($pValue = true) { - self::$_calculationCacheEnabled = $pValue; + public function setCalculationCacheEnabled($pValue = TRUE) { + $this->_calculationCacheEnabled = $pValue; $this->clearCalculationCache(); } // function setCalculationCacheEnabled() @@ -1812,7 +1828,7 @@ class PHPExcel_Calculation { * Enable calculation cache */ public function enableCalculationCache() { - $this->setCalculationCacheEnabled(true); + $this->setCalculationCacheEnabled(TRUE); } // function enableCalculationCache() @@ -1820,7 +1836,7 @@ class PHPExcel_Calculation { * Disable calculation cache */ public function disableCalculationCache() { - $this->setCalculationCacheEnabled(false); + $this->setCalculationCacheEnabled(FALSE); } // function disableCalculationCache() @@ -1828,9 +1844,28 @@ class PHPExcel_Calculation { * Clear calculation cache */ public function clearCalculationCache() { - self::$_calculationCache = array(); + $this->_calculationCache = array(); } // function clearCalculationCache() + /** + * Clear calculation cache for a specified worksheet + */ + public function clearCalculationCacheForWorksheet($worksheetName) { + if (isset($this->_calculationCache[$worksheetName])) { + unset($this->_calculationCache[$worksheetName]); + } + } // function clearCalculationCacheForWorksheet() + + /** + * Rename calculation cache for a specified worksheet + */ + public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName) { + if (isset($this->_calculationCache[$fromWorksheetName])) { + $this->_calculationCache[$toWorksheetName] = &$this->_calculationCache[$fromWorksheetName]; + unset($this->_calculationCache[$fromWorksheetName]); + } + } // function renameCalculationCacheForWorksheet() + /** * Get the currently defined locale code @@ -1850,7 +1885,7 @@ class PHPExcel_Calculation { public function setLocale($locale='en_us') { // Identify our locale and language $language = $locale = strtolower($locale); - if (strpos($locale,'_') !== false) { + if (strpos($locale,'_') !== FALSE) { list($language) = explode('_',$locale); } @@ -1871,14 +1906,14 @@ class PHPExcel_Calculation { // If there isn't a locale specific function file, look for a language specific function file $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'functions'; if (!file_exists($functionNamesFile)) { - return false; + return FALSE; } } // Retrieve the list of locale or language specific function names $localeFunctions = file($functionNamesFile,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($localeFunctions as $localeFunction) { list($localeFunction) = explode('##',$localeFunction); // Strip out comments - if (strpos($localeFunction,'=') !== false) { + if (strpos($localeFunction,'=') !== FALSE) { list($fName,$lfName) = explode('=',$localeFunction); $fName = trim($fName); $lfName = trim($lfName); @@ -1899,7 +1934,7 @@ class PHPExcel_Calculation { $localeSettings = file($configFile,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($localeSettings as $localeSetting) { list($localeSetting) = explode('##',$localeSetting); // Strip out comments - if (strpos($localeSetting,'=') !== false) { + if (strpos($localeSetting,'=') !== FALSE) { list($settingName,$settingValue) = explode('=',$localeSetting); $settingName = strtoupper(trim($settingName)); switch ($settingName) { @@ -1913,11 +1948,11 @@ class PHPExcel_Calculation { } self::$functionReplaceFromExcel = self::$functionReplaceToExcel = - self::$functionReplaceFromLocale = self::$functionReplaceToLocale = null; + self::$functionReplaceFromLocale = self::$functionReplaceToLocale = NULL; self::$_localeLanguage = $locale; - return true; + return TRUE; } - return false; + return FALSE; } // function setLocale() @@ -1927,9 +1962,9 @@ class PHPExcel_Calculation { for ($i = 0; $i < $strlen; ++$i) { $chr = mb_substr($formula,$i,1); switch ($chr) { - case '{' : $inBraces = true; + case '{' : $inBraces = TRUE; break; - case '}' : $inBraces = false; + case '}' : $inBraces = FALSE; break; case $fromSeparator : if (!$inBraces) { @@ -1943,13 +1978,13 @@ class PHPExcel_Calculation { private static function _translateFormula($from,$to,$formula,$fromSeparator,$toSeparator) { // Convert any Excel function names to the required language if (self::$_localeLanguage !== 'en_us') { - $inBraces = false; + $inBraces = FALSE; // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators - if (strpos($formula,'"') !== false) { + if (strpos($formula,'"') !== FALSE) { // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded // the formula $temp = explode('"',$formula); - $i = false; + $i = FALSE; foreach($temp as &$value) { // Only count/replace in alternating array entries if ($i = !$i) { @@ -1970,8 +2005,8 @@ class PHPExcel_Calculation { return $formula; } - private static $functionReplaceFromExcel = null; - private static $functionReplaceToLocale = null; + private static $functionReplaceFromExcel = NULL; + private static $functionReplaceToLocale = NULL; public function _translateFormulaToLocale($formula) { if (self::$functionReplaceFromExcel === NULL) { @@ -1999,8 +2034,8 @@ class PHPExcel_Calculation { } // function _translateFormulaToLocale() - private static $functionReplaceFromLocale = null; - private static $functionReplaceToExcel = null; + private static $functionReplaceFromLocale = NULL; + private static $functionReplaceToExcel = NULL; public function _translateFormulaToEnglish($formula) { if (self::$functionReplaceFromLocale === NULL) { @@ -2096,7 +2131,7 @@ class PHPExcel_Calculation { * @return mixed * @throws PHPExcel_Calculation_Exception */ - public function calculate(PHPExcel_Cell $pCell = null) { + public function calculate(PHPExcel_Cell $pCell = NULL) { try { return $this->calculateCellValue($pCell); } catch (PHPExcel_Exception $e) { @@ -2114,25 +2149,22 @@ class PHPExcel_Calculation { * @return mixed * @throws PHPExcel_Calculation_Exception */ - public function calculateCellValue(PHPExcel_Cell $pCell = null, $resetLog = true) { - if ($resetLog) { - // Initialise the logging settings if requested - $this->formulaError = null; - $this->debugLog = $this->debugLogStack = array(); - $this->_cyclicFormulaCount = 1; - - $returnArrayAsType = self::$returnArrayAsType; - self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY; - } - - // Read the formula from the cell + public function calculateCellValue(PHPExcel_Cell $pCell = NULL, $resetLog = TRUE) { if ($pCell === NULL) { return NULL; } + $returnArrayAsType = self::$returnArrayAsType; if ($resetLog) { - self::$returnArrayAsType = $returnArrayAsType; + // Initialise the logging settings if requested + $this->formulaError = null; + $this->_debugLog->clearLog(); + $this->_cyclicReferenceStack->clear(); + $this->_cyclicFormulaCount = 1; + + self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY; } + // Execute the calculation for the cell formula try { $result = self::_unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell)); @@ -2141,6 +2173,7 @@ class PHPExcel_Calculation { } if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) { + self::$returnArrayAsType = $returnArrayAsType; $testResult = PHPExcel_Calculation_Functions::flattenArray($result); if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) { return PHPExcel_Calculation_Functions::VALUE(); @@ -2161,6 +2194,8 @@ class PHPExcel_Calculation { } $result = array_shift($testResult); } + self::$returnArrayAsType = $returnArrayAsType; + if ($result === NULL) { return 0; @@ -2198,15 +2233,16 @@ class PHPExcel_Calculation { * @return mixed * @throws PHPExcel_Calculation_Exception */ - public function calculateFormula($formula, $cellID=null, PHPExcel_Cell $pCell = null) { + public function calculateFormula($formula, $cellID=NULL, PHPExcel_Cell $pCell = NULL) { // Initialise the logging settings $this->formulaError = null; - $this->debugLog = $this->debugLogStack = array(); + $this->_debugLog->clearLog(); + $this->_cyclicReferenceStack->clear(); // Disable calculation cacheing because it only applies to cell calculations, not straight formulae // But don't actually flush any cache $resetCache = $this->getCalculationCacheEnabled(); - self::$_calculationCacheEnabled = false; + $this->_calculationCacheEnabled = FALSE; // Execute the calculation try { $result = self::_unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell)); @@ -2215,12 +2251,33 @@ class PHPExcel_Calculation { } // Reset calculation cacheing to its previous state - self::$_calculationCacheEnabled = $resetCache; + $this->_calculationCacheEnabled = $resetCache; return $result; } // function calculateFormula() + public function getValueFromCache($worksheetName, $cellID, &$cellValue) { + // Is calculation cacheing enabled? + // Is the value present in calculation cache? +//echo 'Test cache for ',$worksheetName,'!',$cellID,PHP_EOL; + $this->_debugLog->writeDebugLog('Testing cache value for cell ', $worksheetName, '!', $cellID); + if (($this->_calculationCacheEnabled) && (isset($this->_calculationCache[$worksheetName][$cellID]))) { +//echo 'Retrieve from cache',PHP_EOL; + $this->_debugLog->writeDebugLog('Retrieving value for cell ', $worksheetName, '!', $cellID, ' from cache'); + // Return the cached result + $cellValue = $this->_calculationCache[$worksheetName][$cellID]; + return TRUE; + } + return FALSE; + } + + public function saveValueToCache($worksheetName, $cellID, $cellValue) { + if ($this->_calculationCacheEnabled) { + $this->_calculationCache[$worksheetName][$cellID] = $cellValue; + } + } + /** * Parse a cell formula and calculate its value * @@ -2231,45 +2288,23 @@ class PHPExcel_Calculation { * @throws PHPExcel_Calculation_Exception */ public function _calculateFormulaValue($formula, $cellID=null, PHPExcel_Cell $pCell = null) { -// echo ''.$cellID.'
'; $cellValue = ''; // Basic validation that this is indeed a formula - // We simply return the "cell value" (formula) if not + // We simply return the cell value if not $formula = trim($formula); if ($formula{0} != '=') return self::_wrapResult($formula); $formula = ltrim(substr($formula,1)); if (!isset($formula{0})) return self::_wrapResult($formula); - $wsTitle = "\x00Wrk"; - if ($pCell !== NULL) { - $pCellParent = $pCell->getParent(); - if ($pCellParent !== NULL) { - $wsTitle = $pCellParent->getTitle(); - } - } - // Is calculation cacheing enabled? - if ($cellID !== NULL) { - if (self::$_calculationCacheEnabled) { - // Is the value present in calculation cache? - $this->_writeDebug('Testing cache value for cell ', $cellID); -// echo 'Testing cache value
'; - if (isset(self::$_calculationCache[$wsTitle][$cellID])) { -// echo 'Value is in cache
'; - $this->_writeDebug('Retrieving value for ', $cellID, ' from cache'); - // Return the cached result - $returnValue = self::$_calculationCache[$wsTitle][$cellID]; -// echo 'Retrieving data value of '.$returnValue.' for '.$cellID.' from cache
'; - if (is_array($returnValue)) { - $returnValue = PHPExcel_Calculation_Functions::flattenArray($returnValue); - return array_shift($returnValue); - } - return $returnValue; - } - } + $pCellParent = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL; + $wsTitle = ($pCellParent !== NULL) ? $pCellParent->getTitle() : "\x00Wrk"; + + if (($cellID !== NULL) && ($this->getValueFromCache($wsTitle, $cellID, $cellValue))) { + return $cellValue; } - if ((in_array($wsTitle.'!'.$cellID,$this->debugLogStack)) && ($wsTitle != "\x00Wrk")) { + if (($wsTitle{0} !== "\x00") && ($this->_cyclicReferenceStack->onStack($wsTitle.'!'.$cellID))) { if ($this->cyclicFormulaCount <= 0) { return $this->_raiseFormulaError('Cyclic Reference in Formula'); } elseif (($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) && @@ -2287,16 +2322,15 @@ class PHPExcel_Calculation { } } } - $this->debugLogStack[] = $wsTitle.'!'.$cellID; + // Parse the formula onto the token stack and calculate the value + $this->_cyclicReferenceStack->push($wsTitle.'!'.$cellID); $cellValue = $this->_processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell); - array_pop($this->debugLogStack); + $this->_cyclicReferenceStack->pop(); // Save to calculation cache if ($cellID !== NULL) { - if (self::$_calculationCacheEnabled) { - self::$_calculationCache[$wsTitle][$cellID] = $cellValue; - } + $this->saveValueToCache($wsTitle, $cellID, $cellValue); } // Return the calculated value @@ -2457,7 +2491,7 @@ class PHPExcel_Calculation { * @return mixed */ private function _showValue($value) { - if ($this->writeDebugLog) { + if ($this->_debugLog->getWriteDebugLog()) { $testArray = PHPExcel_Calculation_Functions::flattenArray($value); if (count($testArray) == 1) { $value = array_pop($testArray); @@ -2492,7 +2526,7 @@ class PHPExcel_Calculation { * @return mixed */ private function _showTypeDetails($value) { - if ($this->writeDebugLog) { + if ($this->_debugLog->getWriteDebugLog()) { $testArray = PHPExcel_Calculation_Functions::flattenArray($value); if (count($testArray) == 1) { $value = array_pop($testArray); @@ -2527,15 +2561,15 @@ class PHPExcel_Calculation { static $matrixReplaceTo = array('MKMATRIX(MKMATRIX(','),MKMATRIX(','))'); // Convert any Excel matrix references to the MKMATRIX() function - if (strpos($formula,'{') !== false) { + if (strpos($formula,'{') !== FALSE) { // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators - if (strpos($formula,'"') !== false) { + if (strpos($formula,'"') !== FALSE) { // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded // the formula $temp = explode('"',$formula); // Open and Closed counts used for trapping mismatched braces in the formula $openCount = $closeCount = 0; - $i = false; + $i = FALSE; foreach($temp as &$value) { // Only count/replace in alternating array entries if ($i = !$i) { @@ -2578,43 +2612,46 @@ class PHPExcel_Calculation { } // function _mkMatrix() + // Binary Operators + // These operators always work on two values + // Array key is the operator, the value indicates whether this is a left or right associative operator + private static $_operatorAssociativity = array( + '^' => 0, // Exponentiation + '*' => 0, '/' => 0, // Multiplication and Division + '+' => 0, '-' => 0, // Addition and Subtraction + '&' => 0, // Concatenation + '|' => 0, ':' => 0, // Intersect and Range + '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0 // Comparison + ); + + // Comparison (Boolean) Operators + // These operators work on two values, but always return a boolean result + private static $_comparisonOperators = array('>' => TRUE, '<' => TRUE, '=' => TRUE, '>=' => TRUE, '<=' => TRUE, '<>' => TRUE); + + // Operator Precedence + // This list includes all valid operators, whether binary (including boolean) or unary (such as %) + // Array key is the operator, the value is its precedence + private static $_operatorPrecedence = array( + ':' => 8, // Range + '|' => 7, // Intersect + '~' => 6, // Negation + '%' => 5, // Percentage + '^' => 4, // Exponentiation + '*' => 3, '/' => 3, // Multiplication and Division + '+' => 2, '-' => 2, // Addition and Subtraction + '&' => 1, // Concatenation + '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0 // Comparison + ); + // Convert infix to postfix notation - private function _parseFormula($formula, PHPExcel_Cell $pCell = null) { - if (($formula = self::_convertMatrixReferences(trim($formula))) === false) { + private function _parseFormula($formula, PHPExcel_Cell $pCell = NULL) { + if (($formula = self::_convertMatrixReferences(trim($formula))) === FALSE) { return FALSE; } // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet), // so we store the parent worksheet so that we can re-attach it when necessary - $pCellParent = ($pCell !== NULL) ? $pCell->getParent() : NULL; - - // Binary Operators - // These operators always work on two values - // Array key is the operator, the value indicates whether this is a left or right associative operator - $operatorAssociativity = array('^' => 0, // Exponentiation - '*' => 0, '/' => 0, // Multiplication and Division - '+' => 0, '-' => 0, // Addition and Subtraction - '&' => 0, // Concatenation - '|' => 0, ':' => 0, // Intersect and Range - '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0 // Comparison - ); - // Comparison (Boolean) Operators - // These operators work on two values, but always return a boolean result - $comparisonOperators = array('>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true); - - // Operator Precedence - // This list includes all valid operators, whether binary (including boolean) or unary (such as %) - // Array key is the operator, the value is its precedence - $operatorPrecedence = array(':' => 8, // Range - '|' => 7, // Intersect - '~' => 6, // Negation - '%' => 5, // Percentage - '^' => 4, // Exponentiation - '*' => 3, '/' => 3, // Multiplication and Division - '+' => 2, '-' => 2, // Addition and Subtraction - '&' => 1, // Concatenation - '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0 // Comparison - ); + $pCellParent = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL; $regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION. '|'.self::CALCULATION_REGEXP_NUMBER. @@ -2629,55 +2666,55 @@ class PHPExcel_Calculation { $index = 0; $stack = new PHPExcel_Calculation_Token_Stack; $output = array(); - $expectingOperator = false; // We use this test in syntax-checking the expression to determine when a + $expectingOperator = FALSE; // We use this test in syntax-checking the expression to determine when a // - is a negation or + is a positive operator rather than an operation - $expectingOperand = false; // We use this test in syntax-checking the expression to determine whether an operand + $expectingOperand = FALSE; // We use this test in syntax-checking the expression to determine whether an operand // should be null in a function call // The guts of the lexical parser // Loop through the formula extracting each operator and operand in turn - while(true) { -// echo 'Assessing Expression '.substr($formula, $index).'
'; + while(TRUE) { +//echo 'Assessing Expression '.substr($formula, $index),PHP_EOL; $opCharacter = $formula{$index}; // Get the first character of the value at the current index position -// echo 'Initial character of expression block is '.$opCharacter.'
'; - if ((isset($comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset($comparisonOperators[$formula{$index+1}]))) { +//echo 'Initial character of expression block is '.$opCharacter,PHP_EOL; + if ((isset(self::$_comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$_comparisonOperators[$formula{$index+1}]))) { $opCharacter .= $formula{++$index}; -// echo 'Initial character of expression block is comparison operator '.$opCharacter.'
'; +//echo 'Initial character of expression block is comparison operator '.$opCharacter.PHP_EOL; } // Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match); -// echo '$isOperandOrFunction is '.(($isOperandOrFunction) ? 'True' : 'False').'
'; -// var_dump($match); +//echo '$isOperandOrFunction is '.(($isOperandOrFunction) ? 'True' : 'False').PHP_EOL; +//var_dump($match); if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus? -// echo 'Element is a Negation operator
'; +//echo 'Element is a Negation operator',PHP_EOL; $stack->push('Unary Operator','~'); // Put a negation on the stack ++$index; // and drop the negation symbol } elseif ($opCharacter == '%' && $expectingOperator) { -// echo 'Element is a Percentage operator
'; +//echo 'Element is a Percentage operator',PHP_EOL; $stack->push('Unary Operator','%'); // Put a percentage on the stack ++$index; } elseif ($opCharacter == '+' && !$expectingOperator) { // Positive (unary plus rather than binary operator plus) can be discarded? -// echo 'Element is a Positive number, not Plus operator
'; +//echo 'Element is a Positive number, not Plus operator',PHP_EOL; ++$index; // Drop the redundant plus symbol } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) { // We have to explicitly deny a tilde or pipe, because they are legal return $this->_raiseFormulaError("Formula Error: Illegal character '~'"); // on the stack but not in the input expression } elseif ((isset(self::$_operators[$opCharacter]) or $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack? -// echo 'Element with value '.$opCharacter.' is an Operator
'; +//echo 'Element with value '.$opCharacter.' is an Operator',PHP_EOL; while($stack->count() > 0 && ($o2 = $stack->last()) && isset(self::$_operators[$o2['value']]) && - @($operatorAssociativity[$opCharacter] ? $operatorPrecedence[$opCharacter] < $operatorPrecedence[$o2['value']] : $operatorPrecedence[$opCharacter] <= $operatorPrecedence[$o2['value']])) { + @(self::$_operatorAssociativity[$opCharacter] ? self::$_operatorPrecedence[$opCharacter] < self::$_operatorPrecedence[$o2['value']] : self::$_operatorPrecedence[$opCharacter] <= self::$_operatorPrecedence[$o2['value']])) { $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output } $stack->push('Binary Operator',$opCharacter); // Finally put our current operator onto the stack ++$index; - $expectingOperator = false; + $expectingOperator = FALSE; } elseif ($opCharacter == ')' && $expectingOperator) { // Are we expecting to close a parenthesis? -// echo 'Element is a Closing bracket
'; - $expectingOperand = false; +//echo 'Element is a Closing bracket',PHP_EOL; + $expectingOperand = FALSE; while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last ( if ($o2 === NULL) return $this->_raiseFormulaError('Formula Error: Unexpected closing brace ")"'); else $output[] = $o2; @@ -2685,65 +2722,65 @@ class PHPExcel_Calculation { $d = $stack->last(2); if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) { // Did this parenthesis just close a function? $functionName = $matches[1]; // Get the function name -// echo 'Closed Function is '.$functionName.'
'; +//echo 'Closed Function is '.$functionName,PHP_EOL; $d = $stack->pop(); $argumentCount = $d['value']; // See how many arguments there were (argument count is the next value stored on the stack) -// if ($argumentCount == 0) { -// echo 'With no arguments
'; -// } elseif ($argumentCount == 1) { -// echo 'With 1 argument
'; -// } else { -// echo 'With '.$argumentCount.' arguments
'; -// } +//if ($argumentCount == 0) { +// echo 'With no arguments',PHP_EOL; +//} elseif ($argumentCount == 1) { +// echo 'With 1 argument',PHP_EOL; +//} else { +// echo 'With '.$argumentCount.' arguments',PHP_EOL; +//} $output[] = $d; // Dump the argument count on the output $output[] = $stack->pop(); // Pop the function and push onto the output if (isset(self::$_controlFunctions[$functionName])) { -// echo 'Built-in function '.$functionName.'
'; +//echo 'Built-in function '.$functionName,PHP_EOL; $expectedArgumentCount = self::$_controlFunctions[$functionName]['argumentCount']; $functionCall = self::$_controlFunctions[$functionName]['functionCall']; } elseif (isset(self::$_PHPExcelFunctions[$functionName])) { -// echo 'PHPExcel function '.$functionName.'
'; +//echo 'PHPExcel function '.$functionName,PHP_EOL; $expectedArgumentCount = self::$_PHPExcelFunctions[$functionName]['argumentCount']; $functionCall = self::$_PHPExcelFunctions[$functionName]['functionCall']; } else { // did we somehow push a non-function on the stack? this should never happen return $this->_raiseFormulaError("Formula Error: Internal error, non-function on stack"); } // Check the argument count - $argumentCountError = false; + $argumentCountError = FALSE; if (is_numeric($expectedArgumentCount)) { if ($expectedArgumentCount < 0) { -// echo '$expectedArgumentCount is between 0 and '.abs($expectedArgumentCount).'
'; +//echo '$expectedArgumentCount is between 0 and '.abs($expectedArgumentCount),PHP_EOL; if ($argumentCount > abs($expectedArgumentCount)) { - $argumentCountError = true; + $argumentCountError = TRUE; $expectedArgumentCountString = 'no more than '.abs($expectedArgumentCount); } } else { -// echo '$expectedArgumentCount is numeric '.$expectedArgumentCount.'
'; +//echo '$expectedArgumentCount is numeric '.$expectedArgumentCount,PHP_EOL; if ($argumentCount != $expectedArgumentCount) { - $argumentCountError = true; + $argumentCountError = TRUE; $expectedArgumentCountString = $expectedArgumentCount; } } } elseif ($expectedArgumentCount != '*') { $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/',$expectedArgumentCount,$argMatch); -// print_r($argMatch); -// echo '
'; +//print_r($argMatch); +//echo PHP_EOL; switch ($argMatch[2]) { case '+' : if ($argumentCount < $argMatch[1]) { - $argumentCountError = true; + $argumentCountError = TRUE; $expectedArgumentCountString = $argMatch[1].' or more '; } break; case '-' : if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) { - $argumentCountError = true; + $argumentCountError = TRUE; $expectedArgumentCountString = 'between '.$argMatch[1].' and '.$argMatch[3]; } break; case ',' : if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) { - $argumentCountError = true; + $argumentCountError = TRUE; $expectedArgumentCountString = 'either '.$argMatch[1].' or '.$argMatch[3]; } break; @@ -2756,7 +2793,7 @@ class PHPExcel_Calculation { ++$index; } elseif ($opCharacter == ',') { // Is this the separator for function arguments? -// echo 'Element is a Function argument separator
'; +//echo 'Element is a Function argument separator',PHP_EOL; while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last ( if ($o2 === NULL) return $this->_raiseFormulaError("Formula Error: Unexpected ,"); else $output[] = $o2; // pop the argument expression stuff and push onto the output @@ -2764,7 +2801,7 @@ class PHPExcel_Calculation { // If we've a comma when we're expecting an operand, then what we actually have is a null operand; // so push a null onto the stack if (($expectingOperand) || (!$expectingOperator)) { - $output[] = array('type' => 'NULL Value', 'value' => self::$_ExcelConstants['NULL'], 'reference' => null); + $output[] = array('type' => 'NULL Value', 'value' => self::$_ExcelConstants['NULL'], 'reference' => NULL); } // make sure there was a function $d = $stack->last(2); @@ -2773,8 +2810,8 @@ class PHPExcel_Calculation { $d = $stack->pop(); $stack->push($d['type'],++$d['value'],$d['reference']); // increment the argument count $stack->push('Brace', '('); // put the ( back on, we'll need to pop back to it again - $expectingOperator = false; - $expectingOperand = true; + $expectingOperator = FALSE; + $expectingOperand = TRUE; ++$index; } elseif ($opCharacter == '(' && !$expectingOperator) { @@ -2783,8 +2820,8 @@ class PHPExcel_Calculation { ++$index; } elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number? - $expectingOperator = true; - $expectingOperand = false; + $expectingOperator = TRUE; + $expectingOperand = FALSE; $val = $match[1]; $length = strlen($val); // echo 'Element with value '.$val.' is an Operand, Variable, Constant, String, Number, Cell Reference or Function
'; @@ -2797,14 +2834,14 @@ class PHPExcel_Calculation { $ax = preg_match('/^\s*(\s*\))/i', substr($formula, $index+$length), $amatch); if ($ax) { $stack->push('Operand Count for Function '.strtoupper($val).')', 0); - $expectingOperator = true; + $expectingOperator = TRUE; } else { $stack->push('Operand Count for Function '.strtoupper($val).')', 1); - $expectingOperator = false; + $expectingOperator = FALSE; } $stack->push('Brace', '('); } else { // it's a var w/ implicit multiplication - $output[] = array('type' => 'Value', 'value' => $matches[1], 'reference' => null); + $output[] = array('type' => 'Value', 'value' => $matches[1], 'reference' => NULL); } } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $val, $matches)) { // echo 'Element '.$val.' is a Cell reference
'; @@ -2829,7 +2866,7 @@ class PHPExcel_Calculation { } $output[] = array('type' => 'Cell Reference', 'value' => $val, 'reference' => $val); -// $expectingOperator = false; +// $expectingOperator = FALSE; } else { // it's a variable, constant, string, number or boolean // echo 'Element is a Variable, Constant, String, Number or Boolean
'; // If the last entry on the stack was a : operator, then we may have a row or column range reference @@ -2837,12 +2874,12 @@ class PHPExcel_Calculation { if ($testPrevOp['value'] == ':') { $startRowColRef = $output[count($output)-1]['value']; $rangeWS1 = ''; - if (strpos('!',$startRowColRef) !== false) { + if (strpos('!',$startRowColRef) !== FALSE) { list($rangeWS1,$startRowColRef) = explode('!',$startRowColRef); } if ($rangeWS1 != '') $rangeWS1 .= '!'; $rangeWS2 = $rangeWS1; - if (strpos('!',$val) !== false) { + if (strpos('!',$val) !== FALSE) { list($rangeWS2,$val) = explode('!',$val); } if ($rangeWS2 != '') $rangeWS2 .= '!'; @@ -2861,14 +2898,14 @@ class PHPExcel_Calculation { } } - $localeConstant = false; + $localeConstant = FALSE; if ($opCharacter == '"') { // echo 'Element is a String
'; // UnEscape any quotes within the string $val = self::_wrapResult(str_replace('""','"',self::_unwrapResult($val))); } elseif (is_numeric($val)) { // echo 'Element is a Number
'; - if ((strpos($val,'.') !== false) || (stripos($val,'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) { + if ((strpos($val,'.') !== FALSE) || (stripos($val,'e') !== FALSE) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) { // echo 'Casting '.$val.' to float
'; $val = (float) $val; } else { @@ -2879,11 +2916,11 @@ class PHPExcel_Calculation { $excelConstant = trim(strtoupper($val)); // echo 'Element '.$excelConstant.' is an Excel Constant
'; $val = self::$_ExcelConstants[$excelConstant]; - } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$_localeBoolean)) !== false) { + } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$_localeBoolean)) !== FALSE) { // echo 'Element '.$localeConstant.' is an Excel Constant
'; $val = self::$_ExcelConstants[$localeConstant]; } - $details = array('type' => 'Value', 'value' => $val, 'reference' => null); + $details = array('type' => 'Value', 'value' => $val, 'reference' => NULL); if ($localeConstant) { $details['localeValue'] = $localeConstant; } $output[] = $details; } @@ -2893,9 +2930,9 @@ class PHPExcel_Calculation { ++$index; } elseif ($opCharacter == ')') { // miscellaneous error checking if ($expectingOperand) { - $output[] = array('type' => 'NULL Value', 'value' => self::$_ExcelConstants['NULL'], 'reference' => null); - $expectingOperand = false; - $expectingOperator = true; + $output[] = array('type' => 'NULL Value', 'value' => self::$_ExcelConstants['NULL'], 'reference' => NULL); + $expectingOperand = FALSE; + $expectingOperator = TRUE; } else { return $this->_raiseFormulaError("Formula Error: Unexpected ')'"); } @@ -2931,11 +2968,11 @@ class PHPExcel_Calculation { while($stack->count() > 0 && ($o2 = $stack->last()) && isset(self::$_operators[$o2['value']]) && - @($operatorAssociativity[$opCharacter] ? $operatorPrecedence[$opCharacter] < $operatorPrecedence[$o2['value']] : $operatorPrecedence[$opCharacter] <= $operatorPrecedence[$o2['value']])) { + @(self::$_operatorAssociativity[$opCharacter] ? self::$_operatorPrecedence[$opCharacter] < self::$_operatorPrecedence[$o2['value']] : self::$_operatorPrecedence[$opCharacter] <= self::$_operatorPrecedence[$o2['value']])) { $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output } $stack->push('Binary Operator','|'); // Put an Intersect Operator on the stack - $expectingOperator = false; + $expectingOperator = FALSE; } } } @@ -2965,11 +3002,12 @@ class PHPExcel_Calculation { } // evaluate postfix notation - private function _processTokenStack($tokens, $cellID = null, PHPExcel_Cell $pCell = null) { - if ($tokens == false) return false; + private function _processTokenStack($tokens, $cellID = NULL, PHPExcel_Cell $pCell = NULL) { + if ($tokens == FALSE) return FALSE; - // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet), - // so we store the parent worksheet so that we can re-attach it when necessary + // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection), + // so we store the parent cell collection so that we can re-attach it when necessary + $pCellWorksheet = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL; $pCellParent = ($pCell !== NULL) ? $pCell->getParent() : null; $stack = new PHPExcel_Calculation_Token_Stack; @@ -2991,9 +3029,9 @@ class PHPExcel_Calculation { // Log what we're doing if ($token == ':') { - $this->_writeDebug('Evaluating Range ', $this->_showValue($operand1Data['reference']), ' ', $token, ' ', $this->_showValue($operand2Data['reference'])); + $this->_debugLog->writeDebugLog('Evaluating Range ', $this->_showValue($operand1Data['reference']), ' ', $token, ' ', $this->_showValue($operand2Data['reference'])); } else { - $this->_writeDebug('Evaluating ', $this->_showValue($operand1), ' ', $token, ' ', $this->_showValue($operand2)); + $this->_debugLog->writeDebugLog('Evaluating ', $this->_showValue($operand1), ' ', $token, ' ', $this->_showValue($operand2)); } // Process the operation in the appropriate manner @@ -3010,12 +3048,12 @@ class PHPExcel_Calculation { // Binary Operators case ':' : // Range $sheet1 = $sheet2 = ''; - if (strpos($operand1Data['reference'],'!') !== false) { + if (strpos($operand1Data['reference'],'!') !== FALSE) { list($sheet1,$operand1Data['reference']) = explode('!',$operand1Data['reference']); } else { - $sheet1 = ($pCellParent !== NULL) ? $pCellParent->getTitle() : ''; + $sheet1 = ($pCellParent !== NULL) ? $pCellWorksheet->getTitle() : ''; } - if (strpos($operand2Data['reference'],'!') !== false) { + if (strpos($operand2Data['reference'],'!') !== FALSE) { list($sheet2,$operand2Data['reference']) = explode('!',$operand2Data['reference']); } else { $sheet2 = $sheet1; @@ -3049,13 +3087,13 @@ class PHPExcel_Calculation { } $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow); if ($pCellParent !== NULL) { - $cellValue = $this->extractCellRange($cellRef, $pCellParent->getParent()->getSheetByName($sheet1), false); + $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($sheet1), FALSE); } else { return $this->_raiseFormulaError('Unable to access Cell Reference'); } $stack->push('Cell Reference',$cellValue,$cellRef); } else { - $stack->push('Error',PHPExcel_Calculation_Functions::REF(),null); + $stack->push('Error',PHPExcel_Calculation_Functions::REF(),NULL); } break; @@ -3094,13 +3132,13 @@ class PHPExcel_Calculation { $matrixResult = $matrix->concat($operand2); $result = $matrixResult->getArray(); } catch (PHPExcel_Exception $ex) { - $this->_writeDebug('JAMA Matrix Exception: ', $ex->getMessage()); + $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); $result = '#VALUE!'; } } else { $result = '"'.str_replace('""','"',self::_unwrapResult($operand1,'"').self::_unwrapResult($operand2,'"')).'"'; } - $this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($result)); + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result)); $stack->push('Value',$result); break; case '|' : // Intersect @@ -3114,7 +3152,7 @@ class PHPExcel_Calculation { } } $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow); - $this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($cellIntersect)); + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($cellIntersect)); $stack->push('Value',$cellIntersect,$cellRef); break; } @@ -3126,11 +3164,11 @@ class PHPExcel_Calculation { $arg = $arg['value']; if ($token === '~') { // echo 'Token is a negation operator
'; - $this->_writeDebug('Evaluating Negation of ', $this->_showValue($arg)); + $this->_debugLog->writeDebugLog('Evaluating Negation of ', $this->_showValue($arg)); $multiplier = -1; } else { // echo 'Token is a percentile operator
'; - $this->_writeDebug('Evaluating Percentile of ', $this->_showValue($arg)); + $this->_debugLog->writeDebugLog('Evaluating Percentile of ', $this->_showValue($arg)); $multiplier = 0.01; } if (is_array($arg)) { @@ -3140,17 +3178,17 @@ class PHPExcel_Calculation { $matrixResult = $matrix1->arrayTimesEquals($multiplier); $result = $matrixResult->getArray(); } catch (PHPExcel_Exception $ex) { - $this->_writeDebug('JAMA Matrix Exception: ', $ex->getMessage()); + $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); $result = '#VALUE!'; } - $this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($result)); + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result)); $stack->push('Value',$result); } else { $this->_executeNumericBinaryOperation($cellID,$multiplier,$arg,'*','arrayTimesEquals',$stack); } } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $token, $matches)) { - $cellRef = null; + $cellRef = NULL; // echo 'Element '.$token.' is a Cell reference
'; if (isset($matches[8])) { // echo 'Reference is a Range of cells
'; @@ -3161,29 +3199,29 @@ class PHPExcel_Calculation { $cellRef = $matches[6].$matches[7].':'.$matches[9].$matches[10]; if ($matches[2] > '') { $matches[2] = trim($matches[2],"\"'"); - if ((strpos($matches[2],'[') !== false) || (strpos($matches[2],']') !== false)) { + if ((strpos($matches[2],'[') !== FALSE) || (strpos($matches[2],']') !== FALSE)) { // It's a Reference to an external workbook (not currently supported) return $this->_raiseFormulaError('Unable to access External Workbook'); } $matches[2] = trim($matches[2],"\"'"); // echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'
'; - $this->_writeDebug('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]); + $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]); if ($pCellParent !== NULL) { - $cellValue = $this->extractCellRange($cellRef, $pCellParent->getParent()->getSheetByName($matches[2]), false); + $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE); } else { return $this->_raiseFormulaError('Unable to access Cell Reference'); } - $this->_writeDebug('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue)); + $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue)); // $cellRef = $matches[2].'!'.$cellRef; } else { // echo '$cellRef='.$cellRef.' in current worksheet
'; - $this->_writeDebug('Evaluating Cell Range ', $cellRef, ' in current worksheet'); + $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet'); if ($pCellParent !== NULL) { - $cellValue = $this->extractCellRange($cellRef, $pCellParent, false); + $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, FALSE); } else { return $this->_raiseFormulaError('Unable to access Cell Reference'); } - $this->_writeDebug('Evaluation Result for cells ', $cellRef, ' is ', $this->_showTypeDetails($cellValue)); + $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->_showTypeDetails($cellValue)); } } } else { @@ -3195,34 +3233,34 @@ class PHPExcel_Calculation { $cellRef = $matches[6].$matches[7]; if ($matches[2] > '') { $matches[2] = trim($matches[2],"\"'"); - if ((strpos($matches[2],'[') !== false) || (strpos($matches[2],']') !== false)) { + if ((strpos($matches[2],'[') !== FALSE) || (strpos($matches[2],']') !== FALSE)) { // It's a Reference to an external workbook (not currently supported) return $this->_raiseFormulaError('Unable to access External Workbook'); } // echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'
'; - $this->_writeDebug('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]); + $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]); if ($pCellParent !== NULL) { - if ($pCellParent->getParent()->getSheetByName($matches[2])->cellExists($cellRef)) { - $cellValue = $this->extractCellRange($cellRef, $pCellParent->getParent()->getSheetByName($matches[2]), false); + if ($this->_workbook->getSheetByName($matches[2])->cellExists($cellRef)) { + $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE); $pCell->attach($pCellParent); } else { - $cellValue = null; + $cellValue = NULL; } } else { return $this->_raiseFormulaError('Unable to access Cell Reference'); } - $this->_writeDebug('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue)); + $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue)); // $cellRef = $matches[2].'!'.$cellRef; } else { // echo '$cellRef='.$cellRef.' in current worksheet
'; - $this->_writeDebug('Evaluating Cell ', $cellRef, ' in current worksheet'); - if ($pCellParent->cellExists($cellRef)) { - $cellValue = $this->extractCellRange($cellRef, $pCellParent, false); + $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet'); + if ($pCellParent->isDataSet($cellRef)) { + $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, FALSE); $pCell->attach($pCellParent); } else { - $cellValue = null; + $cellValue = NULL; } - $this->_writeDebug('Evaluation Result for cell ', $cellRef, ' is ', $this->_showTypeDetails($cellValue)); + $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->_showTypeDetails($cellValue)); } } } @@ -3235,7 +3273,7 @@ class PHPExcel_Calculation { $argCount = $stack->pop(); $argCount = $argCount['value']; if ($functionName != 'MKMATRIX') { - $this->_writeDebug('Evaluating Function ', self::_localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's')); + $this->_debugLog->writeDebugLog('Evaluating Function ', self::_localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's')); } if ((isset(self::$_PHPExcelFunctions[$functionName])) || (isset(self::$_controlFunctions[$functionName]))) { // function if (isset(self::$_PHPExcelFunctions[$functionName])) { @@ -3278,30 +3316,30 @@ class PHPExcel_Calculation { // print_r($args); // echo '
'; if ($functionName != 'MKMATRIX') { - if ($this->writeDebugLog) { + if ($this->_debugLog->getWriteDebugLog()) { krsort($argArrayVals); - $this->_writeDebug('Evaluating ', self::_localeFunc($functionName), '( ', implode(self::$_localeArgumentSeparator.' ',PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )'); + $this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', implode(self::$_localeArgumentSeparator.' ',PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )'); } } // Process each argument in turn, building the return value as an array // if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) { // $operand1 = $args[1]; -// $this->_writeDebug('Argument is a matrix: ', $this->_showValue($operand1)); +// $this->_debugLog->writeDebugLog('Argument is a matrix: ', $this->_showValue($operand1)); // $result = array(); // $row = 0; // foreach($operand1 as $args) { // if (is_array($args)) { // foreach($args as $arg) { -// $this->_writeDebug('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($arg), ' )'); +// $this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($arg), ' )'); // $r = call_user_func_array($functionCall,$arg); -// $this->_writeDebug('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r)); +// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r)); // $result[$row][] = $r; // } // ++$row; // } else { -// $this->_writeDebug('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($args), ' )'); +// $this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($args), ' )'); // $r = call_user_func_array($functionCall,$args); -// $this->_writeDebug('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r)); +// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r)); // $result[] = $r; // } // } @@ -3310,7 +3348,7 @@ class PHPExcel_Calculation { if ($passCellReference) { $args[] = $pCell; } - if (strpos($functionCall,'::') !== false) { + if (strpos($functionCall,'::') !== FALSE) { $result = call_user_func_array(explode('::',$functionCall),$args); } else { foreach($args as &$arg) { @@ -3321,7 +3359,7 @@ class PHPExcel_Calculation { } // } if ($functionName != 'MKMATRIX') { - $this->_writeDebug('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($result)); + $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($result)); } $stack->push('Value',self::_wrapResult($result)); } @@ -3332,7 +3370,7 @@ class PHPExcel_Calculation { $excelConstant = strtoupper($token); // echo 'Token is a PHPExcel constant: '.$excelConstant.'
'; $stack->push('Constant Value',self::$_ExcelConstants[$excelConstant]); - $this->_writeDebug('Evaluating Constant ', $excelConstant, ' as ', $this->_showTypeDetails(self::$_ExcelConstants[$excelConstant])); + $this->_debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->_showTypeDetails(self::$_ExcelConstants[$excelConstant])); } elseif ((is_numeric($token)) || ($token === NULL) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) { // echo 'Token is a number, boolean, string, null or an Excel error
'; $stack->push('Value',$token); @@ -3341,10 +3379,10 @@ class PHPExcel_Calculation { // echo 'Token is a named range
'; $namedRange = $matches[6]; // echo 'Named Range is '.$namedRange.'
'; - $this->_writeDebug('Evaluating Named Range ', $namedRange); - $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellParent : null), false); + $this->_debugLog->writeDebugLog('Evaluating Named Range ', $namedRange); + $cellValue = $this->extractNamedRange($namedRange, ((NULL !== $pCell) ? $pCellWorksheet : NULL), FALSE); $pCell->attach($pCellParent); - $this->_writeDebug('Evaluation Result for named range ', $namedRange, ' is ', $this->_showTypeDetails($cellValue)); + $this->_debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->_showTypeDetails($cellValue)); $stack->push('Named Range',$cellValue,$namedRange); } else { return $this->_raiseFormulaError("undefined variable '$token'"); @@ -3363,7 +3401,7 @@ class PHPExcel_Calculation { } // function _processTokenStack() - private function _validateBinaryOperand($cellID,&$operand,&$stack) { + private function _validateBinaryOperand($cellID, &$operand, &$stack) { // Numbers, matrices and booleans can pass straight through, as they're already valid if (is_string($operand)) { // We only need special validations for the operand if it is a string @@ -3374,36 +3412,36 @@ class PHPExcel_Calculation { // If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations if ($operand > '' && $operand{0} == '#') { $stack->push('Value', $operand); - $this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($operand)); - return false; + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($operand)); + return FALSE; } elseif (!PHPExcel_Shared_String::convertToNumberIfFraction($operand)) { // If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations $stack->push('Value', '#VALUE!'); - $this->_writeDebug('Evaluation Result is a ', $this->_showTypeDetails('#VALUE!')); - return false; + $this->_debugLog->writeDebugLog('Evaluation Result is a ', $this->_showTypeDetails('#VALUE!')); + return FALSE; } } } // return a true if the value of the operand is one that we can use in normal binary operations - return true; + return TRUE; } // function _validateBinaryOperand() - private function _executeBinaryComparisonOperation($cellID,$operand1,$operand2,$operation,&$stack,$recursingArrays=false) { + private function _executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, &$stack, $recursingArrays=FALSE) { // If we're dealing with matrix operations, we want a matrix result if ((is_array($operand1)) || (is_array($operand2))) { $result = array(); if ((is_array($operand1)) && (!is_array($operand2))) { foreach($operand1 as $x => $operandData) { - $this->_writeDebug('Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2)); + $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2)); $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2,$operation,$stack); $r = $stack->pop(); $result[$x] = $r['value']; } } elseif ((!is_array($operand1)) && (is_array($operand2))) { foreach($operand2 as $x => $operandData) { - $this->_writeDebug('Evaluating Comparison ', $this->_showValue($operand1), ' ', $operation, ' ', $this->_showValue($operandData)); + $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->_showValue($operand1), ' ', $operation, ' ', $this->_showValue($operandData)); $this->_executeBinaryComparisonOperation($cellID,$operand1,$operandData,$operation,$stack); $r = $stack->pop(); $result[$x] = $r['value']; @@ -3411,17 +3449,17 @@ class PHPExcel_Calculation { } else { if (!$recursingArrays) { self::_checkMatrixOperands($operand1,$operand2,2); } foreach($operand1 as $x => $operandData) { - $this->_writeDebug('Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2[$x])); - $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2[$x],$operation,$stack,true); + $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2[$x])); + $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2[$x],$operation,$stack,TRUE); $r = $stack->pop(); $result[$x] = $r['value']; } } // Log the result details - $this->_writeDebug('Comparison Evaluation Result is ', $this->_showTypeDetails($result)); + $this->_debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->_showTypeDetails($result)); // And push the result onto the stack $stack->push('Array',$result); - return true; + return TRUE; } // Simple validate the two operands if they are string values @@ -3457,31 +3495,31 @@ class PHPExcel_Calculation { } // Log the result details - $this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($result)); + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result)); // And push the result onto the stack $stack->push('Value',$result); - return true; + return TRUE; } // function _executeBinaryComparisonOperation() private function _executeNumericBinaryOperation($cellID,$operand1,$operand2,$operation,$matrixFunction,&$stack) { // Validate the two operands - if (!$this->_validateBinaryOperand($cellID,$operand1,$stack)) return false; - if (!$this->_validateBinaryOperand($cellID,$operand2,$stack)) return false; + if (!$this->_validateBinaryOperand($cellID,$operand1,$stack)) return FALSE; + if (!$this->_validateBinaryOperand($cellID,$operand2,$stack)) return FALSE; - $executeMatrixOperation = false; + $executeMatrixOperation = FALSE; // If either of the operands is a matrix, we need to treat them both as matrices // (converting the other operand to a matrix if need be); then perform the required // matrix operation if ((is_array($operand1)) || (is_array($operand2))) { // Ensure that both operands are arrays/matrices - $executeMatrixOperation = true; + $executeMatrixOperation = TRUE; $mSize = array(); list($mSize[],$mSize[],$mSize[],$mSize[]) = self::_checkMatrixOperands($operand1,$operand2,2); // But if they're both single cell matrices, then we can treat them as simple values if (array_sum($mSize) == 4) { - $executeMatrixOperation = false; + $executeMatrixOperation = FALSE; $operand1 = $operand1[0][0]; $operand2 = $operand2[0][0]; } @@ -3495,7 +3533,7 @@ class PHPExcel_Calculation { $matrixResult = $matrix->$matrixFunction($operand2); $result = $matrixResult->getArray(); } catch (PHPExcel_Exception $ex) { - $this->_writeDebug('JAMA Matrix Exception: ', $ex->getMessage()); + $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage()); $result = '#VALUE!'; } } else { @@ -3522,8 +3560,8 @@ class PHPExcel_Calculation { if ($operand2 == 0) { // Trap for Divide by Zero error $stack->push('Value','#DIV/0!'); - $this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails('#DIV/0!')); - return false; + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails('#DIV/0!')); + return FALSE; } else { $result = $operand1/$operand2; } @@ -3537,29 +3575,17 @@ class PHPExcel_Calculation { } // Log the result details - $this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($result)); + $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result)); // And push the result onto the stack $stack->push('Value',$result); - return true; + return TRUE; } // function _executeNumericBinaryOperation() - private function _writeDebug() { - // Only write the debug log if logging is enabled - if ($this->writeDebugLog) { - $message = implode('',func_get_args()); - if ($this->echoDebugLog) { - echo implode(' -> ',$this->debugLogStack).' -> '.$message,'
'; - } - $this->debugLog[] = implode(' -> ',$this->debugLogStack).' -> '.$message; - } - } // function _writeDebug() - - // trigger an error, but nicely, if need be protected function _raiseFormulaError($errorMessage) { $this->formulaError = $errorMessage; - $this->debugLogStack = array(); + $this->_cyclicReferenceStack->clear(); if (!$this->suppressFormulaErrors) throw new PHPExcel_Calculation_Exception($errorMessage); trigger_error($errorMessage, E_USER_ERROR); } // function _raiseFormulaError() @@ -3573,44 +3599,45 @@ class PHPExcel_Calculation { * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned. * @throws PHPExcel_Calculation_Exception */ - public function extractCellRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog=true) { + public function extractCellRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = NULL, $resetLog=TRUE) { // Return value $returnValue = array (); -// echo 'extractCellRange('.$pRange.')
'; +// echo 'extractCellRange('.$pRange.')',PHP_EOL; if ($pSheet !== NULL) { -// echo 'Passed sheet name is '.$pSheet->getTitle().'
'; -// echo 'Range reference is '.$pRange.'
'; + $pSheetName = $pSheet->getTitle(); +// echo 'Passed sheet name is '.$pSheetName.PHP_EOL; +// echo 'Range reference is '.$pRange.PHP_EOL; if (strpos ($pRange, '!') !== false) { -// echo '$pRange reference includes sheet reference
'; - $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pRange, true); - $pSheet = $pSheet->getParent()->getSheetByName($worksheetReference[0]); -// echo 'New sheet name is '.$pSheet->getTitle().'
'; - $pRange = $worksheetReference[1]; -// echo 'Adjusted Range reference is '.$pRange.'
'; +// echo '$pRange reference includes sheet reference',PHP_EOL; + list($pSheetName,$pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true); +// echo 'New sheet name is '.$pSheetName,PHP_EOL; +// echo 'Adjusted Range reference is '.$pRange,PHP_EOL; + $pSheet = $this->_workbook->getSheetByName($pSheetName); } // Extract range $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange); - $pRange = $pSheet->getTitle().'!'.$pRange; + $pRange = $pSheetName.'!'.$pRange; if (!isset($aReferences[1])) { // Single cell in range - list($currentCol,$currentRow) = sscanf($aReferences[0],'%[A-Z]%d'); + sscanf($aReferences[0],'%[A-Z]%d', $currentCol, $currentRow); + $cellValue = NULL; if ($pSheet->cellExists($aReferences[0])) { $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog); } else { - $returnValue[$currentRow][$currentCol] = null; + $returnValue[$currentRow][$currentCol] = NULL; } } else { // Extract cell data for all cells in the range foreach ($aReferences as $reference) { // Extract range - list($currentCol,$currentRow) = sscanf($reference,'%[A-Z]%d'); - + sscanf($reference,'%[A-Z]%d', $currentCol, $currentRow); + $cellValue = NULL; if ($pSheet->cellExists($reference)) { $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog); } else { - $returnValue[$currentRow][$currentCol] = null; + $returnValue[$currentRow][$currentCol] = NULL; } } } @@ -3629,21 +3656,21 @@ class PHPExcel_Calculation { * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned. * @throws PHPExcel_Calculation_Exception */ - public function extractNamedRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog=true) { + public function extractNamedRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = NULL, $resetLog=TRUE) { // Return value $returnValue = array (); // echo 'extractNamedRange('.$pRange.')
'; if ($pSheet !== NULL) { -// echo 'Current sheet name is '.$pSheet->getTitle().'
'; + $pSheetName = $pSheet->getTitle(); +// echo 'Current sheet name is '.$pSheetName.'
'; // echo 'Range reference is '.$pRange.'
'; if (strpos ($pRange, '!') !== false) { -// echo '$pRange reference includes sheet reference
'; - $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pRange, true); - $pSheet = $pSheet->getParent()->getSheetByName($worksheetReference[0]); -// echo 'New sheet name is '.$pSheet->getTitle().'
'; - $pRange = $worksheetReference[1]; -// echo 'Adjusted Range reference is '.$pRange.'
'; +// echo '$pRange reference includes sheet reference',PHP_EOL; + list($pSheetName,$pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true); +// echo 'New sheet name is '.$pSheetName,PHP_EOL; +// echo 'Adjusted Range reference is '.$pRange,PHP_EOL; + $pSheet = $this->_workbook->getSheetByName($pSheetName); } // Named range? @@ -3678,10 +3705,11 @@ class PHPExcel_Calculation { if (!isset($aReferences[1])) { // Single cell (or single column or row) in range list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]); + $cellValue = NULL; if ($pSheet->cellExists($aReferences[0])) { $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog); } else { - $returnValue[$currentRow][$currentCol] = null; + $returnValue[$currentRow][$currentCol] = NULL; } } else { // Extract cell data for all cells in the range @@ -3689,10 +3717,11 @@ class PHPExcel_Calculation { // Extract range list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($reference); // echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'
'; + $cellValue = NULL; if ($pSheet->cellExists($reference)) { $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog); } else { - $returnValue[$currentRow][$currentCol] = null; + $returnValue[$currentRow][$currentCol] = NULL; } } } @@ -3716,7 +3745,7 @@ class PHPExcel_Calculation { if (isset(self::$_PHPExcelFunctions[$pFunction])) { return (self::$_PHPExcelFunctions[$pFunction]['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY'); } else { - return false; + return FALSE; } } // function isImplemented() diff --git a/Classes/PHPExcel/Calculation/Engineering.php b/Classes/PHPExcel/Calculation/Engineering.php index 84dcd38..c0005e7 100644 --- a/Classes/PHPExcel/Calculation/Engineering.php +++ b/Classes/PHPExcel/Calculation/Engineering.php @@ -1932,7 +1932,7 @@ class PHPExcel_Calculation_Engineering { /** * IMLOG2 * - * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format. + * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format. * * Excel Function: * IMLOG2(complexNumber) diff --git a/Classes/PHPExcel/Calculation/Token/Stack.php b/Classes/PHPExcel/Calculation/Token/Stack.php index 081a38d..bdf9d55 100644 --- a/Classes/PHPExcel/Calculation/Token/Stack.php +++ b/Classes/PHPExcel/Calculation/Token/Stack.php @@ -67,7 +67,9 @@ class PHPExcel_Calculation_Token_Stack { } // function last() - function __construct() { + function clear() { + $this->_stack = array(); + $this->_count = 0; } } // class PHPExcel_Calculation_Token_Stack diff --git a/Classes/PHPExcel/Cell.php b/Classes/PHPExcel/Cell.php index 2e9cd32..27fb914 100644 --- a/Classes/PHPExcel/Cell.php +++ b/Classes/PHPExcel/Cell.php @@ -50,13 +50,6 @@ class PHPExcel_Cell */ private static $_valueBinder = NULL; - /** - * Cell Address (e.g. A1) - * - * @var string - */ - private $_coordinate; - /** * Value of the cell * @@ -86,7 +79,7 @@ class PHPExcel_Cell /** * Parent worksheet * - * @var PHPExcel_Worksheet + * @var PHPExcel_CachedObjectStorage_CacheBase */ private $_parent; @@ -110,7 +103,8 @@ class PHPExcel_Cell * @return void **/ public function notifyCacheController() { - $this->_parent->getCellCacheController()->updateCacheData($this); + $this->_parent->updateCacheData($this); + return $this; } @@ -118,7 +112,9 @@ class PHPExcel_Cell $this->_parent = NULL; } - public function attach($parent) { + public function attach(PHPExcel_CachedObjectStorage_CacheBase $parent) { + + $this->_parent = $parent; } @@ -126,23 +122,18 @@ class PHPExcel_Cell /** * Create a new Cell * - * @param string $pColumn - * @param int $pRow * @param mixed $pValue * @param string $pDataType * @param PHPExcel_Worksheet $pSheet * @throws PHPExcel_Exception */ - public function __construct($pCoordinate = 'A1', $pValue = NULL, $pDataType = NULL, PHPExcel_Worksheet $pSheet = NULL) + public function __construct($pValue = NULL, $pDataType = NULL, PHPExcel_Worksheet $pSheet = NULL) { - // Initialise cell coordinate - $this->_coordinate = strtoupper($pCoordinate); - // Initialise cell value $this->_value = $pValue; - // Set worksheet - $this->_parent = $pSheet; + // Set worksheet cache + $this->_parent = $pSheet->getCellCacheController(); // Set datatype? if ($pDataType !== NULL) { @@ -166,8 +157,7 @@ class PHPExcel_Cell */ public function getColumn() { - list($column) = sscanf($this->_coordinate, '%[A-Z]%d'); - return $column; + return $this->_parent->getCurrentColumn(); } /** @@ -177,8 +167,7 @@ class PHPExcel_Cell */ public function getRow() { - list(,$row) = sscanf($this->_coordinate, '%[A-Z]%d'); - return $row; + return $this->_parent->getCurrentRow(); } /** @@ -188,7 +177,7 @@ class PHPExcel_Cell */ public function getCoordinate() { - return $this->_coordinate; + return $this->_parent->getCurrentAddress(); } /** @@ -210,7 +199,7 @@ class PHPExcel_Cell { return (string) PHPExcel_Style_NumberFormat::toFormattedString( $this->getCalculatedValue(), - $this->_parent->getParent()->getCellXfByIndex($this->getXfIndex()) + $this->getWorksheet()->getParent()->getCellXfByIndex($this->getXfIndex()) ->getNumberFormat()->getFormatCode() ); } @@ -284,38 +273,44 @@ class PHPExcel_Cell */ public function getCalculatedValue($resetLog = TRUE) { -// echo 'Cell '.$this->getCoordinate().' value is a '.$this->_dataType.' with a value of '.$this->getValue().'
'; +//echo 'Cell '.$this->getCoordinate().' value is a '.$this->_dataType.' with a value of '.$this->getValue().PHP_EOL; if ($this->_dataType == PHPExcel_Cell_DataType::TYPE_FORMULA) { try { -// echo 'Cell value for '.$this->getCoordinate().' is a formula: Calculating value
'; - $result = PHPExcel_Calculation::getInstance()->calculateCellValue($this,$resetLog); -// echo $this->getCoordinate().' calculation result is '.$result.'
'; +//echo 'Cell value for '.$this->getCoordinate().' is a formula: Calculating value'.PHP_EOL; + $result = PHPExcel_Calculation::getInstance( + $this->getWorksheet()->getParent() + )->calculateCellValue($this,$resetLog); +//echo $this->getCoordinate().' calculation result is '.$result.PHP_EOL; + // We don't yet handle array returns + if (is_array($result)) { + while (is_array($result)) { + $result = array_pop($result); + } + } } catch ( PHPExcel_Exception $ex ) { if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->_calculatedValue !== NULL)) { -// echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().'
'; +//echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().PHP_EOL; return $this->_calculatedValue; // Fallback for calculations referencing external files. } -// echo 'Calculation Exception: '.$ex->getMessage().'
'; +//echo 'Calculation Exception: '.$ex->getMessage().PHP_EOL; $result = '#N/A'; throw( new PHPExcel_Calculation_Exception( - $this->getParent()->getTitle().'!'.$this->getCoordinate().' -> '.$ex->getMessage() + $this->getWorksheet()->getTitle().'!'.$this->getCoordinate().' -> '.$ex->getMessage() ) ); } if ($result === '#Not Yet Implemented') { -// echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().'
'; +//echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().PHP_EOL; return $this->_calculatedValue; // Fallback if calculation engine does not support the formula. } -// echo 'Returning calculated value of '.$result.' for cell '.$this->getCoordinate().'
'; +//echo 'Returning calculated value of '.$result.' for cell '.$this->getCoordinate().PHP_EOL; return $result; + } elseif($this->_value instanceof PHPExcel_RichText) { +// echo 'Cell value for '.$this->getCoordinate().' is rich text: Returning data value of '.$this->_value.'
'; + return $this->_value->getPlainText(); } - -// if ($this->_value === NULL) { -// echo 'Cell '.$this->getCoordinate().' has no value, formula or otherwise
'; -// return NULL; -// } // echo 'Cell value for '.$this->getCoordinate().' is not a formula: Returning data value of '.$this->_value.'
'; return $this->_value; } @@ -388,7 +383,7 @@ class PHPExcel_Cell throw new PHPExcel_Exception('Cannot check for data validation when cell is not bound to a worksheet'); } - return $this->_parent->dataValidationExists($this->getCoordinate()); + return $this->getWorksheet()->dataValidationExists($this->getCoordinate()); } /** @@ -403,7 +398,7 @@ class PHPExcel_Cell throw new PHPExcel_Exception('Cannot get data validation for cell that is not bound to a worksheet'); } - return $this->_parent->getDataValidation($this->getCoordinate()); + return $this->getWorksheet()->getDataValidation($this->getCoordinate()); } /** @@ -419,7 +414,7 @@ class PHPExcel_Cell throw new PHPExcel_Exception('Cannot set data validation for cell that is not bound to a worksheet'); } - $this->_parent->setDataValidation($this->getCoordinate(), $pDataValidation); + $this->getWorksheet()->setDataValidation($this->getCoordinate(), $pDataValidation); return $this->notifyCacheController(); } @@ -436,7 +431,7 @@ class PHPExcel_Cell throw new PHPExcel_Exception('Cannot check for hyperlink when cell is not bound to a worksheet'); } - return $this->_parent->hyperlinkExists($this->getCoordinate()); + return $this->getWorksheet()->hyperlinkExists($this->getCoordinate()); } /** @@ -451,7 +446,7 @@ class PHPExcel_Cell throw new PHPExcel_Exception('Cannot get hyperlink for cell that is not bound to a worksheet'); } - return $this->_parent->getHyperlink($this->getCoordinate()); + return $this->getWorksheet()->getHyperlink($this->getCoordinate()); } /** @@ -467,7 +462,7 @@ class PHPExcel_Cell throw new PHPExcel_Exception('Cannot set hyperlink for cell that is not bound to a worksheet'); } - $this->_parent->setHyperlink($this->getCoordinate(), $pHyperlink); + $this->getWorksheet()->setHyperlink($this->getCoordinate(), $pHyperlink); return $this->notifyCacheController(); } @@ -481,6 +476,15 @@ class PHPExcel_Cell return $this->_parent; } + /** + * Get parent worksheet + * + * @return PHPExcel_Worksheet + */ + public function getWorksheet() { + return $this->_parent->getParent(); + } + /** * Re-bind parent * @@ -488,7 +492,7 @@ class PHPExcel_Cell * @return PHPExcel_Cell */ public function rebindParent(PHPExcel_Worksheet $parent) { - $this->_parent = $parent; + $this->_parent = $parent->getCellCacheController(); return $this->notifyCacheController(); } @@ -818,8 +822,8 @@ class PHPExcel_Cell // Range... list($rangeStart, $rangeEnd) = $range; - list($startCol, $startRow) = sscanf($rangeStart,'%[A-Z]%d'); - list($endCol, $endRow) = sscanf($rangeEnd,'%[A-Z]%d'); + sscanf($rangeStart,'%[A-Z]%d', $startCol, $startRow); + sscanf($rangeEnd,'%[A-Z]%d', $endCol, $endRow); $endCol++; // Current data @@ -841,7 +845,7 @@ class PHPExcel_Cell // Sort the result by column and row $sortKeys = array(); foreach (array_unique($returnValue) as $coord) { - list($column,$row) = sscanf($coord,'%[A-Z]%d'); + sscanf($coord,'%[A-Z]%d', $column, $row); $sortKeys[sprintf('%3s%09d',$column,$row)] = $coord; } ksort($sortKeys); @@ -859,11 +863,11 @@ class PHPExcel_Cell */ public static function compareCells(PHPExcel_Cell $a, PHPExcel_Cell $b) { - if ($a->_row < $b->_row) { + if ($a->getRow() < $b->getRow()) { return -1; - } elseif ($a->_row > $b->_row) { + } elseif ($a->getRow() > $b->getRow()) { return 1; - } elseif (self::columnIndexFromString($a->_column) < self::columnIndexFromString($b->_column)) { + } elseif (self::columnIndexFromString($a->getColumn()) < self::columnIndexFromString($b->getColumn())) { return -1; } else { return 1; diff --git a/Classes/PHPExcel/Cell/AdvancedValueBinder.php b/Classes/PHPExcel/Cell/AdvancedValueBinder.php index 0b5efec..9c96a1b 100644 --- a/Classes/PHPExcel/Cell/AdvancedValueBinder.php +++ b/Classes/PHPExcel/Cell/AdvancedValueBinder.php @@ -86,7 +86,7 @@ class PHPExcel_Cell_AdvancedValueBinder extends PHPExcel_Cell_DefaultValueBinder if ($matches[1] == '-') $value = 0 - $value; $cell->setValueExplicit( (float) $value, PHPExcel_Cell_DataType::TYPE_NUMERIC); // Set style - $cell->getParent()->getStyle( $cell->getCoordinate() ) + $cell->getWorksheet()->getStyle( $cell->getCoordinate() ) ->getNumberFormat()->setFormatCode( '??/??' ); return true; } elseif (preg_match('/^([+-]?)([0-9]*) +([0-9]*)\s?\/\s*([0-9]*)$/', $value, $matches)) { @@ -95,7 +95,7 @@ class PHPExcel_Cell_AdvancedValueBinder extends PHPExcel_Cell_DefaultValueBinder if ($matches[1] == '-') $value = 0 - $value; $cell->setValueExplicit( (float) $value, PHPExcel_Cell_DataType::TYPE_NUMERIC); // Set style - $cell->getParent()->getStyle( $cell->getCoordinate() ) + $cell->getWorksheet()->getStyle( $cell->getCoordinate() ) ->getNumberFormat()->setFormatCode( '# ??/??' ); return true; } @@ -106,7 +106,7 @@ class PHPExcel_Cell_AdvancedValueBinder extends PHPExcel_Cell_DefaultValueBinder $value = (float) str_replace('%', '', $value) / 100; $cell->setValueExplicit( $value, PHPExcel_Cell_DataType::TYPE_NUMERIC); // Set style - $cell->getParent()->getStyle( $cell->getCoordinate() ) + $cell->getWorksheet()->getStyle( $cell->getCoordinate() ) ->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_PERCENTAGE_00 ); return true; } @@ -120,7 +120,7 @@ class PHPExcel_Cell_AdvancedValueBinder extends PHPExcel_Cell_DefaultValueBinder $value = (float) trim(str_replace(array($currencyCode, $thousandsSeparator, $decimalSeparator), array('', '', '.'), $value)); $cell->setValueExplicit( $value, PHPExcel_Cell_DataType::TYPE_NUMERIC); // Set style - $cell->getParent()->getStyle( $cell->getCoordinate() ) + $cell->getWorksheet()->getStyle( $cell->getCoordinate() ) ->getNumberFormat()->setFormatCode( str_replace('$', $currencyCode, PHPExcel_Style_NumberFormat::FORMAT_CURRENCY_USD_SIMPLE ) ); @@ -130,7 +130,7 @@ class PHPExcel_Cell_AdvancedValueBinder extends PHPExcel_Cell_DefaultValueBinder $value = (float) trim(str_replace(array('$',','), '', $value)); $cell->setValueExplicit( $value, PHPExcel_Cell_DataType::TYPE_NUMERIC); // Set style - $cell->getParent()->getStyle( $cell->getCoordinate() ) + $cell->getWorksheet()->getStyle( $cell->getCoordinate() ) ->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_CURRENCY_USD_SIMPLE ); return true; } @@ -142,7 +142,7 @@ class PHPExcel_Cell_AdvancedValueBinder extends PHPExcel_Cell_DefaultValueBinder $days = $h / 24 + $m / 1440; $cell->setValueExplicit($days, PHPExcel_Cell_DataType::TYPE_NUMERIC); // Set style - $cell->getParent()->getStyle( $cell->getCoordinate() ) + $cell->getWorksheet()->getStyle( $cell->getCoordinate() ) ->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME3 ); return true; } @@ -155,7 +155,7 @@ class PHPExcel_Cell_AdvancedValueBinder extends PHPExcel_Cell_DefaultValueBinder // Convert value to number $cell->setValueExplicit($days, PHPExcel_Cell_DataType::TYPE_NUMERIC); // Set style - $cell->getParent()->getStyle( $cell->getCoordinate() ) + $cell->getWorksheet()->getStyle( $cell->getCoordinate() ) ->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME4 ); return true; } @@ -170,7 +170,7 @@ class PHPExcel_Cell_AdvancedValueBinder extends PHPExcel_Cell_DefaultValueBinder } else { $formatCode = 'yyyy-mm-dd'; } - $cell->getParent()->getStyle( $cell->getCoordinate() ) + $cell->getWorksheet()->getStyle( $cell->getCoordinate() ) ->getNumberFormat()->setFormatCode($formatCode); return true; } @@ -180,7 +180,7 @@ class PHPExcel_Cell_AdvancedValueBinder extends PHPExcel_Cell_DefaultValueBinder $value = PHPExcel_Shared_String::SanitizeUTF8($value); $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING); // Set style - $cell->getParent()->getStyle( $cell->getCoordinate() ) + $cell->getWorksheet()->getStyle( $cell->getCoordinate() ) ->getAlignment()->setWrapText(TRUE); return true; } diff --git a/Classes/PHPExcel/Chart/DataSeriesValues.php b/Classes/PHPExcel/Chart/DataSeriesValues.php index 4a20648..034ac9e 100644 --- a/Classes/PHPExcel/Chart/DataSeriesValues.php +++ b/Classes/PHPExcel/Chart/DataSeriesValues.php @@ -279,7 +279,7 @@ class PHPExcel_Chart_DataSeriesValues public function refresh(PHPExcel_Worksheet $worksheet, $flatten = TRUE) { if ($this->_dataSource !== NULL) { - $calcEngine = PHPExcel_Calculation::getInstance(); + $calcEngine = PHPExcel_Calculation::getInstance($worksheet->getParent()); $newDataValues = PHPExcel_Calculation::_unwrapResult( $calcEngine->_calculateFormulaValue( '='.$this->_dataSource, diff --git a/Classes/PHPExcel/Comment.php b/Classes/PHPExcel/Comment.php index 47db7ec..e9f2578 100644 --- a/Classes/PHPExcel/Comment.php +++ b/Classes/PHPExcel/Comment.php @@ -106,10 +106,10 @@ class PHPExcel_Comment implements PHPExcel_IComparable public function __construct() { // Initialise variables - $this->_author = 'Author'; - $this->_text = new PHPExcel_RichText(); - $this->_fillColor = new PHPExcel_Style_Color('FFFFFFE1'); - $this->_alignment = PHPExcel_Style_Alignment::HORIZONTAL_GENERAL; + $this->_author = 'Author'; + $this->_text = new PHPExcel_RichText(); + $this->_fillColor = new PHPExcel_Style_Color('FFFFFFE1'); + $this->_alignment = PHPExcel_Style_Alignment::HORIZONTAL_GENERAL; } /** @@ -314,4 +314,9 @@ class PHPExcel_Comment implements PHPExcel_IComparable } } } + + public function __toString() { + return $this->_text->getPlainText(); + } + } diff --git a/Classes/PHPExcel/Reader/Excel2007.php b/Classes/PHPExcel/Reader/Excel2007.php index bd91592..e2a677d 100644 --- a/Classes/PHPExcel/Reader/Excel2007.php +++ b/Classes/PHPExcel/Reader/Excel2007.php @@ -1608,15 +1608,16 @@ class PHPExcel_Reader_Excel2007 extends PHPExcel_Reader_Abstract implements PHPE break; default: - $range = explode('!', (string)$definedName); - if (count($range) == 2) { - $range[0] = str_replace("''", "'", $range[0]); - $range[0] = str_replace("'", "", $range[0]); - if ($worksheet = $docSheet->getParent()->getSheetByName($range[0])) { - $extractedRange = str_replace('$', '', $range[1]); - $scope = $docSheet->getParent()->getSheet((string)$definedName['localSheetId']); - - $excel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $worksheet, $extractedRange, true, $scope) ); + if ($mapSheetId[(integer) $definedName['localSheetId']] !== null) { + $range = explode('!', (string)$definedName); + if (count($range) == 2) { + $range[0] = str_replace("''", "'", $range[0]); + $range[0] = str_replace("'", "", $range[0]); + if ($worksheet = $docSheet->getParent()->getSheetByName($range[0])) { + $extractedRange = str_replace('$', '', $range[1]); + $scope = $docSheet->getParent()->getSheet($mapSheetId[(integer) $definedName['localSheetId']]); + $excel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $worksheet, $extractedRange, true, $scope) ); + } } } break; diff --git a/Classes/PHPExcel/ReferenceHelper.php b/Classes/PHPExcel/ReferenceHelper.php index ef990bd..94f21b3 100644 --- a/Classes/PHPExcel/ReferenceHelper.php +++ b/Classes/PHPExcel/ReferenceHelper.php @@ -69,24 +69,328 @@ class PHPExcel_ReferenceHelper } /** - * Insert a new column, updating all possible related data + * Compare two column addresses + * Intended for use as a Callback function for sorting column addresses by column * - * @param int $pBefore Insert before this one - * @param int $pNumCols Number of columns to insert - * @param int $pNumRows Number of rows to insert - * @throws PHPExcel_Exception + * @param string $a First column to test (e.g. 'AA') + * @param string $b Second column to test (e.g. 'Z') + * @return integer */ - public function insertNewBefore($pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, PHPExcel_Worksheet $pSheet = null) { + public static function columnSort($a, $b) { + return strcasecmp(strlen($a) . $a, strlen($b) . $b); + } + + /** + * Compare two column addresses + * Intended for use as a Callback function for reverse sorting column addresses by column + * + * @param string $a First column to test (e.g. 'AA') + * @param string $b Second column to test (e.g. 'Z') + * @return integer + */ + public static function columnReverseSort($a, $b) { + return 1 - strcasecmp(strlen($a) . $a, strlen($b) . $b); + } + + /** + * Compare two cell addresses + * Intended for use as a Callback function for sorting cell addresses by column and row + * + * @param string $a First cell to test (e.g. 'AA1') + * @param string $b Second cell to test (e.g. 'Z1') + * @return integer + */ + public static function cellSort($a, $b) { + sscanf($a,'%[A-Z]%d', $ac, $ar); + sscanf($b,'%[A-Z]%d', $bc, $br); + + if ($ar == $br) { + return strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc); + } + return ($ar < $br) ? -1 : 1; + } + + /** + * Compare two cell addresses + * Intended for use as a Callback function for sorting cell addresses by column and row + * + * @param string $a First cell to test (e.g. 'AA1') + * @param string $b Second cell to test (e.g. 'Z1') + * @return integer + */ + public static function cellReverseSort($a, $b) { + sscanf($a,'%[A-Z]%d', $ac, $ar); + sscanf($b,'%[A-Z]%d', $bc, $br); + + if ($ar == $br) { + return 1 - strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc); + } + return ($ar < $br) ? 1 : -1; + } + + /** + * Test whether a cell address falls within a defined range of cells + * + * @param string $cellAddress Address of the cell we're testing + * @param integer $beforeRow Number of the row we're inserting/deleting before + * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion) + * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before + * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion) + * @return boolean + */ + private static function cellAddressInDeleteRange($cellAddress, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols) { + list($cellColumn, $cellRow) = PHPExcel_Cell::coordinateFromString($cellAddress); + $cellColumnIndex = PHPExcel_Cell::columnIndexFromString($cellColumn); + // Is cell within the range of rows/columns if we're deleting + if ($pNumRows < 0 && + ($cellRow >= ($beforeRow + $pNumRows)) && + ($cellRow < $beforeRow)) { + return TRUE; + } elseif ($pNumCols < 0 && + ($cellColumnIndex >= ($beforeColumnIndex + $pNumCols)) && + ($cellColumnIndex < $beforeColumnIndex)) { + return TRUE; + } + return FALSE; + } + + /** + * Update page breaks when inserting/deleting rows/columns + * + * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing + * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1') + * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before + * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion) + * @param integer $beforeRow Number of the row we're inserting/deleting before + * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion) + */ + protected function _adjustPageBreaks(PHPExcel_Worksheet $pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + { + $aBreaks = $pSheet->getBreaks(); + ($pNumCols > 0 || $pNumRows > 0) ? + uksort($aBreaks, array('PHPExcel_ReferenceHelper','cellReverseSort')) : + uksort($aBreaks, array('PHPExcel_ReferenceHelper','cellSort')); + + foreach ($aBreaks as $key => $value) { + if (self::cellAddressInDeleteRange($key, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)) { + // If we're deleting, then clear any defined breaks that are within the range + // of rows/columns that we're deleting + $pSheet->setBreak($key, PHPExcel_Worksheet::BREAK_NONE); + } else { + // Otherwise update any affected breaks by inserting a new break at the appropriate point + // and removing the old affected break + $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); + if ($key != $newReference) { + $pSheet->setBreak($newReference, $value) + ->setBreak($key, PHPExcel_Worksheet::BREAK_NONE); + } + } + } + } + + /** + * Update cell comments when inserting/deleting rows/columns + * + * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing + * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1') + * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before + * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion) + * @param integer $beforeRow Number of the row we're inserting/deleting before + * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion) + */ + protected function _adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + { + $aComments = $pSheet->getComments(); + $aNewComments = array(); // the new array of all comments + + foreach ($aComments as $key => &$value) { + // Any comments inside a deleted range will be ignored + if (!self::cellAddressInDeleteRange($key, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)) { + // Otherwise build a new array of comments indexed by the adjusted cell reference + $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); + $aNewComments[$newReference] = $value; + } + } + // Replace the comments array with the new set of comments + $pSheet->setComments($aNewComments); + } + + /** + * Update hyperlinks when inserting/deleting rows/columns + * + * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing + * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1') + * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before + * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion) + * @param integer $beforeRow Number of the row we're inserting/deleting before + * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion) + */ + protected function _adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + { + $aHyperlinkCollection = $pSheet->getHyperlinkCollection(); + ($pNumCols > 0 || $pNumRows > 0) ? + uksort($aHyperlinkCollection, array('PHPExcel_ReferenceHelper','cellReverseSort')) : + uksort($aHyperlinkCollection, array('PHPExcel_ReferenceHelper','cellSort')); + + foreach ($aHyperlinkCollection as $key => $value) { + $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); + if ($key != $newReference) { + $pSheet->setHyperlink( $newReference, $value ); + $pSheet->setHyperlink( $key, null ); + } + } + } + + /** + * Update data validations when inserting/deleting rows/columns + * + * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing + * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1') + * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before + * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion) + * @param integer $beforeRow Number of the row we're inserting/deleting before + * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion) + */ + protected function _adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + { + $aDataValidationCollection = $pSheet->getDataValidationCollection(); + ($pNumCols > 0 || $pNumRows > 0) ? + uksort($aDataValidationCollection, array('PHPExcel_ReferenceHelper','cellReverseSort')) : + uksort($aDataValidationCollection, array('PHPExcel_ReferenceHelper','cellSort')); + foreach ($aDataValidationCollection as $key => $value) { + $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); + if ($key != $newReference) { + $pSheet->setDataValidation( $newReference, $value ); + $pSheet->setDataValidation( $key, null ); + } + } + } + + /** + * Update merged cells when inserting/deleting rows/columns + * + * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing + * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1') + * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before + * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion) + * @param integer $beforeRow Number of the row we're inserting/deleting before + * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion) + */ + protected function _adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + { + $aMergeCells = $pSheet->getMergeCells(); + $aNewMergeCells = array(); // the new array of all merge cells + foreach ($aMergeCells as $key => &$value) { + $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); + $aNewMergeCells[$newReference] = $newReference; + } + $pSheet->setMergeCells($aNewMergeCells); // replace the merge cells array + } + + /** + * Update protected cells when inserting/deleting rows/columns + * + * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing + * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1') + * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before + * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion) + * @param integer $beforeRow Number of the row we're inserting/deleting before + * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion) + */ + protected function _adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + { + $aProtectedCells = $pSheet->getProtectedCells(); + ($pNumCols > 0 || $pNumRows > 0) ? + uksort($aProtectedCells, array('PHPExcel_ReferenceHelper','cellReverseSort')) : + uksort($aProtectedCells, array('PHPExcel_ReferenceHelper','cellSort')); + foreach ($aProtectedCells as $key => $value) { + $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); + if ($key != $newReference) { + $pSheet->protectCells( $newReference, $value, true ); + $pSheet->unprotectCells( $key ); + } + } + } + + /** + * Update column dimensions when inserting/deleting rows/columns + * + * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing + * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1') + * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before + * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion) + * @param integer $beforeRow Number of the row we're inserting/deleting before + * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion) + */ + protected function _adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + { + $aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true); + if (!empty($aColumnDimensions)) { + foreach ($aColumnDimensions as $objColumnDimension) { + $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows); + list($newReference) = PHPExcel_Cell::coordinateFromString($newReference); + if ($objColumnDimension->getColumnIndex() != $newReference) { + $objColumnDimension->setColumnIndex($newReference); + } + } + $pSheet->refreshColumnDimensions(); + } + } + + /** + * Update row dimensions when inserting/deleting rows/columns + * + * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing + * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1') + * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before + * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion) + * @param integer $beforeRow Number of the row we're inserting/deleting before + * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion) + */ + protected function _adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + { + $aRowDimensions = array_reverse($pSheet->getRowDimensions(), true); + if (!empty($aRowDimensions)) { + foreach ($aRowDimensions as $objRowDimension) { + $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows); + list(, $newReference) = PHPExcel_Cell::coordinateFromString($newReference); + if ($objRowDimension->getRowIndex() != $newReference) { + $objRowDimension->setRowIndex($newReference); + } + } + $pSheet->refreshRowDimensions(); + + $copyDimension = $pSheet->getRowDimension($beforeRow - 1); + for ($i = $beforeRow; $i <= $beforeRow - 1 + $pNumRows; ++$i) { + $newDimension = $pSheet->getRowDimension($i); + $newDimension->setRowHeight($copyDimension->getRowHeight()); + $newDimension->setVisible($copyDimension->getVisible()); + $newDimension->setOutlineLevel($copyDimension->getOutlineLevel()); + $newDimension->setCollapsed($copyDimension->getCollapsed()); + } + } + } + + /** + * Insert a new column or row, updating all possible related data + * + * @param string $pBefore Insert before this cell address (e.g. 'A1') + * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion) + * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion) + * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing + * @throws PHPExcel_Exception + */ + public function insertNewBefore($pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, PHPExcel_Worksheet $pSheet = NULL) + { $remove = ($pNumCols < 0 || $pNumRows < 0); $aCellCollection = $pSheet->getCellCollection(); // Get coordinates of $pBefore $beforeColumn = 'A'; $beforeRow = 1; - list($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString( $pBefore ); + list($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString($pBefore); $beforeColumnIndex = PHPExcel_Cell::columnIndexFromString($beforeColumn); - // Clear cells if we are removing columns or rows $highestColumn = $pSheet->getHighestColumn(); $highestRow = $pSheet->getHighestRow(); @@ -119,7 +423,6 @@ class PHPExcel_ReferenceHelper } } - // Loop through cells, bottom-up, and change cell coordinates while (($cellID = $remove ? array_shift($aCellCollection) : array_pop($aCellCollection))) { $cell = $pSheet->getCell($cellID); @@ -164,7 +467,6 @@ class PHPExcel_ReferenceHelper } } - // Duplicate styles for the newly inserted cells $highestColumn = $pSheet->getHighestColumn(); $highestRow = $pSheet->getHighestRow(); @@ -216,105 +518,29 @@ class PHPExcel_ReferenceHelper } } - // Update worksheet: column dimensions - $aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true); - if (!empty($aColumnDimensions)) { - foreach ($aColumnDimensions as $objColumnDimension) { - $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows); - list($newReference) = PHPExcel_Cell::coordinateFromString($newReference); - if ($objColumnDimension->getColumnIndex() != $newReference) { - $objColumnDimension->setColumnIndex($newReference); - } - } - $pSheet->refreshColumnDimensions(); - } - + $this->_adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows); // Update worksheet: row dimensions - $aRowDimensions = array_reverse($pSheet->getRowDimensions(), true); - if (!empty($aRowDimensions)) { - foreach ($aRowDimensions as $objRowDimension) { - $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows); - list(, $newReference) = PHPExcel_Cell::coordinateFromString($newReference); - if ($objRowDimension->getRowIndex() != $newReference) { - $objRowDimension->setRowIndex($newReference); - } - } - $pSheet->refreshRowDimensions(); + $this->_adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows); - $copyDimension = $pSheet->getRowDimension($beforeRow - 1); - for ($i = $beforeRow; $i <= $beforeRow - 1 + $pNumRows; ++$i) { - $newDimension = $pSheet->getRowDimension($i); - $newDimension->setRowHeight($copyDimension->getRowHeight()); - $newDimension->setVisible($copyDimension->getVisible()); - $newDimension->setOutlineLevel($copyDimension->getOutlineLevel()); - $newDimension->setCollapsed($copyDimension->getCollapsed()); - } - } + // Update worksheet: page breaks + $this->_adjustPageBreaks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows); - - // Update worksheet: breaks - $aBreaks = array_reverse($pSheet->getBreaks(), true); - foreach ($aBreaks as $key => $value) { - $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); - if ($key != $newReference) { - $pSheet->setBreak( $newReference, $value ); - $pSheet->setBreak( $key, PHPExcel_Worksheet::BREAK_NONE ); - } - } - - // Update worksheet: comments - $aComments = $pSheet->getComments(); - $aNewComments = array(); // the new array of all comments - foreach ($aComments as $key => &$value) { - $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); - $aNewComments[$newReference] = $value; - } - $pSheet->setComments($aNewComments); // replace the comments array + // Update worksheet: comments + $this->_adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows); // Update worksheet: hyperlinks - $aHyperlinkCollection = array_reverse($pSheet->getHyperlinkCollection(), true); - foreach ($aHyperlinkCollection as $key => $value) { - $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); - if ($key != $newReference) { - $pSheet->setHyperlink( $newReference, $value ); - $pSheet->setHyperlink( $key, null ); - } - } - + $this->_adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows); // Update worksheet: data validations - $aDataValidationCollection = array_reverse($pSheet->getDataValidationCollection(), true); - foreach ($aDataValidationCollection as $key => $value) { - $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); - if ($key != $newReference) { - $pSheet->setDataValidation( $newReference, $value ); - $pSheet->setDataValidation( $key, null ); - } - } - + $this->_adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows); // Update worksheet: merge cells - $aMergeCells = $pSheet->getMergeCells(); - $aNewMergeCells = array(); // the new array of all merge cells - foreach ($aMergeCells as $key => &$value) { - $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); - $aNewMergeCells[$newReference] = $newReference; - } - $pSheet->setMergeCells($aNewMergeCells); // replace the merge cells array - + $this->_adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows); // Update worksheet: protected cells - $aProtectedCells = array_reverse($pSheet->getProtectedCells(), true); - foreach ($aProtectedCells as $key => $value) { - $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows); - if ($key != $newReference) { - $pSheet->protectCells( $newReference, $value, true ); - $pSheet->unprotectCells( $key ); - } - } - + $this->_adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows); // Update worksheet: autofilter $autoFilter = $pSheet->getAutoFilter(); @@ -323,7 +549,7 @@ class PHPExcel_ReferenceHelper if ($pNumCols != 0) { $autoFilterColumns = array_keys($autoFilter->getColumns()); if (count($autoFilterColumns) > 0) { - list($column,$row) = sscanf($pBefore,'%[A-Z]%d'); + sscanf($pBefore,'%[A-Z]%d', $column, $row); $columnIndex = PHPExcel_Cell::columnIndexFromString($column); list($rangeStart,$rangeEnd) = PHPExcel_Cell::rangeBoundaries($autoFilterRange); if ($columnIndex <= $rangeEnd[0]) { @@ -374,19 +600,16 @@ class PHPExcel_ReferenceHelper $pSheet->setAutoFilter( $this->updateCellReference($autoFilterRange, $pBefore, $pNumCols, $pNumRows) ); } - // Update worksheet: freeze pane if ($pSheet->getFreezePane() != '') { $pSheet->freezePane( $this->updateCellReference($pSheet->getFreezePane(), $pBefore, $pNumCols, $pNumRows) ); } - // Page setup if ($pSheet->getPageSetup()->isPrintAreaSet()) { $pSheet->getPageSetup()->setPrintArea( $this->updateCellReference($pSheet->getPageSetup()->getPrintArea(), $pBefore, $pNumCols, $pNumRows) ); } - // Update worksheet: drawings $aDrawings = $pSheet->getDrawingCollection(); foreach ($aDrawings as $objDrawing) { @@ -396,7 +619,6 @@ class PHPExcel_ReferenceHelper } } - // Update workbook: named ranges if (count($pSheet->getParent()->getNamedRanges()) > 0) { foreach ($pSheet->getParent()->getNamedRanges() as $namedRange) { diff --git a/Classes/PHPExcel/Shared/Date.php b/Classes/PHPExcel/Shared/Date.php index d74e967..68076ce 100644 --- a/Classes/PHPExcel/Shared/Date.php +++ b/Classes/PHPExcel/Shared/Date.php @@ -253,7 +253,7 @@ class PHPExcel_Shared_Date */ public static function isDateTime(PHPExcel_Cell $pCell) { return self::isDateTimeFormat( - $pCell->getParent()->getStyle( + $pCell->getWorksheet()->getStyle( $pCell->getCoordinate() )->getNumberFormat() ); diff --git a/Classes/PHPExcel/Shared/String.php b/Classes/PHPExcel/Shared/String.php index 3e495e8..3897433 100644 --- a/Classes/PHPExcel/Shared/String.php +++ b/Classes/PHPExcel/Shared/String.php @@ -482,7 +482,7 @@ class PHPExcel_Shared_String } /** - * Convert string from one encoding to another. First try iconv, then mbstring, or no convertion + * Convert string from one encoding to another. First try mbstring, then iconv, finally strlen * * @param string $value * @param string $to Encoding to convert to, e.g. 'UTF-8' @@ -548,20 +548,20 @@ class PHPExcel_Shared_String */ public static function CountCharacters($value, $enc = 'UTF-8') { - if (self::getIsIconvEnabled()) { - return iconv_strlen($value, $enc); - } - if (self::getIsMbstringEnabled()) { return mb_strlen($value, $enc); } + if (self::getIsIconvEnabled()) { + return iconv_strlen($value, $enc); + } + // else strlen return strlen($value); } /** - * Get a substring of a UTF-8 encoded string + * Get a substring of a UTF-8 encoded string. First try mbstring, then iconv, finally strlen * * @param string $pValue UTF-8 encoded string * @param int $start Start offset @@ -570,14 +570,14 @@ class PHPExcel_Shared_String */ public static function Substring($pValue = '', $pStart = 0, $pLength = 0) { - if (self::getIsIconvEnabled()) { - return iconv_substr($pValue, $pStart, $pLength, 'UTF-8'); - } - if (self::getIsMbstringEnabled()) { return mb_substr($pValue, $pStart, $pLength, 'UTF-8'); } + if (self::getIsIconvEnabled()) { + return iconv_substr($pValue, $pStart, $pLength, 'UTF-8'); + } + // else substr return substr($pValue, $pStart, $pLength); } diff --git a/Classes/PHPExcel/Style/Color.php b/Classes/PHPExcel/Style/Color.php index ac004db..d1aaeb0 100644 --- a/Classes/PHPExcel/Style/Color.php +++ b/Classes/PHPExcel/Style/Color.php @@ -263,11 +263,7 @@ class PHPExcel_Style_Color extends PHPExcel_Style_Supervisor implements PHPExcel * @return string The red colour component */ public static function getRed($RGB,$hex=TRUE) { - if (strlen($RGB) == 8) { - return self::_getColourComponent($RGB, 2, $hex); - } elseif (strlen($RGB) == 6) { - return self::_getColourComponent($RGB, 0, $hex); - } + return self::_getColourComponent($RGB, strlen($RGB) - 6, $hex); } /** @@ -279,11 +275,7 @@ class PHPExcel_Style_Color extends PHPExcel_Style_Supervisor implements PHPExcel * @return string The green colour component */ public static function getGreen($RGB,$hex=TRUE) { - if (strlen($RGB) == 8) { - return self::_getColourComponent($RGB, 4, $hex); - } elseif (strlen($RGB) == 6) { - return self::_getColourComponent($RGB, 2, $hex); - } + return self::_getColourComponent($RGB, strlen($RGB) - 4, $hex); } /** @@ -295,11 +287,7 @@ class PHPExcel_Style_Color extends PHPExcel_Style_Supervisor implements PHPExcel * @return string The blue colour component */ public static function getBlue($RGB,$hex=TRUE) { - if (strlen($RGB) == 8) { - return self::_getColourComponent($RGB, 6, $hex); - } elseif (strlen($RGB) == 6) { - return self::_getColourComponent($RGB, 4, $hex); - } + return self::_getColourComponent($RGB, strlen($RGB) - 2, $hex); } /** diff --git a/Classes/PHPExcel/Style/NumberFormat.php b/Classes/PHPExcel/Style/NumberFormat.php index ed6ace9..dde1ad9 100644 --- a/Classes/PHPExcel/Style/NumberFormat.php +++ b/Classes/PHPExcel/Style/NumberFormat.php @@ -439,7 +439,7 @@ class PHPExcel_Style_NumberFormat extends PHPExcel_Style_Supervisor implements P * @param array $callBack Callback function for additional formatting of string * @return string Formatted string */ - public static function toFormattedString($value = '', $format = '', $callBack = null) + public static function toFormattedString($value = PHPExcel_Style_NumberFormat::FORMAT_GENERAL, $format = '', $callBack = null) { // For now we do not treat strings although section 4 of a format code affects strings if (!is_numeric($value)) return $value; diff --git a/Classes/PHPExcel/Worksheet.php b/Classes/PHPExcel/Worksheet.php index 2cdaca7..51cb4e7 100644 --- a/Classes/PHPExcel/Worksheet.php +++ b/Classes/PHPExcel/Worksheet.php @@ -376,22 +376,32 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * typically so that the worksheet object can be unset * */ - public function disconnectCells() - { - $this->_cellCollection->unsetWorksheetCells(); - $this->_cellCollection = null; - + public function disconnectCells() { + if ( $this->_cellCollection !== NULL){ + $this->_cellCollection->unsetWorksheetCells(); + $this->_cellCollection = NULL; + } // detach ourself from the workbook, so that it can then delete this worksheet successfully $this->_parent = null; } /** + * Code to execute when this worksheet is unset() + * + */ + function __destruct() { + PHPExcel_Calculation::getInstance($this->_parent) + ->clearCalculationCacheForWorksheet($this->_title); + + $this->disconnectCells(); + } + + /** * Return the cache controller for the cell collection * * @return PHPExcel_CachedObjectStorage_xxx */ - public function getCellCacheController() - { + public function getCellCacheController() { return $this->_cellCollection; } // function getCellCacheController() @@ -698,21 +708,22 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable // loop through all cells in the worksheet foreach ($this->getCellCollection(false) as $cellID) { $cell = $this->getCell($cellID); - if (isset($autoSizes[$cell->getColumn()])) { + if (isset($autoSizes[$this->_cellCollection->getCurrentColumn()])) { // Determine width if cell does not participate in a merge - if (!isset($isMergeCell[$cell->getCoordinate()])) { + if (!isset($isMergeCell[$this->_cellCollection->getCurrentAddress()])) { // Calculated value - $cellValue = $cell->getCalculatedValue(); - // To formatted string - $cellValue = PHPExcel_Style_NumberFormat::toFormattedString($cellValue, $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()); + $cellValue = PHPExcel_Style_NumberFormat::toFormattedString( + $cell->getCalculatedValue(), + $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode() + ); - $autoSizes[$cell->getColumn()] = max( - (float)$autoSizes[$cell->getColumn()], + $autoSizes[$this->_cellCollection->getCurrentColumn()] = max( + (float) $autoSizes[$this->_cellCollection->getCurrentColumn()], (float)PHPExcel_Shared_Font::calculateColumnWidth( - $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(), + $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(), $cellValue, - $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(), + $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(), $this->getDefaultStyle()->getFont() ) ); @@ -735,8 +746,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * * @return PHPExcel */ - public function getParent() - { + public function getParent() { return $this->_parent; } @@ -746,8 +756,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * @param PHPExcel $parent * @return PHPExcel_Worksheet */ - public function rebindParent(PHPExcel $parent) - { + public function rebindParent(PHPExcel $parent) { $namedRanges = $this->_parent->getNamedRanges(); foreach ($namedRanges as $namedRange) { $parent->addNamedRange($namedRange); @@ -780,7 +789,6 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * This should be left as the default true, unless you are * certain that no formula cells on any worksheet contain * references to this worksheet - * * @return PHPExcel_Worksheet */ public function setTitle($pValue = 'Worksheet', $updateFormulaCellReferences = true) @@ -796,16 +804,16 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable // Old title $oldTitle = $this->getTitle(); - if ($this->getParent()) { + if ($this->_parent) { // Is there already such sheet name? - if ($this->getParent()->sheetNameExists($pValue)) { + if ($this->_parent->sheetNameExists($pValue)) { // Use name, but append with lowest possible integer if (PHPExcel_Shared_String::CountCharacters($pValue) > 29) { $pValue = PHPExcel_Shared_String::Substring($pValue,0,29); } $i = 1; - while ($this->getParent()->sheetNameExists($pValue . ' ' . $i)) { + while ($this->_parent->sheetNameExists($pValue . ' ' . $i)) { ++$i; if ($i == 10) { if (PHPExcel_Shared_String::CountCharacters($pValue) > 28) { @@ -827,11 +835,13 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable $this->_title = $pValue; $this->_dirty = true; - if ($this->getParent()) { + if ($this->_parent) { // New title $newTitle = $this->getTitle(); + PHPExcel_Calculation::getInstance($this->_parent) + ->renameCalculationCacheForWorksheet($oldTitle, $newTitle); if ($updateFormulaCellReferences) - PHPExcel_ReferenceHelper::getInstance()->updateNamedFormulas($this->getParent(), $oldTitle, $newTitle); + PHPExcel_ReferenceHelper::getInstance()->updateNamedFormulas($this->_parent, $oldTitle, $newTitle); } return $this; @@ -842,8 +852,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * * @return string Sheet state (visible, hidden, veryHidden) */ - public function getSheetState() - { + public function getSheetState() { return $this->_sheetState; } @@ -853,8 +862,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * @param string $value Sheet state (visible, hidden, veryHidden) * @return PHPExcel_Worksheet */ - public function setSheetState($value = PHPExcel_Worksheet::SHEETSTATE_VISIBLE) - { + public function setSheetState($value = PHPExcel_Worksheet::SHEETSTATE_VISIBLE) { $this->_sheetState = $value; return $this; } @@ -1099,7 +1107,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable // Worksheet reference? if (strpos($pCoordinate, '!') !== false) { $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pCoordinate, true); - return $this->getParent()->getSheetByName($worksheetReference[0])->getCell($worksheetReference[1]); + return $this->_parent->getSheetByName($worksheetReference[0])->getCell($worksheetReference[1]); } // Named range? @@ -1125,7 +1133,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable // Coordinates $aCoordinates = PHPExcel_Cell::coordinateFromString($pCoordinate); - $cell = $this->_cellCollection->addCacheData($pCoordinate,new PHPExcel_Cell($pCoordinate, NULL, PHPExcel_Cell_DataType::TYPE_NULL, $this)); + $cell = $this->_cellCollection->addCacheData($pCoordinate,new PHPExcel_Cell(NULL, PHPExcel_Cell_DataType::TYPE_NULL, $this)); $this->_cellCollectionIsSorted = false; if (PHPExcel_Cell::columnIndexFromString($this->_cachedHighestColumn) < PHPExcel_Cell::columnIndexFromString($aCoordinates[0])) @@ -1165,7 +1173,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable $coordinate = $columnLetter . $pRow; if (!$this->_cellCollection->isDataSet($coordinate)) { - $cell = $this->_cellCollection->addCacheData($coordinate, new PHPExcel_Cell($coordinate, NULL, PHPExcel_Cell_DataType::TYPE_NULL, $this)); + $cell = $this->_cellCollection->addCacheData($coordinate, new PHPExcel_Cell(NULL, PHPExcel_Cell_DataType::TYPE_NULL, $this)); $this->_cellCollectionIsSorted = false; if (PHPExcel_Cell::columnIndexFromString($this->_cachedHighestColumn) < $pColumn) @@ -1191,7 +1199,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable // Worksheet reference? if (strpos($pCoordinate, '!') !== false) { $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pCoordinate, true); - return $this->getParent()->getSheetByName($worksheetReference[0])->cellExists($worksheetReference[1]); + return $this->_parent->getSheetByName($worksheetReference[0])->cellExists($worksheetReference[1]); } // Named range? @@ -1511,7 +1519,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * * Please note that this will overwrite existing cell styles for cells in range! * - * @param [PHPExcel_Style_Conditional] $pCellStyle Cell style to duplicate + * @param array of PHPExcel_Style_Conditional $pCellStyle Cell style to duplicate * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1") * @throws PHPExcel_Exception * @return PHPExcel_Worksheet @@ -1596,7 +1604,13 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable $pCell = strtoupper($pCell); if ($pCell != '') { - $this->_breaks[$pCell] = $pBreak; + if ($pBreak == PHPExcel_Worksheet::BREAK_NONE) { + if (isset($this->_breaks[$pCell])) { + unset($this->_breaks[$pCell]); + } + } else { + $this->_breaks[$pCell] = $pBreak; + } } else { throw new PHPExcel_Exception('No cell coordinate specified.'); } @@ -2168,7 +2182,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable /** * Set comments array for the entire sheet. * - * @param [PHPExcel_Comment] $pValue + * @param array of PHPExcel_Comment * @return PHPExcel_Worksheet */ public function setComments($pValue = array()) @@ -2386,10 +2400,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * True - Return rows and columns indexed by their actual row and column IDs * @return array */ - public function rangeToArray( - $pRange = 'A1', $nullValue = null, $calculateFormulas = true, - $formatData = true, $returnCellRef = false) - { + public function rangeToArray($pRange = 'A1', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) { // Returnvalue $returnValue = array(); // Identify the range that we need to extract from the worksheet @@ -2427,7 +2438,10 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable if ($formatData) { $style = $this->_parent->getCellXfByIndex($cell->getXfIndex()); $returnValue[$rRef][$cRef] = PHPExcel_Style_NumberFormat::toFormattedString( - $returnValue[$rRef][$cRef], $style->getNumberFormat()->getFormatCode() + $returnValue[$rRef][$cRef], + ($style->getNumberFormat()) ? + $style->getNumberFormat()->getFormatCode() : + PHPExcel_Style_NumberFormat::FORMAT_GENERAL ); } } else { @@ -2458,20 +2472,14 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * @return array * @throws PHPExcel_Exception */ - public function namedRangeToArray( - $pNamedRange = '', $nullValue = null, $calculateFormulas = true, - $formatData = true, $returnCellRef = false - ) - { + public function namedRangeToArray($pNamedRange = '', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) { $namedRange = PHPExcel_NamedRange::resolveRange($pNamedRange, $this); if ($namedRange !== NULL) { $pWorkSheet = $namedRange->getWorksheet(); $pCellRange = $namedRange->getRange(); - return $pWorkSheet->rangeToArray( - $pCellRange, - $nullValue, $calculateFormulas, $formatData, $returnCellRef - ); + return $pWorkSheet->rangeToArray( $pCellRange, + $nullValue, $calculateFormulas, $formatData, $returnCellRef); } throw new PHPExcel_Exception('Named Range '.$pNamedRange.' does not exist.'); @@ -2488,8 +2496,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * True - Return rows and columns indexed by their actual row and column IDs * @return array */ - public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) - { + public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) { // Garbage collect... $this->garbageCollect(); @@ -2497,10 +2504,8 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable $maxCol = $this->getHighestColumn(); $maxRow = $this->getHighestRow(); // Return - return $this->rangeToArray( - 'A1:'.$maxCol.$maxRow, - $nullValue, $calculateFormulas, $formatData, $returnCellRef - ); + return $this->rangeToArray( 'A1:'.$maxCol.$maxRow, + $nullValue, $calculateFormulas, $formatData, $returnCellRef); } /** @@ -2509,8 +2514,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * @param integer $startRow The row number at which to start iterating * @return PHPExcel_Worksheet_RowIterator */ - public function getRowIterator($startRow = 1) - { + public function getRowIterator($startRow = 1) { return new PHPExcel_Worksheet_RowIterator($this,$startRow); } @@ -2519,8 +2523,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * * @return PHPExcel_Worksheet */ - public function garbageCollect() - { + public function garbageCollect() { // Flush cache $this->_cellCollection->getCacheData('A1'); // Build a reference table from images @@ -2564,8 +2567,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * * @return string Hash code */ - public function getHashCode() - { + public function getHashCode() { if ($this->_dirty) { $this->_hash = md5( $this->_title . $this->_autoFilter . @@ -2587,8 +2589,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * @param bool $returnRange Return range? (see example) * @return mixed */ - public static function extractSheetTitle($pRange, $returnRange = false) - { + public static function extractSheetTitle($pRange, $returnRange = false) { // Sheet title included? if (($sep = strpos($pRange, '!')) === false) { return ''; @@ -2719,8 +2720,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * @param string $range * @return string Adjusted range value */ - public function shrinkRangeToFit($range) - { + public function shrinkRangeToFit($range) { $maxCol = $this->getHighestColumn(); $maxRow = $this->getHighestRow(); $maxCol = PHPExcel_Cell::columnIndexFromString($maxCol); @@ -2782,8 +2782,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable * * @return PHPExcel_Worksheet */ - public function copy() - { + public function copy() { $copied = clone $this; return $copied; @@ -2792,8 +2791,7 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable /** * Implement PHP __clone to create a deep clone, not just a shallow copy. */ - public function __clone() - { + public function __clone() { foreach ($this as $key => $val) { if ($key == '_parent') { continue; diff --git a/Classes/PHPExcel/Worksheet/AutoFilter.php b/Classes/PHPExcel/Worksheet/AutoFilter.php index c39bfbf..ae05875 100644 --- a/Classes/PHPExcel/Worksheet/AutoFilter.php +++ b/Classes/PHPExcel/Worksheet/AutoFilter.php @@ -725,7 +725,7 @@ class PHPExcel_Worksheet_AutoFilter // Date based if ($dynamicRuleType{0} == 'M' || $dynamicRuleType{0} == 'Q') { // Month or Quarter - list($periodType,$period) = sscanf($dynamicRuleType,'%[A-Z]%d'); + sscanf($dynamicRuleType,'%[A-Z]%d', $periodType, $period); if ($periodType == 'M') { $ruleValues = array($period); } else { diff --git a/Classes/PHPExcel/Writer/CSV.php b/Classes/PHPExcel/Writer/CSV.php index 2ff5b41..3563cb2 100644 --- a/Classes/PHPExcel/Writer/CSV.php +++ b/Classes/PHPExcel/Writer/CSV.php @@ -102,8 +102,8 @@ class PHPExcel_Writer_CSV extends PHPExcel_Writer_Abstract implements PHPExcel_W // Fetch sheet $sheet = $this->_phpExcel->getSheet($this->_sheetIndex); - $saveDebugLog = PHPExcel_Calculation::getInstance()->writeDebugLog; - PHPExcel_Calculation::getInstance()->writeDebugLog = false; + $saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->getWriteDebugLog(); + PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog(FALSE); $saveArrayReturnType = PHPExcel_Calculation::getArrayReturnType(); PHPExcel_Calculation::setArrayReturnType(PHPExcel_Calculation::RETURN_ARRAY_AS_VALUE); @@ -139,7 +139,7 @@ class PHPExcel_Writer_CSV extends PHPExcel_Writer_Abstract implements PHPExcel_W fclose($fileHandle); PHPExcel_Calculation::setArrayReturnType($saveArrayReturnType); - PHPExcel_Calculation::getInstance()->writeDebugLog = $saveDebugLog; + PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog($saveDebugLog); } /** diff --git a/Classes/PHPExcel/Writer/Excel2007.php b/Classes/PHPExcel/Writer/Excel2007.php index a74350c..f09bb27 100644 --- a/Classes/PHPExcel/Writer/Excel2007.php +++ b/Classes/PHPExcel/Writer/Excel2007.php @@ -179,8 +179,8 @@ class PHPExcel_Writer_Excel2007 extends PHPExcel_Writer_Abstract implements PHPE } } - $saveDebugLog = PHPExcel_Calculation::getInstance()->writeDebugLog; - PHPExcel_Calculation::getInstance()->writeDebugLog = false; + $saveDebugLog = PHPExcel_Calculation::getInstance($this->_spreadSheet)->getDebugLog()->getWriteDebugLog(); + PHPExcel_Calculation::getInstance($this->_spreadSheet)->getDebugLog()->setWriteDebugLog(FALSE); $saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType(); PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL); @@ -341,7 +341,7 @@ class PHPExcel_Writer_Excel2007 extends PHPExcel_Writer_Abstract implements PHPE } PHPExcel_Calculation_Functions::setReturnDateType($saveDateReturnType); - PHPExcel_Calculation::getInstance()->writeDebugLog = $saveDebugLog; + PHPExcel_Calculation::getInstance($this->_spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); // Close file if ($objZip->close() === false) { diff --git a/Classes/PHPExcel/Writer/Excel2007/Chart.php b/Classes/PHPExcel/Writer/Excel2007/Chart.php index 1a72738..9ef40f5 100644 --- a/Classes/PHPExcel/Writer/Excel2007/Chart.php +++ b/Classes/PHPExcel/Writer/Excel2007/Chart.php @@ -230,6 +230,7 @@ class PHPExcel_Writer_Excel2007_Chart extends PHPExcel_Writer_Excel2007_WriterPa } $id1 = $id2 = 0; + $this->_seriesIndex = 0; $objWriter->startElement('c:plotArea'); $layout = $plotArea->getLayout(); @@ -737,11 +738,11 @@ class PHPExcel_Writer_Excel2007_Chart extends PHPExcel_Writer_Excel2007_WriterPa $objWriter->startElement('c:ser'); $objWriter->startElement('c:idx'); - $objWriter->writeAttribute('val', $plotSeriesIdx); + $objWriter->writeAttribute('val', $this->_seriesIndex + $plotSeriesIdx); $objWriter->endElement(); $objWriter->startElement('c:order'); - $objWriter->writeAttribute('val', $plotSeriesRef); + $objWriter->writeAttribute('val', $this->_seriesIndex + $plotSeriesRef); $objWriter->endElement(); if (($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART) || @@ -865,6 +866,8 @@ class PHPExcel_Writer_Excel2007_Chart extends PHPExcel_Writer_Excel2007_WriterPa $objWriter->endElement(); } + + $this->_seriesIndex += $plotSeriesIdx + 1; } /** diff --git a/Classes/PHPExcel/Writer/Excel2007/Worksheet.php b/Classes/PHPExcel/Writer/Excel2007/Worksheet.php index 977f172..cb1437d 100644 --- a/Classes/PHPExcel/Writer/Excel2007/Worksheet.php +++ b/Classes/PHPExcel/Writer/Excel2007/Worksheet.php @@ -1074,12 +1074,9 @@ class PHPExcel_Writer_Excel2007_Worksheet extends PHPExcel_Writer_Excel2007_Writ $objWriter->writeAttribute('t', $mappedType); break; case 'f': // Formula - $calculatedValue = null; - if ($this->getParentWriter()->getPreCalculateFormulas()) { - $calculatedValue = $pCell->getCalculatedValue(); - } else { - $calculatedValue = $cellValue; - } + $calculatedValue = ($this->getParentWriter()->getPreCalculateFormulas()) ? + $pCell->getCalculatedValue() : + $cellValue; if (is_string($calculatedValue)) { $objWriter->writeAttribute('t', 'str'); } @@ -1125,7 +1122,7 @@ class PHPExcel_Writer_Excel2007_Worksheet extends PHPExcel_Writer_Excel2007_Writ } if ($this->getParentWriter()->getOffice2003Compatibility() === false) { if ($this->getParentWriter()->getPreCalculateFormulas()) { - $calculatedValue = $pCell->getCalculatedValue(); +// $calculatedValue = $pCell->getCalculatedValue(); if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) != '#') { $objWriter->writeElement('v', PHPExcel_Shared_String::FormatNumber($calculatedValue)); } else { diff --git a/Classes/PHPExcel/Writer/Excel5.php b/Classes/PHPExcel/Writer/Excel5.php index 459653c..37f5b85 100644 --- a/Classes/PHPExcel/Writer/Excel5.php +++ b/Classes/PHPExcel/Writer/Excel5.php @@ -120,8 +120,8 @@ class PHPExcel_Writer_Excel5 extends PHPExcel_Writer_Abstract implements PHPExce // garbage collect $this->_phpExcel->garbageCollect(); - $saveDebugLog = PHPExcel_Calculation::getInstance()->writeDebugLog; - PHPExcel_Calculation::getInstance()->writeDebugLog = false; + $saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->getWriteDebugLog(); + PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog(FALSE); $saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType(); PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL); @@ -226,7 +226,7 @@ class PHPExcel_Writer_Excel5 extends PHPExcel_Writer_Abstract implements PHPExce $res = $root->save($pFilename); PHPExcel_Calculation_Functions::setReturnDateType($saveDateReturnType); - PHPExcel_Calculation::getInstance()->writeDebugLog = $saveDebugLog; + PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog($saveDebugLog); } /** @@ -596,7 +596,6 @@ class PHPExcel_Writer_Excel5 extends PHPExcel_Writer_Abstract implements PHPExce // GKPIDDSI_CATEGORY : Category if($this->_phpExcel->getProperties()->getCategory()){ $dataProp = $this->_phpExcel->getProperties()->getCategory(); - $dataProp = 'Test result file'; $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x02), 'offset' => array('pack' => 'V'), 'type' => array('pack' => 'V', 'data' => 0x1E), @@ -933,4 +932,4 @@ class PHPExcel_Writer_Excel5 extends PHPExcel_Writer_Abstract implements PHPExce return $data; } -} \ No newline at end of file +} diff --git a/Classes/PHPExcel/Writer/HTML.php b/Classes/PHPExcel/Writer/HTML.php index d07dbd2..8744569 100644 --- a/Classes/PHPExcel/Writer/HTML.php +++ b/Classes/PHPExcel/Writer/HTML.php @@ -152,8 +152,8 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_ // garbage collect $this->_phpExcel->garbageCollect(); - $saveDebugLog = PHPExcel_Calculation::getInstance()->writeDebugLog; - PHPExcel_Calculation::getInstance()->writeDebugLog = false; + $saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->getWriteDebugLog(); + PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog(FALSE); $saveArrayReturnType = PHPExcel_Calculation::getArrayReturnType(); PHPExcel_Calculation::setArrayReturnType(PHPExcel_Calculation::RETURN_ARRAY_AS_VALUE); @@ -184,7 +184,7 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_ fclose($fileHandle); PHPExcel_Calculation::setArrayReturnType($saveArrayReturnType); - PHPExcel_Calculation::getInstance()->writeDebugLog = $saveDebugLog; + PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog($saveDebugLog); } /** @@ -394,7 +394,6 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_ // calculate start of , $tbodyStart = $rowMin; - $tbodyEnd = $rowMax; $theadStart = $theadEnd = 0; // default: no no if ($sheet->getPageSetup()->isRowsToRepeatAtTopSet()) { $rowsToRepeatAtTop = $sheet->getPageSetup()->getRowsToRepeatAtTop(); @@ -441,14 +440,12 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_ if ($row == $theadEnd) { $html .= ' ' . PHP_EOL; } - - // ? - if ($row == $tbodyEnd) { - $html .= ' ' . PHP_EOL; - } } $html .= $this->_extendRowsForChartsAndImages($sheet, $row); + // Close table body. + $html .= ' ' . PHP_EOL; + // Write table footer $html .= $this->_generateTableFooter(); @@ -520,9 +517,9 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_ $chartCol = PHPExcel_Cell::columnIndexFromString($chartTL[0]); if ($chartTL[1] > $rowMax) { $rowMax = $chartTL[1]; - } - if ($chartCol > PHPExcel_Cell::columnIndexFromString($colMax)) { - $colMax = $chartTL[0]; + if ($chartCol > PHPExcel_Cell::columnIndexFromString($colMax)) { + $colMax = $chartTL[0]; + } } } } @@ -534,15 +531,15 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_ $imageCol = PHPExcel_Cell::columnIndexFromString($imageTL[0]); if ($imageTL[1] > $rowMax) { $rowMax = $imageTL[1]; - } - if ($imageCol > PHPExcel_Cell::columnIndexFromString($colMax)) { - $colMax = $imageTL[0]; + if ($imageCol > PHPExcel_Cell::columnIndexFromString($colMax)) { + $colMax = $imageTL[0]; + } } } } $html = ''; $colMax++; - while ($row <= $rowMax) { + while ($row < $rowMax) { $html .= ''; for ($col = 'A'; $col != $colMax; ++$col) { $html .= ''; @@ -966,7 +963,10 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_ */ private function _createCSSStyleBorder(PHPExcel_Style_Border $pStyle) { // Create CSS - $css = $this->_mapBorderStyle($pStyle->getBorderStyle()) . ' #' . $pStyle->getColor()->getRGB(); +// $css = $this->_mapBorderStyle($pStyle->getBorderStyle()) . ' #' . $pStyle->getColor()->getRGB(); + // Create CSS - add !important to non-none border styles for merged cells + $borderStyle = $this->_mapBorderStyle($pStyle->getBorderStyle()); + $css = $borderStyle . ' #' . $pStyle->getColor()->getRGB() . (($borderStyle == 'none') ? '' : ' !important'); // Return return $css; @@ -1016,7 +1016,8 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_ // Construct HTML $html = ''; - + $html .= $this->_setMargins($pSheet); + if (!$this->_useInlineCss) { $gridlines = $pSheet->getShowGridLines() ? ' gridlines' : ''; $html .= ' ' . PHP_EOL; @@ -1228,6 +1229,11 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_ $spans = $this->_isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]; $rowSpan = $spans['rowspan']; $colSpan = $spans['colspan']; + + // Also apply style from last cell in merge to fix borders - + // relies on !important for non-none border declarations in _createCSSStyleBorder + $endCellCoord = PHPExcel_Cell::stringFromColumnIndex($colNum + $colSpan - 1) . ($pRow + $rowSpan); + $cssClass .= ' style' . $pSheet->getCell($endCellCoord)->getXfIndex(); } // Write @@ -1495,4 +1501,27 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_ $this->_spansAreCalculated = true; } + private function _setMargins(PHPExcel_Worksheet $pSheet) { + $htmlPage = '@page { '; + $htmlBody = 'body { '; + + $left = PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getLeft()) . 'in; '; + $htmlPage .= 'left-margin: ' . $left; + $htmlBody .= 'left-margin: ' . $left; + $right = PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getRight()) . 'in; '; + $htmlPage .= 'right-margin: ' . $right; + $htmlBody .= 'right-margin: ' . $right; + $top = PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getTop()) . 'in; '; + $htmlPage .= 'top-margin: ' . $top; + $htmlBody .= 'top-margin: ' . $top; + $bottom = PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getBottom()) . 'in; '; + $htmlPage .= 'bottom-margin: ' . $bottom; + $htmlBody .= 'bottom-margin: ' . $bottom; + + $htmlPage .= "}\n"; + $htmlBody .= "}\n"; + + return "\n"; + } + } diff --git a/Examples/06largescale-with-cellcaching-sqlite.php b/Examples/06largescale-with-cellcaching-sqlite.php new file mode 100644 index 0000000..fff3695 --- /dev/null +++ b/Examples/06largescale-with-cellcaching-sqlite.php @@ -0,0 +1,126 @@ +'); + +date_default_timezone_set('Europe/London'); + +/** Include PHPExcel */ +require_once '../Classes/PHPExcel.php'; + +$cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_to_sqlite; +PHPExcel_Settings::setCacheStorageMethod($cacheMethod); +echo date('H:i:s') , " Enable Cell Caching using " , $cacheMethod , " method" , EOL; + + +// Create new PHPExcel object +echo date('H:i:s') , " Create new PHPExcel object" , EOL; +$objPHPExcel = new PHPExcel(); + +// Set document properties +echo date('H:i:s') , " Set properties" , EOL; +$objPHPExcel->getProperties()->setCreator("Maarten Balliauw") + ->setLastModifiedBy("Maarten Balliauw") + ->setTitle("Office 2007 XLSX Test Document") + ->setSubject("Office 2007 XLSX Test Document") + ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.") + ->setKeywords("office 2007 openxml php") + ->setCategory("Test result file"); + + +// Create a first sheet +echo date('H:i:s') , " Add data" , EOL; +$objPHPExcel->setActiveSheetIndex(0); +$objPHPExcel->getActiveSheet()->setCellValue('A1', "Firstname"); +$objPHPExcel->getActiveSheet()->setCellValue('B1', "Lastname"); +$objPHPExcel->getActiveSheet()->setCellValue('C1', "Phone"); +$objPHPExcel->getActiveSheet()->setCellValue('D1', "Fax"); +$objPHPExcel->getActiveSheet()->setCellValue('E1', "Is Client ?"); + + +// Hide "Phone" and "fax" column +echo date('H:i:s') , " Hide 'Phone' and 'fax' columns" , EOL; +$objPHPExcel->getActiveSheet()->getColumnDimension('C')->setVisible(false); +$objPHPExcel->getActiveSheet()->getColumnDimension('D')->setVisible(false); + + +// Set outline levels +echo date('H:i:s') , " Set outline levels" , EOL; +$objPHPExcel->getActiveSheet()->getColumnDimension('E')->setOutlineLevel(1) + ->setVisible(false) + ->setCollapsed(true); + +// Freeze panes +echo date('H:i:s') , " Freeze panes" , EOL; +$objPHPExcel->getActiveSheet()->freezePane('A2'); + + +// Rows to repeat at top +echo date('H:i:s') , " Rows to repeat at top" , EOL; +$objPHPExcel->getActiveSheet()->getPageSetup()->setRowsToRepeatAtTopByStartAndEnd(1, 1); + + +// Add data +for ($i = 2; $i <= 5000; $i++) { + $objPHPExcel->getActiveSheet()->setCellValue('A' . $i, "FName $i") + ->setCellValue('B' . $i, "LName $i") + ->setCellValue('C' . $i, "PhoneNo $i") + ->setCellValue('D' . $i, "FaxNo $i") + ->setCellValue('E' . $i, true); +} + + +// Set active sheet index to the first sheet, so Excel opens this as the first sheet +$objPHPExcel->setActiveSheetIndex(0); + + +// Save Excel 2007 file +echo date('H:i:s') , " Write to Excel2007 format" , EOL; +$callStartTime = microtime(true); + +$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); +$objWriter->save(str_replace('.php', '.xlsx', __FILE__)); +$callEndTime = microtime(true); +$callTime = $callEndTime - $callStartTime; + +echo date('H:i:s') , " File written to " , str_replace('.php', '.xlsx', pathinfo(__FILE__, PATHINFO_BASENAME)) , EOL; +echo 'Call time to write Workbook was ' , sprintf('%.4f',$callTime) , " seconds" , EOL; +// Echo memory usage +echo date('H:i:s') , ' Current memory usage: ' , (memory_get_usage(true) / 1024 / 1024) , " MB" , EOL; + + +// Echo memory peak usage +echo date('H:i:s') , " Peak memory usage: " , (memory_get_peak_usage(true) / 1024 / 1024) , " MB" , EOL; + +// Echo done +echo date('H:i:s') , " Done writing file" , EOL; +echo 'File has been created in ' , getcwd() , EOL; diff --git a/Examples/06largescale-with-cellcaching-sqlite3.php b/Examples/06largescale-with-cellcaching-sqlite3.php new file mode 100644 index 0000000..a4c537a --- /dev/null +++ b/Examples/06largescale-with-cellcaching-sqlite3.php @@ -0,0 +1,126 @@ +'); + +date_default_timezone_set('Europe/London'); + +/** Include PHPExcel */ +require_once '../Classes/PHPExcel.php'; + +$cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_to_sqlite3; +PHPExcel_Settings::setCacheStorageMethod($cacheMethod); +echo date('H:i:s') , " Enable Cell Caching using " , $cacheMethod , " method" , EOL; + + +// Create new PHPExcel object +echo date('H:i:s') , " Create new PHPExcel object" , EOL; +$objPHPExcel = new PHPExcel(); + +// Set document properties +echo date('H:i:s') , " Set properties" , EOL; +$objPHPExcel->getProperties()->setCreator("Maarten Balliauw") + ->setLastModifiedBy("Maarten Balliauw") + ->setTitle("Office 2007 XLSX Test Document") + ->setSubject("Office 2007 XLSX Test Document") + ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.") + ->setKeywords("office 2007 openxml php") + ->setCategory("Test result file"); + + +// Create a first sheet +echo date('H:i:s') , " Add data" , EOL; +$objPHPExcel->setActiveSheetIndex(0); +$objPHPExcel->getActiveSheet()->setCellValue('A1', "Firstname"); +$objPHPExcel->getActiveSheet()->setCellValue('B1', "Lastname"); +$objPHPExcel->getActiveSheet()->setCellValue('C1', "Phone"); +$objPHPExcel->getActiveSheet()->setCellValue('D1', "Fax"); +$objPHPExcel->getActiveSheet()->setCellValue('E1', "Is Client ?"); + + +// Hide "Phone" and "fax" column +echo date('H:i:s') , " Hide 'Phone' and 'fax' columns" , EOL; +$objPHPExcel->getActiveSheet()->getColumnDimension('C')->setVisible(false); +$objPHPExcel->getActiveSheet()->getColumnDimension('D')->setVisible(false); + + +// Set outline levels +echo date('H:i:s') , " Set outline levels" , EOL; +$objPHPExcel->getActiveSheet()->getColumnDimension('E')->setOutlineLevel(1) + ->setVisible(false) + ->setCollapsed(true); + +// Freeze panes +echo date('H:i:s') , " Freeze panes" , EOL; +$objPHPExcel->getActiveSheet()->freezePane('A2'); + + +// Rows to repeat at top +echo date('H:i:s') , " Rows to repeat at top" , EOL; +$objPHPExcel->getActiveSheet()->getPageSetup()->setRowsToRepeatAtTopByStartAndEnd(1, 1); + + +// Add data +for ($i = 2; $i <= 5000; $i++) { + $objPHPExcel->getActiveSheet()->setCellValue('A' . $i, "FName $i") + ->setCellValue('B' . $i, "LName $i") + ->setCellValue('C' . $i, "PhoneNo $i") + ->setCellValue('D' . $i, "FaxNo $i") + ->setCellValue('E' . $i, true); +} + + +// Set active sheet index to the first sheet, so Excel opens this as the first sheet +$objPHPExcel->setActiveSheetIndex(0); + + +// Save Excel 2007 file +echo date('H:i:s') , " Write to Excel2007 format" , EOL; +$callStartTime = microtime(true); + +$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); +$objWriter->save(str_replace('.php', '.xlsx', __FILE__)); +$callEndTime = microtime(true); +$callTime = $callEndTime - $callStartTime; + +echo date('H:i:s') , " File written to " , str_replace('.php', '.xlsx', pathinfo(__FILE__, PATHINFO_BASENAME)) , EOL; +echo 'Call time to write Workbook was ' , sprintf('%.4f',$callTime) , " seconds" , EOL; +// Echo memory usage +echo date('H:i:s') , ' Current memory usage: ' , (memory_get_usage(true) / 1024 / 1024) , " MB" , EOL; + + +// Echo memory peak usage +echo date('H:i:s') , " Peak memory usage: " , (memory_get_peak_usage(true) / 1024 / 1024) , " MB" , EOL; + +// Echo done +echo date('H:i:s') , " Done writing file" , EOL; +echo 'File has been created in ' , getcwd() , EOL; diff --git a/Examples/12cellProtection.php b/Examples/12cellProtection.php new file mode 100644 index 0000000..caa3876 --- /dev/null +++ b/Examples/12cellProtection.php @@ -0,0 +1,107 @@ +'); + +date_default_timezone_set('Europe/London'); + +/** Include PHPExcel */ +require_once '../Classes/PHPExcel.php'; + + +// Create new PHPExcel object +echo date('H:i:s') , " Create new PHPExcel object" , EOL; +$objPHPExcel = new PHPExcel(); + +// Set document properties +echo date('H:i:s') , " Set document properties" , EOL; +$objPHPExcel->getProperties()->setCreator("Mark Baker") + ->setLastModifiedBy("Mark Baker") + ->setTitle("Office 2007 XLSX Test Document") + ->setSubject("Office 2007 XLSX Test Document") + ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.") + ->setKeywords("office 2007 openxml php") + ->setCategory("Test result file"); + + +// Add some data +echo date('H:i:s') , " Add some data" , EOL; +$objPHPExcel->setActiveSheetIndex(0); +$objPHPExcel->getActiveSheet()->setCellValue('A1', 'Crouching'); +$objPHPExcel->getActiveSheet()->setCellValue('B1', 'Tiger'); +$objPHPExcel->getActiveSheet()->setCellValue('A2', 'Hidden'); +$objPHPExcel->getActiveSheet()->setCellValue('B2', 'Dragon'); + +// Rename worksheet +echo date('H:i:s') , " Rename worksheet" , EOL; +$objPHPExcel->getActiveSheet()->setTitle('Simple'); + + +// Set document security +echo date('H:i:s') , " Set cell protection" , EOL; + + +// Set sheet security +echo date('H:i:s') , " Set sheet security" , EOL; +$objPHPExcel->getActiveSheet()->getProtection()->setSheet(true); +$objPHPExcel->getActiveSheet() + ->getStyle('A2:B2') + ->getProtection()->setLocked( + PHPExcel_Style_Protection::PROTECTION_UNPROTECTED + ); + + +// Set active sheet index to the first sheet, so Excel opens this as the first sheet +$objPHPExcel->setActiveSheetIndex(0); + + +// Save Excel 2007 file +echo date('H:i:s') , " Write to Excel2007 format" , EOL; +$callStartTime = microtime(true); + +$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); +$objWriter->save(str_replace('.php', '.xlsx', __FILE__)); +$callEndTime = microtime(true); +$callTime = $callEndTime - $callStartTime; + +echo date('H:i:s') , " File written to " , str_replace('.php', '.xlsx', pathinfo(__FILE__, PATHINFO_BASENAME)) , EOL; +echo 'Call time to write Workbook was ' , sprintf('%.4f',$callTime) , " seconds" , EOL; +// Echo memory usage +echo date('H:i:s') , ' Current memory usage: ' , (memory_get_usage(true) / 1024 / 1024) , " MB" , EOL; + + +// Echo memory peak usage +echo date('H:i:s') , " Peak memory usage: " , (memory_get_peak_usage(true) / 1024 / 1024) , " MB" , EOL; + +// Echo done +echo date('H:i:s') , " Done writing file" , EOL; +echo 'File has been created in ' , getcwd() , EOL; diff --git a/Examples/33chartcreate-area.php b/Examples/33chartcreate-area.php index 1e53b60..925ea0a 100644 --- a/Examples/33chartcreate-area.php +++ b/Examples/33chartcreate-area.php @@ -36,11 +36,9 @@ date_default_timezone_set('Europe/London'); * @version ##VERSION##, ##DATE## */ -/** Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../Classes/'); - /** PHPExcel */ -include 'PHPExcel.php'; +require_once '../Classes/PHPExcel.php'; + $objPHPExcel = new PHPExcel(); $objWorksheet = $objPHPExcel->getActiveSheet(); diff --git a/Examples/33chartcreate-bar-stacked.php b/Examples/33chartcreate-bar-stacked.php index f6465ee..0c604bb 100644 --- a/Examples/33chartcreate-bar-stacked.php +++ b/Examples/33chartcreate-bar-stacked.php @@ -36,11 +36,9 @@ date_default_timezone_set('Europe/London'); * @version ##VERSION##, ##DATE## */ -/** Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../Classes/'); - /** PHPExcel */ -include 'PHPExcel.php'; +require_once '../Classes/PHPExcel.php'; + $objPHPExcel = new PHPExcel(); $objWorksheet = $objPHPExcel->getActiveSheet(); diff --git a/Examples/33chartcreate-bar.php b/Examples/33chartcreate-bar.php index 764942f..daefd66 100644 --- a/Examples/33chartcreate-bar.php +++ b/Examples/33chartcreate-bar.php @@ -36,11 +36,9 @@ date_default_timezone_set('Europe/London'); * @version ##VERSION##, ##DATE## */ -/** Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../Classes/'); - /** PHPExcel */ -include 'PHPExcel.php'; +require_once '../Classes/PHPExcel.php'; + $objPHPExcel = new PHPExcel(); $objWorksheet = $objPHPExcel->getActiveSheet(); diff --git a/Examples/33chartcreate-column-2.php b/Examples/33chartcreate-column-2.php index 9210499..959712b 100644 --- a/Examples/33chartcreate-column-2.php +++ b/Examples/33chartcreate-column-2.php @@ -36,11 +36,9 @@ date_default_timezone_set('Europe/London'); * @version ##VERSION##, ##DATE## */ -/** Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../Classes/'); - /** PHPExcel */ -include 'PHPExcel.php'; +require_once '../Classes/PHPExcel.php'; + $objPHPExcel = new PHPExcel(); $objWorksheet = $objPHPExcel->getActiveSheet(); diff --git a/Examples/33chartcreate-column.php b/Examples/33chartcreate-column.php index 6feacf9..4c2dae6 100644 --- a/Examples/33chartcreate-column.php +++ b/Examples/33chartcreate-column.php @@ -36,11 +36,9 @@ date_default_timezone_set('Europe/London'); * @version ##VERSION##, ##DATE## */ -/** Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../Classes/'); - /** PHPExcel */ -include 'PHPExcel.php'; +require_once '../Classes/PHPExcel.php'; + $objPHPExcel = new PHPExcel(); $objWorksheet = $objPHPExcel->getActiveSheet(); diff --git a/Examples/33chartcreate-composite-chart.php b/Examples/33chartcreate-composite-chart.php new file mode 100644 index 0000000..a9785ff --- /dev/null +++ b/Examples/33chartcreate-composite-chart.php @@ -0,0 +1,203 @@ +'); + +date_default_timezone_set('Europe/London'); + +/** + * PHPExcel + * + * Copyright (C) 2006 - 2012 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel + * @copyright Copyright (c) 2006 - 2012 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version ##VERSION##, ##DATE## + */ + +/** PHPExcel */ +require_once '../Classes/PHPExcel.php'; + + +$objPHPExcel = new PHPExcel(); +$objWorksheet = $objPHPExcel->getActiveSheet(); +$objWorksheet->fromArray( + array( + array('', 'Rainfall (mm)', 'Temperature (°F)', 'Humidity (%)'), + array('Jan', 78, 52, 61), + array('Feb', 64, 54, 62), + array('Mar', 62, 57, 63), + array('Apr', 21, 62, 59), + array('May', 11, 75, 60), + array('Jun', 1, 75, 57), + array('Jul', 1, 79, 56), + array('Aug', 1, 79, 59), + array('Sep', 10, 75, 60), + array('Oct', 40, 68, 63), + array('Nov', 69, 62, 64), + array('Dec', 89, 57, 66), + ) +); + + +// Set the Labels for each data series we want to plot +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$dataseriesLabels1 = array( + new PHPExcel_Chart_DataSeriesValues('String', 'Worksheet!$B$1', NULL, 1), // Temperature +); +$dataseriesLabels2 = array( + new PHPExcel_Chart_DataSeriesValues('String', 'Worksheet!$C$1', NULL, 1), // Rainfall +); +$dataseriesLabels3 = array( + new PHPExcel_Chart_DataSeriesValues('String', 'Worksheet!$D$1', NULL, 1), // Humidity +); + +// Set the X-Axis Labels +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$xAxisTickValues = array( + new PHPExcel_Chart_DataSeriesValues('String', 'Worksheet!$A$2:$A$13', NULL, 12), // Jan to Dec +); + + +// Set the Data values for each data series we want to plot +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$dataSeriesValues1 = array( + new PHPExcel_Chart_DataSeriesValues('Number', 'Worksheet!$B$2:$B$13', NULL, 12), +); + +// Build the dataseries +$series1 = new PHPExcel_Chart_DataSeries( + PHPExcel_Chart_DataSeries::TYPE_BARCHART, // plotType + PHPExcel_Chart_DataSeries::GROUPING_CLUSTERED, // plotGrouping + range(0, count($dataSeriesValues1)-1), // plotOrder + $dataseriesLabels1, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues1 // plotValues +); +// Set additional dataseries parameters +// Make it a vertical column rather than a horizontal bar graph +$series1->setPlotDirection(PHPExcel_Chart_DataSeries::DIRECTION_COL); + + +// Set the Data values for each data series we want to plot +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$dataSeriesValues2 = array( + new PHPExcel_Chart_DataSeriesValues('Number', 'Worksheet!$C$2:$C$13', NULL, 12), +); + +// Build the dataseries +$series2 = new PHPExcel_Chart_DataSeries( + PHPExcel_Chart_DataSeries::TYPE_LINECHART, // plotType + PHPExcel_Chart_DataSeries::GROUPING_STANDARD, // plotGrouping + range(0, count($dataSeriesValues2)-1), // plotOrder + $dataseriesLabels2, // plotLabel + NULL, // plotCategory + $dataSeriesValues2 // plotValues +); + + +// Set the Data values for each data series we want to plot +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$dataSeriesValues3 = array( + new PHPExcel_Chart_DataSeriesValues('Number', 'Worksheet!$D$2:$D$13', NULL, 12), +); + +// Build the dataseries +$series3 = new PHPExcel_Chart_DataSeries( + PHPExcel_Chart_DataSeries::TYPE_AREACHART, // plotType + PHPExcel_Chart_DataSeries::GROUPING_STANDARD, // plotGrouping + range(0, count($dataSeriesValues2)-1), // plotOrder + $dataseriesLabels3, // plotLabel + NULL, // plotCategory + $dataSeriesValues3 // plotValues +); + + +// Set the series in the plot area +$plotarea = new PHPExcel_Chart_PlotArea(NULL, array($series1, $series2, $series3)); +// Set the chart legend +$legend = new PHPExcel_Chart_Legend(PHPExcel_Chart_Legend::POSITION_RIGHT, NULL, false); + +$title = new PHPExcel_Chart_Title('Average Weather Chart for Crete'); + + +// Create the chart +$chart = new PHPExcel_Chart( + 'chart1', // name + $title, // title + $legend, // legend + $plotarea, // plotArea + true, // plotVisibleOnly + 0, // displayBlanksAs + NULL, // xAxisLabel + NULL // yAxisLabel +); + +// Set the position where the chart should appear in the worksheet +$chart->setTopLeftPosition('F2'); +$chart->setBottomRightPosition('O16'); + +// Add the chart to the worksheet +$objWorksheet->addChart($chart); + + +// Save Excel 2007 file +echo date('H:i:s') , " Write to Excel2007 format" , EOL; +$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); +$objWriter->setIncludeCharts(TRUE); +$objWriter->save(str_replace('.php', '.xlsx', __FILE__)); +echo date('H:i:s') , " File written to " , str_replace('.php', '.xlsx', pathinfo(__FILE__, PATHINFO_BASENAME)) , EOL; + + +// Echo memory peak usage +echo date('H:i:s') , " Peak memory usage: " , (memory_get_peak_usage(true) / 1024 / 1024) , " MB" , EOL; + +// Echo done +echo date('H:i:s') , " Done writing file" , EOL; +echo 'File has been created in ' , getcwd() , EOL; diff --git a/Examples/33chartcreate-line.php b/Examples/33chartcreate-line.php index 8fc9714..4745982 100644 --- a/Examples/33chartcreate-line.php +++ b/Examples/33chartcreate-line.php @@ -36,11 +36,9 @@ date_default_timezone_set('Europe/London'); * @version ##VERSION##, ##DATE## */ -/** Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../Classes/'); - /** PHPExcel */ -include 'PHPExcel.php'; +require_once '../Classes/PHPExcel.php'; + $objPHPExcel = new PHPExcel(); $objWorksheet = $objPHPExcel->getActiveSheet(); diff --git a/Examples/33chartcreate-multiple-charts.php b/Examples/33chartcreate-multiple-charts.php index a8943ee..041e03a 100644 --- a/Examples/33chartcreate-multiple-charts.php +++ b/Examples/33chartcreate-multiple-charts.php @@ -36,11 +36,9 @@ date_default_timezone_set('Europe/London'); * @version ##VERSION##, ##DATE## */ -/** Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../Classes/'); - /** PHPExcel */ -include 'PHPExcel.php'; +require_once '../Classes/PHPExcel.php'; + $objPHPExcel = new PHPExcel(); $objWorksheet = $objPHPExcel->getActiveSheet(); diff --git a/Examples/33chartcreate-pie.php b/Examples/33chartcreate-pie.php index fe68ba2..ce9825c 100644 --- a/Examples/33chartcreate-pie.php +++ b/Examples/33chartcreate-pie.php @@ -36,11 +36,9 @@ date_default_timezone_set('Europe/London'); * @version ##VERSION##, ##DATE## */ -/** Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../Classes/'); - /** PHPExcel */ -include 'PHPExcel.php'; +require_once '../Classes/PHPExcel.php'; + $objPHPExcel = new PHPExcel(); $objWorksheet = $objPHPExcel->getActiveSheet(); diff --git a/Examples/33chartcreate-radar.php b/Examples/33chartcreate-radar.php index a1bb413..b3c226a 100644 --- a/Examples/33chartcreate-radar.php +++ b/Examples/33chartcreate-radar.php @@ -36,11 +36,9 @@ date_default_timezone_set('Europe/London'); * @version ##VERSION##, ##DATE## */ -/** Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../Classes/'); - /** PHPExcel */ -include 'PHPExcel.php'; +require_once '../Classes/PHPExcel.php'; + $objPHPExcel = new PHPExcel(); $objWorksheet = $objPHPExcel->getActiveSheet(); diff --git a/Examples/runall.php b/Examples/runall.php index 3dfaa1f..7e97ee8 100644 --- a/Examples/runall.php +++ b/Examples/runall.php @@ -52,6 +52,7 @@ $aTests = array( , '10autofilter-selection-2.php' , '11documentsecurity.php' , '11documentsecurity-xls.php' + , '12cellProtection.php' , '13calculation.php' , '14excel5.php' , '15datavalidation.php' @@ -82,6 +83,8 @@ $aTests = array( , '33chartcreate-line.php' , '33chartcreate-pie.php' , '33chartcreate-radar.php' + , '33chartcreate-multiple-charts.php' + , '33chartcreate-composite.php' , '34chartupdate.php' , '35chartrender.php' , '36chartreadwriteHTML.php' diff --git a/changelog.txt b/changelog.txt index 552cb9e..cfe1749 100644 --- a/changelog.txt +++ b/changelog.txt @@ -23,7 +23,7 @@ ************************************************************************************** -Fixed in develop branch: +Fixed in develop branch for release v1.7.9: - Feature: (MBaker) Include charts option for HTML Writer - Feature: (MBaker) Added composer file - Bugfix: (Asker) Work item 18777 - Error in PHPEXCEL/Calculation.php script on line 2976 (stack pop check) @@ -41,6 +41,9 @@ Fixed in develop branch: - General: (cfhay) Work item 18958 - Memory and Speed improvements in PHPExcel_Reader_Excel5 - General: (MBaker) Work item GH-78 - Modify listWorksheetNames() and listWorksheetInfo to use XMLReader with streamed XML rather than SimpleXML - General: (dbonsch) Restructuring of PHPExcel Exceptions +- General: (MBaker) Work items 16926 and 15145 - Refactor Calculation Engine from singleton to a Multiton + Ensures that calculation cache is maintained independently for different workbooks +- General: (MBaker) Modify cell's getCalculatedValue() method to return the content of RichText objects rather than the RichText object itself - Bugfix: (techhead) Work item GH-70 - Fixed formula/formatting bug when removing rows - Bugfix: (alexgann) Work item GH-63 - Fix to cellExists for non-existent namedRanges - Bugfix: (MBaker) Work item 18844 - cache_in_memory_gzip "eats" last worksheet line, cache_in_memory doesn't @@ -50,6 +53,12 @@ Fixed in develop branch: - Bugfix: (MBaker) Work item GH-104 - echo statements in HTML.php - Feature: (Progi1984) Work item GH-8/CP11704 : Conditional formatting in Excel 5 Writer - Bugfix: (MBaker) Work item GH-113 - canRead() Error for GoogleDocs ODS files: in ODS files from Google Docs there is no mimetype file +- Bugfix: (MBaker) Work item GH-80 - "Sheet index is out of bounds." Exception +- Bugfix: (ccorliss) Work item GH-105 - Fixed number format fatal error +- Bugfix: (MBaker) - Add DROP TABLE in destructor for SQLite and SQLite3 cache controllers +- Bugfix: (alexgann) Work item GH-154 - Fix merged-cell borders on HTML/PDF output +- Bugfix: (Shanto) Work item GH-161 - Fix: Hyperlinks break when removing rows +- Bugfix: (neclimdul) Work item GH-166 - Fix Extra Table Row From Images and Charts -------------------------------------------------------------------------------- @@ -57,7 +66,7 @@ BREAKING CHANGE! As part of the planned changes for handling array formulae in workbooks, there are some changes that will affect the PHPExcel_Cell object methods. -The following methods are now deprecated, and will be removed in version 1.7.9: +The following methods are now deprecated, and will be removed in or after version 1.8.0: getCalculatedValue() The getValue() method will return calculated values for cells containing formulae instead. setCalculatedValue() The cell value will always contain the result of a @@ -67,7 +76,7 @@ The following methods are now deprecated, and will be removed in version 1.7.9: getFormulaAttributes() This will be replaced by the getArrayFormulaRange() method. -The following methods will be added in version 1.7.9 +The following methods will be added in version 1.8.0 getFormula() Use to retrieve a cell formula, will return the cell value if the cell doesn't contain a formula, or is not part of an array formula range. @@ -82,7 +91,7 @@ The following methods will be added in version 1.7.9 or is part of an array formula range or not. getArrayFormulaRange() Use to retrieve an array formula range. -The following methods will be changed in version 1.7.9 +The following methods will be changed in version 1.8.0 setValue() The logic behind this will be modified to store formula values in the new cell property structure, but it will still perform the same function. diff --git a/unitTests/Classes/PHPExcel/Cell/AdvancedValueBinderTest.php b/unitTests/Classes/PHPExcel/Cell/AdvancedValueBinderTest.php index a76a2b2..0a6ca54 100644 --- a/unitTests/Classes/PHPExcel/Cell/AdvancedValueBinderTest.php +++ b/unitTests/Classes/PHPExcel/Cell/AdvancedValueBinderTest.php @@ -35,7 +35,16 @@ class AdvancedValueBinderTest extends PHPUnit_Framework_TestCase */ public function testCurrency($value, $valueBinded, $format, $thousandsSeparator, $decimalSeparator, $currencyCode) { - $sheet = $this->getMock('PHPExcel_Worksheet', array('getStyle', 'getNumberFormat', 'setFormatCode')); + $sheet = $this->getMock( + 'PHPExcel_Worksheet', + array('getStyle', 'getNumberFormat', 'setFormatCode','getCellCacheController') + ); + $cache = $this->getMockBuilder('PHPExcel_CachedObjectStorage_Memory') + ->disableOriginalConstructor() + ->getMock(); + $cache->expects($this->any()) + ->method('getParent') + ->will($this->returnValue($sheet)); $sheet->expects($this->once()) ->method('getStyle') @@ -47,12 +56,15 @@ class AdvancedValueBinderTest extends PHPUnit_Framework_TestCase ->method('setFormatCode') ->with($format) ->will($this->returnSelf()); + $sheet->expects($this->any()) + ->method('getCellCacheController') + ->will($this->returnValue($cache)); PHPExcel_Shared_String::setCurrencyCode($currencyCode); PHPExcel_Shared_String::setDecimalSeparator($decimalSeparator); PHPExcel_Shared_String::setThousandsSeparator($thousandsSeparator); - $cell = new PHPExcel_Cell('A', 1, null, PHPExcel_Cell_DataType::TYPE_STRING, $sheet); + $cell = new PHPExcel_Cell(NULL, PHPExcel_Cell_DataType::TYPE_STRING, $sheet); $binder = new PHPExcel_Cell_AdvancedValueBinder(); $binder->bindValue($cell, $value); diff --git a/unitTests/Classes/PHPExcel/ReferenceHelperTest.php b/unitTests/Classes/PHPExcel/ReferenceHelperTest.php new file mode 100644 index 0000000..f37db69 --- /dev/null +++ b/unitTests/Classes/PHPExcel/ReferenceHelperTest.php @@ -0,0 +1,58 @@ + $value) { + $this->assertEquals($columnExpectedResult[$key], $value); + } + } + + public function testColumnReverseSort() + { + $columnBase = $columnExpectedResult = array( + 'A','B','Z', + 'AA','AB','AZ', + 'BA','BB','BZ', + 'ZA','ZB','ZZ', + 'AAA','AAB','AAZ', + 'ABA','ABB','ABZ', + 'AZA','AZB','AZZ', + 'BAA','BAB','BAZ', + 'BBA','BBB','BBZ', + 'BZA','BZB','BZZ' + ); + shuffle($columnBase); + $columnExpectedResult = array_reverse($columnExpectedResult); + usort($columnBase, array('PHPExcel_ReferenceHelper','columnReverseSort')); + foreach($columnBase as $key => $value) { + $this->assertEquals($columnExpectedResult[$key], $value); + } + } + +} diff --git a/unitTests/Classes/PHPExcel/Worksheet/AutoFilterTest.php b/unitTests/Classes/PHPExcel/Worksheet/AutoFilterTest.php index 6a6d5ae..9907eab 100644 --- a/unitTests/Classes/PHPExcel/Worksheet/AutoFilterTest.php +++ b/unitTests/Classes/PHPExcel/Worksheet/AutoFilterTest.php @@ -18,6 +18,12 @@ class AutoFilterTest extends PHPUnit_Framework_TestCase $this->_mockWorksheetObject = $this->getMockBuilder('PHPExcel_Worksheet') ->disableOriginalConstructor() ->getMock(); + $this->_mockCacheController = $this->getMockBuilder('PHPExcel_CachedObjectStorage_Memory') + ->disableOriginalConstructor() + ->getMock(); + $this->_mockWorksheetObject->expects($this->any()) + ->method('getCellCacheController') + ->will($this->returnValue($this->_mockCacheController)); $this->_testAutoFilterObject = new PHPExcel_Worksheet_AutoFilter( $this->_testInitialRange, diff --git a/unitTests/rawTestData/Calculation/DateTime/DATEVALUE.data b/unitTests/rawTestData/Calculation/DateTime/DATEVALUE.data index 281c707..43970c2 100644 --- a/unitTests/rawTestData/Calculation/DateTime/DATEVALUE.data +++ b/unitTests/rawTestData/Calculation/DateTime/DATEVALUE.data @@ -35,11 +35,11 @@ "22 August 98", 36029 "1st March 2007", 39142 // MS Excel will fail with a #VALUE return, but PHPExcel can parse this date "The 1st day of March 2007", "#VALUE!" -"1 Jan", 40909 -"31/12", 41274 +"1 Jan", 41275 +"31/12", 41639 "12/31", 11658 // Excel reads as 1st December 1931, not 31st December in current year -"5-JUL", 41095 -"5 Jul", 41095 +"5-JUL", 41460 +"5 Jul", 41460 "12/2008", 39783 "10/32", 11963 11, "#VALUE!"