private function PHPExcel_Calculation::_processTokenStack in Loft Data Grids 7.2
Same name and namespace in other branches
- 6.2 vendor/phpoffice/phpexcel/Classes/PHPExcel/Calculation.php \PHPExcel_Calculation::_processTokenStack()
1 call to PHPExcel_Calculation::_processTokenStack()
- PHPExcel_Calculation::_calculateFormulaValue in vendor/
phpoffice/ phpexcel/ Classes/ PHPExcel/ Calculation.php - * Parse a cell formula and calculate its value * *
File
- vendor/
phpoffice/ phpexcel/ Classes/ PHPExcel/ Calculation.php, line 3098
Class
- PHPExcel_Calculation
- PHPExcel_Calculation (Multiton)
Code
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 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();
// Loop through each token in turn
foreach ($tokens as $tokenData) {
// print_r($tokenData);
// echo '<br />';
$token = $tokenData['value'];
// echo '<b>Token is '.$token.'</b><br />';
// if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
if (isset(self::$_binaryOperators[$token])) {
// echo 'Token is a binary operator<br />';
// We must have two operands, error if we don't
if (($operand2Data = $stack
->pop()) === NULL) {
return $this
->_raiseFormulaError('Internal error - Operand value missing from stack');
}
if (($operand1Data = $stack
->pop()) === NULL) {
return $this
->_raiseFormulaError('Internal error - Operand value missing from stack');
}
$operand1 = self::_dataTestReference($operand1Data);
$operand2 = self::_dataTestReference($operand2Data);
// Log what we're doing
if ($token == ':') {
$this->_debugLog
->writeDebugLog('Evaluating Range ', $this
->_showValue($operand1Data['reference']), ' ', $token, ' ', $this
->_showValue($operand2Data['reference']));
}
else {
$this->_debugLog
->writeDebugLog('Evaluating ', $this
->_showValue($operand1), ' ', $token, ' ', $this
->_showValue($operand2));
}
// Process the operation in the appropriate manner
switch ($token) {
// Comparison (Boolean) Operators
case '>':
// Greater than
case '<':
// Less than
case '>=':
// Greater than or Equal to
case '<=':
// Less than or Equal to
case '=':
// Equality
case '<>':
// Inequality
$this
->_executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
break;
// Binary Operators
case ':':
// Range
$sheet1 = $sheet2 = '';
if (strpos($operand1Data['reference'], '!') !== FALSE) {
list($sheet1, $operand1Data['reference']) = explode('!', $operand1Data['reference']);
}
else {
$sheet1 = $pCellParent !== NULL ? $pCellWorksheet
->getTitle() : '';
}
if (strpos($operand2Data['reference'], '!') !== FALSE) {
list($sheet2, $operand2Data['reference']) = explode('!', $operand2Data['reference']);
}
else {
$sheet2 = $sheet1;
}
if ($sheet1 == $sheet2) {
if ($operand1Data['reference'] === NULL) {
if (trim($operand1Data['value']) != '' && is_numeric($operand1Data['value'])) {
$operand1Data['reference'] = $pCell
->getColumn() . $operand1Data['value'];
}
elseif (trim($operand1Data['reference']) == '') {
$operand1Data['reference'] = $pCell
->getCoordinate();
}
else {
$operand1Data['reference'] = $operand1Data['value'] . $pCell
->getRow();
}
}
if ($operand2Data['reference'] === NULL) {
if (trim($operand2Data['value']) != '' && is_numeric($operand2Data['value'])) {
$operand2Data['reference'] = $pCell
->getColumn() . $operand2Data['value'];
}
elseif (trim($operand2Data['reference']) == '') {
$operand2Data['reference'] = $pCell
->getCoordinate();
}
else {
$operand2Data['reference'] = $operand2Data['value'] . $pCell
->getRow();
}
}
$oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference']));
$oCol = $oRow = array();
foreach ($oData as $oDatum) {
$oCR = PHPExcel_Cell::coordinateFromString($oDatum);
$oCol[] = PHPExcel_Cell::columnIndexFromString($oCR[0]) - 1;
$oRow[] = $oCR[1];
}
$cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)) . min($oRow) . ':' . PHPExcel_Cell::stringFromColumnIndex(max($oCol)) . max($oRow);
if ($pCellParent !== NULL) {
$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);
}
break;
case '+':
// Addition
$this
->_executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'plusEquals', $stack);
break;
case '-':
// Subtraction
$this
->_executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'minusEquals', $stack);
break;
case '*':
// Multiplication
$this
->_executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayTimesEquals', $stack);
break;
case '/':
// Division
$this
->_executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayRightDivide', $stack);
break;
case '^':
// Exponential
$this
->_executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'power', $stack);
break;
case '&':
// Concatenation
// 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_bool($operand1)) {
$operand1 = $operand1 ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
}
if (is_bool($operand2)) {
$operand2 = $operand2 ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
}
if (is_array($operand1) || is_array($operand2)) {
// Ensure that both operands are arrays/matrices
self::_checkMatrixOperands($operand1, $operand2, 2);
try {
// Convert operand 1 from a PHP array to a matrix
$matrix = new PHPExcel_Shared_JAMA_Matrix($operand1);
// Perform the required operation against the operand 1 matrix, passing in operand 2
$matrixResult = $matrix
->concat($operand2);
$result = $matrixResult
->getArray();
} catch (PHPExcel_Exception $ex) {
$this->_debugLog
->writeDebugLog('JAMA Matrix Exception: ', $ex
->getMessage());
$result = '#VALUE!';
}
}
else {
$result = '"' . str_replace('""', '"', self::_unwrapResult($operand1, '"') . self::_unwrapResult($operand2, '"')) . '"';
}
$this->_debugLog
->writeDebugLog('Evaluation Result is ', $this
->_showTypeDetails($result));
$stack
->push('Value', $result);
break;
case '|':
// Intersect
$rowIntersect = array_intersect_key($operand1, $operand2);
$cellIntersect = $oCol = $oRow = array();
foreach (array_keys($rowIntersect) as $row) {
$oRow[] = $row;
foreach ($rowIntersect[$row] as $col => $data) {
$oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1;
$cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]);
}
}
$cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)) . min($oRow) . ':' . PHPExcel_Cell::stringFromColumnIndex(max($oCol)) . max($oRow);
$this->_debugLog
->writeDebugLog('Evaluation Result is ', $this
->_showTypeDetails($cellIntersect));
$stack
->push('Value', $cellIntersect, $cellRef);
break;
}
// if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
}
elseif ($token === '~' || $token === '%') {
// echo 'Token is a unary operator<br />';
if (($arg = $stack
->pop()) === NULL) {
return $this
->_raiseFormulaError('Internal error - Operand value missing from stack');
}
$arg = $arg['value'];
if ($token === '~') {
// echo 'Token is a negation operator<br />';
$this->_debugLog
->writeDebugLog('Evaluating Negation of ', $this
->_showValue($arg));
$multiplier = -1;
}
else {
// echo 'Token is a percentile operator<br />';
$this->_debugLog
->writeDebugLog('Evaluating Percentile of ', $this
->_showValue($arg));
$multiplier = 0.01;
}
if (is_array($arg)) {
self::_checkMatrixOperands($arg, $multiplier, 2);
try {
$matrix1 = new PHPExcel_Shared_JAMA_Matrix($arg);
$matrixResult = $matrix1
->arrayTimesEquals($multiplier);
$result = $matrixResult
->getArray();
} catch (PHPExcel_Exception $ex) {
$this->_debugLog
->writeDebugLog('JAMA Matrix Exception: ', $ex
->getMessage());
$result = '#VALUE!';
}
$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;
// echo 'Element '.$token.' is a Cell reference<br />';
if (isset($matches[8])) {
// echo 'Reference is a Range of cells<br />';
if ($pCell === NULL) {
// We can't access the range, so return a REF error
$cellValue = PHPExcel_Calculation_Functions::REF();
}
else {
$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) {
// 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].'<br />';
$this->_debugLog
->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]);
if ($pCellParent !== NULL) {
$cellValue = $this
->extractCellRange($cellRef, $this->_workbook
->getSheetByName($matches[2]), FALSE);
}
else {
return $this
->_raiseFormulaError('Unable to access Cell Reference');
}
$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<br />';
$this->_debugLog
->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet');
if ($pCellParent !== NULL) {
$cellValue = $this
->extractCellRange($cellRef, $pCellWorksheet, FALSE);
}
else {
return $this
->_raiseFormulaError('Unable to access Cell Reference');
}
$this->_debugLog
->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this
->_showTypeDetails($cellValue));
}
}
}
else {
// echo 'Reference is a single Cell<br />';
if ($pCell === NULL) {
// We can't access the cell, so return a REF error
$cellValue = PHPExcel_Calculation_Functions::REF();
}
else {
$cellRef = $matches[6] . $matches[7];
if ($matches[2] > '') {
$matches[2] = trim($matches[2], "\"'");
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].'<br />';
$this->_debugLog
->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]);
if ($pCellParent !== NULL) {
$cellSheet = $this->_workbook
->getSheetByName($matches[2]);
if ($cellSheet && $cellSheet
->cellExists($cellRef)) {
$cellValue = $this
->extractCellRange($cellRef, $this->_workbook
->getSheetByName($matches[2]), FALSE);
$pCell
->attach($pCellParent);
}
else {
$cellValue = NULL;
}
}
else {
return $this
->_raiseFormulaError('Unable to access Cell Reference');
}
$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<br />';
$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;
}
$this->_debugLog
->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this
->_showTypeDetails($cellValue));
}
}
}
$stack
->push('Value', $cellValue, $cellRef);
// if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
}
elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $token, $matches)) {
// echo 'Token is a function<br />';
$functionName = $matches[1];
$argCount = $stack
->pop();
$argCount = $argCount['value'];
if ($functionName != 'MKMATRIX') {
$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])) {
$functionCall = self::$_PHPExcelFunctions[$functionName]['functionCall'];
$passByReference = isset(self::$_PHPExcelFunctions[$functionName]['passByReference']);
$passCellReference = isset(self::$_PHPExcelFunctions[$functionName]['passCellReference']);
}
elseif (isset(self::$_controlFunctions[$functionName])) {
$functionCall = self::$_controlFunctions[$functionName]['functionCall'];
$passByReference = isset(self::$_controlFunctions[$functionName]['passByReference']);
$passCellReference = isset(self::$_controlFunctions[$functionName]['passCellReference']);
}
// get the arguments for this function
// echo 'Function '.$functionName.' expects '.$argCount.' arguments<br />';
$args = $argArrayVals = array();
for ($i = 0; $i < $argCount; ++$i) {
$arg = $stack
->pop();
$a = $argCount - $i - 1;
if ($passByReference && isset(self::$_PHPExcelFunctions[$functionName]['passByReference'][$a]) && self::$_PHPExcelFunctions[$functionName]['passByReference'][$a]) {
if ($arg['reference'] === NULL) {
$args[] = $cellID;
if ($functionName != 'MKMATRIX') {
$argArrayVals[] = $this
->_showValue($cellID);
}
}
else {
$args[] = $arg['reference'];
if ($functionName != 'MKMATRIX') {
$argArrayVals[] = $this
->_showValue($arg['reference']);
}
}
}
else {
$args[] = self::_unwrapResult($arg['value']);
if ($functionName != 'MKMATRIX') {
$argArrayVals[] = $this
->_showValue($arg['value']);
}
}
}
// Reverse the order of the arguments
krsort($args);
if ($passByReference && $argCount == 0) {
$args[] = $cellID;
$argArrayVals[] = $this
->_showValue($cellID);
}
// echo 'Arguments are: ';
// print_r($args);
// echo '<br />';
if ($functionName != 'MKMATRIX') {
if ($this->_debugLog
->getWriteDebugLog()) {
krsort($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->_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->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($arg), ' )');
// $r = call_user_func_array($functionCall,$arg);
// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r));
// $result[$row][] = $r;
// }
// ++$row;
// } else {
// $this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($args), ' )');
// $r = call_user_func_array($functionCall,$args);
// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r));
// $result[] = $r;
// }
// }
// } else {
// Process the argument with the appropriate function call
if ($passCellReference) {
$args[] = $pCell;
}
if (strpos($functionCall, '::') !== FALSE) {
$result = call_user_func_array(explode('::', $functionCall), $args);
}
else {
foreach ($args as &$arg) {
$arg = PHPExcel_Calculation_Functions::flattenSingleValue($arg);
}
unset($arg);
$result = call_user_func_array($functionCall, $args);
}
// }
if ($functionName != 'MKMATRIX') {
$this->_debugLog
->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this
->_showTypeDetails($result));
}
$stack
->push('Value', self::_wrapResult($result));
}
}
else {
// if the token is a number, boolean, string or an Excel error, push it onto the stack
if (isset(self::$_ExcelConstants[strtoupper($token)])) {
$excelConstant = strtoupper($token);
// echo 'Token is a PHPExcel constant: '.$excelConstant.'<br />';
$stack
->push('Constant Value', 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<br />';
$stack
->push('Value', $token);
// if the token is a named range, push the named range name onto the stack
}
elseif (preg_match('/^' . self::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $token, $matches)) {
// echo 'Token is a named range<br />';
$namedRange = $matches[6];
// echo 'Named Range is '.$namedRange.'<br />';
$this->_debugLog
->writeDebugLog('Evaluating Named Range ', $namedRange);
$cellValue = $this
->extractNamedRange($namedRange, NULL !== $pCell ? $pCellWorksheet : NULL, FALSE);
$pCell
->attach($pCellParent);
$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}'");
}
}
}
// when we're out of tokens, the stack should have a single element, the final result
if ($stack
->count() != 1) {
return $this
->_raiseFormulaError("internal error");
}
$output = $stack
->pop();
$output = $output['value'];
// if ((is_array($output)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
// return array_shift(PHPExcel_Calculation_Functions::flattenArray($output));
// }
return $output;
}