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 $pCellParent = !is_null($pCell) ? $pCell->getParent() : null; $stack = new PHPExcel_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 (in_array($token, self::$_binaryOperators, true)) { // echo 'Token is a binary operator<br />'; // We must have two operands, error if we don't if (is_null($operand2Data = $stack->pop())) { return $this->_raiseFormulaError('Internal error - Operand value missing from stack'); } if (is_null($operand1Data = $stack->pop())) { return $this->_raiseFormulaError('Internal error - Operand value missing from stack'); } // Log what we're doing $operand1 = $operand1Data['value']; $operand2 = $operand2Data['value']; if ($token == ':') { $this->_writeDebug('Evaluating Range ' . self::_showValue($operand1Data['reference']) . $token . self::_showValue($operand2Data['reference'])); } else { $this->_writeDebug('Evaluating ' . self::_showValue($operand1) . ' ' . $token . ' ' . self::_showValue($operand2)); } // Process the operation in the appropriate manner switch ($token) { // Comparison (Boolean) Operators case '>': // Greater than // Greater than case '<': // Less than // Less than case '>=': // Greater than or Equal to // Greater than or Equal to case '<=': // Less than or Equal to // Less than or Equal to case '=': // Equality // Equality case '<>': // Inequality $this->_executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack); break; // Binary Operators // Binary Operators case ':': // Range $sheet1 = $sheet2 = ''; if (strpos($operand1Data['reference'], '!') !== false) { list($sheet1, $operand1Data['reference']) = explode('!', $operand1Data['reference']); } else { $sheet1 = !is_null($pCellParent) ? $pCellParent->getTitle() : ''; } if (strpos($operand2Data['reference'], '!') !== false) { list($sheet2, $operand2Data['reference']) = explode('!', $operand2Data['reference']); } else { $sheet2 = $sheet1; } if ($sheet1 == $sheet2) { if (is_null($operand1Data['reference'])) { if (trim($operand1Data['value']) != '' && is_numeric($operand1Data['value'])) { $operand1Data['reference'] = $pCell->getColumn() . $operand1Data['value']; } elseif (trim($operand1Data['reference']) == '') { $operand1Data['reference'] = $pCell->getColumn() . $pCell->getRow(); } else { $operand1Data['reference'] = $operand1Data['value'] . $pCell->getRow(); } } if (is_null($operand2Data['reference'])) { if (trim($operand2Data['value']) != '' && is_numeric($operand2Data['value'])) { $operand2Data['reference'] = $pCell->getColumn() . $operand2Data['value']; } elseif (trim($operand2Data['reference']) == '') { $operand2Data['reference'] = $pCell->getColumn() . $pCell->getRow(); } 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 (!is_null($pCellParent)) { $cellValue = $this->extractCellRange($cellRef, $pCellParent->getParent()->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 Matrix($operand1); // Perform the required operation against the operand 1 matrix, passing in operand 2 $matrixResult = $matrix->concat($operand2); $result = $matrixResult->getArray(); } catch (Exception $ex) { $this->_writeDebug('JAMA Matrix Exception: ' . $ex->getMessage()); $result = '#VALUE!'; } } else { $result = '"' . str_replace('""', '"', self::_unwrapResult($operand1, '"') . self::_unwrapResult($operand2, '"')) . '"'; } $this->_writeDebug('Evaluation Result is ' . self::_showTypeDetails($result)); $stack->push('Value', $result); break; case '|': // Intersect $rowIntersect = array_intersect_key($operand1, $operand2); $cellIntersect = $oCol = $oRow = array(); foreach (array_keys($rowIntersect) as $col) { $oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1; $cellIntersect[$col] = array_intersect_key($operand1[$col], $operand2[$col]); foreach ($cellIntersect[$col] as $row => $data) { $oRow[] = $row; } } $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)) . min($oRow) . ':' . PHPExcel_Cell::stringFromColumnIndex(max($oCol)) . max($oRow); $this->_writeDebug('Evaluation Result is ' . self::_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 (is_null($arg = $stack->pop())) { return $this->_raiseFormulaError('Internal error - Operand value missing from stack'); } $arg = $arg['value']; if ($token === '~') { // echo 'Token is a negation operator<br />'; $this->_writeDebug('Evaluating Negation of ' . self::_showValue($arg)); $multiplier = -1; } else { // echo 'Token is a percentile operator<br />'; $this->_writeDebug('Evaluating Percentile of ' . self::_showValue($arg)); $multiplier = 0.01; } if (is_array($arg)) { self::_checkMatrixOperands($arg, $multiplier, 2); try { $matrix1 = new Matrix($arg); $matrixResult = $matrix1->arrayTimesEquals($multiplier); $result = $matrixResult->getArray(); } catch (Exception $ex) { $this->_writeDebug('JAMA Matrix Exception: ' . $ex->getMessage()); $result = '#VALUE!'; } $this->_writeDebug('Evaluation Result is ' . self::_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 (is_null($pCell)) { // 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], "\"'"); // echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />'; $this->_writeDebug('Evaluating Cell Range ' . $cellRef . ' in worksheet ' . $matches[2]); if (!is_null($pCellParent)) { $cellValue = $this->extractCellRange($cellRef, $pCellParent->getParent()->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 ' . self::_showTypeDetails($cellValue)); // $cellRef = $matches[2].'!'.$cellRef; } else { // echo '$cellRef='.$cellRef.' in current worksheet<br />'; $this->_writeDebug('Evaluating Cell Range ' . $cellRef . ' in current worksheet'); if (!is_null($pCellParent)) { $cellValue = $this->extractCellRange($cellRef, $pCellParent, false); } else { return $this->_raiseFormulaError('Unable to access Cell Reference'); } $this->_writeDebug('Evaluation Result for cells ' . $cellRef . ' is ' . self::_showTypeDetails($cellValue)); } } } else { // echo 'Reference is a single Cell<br />'; if (is_null($pCell)) { // 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], "\"'"); // echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />'; $this->_writeDebug('Evaluating Cell ' . $cellRef . ' in worksheet ' . $matches[2]); if (!is_null($pCellParent)) { if ($pCellParent->getParent()->getSheetByName($matches[2])->cellExists($cellRef)) { $cellValue = $this->extractCellRange($cellRef, $pCellParent->getParent()->getSheetByName($matches[2]), false); $pCell->attach($pCellParent); } else { $cellValue = PHPExcel_Calculation_Functions::REF(); } } else { return $this->_raiseFormulaError('Unable to access Cell Reference'); } $this->_writeDebug('Evaluation Result for cell ' . $cellRef . ' in worksheet ' . $matches[2] . ' is ' . self::_showTypeDetails($cellValue)); // $cellRef = $matches[2].'!'.$cellRef; } else { // echo '$cellRef='.$cellRef.' in current worksheet<br />'; $this->_writeDebug('Evaluating Cell ' . $cellRef . ' in current worksheet'); if ($pCellParent->cellExists($cellRef)) { $cellValue = $this->extractCellRange($cellRef, $pCellParent, false); $pCell->attach($pCellParent); } else { $cellValue = NULL; } $this->_writeDebug('Evaluation Result for cell ' . $cellRef . ' is ' . self::_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->_writeDebug('Evaluating Function ' . self::_localeFunc($functionName) . '() with ' . ($argCount == 0 ? 'no' : $argCount) . ' argument' . ($argCount == 1 ? '' : 's')); } if (array_key_exists($functionName, self::$_PHPExcelFunctions) || array_key_exists($functionName, self::$_controlFunctions)) { // function if (array_key_exists($functionName, self::$_PHPExcelFunctions)) { $functionCall = self::$_PHPExcelFunctions[$functionName]['functionCall']; $passByReference = isset(self::$_PHPExcelFunctions[$functionName]['passByReference']); $passCellReference = isset(self::$_PHPExcelFunctions[$functionName]['passCellReference']); } elseif (array_key_exists($functionName, self::$_controlFunctions)) { $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 (is_null($arg['reference'])) { $args[] = $cellID; if ($functionName != 'MKMATRIX') { $argArrayVals[] = self::_showValue($cellID); } } else { $args[] = $arg['reference']; if ($functionName != 'MKMATRIX') { $argArrayVals[] = self::_showValue($arg['reference']); } } } else { $args[] = self::_unwrapResult($arg['value']); if ($functionName != 'MKMATRIX') { $argArrayVals[] = self::_showValue($arg['value']); } } } // Reverse the order of the arguments krsort($args); if ($passByReference && $argCount == 0) { $args[] = $cellID; $argArrayVals[] = self::_showValue($cellID); } // echo 'Arguments are: '; // print_r($args); // echo '<br />'; if ($functionName != 'MKMATRIX') { krsort($argArrayVals); $this->_writeDebug('Evaluating ' . self::_localeFunc($functionName) . '( ' . implode(self::$_localeArgumentSeparator . ' ', $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: '.self::_showValue($operand1)); // $result = array(); // $row = 0; // foreach($operand1 as $args) { // if (is_array($args)) { // foreach($args as $arg) { // $this->_writeDebug('Evaluating '.self::_localeFunc($functionName).'( '.self::_showValue($arg).' )'); // $r = call_user_func_array($functionCall,$arg); // $this->_writeDebug('Evaluation Result for '.self::_localeFunc($functionName).'() function call is '.self::_showTypeDetails($r)); // $result[$row][] = $r; // } // ++$row; // } else { // $this->_writeDebug('Evaluating '.self::_localeFunc($functionName).'( '.self::_showValue($args).' )'); // $r = call_user_func_array($functionCall,$args); // $this->_writeDebug('Evaluation Result for '.self::_localeFunc($functionName).'() function call is '.self::_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->_writeDebug('Evaluation Result for ' . self::_localeFunc($functionName) . '() function call is ' . self::_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 (array_key_exists(strtoupper($token), self::$_ExcelConstants)) { $excelConstant = strtoupper($token); // echo 'Token is a PHPExcel constant: '.$excelConstant.'<br />'; $stack->push('Constant Value', self::$_ExcelConstants[$excelConstant]); $this->_writeDebug('Evaluating Constant ' . $excelConstant . ' as ' . self::_showTypeDetails(self::$_ExcelConstants[$excelConstant])); } elseif (is_numeric($token) || is_bool($token) || is_null($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->_writeDebug('Evaluating Named Range ' . $namedRange); $cellValue = $this->extractNamedRange($namedRange, null !== $pCell ? $pCellParent : null, false); $pCell->attach($pCellParent); $this->_writeDebug('Evaluation Result for named range ' . $namedRange . ' is ' . self::_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; }
private function _processTokenStack($tokens, $cellID = null, PHPExcel_Cell $pCell = null) { if ($tokens == false) { return false; } $stack = new PHPExcel_Token_Stack(); // Loop through each token in turn foreach ($tokens as $token) { // 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 (in_array($token, $this->_binaryOperators, true)) { // echo 'Token is a binary operator<br />'; // We must have two operands, error if we don't if (is_null($operand2 = $stack->pop())) { return $this->_raiseFormulaError('Internal error - Operand value missing from stack'); } if (is_null($operand1 = $stack->pop())) { return $this->_raiseFormulaError('Internal error - Operand value missing from stack'); } // Log what we're doing $this->_writeDebug($cellID, 'Evaluating ' . self::_showValue($operand1) . ' ' . $token . ' ' . self::_showValue($operand2)); // Process the operation in the appropriate manner switch ($token) { // Comparison (Boolean) Operators case '>': // Greater than // Greater than case '<': // Less than // Less than case '>=': // Greater than or Equal to // Greater than or Equal to case '<=': // Less than or Equal to // Less than or Equal to case '=': // Equality // Equality case '<>': // Inequality $this->_executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack); break; // Binary Operators // Binary Operators 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_array($operand1) || is_array($operand2)) { // Ensure that both operands are arrays/matrices self::_checkMatrixOperands($operand1, $operand2); try { // Convert operand 1 from a PHP array to a matrix $matrix = new Matrix($operand1); // Perform the required operation against the operand 1 matrix, passing in operand 2 $matrixResult = $matrix->concat($operand2); $result = $matrixResult->getArray(); } catch (Exception $ex) { $this->_writeDebug($cellID, 'JAMA Matrix Exception: ' . $ex->getMessage()); $result = '#VALUE!'; } } else { $result = '"' . str_replace('""', '"', self::_unwrapResult($operand1, '"') . self::_unwrapResult($operand2, '"')) . '"'; } $this->_writeDebug($cellID, 'Evaluation Result is ' . self::_showTypeDetails($result)); $stack->push($result); 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 (is_null($arg = $stack->pop())) { return $this->_raiseFormulaError('Internal error - Operand value missing from stack'); } if ($token === "_") { // echo 'Token is a negation operator<br />'; $this->_writeDebug($cellID, 'Evaluating Negation of ' . self::_showValue($arg)); $multiplier = -1; } else { // echo 'Token is a percentile operator<br />'; $this->_writeDebug($cellID, 'Evaluating Percentile of ' . self::_showValue($arg)); $multiplier = 0.01; } if (is_array($arg)) { self::_checkMatrixOperands($arg, $multiplier); try { $matrix1 = new Matrix($arg); $matrixResult = $matrix1->arrayTimesEquals($multiplier); $result = $matrixResult->getArray(); } catch (Exception $ex) { $this->_writeDebug($cellID, 'JAMA Matrix Exception: ' . $ex->getMessage()); $result = '#VALUE!'; } } else { $result = $multiplier * $arg; } $this->_writeDebug($cellID, 'Evaluation Result is ' . self::_showTypeDetails($result)); $stack->push($result); } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token, $matches)) { // echo 'Element '.$token.' is a Cell reference<br />'; if (isset($matches[8])) { // echo 'Reference is a Range of cells<br />'; if (is_null($pCell)) { // 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] > '') { // echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />'; $this->_writeDebug($cellID, 'Evaluating Cell Range ' . $cellRef . ' in worksheet ' . $matches[2]); $cellValue = $this->extractCellRange($cellRef, $pCell->getParent()->getParent()->getSheetByName($matches[2]), false); $this->_writeDebug($cellID, 'Evaluation Result for cells ' . $cellRef . ' in worksheet ' . $matches[2] . ' is ' . self::_showTypeDetails($cellValue)); } else { // echo '$cellRef='.$cellRef.' in current worksheet<br />'; $this->_writeDebug($cellID, 'Evaluating Cell Range ' . $cellRef . ' in current worksheet'); $cellValue = $this->extractCellRange($cellRef, $pCell->getParent(), false); $this->_writeDebug($cellID, 'Evaluation Result for cells ' . $cellRef . ' is ' . self::_showTypeDetails($cellValue)); } } } else { // echo 'Reference is a single Cell<br />'; if (is_null($pCell)) { // 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] > '') { // echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />'; $this->_writeDebug($cellID, 'Evaluating Cell ' . $cellRef . ' in worksheet ' . $matches[2]); if ($pCell->getParent()->cellExists($cellRef)) { $cellValue = $this->extractCellRange($cellRef, $pCell->getParent()->getParent()->getSheetByName($matches[2]), false); } else { $cellValue = null; } $this->_writeDebug($cellID, 'Evaluation Result for cell ' . $cellRef . ' in worksheet ' . $matches[2] . ' is ' . self::_showTypeDetails($cellValue)); } else { // echo '$cellRef='.$cellRef.' in current worksheet<br />'; $this->_writeDebug($cellID, 'Evaluating Cell ' . $cellRef . ' in current worksheet'); if ($pCell->getParent()->cellExists($cellRef)) { $cellValue = $pCell->getParent()->getCell($cellRef)->getCalculatedValue(false); } else { $cellValue = ''; } $this->_writeDebug($cellID, 'Evaluation Result for cell ' . $cellRef . ' is ' . self::_showTypeDetails($cellValue)); } } } $stack->push($cellValue); // 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(); if ($functionName != 'MKMATRIX') { $this->_writeDebug($cellID, 'Evaluating Function ' . $functionName . '() with ' . ($argCount == 0 ? 'no' : $argCount) . ' argument' . ($argCount == 1 ? '' : 's')); } if (array_key_exists($functionName, $this->_controlFunctions) || array_key_exists($functionName, $this->_PHPExcelFunctions)) { // function if (array_key_exists($functionName, $this->_controlFunctions)) { $functionCall = $this->_controlFunctions[$functionName]['functionCall']; } elseif (array_key_exists($functionName, $this->_PHPExcelFunctions)) { $functionCall = $this->_PHPExcelFunctions[$functionName]['functionCall']; } // get the arguments for this function // echo 'Function '.$functionName.' expects '.$argCount.' arguments<br />'; $args = array(); for ($i = $argCount; $i > 0; --$i) { $arg = $stack->pop(); // if (is_null($arg)) return $this->_raiseFormulaError("internal error"); $args[$i] = $arg; } // Reverse the order of the arguments ksort($args); // echo 'Arguments are: '; // print_r($args); // echo '<br />'; if ($functionName != 'MKMATRIX') { $argArrayVals = array(); foreach ($args as &$arg) { $argArrayVals[] = self::_showValue($arg); $arg = self::_unwrapResult($arg); } unset($arg); $this->_writeDebug($cellID, 'Evaluating ' . $functionName . '( ' . implode(', ', $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($cellID,'Argument is a matrix: '.self::_showValue($operand1)); // $result = array(); // $row = 0; // foreach($operand1 as $args) { // if (is_array($args)) { // foreach($args as $arg) { // $this->_writeDebug($cellID,'Evaluating '. $functionName.'( '.self::_showValue($arg).' )'); // $r = call_user_func_array($functionCall,$arg); // $this->_writeDebug($cellID,'Evaluation Result is '.self::_showTypeDetails($r)); // $result[$row][] = $r; // } // ++$row; // } else { // $this->_writeDebug($cellID,'Evaluating '. $functionName.'( '.self::_showValue($args).' )'); // $r = call_user_func_array($functionCall,$args); // $this->_writeDebug($cellID,'Evaluation Result is '.self::_showTypeDetails($r)); // $result[] = $r; // } // } // } else { // Process the argument with the appropriate function call $result = call_user_func_array($functionCall, $args); // } if ($functionName != 'MKMATRIX') { $this->_writeDebug($cellID, 'Evaluation Result is ' . self::_showTypeDetails($result)); } $stack->push(self::_wrapResult($result)); } } else { // if the token is a number, boolean, string or an Excel error, push it onto the stack if (array_key_exists(strtoupper($token), $this->_ExcelConstants)) { $excelConstant = strtoupper($token); // echo 'Token is a PHPExcel constant: '.$excelConstant.'<br />'; $stack->push($this->_ExcelConstants[$excelConstant]); $this->_writeDebug($cellID, 'Evaluating Constant ' . $excelConstant . ' as ' . self::_showTypeDetails($this->_ExcelConstants[$excelConstant])); } elseif (is_null($token) || $token == '' || is_bool($token) || is_numeric($token) || $token[0] == '"' || $token[0] == '#') { // echo 'Token is a number, boolean, string or an Excel error<br />'; $stack->push($token); // if the token is a constant, push the constant value on 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->_writeDebug($cellID, 'Evaluating Named Range ' . $namedRange); $cellValue = $this->extractNamedRange($namedRange, $pCell->getParent(), false); $this->_writeDebug($cellID, 'Evaluation Result for named range ' . $namedRange . ' is ' . self::_showTypeDetails($cellValue)); $stack->push($cellValue); } 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(); if (is_array($output) && self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY) { return array_unshift(array_unshift($output)); } return $output; }