Skip to content

Fix GH-10152: Custom properties of Date's child classes are not serialised #10446

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 146 additions & 19 deletions ext/date/php_date.c
Original file line number Diff line number Diff line change
Expand Up @@ -2254,6 +2254,19 @@ static void date_object_free_storage_period(zend_object *object) /* {{{ */
zend_object_std_dtor(&intern->std);
} /* }}} */

static void add_common_properties(HashTable *myht, zend_object *zobj)
{
HashTable *common;
zend_string *name;
zval *prop;

common = zend_std_get_properties(zobj);

ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_IND(common, name, prop) {
zend_hash_add(myht, name, prop);
} ZEND_HASH_FOREACH_END();
}

/* Advanced Interface */
PHPAPI zval *php_date_instantiate(zend_class_entry *pce, zval *object) /* {{{ */
{
Expand Down Expand Up @@ -2733,6 +2746,8 @@ PHP_METHOD(DateTime, __serialize)
array_init(return_value);
myht = Z_ARRVAL_P(return_value);
date_object_to_hash(dateobj, myht);

add_common_properties(myht, &dateobj->std);
}
/* }}} */

Expand All @@ -2751,9 +2766,36 @@ PHP_METHOD(DateTimeImmutable, __serialize)
array_init(return_value);
myht = Z_ARRVAL_P(return_value);
date_object_to_hash(dateobj, myht);

add_common_properties(myht, &dateobj->std);
}
/* }}} */

static bool date_time_is_internal_property(zend_string *name)
{
if (
zend_string_equals_literal(name, "date") ||
zend_string_equals_literal(name, "timezone_type") ||
zend_string_equals_literal(name, "timezone")
) {
return 1;
}
return 0;
}

static void restore_custom_datetime_properties(zval *object, HashTable *myht)
{
zend_string *prop_name;
zval *prop_val;

ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(myht, prop_name, prop_val) {
if (date_time_is_internal_property(prop_name)) {
continue;
}
add_property_zval_ex(object, ZSTR_VAL(prop_name), ZSTR_LEN(prop_name), prop_val);
} ZEND_HASH_FOREACH_END();
}

/* {{{ */
PHP_METHOD(DateTime, __unserialize)
{
Expand All @@ -2772,6 +2814,8 @@ PHP_METHOD(DateTime, __unserialize)
if (!php_date_initialize_from_hash(&dateobj, myht)) {
zend_throw_error(NULL, "Invalid serialization data for DateTime object");
}

restore_custom_datetime_properties(object, myht);
}
/* }}} */

Expand All @@ -2793,6 +2837,8 @@ PHP_METHOD(DateTimeImmutable, __unserialize)
if (!php_date_initialize_from_hash(&dateobj, myht)) {
zend_throw_error(NULL, "Invalid serialization data for DateTimeImmutable object");
}

restore_custom_datetime_properties(object, myht);
}
/* }}} */

Expand Down Expand Up @@ -3753,9 +3799,35 @@ PHP_METHOD(DateTimeZone, __serialize)
array_init(return_value);
myht = Z_ARRVAL_P(return_value);
date_timezone_object_to_hash(tzobj, myht);

add_common_properties(myht, &tzobj->std);
}
/* }}} */

static bool date_timezone_is_internal_property(zend_string *name)
{
if (
zend_string_equals_literal(name, "timezone_type") ||
zend_string_equals_literal(name, "timezone")
) {
return 1;
}
return 0;
}

static void restore_custom_datetimezone_properties(zval *object, HashTable *myht)
{
zend_string *prop_name;
zval *prop_val;

ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(myht, prop_name, prop_val) {
if (date_timezone_is_internal_property(prop_name)) {
continue;
}
add_property_zval_ex(object, ZSTR_VAL(prop_name), ZSTR_LEN(prop_name), prop_val);
} ZEND_HASH_FOREACH_END();
}

/* {{{ */
PHP_METHOD(DateTimeZone, __unserialize)
{
Expand All @@ -3774,6 +3846,8 @@ PHP_METHOD(DateTimeZone, __unserialize)
if (!php_date_timezone_initialize_from_hash(&object, &tzobj, myht)) {
zend_throw_error(NULL, "Invalid serialization data for DateTimeZone object");
}

restore_custom_datetimezone_properties(object, myht);
}
/* }}} */

Expand Down Expand Up @@ -4344,9 +4418,44 @@ PHP_METHOD(DateInterval, __serialize)
array_init(return_value);
myht = Z_ARRVAL_P(return_value);
date_interval_object_to_hash(intervalobj, myht);

add_common_properties(myht, &intervalobj->std);
}
/* }}} */

