Skip to content

Commit 35b0d5a

Browse files
committed
Declare DatePeriod properties
1 parent 462d00f commit 35b0d5a

8 files changed

+181
-107
lines changed

ext/date/php_date.c

Lines changed: 89 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -172,19 +172,22 @@ static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_
172172
static HashTable *date_object_get_gc_interval(zend_object *object, zval **table, int *n);
173173
static HashTable *date_object_get_properties_interval(zend_object *object);
174174
static HashTable *date_object_get_gc_period(zend_object *object, zval **table, int *n);
175-
static HashTable *date_object_get_properties_period(zend_object *object);
176175
static HashTable *date_object_get_properties_for_timezone(zend_object *object, zend_prop_purpose purpose);
177176
static HashTable *date_object_get_gc_timezone(zend_object *object, zval **table, int *n);
178177
static HashTable *date_object_get_debug_info_timezone(zend_object *object, int *is_temp);
179178
static void php_timezone_to_string(php_timezone_obj *tzobj, zval *zv);
180179

180+
static void create_date_period_datetime(timelib_time *datetime, zend_class_entry *ce, zval *zv);
181+
static void create_date_period_interval(timelib_rel_time *interval, zval *zv);
182+
static void initialize_date_period_properties(php_period_obj *period_obj);
183+
181184
static int date_interval_compare_objects(zval *o1, zval *o2);
182185
static zval *date_interval_read_property(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv);
183186
static zval *date_interval_write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot);
184187
static zval *date_interval_get_property_ptr_ptr(zend_object *object, zend_string *member, int type, void **cache_slot);
188+
static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot);
185189
static zval *date_period_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv);
186190
static zval *date_period_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot);
187-
static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot);
188191

189192
static int date_object_compare_timezone(zval *tz1, zval *tz2);
190193

@@ -1523,10 +1526,21 @@ static void date_period_it_move_forward(zend_object_iterator *iter)
15231526
{
15241527
date_period_it *iterator = (date_period_it *)iter;
15251528
php_period_obj *object = Z_PHPPERIOD_P(&iterator->intern.data);
1526-
timelib_time *it_time = object->current;
1529+
timelib_time *it_time = object->current;
1530+
zval current_zv;
15271531

15281532
date_period_advance(it_time, object->interval);
15291533

1534+
if (UNEXPECTED(!object->std.properties)) {
1535+
rebuild_object_properties(&object->std);
1536+
}
1537+
1538+
create_date_period_datetime(object->current, object->start_ce, &current_zv);
1539+
zend_string *property_name = zend_string_init("current", sizeof("current") - 1, 0);
1540+
zend_std_write_property(&object->std, property_name, &current_zv, NULL);
1541+
zval_ptr_dtor(&current_zv);
1542+
zend_string_release(property_name);
1543+
15301544
iterator->current_index++;
15311545
date_period_it_invalidate_current(iter);
15321546
}
@@ -1724,9 +1738,8 @@ static void date_register_classes(void) /* {{{ */
17241738
date_object_handlers_period.offset = XtOffsetOf(php_period_obj, std);
17251739
date_object_handlers_period.free_obj = date_object_free_storage_period;
17261740
date_object_handlers_period.clone_obj = date_object_clone_period;
1727-
date_object_handlers_period.get_properties = date_object_get_properties_period;
1728-
date_object_handlers_period.get_property_ptr_ptr = date_period_get_property_ptr_ptr;
17291741
date_object_handlers_period.get_gc = date_object_get_gc_period;
1742+
date_object_handlers_period.get_property_ptr_ptr = date_period_get_property_ptr_ptr;
17301743
date_object_handlers_period.read_property = date_period_read_property;
17311744
date_object_handlers_period.write_property = date_period_write_property;
17321745

@@ -4572,6 +4585,8 @@ PHP_METHOD(DatePeriod, __construct)
45724585
dpobj->recurrences = recurrences + dpobj->include_start_date;
45734586

45744587
dpobj->initialized = 1;
4588+
4589+
initialize_date_period_properties(dpobj);
45754590
}
45764591
/* }}} */
45774592

