Skip to content

Commit 2768597

Browse files
committed
Merged pull request #8459
2 parents b46632e + c1ad706 commit 2768597

34 files changed

+643
-521
lines changed

ext/date/php_date.c

Lines changed: 112 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2045,6 +2045,11 @@ static zend_object *date_object_clone_interval(zend_object *this_ptr) /* {{{ */
20452045
php_interval_obj *new_obj = php_interval_obj_from_obj(date_object_new_interval(old_obj->std.ce));
20462046

20472047
zend_objects_clone_members(&new_obj->std, &old_obj->std);
2048+
new_obj->civil_or_wall = old_obj->civil_or_wall;
2049+
new_obj->from_string = old_obj->from_string;
2050+
if (old_obj->date_string) {
2051+
new_obj->date_string = zend_string_copy(old_obj->date_string);
2052+
}
20482053
new_obj->initialized = old_obj->initialized;
20492054
if (old_obj->diff) {
20502055
new_obj->diff = timelib_rel_time_clone(old_obj->diff);
@@ -2061,16 +2066,17 @@ static HashTable *date_object_get_gc_interval(zend_object *object, zval **table,
20612066
return zend_std_get_properties(object);
20622067
} /* }}} */
20632068

2064-
static HashTable *date_object_get_properties_interval(zend_object *object) /* {{{ */
2069+
static void date_interval_object_to_hash(php_interval_obj *intervalobj, HashTable *props)
20652070
{
2066-
HashTable *props;
20672071
zval zv;
2068-
php_interval_obj *intervalobj;
20692072

2070-
intervalobj = php_interval_obj_from_obj(object);
2071-
props = zend_std_get_properties(object);
2072-
if (!intervalobj->initialized) {
2073-
return props;
2073+
/* Records whether this is a special relative interval that needs to be recreated from a string */
2074+
if (intervalobj->from_string) {
2075+
ZVAL_BOOL(&zv, (zend_bool)intervalobj->from_string);
2076+
zend_hash_str_update(props, "from_string", strlen("from_string"), &zv);
2077+
ZVAL_STR_COPY(&zv, intervalobj->date_string);
2078+
zend_hash_str_update(props, "date_string", strlen("date_string"), &zv);
2079+
return;
20742080
}
20752081

20762082
#define PHP_DATE_INTERVAL_ADD_PROPERTY(n,f) \
@@ -2085,20 +2091,31 @@ static HashTable *date_object_get_properties_interval(zend_object *object) /* {{
20852091
PHP_DATE_INTERVAL_ADD_PROPERTY("s", s);
20862092
ZVAL_DOUBLE(&zv, (double)intervalobj->diff->us / 1000000.0);
20872093
zend_hash_str_update(props, "f", sizeof("f") - 1, &zv);
2088-
PHP_DATE_INTERVAL_ADD_PROPERTY("weekday", weekday);
2089-
PHP_DATE_INTERVAL_ADD_PROPERTY("weekday_behavior", weekday_behavior);
2090-
PHP_DATE_INTERVAL_ADD_PROPERTY("first_last_day_of", first_last_day_of);
20912094
PHP_DATE_INTERVAL_ADD_PROPERTY("invert", invert);
20922095
if (intervalobj->diff->days != -99999) {
20932096
PHP_DATE_INTERVAL_ADD_PROPERTY("days", days);
20942097
} else {
20952098
ZVAL_FALSE(&zv);
20962099
zend_hash_str_update(props, "days", sizeof("days")-1, &zv);
20972100
}
2098-
PHP_DATE_INTERVAL_ADD_PROPERTY("special_type", special.type);
2099-
PHP_DATE_INTERVAL_ADD_PROPERTY("special_amount", special.amount);
2100-
PHP_DATE_INTERVAL_ADD_PROPERTY("have_weekday_relative", have_weekday_relative);
2101-
PHP_DATE_INTERVAL_ADD_PROPERTY("have_special_relative", have_special_relative);
2101+
ZVAL_BOOL(&zv, (zend_bool)intervalobj->from_string);
2102+
zend_hash_str_update(props, "from_string", strlen("from_string"), &zv);
2103+
2104+
#undef PHP_DATE_INTERVAL_ADD_PROPERTY
2105+
}
2106+
2107+
static HashTable *date_object_get_properties_interval(zend_object *object) /* {{{ */
2108+
{
2109+
HashTable *props;
2110+
php_interval_obj *intervalobj;
2111+
2112+
intervalobj = php_interval_obj_from_obj(object);
2113+
props = zend_std_get_properties(object);
2114+
if (!intervalobj->initialized) {
2115+
return props;
2116+
}
2117+
2118+
date_interval_object_to_hash(intervalobj, props);
21022119

21032120
return props;
21042121
} /* }}} */
@@ -2166,6 +2183,10 @@ static void date_object_free_storage_interval(zend_object *object) /* {{{ */
21662183
{
21672184
php_interval_obj *intern = php_interval_obj_from_obj(object);
21682185

2186+
if (intern->date_string) {
2187+
zend_string_release(intern->date_string);
2188+
intern->date_string = NULL;
2189+
}
21692190
timelib_rel_time_dtor(intern->diff);
21702191
zend_object_std_dtor(&intern->std);
21712192
} /* }}} */
@@ -3082,7 +3103,7 @@ static void php_date_sub(zval *object, zval *interval, zval *return_value) /* {{
30823103
intobj = Z_PHPINTERVAL_P(interval);
30833104
DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
30843105

3085-
if (intobj->diff->have_special_relative) {
3106+
if (intobj->diff->have_weekday_relative || intobj->diff->have_special_relative) {
30863107
php_error_docref(NULL, E_WARNING, "Only non-special relative time specifications are supported for subtraction");
30873108
return;
30883109
}
@@ -4092,6 +4113,41 @@ PHP_METHOD(DateInterval, __construct)
40924113

40934114
static void php_date_interval_initialize_from_hash(zval **return_value, php_interval_obj **intobj, HashTable *myht) /* {{{ */
40944115
{
4116+
/* If ->diff is already set, then we need to free it first */
4117+
if ((*intobj)->diff) {
4118+
timelib_rel_time_dtor((*intobj)->diff);
4119+
}
4120+
4121+
/* If we have a date_string, use that instead */
4122+
zval *date_str = zend_hash_str_find(myht, "date_string", strlen("date_string"));
4123+
if (date_str && Z_TYPE_P(date_str) == IS_STRING) {
4124+
timelib_time *time;
4125+
timelib_error_container *err = NULL;
4126+
4127+
time = timelib_strtotime(Z_STRVAL_P(date_str), Z_STRLEN_P(date_str), &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
4128+
4129+
if (err->error_count > 0) {
4130+
php_error_docref(NULL,
4131+
E_WARNING,
4132+
"Unknown or bad format (%s) at position %d (%c) while unserializing: %s",
4133+
Z_STRVAL_P(date_str),
4134+
err->error_messages[0].position,
4135+
err->error_messages[0].character ? err->error_messages[0].character : ' ', err->error_messages[0].message);
4136+
}
4137+
4138+
(*intobj)->diff = timelib_rel_time_clone(&time->relative);
4139+
(*intobj)->initialized = 1;
4140+
(*intobj)->civil_or_wall = PHP_DATE_CIVIL;
4141+
(*intobj)->from_string = true;
4142+
(*intobj)->date_string = zend_string_copy(Z_STR_P(date_str));
4143+
4144+
timelib_time_dtor(time);
4145+
timelib_error_container_dtor(err);
4146+
4147+
return;
4148+
}
4149+
4150+
/* Set new value */
40954151
(*intobj)->diff = timelib_rel_time_ctor();
40964152

40974153
#define PHP_DATE_INTERVAL_READ_PROPERTY(element, member, itype, def) \
@@ -4170,6 +4226,7 @@ static void php_date_interval_initialize_from_hash(zval **return_value, php_inte
41704226
(*intobj)->civil_or_wall = val;
41714227
}
41724228
}
4229+
41734230
(*intobj)->initialized = 1;
41744231
} /* }}} */
41754232

@@ -4192,6 +4249,44 @@ PHP_METHOD(DateInterval, __set_state)
41924249
}
41934250
/* }}} */
41944251

4252+
/* {{{ */
4253+
PHP_METHOD(DateInterval, __serialize)
4254+
{
4255+
zval *object = ZEND_THIS;
4256+
php_interval_obj *intervalobj;
4257+
HashTable *myht;
4258+
4259+
ZEND_PARSE_PARAMETERS_NONE();
4260+
4261+
intervalobj = Z_PHPINTERVAL_P(object);
4262+
DATE_CHECK_INITIALIZED(intervalobj->initialized, DateInterval);
4263+
4264+
array_init(return_value);
4265+
myht = Z_ARRVAL_P(return_value);
4266+
date_interval_object_to_hash(intervalobj, myht);
4267+
}
4268+
/* }}} */
4269+
4270+
4271+
/* {{{ */
4272+
PHP_METHOD(DateInterval, __unserialize)
4273+
{
4274+
zval *object = ZEND_THIS;
4275+
php_interval_obj *intervalobj;
4276+
zval *array;
4277+
HashTable *myht;
4278+
4279+
ZEND_PARSE_PARAMETERS_START(1, 1)
4280+
Z_PARAM_ARRAY(array)
4281+
ZEND_PARSE_PARAMETERS_END();
4282+
4283+
intervalobj = Z_PHPINTERVAL_P(object);
4284+
myht = Z_ARRVAL_P(array);
4285+
4286+
php_date_interval_initialize_from_hash(&object, &intervalobj, myht);
4287+
}
4288+
/* }}} */
4289+
41954290
/* {{{ */
41964291
PHP_METHOD(DateInterval, __wakeup)
41974292
{
@@ -4235,6 +4330,8 @@ PHP_FUNCTION(date_interval_create_from_date_string)
42354330
diobj->diff = timelib_rel_time_clone(&time->relative);
42364331
diobj->initialized = 1;
42374332
diobj->civil_or_wall = PHP_DATE_CIVIL;
4333+
diobj->from_string = true;
4334+
diobj->date_string = zend_string_copy(time_str);
42384335

42394336
cleanup:
42404337
timelib_time_dtor(time);

ext/date/php_date.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ static inline php_timezone_obj *php_timezone_obj_from_obj(zend_object *obj) {
7272
struct _php_interval_obj {
7373
timelib_rel_time *diff;
7474
int civil_or_wall;
75+
bool from_string;
76+
zend_string *date_string;
7577
bool initialized;
7678
zend_object std;
7779
};

ext/date/php_date.stub.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,10 @@ public static function createFromDateString(string $datetime): DateInterval|fals
480480
*/
481481
public function format(string $format): string {}
482482

483+
public function __serialize(): array;
484+
485+
public function __unserialize(array $data): void;
486+
483487
/** @tentative-return-type */
484488
public function __wakeup(): void {}
485489

ext/date/php_date_arginfo.h

Lines changed: 9 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: a157de6bca4bcf5a9ddace9e81ef700f132b4dda */
2+
* Stub hash: 4845891ab3872f292438de639953e2022f849125 */
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)
@@ -451,6 +451,10 @@ ZEND_END_ARG_INFO()
451451

452452
#define arginfo_class_DateInterval_format arginfo_class_DateTimeInterface_format
453453

454+
#define arginfo_class_DateInterval___serialize arginfo_timezone_abbreviations_list
455+
456+
#define arginfo_class_DateInterval___unserialize arginfo_class_DateTimeInterface___unserialize
457+
454458
#define arginfo_class_DateInterval___wakeup arginfo_class_DateTimeInterface___wakeup
455459

456460
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateInterval___set_state, 0, 1, DateInterval, 0)
@@ -562,6 +566,8 @@ ZEND_METHOD(DateTimeZone, __unserialize);
562566
ZEND_METHOD(DateTimeZone, __wakeup);
563567
ZEND_METHOD(DateTimeZone, __set_state);
564568
ZEND_METHOD(DateInterval, __construct);
569+
ZEND_METHOD(DateInterval, __serialize);
570+
ZEND_METHOD(DateInterval, __unserialize);
565571
ZEND_METHOD(DateInterval, __wakeup);
566572
ZEND_METHOD(DateInterval, __set_state);
567573
ZEND_METHOD(DatePeriod, __construct);
@@ -714,6 +720,8 @@ static const zend_function_entry class_DateInterval_methods[] = {
714720
ZEND_ME(DateInterval, __construct, arginfo_class_DateInterval___construct, ZEND_ACC_PUBLIC)
715721
ZEND_ME_MAPPING(createFromDateString, date_interval_create_from_date_string, arginfo_class_DateInterval_createFromDateString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
716722
ZEND_ME_MAPPING(format, date_interval_format, arginfo_class_DateInterval_format, ZEND_ACC_PUBLIC)
723+
ZEND_ME(DateInterval, __serialize, arginfo_class_DateInterval___serialize, ZEND_ACC_PUBLIC)
724+
ZEND_ME(DateInterval, __unserialize, arginfo_class_DateInterval___unserialize, ZEND_ACC_PUBLIC)
717725
ZEND_ME(DateInterval, __wakeup, arginfo_class_DateInterval___wakeup, ZEND_ACC_PUBLIC)
718726
ZEND_ME(DateInterval, __set_state, arginfo_class_DateInterval___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
719727
ZEND_FE_END
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
--TEST--
2+
Test DateInterval::__serialize and DateInterval::__unserialize
3+
--FILE--
4+
<?php
5+
date_default_timezone_set("Europe/London");
6+
7+
$d = new DateInterval('P2Y4DT6H8M');
8+
echo "Original object:\n";
9+
var_dump($d);
10+
11+
echo "\n\nSerialised object:\n";
12+
$s = serialize($d);
13+
var_dump($s);
14+
15+
echo "\n\nUnserialised object:\n";
16+
$e = unserialize($s);
17+
var_dump($e);
18+
19+
echo "\n\nCalling __serialize manually:\n";
20+
var_dump($d->__serialize());
21+
22+
echo "\n\nUsed serialised interval:\n";
23+
$now = new DateTimeImmutable("2022-04-22 16:25:11 BST");
24+
var_dump($now->add($e));
25+
var_dump($now->sub($e));
26+
?>
27+
--EXPECTF--
28+
Original object:
29+
object(DateInterval)#1 (10) {
30+
["y"]=>
31+
int(2)
32+
["m"]=>
33+
int(0)
34+
["d"]=>
35+
int(4)
36+
["h"]=>
37+
int(6)
38+
["i"]=>
39+
int(8)
40+
["s"]=>
41+
int(0)
42+
["f"]=>
43+
float(0)
44+
["invert"]=>
45+
int(0)
46+
["days"]=>
47+
bool(false)
48+
["from_string"]=>
49+
bool(false)
50+
}
51+
52+
53+
Serialised object:
54+
string(164) "O:12:"DateInterval":10:{s:1:"y";i:2;s:1:"m";i:0;s:1:"d";i:4;s:1:"h";i:6;s:1:"i";i:8;s:1:"s";i:0;s:1:"f";d:0;s:6:"invert";i:0;s:4:"days";b:0;s:11:"from_string";b:0;}"
55+
56+
57+
Unserialised object:
58+
object(DateInterval)#2 (10) {
59+
["y"]=>
60+
int(2)
61+
["m"]=>
62+
int(0)
63+
["d"]=>
64+
int(4)
65+
["h"]=>
66+
int(6)
67+
["i"]=>
68+
int(8)
69+
["s"]=>
70+
int(0)
71+
["f"]=>
72+
float(0)
73+
["invert"]=>
74+
int(0)
75+
["days"]=>
76+
bool(false)
77+
["from_string"]=>
78+
bool(false)
79+
}
80+
81+
82+
Calling __serialize manually:
83+
array(%d) {
84+
["y"]=>
85+
int(2)
86+
["m"]=>
87+
int(0)
88+
["d"]=>
89+
int(4)
90+
["h"]=>
91+
int(6)
92+
["i"]=>
93+
int(8)
94+
["s"]=>
95+
int(0)
96+
["f"]=>
97+
float(0)
98+
["invert"]=>
99+
int(0)
100+
["days"]=>
101+
bool(false)
102+
["from_string"]=>
103+
bool(false)
104+
}
105+
106+
107+
Used serialised interval:
108+
object(DateTimeImmutable)#4 (3) {
109+
["date"]=>
110+
string(26) "2024-04-26 22:33:11.000000"
111+
["timezone_type"]=>
112+
int(2)
113+
["timezone"]=>
114+
string(3) "BST"
115+
}
116+
object(DateTimeImmutable)#4 (3) {
117+
["date"]=>
118+
string(26) "2020-04-18 10:17:11.000000"
119+
["timezone_type"]=>
120+
int(2)
121+
["timezone"]=>
122+
string(3) "BST"
123+
}

0 commit comments

Comments
 (0)