static bool date_interval_is_internal_property(zend_string *name)
{
if (
zend_string_equals_literal(name, "date_string") ||
zend_string_equals_literal(name, "from_string") ||
zend_string_equals_literal(name, "y") ||
zend_string_equals_literal(name, "m") ||
zend_string_equals_literal(name, "d") ||
zend_string_equals_literal(name, "h") ||
zend_string_equals_literal(name, "i") ||
zend_string_equals_literal(name, "s") ||
zend_string_equals_literal(name, "f") ||
zend_string_equals_literal(name, "invert") ||
zend_string_equals_literal(name, "days")
) {
return 1;
}
return 0;
}

static void restore_custom_dateinterval_properties(zval *object, HashTable *myht)
{
zend_string *prop_name;
zval *prop_val;

ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(myht, prop_name, prop_val) {
if (date_interval_is_internal_property(prop_name)) {
continue;
}
add_property_zval_ex(object, ZSTR_VAL(prop_name), ZSTR_LEN(prop_name), prop_val);
} ZEND_HASH_FOREACH_END();
}


/* {{{ */
PHP_METHOD(DateInterval, __unserialize)
Expand All @@ -4364,6 +4473,7 @@ PHP_METHOD(DateInterval, __unserialize)
myht = Z_ARRVAL_P(array);

php_date_interval_initialize_from_hash(&object, &intervalobj, myht);
restore_custom_dateinterval_properties(object, myht);
}
/* }}} */

Expand Down Expand Up @@ -5269,9 +5379,44 @@ PHP_METHOD(DatePeriod, __serialize)
array_init(return_value);
myht = Z_ARRVAL_P(return_value);
date_period_object_to_hash(period_obj, myht);

add_common_properties(myht, &period_obj->std);
}
/* }}} */

/* {{{ date_period_is_internal_property
* Common for date_period_read_property(), date_period_write_property(), and
* restore_custom_dateperiod_properties functions
*/
static bool date_period_is_internal_property(zend_string *name)
{
if (
zend_string_equals_literal(name, "start") ||
zend_string_equals_literal(name, "current") ||
zend_string_equals_literal(name, "end") ||
zend_string_equals_literal(name, "interval") ||
zend_string_equals_literal(name, "recurrences") ||
zend_string_equals_literal(name, "include_start_date") ||
zend_string_equals_literal(name, "include_end_date")
) {
return 1;
}
return 0;
}
/* }}} */

static void restore_custom_dateperiod_properties(zval *object, HashTable *myht)
{
zend_string *prop_name;
zval *prop_val;

ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(myht, prop_name, prop_val) {
if (date_period_is_internal_property(prop_name)) {
continue;
}
add_property_zval_ex(object, ZSTR_VAL(prop_name), ZSTR_LEN(prop_name), prop_val);
} ZEND_HASH_FOREACH_END();
}

/* {{{ */
PHP_METHOD(DatePeriod, __unserialize)
Expand All @@ -5291,6 +5436,7 @@ PHP_METHOD(DatePeriod, __unserialize)
if (!php_date_period_initialize_from_hash(period_obj, myht)) {
zend_throw_error(NULL, "Invalid serialization data for DatePeriod object");
}
restore_custom_dateperiod_properties(object, myht);
}
/* }}} */

Expand All @@ -5313,25 +5459,6 @@ PHP_METHOD(DatePeriod, __wakeup)
}
/* }}} */

/* {{{ date_period_is_internal_property
* Common for date_period_read_property() and date_period_write_property() functions
*/
static bool date_period_is_internal_property(zend_string *name)
{
if (zend_string_equals_literal(name, "recurrences")
|| zend_string_equals_literal(name, "include_start_date")
|| zend_string_equals_literal(name, "include_end_date")
|| zend_string_equals_literal(name, "start")
|| zend_string_equals_literal(name, "current")
|| zend_string_equals_literal(name, "end")
|| zend_string_equals_literal(name, "interval")
) {
return 1;
}
return 0;
}
/* }}} */

