Add DateTime/DateTimeImmutable's __serialize and __unserialize methods

This commit is contained in:
Derick Rethans 2022-04-14 16:34:51 +01:00
parent 90a845c13f
commit 8b2ee5388c
11 changed files with 387 additions and 33 deletions

View file

@ -1811,28 +1811,9 @@ static HashTable *date_object_get_gc_timezone(zend_object *object, zval **table,
return zend_std_get_properties(object);
} /* }}} */
static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_purpose purpose) /* {{{ */
static void date_object_to_hash(php_date_obj *dateobj, HashTable *props)
{
HashTable *props;
zval zv;
php_date_obj *dateobj;
switch (purpose) {
case ZEND_PROP_PURPOSE_DEBUG:
case ZEND_PROP_PURPOSE_SERIALIZE:
case ZEND_PROP_PURPOSE_VAR_EXPORT:
case ZEND_PROP_PURPOSE_JSON:
case ZEND_PROP_PURPOSE_ARRAY_CAST:
break;
default:
return zend_std_get_properties_for(object, purpose);
}
dateobj = php_date_obj_from_obj(object);
props = zend_array_dup(zend_std_get_properties(object));
if (!dateobj->time) {
return props;
}
/* first we add the date and time in ISO format */
ZVAL_STR(&zv, date_format("Y-m-d H:i:s.u", sizeof("Y-m-d H:i:s.u")-1, dateobj->time, 1));
@ -1865,6 +1846,31 @@ static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_
}
zend_hash_str_update(props, "timezone", sizeof("timezone")-1, &zv);
}
}
static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_purpose purpose) /* {{{ */
{
HashTable *props;
php_date_obj *dateobj;
switch (purpose) {
case ZEND_PROP_PURPOSE_DEBUG:
case ZEND_PROP_PURPOSE_SERIALIZE:
case ZEND_PROP_PURPOSE_VAR_EXPORT:
case ZEND_PROP_PURPOSE_JSON:
case ZEND_PROP_PURPOSE_ARRAY_CAST:
break;
default:
return zend_std_get_properties_for(object, purpose);
}
dateobj = php_date_obj_from_obj(object);
props = zend_array_dup(zend_std_get_properties(object));
if (!dateobj->time) {
return props;
}
date_object_to_hash(dateobj, props);
return props;
} /* }}} */
@ -2623,6 +2629,84 @@ PHP_METHOD(DateTimeImmutable, __set_state)
}
/* }}} */
/* {{{ */
PHP_METHOD(DateTime, __serialize)
{
zval *object = ZEND_THIS;
php_date_obj *dateobj;
HashTable *myht;
ZEND_PARSE_PARAMETERS_NONE();
dateobj = Z_PHPDATE_P(object);
DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
array_init(return_value);
myht = Z_ARRVAL_P(return_value);
date_object_to_hash(dateobj, myht);
}
/* }}} */
/* {{{ */
PHP_METHOD(DateTimeImmutable, __serialize)
{
zval *object = ZEND_THIS;
php_date_obj *dateobj;
HashTable *myht;
ZEND_PARSE_PARAMETERS_NONE();
dateobj = Z_PHPDATE_P(object);
DATE_CHECK_INITIALIZED(dateobj->time, DateTimeImmutable);
array_init(return_value);
myht = Z_ARRVAL_P(return_value);
date_object_to_hash(dateobj, myht);
}
/* }}} */
/* {{{ */
PHP_METHOD(DateTime, __unserialize)
{
zval *object = ZEND_THIS;
php_date_obj *dateobj;
zval *array;
HashTable *myht;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY(array)
ZEND_PARSE_PARAMETERS_END();
dateobj = Z_PHPDATE_P(object);
myht = Z_ARRVAL_P(array);
if (!php_date_initialize_from_hash(&dateobj, myht)) {
zend_throw_error(NULL, "Invalid serialization data for DateTime object");
}
}
/* }}} */
/* {{{ */
PHP_METHOD(DateTimeImmutable, __unserialize)
{
zval *object = ZEND_THIS;
php_date_obj *dateobj;
zval *array;
HashTable *myht;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY(array)
ZEND_PARSE_PARAMETERS_END();
dateobj = Z_PHPDATE_P(object);
myht = Z_ARRVAL_P(array);
if (!php_date_initialize_from_hash(&dateobj, myht)) {
zend_throw_error(NULL, "Invalid serialization data for DateTimeImmutable object");
}
}
/* }}} */
/* {{{ */
PHP_METHOD(DateTime, __wakeup)
{

View file

@ -205,12 +205,20 @@ interface DateTimeInterface
/** @tentative-return-type */
public function __wakeup(): void;
public function __serialize(): array;
public function __unserialize(array $data): void;
}
class DateTime implements DateTimeInterface
{
public function __construct(string $datetime = "now", ?DateTimeZone $timezone = null) {}
public function __serialize(): array {}
public function __unserialize(array $data): void {}
/** @tentative-return-type */
public function __wakeup(): void {}
@ -318,6 +326,10 @@ class DateTimeImmutable implements DateTimeInterface
{
public function __construct(string $datetime = "now", ?DateTimeZone $timezone = null) {}
public function __serialize(): array {}
public function __unserialize(array $data): void {}
/** @tentative-return-type */
public function __wakeup(): void {}

View file

@ -245,11 +245,21 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeInterface___wakeup, 0, 0, IS_VOID, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_DateTimeInterface___serialize arginfo_timezone_abbreviations_list
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DateTimeInterface___unserialize, 0, 1, IS_VOID, 0)
ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DateTime___construct, 0, 0, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, datetime, IS_STRING, 0, "\"now\"")
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null")
ZEND_END_ARG_INFO()
#define arginfo_class_DateTime___serialize arginfo_timezone_abbreviations_list
#define arginfo_class_DateTime___unserialize arginfo_class_DateTimeInterface___unserialize
#define arginfo_class_DateTime___wakeup arginfo_class_DateTimeInterface___wakeup
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTime___set_state, 0, 1, DateTime, 0)
@ -322,6 +332,10 @@ ZEND_END_ARG_INFO()
#define arginfo_class_DateTimeImmutable___construct arginfo_class_DateTime___construct
#define arginfo_class_DateTimeImmutable___serialize arginfo_timezone_abbreviations_list
#define arginfo_class_DateTimeImmutable___unserialize arginfo_class_DateTimeInterface___unserialize
#define arginfo_class_DateTimeImmutable___wakeup arginfo_class_DateTimeInterface___wakeup
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_DateTimeImmutable___set_state, 0, 1, DateTimeImmutable, 0)
@ -517,11 +531,15 @@ ZEND_FUNCTION(date_sunrise);
ZEND_FUNCTION(date_sunset);
ZEND_FUNCTION(date_sun_info);
ZEND_METHOD(DateTime, __construct);
ZEND_METHOD(DateTime, __serialize);
ZEND_METHOD(DateTime, __unserialize);
ZEND_METHOD(DateTime, __wakeup);
ZEND_METHOD(DateTime, __set_state);
ZEND_METHOD(DateTime, createFromImmutable);
ZEND_METHOD(DateTime, createFromInterface);
ZEND_METHOD(DateTimeImmutable, __construct);
ZEND_METHOD(DateTimeImmutable, __serialize);
ZEND_METHOD(DateTimeImmutable, __unserialize);
ZEND_METHOD(DateTimeImmutable, __wakeup);
ZEND_METHOD(DateTimeImmutable, __set_state);
ZEND_METHOD(DateTimeImmutable, modify);
@ -610,12 +628,16 @@ static const zend_function_entry class_DateTimeInterface_methods[] = {
ZEND_ABSTRACT_ME_WITH_FLAGS(DateTimeInterface, getTimestamp, arginfo_class_DateTimeInterface_getTimestamp, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT)
ZEND_ABSTRACT_ME_WITH_FLAGS(DateTimeInterface, diff, arginfo_class_DateTimeInterface_diff, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT)
ZEND_ABSTRACT_ME_WITH_FLAGS(DateTimeInterface, __wakeup, arginfo_class_DateTimeInterface___wakeup, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT)
ZEND_ABSTRACT_ME_WITH_FLAGS(DateTimeInterface, __serialize, arginfo_class_DateTimeInterface___serialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT)
ZEND_ABSTRACT_ME_WITH_FLAGS(DateTimeInterface, __unserialize, arginfo_class_DateTimeInterface___unserialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT)
ZEND_FE_END
};
static const zend_function_entry class_DateTime_methods[] = {
ZEND_ME(DateTime, __construct, arginfo_class_DateTime___construct, ZEND_ACC_PUBLIC)
ZEND_ME(DateTime, __serialize, arginfo_class_DateTime___serialize, ZEND_ACC_PUBLIC)
ZEND_ME(DateTime, __unserialize, arginfo_class_DateTime___unserialize, ZEND_ACC_PUBLIC)
ZEND_ME(DateTime, __wakeup, arginfo_class_DateTime___wakeup, ZEND_ACC_PUBLIC)
ZEND_ME(DateTime, __set_state, arginfo_class_DateTime___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(DateTime, createFromImmutable, arginfo_class_DateTime_createFromImmutable, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
@ -641,6 +663,8 @@ static const zend_function_entry class_DateTime_methods[] = {
static const zend_function_entry class_DateTimeImmutable_methods[] = {
ZEND_ME(DateTimeImmutable, __construct, arginfo_class_DateTimeImmutable___construct, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, __serialize, arginfo_class_DateTimeImmutable___serialize, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, __unserialize, arginfo_class_DateTimeImmutable___unserialize, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, __wakeup, arginfo_class_DateTimeImmutable___wakeup, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, __set_state, arginfo_class_DateTimeImmutable___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME_MAPPING(createFromFormat, date_create_immutable_from_format, arginfo_class_DateTimeImmutable_createFromFormat, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)

View file

@ -0,0 +1,118 @@
--TEST--
Test DateTimeImmutable::__serialize and DateTimeImmutable::__unserialize
--FILE--
<?php
//Set the default time zone
date_default_timezone_set("Europe/London");
$d = new DateTimeImmutable("2022-04-14 11:27:42");
echo "Original object:\n";
var_dump($d);
echo "\n\nSerialised object:\n";
$s = serialize($d);
var_dump($s);
echo "\n\nUnserialised object:\n";
$e = unserialize($s);
var_dump($e);
echo "\n\nCalling __serialize manually:\n";
var_dump($d->__serialize());
echo "\n\nCalling __unserialize manually:\n";
$d = new DateTimeImmutable;
$d->__unserialize(
[
'date' => '2022-04-14 11:27:42.541106',
'timezone_type' => 3,
'timezone' => 'UTC',
]
);
var_dump($d);
echo "\n\nCalling __unserialize a few more times, with abbreviations:\n";
$d->__unserialize(
[
'date' => '2022-04-14 11:27:42.541106',
'timezone_type' => 2,
'timezone' => 'CEST',
]
);
var_dump($d);
$d->__unserialize(
[
'date' => '2022-04-14 11:27:42.541106',
'timezone_type' => 1,
'timezone' => '+0130',
]
);
var_dump($d);
?>
--EXPECTF--
Original object:
object(DateTimeImmutable)#%d (%d) {
["date"]=>
string(26) "2022-04-14 11:27:42.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/London"
}
Serialised object:
string(135) "O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2022-04-14 11:27:42.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/London";}"
Unserialised object:
object(DateTimeImmutable)#%d (%d) {
["date"]=>
string(26) "2022-04-14 11:27:42.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/London"
}
Calling __serialize manually:
array(3) {
["date"]=>
string(26) "2022-04-14 11:27:42.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/London"
}
Calling __unserialize manually:
object(DateTimeImmutable)#%d (%d) {
["date"]=>
string(26) "2022-04-14 11:27:42.541106"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}
Calling __unserialize a few more times, with abbreviations:
object(DateTimeImmutable)#%d (%d) {
["date"]=>
string(26) "2022-04-14 11:27:42.541106"
["timezone_type"]=>
int(2)
["timezone"]=>
string(4) "CEST"
}
object(DateTimeImmutable)#%d (%d) {
["date"]=>
string(26) "2022-04-14 11:27:42.541106"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+01:30"
}

