diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 25b9bf7cbe6..692136bbad2 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -1486,7 +1486,8 @@ static void create_date_period_datetime(timelib_time *datetime, zend_class_entry if (datetime) { php_date_obj *date_obj; - object_init_ex(zv, ce); + zend_result result = object_init_ex(zv, ce); + ZEND_ASSERT(result == SUCCESS && "should succeed as it reuses an existing object's ce"); date_obj = Z_PHPDATE_P(zv); date_obj->time = timelib_time_clone(datetime); } else { @@ -2318,6 +2319,7 @@ static void add_common_properties(HashTable *myht, zend_object *zobj) } /* Advanced Interface */ +/* TODO: remove this API because it is unsafe to use as-is, as it does not propagate the failure/success status. */ PHPAPI zval *php_date_instantiate(zend_class_entry *pce, zval *object) /* {{{ */ { object_init_ex(object, pce); @@ -2595,7 +2597,9 @@ PHP_FUNCTION(date_create_from_format) Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone) ZEND_PARSE_PARAMETERS_END(); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date) != SUCCESS) { + RETURN_THROWS(); + } if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, PHP_DATE_INIT_FORMAT)) { zval_ptr_dtor(return_value); RETURN_FALSE; @@ -2617,7 +2621,9 @@ PHP_FUNCTION(date_create_immutable_from_format) Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone) ZEND_PARSE_PARAMETERS_END(); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable) != SUCCESS) { + RETURN_THROWS(); + } if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, PHP_DATE_INIT_FORMAT)) { zval_ptr_dtor(return_value); RETURN_FALSE; @@ -2673,7 +2679,9 @@ PHP_METHOD(DateTime, createFromImmutable) old_obj = Z_PHPDATE_P(datetimeimmutable_object); DATE_CHECK_INITIALIZED(old_obj->time, Z_OBJCE_P(datetimeimmutable_object)); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date) != SUCCESS) { + RETURN_THROWS(); + } new_obj = Z_PHPDATE_P(return_value); new_obj->time = timelib_time_clone(old_obj->time); @@ -2694,7 +2702,9 @@ PHP_METHOD(DateTime, createFromInterface) old_obj = Z_PHPDATE_P(datetimeinterface_object); DATE_CHECK_INITIALIZED(old_obj->time, Z_OBJCE_P(datetimeinterface_object)); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date) != SUCCESS) { + RETURN_THROWS(); + } new_obj = Z_PHPDATE_P(return_value); new_obj->time = timelib_time_clone(old_obj->time); @@ -2712,7 +2722,9 @@ PHP_METHOD(DateTime, createFromTimestamp) Z_PARAM_NUMBER(value) ZEND_PARSE_PARAMETERS_END(); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, &new_object); + if (object_init_ex(&new_object, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date) != SUCCESS) { + RETURN_THROWS(); + } new_dateobj = Z_PHPDATE_P(&new_object); switch (Z_TYPE_P(value)) { @@ -2748,7 +2760,9 @@ PHP_METHOD(DateTimeImmutable, createFromMutable) old_obj = Z_PHPDATE_P(datetime_object); DATE_CHECK_INITIALIZED(old_obj->time, Z_OBJCE_P(datetime_object)); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable) != SUCCESS) { + RETURN_THROWS(); + } new_obj = Z_PHPDATE_P(return_value); new_obj->time = timelib_time_clone(old_obj->time); @@ -2769,7 +2783,9 @@ PHP_METHOD(DateTimeImmutable, createFromInterface) old_obj = Z_PHPDATE_P(datetimeinterface_object); DATE_CHECK_INITIALIZED(old_obj->time, Z_OBJCE_P(datetimeinterface_object)); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable) != SUCCESS) { + RETURN_THROWS(); + } new_obj = Z_PHPDATE_P(return_value); new_obj->time = timelib_time_clone(old_obj->time); @@ -2787,7 +2803,9 @@ PHP_METHOD(DateTimeImmutable, createFromTimestamp) Z_PARAM_NUMBER(value) ZEND_PARSE_PARAMETERS_END(); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, &new_object); + if (object_init_ex(&new_object, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable) != SUCCESS) { + RETURN_THROWS(); + } new_dateobj = Z_PHPDATE_P(&new_object); switch (Z_TYPE_P(value)) { @@ -5108,7 +5126,9 @@ PHP_METHOD(DatePeriod, createFromISO8601String) Z_PARAM_LONG(options) ZEND_PARSE_PARAMETERS_END(); - object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_period); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_period) != SUCCESS) { + RETURN_THROWS(); + } dpobj = Z_PHPPERIOD_P(return_value); dpobj->current = NULL; diff --git a/ext/date/tests/instantiate_uninstantiable_classes.phpt b/ext/date/tests/instantiate_uninstantiable_classes.phpt new file mode 100644 index 00000000000..634ed635b2d --- /dev/null +++ b/ext/date/tests/instantiate_uninstantiable_classes.phpt @@ -0,0 +1,82 @@ +--TEST-- +Instantiating uninstantiable classes via static constructors +--FILE-- +getMessage(), "\n"; +} + +try { + MyDateTime::createFromFormat('Y-m-d', '2025-01-01'); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTime::createFromImmutable(new DateTimeImmutable()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTime::createFromInterface(new DateTimeImmutable()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTime::createFromTimestamp(0); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTimeImmutable::createFromFormat('Y-m-d', '2025-01-01'); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTimeImmutable::createFromMutable(new DateTime()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTimeImmutable::createFromInterface(new DateTime()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTimeImmutable::createFromTimestamp(0); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Cannot instantiate abstract class MyDatePeriod +Cannot instantiate abstract class MyDateTime +Cannot instantiate abstract class MyDateTime +Cannot instantiate abstract class MyDateTime +Cannot instantiate abstract class MyDateTime +Cannot instantiate abstract class MyDateTimeImmutable +Cannot instantiate abstract class MyDateTimeImmutable +Cannot instantiate abstract class MyDateTimeImmutable +Cannot instantiate abstract class MyDateTimeImmutable