Skip to content

Commit 3cfbbf2

Browse files
committed
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.
1 parent d65f93f commit 3cfbbf2

File tree

4 files changed

+52
-8
lines changed

4 files changed

+52
-8
lines changed

UPGRADING

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ PHP 7.4 UPGRADE NOTES
3232
- Date:
3333
. Calling var_dump() or similar on a DateTime(Immutable) instance will no
3434
longer leave behind accessible properties on the object.
35+
. Comparison of DateInterval objects (using ==, < and so on) will now generate
36+
a warning and always return false. Previously all DateInterval objects were
37+
considered equal, unless they had properties.
3538

3639
- Intl:
3740
. The default parameter value of idn_to_ascii() and idn_to_utf8() is now

ext/date/php_date.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -656,8 +656,9 @@ static HashTable *date_object_get_gc_timezone(zval *object, zval **table, int *n
656656
static HashTable *date_object_get_debug_info_timezone(zval *object, int *is_temp);
657657
static void php_timezone_to_string(php_timezone_obj *tzobj, zval *zv);
658658

659-
zval *date_interval_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv);
660-
zval *date_interval_write_property(zval *object, zval *member, zval *value, void **cache_slot);
659+
static int date_interval_compare_objects(zval *o1, zval *o2);
660+
static zval *date_interval_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv);
661+
static zval *date_interval_write_property(zval *object, zval *member, zval *value, void **cache_slot);
661662
static zval *date_interval_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot);
662663
static zval *date_period_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv);
663664
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) /* {{{ */
21642165
date_object_handlers_interval.get_properties = date_object_get_properties_interval;
21652166
date_object_handlers_interval.get_property_ptr_ptr = date_interval_get_property_ptr_ptr;
21662167
date_object_handlers_interval.get_gc = date_object_get_gc_interval;
2168+
date_object_handlers_interval.compare_objects = date_interval_compare_objects;
21672169

21682170
INIT_CLASS_ENTRY(ce_period, "DatePeriod", date_funcs_period);
21692171
ce_period.create_object = date_object_new_period;
@@ -4142,8 +4144,16 @@ static int date_interval_initialize(timelib_rel_time **rt, /*const*/ char *forma
41424144
return retval;
41434145
} /* }}} */
41444146

4147+
static int date_interval_compare_objects(zval *o1, zval *o2) {
4148+
/* There is no well defined way to compare intervals like P1M and P30D, which may compare
4149+
* smaller, equal or greater depending on the point in time at which the interval starts. As
4150+
* such, we treat DateInterval objects are non-comparable and emit a warning. */
4151+
zend_error(E_WARNING, "Cannot compare DateInterval objects");
4152+
return 1;
4153+
}
4154+
41454155
/* {{{ date_interval_read_property */
4146-
zval *date_interval_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv)
4156+
static zval *date_interval_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv)
41474157
{
41484158
php_interval_obj *obj;
41494159
zval *retval;
@@ -4214,7 +4224,7 @@ zval *date_interval_read_property(zval *object, zval *member, int type, void **c
42144224
/* }}} */
42154225

42164226
/* {{{ date_interval_write_property */
4217-
zval *date_interval_write_property(zval *object, zval *member, zval *value, void **cache_slot)
4227+
static zval *date_interval_write_property(zval *object, zval *member, zval *value, void **cache_slot)
42184228
{
42194229
php_interval_obj *obj;
42204230
zval tmp_member;

ext/date/tests/bug70810.phpt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
Bug #70810: DateInterval equals every other DateInterval
3+
--FILE--
4+
<?php
5+
6+
$i1 = new DateInterval('P1D');
7+
$i2 = new DateInterval('PT1H');
8+
var_dump($i1 == $i2);
9+
var_dump($i1 < $i2);
10+
var_dump($i1 > $i2);
11+
12+
$i2 = new DateInterval('P1D');
13+
var_dump($i1 == $i2);
14+
var_dump($i1 < $i2);
15+
var_dump($i1 > $i2);
16+
17+
?>
18+
--EXPECTF--
19+
Warning: Cannot compare DateInterval objects in %s on line %d
20+
bool(false)
21+
22+
Warning: Cannot compare DateInterval objects in %s on line %d
23+
bool(false)
24+
25+
Warning: Cannot compare DateInterval objects in %s on line %d
26+
bool(false)
27+
28+
Warning: Cannot compare DateInterval objects in %s on line %d
29+
bool(false)
30+
31+
Warning: Cannot compare DateInterval objects in %s on line %d
32+
bool(false)
33+
34+
Warning: Cannot compare DateInterval objects in %s on line %d
35+
bool(false)

ext/date/tests/bug74639.phpt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ if ($period->getEndDate() != $clonedPeriod->getEndDate()) {
1919
echo "failure\n";
2020
}
2121

22-
if ($period->getDateInterval() != $clonedPeriod->getDateInterval()) {
23-
echo "failure\n";
24-
}
25-
2622
if ($interval->format('Y-m-d H:i:s') != $clonedInterval->format('Y-m-d H:i:s')) {
2723
echo "failure\n";
2824
}

0 commit comments

Comments
 (0)