Skip to content

DatePeriod properties cannot be made readonly #9013

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 29 additions & 20 deletions ext/date/php_date.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, &current_zv);
zend_string *property_name = zend_string_init("current", sizeof("current") - 1, 0);
zend_std_write_property(&period_obj->std, property_name, &current_zv, NULL);
zval_ptr_dtor(&current_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 */
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}
Expand Down
18 changes: 12 additions & 6 deletions ext/date/php_date.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 7 additions & 7 deletions ext/date/php_date_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions ext/date/tests/date_period_set_state1.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
--TEST--
Test that DatePeriod::__set_state() can be called directly
--FILE--
<?php

$period = DatePeriod::__set_state(
[
"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)
}
18 changes: 18 additions & 0 deletions ext/date/tests/date_period_set_state2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Test that calling DatePeriod::__set_state() directly with missing arguments throws
--FILE--
<?php

try {
DatePeriod::__set_state(
[
"start" => new DateTime,
]
);
} catch (\Error $exception) {
echo $exception->getMessage() . "\n";
}

?>
--EXPECT--
Invalid serialization data for DatePeriod object
77 changes: 77 additions & 0 deletions ext/date/tests/date_period_unserialize1.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
--TEST--
Test that DatePeriod::__unserialize() can be called directly
--FILE--
<?php

$start = new DateTime("2022-07-14 00:00:00", new DateTimeZone("UTC"));
$interval = new DateInterval('P1D');
$end = new DateTime("2022-07-16 00:00:00", new DateTimeZone("UTC"));
$period = new DatePeriod($start, $interval, $end);

$period->__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)
}
23 changes: 23 additions & 0 deletions ext/date/tests/date_period_unserialize2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Test that calling DatePeriod::__unserialize() directly with missing arguments throws
--FILE--
<?php

$start = new DateTime("2022-07-14 00:00:00", new DateTimeZone("UTC"));
$interval = new DateInterval('P1D');
$end = new DateTime("2022-07-16 00:00:00", new DateTimeZone("UTC"));
$period = new DatePeriod($start, $interval, $end);

try {
$period->__unserialize(
[
"start" => new DateTime,
]
);
} catch (\Error $exception) {
echo $exception->getMessage() . "\n";
}

?>
--EXPECT--
Invalid serialization data for DatePeriod object
Loading