Skip to content

Commit 181623f

Browse files
committed
Add DateTimeZone's __serialize and __unserialize methods
1 parent 8b2ee53 commit 181623f

6 files changed

+151
-21
lines changed

ext/date/php_date.c

+63-18
Original file line numberDiff line numberDiff line change
@@ -1969,10 +1969,20 @@ static void php_timezone_to_string(php_timezone_obj *tzobj, zval *zv)
19691969
}
19701970
}
19711971

1972+
void date_timezone_object_to_hash(php_timezone_obj *tzobj, HashTable *props)
1973+
{
1974+
zval zv;
1975+
1976+
ZVAL_LONG(&zv, tzobj->type);
1977+
zend_hash_str_update(props, "timezone_type", strlen("timezone_type"), &zv);
1978+
1979+
php_timezone_to_string(tzobj, &zv);
1980+
zend_hash_str_update(props, "timezone", strlen("timezone"), &zv);
1981+
}
1982+
19721983
static HashTable *date_object_get_properties_for_timezone(zend_object *object, zend_prop_purpose purpose) /* {{{ */
19731984
{
19741985
HashTable *props;
1975-
zval zv;
19761986
php_timezone_obj *tzobj;
19771987

19781988
switch (purpose) {
@@ -1992,11 +2002,7 @@ static HashTable *date_object_get_properties_for_timezone(zend_object *object, z
19922002
return props;
19932003
}
19942004

1995-
ZVAL_LONG(&zv, tzobj->type);
1996-
zend_hash_str_update(props, "timezone_type", sizeof("timezone_type")-1, &zv);
1997-
1998-
php_timezone_to_string(tzobj, &zv);
1999-
zend_hash_str_update(props, "timezone", sizeof("timezone")-1, &zv);
2005+
date_timezone_object_to_hash(tzobj, props);
20002006

20012007
return props;
20022008
} /* }}} */
@@ -3500,7 +3506,7 @@ PHP_FUNCTION(date_diff)
35003506
}
35013507
/* }}} */
35023508

3503-
static zend_result timezone_initialize(php_timezone_obj *tzobj, const char *tz, size_t tz_len) /* {{{ */
3509+
static bool timezone_initialize(php_timezone_obj *tzobj, const char *tz, size_t tz_len) /* {{{ */
35043510
{
35053511
timelib_time *dummy_t = ecalloc(1, sizeof(timelib_time));
35063512
int dst, not_found;
@@ -3509,19 +3515,19 @@ static zend_result timezone_initialize(php_timezone_obj *tzobj, const char *tz,
35093515
if (strlen(tz) != tz_len) {
35103516
php_error_docref(NULL, E_WARNING, "Timezone must not contain null bytes");
35113517
efree(dummy_t);
3512-
return FAILURE;
3518+
return false;
35133519
}
35143520

35153521
dummy_t->z = timelib_parse_zone(&tz, &dst, dummy_t, &not_found, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
35163522
if (not_found) {
35173523
php_error_docref(NULL, E_WARNING, "Unknown or bad timezone (%s)", orig_tz);
35183524
efree(dummy_t);
3519-
return FAILURE;
3525+
return false;
35203526
} else {
35213527
set_timezone_from_timelib_time(tzobj, dummy_t);
35223528
timelib_free(dummy_t->tz_abbr);
35233529
efree(dummy_t);
3524-
return SUCCESS;
3530+
return true;
35253531
}
35263532
} /* }}} */
35273533

@@ -3536,7 +3542,7 @@ PHP_FUNCTION(timezone_open)
35363542
ZEND_PARSE_PARAMETERS_END();
35373543

35383544
tzobj = Z_PHPTIMEZONE_P(php_date_instantiate(date_ce_timezone, return_value));
3539-
if (FAILURE == timezone_initialize(tzobj, ZSTR_VAL(tz), ZSTR_LEN(tz))) {
3545+
if (!timezone_initialize(tzobj, ZSTR_VAL(tz), ZSTR_LEN(tz))) {
35403546
zval_ptr_dtor(return_value);
35413547
RETURN_FALSE;
35423548
}
@@ -3561,25 +3567,25 @@ PHP_METHOD(DateTimeZone, __construct)
35613567
}
35623568
/* }}} */
35633569

3564-
static zend_result php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, HashTable *myht) /* {{{ */
3570+
static bool php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, HashTable *myht) /* {{{ */
35653571
{
35663572
zval *z_timezone_type;
35673573

35683574
if ((z_timezone_type = zend_hash_str_find(myht, "timezone_type", sizeof("timezone_type") - 1)) == NULL) {
3569-
return FAILURE;
3575+
return false;
35703576
}
35713577

35723578
zval *z_timezone;
35733579

35743580
if ((z_timezone = zend_hash_str_find(myht, "timezone", sizeof("timezone") - 1)) == NULL) {
3575-
return FAILURE;
3581+
return false;
35763582
}
35773583

35783584
if (Z_TYPE_P(z_timezone_type) != IS_LONG) {
3579-
return FAILURE;
3585+
return false;
35803586
}
35813587
if (Z_TYPE_P(z_timezone) != IS_STRING) {
3582-
return FAILURE;
3588+
return false;
35833589
}
35843590
return timezone_initialize(*tzobj, Z_STRVAL_P(z_timezone), Z_STRLEN_P(z_timezone));
35853591
} /* }}} */
@@ -3599,7 +3605,7 @@ PHP_METHOD(DateTimeZone, __set_state)
35993605

