Skip to content

Commit 3af8e9d

Browse files
committed
Introduce a way to (de)serialize subclasses of un(de)serializable classes if the appropriate methods are present
This introduces the class flag ZEND_ACC_SUBCLASS_SERIALIZABLE, which allows classes which are not serializable / deserializable to become that if a subclass implements the appropriate methods. Setting this flag through the stubs can be done using @subclass-serializable. Introducing this through behaviour a new flag allows for opt-in behaviour, which is backwards compatible. Fixes GH-10201
1 parent 55514a1 commit 3af8e9d

11 files changed

+65
-18
lines changed

Zend/zend_compile.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ typedef struct _zend_oparray_context {
237237
/* or IS_CONSTANT_VISITED_MARK | | | */
238238
#define ZEND_CLASS_CONST_IS_CASE (1 << 6) /* | | | X */
239239
/* | | | */
240-
/* Class Flags (unused: 15,21,30,31) | | | */
240+
/* Class Flags (unused: 15,21,31) | | | */
241241
/* =========== | | | */
242242
/* | | | */
243243
/* Special class types | | | */
@@ -297,6 +297,11 @@ typedef struct _zend_oparray_context {
297297
/* Class cannot be serialized or unserialized | | | */
298298
#define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */
299299
/* | | | */
300+
/* Subclass can be serialized or unserialized even if | | | */
301+
/* the parent cannot be, on the condition that the | | | */
302+
/* subclass implements the appropriate methods. | | | */
303+
#define ZEND_ACC_SUBCLASS_SERIALIZABLE (1 << 30) /* X | | | */
304+
/* | | | */
300305
/* Function Flags (unused: 27-30) | | | */
301306
/* ============== | | | */
302307
/* | | | */

Zend/zend_inheritance.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1594,7 +1594,15 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
15941594
ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
15951595
}
15961596
}
1597-
ce->ce_flags |= parent_ce->ce_flags & (ZEND_HAS_STATIC_IN_METHODS | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_USE_GUARDS | ZEND_ACC_NOT_SERIALIZABLE);
1597+
ce->ce_flags |= parent_ce->ce_flags & (ZEND_HAS_STATIC_IN_METHODS | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_USE_GUARDS | ZEND_ACC_NOT_SERIALIZABLE | ZEND_ACC_SUBCLASS_SERIALIZABLE);
1598+
1599+
if ((parent_ce->ce_flags & (ZEND_ACC_NOT_SERIALIZABLE | ZEND_ACC_SUBCLASS_SERIALIZABLE)) == (ZEND_ACC_NOT_SERIALIZABLE | ZEND_ACC_SUBCLASS_SERIALIZABLE)) {
1600+
if (ce->__serialize || ce->__unserialize
1601+
|| zend_hash_find_known_hash(&ce->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP))
1602+
|| zend_hash_find_known_hash(&ce->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP))) {
1603+
ce->ce_flags &= ~(ZEND_ACC_NOT_SERIALIZABLE | ZEND_ACC_SUBCLASS_SERIALIZABLE);
1604+
}
1605+
}
15981606
}
15991607
/* }}} */
16001608