View file

@ -0,0 +1,118 @@
--TEST--
Test DateTime::__serialize and DateTime::__unserialize
--FILE--
<?php
//Set the default time zone
date_default_timezone_set("Europe/London");
$d = new DateTime("2022-04-14 11:27:42");
echo "Original object:\n";
var_dump($d);
echo "\n\nSerialised object:\n";
$s = serialize($d);
var_dump($s);
echo "\n\nUnserialised object:\n";
$e = unserialize($s);
var_dump($e);
echo "\n\nCalling __serialize manually:\n";
var_dump($d->__serialize());
echo "\n\nCalling __unserialize manually:\n";
$d = new DateTime;
$d->__unserialize(
[
'date' => '2022-04-14 11:27:42.541106',
'timezone_type' => 3,
'timezone' => 'UTC',
]
);
var_dump($d);
echo "\n\nCalling __unserialize a few more times, with abbreviations:\n";
$d->__unserialize(
[
'date' => '2022-04-14 11:27:42.541106',
'timezone_type' => 2,
'timezone' => 'CEST',
]
);
var_dump($d);
$d->__unserialize(
[
'date' => '2022-04-14 11:27:42.541106',
'timezone_type' => 1,
'timezone' => '+0130',
]
);
var_dump($d);
?>
--EXPECTF--
Original object:
object(DateTime)#%d (%d) {
["date"]=>
string(26) "2022-04-14 11:27:42.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/London"
}
Serialised object:
string(125) "O:8:"DateTime":3:{s:4:"date";s:26:"2022-04-14 11:27:42.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/London";}"
Unserialised object:
object(DateTime)#%d (%d) {
["date"]=>
string(26) "2022-04-14 11:27:42.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/London"
}
Calling __serialize manually:
array(3) {
["date"]=>
string(26) "2022-04-14 11:27:42.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/London"
}
Calling __unserialize manually:
object(DateTime)#%d (%d) {
["date"]=>
string(26) "2022-04-14 11:27:42.541106"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}
Calling __unserialize a few more times, with abbreviations:
object(DateTime)#%d (%d) {
["date"]=>
string(26) "2022-04-14 11:27:42.541106"
["timezone_type"]=>
int(2)
["timezone"]=>
string(4) "CEST"
}
object(DateTime)#%d (%d) {
["date"]=>
string(26) "2022-04-14 11:27:42.541106"
["timezone_type"]=>
int(1)
["timezone"]=>
string(6) "+01:30"
}

