Skip to content

Commit e13d60c

Browse files
authored
DatePeriod properties cannot be made readonly (php#9013)
1 parent 0dbedb3 commit e13d60c

8 files changed

+320
-33
lines changed

ext/date/php_date.c

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,35 +1410,44 @@ static void create_date_period_interval(timelib_rel_time *interval, zval *zv)
14101410
}
14111411
}
14121412

1413+
static void write_date_period_property(zend_object *obj, const char *name, const size_t name_len, zval *zv)
1414+
{
1415+
zend_string *property_name = zend_string_init(name, name_len, 0);
1416+
1417+
zend_std_write_property(obj, property_name, zv, NULL);
1418+
1419+
zval_ptr_dtor(zv);
1420+
zend_string_release(property_name);
1421+
}
1422+
14131423
static void initialize_date_period_properties(php_period_obj *period_obj)
14141424
{
1415-
zval start_zv, current_zv, end_zv, interval_zv;
1425+
zval zv;
14161426

14171427
if (UNEXPECTED(!period_obj->std.properties)) {
14181428
rebuild_object_properties(&period_obj->std);
14191429
}
14201430

1421-
create_date_period_datetime(period_obj->start, period_obj->start_ce, &start_zv);
1422-
zend_update_property(date_ce_period, &period_obj->std, "start", sizeof("start") - 1, &start_zv);
1423-
zval_ptr_dtor(&start_zv);
1431+
create_date_period_datetime(period_obj->start, period_obj->start_ce, &zv);
1432+
write_date_period_property(&period_obj->std, "start", sizeof("start") - 1, &zv);
14241433

1425-
create_date_period_datetime(period_obj->current, period_obj->start_ce, &current_zv);
1426-
zend_string *property_name = zend_string_init("current", sizeof("current") - 1, 0);
1427-
zend_std_write_property(&period_obj->std, property_name, &current_zv, NULL);
1428-
zval_ptr_dtor(&current_zv);
1429-
zend_string_release(property_name);
1434+
create_date_period_datetime(period_obj->current, period_obj->start_ce, &zv);
1435+
write_date_period_property(&period_obj->std, "current", sizeof("current") - 1, &zv);
14301436

1431-
create_date_period_datetime(period_obj->end, period_obj->start_ce, &end_zv);
1432-
zend_update_property(date_ce_period, &period_obj->std, "end", sizeof("end") - 1, &end_zv);
1433-
zval_ptr_dtor(&end_zv);
1437+
create_date_period_datetime(period_obj->end, period_obj->start_ce, &zv);
1438+
write_date_period_property(&period_obj->std, "end", sizeof("end") - 1, &zv);
14341439

1435-
create_date_period_interval(period_obj->interval, &interval_zv);
1436-
zend_update_property(date_ce_period, &period_obj->std, "interval", sizeof("interval") - 1, &interval_zv);
1437-
zval_ptr_dtor(&interval_zv);
1440+
create_date_period_interval(period_obj->interval, &zv);
1441+
write_date_period_property(&period_obj->std, "interval", sizeof("interval") - 1, &zv);
14381442

1439-
zend_update_property_long(date_ce_period, &period_obj->std, "recurrences", sizeof("recurrences") - 1, (zend_long) period_obj->recurrences);
1440-
zend_update_property_bool(date_ce_period, &period_obj->std, "include_start_date", sizeof("include_start_date") - 1, period_obj->include_start_date);
1441-
zend_update_property_bool(date_ce_period, &period_obj->std, "include_end_date", sizeof("include_end_date") - 1, period_obj->include_end_date);
1443+
ZVAL_LONG(&zv, (zend_long) period_obj->recurrences);
1444+
write_date_period_property(&period_obj->std, "recurrences", sizeof("recurrences") - 1, &zv);
1445+
1446+
ZVAL_BOOL(&zv, period_obj->include_start_date);
1447+
write_date_period_property(&period_obj->std, "include_start_date", sizeof("include_start_date") - 1, &zv);
1448+
1449+
ZVAL_BOOL(&zv, period_obj->include_end_date);
1450+
write_date_period_property(&period_obj->std, "include_end_date", sizeof("include_end_date") - 1, &zv);
14421451
}
14431452

