Skip to content

Commit 21fe727

Browse files
authored
Declare DatePeriod properties (#8534)
1 parent fc799ec commit 21fe727

File tree

6 files changed

+166
-85
lines changed

6 files changed

+166
-85
lines changed

ext/date/php_date.c

Lines changed: 87 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,6 @@ static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_
299299
static HashTable *date_object_get_gc_interval(zend_object *object, zval **table, int *n);
300300
static HashTable *date_object_get_properties_interval(zend_object *object);
301301
static HashTable *date_object_get_gc_period(zend_object *object, zval **table, int *n);
302-
static HashTable *date_object_get_properties_period(zend_object *object);
303302
static HashTable *date_object_get_properties_for_timezone(zend_object *object, zend_prop_purpose purpose);
304303
static HashTable *date_object_get_gc_timezone(zend_object *object, zval **table, int *n);
305304
static HashTable *date_object_get_debug_info_timezone(zend_object *object, int *is_temp);
@@ -1396,6 +1395,64 @@ PHP_FUNCTION(getdate)
13961395
}
13971396
/* }}} */
13981397

1398+
static void create_date_period_datetime(timelib_time *datetime, zend_class_entry *ce, zval *zv)
1399+
{
1400+
if (datetime) {
1401+
php_date_obj *date_obj;
1402+
1403+
object_init_ex(zv, ce);
1404+
date_obj = Z_PHPDATE_P(zv);
1405+
date_obj->time = timelib_time_clone(datetime);
1406+
} else {
1407+
ZVAL_NULL(zv);
1408+
}
1409+
}
1410+
1411+
static void create_date_period_interval(timelib_rel_time *interval, zval *zv)
1412+
{
1413+
if (interval) {
1414+
php_interval_obj *interval_obj;
1415+
1416+
object_init_ex(zv, date_ce_interval);
1417+
interval_obj = Z_PHPINTERVAL_P(zv);
1418+
interval_obj->diff = timelib_rel_time_clone(interval);
1419+
interval_obj->initialized = 1;
1420+
} else {
1421+
ZVAL_NULL(zv);
1422+
}
1423+
}
1424+
1425+
static void initialize_date_period_properties(php_period_obj *period_obj)
1426+
{
1427+
zval start_zv, current_zv, end_zv, interval_zv;
1428+
1429+
if (UNEXPECTED(!period_obj->std.properties)) {
1430+
rebuild_object_properties(&period_obj->std);
1431+
}
1432+
1433+
create_date_period_datetime(period_obj->start, period_obj->start_ce, &start_zv);
1434+
zend_update_property(date_ce_period, &period_obj->std, "start", sizeof("start") - 1, &start_zv);
1435+
zval_ptr_dtor(&start_zv);
1436+
1437+
create_date_period_datetime(period_obj->current, period_obj->start_ce, &current_zv);
1438+
zend_string *property_name = zend_string_init("current", sizeof("current") - 1, 0);
1439+
zend_std_write_property(&period_obj->std, property_name, &current_zv, NULL);
1440+
zval_ptr_dtor(&current_zv);
1441+
zend_string_release(property_name);
1442+
1443+
create_date_period_datetime(period_obj->end, period_obj->start_ce, &end_zv);
1444+
zend_update_property(date_ce_period, &period_obj->std, "end", sizeof("end") - 1, &end_zv);
1445+
zval_ptr_dtor(&end_zv);
1446+
1447+
create_date_period_interval(period_obj->interval, &interval_zv);
1448+
zend_update_property(date_ce_period, &period_obj->std, "interval", sizeof("interval") - 1, &interval_zv);
1449+
zval_ptr_dtor(&interval_zv);
1450+
1451+
zend_update_property_long(date_ce_period, &period_obj->std, "recurrences", sizeof("recurrences") - 1, (zend_long) period_obj->recurrences);
1452+
zend_update_property_bool(date_ce_period, &period_obj->std, "include_start_date", sizeof("include_start_date") - 1, period_obj->include_start_date);
1453+
zend_update_property_bool(date_ce_period, &period_obj->std, "include_end_date", sizeof("include_end_date") - 1, period_obj->include_end_date);
1454+
}
1455+
13991456
/* define an overloaded iterator structure */
14001457
typedef struct {
14011458
zend_object_iterator intern;
@@ -1491,10 +1548,21 @@ static void date_period_it_move_forward(zend_object_iterator *iter)
14911548
{
14921549
date_period_it *iterator = (date_period_it *)iter;
14931550
php_period_obj *object = Z_PHPPERIOD_P(&iterator->intern.data);
1494-
timelib_time *it_time = object->current;
1551+
timelib_time *it_time = object->current;
1552+
zval current_zv;
14951553

14961554
date_period_advance(it_time, object->interval);
14971555

1556+
if (UNEXPECTED(!object->std.properties)) {
1557+
rebuild_object_properties(&object->std);
1558+
}
1559+
1560+
create_date_period_datetime(object->current, object->start_ce, &current_zv);
1561+
zend_string *property_name = zend_string_init("current", sizeof("current") - 1, 0);
1562+
zend_std_write_property(&object->std, property_name, &current_zv, NULL);
1563+
zval_ptr_dtor(&current_zv);
1564+
zend_string_release(property_name);
1565+
14981566
iterator->current_index++;
14991567
date_period_it_invalidate_current(iter);
15001568
}
@@ -1657,9 +1725,8 @@ static void date_register_classes(void) /* {{{ */
16571725
date_object_handlers_period.offset = XtOffsetOf(php_period_obj, std);
16581726
date_object_handlers_period.free_obj = date_object_free_storage_period;
16591727
date_object_handlers_period.clone_obj = date_object_clone_period;
1660-
date_object_handlers_period.get_properties = date_object_get_properties_period;
1661-
date_object_handlers_period.get_property_ptr_ptr = date_period_get_property_ptr_ptr;
16621728
date_object_handlers_period.get_gc = date_object_get_gc_period;
1729+
date_object_handlers_period.get_property_ptr_ptr = date_period_get_property_ptr_ptr;
16631730
date_object_handlers_period.read_property = date_period_read_property;
16641731
date_object_handlers_period.write_property = date_period_write_property;
16651732
} /* }}} */
@@ -4509,6 +4576,8 @@ PHP_METHOD(DatePeriod, __construct)
45094576
dpobj->recurrences = recurrences + dpobj->include_start_date + dpobj->include_end_date;
45104577

