From 3cfbbf29561f931560e108fb47bbdef84e12fd5f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 18 Apr 2019 13:46:10 +0200 Subject: [PATCH] Make DateInterval objects uncomparable Arbitrary DateInterval objects don't have well-defined comparison semantics. Throw a warning and treat the objects as uncomparable. Support for comparing DateInterval objects returned by DateTime::diff() may be added in the future. --- UPGRADING | 3 +++ ext/date/php_date.c | 18 ++++++++++++++---- ext/date/tests/bug70810.phpt | 35 +++++++++++++++++++++++++++++++++++ ext/date/tests/bug74639.phpt | 4 ---- 4 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 ext/date/tests/bug70810.phpt diff --git a/UPGRADING b/UPGRADING index 9768e5f7e4..0bcdae1971 100644 --- a/UPGRADING +++ b/UPGRADING @@ -32,6 +32,9 @@ PHP 7.4 UPGRADE NOTES - Date: . Calling var_dump() or similar on a DateTime(Immutable) instance will no longer leave behind accessible properties on the object. + . Comparison of DateInterval objects (using ==, < and so on) will now generate + a warning and always return false. Previously all DateInterval objects were + considered equal, unless they had properties. - Intl: . The default parameter value of idn_to_ascii() and idn_to_utf8() is now diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 03fdc18284..4d0a20df97 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -656,8 +656,9 @@ static HashTable *date_object_get_gc_timezone(zval *object, zval **table, int *n static HashTable *date_object_get_debug_info_timezone(zval *object, int *is_temp); static void php_timezone_to_string(php_timezone_obj *tzobj, zval *zv); -zval *date_interval_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv); -zval *date_interval_write_property(zval *object, zval *member, zval *value, void **cache_slot); +static int date_interval_compare_objects(zval *o1, zval *o2); +static zval *date_interval_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv); +static zval *date_interval_write_property(zval *object, zval *member, zval *value, void **cache_slot); static zval *date_interval_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot); static zval *date_period_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv); static zval *date_period_write_property(zval *object, zval *member, zval *value, void **cache_slot); @@ -2164,6 +2165,7 @@ static void date_register_classes(void) /* {{{ */ date_object_handlers_interval.get_properties = date_object_get_properties_interval; date_object_handlers_interval.get_property_ptr_ptr = date_interval_get_property_ptr_ptr; date_object_handlers_interval.get_gc = date_object_get_gc_interval; + date_object_handlers_interval.compare_objects = date_interval_compare_objects; INIT_CLASS_ENTRY(ce_period, "DatePeriod", date_funcs_period); ce_period.create_object = date_object_new_period; @@ -4142,8 +4144,16 @@ static int date_interval_initialize(timelib_rel_time **rt, /*const*/ char *forma return retval; } /* }}} */ +static int date_interval_compare_objects(zval *o1, zval *o2) { + /* There is no well defined way to compare intervals like P1M and P30D, which may compare + * smaller, equal or greater depending on the point in time at which the interval starts. As + * such, we treat DateInterval objects are non-comparable and emit a warning. */ + zend_error(E_WARNING, "Cannot compare DateInterval objects"); + return 1; +} + /* {{{ date_interval_read_property */ -zval *date_interval_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) +static zval *date_interval_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) { php_interval_obj *obj; zval *retval; @@ -4214,7 +4224,7 @@ zval *date_interval_read_property(zval *object, zval *member, int type, void **c /* }}} */ /* {{{ date_interval_write_property */ -zval *date_interval_write_property(zval *object, zval *member, zval *value, void **cache_slot) +static zval *date_interval_write_property(zval *object, zval *member, zval *value, void **cache_slot) { php_interval_obj *obj; zval tmp_member; diff --git a/ext/date/tests/bug70810.phpt b/ext/date/tests/bug70810.phpt new file mode 100644 index 0000000000..1918e56f90 --- /dev/null +++ b/ext/date/tests/bug70810.phpt @@ -0,0 +1,35 @@ +--TEST-- +Bug #70810: DateInterval equals every other DateInterval +--FILE-- + $i2); + +$i2 = new DateInterval('P1D'); +var_dump($i1 == $i2); +var_dump($i1 < $i2); +var_dump($i1 > $i2); + +?> +--EXPECTF-- +Warning: Cannot compare DateInterval objects in %s on line %d +bool(false) + +Warning: Cannot compare DateInterval objects in %s on line %d +bool(false) + +Warning: Cannot compare DateInterval objects in %s on line %d +bool(false) + +Warning: Cannot compare DateInterval objects in %s on line %d +bool(false) + +Warning: Cannot compare DateInterval objects in %s on line %d +bool(false) + +Warning: Cannot compare DateInterval objects in %s on line %d +bool(false) diff --git a/ext/date/tests/bug74639.phpt b/ext/date/tests/bug74639.phpt index 43eccc974d..7a9c31e530 100644 --- a/ext/date/tests/bug74639.phpt +++ b/ext/date/tests/bug74639.phpt @@ -19,10 +19,6 @@ if ($period->getEndDate() != $clonedPeriod->getEndDate()) { echo "failure\n"; } -if ($period->getDateInterval() != $clonedPeriod->getDateInterval()) { - echo "failure\n"; -} - if ($interval->format('Y-m-d H:i:s') != $clonedInterval->format('Y-m-d H:i:s')) { echo "failure\n"; } -- 2.40.0