PHP 8.1: Fix FunctionExpression::Evaluate() "TO_DAYS" misalignment due to PHP 8.1 bug fix

This commit is contained in:
Molkobain
2023-02-15 21:30:41 +01:00
parent d0b000d125
commit 737388053f
2 changed files with 34 additions and 2 deletions

View File

@@ -2860,9 +2860,26 @@ class FunctionExpression extends Expression
{ {
throw new \Exception("Function {$this->m_sVerb} requires 1 argument"); throw new \Exception("Function {$this->m_sVerb} requires 1 argument");
} }
// N°5985 - Since PHP 8.1+, a bug fix on \DateTimeInterval for a date anterior to "1937-05-23" now returns a different number of days. The workaround below aim at making the code work with PHP 7.4 => 8.2+
//
// $oDate = new DateTimeImmutable('2020-01-02');
// $oZero = new DateTimeImmutable('1937-05-22');
// $iRet = (int) $oDate->diff($oZero)->format('%a');
// echo $iRet."\n"; // 30174 (PHP 8.0) vs 30175 (PHP 8.1+)
//
// $oDate = new DateTimeImmutable('2020-01-02');
// $oZero = new DateTimeImmutable('1937-05-23');
// $iRet = (int) $oDate->diff($oZero)->format('%a');
// echo $iRet."\n"; // 30174 (PHP 8.0) vs 30174 (PHP 8.1+)
//
// To work around that we take 1970-01-01 as "zero date" and we offset it with the number of days between 1582-01-01 and 1970-01-01.
// Note that as the "target" date could be between 1582-01-01 and 1970-01-01 we have to format the interval with the "-"/"+" sign in order to correct the number of days.
$oDate = new DateTime($this->m_aArgs[0]->Evaluate($aArgs)); $oDate = new DateTime($this->m_aArgs[0]->Evaluate($aArgs));
$oZero = new DateTime('1582-01-01'); $oZero = new DateTime('1970-01-01');
$iRet = (int) $oDate->diff($oZero)->format('%a') + 577815; $iDaysBetween19700101And15800101 = 141713;
$iRet = (int) $oZero->diff($oDate)->format('%R%a') + 577815 + $iDaysBetween19700101And15800101;
return $iRet; return $iRet;
case 'FROM_DAYS': case 'FROM_DAYS':

View File

