From 3b2f2ce4748328fc696cd3d27887bf61e22ca023 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 9 Aug 2019 14:33:59 +0200 Subject: Make uninitialized DateTime an Error This avoids many spurious false return values. --- ext/date/php_date.c | 4 +- ext/date/php_date.stub.php | 76 ++++++++++------------- ext/date/php_date_arginfo.h | 34 +++++----- ext/date/tests/bug48476.phpt | 32 ++++++---- ext/date/tests/bug67118.phpt | 8 ++- ext/date/tests/oo_001.phpt | 24 ++++--- ext/intl/tests/dateformat_formatObject_error.phpt | 10 +-- scripts/dev/gen_stub.php | 10 ++- 8 files changed, 106 insertions(+), 92 deletions(-) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index aa457065d4..c3ffeba725 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -303,8 +303,8 @@ static zend_object_handlers date_object_handlers_period; #define DATE_CHECK_INITIALIZED(member, class_name) \ if (!(member)) { \ - php_error_docref(NULL, E_WARNING, "The " #class_name " object has not been correctly initialized by its constructor"); \ - RETURN_FALSE; \ + zend_throw_error(NULL, "The " #class_name " object has not been correctly initialized by its constructor"); \ + return; \ } static void date_object_free_storage_date(zend_object *object); diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php index 1d448b9102..667cbc1b91 100644 --- a/ext/date/php_date.stub.php +++ b/ext/date/php_date.stub.php @@ -57,42 +57,33 @@ function date_parse_from_format(string $format, string $date): array {} /** @return array|false */ function date_get_last_errors() {} -/** @return string|false */ -function date_format(DateTimeInterface $object, string $format) {} +function date_format(DateTimeInterface $object, string $format): string {} /** @return DateTime|false */ function date_modify(DateTime $object, string $modify) {} -/** @return DateTime|false */ -function date_add(DateTime $object, DateInterval $interval) {} +function date_add(DateTime $object, DateInterval $interval): DateTime {} -/** @return DateTime|false */ -function date_sub(DateTime $object, DateInterval $interval) {} +function date_sub(DateTime $object, DateInterval $interval): DateTime {} /** @return DateTimeZone|false */ function date_timezone_get(DateTimeInterface $object) {} -/** @return DateTime|false */ -function date_timezone_set(DateTimeInterface $object, DateTimeZone $timezone) {} +function date_timezone_set(DateTimeInterface $object, DateTimeZone $timezone): DateTime {} -/** @return int|false */ -function date_offset_get(DateTimeInterface $object) {} +function date_offset_get(DateTimeInterface $object): int {} -/** @return DateInterval|false */ -function date_diff(DateTimeInterface $object, DateTimeInterface $object2, bool $absolute = false) {} +function date_diff( + DateTimeInterface $object, DateTimeInterface $object2, bool $absolute = false): DateInterval {} -/** @return DateTime|false */ function date_time_set( - DateTime $object, int $hour, int $minute, int $second = 0, int $microseconds = 0) {} + DateTime $object, int $hour, int $minute, int $second = 0, int $microseconds = 0): DateTime {} -/** @return DateTime|false */ -function date_date_set(DateTime $object, int $year, int $month, int $day) {} +function date_date_set(DateTime $object, int $year, int $month, int $day): DateTime {} -/** @return DateTime|false */ -function date_isodate_set(DateTime $object, int $year, int $week, int $day = 1) {} +function date_isodate_set(DateTime $object, int $year, int $week, int $day = 1): DateTime {} -/** @return DateTime|false */ -function date_timestamp_set(DateTime $object, int $timestamp) {} +function date_timestamp_set(DateTime $object, int $timestamp): DateTime {} /** @return int|false */ function date_timestamp_get(DateTimeInterface $object) {} @@ -100,14 +91,12 @@ function date_timestamp_get(DateTimeInterface $object) {} /** @return DateTimeZone|false */ function timezone_open(string $timezone) {} -/** @return string|false */ -function timezone_name_get(DateTimeZone $object) {} +function timezone_name_get(DateTimeZone $object): string {} /** @return string|false */ function timezone_name_from_abbr(string $abbr, int $gmtoffset = -1, int $isdst = -1) {} -/** @return int|false */ -function timezone_offset_get(DateTimeZone $object, DateTimeInterface $datetime) {} +function timezone_offset_get(DateTimeZone $object, DateTimeInterface $datetime): int {} /** @return array|false */ function timezone_transitions_get( @@ -126,8 +115,7 @@ function timezone_version_get(): string {} /** @return DateInterval|false */ function date_interval_create_from_date_string(string $time) {} -/** @return string|false */ -function date_interval_format(DateInterval $object, string $format) {} +function date_interval_format(DateInterval $object, string $format): string {} function date_default_timezone_set(string $timezone_identifier): bool {} @@ -188,25 +176,25 @@ class DateTime implements DateTimeInterface { /** @return DateTime|false */ public function modify(string $modify); - /** @return DateTime|false */ + /** @return DateTime */ public function add(DateInterval $interval); - /** @return DateTime|false */ + /** @return DateTime */ public function sub(DateInterval $interval); - /** @return DateTime|false */ + /** @return DateTime */ public function setTimezone(DateTimeZone $timezone); - /** @return DateTime|false */ + /** @return DateTime */ public function setTime(int $hour, int $minute, int $second = 0, int $microseconds = 0); - /** @return DateTime|false */ + /** @return DateTime */ public function setDate(int $year, int $month, int $day); - /** @return DateTime|false */ + /** @return DateTime */ public function setISODate(int $year, int $week, int $day = 1); - /** @return DateTime|false */ + /** @return DateTime */ public function setTimestamp(int $timestampt); } @@ -222,35 +210,35 @@ class DateTimeImmutable implements DateTimeInterface { /** @return DateTimeImmutable|false */ public function modify(string $modify); - /** @return DateTimeImmutable|false */ + /** @return DateTimeImmutable */ public function add(DateInterval $interval); - /** @return DateTimeImmutable|false */ + /** @return DateTimeImmutable */ public function sub(DateInterval $interval); - /** @return DateTimeImmutable|false */ + /** @return DateTimeImmutable */ public function setTimezone(DateTimeZone $timezone); - /** @return DateTimeImmutable|false */ + /** @return DateTimeImmutable */ public function setTime(int $hour, int $minute, int $second = 0, int $microseconds = 0); - /** @return DateTimeImmutable|false */ + /** @return DateTimeImmutable */ public function setDate(int $year, int $month, int $day); - /** @return DateTimeImmutable|false */ + /** @return DateTimeImmutable */ public function setISODate(int $year, int $week, int $day = 1); - /** @return DateTimeImmutable|false */ - public function setTimestamp(int $timestampt); + /** @return DateTimeImmutable */ + public function setTimestamp(int $timestamp); } class DateTimeZone { public function __construct(string $timezone); - /** @return string|false */ + /** @return string */ public function getName(); - /** @return int|false */ + /** @return int */ public function getOffset(DateTimeInterface $datetime); /** @return array|false */ @@ -278,7 +266,7 @@ class DateInterval { /** @return DateInterval|false */ public static function createFromDateString(string $time); - /** @return string|false */ + /** @return string */ public function format(string $format); public function __wakeup(); diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 70381437c7..841ff28a6e 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -77,7 +77,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_date_get_last_errors, 0, 0, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_format, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_date_format, 0, 2, IS_STRING, 0) ZEND_ARG_OBJ_INFO(0, object, DateTimeInterface, 0) ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -87,7 +87,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_date_modify, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, modify, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_add, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_date_add, 0, 2, DateTime, 0) ZEND_ARG_OBJ_INFO(0, object, DateTime, 0) ZEND_ARG_OBJ_INFO(0, interval, DateInterval, 0) ZEND_END_ARG_INFO() @@ -98,20 +98,22 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timezone_get, 0, 0, 1) ZEND_ARG_OBJ_INFO(0, object, DateTimeInterface, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timezone_set, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_date_timezone_set, 0, 2, DateTime, 0) ZEND_ARG_OBJ_INFO(0, object, DateTimeInterface, 0) ZEND_ARG_OBJ_INFO(0, timezone, DateTimeZone, 0) ZEND_END_ARG_INFO() -#define arginfo_date_offset_get arginfo_date_timezone_get +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_date_offset_get, 0, 1, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, object, DateTimeInterface, 0) +ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_diff, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_date_diff, 0, 2, DateInterval, 0) ZEND_ARG_OBJ_INFO(0, object, DateTimeInterface, 0) ZEND_ARG_OBJ_INFO(0, object2, DateTimeInterface, 0) ZEND_ARG_TYPE_INFO(0, absolute, _IS_BOOL, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_time_set, 0, 0, 3) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_date_time_set, 0, 3, DateTime, 0) ZEND_ARG_OBJ_INFO(0, object, DateTime, 0) ZEND_ARG_TYPE_INFO(0, hour, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, minute, IS_LONG, 0) @@ -119,21 +121,21 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_date_time_set, 0, 0, 3) ZEND_ARG_TYPE_INFO(0, microseconds, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_date_set, 0, 0, 4) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_date_date_set, 0, 4, DateTime, 0) ZEND_ARG_OBJ_INFO(0, object, DateTime, 0) ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, month, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, day, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_isodate_set, 0, 0, 3) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_date_isodate_set, 0, 3, DateTime, 0) ZEND_ARG_OBJ_INFO(0, object, DateTime, 0) ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, week, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, day, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timestamp_set, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_date_timestamp_set, 0, 2, DateTime, 0) ZEND_ARG_OBJ_INFO(0, object, DateTime, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -144,7 +146,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_open, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, timezone, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_name_get, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_timezone_name_get, 0, 1, IS_STRING, 0) ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 0) ZEND_END_ARG_INFO() @@ -154,7 +156,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_name_from_abbr, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, isdst, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_offset_get, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_timezone_offset_get, 0, 2, IS_LONG, 0) ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 0) ZEND_ARG_OBJ_INFO(0, datetime, DateTimeInterface, 0) ZEND_END_ARG_INFO() @@ -165,7 +167,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_transitions_get, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, timestamp_end, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_timezone_location_get arginfo_timezone_name_get +ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_location_get, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_identifiers_list, 0, 0, 0) ZEND_ARG_TYPE_INFO(0, what, IS_LONG, 0) @@ -182,7 +186,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_date_interval_create_from_date_string, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, time, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_interval_format, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_date_interval_format, 0, 2, IS_STRING, 0) ZEND_ARG_OBJ_INFO(0, object, DateInterval, 0) ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -300,7 +304,9 @@ ZEND_END_ARG_INFO() #define arginfo_DateTimeImmutable_setISODate arginfo_DateTime_setISODate -#define arginfo_DateTimeImmutable_setTimestamp arginfo_DateTime_setTimestamp +ZEND_BEGIN_ARG_INFO_EX(arginfo_DateTimeImmutable_setTimestamp, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) +ZEND_END_ARG_INFO() #define arginfo_DateTimeZone___construct arginfo_timezone_open diff --git a/ext/date/tests/bug48476.phpt b/ext/date/tests/bug48476.phpt index 1335025c76..02086e0e56 100644 --- a/ext/date/tests/bug48476.phpt +++ b/ext/date/tests/bug48476.phpt @@ -10,22 +10,28 @@ class MyDateTimeZone extends DateTimeZone { } $o = new MyDateTime; -var_dump($o->format("d")); +try { + var_dump($o->format("d")); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} $x = clone $o; -var_dump($x->format("d")); +try { + var_dump($x->format("d")); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} clone $o; - -var_dump(timezone_location_get(clone new MyDateTimezone)); +try { + var_dump(timezone_location_get(clone new MyDateTimezone)); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} ?> ---EXPECTF-- -Warning: DateTime::format(): The DateTime object has not been correctly initialized by its constructor in %sbug48476.php on line 10 -bool(false) - -Warning: DateTime::format(): The DateTime object has not been correctly initialized by its constructor in %sbug48476.php on line 13 -bool(false) - -Warning: timezone_location_get(): The DateTimeZone object has not been correctly initialized by its constructor in %sbug48476.php on line 18 -bool(false) +--EXPECT-- +The DateTime object has not been correctly initialized by its constructor +The DateTime object has not been correctly initialized by its constructor +The DateTimeZone object has not been correctly initialized by its constructor diff --git a/ext/date/tests/bug67118.phpt b/ext/date/tests/bug67118.phpt index 332142856e..5a7d2d1d07 100644 --- a/ext/date/tests/bug67118.phpt +++ b/ext/date/tests/bug67118.phpt @@ -23,5 +23,9 @@ class mydt extends datetime new mydt("Funktionsansvarig rÄdgivning och juridik", "UTC"); ?> --EXPECTF-- -Warning: DateTime::format(): The DateTime object has not been correctly initialized by its constructor in %s on line %d -Bad date +Fatal error: Uncaught Error: The DateTime object has not been correctly initialized by its constructor in %s:%d +Stack trace: +#0 %s(%d): DateTime->format('Y') +#1 %s(%d): mydt->__construct(%s) +#2 {main} + thrown in %s on line %d diff --git a/ext/date/tests/oo_001.phpt b/ext/date/tests/oo_001.phpt index 7cbf847c52..111364ec83 100644 --- a/ext/date/tests/oo_001.phpt +++ b/ext/date/tests/oo_001.phpt @@ -15,8 +15,12 @@ class _t extends DateTimeZone { $d = new DateTime; var_dump($d->format("Y-m-d H:i:s")); -$d = new _d; -var_dump($d->format("Y-m-d H:i:s")); +try { + $d = new _d; + var_dump($d->format("Y-m-d H:i:s")); +} catch (Error $e) { + echo $e->getMessage(),"\n"; +} try { new DateTime("1am todax"); @@ -27,8 +31,12 @@ try { $t = new DateTimeZone("UTC"); var_dump($t->getName()); -$t = new _t; -var_dump($t->getName()); +try { + $t = new _t; + var_dump($t->getName()); +} catch (Error $e) { + echo $e->getMessage(),"\n"; +} try { new DateTimeZone("GottaFindThisOne"); @@ -40,13 +48,9 @@ echo "DONE\n"; ?> --EXPECTF-- string(19) "%d-%d-%d %d:%d:%d" - -Warning: DateTime::format(): The DateTime object has not been correctly initialized by its constructor in %soo_001.php on line %d -bool(false) +The DateTime object has not been correctly initialized by its constructor DateTime::__construct(): Failed to parse time string (1am todax) at position 4 (t): The timezone could not be found in the database string(3) "UTC" - -Warning: DateTimeZone::getName(): The DateTimeZone object has not been correctly initialized by its constructor in %soo_001.php on line %d -bool(false) +The DateTimeZone object has not been correctly initialized by its constructor DateTimeZone::__construct(): Unknown or bad timezone (GottaFindThisOne) DONE diff --git a/ext/intl/tests/dateformat_formatObject_error.phpt b/ext/intl/tests/dateformat_formatObject_error.phpt index 24be090035..7c1140c09d 100644 --- a/ext/intl/tests/dateformat_formatObject_error.phpt +++ b/ext/intl/tests/dateformat_formatObject_error.phpt @@ -15,7 +15,11 @@ var_dump(IntlDateFormatter::formatObject(new stdclass)); class A extends IntlCalendar {function __construct(){}} var_dump(IntlDateFormatter::formatObject(new A)); class B extends DateTime {function __construct(){}} -var_dump(IntlDateFormatter::formatObject(new B)); +try { + var_dump(IntlDateFormatter::formatObject(new B)); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} $cal = IntlCalendar::createInstance(); var_dump(IntlDateFormatter::formatObject($cal, -2)); @@ -34,10 +38,8 @@ bool(false) Warning: IntlDateFormatter::formatObject(): datefmt_format_object: bad IntlCalendar instance: not initialized properly in %s on line %d bool(false) -Warning: DateTime::getTimestamp(): The DateTime object has not been correctly initialized by its constructor in %s on line %d - Warning: IntlDateFormatter::formatObject(): datefmt_format_object: error calling ::getTimeStamp() on the object in %s on line %d -bool(false) +The DateTime object has not been correctly initialized by its constructor Warning: IntlDateFormatter::formatObject(): datefmt_format_object: the date/time format type is invalid in %s on line %d bool(false) diff --git a/scripts/dev/gen_stub.php b/scripts/dev/gen_stub.php index 9a71e7db82..59b7d448f6 100755 --- a/scripts/dev/gen_stub.php +++ b/scripts/dev/gen_stub.php @@ -199,7 +199,7 @@ function parseClass(Stmt\Class_ $class): ClassInfo { $className = $class->name->toString(); foreach ($class as $stmt) { if (!$stmt instanceof Stmt\ClassMethod) { - throw new Exception("Not implemented"); + throw new Exception("Not implemented class statement"); } $funcs[] = parseFunctionLike($className . '_' . $stmt->name->toString(), $stmt); @@ -230,7 +230,7 @@ function parseStubFile(string $fileName) { $text = trim($comment->getText()); if (preg_match('/^#if\s+(.+)$/', $text, $matches)) { if ($cond !== null) { - throw new Exception("Not implemented"); + throw new Exception("Not implemented preprocessor directive"); } $cond = $matches[1]; } else if ($text === '#endif') { @@ -284,7 +284,11 @@ function funcInfoToCode(FuncInfo $funcInfo): string { $returnType->toTypeCode(), $returnType->isNullable ); } else { - throw new Exception("Not implemented"); + $code .= sprintf( + "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_%s, %d, %d, %s, %d)\n", + $funcInfo->name, $funcInfo->return->byRef, $funcInfo->numRequiredArgs, + $returnType->name, $returnType->isNullable + ); } } else { $code .= sprintf( -- cgit v1.2.1