/* {{{ date_period_read_property */
static zval *date_period_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
{
Expand Down
23 changes: 23 additions & 0 deletions ext/date/tests/DateTimeImmutable_inherited_serialization.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Inherited DateTimeImmutable serialisation with custom properties
--FILE--
<?php
date_default_timezone_set("Europe/London");

class MyDateTimeImmutable extends DateTimeImmutable
{
public function __construct(
string $datetime = "now",
?DateTimeZone $timezone = null,
public ?bool $myProperty = null,
) {
parent::__construct($datetime, $timezone);
}
}

$d = new MyDateTimeImmutable("2023-01-25 16:32:55", myProperty: true);
$e = unserialize(serialize($d));
var_dump($e->myProperty);
?>
--EXPECTF--
bool(true)
22 changes: 22 additions & 0 deletions ext/date/tests/DateTimeInterval_inherited_serialization.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Inherited DateTimeInterval serialisation with custom properties
--FILE--
<?php
date_default_timezone_set("Europe/London");

class MyDateInterval extends DateInterval
{
public function __construct(
string $duration,
public ?bool $myProperty = null,
) {
parent::__construct($duration);
}
}

$d = new MyDateInterval("P1W2D", myProperty: true);
$e = unserialize(serialize($d));
var_dump($e->myProperty);
?>
--EXPECTF--
bool(true)
25 changes: 25 additions & 0 deletions ext/date/tests/DateTimePeriod_inherited_serialization.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Inherited DateTimePeriod serialisation with custom properties
--FILE--
<?php
date_default_timezone_set("Europe/London");

class MyDatePeriod extends DatePeriod
{
public function __construct(
DateTimeInterface $start,
DateInterval $interval,
int $recurrences,
int $options = 0,
public ?bool $myProperty = null,
) {
parent::__construct($start, $interval, $recurrences, $options);
}
}

$d = new MyDatePeriod(new DateTimeImmutable(), new DateInterval("PT5S"), 5, myProperty: true);
$e = unserialize(serialize($d));
var_dump($e->myProperty);
?>
--EXPECTF--
bool(true)
22 changes: 22 additions & 0 deletions ext/date/tests/DateTimeZone_inherited_serialization.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Inherited DateTimeZone serialisation with custom properties
--FILE--
<?php
date_default_timezone_set("Europe/London");

class MyDateTimeZone extends DateTimeZone
{
public function __construct(
string $timezone = "Europe/Kyiv",
public ?bool $myProperty = null,
) {
parent::__construct($timezone);
}
}

$d = new MyDateTimeZone("Europe/London", myProperty: true);
$e = unserialize(serialize($d));
var_dump($e->myProperty);
?>
--EXPECTF--
bool(true)
23 changes: 23 additions & 0 deletions ext/date/tests/DateTime_inherited_serialization.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Inherited DateTime serialisation with custom properties
--FILE--
<?php
date_default_timezone_set("Europe/London");

class MyDateTime extends DateTime
{
public function __construct(
string $datetime = "now",
?DateTimeZone $timezone = null,
public ?bool $myProperty = null,
) {
parent::__construct($datetime, $timezone);
}
}

$d = new MyDateTime("2023-01-25 16:32:55", myProperty: true);
$e = unserialize(serialize($d));
var_dump($e->myProperty);
?>
--EXPECTF--
bool(true)
2 changes: 1 addition & 1 deletion ext/date/tests/bug53437_var3.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Bug #53437 DateInterval unserialize bad data, 32 bit
<?php if (PHP_INT_SIZE != 4) { die('skip 32 bit only'); } ?>
--FILE--
<?php
$s = 'O:12:"DateInterval":15:{s:1:"y";s:1:"2";s:1:"m";s:1:"0";s:1:"d";s:3:"bla";s:1:"h";s:1:"6";s:1:"i";s:1:"8";s:1:"s";s:1:"0";s:7:"weekday";i:10;s:16:"weekday_behavior";i:10;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";s:4:"aoeu";s:12:"special_type";i:0;s:14:"special_amount";s:21:"234523452345234532455";s:21:"have_weekday_relative";i:21474836489;s:21:"have_special_relative";s:3:"bla";}';
$s = 'O:12:"DateInterval":8:{s:1:"y";s:1:"2";s:1:"m";s:1:"0";s:1:"d";s:3:"bla";s:1:"h";s:1:"6";s:1:"i";s:1:"8";s:1:"s";s:1:"0";s:6:"invert";i:0;s:4:"days";s:4:"aoeu";}';

$di = unserialize($s);
var_dump($di);
Expand Down
2 changes: 1 addition & 1 deletion ext/date/tests/bug53437_var5.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Bug #53437 (DateInterval unserialize bad data, 64 bit)
<?php if (PHP_INT_SIZE != 8) { die('skip true 64 bit only'); } ?>
--FILE--
<?php
$s = 'O:12:"DateInterval":15:{s:1:"y";s:1:"2";s:1:"m";s:1:"0";s:1:"d";s:3:"bla";s:1:"h";s:1:"6";s:1:"i";s:1:"8";s:1:"s";s:1:"0";s:7:"weekday";i:10;s:16:"weekday_behavior";i:10;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";s:4:"aoeu";s:12:"special_type";i:0;s:14:"special_amount";s:21:"234523452345234532455";s:21:"have_weekday_relative";i:21474836489;s:21:"have_special_relative";s:3:"bla";}';
$s = 'O:12:"DateInterval":8:{s:1:"y";s:1:"2";s:1:"m";s:1:"0";s:1:"d";s:3:"bla";s:1:"h";s:1:"6";s:1:"i";s:1:"8";s:1:"s";s:1:"0";s:6:"invert";i:0;s:4:"days";s:4:"aoeu";}';

$di = unserialize($s);
var_dump($di);
Expand Down
Loading