mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix GH-10152: Custom properties of Date's child classes are not serialised
This commit is contained in:
@@ -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) /* {{{ */
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
ext/date/tests/DateTimeInterval_inherited_serialization.phpt
Normal file
22
ext/date/tests/DateTimeInterval_inherited_serialization.phpt
Normal file
@@ -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
ext/date/tests/DateTimePeriod_inherited_serialization.phpt
Normal file
25
ext/date/tests/DateTimePeriod_inherited_serialization.phpt
Normal file
@@ -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
ext/date/tests/DateTimeZone_inherited_serialization.phpt
Normal file
22
ext/date/tests/DateTimeZone_inherited_serialization.phpt
Normal file
@@ -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
ext/date/tests/DateTime_inherited_serialization.phpt
Normal file
23
ext/date/tests/DateTime_inherited_serialization.phpt
Normal file
@@ -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)
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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":16:{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:1:"f";d:0.123654;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":9:{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:1:"f";d:0.123654;s:6:"invert";i:0;s:4:"days";s:4:"aoeu";}';
|
||||
|
||||
$di = unserialize($s);
|
||||
var_dump($di);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Bug #79015 (undefined-behavior in php_date.c)
|
||||
--FILE--
|
||||
<?php
|
||||
$payload = 'O:12:"DateInterval":16:{s:1:"y";i:1;s:1:"m";i:0;s:1:"d";i:4;s:1:"h";i:0;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";i:9999999999990;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";b:0;s:12:"special_type";i:0;s:14:"special_amount";i:0;s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}';
|
||||
$payload = 'O:12:"DateInterval":9:{s:1:"y";i:1;s:1:"m";i:0;s:1:"d";i:4;s:1:"h";i:0;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";i:9999999999990;s:6:"invert";i:0;s:4:"days";b:0;}';
|
||||
var_dump(unserialize($payload));
|
||||
?>
|
||||
--EXPECTF--
|
||||
|
||||
23
ext/date/tests/gh10152.phpt
Normal file
23
ext/date/tests/gh10152.phpt
Normal file
@@ -0,0 +1,23 @@
|
||||
--TEST--
|
||||
GH-10152: Custom properties of DateTimeImmutable child classes are not serialized
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class MyDateTimeImmutable extends DateTimeImmutable {
|
||||
public function __construct(
|
||||
string $datetime = "now",
|
||||
?DateTimeZone $timezone = null,
|
||||
public ?bool $myProperty = null,
|
||||
) {
|
||||
parent::__construct($datetime, $timezone);
|
||||
}
|
||||
}
|
||||
|
||||
$datetime = new MyDateTimeImmutable('2022-12-22T11:26:00Z', myProperty: true);
|
||||
$serialized = serialize($datetime);
|
||||
$unserialized = unserialize($serialized);
|
||||
|
||||
var_dump($unserialized->myProperty);
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
Reference in New Issue
Block a user