Comparison operators on strings are usually case insensitive

Excel, Gnumeric and Google Spreadsheet are case insensitive, so the
default behavior of PHPExcel is modified accordingly.

However OpenOffice is case sensitive and is also supported via the
compatibility mode of PHPExcel.

Fixes #31
This commit is contained in:
Adrien Crivelli 2013-11-20 18:55:05 +09:00
parent f5bd6dc0f2
commit f2e24ecdd4
3 changed files with 137 additions and 4 deletions

View File

@ -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

View File

@ -0,0 +1,35 @@
<?php
require_once 'testDataFileIterator.php';
class CalculationTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
if (!defined('PHPEXCEL_ROOT')) {
define('PHPEXCEL_ROOT', APPLICATION_PATH . '/');
}
require_once(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
}
/**
* @dataProvider providerBinaryComparisonOperation
*/
public function testBinaryComparisonOperation($formula, $expectedResultExcel, $expectedResultOpenOffice)
{
PHPExcel_Calculation_Functions::setCompatibilityMode(PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL);
$resultExcel = \PHPExcel_Calculation::getInstance()->_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');
}
}

View File

@ -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