View file

@ -8,7 +8,7 @@ var_dump(unserialize('O:8:"DateTime":0:{}') == new DateTime);
--EXPECTF--
Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug55397.php:%d
Stack trace:
#0 [internal function]: DateTime->__wakeup()
#0 [internal function]: DateTime->__unserialize(Array)
#1 %sbug55397.php(%d): unserialize('O:8:"DateTime":...')
#2 {main}
thrown in %sbug55397.php on line %d

View file

@ -13,7 +13,7 @@ try {
--EXPECTF--
Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug62852.php:%d
Stack trace:
#0 [internal function]: DateTime->__wakeup()
#0 [internal function]: DateTime->__unserialize(Array)
#1 %sbug62852.php(%d): unserialize('O:8:"DateTime":...')
#2 {main}
thrown in %sbug62852.php on line %d

View file

@ -24,8 +24,7 @@ var_dump( $foo );
--EXPECTF--
Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug62852_var2.php:%d
Stack trace:
#0 %sbug62852_var2.php(%d): DateTime->__wakeup()
#1 [internal function]: Foo->__wakeup()
#2 %sbug62852_var2.php(%d): unserialize('O:3:"Foo":3:{s:...')
#3 {main}
#0 [internal function]: DateTime->__unserialize(Array)
#1 %sbug62852_var2.php(%d): unserialize('O:3:"Foo":3:{s:...')
#2 {main}
thrown in %sbug62852_var2.php on line %d

View file

@ -24,8 +24,7 @@ var_dump( $foo );
--EXPECTF--
Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug62852_var3.php:%d
Stack trace:
#0 %sbug62852_var3.php(%d): DateTime->__wakeup()
#1 [internal function]: Foo->__wakeup()
#2 %sbug62852_var3.php(%d): unserialize('O:3:"Foo":3:{s:...')
#3 {main}
#0 [internal function]: DateTime->__unserialize(Array)
#1 %sbug62852_var3.php(%d): unserialize('O:3:"Foo":3:{s:...')
#2 {main}
thrown in %sbug62852_var3.php on line %d

View file

@ -1,5 +1,5 @@
--TEST--
Test for bug #66721: __wakeup of DateTime segfaults when invalid object data is supplied
Test for bug #66721: __unserialize of DateTime segfaults when invalid object data is supplied
--CREDITS--
Boro Sitnikovski <buritomath@yahoo.com>
--FILE--
@ -10,7 +10,7 @@ var_dump(unserialize($y));
--EXPECTF--
Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug66721.php:%d
Stack trace:
#0 [internal function]: DateTime->__wakeup()
#0 [internal function]: DateTime->__unserialize(Array)
#1 %sbug66721.php(%d): unserialize('O:8:"DateTime":...')
#2 {main}
thrown in %sbug66721.php on line %d

View file

@ -8,7 +8,7 @@ var_dump($data);
--EXPECTF--
Fatal error: Uncaught Error: Invalid serialization data for DateTime object in %sbug68942_2.php:%d
Stack trace:
#0 [internal function]: DateTime->__wakeup()
#0 [internal function]: DateTime->__unserialize(Array)
#1 %sbug68942_2.php(%d): unserialize('a:2:{i:0;O:8:"D...')
#2 {main}
thrown in %sbug68942_2.php on line %d