45114578
dpobj->initialized = 1;
4579+
4580+
initialize_date_period_properties(dpobj);
45124581
}
45134582
/* }}} */
45144583

@@ -4972,45 +5041,16 @@ static void date_period_object_to_hash(php_period_obj *period_obj, HashTable *pr
49725041
{
49735042
zval zv;
49745043

4975-
if (period_obj->start) {
4976-
php_date_obj *date_obj;
4977-
object_init_ex(&zv, period_obj->start_ce);
4978-
date_obj = Z_PHPDATE_P(&zv);
4979-
date_obj->time = timelib_time_clone(period_obj->start);
4980-
} else {
4981-
ZVAL_NULL(&zv);
4982-
}
5044+
create_date_period_datetime(period_obj->start, period_obj->start_ce, &zv);
49835045
zend_hash_str_update(props, "start", sizeof("start")-1, &zv);
49845046

4985-
if (period_obj->current) {
4986-
php_date_obj *date_obj;
4987-
object_init_ex(&zv, period_obj->start_ce);
4988-
date_obj = Z_PHPDATE_P(&zv);
4989-
date_obj->time = timelib_time_clone(period_obj->current);
4990-
} else {
4991-
ZVAL_NULL(&zv);
4992-
}
5047+
create_date_period_datetime(period_obj->current, period_obj->start_ce, &zv);
49935048
zend_hash_str_update(props, "current", sizeof("current")-1, &zv);
49945049

4995-
if (period_obj->end) {
4996-
php_date_obj *date_obj;
4997-
object_init_ex(&zv, period_obj->start_ce);
4998-
date_obj = Z_PHPDATE_P(&zv);
4999-
date_obj->time = timelib_time_clone(period_obj->end);
5000-
} else {
5001-
ZVAL_NULL(&zv);
5002-
}
5050+
create_date_period_datetime(period_obj->end, period_obj->start_ce, &zv);
50035051
zend_hash_str_update(props, "end", sizeof("end")-1, &zv);
50045052

5005-
if (period_obj->interval) {
5006-
php_interval_obj *interval_obj;
5007-
object_init_ex(&zv, date_ce_interval);
5008-
interval_obj = Z_PHPINTERVAL_P(&zv);
5009-
interval_obj->diff = timelib_rel_time_clone(period_obj->interval);
5010-
interval_obj->initialized = 1;
5011-
} else {
5012-
ZVAL_NULL(&zv);
5013-
}
5053+
create_date_period_interval(period_obj->interval, &zv);
50145054
zend_hash_str_update(props, "interval", sizeof("interval")-1, &zv);
50155055

50165056
/* converted to larger type (int->long); must check when unserializing */
@@ -5024,22 +5064,6 @@ static void date_period_object_to_hash(php_period_obj *period_obj, HashTable *pr
50245064
zend_hash_str_update(props, "include_end_date", sizeof("include_end_date")-1, &zv);
50255065
}
50265066

5027-
static HashTable *date_object_get_properties_period(zend_object *object) /* {{{ */
5028-
{
5029-
HashTable *props;
5030-
php_period_obj *period_obj;
5031-
5032-
period_obj = php_period_obj_from_obj(object);
5033-
props = zend_std_get_properties(object);
5034-
if (!period_obj->start) {
5035-
return props;
5036-
}
5037-
5038-
date_period_object_to_hash(period_obj, props);
5039-
5040-
return props;
5041-
} /* }}} */
5042-
50435067
static bool php_date_period_initialize_from_hash(php_period_obj *period_obj, HashTable *myht) /* {{{ */
50445068
{
50455069
zval *ht_entry;
@@ -5125,6 +5149,8 @@ static bool php_date_period_initialize_from_hash(php_period_obj *period_obj, Has
51255149

51265150
period_obj->initialized = 1;
51275151

5152+
initialize_date_period_properties(period_obj);
5153+
51285154
return 1;
51295155
} /* }}} */
51305156

@@ -5208,10 +5234,10 @@ PHP_METHOD(DatePeriod, __wakeup)
52085234
}
52095235
/* }}} */
52105236

5211-
/* {{{ date_period_is_magic_property
5237+
/* {{{ date_period_is_internal_property
52125238
* Common for date_period_read_property() and date_period_write_property() functions
52135239
*/
5214-
static bool date_period_is_magic_property(zend_string *name)
5240+
static bool date_period_is_internal_property(zend_string *name)
52155241
{
52165242
if (zend_string_equals_literal(name, "recurrences")
52175243
|| zend_string_equals_literal(name, "include_start_date")
@@ -5231,38 +5257,32 @@ static bool date_period_is_magic_property(zend_string *name)
52315257
static zval *date_period_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
52325258
{
52335259
if (type != BP_VAR_IS && type != BP_VAR_R) {
5234-
if (date_period_is_magic_property(name)) {
5235-
zend_throw_error(NULL, "Retrieval of DatePeriod->%s for modification is unsupported", ZSTR_VAL(name));
5260+
if (date_period_is_internal_property(name)) {
5261+
zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
52365262
return &EG(uninitialized_zval);
52375263
}
52385264
}
52395265

5240-
object->handlers->get_properties(object); /* build properties hash table */
5241-
52425266
return zend_std_read_property(object, name, type, cache_slot, rv);
52435267
}
52445268
/* }}} */
52455269

5246-
/* {{{ date_period_write_property */
52475270
static zval *date_period_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
52485271
{
5249-
if (date_period_is_magic_property(name)) {
5250-
zend_throw_error(NULL, "Writing to DatePeriod->%s is unsupported", ZSTR_VAL(name));
5272+
if (zend_string_equals_literal(name, "current")) {
5273+
zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
52515274
return value;
52525275
}
52535276

52545277
return zend_std_write_property(object, name, value, cache_slot);
52555278
}
5256-
/* }}} */
52575279

5258-
/* {{{ date_period_get_property_ptr_ptr */
52595280
static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
52605281
{
5261-
if (date_period_is_magic_property(name)) {
5262-
zend_throw_error(NULL, "Retrieval of DatePeriod->%s for modification is unsupported", ZSTR_VAL(name));
5282+
if (zend_string_equals_literal(name, "current")) {
5283+
zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
52635284
return &EG(error_zval);
52645285
}
52655286

52665287
return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
52675288
}
5268-
/* }}} */

ext/date/php_date.stub.php

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

697+
public readonly ?DateTimeInterface $start;
698+
/** @readonly */
699+
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;
705+
697706
/**
698707
* @param DateTimeInterface|string $start
699708
* @param DateInterval|int $interval

ext/date/php_date_arginfo.h

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 2cdf90d626d020b7e746cbccdb87833720d87132 */
2+
* Stub hash: 7ef8c504e35ca49f2c7442bd572b196587a36b7b */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)
55
ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0)
@@ -1026,5 +1026,51 @@ static zend_class_entry *register_class_DatePeriod(zend_class_entry *class_entry
10261026
zend_declare_class_constant_ex(class_entry, const_INCLUDE_END_DATE_name, &const_INCLUDE_END_DATE_value, ZEND_ACC_PUBLIC, NULL);
10271027
zend_string_release(const_INCLUDE_END_DATE_name);
10281028

1029+
zend_string *property_start_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1);
1030+
zval property_start_default_value;
1031+
ZVAL_UNDEF(&property_start_default_value);
1032+
zend_string *property_start_name = zend_string_init("start", sizeof("start") - 1, 1);
1033+
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));
1034+
zend_string_release(property_start_name);
1035+
1036+
zend_string *property_current_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1);
1037+
zval property_current_default_value;
1038+
ZVAL_UNDEF(&property_current_default_value);
1039+
zend_string *property_current_name = zend_string_init("current", sizeof("current") - 1, 1);
1040+
zend_declare_typed_property(class_entry, property_current_name, &property_current_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_current_class_DateTimeInterface, 0, MAY_BE_NULL));
1041+
zend_string_release(property_current_name);
1042+
1043+
zend_string *property_end_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1);
1044+
zval property_end_default_value;
1045+
ZVAL_UNDEF(&property_end_default_value);
1046+
zend_string *property_end_name = zend_string_init("end", sizeof("end") - 1, 1);
1047+
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));
1048+
zend_string_release(property_end_name);
1049+
1050+
zend_string *property_interval_class_DateInterval = zend_string_init("DateInterval", sizeof("DateInterval")-1, 1);
1051+
zval property_interval_default_value;
1052+
ZVAL_UNDEF(&property_interval_default_value);
1053+
zend_string *property_interval_name = zend_string_init("interval", sizeof("interval") - 1, 1);
1054+
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));
1055+
zend_string_release(property_interval_name);
1056+
1057+
zval property_recurrences_default_value;
1058+
ZVAL_UNDEF(&property_recurrences_default_value);
1059+
zend_string *property_recurrences_name = zend_string_init("recurrences", sizeof("recurrences") - 1, 1);
1060+
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));
1061+
zend_string_release(property_recurrences_name);
1062+
1063+
zval property_include_start_date_default_value;
1064+
ZVAL_UNDEF(&property_include_start_date_default_value);
1065+
zend_string *property_include_start_date_name = zend_string_init("include_start_date", sizeof("include_start_date") - 1, 1);
1066+
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));
1067+
zend_string_release(property_include_start_date_name);
1068+
1069+
zval property_include_end_date_default_value;
1070+
ZVAL_UNDEF(&property_include_end_date_default_value);
1071+
zend_string *property_include_end_date_name = zend_string_init("include_end_date", sizeof("include_end_date") - 1, 1);
1072+
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));
1073+
zend_string_release(property_include_end_date_name);
1074+
10291075
return class_entry;
10301076
}