@@ -104,17 +104,21 @@ class ExpressionEvaluateTest extends iTopDataTestCase
// The bare minimum // The bare minimum
array('"blah"', 'blah'), array('"blah"', 'blah'),
array('"\\\\"', '\\'), array('"\\\\"', '\\'),
// Arithmetics // Arithmetics
array('2+2', 4), array('2+2', 4),
array('2+2-2', 2), array('2+2-2', 2),
array('2*(3+4)', 14), array('2*(3+4)', 14),
array('(2*3)+4', 10), array('(2*3)+4', 10),
array('2*3+4', 10), array('2*3+4', 10),
// Strings // Strings
array("CONCAT('hello', 'world')", 'helloworld'), array("CONCAT('hello', 'world')", 'helloworld'),
// Not yet parsed - array("CONCAT_WS(' ', 'hello', 'world')", 'hello world'), // Not yet parsed - array("CONCAT_WS(' ', 'hello', 'world')", 'hello world'),
array("SUBSTR('abcdef', 2, 3)", 'bcd'), array("SUBSTR('abcdef', 2, 3)", 'bcd'),
array("TRIM(' Sin dolor ')", 'Sin dolor'), array("TRIM(' Sin dolor ')", 'Sin dolor'),
// Comparison operators // Comparison operators
array('1 = 1', 1), array('1 = 1', 1),
array('1 != 1', 0), array('1 != 1', 0),
@@ -141,6 +145,7 @@ class ExpressionEvaluateTest extends iTopDataTestCase
array('"2020-06-12 17:35:13" > "2020-06-12"', 1), array('"2020-06-12 17:35:13" > "2020-06-12"', 1),
array('"2020-06-12 17:35:13" < "2020-06-12"', 0), array('"2020-06-12 17:35:13" < "2020-06-12"', 0),
array('"2020-06-12 00:00:00" = "2020-06-12"', 0), array('"2020-06-12 00:00:00" = "2020-06-12"', 0),
// Logical operators // Logical operators
array('0 AND 0', 0), array('0 AND 0', 0),
array('1 AND 0', 0), array('1 AND 0', 0),
@@ -151,12 +156,14 @@ class ExpressionEvaluateTest extends iTopDataTestCase
array('1 OR 0', 1), array('1 OR 0', 1),
array('1 OR 1', 1), array('1 OR 1', 1),
array('1 AND 0 OR 1', 1), array('1 AND 0 OR 1', 1),
// Casting // Casting
array('1 AND "blah"', 0), array('1 AND "blah"', 0),
array('1 AND "1"', 1), array('1 AND "1"', 1),
array('1 AND "2"', 1), array('1 AND "2"', 1),
array('1 AND "0"', 0), array('1 AND "0"', 0),
array('1 AND "-1"', 1), array('1 AND "-1"', 1),
// Null // Null
array('NULL', null), array('NULL', null),
array('1 AND NULL', null), array('1 AND NULL', null),
@@ -165,12 +172,14 @@ class ExpressionEvaluateTest extends iTopDataTestCase
array('COALESCE(321, 123)', 321), array('COALESCE(321, 123)', 321),
array('ISNULL(NULL)', 1), array('ISNULL(NULL)', 1),
array('ISNULL(123)', 0), array('ISNULL(123)', 0),
// Date functions // Date functions
array("DATE('2020-03-12 13:18:30')", '2020-03-12'), array("DATE('2020-03-12 13:18:30')", '2020-03-12'),
array("DATE_FORMAT('2009-10-04 22:23:00', '%Y %m %d %H %i %s')", '2009 10 04 22 23 00'), array("DATE_FORMAT('2009-10-04 22:23:00', '%Y %m %d %H %i %s')", '2009 10 04 22 23 00'),
array("DATE(NOW()) = CURRENT_DATE()", 1), // Could fail if executed around midnight! array("DATE(NOW()) = CURRENT_DATE()", 1), // Could fail if executed around midnight!
array("TO_DAYS('2020-01-02')", 737791), array("TO_DAYS('2020-01-02')", 737791),
array("FROM_DAYS(737791)", '2020-01-02'), array("FROM_DAYS(737791)", '2020-01-02'),
array("FROM_DAYS(TO_DAYS('2020-01-02'))", '2020-01-02'), // Back and forth conversion to ensure it returns the same
array("YEAR('2020-05-03')", 2020), array("YEAR('2020-05-03')", 2020),
array("MONTH('2020-05-03')", 5), array("MONTH('2020-05-03')", 5),
array("DAY('2020-05-03')", 3), array("DAY('2020-05-03')", 3),
@@ -178,6 +187,7 @@ class ExpressionEvaluateTest extends iTopDataTestCase
array("DATE_ADD('2020-02-28 18:00:00', INTERVAL 1 DAY)", '2020-02-29 18:00:00'), array("DATE_ADD('2020-02-28 18:00:00', INTERVAL 1 DAY)", '2020-02-29 18:00:00'),
array("DATE_SUB('2020-03-01 18:00:00', INTERVAL 1 HOUR)", '2020-03-01 17:00:00'), array("DATE_SUB('2020-03-01 18:00:00', INTERVAL 1 HOUR)", '2020-03-01 17:00:00'),
array("DATE_SUB('2020-03-01 18:00:00', INTERVAL 1 DAY)", '2020-02-29 18:00:00'), array("DATE_SUB('2020-03-01 18:00:00', INTERVAL 1 DAY)", '2020-02-29 18:00:00'),
// Misc. functions // Misc. functions
array('IF(1, 123, 567)', 123), array('IF(1, 123, 567)', 123),
array('IF(0, 123, 567)', 567), array('IF(0, 123, 567)', 567),
@@ -187,6 +197,11 @@ class ExpressionEvaluateTest extends iTopDataTestCase
array('INET_ATON("128.0.0.1")', 2147483649), array('INET_ATON("128.0.0.1")', 2147483649),
array('INET_NTOA(2147483649)', '128.0.0.1'), array('INET_NTOA(2147483649)', '128.0.0.1'),
); );
// N°5985 - Test bidirectional conversion across the centuries to ensure that it works on PHP 7.4 => 8.2+ even though the bug has been fixed in PHP 8.1 but still exists in PHP 7.4 => 8.1
for ($iUpperYearBound = 1600; $iUpperYearBound <= 2100; $iUpperYearBound = $iUpperYearBound + 25) {
$aExpressions[] = array("FROM_DAYS(TO_DAYS('$iUpperYearBound-01-02'))", "$iUpperYearBound-01-02");
}
} }
// Build a comprehensive index // Build a comprehensive index