From 1f112651c418c58641fcc7e0919296e346931159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 27 May 2025 14:31:03 +0200 Subject: [PATCH 01/15] Add Uri\WhatWg classes to ext/uri Relates to #14461 and https://wiki.php.net/rfc/url_parsing_api --- Zend/zend_string.h | 2 + build/gen_stub.php | 2 + ext/uri/config.m4 | 4 +- ext/uri/config.w32 | 4 +- ext/uri/php_lexbor.c | 604 ++++++++++++++++++++++++++++++++++++++ ext/uri/php_lexbor.h | 25 ++ ext/uri/php_uri.c | 577 ++++++++++++++++++++++++++++++++++++ ext/uri/php_uri.h | 3 + ext/uri/php_uri.stub.php | 108 +++++++ ext/uri/php_uri_arginfo.h | 275 ++++++++++++++++- ext/uri/php_uri_common.c | 40 +++ ext/uri/php_uri_common.h | 190 ++++++++++++ ext/uri/tests/003.phpt | 32 ++ ext/uri/tests/004.phpt | 25 ++ ext/uri/tests/005.phpt | 38 +++ ext/uri/tests/006.phpt | 30 ++ ext/uri/tests/007.phpt | 63 ++++ ext/uri/tests/008.phpt | 34 +++ ext/uri/tests/009.phpt | 29 ++ ext/uri/tests/010.phpt | 48 +++ ext/uri/tests/011.phpt | 22 ++ ext/uri/tests/012.phpt | 49 ++++ ext/uri/tests/013.phpt | 87 ++++++ ext/uri/tests/014.phpt | 12 + ext/uri/tests/015.phpt | 18 ++ ext/uri/tests/018.phpt | 50 ++++ ext/uri/tests/019.phpt | 55 ++++ ext/uri/tests/022.phpt | 14 + ext/uri/tests/023.phpt | 31 ++ ext/uri/tests/024.phpt | 29 ++ ext/uri/tests/025.phpt | 29 ++ ext/uri/tests/026.phpt | 67 +++++ ext/uri/tests/027.phpt | 36 +++ ext/uri/tests/028.phpt | 37 +++ ext/uri/tests/029.phpt | 40 +++ ext/uri/tests/030.phpt | 31 ++ ext/uri/tests/031.phpt | 91 ++++++ ext/uri/tests/032.phpt | 13 + ext/uri/tests/033.phpt | 15 + ext/uri/tests/034.phpt | 14 + ext/uri/tests/035.phpt | 24 ++ ext/uri/tests/036.phpt | 24 ++ ext/uri/tests/038.phpt | 26 ++ ext/uri/tests/039.phpt | 34 +++ ext/uri/tests/040.phpt | 32 ++ ext/uri/tests/041.phpt | 26 ++ ext/uri/tests/042.phpt | 29 ++ ext/uri/tests/043.phpt | 71 +++++ ext/uri/tests/045.phpt | 16 + ext/uri/tests/046.phpt | 30 ++ ext/uri/tests/047.phpt | 27 ++ ext/uri/tests/049.phpt | 13 + ext/uri/tests/050.phpt | 16 + ext/uri/tests/051.phpt | 35 +++ 54 files changed, 3272 insertions(+), 4 deletions(-) create mode 100644 ext/uri/php_lexbor.c create mode 100644 ext/uri/php_lexbor.h create mode 100644 ext/uri/php_uri_common.c create mode 100644 ext/uri/php_uri_common.h create mode 100644 ext/uri/tests/003.phpt create mode 100644 ext/uri/tests/004.phpt create mode 100644 ext/uri/tests/005.phpt create mode 100644 ext/uri/tests/006.phpt create mode 100644 ext/uri/tests/007.phpt create mode 100644 ext/uri/tests/008.phpt create mode 100644 ext/uri/tests/009.phpt create mode 100644 ext/uri/tests/010.phpt create mode 100644 ext/uri/tests/011.phpt create mode 100644 ext/uri/tests/012.phpt create mode 100644 ext/uri/tests/013.phpt create mode 100644 ext/uri/tests/014.phpt create mode 100644 ext/uri/tests/015.phpt create mode 100644 ext/uri/tests/018.phpt create mode 100644 ext/uri/tests/019.phpt create mode 100644 ext/uri/tests/022.phpt create mode 100644 ext/uri/tests/023.phpt create mode 100644 ext/uri/tests/024.phpt create mode 100644 ext/uri/tests/025.phpt create mode 100644 ext/uri/tests/026.phpt create mode 100644 ext/uri/tests/027.phpt create mode 100644 ext/uri/tests/028.phpt create mode 100644 ext/uri/tests/029.phpt create mode 100644 ext/uri/tests/030.phpt create mode 100644 ext/uri/tests/031.phpt create mode 100644 ext/uri/tests/032.phpt create mode 100644 ext/uri/tests/033.phpt create mode 100644 ext/uri/tests/034.phpt create mode 100644 ext/uri/tests/035.phpt create mode 100644 ext/uri/tests/036.phpt create mode 100644 ext/uri/tests/038.phpt create mode 100644 ext/uri/tests/039.phpt create mode 100644 ext/uri/tests/040.phpt create mode 100644 ext/uri/tests/041.phpt create mode 100644 ext/uri/tests/042.phpt create mode 100644 ext/uri/tests/043.phpt create mode 100644 ext/uri/tests/045.phpt create mode 100644 ext/uri/tests/046.phpt create mode 100644 ext/uri/tests/047.phpt create mode 100644 ext/uri/tests/049.phpt create mode 100644 ext/uri/tests/050.phpt create mode 100644 ext/uri/tests/051.phpt diff --git a/Zend/zend_string.h b/Zend/zend_string.h index e9e2b947a6c91..0b2a484016ec3 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -597,7 +597,9 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_HOST, "host") \ _(ZEND_STR_PORT, "port") \ _(ZEND_STR_USER, "user") \ + _(ZEND_STR_USERNAME, "username") \ _(ZEND_STR_PASS, "pass") \ + _(ZEND_STR_PASSWORD, "password") \ _(ZEND_STR_PATH, "path") \ _(ZEND_STR_QUERY, "query") \ _(ZEND_STR_FRAGMENT, "fragment") \ diff --git a/build/gen_stub.php b/build/gen_stub.php index 13ef9e60f334d..0e87cdd9a0b40 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3055,6 +3055,8 @@ class PropertyInfo extends VariableLike private const PHP_85_KNOWN = [ "self" => "ZEND_STR_SELF", "parent" => "ZEND_STR_PARENT", + "username" => "ZEND_STR_USERNAME", + "password" => "ZEND_STR_PASSWORD", ]; /** diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index f29bbe58bd32e..cb57ff88d5268 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -2,7 +2,9 @@ dnl Configure options dnl PHP_INSTALL_HEADERS([ext/uri], m4_normalize([ + php_lexbor.h php_uri.h + php_uri_common.h ])) AC_DEFINE([URI_ENABLE_ANSI], [1], [Define to 1 for enabling ANSI support of uriparser.]) @@ -15,6 +17,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 $URIPARSER_SOURCES], [no],,[-I$ext_srcdir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) +PHP_NEW_EXTENSION(uri, [php_lexbor.c php_uri.c php_uri_common.c $URIPARSER_SOURCES], [no],,[-Iext_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 962f7bee0660e..9c6af0cc5fa7b 100644 --- a/ext/uri/config.w32 +++ b/ext/uri/config.w32 @@ -1,4 +1,4 @@ -EXTENSION("uri", "php_uri.c", false /* never shared */, "/I ext/lexbor /I ext/uri/uriparser/include /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +EXTENSION("uri", "php_lexbor.c php_uri.c php_uri_common.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 UriEscape.c UriFile.c UriIp4.c UriIp4Base.c UriMemory.c UriNormalize.c UriNormalizeBase.c UriParse.c UriParseBase.c UriQuery.c UriRecompose.c UriShorten.c", "uri"); -PHP_INSTALL_HEADERS("ext/uri", "php_uri.h uriparser/src uriparser/include"); +PHP_INSTALL_HEADERS("ext/uri", "php_lexbor.h php_uri.h php_uri_common.h uriparser/src uriparser/include"); diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c new file mode 100644 index 0000000000000..bcc5ddf3f76ff --- /dev/null +++ b/ext/uri/php_lexbor.c @@ -0,0 +1,604 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "php_lexbor.h" +#include "php_uri_common.h" +#include "Zend/zend_enum.h" +#include "Zend/zend_smart_str.h" +#ifdef HAVE_ARPA_INET_H +#include +#endif + +static zend_result lexbor_init_parser(void); +static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors); +static void lexbor_create_invalid_uri_exception(zval *exception_zv, zval *errors); +static void *lexbor_clone_uri(void *uri); +static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment); +static void lexbor_free_uri(void *uri); +static zend_result lexbor_destroy_parser(void); + +HashTable lexbor_property_handlers; + +lxb_url_parser_t *lexbor_parser; +int lexbor_urls; + +const uri_handler_t lexbor_uri_handler = { + URI_PARSER_WHATWG, + lexbor_init_parser, + lexbor_parse_uri, + lexbor_create_invalid_uri_exception, + lexbor_clone_uri, + lexbor_uri_to_string, + lexbor_free_uri, + lexbor_destroy_parser, + &lexbor_property_handlers +}; + +#define ZVAL_TO_LEXBOR_STR(value, str) do { \ + if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) > 0) { \ + lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \ + } else if (Z_TYPE_P(value) == IS_LONG && Z_LVAL_P(value) != 0) { \ + ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); \ + lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \ + } else { \ + lexbor_str_init(&str, lexbor_parser->mraw, 0); \ + } \ +} while (0) + +#define CHECK_WRITE_RESULT(status, lexbor_uri, str, errors) do { \ + if (status != LXB_STATUS_OK) { \ + fill_errors(errors); \ + return FAILURE; \ + } \ + return SUCCESS; \ +} while (0) + +#define LEXBOR_READ_ASCII_URI_COMPONENT(start, len, read_mode, retval) do { \ + switch (read_mode) { \ + case URI_COMPONENT_READ_RAW: /* Intentional fallthrough */ \ + case URI_COMPONENT_READ_NORMALIZED_UNICODE: /* Intentional fallthrough */ \ + case URI_COMPONENT_READ_NORMALIZED_ASCII: { \ + ZVAL_STRINGL(retval, (const char *) start, len); \ + break; \ + } \ + EMPTY_SWITCH_DEFAULT_CASE() \ + } \ +} while (0) + +static void lexbor_cleanup_parser(void) +{ + if (++lexbor_urls % 500 == 0) { + lexbor_mraw_clean(lexbor_parser->mraw); + } + + lxb_url_parser_clean(lexbor_parser); +} + +static void fill_errors(zval *errors) +{ + if (errors == NULL || lexbor_parser->log == NULL) { + return; + } + + if (lexbor_array_obj_size(&lexbor_parser->log->list)) { + array_init(errors); + } + + lexbor_plog_entry_t *lxb_error; + while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser->log->list)) != NULL) { + zval error; + object_init_ex(&error, whatwg_url_validation_error_ce); + zend_update_property_string(whatwg_url_validation_error_ce, Z_OBJ(error), "context", sizeof("context") - 1, (const char *) lxb_error->data); + + zend_string *error_str; + zval failure; + switch (lxb_error->id) { + case LXB_URL_ERROR_TYPE_DOMAIN_TO_ASCII: + error_str = zend_string_init("DomainToAscii", sizeof("DomainToAscii"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_DOMAIN_TO_UNICODE: + error_str = zend_string_init("DomainToUnicode", sizeof("DomainToUnicode"), false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_DOMAIN_INVALID_CODE_POINT: + error_str = zend_string_init("DomainInvalidCodePoint", sizeof("DomainInvalidCodePoint"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_HOST_INVALID_CODE_POINT: + error_str = zend_string_init("HostInvalidCodePoint", sizeof("HostInvalidCodePoint"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_EMPTY_PART: + error_str = zend_string_init("Ipv4EmptyPart", sizeof("Ipv4EmptyPart"), false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_TOO_MANY_PARTS: + error_str = zend_string_init("Ipv4TooManyParts", sizeof("Ipv4TooManyParts"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_NON_NUMERIC_PART: + error_str = zend_string_init("Ipv4NonNumericPart", sizeof("Ipv4NonNumericPart"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_NON_DECIMAL_PART: + error_str = zend_string_init("Ipv4NonDecimalPart", sizeof("Ipv4NonDecimalPart"), false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_OUT_OF_RANGE_PART: + error_str = zend_string_init("Ipv4OutOfRangePart", sizeof("Ipv4OutOfRangePart"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_UNCLOSED: + error_str = zend_string_init("Ipv6Unclosed", sizeof("Ipv6Unclosed"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_INVALID_COMPRESSION: + error_str = zend_string_init("Ipv6InvalidCompression", sizeof("Ipv6InvalidCompression"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_TOO_MANY_PIECES: + error_str = zend_string_init("Ipv6TooManyPieces", sizeof("Ipv6TooManyPieces"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_MULTIPLE_COMPRESSION: + error_str = zend_string_init("Ipv6MultipleCompression", sizeof("Ipv6MultipleCompression"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_INVALID_CODE_POINT: + error_str = zend_string_init("Ipv6InvalidCodePoint", sizeof("Ipv6InvalidCodePoint"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_TOO_FEW_PIECES: + error_str = zend_string_init("Ipv6TooFewPieces", sizeof("Ipv6TooFewPieces"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_MANY_PIECES: + error_str = zend_string_init("Ipv4InIpv6TooManyPieces", sizeof("Ipv4InIpv6TooManyPieces"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_INVALID_CODE_POINT: + error_str = zend_string_init("Ipv4InIpv6InvalidCodePoint", sizeof("Ipv4InIpv6InvalidCodePoint"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_OUT_OF_RANGE_PART: + error_str = zend_string_init("Ipv4InIpv6OutOfRangePart", sizeof("Ipv4InIpv6OutOfRangePart"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_FEW_PARTS: + error_str = zend_string_init("Ipv4InIpv6TooFewParts", sizeof("Ipv4InIpv6TooFewParts"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_INVALID_URL_UNIT: + error_str = zend_string_init("InvalidUrlUnit", sizeof("InvalidUrlUnit"), false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_SPECIAL_SCHEME_MISSING_FOLLOWING_SOLIDUS: + error_str = zend_string_init("SpecialSchemeMissingFollowingSolidus", sizeof("SpecialSchemeMissingFollowingSolidus"), false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_MISSING_SCHEME_NON_RELATIVE_URL: + error_str = zend_string_init("MissingSchemeNonRelativeUrl", sizeof("MissingSchemeNonRelativeUrl"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_INVALID_REVERSE_SOLIDUS: + error_str = zend_string_init("InvalidReverseSoldius", sizeof("InvalidReverseSoldius"), false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS: + error_str = zend_string_init("InvalidCredentials", sizeof("InvalidCredentials"), false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_HOST_MISSING: + error_str = zend_string_init("HostMissing", sizeof("HostMissing"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_PORT_OUT_OF_RANGE: + error_str = zend_string_init("PortOutOfRange", sizeof("PortOutOfRange"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_PORT_INVALID: + error_str = zend_string_init("PortInvalid", sizeof("PortInvalid"), false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER: + error_str = zend_string_init("FileInvalidWindowsDriveLetter", sizeof("FileInvalidWindowsDriveLetter"), false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER_HOST: + error_str = zend_string_init("FileInvalidWindowsDriveLetterHost", sizeof("FileInvalidWindowsDriveLetterHost"), false); + ZVAL_FALSE(&failure); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + + zval error_type; + zend_enum_new(&error_type, whatwg_url_validation_error_type_ce, error_str, NULL); + zend_update_property(whatwg_url_validation_error_ce, Z_OBJ(error), "type", sizeof("type") - 1, &error_type); + zend_string_release(error_str); + zval_ptr_dtor(&error_type); + + zend_update_property(whatwg_url_validation_error_ce, Z_OBJ(error), "failure", sizeof("failure") - 1, &failure); + zval_ptr_dtor(&failure); + + add_next_index_zval(errors, &error); + } + + lexbor_array_obj_clean(&lexbor_parser->log->list); // check if needed +} + +static lxb_status_t lexbor_serialize_callback(const lxb_char_t *data, size_t length, void *ctx) +{ + smart_str *uri_str = (smart_str *) ctx; + + if (data != NULL && length > 0) { + smart_str_appendl(uri_str, (const char *) data, length); + } + + return LXB_STATUS_OK; +} + +static zend_result lexbor_read_scheme(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + + ZEND_ASSERT(lexbor_uri->scheme.type != LXB_URL_SCHEMEL_TYPE__UNDEF); + + ZVAL_STRINGL(retval, (const char *) lexbor_uri->scheme.name.data, lexbor_uri->scheme.name.length); + + return SUCCESS; +} + +static zend_result lexbor_write_scheme(uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lexbor_str_t str = {0}; + + ZVAL_TO_LEXBOR_STR(value, str); + + lxb_status_t status = lxb_url_api_protocol_set(lexbor_uri, lexbor_parser, str.data, str.length); + + CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); +} + +static zend_result lexbor_read_username(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + + if (lexbor_uri->username.length) { + LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->username.data, lexbor_uri->username.length, read_mode, retval); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lexbor_str_t str = {0}; + + ZVAL_TO_LEXBOR_STR(value, str); + + lxb_status_t status = lxb_url_api_username_set(lexbor_uri, str.data, str.length); + + CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); +} + +static zend_result lexbor_read_password(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + + if (lexbor_uri->password.length > 0) { + LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->password.data, lexbor_uri->password.length, read_mode, retval); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_password(uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lexbor_str_t str = {0}; + + ZVAL_TO_LEXBOR_STR(value, str); + + lxb_status_t status = lxb_url_api_password_set(lexbor_uri, str.data, str.length); + + CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); +} + +static zend_result lexbor_read_host(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + + if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV4) { + smart_str host_str = {0}; + + lxb_url_serialize_host_ipv4((zend_ulong) lexbor_uri->host.u.ipv4, lexbor_serialize_callback, (void *) &host_str); + + ZVAL_STR(retval, smart_str_extract(&host_str)); + } else if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV6) { + smart_str host_str = {0}; + + smart_str_appendc(&host_str, '['); + lxb_url_serialize_host_ipv6(lexbor_uri->host.u.ipv6, lexbor_serialize_callback, (void *) &host_str); + smart_str_appendc(&host_str, ']'); + + ZVAL_STR(retval, smart_str_extract(&host_str)); + } else if (lexbor_uri->host.type != LXB_URL_HOST_TYPE_EMPTY && lexbor_uri->host.type != LXB_URL_HOST_TYPE__UNDEF) { + + switch (read_mode) { + case URI_COMPONENT_READ_NORMALIZED_UNICODE: { + smart_str host_str = {0}; + if (lexbor_parser->idna == NULL) { + lexbor_parser->idna = lxb_unicode_idna_create(); + lxb_status_t status = lxb_unicode_idna_init(lexbor_parser->idna); + if (status != LXB_STATUS_OK) { + return FAILURE; + } + } + lxb_url_serialize_host_unicode(lexbor_parser->idna, &lexbor_uri->host, lexbor_serialize_callback, (void *) &host_str); + lxb_unicode_idna_clean(lexbor_parser->idna); + + ZVAL_STR(retval, smart_str_extract(&host_str)); + break; + } + case URI_COMPONENT_READ_NORMALIZED_ASCII: /* Intentional fallthrough */ + case URI_COMPONENT_READ_RAW: + ZVAL_STRINGL(retval, (const char *) lexbor_uri->host.u.domain.data, lexbor_uri->host.u.domain.length); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_host(uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lexbor_str_t str = {0}; + + ZVAL_TO_LEXBOR_STR(value, str); + + lxb_status_t status = lxb_url_api_hostname_set(lexbor_uri, lexbor_parser, str.data, str.length); + + CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); +} + +static zend_result lexbor_read_port(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + + if (lexbor_uri->has_port) { + ZVAL_LONG(retval, lexbor_uri->port); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_port(uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lexbor_str_t str = {0}; + + ZVAL_TO_LEXBOR_STR(value, str); + + lxb_status_t status = lxb_url_api_port_set(lexbor_uri, lexbor_parser, str.data, str.length); + + CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); +} + +static zend_result lexbor_read_path(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + + if (lexbor_uri->path.str.length) { + LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->path.str.data, lexbor_uri->path.str.length, read_mode, retval); + } else { + ZVAL_EMPTY_STRING(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_path(uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lexbor_str_t str = {0}; + + ZVAL_TO_LEXBOR_STR(value, str); + + lxb_status_t status = lxb_url_api_pathname_set(lexbor_uri, lexbor_parser, str.data, str.length); + + CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); +} + +static zend_result lexbor_read_query(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + + if (lexbor_uri->query.length) { + LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->query.data, lexbor_uri->query.length, read_mode, retval); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_query(uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lexbor_str_t str = {0}; + + ZVAL_TO_LEXBOR_STR(value, str); + + lxb_status_t status = lxb_url_api_search_set(lexbor_uri, lexbor_parser, str.data, str.length); + + CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); +} + +static zend_result lexbor_read_fragment(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + + if (lexbor_uri->fragment.length) { + LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->fragment.data, lexbor_uri->fragment.length, read_mode, retval); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_fragment(uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lexbor_str_t str = {0}; + + ZVAL_TO_LEXBOR_STR(value, str); + + lxb_status_t status = lxb_url_api_hash_set(lexbor_uri, lexbor_parser, str.data, str.length); + + CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); +} + +static zend_result lexbor_init_parser(void) +{ + lexbor_mraw_t *mraw = lexbor_mraw_create(); + lxb_status_t status = lexbor_mraw_init(mraw, 4096 * 2); + if (status != LXB_STATUS_OK) { + lexbor_mraw_destroy(mraw, true); + return FAILURE; + } + + lxb_url_parser_t *parser = lxb_url_parser_create(); + status = lxb_url_parser_init(parser, mraw); + if (status != LXB_STATUS_OK) { + lxb_url_parser_destroy(parser, true); + return FAILURE; + } + + lexbor_parser = parser; + lexbor_urls = 0; + + zend_hash_init(&lexbor_property_handlers, 8, NULL, NULL, true); + + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_SCHEME), lexbor_read_scheme, lexbor_write_scheme); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_USERNAME), lexbor_read_username, lexbor_write_username); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PASSWORD), lexbor_read_password, lexbor_write_password); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_HOST), lexbor_read_host, lexbor_write_host); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PORT), lexbor_read_port, lexbor_write_port); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PATH), lexbor_read_path, lexbor_write_path); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_QUERY), lexbor_read_query, lexbor_write_query); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_FRAGMENT), lexbor_read_fragment, lexbor_write_fragment); + + return SUCCESS; +} + +static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors) +{ + lexbor_cleanup_parser(); + + lxb_url_t *url, *lexbor_base_url = NULL; + + if (base_url) { + lexbor_base_url = (lxb_url_t *) base_url; + } + + url = lxb_url_parse(lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str)); + fill_errors(errors); + + return url; +} + +static void lexbor_create_invalid_uri_exception(zval *exception_zv, zval *errors) +{ + object_init_ex(exception_zv, whatwg_invalid_url_exception_ce); + + zend_update_property_string( + whatwg_invalid_url_exception_ce, + Z_OBJ_P(exception_zv), + ZSTR_VAL(ZSTR_KNOWN(ZEND_STR_MESSAGE)), + ZSTR_LEN(ZSTR_KNOWN(ZEND_STR_MESSAGE)), + "URL parsing failed" + ); + + zend_update_property(whatwg_invalid_url_exception_ce, Z_OBJ_P(exception_zv), "errors", sizeof("errors") - 1, errors); +} + + +static void *lexbor_clone_uri(void *uri) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) uri; + + return lxb_url_clone(lexbor_parser->mraw, lexbor_uri); +} + +static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) uri; + smart_str uri_str = {0}; + + switch (recomposition_mode) { + case URI_RECOMPOSITION_RAW_UNICODE: /* Intentional fallthrough */ + case URI_RECOMPOSITION_NORMALIZED_UNICODE: + if (lexbor_parser->idna == NULL) { + lexbor_parser->idna = lxb_unicode_idna_create(); + lxb_status_t status = lxb_unicode_idna_init(lexbor_parser->idna); + if (status != LXB_STATUS_OK) { + return NULL; + } + } + lxb_url_serialize_idna(lexbor_parser->idna, lexbor_uri, lexbor_serialize_callback, (void *) &uri_str, exclude_fragment); + lxb_unicode_idna_clean(lexbor_parser->idna); + break; + case URI_RECOMPOSITION_RAW_ASCII: /* Intentional fallthrough */ + case URI_RECOMPOSITION_NORMALIZED_ASCII: + lxb_url_serialize(lexbor_uri, lexbor_serialize_callback, (void *) &uri_str, exclude_fragment); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + + return smart_str_extract(&uri_str); +} + +static void lexbor_free_uri(void *uri) +{ +} + +static zend_result lexbor_destroy_parser(void) +{ + lxb_url_parser_memory_destroy(lexbor_parser); + lxb_url_parser_destroy(lexbor_parser, true); + + lexbor_parser = NULL; + lexbor_urls = 0; + + zend_hash_destroy(&lexbor_property_handlers); + + return SUCCESS; +} diff --git a/ext/uri/php_lexbor.h b/ext/uri/php_lexbor.h new file mode 100644 index 0000000000000..af6c094b46620 --- /dev/null +++ b/ext/uri/php_lexbor.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 | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_LEXBOR_H +#define PHP_LEXBOR_H + +#include +#include + +extern const uri_handler_t lexbor_uri_handler; + +#endif diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index bd5c622ed3765..4f6092b0f5c95 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -22,16 +22,24 @@ #include "Zend/zend_interfaces.h" #include "Zend/zend_exceptions.h" #include "Zend/zend_attributes.h" +#include "Zend/zend_enum.h" #include "main/php_ini.h" #include "ext/standard/info.h" #include "php_uri.h" +#include "php_uri_common.h" +#include "php_lexbor.h" #include "php_uri_arginfo.h" #include "uriparser/src/UriConfig.h" +zend_class_entry *whatwg_url_ce; +zend_object_handlers whatwg_uri_object_handlers; +zend_class_entry *uri_comparison_mode_ce; zend_class_entry *uri_exception_ce; zend_class_entry *invalid_uri_exception_ce; zend_class_entry *whatwg_invalid_url_exception_ce; +zend_class_entry *whatwg_url_validation_error_type_ce; +zend_class_entry *whatwg_url_validation_error_ce; #define URIPARSER_VERSION PACKAGE_VERSION @@ -40,12 +48,565 @@ static const zend_module_dep uri_deps[] = { ZEND_MOD_END }; +static zend_array uri_handlers; + +static zend_object *uri_clone_obj_handler(zend_object *object); + +static uri_handler_t *uri_handler_by_name(const char *handler_name, size_t handler_name_len) +{ + return zend_hash_str_find_ptr(&uri_handlers, handler_name, handler_name_len); +} + +static HashTable *uri_get_debug_properties(zend_object *object) +{ + uri_internal_t *internal_uri = uri_internal_from_obj(object); + ZEND_ASSERT(internal_uri != NULL); + + zend_string *string_key; + uri_property_handler_t *property_handler; + + HashTable *std_properties = zend_std_get_properties(object); + HashTable *result = zend_array_dup(std_properties); + + if (UNEXPECTED(internal_uri->uri == NULL)) { + return result; + } + + zend_string *object_str = ZSTR_INIT_LITERAL("(object value omitted)", false); + + const HashTable *property_handlers = internal_uri->handler->property_handlers; + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(property_handlers, string_key, property_handler) { + zval value; + + ZEND_ASSERT(string_key != NULL); + + if (property_handler->read_func(internal_uri, URI_COMPONENT_READ_RAW, &value) == FAILURE) { + continue; + } + + if (Z_TYPE(value) == IS_OBJECT) { + zval_ptr_dtor(&value); + ZVAL_NEW_STR(&value, object_str); + zend_string_addref(object_str); + } + + zend_hash_update(result, string_key, &value); + } ZEND_HASH_FOREACH_END(); + + zend_string_release_ex(object_str, false); + + return result; +} + +PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) +{ + zend_string *context; + zval *type; + bool failure; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(context) + Z_PARAM_OBJECT_OF_CLASS(type, whatwg_url_validation_error_type_ce) + Z_PARAM_BOOL(failure) + ZEND_PARSE_PARAMETERS_END(); + + zend_update_property_str(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), "context", sizeof("context") - 1, context); + zend_update_property(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), "type", sizeof("type") - 1, type); + zval failure_zv; + ZVAL_BOOL(&failure_zv, failure); + zend_update_property(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), "failure", sizeof("failure") - 1, &failure_zv); + zval_ptr_dtor(&failure_zv); +} + +static zend_result pass_errors_by_ref(zval *errors_zv, zval *errors) +{ + ZEND_ASSERT(Z_TYPE_P(errors) == IS_UNDEF || Z_TYPE_P(errors) == IS_ARRAY); + + if (Z_TYPE_P(errors) != IS_ARRAY) { + return SUCCESS; + } + + if (errors_zv != NULL) { + errors_zv = zend_try_array_init(errors_zv); + if (!errors_zv) { + return FAILURE; + } + + zval *error; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(errors), error) { + Z_ADDREF_P(error); + zend_hash_next_index_insert_new(Z_ARRVAL_P(errors_zv), error); + } ZEND_HASH_FOREACH_END(); + } + + return SUCCESS; +} + +PHPAPI void php_uri_instantiate_uri( + INTERNAL_FUNCTION_PARAMETERS, const uri_handler_t *handler, const zend_string *uri_str, const zend_object *base_url_object, + bool should_throw, bool should_update_this_object, zval *errors_zv +) { + zval errors; + ZVAL_UNDEF(&errors); + + void *base_url = NULL; + if (base_url_object != NULL) { + uri_internal_t *internal_base_url = uri_internal_from_obj(base_url_object); + URI_ASSERT_INITIALIZATION(internal_base_url); + base_url = internal_base_url->uri; + } + + void *uri = handler->parse_uri(uri_str, base_url, should_throw || errors_zv != NULL ? &errors : NULL); + if (UNEXPECTED(uri == NULL)) { + if (should_throw) { + throw_invalid_uri_exception(handler, &errors); + zval_ptr_dtor(&errors); + RETURN_THROWS(); + } else { + pass_errors_by_ref(errors_zv, &errors); + zval_ptr_dtor(&errors); + RETURN_NULL(); + } + } + + pass_errors_by_ref(errors_zv, &errors); + zval_ptr_dtor(&errors); + + uri_object_t *uri_object; + if (should_update_this_object) { + uri_object = Z_URI_OBJECT_P(ZEND_THIS); + } else { + if (EX(func)->common.fn_flags & ZEND_ACC_STATIC) { + object_init_ex(return_value, Z_CE_P(ZEND_THIS)); + } else { + object_init_ex(return_value, Z_OBJCE_P(ZEND_THIS)); + } + uri_object = Z_URI_OBJECT_P(return_value); + } + + uri_object->internal.handler = handler; + uri_object->internal.uri = uri; +} + +static void create_whatwg_uri(INTERNAL_FUNCTION_PARAMETERS, bool is_constructor) +{ + zend_string *uri_str; + zend_object *base_url_object = NULL; + zval *errors = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_PATH_STR(uri_str) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(base_url_object, whatwg_url_ce) + Z_PARAM_ZVAL(errors) + ZEND_PARSE_PARAMETERS_END(); + + php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, &lexbor_uri_handler, uri_str, base_url_object, is_constructor, is_constructor, errors); +} + +PHP_METHOD(Uri_WhatWg_Url, parse) +{ + create_whatwg_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(Uri_WhatWg_Url, __construct) +{ + create_whatwg_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +static void uri_equals(INTERNAL_FUNCTION_PARAMETERS, zend_object *that_object, zend_object *comparison_mode) +{ + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *this_internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(this_internal_uri); + + uri_internal_t *that_internal_uri = uri_internal_from_obj(that_object); + URI_ASSERT_INITIALIZATION(that_internal_uri); + + if (this_object->ce != that_object->ce && + !instanceof_function(this_object->ce, that_object->ce) && + !instanceof_function(that_object->ce, this_object->ce) + ) { + RETURN_FALSE; + } + + bool exclude_fragment = true; + if (comparison_mode) { + zval *case_name = zend_enum_fetch_case_name(comparison_mode); + zend_string *comparison_mode_name = Z_STR_P(case_name); + + exclude_fragment = ZSTR_VAL(comparison_mode_name)[0] + ZSTR_LEN(comparison_mode_name) == 'E' + sizeof("ExcludeFragment") - 1; + } + + zend_string *this_str = this_internal_uri->handler->uri_to_string( + this_internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, exclude_fragment); + if (this_str == NULL) { + zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(this_object->ce->name)); + RETURN_THROWS(); + } + + zend_string *that_str = that_internal_uri->handler->uri_to_string( + that_internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, exclude_fragment); + if (that_str == NULL) { + zend_string_release(this_str); + zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(that_object->ce->name)); + RETURN_THROWS(); + } + + RETVAL_BOOL(zend_string_equals(this_str, that_str)); + + zend_string_release(this_str); + zend_string_release(that_str); +} + +static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_name) +{ + HashTable *data; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(data) + ZEND_PARSE_PARAMETERS_END(); + + zend_object *object = Z_OBJ_P(ZEND_THIS); + + /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */ + if (zend_hash_num_elements(data) != 2) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + /* Unserialize state: "uri" key in the first array */ + zval *arr = zend_hash_index_find(data, 0); + if (arr == NULL || Z_TYPE_P(arr) != IS_ARRAY) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + zval *uri_zv = zend_hash_str_find_ind(Z_ARRVAL_P(arr), URI_SERIALIZED_PROPERTY_NAME, sizeof(URI_SERIALIZED_PROPERTY_NAME) - 1); + if (uri_zv == NULL || Z_TYPE_P(uri_zv) != IS_STRING) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + zval errors; + ZVAL_UNDEF(&errors); + + uri_internal_t *internal_uri = uri_internal_from_obj(object); + internal_uri->handler = uri_handler_by_name(handler_name, strlen(handler_name)); + if (internal_uri->uri != NULL) { + internal_uri->handler->free_uri(internal_uri->uri); + } + internal_uri->uri = internal_uri->handler->parse_uri(Z_STR_P(uri_zv), NULL, &errors); + if (internal_uri->uri == NULL) { + throw_invalid_uri_exception(internal_uri->handler, &errors); + zval_ptr_dtor(&errors); + RETURN_THROWS(); + } + zval_ptr_dtor(&errors); + + /* Unserialize regular properties: second array */ + arr = zend_hash_index_find(data, 1); + if (arr == NULL || Z_TYPE_P(arr) != IS_ARRAY) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + object_properties_load(object, Z_ARRVAL_P(arr)); + if (EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } +} + +PHP_METHOD(Uri_WhatWg_Url, getScheme) +{ + URI_GETTER(ZSTR_KNOWN(ZEND_STR_SCHEME), URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withScheme) +{ + URI_WITHER_STR(ZSTR_KNOWN(ZEND_STR_SCHEME)); +} + +PHP_METHOD(Uri_WhatWg_Url, getUsername) +{ + URI_GETTER(ZSTR_KNOWN(ZEND_STR_USERNAME), URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withUsername) +{ + URI_WITHER_STR_OR_NULL(ZSTR_KNOWN(ZEND_STR_USERNAME)); +} + +PHP_METHOD(Uri_WhatWg_Url, getPassword) +{ + URI_GETTER(ZSTR_KNOWN(ZEND_STR_PASSWORD), URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withPassword) +{ + URI_WITHER_STR_OR_NULL(ZSTR_KNOWN(ZEND_STR_PASSWORD)); +} + +PHP_METHOD(Uri_WhatWg_Url, getAsciiHost) +{ + URI_GETTER(ZSTR_KNOWN(ZEND_STR_HOST), URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, getUnicodeHost) +{ + URI_GETTER(ZSTR_KNOWN(ZEND_STR_HOST), URI_COMPONENT_READ_NORMALIZED_UNICODE); +} + +PHP_METHOD(Uri_WhatWg_Url, withHost) +{ + zend_string *value; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_PATH_STR_OR_NULL(value) + ZEND_PARSE_PARAMETERS_END(); + + zval zv; + if (value == NULL) { + ZVAL_NULL(&zv); + } else { + ZVAL_STR_COPY(&zv, value); + } + + URI_WITHER_COMMON(ZSTR_KNOWN(ZEND_STR_HOST), &zv, return_value); +} + +PHP_METHOD(Uri_WhatWg_Url, getPort) +{ + URI_GETTER(ZSTR_KNOWN(ZEND_STR_PORT), URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withPort) +{ + URI_WITHER_LONG_OR_NULL(ZSTR_KNOWN(ZEND_STR_PORT)); +} + +PHP_METHOD(Uri_WhatWg_Url, getPath) +{ + URI_GETTER(ZSTR_KNOWN(ZEND_STR_PATH), URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withPath) +{ + URI_WITHER_STR(ZSTR_KNOWN(ZEND_STR_PATH)); +} + +PHP_METHOD(Uri_WhatWg_Url, getQuery) +{ + URI_GETTER(ZSTR_KNOWN(ZEND_STR_QUERY), URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withQuery) +{ + URI_WITHER_STR_OR_NULL(ZSTR_KNOWN(ZEND_STR_QUERY)); +} + +PHP_METHOD(Uri_WhatWg_Url, getFragment) +{ + URI_GETTER(ZSTR_KNOWN(ZEND_STR_FRAGMENT), URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withFragment) +{ + URI_WITHER_STR_OR_NULL(ZSTR_KNOWN(ZEND_STR_FRAGMENT)); +} + +PHP_METHOD(Uri_WhatWg_Url, equals) +{ + zend_object *that_object; + zend_object *comparison_mode = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJ_OF_CLASS(that_object, whatwg_url_ce) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS(comparison_mode, uri_comparison_mode_ce); + ZEND_PARSE_PARAMETERS_END(); + + uri_equals(INTERNAL_FUNCTION_PARAM_PASSTHRU, that_object, comparison_mode); +} + +PHP_METHOD(Uri_WhatWg_Url, toUnicodeString) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + RETURN_STR(internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_UNICODE, false)); +} + +PHP_METHOD(Uri_WhatWg_Url, toAsciiString) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + RETURN_STR(internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_ASCII, false)); +} + +PHP_METHOD(Uri_WhatWg_Url, resolve) +{ + zend_string *uri_str; + zval *errors = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_PATH_STR(uri_str) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(errors) + ZEND_PARSE_PARAMETERS_END(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, internal_uri->handler, uri_str, this_object, true, false, errors); +} + +PHP_METHOD(Uri_WhatWg_Url, __serialize) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + /* Serialize state: "uri" key in the first array */ + zend_string *uri_str = internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_ASCII, false); + if (uri_str == NULL) { + zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(this_object->ce->name)); + RETURN_THROWS(); + } + zval tmp; + ZVAL_STR(&tmp, uri_str); + + array_init(return_value); + + zval arr; + array_init(&arr); + zend_hash_str_add_new(Z_ARRVAL(arr), URI_SERIALIZED_PROPERTY_NAME, sizeof(URI_SERIALIZED_PROPERTY_NAME) - 1, &tmp); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &arr); + + /* Serialize regular properties: second array */ + ZVAL_ARR(&arr, this_object->handlers->get_properties(this_object)); + Z_TRY_ADDREF(arr); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &arr); +} + +PHP_METHOD(Uri_WhatWg_Url, __unserialize) +{ + uri_unserialize(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PARSER_WHATWG); +} + +PHP_METHOD(Uri_WhatWg_Url, __debugInfo) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *object = Z_OBJ_P(ZEND_THIS); + + RETURN_ARR(uri_get_debug_properties(object)); +} + +static zend_object *uri_create_object_handler(zend_class_entry *class_type) +{ + uri_object_t *uri_object = zend_object_alloc(sizeof(uri_object_t), class_type); + + uri_object->internal.uri = NULL; + uri_object->internal.handler = NULL; + + zend_object_std_init(&uri_object->std, class_type); + object_properties_init(&uri_object->std, class_type); + + return &uri_object->std; +} + +static void uri_free_obj_handler(zend_object *object) +{ + uri_object_t *uri_object = uri_object_from_obj(object); + ZEND_ASSERT(uri_object != NULL); + + if (UNEXPECTED(uri_object->internal.uri != NULL)) { + uri_object->internal.handler->free_uri(uri_object->internal.uri); + uri_object->internal.handler = NULL; + uri_object->internal.uri = NULL; + } + + zend_object_std_dtor(&uri_object->std); +} + +static zend_object *uri_clone_obj_handler(zend_object *object) +{ + uri_object_t *uri_object = uri_object_from_obj(object); + uri_internal_t *internal_uri = uri_internal_from_obj(object); + + zend_object *new_object = uri_create_object_handler(object->ce); + ZEND_ASSERT(new_object != NULL); + uri_object_t *new_uri_object = uri_object_from_obj(new_object); + + URI_ASSERT_INITIALIZATION(internal_uri); + + new_uri_object->internal.handler = internal_uri->handler; + + void *uri = internal_uri->handler->clone_uri(internal_uri->uri); + ZEND_ASSERT(uri != NULL); + + new_uri_object->internal.uri = uri; + + zend_objects_clone_members(&new_uri_object->std, &uri_object->std); + + return &new_uri_object->std; +} + +PHPAPI void php_uri_implementation_set_object_handlers(zend_class_entry *ce, zend_object_handlers *object_handlers) +{ + ce->create_object = uri_create_object_handler; + ce->default_object_handlers = object_handlers; + memcpy(object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + object_handlers->offset = XtOffsetOf(uri_object_t, std); + object_handlers->free_obj = uri_free_obj_handler; + object_handlers->clone_obj = uri_clone_obj_handler; +} + +zend_result uri_handler_register(const uri_handler_t *uri_handler) +{ + zend_string *key = zend_string_init_interned(uri_handler->name, strlen(uri_handler->name), 1); + + ZEND_ASSERT(uri_handler->name != NULL); + ZEND_ASSERT(uri_handler->init_parser != NULL); + ZEND_ASSERT(uri_handler->parse_uri != NULL); + ZEND_ASSERT(uri_handler->create_invalid_uri_exception != NULL); + ZEND_ASSERT(uri_handler->clone_uri != NULL); + ZEND_ASSERT(uri_handler->uri_to_string != NULL); + ZEND_ASSERT(uri_handler->free_uri != NULL); + ZEND_ASSERT(uri_handler->destroy_parser != NULL); + ZEND_ASSERT(uri_handler->property_handlers != NULL); + + return zend_hash_add_ptr(&uri_handlers, key, (void *) uri_handler) ? SUCCESS : FAILURE; +} static PHP_MINIT_FUNCTION(uri) { + whatwg_url_ce = register_class_Uri_WhatWg_Url(); + php_uri_implementation_set_object_handlers(whatwg_url_ce, &whatwg_uri_object_handlers); + + uri_comparison_mode_ce = register_class_Uri_UriComparisonMode(); uri_exception_ce = register_class_Uri_UriException(zend_ce_exception); invalid_uri_exception_ce = register_class_Uri_InvalidUriException(uri_exception_ce); whatwg_invalid_url_exception_ce = register_class_Uri_WhatWg_InvalidUrlException(invalid_uri_exception_ce); + whatwg_url_validation_error_ce = register_class_Uri_WhatWg_UrlValidationError(); + whatwg_url_validation_error_type_ce = register_class_Uri_WhatWg_UrlValidationErrorType(); + + zend_hash_init(&uri_handlers, 4, NULL, ZVAL_PTR_DTOR, true); + + if (uri_handler_register(&lexbor_uri_handler) == FAILURE) { + return FAILURE; + } return SUCCESS; } @@ -60,18 +621,34 @@ static PHP_MINFO_FUNCTION(uri) static PHP_MSHUTDOWN_FUNCTION(uri) { + zend_hash_destroy(&uri_handlers); return SUCCESS; } PHP_RINIT_FUNCTION(uri) { + uri_handler_t *handler; + + ZEND_HASH_MAP_FOREACH_PTR(&uri_handlers, handler) { + if (handler->init_parser() == FAILURE) { + return FAILURE; + } + } ZEND_HASH_FOREACH_END(); return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(uri) { + uri_handler_t *handler; + + ZEND_HASH_MAP_FOREACH_PTR(&uri_handlers, handler) { + if (handler->destroy_parser() == FAILURE) { + return FAILURE; + } + } ZEND_HASH_FOREACH_END(); + return SUCCESS; } diff --git a/ext/uri/php_uri.h b/ext/uri/php_uri.h index 79dfced4a721a..9e22c227cbf83 100644 --- a/ext/uri/php_uri.h +++ b/ext/uri/php_uri.h @@ -17,8 +17,11 @@ #ifndef PHP_URI_H #define PHP_URI_H +#include "php_uri_common.h" + extern zend_module_entry uri_module_entry; #define phpext_uri_ptr &uri_module_entry +PHPAPI void php_uri_implementation_set_object_handlers(zend_class_entry *ce, zend_object_handlers *object_handlers); #endif diff --git a/ext/uri/php_uri.stub.php b/ext/uri/php_uri.stub.php index 926be1fbb8267..2434a2eed34ba 100644 --- a/ext/uri/php_uri.stub.php +++ b/ext/uri/php_uri.stub.php @@ -12,6 +12,12 @@ class UriException extends \Exception class InvalidUriException extends \Uri\UriException { } + + enum UriComparisonMode + { + case IncludeFragment; + case ExcludeFragment; + } } namespace Uri\WhatWg { @@ -20,4 +26,106 @@ class InvalidUrlException extends \Uri\InvalidUriException { public readonly array $errors; } + + enum UrlValidationErrorType + { + case DomainToAscii; + case DomainToUnicode; + case DomainInvalidCodePoint; + case HostInvalidCodePoint; + case Ipv4EmptyPart; + case Ipv4TooManyParts; + case Ipv4NonNumericPart; + case Ipv4NonDecimalPart; + case Ipv4OutOfRangePart; + case Ipv6Unclosed; + case Ipv6InvalidCompression; + case Ipv6TooManyPieces; + case Ipv6MultipleCompression; + case Ipv6InvalidCodePoint; + case Ipv6TooFewPieces; + case Ipv4InIpv6TooManyPieces; + case Ipv4InIpv6InvalidCodePoint; + case Ipv4InIpv6OutOfRangePart; + case Ipv4InIpv6TooFewParts; + case InvalidUrlUnit; + case SpecialSchemeMissingFollowingSolidus; + case MissingSchemeNonRelativeUrl; + case InvalidReverseSoldius; + case InvalidCredentials; + case HostMissing; + case PortOutOfRange; + case PortInvalid; + case FileInvalidWindowsDriveLetter; + case FileInvalidWindowsDriveLetterHost; + } + + /** @strict-properties */ + final readonly class UrlValidationError + { + public string $context; + public \Uri\WhatWg\UrlValidationErrorType $type; + public bool $failure; + + public function __construct(string $context, \Uri\WhatWg\UrlValidationErrorType $type, bool $failure) {} + } + + /** @strict-properties */ + final readonly class Url + { + /** @param array $errors */ + public static function parse(string $uri, ?\Uri\WhatWg\Url $baseUrl = null, &$errors = null): ?static {} + + /** @param array $softErrors */ + public function __construct(string $uri, ?\Uri\WhatWg\Url $baseUrl = null, &$softErrors = null) {} + + public function getScheme(): string {} + + public function withScheme(string $scheme): static {} + + public function getUsername(): ?string {} + + public function withUsername(?string $username): static {} + + public function getPassword(): ?string {} + + public function withPassword(#[\SensitiveParameter] ?string $password): static {} + + public function getAsciiHost(): ?string {} + + public function getUnicodeHost(): ?string {} + + public function withHost(?string $host): static {} + + public function getPort(): ?int {} + + public function withPort(?int $port): static {} + + public function getPath(): string {} + + public function withPath(string $path): static {} + + public function getQuery(): ?string {} + + public function withQuery(?string $query): static {} + + public function getFragment(): ?string {} + + public function withFragment(?string $fragment): static {} + + public function equals(\Uri\WhatWg\Url $url, \Uri\UriComparisonMode $comparisonMode = \Uri\UriComparisonMode::ExcludeFragment): bool {} + + public function toAsciiString(): string {} + + public function toUnicodeString(): string {} + + /** @param array $softErrors */ + public function resolve(string $uri, &$softErrors = null): static {} + + public function __serialize(): array {} + + public function __unserialize(array $data): void {} + + public function __debugInfo(): array {} + } } diff --git a/ext/uri/php_uri_arginfo.h b/ext/uri/php_uri_arginfo.h index 124a6b3719849..1adf8f71eb217 100644 --- a/ext/uri/php_uri_arginfo.h +++ b/ext/uri/php_uri_arginfo.h @@ -1,5 +1,162 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 35460b24cf237585dabdc9813212c343034cf591 */ + * Stub hash: 24e5f7ae5b0f3938a6c3e5e733131a05d10e4e3b */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_UrlValidationError___construct, 0, 0, 3) + ZEND_ARG_TYPE_INFO(0, context, IS_STRING, 0) + ZEND_ARG_OBJ_INFO(0, type, Uri\\WhatWg\\\125rlValidationErrorType, 0) + ZEND_ARG_TYPE_INFO(0, failure, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_parse, 0, 1, IS_STATIC, 1) + ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, baseUrl, Uri\\WhatWg\\\125rl, 1, "null") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, errors, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_Url___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, baseUrl, Uri\\WhatWg\\\125rl, 1, "null") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, softErrors, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_getScheme, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withScheme, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, scheme, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_getUsername, 0, 0, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withUsername, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, username, IS_STRING, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_getPassword arginfo_class_Uri_WhatWg_Url_getUsername + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPassword, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_getAsciiHost arginfo_class_Uri_WhatWg_Url_getUsername + +#define arginfo_class_Uri_WhatWg_Url_getUnicodeHost arginfo_class_Uri_WhatWg_Url_getUsername + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withHost, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_getPort, 0, 0, IS_LONG, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPort, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_getPath arginfo_class_Uri_WhatWg_Url_getScheme + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPath, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_getQuery arginfo_class_Uri_WhatWg_Url_getUsername + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withQuery, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_getFragment arginfo_class_Uri_WhatWg_Url_getUsername + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withFragment, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, fragment, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_equals, 0, 1, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, url, Uri\\WhatWg\\\125rl, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, comparisonMode, Uri\\\125riComparisonMode, 0, "Uri\\UriComparisonMode::ExcludeFragment") +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_toAsciiString arginfo_class_Uri_WhatWg_Url_getScheme + +#define arginfo_class_Uri_WhatWg_Url_toUnicodeString arginfo_class_Uri_WhatWg_Url_getScheme + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_resolve, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, softErrors, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url___serialize, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url___unserialize, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url___debugInfo arginfo_class_Uri_WhatWg_Url___serialize + +ZEND_METHOD(Uri_WhatWg_UrlValidationError, __construct); +ZEND_METHOD(Uri_WhatWg_Url, parse); +ZEND_METHOD(Uri_WhatWg_Url, __construct); +ZEND_METHOD(Uri_WhatWg_Url, getScheme); +ZEND_METHOD(Uri_WhatWg_Url, withScheme); +ZEND_METHOD(Uri_WhatWg_Url, getUsername); +ZEND_METHOD(Uri_WhatWg_Url, withUsername); +ZEND_METHOD(Uri_WhatWg_Url, getPassword); +ZEND_METHOD(Uri_WhatWg_Url, withPassword); +ZEND_METHOD(Uri_WhatWg_Url, getAsciiHost); +ZEND_METHOD(Uri_WhatWg_Url, getUnicodeHost); +ZEND_METHOD(Uri_WhatWg_Url, withHost); +ZEND_METHOD(Uri_WhatWg_Url, getPort); +ZEND_METHOD(Uri_WhatWg_Url, withPort); +ZEND_METHOD(Uri_WhatWg_Url, getPath); +ZEND_METHOD(Uri_WhatWg_Url, withPath); +ZEND_METHOD(Uri_WhatWg_Url, getQuery); +ZEND_METHOD(Uri_WhatWg_Url, withQuery); +ZEND_METHOD(Uri_WhatWg_Url, getFragment); +ZEND_METHOD(Uri_WhatWg_Url, withFragment); +ZEND_METHOD(Uri_WhatWg_Url, equals); +ZEND_METHOD(Uri_WhatWg_Url, toAsciiString); +ZEND_METHOD(Uri_WhatWg_Url, toUnicodeString); +ZEND_METHOD(Uri_WhatWg_Url, resolve); +ZEND_METHOD(Uri_WhatWg_Url, __serialize); +ZEND_METHOD(Uri_WhatWg_Url, __unserialize); +ZEND_METHOD(Uri_WhatWg_Url, __debugInfo); + +static const zend_function_entry class_Uri_WhatWg_UrlValidationError_methods[] = { + ZEND_ME(Uri_WhatWg_UrlValidationError, __construct, arginfo_class_Uri_WhatWg_UrlValidationError___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static const zend_function_entry class_Uri_WhatWg_Url_methods[] = { + ZEND_ME(Uri_WhatWg_Url, parse, arginfo_class_Uri_WhatWg_Url_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(Uri_WhatWg_Url, __construct, arginfo_class_Uri_WhatWg_Url___construct, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getScheme, arginfo_class_Uri_WhatWg_Url_getScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withScheme, arginfo_class_Uri_WhatWg_Url_withScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getUsername, arginfo_class_Uri_WhatWg_Url_getUsername, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withUsername, arginfo_class_Uri_WhatWg_Url_withUsername, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getPassword, arginfo_class_Uri_WhatWg_Url_getPassword, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withPassword, arginfo_class_Uri_WhatWg_Url_withPassword, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getAsciiHost, arginfo_class_Uri_WhatWg_Url_getAsciiHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getUnicodeHost, arginfo_class_Uri_WhatWg_Url_getUnicodeHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withHost, arginfo_class_Uri_WhatWg_Url_withHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getPort, arginfo_class_Uri_WhatWg_Url_getPort, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withPort, arginfo_class_Uri_WhatWg_Url_withPort, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getPath, arginfo_class_Uri_WhatWg_Url_getPath, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withPath, arginfo_class_Uri_WhatWg_Url_withPath, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getQuery, arginfo_class_Uri_WhatWg_Url_getQuery, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withQuery, arginfo_class_Uri_WhatWg_Url_withQuery, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getFragment, arginfo_class_Uri_WhatWg_Url_getFragment, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withFragment, arginfo_class_Uri_WhatWg_Url_withFragment, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, equals, arginfo_class_Uri_WhatWg_Url_equals, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, toAsciiString, arginfo_class_Uri_WhatWg_Url_toAsciiString, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, toUnicodeString, arginfo_class_Uri_WhatWg_Url_toUnicodeString, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, resolve, arginfo_class_Uri_WhatWg_Url_resolve, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, __serialize, arginfo_class_Uri_WhatWg_Url___serialize, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, __unserialize, arginfo_class_Uri_WhatWg_Url___unserialize, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, __debugInfo, arginfo_class_Uri_WhatWg_Url___debugInfo, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; static zend_class_entry *register_class_Uri_UriException(zend_class_entry *class_entry_Exception) { @@ -21,6 +178,17 @@ static zend_class_entry *register_class_Uri_InvalidUriException(zend_class_entry return class_entry; } +static zend_class_entry *register_class_Uri_UriComparisonMode(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\UriComparisonMode", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "IncludeFragment", NULL); + + zend_enum_add_case_cstr(class_entry, "ExcludeFragment", NULL); + + return class_entry; +} + static zend_class_entry *register_class_Uri_WhatWg_InvalidUrlException(zend_class_entry *class_entry_Uri_InvalidUriException) { zend_class_entry ce, *class_entry; @@ -36,3 +204,108 @@ static zend_class_entry *register_class_Uri_WhatWg_InvalidUrlException(zend_clas return class_entry; } + +static zend_class_entry *register_class_Uri_WhatWg_UrlValidationErrorType(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\WhatWg\\UrlValidationErrorType", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "DomainToAscii", NULL); + + zend_enum_add_case_cstr(class_entry, "DomainToUnicode", NULL); + + zend_enum_add_case_cstr(class_entry, "DomainInvalidCodePoint", NULL); + + zend_enum_add_case_cstr(class_entry, "HostInvalidCodePoint", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4EmptyPart", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4TooManyParts", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4NonNumericPart", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4NonDecimalPart", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4OutOfRangePart", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6Unclosed", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6InvalidCompression", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6TooManyPieces", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6MultipleCompression", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6InvalidCodePoint", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6TooFewPieces", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4InIpv6TooManyPieces", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4InIpv6InvalidCodePoint", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4InIpv6OutOfRangePart", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4InIpv6TooFewParts", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidUrlUnit", NULL); + + zend_enum_add_case_cstr(class_entry, "SpecialSchemeMissingFollowingSolidus", NULL); + + zend_enum_add_case_cstr(class_entry, "MissingSchemeNonRelativeUrl", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidReverseSoldius", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidCredentials", NULL); + + zend_enum_add_case_cstr(class_entry, "HostMissing", NULL); + + zend_enum_add_case_cstr(class_entry, "PortOutOfRange", NULL); + + zend_enum_add_case_cstr(class_entry, "PortInvalid", NULL); + + zend_enum_add_case_cstr(class_entry, "FileInvalidWindowsDriveLetter", NULL); + + zend_enum_add_case_cstr(class_entry, "FileInvalidWindowsDriveLetterHost", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_Uri_WhatWg_UrlValidationError(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Uri\\WhatWg", "UrlValidationError", class_Uri_WhatWg_UrlValidationError_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_READONLY_CLASS); + + zval property_context_default_value; + ZVAL_UNDEF(&property_context_default_value); + zend_string *property_context_name = zend_string_init("context", sizeof("context") - 1, 1); + zend_declare_typed_property(class_entry, property_context_name, &property_context_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_context_name); + + zval property_type_default_value; + ZVAL_UNDEF(&property_type_default_value); + zend_string *property_type_class_Uri_WhatWg_UrlValidationErrorType = zend_string_init("Uri\\WhatWg\\\125rlValidationErrorType", sizeof("Uri\\WhatWg\\\125rlValidationErrorType")-1, 1); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_TYPE), &property_type_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_type_class_Uri_WhatWg_UrlValidationErrorType, 0, 0)); + + zval property_failure_default_value; + ZVAL_UNDEF(&property_failure_default_value); + zend_string *property_failure_name = zend_string_init("failure", sizeof("failure") - 1, 1); + zend_declare_typed_property(class_entry, property_failure_name, &property_failure_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_failure_name); + + return class_entry; +} + +static zend_class_entry *register_class_Uri_WhatWg_Url(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Uri\\WhatWg", "Url", class_Uri_WhatWg_Url_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_READONLY_CLASS); + + + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "withpassword", sizeof("withpassword") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + return class_entry; +} diff --git a/ext/uri/php_uri_common.c b/ext/uri/php_uri_common.c new file mode 100644 index 0000000000000..768597f420c19 --- /dev/null +++ b/ext/uri/php_uri_common.c @@ -0,0 +1,40 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "Zend/zend_interfaces.h" +#include "Zend/zend_exceptions.h" +#include "php_uri_common.h" + +void uri_register_property_handler(HashTable *property_handlers, zend_string *name, const uri_property_handler_t *handler) +{ + zend_hash_add_new_ptr(property_handlers, name, (void *) handler); + zend_string_release_ex(name, true); +} + +uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name) +{ + return zend_hash_find_ptr(internal_uri->handler->property_handlers, name); +} + +void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors) +{ + zval exception_zv; + + uri_handler->create_invalid_uri_exception(&exception_zv, errors); + + zend_throw_exception_object(&exception_zv); +} diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h new file mode 100644 index 0000000000000..e69cc0aae5aec --- /dev/null +++ b/ext/uri/php_uri_common.h @@ -0,0 +1,190 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_URI_COMMON_H +#define PHP_URI_COMMON_H + +extern zend_class_entry *whatwg_url_ce; +extern zend_object_handlers whatwg_uri_object_handlers; +extern zend_class_entry *uri_comparison_mode_ce; +extern zend_class_entry *uri_exception_ce; +extern zend_class_entry *invalid_uri_exception_ce; +extern zend_class_entry *whatwg_invalid_url_exception_ce; +extern zend_class_entry *whatwg_url_validation_error_type_ce; +extern zend_class_entry *whatwg_url_validation_error_ce; + +typedef enum { + URI_RECOMPOSITION_RAW_ASCII, + URI_RECOMPOSITION_RAW_UNICODE, + URI_RECOMPOSITION_NORMALIZED_ASCII, + URI_RECOMPOSITION_NORMALIZED_UNICODE, +} uri_recomposition_mode_t; + +typedef enum { + URI_COMPONENT_READ_RAW, + URI_COMPONENT_READ_NORMALIZED_ASCII, + URI_COMPONENT_READ_NORMALIZED_UNICODE, +} uri_component_read_mode_t; + +typedef struct uri_handler_t { + const char *name; + + zend_result (*init_parser)(void); + void *(*parse_uri)(const zend_string *uri_str, const void *base_url, zval *errors); + void (*create_invalid_uri_exception)(zval *exception_zv, zval *errors); + void *(*clone_uri)(void *uri); + zend_string *(*uri_to_string)(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment); + void (*free_uri)(void *uri); + zend_result (*destroy_parser)(void); + HashTable *property_handlers; +} uri_handler_t; + +typedef struct uri_internal_t { + const uri_handler_t *handler; + void *uri; +} uri_internal_t; + +typedef struct uri_object_t { + uri_internal_t internal; + zend_object std; +} uri_object_t; + +static inline uri_object_t *uri_object_from_obj(const zend_object *object) { + return (uri_object_t*)((char*)(object) - XtOffsetOf(uri_object_t, std)); +} + +static inline uri_internal_t *uri_internal_from_obj(const zend_object *object) { + return &(uri_object_from_obj(object)->internal); +} + +#define Z_URI_OBJECT_P(zv) uri_object_from_obj(Z_OBJ_P((zv))) +#define Z_URI_INTERNAL_P(zv) uri_internal_from_obj(Z_OBJ_P((zv))) + +#define URI_PARSER_WHATWG "Uri\\WhatWg\\Url" +#define URI_SERIALIZED_PROPERTY_NAME "uri" + +typedef zend_result (*uri_read_t)(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval); + +typedef zend_result (*uri_write_t)(uri_internal_t *internal_uri, zval *value, zval *errors); + +typedef struct uri_property_handler_t { + uri_read_t read_func; + uri_write_t write_func; +} uri_property_handler_t; + +#define URI_REGISTER_PROPERTY_READ_HANDLER(property_handlers, name, property_read_func) do { \ + static const uri_property_handler_t handler = {.read_func = property_read_func, .write_func = NULL}; \ + uri_register_property_handler(property_handlers, name, &handler); \ +} while (0) + +#define URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(property_handlers, name, property_read_func, property_write_func) do { \ + static const uri_property_handler_t handler = {.read_func = property_read_func, .write_func = property_write_func}; \ + uri_register_property_handler(property_handlers, name, &handler); \ +} while (0) + +void uri_register_property_handler(HashTable *property_handlers, zend_string *name, const uri_property_handler_t *handler); +zend_result uri_handler_register(const uri_handler_t *uri_handler); +uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name); +void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors); + +#define URI_ASSERT_INITIALIZATION(internal_uri) do { \ + ZEND_ASSERT(internal_uri != NULL && internal_uri->uri != NULL); \ +} while (0) + +#define URI_GETTER(property_name, component_read_mode) do { \ + ZEND_PARSE_PARAMETERS_NONE(); \ + uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); \ + URI_ASSERT_INITIALIZATION(internal_uri); \ + const uri_property_handler_t *property_handler = uri_property_handler_from_internal_uri(internal_uri, property_name); \ + ZEND_ASSERT(property_handler != NULL); \ + if (UNEXPECTED(property_handler->read_func(internal_uri, component_read_mode, return_value) == FAILURE)) { \ + zend_throw_error(NULL, "%s::$%s property cannot be retrieved", ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(property_name)); \ + RETURN_THROWS(); \ + } \ +} while (0) + +#define URI_WITHER_COMMON(property_name, property_zv, return_value) \ + uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); \ + URI_ASSERT_INITIALIZATION(internal_uri); \ + const uri_property_handler_t *property_handler = uri_property_handler_from_internal_uri(internal_uri, property_name); \ + ZEND_ASSERT(property_handler != NULL); \ + zend_object *new_object = uri_clone_obj_handler(Z_OBJ_P(ZEND_THIS)); \ + if (UNEXPECTED(EG(exception) != NULL)) { \ + zend_object_release(new_object); \ + zval_ptr_dtor(property_zv); \ + RETURN_THROWS(); \ + } \ + uri_internal_t *new_internal_uri = uri_internal_from_obj(new_object); \ + URI_ASSERT_INITIALIZATION(new_internal_uri); /* TODO fix memory leak of new_object */ \ + if (property_handler->write_func == NULL) { \ + zend_readonly_property_modification_error_ex(ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(property_name)); \ + zend_object_release(new_object); \ + zval_ptr_dtor(property_zv); \ + RETURN_THROWS(); \ + } \ + zval errors; \ + ZVAL_UNDEF(&errors); \ + if (property_handler->write_func(new_internal_uri, property_zv, &errors) == FAILURE) { \ + throw_invalid_uri_exception(new_internal_uri->handler, &errors); \ + zval_ptr_dtor(&errors); \ + zend_object_release(new_object); \ + zval_ptr_dtor(property_zv); \ + RETURN_THROWS(); \ + } \ + ZEND_ASSERT(Z_TYPE(errors) == IS_UNDEF); \ + ZVAL_OBJ(return_value, new_object); \ + zval_ptr_dtor(property_zv); + +#define URI_WITHER_STR(property_name) do { \ + zend_string *value; \ + ZEND_PARSE_PARAMETERS_START(1, 1) \ + Z_PARAM_PATH_STR(value) \ + ZEND_PARSE_PARAMETERS_END(); \ + zval zv; \ + ZVAL_STR_COPY(&zv, value); \ + URI_WITHER_COMMON(property_name, &zv, return_value) \ +} while (0) + +#define URI_WITHER_STR_OR_NULL(property_name) do { \ + zend_string *value; \ + ZEND_PARSE_PARAMETERS_START(1, 1) \ + Z_PARAM_PATH_STR_OR_NULL(value) \ + ZEND_PARSE_PARAMETERS_END(); \ + zval zv; \ + if (value == NULL) { \ + ZVAL_NULL(&zv); \ + } else { \ + ZVAL_STR_COPY(&zv, value); \ + } \ + URI_WITHER_COMMON(property_name, &zv, return_value) \ +} while (0) + +#define URI_WITHER_LONG_OR_NULL(property_name) do { \ + zend_long value; \ + bool value_is_null; \ + ZEND_PARSE_PARAMETERS_START(1, 1) \ + Z_PARAM_LONG_OR_NULL(value, value_is_null) \ + ZEND_PARSE_PARAMETERS_END(); \ + zval zv; \ + if (value_is_null) {\ + ZVAL_NULL(&zv); \ + } else { \ + ZVAL_LONG(&zv, value); \ + } \ + URI_WITHER_COMMON(property_name, &zv, return_value); \ +} while (0) + +#endif diff --git a/ext/uri/tests/003.phpt b/ext/uri/tests/003.phpt new file mode 100644 index 0000000000000..bcd6e417441c2 --- /dev/null +++ b/ext/uri/tests/003.phpt @@ -0,0 +1,32 @@ +--TEST-- +Parse URL exotic URLs +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + string(8) "username" + ["password"]=> + string(8) "password" + ["host"]=> + string(18) "xn--hostname-b1aaa" + ["port"]=> + int(9090) + ["path"]=> + string(5) "/path" + ["query"]=> + string(14) "arg=va%C3%A9ue" + ["fragment"]=> + string(6) "anchor" +} +NULL diff --git a/ext/uri/tests/004.phpt b/ext/uri/tests/004.phpt new file mode 100644 index 0000000000000..04127a7ded0d3 --- /dev/null +++ b/ext/uri/tests/004.phpt @@ -0,0 +1,25 @@ +--TEST-- +Parse invalid URLs +--EXTENSIONS-- +uri +--FILE-- +getMessage() . "\n"; +} + +var_dump(Uri\WhatWg\Url::parse("")); + +var_dump(Uri\WhatWg\Url::parse("192.168/contact.html", null)); + +var_dump(Uri\WhatWg\Url::parse("http://RuPaul's Drag Race All Stars 7 Winners Cast on This Season's", null)); + +?> +--EXPECTF-- +URL parsing failed +NULL +NULL +NULL diff --git a/ext/uri/tests/005.phpt b/ext/uri/tests/005.phpt new file mode 100644 index 0000000000000..262d43a75406b --- /dev/null +++ b/ext/uri/tests/005.phpt @@ -0,0 +1,38 @@ +--TEST-- +Parse multibyte URLs +--EXTENSIONS-- +uri +--FILE-- +getAsciiHost()); +var_dump($url->getUnicodeHost()); +var_dump($url->toAsciiString()); +var_dump($url->toUnicodeString()); + +?> +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + string(8) "username" + ["password"]=> + string(8) "password" + ["host"]=> + string(18) "xn--hostname-b1aaa" + ["port"]=> + int(9090) + ["path"]=> + string(5) "/path" + ["query"]=> + string(14) "arg=va%C3%A9ue" + ["fragment"]=> + string(6) "anchor" +} +string(18) "xn--hostname-b1aaa" +string(14) "héééostname" +string(75) "http://username:password@xn--hostname-b1aaa:9090/path?arg=va%C3%A9ue#anchor" +string(71) "http://username:password@héééostname:9090/path?arg=va%C3%A9ue#anchor" diff --git a/ext/uri/tests/006.phpt b/ext/uri/tests/006.phpt new file mode 100644 index 0000000000000..0aba3e9e46b5e --- /dev/null +++ b/ext/uri/tests/006.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test successful manual Uri child instance creation +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(8) "username" + ["password"]=> + string(8) "password" + ["host"]=> + string(11) "example.com" + ["port"]=> + int(8080) + ["path"]=> + string(5) "/path" + ["query"]=> + string(3) "q=r" + ["fragment"]=> + string(8) "fragment" +} diff --git a/ext/uri/tests/007.phpt b/ext/uri/tests/007.phpt new file mode 100644 index 0000000000000..e60e69fc113a3 --- /dev/null +++ b/ext/uri/tests/007.phpt @@ -0,0 +1,63 @@ +--TEST-- +Test URI creation errors +--EXTENSIONS-- +uri +--FILE-- +getMessage() . "\n"; + var_dump($e->errors); +} + +$failures = []; +$url = new Uri\WhatWg\Url(" https://example.org ", null, $failures); +var_dump($url->toAsciiString()); +var_dump($failures); + +?> +--EXPECTF-- +URL parsing failed +array(%d) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(26) "password/path?q=r#fragment" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::PortInvalid) + ["failure"]=> + bool(true) + } + [1]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(36) "@username:password/path?q=r#fragment" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidCredentials) + ["failure"]=> + bool(false) + } +} +string(20) "https://example.org/" +array(2) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(1) " " + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit) + ["failure"]=> + bool(false) + } + [1]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(21) " https://example.org " + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit) + ["failure"]=> + bool(false) + } +} diff --git a/ext/uri/tests/008.phpt b/ext/uri/tests/008.phpt new file mode 100644 index 0000000000000..f4fddcd8eb777 --- /dev/null +++ b/ext/uri/tests/008.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri getters +--EXTENSIONS-- +uri +--FILE-- +getScheme()); + var_dump($url->getUsername()); + var_dump($url->getPassword()); + var_dump($url->getAsciiHost()); + var_dump($url->getUnicodeHost()); + var_dump($url->getPort()); + var_dump($url->getPath()); + var_dump($url->getQuery()); + var_dump($url->getFragment()); +} + +$url = Uri\WhatWg\Url::parse("https://username:password@www.google.com:8080/pathname1/pathname2/pathname3?query=true#hash-exists"); +callWhatWgGetters($url); + +?> +--EXPECT-- +string(5) "https" +string(8) "username" +string(8) "password" +string(14) "www.google.com" +string(14) "www.google.com" +int(8080) +string(30) "/pathname1/pathname2/pathname3" +string(10) "query=true" +string(11) "hash-exists" diff --git a/ext/uri/tests/009.phpt b/ext/uri/tests/009.phpt new file mode 100644 index 0000000000000..1b279588c0167 --- /dev/null +++ b/ext/uri/tests/009.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test parsing with IANA schemes +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(16) "chrome-extension" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/010.phpt b/ext/uri/tests/010.phpt new file mode 100644 index 0000000000000..4ec13f652f60c --- /dev/null +++ b/ext/uri/tests/010.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test parsing URIs when a base URI is present +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(14) "/path/to/file1" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(8) "test.com" + ["port"]=> + NULL + ["path"]=> + string(14) "/path/to/file1" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/011.phpt b/ext/uri/tests/011.phpt new file mode 100644 index 0000000000000..283886fb34fbb --- /dev/null +++ b/ext/uri/tests/011.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test encoding and normalization +--EXTENSIONS-- +uri +--FILE-- +toAsciiString()); +var_dump(Uri\WhatWg\Url::parse("https://www.example.com:443/dir1/../dir2")->toAsciiString()); +var_dump(Uri\WhatWg\Url::parse("https://你好你好")->toAsciiString()); +var_dump(Uri\WhatWg\Url::parse("https://你好你好")->toUnicodeString()); +var_dump(Uri\WhatWg\Url::parse("https://0Xc0.0250.01")->toAsciiString()); +var_dump(Uri\WhatWg\Url::parse("HttPs://0300.0250.0000.0001/path?query=foo%20bar")->toAsciiString()); + +?> +--EXPECT-- +string(23) "http://www.example.com/" +string(28) "https://www.example.com/dir2" +string(23) "https://xn--6qqa088eba/" +string(21) "https://你好你好/" +string(20) "https://192.168.0.1/" +string(40) "https://192.168.0.1/path?query=foo%20bar" diff --git a/ext/uri/tests/012.phpt b/ext/uri/tests/012.phpt new file mode 100644 index 0000000000000..0784a74e625f0 --- /dev/null +++ b/ext/uri/tests/012.phpt @@ -0,0 +1,49 @@ +--TEST-- +Test parsing of various schemes +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(6) "mailto" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(15) "Joe@Example.COM" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "file" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(30) "/E:/Documents%20and%20Settings" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/013.phpt b/ext/uri/tests/013.phpt new file mode 100644 index 0000000000000..016fe6632782c --- /dev/null +++ b/ext/uri/tests/013.phpt @@ -0,0 +1,87 @@ +--TEST-- +Test parsing of query strings +--EXTENSIONS-- +uri +--FILE-- + + @")); + +?> +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + string(25) "foo=Hell%C3%B3+W%C3%B6rld" + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + string(27) "foo=Hell%C3%B3%20W%C3%B6rld" + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + string(30) "foobar=%27%3Cscript%3E+%2B+%40" + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + string(30) "foobar=%27%3Cscript%3E%20+%20@" + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/014.phpt b/ext/uri/tests/014.phpt new file mode 100644 index 0000000000000..224b0c9b64fb4 --- /dev/null +++ b/ext/uri/tests/014.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test recomposition of URIs +--EXTENSIONS-- +uri +--FILE-- +toAsciiString()); + +?> +--EXPECT-- +string(45) "http://example.com/?foo=Hell%C3%B3+W%C3%B6rld" diff --git a/ext/uri/tests/015.phpt b/ext/uri/tests/015.phpt new file mode 100644 index 0000000000000..4df353e942186 --- /dev/null +++ b/ext/uri/tests/015.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test instantiation without calling constructor +--EXTENSIONS-- +reflection +uri +--FILE-- +newInstanceWithoutConstructor(); +} catch (ReflectionException $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Class Uri\WhatWg\Url is an internal class marked as final that cannot be instantiated without invoking its constructor diff --git a/ext/uri/tests/018.phpt b/ext/uri/tests/018.phpt new file mode 100644 index 0000000000000..bf8caffb5e7ec --- /dev/null +++ b/ext/uri/tests/018.phpt @@ -0,0 +1,50 @@ +--TEST-- +Test property mutation +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#1 (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#2 (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/019.phpt b/ext/uri/tests/019.phpt new file mode 100644 index 0000000000000..df19fb14196ac --- /dev/null +++ b/ext/uri/tests/019.phpt @@ -0,0 +1,55 @@ +--TEST-- +Test IDNA support +--EXTENSIONS-- +uri +--FILE-- +getAsciiHost()); +var_dump($url->getUnicodeHost()); +var_dump($url->toAsciiString()); +var_dump($url->toUnicodeString()); + +?> +--EXPECTF-- +NULL +array(1) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(4) "🐘" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::MissingSchemeNonRelativeUrl) + ["failure"]=> + bool(true) + } +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(12) "xn--go8h.com" + ["port"]=> + NULL + ["path"]=> + string(13) "/%F0%9F%90%98" + ["query"]=> + string(25) "%F0%9F%90%98=%F0%9F%90%98" + ["fragment"]=> + NULL +} +string(12) "xn--go8h.com" +string(8) "🐘.com" +string(59) "https://xn--go8h.com/%F0%9F%90%98?%F0%9F%90%98=%F0%9F%90%98" +string(55) "https://🐘.com/%F0%9F%90%98?%F0%9F%90%98=%F0%9F%90%98" diff --git a/ext/uri/tests/022.phpt b/ext/uri/tests/022.phpt new file mode 100644 index 0000000000000..1e920c5055f81 --- /dev/null +++ b/ext/uri/tests/022.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test extension of Uri\WhatWg\Url +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +Fatal error: Class MyWhatWgUri cannot extend final class Uri\WhatWg\Url in %s on line %d diff --git a/ext/uri/tests/023.phpt b/ext/uri/tests/023.phpt new file mode 100644 index 0000000000000..b48e2df838eef --- /dev/null +++ b/ext/uri/tests/023.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test property mutation - scheme +--EXTENSIONS-- +uri +--FILE-- +withScheme("http"); + +var_dump($url1->getScheme()); +var_dump($url2->getScheme()); + +try { + $url2->withScheme(""); +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +try { + $url2->withScheme("http%73"); +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +string(5) "https" +string(4) "http" +URL parsing failed +URL parsing failed diff --git a/ext/uri/tests/024.phpt b/ext/uri/tests/024.phpt new file mode 100644 index 0000000000000..907be0091d7c7 --- /dev/null +++ b/ext/uri/tests/024.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test property mutation - username +--EXTENSIONS-- +uri +--FILE-- +withUsername("user"); +$url3 = $url2->withUsername(null); +$url4 = $url3->withUsername("%75s%2Fr"); // us/r +$url5 = $url4->withUsername("u:s/r"); + +$url6 = Uri\WhatWg\Url::parse("file:///foo/bar/"); +$url6 = $url6->withUsername("user"); + +var_dump($url2->getUsername()); +var_dump($url3->getUsername()); +var_dump($url4->getUsername()); +var_dump($url5->getUsername()); +var_dump($url6->getUsername()); + +?> +--EXPECT-- +string(4) "user" +NULL +string(8) "%75s%2Fr" +string(9) "u%3As%2Fr" +NULL diff --git a/ext/uri/tests/025.phpt b/ext/uri/tests/025.phpt new file mode 100644 index 0000000000000..dafc36043bcbc --- /dev/null +++ b/ext/uri/tests/025.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test property mutation - password +--EXTENSIONS-- +uri +--FILE-- +withPassword("pass"); +$url3 = $url2->withPassword(null); +$url4 = $url3->withPassword("p%61ss"); +$url5 = $url4->withPassword("p:s/"); + +$url6 = Uri\WhatWg\Url::parse("file:///foo/bar/"); +$url6 = $url6->withUsername("pass"); + +var_dump($url2->getPassword()); +var_dump($url3->getPassword()); +var_dump($url4->getPassword()); +var_dump($url5->getPassword()); +var_dump($url6->getPassword()); + +?> +--EXPECT-- +string(4) "pass" +NULL +string(6) "p%61ss" +string(8) "p%3As%2F" +NULL diff --git a/ext/uri/tests/026.phpt b/ext/uri/tests/026.phpt new file mode 100644 index 0000000000000..4640ebebae52d --- /dev/null +++ b/ext/uri/tests/026.phpt @@ -0,0 +1,67 @@ +--TEST-- +Test property mutation - host +--EXTENSIONS-- +uri +--FILE-- +withHost("test.com"); +$url3 = $url2->withHost("t%65st.com"); // test.com +$url4 = $url3->withHost("test.com:8080"); + +var_dump($url1->getAsciiHost()); +var_dump($url2->getAsciiHost()); +var_dump($url3->getAsciiHost()); +var_dump($url4->getAsciiHost()); +var_dump($url4->getPort()); + +try { + $url4->withHost("t%3As%2Ft.com"); // t:s/t.com +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +var_dump($url4->withHost("t:s/t.com")); + +try { + $url2->withHost(null); +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +$url1 = Uri\WhatWg\Url::parse("ftp://foo.com?query=abc#foo"); +$url2 = $url1->withHost("test.com"); + +var_dump($url1->getAsciiHost()); +var_dump($url2->getAsciiHost()); + +?> +--EXPECTF-- +string(11) "example.com" +string(8) "test.com" +string(8) "test.com" +string(8) "test.com" +NULL +URL parsing failed +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(8) "test.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +URL parsing failed +string(7) "foo.com" +string(8) "test.com" diff --git a/ext/uri/tests/027.phpt b/ext/uri/tests/027.phpt new file mode 100644 index 0000000000000..79c121dd7f383 --- /dev/null +++ b/ext/uri/tests/027.phpt @@ -0,0 +1,36 @@ +--TEST-- +Test property mutation - port +--EXTENSIONS-- +uri +--FILE-- +withPort(22); +$url3 = $url2->withPort(null); + +var_dump($url1->getPort()); +var_dump($url2->getPort()); +var_dump($url3->getPort()); + +$url1 = Uri\WhatWg\Url::parse("ftp://foo.com:443?query=abc#foo"); +$url2 = $url1->withPort(8080); + +var_dump($url1->getPort()); +var_dump($url2->getPort()); + +$url1 = Uri\WhatWg\Url::parse("file:///foo/bar"); +$url2 = $url1->withPort(80); + +var_dump($url1->getPort()); +var_dump($url2->getPort()); + +?> +--EXPECT-- +int(8080) +int(22) +NULL +int(443) +int(8080) +NULL +NULL diff --git a/ext/uri/tests/028.phpt b/ext/uri/tests/028.phpt new file mode 100644 index 0000000000000..fd565c900e02f --- /dev/null +++ b/ext/uri/tests/028.phpt @@ -0,0 +1,37 @@ +--TEST-- +Test property mutation - path +--EXTENSIONS-- +uri +--FILE-- +withPath("/foo"); +$url3 = $url2->withPath(""); +$url4 = $url3->withPath("t%65st"); +$url5 = $url4->withPath("/foo%2Fbar"); +$url6 = $url5->withPath("/#"); + +var_dump($url1->getPath()); +var_dump($url2->getPath()); +var_dump($url3->getPath()); +var_dump($url4->getPath()); +var_dump($url5->getPath()); +var_dump($url6->getPath()); + +$url1 = Uri\WhatWg\Url::parse("https://example.com/"); +$uri2 = $url1->withPath("/foo"); + +var_dump($url1->getPath()); +var_dump($url2->getPath()); + +?> +--EXPECT-- +string(9) "/foo/bar/" +string(4) "/foo" +string(1) "/" +string(7) "/t%65st" +string(10) "/foo%2Fbar" +string(4) "/%23" +string(1) "/" +string(4) "/foo" diff --git a/ext/uri/tests/029.phpt b/ext/uri/tests/029.phpt new file mode 100644 index 0000000000000..e23008a65ad6a --- /dev/null +++ b/ext/uri/tests/029.phpt @@ -0,0 +1,40 @@ +--TEST-- +Test property mutation - query +--EXTENSIONS-- +uri +--FILE-- +withQuery("?foo=baz"); +$url3 = $url2->withQuery(null); + +var_dump($url1->getQuery()); +var_dump($url2->getQuery()); +var_dump($url3->getQuery()); + +$url1 = Uri\WhatWg\Url::parse("https://example.com"); +$url2 = $url1->withQuery("?foo=bar&foo=baz"); +$url3 = $url1->withQuery("foo=bar&foo=baz"); +$url4 = $url3->withQuery("t%65st"); +$url5 = $url4->withQuery("foo=foo%26bar&baz=/qux%3D"); +$url6 = $url5->withQuery("#"); + +var_dump($url1->getQuery()); +var_dump($url2->getQuery()); +var_dump($url3->getQuery()); +var_dump($url4->getQuery()); +var_dump($url5->getQuery()); +var_dump($url6->getQuery()); + +?> +--EXPECT-- +string(7) "foo=bar" +string(7) "foo=baz" +NULL +NULL +string(15) "foo=bar&foo=baz" +string(15) "foo=bar&foo=baz" +string(6) "t%65st" +string(25) "foo=foo%26bar&baz=/qux%3D" +string(3) "%23" diff --git a/ext/uri/tests/030.phpt b/ext/uri/tests/030.phpt new file mode 100644 index 0000000000000..6bb85e6720c95 --- /dev/null +++ b/ext/uri/tests/030.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test property mutation - fragment +--EXTENSIONS-- +uri +--FILE-- +withFragment("#fragment2"); +$url3 = $url2->withFragment(null); +$url4 = $url3->withFragment(" "); + +var_dump($url1->getFragment()); +var_dump($url2->getFragment()); +var_dump($url3->getFragment()); +var_dump($url4->getFragment()); + +$url1 = Uri\WhatWg\Url::parse("https://example.com?abc=def"); +$url2 = $url1->withFragment("#fragment"); + +var_dump($url1->getFragment()); +var_dump($url2->getFragment()); + +?> +--EXPECT-- +string(9) "fragment1" +string(9) "fragment2" +NULL +string(3) "%20" +NULL +string(8) "fragment" diff --git a/ext/uri/tests/031.phpt b/ext/uri/tests/031.phpt new file mode 100644 index 0000000000000..2ceb64ff0b0e4 --- /dev/null +++ b/ext/uri/tests/031.phpt @@ -0,0 +1,91 @@ +--TEST-- +Test serialization and unserialization +--EXTENSIONS-- +uri +--FILE-- +getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":3:{i:0;a:0:{}i:1;a:0:{}i:2;a:0:{}}'); // more than 2 items +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;N;i:1;a:0:{}}'); // first item is not an array +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:0:{}i:1;a:0:{}}'); // first array is empty +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";i:1;}i:1;a:0:{}}'); // "uri" key in first array is not a string +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";s:11:"invalid-url";}i:1;a:0:{}}'); // "uri" key in first array contains invalid URL +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";s:19:"https://example.com";}i:1;s:0:"";}'); // second item in not an array +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";s:19:"https://example.com";}i:1;a:1:{s:5:"prop1";i:123;}}'); // second array contains property +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECTF-- +string(162) "O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";s:98:"https://username:password@www.google.com:8080/pathname1/pathname2/pathname3?query=true#hash-exists";}i:1;a:0:{}}" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(8) "username" + ["password"]=> + string(8) "password" + ["host"]=> + string(14) "www.google.com" + ["port"]=> + int(8080) + ["path"]=> + string(30) "/pathname1/pathname2/pathname3" + ["query"]=> + string(10) "query=true" + ["fragment"]=> + string(11) "hash-exists" +} +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +URL parsing failed +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object diff --git a/ext/uri/tests/032.phpt b/ext/uri/tests/032.phpt new file mode 100644 index 0000000000000..93bb80bcdb72a --- /dev/null +++ b/ext/uri/tests/032.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test JSON encoding +--EXTENSIONS-- +uri +--FILE-- + +--EXPECT-- +string(2) "{}" diff --git a/ext/uri/tests/033.phpt b/ext/uri/tests/033.phpt new file mode 100644 index 0000000000000..5b74af9b74f74 --- /dev/null +++ b/ext/uri/tests/033.phpt @@ -0,0 +1,15 @@ +--TEST-- +Test var_export +--EXTENSIONS-- +uri +--FILE-- + +--EXPECT-- +\Uri\WhatWg\Url::__set_state(array( +)) diff --git a/ext/uri/tests/034.phpt b/ext/uri/tests/034.phpt new file mode 100644 index 0000000000000..ccb0d9f6e5347 --- /dev/null +++ b/ext/uri/tests/034.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test array cast +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +array(%d) { +} diff --git a/ext/uri/tests/035.phpt b/ext/uri/tests/035.phpt new file mode 100644 index 0000000000000..6760e5dc0fb7a --- /dev/null +++ b/ext/uri/tests/035.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test URI parsing containing null bytes +--EXTENSIONS-- +uri +--FILE-- +getMessage() . "\n"; +} + +$url = new Uri\WhatWg\Url("https://example.com"); +try { + $url->withHost("exam\0ple.com"); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Uri\WhatWg\Url::__construct(): Argument #1 ($uri) must not contain any null bytes +Uri\WhatWg\Url::withHost(): Argument #1 ($host) must not contain any null bytes diff --git a/ext/uri/tests/036.phpt b/ext/uri/tests/036.phpt new file mode 100644 index 0000000000000..adc4041db5506 --- /dev/null +++ b/ext/uri/tests/036.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test URI equality checks +--EXTENSIONS-- +uri +--FILE-- +equals(new Uri\WhatWg\Url("https://example.com"))); // true: identical URIs +var_dump(new Uri\WhatWg\Url("https://example.com#foo")->equals(new Uri\WhatWg\Url("https://example.com#bar"), Uri\UriComparisonMode::ExcludeFragment)); // true: fragment differs, but fragment is excluded +var_dump(new Uri\WhatWg\Url("https://example.com#foo")->equals(new Uri\WhatWg\Url("https://example.com#bar"), Uri\UriComparisonMode::IncludeFragment)); // false: fragment differs and fragment is included +var_dump(new Uri\WhatWg\Url("https://example.com/foo/..")->equals(new Uri\WhatWg\Url("https://example.com"))); // true: both URIs are https://example.com/ after normalization +var_dump(new Uri\WhatWg\Url("https://example.com/foo/..")->equals(new Uri\WhatWg\Url("https://example.com/"))); // true: both URIs are https://example.com/ after normalization +var_dump(new Uri\WhatWg\Url("http://example%2ecom/foo%2fb%61r")->equals(new Uri\WhatWg\Url("http://example%2ecom/foo/bar"))); // false: WHATWG doesn't percent-decode the path during normalization +var_dump(new Uri\WhatWg\Url("http://example%2ecom/foo/b%61r")->equals(new Uri\WhatWg\Url("http://example.com/foo/b%61r"))); // true: WHATWG percent-decodes the host during normalization + +?> +--EXPECT-- +bool(true) +bool(true) +bool(false) +bool(true) +bool(true) +bool(false) +bool(true) diff --git a/ext/uri/tests/038.phpt b/ext/uri/tests/038.phpt new file mode 100644 index 0000000000000..06171b258d6f8 --- /dev/null +++ b/ext/uri/tests/038.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test toString() +--EXTENSIONS-- +uri +--FILE-- +toUnicodeString()); +var_dump($url1->toAsciiString()); +var_dump($url2->toUnicodeString()); +var_dump($url2->toAsciiString()); +var_dump($url3->toUnicodeString()); +var_dump($url3->toAsciiString()); + +?> +--EXPECT-- +string(20) "https://example.com/" +string(20) "https://example.com/" +string(20) "https://example.com/" +string(20) "https://example.com/" +string(20) "https://example.com/" +string(20) "https://example.com/" diff --git a/ext/uri/tests/039.phpt b/ext/uri/tests/039.phpt new file mode 100644 index 0000000000000..6bf57cde97d95 --- /dev/null +++ b/ext/uri/tests/039.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test percent-encoding of different URI components +--EXTENSIONS-- +uri +--FILE-- +getScheme()); + var_dump($url->getUsername()); + var_dump($url->getPassword()); + var_dump($url->getAsciiHost()); + var_dump($url->getUnicodeHost()); + var_dump($url->getPort()); + var_dump($url->getPath()); + var_dump($url->getQuery()); + var_dump($url->getFragment()); +} + +$url = Uri\WhatWg\Url::parse("http://%61pple:p%61ss@ex%61mple.com/foob%61r?%61bc=%61bc#%61bc"); +callWhatWgGetters($url); + +?> +--EXPECT-- +string(4) "http" +string(7) "%61pple" +string(6) "p%61ss" +string(11) "example.com" +string(11) "example.com" +NULL +string(9) "/foob%61r" +string(11) "%61bc=%61bc" +string(5) "%61bc" diff --git a/ext/uri/tests/040.phpt b/ext/uri/tests/040.phpt new file mode 100644 index 0000000000000..6bd66fd396f21 --- /dev/null +++ b/ext/uri/tests/040.phpt @@ -0,0 +1,32 @@ +--TEST-- +Test HTTP URL validation +--EXTENSIONS-- +uri +--FILE-- +toAsciiString()); + +?> +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +string(20) "https://example.com/" diff --git a/ext/uri/tests/041.phpt b/ext/uri/tests/041.phpt new file mode 100644 index 0000000000000..5cfcec2628dab --- /dev/null +++ b/ext/uri/tests/041.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test relative URI parsing +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +NULL +array(%d) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(15) "?query#fragment" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::MissingSchemeNonRelativeUrl) + ["failure"]=> + bool(true) + } +} diff --git a/ext/uri/tests/042.phpt b/ext/uri/tests/042.phpt new file mode 100644 index 0000000000000..caf63366e2b19 --- /dev/null +++ b/ext/uri/tests/042.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test URN parsing +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(3) "urn" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(41) "uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/043.phpt b/ext/uri/tests/043.phpt new file mode 100644 index 0000000000000..d9f17d45024c9 --- /dev/null +++ b/ext/uri/tests/043.phpt @@ -0,0 +1,71 @@ +--TEST-- +Test reference resolution +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(14) "/without-base/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(10) "/with-base" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(8) "test.com" + ["port"]=> + NULL + ["path"]=> + string(18) "/with-base-in-vain" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/045.phpt b/ext/uri/tests/045.phpt new file mode 100644 index 0000000000000..13137d6a42b69 --- /dev/null +++ b/ext/uri/tests/045.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test percent-decoding of reserved characters in the path +--EXTENSIONS-- +uri +--FILE-- +toAsciiString()); +var_dump($url->getPath()); + +?> +--EXPECT-- +string(33) "https://example.com/foo/bar%2Fbaz" +string(14) "/foo/bar%2Fbaz" diff --git a/ext/uri/tests/046.phpt b/ext/uri/tests/046.phpt new file mode 100644 index 0000000000000..cf283ad5297e5 --- /dev/null +++ b/ext/uri/tests/046.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test special path variants +--EXTENSIONS-- +uri +--FILE-- +toAsciiString()); +var_dump($url->getPath()); + +$url = new Uri\Whatwg\Url("https://example.com"); + +var_dump($url->toAsciiString()); +var_dump($url->getPath()); + +$url = new Uri\Whatwg\Url("https://example.com/"); + +var_dump($url->toAsciiString()); +var_dump($url->getPath()); + +?> +--EXPECT-- +string(26) "mailto:johndoe@example.com" +string(19) "johndoe@example.com" +string(20) "https://example.com/" +string(1) "/" +string(20) "https://example.com/" +string(1) "/" diff --git a/ext/uri/tests/047.phpt b/ext/uri/tests/047.phpt new file mode 100644 index 0000000000000..4ab3a0584de79 --- /dev/null +++ b/ext/uri/tests/047.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test IP addresses +--EXTENSIONS-- +uri +--FILE-- +getAsciiHost()); +var_dump($url->toAsciiString()); + +$url = new Uri\WhatWg\Url("https://[2001:0db8:0001:0000:0000:0ab9:C0A8:0102]"); +var_dump($url->getAsciiHost()); +var_dump($url->toAsciiString()); + +$url = new Uri\WhatWg\Url("https://[0:0::1]"); +var_dump($url->getAsciiHost()); +var_dump($url->toAsciiString()); + +?> +--EXPECT-- +string(11) "192.168.0.1" +string(20) "https://192.168.0.1/" +string(26) "[2001:db8:1::ab9:c0a8:102]" +string(35) "https://[2001:db8:1::ab9:c0a8:102]/" +string(5) "[::1]" +string(14) "https://[::1]/" diff --git a/ext/uri/tests/049.phpt b/ext/uri/tests/049.phpt new file mode 100644 index 0000000000000..41e6eaeea3cf9 --- /dev/null +++ b/ext/uri/tests/049.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test percent-encoding normalization - special case +--EXTENSIONS-- +uri +--FILE-- +getPath()); + +?> +--EXPECT-- +string(14) "/foo/bar%2Fbaz" diff --git a/ext/uri/tests/050.phpt b/ext/uri/tests/050.phpt new file mode 100644 index 0000000000000..12af66721cf65 --- /dev/null +++ b/ext/uri/tests/050.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test resolve() method - success cases +--EXTENSIONS-- +uri +--FILE-- +resolve("/foo/")->toAsciiString()); +var_dump($url->resolve("https://test.com/foo")->toAsciiString()); + +?> +--EXPECTF-- +string(24) "https://example.com/foo/" +string(20) "https://test.com/foo" diff --git a/ext/uri/tests/051.phpt b/ext/uri/tests/051.phpt new file mode 100644 index 0000000000000..5911f8767567c --- /dev/null +++ b/ext/uri/tests/051.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test resolve() method - error cases +--EXTENSIONS-- +uri +--FILE-- +resolve("https://1.2.3.4.5"); +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +$softErrors = []; + +var_dump($url->resolve(" /foo", $softErrors)->toAsciiString()); +var_dump($softErrors); + +?> +--EXPECTF-- +URL parsing failed +string(23) "https://example.com/foo" +array(%d) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(5) " /foo" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit) + ["failure"]=> + bool(false) + } +} From b27c940059051bf14000b96347950084a1159732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Wed, 28 May 2025 23:05:39 +0200 Subject: [PATCH 02/15] Partial review fixes --- ext/uri/php_lexbor.c | 181 ++++++++++++++++++++------------------- ext/uri/php_uri.c | 46 +++++----- ext/uri/php_uri_common.h | 4 +- ext/uri/tests/031.phpt | 11 ++- 4 files changed, 127 insertions(+), 115 deletions(-) diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index bcc5ddf3f76ff..9174a9227407f 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -31,10 +31,10 @@ static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t rec static void lexbor_free_uri(void *uri); static zend_result lexbor_destroy_parser(void); -HashTable lexbor_property_handlers; +static HashTable lexbor_property_handlers; -lxb_url_parser_t *lexbor_parser; -int lexbor_urls; +ZEND_TLS lxb_url_parser_t *lexbor_parser; +ZEND_TLS int lexbor_urls; const uri_handler_t lexbor_uri_handler = { URI_PARSER_WHATWG, @@ -48,11 +48,19 @@ const uri_handler_t lexbor_uri_handler = { &lexbor_property_handlers }; -#define ZVAL_TO_LEXBOR_STR(value, str) do { \ +#define MAX_URL_COUNT 500 + +#define ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str) do { \ if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) > 0) { \ lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \ - } else if (Z_TYPE_P(value) == IS_LONG && Z_LVAL_P(value) != 0) { \ - ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); \ + } else { \ + lexbor_str_init(&str, lexbor_parser->mraw, 0); \ + } \ +} while (0) + +#define ZVAL_LONG_OR_NULL_TO_LEXBOR_STR(value, str) do { \ + if (Z_TYPE_P(value) == IS_LONG) { \ + ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); \ lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \ } else { \ lexbor_str_init(&str, lexbor_parser->mraw, 0); \ @@ -68,26 +76,22 @@ const uri_handler_t lexbor_uri_handler = { } while (0) #define LEXBOR_READ_ASCII_URI_COMPONENT(start, len, read_mode, retval) do { \ - switch (read_mode) { \ - case URI_COMPONENT_READ_RAW: /* Intentional fallthrough */ \ - case URI_COMPONENT_READ_NORMALIZED_UNICODE: /* Intentional fallthrough */ \ - case URI_COMPONENT_READ_NORMALIZED_ASCII: { \ - ZVAL_STRINGL(retval, (const char *) start, len); \ - break; \ - } \ - EMPTY_SWITCH_DEFAULT_CASE() \ - } \ + ZVAL_STRINGL(retval, (const char *) start, len); \ } while (0) static void lexbor_cleanup_parser(void) { - if (++lexbor_urls % 500 == 0) { + if (++lexbor_urls % MAX_URL_COUNT == 0) { lexbor_mraw_clean(lexbor_parser->mraw); } lxb_url_parser_clean(lexbor_parser); } +/** + * Creates a Uri\WhatWg\UrlValidationError class by mapping error codes listed in + * https://url.spec.whatwg.org/#writing to a Uri\WhatWg\UrlValidationErrorType enum + */ static void fill_errors(zval *errors) { if (errors == NULL || lexbor_parser->log == NULL) { @@ -102,125 +106,125 @@ static void fill_errors(zval *errors) while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser->log->list)) != NULL) { zval error; object_init_ex(&error, whatwg_url_validation_error_ce); - zend_update_property_string(whatwg_url_validation_error_ce, Z_OBJ(error), "context", sizeof("context") - 1, (const char *) lxb_error->data); + zend_update_property_string(whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data); zend_string *error_str; zval failure; switch (lxb_error->id) { case LXB_URL_ERROR_TYPE_DOMAIN_TO_ASCII: - error_str = zend_string_init("DomainToAscii", sizeof("DomainToAscii"), false); + error_str = ZSTR_INIT_LITERAL("DomainToAscii", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_DOMAIN_TO_UNICODE: - error_str = zend_string_init("DomainToUnicode", sizeof("DomainToUnicode"), false); + error_str = ZSTR_INIT_LITERAL("DomainToUnicode", false); ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_DOMAIN_INVALID_CODE_POINT: - error_str = zend_string_init("DomainInvalidCodePoint", sizeof("DomainInvalidCodePoint"), false); + error_str = ZSTR_INIT_LITERAL("DomainInvalidCodePoint", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_HOST_INVALID_CODE_POINT: - error_str = zend_string_init("HostInvalidCodePoint", sizeof("HostInvalidCodePoint"), false); + error_str = ZSTR_INIT_LITERAL("HostInvalidCodePoint", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_EMPTY_PART: - error_str = zend_string_init("Ipv4EmptyPart", sizeof("Ipv4EmptyPart"), false); + error_str = ZSTR_INIT_LITERAL("Ipv4EmptyPart", false); ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_TOO_MANY_PARTS: - error_str = zend_string_init("Ipv4TooManyParts", sizeof("Ipv4TooManyParts"), false); + error_str = ZSTR_INIT_LITERAL("Ipv4TooManyParts", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_NON_NUMERIC_PART: - error_str = zend_string_init("Ipv4NonNumericPart", sizeof("Ipv4NonNumericPart"), false); + error_str = ZSTR_INIT_LITERAL("Ipv4NonNumericPart", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_NON_DECIMAL_PART: - error_str = zend_string_init("Ipv4NonDecimalPart", sizeof("Ipv4NonDecimalPart"), false); + error_str = ZSTR_INIT_LITERAL("Ipv4NonDecimalPart", false); ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_OUT_OF_RANGE_PART: - error_str = zend_string_init("Ipv4OutOfRangePart", sizeof("Ipv4OutOfRangePart"), false); + error_str = ZSTR_INIT_LITERAL("Ipv4OutOfRangePart", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_UNCLOSED: - error_str = zend_string_init("Ipv6Unclosed", sizeof("Ipv6Unclosed"), false); + error_str = ZSTR_INIT_LITERAL("Ipv6Unclosed", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_INVALID_COMPRESSION: - error_str = zend_string_init("Ipv6InvalidCompression", sizeof("Ipv6InvalidCompression"), false); + error_str = ZSTR_INIT_LITERAL("Ipv6InvalidCompression", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_TOO_MANY_PIECES: - error_str = zend_string_init("Ipv6TooManyPieces", sizeof("Ipv6TooManyPieces"), false); + error_str = ZSTR_INIT_LITERAL("Ipv6TooManyPieces", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_MULTIPLE_COMPRESSION: - error_str = zend_string_init("Ipv6MultipleCompression", sizeof("Ipv6MultipleCompression"), false); + error_str = ZSTR_INIT_LITERAL("Ipv6MultipleCompression", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_INVALID_CODE_POINT: - error_str = zend_string_init("Ipv6InvalidCodePoint", sizeof("Ipv6InvalidCodePoint"), false); + error_str = ZSTR_INIT_LITERAL("Ipv6InvalidCodePoint", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_TOO_FEW_PIECES: - error_str = zend_string_init("Ipv6TooFewPieces", sizeof("Ipv6TooFewPieces"), false); + error_str = ZSTR_INIT_LITERAL("Ipv6TooFewPieces", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_MANY_PIECES: - error_str = zend_string_init("Ipv4InIpv6TooManyPieces", sizeof("Ipv4InIpv6TooManyPieces"), false); + error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6TooManyPieces", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_INVALID_CODE_POINT: - error_str = zend_string_init("Ipv4InIpv6InvalidCodePoint", sizeof("Ipv4InIpv6InvalidCodePoint"), false); + error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6InvalidCodePoint", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_OUT_OF_RANGE_PART: - error_str = zend_string_init("Ipv4InIpv6OutOfRangePart", sizeof("Ipv4InIpv6OutOfRangePart"), false); + error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6OutOfRangePart", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_FEW_PARTS: - error_str = zend_string_init("Ipv4InIpv6TooFewParts", sizeof("Ipv4InIpv6TooFewParts"), false); + error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6TooFewParts", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_INVALID_URL_UNIT: - error_str = zend_string_init("InvalidUrlUnit", sizeof("InvalidUrlUnit"), false); + error_str = ZSTR_INIT_LITERAL("InvalidUrlUnit", false); ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_SPECIAL_SCHEME_MISSING_FOLLOWING_SOLIDUS: - error_str = zend_string_init("SpecialSchemeMissingFollowingSolidus", sizeof("SpecialSchemeMissingFollowingSolidus"), false); + error_str = ZSTR_INIT_LITERAL("SpecialSchemeMissingFollowingSolidus", false); ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_MISSING_SCHEME_NON_RELATIVE_URL: - error_str = zend_string_init("MissingSchemeNonRelativeUrl", sizeof("MissingSchemeNonRelativeUrl"), false); + error_str = ZSTR_INIT_LITERAL("MissingSchemeNonRelativeUrl", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_INVALID_REVERSE_SOLIDUS: - error_str = zend_string_init("InvalidReverseSoldius", sizeof("InvalidReverseSoldius"), false); + error_str = ZSTR_INIT_LITERAL("InvalidReverseSoldius", false); ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS: - error_str = zend_string_init("InvalidCredentials", sizeof("InvalidCredentials"), false); + error_str = ZSTR_INIT_LITERAL("InvalidCredentials", false); ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_HOST_MISSING: - error_str = zend_string_init("HostMissing", sizeof("HostMissing"), false); + error_str = ZSTR_INIT_LITERAL("HostMissing", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_PORT_OUT_OF_RANGE: - error_str = zend_string_init("PortOutOfRange", sizeof("PortOutOfRange"), false); + error_str = ZSTR_INIT_LITERAL("PortOutOfRange", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_PORT_INVALID: - error_str = zend_string_init("PortInvalid", sizeof("PortInvalid"), false); + error_str = ZSTR_INIT_LITERAL("PortInvalid", false); ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER: - error_str = zend_string_init("FileInvalidWindowsDriveLetter", sizeof("FileInvalidWindowsDriveLetter"), false); + error_str = ZSTR_INIT_LITERAL("FileInvalidWindowsDriveLetter", false); ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER_HOST: - error_str = zend_string_init("FileInvalidWindowsDriveLetterHost", sizeof("FileInvalidWindowsDriveLetterHost"), false); + error_str = ZSTR_INIT_LITERAL("FileInvalidWindowsDriveLetterHost", false); ZVAL_FALSE(&failure); break; EMPTY_SWITCH_DEFAULT_CASE() @@ -228,22 +232,21 @@ static void fill_errors(zval *errors) zval error_type; zend_enum_new(&error_type, whatwg_url_validation_error_type_ce, error_str, NULL); - zend_update_property(whatwg_url_validation_error_ce, Z_OBJ(error), "type", sizeof("type") - 1, &error_type); - zend_string_release(error_str); + zend_update_property(whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("type"), &error_type); + zend_string_release_ex(error_str, false); zval_ptr_dtor(&error_type); - zend_update_property(whatwg_url_validation_error_ce, Z_OBJ(error), "failure", sizeof("failure") - 1, &failure); - zval_ptr_dtor(&failure); + zend_update_property(whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("failure"), &failure); add_next_index_zval(errors, &error); } - lexbor_array_obj_clean(&lexbor_parser->log->list); // check if needed + lexbor_array_obj_clean(&lexbor_parser->log->list); // TODO check if needed } static lxb_status_t lexbor_serialize_callback(const lxb_char_t *data, size_t length, void *ctx) { - smart_str *uri_str = (smart_str *) ctx; + smart_str *uri_str = ctx; if (data != NULL && length > 0) { smart_str_appendl(uri_str, (const char *) data, length); @@ -254,7 +257,7 @@ static lxb_status_t lexbor_serialize_callback(const lxb_char_t *data, size_t len static zend_result lexbor_read_scheme(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; ZEND_ASSERT(lexbor_uri->scheme.type != LXB_URL_SCHEMEL_TYPE__UNDEF); @@ -265,10 +268,10 @@ static zend_result lexbor_read_scheme(const uri_internal_t *internal_uri, uri_co static zend_result lexbor_write_scheme(uri_internal_t *internal_uri, zval *value, zval *errors) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_TO_LEXBOR_STR(value, str); + ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); lxb_status_t status = lxb_url_api_protocol_set(lexbor_uri, lexbor_parser, str.data, str.length); @@ -277,7 +280,7 @@ static zend_result lexbor_write_scheme(uri_internal_t *internal_uri, zval *value static zend_result lexbor_read_username(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->username.length) { LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->username.data, lexbor_uri->username.length, read_mode, retval); @@ -290,10 +293,10 @@ static zend_result lexbor_read_username(const uri_internal_t *internal_uri, uri_ static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *value, zval *errors) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_TO_LEXBOR_STR(value, str); + ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); lxb_status_t status = lxb_url_api_username_set(lexbor_uri, str.data, str.length); @@ -302,7 +305,7 @@ static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *val static zend_result lexbor_read_password(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->password.length > 0) { LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->password.data, lexbor_uri->password.length, read_mode, retval); @@ -315,10 +318,10 @@ static zend_result lexbor_read_password(const uri_internal_t *internal_uri, uri_ static zend_result lexbor_write_password(uri_internal_t *internal_uri, zval *value, zval *errors) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_TO_LEXBOR_STR(value, str); + ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); lxb_status_t status = lxb_url_api_password_set(lexbor_uri, str.data, str.length); @@ -327,24 +330,23 @@ static zend_result lexbor_write_password(uri_internal_t *internal_uri, zval *val static zend_result lexbor_read_host(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV4) { smart_str host_str = {0}; - lxb_url_serialize_host_ipv4((zend_ulong) lexbor_uri->host.u.ipv4, lexbor_serialize_callback, (void *) &host_str); + lxb_url_serialize_host_ipv4(lexbor_uri->host.u.ipv4, lexbor_serialize_callback, &host_str); ZVAL_STR(retval, smart_str_extract(&host_str)); } else if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV6) { smart_str host_str = {0}; smart_str_appendc(&host_str, '['); - lxb_url_serialize_host_ipv6(lexbor_uri->host.u.ipv6, lexbor_serialize_callback, (void *) &host_str); + lxb_url_serialize_host_ipv6(lexbor_uri->host.u.ipv6, lexbor_serialize_callback, &host_str); smart_str_appendc(&host_str, ']'); ZVAL_STR(retval, smart_str_extract(&host_str)); } else if (lexbor_uri->host.type != LXB_URL_HOST_TYPE_EMPTY && lexbor_uri->host.type != LXB_URL_HOST_TYPE__UNDEF) { - switch (read_mode) { case URI_COMPONENT_READ_NORMALIZED_UNICODE: { smart_str host_str = {0}; @@ -355,13 +357,14 @@ static zend_result lexbor_read_host(const uri_internal_t *internal_uri, uri_comp return FAILURE; } } - lxb_url_serialize_host_unicode(lexbor_parser->idna, &lexbor_uri->host, lexbor_serialize_callback, (void *) &host_str); + lxb_url_serialize_host_unicode(lexbor_parser->idna, &lexbor_uri->host, lexbor_serialize_callback, &host_str); lxb_unicode_idna_clean(lexbor_parser->idna); ZVAL_STR(retval, smart_str_extract(&host_str)); break; } - case URI_COMPONENT_READ_NORMALIZED_ASCII: /* Intentional fallthrough */ + case URI_COMPONENT_READ_NORMALIZED_ASCII: + ZEND_FALLTHROUGH; case URI_COMPONENT_READ_RAW: ZVAL_STRINGL(retval, (const char *) lexbor_uri->host.u.domain.data, lexbor_uri->host.u.domain.length); break; @@ -376,10 +379,10 @@ static zend_result lexbor_read_host(const uri_internal_t *internal_uri, uri_comp static zend_result lexbor_write_host(uri_internal_t *internal_uri, zval *value, zval *errors) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_TO_LEXBOR_STR(value, str); + ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); lxb_status_t status = lxb_url_api_hostname_set(lexbor_uri, lexbor_parser, str.data, str.length); @@ -388,7 +391,7 @@ static zend_result lexbor_write_host(uri_internal_t *internal_uri, zval *value, static zend_result lexbor_read_port(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->has_port) { ZVAL_LONG(retval, lexbor_uri->port); @@ -401,10 +404,10 @@ static zend_result lexbor_read_port(const uri_internal_t *internal_uri, uri_comp static zend_result lexbor_write_port(uri_internal_t *internal_uri, zval *value, zval *errors) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_TO_LEXBOR_STR(value, str); + ZVAL_LONG_OR_NULL_TO_LEXBOR_STR(value, str); lxb_status_t status = lxb_url_api_port_set(lexbor_uri, lexbor_parser, str.data, str.length); @@ -413,7 +416,7 @@ static zend_result lexbor_write_port(uri_internal_t *internal_uri, zval *value, static zend_result lexbor_read_path(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->path.str.length) { LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->path.str.data, lexbor_uri->path.str.length, read_mode, retval); @@ -426,10 +429,10 @@ static zend_result lexbor_read_path(const uri_internal_t *internal_uri, uri_comp static zend_result lexbor_write_path(uri_internal_t *internal_uri, zval *value, zval *errors) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_TO_LEXBOR_STR(value, str); + ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); lxb_status_t status = lxb_url_api_pathname_set(lexbor_uri, lexbor_parser, str.data, str.length); @@ -438,7 +441,7 @@ static zend_result lexbor_write_path(uri_internal_t *internal_uri, zval *value, static zend_result lexbor_read_query(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->query.length) { LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->query.data, lexbor_uri->query.length, read_mode, retval); @@ -451,10 +454,10 @@ static zend_result lexbor_read_query(const uri_internal_t *internal_uri, uri_com static zend_result lexbor_write_query(uri_internal_t *internal_uri, zval *value, zval *errors) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_TO_LEXBOR_STR(value, str); + ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); lxb_status_t status = lxb_url_api_search_set(lexbor_uri, lexbor_parser, str.data, str.length); @@ -463,7 +466,7 @@ static zend_result lexbor_write_query(uri_internal_t *internal_uri, zval *value, static zend_result lexbor_read_fragment(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->fragment.length) { LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->fragment.data, lexbor_uri->fragment.length, read_mode, retval); @@ -476,10 +479,10 @@ static zend_result lexbor_read_fragment(const uri_internal_t *internal_uri, uri_ static zend_result lexbor_write_fragment(uri_internal_t *internal_uri, zval *value, zval *errors) { - lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri; + lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_TO_LEXBOR_STR(value, str); + ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); lxb_status_t status = lxb_url_api_hash_set(lexbor_uri, lexbor_parser, str.data, str.length); @@ -499,6 +502,7 @@ static zend_result lexbor_init_parser(void) status = lxb_url_parser_init(parser, mraw); if (status != LXB_STATUS_OK) { lxb_url_parser_destroy(parser, true); + lexbor_mraw_destroy(mraw, true); return FAILURE; } @@ -523,7 +527,8 @@ static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, { lexbor_cleanup_parser(); - lxb_url_t *url, *lexbor_base_url = NULL; + lxb_url_t *url; + const lxb_url_t *lexbor_base_url = NULL; if (base_url) { lexbor_base_url = (lxb_url_t *) base_url; @@ -547,7 +552,7 @@ static void lexbor_create_invalid_uri_exception(zval *exception_zv, zval *errors "URL parsing failed" ); - zend_update_property(whatwg_invalid_url_exception_ce, Z_OBJ_P(exception_zv), "errors", sizeof("errors") - 1, errors); + zend_update_property(whatwg_invalid_url_exception_ce, Z_OBJ_P(exception_zv), ZEND_STRL("errors"), errors); } @@ -564,7 +569,8 @@ static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t rec smart_str uri_str = {0}; switch (recomposition_mode) { - case URI_RECOMPOSITION_RAW_UNICODE: /* Intentional fallthrough */ + case URI_RECOMPOSITION_RAW_UNICODE: + ZEND_FALLTHROUGH; case URI_RECOMPOSITION_NORMALIZED_UNICODE: if (lexbor_parser->idna == NULL) { lexbor_parser->idna = lxb_unicode_idna_create(); @@ -573,12 +579,13 @@ static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t rec return NULL; } } - lxb_url_serialize_idna(lexbor_parser->idna, lexbor_uri, lexbor_serialize_callback, (void *) &uri_str, exclude_fragment); + lxb_url_serialize_idna(lexbor_parser->idna, lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment); lxb_unicode_idna_clean(lexbor_parser->idna); break; - case URI_RECOMPOSITION_RAW_ASCII: /* Intentional fallthrough */ + case URI_RECOMPOSITION_RAW_ASCII: + ZEND_FALLTHROUGH; case URI_RECOMPOSITION_NORMALIZED_ASCII: - lxb_url_serialize(lexbor_uri, lexbor_serialize_callback, (void *) &uri_str, exclude_fragment); + lxb_url_serialize(lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment); break; EMPTY_SWITCH_DEFAULT_CASE() } diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 4f6092b0f5c95..65adc85e085bb 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -84,12 +84,6 @@ static HashTable *uri_get_debug_properties(zend_object *object) continue; } - if (Z_TYPE(value) == IS_OBJECT) { - zval_ptr_dtor(&value); - ZVAL_NEW_STR(&value, object_str); - zend_string_addref(object_str); - } - zend_hash_update(result, string_key, &value); } ZEND_HASH_FOREACH_END(); @@ -110,19 +104,18 @@ PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) Z_PARAM_BOOL(failure) ZEND_PARSE_PARAMETERS_END(); - zend_update_property_str(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), "context", sizeof("context") - 1, context); - zend_update_property(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), "type", sizeof("type") - 1, type); + zend_update_property_str(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("context"), context); + zend_update_property(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); zval failure_zv; ZVAL_BOOL(&failure_zv, failure); - zend_update_property(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), "failure", sizeof("failure") - 1, &failure_zv); - zval_ptr_dtor(&failure_zv); + zend_update_property(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("failure"), &failure_zv); } static zend_result pass_errors_by_ref(zval *errors_zv, zval *errors) { ZEND_ASSERT(Z_TYPE_P(errors) == IS_UNDEF || Z_TYPE_P(errors) == IS_ARRAY); - if (Z_TYPE_P(errors) != IS_ARRAY) { + if (Z_ISUNDEF_P(errors)) { return SUCCESS; } @@ -233,9 +226,7 @@ static void uri_equals(INTERNAL_FUNCTION_PARAMETERS, zend_object *that_object, z bool exclude_fragment = true; if (comparison_mode) { zval *case_name = zend_enum_fetch_case_name(comparison_mode); - zend_string *comparison_mode_name = Z_STR_P(case_name); - - exclude_fragment = ZSTR_VAL(comparison_mode_name)[0] + ZSTR_LEN(comparison_mode_name) == 'E' + sizeof("ExcludeFragment") - 1; + exclude_fragment = zend_string_equals_literal(Z_STR_P(case_name), "ExcludeFragment"); } zend_string *this_str = this_internal_uri->handler->uri_to_string( @@ -282,7 +273,13 @@ static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_na RETURN_THROWS(); } - zval *uri_zv = zend_hash_str_find_ind(Z_ARRVAL_P(arr), URI_SERIALIZED_PROPERTY_NAME, sizeof(URI_SERIALIZED_PROPERTY_NAME) - 1); + /* Verify the expected number of elements inside the first array, this implicitly ensures that no additional elements are present. */ + if (zend_hash_num_elements(Z_ARRVAL_P(arr)) != 1) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + zval *uri_zv = zend_hash_str_find_ind(Z_ARRVAL_P(arr), ZEND_STRL(URI_SERIALIZED_PROPERTY_NAME)); if (uri_zv == NULL || Z_TYPE_P(uri_zv) != IS_STRING) { zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); RETURN_THROWS(); @@ -298,7 +295,7 @@ static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_na } internal_uri->uri = internal_uri->handler->parse_uri(Z_STR_P(uri_zv), NULL, &errors); if (internal_uri->uri == NULL) { - throw_invalid_uri_exception(internal_uri->handler, &errors); + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); zval_ptr_dtor(&errors); RETURN_THROWS(); } @@ -423,7 +420,7 @@ PHP_METHOD(Uri_WhatWg_Url, equals) ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_OBJ_OF_CLASS(that_object, whatwg_url_ce) Z_PARAM_OPTIONAL - Z_PARAM_OBJ_OF_CLASS(comparison_mode, uri_comparison_mode_ce); + Z_PARAM_OBJ_OF_CLASS(comparison_mode, uri_comparison_mode_ce) ZEND_PARSE_PARAMETERS_END(); uri_equals(INTERNAL_FUNCTION_PARAM_PASSTHRU, that_object, comparison_mode); @@ -490,7 +487,7 @@ PHP_METHOD(Uri_WhatWg_Url, __serialize) zval arr; array_init(&arr); - zend_hash_str_add_new(Z_ARRVAL(arr), URI_SERIALIZED_PROPERTY_NAME, sizeof(URI_SERIALIZED_PROPERTY_NAME) - 1, &tmp); + zend_hash_str_add_new(Z_ARRVAL(arr), ZEND_STRL(URI_SERIALIZED_PROPERTY_NAME), &tmp); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &arr); /* Serialize regular properties: second array */ @@ -517,9 +514,6 @@ static zend_object *uri_create_object_handler(zend_class_entry *class_type) { uri_object_t *uri_object = zend_object_alloc(sizeof(uri_object_t), class_type); - uri_object->internal.uri = NULL; - uri_object->internal.handler = NULL; - zend_object_std_init(&uri_object->std, class_type); object_properties_init(&uri_object->std, class_type); @@ -545,12 +539,12 @@ static zend_object *uri_clone_obj_handler(zend_object *object) uri_object_t *uri_object = uri_object_from_obj(object); uri_internal_t *internal_uri = uri_internal_from_obj(object); + URI_ASSERT_INITIALIZATION(internal_uri); + zend_object *new_object = uri_create_object_handler(object->ce); ZEND_ASSERT(new_object != NULL); uri_object_t *new_uri_object = uri_object_from_obj(new_object); - URI_ASSERT_INITIALIZATION(internal_uri); - new_uri_object->internal.handler = internal_uri->handler; void *uri = internal_uri->handler->clone_uri(internal_uri->uri); @@ -587,7 +581,11 @@ zend_result uri_handler_register(const uri_handler_t *uri_handler) ZEND_ASSERT(uri_handler->destroy_parser != NULL); ZEND_ASSERT(uri_handler->property_handlers != NULL); - return zend_hash_add_ptr(&uri_handlers, key, (void *) uri_handler) ? SUCCESS : FAILURE; + zend_result result = zend_hash_add_ptr(&uri_handlers, key, (void *) uri_handler) != NULL ? SUCCESS : FAILURE; + + zend_string_release(key); + + return result; } static PHP_MINIT_FUNCTION(uri) diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index e69cc0aae5aec..e576cea49c4f6 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -144,8 +144,8 @@ void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors) zval_ptr_dtor(property_zv); \ RETURN_THROWS(); \ } \ - ZEND_ASSERT(Z_TYPE(errors) == IS_UNDEF); \ - ZVAL_OBJ(return_value, new_object); \ + ZEND_ASSERT(Z_ISUNDEF(errors)); \ + RETVAL_OBJ(new_object); \ zval_ptr_dtor(property_zv); #define URI_WITHER_STR(property_name) do { \ diff --git a/ext/uri/tests/031.phpt b/ext/uri/tests/031.phpt index 2ceb64ff0b0e4..0572a4ec11fe6 100644 --- a/ext/uri/tests/031.phpt +++ b/ext/uri/tests/031.phpt @@ -36,6 +36,12 @@ try { echo $e->getMessage() . "\n"; } +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:2:{s:3:"uri";s:19:"https://example.com";s:1:"a";i:1;}i:1;a:0:{}}'); // "uri" key in first array contains more than 1 item +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + try { unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";i:1;}i:1;a:0:{}}'); // "uri" key in first array is not a string } catch (Exception $e) { @@ -44,7 +50,7 @@ try { try { unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";s:11:"invalid-url";}i:1;a:0:{}}'); // "uri" key in first array contains invalid URL -} catch (Uri\WhatWg\InvalidUrlException $e) { +} catch (Exception $e) { echo $e->getMessage() . "\n"; } @@ -86,6 +92,7 @@ Invalid serialization data for Uri\WhatWg\Url object Invalid serialization data for Uri\WhatWg\Url object Invalid serialization data for Uri\WhatWg\Url object Invalid serialization data for Uri\WhatWg\Url object -URL parsing failed +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object Invalid serialization data for Uri\WhatWg\Url object Invalid serialization data for Uri\WhatWg\Url object From 42aac848eb773ce99f9bc485c7017708abb9b553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Fri, 30 May 2025 09:55:49 +0200 Subject: [PATCH 03/15] Another set of changes after review --- ext/uri/php_lexbor.c | 153 +++++++++++++++++++++++---------------- ext/uri/php_uri.c | 108 +++++++++++++-------------- ext/uri/php_uri_common.c | 107 ++++++++++++++++++++++++++- ext/uri/php_uri_common.h | 100 +++---------------------- ext/uri/tests/052.phpt | 28 +++++++ 5 files changed, 286 insertions(+), 210 deletions(-) create mode 100644 ext/uri/tests/052.phpt diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index 9174a9227407f..a1a0e6866d393 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -67,7 +67,7 @@ const uri_handler_t lexbor_uri_handler = { } \ } while (0) -#define CHECK_WRITE_RESULT(status, lexbor_uri, str, errors) do { \ +#define CHECK_WRITE_RESULT(status, errors) do { \ if (status != LXB_STATUS_OK) { \ fill_errors(errors); \ return FAILURE; \ @@ -94,19 +94,21 @@ static void lexbor_cleanup_parser(void) */ static void fill_errors(zval *errors) { - if (errors == NULL || lexbor_parser->log == NULL) { + if (errors == NULL) { return; } - if (lexbor_array_obj_size(&lexbor_parser->log->list)) { - array_init(errors); + array_init(errors); + + if (lexbor_parser->log == NULL) { + return; } lexbor_plog_entry_t *lxb_error; while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser->log->list)) != NULL) { zval error; - object_init_ex(&error, whatwg_url_validation_error_ce); - zend_update_property_string(whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data); + object_init_ex(&error, uri_whatwg_url_validation_error_ce); + zend_update_property_string(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data); zend_string *error_str; zval failure; @@ -231,17 +233,15 @@ static void fill_errors(zval *errors) } zval error_type; - zend_enum_new(&error_type, whatwg_url_validation_error_type_ce, error_str, NULL); - zend_update_property(whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("type"), &error_type); + zend_enum_new(&error_type, uri_whatwg_url_validation_error_type_ce, error_str, NULL); + zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("type"), &error_type); zend_string_release_ex(error_str, false); zval_ptr_dtor(&error_type); - zend_update_property(whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("failure"), &failure); + zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("failure"), &failure); add_next_index_zval(errors, &error); } - - lexbor_array_obj_clean(&lexbor_parser->log->list); // TODO check if needed } static lxb_status_t lexbor_serialize_callback(const lxb_char_t *data, size_t length, void *ctx) @@ -273,9 +273,13 @@ static zend_result lexbor_write_scheme(uri_internal_t *internal_uri, zval *value ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); - lxb_status_t status = lxb_url_api_protocol_set(lexbor_uri, lexbor_parser, str.data, str.length); + if (lxb_url_api_protocol_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + fill_errors(errors); + + return FAILURE; + } - CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); + return SUCCESS; } static zend_result lexbor_read_username(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) @@ -298,9 +302,13 @@ static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *val ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); - lxb_status_t status = lxb_url_api_username_set(lexbor_uri, str.data, str.length); + if (lxb_url_api_username_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { + fill_errors(errors); + + return FAILURE; + } - CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); + return SUCCESS; } static zend_result lexbor_read_password(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) @@ -323,9 +331,24 @@ static zend_result lexbor_write_password(uri_internal_t *internal_uri, zval *val ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); - lxb_status_t status = lxb_url_api_password_set(lexbor_uri, str.data, str.length); + if (lxb_url_api_password_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { + fill_errors(errors); - CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); + return FAILURE; + } + + return SUCCESS; +} + +static ZEND_RESULT_CODE init_idna(void) +{ + if (lexbor_parser->idna != NULL) { + return SUCCESS; + } + + lexbor_parser->idna = lxb_unicode_idna_create(); + + return lxb_unicode_idna_init(lexbor_parser->idna) == LXB_STATUS_OK ? SUCCESS : FAILURE; } static zend_result lexbor_read_host(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) @@ -337,7 +360,7 @@ static zend_result lexbor_read_host(const uri_internal_t *internal_uri, uri_comp lxb_url_serialize_host_ipv4(lexbor_uri->host.u.ipv4, lexbor_serialize_callback, &host_str); - ZVAL_STR(retval, smart_str_extract(&host_str)); + ZVAL_NEW_STR(retval, smart_str_extract(&host_str)); } else if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV6) { smart_str host_str = {0}; @@ -345,22 +368,18 @@ static zend_result lexbor_read_host(const uri_internal_t *internal_uri, uri_comp lxb_url_serialize_host_ipv6(lexbor_uri->host.u.ipv6, lexbor_serialize_callback, &host_str); smart_str_appendc(&host_str, ']'); - ZVAL_STR(retval, smart_str_extract(&host_str)); + ZVAL_NEW_STR(retval, smart_str_extract(&host_str)); } else if (lexbor_uri->host.type != LXB_URL_HOST_TYPE_EMPTY && lexbor_uri->host.type != LXB_URL_HOST_TYPE__UNDEF) { switch (read_mode) { case URI_COMPONENT_READ_NORMALIZED_UNICODE: { smart_str host_str = {0}; - if (lexbor_parser->idna == NULL) { - lexbor_parser->idna = lxb_unicode_idna_create(); - lxb_status_t status = lxb_unicode_idna_init(lexbor_parser->idna); - if (status != LXB_STATUS_OK) { - return FAILURE; - } + if (init_idna() == FAILURE) { + return FAILURE; } lxb_url_serialize_host_unicode(lexbor_parser->idna, &lexbor_uri->host, lexbor_serialize_callback, &host_str); lxb_unicode_idna_clean(lexbor_parser->idna); - ZVAL_STR(retval, smart_str_extract(&host_str)); + ZVAL_NEW_STR(retval, smart_str_extract(&host_str)); break; } case URI_COMPONENT_READ_NORMALIZED_ASCII: @@ -384,9 +403,13 @@ static zend_result lexbor_write_host(uri_internal_t *internal_uri, zval *value, ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); - lxb_status_t status = lxb_url_api_hostname_set(lexbor_uri, lexbor_parser, str.data, str.length); + if (lxb_url_api_hostname_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + fill_errors(errors); + + return FAILURE; + } - CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); + return SUCCESS; } static zend_result lexbor_read_port(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) @@ -409,9 +432,13 @@ static zend_result lexbor_write_port(uri_internal_t *internal_uri, zval *value, ZVAL_LONG_OR_NULL_TO_LEXBOR_STR(value, str); - lxb_status_t status = lxb_url_api_port_set(lexbor_uri, lexbor_parser, str.data, str.length); + if (lxb_url_api_port_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + fill_errors(errors); + + return FAILURE; + } - CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); + return SUCCESS; } static zend_result lexbor_read_path(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) @@ -434,9 +461,13 @@ static zend_result lexbor_write_path(uri_internal_t *internal_uri, zval *value, ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); - lxb_status_t status = lxb_url_api_pathname_set(lexbor_uri, lexbor_parser, str.data, str.length); + if (lxb_url_api_pathname_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + fill_errors(errors); - CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); + return FAILURE; + } + + return SUCCESS; } static zend_result lexbor_read_query(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) @@ -459,9 +490,13 @@ static zend_result lexbor_write_query(uri_internal_t *internal_uri, zval *value, ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); - lxb_status_t status = lxb_url_api_search_set(lexbor_uri, lexbor_parser, str.data, str.length); + if ( lxb_url_api_search_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + fill_errors(errors); - CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); + return FAILURE; + } + + return SUCCESS; } static zend_result lexbor_read_fragment(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) @@ -484,9 +519,13 @@ static zend_result lexbor_write_fragment(uri_internal_t *internal_uri, zval *val ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); - lxb_status_t status = lxb_url_api_hash_set(lexbor_uri, lexbor_parser, str.data, str.length); + if (lxb_url_api_hash_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + fill_errors(errors); + + return FAILURE; + } - CHECK_WRITE_RESULT(status, lexbor_uri, str, errors); + return SUCCESS; } static zend_result lexbor_init_parser(void) @@ -498,15 +537,15 @@ static zend_result lexbor_init_parser(void) return FAILURE; } - lxb_url_parser_t *parser = lxb_url_parser_create(); - status = lxb_url_parser_init(parser, mraw); + lexbor_parser = lxb_url_parser_create(); + status = lxb_url_parser_init(lexbor_parser, mraw); if (status != LXB_STATUS_OK) { - lxb_url_parser_destroy(parser, true); + lxb_url_parser_destroy(lexbor_parser, true); + lexbor_parser = NULL; lexbor_mraw_destroy(mraw, true); return FAILURE; } - lexbor_parser = parser; lexbor_urls = 0; zend_hash_init(&lexbor_property_handlers, 8, NULL, NULL, true); @@ -527,14 +566,8 @@ static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, { lexbor_cleanup_parser(); - lxb_url_t *url; - const lxb_url_t *lexbor_base_url = NULL; - - if (base_url) { - lexbor_base_url = (lxb_url_t *) base_url; - } - - url = lxb_url_parse(lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str)); + const lxb_url_t *lexbor_base_url = base_url; + lxb_url_t *url = lxb_url_parse(lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str)); fill_errors(errors); return url; @@ -542,20 +575,16 @@ static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, static void lexbor_create_invalid_uri_exception(zval *exception_zv, zval *errors) { - object_init_ex(exception_zv, whatwg_invalid_url_exception_ce); + object_init_ex(exception_zv, uri_whatwg_invalid_url_exception_ce); - zend_update_property_string( - whatwg_invalid_url_exception_ce, - Z_OBJ_P(exception_zv), - ZSTR_VAL(ZSTR_KNOWN(ZEND_STR_MESSAGE)), - ZSTR_LEN(ZSTR_KNOWN(ZEND_STR_MESSAGE)), - "URL parsing failed" - ); + zval value; + ZVAL_STRING(&value, "URL parsing failed"); + zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(exception_zv), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value); + zval_ptr_dtor(&value); - zend_update_property(whatwg_invalid_url_exception_ce, Z_OBJ_P(exception_zv), ZEND_STRL("errors"), errors); + zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(exception_zv), ZEND_STRL("errors"), errors); } - static void *lexbor_clone_uri(void *uri) { lxb_url_t *lexbor_uri = (lxb_url_t *) uri; @@ -572,12 +601,8 @@ static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t rec case URI_RECOMPOSITION_RAW_UNICODE: ZEND_FALLTHROUGH; case URI_RECOMPOSITION_NORMALIZED_UNICODE: - if (lexbor_parser->idna == NULL) { - lexbor_parser->idna = lxb_unicode_idna_create(); - lxb_status_t status = lxb_unicode_idna_init(lexbor_parser->idna); - if (status != LXB_STATUS_OK) { - return NULL; - } + if (init_idna() == FAILURE) { + return NULL; } lxb_url_serialize_idna(lexbor_parser->idna, lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment); lxb_unicode_idna_clean(lexbor_parser->idna); diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 65adc85e085bb..23eb133fc19b6 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -23,7 +23,6 @@ #include "Zend/zend_exceptions.h" #include "Zend/zend_attributes.h" #include "Zend/zend_enum.h" -#include "main/php_ini.h" #include "ext/standard/info.h" #include "php_uri.h" @@ -32,14 +31,14 @@ #include "php_uri_arginfo.h" #include "uriparser/src/UriConfig.h" -zend_class_entry *whatwg_url_ce; -zend_object_handlers whatwg_uri_object_handlers; +zend_class_entry *uri_whatwg_url_ce; +zend_object_handlers uri_whatwg_uri_object_handlers; zend_class_entry *uri_comparison_mode_ce; zend_class_entry *uri_exception_ce; -zend_class_entry *invalid_uri_exception_ce; -zend_class_entry *whatwg_invalid_url_exception_ce; -zend_class_entry *whatwg_url_validation_error_type_ce; -zend_class_entry *whatwg_url_validation_error_ce; +zend_class_entry *uri_invalid_uri_exception_ce; +zend_class_entry *uri_whatwg_invalid_url_exception_ce; +zend_class_entry *uri_whatwg_url_validation_error_type_ce; +zend_class_entry *uri_whatwg_url_validation_error_ce; #define URIPARSER_VERSION PACKAGE_VERSION @@ -50,8 +49,6 @@ static const zend_module_dep uri_deps[] = { static zend_array uri_handlers; -static zend_object *uri_clone_obj_handler(zend_object *object); - static uri_handler_t *uri_handler_by_name(const char *handler_name, size_t handler_name_len) { return zend_hash_str_find_ptr(&uri_handlers, handler_name, handler_name_len); @@ -72,8 +69,6 @@ static HashTable *uri_get_debug_properties(zend_object *object) return result; } - zend_string *object_str = ZSTR_INIT_LITERAL("(object value omitted)", false); - const HashTable *property_handlers = internal_uri->handler->property_handlers; ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(property_handlers, string_key, property_handler) { zval value; @@ -87,8 +82,6 @@ static HashTable *uri_get_debug_properties(zend_object *object) zend_hash_update(result, string_key, &value); } ZEND_HASH_FOREACH_END(); - zend_string_release_ex(object_str, false); - return result; } @@ -100,15 +93,24 @@ PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) ZEND_PARSE_PARAMETERS_START(3, 3) Z_PARAM_STR(context) - Z_PARAM_OBJECT_OF_CLASS(type, whatwg_url_validation_error_type_ce) + Z_PARAM_OBJECT_OF_CLASS(type, uri_whatwg_url_validation_error_type_ce) Z_PARAM_BOOL(failure) ZEND_PARSE_PARAMETERS_END(); - zend_update_property_str(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("context"), context); - zend_update_property(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); + zend_update_property_str(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("context"), context); + if (EG(exception)) { + RETURN_THROWS(); + } + zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); + if (EG(exception)) { + RETURN_THROWS(); + } zval failure_zv; ZVAL_BOOL(&failure_zv, failure); - zend_update_property(whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("failure"), &failure_zv); + zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("failure"), &failure_zv); + if (EG(exception)) { + RETURN_THROWS(); + } } static zend_result pass_errors_by_ref(zval *errors_zv, zval *errors) @@ -190,7 +192,7 @@ static void create_whatwg_uri(INTERNAL_FUNCTION_PARAMETERS, bool is_constructor) ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_PATH_STR(uri_str) Z_PARAM_OPTIONAL - Z_PARAM_OBJ_OF_CLASS_OR_NULL(base_url_object, whatwg_url_ce) + Z_PARAM_OBJ_OF_CLASS_OR_NULL(base_url_object, uri_whatwg_url_ce) Z_PARAM_ZVAL(errors) ZEND_PARSE_PARAMETERS_END(); @@ -317,99 +319,93 @@ static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_na PHP_METHOD(Uri_WhatWg_Url, getScheme) { - URI_GETTER(ZSTR_KNOWN(ZEND_STR_SCHEME), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_SCHEME), + URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withScheme) { - URI_WITHER_STR(ZSTR_KNOWN(ZEND_STR_SCHEME)); + uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_SCHEME)); } PHP_METHOD(Uri_WhatWg_Url, getUsername) { - URI_GETTER(ZSTR_KNOWN(ZEND_STR_USERNAME), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_USERNAME), + URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withUsername) { - URI_WITHER_STR_OR_NULL(ZSTR_KNOWN(ZEND_STR_USERNAME)); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_USERNAME)); } PHP_METHOD(Uri_WhatWg_Url, getPassword) { - URI_GETTER(ZSTR_KNOWN(ZEND_STR_PASSWORD), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PASSWORD), + URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withPassword) { - URI_WITHER_STR_OR_NULL(ZSTR_KNOWN(ZEND_STR_PASSWORD)); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PASSWORD)); } PHP_METHOD(Uri_WhatWg_Url, getAsciiHost) { - URI_GETTER(ZSTR_KNOWN(ZEND_STR_HOST), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_HOST), URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, getUnicodeHost) { - URI_GETTER(ZSTR_KNOWN(ZEND_STR_HOST), URI_COMPONENT_READ_NORMALIZED_UNICODE); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_HOST), + URI_COMPONENT_READ_NORMALIZED_UNICODE); } PHP_METHOD(Uri_WhatWg_Url, withHost) { - zend_string *value; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_PATH_STR_OR_NULL(value) - ZEND_PARSE_PARAMETERS_END(); - - zval zv; - if (value == NULL) { - ZVAL_NULL(&zv); - } else { - ZVAL_STR_COPY(&zv, value); - } - - URI_WITHER_COMMON(ZSTR_KNOWN(ZEND_STR_HOST), &zv, return_value); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_HOST)); } PHP_METHOD(Uri_WhatWg_Url, getPort) { - URI_GETTER(ZSTR_KNOWN(ZEND_STR_PORT), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PORT), URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withPort) { - URI_WITHER_LONG_OR_NULL(ZSTR_KNOWN(ZEND_STR_PORT)); + uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PORT)); } PHP_METHOD(Uri_WhatWg_Url, getPath) { - URI_GETTER(ZSTR_KNOWN(ZEND_STR_PATH), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PATH), URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withPath) { - URI_WITHER_STR(ZSTR_KNOWN(ZEND_STR_PATH)); + uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PATH)); } PHP_METHOD(Uri_WhatWg_Url, getQuery) { - URI_GETTER(ZSTR_KNOWN(ZEND_STR_QUERY), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_QUERY), + URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withQuery) { - URI_WITHER_STR_OR_NULL(ZSTR_KNOWN(ZEND_STR_QUERY)); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_QUERY)); } PHP_METHOD(Uri_WhatWg_Url, getFragment) { - URI_GETTER(ZSTR_KNOWN(ZEND_STR_FRAGMENT), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_FRAGMENT), + URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withFragment) { - URI_WITHER_STR_OR_NULL(ZSTR_KNOWN(ZEND_STR_FRAGMENT)); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_FRAGMENT)); } PHP_METHOD(Uri_WhatWg_Url, equals) @@ -418,7 +414,7 @@ PHP_METHOD(Uri_WhatWg_Url, equals) zend_object *comparison_mode = NULL; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_OBJ_OF_CLASS(that_object, whatwg_url_ce) + Z_PARAM_OBJ_OF_CLASS(that_object, uri_whatwg_url_ce) Z_PARAM_OPTIONAL Z_PARAM_OBJ_OF_CLASS(comparison_mode, uri_comparison_mode_ce) ZEND_PARSE_PARAMETERS_END(); @@ -534,7 +530,7 @@ static void uri_free_obj_handler(zend_object *object) zend_object_std_dtor(&uri_object->std); } -static zend_object *uri_clone_obj_handler(zend_object *object) +zend_object *uri_clone_obj_handler(zend_object *object) { uri_object_t *uri_object = uri_object_from_obj(object); uri_internal_t *internal_uri = uri_internal_from_obj(object); @@ -590,17 +586,17 @@ zend_result uri_handler_register(const uri_handler_t *uri_handler) static PHP_MINIT_FUNCTION(uri) { - whatwg_url_ce = register_class_Uri_WhatWg_Url(); - php_uri_implementation_set_object_handlers(whatwg_url_ce, &whatwg_uri_object_handlers); + uri_whatwg_url_ce = register_class_Uri_WhatWg_Url(); + php_uri_implementation_set_object_handlers(uri_whatwg_url_ce, &uri_whatwg_uri_object_handlers); uri_comparison_mode_ce = register_class_Uri_UriComparisonMode(); uri_exception_ce = register_class_Uri_UriException(zend_ce_exception); - invalid_uri_exception_ce = register_class_Uri_InvalidUriException(uri_exception_ce); - whatwg_invalid_url_exception_ce = register_class_Uri_WhatWg_InvalidUrlException(invalid_uri_exception_ce); - whatwg_url_validation_error_ce = register_class_Uri_WhatWg_UrlValidationError(); - whatwg_url_validation_error_type_ce = register_class_Uri_WhatWg_UrlValidationErrorType(); + uri_invalid_uri_exception_ce = register_class_Uri_InvalidUriException(uri_exception_ce); + uri_whatwg_invalid_url_exception_ce = register_class_Uri_WhatWg_InvalidUrlException(uri_invalid_uri_exception_ce); + uri_whatwg_url_validation_error_ce = register_class_Uri_WhatWg_UrlValidationError(); + uri_whatwg_url_validation_error_type_ce = register_class_Uri_WhatWg_UrlValidationErrorType(); - zend_hash_init(&uri_handlers, 4, NULL, ZVAL_PTR_DTOR, true); + zend_hash_init(&uri_handlers, 4, NULL, NULL, true); if (uri_handler_register(&lexbor_uri_handler) == FAILURE) { return FAILURE; diff --git a/ext/uri/php_uri_common.c b/ext/uri/php_uri_common.c index 768597f420c19..cc7f87a16096b 100644 --- a/ext/uri/php_uri_common.c +++ b/ext/uri/php_uri_common.c @@ -22,7 +22,6 @@ void uri_register_property_handler(HashTable *property_handlers, zend_string *name, const uri_property_handler_t *handler) { zend_hash_add_new_ptr(property_handlers, name, (void *) handler); - zend_string_release_ex(name, true); } uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name) @@ -38,3 +37,109 @@ void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors) zend_throw_exception_object(&exception_zv); } + +void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name, uri_component_read_mode_t component_read_mode) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); + URI_ASSERT_INITIALIZATION(internal_uri); + + const uri_property_handler_t *property_handler = uri_property_handler_from_internal_uri(internal_uri, property_name); + ZEND_ASSERT(property_handler != NULL); + + if (UNEXPECTED(property_handler->read_func(internal_uri, component_read_mode, return_value) == FAILURE)) { + zend_throw_error(NULL, "%s::$%s property cannot be retrieved", ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(property_name)); + RETURN_THROWS(); + } +} + +static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name, zval *property_zv) +{ + uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); + URI_ASSERT_INITIALIZATION(internal_uri); + + const uri_property_handler_t *property_handler = uri_property_handler_from_internal_uri(internal_uri, property_name); + ZEND_ASSERT(property_handler != NULL); + + zend_object *new_object = uri_clone_obj_handler(Z_OBJ_P(ZEND_THIS)); + if (UNEXPECTED(EG(exception) != NULL)) { + zend_object_release(new_object); + zval_ptr_dtor(property_zv); + RETURN_THROWS(); + } + + uri_internal_t *new_internal_uri = uri_internal_from_obj(new_object); + URI_ASSERT_INITIALIZATION(new_internal_uri); + if (property_handler->write_func == NULL) { + zend_readonly_property_modification_error_ex(ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(property_name)); + zend_object_release(new_object); + zval_ptr_dtor(property_zv); + RETURN_THROWS(); + } + + zval errors; + ZVAL_UNDEF(&errors); + if (property_handler->write_func(new_internal_uri, property_zv, &errors) == FAILURE) { + throw_invalid_uri_exception(new_internal_uri->handler, &errors); + zval_ptr_dtor(&errors); + zend_object_release(new_object); + zval_ptr_dtor(property_zv); + RETURN_THROWS(); + } + + ZEND_ASSERT(Z_ISUNDEF(errors)); + RETVAL_OBJ(new_object); + zval_ptr_dtor(property_zv); +} + +void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name) +{ + zend_string *value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_PATH_STR(value) + ZEND_PARSE_PARAMETERS_END(); + + zval zv; + ZVAL_STR_COPY(&zv, value); + + uri_write_component_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_name, &zv); +} + +void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name) +{ + zend_string *value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_PATH_STR_OR_NULL(value) + ZEND_PARSE_PARAMETERS_END(); + + zval zv; + if (value == NULL) { + ZVAL_NULL(&zv); + } else { + ZVAL_STR_COPY(&zv, value); + } + + uri_write_component_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_name, &zv); +} + +void uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name) +{ + zend_long value; + bool value_is_null; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG_OR_NULL(value, value_is_null) + ZEND_PARSE_PARAMETERS_END(); + + zval zv; + if (value_is_null) { + ZVAL_NULL(&zv); + } else { + ZVAL_LONG(&zv, value); + } + + uri_write_component_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_name, &zv); +} diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index e576cea49c4f6..7b4b1062df6d6 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -17,14 +17,15 @@ #ifndef PHP_URI_COMMON_H #define PHP_URI_COMMON_H -extern zend_class_entry *whatwg_url_ce; -extern zend_object_handlers whatwg_uri_object_handlers; +extern zend_class_entry *uri_whatwg_url_ce; +extern zend_object_handlers uri_whatwg_uri_object_handlers; extern zend_class_entry *uri_comparison_mode_ce; extern zend_class_entry *uri_exception_ce; -extern zend_class_entry *invalid_uri_exception_ce; -extern zend_class_entry *whatwg_invalid_url_exception_ce; -extern zend_class_entry *whatwg_url_validation_error_type_ce; -extern zend_class_entry *whatwg_url_validation_error_ce; +extern zend_class_entry *uri_invalid_uri_exception_ce; +extern zend_class_entry *uri_whatwg_invalid_url_exception_ce; +extern zend_class_entry *uri_whatwg_url_validation_error_type_ce; +extern zend_class_entry *uri_whatwg_url_validation_error_ce; +extern zend_object *uri_clone_obj_handler(zend_object *object); typedef enum { URI_RECOMPOSITION_RAW_ASCII, @@ -99,92 +100,13 @@ void uri_register_property_handler(HashTable *property_handlers, zend_string *na zend_result uri_handler_register(const uri_handler_t *uri_handler); uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name); void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors); +void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name, uri_component_read_mode_t component_read_mode); +void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name); +void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name); +void uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name); #define URI_ASSERT_INITIALIZATION(internal_uri) do { \ ZEND_ASSERT(internal_uri != NULL && internal_uri->uri != NULL); \ } while (0) -#define URI_GETTER(property_name, component_read_mode) do { \ - ZEND_PARSE_PARAMETERS_NONE(); \ - uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); \ - URI_ASSERT_INITIALIZATION(internal_uri); \ - const uri_property_handler_t *property_handler = uri_property_handler_from_internal_uri(internal_uri, property_name); \ - ZEND_ASSERT(property_handler != NULL); \ - if (UNEXPECTED(property_handler->read_func(internal_uri, component_read_mode, return_value) == FAILURE)) { \ - zend_throw_error(NULL, "%s::$%s property cannot be retrieved", ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(property_name)); \ - RETURN_THROWS(); \ - } \ -} while (0) - -#define URI_WITHER_COMMON(property_name, property_zv, return_value) \ - uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); \ - URI_ASSERT_INITIALIZATION(internal_uri); \ - const uri_property_handler_t *property_handler = uri_property_handler_from_internal_uri(internal_uri, property_name); \ - ZEND_ASSERT(property_handler != NULL); \ - zend_object *new_object = uri_clone_obj_handler(Z_OBJ_P(ZEND_THIS)); \ - if (UNEXPECTED(EG(exception) != NULL)) { \ - zend_object_release(new_object); \ - zval_ptr_dtor(property_zv); \ - RETURN_THROWS(); \ - } \ - uri_internal_t *new_internal_uri = uri_internal_from_obj(new_object); \ - URI_ASSERT_INITIALIZATION(new_internal_uri); /* TODO fix memory leak of new_object */ \ - if (property_handler->write_func == NULL) { \ - zend_readonly_property_modification_error_ex(ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(property_name)); \ - zend_object_release(new_object); \ - zval_ptr_dtor(property_zv); \ - RETURN_THROWS(); \ - } \ - zval errors; \ - ZVAL_UNDEF(&errors); \ - if (property_handler->write_func(new_internal_uri, property_zv, &errors) == FAILURE) { \ - throw_invalid_uri_exception(new_internal_uri->handler, &errors); \ - zval_ptr_dtor(&errors); \ - zend_object_release(new_object); \ - zval_ptr_dtor(property_zv); \ - RETURN_THROWS(); \ - } \ - ZEND_ASSERT(Z_ISUNDEF(errors)); \ - RETVAL_OBJ(new_object); \ - zval_ptr_dtor(property_zv); - -#define URI_WITHER_STR(property_name) do { \ - zend_string *value; \ - ZEND_PARSE_PARAMETERS_START(1, 1) \ - Z_PARAM_PATH_STR(value) \ - ZEND_PARSE_PARAMETERS_END(); \ - zval zv; \ - ZVAL_STR_COPY(&zv, value); \ - URI_WITHER_COMMON(property_name, &zv, return_value) \ -} while (0) - -#define URI_WITHER_STR_OR_NULL(property_name) do { \ - zend_string *value; \ - ZEND_PARSE_PARAMETERS_START(1, 1) \ - Z_PARAM_PATH_STR_OR_NULL(value) \ - ZEND_PARSE_PARAMETERS_END(); \ - zval zv; \ - if (value == NULL) { \ - ZVAL_NULL(&zv); \ - } else { \ - ZVAL_STR_COPY(&zv, value); \ - } \ - URI_WITHER_COMMON(property_name, &zv, return_value) \ -} while (0) - -#define URI_WITHER_LONG_OR_NULL(property_name) do { \ - zend_long value; \ - bool value_is_null; \ - ZEND_PARSE_PARAMETERS_START(1, 1) \ - Z_PARAM_LONG_OR_NULL(value, value_is_null) \ - ZEND_PARSE_PARAMETERS_END(); \ - zval zv; \ - if (value_is_null) {\ - ZVAL_NULL(&zv); \ - } else { \ - ZVAL_LONG(&zv, value); \ - } \ - URI_WITHER_COMMON(property_name, &zv, return_value); \ -} while (0) - #endif diff --git a/ext/uri/tests/052.phpt b/ext/uri/tests/052.phpt new file mode 100644 index 0000000000000..af7d05b893ea5 --- /dev/null +++ b/ext/uri/tests/052.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test UrlValidationError constructor error handling +--EXTENSIONS-- +uri +--FILE-- +__construct('bar', Uri\WhatWg\UrlValidationErrorType::HostMissing, false); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +var_dump($r); + +?> +--EXPECTF-- +Cannot modify readonly property Uri\WhatWg\UrlValidationError::$context +object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(3) "foo" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::DomainInvalidCodePoint) + ["failure"]=> + bool(true) +} From eb755a00a00ba7607511f97abb13c7d4183c0fa2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 30 May 2025 23:00:15 +0200 Subject: [PATCH 04/15] Fix Windows build The includes are not on the system path, but should be taken from the build include paths. --- ext/uri/php_lexbor.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/uri/php_lexbor.h b/ext/uri/php_lexbor.h index af6c094b46620..a2665aaba7ee5 100644 --- a/ext/uri/php_lexbor.h +++ b/ext/uri/php_lexbor.h @@ -17,8 +17,8 @@ #ifndef PHP_LEXBOR_H #define PHP_LEXBOR_H -#include -#include +#include "php_uri_common.h" +#include "lexbor/url/url.h" extern const uri_handler_t lexbor_uri_handler; From 1604e6a1c0a2084ab1ec12634ec9f4df0f5bec37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sat, 31 May 2025 11:12:00 +0200 Subject: [PATCH 05/15] Review fixes --- ext/uri/php_lexbor.c | 64 ++++++++++++++------------------ ext/uri/php_lexbor.h | 6 +++ ext/uri/php_uri.c | 77 ++++++++++++++++++++++++++++++--------- ext/uri/php_uri.stub.php | 2 + ext/uri/php_uri_arginfo.h | 17 ++++++++- ext/uri/php_uri_common.c | 8 +--- ext/uri/php_uri_common.h | 3 +- ext/uri/tests/053.phpt | 63 ++++++++++++++++++++++++++++++++ 8 files changed, 177 insertions(+), 63 deletions(-) create mode 100644 ext/uri/tests/053.phpt diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index a1a0e6866d393..e8c3810391b92 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -23,13 +23,11 @@ #include #endif -static zend_result lexbor_init_parser(void); static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors); static void lexbor_create_invalid_uri_exception(zval *exception_zv, zval *errors); static void *lexbor_clone_uri(void *uri); static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment); static void lexbor_free_uri(void *uri); -static zend_result lexbor_destroy_parser(void); static HashTable lexbor_property_handlers; @@ -38,13 +36,11 @@ ZEND_TLS int lexbor_urls; const uri_handler_t lexbor_uri_handler = { URI_PARSER_WHATWG, - lexbor_init_parser, lexbor_parse_uri, lexbor_create_invalid_uri_exception, lexbor_clone_uri, lexbor_uri_to_string, lexbor_free_uri, - lexbor_destroy_parser, &lexbor_property_handlers }; @@ -67,14 +63,6 @@ const uri_handler_t lexbor_uri_handler = { } \ } while (0) -#define CHECK_WRITE_RESULT(status, errors) do { \ - if (status != LXB_STATUS_OK) { \ - fill_errors(errors); \ - return FAILURE; \ - } \ - return SUCCESS; \ -} while (0) - #define LEXBOR_READ_ASCII_URI_COMPONENT(start, len, read_mode, retval) do { \ ZVAL_STRINGL(retval, (const char *) start, len); \ } while (0) @@ -528,7 +516,26 @@ static zend_result lexbor_write_fragment(uri_internal_t *internal_uri, zval *val return SUCCESS; } -static zend_result lexbor_init_parser(void) +void lexbor_module_init(void) +{ + zend_hash_init(&lexbor_property_handlers, 8, NULL, NULL, true); + + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_SCHEME), lexbor_read_scheme, lexbor_write_scheme); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_USERNAME), lexbor_read_username, lexbor_write_username); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PASSWORD), lexbor_read_password, lexbor_write_password); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_HOST), lexbor_read_host, lexbor_write_host); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PORT), lexbor_read_port, lexbor_write_port); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PATH), lexbor_read_path, lexbor_write_path); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_QUERY), lexbor_read_query, lexbor_write_query); + URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_FRAGMENT), lexbor_read_fragment, lexbor_write_fragment); +} + +void lexbor_module_shutdown(void) +{ + zend_hash_destroy(&lexbor_property_handlers); +} + +zend_result lexbor_request_init(void) { lexbor_mraw_t *mraw = lexbor_mraw_create(); lxb_status_t status = lexbor_mraw_init(mraw, 4096 * 2); @@ -548,18 +555,16 @@ static zend_result lexbor_init_parser(void) lexbor_urls = 0; - zend_hash_init(&lexbor_property_handlers, 8, NULL, NULL, true); + return SUCCESS; +} - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_SCHEME), lexbor_read_scheme, lexbor_write_scheme); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_USERNAME), lexbor_read_username, lexbor_write_username); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PASSWORD), lexbor_read_password, lexbor_write_password); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_HOST), lexbor_read_host, lexbor_write_host); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PORT), lexbor_read_port, lexbor_write_port); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PATH), lexbor_read_path, lexbor_write_path); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_QUERY), lexbor_read_query, lexbor_write_query); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_FRAGMENT), lexbor_read_fragment, lexbor_write_fragment); +void lexbor_request_shutdown(void) +{ + lxb_url_parser_memory_destroy(lexbor_parser); + lxb_url_parser_destroy(lexbor_parser, true); - return SUCCESS; + lexbor_parser = NULL; + lexbor_urls = 0; } static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors) @@ -621,16 +626,3 @@ static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t rec static void lexbor_free_uri(void *uri) { } - -static zend_result lexbor_destroy_parser(void) -{ - lxb_url_parser_memory_destroy(lexbor_parser); - lxb_url_parser_destroy(lexbor_parser, true); - - lexbor_parser = NULL; - lexbor_urls = 0; - - zend_hash_destroy(&lexbor_property_handlers); - - return SUCCESS; -} diff --git a/ext/uri/php_lexbor.h b/ext/uri/php_lexbor.h index a2665aaba7ee5..24f4fbfffe5e6 100644 --- a/ext/uri/php_lexbor.h +++ b/ext/uri/php_lexbor.h @@ -22,4 +22,10 @@ extern const uri_handler_t lexbor_uri_handler; +void lexbor_module_init(void); +void lexbor_module_shutdown(void); + +zend_result lexbor_request_init(void); +void lexbor_request_shutdown(void); + #endif diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 23eb133fc19b6..2bb48c80b3cbc 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -85,6 +85,56 @@ static HashTable *uri_get_debug_properties(zend_object *object) return result; } +PHP_METHOD(Uri_WhatWg_InvalidUrlException, __construct) +{ + zend_string *message = NULL; + zval *errors = NULL; + zend_long code = 0; + zval *previous = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 4) + Z_PARAM_OPTIONAL + Z_PARAM_STR(message) + Z_PARAM_ARRAY(errors) + Z_PARAM_LONG(code) + Z_PARAM_OBJECT_OF_CLASS_OR_NULL(previous, zend_ce_throwable) + ZEND_PARSE_PARAMETERS_END(); + + zval tmp; + if (message != NULL) { + ZVAL_STR(&tmp, message); + zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); + if (EG(exception)) { + RETURN_THROWS(); + } + } + + if (errors == NULL) { + ZVAL_EMPTY_ARRAY(&tmp); + zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), &tmp); + } else { + zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), errors); + } + if (EG(exception)) { + RETURN_THROWS(); + } + + if (code != 0) { + ZVAL_LONG(&tmp, code); + zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_CODE), &tmp); + if (EG(exception)) { + RETURN_THROWS(); + } + } + + if (previous != NULL) { + zend_update_property_ex(zend_ce_exception, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); + if (EG(exception)) { + RETURN_THROWS(); + } + } +} + PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) { zend_string *context; @@ -101,10 +151,12 @@ PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) if (EG(exception)) { RETURN_THROWS(); } + zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); if (EG(exception)) { RETURN_THROWS(); } + zval failure_zv; ZVAL_BOOL(&failure_zv, failure); zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("failure"), &failure_zv); @@ -519,7 +571,6 @@ static zend_object *uri_create_object_handler(zend_class_entry *class_type) static void uri_free_obj_handler(zend_object *object) { uri_object_t *uri_object = uri_object_from_obj(object); - ZEND_ASSERT(uri_object != NULL); if (UNEXPECTED(uri_object->internal.uri != NULL)) { uri_object->internal.handler->free_uri(uri_object->internal.uri); @@ -568,13 +619,11 @@ zend_result uri_handler_register(const uri_handler_t *uri_handler) zend_string *key = zend_string_init_interned(uri_handler->name, strlen(uri_handler->name), 1); ZEND_ASSERT(uri_handler->name != NULL); - ZEND_ASSERT(uri_handler->init_parser != NULL); ZEND_ASSERT(uri_handler->parse_uri != NULL); ZEND_ASSERT(uri_handler->create_invalid_uri_exception != NULL); ZEND_ASSERT(uri_handler->clone_uri != NULL); ZEND_ASSERT(uri_handler->uri_to_string != NULL); ZEND_ASSERT(uri_handler->free_uri != NULL); - ZEND_ASSERT(uri_handler->destroy_parser != NULL); ZEND_ASSERT(uri_handler->property_handlers != NULL); zend_result result = zend_hash_add_ptr(&uri_handlers, key, (void *) uri_handler) != NULL ? SUCCESS : FAILURE; @@ -602,6 +651,8 @@ static PHP_MINIT_FUNCTION(uri) return FAILURE; } + lexbor_module_init(); + return SUCCESS; } @@ -615,6 +666,8 @@ static PHP_MINFO_FUNCTION(uri) static PHP_MSHUTDOWN_FUNCTION(uri) { + lexbor_module_shutdown(); + zend_hash_destroy(&uri_handlers); return SUCCESS; @@ -622,26 +675,16 @@ static PHP_MSHUTDOWN_FUNCTION(uri) PHP_RINIT_FUNCTION(uri) { - uri_handler_t *handler; - - ZEND_HASH_MAP_FOREACH_PTR(&uri_handlers, handler) { - if (handler->init_parser() == FAILURE) { - return FAILURE; - } - } ZEND_HASH_FOREACH_END(); + if (lexbor_request_init() == FAILURE) { + return FAILURE; + } return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(uri) { - uri_handler_t *handler; - - ZEND_HASH_MAP_FOREACH_PTR(&uri_handlers, handler) { - if (handler->destroy_parser() == FAILURE) { - return FAILURE; - } - } ZEND_HASH_FOREACH_END(); + lexbor_request_shutdown(); return SUCCESS; } diff --git a/ext/uri/php_uri.stub.php b/ext/uri/php_uri.stub.php index 2434a2eed34ba..ef49e4ba6f968 100644 --- a/ext/uri/php_uri.stub.php +++ b/ext/uri/php_uri.stub.php @@ -25,6 +25,8 @@ enum UriComparisonMode class InvalidUrlException extends \Uri\InvalidUriException { public readonly array $errors; + + public function __construct(string $message = "", array $errors = [], int $code = 0, ?\Throwable $previous = null) {} } enum UrlValidationErrorType diff --git a/ext/uri/php_uri_arginfo.h b/ext/uri/php_uri_arginfo.h index 1adf8f71eb217..0ae755a9f70dc 100644 --- a/ext/uri/php_uri_arginfo.h +++ b/ext/uri/php_uri_arginfo.h @@ -1,5 +1,12 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 24e5f7ae5b0f3938a6c3e5e733131a05d10e4e3b */ + * Stub hash: 1945c28deef13c2af552b18c2a5a6c7798d4aeec */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_InvalidUrlException___construct, 0, 0, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 0, "\"\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, errors, IS_ARRAY, 0, "[]") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, code, IS_LONG, 0, "0") + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, previous, Throwable, 1, "null") +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_UrlValidationError___construct, 0, 0, 3) ZEND_ARG_TYPE_INFO(0, context, IS_STRING, 0) @@ -95,6 +102,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_WhatWg_Url___debugInfo arginfo_class_Uri_WhatWg_Url___serialize +ZEND_METHOD(Uri_WhatWg_InvalidUrlException, __construct); ZEND_METHOD(Uri_WhatWg_UrlValidationError, __construct); ZEND_METHOD(Uri_WhatWg_Url, parse); ZEND_METHOD(Uri_WhatWg_Url, __construct); @@ -123,6 +131,11 @@ ZEND_METHOD(Uri_WhatWg_Url, __serialize); ZEND_METHOD(Uri_WhatWg_Url, __unserialize); ZEND_METHOD(Uri_WhatWg_Url, __debugInfo); +static const zend_function_entry class_Uri_WhatWg_InvalidUrlException_methods[] = { + ZEND_ME(Uri_WhatWg_InvalidUrlException, __construct, arginfo_class_Uri_WhatWg_InvalidUrlException___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static const zend_function_entry class_Uri_WhatWg_UrlValidationError_methods[] = { ZEND_ME(Uri_WhatWg_UrlValidationError, __construct, arginfo_class_Uri_WhatWg_UrlValidationError___construct, ZEND_ACC_PUBLIC) ZEND_FE_END @@ -193,7 +206,7 @@ static zend_class_entry *register_class_Uri_WhatWg_InvalidUrlException(zend_clas { zend_class_entry ce, *class_entry; - INIT_NS_CLASS_ENTRY(ce, "Uri\\WhatWg", "InvalidUrlException", NULL); + INIT_NS_CLASS_ENTRY(ce, "Uri\\WhatWg", "InvalidUrlException", class_Uri_WhatWg_InvalidUrlException_methods); class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Uri_InvalidUriException, ZEND_ACC_NO_DYNAMIC_PROPERTIES); zval property_errors_default_value; diff --git a/ext/uri/php_uri_common.c b/ext/uri/php_uri_common.c index cc7f87a16096b..d2ae0d48388dc 100644 --- a/ext/uri/php_uri_common.c +++ b/ext/uri/php_uri_common.c @@ -65,7 +65,6 @@ static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, zend_string *pr zend_object *new_object = uri_clone_obj_handler(Z_OBJ_P(ZEND_THIS)); if (UNEXPECTED(EG(exception) != NULL)) { zend_object_release(new_object); - zval_ptr_dtor(property_zv); RETURN_THROWS(); } @@ -74,7 +73,6 @@ static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, zend_string *pr if (property_handler->write_func == NULL) { zend_readonly_property_modification_error_ex(ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(property_name)); zend_object_release(new_object); - zval_ptr_dtor(property_zv); RETURN_THROWS(); } @@ -84,13 +82,11 @@ static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, zend_string *pr throw_invalid_uri_exception(new_internal_uri->handler, &errors); zval_ptr_dtor(&errors); zend_object_release(new_object); - zval_ptr_dtor(property_zv); RETURN_THROWS(); } ZEND_ASSERT(Z_ISUNDEF(errors)); RETVAL_OBJ(new_object); - zval_ptr_dtor(property_zv); } void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name) @@ -102,7 +98,7 @@ void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, zend_string *property ZEND_PARSE_PARAMETERS_END(); zval zv; - ZVAL_STR_COPY(&zv, value); + ZVAL_STR(&zv, value); uri_write_component_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_name, &zv); } @@ -119,7 +115,7 @@ void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, zend_string * if (value == NULL) { ZVAL_NULL(&zv); } else { - ZVAL_STR_COPY(&zv, value); + ZVAL_STR(&zv, value); } uri_write_component_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_name, &zv); diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index 7b4b1062df6d6..ce11a8394951d 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -43,13 +43,12 @@ typedef enum { typedef struct uri_handler_t { const char *name; - zend_result (*init_parser)(void); void *(*parse_uri)(const zend_string *uri_str, const void *base_url, zval *errors); void (*create_invalid_uri_exception)(zval *exception_zv, zval *errors); void *(*clone_uri)(void *uri); zend_string *(*uri_to_string)(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment); void (*free_uri)(void *uri); - zend_result (*destroy_parser)(void); + HashTable *property_handlers; } uri_handler_t; diff --git a/ext/uri/tests/053.phpt b/ext/uri/tests/053.phpt new file mode 100644 index 0000000000000..93ff77b15c0a5 --- /dev/null +++ b/ext/uri/tests/053.phpt @@ -0,0 +1,63 @@ +--TEST-- +Test InvalidUrlException constructor error handling +--EXTENSIONS-- +uri +--FILE-- +__construct("foo"); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + $r->__construct("bar", []); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + $r->__construct("baz", [], false); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + $r->__construct("qax", [], false, null); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +var_dump($r->getMessage()); +var_dump($r->errors); +var_dump($r->getCode()); +var_dump($r->getPrevious()::class); + +?> +--EXPECTF-- +Cannot modify readonly property Uri\WhatWg\InvalidUrlException::$errors +Cannot modify readonly property Uri\WhatWg\InvalidUrlException::$errors +Cannot modify readonly property Uri\WhatWg\InvalidUrlException::$errors +Cannot modify readonly property Uri\WhatWg\InvalidUrlException::$errors +string(3) "qax" +array(%d) { + [%d]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(3) "abc" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::DomainInvalidCodePoint) + ["failure"]=> + bool(true) + } +} +int(1) +string(9) "Exception" From 65f66c985ceef8076b66501970b9c96c529d8f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 1 Jun 2025 13:31:48 +0200 Subject: [PATCH 06/15] Fix memory leak + add asserts --- ext/uri/php_lexbor.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index e8c3810391b92..af1f4cfb64b8a 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -50,6 +50,7 @@ const uri_handler_t lexbor_uri_handler = { if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) > 0) { \ lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \ } else { \ + ZEND_ASSERT(Z_ISNULL_P(value) || (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) == 0)); \ lexbor_str_init(&str, lexbor_parser->mraw, 0); \ } \ } while (0) @@ -58,7 +59,9 @@ const uri_handler_t lexbor_uri_handler = { if (Z_TYPE_P(value) == IS_LONG) { \ ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); \ lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \ + zval_ptr_dtor(value); \ } else { \ + ZEND_ASSERT(Z_ISNULL_P(value)); \ lexbor_str_init(&str, lexbor_parser->mraw, 0); \ } \ } while (0) From 2f177ebfdead3274e47a35d86efd6af6971d508d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 1 Jun 2025 23:38:56 +0200 Subject: [PATCH 07/15] Next review round fixes --- ext/uri/php_lexbor.c | 118 +++++++++++++++++---------------------- ext/uri/php_uri.c | 80 +++++++++++++++----------- ext/uri/php_uri_common.c | 31 +++++++--- ext/uri/php_uri_common.h | 46 +++++++-------- 4 files changed, 146 insertions(+), 129 deletions(-) diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index af1f4cfb64b8a..ba8f1440e0b97 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -23,35 +23,20 @@ #include #endif -static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors); -static void lexbor_create_invalid_uri_exception(zval *exception_zv, zval *errors); -static void *lexbor_clone_uri(void *uri); -static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment); -static void lexbor_free_uri(void *uri); - -static HashTable lexbor_property_handlers; - ZEND_TLS lxb_url_parser_t *lexbor_parser; ZEND_TLS int lexbor_urls; -const uri_handler_t lexbor_uri_handler = { - URI_PARSER_WHATWG, - lexbor_parse_uri, - lexbor_create_invalid_uri_exception, - lexbor_clone_uri, - lexbor_uri_to_string, - lexbor_free_uri, - &lexbor_property_handlers -}; - -#define MAX_URL_COUNT 500 +#define LEXBOR_MAX_URL_COUNT 500 +#define LEXBOR_MRAW_BYTE_SIZE 8192 #define ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str) do { \ if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) > 0) { \ - lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \ + str.data = (lxb_char_t *) Z_STRVAL_P(value); \ + str.length = Z_STRLEN_P(value); \ } else { \ ZEND_ASSERT(Z_ISNULL_P(value) || (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) == 0)); \ - lexbor_str_init(&str, lexbor_parser->mraw, 0); \ + str.data = (lxb_char_t *) ""; \ + str.length = 0; \ } \ } while (0) @@ -59,20 +44,21 @@ const uri_handler_t lexbor_uri_handler = { if (Z_TYPE_P(value) == IS_LONG) { \ ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); \ lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \ - zval_ptr_dtor(value); \ + zval_ptr_dtor_str(value); \ } else { \ ZEND_ASSERT(Z_ISNULL_P(value)); \ - lexbor_str_init(&str, lexbor_parser->mraw, 0); \ + str.data = (lxb_char_t *) ""; \ + str.length = 0; \ } \ } while (0) -#define LEXBOR_READ_ASCII_URI_COMPONENT(start, len, read_mode, retval) do { \ +#define LEXBOR_READ_ASCII_URI_COMPONENT(start, len, retval) do { \ ZVAL_STRINGL(retval, (const char *) start, len); \ } while (0) static void lexbor_cleanup_parser(void) { - if (++lexbor_urls % MAX_URL_COUNT == 0) { + if (++lexbor_urls % LEXBOR_MAX_URL_COUNT == 0) { lexbor_mraw_clean(lexbor_parser->mraw); } @@ -246,7 +232,7 @@ static lxb_status_t lexbor_serialize_callback(const lxb_char_t *data, size_t len return LXB_STATUS_OK; } -static zend_result lexbor_read_scheme(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +static zend_result lexbor_read_scheme(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { lxb_url_t *lexbor_uri = internal_uri->uri; @@ -257,7 +243,7 @@ static zend_result lexbor_read_scheme(const uri_internal_t *internal_uri, uri_co return SUCCESS; } -static zend_result lexbor_write_scheme(uri_internal_t *internal_uri, zval *value, zval *errors) +static zend_result lexbor_write_scheme(struct uri_internal_t *internal_uri, zval *value, zval *errors) { lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; @@ -273,12 +259,12 @@ static zend_result lexbor_write_scheme(uri_internal_t *internal_uri, zval *value return SUCCESS; } -static zend_result lexbor_read_username(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +static zend_result lexbor_read_username(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->username.length) { - LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->username.data, lexbor_uri->username.length, read_mode, retval); + LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->username.data, lexbor_uri->username.length, retval); } else { ZVAL_NULL(retval); } @@ -302,12 +288,12 @@ static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *val return SUCCESS; } -static zend_result lexbor_read_password(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +static zend_result lexbor_read_password(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->password.length > 0) { - LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->password.data, lexbor_uri->password.length, read_mode, retval); + LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->password.data, lexbor_uri->password.length, retval); } else { ZVAL_NULL(retval); } @@ -315,7 +301,7 @@ static zend_result lexbor_read_password(const uri_internal_t *internal_uri, uri_ return SUCCESS; } -static zend_result lexbor_write_password(uri_internal_t *internal_uri, zval *value, zval *errors) +static zend_result lexbor_write_password(struct uri_internal_t *internal_uri, zval *value, zval *errors) { lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; @@ -342,7 +328,7 @@ static ZEND_RESULT_CODE init_idna(void) return lxb_unicode_idna_init(lexbor_parser->idna) == LXB_STATUS_OK ? SUCCESS : FAILURE; } -static zend_result lexbor_read_host(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +static zend_result lexbor_read_host(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { lxb_url_t *lexbor_uri = internal_uri->uri; @@ -387,7 +373,7 @@ static zend_result lexbor_read_host(const uri_internal_t *internal_uri, uri_comp return SUCCESS; } -static zend_result lexbor_write_host(uri_internal_t *internal_uri, zval *value, zval *errors) +static zend_result lexbor_write_host(struct uri_internal_t *internal_uri, zval *value, zval *errors) { lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; @@ -403,7 +389,7 @@ static zend_result lexbor_write_host(uri_internal_t *internal_uri, zval *value, return SUCCESS; } -static zend_result lexbor_read_port(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +static zend_result lexbor_read_port(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { lxb_url_t *lexbor_uri = internal_uri->uri; @@ -416,7 +402,7 @@ static zend_result lexbor_read_port(const uri_internal_t *internal_uri, uri_comp return SUCCESS; } -static zend_result lexbor_write_port(uri_internal_t *internal_uri, zval *value, zval *errors) +static zend_result lexbor_write_port(struct uri_internal_t *internal_uri, zval *value, zval *errors) { lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; @@ -432,12 +418,12 @@ static zend_result lexbor_write_port(uri_internal_t *internal_uri, zval *value, return SUCCESS; } -static zend_result lexbor_read_path(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +static zend_result lexbor_read_path(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->path.str.length) { - LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->path.str.data, lexbor_uri->path.str.length, read_mode, retval); + LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->path.str.data, lexbor_uri->path.str.length, retval); } else { ZVAL_EMPTY_STRING(retval); } @@ -445,7 +431,7 @@ static zend_result lexbor_read_path(const uri_internal_t *internal_uri, uri_comp return SUCCESS; } -static zend_result lexbor_write_path(uri_internal_t *internal_uri, zval *value, zval *errors) +static zend_result lexbor_write_path(struct uri_internal_t *internal_uri, zval *value, zval *errors) { lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; @@ -461,12 +447,12 @@ static zend_result lexbor_write_path(uri_internal_t *internal_uri, zval *value, return SUCCESS; } -static zend_result lexbor_read_query(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +static zend_result lexbor_read_query(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->query.length) { - LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->query.data, lexbor_uri->query.length, read_mode, retval); + LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->query.data, lexbor_uri->query.length, retval); } else { ZVAL_NULL(retval); } @@ -474,7 +460,7 @@ static zend_result lexbor_read_query(const uri_internal_t *internal_uri, uri_com return SUCCESS; } -static zend_result lexbor_write_query(uri_internal_t *internal_uri, zval *value, zval *errors) +static zend_result lexbor_write_query(struct uri_internal_t *internal_uri, zval *value, zval *errors) { lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; @@ -490,12 +476,12 @@ static zend_result lexbor_write_query(uri_internal_t *internal_uri, zval *value, return SUCCESS; } -static zend_result lexbor_read_fragment(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +static zend_result lexbor_read_fragment(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->fragment.length) { - LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->fragment.data, lexbor_uri->fragment.length, read_mode, retval); + LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->fragment.data, lexbor_uri->fragment.length, retval); } else { ZVAL_NULL(retval); } @@ -503,7 +489,7 @@ static zend_result lexbor_read_fragment(const uri_internal_t *internal_uri, uri_ return SUCCESS; } -static zend_result lexbor_write_fragment(uri_internal_t *internal_uri, zval *value, zval *errors) +static zend_result lexbor_write_fragment(struct uri_internal_t *internal_uri, zval *value, zval *errors) { lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; @@ -519,29 +505,10 @@ static zend_result lexbor_write_fragment(uri_internal_t *internal_uri, zval *val return SUCCESS; } -void lexbor_module_init(void) -{ - zend_hash_init(&lexbor_property_handlers, 8, NULL, NULL, true); - - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_SCHEME), lexbor_read_scheme, lexbor_write_scheme); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_USERNAME), lexbor_read_username, lexbor_write_username); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PASSWORD), lexbor_read_password, lexbor_write_password); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_HOST), lexbor_read_host, lexbor_write_host); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PORT), lexbor_read_port, lexbor_write_port); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_PATH), lexbor_read_path, lexbor_write_path); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_QUERY), lexbor_read_query, lexbor_write_query); - URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(&lexbor_property_handlers, ZSTR_KNOWN(ZEND_STR_FRAGMENT), lexbor_read_fragment, lexbor_write_fragment); -} - -void lexbor_module_shutdown(void) -{ - zend_hash_destroy(&lexbor_property_handlers); -} - zend_result lexbor_request_init(void) { lexbor_mraw_t *mraw = lexbor_mraw_create(); - lxb_status_t status = lexbor_mraw_init(mraw, 4096 * 2); + lxb_status_t status = lexbor_mraw_init(mraw, LEXBOR_MRAW_BYTE_SIZE); if (status != LXB_STATUS_OK) { lexbor_mraw_destroy(mraw, true); return FAILURE; @@ -588,7 +555,7 @@ static void lexbor_create_invalid_uri_exception(zval *exception_zv, zval *errors zval value; ZVAL_STRING(&value, "URL parsing failed"); zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(exception_zv), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value); - zval_ptr_dtor(&value); + zval_ptr_dtor_str(&value); zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(exception_zv), ZEND_STRL("errors"), errors); } @@ -629,3 +596,22 @@ static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t rec static void lexbor_free_uri(void *uri) { } + +const uri_handler_t lexbor_uri_handler = { + .name = URI_PARSER_WHATWG, + .parse_uri = lexbor_parse_uri, + .create_invalid_uri_exception = lexbor_create_invalid_uri_exception, + .clone_uri = lexbor_clone_uri, + .uri_to_string = lexbor_uri_to_string, + .free_uri = lexbor_free_uri, + { + .scheme = {.read_func = lexbor_read_scheme, .write_func = lexbor_write_scheme}, + .username = {.read_func = lexbor_read_username, .write_func = lexbor_write_username}, + .password = {.read_func = lexbor_read_password, .write_func = lexbor_write_password}, + .host = {.read_func = lexbor_read_host, .write_func = lexbor_write_host}, + .port = {.read_func = lexbor_read_port, .write_func = lexbor_write_port}, + .path = {.read_func = lexbor_read_path, .write_func = lexbor_write_path}, + .query = {.read_func = lexbor_read_query, .write_func = lexbor_write_query}, + .fragment = {.read_func = lexbor_read_fragment, .write_func = lexbor_write_fragment}, + } +}; diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 2bb48c80b3cbc..8493d0136cf58 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -59,9 +59,6 @@ static HashTable *uri_get_debug_properties(zend_object *object) uri_internal_t *internal_uri = uri_internal_from_obj(object); ZEND_ASSERT(internal_uri != NULL); - zend_string *string_key; - uri_property_handler_t *property_handler; - HashTable *std_properties = zend_std_get_properties(object); HashTable *result = zend_array_dup(std_properties); @@ -69,18 +66,40 @@ static HashTable *uri_get_debug_properties(zend_object *object) return result; } - const HashTable *property_handlers = internal_uri->handler->property_handlers; - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(property_handlers, string_key, property_handler) { - zval value; + const uri_property_handlers_t property_handlers = internal_uri->handler->property_handlers; - ZEND_ASSERT(string_key != NULL); + zval tmp; + if (property_handlers.scheme.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_SCHEME), &tmp); + } - if (property_handler->read_func(internal_uri, URI_COMPONENT_READ_RAW, &value) == FAILURE) { - continue; - } + if (property_handlers.username.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_USERNAME), &tmp); + } + + if (property_handlers.password.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_PASSWORD), &tmp); + } + + if (property_handlers.host.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_HOST), &tmp); + } + + if (property_handlers.port.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_PORT), &tmp); + } + + if (property_handlers.path.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_PATH), &tmp); + } + + if (property_handlers.query.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_QUERY), &tmp); + } - zend_hash_update(result, string_key, &value); - } ZEND_HASH_FOREACH_END(); + if (property_handlers.fragment.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_FRAGMENT), &tmp); + } return result; } @@ -173,17 +192,13 @@ static zend_result pass_errors_by_ref(zval *errors_zv, zval *errors) return SUCCESS; } - if (errors_zv != NULL) { - errors_zv = zend_try_array_init(errors_zv); - if (!errors_zv) { - return FAILURE; - } + if (errors_zv == NULL) { + return SUCCESS; + } - zval *error; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(errors), error) { - Z_ADDREF_P(error); - zend_hash_next_index_insert_new(Z_ARRVAL_P(errors_zv), error); - } ZEND_HASH_FOREACH_END(); + ZEND_TRY_ASSIGN_REF_ARR(errors_zv, Z_ARRVAL_P(errors)); + if (EG(exception)) { + return FAILURE; } return SUCCESS; @@ -210,14 +225,16 @@ PHPAPI void php_uri_instantiate_uri( zval_ptr_dtor(&errors); RETURN_THROWS(); } else { - pass_errors_by_ref(errors_zv, &errors); - zval_ptr_dtor(&errors); + if (pass_errors_by_ref(errors_zv, &errors) == FAILURE) { + RETURN_THROWS(); + } RETURN_NULL(); } } - pass_errors_by_ref(errors_zv, &errors); - zval_ptr_dtor(&errors); + if (pass_errors_by_ref(errors_zv, &errors) == FAILURE) { + RETURN_THROWS(); + } uri_object_t *uri_object; if (should_update_this_object) { @@ -362,8 +379,8 @@ static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_na RETURN_THROWS(); } - object_properties_load(object, Z_ARRVAL_P(arr)); - if (EG(exception)) { + /* Verify that there is no regular property in the second array, because the URI classes have no properties and they are final. */ + if (zend_hash_num_elements(Z_ARRVAL_P(arr)) > 0) { zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); RETURN_THROWS(); } @@ -624,11 +641,10 @@ zend_result uri_handler_register(const uri_handler_t *uri_handler) ZEND_ASSERT(uri_handler->clone_uri != NULL); ZEND_ASSERT(uri_handler->uri_to_string != NULL); ZEND_ASSERT(uri_handler->free_uri != NULL); - ZEND_ASSERT(uri_handler->property_handlers != NULL); zend_result result = zend_hash_add_ptr(&uri_handlers, key, (void *) uri_handler) != NULL ? SUCCESS : FAILURE; - zend_string_release(key); + zend_string_release_ex(key, true); return result; } @@ -651,8 +667,6 @@ static PHP_MINIT_FUNCTION(uri) return FAILURE; } - lexbor_module_init(); - return SUCCESS; } @@ -666,8 +680,6 @@ static PHP_MINFO_FUNCTION(uri) static PHP_MSHUTDOWN_FUNCTION(uri) { - lexbor_module_shutdown(); - zend_hash_destroy(&uri_handlers); return SUCCESS; diff --git a/ext/uri/php_uri_common.c b/ext/uri/php_uri_common.c index d2ae0d48388dc..6f1dd9d77c784 100644 --- a/ext/uri/php_uri_common.c +++ b/ext/uri/php_uri_common.c @@ -19,14 +19,31 @@ #include "Zend/zend_exceptions.h" #include "php_uri_common.h" -void uri_register_property_handler(HashTable *property_handlers, zend_string *name, const uri_property_handler_t *handler) +const uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name) { - zend_hash_add_new_ptr(property_handlers, name, (void *) handler); -} - -uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name) -{ - return zend_hash_find_ptr(internal_uri->handler->property_handlers, name); + switch (ZSTR_VAL(name)[0]) { + case 's': /* scheme */ + return &internal_uri->handler->property_handlers.scheme; + case 'u': /* username */ + return &internal_uri->handler->property_handlers.username; + case 'p': /* password, port, path */ + if (name == ZSTR_KNOWN(ZEND_STR_PASSWORD)) { + return &internal_uri->handler->property_handlers.password; + } else if (name == ZSTR_KNOWN(ZEND_STR_PORT)) { + return &internal_uri->handler->property_handlers.port; + } else if (name == ZSTR_KNOWN(ZEND_STR_PATH)) { + return &internal_uri->handler->property_handlers.path; + } else { + ZEND_UNREACHABLE(); + } + case 'h': /* host */ + return &internal_uri->handler->property_handlers.host; + case 'q': /* query */ + return &internal_uri->handler->property_handlers.query; + case 'f': /* fragment */ + return &internal_uri->handler->property_handlers.fragment; + EMPTY_SWITCH_DEFAULT_CASE() + } } void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors) diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index ce11a8394951d..951dfe5579016 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -40,6 +40,28 @@ typedef enum { URI_COMPONENT_READ_NORMALIZED_UNICODE, } uri_component_read_mode_t; +struct uri_internal_t; + +typedef zend_result (*uri_read_t)(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval); + +typedef zend_result (*uri_write_t)(struct uri_internal_t *internal_uri, zval *value, zval *errors); + +typedef struct uri_property_handler_t { + uri_read_t read_func; + uri_write_t write_func; +} uri_property_handler_t; + +typedef struct uri_property_handlers_t { + uri_property_handler_t scheme; + uri_property_handler_t username; + uri_property_handler_t password; + uri_property_handler_t host; + uri_property_handler_t port; + uri_property_handler_t path; + uri_property_handler_t query; + uri_property_handler_t fragment; +} uri_property_handlers_t; + typedef struct uri_handler_t { const char *name; @@ -49,7 +71,7 @@ typedef struct uri_handler_t { zend_string *(*uri_to_string)(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment); void (*free_uri)(void *uri); - HashTable *property_handlers; + const uri_property_handlers_t property_handlers; } uri_handler_t; typedef struct uri_internal_t { @@ -76,28 +98,8 @@ static inline uri_internal_t *uri_internal_from_obj(const zend_object *object) { #define URI_PARSER_WHATWG "Uri\\WhatWg\\Url" #define URI_SERIALIZED_PROPERTY_NAME "uri" -typedef zend_result (*uri_read_t)(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval); - -typedef zend_result (*uri_write_t)(uri_internal_t *internal_uri, zval *value, zval *errors); - -typedef struct uri_property_handler_t { - uri_read_t read_func; - uri_write_t write_func; -} uri_property_handler_t; - -#define URI_REGISTER_PROPERTY_READ_HANDLER(property_handlers, name, property_read_func) do { \ - static const uri_property_handler_t handler = {.read_func = property_read_func, .write_func = NULL}; \ - uri_register_property_handler(property_handlers, name, &handler); \ -} while (0) - -#define URI_REGISTER_PROPERTY_READ_WRITE_HANDLER(property_handlers, name, property_read_func, property_write_func) do { \ - static const uri_property_handler_t handler = {.read_func = property_read_func, .write_func = property_write_func}; \ - uri_register_property_handler(property_handlers, name, &handler); \ -} while (0) - -void uri_register_property_handler(HashTable *property_handlers, zend_string *name, const uri_property_handler_t *handler); zend_result uri_handler_register(const uri_handler_t *uri_handler); -uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name); +const uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name); void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors); void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name, uri_component_read_mode_t component_read_mode); void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name); From e57b932354e71b7b1ed6ac699bb1435d066bedf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 3 Jun 2025 11:46:24 +0200 Subject: [PATCH 08/15] Another review round --- Zend/zend_exceptions.c | 61 ++++++++++++----------------- Zend/zend_exceptions.h | 2 + ext/uri/php_lexbor.c | 10 ++++- ext/uri/php_uri.c | 83 ++++++++++++++++------------------------ ext/uri/php_uri_common.c | 69 +++++++++++++++++++++------------ ext/uri/php_uri_common.h | 39 ++++++++++++++++--- 6 files changed, 149 insertions(+), 115 deletions(-) diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 7777c5fa62e48..ab9c815718a0d 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -329,24 +329,15 @@ ZEND_COLD ZEND_METHOD(Exception, __clone) } /* }}} */ -/* {{{ Exception constructor */ -ZEND_METHOD(Exception, __construct) +ZEND_API zend_result zend_update_exception_properties(INTERNAL_FUNCTION_PARAMETERS, zend_string *message, zend_long code, zval *previous) { - zend_string *message = NULL; - zend_long code = 0; - zval tmp, *object, *previous = NULL; - - object = ZEND_THIS; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) { - RETURN_THROWS(); - } + zval tmp, *object = ZEND_THIS; if (message) { ZVAL_STR_COPY(&tmp, message); zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_MESSAGE_OFF, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); + return FAILURE; } } @@ -354,7 +345,7 @@ ZEND_METHOD(Exception, __construct) ZVAL_LONG(&tmp, code); zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_CODE_OFF, ZSTR_KNOWN(ZEND_STR_CODE), &tmp); if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); + return FAILURE; } } @@ -362,9 +353,27 @@ ZEND_METHOD(Exception, __construct) Z_ADDREF_P(previous); zend_update_property_num_checked(zend_ce_exception, Z_OBJ_P(object), ZEND_EXCEPTION_PREVIOUS_OFF, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); + return FAILURE; } } + + return SUCCESS; +} + +/* {{{ Exception constructor */ +ZEND_METHOD(Exception, __construct) +{ + zend_string *message = NULL; + zend_long code = 0; + zval *previous = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) { + RETURN_THROWS(); + } + + if (zend_update_exception_properties(INTERNAL_FUNCTION_PARAM_PASSTHRU, message, code, previous) == FAILURE) { + RETURN_THROWS(); + } } /* }}} */ @@ -401,28 +410,8 @@ ZEND_METHOD(ErrorException, __construct) object = ZEND_THIS; - if (message) { - ZVAL_STR_COPY(&tmp, message); - zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_MESSAGE_OFF, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); - if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); - } - } - - if (code) { - ZVAL_LONG(&tmp, code); - zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_CODE_OFF, ZSTR_KNOWN(ZEND_STR_CODE), &tmp); - if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); - } - } - - if (previous) { - Z_ADDREF_P(previous); - zend_update_property_num_checked(zend_ce_exception, Z_OBJ_P(object), ZEND_EXCEPTION_PREVIOUS_OFF, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); - if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); - } + if (zend_update_exception_properties(INTERNAL_FUNCTION_PARAM_PASSTHRU, message, code, previous) == FAILURE) { + RETURN_THROWS(); } ZVAL_LONG(&tmp, severity); diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index d0138021d1ea3..86dc379cce871 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -69,6 +69,8 @@ ZEND_API zend_object *zend_throw_error_exception(zend_class_entry *exception_ce, extern ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); +ZEND_API zend_result zend_update_exception_properties(INTERNAL_FUNCTION_PARAMETERS, zend_string *message, zend_long code, zval *previous); + /* show an exception using zend_error(severity,...), severity should be E_ERROR */ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity); ZEND_NORETURN void zend_exception_uncaught_error(const char *prefix, ...) ZEND_ATTRIBUTE_FORMAT(printf, 1, 2); diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index ba8f1440e0b97..f90a941e18bdb 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -67,7 +67,11 @@ static void lexbor_cleanup_parser(void) /** * Creates a Uri\WhatWg\UrlValidationError class by mapping error codes listed in - * https://url.spec.whatwg.org/#writing to a Uri\WhatWg\UrlValidationErrorType enum + * https://url.spec.whatwg.org/#writing to a Uri\WhatWg\UrlValidationErrorType enum. + * The result is passed by reference to the errors parameter. + * + * When errors is NULL, the caller is not interested in the additional error information, + * so the function does nothing. */ static void fill_errors(zval *errors) { @@ -75,6 +79,8 @@ static void fill_errors(zval *errors) return; } + ZEND_ASSERT(Z_ISUNDEF_P(errors)); + array_init(errors); if (lexbor_parser->log == NULL) { @@ -550,6 +556,8 @@ static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, static void lexbor_create_invalid_uri_exception(zval *exception_zv, zval *errors) { + ZEND_ASSERT(Z_TYPE_P(errors) == IS_ARRAY && "Lexbor always returns an array of errors when URI parsing fails"); + object_init_ex(exception_zv, uri_whatwg_invalid_url_exception_ce); zval value; diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 8493d0136cf58..ea323a64e73ef 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -119,16 +119,12 @@ PHP_METHOD(Uri_WhatWg_InvalidUrlException, __construct) Z_PARAM_OBJECT_OF_CLASS_OR_NULL(previous, zend_ce_throwable) ZEND_PARSE_PARAMETERS_END(); - zval tmp; - if (message != NULL) { - ZVAL_STR(&tmp, message); - zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); - if (EG(exception)) { - RETURN_THROWS(); - } + if (zend_update_exception_properties(INTERNAL_FUNCTION_PARAM_PASSTHRU, message, code, previous) == FAILURE) { + RETURN_THROWS(); } if (errors == NULL) { + zval tmp; ZVAL_EMPTY_ARRAY(&tmp); zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), &tmp); } else { @@ -137,21 +133,6 @@ PHP_METHOD(Uri_WhatWg_InvalidUrlException, __construct) if (EG(exception)) { RETURN_THROWS(); } - - if (code != 0) { - ZVAL_LONG(&tmp, code); - zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_CODE), &tmp); - if (EG(exception)) { - RETURN_THROWS(); - } - } - - if (previous != NULL) { - zend_update_property_ex(zend_ce_exception, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); - if (EG(exception)) { - RETURN_THROWS(); - } - } } PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) @@ -184,20 +165,30 @@ PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) } } -static zend_result pass_errors_by_ref(zval *errors_zv, zval *errors) +/** + * Pass the errors parameter by ref to errors_zv for userland, and frees it if + * it is not not needed anymore. + */ +static zend_result pass_errors_by_ref_and_free(zval *errors_zv, zval *errors) { ZEND_ASSERT(Z_TYPE_P(errors) == IS_UNDEF || Z_TYPE_P(errors) == IS_ARRAY); + /* There was no error during parsing */ if (Z_ISUNDEF_P(errors)) { return SUCCESS; } + /* The errors parameter is an array, but the pass-by ref argument stored by + * errors_zv was not passed - the URI implementation either doesn't support + * returning additional error information, or the caller is not interested in it */ if (errors_zv == NULL) { + zval_ptr_dtor(errors); return SUCCESS; } ZEND_TRY_ASSIGN_REF_ARR(errors_zv, Z_ARRVAL_P(errors)); if (EG(exception)) { + zval_ptr_dtor(errors); return FAILURE; } @@ -225,14 +216,14 @@ PHPAPI void php_uri_instantiate_uri( zval_ptr_dtor(&errors); RETURN_THROWS(); } else { - if (pass_errors_by_ref(errors_zv, &errors) == FAILURE) { + if (pass_errors_by_ref_and_free(errors_zv, &errors) == FAILURE) { RETURN_THROWS(); } RETURN_NULL(); } } - if (pass_errors_by_ref(errors_zv, &errors) == FAILURE) { + if (pass_errors_by_ref_and_free(errors_zv, &errors) == FAILURE) { RETURN_THROWS(); } @@ -388,93 +379,87 @@ static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_na PHP_METHOD(Uri_WhatWg_Url, getScheme) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_SCHEME), - URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME, URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withScheme) { - uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_SCHEME)); + uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME); } PHP_METHOD(Uri_WhatWg_Url, getUsername) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_USERNAME), - URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_USERNAME, URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withUsername) { - uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_USERNAME)); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_USERNAME); } PHP_METHOD(Uri_WhatWg_Url, getPassword) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PASSWORD), - URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PASSWORD, URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withPassword) { - uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PASSWORD)); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PASSWORD); } PHP_METHOD(Uri_WhatWg_Url, getAsciiHost) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_HOST), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST, URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, getUnicodeHost) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_HOST), - URI_COMPONENT_READ_NORMALIZED_UNICODE); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST, URI_COMPONENT_READ_NORMALIZED_UNICODE); } PHP_METHOD(Uri_WhatWg_Url, withHost) { - uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_HOST)); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST); } PHP_METHOD(Uri_WhatWg_Url, getPort) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PORT), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PORT, URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withPort) { - uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PORT)); + uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PORT); } PHP_METHOD(Uri_WhatWg_Url, getPath) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PATH), URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH, URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withPath) { - uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_PATH)); + uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH); } PHP_METHOD(Uri_WhatWg_Url, getQuery) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_QUERY), - URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY, URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withQuery) { - uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_QUERY)); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY); } PHP_METHOD(Uri_WhatWg_Url, getFragment) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_FRAGMENT), - URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT, URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_WhatWg_Url, withFragment) { - uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZSTR_KNOWN(ZEND_STR_FRAGMENT)); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT); } PHP_METHOD(Uri_WhatWg_Url, equals) @@ -557,7 +542,7 @@ PHP_METHOD(Uri_WhatWg_Url, __serialize) /* Serialize regular properties: second array */ ZVAL_ARR(&arr, this_object->handlers->get_properties(this_object)); - Z_TRY_ADDREF(arr); + Z_ADDREF(arr); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &arr); } diff --git a/ext/uri/php_uri_common.c b/ext/uri/php_uri_common.c index 6f1dd9d77c784..f693ddc4793a5 100644 --- a/ext/uri/php_uri_common.c +++ b/ext/uri/php_uri_common.c @@ -19,33 +19,52 @@ #include "Zend/zend_exceptions.h" #include "php_uri_common.h" -const uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name) +const uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, uri_property_name_t property_name) { - switch (ZSTR_VAL(name)[0]) { - case 's': /* scheme */ + switch (property_name) { + case URI_PROPERTY_NAME_SCHEME: return &internal_uri->handler->property_handlers.scheme; - case 'u': /* username */ + case URI_PROPERTY_NAME_USERNAME: return &internal_uri->handler->property_handlers.username; - case 'p': /* password, port, path */ - if (name == ZSTR_KNOWN(ZEND_STR_PASSWORD)) { - return &internal_uri->handler->property_handlers.password; - } else if (name == ZSTR_KNOWN(ZEND_STR_PORT)) { - return &internal_uri->handler->property_handlers.port; - } else if (name == ZSTR_KNOWN(ZEND_STR_PATH)) { - return &internal_uri->handler->property_handlers.path; - } else { - ZEND_UNREACHABLE(); - } - case 'h': /* host */ + case URI_PROPERTY_NAME_PASSWORD: + return &internal_uri->handler->property_handlers.password; + case URI_PROPERTY_NAME_HOST: return &internal_uri->handler->property_handlers.host; - case 'q': /* query */ + case URI_PROPERTY_NAME_PORT: + return &internal_uri->handler->property_handlers.port; + case URI_PROPERTY_NAME_PATH: + return &internal_uri->handler->property_handlers.path; + case URI_PROPERTY_NAME_QUERY: return &internal_uri->handler->property_handlers.query; - case 'f': /* fragment */ + case URI_PROPERTY_NAME_FRAGMENT: return &internal_uri->handler->property_handlers.fragment; EMPTY_SWITCH_DEFAULT_CASE() } } +static zend_string *get_known_string_by_property_name(uri_property_name_t property_name) +{ + switch (property_name) { + case URI_PROPERTY_NAME_SCHEME: + return ZSTR_KNOWN(ZEND_STR_SCHEME); + case URI_PROPERTY_NAME_USERNAME: + return ZSTR_KNOWN(ZEND_STR_USERNAME); + case URI_PROPERTY_NAME_PASSWORD: + return ZSTR_KNOWN(ZEND_STR_PASSWORD); + case URI_PROPERTY_NAME_HOST: + return ZSTR_KNOWN(ZEND_STR_HOST); + case URI_PROPERTY_NAME_PORT: + return ZSTR_KNOWN(ZEND_STR_PORT); + case URI_PROPERTY_NAME_PATH: + return ZSTR_KNOWN(ZEND_STR_PATH); + case URI_PROPERTY_NAME_QUERY: + return ZSTR_KNOWN(ZEND_STR_QUERY); + case URI_PROPERTY_NAME_FRAGMENT: + return ZSTR_KNOWN(ZEND_STR_FRAGMENT); + EMPTY_SWITCH_DEFAULT_CASE() + } +} + void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors) { zval exception_zv; @@ -55,7 +74,7 @@ void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors) zend_throw_exception_object(&exception_zv); } -void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name, uri_component_read_mode_t component_read_mode) +void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name, uri_component_read_mode_t component_read_mode) { ZEND_PARSE_PARAMETERS_NONE(); @@ -66,12 +85,13 @@ void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name ZEND_ASSERT(property_handler != NULL); if (UNEXPECTED(property_handler->read_func(internal_uri, component_read_mode, return_value) == FAILURE)) { - zend_throw_error(NULL, "%s::$%s property cannot be retrieved", ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(property_name)); + zend_throw_error(NULL, "%s::$%s property cannot be retrieved", ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), + ZSTR_VAL(get_known_string_by_property_name(property_name))); RETURN_THROWS(); } } -static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name, zval *property_zv) +static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name, zval *property_zv) { uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); URI_ASSERT_INITIALIZATION(internal_uri); @@ -88,7 +108,8 @@ static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, zend_string *pr uri_internal_t *new_internal_uri = uri_internal_from_obj(new_object); URI_ASSERT_INITIALIZATION(new_internal_uri); if (property_handler->write_func == NULL) { - zend_readonly_property_modification_error_ex(ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(property_name)); + zend_readonly_property_modification_error_ex(ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), + ZSTR_VAL(get_known_string_by_property_name(property_name))); zend_object_release(new_object); RETURN_THROWS(); } @@ -106,7 +127,7 @@ static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, zend_string *pr RETVAL_OBJ(new_object); } -void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name) +void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name) { zend_string *value; @@ -120,7 +141,7 @@ void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, zend_string *property uri_write_component_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_name, &zv); } -void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name) +void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name) { zend_string *value; @@ -138,7 +159,7 @@ void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, zend_string * uri_write_component_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_name, &zv); } -void uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name) +void uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name) { zend_long value; bool value_is_null; diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index 951dfe5579016..f6607adf35bec 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -46,6 +46,17 @@ typedef zend_result (*uri_read_t)(const struct uri_internal_t *internal_uri, uri typedef zend_result (*uri_write_t)(struct uri_internal_t *internal_uri, zval *value, zval *errors); +typedef enum { + URI_PROPERTY_NAME_SCHEME, + URI_PROPERTY_NAME_USERNAME, + URI_PROPERTY_NAME_PASSWORD, + URI_PROPERTY_NAME_HOST, + URI_PROPERTY_NAME_PORT, + URI_PROPERTY_NAME_PATH, + URI_PROPERTY_NAME_QUERY, + URI_PROPERTY_NAME_FRAGMENT, +} uri_property_name_t; + typedef struct uri_property_handler_t { uri_read_t read_func; uri_write_t write_func; @@ -65,7 +76,25 @@ typedef struct uri_property_handlers_t { typedef struct uri_handler_t { const char *name; + /** + * Parse a URI string into a URI. + * + * If the URI string is valid, a URI is returned. In case of failure, NULL is + * returned. + * + * The errors by-ref parameter can contain errors that occurred during parsing. + * If the input value is NULL, or there were no errors, errors should not be changed. + * + * If the URI string is valid and the base_url URI is not NULL, the URI object + * is resolved against the base_url. + */ void *(*parse_uri)(const zend_string *uri_str, const void *base_url, zval *errors); + /** + * Create a Uri\InvalidUriException instance based on the errors parameter. + * The errors parameter is either an array or an UNDEF zval. + * + * The exception object is passed by ref to the exception_zv parameter. + */ void (*create_invalid_uri_exception)(zval *exception_zv, zval *errors); void *(*clone_uri)(void *uri); zend_string *(*uri_to_string)(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment); @@ -99,12 +128,12 @@ static inline uri_internal_t *uri_internal_from_obj(const zend_object *object) { #define URI_SERIALIZED_PROPERTY_NAME "uri" zend_result uri_handler_register(const uri_handler_t *uri_handler); -const uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name); +const uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, uri_property_name_t property_name); void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors); -void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name, uri_component_read_mode_t component_read_mode); -void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name); -void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name); -void uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAMETERS, zend_string *property_name); +void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name, uri_component_read_mode_t component_read_mode); +void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name); +void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name); +void uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name); #define URI_ASSERT_INITIALIZATION(internal_uri) do { \ ZEND_ASSERT(internal_uri != NULL && internal_uri->uri != NULL); \ From 9efa571c5b5ce4b049958b2bb91d2d03fa532458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 3 Jun 2025 12:25:08 +0200 Subject: [PATCH 09/15] Fix wrong conflict resulution --- ext/uri/config.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index cb57ff88d5268..08dc044d8d29f 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -17,6 +17,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_lexbor.c php_uri.c php_uri_common.c $URIPARSER_SOURCES], [no],,[-Iext_srcdir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) +PHP_NEW_EXTENSION(uri, [php_lexbor.c php_uri.c php_uri_common.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) From 21596a616f3e1edae05f4844bf84c10745c8d5a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 3 Jun 2025 13:57:59 +0200 Subject: [PATCH 10/15] Convert more macros functions --- ext/uri/php_lexbor.c | 76 +++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index f90a941e18bdb..103aa366ef498 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -29,32 +29,30 @@ ZEND_TLS int lexbor_urls; #define LEXBOR_MAX_URL_COUNT 500 #define LEXBOR_MRAW_BYTE_SIZE 8192 -#define ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str) do { \ - if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) > 0) { \ - str.data = (lxb_char_t *) Z_STRVAL_P(value); \ - str.length = Z_STRLEN_P(value); \ - } else { \ - ZEND_ASSERT(Z_ISNULL_P(value) || (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) == 0)); \ - str.data = (lxb_char_t *) ""; \ - str.length = 0; \ - } \ -} while (0) - -#define ZVAL_LONG_OR_NULL_TO_LEXBOR_STR(value, str) do { \ - if (Z_TYPE_P(value) == IS_LONG) { \ - ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); \ - lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \ - zval_ptr_dtor_str(value); \ - } else { \ - ZEND_ASSERT(Z_ISNULL_P(value)); \ - str.data = (lxb_char_t *) ""; \ - str.length = 0; \ - } \ -} while (0) - -#define LEXBOR_READ_ASCII_URI_COMPONENT(start, len, retval) do { \ - ZVAL_STRINGL(retval, (const char *) start, len); \ -} while (0) +static zend_always_inline void zval_string_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str) +{ + if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) > 0) { + lexbor_str->data = (lxb_char_t *) Z_STRVAL_P(value); + lexbor_str->length = Z_STRLEN_P(value); + } else { + ZEND_ASSERT(Z_ISNULL_P(value) || (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) == 0)); + lexbor_str->data = (lxb_char_t *) ""; + lexbor_str->length = 0; + } +} + +static zend_always_inline void zval_long_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str) +{ + if (Z_TYPE_P(value) == IS_LONG) { + ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); + lexbor_str_init_append(lexbor_str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); + zval_ptr_dtor_str(value); + } else { + ZEND_ASSERT(Z_ISNULL_P(value)); + lexbor_str->data = (lxb_char_t *) ""; + lexbor_str->length = 0; + } +} static void lexbor_cleanup_parser(void) { @@ -254,7 +252,7 @@ static zend_result lexbor_write_scheme(struct uri_internal_t *internal_uri, zval lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); + zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_protocol_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); @@ -270,7 +268,7 @@ static zend_result lexbor_read_username(const struct uri_internal_t *internal_ur lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->username.length) { - LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->username.data, lexbor_uri->username.length, retval); + ZVAL_STRINGL(retval, (const char *) lexbor_uri->username.data, lexbor_uri->username.length); } else { ZVAL_NULL(retval); } @@ -283,7 +281,7 @@ static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *val lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); + zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_username_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); @@ -299,7 +297,7 @@ static zend_result lexbor_read_password(const struct uri_internal_t *internal_ur lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->password.length > 0) { - LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->password.data, lexbor_uri->password.length, retval); + ZVAL_STRINGL(retval, (const char *) lexbor_uri->password.data, lexbor_uri->password.length); } else { ZVAL_NULL(retval); } @@ -312,7 +310,7 @@ static zend_result lexbor_write_password(struct uri_internal_t *internal_uri, zv lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); + zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_password_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); @@ -384,7 +382,7 @@ static zend_result lexbor_write_host(struct uri_internal_t *internal_uri, zval * lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); + zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_hostname_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); @@ -413,7 +411,7 @@ static zend_result lexbor_write_port(struct uri_internal_t *internal_uri, zval * lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_LONG_OR_NULL_TO_LEXBOR_STR(value, str); + zval_long_or_null_to_lexbor_str(value, &str); if (lxb_url_api_port_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); @@ -429,7 +427,7 @@ static zend_result lexbor_read_path(const struct uri_internal_t *internal_uri, u lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->path.str.length) { - LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->path.str.data, lexbor_uri->path.str.length, retval); + ZVAL_STRINGL(retval, (const char *) lexbor_uri->path.str.data, lexbor_uri->path.str.length); } else { ZVAL_EMPTY_STRING(retval); } @@ -442,7 +440,7 @@ static zend_result lexbor_write_path(struct uri_internal_t *internal_uri, zval * lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); + zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_pathname_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); @@ -458,7 +456,7 @@ static zend_result lexbor_read_query(const struct uri_internal_t *internal_uri, lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->query.length) { - LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->query.data, lexbor_uri->query.length, retval); + ZVAL_STRINGL(retval, (const char *) lexbor_uri->query.data, lexbor_uri->query.length); } else { ZVAL_NULL(retval); } @@ -471,7 +469,7 @@ static zend_result lexbor_write_query(struct uri_internal_t *internal_uri, zval lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); + zval_string_or_null_to_lexbor_str(value, &str); if ( lxb_url_api_search_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); @@ -487,7 +485,7 @@ static zend_result lexbor_read_fragment(const struct uri_internal_t *internal_ur lxb_url_t *lexbor_uri = internal_uri->uri; if (lexbor_uri->fragment.length) { - LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->fragment.data, lexbor_uri->fragment.length, retval); + ZVAL_STRINGL(retval, (const char *) lexbor_uri->fragment.data, lexbor_uri->fragment.length); } else { ZVAL_NULL(retval); } @@ -500,7 +498,7 @@ static zend_result lexbor_write_fragment(struct uri_internal_t *internal_uri, zv lxb_url_t *lexbor_uri = internal_uri->uri; lexbor_str_t str = {0}; - ZVAL_STRING_OR_NULL_TO_LEXBOR_STR(value, str); + zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_hash_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); From c64d869f7aec9ed82789179434183de3a29dd7d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 5 Jun 2025 22:45:56 +0200 Subject: [PATCH 11/15] Fix review comments by DanielEScherzer --- ext/uri/php_lexbor.c | 6 +++--- ext/uri/php_uri.c | 9 ++------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index 103aa366ef498..94c2623f1e778 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -215,7 +215,7 @@ static void fill_errors(zval *errors) zval error_type; zend_enum_new(&error_type, uri_whatwg_url_validation_error_type_ce, error_str, NULL); - zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("type"), &error_type); + zend_update_property_ex(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZSTR_KNOWN(ZEND_STR_TYPE), &error_type); zend_string_release_ex(error_str, false); zval_ptr_dtor(&error_type); @@ -321,7 +321,7 @@ static zend_result lexbor_write_password(struct uri_internal_t *internal_uri, zv return SUCCESS; } -static ZEND_RESULT_CODE init_idna(void) +static zend_result init_idna(void) { if (lexbor_parser->idna != NULL) { return SUCCESS; @@ -471,7 +471,7 @@ static zend_result lexbor_write_query(struct uri_internal_t *internal_uri, zval zval_string_or_null_to_lexbor_str(value, &str); - if ( lxb_url_api_search_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + if (lxb_url_api_search_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); return FAILURE; diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index ea323a64e73ef..3ab7b93d85f4a 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -152,7 +152,7 @@ PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) RETURN_THROWS(); } - zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); + zend_update_property_ex(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_TYPE), type); if (EG(exception)) { RETURN_THROWS(); } @@ -347,21 +347,16 @@ static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_na RETURN_THROWS(); } - zval errors; - ZVAL_UNDEF(&errors); - uri_internal_t *internal_uri = uri_internal_from_obj(object); internal_uri->handler = uri_handler_by_name(handler_name, strlen(handler_name)); if (internal_uri->uri != NULL) { internal_uri->handler->free_uri(internal_uri->uri); } - internal_uri->uri = internal_uri->handler->parse_uri(Z_STR_P(uri_zv), NULL, &errors); + internal_uri->uri = internal_uri->handler->parse_uri(Z_STR_P(uri_zv), NULL, NULL); if (internal_uri->uri == NULL) { zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); - zval_ptr_dtor(&errors); RETURN_THROWS(); } - zval_ptr_dtor(&errors); /* Unserialize regular properties: second array */ arr = zend_hash_index_find(data, 1); From 05beefa192543ec63d1ebbdfaf6b11d264cfd60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 5 Jun 2025 23:23:57 +0200 Subject: [PATCH 12/15] Don't allocate the lexbor parser on the heap --- ext/uri/php_lexbor.c | 53 +++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index 94c2623f1e778..366cd5ff81f80 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -23,7 +23,7 @@ #include #endif -ZEND_TLS lxb_url_parser_t *lexbor_parser; +ZEND_TLS lxb_url_parser_t lexbor_parser; ZEND_TLS int lexbor_urls; #define LEXBOR_MAX_URL_COUNT 500 @@ -45,7 +45,7 @@ static zend_always_inline void zval_long_or_null_to_lexbor_str(zval *value, lexb { if (Z_TYPE_P(value) == IS_LONG) { ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); - lexbor_str_init_append(lexbor_str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); + lexbor_str_init_append(lexbor_str, lexbor_parser.mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); zval_ptr_dtor_str(value); } else { ZEND_ASSERT(Z_ISNULL_P(value)); @@ -57,10 +57,10 @@ static zend_always_inline void zval_long_or_null_to_lexbor_str(zval *value, lexb static void lexbor_cleanup_parser(void) { if (++lexbor_urls % LEXBOR_MAX_URL_COUNT == 0) { - lexbor_mraw_clean(lexbor_parser->mraw); + lexbor_mraw_clean(lexbor_parser.mraw); } - lxb_url_parser_clean(lexbor_parser); + lxb_url_parser_clean(&lexbor_parser); } /** @@ -81,12 +81,12 @@ static void fill_errors(zval *errors) array_init(errors); - if (lexbor_parser->log == NULL) { + if (lexbor_parser.log == NULL) { return; } lexbor_plog_entry_t *lxb_error; - while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser->log->list)) != NULL) { + while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser.log->list)) != NULL) { zval error; object_init_ex(&error, uri_whatwg_url_validation_error_ce); zend_update_property_string(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data); @@ -254,7 +254,7 @@ static zend_result lexbor_write_scheme(struct uri_internal_t *internal_uri, zval zval_string_or_null_to_lexbor_str(value, &str); - if (lxb_url_api_protocol_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + if (lxb_url_api_protocol_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); return FAILURE; @@ -323,13 +323,13 @@ static zend_result lexbor_write_password(struct uri_internal_t *internal_uri, zv static zend_result init_idna(void) { - if (lexbor_parser->idna != NULL) { + if (lexbor_parser.idna != NULL) { return SUCCESS; } - lexbor_parser->idna = lxb_unicode_idna_create(); + lexbor_parser.idna = lxb_unicode_idna_create(); - return lxb_unicode_idna_init(lexbor_parser->idna) == LXB_STATUS_OK ? SUCCESS : FAILURE; + return lxb_unicode_idna_init(lexbor_parser.idna) == LXB_STATUS_OK ? SUCCESS : FAILURE; } static zend_result lexbor_read_host(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) @@ -357,8 +357,8 @@ static zend_result lexbor_read_host(const struct uri_internal_t *internal_uri, u if (init_idna() == FAILURE) { return FAILURE; } - lxb_url_serialize_host_unicode(lexbor_parser->idna, &lexbor_uri->host, lexbor_serialize_callback, &host_str); - lxb_unicode_idna_clean(lexbor_parser->idna); + lxb_url_serialize_host_unicode(lexbor_parser.idna, &lexbor_uri->host, lexbor_serialize_callback, &host_str); + lxb_unicode_idna_clean(lexbor_parser.idna); ZVAL_NEW_STR(retval, smart_str_extract(&host_str)); break; @@ -384,7 +384,7 @@ static zend_result lexbor_write_host(struct uri_internal_t *internal_uri, zval * zval_string_or_null_to_lexbor_str(value, &str); - if (lxb_url_api_hostname_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + if (lxb_url_api_hostname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); return FAILURE; @@ -413,7 +413,7 @@ static zend_result lexbor_write_port(struct uri_internal_t *internal_uri, zval * zval_long_or_null_to_lexbor_str(value, &str); - if (lxb_url_api_port_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + if (lxb_url_api_port_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); return FAILURE; @@ -442,7 +442,7 @@ static zend_result lexbor_write_path(struct uri_internal_t *internal_uri, zval * zval_string_or_null_to_lexbor_str(value, &str); - if (lxb_url_api_pathname_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + if (lxb_url_api_pathname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); return FAILURE; @@ -471,7 +471,7 @@ static zend_result lexbor_write_query(struct uri_internal_t *internal_uri, zval zval_string_or_null_to_lexbor_str(value, &str); - if (lxb_url_api_search_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + if (lxb_url_api_search_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); return FAILURE; @@ -500,7 +500,7 @@ static zend_result lexbor_write_fragment(struct uri_internal_t *internal_uri, zv zval_string_or_null_to_lexbor_str(value, &str); - if (lxb_url_api_hash_set(lexbor_uri, lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + if (lxb_url_api_hash_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { fill_errors(errors); return FAILURE; @@ -518,11 +518,9 @@ zend_result lexbor_request_init(void) return FAILURE; } - lexbor_parser = lxb_url_parser_create(); - status = lxb_url_parser_init(lexbor_parser, mraw); + status = lxb_url_parser_init(&lexbor_parser, mraw); if (status != LXB_STATUS_OK) { - lxb_url_parser_destroy(lexbor_parser, true); - lexbor_parser = NULL; + lxb_url_parser_destroy(&lexbor_parser, false); lexbor_mraw_destroy(mraw, true); return FAILURE; } @@ -534,10 +532,9 @@ zend_result lexbor_request_init(void) void lexbor_request_shutdown(void) { - lxb_url_parser_memory_destroy(lexbor_parser); - lxb_url_parser_destroy(lexbor_parser, true); + lxb_url_parser_memory_destroy(&lexbor_parser); + lxb_url_parser_destroy(&lexbor_parser, false); - lexbor_parser = NULL; lexbor_urls = 0; } @@ -546,7 +543,7 @@ static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, lexbor_cleanup_parser(); const lxb_url_t *lexbor_base_url = base_url; - lxb_url_t *url = lxb_url_parse(lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str)); + lxb_url_t *url = lxb_url_parse(&lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str)); fill_errors(errors); return url; @@ -570,7 +567,7 @@ static void *lexbor_clone_uri(void *uri) { lxb_url_t *lexbor_uri = (lxb_url_t *) uri; - return lxb_url_clone(lexbor_parser->mraw, lexbor_uri); + return lxb_url_clone(lexbor_parser.mraw, lexbor_uri); } static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment) @@ -585,8 +582,8 @@ static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t rec if (init_idna() == FAILURE) { return NULL; } - lxb_url_serialize_idna(lexbor_parser->idna, lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment); - lxb_unicode_idna_clean(lexbor_parser->idna); + lxb_url_serialize_idna(lexbor_parser.idna, lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment); + lxb_unicode_idna_clean(lexbor_parser.idna); break; case URI_RECOMPOSITION_RAW_ASCII: ZEND_FALLTHROUGH; From fe930af84b698b3aa1a39a9fb2f8c978233498fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sat, 7 Jun 2025 23:11:23 +0200 Subject: [PATCH 13/15] Remove the create_invalid_uri_exception URI handler --- ext/uri/php_lexbor.c | 62 ++++++++++++++++++++++++++-------------- ext/uri/php_lexbor.h | 3 +- ext/uri/php_uri.c | 6 ++-- ext/uri/php_uri_common.c | 10 ------- ext/uri/php_uri_common.h | 14 ++++----- 5 files changed, 48 insertions(+), 47 deletions(-) diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index 366cd5ff81f80..a1e69b3160fbe 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -19,6 +19,7 @@ #include "php_uri_common.h" #include "Zend/zend_enum.h" #include "Zend/zend_smart_str.h" +#include "Zend/zend_exceptions.h" #ifdef HAVE_ARPA_INET_H #include #endif @@ -225,6 +226,30 @@ static void fill_errors(zval *errors) } } +static void throw_invalid_url_exception(zval *errors) +{ + ZEND_ASSERT(errors != NULL && Z_TYPE_P(errors) == IS_ARRAY); + + zval exception; + + object_init_ex(&exception, uri_whatwg_invalid_url_exception_ce); + + zval value; + ZVAL_STRING(&value, "URL parsing failed"); + zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ(exception), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value); + zval_ptr_dtor_str(&value); + + zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ(exception), ZEND_STRL("errors"), errors); + + zend_throw_exception_object(&exception); +} + +static void throw_invalid_url_exception_during_write(zval *errors) +{ + fill_errors(errors); + throw_invalid_url_exception(errors); +} + static lxb_status_t lexbor_serialize_callback(const lxb_char_t *data, size_t length, void *ctx) { smart_str *uri_str = ctx; @@ -255,7 +280,7 @@ static zend_result lexbor_write_scheme(struct uri_internal_t *internal_uri, zval zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_protocol_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - fill_errors(errors); + throw_invalid_url_exception_during_write(errors); return FAILURE; } @@ -284,7 +309,7 @@ static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *val zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_username_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { - fill_errors(errors); + throw_invalid_url_exception_during_write(errors); return FAILURE; } @@ -313,7 +338,7 @@ static zend_result lexbor_write_password(struct uri_internal_t *internal_uri, zv zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_password_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { - fill_errors(errors); + throw_invalid_url_exception_during_write(errors); return FAILURE; } @@ -385,7 +410,7 @@ static zend_result lexbor_write_host(struct uri_internal_t *internal_uri, zval * zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_hostname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - fill_errors(errors); + throw_invalid_url_exception_during_write(errors); return FAILURE; } @@ -414,7 +439,7 @@ static zend_result lexbor_write_port(struct uri_internal_t *internal_uri, zval * zval_long_or_null_to_lexbor_str(value, &str); if (lxb_url_api_port_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - fill_errors(errors); + throw_invalid_url_exception_during_write(errors); return FAILURE; } @@ -443,7 +468,7 @@ static zend_result lexbor_write_path(struct uri_internal_t *internal_uri, zval * zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_pathname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - fill_errors(errors); + throw_invalid_url_exception_during_write(errors); return FAILURE; } @@ -472,7 +497,7 @@ static zend_result lexbor_write_query(struct uri_internal_t *internal_uri, zval zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_search_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - fill_errors(errors); + throw_invalid_url_exception_during_write(errors); return FAILURE; } @@ -501,7 +526,7 @@ static zend_result lexbor_write_fragment(struct uri_internal_t *internal_uri, zv zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_hash_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - fill_errors(errors); + throw_invalid_url_exception_during_write(errors); return FAILURE; } @@ -538,29 +563,23 @@ void lexbor_request_shutdown(void) lexbor_urls = 0; } -static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors) +lxb_url_t *lexbor_parse_uri_ex(const zend_string *uri_str, const lxb_url_t *lexbor_base_url, zval *errors, bool silent) { lexbor_cleanup_parser(); - const lxb_url_t *lexbor_base_url = base_url; lxb_url_t *url = lxb_url_parse(&lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str)); fill_errors(errors); + if (url == NULL && !silent) { + throw_invalid_url_exception(errors); + } + return url; } -static void lexbor_create_invalid_uri_exception(zval *exception_zv, zval *errors) +static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors, bool silent) { - ZEND_ASSERT(Z_TYPE_P(errors) == IS_ARRAY && "Lexbor always returns an array of errors when URI parsing fails"); - - object_init_ex(exception_zv, uri_whatwg_invalid_url_exception_ce); - - zval value; - ZVAL_STRING(&value, "URL parsing failed"); - zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(exception_zv), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value); - zval_ptr_dtor_str(&value); - - zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(exception_zv), ZEND_STRL("errors"), errors); + return lexbor_parse_uri_ex(uri_str, base_url, errors, silent); } static void *lexbor_clone_uri(void *uri) @@ -603,7 +622,6 @@ static void lexbor_free_uri(void *uri) const uri_handler_t lexbor_uri_handler = { .name = URI_PARSER_WHATWG, .parse_uri = lexbor_parse_uri, - .create_invalid_uri_exception = lexbor_create_invalid_uri_exception, .clone_uri = lexbor_clone_uri, .uri_to_string = lexbor_uri_to_string, .free_uri = lexbor_free_uri, diff --git a/ext/uri/php_lexbor.h b/ext/uri/php_lexbor.h index 24f4fbfffe5e6..30d68b5cdf681 100644 --- a/ext/uri/php_lexbor.h +++ b/ext/uri/php_lexbor.h @@ -22,8 +22,7 @@ extern const uri_handler_t lexbor_uri_handler; -void lexbor_module_init(void); -void lexbor_module_shutdown(void); +lxb_url_t *lexbor_parse_uri_ex(const zend_string *uri_str, const lxb_url_t *lexbor_base_url, zval *errors, bool silent); zend_result lexbor_request_init(void); void lexbor_request_shutdown(void); diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 3ab7b93d85f4a..5b2e21b1625a3 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -209,10 +209,9 @@ PHPAPI void php_uri_instantiate_uri( base_url = internal_base_url->uri; } - void *uri = handler->parse_uri(uri_str, base_url, should_throw || errors_zv != NULL ? &errors : NULL); + void *uri = handler->parse_uri(uri_str, base_url, should_throw || errors_zv != NULL ? &errors : NULL, !should_throw); if (UNEXPECTED(uri == NULL)) { if (should_throw) { - throw_invalid_uri_exception(handler, &errors); zval_ptr_dtor(&errors); RETURN_THROWS(); } else { @@ -352,7 +351,7 @@ static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_na if (internal_uri->uri != NULL) { internal_uri->handler->free_uri(internal_uri->uri); } - internal_uri->uri = internal_uri->handler->parse_uri(Z_STR_P(uri_zv), NULL, NULL); + internal_uri->uri = internal_uri->handler->parse_uri(Z_STR_P(uri_zv), NULL, NULL, true); if (internal_uri->uri == NULL) { zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); RETURN_THROWS(); @@ -617,7 +616,6 @@ zend_result uri_handler_register(const uri_handler_t *uri_handler) ZEND_ASSERT(uri_handler->name != NULL); ZEND_ASSERT(uri_handler->parse_uri != NULL); - ZEND_ASSERT(uri_handler->create_invalid_uri_exception != NULL); ZEND_ASSERT(uri_handler->clone_uri != NULL); ZEND_ASSERT(uri_handler->uri_to_string != NULL); ZEND_ASSERT(uri_handler->free_uri != NULL); diff --git a/ext/uri/php_uri_common.c b/ext/uri/php_uri_common.c index f693ddc4793a5..9128b942e57b8 100644 --- a/ext/uri/php_uri_common.c +++ b/ext/uri/php_uri_common.c @@ -65,15 +65,6 @@ static zend_string *get_known_string_by_property_name(uri_property_name_t proper } } -void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors) -{ - zval exception_zv; - - uri_handler->create_invalid_uri_exception(&exception_zv, errors); - - zend_throw_exception_object(&exception_zv); -} - void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name, uri_component_read_mode_t component_read_mode) { ZEND_PARSE_PARAMETERS_NONE(); @@ -117,7 +108,6 @@ static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, uri_property_na zval errors; ZVAL_UNDEF(&errors); if (property_handler->write_func(new_internal_uri, property_zv, &errors) == FAILURE) { - throw_invalid_uri_exception(new_internal_uri->handler, &errors); zval_ptr_dtor(&errors); zend_object_release(new_object); RETURN_THROWS(); diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index f6607adf35bec..1aee1cd512472 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -83,19 +83,16 @@ typedef struct uri_handler_t { * returned. * * The errors by-ref parameter can contain errors that occurred during parsing. - * If the input value is NULL, or there were no errors, errors should not be changed. + * If the input value is NULL, or there were no errors, the errors parameter should + * not be modified. * * If the URI string is valid and the base_url URI is not NULL, the URI object * is resolved against the base_url. - */ - void *(*parse_uri)(const zend_string *uri_str, const void *base_url, zval *errors); - /** - * Create a Uri\InvalidUriException instance based on the errors parameter. - * The errors parameter is either an array or an UNDEF zval. * - * The exception object is passed by ref to the exception_zv parameter. + * If the silent parameter is true, a Uri\InvalidUriException instance must be thrown. + * If the parameter is false, the possible errors should be handled by the caller. */ - void (*create_invalid_uri_exception)(zval *exception_zv, zval *errors); + void *(*parse_uri)(const zend_string *uri_str, const void *base_url, zval *errors, bool silent); void *(*clone_uri)(void *uri); zend_string *(*uri_to_string)(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment); void (*free_uri)(void *uri); @@ -129,7 +126,6 @@ static inline uri_internal_t *uri_internal_from_obj(const zend_object *object) { zend_result uri_handler_register(const uri_handler_t *uri_handler); const uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, uri_property_name_t property_name); -void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors); void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name, uri_component_read_mode_t component_read_mode); void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name); void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name); From 928653a03a76cdcc4b4a34fea9c33b29dccd72d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 9 Jun 2025 23:27:45 +0200 Subject: [PATCH 14/15] Update type of lexbor_urls --- ext/uri/php_lexbor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index a1e69b3160fbe..82f3919bb6a97 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -25,7 +25,7 @@ #endif ZEND_TLS lxb_url_parser_t lexbor_parser; -ZEND_TLS int lexbor_urls; +ZEND_TLS unsigned short int lexbor_urls; #define LEXBOR_MAX_URL_COUNT 500 #define LEXBOR_MRAW_BYTE_SIZE 8192 @@ -59,6 +59,7 @@ static void lexbor_cleanup_parser(void) { if (++lexbor_urls % LEXBOR_MAX_URL_COUNT == 0) { lexbor_mraw_clean(lexbor_parser.mraw); + lexbor_urls = 0; } lxb_url_parser_clean(&lexbor_parser); From c4a7bc32c10e2a56b04f900ec3a2399db21f4859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 10 Jun 2025 10:16:31 +0200 Subject: [PATCH 15/15] [skip ci] Add upgrading internals note for zend_update_exception_properties() --- UPGRADING.INTERNALS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 574d5f0827ff7..e4cf9e9c94b0d 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -34,6 +34,9 @@ PHP 8.5 INTERNALS UPGRADE NOTES . Added ZEND_NONSTRING attribute macro for character arrays that do not represent strings. This allows to silence the GCC 15.x `-Wunterminated-string-initialization` warning. + . Added the zend_update_exception_properties() function for instantiating + Exception child classes. It updates the $message, $code, and $previous + properties. ======================== 2. Build system changes