36003606
php_date_instantiate(date_ce_timezone, return_value);
36013607
tzobj = Z_PHPTIMEZONE_P(return_value);
3602-
if (php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht) == FAILURE) {
3608+
if (!php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht)) {
36033609
zend_throw_error(NULL, "Timezone initialization failed");
36043610
zval_ptr_dtor(return_value);
36053611
}
@@ -3619,12 +3625,51 @@ PHP_METHOD(DateTimeZone, __wakeup)
36193625

36203626
myht = Z_OBJPROP_P(object);
36213627

3622-
if (php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht) == FAILURE) {
3628+
if (!php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht)) {
36233629
zend_throw_error(NULL, "Timezone initialization failed");
36243630
}
36253631
}
36263632
/* }}} */
36273633

3634+
/* {{{ */
3635+
PHP_METHOD(DateTimeZone, __serialize)
3636+
{
3637+
zval *object = ZEND_THIS;
3638+
php_timezone_obj *tzobj;
3639+
HashTable *myht;
3640+
3641+
ZEND_PARSE_PARAMETERS_NONE();
3642+
3643+
tzobj = Z_PHPTIMEZONE_P(object);
3644+
DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3645+
3646+
array_init(return_value);
3647+
myht = Z_ARRVAL_P(return_value);
3648+
date_timezone_object_to_hash(tzobj, myht);
3649+
}
3650+
/* }}} */
3651+
3652+
/* {{{ */
3653+
PHP_METHOD(DateTimeZone, __unserialize)
3654+
{
3655+
zval *object = ZEND_THIS;
3656+
php_timezone_obj *tzobj;
3657+
zval *array;
3658+
HashTable *myht;
3659+
3660+
ZEND_PARSE_PARAMETERS_START(1, 1)
3661+
Z_PARAM_ARRAY(array)
3662+
ZEND_PARSE_PARAMETERS_END();
3663+
3664+
tzobj = Z_PHPTIMEZONE_P(object);
3665+
myht = Z_ARRVAL_P(array);
3666+
3667+
if (!php_date_timezone_initialize_from_hash(&object, &tzobj, myht)) {
3668+
zend_throw_error(NULL, "Invalid serialization data for DateTimeZone object");
3669+
}
3670+
}
3671+
/* }}} */
3672+
36283673
/* {{{ Returns the name of the timezone. */
36293674
PHP_FUNCTION(timezone_name_get)
36303675
{

ext/date/php_date.stub.php

+4
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,10 @@ public static function listAbbreviations(): array {}
453453
*/
454454
public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array {}
455455

456+
public function __serialize(): array {}
457+
458+
public function __unserialize(array $data): void {}
459+
456460
/** @tentative-return-type */
457461
public function __wakeup(): void {}
458462

ext/date/php_date_arginfo.h

+9-1
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: ea354510fbf64c42ee1cdd6fd786ab937516226c */
2+
* Stub hash: a157de6bca4bcf5a9ddace9e81ef700f132b4dda */
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)
@@ -431,6 +431,10 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeZone_lis
431431
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, countryCode, IS_STRING, 1, "null")
432432
ZEND_END_ARG_INFO()
433433

434+
#define arginfo_class_DateTimeZone___serialize arginfo_timezone_abbreviations_list
435+
436+
#define arginfo_class_DateTimeZone___unserialize arginfo_class_DateTimeInterface___unserialize
437+
434438
#define arginfo_class_DateTimeZone___wakeup arginfo_class_DateTimeInterface___wakeup
435439

