Commit 48190ac8ed8 for php.net

commit 48190ac8ed88087583572e6c187a9cfd9c3d5169
Author: Gina Peter Banyard <girgias@php.net>
Date:   Tue Sep 1 17:10:31 2026 +0100

    ext/date: Throw exceptions directly (#19166)

diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index 9f9f0a6159e..27664623c6b 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -3258,7 +3258,12 @@ PHP_FUNCTION(date_format)
 }
 /* }}} */

-static bool php_date_modify(zval *object, char *modify, size_t modify_len) /* {{{ */
+typedef enum {
+	PHP_DATE_MODIFY_WARNING,
+	PHP_DATE_MODIFY_THROW
+} php_date_modify_error_mode;
+
+static bool php_date_modify(zval *object, char *modify, size_t modify_len, const php_date_modify_error_mode error_mode) /* {{{ */
 {
 	php_date_obj *dateobj;
 	timelib_time *tmp_time;
@@ -3278,10 +3283,22 @@ static bool php_date_modify(zval *object, char *modify, size_t modify_len) /* {{

 	if (err && err->error_count) {
 		/* spit out the first library error message, at least */
-		php_error_docref(NULL, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", modify,
-			err->error_messages[0].position,
-			err->error_messages[0].character ? err->error_messages[0].character : ' ',
-			err->error_messages[0].message);
+		if (error_mode == PHP_DATE_MODIFY_THROW) {
+			zend_string *func_name = get_active_function_or_method_name();
+			zend_throw_exception_ex(date_ce_date_malformed_string_exception, 0,
+				"%s(): Failed to parse time string (%s) at position %d (%c): %s",
+				ZSTR_VAL(func_name),
+				modify,
+				err->error_messages[0].position,
+				err->error_messages[0].character ? err->error_messages[0].character : ' ',
+				err->error_messages[0].message);
+			zend_string_release_ex(func_name, false);
+		} else {
+			php_error_docref(NULL, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", modify,
+				err->error_messages[0].position,
+				err->error_messages[0].character ? err->error_messages[0].character : ' ',
+				err->error_messages[0].message);
+		}
 		timelib_time_dtor(tmp_time);
 		return false;
 	}
@@ -3350,7 +3367,7 @@ PHP_FUNCTION(date_modify)
 		RETURN_THROWS();
 	}

-	if (!php_date_modify(object, modify, modify_len)) {
+	if (!php_date_modify(object, modify, modify_len, PHP_DATE_MODIFY_WARNING)) {
 		RETURN_FALSE;
 	}

@@ -3364,21 +3381,16 @@ PHP_METHOD(DateTime, modify)
 	zval                *object;
 	char                *modify;
 	size_t               modify_len;
-	zend_error_handling  zeh;

 	object = ZEND_THIS;
 	ZEND_PARSE_PARAMETERS_START(1, 1)
 		Z_PARAM_STRING(modify, modify_len)
 	ZEND_PARSE_PARAMETERS_END();

-	zend_replace_error_handling(EH_THROW, date_ce_date_malformed_string_exception, &zeh);
-	if (!php_date_modify(object, modify, modify_len)) {
-		zend_restore_error_handling(&zeh);
+	if (!php_date_modify(object, modify, modify_len, PHP_DATE_MODIFY_THROW)) {
 		RETURN_THROWS();
 	}

-	zend_restore_error_handling(&zeh);
-
 	RETURN_OBJ_COPY(Z_OBJ_P(object));
 }
 /* }}} */
@@ -3389,7 +3401,6 @@ PHP_METHOD(DateTimeImmutable, modify)
 	zval *object, new_object;
 	char *modify;
 	size_t   modify_len;
-	zend_error_handling zeh;

 	object = ZEND_THIS;
 	ZEND_PARSE_PARAMETERS_START(1, 1)
@@ -3398,15 +3409,11 @@ PHP_METHOD(DateTimeImmutable, modify)

 	date_clone_immutable(object, &new_object);

-	zend_replace_error_handling(EH_THROW, date_ce_date_malformed_string_exception, &zeh);
-	if (!php_date_modify(&new_object, modify, modify_len)) {
+	if (!php_date_modify(&new_object, modify, modify_len, PHP_DATE_MODIFY_THROW)) {
 		zval_ptr_dtor(&new_object);
-		zend_restore_error_handling(&zeh);
 		RETURN_THROWS();
 	}

-	zend_restore_error_handling(&zeh);
-
 	RETURN_OBJ(Z_OBJ(new_object));
 }
 /* }}} */
@@ -3463,7 +3470,7 @@ PHP_METHOD(DateTimeImmutable, add)
 }
 /* }}} */

-static void php_date_sub(zval *object, zval *interval, zval *return_value) /* {{{ */
+static void php_date_sub(zval *object, zval *interval, zval *return_value, const bool should_throw) /* {{{ */
 {
 	php_date_obj     *dateobj;
 	php_interval_obj *intobj;
@@ -3475,7 +3482,15 @@ static void php_date_sub(zval *object, zval *interval, zval *return_value) /* {{
 	DATE_CHECK_INITIALIZED(intobj->initialized, Z_OBJCE_P(interval));

 	if (intobj->diff->have_weekday_relative || intobj->diff->have_special_relative) {
-		php_error_docref(NULL, E_WARNING, "Only non-special relative time specifications are supported for subtraction");
+		if (should_throw) {
+			zend_string *func_name = get_active_function_or_method_name();
+			zend_throw_exception_ex(date_ce_date_invalid_operation_exception, 0,
+				"%s(): Only non-special relative time specifications are supported for subtraction",
+				ZSTR_VAL(func_name));
+			zend_string_release_ex(func_name, false);
+		} else {
+			php_error_docref(NULL, E_WARNING, "Only non-special relative time specifications are supported for subtraction");
+		}
 		return;
 	}

@@ -3497,7 +3512,7 @@ PHP_FUNCTION(date_sub)
 		RETURN_THROWS();
 	}

-	php_date_sub(object, interval, return_value);
+	php_date_sub(object, interval, return_value, false);
 	RETURN_OBJ_COPY(Z_OBJ_P(object));
 }
 /* }}} */
@@ -3506,15 +3521,12 @@ PHP_FUNCTION(date_sub)
 PHP_METHOD(DateTime, sub)
 {
 	zval *object, *interval;
-	zend_error_handling zeh;

 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
 		RETURN_THROWS();
 	}

-	zend_replace_error_handling(EH_THROW, date_ce_date_invalid_operation_exception, &zeh);
-	php_date_sub(object, interval, return_value);
-	zend_restore_error_handling(&zeh);
+	php_date_sub(object, interval, return_value, true);

 	RETURN_OBJ_COPY(Z_OBJ_P(object));
 }
@@ -3524,7 +3536,6 @@ PHP_METHOD(DateTime, sub)
 PHP_METHOD(DateTimeImmutable, sub)
 {
 	zval *object, *interval, new_object;
-	zend_error_handling zeh;

 	object = ZEND_THIS;
 	ZEND_PARSE_PARAMETERS_START(1, 1)
@@ -3533,9 +3544,7 @@ PHP_METHOD(DateTimeImmutable, sub)

 	date_clone_immutable(object, &new_object);

-	zend_replace_error_handling(EH_THROW, date_ce_date_invalid_operation_exception, &zeh);
-	php_date_sub(&new_object, interval, return_value);
-	zend_restore_error_handling(&zeh);
+	php_date_sub(&new_object, interval, return_value, true);

 	RETURN_OBJ(Z_OBJ(new_object));
 }