@@ -5035,45 +5050,16 @@ static void date_period_object_to_hash(php_period_obj *period_obj, HashTable *pr
50355050
{
50365051
zval zv;
50375052

5038-
if (period_obj->start) {
5039-
php_date_obj *date_obj;
5040-
object_init_ex(&zv, period_obj->start_ce);
5041-
date_obj = Z_PHPDATE_P(&zv);
5042-
date_obj->time = timelib_time_clone(period_obj->start);
5043-
} else {
5044-
ZVAL_NULL(&zv);
5045-
}
5053+
create_date_period_datetime(period_obj->start, period_obj->start_ce, &zv);
50465054
zend_hash_str_update(props, "start", sizeof("start")-1, &zv);
50475055

5048-
if (period_obj->current) {
5049-
php_date_obj *date_obj;
5050-
object_init_ex(&zv, period_obj->start_ce);
5051-
date_obj = Z_PHPDATE_P(&zv);
5052-
date_obj->time = timelib_time_clone(period_obj->current);
5053-
} else {
5054-
ZVAL_NULL(&zv);
5055-
}
5056+
create_date_period_datetime(period_obj->current, period_obj->start_ce, &zv);
50565057
zend_hash_str_update(props, "current", sizeof("current")-1, &zv);
50575058

5058-
if (period_obj->end) {
5059-
php_date_obj *date_obj;
5060-
object_init_ex(&zv, period_obj->start_ce);
5061-
date_obj = Z_PHPDATE_P(&zv);
5062-
date_obj->time = timelib_time_clone(period_obj->end);
5063-
} else {
5064-
ZVAL_NULL(&zv);
5065-
}
5059+
create_date_period_datetime(period_obj->end, period_obj->start_ce, &zv);
50665060
zend_hash_str_update(props, "end", sizeof("end")-1, &zv);
50675061

5068-
if (period_obj->interval) {
5069-
php_interval_obj *interval_obj;
5070-
object_init_ex(&zv, date_ce_interval);
5071-
interval_obj = Z_PHPINTERVAL_P(&zv);
5072-
interval_obj->diff = timelib_rel_time_clone(period_obj->interval);
5073-
interval_obj->initialized = 1;
5074-
} else {
5075-
ZVAL_NULL(&zv);
5076-
}
5062+
create_date_period_interval(period_obj->interval, &zv);
50775063
zend_hash_str_update(props, "interval", sizeof("interval")-1, &zv);
50785064

50795065
/* converted to larger type (int->long); must check when unserializing */
@@ -5084,22 +5070,6 @@ static void date_period_object_to_hash(php_period_obj *period_obj, HashTable *pr
50845070
zend_hash_str_update(props, "include_start_date", sizeof("include_start_date")-1, &zv);
50855071
}
50865072

5087-
static HashTable *date_object_get_properties_period(zend_object *object) /* {{{ */
5088-
{
5089-
HashTable *props;
5090-
php_period_obj *period_obj;
5091-
5092-
period_obj = php_period_obj_from_obj(object);
5093-
props = zend_std_get_properties(object);
5094-
if (!period_obj->start) {
5095-
return props;
5096-
}
5097-
5098-
date_period_object_to_hash(period_obj, props);
5099-
5100-
return props;
5101-
} /* }}} */
5102-
51035073
static bool php_date_period_initialize_from_hash(php_period_obj *period_obj, HashTable *myht) /* {{{ */
51045074
{
51055075
zval *ht_entry;
@@ -5177,6 +5147,8 @@ static bool php_date_period_initialize_from_hash(php_period_obj *period_obj, Has
51775147

51785148
period_obj->initialized = 1;
51795149

5150+
initialize_date_period_properties(period_obj);
5151+
51805152
return 1;
51815153
} /* }}} */
51825154

@@ -5260,10 +5232,10 @@ PHP_METHOD(DatePeriod, __wakeup)
52605232
}
52615233
/* }}} */
52625234

5263-
/* {{{ date_period_is_magic_property
5235+
/* {{{ date_period_is_internal_property
52645236
* Common for date_period_read_property() and date_period_write_property() functions
52655237
*/
5266-
static bool date_period_is_magic_property(zend_string *name)
5238+
static bool date_period_is_internal_property(zend_string *name)
52675239
{
52685240
if (zend_string_equals_literal(name, "recurrences")
52695241
|| zend_string_equals_literal(name, "include_start_date")
@@ -5282,38 +5254,87 @@ static bool date_period_is_magic_property(zend_string *name)
52825254
static zval *date_period_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
52835255
{
52845256
if (type != BP_VAR_IS && type != BP_VAR_R) {
5285-
if (date_period_is_magic_property(name)) {
5286-
zend_throw_error(NULL, "Retrieval of DatePeriod->%s for modification is unsupported", ZSTR_VAL(name));
5257+
if (date_period_is_internal_property(name)) {
5258+
zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
52875259
return &EG(uninitialized_zval);
52885260
}
52895261
}
52905262

5291-
object->handlers->get_properties(object); /* build properties hash table */
5292-
52935263
return zend_std_read_property(object, name, type, cache_slot, rv);
52945264
}
52955265
/* }}} */
52965266

5297-
/* {{{ date_period_write_property */
52985267
static zval *date_period_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
52995268
{
5300-
if (date_period_is_magic_property(name)) {
5301-
zend_throw_error(NULL, "Writing to DatePeriod->%s is unsupported", ZSTR_VAL(name));
5269+
if (zend_string_equals_literal(name, "current")) {
5270+
zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
53025271
return value;
53035272
}
53045273

53055274
return zend_std_write_property(object, name, value, cache_slot);
53065275
}
5307-
/* }}} */
53085276

5309-
/* {{{ date_period_get_property_ptr_ptr */
53105277
static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
53115278
{
5312-
if (date_period_is_magic_property(name)) {
5313-
zend_throw_error(NULL, "Retrieval of DatePeriod->%s for modification is unsupported", ZSTR_VAL(name));
5279+
if (zend_string_equals_literal(name, "current")) {
5280+
zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
53145281
return &EG(error_zval);
53155282
}
53165283

53175284
return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
53185285
}
5319-
/* }}} */
5286+
5287+
static void create_date_period_datetime(timelib_time *datetime, zend_class_entry *ce, zval *zv)
5288+
{
5289+
if (datetime) {
5290+
php_date_obj *date_obj;
5291+
object_init_ex(zv, ce);
5292+
date_obj = Z_PHPDATE_P(zv);
5293+
date_obj->time = timelib_time_clone(datetime);
5294+
} else {
5295+
ZVAL_NULL(zv);
5296+
}
5297+
}
5298+
5299+
static void create_date_period_interval(timelib_rel_time *interval, zval *zv)
5300+
{
5301+
if (interval) {
5302+
php_interval_obj *interval_obj;
5303+
object_init_ex(zv, date_ce_interval);
5304+
interval_obj = Z_PHPINTERVAL_P(zv);
5305+
interval_obj->diff = timelib_rel_time_clone(interval);
5306+
interval_obj->initialized = 1;
5307+
} else {
5308+
ZVAL_NULL(zv);
5309+
}
5310+
}
5311+
5312+
static void initialize_date_period_properties(php_period_obj *period_obj)
5313+
{
5314+
zval start_zv, current_zv, end_zv, interval_zv;
5315+
5316+
if (UNEXPECTED(!period_obj->std.properties)) {
5317+
rebuild_object_properties(&period_obj->std);
5318+
}
5319+
5320+
create_date_period_datetime(period_obj->start, period_obj->start_ce, &start_zv);
5321+
zend_update_property(date_ce_period, &period_obj->std, "start", sizeof("start") - 1, &start_zv);
5322+
zval_ptr_dtor(&start_zv);
5323+
5324+
create_date_period_datetime(period_obj->current, period_obj->start_ce, &current_zv);
5325+
zend_string *property_name = zend_string_init("current", sizeof("current") - 1, 0);
5326+
zend_std_write_property(&period_obj->std, property_name, &current_zv, NULL);
5327+
zval_ptr_dtor(&current_zv);
5328+
zend_string_release(property_name);
5329+
5330+
create_date_period_datetime(period_obj->end, period_obj->start_ce, &end_zv);
5331+
zend_update_property(date_ce_period, &period_obj->std, "end", sizeof("end") - 1, &end_zv);
5332+
zval_ptr_dtor(&end_zv);
5333+
5334+
create_date_period_interval(period_obj->interval, &interval_zv);
5335+
zend_update_property(date_ce_period, &period_obj->std, "interval", sizeof("interval") - 1, &interval_zv);
5336+
zval_ptr_dtor(&interval_zv);
5337+
5338+
zend_update_property_long(date_ce_period, &period_obj->std, "recurrences", sizeof("recurrences") - 1, (zend_long) period_obj->recurrences);
5339+
zend_update_property_bool(date_ce_period, &period_obj->std, "include_start_date", sizeof("include_start_date") - 1, period_obj->include_start_date);
5340+
}

ext/date/php_date.stub.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,13 @@ public static function __set_state(array $array): DateInterval {}
495495

496496
class DatePeriod implements IteratorAggregate
497497
{
498+
public readonly ?DateTimeInterface $start;
499+
public ?DateTimeInterface $current;
500+
public readonly ?DateTimeInterface $end;
501+
public readonly ?DateInterval $interval;
502+
public readonly int $recurrences;
503+
public readonly bool $include_start_date;
504+
498505
/**
499506
* @param DateTimeInterface|string $start
500507
* @param DateInterval|int $interval

ext/date/php_date_arginfo.h

Lines changed: 41 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: ca149a2efec878f0730eb744f4e11f6462d70d83 */
2+
* Stub hash: 800df7ef926aea4d1291f19198553392e3c39572 */
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)
@@ -808,5 +808,45 @@ static zend_class_entry *register_class_DatePeriod(zend_class_entry *class_entry
808808
class_entry = zend_register_internal_class_ex(&ce, NULL);
809809
zend_class_implements(class_entry, 1, class_entry_IteratorAggregate);
810810

811+
zend_string *property_start_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1);
812+
zval property_start_default_value;
813+
ZVAL_UNDEF(&property_start_default_value);
814+
zend_string *property_start_name = zend_string_init("start", sizeof("start") - 1, 1);
815+
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));
816+
zend_string_release(property_start_name);
817+
818+
zend_string *property_current_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1);
819+
zval property_current_default_value;
820+
ZVAL_UNDEF(&property_current_default_value);
821+
zend_string *property_current_name = zend_string_init("current", sizeof("current") - 1, 1);
822+
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));
823+
zend_string_release(property_current_name);
824+
825+
zend_string *property_end_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1);
826+
zval property_end_default_value;
827+
ZVAL_UNDEF(&property_end_default_value);
828+
zend_string *property_end_name = zend_string_init("end", sizeof("end") - 1, 1);
829+
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));
830+
zend_string_release(property_end_name);
831+
832+
zend_string *property_interval_class_DateInterval = zend_string_init("DateInterval", sizeof("DateInterval")-1, 1);
833+
zval property_interval_default_value;
834+
ZVAL_UNDEF(&property_interval_default_value);
835+
zend_string *property_interval_name = zend_string_init("interval", sizeof("interval") - 1, 1);
836+
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));
837+
zend_string_release(property_interval_name);
838+
839+
zval property_recurrences_default_value;
840+
ZVAL_UNDEF(&property_recurrences_default_value);
841+
zend_string *property_recurrences_name = zend_string_init("recurrences", sizeof("recurrences") - 1, 1);
842+
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));
843+
zend_string_release(property_recurrences_name);
844+
845+
zval property_include_start_date_default_value;
846+
ZVAL_UNDEF(&property_include_start_date_default_value);
847+
zend_string *property_include_start_date_name = zend_string_init("include_start_date", sizeof("include_start_date") - 1, 1);
848+
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));
849+
zend_string_release(property_include_start_date_name);
850+
811851
return class_entry;
812852
}

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

0 commit comments

Comments
 (0)