build/gen_stub.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,8 @@ class ClassInfo {
16461646
public $isStrictProperties;
16471647
/** @var bool */
16481648
public $isNotSerializable;
1649+
/** @var bool */
1650+
public $isSubclassSerializable;
16491651
/** @var Name[] */
16501652
public $extends;
16511653
/** @var Name[] */
@@ -1673,6 +1675,7 @@ public function __construct(
16731675
bool $isDeprecated,
16741676
bool $isStrictProperties,
16751677
bool $isNotSerializable,
1678+
bool $isSubclassSerializable,
16761679
array $extends,
16771680
array $implements,
16781681
array $propertyInfos,
@@ -1687,6 +1690,7 @@ public function __construct(
16871690
$this->isDeprecated = $isDeprecated;
16881691
$this->isStrictProperties = $isStrictProperties;
16891692
$this->isNotSerializable = $isNotSerializable;
1693+
$this->isSubclassSerializable = $isSubclassSerializable;
16901694
$this->extends = $extends;
16911695
$this->implements = $implements;
16921696
$this->propertyInfos = $propertyInfos;
@@ -1794,6 +1798,10 @@ private function getFlagsAsString(): string
17941798
$flags[] = "ZEND_ACC_NOT_SERIALIZABLE";
17951799
}
17961800

1801+
if ($this->isSubclassSerializable) {
1802+
$flags[] = "ZEND_ACC_SUBCLASS_SERIALIZABLE";
1803+
}
1804+
17971805
return implode("|", $flags);
17981806
}
17991807

@@ -2437,6 +2445,7 @@ function parseClass(
24372445
$isDeprecated = false;
24382446
$isStrictProperties = false;
24392447
$isNotSerializable = false;
2448+
$isSubclassSerializable = false;
24402449

24412450
if ($comment) {
24422451
$tags = parseDocComment($comment);
@@ -2449,10 +2458,16 @@ function parseClass(
24492458
$isStrictProperties = true;
24502459
} else if ($tag->name === 'not-serializable') {
24512460
$isNotSerializable = true;
2461+
} else if ($tag->name == 'subclass-serializable') {
2462+
$isSubclassSerializable = true;
24522463
}
24532464
}
24542465
}
24552466

2467+
if (!$isNotSerializable && $isSubclassSerializable) {
2468+
throw new Exception("@subclass-serializable without @not-serializable is a no-op");
2469+
}
2470+
24562471
$extends = [];
24572472
$implements = [];
24582473

@@ -2484,6 +2499,7 @@ function parseClass(
24842499
$isDeprecated,
24852500
$isStrictProperties,
24862501
$isNotSerializable,
2502+
$isSubclassSerializable,
24872503
$extends,
24882504
$implements,
24892505
$properties,

ext/curl/curl_file.stub.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
/** @generate-class-entries */
44

5-
/** @not-serializable */
5+
/**
6+
* @not-serializable
7+
* @subclass-serializable
8+
*/
69
class CURLFile
710
{
811
public string $name = "";

ext/curl/curl_file_arginfo.h

Lines changed: 2 additions & 2 deletions
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: 0d09bd2f3b0a155cef25ca343319ecf470424d71 */
2+
* Stub hash: 18acd3602d7ae2e87772a53d17fdde6cf8954dde */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_CURLFile___construct, 0, 0, 1)
55
ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
@@ -60,7 +60,7 @@ static zend_class_entry *register_class_CURLFile(void)
6060

6161
INIT_CLASS_ENTRY(ce, "CURLFile", class_CURLFile_methods);
6262
class_entry = zend_register_internal_class_ex(&ce, NULL);
63-
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
63+
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE|ZEND_ACC_SUBCLASS_SERIALIZABLE;
6464

6565
zval property_name_default_value;
6666
ZVAL_EMPTY_STRING(&property_name_default_value);

ext/dom/php_dom.stub.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ public function after(...$nodes): void;
5656
public function replaceWith(...$nodes): void;
5757
}
5858

59-
/** @not-serializable */
59+
/**
60+
* @not-serializable
61+
* @subclass-serializable
62+
*/
6063
class DOMNode
6164
{
6265
/** @readonly */
@@ -156,7 +159,10 @@ public function removeChild(DOMNode $child) {}
156159
public function replaceChild(DOMNode $node, DOMNode $child) {}
157160
}
158161

159-
/** @not-serializable */
162+
/**
163+
* @not-serializable
164+
* @subclass-serializable
165+
*/
160166
class DOMNameSpaceNode
161167
{
162168
/** @readonly */
@@ -666,7 +672,10 @@ public function __construct(string $name, string $value = "") {}
666672
}
667673

668674
#ifdef LIBXML_XPATH_ENABLED
669-
/** @not-serializable */
675+
/**
676+
* @not-serializable
677+
* @subclass-serializable
678+
*/
670679
class DOMXPath
671680
{
672681
/** @readonly */

ext/dom/php_dom_arginfo.h

Lines changed: 4 additions & 4 deletions
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: 74698bea9c5e0635cf91345e8512b9677489510c */
2+
* Stub hash: 683450c0a374ccefad061e9a9a94e750259f7ce5 */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_dom_import_simplexml, 0, 1, DOMElement, 0)
55
ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0)
@@ -989,7 +989,7 @@ static zend_class_entry *register_class_DOMNode(void)
989989

