diff --git a/Classes/PHPExcel/Calculation.php b/Classes/PHPExcel/Calculation.php index 152c98e1..19e8edd9 100644 --- a/Classes/PHPExcel/Calculation.php +++ b/Classes/PHPExcel/Calculation.php @@ -3557,15 +3557,37 @@ class PHPExcel_Calculation { if (is_string($operand1) && $operand1 > '' && $operand1{0} == '"') { $operand1 = self::_unwrapResult($operand1); } if (is_string($operand2) && $operand2 > '' && $operand2{0} == '"') { $operand2 = self::_unwrapResult($operand2); } + // Use case insensitive comparaison if not OpenOffice mode + if (PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) + { + if (is_string($operand1)) { + $operand1 = strtoupper($operand1); + } + + if (is_string($operand2)) { + $operand2 = strtoupper($operand2); + } + } + + $useLowercaseFirstComparison = is_string($operand1) && is_string($operand2) && PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE; + // execute the necessary operation switch ($operation) { // Greater than case '>': - $result = ($operand1 > $operand2); + if ($useLowercaseFirstComparison) { + $result = $this->strcmpLowercaseFirst($operand1, $operand2) > 0; + } else { + $result = ($operand1 > $operand2); + } break; // Less than case '<': - $result = ($operand1 < $operand2); + if ($useLowercaseFirstComparison) { + $result = $this->strcmpLowercaseFirst($operand1, $operand2) < 0; + } else { + $result = ($operand1 < $operand2); + } break; // Equality case '=': @@ -3573,11 +3595,19 @@ class PHPExcel_Calculation { break; // Greater than or equal case '>=': - $result = ($operand1 >= $operand2); + if ($useLowercaseFirstComparison) { + $result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0; + } else { + $result = ($operand1 >= $operand2); + } break; // Less than or equal case '<=': - $result = ($operand1 <= $operand2); + if ($useLowercaseFirstComparison) { + $result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0; + } else { + $result = ($operand1 <= $operand2); + } break; // Inequality case '<>': @@ -3592,6 +3622,21 @@ class PHPExcel_Calculation { return TRUE; } // function _executeBinaryComparisonOperation() + /** + * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters + * @param string $str1 + * @param string $str2 + * @return integer + */ + private function strcmpLowercaseFirst($str1, $str2) + { + $from = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + $to = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $inversedStr1 = strtr($str1, $from, $to); + $inversedStr2 = strtr($str2, $from, $to); + + return strcmp($inversedStr1, $inversedStr2); + } private function _executeNumericBinaryOperation($cellID,$operand1,$operand2,$operation,$matrixFunction,&$stack) { // Validate the two operands diff --git a/unitTests/Classes/PHPExcel/CalculationTest.php b/unitTests/Classes/PHPExcel/CalculationTest.php new file mode 100644 index 00000000..8f8ce7e0 --- /dev/null +++ b/unitTests/Classes/PHPExcel/CalculationTest.php @@ -0,0 +1,35 @@ +_calculateFormulaValue($formula); + $this->assertEquals($expectedResultExcel, $resultExcel, 'should be Excel compatible'); + + PHPExcel_Calculation_Functions::setCompatibilityMode(PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE); + $resultOpenOffice = \PHPExcel_Calculation::getInstance()->_calculateFormulaValue($formula); + $this->assertEquals($expectedResultOpenOffice, $resultOpenOffice, 'should be OpenOffice compatible'); + } + + public function providerBinaryComparisonOperation() + { + return new testDataFileIterator('rawTestData/CalculationBinaryComparisonOperation.data'); + } + +} diff --git a/unitTests/rawTestData/CalculationBinaryComparisonOperation.data b/unitTests/rawTestData/CalculationBinaryComparisonOperation.data new file mode 100644 index 00000000..de04cbf7 --- /dev/null +++ b/unitTests/rawTestData/CalculationBinaryComparisonOperation.data @@ -0,0 +1,53 @@ +# formula, expectedResultExcel, expectedResultOpenOffice +'=TRUE', TRUE, TRUE +'=1 + 2.5', 3.5, 3.5 +'=2.5 + 1', 3.5, 3.5 +'=1 - 2.5', -1.5, -1.5 +'=2.5 - 1', 1.5, 1.5 +'=3 > 1', TRUE, TRUE +'=3 > 3', FALSE, FALSE +'=1 > 3', FALSE, FALSE +'=3 < 1', FALSE, FALSE +'=3 < 3', FALSE, FALSE +'=1 < 3', TRUE, TRUE +'=3 = 1', FALSE, FALSE +'=3 = 3', TRUE, TRUE +'=1 = 1.0', TRUE, TRUE +'=3 >= 1', TRUE, TRUE +'=3 >= 3', TRUE, TRUE +'=1 >= 3', FALSE, FALSE +'=3 <= 1', FALSE, FALSE +'=3 <= 3', TRUE, TRUE +'=1 <= 3', TRUE, TRUE +'=3 <> 1', TRUE, TRUE +'=3 <> 3', FALSE, FALSE +'=1 <> 1.0', FALSE, FALSE +'="a" > "a"', FALSE, FALSE +'="A" > "A"', FALSE, FALSE +'="A" > "a"', FALSE, TRUE +'="a" > "A"', FALSE, FALSE +'="a" < "a"', FALSE, FALSE +'="A" < "A"', FALSE, FALSE +'="A" < "a"', FALSE, FALSE +'="a" < "A"', FALSE, TRUE +'="a" = "a"', TRUE, TRUE +'="A" = "A"', TRUE, TRUE +'="A" = "a"', TRUE, FALSE +'="a" = "A"', TRUE, FALSE +'="a" <= "a"', TRUE, TRUE +'="A" <= "A"', TRUE, TRUE +'="A" <= "a"', TRUE, FALSE +'="a" <= "A"', TRUE, TRUE +'="a" >= "a"', TRUE, TRUE +'="A" >= "A"', TRUE, TRUE +'="A" >= "a"', TRUE, TRUE +'="a" >= "A"', TRUE, FALSE +'="a" <> "a"', FALSE, FALSE +'="A" <> "A"', FALSE, FALSE +'="A" <> "a"', FALSE, TRUE +'="a" <> "A"', FALSE, TRUE +'="A" > "b"', FALSE, TRUE +'="a" > "b"', FALSE, FALSE +'="b" > "a"', TRUE, TRUE +'="b" > "A"', TRUE, FALSE +'="a2" > "a10"', TRUE, TRUE // Test natural sorting is not used