From 5f09944662e09ea0b3f93cfab8702f188955e68c Mon Sep 17 00:00:00 2001 From: nikita2206 Date: Tue, 29 Oct 2013 19:40:17 +0400 Subject: [PATCH] Fixed bug #65768: DateTimeImmutable::diff does not work This commit also prevents user classes from directly implementing DateTimeInterface, because ext/date relies on classes implementing it to support certain internal structures. --- NEWS | 1 + ext/date/php_date.c | 21 +++++++++++++++++---- tests/classes/bug65768.phpt | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 tests/classes/bug65768.phpt diff --git a/NEWS b/NEWS index d16e8bdf4bc..55ee92e4e67 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ PHP NEWS - Date: . Fixed bug #66060 (Heap buffer over-read in DateInterval). (Remi) + . Fixed bug #65768 (DateTimeImmutable::diff does not work). (Nikita Nefedov) ?? ??? 2013, PHP 5.5.7 diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 742a2f60c32..cd3bf745dcb 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -1989,12 +1989,25 @@ zend_object_iterator *date_object_period_get_iterator(zend_class_entry *ce, zval return (zend_object_iterator*)iterator; } +static int implement_date_interface_handler(zend_class_entry *interface, zend_class_entry *implementor TSRMLS_DC) +{ + if (implementor->type == ZEND_USER_CLASS && + !instanceof_function(implementor, date_ce_date TSRMLS_CC) && + !instanceof_function(implementor, date_ce_immutable TSRMLS_CC) + ) { + zend_error(E_ERROR, "DateTimeInterface can't be implemented by user classes"); + } + + return SUCCESS; +} + static void date_register_classes(TSRMLS_D) { zend_class_entry ce_date, ce_immutable, ce_timezone, ce_interval, ce_period, ce_interface; INIT_CLASS_ENTRY(ce_interface, "DateTimeInterface", date_funcs_interface); date_ce_interface = zend_register_internal_interface(&ce_interface TSRMLS_CC); + date_ce_interface->interface_gets_implemented = implement_date_interface_handler; INIT_CLASS_ENTRY(ce_date, "DateTime", date_funcs_date); ce_date.create_object = date_object_new_date; @@ -3612,13 +3625,13 @@ PHP_FUNCTION(date_diff) php_interval_obj *interval; long absolute = 0; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO|l", &object1, date_ce_date, &object2, date_ce_date, &absolute) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO|l", &object1, date_ce_interface, &object2, date_ce_interface, &absolute) == FAILURE) { RETURN_FALSE; } dateobj1 = (php_date_obj *) zend_object_store_get_object(object1 TSRMLS_CC); dateobj2 = (php_date_obj *) zend_object_store_get_object(object2 TSRMLS_CC); - DATE_CHECK_INITIALIZED(dateobj1->time, DateTime); - DATE_CHECK_INITIALIZED(dateobj2->time, DateTime); + DATE_CHECK_INITIALIZED(dateobj1->time, DateTimeInterface); + DATE_CHECK_INITIALIZED(dateobj2->time, DateTimeInterface); timelib_update_ts(dateobj1->time, NULL); timelib_update_ts(dateobj2->time, NULL); @@ -4396,7 +4409,7 @@ PHP_METHOD(DatePeriod, __construct) zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC); if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "OOl|l", &start, date_ce_interface, &interval, date_ce_interval, &recurrences, &options) == FAILURE) { - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "OOO|l", &start, date_ce_interface, &interval, date_ce_interval, &end, date_ce_date, &options) == FAILURE) { + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "OOO|l", &start, date_ce_interface, &interval, date_ce_interval, &end, date_ce_interface, &options) == FAILURE) { if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &isostr, &isostr_len, &options) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "This constructor accepts either (DateTimeInterface, DateInterval, int) OR (DateTimeInterface, DateInterval, DateTime) OR (string) as arguments."); zend_restore_error_handling(&error_handling TSRMLS_CC); diff --git a/tests/classes/bug65768.phpt b/tests/classes/bug65768.phpt new file mode 100644 index 00000000000..17ec93cf249 --- /dev/null +++ b/tests/classes/bug65768.phpt @@ -0,0 +1,36 @@ +--TEST-- +Bug #65768: date_diff accepts only DateTime instance even though docs say about DateTimeInterface +--INI-- +date.timezone=Europe/London +--FILE-- +diff($dti1); +echo $diff1->y, " ", $diff1->m, " ", $diff1->d, " ", + $diff1->h, " ", $diff1->i, " ", $diff1->s, "\n"; + +$diff2 = $dti1->diff($dti2); +echo $diff2->y, " ", $diff2->m, " ", $diff2->d, " ", + $diff2->h, " ", $diff2->i, " ", $diff2->s, "\n"; + +$diff3 = date_diff($dt1, $dti2); +echo $diff3->y, " ", $diff3->m, " ", $diff3->d, " ", + $diff3->h, " ", $diff3->i, " ", $diff3->s, "\n"; + +class cdt1 extends DateTime implements DateTimeInterface {} + +class cdt2 extends DateTimeImmutable implements DateTimeInterface {} + +class cdt3 implements DateTimeInterface {} + +?> +--EXPECTF-- +0 0 5 0 0 0 +0 0 3 0 0 0 +0 0 8 0 0 0 + +Fatal error: DateTimeInterface can't be implemented by user classes in %sbug65768.php on line %d