14441453
/* define an overloaded iterator structure */
@@ -5283,7 +5292,7 @@ static zval *date_period_read_property(zend_object *object, zend_string *name, i
52835292

52845293
static zval *date_period_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
52855294
{
5286-
if (zend_string_equals_literal(name, "current")) {
5295+
if (date_period_is_internal_property(name)) {
52875296
zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
52885297
return value;
52895298
}
@@ -5293,7 +5302,7 @@ static zval *date_period_write_property(zend_object *object, zend_string *name,
52935302

52945303
static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
52955304
{
5296-
if (zend_string_equals_literal(name, "current")) {
5305+
if (date_period_is_internal_property(name)) {
52975306
zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
52985307
return &EG(error_zval);
52995308
}

ext/date/php_date.stub.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -694,14 +694,20 @@ class DatePeriod implements IteratorAggregate
694694
*/
695695
public const INCLUDE_END_DATE = UNKNOWN;
696696

697-
public readonly ?DateTimeInterface $start;
697+
/** @readonly */
698+
public ?DateTimeInterface $start;
698699
/** @readonly */
699700
public ?DateTimeInterface $current;
700-
public readonly ?DateTimeInterface $end;
701-
public readonly ?DateInterval $interval;
702-
public readonly int $recurrences;
703-
public readonly bool $include_start_date;
704-
public readonly bool $include_end_date;
701+
/** @readonly */
702+
public ?DateTimeInterface $end;
703+
/** @readonly */
704+
public ?DateInterval $interval;
705+
/** @readonly */
706+
public int $recurrences;
707+
/** @readonly */
708+
public bool $include_start_date;
709+
/** @readonly */
710+
public bool $include_end_date;
705711

706712
/**
707713
* @param DateTimeInterface|string $start

ext/date/php_date_arginfo.h

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
--TEST--
2+
Test that DatePeriod::__set_state() can be called directly
3+
--FILE--
4+
<?php
5+
6+
$period = DatePeriod::__set_state(
7+
[
8+
"start" => new DateTime("2022-07-14 12:00:00", new DateTimeZone("UTC")),
9+
"end" => new DateTime("2022-07-16 12:00:00", new DateTimeZone("UTC")),
10+
"current" => null,
11+
"interval" => new DateInterval('P2D'),
12+
"recurrences" => 2,
13+
"include_start_date" => false,
14+
"include_end_date" => true,
15+
]
16+
);
17+
18+
var_dump($period);
19+
20+
?>
21+
--EXPECTF--
22+
object(DatePeriod)#%d (%d) {
23+
["start"]=>
24+
object(DateTime)#%d (%d) {
25+
["date"]=>
26+
string(26) "2022-07-14 12:00:00.000000"
27+
["timezone_type"]=>
28+
int(3)
29+
["timezone"]=>
30+
string(3) "UTC"
31+
}
32+
["current"]=>
33+
NULL
34+
["end"]=>
35+
object(DateTime)#%d (%d) {
36+
["date"]=>
37+
string(26) "2022-07-16 12:00:00.000000"
38+
["timezone_type"]=>
39+
int(3)
40+
["timezone"]=>
41+
string(3) "UTC"
42+
}
43+
["interval"]=>
44+
object(DateInterval)#%d (%d) {
45+
["y"]=>
46+
int(0)
47+
["m"]=>
48+
int(0)
49+
["d"]=>
50+
int(2)
51+
["h"]=>
52+
int(0)
53+
["i"]=>
54+
int(0)
55+
["s"]=>
56+
int(0)
57+
["f"]=>
58+
float(0)
59+
["invert"]=>
60+
int(0)
61+
["days"]=>
62+
bool(false)
63+
["from_string"]=>
64+
bool(false)
65+
}
66+
["recurrences"]=>
67+
int(2)
68+
["include_start_date"]=>
69+
bool(false)
70+
["include_end_date"]=>
71+
bool(true)
72+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Test that calling DatePeriod::__set_state() directly with missing arguments throws
3+
--FILE--
4+
<?php
5+
6+
try {
7+
DatePeriod::__set_state(
8+
[
9+
"start" => new DateTime,
10+
]
11+
);
12+
} catch (\Error $exception) {
13+
echo $exception->getMessage() . "\n";
14+
}
15+
16+
?>
17+
--EXPECT--
18+
Invalid serialization data for DatePeriod object
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
--TEST--
2+
Test that DatePeriod::__unserialize() can be called directly
3+
--FILE--
4+
<?php
5+
6+
$start = new DateTime("2022-07-14 00:00:00", new DateTimeZone("UTC"));
7+
$interval = new DateInterval('P1D');
8+
$end = new DateTime("2022-07-16 00:00:00", new DateTimeZone("UTC"));
9+
$period = new DatePeriod($start, $interval, $end);
10+
11+
$period->__unserialize(
12+
[
13+
"start" => new DateTime("2022-07-14 12:00:00", new DateTimeZone("UTC")),
14+
"end" => new DateTime("2022-07-16 12:00:00", new DateTimeZone("UTC")),
15+
"current" => null,
16+
"interval" => new DateInterval('P2D'),
17+
"recurrences" => 2,
18+
"include_start_date" => false,
19+
"include_end_date" => true,
20+
]
21+
);
22+
23+
var_dump($period);
24+
25+
?>
26+
--EXPECTF--
27+
object(DatePeriod)#%d (%d) {
28+
["start"]=>
29+
object(DateTime)#%d (%d) {
30+
["date"]=>
31+
string(26) "2022-07-14 12:00:00.000000"
32+
["timezone_type"]=>
33+
int(3)
34+
["timezone"]=>
35+
string(3) "UTC"
36+
}
37+
["current"]=>
38+
NULL
39+
["end"]=>
40+
object(DateTime)#%d (%d) {
41+
["date"]=>
42+
string(26) "2022-07-16 12:00:00.000000"
43+
["timezone_type"]=>
44+
int(3)
45+
["timezone"]=>
46+
string(3) "UTC"
47+
}
48+
["interval"]=>
49+
object(DateInterval)#%d (%d) {
50+
["y"]=>
51+
int(0)
52+
["m"]=>
53+
int(0)
54+
["d"]=>
55+
int(2)
56+
["h"]=>
57+
int(0)
58+
["i"]=>
59+
int(0)
60+
["s"]=>
61+
int(0)
62+
["f"]=>
63+
float(0)
64+
["invert"]=>
65+
int(0)
66+
["days"]=>
67+
bool(false)
68+
["from_string"]=>
69+
bool(false)
70+
}
71+
["recurrences"]=>
72+
int(2)
73+
["include_start_date"]=>
74+
bool(false)
75+
["include_end_date"]=>
76+
bool(true)
77+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Test that calling DatePeriod::__unserialize() directly with missing arguments throws
3+
--FILE--
4+
<?php
5+
6+
$start = new DateTime("2022-07-14 00:00:00", new DateTimeZone("UTC"));
7+
$interval = new DateInterval('P1D');
8+
$end = new DateTime("2022-07-16 00:00:00", new DateTimeZone("UTC"));
9+
$period = new DatePeriod($start, $interval, $end);
10+
11+
try {
12+
$period->__unserialize(
13+
[
14+
"start" => new DateTime,
15+
]
16+
);
17+
} catch (\Error $exception) {
18+
echo $exception->getMessage() . "\n";
19+
}
20+
21+
?>
22+
--EXPECT--
23+
Invalid serialization data for DatePeriod object

0 commit comments

Comments
 (0)