mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2: Fix GH-8996: DOMNode serialization on PHP ^8.1 Fix GH-12380: JIT+private array property access inside closure accesses private property in child class
This commit is contained in:
commit
58a1103bee
9 changed files with 270 additions and 11 deletions
3
NEWS
3
NEWS
|
@ -17,6 +17,7 @@ PHP NEWS
|
|||
. Restore old namespace reconciliation behaviour. (nielsdos)
|
||||
. Fix broken cache invalidation with deallocated and reallocated document
|
||||
node. (nielsdos)
|
||||
. Fixed bug GH-8996 (DOMNode serialization on PHP ^8.1). (nielsdos)
|
||||
|
||||
- Fileinfo:
|
||||
. Fixed bug GH-11891 (fileinfo returns text/xml for some svg files). (usarise)
|
||||
|
@ -31,6 +32,8 @@ PHP NEWS
|
|||
|
||||
- Opcache:
|
||||
. Fixed opcache_invalidate() on deleted file. (mikhainin)
|
||||
. Fixed bug GH-12380 (JIT+private array property access inside closure
|
||||
accesses private property in child class). (nielsdos)
|
||||
|
||||
- SimpleXML:
|
||||
. Apply iterator fixes only on master. (nielsdos)
|
||||
|
|
|
@ -2030,4 +2030,25 @@ PHP_METHOD(DOMNode, getRootNode)
|
|||
}
|
||||
/* }}} */
|
||||
|
||||
/**
|
||||
* We want to block the serialization and unserialization of DOM classes.
|
||||
* However, using @not-serializable makes the child classes also not serializable, even if the user implements the methods.
|
||||
* So instead, we implement the methods wherein we throw exceptions.
|
||||
* The reason we choose these methods is because:
|
||||
* - If the user implements __serialize / __unserialize, the respective throwing methods are not called.
|
||||
* - If the user implements __sleep / __wakeup, then it's also not a problem because they will not enter the throwing methods.
|
||||
*/
|
||||
|
||||
PHP_METHOD(DOMNode, __sleep)
|
||||
{
|
||||
zend_throw_exception_ex(NULL, 0, "Serialization of '%s' is not allowed, unless serialization methods are implemented in a subclass", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
PHP_METHOD(DOMNode, __wakeup)
|
||||
{
|
||||
zend_throw_exception_ex(NULL, 0, "Unserialization of '%s' is not allowed, unless unserialization methods are implemented in a subclass", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -294,7 +294,6 @@ interface DOMChildNode
|
|||
public function replaceWith(...$nodes): void;
|
||||
}
|
||||
|
||||
/** @not-serializable */
|
||||
class DOMNode
|
||||
{
|
||||
/** @readonly */
|
||||
|
@ -348,6 +347,10 @@ class DOMNode
|
|||
|
||||
public string $textContent;
|
||||
|
||||
public function __sleep(): array {}
|
||||
|
||||
public function __wakeup(): void {}
|
||||
|
||||
/** @return DOMNode|false */
|
||||
public function appendChild(DOMNode $node) {}
|
||||
|
||||
|
@ -406,7 +409,6 @@ class DOMNode
|
|||
public function getRootNode(?array $options = null): DOMNode {}
|
||||
}
|
||||
|
||||
/** @not-serializable */
|
||||
class DOMNameSpaceNode
|
||||
{
|
||||
/** @readonly */
|
||||
|
@ -438,6 +440,12 @@ class DOMNameSpaceNode
|
|||
|
||||
/** @readonly */
|
||||
public ?DOMElement $parentElement;
|
||||
|
||||
/** @implementation-alias DOMNode::__sleep */
|
||||
public function __sleep(): array {}
|
||||
|
||||
/** @implementation-alias DOMNode::__wakeup */
|
||||
public function __wakeup(): void {}
|
||||
}
|
||||
|
||||
class DOMImplementation
|
||||
|
|
22
ext/dom/php_dom_arginfo.h
generated
22
ext/dom/php_dom_arginfo.h
generated
|
@ -1,5 +1,5 @@
|
|||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: ebe9bcbd185e1973b5447beb306bd9d93051f415 */
|
||||
* Stub hash: a20d21c1796ebb43028856f0ec2d53dcaded6cc0 */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_dom_import_simplexml, 0, 1, DOMElement, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0)
|
||||
|
@ -30,6 +30,11 @@ ZEND_END_ARG_INFO()
|
|||
|
||||
#define arginfo_class_DOMChildNode_replaceWith arginfo_class_DOMParentNode_append
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOMNode___sleep, 0, 0, IS_ARRAY, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_DOMNode___wakeup arginfo_class_DOMChildNode_remove
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DOMNode_appendChild, 0, 0, 1)
|
||||
ZEND_ARG_OBJ_INFO(0, node, DOMNode, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
@ -114,6 +119,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOMNode_getRootNode, 0, 0,
|
|||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_DOMNameSpaceNode___sleep arginfo_class_DOMNode___sleep
|
||||
|
||||
#define arginfo_class_DOMNameSpaceNode___wakeup arginfo_class_DOMChildNode_remove
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMImplementation_getFeature, 0, 2, IS_NEVER, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, feature, IS_STRING, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, version, IS_STRING, 0)
|
||||
|
@ -205,8 +214,7 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMElement_getAt
|
|||
ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOMElement_getAttributeNames, 0, 0, IS_ARRAY, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
#define arginfo_class_DOMElement_getAttributeNames arginfo_class_DOMNode___sleep
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMElement_getAttributeNS, 0, 2, IS_STRING, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, namespace, IS_STRING, 1)
|
||||
|
@ -533,6 +541,8 @@ ZEND_END_ARG_INFO()
|
|||
ZEND_FUNCTION(dom_import_simplexml);
|
||||
ZEND_METHOD(DOMCdataSection, __construct);
|
||||
ZEND_METHOD(DOMComment, __construct);
|
||||
ZEND_METHOD(DOMNode, __sleep);
|
||||
ZEND_METHOD(DOMNode, __wakeup);
|
||||
ZEND_METHOD(DOMNode, appendChild);
|
||||
ZEND_METHOD(DOMNode, C14N);
|
||||
ZEND_METHOD(DOMNode, C14NFile);
|
||||
|
@ -725,6 +735,8 @@ static const zend_function_entry class_DOMChildNode_methods[] = {
|
|||
|
||||
|
||||
static const zend_function_entry class_DOMNode_methods[] = {
|
||||
ZEND_ME(DOMNode, __sleep, arginfo_class_DOMNode___sleep, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(DOMNode, __wakeup, arginfo_class_DOMNode___wakeup, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(DOMNode, appendChild, arginfo_class_DOMNode_appendChild, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(DOMNode, C14N, arginfo_class_DOMNode_C14N, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(DOMNode, C14NFile, arginfo_class_DOMNode_C14NFile, ZEND_ACC_PUBLIC)
|
||||
|
@ -750,6 +762,8 @@ static const zend_function_entry class_DOMNode_methods[] = {
|
|||
|
||||
|
||||
static const zend_function_entry class_DOMNameSpaceNode_methods[] = {
|
||||
ZEND_MALIAS(DOMNode, __sleep, __sleep, arginfo_class_DOMNameSpaceNode___sleep, ZEND_ACC_PUBLIC)
|
||||
ZEND_MALIAS(DOMNode, __wakeup, __wakeup, arginfo_class_DOMNameSpaceNode___wakeup, ZEND_ACC_PUBLIC)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
|
@ -1096,7 +1110,6 @@ static zend_class_entry *register_class_DOMNode(void)
|
|||
|
||||
INIT_CLASS_ENTRY(ce, "DOMNode", class_DOMNode_methods);
|
||||
class_entry = zend_register_internal_class_ex(&ce, NULL);
|
||||
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
|
||||
|
||||
zval property_nodeName_default_value;
|
||||
ZVAL_UNDEF(&property_nodeName_default_value);
|
||||
|
@ -1224,7 +1237,6 @@ static zend_class_entry *register_class_DOMNameSpaceNode(void)
|
|||
|
||||
INIT_CLASS_ENTRY(ce, "DOMNameSpaceNode", class_DOMNameSpaceNode_methods);
|
||||
class_entry = zend_register_internal_class_ex(&ce, NULL);
|
||||
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
|
||||
|
||||
zval property_nodeName_default_value;
|
||||
ZVAL_UNDEF(&property_nodeName_default_value);
|
||||
|
|
120
ext/dom/tests/gh8996.phpt
Normal file
120
ext/dom/tests/gh8996.phpt
Normal file
|
@ -0,0 +1,120 @@
|
|||
--TEST--
|
||||
GH-8996: DOMNode serialization on PHP ^8.1
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
echo "=== __sleep and __wakeup ===\n";
|
||||
|
||||
class SerializableDomDocumentSleepWakeup extends DOMDocument
|
||||
{
|
||||
private $xmlData;
|
||||
|
||||
public function __sleep(): array
|
||||
{
|
||||
$this->xmlData = $this->saveXML();
|
||||
return ['xmlData'];
|
||||
}
|
||||
|
||||
public function __wakeup(): void
|
||||
{
|
||||
$this->loadXML($this->xmlData);
|
||||
}
|
||||
}
|
||||
|
||||
$dom = new SerializableDomDocumentSleepWakeup('1.0', 'UTF-8');
|
||||
$dom->loadXML('<tag>value</tag>');
|
||||
|
||||
$serialized = serialize($dom);
|
||||
var_dump($serialized);
|
||||
$unserialized = unserialize($serialized);
|
||||
|
||||
echo "Serialized:\n-----------\n$serialized\n-----------\nRestored:\n-----------\n{$unserialized->saveXml()}";
|
||||
|
||||
echo "=== __serialize and __unserialize ===\n";
|
||||
|
||||
class SerializableDomDocument__Serialize__Unserialize extends DOMDocument
|
||||
{
|
||||
public function __serialize(): array
|
||||
{
|
||||
return ['xmlData' => $this->saveXML()];
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
$this->loadXML($data['xmlData']);
|
||||
}
|
||||
}
|
||||
|
||||
$dom = new SerializableDomDocument__Serialize__Unserialize('1.0', 'UTF-8');
|
||||
$dom->loadXML('<tag>value</tag>');
|
||||
|
||||
$serialized = serialize($dom);
|
||||
$unserialized = unserialize($serialized);
|
||||
|
||||
echo "Serialized:\n-----------\n$serialized\n-----------\nRestored:\n-----------\n{$unserialized->saveXml()}";
|
||||
|
||||
echo "=== serialize and unserialize ===\n";
|
||||
|
||||
class SerializableDomDocumentSerializeUnserialize extends DOMDocument implements Serializable
|
||||
{
|
||||
public function serialize(): ?string
|
||||
{
|
||||
return $this->saveXML();
|
||||
}
|
||||
|
||||
public function unserialize(string $data): void
|
||||
{
|
||||
$this->loadXML($data);
|
||||
}
|
||||
}
|
||||
|
||||
$dom = new SerializableDomDocumentSerializeUnserialize('1.0', 'UTF-8');
|
||||
$dom->loadXML('<tag>value</tag>');
|
||||
|
||||
$serialized = serialize($dom);
|
||||
$unserialized = unserialize($serialized);
|
||||
|
||||
echo "Serialized:\n-----------\n$serialized\n-----------\nRestored:\n-----------\n{$unserialized->saveXml()}";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
=== __sleep and __wakeup ===
|
||||
string(144) "O:34:"SerializableDomDocumentSleepWakeup":1:{s:43:"%0SerializableDomDocumentSleepWakeup%0xmlData";s:39:"<?xml version="1.0"?>
|
||||
<tag>value</tag>
|
||||
";}"
|
||||
Serialized:
|
||||
-----------
|
||||
O:34:"SerializableDomDocumentSleepWakeup":1:{s:43:"%0SerializableDomDocumentSleepWakeup%0xmlData";s:39:"<?xml version="1.0"?>
|
||||
<tag>value</tag>
|
||||
";}
|
||||
-----------
|
||||
Restored:
|
||||
-----------
|
||||
<?xml version="1.0"?>
|
||||
<tag>value</tag>
|
||||
=== __serialize and __unserialize ===
|
||||
Serialized:
|
||||
-----------
|
||||
O:47:"SerializableDomDocument__Serialize__Unserialize":1:{s:7:"xmlData";s:39:"<?xml version="1.0"?>
|
||||
<tag>value</tag>
|
||||
";}
|
||||
-----------
|
||||
Restored:
|
||||
-----------
|
||||
<?xml version="1.0"?>
|
||||
<tag>value</tag>
|
||||
=== serialize and unserialize ===
|
||||
|
||||
Deprecated: SerializableDomDocumentSerializeUnserialize implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in %s on line %d
|
||||
Serialized:
|
||||
-----------
|
||||
C:43:"SerializableDomDocumentSerializeUnserialize":39:{<?xml version="1.0"?>
|
||||
<tag>value</tag>
|
||||
}
|
||||
-----------
|
||||
Restored:
|
||||
-----------
|
||||
<?xml version="1.0"?>
|
||||
<tag>value</tag>
|
|
@ -36,7 +36,7 @@ try {
|
|||
|
||||
?>
|
||||
--EXPECT--
|
||||
Serialization of 'DOMDocument' is not allowed
|
||||
Serialization of 'DOMElement' is not allowed
|
||||
Serialization of 'DOMDocument' is not allowed, unless serialization methods are implemented in a subclass
|
||||
Serialization of 'DOMElement' is not allowed, unless serialization methods are implemented in a subclass
|
||||
Serialization of 'DOMXPath' is not allowed
|
||||
Serialization of 'DOMNameSpaceNode' is not allowed
|
||||
Serialization of 'DOMNameSpaceNode' is not allowed, unless serialization methods are implemented in a subclass
|
||||
|
|
29
ext/dom/tests/not_unserializable.phpt
Normal file
29
ext/dom/tests/not_unserializable.phpt
Normal file
|
@ -0,0 +1,29 @@
|
|||
--TEST--
|
||||
DOM classes are not unserializable
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$classes = [
|
||||
"DOMXPath",
|
||||
"DOMDocument",
|
||||
"DOMNode",
|
||||
"DOMNameSpaceNode",
|
||||
];
|
||||
|
||||
foreach ($classes as $class)
|
||||
{
|
||||
try {
|
||||
unserialize('O:' . strlen($class) . ':"' . $class . '":0:{}');
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Unserialization of 'DOMXPath' is not allowed
|
||||
Unserialization of 'DOMDocument' is not allowed, unless unserialization methods are implemented in a subclass
|
||||
Unserialization of 'DOMNode' is not allowed, unless unserialization methods are implemented in a subclass
|
||||
Unserialization of 'DOMNameSpaceNode' is not allowed, unless unserialization methods are implemented in a subclass
|
|
@ -682,7 +682,11 @@ static zend_property_info* zend_get_known_property_info(const zend_op_array *op_
|
|||
return info;
|
||||
} else if (on_this) {
|
||||
if (ce == info->ce) {
|
||||
return info;
|
||||
if (ce == op_array->scope) {
|
||||
return info;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
} else if ((info->flags & ZEND_ACC_PROTECTED)
|
||||
&& instanceof_function_slow(ce, info->ce)) {
|
||||
return info;
|
||||
|
|
62
ext/opcache/tests/jit/gh12380.phpt
Normal file
62
ext/opcache/tests/jit/gh12380.phpt
Normal file
|
@ -0,0 +1,62 @@
|
|||
--TEST--
|
||||
GH-12380: JIT+private array property access inside closure accesses private property in child class
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.file_update_protection=0
|
||||
opcache.jit_buffer_size=1M
|
||||
opcache.protect_memory=1
|
||||
opcache.jit=tracing
|
||||
opcache.jit_hot_loop=1
|
||||
opcache.jit_hot_func=1
|
||||
opcache.jit_hot_return=1
|
||||
opcache.jit_hot_side_exit=1
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
abstract class a
|
||||
{
|
||||
private int $v = 1;
|
||||
|
||||
public function test(): void
|
||||
{
|
||||
var_dump($this->v);
|
||||
(function (): void {
|
||||
var_dump($this->v);
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
final class b extends a {
|
||||
private int $v = 0;
|
||||
}
|
||||
$a = new b;
|
||||
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$a->test();
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
||||
int(1)
|
Loading…
Add table
Add a link
Reference in a new issue