Refactoring of calculation engine using the multiton pattern to eliminate caching issues when working with multiple workbooks

Refactoring of calculation engine for improved performance and memory usage
Refactoring of cell object to eliminate data duplication and reduce memory
This commit is contained in:
Mark Baker 2013-02-15 15:42:06 +00:00
parent 6fd6b4d044
commit 3886c47ebe
13 changed files with 2702 additions and 2596 deletions

View File

@ -70,7 +70,7 @@ class PHPExcel
*/ */
private $_workSheetCollection = array(); private $_workSheetCollection = array();
/** /**
* Calculation Engine * Calculation Engine
* *
* @var PHPExcel_Calculation * @var PHPExcel_Calculation
@ -878,7 +878,7 @@ class PHPExcel
$columnDimension->setXfIndex( $map[$columnDimension->getXfIndex()] ); $columnDimension->setXfIndex( $map[$columnDimension->getXfIndex()] );
} }
// also do garbage collection for all the sheets // also do garbage collection for all the sheets
$sheet->garbageCollect(); $sheet->garbageCollect();
} }
} }

View File

@ -86,6 +86,11 @@ abstract class PHPExcel_CachedObjectStorage_CacheBase {
} // function __construct() } // function __construct()
public function getParent()
{
return $this->_parent;
}
/** /**
* Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell?
* *
@ -188,6 +193,23 @@ abstract class PHPExcel_CachedObjectStorage_CacheBase {
} }
public function getCurrentAddress()
{
return $this->_currentObjectID;
}
public function getCurrentColumn()
{
list($column,$row) = sscanf($this->_currentObjectID, '%[A-Z]%d');
return $column;
}
public function getCurrentRow()
{
list($column,$row) = sscanf($this->_currentObjectID, '%[A-Z]%d');
return $row;
}
/** /**
* Get highest worksheet column * Get highest worksheet column
* *

View File

@ -48,11 +48,15 @@ class PHPExcel_CachedObjectStorage_Memory extends PHPExcel_CachedObjectStorage_C
* *
* @param string $pCoord Coordinate address of the cell to update * @param string $pCoord Coordinate address of the cell to update
* @param PHPExcel_Cell $cell Cell to update * @param PHPExcel_Cell $cell Cell to update
* @return void * @return PHPExcel_Cell
* @throws PHPExcel_Exception * @throws PHPExcel_Exception
*/ */
public function addCacheData($pCoord, PHPExcel_Cell $cell) { public function addCacheData($pCoord, PHPExcel_Cell $cell) {
$this->_cellCache[$pCoord] = $cell; $this->_cellCache[$pCoord] = $cell;
// Set current entry to the new/updated entry
$this->_currentObjectID = $pCoord;
return $cell; return $cell;
} // function addCacheData() } // function addCacheData()
@ -67,10 +71,14 @@ class PHPExcel_CachedObjectStorage_Memory extends PHPExcel_CachedObjectStorage_C
public function getCacheData($pCoord) { public function getCacheData($pCoord) {
// Check if the entry that has been requested actually exists // Check if the entry that has been requested actually exists
if (!isset($this->_cellCache[$pCoord])) { if (!isset($this->_cellCache[$pCoord])) {
$this->_currentObjectID = NULL;
// Return null if requested entry doesn't exist in cache // Return null if requested entry doesn't exist in cache
return null; return null;
} }
// Set current entry to the requested entry
$this->_currentObjectID = $pCoord;
// Return requested entry // Return requested entry
return $this->_cellCache[$pCoord]; return $this->_cellCache[$pCoord];
} // function getCacheData() } // function getCacheData()

View File

@ -0,0 +1,109 @@
<?php
/**
* 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_Calculation
* @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_Calculation_Logger
*
* @category PHPExcel
* @package PHPExcel_Calculation
* @copyright Copyright (c) 2006 - 2012 PHPExcel (http://www.codeplex.com/PHPExcel)
*/
class PHPExcel_CalcEngine_Logger {
/**
* 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
*
* @var boolean
*
*/
private $_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
*
* @var boolean
*
*/
private $_echoDebugLog = FALSE;
/**
* The debug log generated by the calculation engine
*
* @var string[]
*
*/
private $_debugLog = array();
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(array $cellReferencePath) {
// Only write the debug log if logging is enabled
if ($this->_writeDebugLog) {
$message = func_get_args();
array_shift($message);
$cellReference = implode(' -> ', $cellReferencePath);
$message = implode($message);
if ($this->_echoDebugLog) {
echo $cellReference, (count($cellReferencePath) > 0 ? ' => ' : ''), $message,PHP_EOL;
}
$this->_debugLog[] = $cellReference . (count($cellReferencePath) > 0 ? ' => ' : '') . $message;
}
} // function _writeDebug()
public function clearLog() {
$this->_debugLog = array();
} // function flushLogger()
public function getLog() {
return $this->_debugLog;
} // function flushLogger()
} // class PHPExcel_Calculation_Logger

View File

@ -156,6 +156,15 @@ class PHPExcel_Calculation {
'|' => 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 * Flag to determine how formula errors should be handled
* If true, then a user error will be triggered * If true, then a user error will be triggered
@ -176,30 +185,6 @@ class PHPExcel_Calculation {
*/ */
public $formulaError = NULL; 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;
/** /**
* An array of the nested cell references accessed by the calculation engine, used for the debug log * An array of the nested cell references accessed by the calculation engine, used for the debug log
* *
@ -207,22 +192,14 @@ class PHPExcel_Calculation {
* @var array of string * @var array of string
* *
*/ */
private $debugLogStack = array(); private $cyclicReferenceStack = array();
/**
* The debug log generated by the calculation engine
*
* @access public
* @var array of string
*
*/
public $debugLog = array();
private $_cyclicFormulaCount = 0; private $_cyclicFormulaCount = 0;
private $_cyclicFormulaCell = ''; private $_cyclicFormulaCell = '';
public $cyclicFormulaCount = 0; public $cyclicFormulaCount = 0;
private $_savedPrecision = 12; private $_savedPrecision = 14;
private static $_localeLanguage = 'en_us'; // US English (default locale) private static $_localeLanguage = 'en_us'; // US English (default locale)
@ -1688,7 +1665,7 @@ class PHPExcel_Calculation {
private function __construct(PHPExcel $workbook = NULL) { private function __construct(PHPExcel $workbook = NULL) {
$setPrecision = (PHP_INT_SIZE == 4) ? 12 : 16; $setPrecision = (PHP_INT_SIZE == 4) ? 14 : 16;
$this->_savedPrecision = ini_get('precision'); $this->_savedPrecision = ini_get('precision');
if ($this->_savedPrecision < $setPrecision) { if ($this->_savedPrecision < $setPrecision) {
ini_set('precision',$setPrecision); ini_set('precision',$setPrecision);
@ -1699,6 +1676,7 @@ class PHPExcel_Calculation {
} }
$this->_workbook = $workbook; $this->_workbook = $workbook;
$this->_debugLog = new PHPExcel_CalcEngine_Logger();
} // function __construct() } // function __construct()
@ -1759,6 +1737,10 @@ class PHPExcel_Calculation {
} // function flushInstance() } // function flushInstance()
public function getDebugLog() {
return $this->_debugLog;
}
/** /**
* __clone implementation. Cloning should not be allowed in a Singleton! * __clone implementation. Cloning should not be allowed in a Singleton!
* *
@ -1871,7 +1853,7 @@ class PHPExcel_Calculation {
if (isset($this->_calculationCache[$worksheetName])) { if (isset($this->_calculationCache[$worksheetName])) {
unset($this->_calculationCache[$worksheetName]); unset($this->_calculationCache[$worksheetName]);
} }
} // function clearCalculationCache() } // function clearCalculationCacheForWorksheet()
/** /**
* Rename calculation cache for a specified worksheet * Rename calculation cache for a specified worksheet
@ -1881,7 +1863,7 @@ class PHPExcel_Calculation {
$this->_calculationCache[$toWorksheetName] = &$this->_calculationCache[$fromWorksheetName]; $this->_calculationCache[$toWorksheetName] = &$this->_calculationCache[$fromWorksheetName];
unset($this->_calculationCache[$fromWorksheetName]); unset($this->_calculationCache[$fromWorksheetName]);
} }
} // function clearCalculationCache() } // function renameCalculationCacheForWorksheet()
/** /**
@ -2174,15 +2156,14 @@ class PHPExcel_Calculation {
$returnArrayAsType = self::$returnArrayAsType; $returnArrayAsType = self::$returnArrayAsType;
if ($resetLog) { if ($resetLog) {
// Initialise the logging settings if requested // Initialise the logging settings if requested
$this->formulaError = NULL; $this->formulaError = null;
$this->debugLog = $this->debugLogStack = array(); $this->_debugLog->clearLog();
$this->cyclicReferenceStack = array();
$this->_cyclicFormulaCount = 1; $this->_cyclicFormulaCount = 1;
self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY; self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
} }
if ($resetLog) {
}
// Execute the calculation for the cell formula // Execute the calculation for the cell formula
try { try {
$result = self::_unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell)); $result = self::_unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
@ -2254,7 +2235,8 @@ class PHPExcel_Calculation {
public function calculateFormula($formula, $cellID=NULL, PHPExcel_Cell $pCell = NULL) { public function calculateFormula($formula, $cellID=NULL, PHPExcel_Cell $pCell = NULL) {
// Initialise the logging settings // Initialise the logging settings
$this->formulaError = null; $this->formulaError = null;
$this->debugLog = $this->debugLogStack = array(); $this->_debugLog->clearLog();
$this->cyclicReferenceStack = array();
// Disable calculation cacheing because it only applies to cell calculations, not straight formulae // Disable calculation cacheing because it only applies to cell calculations, not straight formulae
// But don't actually flush any cache // But don't actually flush any cache
@ -2277,9 +2259,11 @@ class PHPExcel_Calculation {
public function getValueFromCache($worksheetName, $cellID, &$cellValue) { public function getValueFromCache($worksheetName, $cellID, &$cellValue) {
// Is calculation cacheing enabled? // Is calculation cacheing enabled?
// Is the value present in calculation cache? // Is the value present in calculation cache?
$this->_writeDebug('Testing cache value for cell ', $worksheetName, '!', $cellID); //echo 'Test cache for ',$worksheetName,'!',$cellID,PHP_EOL;
$this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Testing cache value for cell ', $worksheetName, '!', $cellID);
if (($this->_calculationCacheEnabled) && (isset($this->_calculationCache[$worksheetName][$cellID]))) { if (($this->_calculationCacheEnabled) && (isset($this->_calculationCache[$worksheetName][$cellID]))) {
$this->_writeDebug('Retrieving value for cell ', $worksheetName, '!', $cellID, ' from cache'); //echo 'Retrieve from cache',PHP_EOL;
$this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Retrieving value for cell ', $worksheetName, '!', $cellID, ' from cache');
// Return the cached result // Return the cached result
$cellValue = $this->_calculationCache[$worksheetName][$cellID]; $cellValue = $this->_calculationCache[$worksheetName][$cellID];
return TRUE; return TRUE;
@ -2312,14 +2296,14 @@ class PHPExcel_Calculation {
$formula = ltrim(substr($formula,1)); $formula = ltrim(substr($formula,1));
if (!isset($formula{0})) return self::_wrapResult($formula); if (!isset($formula{0})) return self::_wrapResult($formula);
$pCellParent = ($pCell !== NULL) ? $pCell->getParent() : NULL; $pCellParent = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL;
$wsTitle = ($pCellParent !== NULL) ? $pCellParent->getTitle() : "\x00Wrk"; $wsTitle = ($pCellParent !== NULL) ? $pCellParent->getTitle() : "\x00Wrk";
if (($cellID !== NULL) && ($this->getValueFromCache($wsTitle, $cellID, $cellValue))) { if (($cellID !== NULL) && ($this->getValueFromCache($wsTitle, $cellID, $cellValue))) {
return $cellValue; return $cellValue;
} }
if (($wsTitle{0} !== "\x00") && (in_array($wsTitle.'!'.$cellID,$this->debugLogStack))) { if (($wsTitle{0} !== "\x00") && (in_array($wsTitle.'!'.$cellID,$this->cyclicReferenceStack))) {
if ($this->cyclicFormulaCount <= 0) { if ($this->cyclicFormulaCount <= 0) {
return $this->_raiseFormulaError('Cyclic Reference in Formula'); return $this->_raiseFormulaError('Cyclic Reference in Formula');
} elseif (($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) && } elseif (($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) &&
@ -2337,10 +2321,10 @@ class PHPExcel_Calculation {
} }
} }
} }
$this->debugLogStack[] = $wsTitle.'!'.$cellID; $this->cyclicReferenceStack[] = $wsTitle.'!'.$cellID;
// Parse the formula onto the token stack and calculate the value // Parse the formula onto the token stack and calculate the value
$cellValue = $this->_processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell); $cellValue = $this->_processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell);
array_pop($this->debugLogStack); array_pop($this->cyclicReferenceStack);
// Save to calculation cache // Save to calculation cache
if ($cellID !== NULL) { if ($cellID !== NULL) {
@ -2505,7 +2489,7 @@ class PHPExcel_Calculation {
* @return mixed * @return mixed
*/ */
private function _showValue($value) { private function _showValue($value) {
if ($this->writeDebugLog) { if ($this->_debugLog->getWriteDebugLog()) {
$testArray = PHPExcel_Calculation_Functions::flattenArray($value); $testArray = PHPExcel_Calculation_Functions::flattenArray($value);
if (count($testArray) == 1) { if (count($testArray) == 1) {
$value = array_pop($testArray); $value = array_pop($testArray);
@ -2540,7 +2524,7 @@ class PHPExcel_Calculation {
* @return mixed * @return mixed
*/ */
private function _showTypeDetails($value) { private function _showTypeDetails($value) {
if ($this->writeDebugLog) { if ($this->_debugLog->getWriteDebugLog()) {
$testArray = PHPExcel_Calculation_Functions::flattenArray($value); $testArray = PHPExcel_Calculation_Functions::flattenArray($value);
if (count($testArray) == 1) { if (count($testArray) == 1) {
$value = array_pop($testArray); $value = array_pop($testArray);
@ -2665,7 +2649,7 @@ class PHPExcel_Calculation {
// If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet), // 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 // so we store the parent worksheet so that we can re-attach it when necessary
$pCellParent = ($pCell !== NULL) ? $pCell->getParent() : NULL; $pCellParent = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL;
$regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION. $regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION.
'|'.self::CALCULATION_REGEXP_NUMBER. '|'.self::CALCULATION_REGEXP_NUMBER.
@ -3019,9 +3003,10 @@ class PHPExcel_Calculation {
private function _processTokenStack($tokens, $cellID = NULL, PHPExcel_Cell $pCell = NULL) { private function _processTokenStack($tokens, $cellID = NULL, PHPExcel_Cell $pCell = NULL) {
if ($tokens == FALSE) return FALSE; 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), // 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 worksheet so that we can re-attach it when necessary // so we store the parent cell collection so that we can re-attach it when necessary
$pCellParent = ($pCell !== NULL) ? $pCell->getParent() : NULL; $pCellWorksheet = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL;
$pCellParent = ($pCell !== NULL) ? $pCell->getParent() : null;
$stack = new PHPExcel_Calculation_Token_Stack; $stack = new PHPExcel_Calculation_Token_Stack;
// Loop through each token in turn // Loop through each token in turn
@ -3042,9 +3027,9 @@ class PHPExcel_Calculation {
// Log what we're doing // Log what we're doing
if ($token == ':') { if ($token == ':') {
$this->_writeDebug('Evaluating Range ', $this->_showValue($operand1Data['reference']), ' ', $token, ' ', $this->_showValue($operand2Data['reference'])); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Range ', $this->_showValue($operand1Data['reference']), ' ', $token, ' ', $this->_showValue($operand2Data['reference']));
} else { } else {
$this->_writeDebug('Evaluating ', $this->_showValue($operand1), ' ', $token, ' ', $this->_showValue($operand2)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating ', $this->_showValue($operand1), ' ', $token, ' ', $this->_showValue($operand2));
} }
// Process the operation in the appropriate manner // Process the operation in the appropriate manner
@ -3064,7 +3049,7 @@ class PHPExcel_Calculation {
if (strpos($operand1Data['reference'],'!') !== FALSE) { if (strpos($operand1Data['reference'],'!') !== FALSE) {
list($sheet1,$operand1Data['reference']) = explode('!',$operand1Data['reference']); list($sheet1,$operand1Data['reference']) = explode('!',$operand1Data['reference']);
} else { } 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']); list($sheet2,$operand2Data['reference']) = explode('!',$operand2Data['reference']);
@ -3145,13 +3130,13 @@ class PHPExcel_Calculation {
$matrixResult = $matrix->concat($operand2); $matrixResult = $matrix->concat($operand2);
$result = $matrixResult->getArray(); $result = $matrixResult->getArray();
} catch (PHPExcel_Exception $ex) { } catch (PHPExcel_Exception $ex) {
$this->_writeDebug('JAMA Matrix Exception: ', $ex->getMessage()); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'JAMA Matrix Exception: ', $ex->getMessage());
$result = '#VALUE!'; $result = '#VALUE!';
} }
} else { } else {
$result = '"'.str_replace('""','"',self::_unwrapResult($operand1,'"').self::_unwrapResult($operand2,'"')).'"'; $result = '"'.str_replace('""','"',self::_unwrapResult($operand1,'"').self::_unwrapResult($operand2,'"')).'"';
} }
$this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($result)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result is ', $this->_showTypeDetails($result));
$stack->push('Value',$result); $stack->push('Value',$result);
break; break;
case '|' : // Intersect case '|' : // Intersect
@ -3165,7 +3150,7 @@ class PHPExcel_Calculation {
} }
} }
$cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow); $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($this->cyclicReferenceStack, 'Evaluation Result is ', $this->_showTypeDetails($cellIntersect));
$stack->push('Value',$cellIntersect,$cellRef); $stack->push('Value',$cellIntersect,$cellRef);
break; break;
} }
@ -3177,11 +3162,11 @@ class PHPExcel_Calculation {
$arg = $arg['value']; $arg = $arg['value'];
if ($token === '~') { if ($token === '~') {
// echo 'Token is a negation operator<br />'; // echo 'Token is a negation operator<br />';
$this->_writeDebug('Evaluating Negation of ', $this->_showValue($arg)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Negation of ', $this->_showValue($arg));
$multiplier = -1; $multiplier = -1;
} else { } else {
// echo 'Token is a percentile operator<br />'; // echo 'Token is a percentile operator<br />';
$this->_writeDebug('Evaluating Percentile of ', $this->_showValue($arg)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Percentile of ', $this->_showValue($arg));
$multiplier = 0.01; $multiplier = 0.01;
} }
if (is_array($arg)) { if (is_array($arg)) {
@ -3191,10 +3176,10 @@ class PHPExcel_Calculation {
$matrixResult = $matrix1->arrayTimesEquals($multiplier); $matrixResult = $matrix1->arrayTimesEquals($multiplier);
$result = $matrixResult->getArray(); $result = $matrixResult->getArray();
} catch (PHPExcel_Exception $ex) { } catch (PHPExcel_Exception $ex) {
$this->_writeDebug('JAMA Matrix Exception: ', $ex->getMessage()); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'JAMA Matrix Exception: ', $ex->getMessage());
$result = '#VALUE!'; $result = '#VALUE!';
} }
$this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($result)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result is ', $this->_showTypeDetails($result));
$stack->push('Value',$result); $stack->push('Value',$result);
} else { } else {
$this->_executeNumericBinaryOperation($cellID,$multiplier,$arg,'*','arrayTimesEquals',$stack); $this->_executeNumericBinaryOperation($cellID,$multiplier,$arg,'*','arrayTimesEquals',$stack);
@ -3218,23 +3203,23 @@ class PHPExcel_Calculation {
} }
$matches[2] = trim($matches[2],"\"'"); $matches[2] = trim($matches[2],"\"'");
// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />'; // echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />';
$this->_writeDebug('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]);
if ($pCellParent !== NULL) { if ($pCellParent !== NULL) {
$cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE); $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE);
} else { } else {
return $this->_raiseFormulaError('Unable to access Cell Reference'); 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($this->cyclicReferenceStack, 'Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue));
// $cellRef = $matches[2].'!'.$cellRef; // $cellRef = $matches[2].'!'.$cellRef;
} else { } else {
// echo '$cellRef='.$cellRef.' in current worksheet<br />'; // echo '$cellRef='.$cellRef.' in current worksheet<br />';
$this->_writeDebug('Evaluating Cell Range ', $cellRef, ' in current worksheet'); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Cell Range ', $cellRef, ' in current worksheet');
if ($pCellParent !== NULL) { if ($pCellParent !== NULL) {
$cellValue = $this->extractCellRange($cellRef, $pCellParent, FALSE); $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, FALSE);
} else { } else {
return $this->_raiseFormulaError('Unable to access Cell Reference'); return $this->_raiseFormulaError('Unable to access Cell Reference');
} }
$this->_writeDebug('Evaluation Result for cells ', $cellRef, ' is ', $this->_showTypeDetails($cellValue)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result for cells ', $cellRef, ' is ', $this->_showTypeDetails($cellValue));
} }
} }
} else { } else {
@ -3251,7 +3236,7 @@ class PHPExcel_Calculation {
return $this->_raiseFormulaError('Unable to access External Workbook'); return $this->_raiseFormulaError('Unable to access External Workbook');
} }
// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />'; // echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />';
$this->_writeDebug('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]);
if ($pCellParent !== NULL) { if ($pCellParent !== NULL) {
if ($this->_workbook->getSheetByName($matches[2])->cellExists($cellRef)) { if ($this->_workbook->getSheetByName($matches[2])->cellExists($cellRef)) {
$cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE); $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE);
@ -3262,18 +3247,18 @@ class PHPExcel_Calculation {
} else { } else {
return $this->_raiseFormulaError('Unable to access Cell Reference'); 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($this->cyclicReferenceStack, 'Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue));
// $cellRef = $matches[2].'!'.$cellRef; // $cellRef = $matches[2].'!'.$cellRef;
} else { } else {
// echo '$cellRef='.$cellRef.' in current worksheet<br />'; // echo '$cellRef='.$cellRef.' in current worksheet<br />';
$this->_writeDebug('Evaluating Cell ', $cellRef, ' in current worksheet'); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Cell ', $cellRef, ' in current worksheet');
if ($pCellParent->cellExists($cellRef)) { if ($pCellParent->isDataSet($cellRef)) {
$cellValue = $this->extractCellRange($cellRef, $pCellParent, FALSE); $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, FALSE);
$pCell->attach($pCellParent); $pCell->attach($pCellParent);
} else { } else {
$cellValue = NULL; $cellValue = NULL;
} }
$this->_writeDebug('Evaluation Result for cell ', $cellRef, ' is ', $this->_showTypeDetails($cellValue)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result for cell ', $cellRef, ' is ', $this->_showTypeDetails($cellValue));
} }
} }
} }
@ -3286,7 +3271,7 @@ class PHPExcel_Calculation {
$argCount = $stack->pop(); $argCount = $stack->pop();
$argCount = $argCount['value']; $argCount = $argCount['value'];
if ($functionName != 'MKMATRIX') { if ($functionName != 'MKMATRIX') {
$this->_writeDebug('Evaluating Function ', self::_localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's')); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, '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])) || (isset(self::$_controlFunctions[$functionName]))) { // function
if (isset(self::$_PHPExcelFunctions[$functionName])) { if (isset(self::$_PHPExcelFunctions[$functionName])) {
@ -3329,30 +3314,30 @@ class PHPExcel_Calculation {
// print_r($args); // print_r($args);
// echo '<br />'; // echo '<br />';
if ($functionName != 'MKMATRIX') { if ($functionName != 'MKMATRIX') {
if ($this->writeDebugLog) { if ($this->_debugLog->getWriteDebugLog()) {
krsort($argArrayVals); krsort($argArrayVals);
$this->_writeDebug('Evaluating ', self::_localeFunc($functionName), '( ', implode(self::$_localeArgumentSeparator.' ',PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )'); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating ', self::_localeFunc($functionName), '( ', implode(self::$_localeArgumentSeparator.' ',PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )');
} }
} }
// Process each argument in turn, building the return value as an array // Process each argument in turn, building the return value as an array
// if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) { // if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) {
// $operand1 = $args[1]; // $operand1 = $args[1];
// $this->_writeDebug('Argument is a matrix: ', $this->_showValue($operand1)); // $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Argument is a matrix: ', $this->_showValue($operand1));
// $result = array(); // $result = array();
// $row = 0; // $row = 0;
// foreach($operand1 as $args) { // foreach($operand1 as $args) {
// if (is_array($args)) { // if (is_array($args)) {
// foreach($args as $arg) { // foreach($args as $arg) {
// $this->_writeDebug('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($arg), ' )'); // $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($arg), ' )');
// $r = call_user_func_array($functionCall,$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($this->cyclicReferenceStack, 'Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r));
// $result[$row][] = $r; // $result[$row][] = $r;
// } // }
// ++$row; // ++$row;
// } else { // } else {
// $this->_writeDebug('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($args), ' )'); // $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($args), ' )');
// $r = call_user_func_array($functionCall,$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($this->cyclicReferenceStack, 'Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r));
// $result[] = $r; // $result[] = $r;
// } // }
// } // }
@ -3372,7 +3357,7 @@ class PHPExcel_Calculation {
} }
// } // }
if ($functionName != 'MKMATRIX') { if ($functionName != 'MKMATRIX') {
$this->_writeDebug('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($result)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($result));
} }
$stack->push('Value',self::_wrapResult($result)); $stack->push('Value',self::_wrapResult($result));
} }
@ -3383,7 +3368,7 @@ class PHPExcel_Calculation {
$excelConstant = strtoupper($token); $excelConstant = strtoupper($token);
// echo 'Token is a PHPExcel constant: '.$excelConstant.'<br />'; // echo 'Token is a PHPExcel constant: '.$excelConstant.'<br />';
$stack->push('Constant Value',self::$_ExcelConstants[$excelConstant]); $stack->push('Constant Value',self::$_ExcelConstants[$excelConstant]);
$this->_writeDebug('Evaluating Constant ', $excelConstant, ' as ', $this->_showTypeDetails(self::$_ExcelConstants[$excelConstant])); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Constant ', $excelConstant, ' as ', $this->_showTypeDetails(self::$_ExcelConstants[$excelConstant]));
} elseif ((is_numeric($token)) || ($token === NULL) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) { } 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<br />'; // echo 'Token is a number, boolean, string, null or an Excel error<br />';
$stack->push('Value',$token); $stack->push('Value',$token);
@ -3392,10 +3377,10 @@ class PHPExcel_Calculation {
// echo 'Token is a named range<br />'; // echo 'Token is a named range<br />';
$namedRange = $matches[6]; $namedRange = $matches[6];
// echo 'Named Range is '.$namedRange.'<br />'; // echo 'Named Range is '.$namedRange.'<br />';
$this->_writeDebug('Evaluating Named Range ', $namedRange); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Named Range ', $namedRange);
$cellValue = $this->extractNamedRange($namedRange, ((NULL !== $pCell) ? $pCellParent : NULL), FALSE); $cellValue = $this->extractNamedRange($namedRange, ((NULL !== $pCell) ? $pCellWorksheet : NULL), FALSE);
$pCell->attach($pCellParent); $pCell->attach($pCellParent);
$this->_writeDebug('Evaluation Result for named range ', $namedRange, ' is ', $this->_showTypeDetails($cellValue)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result for named range ', $namedRange, ' is ', $this->_showTypeDetails($cellValue));
$stack->push('Named Range',$cellValue,$namedRange); $stack->push('Named Range',$cellValue,$namedRange);
} else { } else {
return $this->_raiseFormulaError("undefined variable '$token'"); return $this->_raiseFormulaError("undefined variable '$token'");
@ -3425,12 +3410,12 @@ 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 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} == '#') { if ($operand > '' && $operand{0} == '#') {
$stack->push('Value', $operand); $stack->push('Value', $operand);
$this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($operand)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result is ', $this->_showTypeDetails($operand));
return FALSE; return FALSE;
} elseif (!PHPExcel_Shared_String::convertToNumberIfFraction($operand)) { } 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 // 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!'); $stack->push('Value', '#VALUE!');
$this->_writeDebug('Evaluation Result is a ', $this->_showTypeDetails('#VALUE!')); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result is a ', $this->_showTypeDetails('#VALUE!'));
return FALSE; return FALSE;
} }
} }
@ -3447,14 +3432,14 @@ class PHPExcel_Calculation {
$result = array(); $result = array();
if ((is_array($operand1)) && (!is_array($operand2))) { if ((is_array($operand1)) && (!is_array($operand2))) {
foreach($operand1 as $x => $operandData) { foreach($operand1 as $x => $operandData) {
$this->_writeDebug('Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2));
$this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2,$operation,$stack); $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2,$operation,$stack);
$r = $stack->pop(); $r = $stack->pop();
$result[$x] = $r['value']; $result[$x] = $r['value'];
} }
} elseif ((!is_array($operand1)) && (is_array($operand2))) { } elseif ((!is_array($operand1)) && (is_array($operand2))) {
foreach($operand2 as $x => $operandData) { foreach($operand2 as $x => $operandData) {
$this->_writeDebug('Evaluating Comparison ', $this->_showValue($operand1), ' ', $operation, ' ', $this->_showValue($operandData)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Comparison ', $this->_showValue($operand1), ' ', $operation, ' ', $this->_showValue($operandData));
$this->_executeBinaryComparisonOperation($cellID,$operand1,$operandData,$operation,$stack); $this->_executeBinaryComparisonOperation($cellID,$operand1,$operandData,$operation,$stack);
$r = $stack->pop(); $r = $stack->pop();
$result[$x] = $r['value']; $result[$x] = $r['value'];
@ -3462,14 +3447,14 @@ class PHPExcel_Calculation {
} else { } else {
if (!$recursingArrays) { self::_checkMatrixOperands($operand1,$operand2,2); } if (!$recursingArrays) { self::_checkMatrixOperands($operand1,$operand2,2); }
foreach($operand1 as $x => $operandData) { foreach($operand1 as $x => $operandData) {
$this->_writeDebug('Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2[$x])); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2[$x]));
$this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2[$x],$operation,$stack,TRUE); $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2[$x],$operation,$stack,TRUE);
$r = $stack->pop(); $r = $stack->pop();
$result[$x] = $r['value']; $result[$x] = $r['value'];
} }
} }
// Log the result details // Log the result details
$this->_writeDebug('Comparison Evaluation Result is ', $this->_showTypeDetails($result)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Comparison Evaluation Result is ', $this->_showTypeDetails($result));
// And push the result onto the stack // And push the result onto the stack
$stack->push('Array',$result); $stack->push('Array',$result);
return TRUE; return TRUE;
@ -3508,7 +3493,7 @@ class PHPExcel_Calculation {
} }
// Log the result details // Log the result details
$this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($result)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result is ', $this->_showTypeDetails($result));
// And push the result onto the stack // And push the result onto the stack
$stack->push('Value',$result); $stack->push('Value',$result);
return TRUE; return TRUE;
@ -3546,7 +3531,7 @@ class PHPExcel_Calculation {
$matrixResult = $matrix->$matrixFunction($operand2); $matrixResult = $matrix->$matrixFunction($operand2);
$result = $matrixResult->getArray(); $result = $matrixResult->getArray();
} catch (PHPExcel_Exception $ex) { } catch (PHPExcel_Exception $ex) {
$this->_writeDebug('JAMA Matrix Exception: ', $ex->getMessage()); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'JAMA Matrix Exception: ', $ex->getMessage());
$result = '#VALUE!'; $result = '#VALUE!';
} }
} else { } else {
@ -3573,7 +3558,7 @@ class PHPExcel_Calculation {
if ($operand2 == 0) { if ($operand2 == 0) {
// Trap for Divide by Zero error // Trap for Divide by Zero error
$stack->push('Value','#DIV/0!'); $stack->push('Value','#DIV/0!');
$this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails('#DIV/0!')); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result is ', $this->_showTypeDetails('#DIV/0!'));
return FALSE; return FALSE;
} else { } else {
$result = $operand1/$operand2; $result = $operand1/$operand2;
@ -3588,29 +3573,17 @@ class PHPExcel_Calculation {
} }
// Log the result details // Log the result details
$this->_writeDebug('Evaluation Result is ', $this->_showTypeDetails($result)); $this->_debugLog->writeDebugLog($this->cyclicReferenceStack, 'Evaluation Result is ', $this->_showTypeDetails($result));
// And push the result onto the stack // And push the result onto the stack
$stack->push('Value',$result); $stack->push('Value',$result);
return TRUE; return TRUE;
} // function _executeNumericBinaryOperation() } // 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,'<br />';
}
$this->debugLog[] = implode(' -> ',$this->debugLogStack).' -> '.$message;
}
} // function _writeDebug()
// trigger an error, but nicely, if need be // trigger an error, but nicely, if need be
protected function _raiseFormulaError($errorMessage) { protected function _raiseFormulaError($errorMessage) {
$this->formulaError = $errorMessage; $this->formulaError = $errorMessage;
$this->debugLogStack = array(); $this->cyclicReferenceStack = array();
if (!$this->suppressFormulaErrors) throw new PHPExcel_Calculation_Exception($errorMessage); if (!$this->suppressFormulaErrors) throw new PHPExcel_Calculation_Exception($errorMessage);
trigger_error($errorMessage, E_USER_ERROR); trigger_error($errorMessage, E_USER_ERROR);
} // function _raiseFormulaError() } // function _raiseFormulaError()
@ -3628,25 +3601,26 @@ class PHPExcel_Calculation {
// Return value // Return value
$returnValue = array (); $returnValue = array ();
// echo 'extractCellRange('.$pRange.')<br />'; // echo 'extractCellRange('.$pRange.')',PHP_EOL;
if ($pSheet !== NULL) { if ($pSheet !== NULL) {
// echo 'Passed sheet name is '.$pSheet->getTitle().'<br />'; $pSheetName = $pSheet->getTitle();
// echo 'Range reference is '.$pRange.'<br />'; // echo 'Passed sheet name is '.$pSheetName.PHP_EOL;
if (strpos ($pRange, '!') !== FALSE) { // echo 'Range reference is '.$pRange.PHP_EOL;
// echo '$pRange reference includes sheet reference<br />'; if (strpos ($pRange, '!') !== false) {
$worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pRange, TRUE); // echo '$pRange reference includes sheet reference',PHP_EOL;
$pSheet = $this->_workbook->getSheetByName($worksheetReference[0]); list($pSheetName,$pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
// echo 'New sheet name is '.$pSheet->getTitle().'<br />'; // echo 'New sheet name is '.$pSheetName,PHP_EOL;
$pRange = $worksheetReference[1]; // echo 'Adjusted Range reference is '.$pRange,PHP_EOL;
// echo 'Adjusted Range reference is '.$pRange.'<br />'; $pSheet = $this->_workbook->getSheetByName($pSheetName);
} }
// Extract range // Extract range
$aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange); $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
$pRange = $pSheet->getTitle().'!'.$pRange; $pRange = $pSheetName.'!'.$pRange;
if (!isset($aReferences[1])) { if (!isset($aReferences[1])) {
// Single cell in range // Single cell in range
list($currentCol,$currentRow) = sscanf($aReferences[0],'%[A-Z]%d'); list($currentCol,$currentRow) = sscanf($aReferences[0],'%[A-Z]%d');
$cellValue = NULL;
if ($pSheet->cellExists($aReferences[0])) { if ($pSheet->cellExists($aReferences[0])) {
$returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog); $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
} else { } else {
@ -3657,7 +3631,7 @@ class PHPExcel_Calculation {
foreach ($aReferences as $reference) { foreach ($aReferences as $reference) {
// Extract range // Extract range
list($currentCol,$currentRow) = sscanf($reference,'%[A-Z]%d'); list($currentCol,$currentRow) = sscanf($reference,'%[A-Z]%d');
$cellValue = NULL;
if ($pSheet->cellExists($reference)) { if ($pSheet->cellExists($reference)) {
$returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog); $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
} else { } else {
@ -3686,15 +3660,15 @@ class PHPExcel_Calculation {
// echo 'extractNamedRange('.$pRange.')<br />'; // echo 'extractNamedRange('.$pRange.')<br />';
if ($pSheet !== NULL) { if ($pSheet !== NULL) {
// echo 'Current sheet name is '.$pSheet->getTitle().'<br />'; $pSheetName = $pSheet->getTitle();
// echo 'Current sheet name is '.$pSheetName.'<br />';
// echo 'Range reference is '.$pRange.'<br />'; // echo 'Range reference is '.$pRange.'<br />';
if (strpos ($pRange, '!') !== FALSE) { if (strpos ($pRange, '!') !== false) {
// echo '$pRange reference includes sheet reference<br />'; // echo '$pRange reference includes sheet reference',PHP_EOL;
$worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pRange, TRUE); list($pSheetName,$pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
$pSheet = $this->_workbook->getSheetByName($worksheetReference[0]); // echo 'New sheet name is '.$pSheetName,PHP_EOL;
// echo 'New sheet name is '.$pSheet->getTitle().'<br />'; // echo 'Adjusted Range reference is '.$pRange,PHP_EOL;
$pRange = $worksheetReference[1]; $pSheet = $this->_workbook->getSheetByName($pSheetName);
// echo 'Adjusted Range reference is '.$pRange.'<br />';
} }
// Named range? // Named range?
@ -3729,6 +3703,7 @@ class PHPExcel_Calculation {
if (!isset($aReferences[1])) { if (!isset($aReferences[1])) {
// Single cell (or single column or row) in range // Single cell (or single column or row) in range
list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]); list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]);
$cellValue = NULL;
if ($pSheet->cellExists($aReferences[0])) { if ($pSheet->cellExists($aReferences[0])) {
$returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog); $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
} else { } else {
@ -3740,6 +3715,7 @@ class PHPExcel_Calculation {
// Extract range // Extract range
list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($reference); list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($reference);
// echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'<br />'; // echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'<br />';
$cellValue = NULL;
if ($pSheet->cellExists($reference)) { if ($pSheet->cellExists($reference)) {
$returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog); $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
} else { } else {

View File

@ -67,7 +67,9 @@ class PHPExcel_Calculation_Token_Stack {
} // function last() } // function last()
function __construct() { function clear() {
$this->_stack = array();
$this->_count = 0;
} }
} // class PHPExcel_Calculation_Token_Stack } // class PHPExcel_Calculation_Token_Stack

View File

@ -50,13 +50,6 @@ class PHPExcel_Cell
*/ */
private static $_valueBinder = NULL; private static $_valueBinder = NULL;
/**
* Cell Address (e.g. A1)
*
* @var string
*/
private $_coordinate;
/** /**
* Value of the cell * Value of the cell
* *
@ -86,7 +79,7 @@ class PHPExcel_Cell
/** /**
* Parent worksheet * Parent worksheet
* *
* @var PHPExcel_Worksheet * @var PHPExcel_CachedObjectStorage_CacheBase
*/ */
private $_parent; private $_parent;
@ -110,7 +103,7 @@ class PHPExcel_Cell
* @return void * @return void
**/ **/
public function notifyCacheController() { public function notifyCacheController() {
$this->_parent->getCellCacheController()->updateCacheData($this); $this->_parent->updateCacheData($this);
return $this; return $this;
} }
@ -118,7 +111,9 @@ class PHPExcel_Cell
$this->_parent = NULL; $this->_parent = NULL;
} }
public function attach($parent) { public function attach(PHPExcel_CachedObjectStorage_CacheBase $parent) {
$this->_parent = $parent; $this->_parent = $parent;
} }
@ -126,23 +121,18 @@ class PHPExcel_Cell
/** /**
* Create a new Cell * Create a new Cell
* *
* @param string $pColumn
* @param int $pRow
* @param mixed $pValue * @param mixed $pValue
* @param string $pDataType * @param string $pDataType
* @param PHPExcel_Worksheet $pSheet * @param PHPExcel_Worksheet $pSheet
* @throws PHPExcel_Exception * @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 // Initialise cell value
$this->_value = $pValue; $this->_value = $pValue;
// Set worksheet // Set worksheet cache
$this->_parent = $pSheet; $this->_parent = $pSheet->getCellCacheController();
// Set datatype? // Set datatype?
if ($pDataType !== NULL) { if ($pDataType !== NULL) {
@ -166,8 +156,7 @@ class PHPExcel_Cell
*/ */
public function getColumn() public function getColumn()
{ {
list($column) = sscanf($this->_coordinate, '%[A-Z]%d'); return $this->_parent->getCurrentColumn();
return $column;
} }
/** /**
@ -177,8 +166,7 @@ class PHPExcel_Cell
*/ */
public function getRow() public function getRow()
{ {
list(,$row) = sscanf($this->_coordinate, '%[A-Z]%d'); return $this->_parent->getCurrentRow();
return $row;
} }
/** /**
@ -188,7 +176,7 @@ class PHPExcel_Cell
*/ */
public function getCoordinate() public function getCoordinate()
{ {
return $this->_coordinate; return $this->_parent->getCurrentAddress();
} }
/** /**
@ -210,7 +198,7 @@ class PHPExcel_Cell
{ {
return (string) PHPExcel_Style_NumberFormat::toFormattedString( return (string) PHPExcel_Style_NumberFormat::toFormattedString(
$this->getCalculatedValue(), $this->getCalculatedValue(),
$this->_parent->getParent()->getCellXfByIndex($this->getXfIndex()) $this->getWorksheet()->getParent()->getCellXfByIndex($this->getXfIndex())
->getNumberFormat()->getFormatCode() ->getNumberFormat()->getFormatCode()
); );
} }
@ -289,7 +277,7 @@ class PHPExcel_Cell
try { try {
// echo 'Cell value for '.$this->getCoordinate().' is a formula: Calculating value<br />'; // echo 'Cell value for '.$this->getCoordinate().' is a formula: Calculating value<br />';
$result = PHPExcel_Calculation::getInstance( $result = PHPExcel_Calculation::getInstance(
$this->getParent()->getParent() $this->getWorksheet()->getParent()
)->calculateCellValue($this,$resetLog); )->calculateCellValue($this,$resetLog);
// $result = $this->getParent()->getParent()->getCalculationEngine()->calculateCellValue($this,$resetLog); // $result = $this->getParent()->getParent()->getCalculationEngine()->calculateCellValue($this,$resetLog);
// echo $this->getCoordinate().' calculation result is '.$result.'<br />'; // echo $this->getCoordinate().' calculation result is '.$result.'<br />';
@ -302,7 +290,7 @@ class PHPExcel_Cell
$result = '#N/A'; $result = '#N/A';
throw( throw(
new PHPExcel_Calculation_Exception( new PHPExcel_Calculation_Exception(
$this->getParent()->getTitle().'!'.$this->getCoordinate().' -> '.$ex->getMessage() $this->getWorksheet()->getTitle().'!'.$this->getCoordinate().' -> '.$ex->getMessage()
) )
); );
} }
@ -391,7 +379,7 @@ class PHPExcel_Cell
throw new PHPExcel_Exception('Cannot check for data validation when cell is not bound to a worksheet'); 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());
} }
/** /**
@ -406,7 +394,7 @@ class PHPExcel_Cell
throw new PHPExcel_Exception('Cannot get data validation for cell that is not bound to a worksheet'); 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());
} }
/** /**
@ -422,7 +410,7 @@ class PHPExcel_Cell
throw new PHPExcel_Exception('Cannot set data validation for cell that is not bound to a worksheet'); 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(); return $this->notifyCacheController();
} }
@ -439,7 +427,7 @@ class PHPExcel_Cell
throw new PHPExcel_Exception('Cannot check for hyperlink when cell is not bound to a worksheet'); 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());
} }
/** /**
@ -454,7 +442,7 @@ class PHPExcel_Cell
throw new PHPExcel_Exception('Cannot get hyperlink for cell that is not bound to a worksheet'); 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());
} }
/** /**
@ -470,7 +458,7 @@ class PHPExcel_Cell
throw new PHPExcel_Exception('Cannot set hyperlink for cell that is not bound to a worksheet'); 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(); return $this->notifyCacheController();
} }
@ -484,6 +472,15 @@ class PHPExcel_Cell
return $this->_parent; return $this->_parent;
} }
/**
* Get parent worksheet
*
* @return PHPExcel_Worksheet
*/
public function getWorksheet() {
return $this->_parent->getParent();
}
/** /**
* Re-bind parent * Re-bind parent
* *
@ -491,7 +488,7 @@ class PHPExcel_Cell
* @return PHPExcel_Cell * @return PHPExcel_Cell
*/ */
public function rebindParent(PHPExcel_Worksheet $parent) { public function rebindParent(PHPExcel_Worksheet $parent) {
$this->_parent = $parent; $this->_parent = $parent->getCellCacheController();
return $this->notifyCacheController(); return $this->notifyCacheController();
} }

File diff suppressed because it is too large Load Diff

View File

@ -102,8 +102,8 @@ class PHPExcel_Writer_CSV extends PHPExcel_Writer_Abstract implements PHPExcel_W
// Fetch sheet // Fetch sheet
$sheet = $this->_phpExcel->getSheet($this->_sheetIndex); $sheet = $this->_phpExcel->getSheet($this->_sheetIndex);
$saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->writeDebugLog; $saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->getWriteDebugLog();
PHPExcel_Calculation::getInstance($this->_phpExcel)->writeDebugLog = FALSE; PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog(FALSE);
$saveArrayReturnType = PHPExcel_Calculation::getArrayReturnType(); $saveArrayReturnType = PHPExcel_Calculation::getArrayReturnType();
PHPExcel_Calculation::setArrayReturnType(PHPExcel_Calculation::RETURN_ARRAY_AS_VALUE); 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); fclose($fileHandle);
PHPExcel_Calculation::setArrayReturnType($saveArrayReturnType); PHPExcel_Calculation::setArrayReturnType($saveArrayReturnType);
PHPExcel_Calculation::getInstance($this->_phpExcel)->writeDebugLog = $saveDebugLog; PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog($saveDebugLog);
} }
/** /**

View File

@ -179,8 +179,8 @@ class PHPExcel_Writer_Excel2007 extends PHPExcel_Writer_Abstract implements PHPE
} }
} }
$saveDebugLog = PHPExcel_Calculation::getInstance($this->_spreadSheet)->writeDebugLog; $saveDebugLog = PHPExcel_Calculation::getInstance($this->_spreadSheet)->getDebugLog()->getWriteDebugLog();
PHPExcel_Calculation::getInstance($this->_spreadSheet)->writeDebugLog = false; PHPExcel_Calculation::getInstance($this->_spreadSheet)->getDebugLog()->setWriteDebugLog(FALSE);
$saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType(); $saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType();
PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL); 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_Functions::setReturnDateType($saveDateReturnType);
PHPExcel_Calculation::getInstance($this->_spreadSheet)->writeDebugLog = $saveDebugLog; PHPExcel_Calculation::getInstance($this->_spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
// Close file // Close file
if ($objZip->close() === false) { if ($objZip->close() === false) {

View File

@ -1074,12 +1074,9 @@ class PHPExcel_Writer_Excel2007_Worksheet extends PHPExcel_Writer_Excel2007_Writ
$objWriter->writeAttribute('t', $mappedType); $objWriter->writeAttribute('t', $mappedType);
break; break;
case 'f': // Formula case 'f': // Formula
$calculatedValue = null; $calculatedValue = ($this->getParentWriter()->getPreCalculateFormulas()) ?
if ($this->getParentWriter()->getPreCalculateFormulas()) { $pCell->getCalculatedValue() :
$calculatedValue = $pCell->getCalculatedValue(); $cellValue;
} else {
$calculatedValue = $cellValue;
}
if (is_string($calculatedValue)) { if (is_string($calculatedValue)) {
$objWriter->writeAttribute('t', 'str'); $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()->getOffice2003Compatibility() === false) {
if ($this->getParentWriter()->getPreCalculateFormulas()) { if ($this->getParentWriter()->getPreCalculateFormulas()) {
$calculatedValue = $pCell->getCalculatedValue(); // $calculatedValue = $pCell->getCalculatedValue();
if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) != '#') { if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) != '#') {
$objWriter->writeElement('v', PHPExcel_Shared_String::FormatNumber($calculatedValue)); $objWriter->writeElement('v', PHPExcel_Shared_String::FormatNumber($calculatedValue));
} else { } else {

View File

@ -120,13 +120,13 @@ class PHPExcel_Writer_Excel5 extends PHPExcel_Writer_Abstract implements PHPExce
// garbage collect // garbage collect
$this->_phpExcel->garbageCollect(); $this->_phpExcel->garbageCollect();
$saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->writeDebugLog; $saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->getWriteDebugLog();
PHPExcel_Calculation::getInstance($this->_phpExcel)->writeDebugLog = false; PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog(FALSE);
$saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType(); $saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType();
PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL); PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL);
// initialize colors array // initialize colors array
$this->_colors = array(); $this->_colors = array();
// Initialise workbook writer // Initialise workbook writer
$this->_writerWorkbook = new PHPExcel_Writer_Excel5_Workbook($this->_phpExcel, $this->_writerWorkbook = new PHPExcel_Writer_Excel5_Workbook($this->_phpExcel,
@ -226,7 +226,7 @@ class PHPExcel_Writer_Excel5 extends PHPExcel_Writer_Abstract implements PHPExce
$res = $root->save($pFilename); $res = $root->save($pFilename);
PHPExcel_Calculation_Functions::setReturnDateType($saveDateReturnType); PHPExcel_Calculation_Functions::setReturnDateType($saveDateReturnType);
PHPExcel_Calculation::getInstance($this->_phpExcel)->writeDebugLog = $saveDebugLog; PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog($saveDebugLog);
} }
/** /**

View File

@ -152,8 +152,8 @@ class PHPExcel_Writer_HTML extends PHPExcel_Writer_Abstract implements PHPExcel_
// garbage collect // garbage collect
$this->_phpExcel->garbageCollect(); $this->_phpExcel->garbageCollect();
$saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->writeDebugLog; $saveDebugLog = PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->getWriteDebugLog();
PHPExcel_Calculation::getInstance($this->_phpExcel)->writeDebugLog = false; PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog(FALSE);
$saveArrayReturnType = PHPExcel_Calculation::getArrayReturnType(); $saveArrayReturnType = PHPExcel_Calculation::getArrayReturnType();
PHPExcel_Calculation::setArrayReturnType(PHPExcel_Calculation::RETURN_ARRAY_AS_VALUE); 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); fclose($fileHandle);
PHPExcel_Calculation::setArrayReturnType($saveArrayReturnType); PHPExcel_Calculation::setArrayReturnType($saveArrayReturnType);
PHPExcel_Calculation::getInstance($this->_phpExcel)->writeDebugLog = $saveDebugLog; PHPExcel_Calculation::getInstance($this->_phpExcel)->getDebugLog()->setWriteDebugLog($saveDebugLog);
} }
/** /**