From 3888fa0fffb88d04c88c4a49d11a5e683d771394 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 26 Aug 2025 14:30:19 +0300 Subject: [PATCH 1/4] [ci skip] Update NEWS for PHP 8.5.0 beta3 --- NEWS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 8a7647c98359c..fa94062bc995b 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.5.0beta2 +?? ??? ????, PHP 8.5.0beta3 + + +28 Aug 2025, PHP 8.5.0beta2 - Core: . Fixed bug GH-18850 (Repeated inclusion of file with __halt_compiler() From ae1438b5e55f5c9fe6e7d7dc4df8c761119b5c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 26 Aug 2025 14:49:42 +0200 Subject: [PATCH 2/4] uri/standard: Move the `parse_url()` URI parser into ext/uri/ (#19587) * uri/standard: Move the `parse_url()` URI parser into ext/uri/ Making ext/standard depend on ext/uri/ is a bit iffy (given that it is called *standard*) and having the parser implementation in ext/uri/ makes it easier to find and keep in sync. * uri: Mark local pointers as `const` in uri_parser_php_parse_url.c * uri: Remove useless explicit cast from void in uri_parser_php_parse_url.c * uri: Properly prefix symbols in uri_parser_php_parse_url.[ch] * uri: Remove useless `throw_invalid_uri_exception()` helper in uri_parser_php_parse_url.c --- ext/filter/logical_filters.c | 4 +- ext/standard/basic_functions.c | 1 - ext/standard/url.c | 175 +-------------------------- ext/standard/url.h | 2 - ext/uri/config.m4 | 3 +- ext/uri/config.w32 | 4 +- ext/uri/php_uri.c | 11 +- ext/uri/php_uri_common.h | 2 +- ext/uri/uri_parser_php_parse_url.c | 184 +++++++++++++++++++++++++++++ ext/uri/uri_parser_php_parse_url.h | 25 ++++ 10 files changed, 225 insertions(+), 186 deletions(-) create mode 100644 ext/uri/uri_parser_php_parse_url.c create mode 100644 ext/uri/uri_parser_php_parse_url.h diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index cf5602e39291d..def5331720d43 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -634,7 +634,7 @@ zend_result php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ if ( /* Skipping these checks is possible because the new URI implementations perform comprehensive validations. */ - strcmp(uri_parser->name, URI_PARSER_PHP) == 0 && + strcmp(uri_parser->name, PHP_URI_PARSER_PHP_PARSE_URL) == 0 && /* An IPv6 enclosed by square brackets is a valid hostname.*/ !php_filter_is_valid_ipv6_hostname(uri->host) && /* Validate domain. @@ -655,7 +655,7 @@ zend_result php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ RETURN_VALIDATION_FAILED } - if (strcmp(uri_parser->name, URI_PARSER_PHP) == 0 && + if (strcmp(uri_parser->name, PHP_URI_PARSER_PHP_PARSE_URL) == 0 && ( (uri->user != NULL && !is_userinfo_valid(uri->user)) || (uri->password != NULL && !is_userinfo_valid(uri->password)) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index ee148ff6c9226..aa5c2a2cedfff 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -305,7 +305,6 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ BASIC_MINIT_SUBMODULE(user_filters) BASIC_MINIT_SUBMODULE(password) BASIC_MINIT_SUBMODULE(image) - BASIC_MINIT_SUBMODULE(url) #ifdef ZTS BASIC_MINIT_SUBMODULE(localeconv) diff --git a/ext/standard/url.c b/ext/standard/url.c index dfd5fa46ccc0c..504805484ef2c 100644 --- a/ext/standard/url.c +++ b/ext/standard/url.c @@ -25,8 +25,6 @@ #include "file.h" #include "zend_simd.h" #include "Zend/zend_smart_str.h" -#include "Zend/zend_exceptions.h" -#include "ext/uri/php_uri.h" /* {{{ free_url */ PHPAPI void php_url_free(php_url *theurl) @@ -49,13 +47,6 @@ PHPAPI void php_url_free(php_url *theurl) } /* }}} */ -static void parse_url_free_uri(void *uri) -{ - php_url *parse_url_uri = (php_url *) uri; - - php_url_free(parse_url_uri); -} - static void php_replace_controlchars(char *str, size_t len) { unsigned char *s = (unsigned char *)str; @@ -320,166 +311,7 @@ PHPAPI php_url *php_url_parse_ex2(char const *str, size_t length, bool *has_port return ret; } - -static void parse_url_decode_component(zval *zv, uri_component_read_mode_t read_mode) -{ - if (Z_TYPE_P(zv) != IS_STRING) { - return; - } - - if (read_mode == URI_COMPONENT_READ_RAW) { - return; - } - - php_raw_url_decode(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); -} - -static zend_result parse_url_read_scheme(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) -{ - php_url *parse_url_uri = internal_uri->uri; - - if (parse_url_uri->scheme) { - ZVAL_STR_COPY(retval, parse_url_uri->scheme); - parse_url_decode_component(retval, read_mode); - } else { - ZVAL_NULL(retval); - } - - return SUCCESS; -} - -static zend_result parse_url_read_username(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) -{ - php_url *parse_url_uri = internal_uri->uri; - - if (parse_url_uri->user) { - ZVAL_STR_COPY(retval, parse_url_uri->user); - parse_url_decode_component(retval, read_mode); - } else { - ZVAL_NULL(retval); - } - - return SUCCESS; -} - -static zend_result parse_url_read_password(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) -{ - php_url *parse_url_uri = internal_uri->uri; - - if (parse_url_uri->pass) { - ZVAL_STR_COPY(retval, parse_url_uri->pass); - parse_url_decode_component(retval, read_mode); - } else { - ZVAL_NULL(retval); - } - - return SUCCESS; -} - -static zend_result parse_url_read_host(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) -{ - php_url *parse_url_uri = internal_uri->uri; - - if (parse_url_uri->host) { - ZVAL_STR_COPY(retval, parse_url_uri->host); - parse_url_decode_component(retval, read_mode); - } else { - ZVAL_NULL(retval); - } - - return SUCCESS; -} - -static zend_result parse_url_read_port(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) -{ - php_url *parse_url_uri = internal_uri->uri; - - if (parse_url_uri->port) { - ZVAL_LONG(retval, parse_url_uri->port); - parse_url_decode_component(retval, read_mode); - } else { - ZVAL_NULL(retval); - } - - return SUCCESS; -} - -static zend_result parse_url_read_path(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) -{ - php_url *parse_url_uri = internal_uri->uri; - - if (parse_url_uri->path) { - ZVAL_STR_COPY(retval, parse_url_uri->path); - parse_url_decode_component(retval, read_mode); - } else { - ZVAL_NULL(retval); - } - - return SUCCESS; -} - -static zend_result parse_url_read_query(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) -{ - php_url *parse_url_uri = internal_uri->uri; - - if (parse_url_uri->query) { - ZVAL_STR_COPY(retval, parse_url_uri->query); - parse_url_decode_component(retval, read_mode); - } else { - ZVAL_NULL(retval); - } - - return SUCCESS; -} - -static zend_result parse_url_read_fragment(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) -{ - php_url *parse_url_uri = internal_uri->uri; - - if (parse_url_uri->fragment) { - ZVAL_STR_COPY(retval, parse_url_uri->fragment); - parse_url_decode_component(retval, read_mode); - } else { - ZVAL_NULL(retval); - } - - return SUCCESS; -} - -static void throw_invalid_uri_exception(void) -{ - zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0); -} - -static void *parse_url_parse_uri(const char *uri_str, size_t uri_str_len, const void *base_url, zval *errors, bool silent) -{ - bool has_port; - - php_url *url = php_url_parse_ex2(uri_str, uri_str_len, &has_port); - if (url == NULL && !silent) { - throw_invalid_uri_exception(); - } - - return url; -} - -const uri_parser_t parse_url_uri_parser = { - .name = URI_PARSER_PHP, - .parse_uri = parse_url_parse_uri, - .clone_uri = NULL, - .uri_to_string = NULL, - .free_uri = parse_url_free_uri, - { - .scheme = {.read_func = parse_url_read_scheme, .write_func = NULL}, - .username = {.read_func = parse_url_read_username, .write_func = NULL}, - .password = {.read_func = parse_url_read_password, .write_func = NULL}, - .host = {.read_func = parse_url_read_host, .write_func = NULL}, - .port = {.read_func = parse_url_read_port, .write_func = NULL}, - .path = {.read_func = parse_url_read_path, .write_func = NULL}, - .query = {.read_func = parse_url_read_query, .write_func = NULL}, - .fragment = {.read_func = parse_url_read_fragment, .write_func = NULL}, - } -}; +/* }}} */ /* {{{ Parse a URL and return its components */ PHP_FUNCTION(parse_url) @@ -921,8 +753,3 @@ PHP_FUNCTION(get_headers) php_stream_close(stream); } /* }}} */ - -PHP_MINIT_FUNCTION(url) -{ - return php_uri_parser_register(&parse_url_uri_parser); -} diff --git a/ext/standard/url.h b/ext/standard/url.h index aefc362744cfd..3885ecece5780 100644 --- a/ext/standard/url.h +++ b/ext/standard/url.h @@ -17,8 +17,6 @@ #ifndef URL_H #define URL_H -PHP_MINIT_FUNCTION(url); - typedef struct php_url { zend_string *scheme; zend_string *user; diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index a43505e2ca9eb..8606b56705d55 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -6,6 +6,7 @@ PHP_INSTALL_HEADERS([ext/uri], m4_normalize([ php_uri_common.h uri_parser_rfc3986.h uri_parser_whatwg.h + uri_parser_php_parse_url.h ])) AC_DEFINE([URI_ENABLE_ANSI], [1], [Define to 1 for enabling ANSI support of uriparser.]) @@ -18,6 +19,6 @@ $URIPARSER_DIR/src/UriMemory.c $URIPARSER_DIR/src/UriNormalize.c $URIPARSER_DIR/ $URIPARSER_DIR/src/UriParse.c $URIPARSER_DIR/src/UriParseBase.c $URIPARSER_DIR/src/UriQuery.c \ $URIPARSER_DIR/src/UriRecompose.c $URIPARSER_DIR/src/UriResolve.c $URIPARSER_DIR/src/UriShorten.c" -PHP_NEW_EXTENSION(uri, [php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_whatwg.c $URIPARSER_SOURCES], [no],,[-I$ext_srcdir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) +PHP_NEW_EXTENSION(uri, [php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_whatwg.c uri_parser_php_parse_url.c $URIPARSER_SOURCES], [no],,[-I$ext_srcdir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) PHP_ADD_EXTENSION_DEP(uri, lexbor) PHP_ADD_BUILD_DIR($ext_builddir/$URIPARSER_DIR/src $ext_builddir/$URIPARSER_DIR/include) diff --git a/ext/uri/config.w32 b/ext/uri/config.w32 index c5ed6fdc17267..8d1452ccfb862 100644 --- a/ext/uri/config.w32 +++ b/ext/uri/config.w32 @@ -1,4 +1,4 @@ -EXTENSION("uri", "php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_whatwg.c ", false /* never shared */, "/I ext/lexbor /I ext/uri/uriparser/include /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +EXTENSION("uri", "php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_whatwg.c uri_parser_php_parse_url.c", false /* never shared */, "/I ext/lexbor /I ext/uri/uriparser/include /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); AC_DEFINE("URI_ENABLE_ANSI", 1, "Define to 1 for enabling ANSI support of uriparser.") AC_DEFINE("URI_NO_UNICODE", 1, "Define to 1 for disabling unicode support of uriparser.") @@ -6,4 +6,4 @@ ADD_FLAG("CFLAGS_URI", "/D URI_STATIC_BUILD"); ADD_EXTENSION_DEP('uri', 'lexbor'); ADD_SOURCES("ext/uri/uriparser/src", "UriCommon.c UriCompare.c UriCopy.c UriEscape.c UriFile.c UriIp4.c UriIp4Base.c UriMemory.c UriNormalize.c UriNormalizeBase.c UriParse.c UriParseBase.c UriQuery.c UriRecompose.c UriResolve.c UriShorten.c", "uri"); -PHP_INSTALL_HEADERS("ext/uri", "php_uri.h php_uri_common.h uri_parser_rfc3986.h uri_parser_whatwg.h uriparser/src uriparser/include"); +PHP_INSTALL_HEADERS("ext/uri", "php_uri.h php_uri_common.h uri_parser_rfc3986.h uri_parser_whatwg.h uri_parser_php_parse_url.h uriparser/src uriparser/include"); diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index e440ba8b8829c..bfbe34a9699f1 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -28,6 +28,7 @@ #include "php_uri.h" #include "uri_parser_whatwg.h" #include "uri_parser_rfc3986.h" +#include "uri_parser_php_parse_url.h" #include "php_uri_arginfo.h" #include "uriparser/src/UriConfig.h" @@ -109,7 +110,7 @@ static HashTable *uri_get_debug_properties(zend_object *object) PHPAPI uri_parser_t *php_uri_get_parser(const zend_string *uri_parser_name) { if (uri_parser_name == NULL) { - return uri_parser_by_name(URI_PARSER_PHP, sizeof(URI_PARSER_PHP) - 1); + return uri_parser_by_name(PHP_URI_PARSER_PHP_PARSE_URL, sizeof(PHP_URI_PARSER_PHP_PARSE_URL) - 1); } return uri_parser_by_name(ZSTR_VAL(uri_parser_name), ZSTR_LEN(uri_parser_name)); @@ -1021,8 +1022,8 @@ PHPAPI zend_result php_uri_parser_register(const uri_parser_t *uri_parser) ZEND_ASSERT(uri_parser->name != NULL); ZEND_ASSERT(uri_parser->parse_uri != NULL); - ZEND_ASSERT(uri_parser->clone_uri != NULL || strcmp(uri_parser->name, URI_PARSER_PHP) == 0); - ZEND_ASSERT(uri_parser->uri_to_string != NULL || strcmp(uri_parser->name, URI_PARSER_PHP) == 0); + ZEND_ASSERT(uri_parser->clone_uri != NULL || strcmp(uri_parser->name, PHP_URI_PARSER_PHP_PARSE_URL) == 0); + ZEND_ASSERT(uri_parser->uri_to_string != NULL || strcmp(uri_parser->name, PHP_URI_PARSER_PHP_PARSE_URL) == 0); ZEND_ASSERT(uri_parser->free_uri != NULL); zend_result result = zend_hash_add_ptr(&uri_parsers, key, (void *) uri_parser) != NULL ? SUCCESS : FAILURE; @@ -1057,6 +1058,10 @@ static PHP_MINIT_FUNCTION(uri) return FAILURE; } + if (php_uri_parser_register(&php_uri_parser_php_parse_url) == FAILURE) { + return FAILURE; + } + return SUCCESS; } diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index afffdfb8f5cf0..fb465bed502ab 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -164,7 +164,7 @@ static inline uri_internal_t *uri_internal_from_obj(const zend_object *object) { #define PHP_URI_PARSER_RFC3986 "Uri\\Rfc3986\\Uri" #define PHP_URI_PARSER_WHATWG "Uri\\WhatWg\\Url" -#define URI_PARSER_PHP "parse_url" +#define PHP_URI_PARSER_PHP_PARSE_URL "parse_url" #define URI_SERIALIZED_PROPERTY_NAME "uri" const uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, uri_property_name_t property_name); diff --git a/ext/uri/uri_parser_php_parse_url.c b/ext/uri/uri_parser_php_parse_url.c new file mode 100644 index 0000000000000..ae950ed48885e --- /dev/null +++ b/ext/uri/uri_parser_php_parse_url.c @@ -0,0 +1,184 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Máté Kocsis | + | Tim Düsterhus | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "uri_parser_php_parse_url.h" +#include "php_uri_common.h" +#include "Zend/zend_exceptions.h" +#include "ext/standard/url.h" + +static void decode_component(zval *zv, uri_component_read_mode_t read_mode) +{ + if (Z_TYPE_P(zv) != IS_STRING) { + return; + } + + if (read_mode == URI_COMPONENT_READ_RAW) { + return; + } + + php_raw_url_decode(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); +} + +static zend_result uri_parser_php_parse_url_scheme_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + const php_url *parse_url_uri = internal_uri->uri; + + if (parse_url_uri->scheme) { + ZVAL_STR_COPY(retval, parse_url_uri->scheme); + decode_component(retval, read_mode); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result uri_parser_php_parse_url_username_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + const php_url *parse_url_uri = internal_uri->uri; + + if (parse_url_uri->user) { + ZVAL_STR_COPY(retval, parse_url_uri->user); + decode_component(retval, read_mode); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result uri_parser_php_parse_url_password_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + const php_url *parse_url_uri = internal_uri->uri; + + if (parse_url_uri->pass) { + ZVAL_STR_COPY(retval, parse_url_uri->pass); + decode_component(retval, read_mode); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result uri_parser_php_parse_url_host_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + const php_url *parse_url_uri = internal_uri->uri; + + if (parse_url_uri->host) { + ZVAL_STR_COPY(retval, parse_url_uri->host); + decode_component(retval, read_mode); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result uri_parser_php_parse_url_port_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + const php_url *parse_url_uri = internal_uri->uri; + + if (parse_url_uri->port) { + ZVAL_LONG(retval, parse_url_uri->port); + decode_component(retval, read_mode); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result uri_parser_php_parse_url_path_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + const php_url *parse_url_uri = internal_uri->uri; + + if (parse_url_uri->path) { + ZVAL_STR_COPY(retval, parse_url_uri->path); + decode_component(retval, read_mode); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result uri_parser_php_parse_url_query_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + const php_url *parse_url_uri = internal_uri->uri; + + if (parse_url_uri->query) { + ZVAL_STR_COPY(retval, parse_url_uri->query); + decode_component(retval, read_mode); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result uri_parser_php_parse_url_fragment_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + const php_url *parse_url_uri = internal_uri->uri; + + if (parse_url_uri->fragment) { + ZVAL_STR_COPY(retval, parse_url_uri->fragment); + decode_component(retval, read_mode); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static void *uri_parser_php_parse_url_parse(const char *uri_str, size_t uri_str_len, const void *base_url, zval *errors, bool silent) +{ + bool has_port; + + php_url *url = php_url_parse_ex2(uri_str, uri_str_len, &has_port); + if (url == NULL && !silent) { + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0); + } + + return url; +} + +static void uri_parser_php_parse_url_free(void *uri) +{ + php_url *parse_url_uri = uri; + + php_url_free(parse_url_uri); +} + +const uri_parser_t php_uri_parser_php_parse_url = { + .name = PHP_URI_PARSER_PHP_PARSE_URL, + .parse_uri = uri_parser_php_parse_url_parse, + .clone_uri = NULL, + .uri_to_string = NULL, + .free_uri = uri_parser_php_parse_url_free, + { + .scheme = {.read_func = uri_parser_php_parse_url_scheme_read, .write_func = NULL}, + .username = {.read_func = uri_parser_php_parse_url_username_read, .write_func = NULL}, + .password = {.read_func = uri_parser_php_parse_url_password_read, .write_func = NULL}, + .host = {.read_func = uri_parser_php_parse_url_host_read, .write_func = NULL}, + .port = {.read_func = uri_parser_php_parse_url_port_read, .write_func = NULL}, + .path = {.read_func = uri_parser_php_parse_url_path_read, .write_func = NULL}, + .query = {.read_func = uri_parser_php_parse_url_query_read, .write_func = NULL}, + .fragment = {.read_func = uri_parser_php_parse_url_fragment_read, .write_func = NULL}, + } +}; diff --git a/ext/uri/uri_parser_php_parse_url.h b/ext/uri/uri_parser_php_parse_url.h new file mode 100644 index 0000000000000..ef98088a26c45 --- /dev/null +++ b/ext/uri/uri_parser_php_parse_url.h @@ -0,0 +1,25 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Máté Kocsis | + | Tim Düsterhus | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_URI_PARSER_PHP_PARSE_URL_H +#define PHP_URI_PARSER_PHP_PARSE_URL_H + +#include "php_uri_common.h" + +extern const uri_parser_t php_uri_parser_php_parse_url; + +#endif From b7d66189cb7c9ed09bd3971fb50dd2c44ce9d3fc Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 26 Aug 2025 13:34:49 +0200 Subject: [PATCH 3/4] Fix RC violation of session SID constant deprecation attribute Don't use persistent string/attribute for the global SID constant, which is created at runtime and destroyed at the end of the request. Requires CFLAGS="-DZEND_RC_DEBUG=1" to reproduce. Closes GH-19595 --- NEWS | 2 ++ ext/session/session.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index fa94062bc995b..c4e0909050960 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.5.0beta3 +- Session: + . Fix RC violation of session SID constant deprecation attribute. (ilutov) 28 Aug 2025, PHP 8.5.0beta2 diff --git a/ext/session/session.c b/ext/session/session.c index a7a253d8bff41..66912fb1016c6 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -1529,8 +1529,8 @@ PHPAPI zend_result php_session_reset_id(void) } } if (new_sid_constant != NULL) { - zend_string *deprecation_reason = zend_string_init("as GET/POST sessions were deprecated", strlen("as GET/POST sessions were deprecated"), 1); - zend_attribute *deprecation_attrib = zend_add_global_constant_attribute(new_sid_constant, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *deprecation_reason = zend_string_init("as GET/POST sessions were deprecated", strlen("as GET/POST sessions were deprecated"), 0); + zend_attribute *deprecation_attrib = zend_add_attribute(&new_sid_constant->attributes, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2, 0, 0, 0); ZVAL_STR(&deprecation_attrib->args[0].value, ZSTR_KNOWN(ZEND_STR_8_DOT_4)); deprecation_attrib->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); ZVAL_STR(&deprecation_attrib->args[1].value, deprecation_reason); From e18ef6caeae4152b95d65a7312412bbe201643a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 26 Aug 2025 16:52:29 +0200 Subject: [PATCH 4/4] zend_weakrefs: Remove layer of indirection in `zend_weakrefs_hash_clean()` (#19580) * zend_weakrefs: Remove layer of indirection in `zend_weakrefs_hash_clean()` Following php/php-src#16439. * zend_weakrefs: Include `zend_weakmap_free_obj()` optimization in `zend_weakrefs_hash_clean()` --- Zend/zend_weakrefs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index ae8879f360b80..0c729755ff445 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -138,8 +138,8 @@ static void zend_weakref_unregister(zend_object *object, void *payload, bool wea if (weakref_free) { zend_weakref_unref_single(ptr, tag, object); } else { - /* The optimization of skipping unref is only used in the destructor of WeakMap */ - ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP); + /* The optimization of skipping unref is used for zend_weakrefs_hash_clean_ex() */ + ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP || ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_BARE_HT); } return; } @@ -163,8 +163,8 @@ static void zend_weakref_unregister(zend_object *object, void *payload, bool wea zend_weakref_unref_single( ZEND_WEAKREF_GET_PTR(payload), ZEND_WEAKREF_GET_TAG(payload), object); } else { - /* The optimization of skipping unref is only used in the destructor of WeakMap */ - ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP); + /* The optimization of skipping unref is used for zend_weakrefs_hash_clean_ex() */ + ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP || ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_BARE_HT); } } @@ -187,11 +187,20 @@ ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key) { return FAILURE; } -ZEND_API void zend_weakrefs_hash_clean(HashTable *ht) { +static void zend_weakrefs_hash_clean_ex(HashTable *ht, int type) { zend_ulong obj_key; - ZEND_HASH_FOREACH_NUM_KEY(ht, obj_key) { - zend_weakrefs_hash_del(ht, zend_weakref_key_to_object(obj_key)); + ZEND_HASH_MAP_FOREACH_NUM_KEY(ht, obj_key) { + /* Optimization: Don't call zend_weakref_unref_single to free individual entries from ht when unregistering (which would do a hash table lookup, call zend_hash_index_del, and skip over any bucket collisions). + * Let freeing the corresponding values for WeakMap entries be done in zend_hash_clean, freeing objects sequentially. + * The performance difference is notable for larger WeakMaps with worse cache locality. */ + zend_weakref_unregister( + zend_weakref_key_to_object(obj_key), ZEND_WEAKREF_ENCODE(ht, type), 0); } ZEND_HASH_FOREACH_END(); + zend_hash_clean(ht); +} + +ZEND_API void zend_weakrefs_hash_clean(HashTable *ht) { + zend_weakrefs_hash_clean_ex(ht, ZEND_WEAKREF_TAG_BARE_HT); } void zend_weakrefs_init(void) { @@ -340,14 +349,7 @@ static zend_object *zend_weakmap_create_object(zend_class_entry *ce) static void zend_weakmap_free_obj(zend_object *object) { zend_weakmap *wm = zend_weakmap_from(object); - zend_ulong obj_key; - ZEND_HASH_MAP_FOREACH_NUM_KEY(&wm->ht, obj_key) { - /* Optimization: Don't call zend_weakref_unref_single to free individual entries from wm->ht when unregistering (which would do a hash table lookup, call zend_hash_index_del, and skip over any bucket collisions). - * Let freeing the corresponding values for WeakMap entries be done in zend_hash_destroy, freeing objects sequentially. - * The performance difference is notable for larger WeakMaps with worse cache locality. */ - zend_weakref_unregister( - zend_weakref_key_to_object(obj_key), ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP), 0); - } ZEND_HASH_FOREACH_END(); + zend_weakrefs_hash_clean_ex(&wm->ht, ZEND_WEAKREF_TAG_MAP); zend_hash_destroy(&wm->ht); zend_object_std_dtor(&wm->std); }