436440
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeZone___set_state, 0, 1, DateTimeZone, 0)
@@ -553,6 +557,8 @@ ZEND_METHOD(DateTimeImmutable, setTimestamp);
553557
ZEND_METHOD(DateTimeImmutable, createFromMutable);
554558
ZEND_METHOD(DateTimeImmutable, createFromInterface);
555559
ZEND_METHOD(DateTimeZone, __construct);
560+
ZEND_METHOD(DateTimeZone, __serialize);
561+
ZEND_METHOD(DateTimeZone, __unserialize);
556562
ZEND_METHOD(DateTimeZone, __wakeup);
557563
ZEND_METHOD(DateTimeZone, __set_state);
558564
ZEND_METHOD(DateInterval, __construct);
@@ -696,6 +702,8 @@ static const zend_function_entry class_DateTimeZone_methods[] = {
696702
ZEND_ME_MAPPING(getLocation, timezone_location_get, arginfo_class_DateTimeZone_getLocation, ZEND_ACC_PUBLIC)
697703
ZEND_ME_MAPPING(listAbbreviations, timezone_abbreviations_list, arginfo_class_DateTimeZone_listAbbreviations, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
698704
ZEND_ME_MAPPING(listIdentifiers, timezone_identifiers_list, arginfo_class_DateTimeZone_listIdentifiers, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
705+
ZEND_ME(DateTimeZone, __serialize, arginfo_class_DateTimeZone___serialize, ZEND_ACC_PUBLIC)
706+
ZEND_ME(DateTimeZone, __unserialize, arginfo_class_DateTimeZone___unserialize, ZEND_ACC_PUBLIC)
699707
ZEND_ME(DateTimeZone, __wakeup, arginfo_class_DateTimeZone___wakeup, ZEND_ACC_PUBLIC)
700708
ZEND_ME(DateTimeZone, __set_state, arginfo_class_DateTimeZone___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
701709
ZEND_FE_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
--TEST--
2+
Test DateTimeZone::__serialize and DateTime::__unserialize
3+
--FILE--
4+
<?php
5+
$tz = new DateTimeZone("CEST");
6+
var_dump($tz->__serialize());
7+
8+
$tz = new DateTimeZone("UTC");
9+
$tz->__unserialize(
10+
[
11+
'timezone_type' => 3,
12+
'timezone' => 'Europe/London',
13+
]
14+
);
15+
var_dump($tz);
16+
17+
$tz->__unserialize(
18+
[
19+
'timezone_type' => 2,
20+
'timezone' => 'CEST',
21+
]
22+
);
23+
var_dump($tz);
24+
25+
$tz->__unserialize(
26+
[
27+
'timezone_type' => 1,
28+
'timezone' => '+0130',
29+
]
30+
);
31+
var_dump($tz);
32+
33+
?>
34+
--EXPECTF--
35+
array(2) {
36+
["timezone_type"]=>
37+
int(2)
38+
["timezone"]=>
39+
string(4) "CEST"
40+
}
41+
object(DateTimeZone)#%d (%d) {
42+
["timezone_type"]=>
43+
int(3)
44+
["timezone"]=>
45+
string(13) "Europe/London"
46+
}
47+
object(DateTimeZone)#%d (%d) {
48+
["timezone_type"]=>
49+
int(2)
50+
["timezone"]=>
51+
string(4) "CEST"
52+
}
53+
object(DateTimeZone)#%d (%d) {
54+
["timezone_type"]=>
55+
int(1)
56+
["timezone"]=>
57+
string(6) "+01:30"
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Test unserialization of DateTimeZone with null byte
3+
--FILE--
4+
<?php
5+
$serialized = 'O:12:"DateTimeZone":2:{s:13:"timezone_type";i:3;s:8:"timezone";s:17:"Ame' . "\0" .'rica/New_York";}';
6+
7+
try {
8+
$tz = unserialize($serialized);
9+
} catch (Throwable $e) {
10+
echo $e->getMessage(), "\n";
11+
}
12+
?>
13+
--EXPECTF--
14+
Warning: DateTimeZone::__unserialize(): Timezone must not contain null bytes in %s on line %d
15+
Invalid serialization data for DateTimeZone object

ext/date/tests/bug68942.phpt

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ $data = unserialize('a:2:{i:0;O:12:"DateTimeZone":2:{s:13:"timezone_type";a:2:{i
66
var_dump($data);
77
?>
88
--EXPECTF--
9-
Fatal error: Uncaught Error: Timezone initialization failed in %s:%d
9+
Fatal error: Uncaught Error: Invalid serialization data for DateTimeZone object in %s:%d
1010
Stack trace:
11-
#0 [internal function]: DateTimeZone->__wakeup()
11+
#0 [internal function]: DateTimeZone->__unserialize(Array)
1212
#1 %s(%d): unserialize('a:2:{i:0;O:12:"...')
1313
#2 {main}
1414
thrown in %s on line %d

0 commit comments

Comments
 (0)