ext/date/tests/DatePeriod_properties2.phpt

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,23 @@ foreach ($properties as $property) {
3030
}
3131
}
3232

33+
try {
34+
$period->start->modify("+1 hour");
35+
} catch (Error $e) {
36+
echo $e->getMessage() . "\n";
37+
}
38+
3339
?>
3440
--EXPECT--
35-
Writing to DatePeriod->recurrences is unsupported
36-
Retrieval of DatePeriod->recurrences for modification is unsupported
37-
Writing to DatePeriod->include_start_date is unsupported
38-
Retrieval of DatePeriod->include_start_date for modification is unsupported
39-
Writing to DatePeriod->start is unsupported
40-
Retrieval of DatePeriod->start for modification is unsupported
41-
Writing to DatePeriod->current is unsupported
42-
Retrieval of DatePeriod->current for modification is unsupported
43-
Writing to DatePeriod->end is unsupported
44-
Retrieval of DatePeriod->end for modification is unsupported
45-
Writing to DatePeriod->interval is unsupported
46-
Retrieval of DatePeriod->interval for modification is unsupported
41+
Cannot modify readonly property DatePeriod::$recurrences
42+
Cannot modify readonly property DatePeriod::$recurrences
43+
Cannot modify readonly property DatePeriod::$include_start_date
44+
Cannot modify readonly property DatePeriod::$include_start_date
45+
Cannot modify readonly property DatePeriod::$start
46+
Cannot modify readonly property DatePeriod::$start
47+
Cannot modify readonly property DatePeriod::$current
48+
Cannot modify readonly property DatePeriod::$current
49+
Cannot modify readonly property DatePeriod::$end
50+
Cannot modify readonly property DatePeriod::$end
51+
Cannot modify readonly property DatePeriod::$interval
52+
Cannot modify readonly property DatePeriod::$interval

ext/opcache/tests/jit/bug80634.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows
1919
$v = new SomeClass(5);
2020
?>
2121
--EXPECTF--
22-
Fatal error: Uncaught Error: Writing to DatePeriod->interval is unsupported in %spreload_bug80634.inc:7
22+
Fatal error: Uncaught Error: Cannot modify readonly property DatePeriod::$current in %spreload_bug80634.inc:7
2323
Stack trace:
2424
#0 %sbug80634.php(2): SomeClass->__construct(5)
2525
#1 {main}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<?php
22
class SomeClass extends \DatePeriod {
3-
public $interval;
3+
public ?DateTimeInterface $current;
44

55
public function __construct(int $v) {
66
parent::__construct(new \DateTime('2020-12-31'), new \DateInterval("P1Y"), 1);
7-
$this->interval = $v;
8-
var_dump($this->interval);
7+
$this->current = $v;
8+
var_dump($this->current);
99
}
10-
}
10+
}

0 commit comments

Comments
 (0)