diff --git a/ext/date/php_date.c b/ext/date/php_date.c index c57353ce4e8bc..3012e5bdd118f 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -1410,35 +1410,44 @@ static void create_date_period_interval(timelib_rel_time *interval, zval *zv) } } +static void write_date_period_property(zend_object *obj, const char *name, const size_t name_len, zval *zv) +{ + zend_string *property_name = zend_string_init(name, name_len, 0); + + zend_std_write_property(obj, property_name, zv, NULL); + + zval_ptr_dtor(zv); + zend_string_release(property_name); +} + static void initialize_date_period_properties(php_period_obj *period_obj) { - zval start_zv, current_zv, end_zv, interval_zv; + zval zv; if (UNEXPECTED(!period_obj->std.properties)) { rebuild_object_properties(&period_obj->std); } - create_date_period_datetime(period_obj->start, period_obj->start_ce, &start_zv); - zend_update_property(date_ce_period, &period_obj->std, "start", sizeof("start") - 1, &start_zv); - zval_ptr_dtor(&start_zv); + create_date_period_datetime(period_obj->start, period_obj->start_ce, &zv); + write_date_period_property(&period_obj->std, "start", sizeof("start") - 1, &zv); - create_date_period_datetime(period_obj->current, period_obj->start_ce, ¤t_zv); - zend_string *property_name = zend_string_init("current", sizeof("current") - 1, 0); - zend_std_write_property(&period_obj->std, property_name, ¤t_zv, NULL); - zval_ptr_dtor(¤t_zv); - zend_string_release(property_name); + create_date_period_datetime(period_obj->current, period_obj->start_ce, &zv); + write_date_period_property(&period_obj->std, "current", sizeof("current") - 1, &zv); - create_date_period_datetime(period_obj->end, period_obj->start_ce, &end_zv); - zend_update_property(date_ce_period, &period_obj->std, "end", sizeof("end") - 1, &end_zv); - zval_ptr_dtor(&end_zv); + create_date_period_datetime(period_obj->end, period_obj->start_ce, &zv); + write_date_period_property(&period_obj->std, "end", sizeof("end") - 1, &zv); - create_date_period_interval(period_obj->interval, &interval_zv); - zend_update_property(date_ce_period, &period_obj->std, "interval", sizeof("interval") - 1, &interval_zv); - zval_ptr_dtor(&interval_zv); + create_date_period_interval(period_obj->interval, &zv); + write_date_period_property(&period_obj->std, "interval", sizeof("interval") - 1, &zv); - zend_update_property_long(date_ce_period, &period_obj->std, "recurrences", sizeof("recurrences") - 1, (zend_long) period_obj->recurrences); - zend_update_property_bool(date_ce_period, &period_obj->std, "include_start_date", sizeof("include_start_date") - 1, period_obj->include_start_date); - zend_update_property_bool(date_ce_period, &period_obj->std, "include_end_date", sizeof("include_end_date") - 1, period_obj->include_end_date); + ZVAL_LONG(&zv, (zend_long) period_obj->recurrences); + write_date_period_property(&period_obj->std, "recurrences", sizeof("recurrences") - 1, &zv); + + ZVAL_BOOL(&zv, period_obj->include_start_date); + write_date_period_property(&period_obj->std, "include_start_date", sizeof("include_start_date") - 1, &zv); + + ZVAL_BOOL(&zv, period_obj->include_end_date); + write_date_period_property(&period_obj->std, "include_end_date", sizeof("include_end_date") - 1, &zv); } /* define an overloaded iterator structure */ @@ -5283,7 +5292,7 @@ static zval *date_period_read_property(zend_object *object, zend_string *name, i static zval *date_period_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) { - if (zend_string_equals_literal(name, "current")) { + if (date_period_is_internal_property(name)) { zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name)); return value; } @@ -5293,7 +5302,7 @@ static zval *date_period_write_property(zend_object *object, zend_string *name, static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot) { - if (zend_string_equals_literal(name, "current")) { + if (date_period_is_internal_property(name)) { zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name)); return &EG(error_zval); } diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php index 4edd19f36f1ca..822b5052898e6 100644 --- a/ext/date/php_date.stub.php +++ b/ext/date/php_date.stub.php @@ -694,14 +694,20 @@ class DatePeriod implements IteratorAggregate */ public const INCLUDE_END_DATE = UNKNOWN; - public readonly ?DateTimeInterface $start; + /** @readonly */ + public ?DateTimeInterface $start; /** @readonly */ public ?DateTimeInterface $current; - public readonly ?DateTimeInterface $end; - public readonly ?DateInterval $interval; - public readonly int $recurrences; - public readonly bool $include_start_date; - public readonly bool $include_end_date; + /** @readonly */ + public ?DateTimeInterface $end; + /** @readonly */ + public ?DateInterval $interval; + /** @readonly */ + public int $recurrences; + /** @readonly */ + public bool $include_start_date; + /** @readonly */ + public bool $include_end_date; /** * @param DateTimeInterface|string $start diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 6676464160dcb..73a83650fe318 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7ef8c504e35ca49f2c7442bd572b196587a36b7b */ + * Stub hash: 1fdf01c80508225887bac55c789ce93a314dd316 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) @@ -1030,7 +1030,7 @@ static zend_class_entry *register_class_DatePeriod(zend_class_entry *class_entry zval property_start_default_value; ZVAL_UNDEF(&property_start_default_value); zend_string *property_start_name = zend_string_init("start", sizeof("start") - 1, 1); - zend_declare_typed_property(class_entry, property_start_name, &property_start_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_start_class_DateTimeInterface, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_start_name, &property_start_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_start_class_DateTimeInterface, 0, MAY_BE_NULL)); zend_string_release(property_start_name); zend_string *property_current_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1); @@ -1044,32 +1044,32 @@ static zend_class_entry *register_class_DatePeriod(zend_class_entry *class_entry zval property_end_default_value; ZVAL_UNDEF(&property_end_default_value); zend_string *property_end_name = zend_string_init("end", sizeof("end") - 1, 1); - zend_declare_typed_property(class_entry, property_end_name, &property_end_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_end_class_DateTimeInterface, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_end_name, &property_end_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_end_class_DateTimeInterface, 0, MAY_BE_NULL)); zend_string_release(property_end_name); zend_string *property_interval_class_DateInterval = zend_string_init("DateInterval", sizeof("DateInterval")-1, 1); zval property_interval_default_value; ZVAL_UNDEF(&property_interval_default_value); zend_string *property_interval_name = zend_string_init("interval", sizeof("interval") - 1, 1); - zend_declare_typed_property(class_entry, property_interval_name, &property_interval_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_interval_class_DateInterval, 0, MAY_BE_NULL)); + zend_declare_typed_property(class_entry, property_interval_name, &property_interval_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_interval_class_DateInterval, 0, MAY_BE_NULL)); zend_string_release(property_interval_name); zval property_recurrences_default_value; ZVAL_UNDEF(&property_recurrences_default_value); zend_string *property_recurrences_name = zend_string_init("recurrences", sizeof("recurrences") - 1, 1); - zend_declare_typed_property(class_entry, property_recurrences_name, &property_recurrences_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_declare_typed_property(class_entry, property_recurrences_name, &property_recurrences_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(property_recurrences_name); zval property_include_start_date_default_value; ZVAL_UNDEF(&property_include_start_date_default_value); zend_string *property_include_start_date_name = zend_string_init("include_start_date", sizeof("include_start_date") - 1, 1); - zend_declare_typed_property(class_entry, property_include_start_date_name, &property_include_start_date_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_declare_typed_property(class_entry, property_include_start_date_name, &property_include_start_date_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); zend_string_release(property_include_start_date_name); zval property_include_end_date_default_value; ZVAL_UNDEF(&property_include_end_date_default_value); zend_string *property_include_end_date_name = zend_string_init("include_end_date", sizeof("include_end_date") - 1, 1); - zend_declare_typed_property(class_entry, property_include_end_date_name, &property_include_end_date_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_declare_typed_property(class_entry, property_include_end_date_name, &property_include_end_date_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); zend_string_release(property_include_end_date_name); return class_entry; diff --git a/ext/date/tests/date_period_set_state1.phpt b/ext/date/tests/date_period_set_state1.phpt new file mode 100644 index 0000000000000..b3824cee4e8dd --- /dev/null +++ b/ext/date/tests/date_period_set_state1.phpt @@ -0,0 +1,72 @@ +--TEST-- +Test that DatePeriod::__set_state() can be called directly +--FILE-- + new DateTime("2022-07-14 12:00:00", new DateTimeZone("UTC")), + "end" => new DateTime("2022-07-16 12:00:00", new DateTimeZone("UTC")), + "current" => null, + "interval" => new DateInterval('P2D'), + "recurrences" => 2, + "include_start_date" => false, + "include_end_date" => true, + ] +); + +var_dump($period); + +?> +--EXPECTF-- +object(DatePeriod)#%d (%d) { + ["start"]=> + object(DateTime)#%d (%d) { + ["date"]=> + string(26) "2022-07-14 12:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["current"]=> + NULL + ["end"]=> + object(DateTime)#%d (%d) { + ["date"]=> + string(26) "2022-07-16 12:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["interval"]=> + object(DateInterval)#%d (%d) { + ["y"]=> + int(0) + ["m"]=> + int(0) + ["d"]=> + int(2) + ["h"]=> + int(0) + ["i"]=> + int(0) + ["s"]=> + int(0) + ["f"]=> + float(0) + ["invert"]=> + int(0) + ["days"]=> + bool(false) + ["from_string"]=> + bool(false) + } + ["recurrences"]=> + int(2) + ["include_start_date"]=> + bool(false) + ["include_end_date"]=> + bool(true) +} diff --git a/ext/date/tests/date_period_set_state2.phpt b/ext/date/tests/date_period_set_state2.phpt new file mode 100644 index 0000000000000..7d77a632dc7e2 --- /dev/null +++ b/ext/date/tests/date_period_set_state2.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that calling DatePeriod::__set_state() directly with missing arguments throws +--FILE-- + new DateTime, + ] + ); +} catch (\Error $exception) { + echo $exception->getMessage() . "\n"; +} + +?> +--EXPECT-- +Invalid serialization data for DatePeriod object diff --git a/ext/date/tests/date_period_unserialize1.phpt b/ext/date/tests/date_period_unserialize1.phpt new file mode 100644 index 0000000000000..9ab7dd5148590 --- /dev/null +++ b/ext/date/tests/date_period_unserialize1.phpt @@ -0,0 +1,77 @@ +--TEST-- +Test that DatePeriod::__unserialize() can be called directly +--FILE-- +__unserialize( + [ + "start" => new DateTime("2022-07-14 12:00:00", new DateTimeZone("UTC")), + "end" => new DateTime("2022-07-16 12:00:00", new DateTimeZone("UTC")), + "current" => null, + "interval" => new DateInterval('P2D'), + "recurrences" => 2, + "include_start_date" => false, + "include_end_date" => true, + ] +); + +var_dump($period); + +?> +--EXPECTF-- +object(DatePeriod)#%d (%d) { + ["start"]=> + object(DateTime)#%d (%d) { + ["date"]=> + string(26) "2022-07-14 12:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["current"]=> + NULL + ["end"]=> + object(DateTime)#%d (%d) { + ["date"]=> + string(26) "2022-07-16 12:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["interval"]=> + object(DateInterval)#%d (%d) { + ["y"]=> + int(0) + ["m"]=> + int(0) + ["d"]=> + int(2) + ["h"]=> + int(0) + ["i"]=> + int(0) + ["s"]=> + int(0) + ["f"]=> + float(0) + ["invert"]=> + int(0) + ["days"]=> + bool(false) + ["from_string"]=> + bool(false) + } + ["recurrences"]=> + int(2) + ["include_start_date"]=> + bool(false) + ["include_end_date"]=> + bool(true) +} diff --git a/ext/date/tests/date_period_unserialize2.phpt b/ext/date/tests/date_period_unserialize2.phpt new file mode 100644 index 0000000000000..c368155dbe83e --- /dev/null +++ b/ext/date/tests/date_period_unserialize2.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test that calling DatePeriod::__unserialize() directly with missing arguments throws +--FILE-- +__unserialize( + [ + "start" => new DateTime, + ] + ); +} catch (\Error $exception) { + echo $exception->getMessage() . "\n"; +} + +?> +--EXPECT-- +Invalid serialization data for DatePeriod object diff --git a/ext/date/tests/date_period_unserialize3.phpt b/ext/date/tests/date_period_unserialize3.phpt new file mode 100644 index 0000000000000..4213320e30ae2 --- /dev/null +++ b/ext/date/tests/date_period_unserialize3.phpt @@ -0,0 +1,82 @@ +--TEST-- +Test that calling DatePeriod::__unserialize() directly with wrong argument type throws +--FILE-- +__unserialize( + [ + "current" => new DateTime, + "start" => new DateTime, + "end" => new DateTime, + "interval" => new DateInterval('P2D'), + "recurrences" => 2, + "include_start_date" => "wrong type", + "include_end_date" => true, + ] + ); +} catch (\Error $exception) { + echo $exception->getMessage() . "\n"; +} + +var_dump($period); + +?> +--EXPECTF-- +Invalid serialization data for DatePeriod object +object(DatePeriod)#%d (%d) { + ["start"]=> + object(DateTime)#%d (%d) { + ["date"]=> + string(26) "2022-07-14 00:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["current"]=> + NULL + ["end"]=> + object(DateTime)#%d (%d) { + ["date"]=> + string(26) "2022-07-16 00:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["interval"]=> + object(DateInterval)#%d (%d) { + ["y"]=> + int(0) + ["m"]=> + int(0) + ["d"]=> + int(1) + ["h"]=> + int(0) + ["i"]=> + int(0) + ["s"]=> + int(0) + ["f"]=> + float(0) + ["invert"]=> + int(0) + ["days"]=> + bool(false) + ["from_string"]=> + bool(false) + } + ["recurrences"]=> + int(1) + ["include_start_date"]=> + bool(true) + ["include_end_date"]=> + bool(false) +}