diff --git a/NEWS b/NEWS index 1634189bde10f..38d3bde8a746e 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ PHP NEWS - Core: . Added first-class callable cache to share instances for the duration of the request. (ilutov) + . It is now possible to use reference assign on WeakMap without the key + needing to be present beforehand. (nielsdos) - Intl: . Added IntlNumberRangeFormatter class to format an interval of two numbers diff --git a/UPGRADING b/UPGRADING index 51a057003b544..6873c276a0964 100644 --- a/UPGRADING +++ b/UPGRADING @@ -23,6 +23,10 @@ PHP 8.6 UPGRADE NOTES 2. New Features ======================================== +- Core: + . It is now possible to use reference assign on WeakMap without the key + needing to be present beforehand. + - Intl: . Added IntlNumberRangeFormatter class to format an interval of two numbers with a given skeleton, locale, IntlNumberRangeFormatter::COLLAPSE_AUTO, IntlNumberRangeFormatter::COLLAPSE_NONE, IntlNumberRangeFormatter::COLLAPSE_UNIT, IntlNumberRangeFormatter::COLLAPSE_ALL collapse and IntlNumberRangeFormatter::IDENTITY_FALLBACK_SINGLE_VALUE, IntlNumberRangeFormatter::IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE, IntlNumberRangeFormatter::IDENTITY_FALLBACK_APPROXIMATELY and diff --git a/Zend/tests/lazy_objects/gh20085.phpt b/Zend/tests/lazy_objects/gh20085.phpt new file mode 100644 index 0000000000000..5624f71ea8b47 --- /dev/null +++ b/Zend/tests/lazy_objects/gh20085.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-20085 (Assertion failure when combining lazy object get_properties exception with foreach loop) +--FILE-- +a = 1; + } +} +$obj = new C; +$reflector = new ReflectionClass(C::class); +foreach ($obj as &$value) { + $obj = $reflector->newLazyGhost(function ($obj) { + throw new Error; + }); +} +echo !obj; +?> +--EXPECTF-- +Fatal error: Uncaught Error in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/weakrefs/weakmap_by_ref_dimension_assign.phpt b/Zend/tests/weakrefs/weakmap_by_ref_dimension_assign.phpt new file mode 100644 index 0000000000000..6444de7eea6ba --- /dev/null +++ b/Zend/tests/weakrefs/weakmap_by_ref_dimension_assign.phpt @@ -0,0 +1,13 @@ +--TEST-- +By-ref assign of WeakMap dimension +--FILE-- + +--EXPECT-- +int(1) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 68449b9ef0509..d71c06c0a1354 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7307,7 +7307,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ - ZEND_VM_C_GOTO(fe_fetch_w_exit); + ZEND_VM_C_GOTO(fe_fetch_w_exit_exc); } pos++; value = &p->val; @@ -7403,6 +7403,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) } } else { zend_error(E_WARNING, "foreach() argument must be of type array|object, %s given", zend_zval_value_name(array)); +ZEND_VM_C_LABEL(fe_fetch_w_exit_exc): if (UNEXPECTED(EG(exception))) { UNDEF_RESULT(); HANDLE_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 0090bf77dd30c..50b65c864ed21 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -23631,7 +23631,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FE_FETCH_RW_S while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ - goto fe_fetch_w_exit; + goto fe_fetch_w_exit_exc; } pos++; value = &p->val; @@ -23727,6 +23727,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FE_FETCH_RW_S } } else { zend_error(E_WARNING, "foreach() argument must be of type array|object, %s given", zend_zval_value_name(array)); +fe_fetch_w_exit_exc: if (UNEXPECTED(EG(exception))) { UNDEF_RESULT(); HANDLE_EXCEPTION(); @@ -78868,7 +78869,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FE_FETCH_RW_SPEC_V while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ - goto fe_fetch_w_exit; + goto fe_fetch_w_exit_exc; } pos++; value = &p->val; @@ -78964,6 +78965,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FE_FETCH_RW_SPEC_V } } else { zend_error(E_WARNING, "foreach() argument must be of type array|object, %s given", zend_zval_value_name(array)); +fe_fetch_w_exit_exc: if (UNEXPECTED(EG(exception))) { UNDEF_RESULT(); HANDLE_EXCEPTION(); diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 518d2d8eb9ee0..83a28808811d5 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -370,18 +370,25 @@ static zval *zend_weakmap_read_dimension(zend_object *object, zval *offset, int zend_weakmap *wm = zend_weakmap_from(object); zend_object *obj_addr = Z_OBJ_P(offset); zval *zv = zend_hash_index_find(&wm->ht, zend_object_to_weakref_key(obj_addr)); - if (zv == NULL) { - if (type != BP_VAR_IS) { - zend_throw_error(NULL, - "Object %s#%d not contained in WeakMap", ZSTR_VAL(obj_addr->ce->name), obj_addr->handle); + if (type == BP_VAR_W || type == BP_VAR_RW) { + if (zv == NULL) { + zval value; + zend_weakref_register(obj_addr, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP)); + ZVAL_NULL(&value); + zv = zend_hash_index_add_new(&wm->ht, zend_object_to_weakref_key(obj_addr), &value); + } + ZVAL_MAKE_REF(zv); + } else { + if (zv == NULL) { + if (type != BP_VAR_IS) { + zend_throw_error(NULL, + "Object %s#%d not contained in WeakMap", ZSTR_VAL(obj_addr->ce->name), obj_addr->handle); + return NULL; + } return NULL; } - return NULL; } - if (type == BP_VAR_W || type == BP_VAR_RW) { - ZVAL_MAKE_REF(zv); - } return zv; } diff --git a/ext/dom/document.c b/ext/dom/document.c index 6db729535d30a..c56ec257cec31 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -2040,8 +2040,6 @@ PHP_METHOD(DOMDocument, relaxNGValidateSource) #endif -#ifdef LIBXML_HTML_ENABLED - static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */ { char *source; @@ -2234,8 +2232,6 @@ PHP_METHOD(DOMDocument, saveHTML) } /* }}} end dom_document_save_html */ -#endif /* defined(LIBXML_HTML_ENABLED) */ - /* {{{ Register extended class used to create base node type */ static void dom_document_register_node_class(INTERNAL_FUNCTION_PARAMETERS, bool modern) { diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 095d07e875ea9..d360163ab5e5f 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1369,9 +1369,7 @@ PHP_MINFO_FUNCTION(dom) php_info_print_table_row(2, "DOM/XML", "enabled"); php_info_print_table_row(2, "DOM/XML API Version", DOM_API_VERSION); php_info_print_table_row(2, "libxml Version", LIBXML_DOTTED_VERSION); -#ifdef LIBXML_HTML_ENABLED php_info_print_table_row(2, "HTML Support", "enabled"); -#endif #ifdef LIBXML_XPATH_ENABLED php_info_print_table_row(2, "XPath Support", "enabled"); #endif @@ -2709,20 +2707,10 @@ xmlChar *php_dom_libxml_fix_file_path(xmlChar *path) xmlDocPtr php_dom_create_html_doc(void) { -#ifdef LIBXML_HTML_ENABLED xmlDocPtr lxml_doc = htmlNewDocNoDtD(NULL, NULL); if (EXPECTED(lxml_doc)) { lxml_doc->dict = xmlDictCreate(); } -#else - /* If HTML support is not enabled, then htmlNewDocNoDtD() is not available. - * This code mimics the behaviour. */ - xmlDocPtr lxml_doc = xmlNewDoc((const xmlChar *) "1.0"); - if (EXPECTED(lxml_doc)) { - lxml_doc->type = XML_HTML_DOCUMENT_NODE; - lxml_doc->dict = xmlDictCreate(); - } -#endif return lxml_doc; } diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 34cc4af85c568..e44f74eadeb37 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -34,10 +34,8 @@ extern zend_module_entry dom_module_entry; #include #include #include -#ifdef LIBXML_HTML_ENABLED #include #include -#endif #ifdef LIBXML_XPATH_ENABLED #include #include diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php index 7236e80b4d247..37a1bea62b627 100644 --- a/ext/dom/php_dom.stub.php +++ b/ext/dom/php_dom.stub.php @@ -1013,7 +1013,6 @@ public function registerNodeClass(string $baseClass, ?string $extendedClass): tr /** @tentative-return-type */ public function save(string $filename, int $options = 0): int|false {} -#ifdef LIBXML_HTML_ENABLED /** @tentative-return-type */ public function loadHTML(string $source, int $options = 0): bool {} @@ -1025,7 +1024,6 @@ public function saveHTML(?DOMNode $node = null): string|false {} /** @tentative-return-type */ public function saveHTMLFile(string $filename): int|false {} -#endif /** @tentative-return-type */ public function saveXML(?DOMNode $node = null, int $options = 0): string|false {} diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h index 9b2e467d1c6a9..1f3f9fd0a8a24 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 757889c0ca89cc8e9905ba465e0621fe89b6e716 */ + * Stub hash: e3495cb89e4466d9102abb10bf6461989b7c8ba9 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_dom_import_simplexml, 0, 1, DOMAttr|DOMElement, 0) ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0) @@ -404,16 +404,9 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DOMDocument_save ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -#if defined(LIBXML_HTML_ENABLED) -ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMDocument_loadHTML, 0, 1, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, source, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0") -ZEND_END_ARG_INFO() +#define arginfo_class_DOMDocument_loadHTML arginfo_class_DOMDocument_loadXML -ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMDocument_loadHTMLFile, 0, 1, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, "0") -ZEND_END_ARG_INFO() +#define arginfo_class_DOMDocument_loadHTMLFile arginfo_class_DOMDocument_load ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DOMDocument_saveHTML, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, node, DOMNode, 1, "null") @@ -422,7 +415,6 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DOMDocument_saveHTMLFile, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DOMDocument_saveXML, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, node, DOMNode, 1, "null") @@ -1228,12 +1220,10 @@ ZEND_METHOD(DOMDocument, loadXML); ZEND_METHOD(DOMDocument, normalizeDocument); ZEND_METHOD(DOMDocument, registerNodeClass); ZEND_METHOD(DOMDocument, save); -#if defined(LIBXML_HTML_ENABLED) ZEND_METHOD(DOMDocument, loadHTML); ZEND_METHOD(DOMDocument, loadHTMLFile); ZEND_METHOD(DOMDocument, saveHTML); ZEND_METHOD(DOMDocument, saveHTMLFile); -#endif ZEND_METHOD(DOMDocument, saveXML); #if defined(LIBXML_SCHEMAS_ENABLED) ZEND_METHOD(DOMDocument, schemaValidate); @@ -1496,12 +1486,10 @@ static const zend_function_entry class_DOMDocument_methods[] = { ZEND_ME(DOMDocument, normalizeDocument, arginfo_class_DOMDocument_normalizeDocument, ZEND_ACC_PUBLIC) ZEND_ME(DOMDocument, registerNodeClass, arginfo_class_DOMDocument_registerNodeClass, ZEND_ACC_PUBLIC) ZEND_ME(DOMDocument, save, arginfo_class_DOMDocument_save, ZEND_ACC_PUBLIC) -#if defined(LIBXML_HTML_ENABLED) ZEND_ME(DOMDocument, loadHTML, arginfo_class_DOMDocument_loadHTML, ZEND_ACC_PUBLIC) ZEND_ME(DOMDocument, loadHTMLFile, arginfo_class_DOMDocument_loadHTMLFile, ZEND_ACC_PUBLIC) ZEND_ME(DOMDocument, saveHTML, arginfo_class_DOMDocument_saveHTML, ZEND_ACC_PUBLIC) ZEND_ME(DOMDocument, saveHTMLFile, arginfo_class_DOMDocument_saveHTMLFile, ZEND_ACC_PUBLIC) -#endif ZEND_ME(DOMDocument, saveXML, arginfo_class_DOMDocument_saveXML, ZEND_ACC_PUBLIC) #if defined(LIBXML_SCHEMAS_ENABLED) ZEND_ME(DOMDocument, schemaValidate, arginfo_class_DOMDocument_schemaValidate, ZEND_ACC_PUBLIC) diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c index fc3c93fc2b053..2daf98661099a 100644 --- a/ext/random/randomizer.c +++ b/ext/random/randomizer.c @@ -509,8 +509,7 @@ PHP_METHOD(Random_Randomizer, __serialize) ZEND_PARSE_PARAMETERS_NONE(); array_init(return_value); - ZVAL_ARR(&t, zend_std_get_properties(&randomizer->std)); - Z_TRY_ADDREF(t); + ZVAL_ARR(&t, zend_array_dup(zend_std_get_properties(&randomizer->std))); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &t); } /* }}} */ diff --git a/ext/random/tests/03_randomizer/methods/__serialize_indirects.phpt b/ext/random/tests/03_randomizer/methods/__serialize_indirects.phpt new file mode 100644 index 0000000000000..1207e18dd829c --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/__serialize_indirects.phpt @@ -0,0 +1,18 @@ +--TEST-- +Random: Engine: __serialize() must not expose INDIRECTs +--FILE-- +__serialize()); + +?> +--EXPECT-- +array(1) { + [0]=> + array(1) { + ["engine"]=> + object(Random\Engine\Secure)#2 (0) { + } + } +} diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index 19e86b0d80b72..254fbde7b3ff0 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -1167,7 +1167,7 @@ static zend_result spl_heap_unserialize_internal_state(HashTable *state_ht, spl_ return SUCCESS; } -PHP_METHOD(SplPriorityQueue, __serialize) +static void spl_heap_serialize_internal(INTERNAL_FUNCTION_PARAMETERS, bool is_pqueue) { spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); zval props, state; @@ -1185,14 +1185,18 @@ PHP_METHOD(SplPriorityQueue, __serialize) array_init(return_value); - ZVAL_ARR(&props, zend_std_get_properties(&intern->std)); - Z_TRY_ADDREF(props); + ZVAL_ARR(&props, zend_array_dup(zend_std_get_properties(&intern->std))); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &props); - spl_heap_serialize_internal_state(&state, intern, true); + spl_heap_serialize_internal_state(&state, intern, is_pqueue); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &state); } +PHP_METHOD(SplPriorityQueue, __serialize) +{ + spl_heap_serialize_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + PHP_METHOD(SplPriorityQueue, __unserialize) { HashTable *data; @@ -1241,28 +1245,7 @@ PHP_METHOD(SplPriorityQueue, __unserialize) PHP_METHOD(SplHeap, __serialize) { - spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); - zval props, state; - - ZEND_PARSE_PARAMETERS_NONE(); - - if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) { - RETURN_THROWS(); - } - - if (intern->heap->flags & SPL_HEAP_WRITE_LOCKED) { - zend_throw_exception(spl_ce_RuntimeException, "Cannot serialize heap while it is being modified.", 0); - RETURN_THROWS(); - } - - array_init(return_value); - - ZVAL_ARR(&props, zend_std_get_properties(&intern->std)); - Z_TRY_ADDREF(props); - zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &props); - - spl_heap_serialize_internal_state(&state, intern, false); - zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &state); + spl_heap_serialize_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } PHP_METHOD(SplHeap, __unserialize) diff --git a/ext/spl/tests/SplHeap_serialize_indexed_format.phpt b/ext/spl/tests/SplHeap_serialize_indexed_format.phpt index c201feb5716f6..21ea74c6bff9b 100644 --- a/ext/spl/tests/SplHeap_serialize_indexed_format.phpt +++ b/ext/spl/tests/SplHeap_serialize_indexed_format.phpt @@ -74,9 +74,9 @@ array(2) { [0]=> array(2) { ["flags"]=> - UNKNOWN:0 + string(13) "user_property" ["heap_elements"]=> - UNKNOWN:0 + string(13) "user_property" } [1]=> array(2) { diff --git a/ext/spl/tests/gh20101.phpt b/ext/spl/tests/gh20101.phpt new file mode 100644 index 0000000000000..ad2399d76c8e6 --- /dev/null +++ b/ext/spl/tests/gh20101.phpt @@ -0,0 +1,49 @@ +--TEST-- +GH-20101 (SplHeap/SplPriorityQueue serialization exposes INDIRECTs) +--FILE-- +__serialize(); +var_dump($data); + +class CustomPriorityQueue extends SplPriorityQueue { + public $field = 0; +} +$pqueue = new CustomPriorityQueue(); +$data = $pqueue->__serialize(); +var_dump($data); +?> +--EXPECT-- +array(2) { + [0]=> + array(1) { + ["field"]=> + int(0) + } + [1]=> + array(2) { + ["flags"]=> + int(0) + ["heap_elements"]=> + array(0) { + } + } +} +array(2) { + [0]=> + array(1) { + ["field"]=> + int(0) + } + [1]=> + array(2) { + ["flags"]=> + int(1) + ["heap_elements"]=> + array(0) { + } + } +}