990990
INIT_CLASS_ENTRY(ce, "DOMNode", class_DOMNode_methods);
991991
class_entry = zend_register_internal_class_ex(&ce, NULL);
992-
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
992+
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE|ZEND_ACC_SUBCLASS_SERIALIZABLE;
993993

994994
zval property_nodeName_default_value;
995995
ZVAL_UNDEF(&property_nodeName_default_value);
@@ -1104,7 +1104,7 @@ static zend_class_entry *register_class_DOMNameSpaceNode(void)
11041104

11051105
INIT_CLASS_ENTRY(ce, "DOMNameSpaceNode", class_DOMNameSpaceNode_methods);
11061106
class_entry = zend_register_internal_class_ex(&ce, NULL);
1107-
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
1107+
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE|ZEND_ACC_SUBCLASS_SERIALIZABLE;
11081108

11091109
zval property_nodeName_default_value;
11101110
ZVAL_UNDEF(&property_nodeName_default_value);
@@ -1656,7 +1656,7 @@ static zend_class_entry *register_class_DOMXPath(void)
16561656

16571657
INIT_CLASS_ENTRY(ce, "DOMXPath", class_DOMXPath_methods);
16581658
class_entry = zend_register_internal_class_ex(&ce, NULL);
1659-
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
1659+
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE|ZEND_ACC_SUBCLASS_SERIALIZABLE;
16601660

16611661
zend_string *property_document_class_DOMDocument = zend_string_init("DOMDocument", sizeof("DOMDocument")-1, 1);
16621662
zval property_document_default_value;

ext/intl/formatter/formatter.stub.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
/** @generate-class-entries */
44

5-
/** @not-serializable */
5+
/**
6+
* @not-serializable
7+
* @subclass-serializable
8+
*/
69
class NumberFormatter
710
{
811
public function __construct(string $locale, int $style, ?string $pattern = null) {}

ext/intl/formatter/formatter_arginfo.h

Lines changed: 2 additions & 2 deletions
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: f76ad76b08b7ca47883659fabfcc0882a2820c43 */
2+
* Stub hash: 156855567aa4910e6954afca06158852d006c2d6 */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_NumberFormatter___construct, 0, 0, 2)
55
ZEND_ARG_TYPE_INFO(0, locale, IS_STRING, 0)
@@ -126,7 +126,7 @@ static zend_class_entry *register_class_NumberFormatter(void)
126126

127127
INIT_CLASS_ENTRY(ce, "NumberFormatter", class_NumberFormatter_methods);
128128
class_entry = zend_register_internal_class_ex(&ce, NULL);
129-
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
129+
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE|ZEND_ACC_SUBCLASS_SERIALIZABLE;
130130

131131
return class_entry;
132132
}

ext/simplexml/simplexml.stub.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ function simplexml_load_string(string $data, ?string $class_name = SimpleXMLElem
88

99
function simplexml_import_dom(SimpleXMLElement|DOMNode $node, ?string $class_name = SimpleXMLElement::class): ?SimpleXMLElement {}
1010

11-
/** @not-serializable */
11+
/**
12+
* @not-serializable
13+
* @subclass-serializable
14+
*/
1215
class SimpleXMLElement implements Stringable, Countable, RecursiveIterator
1316
{
1417
/** @tentative-return-type */

ext/simplexml/simplexml_arginfo.h

Lines changed: 2 additions & 2 deletions
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: 06c88dc2fb5582a6d21c11aee6ac0a0538e70cbc */
2+
* Stub hash: 373d138420453f0b7a7e6fdd711b869229e97c5a */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_simplexml_load_file, 0, 1, SimpleXMLElement, MAY_BE_FALSE)
55
ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
@@ -170,7 +170,7 @@ static zend_class_entry *register_class_SimpleXMLElement(zend_class_entry *class
170170

171171
INIT_CLASS_ENTRY(ce, "SimpleXMLElement", class_SimpleXMLElement_methods);
172172
class_entry = zend_register_internal_class_ex(&ce, NULL);
173-
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
173+
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE|ZEND_ACC_SUBCLASS_SERIALIZABLE;
174174
zend_class_implements(class_entry, 3, class_entry_Stringable, class_entry_Countable, class_entry_RecursiveIterator);
175175

176176
return class_entry;

0 commit comments

Comments
 (0)