diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 58f34a370151..0a3adca165f9 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -12,6 +12,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_smart_str.h" #ifdef HAVE_CONFIG_H # include #endif @@ -30,9 +31,11 @@ #include "php_uri_arginfo.h" #include "uriparser/Uri.h" +zend_class_entry *php_uri_ce_rfc3986_uri_builder; zend_class_entry *php_uri_ce_rfc3986_uri; zend_class_entry *php_uri_ce_rfc3986_uri_type; zend_class_entry *php_uri_ce_rfc3986_uri_host_type; +zend_class_entry *php_uri_ce_whatwg_url_builder; zend_class_entry *php_uri_ce_whatwg_url; zend_class_entry *php_uri_ce_comparison_mode; zend_class_entry *php_uri_ce_exception; @@ -46,6 +49,9 @@ zend_class_entry *php_uri_ce_whatwg_url_validation_error; static zend_object_handlers object_handlers_rfc3986_uri; static zend_object_handlers object_handlers_whatwg_uri; +typedef zend_result (*php_uri_component_validator_string)(const zend_string *component); +typedef zend_result (*php_uri_component_validator_long)(zend_long component); + static const zend_module_dep uri_deps[] = { ZEND_MOD_REQUIRED("lexbor") ZEND_MOD_END @@ -53,6 +59,32 @@ static const zend_module_dep uri_deps[] = { static zend_array uri_parsers; +static zend_always_inline zval *php_uri_deref(zval *zv) +{ + if (UNEXPECTED(Z_TYPE_P(zv) == IS_REFERENCE)) { + return Z_REFVAL_P(zv); + } + + return zv; +} + +#define Z_RFC3986_URI_PROP_SCHEME_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 0)) +#define Z_RFC3986_URI_PROP_USERINFO_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 1)) +#define Z_RFC3986_URI_PROP_HOST_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 2)) +#define Z_RFC3986_URI_PROP_PORT_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 3)) +#define Z_RFC3986_URI_PROP_PATH_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 4)) +#define Z_RFC3986_URI_PROP_QUERY_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 5)) +#define Z_RFC3986_URI_PROP_FRAGMENT_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 6)) + +#define Z_WHATWG_URL_PROP_SCHEME_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 0)) +#define Z_WHATWG_URL_PROP_USERNAME_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 1)) +#define Z_WHATWG_URL_PROP_PASSWORD_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 2)) +#define Z_WHATWG_URL_PROP_HOST_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 3)) +#define Z_WHATWG_URL_PROP_PORT_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 4)) +#define Z_WHATWG_URL_PROP_PATH_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 5)) +#define Z_WHATWG_URL_PROP_QUERY_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 6)) +#define Z_WHATWG_URL_PROP_FRAGMENT_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 7)) + static HashTable *uri_get_debug_properties(php_uri_object *object) { const HashTable *std_properties = zend_std_get_properties(&object->std); @@ -1044,6 +1076,318 @@ PHP_METHOD(Uri_WhatWg_Url, __debugInfo) RETURN_ARR(uri_get_debug_properties(uri_object)); } +PHP_METHOD(Uri_Rfc3986_UriBuilder, reset) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + convert_to_null(Z_RFC3986_URI_PROP_SCHEME_P(ZEND_THIS)); + convert_to_null(Z_RFC3986_URI_PROP_USERINFO_P(ZEND_THIS)); + convert_to_null(Z_RFC3986_URI_PROP_HOST_P(ZEND_THIS)); + convert_to_null(Z_RFC3986_URI_PROP_PORT_P(ZEND_THIS)); + zval_ptr_dtor(Z_RFC3986_URI_PROP_PATH_P(ZEND_THIS)); + ZVAL_EMPTY_STRING(Z_RFC3986_URI_PROP_PATH_P(ZEND_THIS)); + convert_to_null(Z_RFC3986_URI_PROP_QUERY_P(ZEND_THIS)); + convert_to_null(Z_RFC3986_URI_PROP_FRAGMENT_P(ZEND_THIS)); + + RETVAL_COPY(ZEND_THIS); +} + +ZEND_ATTRIBUTE_NONNULL static void php_uri_builder_set_component_string( + INTERNAL_FUNCTION_PARAMETERS, const char *name, const size_t name_length, + const php_uri_component_validator_string validator +) { + zend_string *component; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(component) + ZEND_PARSE_PARAMETERS_END(); + + if (validator(component) == FAILURE) { + RETURN_THROWS(); + } + + zend_update_property_str(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length, component); + + RETVAL_COPY(ZEND_THIS); +} + +ZEND_ATTRIBUTE_NONNULL static void php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAMETERS, const char *name, const size_t name_length, + const php_uri_component_validator_string validator +) { + zend_string *component; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR_OR_NULL(component) + ZEND_PARSE_PARAMETERS_END(); + + if (component == NULL) { + zend_update_property_null(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length); + } else { + if (validator(component) == FAILURE) { + RETURN_THROWS(); + } + + zend_update_property_str(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length, component); + } + + RETVAL_COPY(ZEND_THIS); +} + +ZEND_ATTRIBUTE_NONNULL_ARGS(1) static void php_uri_builder_set_component_long_or_null( + INTERNAL_FUNCTION_PARAMETERS, const char *name, const size_t name_length, + const php_uri_component_validator_long validator +) { + zend_long component; + bool component_is_null; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG_OR_NULL(component, component_is_null) + ZEND_PARSE_PARAMETERS_END(); + + if (component_is_null) { + zend_update_property_null(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length); + } else { + if (validator(component) == FAILURE) { + RETURN_THROWS(); + } + + zend_update_property_long(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length, component); + } + + RETVAL_COPY(ZEND_THIS); +} + +PHP_METHOD(Uri_Rfc3986_UriBuilder, setScheme) +{ + php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("scheme"), + php_uri_parser_rfc3986_validate_scheme + ); +} + +PHP_METHOD(Uri_Rfc3986_UriBuilder, setUserInfo) +{ + php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("userinfo"), + php_uri_parser_rfc3986_validate_userinfo + ); +} + +PHP_METHOD(Uri_Rfc3986_UriBuilder, setHost) +{ + php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("host"), + php_uri_parser_rfc3986_validate_host + ); +} + +PHP_METHOD(Uri_Rfc3986_UriBuilder, setPort) +{ + php_uri_builder_set_component_long_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("port"), + php_uri_parser_rfc3986_validate_port + ); +} + +PHP_METHOD(Uri_Rfc3986_UriBuilder, setPath) +{ + php_uri_builder_set_component_string( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("path"), + php_uri_parser_rfc3986_validate_path + ); +} + +PHP_METHOD(Uri_Rfc3986_UriBuilder, setQuery) +{ + php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("query"), + php_uri_parser_rfc3986_validate_query + ); +} + +PHP_METHOD(Uri_Rfc3986_UriBuilder, setFragment) +{ + php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("fragment"), + php_uri_parser_rfc3986_validate_fragment + ); +} + +PHP_METHOD(Uri_Rfc3986_UriBuilder, build) +{ + zval *base_url = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_OBJECT_OF_CLASS_OR_NULL(base_url, php_uri_ce_rfc3986_uri) + ZEND_PARSE_PARAMETERS_END(); + + const zval *scheme = Z_RFC3986_URI_PROP_SCHEME_P(ZEND_THIS); + const zval *userinfo = Z_RFC3986_URI_PROP_USERINFO_P(ZEND_THIS); + const zval *host = Z_RFC3986_URI_PROP_HOST_P(ZEND_THIS); + const zval *port = Z_RFC3986_URI_PROP_PORT_P(ZEND_THIS); + const zval *path = Z_RFC3986_URI_PROP_PATH_P(ZEND_THIS); + const zval *query = Z_RFC3986_URI_PROP_QUERY_P(ZEND_THIS); + const zval *fragment = Z_RFC3986_URI_PROP_FRAGMENT_P(ZEND_THIS); + + php_uri_parser_rfc3986_uris *base_uris = NULL; + if (base_url != NULL) { + base_uris = Z_URI_OBJECT_P(base_url)->uri; + } + + php_uri_parser_rfc3986_uris *uriparser_uris = php_uri_parser_rfc3986_build_from_zval( + base_uris, scheme, userinfo, host, port, path, query, fragment + ); + if (uriparser_uris == NULL) { + RETURN_THROWS(); + } + + object_init_ex(return_value, php_uri_ce_rfc3986_uri); + php_uri_object *uri_object = Z_URI_OBJECT_P(return_value); + uri_object->parser = &php_uri_parser_rfc3986; + uri_object->uri = uriparser_uris; +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, __construct) +{ + ZEND_PARSE_PARAMETERS_NONE(); +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, reset) +{ + zval_ptr_dtor(Z_WHATWG_URL_PROP_SCHEME_P(ZEND_THIS)); + ZVAL_EMPTY_STRING(Z_WHATWG_URL_PROP_SCHEME_P(ZEND_THIS)); + convert_to_null(Z_WHATWG_URL_PROP_USERNAME_P(ZEND_THIS)); + convert_to_null(Z_WHATWG_URL_PROP_PASSWORD_P(ZEND_THIS)); + convert_to_null(Z_WHATWG_URL_PROP_HOST_P(ZEND_THIS)); + convert_to_null(Z_WHATWG_URL_PROP_PORT_P(ZEND_THIS)); + zval_ptr_dtor(Z_WHATWG_URL_PROP_PATH_P(ZEND_THIS)); + ZVAL_EMPTY_STRING(Z_WHATWG_URL_PROP_PATH_P(ZEND_THIS)); + convert_to_null(Z_WHATWG_URL_PROP_QUERY_P(ZEND_THIS)); + convert_to_null(Z_WHATWG_URL_PROP_FRAGMENT_P(ZEND_THIS)); + + RETVAL_COPY(ZEND_THIS); +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, setScheme) +{ + php_uri_builder_set_component_string( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("scheme"), + php_uri_parser_whatwg_validate_scheme + ); +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, setUsername) +{ + php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("username"), + php_uri_parser_whatwg_validate_none + ); +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, setPassword) +{ + php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("password"), + php_uri_parser_whatwg_validate_none + ); +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, setHost) +{ + php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("host"), + php_uri_parser_whatwg_validate_none + ); +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, setPort) +{ + php_uri_builder_set_component_long_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("port"), + php_uri_parser_whatwg_validate_port + ); +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, setPath) +{ + php_uri_builder_set_component_string( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("path"), + php_uri_parser_whatwg_validate_none + ); +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, setQuery) +{ + php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("query"), + php_uri_parser_whatwg_validate_none + ); +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, setFragment) +{ + php_uri_builder_set_component_string_or_null( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("fragment"), + php_uri_parser_whatwg_validate_none + ); +} + +PHP_METHOD(Uri_WhatWg_UrlBuilder, build) +{ + zval *base_url_zv = NULL; + zval *errors = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_OBJECT_OF_CLASS_OR_NULL(base_url_zv, php_uri_ce_whatwg_url) + Z_PARAM_ZVAL(errors) + ZEND_PARSE_PARAMETERS_END(); + + const zval *scheme = Z_WHATWG_URL_PROP_SCHEME_P(ZEND_THIS); + const zval *username = Z_WHATWG_URL_PROP_USERNAME_P(ZEND_THIS); + const zval *password = Z_WHATWG_URL_PROP_PASSWORD_P(ZEND_THIS); + const zval *host = Z_WHATWG_URL_PROP_HOST_P(ZEND_THIS); + const zval *port = Z_WHATWG_URL_PROP_PORT_P(ZEND_THIS); + const zval *path = Z_WHATWG_URL_PROP_PATH_P(ZEND_THIS); + const zval *query = Z_WHATWG_URL_PROP_QUERY_P(ZEND_THIS); + const zval *fragment = Z_WHATWG_URL_PROP_FRAGMENT_P(ZEND_THIS); + + lxb_url_t *base_url = NULL; + if (base_url_zv != NULL) { + base_url = Z_URI_OBJECT_P(base_url_zv)->uri; + } + + lxb_url_t *lexbor_url = php_uri_parser_whatwg_build_from_zval( + base_url, scheme, username, password, host, port, path, query, fragment, + errors + ); + if (lexbor_url == NULL) { + RETURN_THROWS(); + } + + object_init_ex(return_value, php_uri_ce_whatwg_url); + php_uri_object *uri_object = Z_URI_OBJECT_P(return_value); + uri_object->parser = &php_uri_parser_whatwg; + uri_object->uri = lexbor_url; +} + PHPAPI php_uri_object *php_uri_object_create(zend_class_entry *class_type, const php_uri_parser *parser) { php_uri_object *uri_object = zend_object_alloc(sizeof(*uri_object), class_type); @@ -1113,6 +1457,8 @@ PHPAPI zend_result php_uri_parser_register(const php_uri_parser *uri_parser) static PHP_MINIT_FUNCTION(uri) { + php_uri_ce_rfc3986_uri_builder = register_class_Uri_Rfc3986_UriBuilder(); + php_uri_ce_rfc3986_uri = register_class_Uri_Rfc3986_Uri(); php_uri_ce_rfc3986_uri->create_object = php_uri_object_create_rfc3986; php_uri_ce_rfc3986_uri->default_object_handlers = &object_handlers_rfc3986_uri; @@ -1124,6 +1470,8 @@ static PHP_MINIT_FUNCTION(uri) php_uri_ce_rfc3986_uri_type = register_class_Uri_Rfc3986_UriType(); php_uri_ce_rfc3986_uri_host_type = register_class_Uri_Rfc3986_UriHostType(); + php_uri_ce_whatwg_url_builder = register_class_Uri_WhatWg_UrlBuilder(); + php_uri_ce_whatwg_url = register_class_Uri_WhatWg_Url(); php_uri_ce_whatwg_url->create_object = php_uri_object_create_whatwg; php_uri_ce_whatwg_url->default_object_handlers = &object_handlers_whatwg_uri; diff --git a/ext/uri/php_uri.stub.php b/ext/uri/php_uri.stub.php index b0b83fcf83ec..28e042e206c1 100644 --- a/ext/uri/php_uri.stub.php +++ b/ext/uri/php_uri.stub.php @@ -45,6 +45,35 @@ enum UriHostType case RegisteredName; } + final class UriBuilder + { + private ?string $scheme = null; + private ?string $userinfo = null; + private ?string $host = null; + private ?int $port = null; + private string $path = ""; + private ?string $query = null; + private ?string $fragment = null; + + public function reset(): static {} + + public function setScheme(?string $scheme): static {} + + public function setUserInfo(#[\SensitiveParameter] ?string $userInfo): static {} + + public function setHost(?string $host): static {} + + public function setPort(?int $port): static {} + + public function setPath(string $path): static {} + + public function setQuery(?string $query): static {} + + public function setFragment(?string $fragment): static {} + + public function build(?\Uri\Rfc3986\Uri $baseUrl = null): \Uri\Rfc3986\Uri {} + } + /** @strict-properties */ final readonly class Uri { @@ -181,6 +210,41 @@ enum UrlHostType case Empty; } + final class UrlBuilder + { + private string $scheme = ""; + private ?string $username = null; + private ?string $password = null; + private ?string $host = null; + private ?int $port = null; + private string $path = ""; + private ?string $query = null; + private ?string $fragment = null; + + public function __construct() {} + + public function reset(): static {} + + public function setScheme(string $scheme): static {} + + public function setUsername(?string $username): static {} + + public function setPassword(#[\SensitiveParameter] ?string $password): static {} + + public function setHost(?string $host): static {} + + public function setPort(?int $port): static {} + + public function setPath(string $path): static {} + + public function setQuery(?string $query): static {} + + public function setFragment(?string $fragment): static {} + + /** @param array $errors */ + public function build(?\Uri\WhatWg\Url $baseUrl = null, &$errors = null): \Uri\WhatWg\Url {} + } + /** @strict-properties */ final readonly class Url { diff --git a/ext/uri/php_uri_arginfo.h b/ext/uri/php_uri_arginfo.h index 0fb464ee74aa..28b62950e713 100644 --- a/ext/uri/php_uri_arginfo.h +++ b/ext/uri/php_uri_arginfo.h @@ -1,7 +1,42 @@ /* This is a generated file, edit php_uri.stub.php instead. - * Stub hash: a3b4696ac001d537cc34b818715c7eb382c17c5b + * Stub hash: a5d9039ac35f7c2ff2d899bb622e33c92434c17f * Has decl header: yes */ +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_UriBuilder_reset, 0, 0, IS_STATIC, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_UriBuilder_setScheme, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, scheme, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_UriBuilder_setUserInfo, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, userInfo, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_UriBuilder_setHost, 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_Rfc3986_UriBuilder_setPort, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_UriBuilder_setPath, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_UriBuilder_setQuery, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_UriBuilder_setFragment, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, fragment, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Uri_Rfc3986_UriBuilder_build, 0, 0, Uri\\Rfc3986\\\125ri, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, baseUrl, Uri\\Rfc3986\\\125ri, 1, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_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\\Rfc3986\\\125ri, 1, "null") @@ -20,9 +55,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_Rfc3986_Uri_getRawScheme arginfo_class_Uri_Rfc3986_Uri_getScheme -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withScheme, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, scheme, IS_STRING, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_Rfc3986_Uri_withScheme arginfo_class_Uri_Rfc3986_UriBuilder_setScheme #define arginfo_class_Uri_Rfc3986_Uri_getUserInfo arginfo_class_Uri_Rfc3986_Uri_getScheme @@ -47,41 +80,31 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getHostType, 0, 0, Uri\\Rfc3986\\\125riHostType, 1) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withHost, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_Rfc3986_Uri_withHost arginfo_class_Uri_Rfc3986_UriBuilder_setHost ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getPort, 0, 0, IS_LONG, 1) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withPort, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_Rfc3986_Uri_withPort arginfo_class_Uri_Rfc3986_UriBuilder_setPort ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getPath, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_Uri_Rfc3986_Uri_getRawPath arginfo_class_Uri_Rfc3986_Uri_getPath -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withPath, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_Rfc3986_Uri_withPath arginfo_class_Uri_Rfc3986_UriBuilder_setPath #define arginfo_class_Uri_Rfc3986_Uri_getQuery arginfo_class_Uri_Rfc3986_Uri_getScheme #define arginfo_class_Uri_Rfc3986_Uri_getRawQuery arginfo_class_Uri_Rfc3986_Uri_getScheme -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withQuery, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_Rfc3986_Uri_withQuery arginfo_class_Uri_Rfc3986_UriBuilder_setQuery #define arginfo_class_Uri_Rfc3986_Uri_getFragment arginfo_class_Uri_Rfc3986_Uri_getScheme #define arginfo_class_Uri_Rfc3986_Uri_getRawFragment arginfo_class_Uri_Rfc3986_Uri_getScheme -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withFragment, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, fragment, IS_STRING, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_Rfc3986_Uri_withFragment arginfo_class_Uri_Rfc3986_UriBuilder_setFragment ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_equals, 0, 1, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, uri, Uri\\Rfc3986\\\125ri, 0) @@ -118,6 +141,38 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_UrlValidationError___construct, ZEND_ARG_TYPE_INFO(0, failure, _IS_BOOL, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_UrlBuilder___construct, 0, 0, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_UrlBuilder_reset arginfo_class_Uri_Rfc3986_UriBuilder_reset + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_UrlBuilder_setScheme, 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_UrlBuilder_setUsername, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, username, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_UrlBuilder_setPassword, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_UrlBuilder_setHost arginfo_class_Uri_Rfc3986_UriBuilder_setHost + +#define arginfo_class_Uri_WhatWg_UrlBuilder_setPort arginfo_class_Uri_Rfc3986_UriBuilder_setPort + +#define arginfo_class_Uri_WhatWg_UrlBuilder_setPath arginfo_class_Uri_Rfc3986_UriBuilder_setPath + +#define arginfo_class_Uri_WhatWg_UrlBuilder_setQuery arginfo_class_Uri_Rfc3986_UriBuilder_setQuery + +#define arginfo_class_Uri_WhatWg_UrlBuilder_setFragment arginfo_class_Uri_Rfc3986_UriBuilder_setFragment + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Uri_WhatWg_UrlBuilder_build, 0, 0, Uri\\WhatWg\\\125rl, 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_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") @@ -132,24 +187,18 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_WhatWg_Url_getScheme arginfo_class_Uri_Rfc3986_Uri_getPath -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() +#define arginfo_class_Uri_WhatWg_Url_withScheme arginfo_class_Uri_WhatWg_UrlBuilder_setScheme ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_isSpecialScheme, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() #define arginfo_class_Uri_WhatWg_Url_getUsername arginfo_class_Uri_Rfc3986_Uri_getScheme -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_withUsername arginfo_class_Uri_WhatWg_UrlBuilder_setUsername #define arginfo_class_Uri_WhatWg_Url_getPassword arginfo_class_Uri_Rfc3986_Uri_getScheme -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_withPassword arginfo_class_Uri_WhatWg_UrlBuilder_setPassword #define arginfo_class_Uri_WhatWg_Url_getAsciiHost arginfo_class_Uri_Rfc3986_Uri_getScheme @@ -158,23 +207,23 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Uri_WhatWg_Url_getHostType, 0, 0, Uri\\WhatWg\\\125rlHostType, 1) ZEND_END_ARG_INFO() -#define arginfo_class_Uri_WhatWg_Url_withHost arginfo_class_Uri_Rfc3986_Uri_withHost +#define arginfo_class_Uri_WhatWg_Url_withHost arginfo_class_Uri_Rfc3986_UriBuilder_setHost #define arginfo_class_Uri_WhatWg_Url_getPort arginfo_class_Uri_Rfc3986_Uri_getPort -#define arginfo_class_Uri_WhatWg_Url_withPort arginfo_class_Uri_Rfc3986_Uri_withPort +#define arginfo_class_Uri_WhatWg_Url_withPort arginfo_class_Uri_Rfc3986_UriBuilder_setPort #define arginfo_class_Uri_WhatWg_Url_getPath arginfo_class_Uri_Rfc3986_Uri_getPath -#define arginfo_class_Uri_WhatWg_Url_withPath arginfo_class_Uri_Rfc3986_Uri_withPath +#define arginfo_class_Uri_WhatWg_Url_withPath arginfo_class_Uri_Rfc3986_UriBuilder_setPath #define arginfo_class_Uri_WhatWg_Url_getQuery arginfo_class_Uri_Rfc3986_Uri_getScheme -#define arginfo_class_Uri_WhatWg_Url_withQuery arginfo_class_Uri_Rfc3986_Uri_withQuery +#define arginfo_class_Uri_WhatWg_Url_withQuery arginfo_class_Uri_Rfc3986_UriBuilder_setQuery #define arginfo_class_Uri_WhatWg_Url_getFragment arginfo_class_Uri_Rfc3986_Uri_getScheme -#define arginfo_class_Uri_WhatWg_Url_withFragment arginfo_class_Uri_Rfc3986_Uri_withFragment +#define arginfo_class_Uri_WhatWg_Url_withFragment arginfo_class_Uri_Rfc3986_UriBuilder_setFragment 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) @@ -196,6 +245,15 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_WhatWg_Url___debugInfo arginfo_class_Uri_Rfc3986_Uri___serialize +ZEND_METHOD(Uri_Rfc3986_UriBuilder, reset); +ZEND_METHOD(Uri_Rfc3986_UriBuilder, setScheme); +ZEND_METHOD(Uri_Rfc3986_UriBuilder, setUserInfo); +ZEND_METHOD(Uri_Rfc3986_UriBuilder, setHost); +ZEND_METHOD(Uri_Rfc3986_UriBuilder, setPort); +ZEND_METHOD(Uri_Rfc3986_UriBuilder, setPath); +ZEND_METHOD(Uri_Rfc3986_UriBuilder, setQuery); +ZEND_METHOD(Uri_Rfc3986_UriBuilder, setFragment); +ZEND_METHOD(Uri_Rfc3986_UriBuilder, build); ZEND_METHOD(Uri_Rfc3986_Uri, parse); ZEND_METHOD(Uri_Rfc3986_Uri, __construct); ZEND_METHOD(Uri_Rfc3986_Uri, getUriType); @@ -233,6 +291,17 @@ ZEND_METHOD(Uri_Rfc3986_Uri, __unserialize); ZEND_METHOD(Uri_Rfc3986_Uri, __debugInfo); ZEND_METHOD(Uri_WhatWg_InvalidUrlException, __construct); ZEND_METHOD(Uri_WhatWg_UrlValidationError, __construct); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, __construct); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, reset); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, setScheme); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, setUsername); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, setPassword); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, setHost); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, setPort); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, setPath); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, setQuery); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, setFragment); +ZEND_METHOD(Uri_WhatWg_UrlBuilder, build); ZEND_METHOD(Uri_WhatWg_Url, parse); ZEND_METHOD(Uri_WhatWg_Url, __construct); ZEND_METHOD(Uri_WhatWg_Url, getScheme); @@ -251,6 +320,19 @@ 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_Rfc3986_UriBuilder_methods[] = { + ZEND_ME(Uri_Rfc3986_UriBuilder, reset, arginfo_class_Uri_Rfc3986_UriBuilder_reset, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_UriBuilder, setScheme, arginfo_class_Uri_Rfc3986_UriBuilder_setScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_UriBuilder, setUserInfo, arginfo_class_Uri_Rfc3986_UriBuilder_setUserInfo, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_UriBuilder, setHost, arginfo_class_Uri_Rfc3986_UriBuilder_setHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_UriBuilder, setPort, arginfo_class_Uri_Rfc3986_UriBuilder_setPort, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_UriBuilder, setPath, arginfo_class_Uri_Rfc3986_UriBuilder_setPath, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_UriBuilder, setQuery, arginfo_class_Uri_Rfc3986_UriBuilder_setQuery, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_UriBuilder, setFragment, arginfo_class_Uri_Rfc3986_UriBuilder_setFragment, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_UriBuilder, build, arginfo_class_Uri_Rfc3986_UriBuilder_build, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static const zend_function_entry class_Uri_Rfc3986_Uri_methods[] = { ZEND_ME(Uri_Rfc3986_Uri, parse, arginfo_class_Uri_Rfc3986_Uri_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(Uri_Rfc3986_Uri, __construct, arginfo_class_Uri_Rfc3986_Uri___construct, ZEND_ACC_PUBLIC) @@ -300,6 +382,21 @@ static const zend_function_entry class_Uri_WhatWg_UrlValidationError_methods[] = ZEND_FE_END }; +static const zend_function_entry class_Uri_WhatWg_UrlBuilder_methods[] = { + ZEND_ME(Uri_WhatWg_UrlBuilder, __construct, arginfo_class_Uri_WhatWg_UrlBuilder___construct, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_UrlBuilder, reset, arginfo_class_Uri_WhatWg_UrlBuilder_reset, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_UrlBuilder, setScheme, arginfo_class_Uri_WhatWg_UrlBuilder_setScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_UrlBuilder, setUsername, arginfo_class_Uri_WhatWg_UrlBuilder_setUsername, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_UrlBuilder, setPassword, arginfo_class_Uri_WhatWg_UrlBuilder_setPassword, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_UrlBuilder, setHost, arginfo_class_Uri_WhatWg_UrlBuilder_setHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_UrlBuilder, setPort, arginfo_class_Uri_WhatWg_UrlBuilder_setPort, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_UrlBuilder, setPath, arginfo_class_Uri_WhatWg_UrlBuilder_setPath, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_UrlBuilder, setQuery, arginfo_class_Uri_WhatWg_UrlBuilder_setQuery, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_UrlBuilder, setFragment, arginfo_class_Uri_WhatWg_UrlBuilder_setFragment, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_UrlBuilder, build, arginfo_class_Uri_WhatWg_UrlBuilder_build, 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) @@ -403,6 +500,49 @@ static zend_class_entry *register_class_Uri_Rfc3986_UriHostType(void) return class_entry; } +static zend_class_entry *register_class_Uri_Rfc3986_UriBuilder(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Uri\\Rfc3986", "UriBuilder", class_Uri_Rfc3986_UriBuilder_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL); + + zval property_scheme_default_value; + ZVAL_NULL(&property_scheme_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_SCHEME), &property_scheme_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + + zval property_userinfo_default_value; + ZVAL_NULL(&property_userinfo_default_value); + zend_string *property_userinfo_name = zend_string_init("userinfo", sizeof("userinfo") - 1, true); + zend_declare_typed_property(class_entry, property_userinfo_name, &property_userinfo_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release_ex(property_userinfo_name, true); + + zval property_host_default_value; + ZVAL_NULL(&property_host_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_HOST), &property_host_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + + zval property_port_default_value; + ZVAL_NULL(&property_port_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PORT), &property_port_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG|MAY_BE_NULL)); + + zval property_path_default_value; + ZVAL_EMPTY_STRING(&property_path_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PATH), &property_path_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + + zval property_query_default_value; + ZVAL_NULL(&property_query_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_QUERY), &property_query_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + + zval property_fragment_default_value; + ZVAL_NULL(&property_fragment_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_FRAGMENT), &property_fragment_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + + + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setuserinfo", sizeof("setuserinfo") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + return class_entry; +} + static zend_class_entry *register_class_Uri_Rfc3986_Uri(void) { zend_class_entry ce, *class_entry; @@ -541,6 +681,51 @@ static zend_class_entry *register_class_Uri_WhatWg_UrlHostType(void) return class_entry; } +static zend_class_entry *register_class_Uri_WhatWg_UrlBuilder(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Uri\\WhatWg", "UrlBuilder", class_Uri_WhatWg_UrlBuilder_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL); + + zval property_scheme_default_value; + ZVAL_EMPTY_STRING(&property_scheme_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_SCHEME), &property_scheme_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + + zval property_username_default_value; + ZVAL_NULL(&property_username_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_USERNAME), &property_username_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + + zval property_password_default_value; + ZVAL_NULL(&property_password_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PASSWORD), &property_password_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + + zval property_host_default_value; + ZVAL_NULL(&property_host_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_HOST), &property_host_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + + zval property_port_default_value; + ZVAL_NULL(&property_port_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PORT), &property_port_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG|MAY_BE_NULL)); + + zval property_path_default_value; + ZVAL_EMPTY_STRING(&property_path_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PATH), &property_path_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + + zval property_query_default_value; + ZVAL_NULL(&property_query_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_QUERY), &property_query_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + + zval property_fragment_default_value; + ZVAL_NULL(&property_fragment_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_FRAGMENT), &property_fragment_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + + + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setpassword", sizeof("setpassword") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + return class_entry; +} + static zend_class_entry *register_class_Uri_WhatWg_Url(void) { zend_class_entry ce, *class_entry; diff --git a/ext/uri/php_uri_common.c b/ext/uri/php_uri_common.c index 5d87b7847b34..e182bda643f3 100644 --- a/ext/uri/php_uri_common.c +++ b/ext/uri/php_uri_common.c @@ -40,6 +40,31 @@ static zend_string *get_known_string_by_property_name(php_uri_property_name prop } } +zend_result php_uri_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_TMP(errors_zv, errors); + if (EG(exception)) { + return FAILURE; + } + + return SUCCESS; +} + void php_uri_property_read_helper(INTERNAL_FUNCTION_PARAMETERS, php_uri_property_name property_name, php_uri_component_read_mode component_read_mode) { ZEND_PARSE_PARAMETERS_NONE(); diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index f83327690dd4..31ef1dd2130c 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -17,9 +17,11 @@ #include "php_uri_decl.h" +extern zend_class_entry *php_uri_ce_rfc3986_uri_builder; extern zend_class_entry *php_uri_ce_rfc3986_uri; extern zend_class_entry *php_uri_ce_rfc3986_uri_type; extern zend_class_entry *php_uri_ce_rfc3986_uri_host_type; +extern zend_class_entry *php_uri_ce_whatwg_url_builder; extern zend_class_entry *php_uri_ce_whatwg_url; extern zend_class_entry *php_uri_ce_comparison_mode; extern zend_class_entry *php_uri_ce_exception; @@ -45,7 +47,7 @@ typedef enum php_uri_component_read_mode { typedef zend_result (*php_uri_property_handler_read)(void *uri, php_uri_component_read_mode read_mode, zval *retval); -typedef zend_result (*php_uri_property_handler_write)(void *uri, zval *value, zval *errors); +typedef zend_result (*php_uri_property_handler_write)(void *uri, const zval *value, zval *errors); typedef enum php_uri_property_name { PHP_URI_PROPERTY_NAME_SCHEME, @@ -185,6 +187,7 @@ static inline const php_uri_property_handler *php_uri_parser_property_handler_by } } +zend_result php_uri_pass_errors_by_ref_and_free(zval *errors_zv, zval *errors); void php_uri_property_read_helper(INTERNAL_FUNCTION_PARAMETERS, php_uri_property_name property_name, php_uri_component_read_mode component_read_mode); void php_uri_property_write_str_helper(INTERNAL_FUNCTION_PARAMETERS, php_uri_property_name property_name); void php_uri_property_write_str_or_null_helper(INTERNAL_FUNCTION_PARAMETERS, php_uri_property_name property_name); diff --git a/ext/uri/php_uri_decl.h b/ext/uri/php_uri_decl.h index d1fd58d04b2c..e5280de48bbb 100644 --- a/ext/uri/php_uri_decl.h +++ b/ext/uri/php_uri_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit php_uri.stub.php instead. - * Stub hash: a3b4696ac001d537cc34b818715c7eb382c17c5b */ + * Stub hash: a5d9039ac35f7c2ff2d899bb622e33c92434c17f */ -#ifndef ZEND_PHP_URI_DECL_a3b4696ac001d537cc34b818715c7eb382c17c5b_H -#define ZEND_PHP_URI_DECL_a3b4696ac001d537cc34b818715c7eb382c17c5b_H +#ifndef ZEND_PHP_URI_DECL_a5d9039ac35f7c2ff2d899bb622e33c92434c17f_H +#define ZEND_PHP_URI_DECL_a5d9039ac35f7c2ff2d899bb622e33c92434c17f_H typedef enum zend_enum_Uri_UriComparisonMode { ZEND_ENUM_Uri_UriComparisonMode_IncludeFragment = 1, @@ -63,4 +63,4 @@ typedef enum zend_enum_Uri_WhatWg_UrlHostType { ZEND_ENUM_Uri_WhatWg_UrlHostType_Empty = 5, } zend_enum_Uri_WhatWg_UrlHostType; -#endif /* ZEND_PHP_URI_DECL_a3b4696ac001d537cc34b818715c7eb382c17c5b_H */ +#endif /* ZEND_PHP_URI_DECL_a5d9039ac35f7c2ff2d899bb622e33c92434c17f_H */ diff --git a/ext/uri/tests/rfc3986/builder/all_success_with_reset.phpt b/ext/uri/tests/rfc3986/builder/all_success_with_reset.phpt new file mode 100644 index 000000000000..96c46744bf17 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/all_success_with_reset.phpt @@ -0,0 +1,63 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder all components - success - calling reset() afterwards +--FILE-- +setScheme("https") + ->setUserInfo("user:info") + ->setHost("example.com") + ->setPort(443) + ->setPath("/foo/bar/baz") + ->setQuery("foo=1&bar=baz") + ->setFragment("fragment"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +$uri = $builder->reset()->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(68) "https://user:info@example.com:443/foo/bar/baz?foo=1&bar=baz#fragment" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(4) "user" + ["password"]=> + string(4) "info" + ["host"]=> + string(11) "example.com" + ["port"]=> + int(443) + ["path"]=> + string(12) "/foo/bar/baz" + ["query"]=> + string(13) "foo=1&bar=baz" + ["fragment"]=> + string(8) "fragment" +} +string(0) "" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/basic_error_with_base.phpt b/ext/uri/tests/rfc3986/builder/basic_error_with_base.phpt new file mode 100644 index 000000000000..284de4e6a522 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/basic_error_with_base.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder basic - error - with base URL +--FILE-- +setPath("/foo/bar/baz"); + +try { + $builder->build(new Uri\Rfc3986\Uri("/foo/bar")); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified base URI must be absolute diff --git a/ext/uri/tests/rfc3986/builder/basic_success_with_base.phpt b/ext/uri/tests/rfc3986/builder/basic_success_with_base.phpt new file mode 100644 index 000000000000..3cb07f3f6a21 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/basic_success_with_base.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder basic - success - with base URL +--FILE-- +setPath("/foo/bar/baz"); +$uri = $builder->build(new Uri\Rfc3986\Uri("https://example.com")); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(31) "https://example.com/foo/bar/baz" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(12) "/foo/bar/baz" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/fragment_error_special_char.phpt b/ext/uri/tests/rfc3986/builder/fragment_error_special_char.phpt new file mode 100644 index 000000000000..8ed2a8938e9a --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/fragment_error_special_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setFragment() - error - contains invalid special character +--FILE-- +setFragment("#foo"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified fragment is malformed diff --git a/ext/uri/tests/rfc3986/builder/fragment_error_unicode_char.phpt b/ext/uri/tests/rfc3986/builder/fragment_error_unicode_char.phpt new file mode 100644 index 000000000000..2a15d5e85a82 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/fragment_error_unicode_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setFragment() - error - contains Unicode character +--FILE-- +setFragment("főő"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified fragment is malformed diff --git a/ext/uri/tests/rfc3986/builder/host_error_ipv6_closing_brace.phpt b/ext/uri/tests/rfc3986/builder/host_error_ipv6_closing_brace.phpt new file mode 100644 index 000000000000..45eef13faf71 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/host_error_ipv6_closing_brace.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setHost() - error - missing IPv6 closing brace +--FILE-- +setHost("[2001:%30db8:85a3:0000:0000:8a2e:0370:7334"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified host is malformed diff --git a/ext/uri/tests/rfc3986/builder/host_error_percent_encoding1.phpt b/ext/uri/tests/rfc3986/builder/host_error_percent_encoding1.phpt new file mode 100644 index 000000000000..ae06238a1f8f --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/host_error_percent_encoding1.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setHost() - error - invalid percent encoding octet in registered name +--FILE-- +setHost("ex%3mple.co"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified host is malformed diff --git a/ext/uri/tests/rfc3986/builder/host_error_percent_encoding2.phpt b/ext/uri/tests/rfc3986/builder/host_error_percent_encoding2.phpt new file mode 100644 index 000000000000..cc514ec69191 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/host_error_percent_encoding2.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setHost() - error - invalid percent encoded octet in IPv6 +--FILE-- +setHost("[2001:%308:85a3:0000:0000:8a2e:0370:7334]"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified host is malformed diff --git a/ext/uri/tests/rfc3986/builder/host_success_ip4_to_regname.phpt b/ext/uri/tests/rfc3986/builder/host_success_ip4_to_regname.phpt new file mode 100644 index 000000000000..6b0bc758fae5 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/host_success_ip4_to_regname.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setHost() - success - invalid IPv4 address falls back to a registered name +--FILE-- +setHost("192.168.%30.1"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(15) "//192.168.%30.1" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(13) "192.168.%30.1" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/host_success_ipv4.phpt b/ext/uri/tests/rfc3986/builder/host_success_ipv4.phpt new file mode 100644 index 000000000000..8ae65be833a6 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/host_success_ipv4.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setHost() - success - IPv4 address +--FILE-- +setHost("192.168.0.1"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(13) "//192.168.0.1" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "192.168.0.1" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/host_success_ipv6.phpt b/ext/uri/tests/rfc3986/builder/host_success_ipv6.phpt new file mode 100644 index 000000000000..73f73df17e25 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/host_success_ipv6.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setHost() - success - IPv6 address +--FILE-- +setHost("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(43) "//[2001:0db8:85a3:0000:0000:8a2e:0370:7334]" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(41) "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/host_success_ipvfuture.phpt b/ext/uri/tests/rfc3986/builder/host_success_ipvfuture.phpt new file mode 100644 index 000000000000..3a44bb20721a --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/host_success_ipvfuture.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setHost() - success - IPvFuture address +--FILE-- +setHost("[v1.2001:db8::1]"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(18) "//[v1.2001:db8::1]" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(16) "[v1.2001:db8::1]" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/host_success_null.phpt b/ext/uri/tests/rfc3986/builder/host_success_null.phpt new file mode 100644 index 000000000000..b3c79f5e7b0f --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/host_success_null.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setHost() - success - null +--FILE-- +setHost(null); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(0) "" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/host_success_regname.phpt b/ext/uri/tests/rfc3986/builder/host_success_regname.phpt new file mode 100644 index 000000000000..9bf4a4e97c7f --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/host_success_regname.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setHost() - success - Registered name +--FILE-- +setHost("www.example.com"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(17) "//www.example.com" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(15) "www.example.com" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/path_error_first_segment_colon.phpt b/ext/uri/tests/rfc3986/builder/path_error_first_segment_colon.phpt new file mode 100644 index 000000000000..711d76187db2 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/path_error_first_segment_colon.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPath() - error - contains a colon in the first path segment when the URI doesn't contain a scheme +--FILE-- +setPath("fo:o/bar/baz"); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The path must not begin with ":" when the URI doesn't contain a scheme diff --git a/ext/uri/tests/rfc3986/builder/path_error_leading_double_slash.phpt b/ext/uri/tests/rfc3986/builder/path_error_leading_double_slash.phpt new file mode 100644 index 000000000000..f32cedde136e --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/path_error_leading_double_slash.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPath() - error - contains leading double slash when the host is not present +--FILE-- +setPath("//foo/bar/baz"); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The path must not begin with "//" when the URI doesn't contain a host diff --git a/ext/uri/tests/rfc3986/builder/path_error_missing_leading_slash.phpt b/ext/uri/tests/rfc3986/builder/path_error_missing_leading_slash.phpt new file mode 100644 index 000000000000..97b6c3e8b854 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/path_error_missing_leading_slash.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPath() - error - missing a leading slash when the URI contains a host +--FILE-- +setPath("foo/bar/baz"); +$builder->setHost("example.com"); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified path is malformed diff --git a/ext/uri/tests/rfc3986/builder/path_error_special_char.phpt b/ext/uri/tests/rfc3986/builder/path_error_special_char.phpt new file mode 100644 index 000000000000..039b22770246 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/path_error_special_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPath() - error - contains invalid special character +--FILE-- +setPath("#foo"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified path is malformed diff --git a/ext/uri/tests/rfc3986/builder/path_success_empty_string.phpt b/ext/uri/tests/rfc3986/builder/path_success_empty_string.phpt new file mode 100644 index 000000000000..7c1dfdd60d13 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/path_success_empty_string.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPath() - success - empty string +--FILE-- +setPath(""); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(0) "" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/path_success_first_segment_colon.phpt b/ext/uri/tests/rfc3986/builder/path_success_first_segment_colon.phpt new file mode 100644 index 000000000000..b144a7dd3be0 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/path_success_first_segment_colon.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPath() - success - contains a colon in the first segment when the scheme is present +--FILE-- +setScheme("https"); +$builder->setPath(":foo/bar/baz"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(%d) "https::foo/bar/baz" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(12) ":foo/bar/baz" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/path_success_leading_double_slash.phpt b/ext/uri/tests/rfc3986/builder/path_success_leading_double_slash.phpt new file mode 100644 index 000000000000..eaa6c38b086b --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/path_success_leading_double_slash.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPath() - success - begins with double slashes when the URI contains a host +--FILE-- +setHost("example.com"); +$builder->setPath("//foo/bar/baz"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(26) "//example.com//foo/bar/baz" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(13) "//foo/bar/baz" + ["query"]=> + NULL + ["fragment"]=> + NULL +} \ No newline at end of file diff --git a/ext/uri/tests/rfc3986/builder/port_error_missing_host.phpt b/ext/uri/tests/rfc3986/builder/port_error_missing_host.phpt new file mode 100644 index 000000000000..4b40e84c78f3 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/port_error_missing_host.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPort() - error - missing host +--FILE-- +setPort(443); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: Cannot set a port without having a host diff --git a/ext/uri/tests/rfc3986/builder/port_error_negative.phpt b/ext/uri/tests/rfc3986/builder/port_error_negative.phpt new file mode 100644 index 000000000000..4cbc0e1c69d7 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/port_error_negative.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPort() - error - negative number +--FILE-- +setPort(-1); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified port is malformed diff --git a/ext/uri/tests/rfc3986/builder/port_success_large.phpt b/ext/uri/tests/rfc3986/builder/port_success_large.phpt new file mode 100644 index 000000000000..010af1cf1ef8 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/port_success_large.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPort() - success - large number +--FILE-- +setPort(PHP_INT_MAX); +$builder->setHost("example.com"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(%d) "//example.com:%d" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + int(%d) + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/port_success_null.phpt b/ext/uri/tests/rfc3986/builder/port_success_null.phpt new file mode 100644 index 000000000000..b1fbdfee25ba --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/port_success_null.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setPort() - success - null +--FILE-- +setPort(null); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(0) "" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/query_error_special_char.phpt b/ext/uri/tests/rfc3986/builder/query_error_special_char.phpt new file mode 100644 index 000000000000..ecfced24a44c --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/query_error_special_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setQuery() - error - contains invalid special character +--FILE-- +setQuery("#foo"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified query is malformed diff --git a/ext/uri/tests/rfc3986/builder/query_error_unicode_char.phpt b/ext/uri/tests/rfc3986/builder/query_error_unicode_char.phpt new file mode 100644 index 000000000000..4c212cddd631 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/query_error_unicode_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setQuery() - error - contains Unicode character +--FILE-- +setQuery("főő"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified query is malformed diff --git a/ext/uri/tests/rfc3986/builder/query_success_basic.phpt b/ext/uri/tests/rfc3986/builder/query_success_basic.phpt new file mode 100644 index 000000000000..eec7c5cc38b1 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/query_success_basic.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setQuery() - success - basic +--FILE-- +setQuery("foo=1&bar=baz"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(14) "?foo=1&bar=baz" +object(Uri\Rfc3986\Uri)#2 (8) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + string(13) "foo=1&bar=baz" + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/scheme_error_empty.phpt b/ext/uri/tests/rfc3986/builder/scheme_error_empty.phpt new file mode 100644 index 000000000000..13594c163363 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/scheme_error_empty.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setScheme() - error - empty +--FILE-- +setScheme(""); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified scheme is malformed diff --git a/ext/uri/tests/rfc3986/builder/scheme_error_first_char.phpt b/ext/uri/tests/rfc3986/builder/scheme_error_first_char.phpt new file mode 100644 index 000000000000..c178a0fa1f1e --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/scheme_error_first_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setScheme() - error - first character is not alpha +--FILE-- +setScheme("1"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified scheme is malformed diff --git a/ext/uri/tests/rfc3986/builder/scheme_error_special_char.phpt b/ext/uri/tests/rfc3986/builder/scheme_error_special_char.phpt new file mode 100644 index 000000000000..4941db44aa7d --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/scheme_error_special_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setScheme() - error - contains invalid special character +--FILE-- +setScheme(":"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified scheme is malformed diff --git a/ext/uri/tests/rfc3986/builder/scheme_success_basic.phpt b/ext/uri/tests/rfc3986/builder/scheme_success_basic.phpt new file mode 100644 index 000000000000..1b0f51db3c49 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/scheme_success_basic.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setScheme() - success - basic +--FILE-- +setScheme("scheme"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(7) "scheme:" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(6) "scheme" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/scheme_success_null.phpt b/ext/uri/tests/rfc3986/builder/scheme_success_null.phpt new file mode 100644 index 000000000000..d454f6da4f02 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/scheme_success_null.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setScheme() - success - null +--FILE-- +setScheme(null); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(0) "" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/scheme_success_special.phpt b/ext/uri/tests/rfc3986/builder/scheme_success_special.phpt new file mode 100644 index 000000000000..d7e6a2326a62 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/scheme_success_special.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setScheme() - success - contains digit & special characters +--FILE-- +setScheme("my-12+34.scheme"); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(16) "my-12+34.scheme:" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(15) "my-12+34.scheme" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/builder/userinfo_error_missing_host.phpt b/ext/uri/tests/rfc3986/builder/userinfo_error_missing_host.phpt new file mode 100644 index 000000000000..a6919e8ffd0a --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/userinfo_error_missing_host.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setUserInfo() - error - missing host +--FILE-- +setUserInfo("user:pass"); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: Cannot set a userinfo without having a host diff --git a/ext/uri/tests/rfc3986/builder/userinfo_error_percent_encoding.phpt b/ext/uri/tests/rfc3986/builder/userinfo_error_percent_encoding.phpt new file mode 100644 index 000000000000..0830a57864d7 --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/userinfo_error_percent_encoding.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setUserInfo() - error - invalid percent encoding +--FILE-- +setUserInfo("%3"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified userinfo is malformed diff --git a/ext/uri/tests/rfc3986/builder/userinfo_error_special_char.phpt b/ext/uri/tests/rfc3986/builder/userinfo_error_special_char.phpt new file mode 100644 index 000000000000..9d3fb2544daf --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/userinfo_error_special_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setUserInfo() - error - contains invalid special character +--FILE-- +setUserInfo("<>"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified userinfo is malformed diff --git a/ext/uri/tests/rfc3986/builder/userinfo_success_null.phpt b/ext/uri/tests/rfc3986/builder/userinfo_success_null.phpt new file mode 100644 index 000000000000..d0735f291faa --- /dev/null +++ b/ext/uri/tests/rfc3986/builder/userinfo_success_null.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\Rfc3986\UriBuilder::setUserInfo() - success - null +--FILE-- +setUserInfo(null); +$uri = $builder->build(); + +var_dump($uri->toRawString()); +var_dump($uri); + +?> +--EXPECTF-- +string(0) "" +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/rfc3986/modification/port_error_negative.phpt b/ext/uri/tests/rfc3986/modification/port_error_negative.phpt index b8a25ee76667..fe07df976326 100644 --- a/ext/uri/tests/rfc3986/modification/port_error_negative.phpt +++ b/ext/uri/tests/rfc3986/modification/port_error_negative.phpt @@ -1,5 +1,5 @@ --TEST-- -Test Uri\Rfc3986\Uri component modification - host - too small number +Test Uri\Rfc3986\Uri component modification - port - too small number --EXTENSIONS-- uri --FILE-- diff --git a/ext/uri/tests/whatwg/builder/all_success_with_reset.phpt b/ext/uri/tests/whatwg/builder/all_success_with_reset.phpt new file mode 100644 index 000000000000..7352f9b046be --- /dev/null +++ b/ext/uri/tests/whatwg/builder/all_success_with_reset.phpt @@ -0,0 +1,68 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder all components - success - calling reset() afterwards +--FILE-- +setScheme("https") + ->setUsername("user") + ->setPassword("pass") + ->setHost("example.com") + ->setPort(444) + ->setPath("/foo/bar/baz") + ->setQuery("foo=1&bar=baz") + ->setFragment("fragment"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +$url = $builder + ->reset() + ->setScheme("scheme") + ->setHost("www.example.com") + ->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(68) "https://user:pass@example.com:444/foo/bar/baz?foo=1&bar=baz#fragment" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(4) "user" + ["password"]=> + string(4) "pass" + ["host"]=> + string(11) "example.com" + ["port"]=> + int(444) + ["path"]=> + string(12) "/foo/bar/baz" + ["query"]=> + string(13) "foo=1&bar=baz" + ["fragment"]=> + string(8) "fragment" +} +string(24) "scheme://www.example.com" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(6) "scheme" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(15) "www.example.com" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/basic_error_with_base.phpt b/ext/uri/tests/whatwg/builder/basic_error_with_base.phpt new file mode 100644 index 000000000000..dbf3fe00c3c2 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/basic_error_with_base.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder basic - error - with base URL +--FILE-- +setPath("/foo/bar/baz"); + +try { + $builder->build(new Uri\WhatWg\Url("/foo/bar")); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified base URI must be absolute diff --git a/ext/uri/tests/whatwg/builder/basic_success_with_base.phpt b/ext/uri/tests/whatwg/builder/basic_success_with_base.phpt new file mode 100644 index 000000000000..e9a6caf789f6 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/basic_success_with_base.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder basic - success - with base URL +--FILE-- +setPath("/foo/bar/baz"); +$url = $builder->build(new Uri\WhatWg\Url("https://example.com")); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(31) "https://example.com/foo/bar/baz" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(12) "/foo/bar/baz" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/fragment_error_unicode_char.phpt b/ext/uri/tests/whatwg/builder/fragment_error_unicode_char.phpt new file mode 100644 index 000000000000..fc46470f489a --- /dev/null +++ b/ext/uri/tests/whatwg/builder/fragment_error_unicode_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setFragment() - error - contains Unicode character +--FILE-- +setFragment("főő"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- + diff --git a/ext/uri/tests/whatwg/builder/fragment_success_hashmark.phpt b/ext/uri/tests/whatwg/builder/fragment_success_hashmark.phpt new file mode 100644 index 000000000000..d5336048136e --- /dev/null +++ b/ext/uri/tests/whatwg/builder/fragment_success_hashmark.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setFragment() - success - leading hashmark is ignored +--FILE-- +setScheme("https"); +$builder->setHost("example.com"); +$builder->setFragment("#foo"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(24) "https://example.com/#foo" +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"]=> + string(3) "foo" +} diff --git a/ext/uri/tests/whatwg/builder/fragment_success_special_char.phpt b/ext/uri/tests/whatwg/builder/fragment_success_special_char.phpt new file mode 100644 index 000000000000..5d49a6354409 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/fragment_success_special_char.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setFragment() - success - contains special character +--FILE-- +setScheme("https"); +$builder->setHost("example.com"); +$builder->setFragment(" "); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(33) "https://example.com/#%20%3Cfoo%3E" +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"]=> + string(12) "%20%3Cfoo%3E" +} diff --git a/ext/uri/tests/whatwg/builder/fragment_success_tab_newline.phpt b/ext/uri/tests/whatwg/builder/fragment_success_tab_newline.phpt new file mode 100644 index 000000000000..5812f4e8b573 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/fragment_success_tab_newline.phpt @@ -0,0 +1,58 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setFragment() - success - contains tab an newline characters +--FILE-- +setScheme("\tfo\no"); +$builder->setHost("example.com"); +$builder->setFragment("\tfoo"); +$errors = []; +$url = $builder->build(errors: $errors); + +var_dump($url->toAsciiString()); +var_dump($url); +var_dump($errors); + +?> +--EXPECTF-- +string(21) "foo://example.com#foo" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(3) "foo" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + string(3) "foo" +} +array(%d) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(5) " fo +o" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit) + ["failure"]=> + bool(false) + } + [1]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(4) " foo" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit) + ["failure"]=> + bool(false) + } +} diff --git a/ext/uri/tests/whatwg/builder/host_error_ipv6_closing_brace.phpt b/ext/uri/tests/whatwg/builder/host_error_ipv6_closing_brace.phpt new file mode 100644 index 000000000000..c19c0e5d0dea --- /dev/null +++ b/ext/uri/tests/whatwg/builder/host_error_ipv6_closing_brace.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setHost() - error - missing IPv6 closing brace +--FILE-- +setScheme("https"); +$builder->setHost("[2001:%30db8:85a3:0000:0000:8a2e:0370:7334"); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified host is malformed (Ipv6Unclosed) diff --git a/ext/uri/tests/whatwg/builder/host_error_null_special.phpt b/ext/uri/tests/whatwg/builder/host_error_null_special.phpt new file mode 100644 index 000000000000..973f4ef9aca3 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/host_error_null_special.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setHost() - success - null in case of special URLs +--FILE-- +setScheme("https"); +$builder->setHost(null); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified host is malformed (HostMissing) diff --git a/ext/uri/tests/whatwg/builder/host_error_percent_encoding1.phpt b/ext/uri/tests/whatwg/builder/host_error_percent_encoding1.phpt new file mode 100644 index 000000000000..2dc8c76189fd --- /dev/null +++ b/ext/uri/tests/whatwg/builder/host_error_percent_encoding1.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setHost() - error - invalid percent encoding octet in registered name +--FILE-- +setScheme("https"); +$builder->setHost("ex%3mple.co"); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified host is malformed (DomainInvalidCodePoint) diff --git a/ext/uri/tests/whatwg/builder/host_error_percent_encoding2.phpt b/ext/uri/tests/whatwg/builder/host_error_percent_encoding2.phpt new file mode 100644 index 000000000000..6b5e120792ce --- /dev/null +++ b/ext/uri/tests/whatwg/builder/host_error_percent_encoding2.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setHost() - error - invalid percent encoded octet in IPv6 +--FILE-- +setScheme("https"); +$builder->setHost("[2001:%308:85a3:0000:0000:8a2e:0370:7334]"); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified host is malformed (Ipv6InvalidCodePoint) diff --git a/ext/uri/tests/whatwg/builder/host_error_percent_encoding3.phpt b/ext/uri/tests/whatwg/builder/host_error_percent_encoding3.phpt new file mode 100644 index 000000000000..aee9f95d1f44 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/host_error_percent_encoding3.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setHost() - error - invalid percent encoded octet in IPv4 +--FILE-- +setScheme("https"); +$builder->setHost("192.168.%8.1"); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified host is malformed (DomainInvalidCodePoint) diff --git a/ext/uri/tests/whatwg/builder/host_success_ipv4.phpt b/ext/uri/tests/whatwg/builder/host_success_ipv4.phpt new file mode 100644 index 000000000000..301f6ee325e0 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/host_success_ipv4.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setHost() - success - IPv4 address +--FILE-- +setScheme("https") + ->setHost("192.168.0.1"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(20) "https://192.168.0.1/" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "192.168.0.1" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/host_success_ipv4_percent_encoding.phpt b/ext/uri/tests/whatwg/builder/host_success_ipv4_percent_encoding.phpt new file mode 100644 index 000000000000..367d7551cbcd --- /dev/null +++ b/ext/uri/tests/whatwg/builder/host_success_ipv4_percent_encoding.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setHost() - success - percent-encoding in IPv4 address +--FILE-- +setScheme("https"); +$builder->setHost("192.168.%30.1"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(20) "https://192.168.0.1/" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "192.168.0.1" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/host_success_ipv6.phpt b/ext/uri/tests/whatwg/builder/host_success_ipv6.phpt new file mode 100644 index 000000000000..b87d5706faa5 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/host_success_ipv6.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setHost() - success - IPv6 address +--FILE-- +setScheme("https") + ->setHost("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(39) "https://[2001:db8:85a3::8a2e:370:7334]/" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(30) "[2001:db8:85a3::8a2e:370:7334]" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/host_success_null.phpt b/ext/uri/tests/whatwg/builder/host_success_null.phpt new file mode 100644 index 000000000000..ab653e6612c9 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/host_success_null.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setHost() - success - null in case of non-special URLs +--FILE-- +setScheme("scheme"); +$builder->setHost(null); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(9) "scheme://" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(6) "scheme" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(0) "" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/host_success_regname.phpt b/ext/uri/tests/whatwg/builder/host_success_regname.phpt new file mode 100644 index 000000000000..8fb1c9a1a233 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/host_success_regname.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setHost() - success - Registered name +--FILE-- +setScheme("https"); +$builder->setHost("www.example.com"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(24) "https://www.example.com/" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(15) "www.example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/password_error_missing_host.phpt b/ext/uri/tests/whatwg/builder/password_error_missing_host.phpt new file mode 100644 index 000000000000..1d55b74a4f50 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/password_error_missing_host.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPassword() - error - missing host +--FILE-- +setScheme("https"); +$builder->setPassword("pass"); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified host is malformed (HostMissing) diff --git a/ext/uri/tests/whatwg/builder/password_success_file_scheme.phpt b/ext/uri/tests/whatwg/builder/password_success_file_scheme.phpt new file mode 100644 index 000000000000..c759f1a41204 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/password_success_file_scheme.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPassword() - success - ignored for file scheme +--FILE-- +setScheme("file"); +$builder->setPath("C:/a.txt"); +$builder->setPassword("password"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(16) "file:///C:/a.txt" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "file" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(0) "" + ["port"]=> + NULL + ["path"]=> + string(9) "/C:/a.txt" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/path_success_empty_string.phpt b/ext/uri/tests/whatwg/builder/path_success_empty_string.phpt new file mode 100644 index 000000000000..c0e4119374c1 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/path_success_empty_string.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPath() - success - empty string +--FILE-- +setScheme("file"); +$builder->setPath(""); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(8) "file:///" +object(Uri\WhatWg\Url)#2 (8) { + ["scheme"]=> + string(4) "file" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(0) "" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/path_success_first_segment_colon.phpt b/ext/uri/tests/whatwg/builder/path_success_first_segment_colon.phpt new file mode 100644 index 000000000000..b3fb5fa63539 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/path_success_first_segment_colon.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPath() - success - contains a colon in the first segment when the scheme is present +--FILE-- +setScheme("file"); +$builder->setPath(":foo/bar/baz"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(20) "file:///:foo/bar/baz" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "file" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(0) "" + ["port"]=> + NULL + ["path"]=> + string(13) "/:foo/bar/baz" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/path_success_leading_double_slash1.phpt b/ext/uri/tests/whatwg/builder/path_success_leading_double_slash1.phpt new file mode 100644 index 000000000000..e3607693eed3 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/path_success_leading_double_slash1.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPath() - success - begins with double slashes when the host is not present +--FILE-- +setScheme("file"); +$builder->setPath("//foo/bar/baz"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(20) "file:////foo/bar/baz" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "file" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(0) "" + ["port"]=> + NULL + ["path"]=> + string(13) "//foo/bar/baz" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/path_success_leading_double_slash2.phpt b/ext/uri/tests/whatwg/builder/path_success_leading_double_slash2.phpt new file mode 100644 index 000000000000..ff7727870c23 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/path_success_leading_double_slash2.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPath() - success - begins with double slashes when the opaque URL contains a host +--FILE-- +setScheme("scheme"); +$builder->setHost("example.com"); +$builder->setPath("//foo/bar/baz"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(33) "scheme://example.com//foo/bar/baz" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(6) "scheme" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(13) "//foo/bar/baz" + ["query"]=> + NULL + ["fragment"]=> + NULL +} \ No newline at end of file diff --git a/ext/uri/tests/whatwg/builder/path_success_special_char.phpt b/ext/uri/tests/whatwg/builder/path_success_special_char.phpt new file mode 100644 index 000000000000..90bf35635e5b --- /dev/null +++ b/ext/uri/tests/whatwg/builder/path_success_special_char.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPath() - success - contains special character +--FILE-- +setScheme("scheme"); +$builder->setPath("#foo"); + +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(16) "scheme:///%23foo" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(6) "scheme" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(0) "" + ["port"]=> + NULL + ["path"]=> + string(7) "/%23foo" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/path_success_without_leading_slash.phpt b/ext/uri/tests/whatwg/builder/path_success_without_leading_slash.phpt new file mode 100644 index 000000000000..8bddbb4ff446 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/path_success_without_leading_slash.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPath() - success - without a leading slash when the URL contains a host +--FILE-- +setScheme("https"); +$builder->setPath("foo/bar/baz"); +$builder->setHost("example.com"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(31) "https://example.com/foo/bar/baz" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(12) "/foo/bar/baz" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/port_error_large.phpt b/ext/uri/tests/whatwg/builder/port_error_large.phpt new file mode 100644 index 000000000000..94205613a70c --- /dev/null +++ b/ext/uri/tests/whatwg/builder/port_error_large.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPort() - error - too large number +--FILE-- +setPort(PHP_INT_MAX); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified port is malformed diff --git a/ext/uri/tests/whatwg/builder/port_error_negative.phpt b/ext/uri/tests/whatwg/builder/port_error_negative.phpt new file mode 100644 index 000000000000..eb2b5449ccb3 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/port_error_negative.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPort() - error - negative number +--FILE-- +setPort(-1); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified port is malformed diff --git a/ext/uri/tests/whatwg/builder/port_success_default.phpt b/ext/uri/tests/whatwg/builder/port_success_default.phpt new file mode 100644 index 000000000000..f6f2d6b3c8b4 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/port_success_default.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPort() - success - default port +--FILE-- +setScheme("https"); +$builder->setPort(444); +$builder->setHost("example.com"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(24) "https://example.com:444/" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + int(444) + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/port_success_empty_opaque_host.phpt b/ext/uri/tests/whatwg/builder/port_success_empty_opaque_host.phpt new file mode 100644 index 000000000000..67a8a8aedcb1 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/port_success_empty_opaque_host.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPort() - success - added for empty opaque host +--FILE-- +setScheme("scheme"); +$builder->setPort(443); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(13) "scheme://:443" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(6) "scheme" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(0) "" + ["port"]=> + int(443) + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/port_success_file_scheme.phpt b/ext/uri/tests/whatwg/builder/port_success_file_scheme.phpt new file mode 100644 index 000000000000..4e5aee742be9 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/port_success_file_scheme.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPort() - success - ignored for file scheme +--FILE-- +setScheme("file"); +$builder->setPath("C:/a.txt"); +$builder->setPort(443); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(16) "file:///C:/a.txt" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "file" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(0) "" + ["port"]=> + NULL + ["path"]=> + string(9) "/C:/a.txt" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/port_success_non_default.phpt b/ext/uri/tests/whatwg/builder/port_success_non_default.phpt new file mode 100644 index 000000000000..77d2ecfad4a8 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/port_success_non_default.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPort() - success - non-default port +--FILE-- +setScheme("https"); +$builder->setPort(443); +$builder->setHost("example.com"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(20) "https://example.com/" +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 +} diff --git a/ext/uri/tests/whatwg/builder/port_success_null.phpt b/ext/uri/tests/whatwg/builder/port_success_null.phpt new file mode 100644 index 000000000000..326e4b8d5aa6 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/port_success_null.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setPort() - success - null +--FILE-- +setScheme("https"); +$builder->setHost("example.com"); +$builder->setPort(null); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(20) "https://example.com/" +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 +} diff --git a/ext/uri/tests/whatwg/builder/query_success_basic.phpt b/ext/uri/tests/whatwg/builder/query_success_basic.phpt new file mode 100644 index 000000000000..ce478c7c2753 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/query_success_basic.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setQuery() - success - basic +--FILE-- +setScheme("https"); +$builder->setHost("example.com"); +$builder->setQuery("foo=1&bar=baz"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(34) "https://example.com/?foo=1&bar=baz" +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"]=> + string(13) "foo=1&bar=baz" + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/query_success_question_mark.phpt b/ext/uri/tests/whatwg/builder/query_success_question_mark.phpt new file mode 100644 index 000000000000..b69bec1854ea --- /dev/null +++ b/ext/uri/tests/whatwg/builder/query_success_question_mark.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setQuery() - success - leading question mark is ignored +--FILE-- +setScheme("https"); +$builder->setHost("example.com"); +$builder->setQuery("?foo"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(24) "https://example.com/?foo" +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"]=> + string(3) "foo" + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/query_success_unicode_char.phpt b/ext/uri/tests/whatwg/builder/query_success_unicode_char.phpt new file mode 100644 index 000000000000..b649c772c303 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/query_success_unicode_char.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setQuery() - success - contains Unicode character +--FILE-- +setScheme("https"); +$builder->setHost("example.com"); +$builder->setQuery("főő"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(34) "https://example.com/?f%C5%91%C5%91" +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"]=> + string(13) "f%C5%91%C5%91" + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/scheme_error_empty.phpt b/ext/uri/tests/whatwg/builder/scheme_error_empty.phpt new file mode 100644 index 000000000000..985cfe30eeac --- /dev/null +++ b/ext/uri/tests/whatwg/builder/scheme_error_empty.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setScheme() - error - empty +--FILE-- +setScheme(""); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified scheme is malformed diff --git a/ext/uri/tests/whatwg/builder/scheme_error_empty_string.phpt b/ext/uri/tests/whatwg/builder/scheme_error_empty_string.phpt new file mode 100644 index 000000000000..163949df8b46 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/scheme_error_empty_string.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setScheme() - error - empty string +--FILE-- +setScheme(""); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified scheme is malformed diff --git a/ext/uri/tests/whatwg/builder/scheme_error_first_char.phpt b/ext/uri/tests/whatwg/builder/scheme_error_first_char.phpt new file mode 100644 index 000000000000..c5b80a866c33 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/scheme_error_first_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setScheme() - error - first character is not alpha +--FILE-- +setScheme("1"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified scheme is malformed diff --git a/ext/uri/tests/whatwg/builder/scheme_error_special_char.phpt b/ext/uri/tests/whatwg/builder/scheme_error_special_char.phpt new file mode 100644 index 000000000000..eaf59f922ecd --- /dev/null +++ b/ext/uri/tests/whatwg/builder/scheme_error_special_char.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setScheme() - error - contains invalid special character +--FILE-- +setScheme(":"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified scheme is malformed diff --git a/ext/uri/tests/whatwg/builder/scheme_success_non_special.phpt b/ext/uri/tests/whatwg/builder/scheme_success_non_special.phpt new file mode 100644 index 000000000000..b3747361aea5 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/scheme_success_non_special.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setScheme() - success - non-special scheme +--FILE-- +setScheme("foo"); +$builder->setHost("example.com"); +$builder->setPath("/foo/bar/baz"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(29) "foo://example.com/foo/bar/baz" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(3) "foo" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(12) "/foo/bar/baz" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/scheme_success_special.phpt b/ext/uri/tests/whatwg/builder/scheme_success_special.phpt new file mode 100644 index 000000000000..5c12cca60566 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/scheme_success_special.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setScheme() - success - contains digit & special characters +--FILE-- +setScheme("my-12+34.scheme"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(18) "my-12+34.scheme://" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(15) "my-12+34.scheme" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(0) "" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} \ No newline at end of file diff --git a/ext/uri/tests/whatwg/builder/username_error_missing_host.phpt b/ext/uri/tests/whatwg/builder/username_error_missing_host.phpt new file mode 100644 index 000000000000..609d1d49a5c3 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/username_error_missing_host.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setUsername() - error - missing host +--FILE-- +setScheme("https"); +$builder->setUsername("user"); + +try { + $builder->build(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified host is malformed (HostMissing) diff --git a/ext/uri/tests/whatwg/builder/username_success_file_scheme.phpt b/ext/uri/tests/whatwg/builder/username_success_file_scheme.phpt new file mode 100644 index 000000000000..7244a238099b --- /dev/null +++ b/ext/uri/tests/whatwg/builder/username_success_file_scheme.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setUsername() - success - ignored for file scheme +--FILE-- +setScheme("file"); +$builder->setPath("C:/a.txt"); +$builder->setUsername("user"); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(16) "file:///C:/a.txt" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "file" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(0) "" + ["port"]=> + NULL + ["path"]=> + string(9) "/C:/a.txt" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/username_success_invalid_percent_encoding.phpt b/ext/uri/tests/whatwg/builder/username_success_invalid_percent_encoding.phpt new file mode 100644 index 000000000000..d2bd55f91360 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/username_success_invalid_percent_encoding.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setUsername() - success - invalid percent encoding +--FILE-- +setScheme("https"); +$builder->setHost("example.com"); +$builder->setUsername("%3"); /* TODO double check why does it work? */ +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(23) "https://%3@example.com/" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(2) "%3" + ["password"]=> + string(0) "" + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/username_success_null.phpt b/ext/uri/tests/whatwg/builder/username_success_null.phpt new file mode 100644 index 000000000000..00a64ff7b428 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/username_success_null.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setUsername() - success - null +--FILE-- +setScheme("https"); +$builder->setHost("example.com"); +$builder->setUsername(null); +$url = $builder->build(); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(20) "https://example.com/" +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 +} diff --git a/ext/uri/tests/whatwg/builder/username_success_special_char.phpt b/ext/uri/tests/whatwg/builder/username_success_special_char.phpt new file mode 100644 index 000000000000..a972b1e4fbfb --- /dev/null +++ b/ext/uri/tests/whatwg/builder/username_success_special_char.phpt @@ -0,0 +1,36 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setUsername() - success - contains special characters +--FILE-- +setScheme("https"); +$builder->setHost("example.com"); +$builder->setUsername("~%#"); +$errors = []; +$url = $builder->build(null, $errors); + +var_dump($url->toAsciiString()); +var_dump($url); + +?> +--EXPECTF-- +string(26) "https://~%%23@example.com/" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(5) "~%%23" + ["password"]=> + string(0) "" + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/whatwg/builder/username_success_tab_newline.phpt b/ext/uri/tests/whatwg/builder/username_success_tab_newline.phpt new file mode 100644 index 000000000000..021ac2d61907 --- /dev/null +++ b/ext/uri/tests/whatwg/builder/username_success_tab_newline.phpt @@ -0,0 +1,49 @@ +--TEST-- +Test Uri\WhatWg\UrlBuilder::setUsername() - success - contains tab an newline characters +--FILE-- +setScheme("\tfo\no"); +$builder->setHost("example.com"); +$builder->setUsername("f\no\ro\t"); +$errors = []; +$url = $builder->build(errors: $errors); + +var_dump($url->toAsciiString()); +var_dump($url); +var_dump($errors); + +?> +--EXPECT-- +string(30) "foo://f%0Ao%0Do%09@example.com" +object(Uri\WhatWg\Url)#4 (8) { + ["scheme"]=> + string(3) "foo" + ["username"]=> + string(12) "f%0Ao%0Do%09" + ["password"]=> + string(0) "" + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +array(1) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#2 (3) { + ["context"]=> + string(5) " fo +o" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit) + ["failure"]=> + bool(false) + } +} diff --git a/ext/uri/tests/whatwg/modification/multiple_error_with_warnings.phpt b/ext/uri/tests/whatwg/modification/multiple_error_with_warnings.phpt new file mode 100644 index 000000000000..a824e110f216 --- /dev/null +++ b/ext/uri/tests/whatwg/modification/multiple_error_with_warnings.phpt @@ -0,0 +1,25 @@ +--TEST-- +Test Uri\WhatWg\Url component modification - error - modifying multiple components with warnings before throwing an exception +--EXTENSIONS-- +uri +--FILE-- +withScheme("\tscheme") + ->withHost("\tex.com") + ->withQuery("\refoo=bar") + ->withFragment("\nfoo"); + +try { + $url->withScheme("0"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; + var_dump($e->errors); +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified scheme is malformed +array(0) { +} diff --git a/ext/uri/uri_parser_rfc3986.c b/ext/uri/uri_parser_rfc3986.c index 4e2c5656aa77..49acd6d1e0a7 100644 --- a/ext/uri/uri_parser_rfc3986.c +++ b/ext/uri/uri_parser_rfc3986.c @@ -103,7 +103,7 @@ ZEND_ATTRIBUTE_NONNULL static UriUriA *get_normalized_uri(php_uri_parser_rfc3986 return &uriparser_uris->normalized_uri; } -ZEND_ATTRIBUTE_NONNULL static UriUriA *get_uri_for_reading(php_uri_parser_rfc3986_uris *uriparser_uris, php_uri_component_read_mode read_mode) +ZEND_ATTRIBUTE_NONNULL static UriUriA *get_uri_for_reading(php_uri_parser_rfc3986_uris *uriparser_uris, const php_uri_component_read_mode read_mode) { switch (read_mode) { case PHP_URI_COMPONENT_READ_MODE_RAW: @@ -140,7 +140,7 @@ ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_uri_type_read(php_uri_parser_ ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_rfc3986_uri_type, type)); } -ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_scheme_read(void *uri, php_uri_component_read_mode read_mode, zval *retval) +ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_scheme_read(void *uri, const php_uri_component_read_mode read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(uri, read_mode); @@ -153,7 +153,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_scheme_read(voi return SUCCESS; } -static zend_result php_uri_parser_rfc3986_scheme_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_rfc3986_scheme_write(void *uri, const zval *value, zval *errors) { UriUriA *uriparser_uri = get_uri_for_writing(uri); int result; @@ -177,7 +177,7 @@ static zend_result php_uri_parser_rfc3986_scheme_write(void *uri, zval *value, z } } -ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_userinfo_read(php_uri_parser_rfc3986_uris *uri, php_uri_component_read_mode read_mode, zval *retval) +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_userinfo_read(php_uri_parser_rfc3986_uris *uri, const php_uri_component_read_mode read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(uri, read_mode); @@ -190,7 +190,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_userinfo_read(php_uri_ return SUCCESS; } -zend_result php_uri_parser_rfc3986_userinfo_write(php_uri_parser_rfc3986_uris *uri, zval *value, zval *errors) +zend_result php_uri_parser_rfc3986_userinfo_write(php_uri_parser_rfc3986_uris *uri, const zval *value, zval *errors) { UriUriA *uriparser_uri = get_uri_for_writing(uri); int result; @@ -217,7 +217,7 @@ zend_result php_uri_parser_rfc3986_userinfo_write(php_uri_parser_rfc3986_uris *u } } -ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_username_read(void *uri, php_uri_component_read_mode read_mode, zval *retval) +ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_username_read(void *uri, const php_uri_component_read_mode read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(uri, read_mode); @@ -239,7 +239,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_username_read(v return SUCCESS; } -ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_password_read(void *uri, php_uri_component_read_mode read_mode, zval *retval) +ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_password_read(void *uri, const php_uri_component_read_mode read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(uri, read_mode); @@ -258,7 +258,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_password_read(v return SUCCESS; } -ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_host_read(void *uri, php_uri_component_read_mode read_mode, zval *retval) +ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_host_read(void *uri, const php_uri_component_read_mode read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(uri, read_mode); @@ -305,7 +305,7 @@ ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_host_type_read(php_uri_parser ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_rfc3986_uri_host_type, type)); } -static zend_result php_uri_parser_rfc3986_host_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_rfc3986_host_write(void *uri, const zval *value, zval *errors) { UriUriA *uriparser_uri = get_uri_for_writing(uri); int result; @@ -335,7 +335,7 @@ static zend_result php_uri_parser_rfc3986_host_write(void *uri, zval *value, zva } } -ZEND_ATTRIBUTE_NONNULL static zend_long port_str_to_zend_long_checked(const char *str, size_t len) +ZEND_ATTRIBUTE_NONNULL static zend_long port_str_to_zend_long_checked(const char *str, const size_t len) { if (len > MAX_LENGTH_OF_LONG) { return -1; @@ -353,7 +353,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_long port_str_to_zend_long_checked(const char return (zend_long)result; } -ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_port_read(void *uri, php_uri_component_read_mode read_mode, zval *retval) +ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_port_read(void *uri, const php_uri_component_read_mode read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(uri, read_mode); @@ -366,7 +366,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_port_read(void return SUCCESS; } -static zend_result php_uri_parser_rfc3986_port_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_rfc3986_port_write(void *uri, const zval *value, zval *errors) { UriUriA *uriparser_uri = get_uri_for_writing(uri); int result; @@ -395,7 +395,7 @@ static zend_result php_uri_parser_rfc3986_port_write(void *uri, zval *value, zva } } -ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_path_read(void *uri, php_uri_component_read_mode read_mode, zval *retval) +ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_path_read(void *uri, const php_uri_component_read_mode read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(uri, read_mode); @@ -439,7 +439,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_path_read(void return SUCCESS; } -static zend_result php_uri_parser_rfc3986_path_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_rfc3986_path_write(void *uri, const zval *value, zval *errors) { UriUriA *uriparser_uri = get_uri_for_writing(uri); int result; @@ -476,7 +476,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_query_read(void return SUCCESS; } -static zend_result php_uri_parser_rfc3986_query_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_rfc3986_query_write(void *uri, const zval *value, zval *errors) { UriUriA *uriparser_uri = get_uri_for_writing(uri); int result; @@ -500,7 +500,7 @@ static zend_result php_uri_parser_rfc3986_query_write(void *uri, zval *value, zv } } -ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_fragment_read(void *uri, php_uri_component_read_mode read_mode, zval *retval) +ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_fragment_read(void *uri, const php_uri_component_read_mode read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(uri, read_mode); @@ -513,7 +513,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_fragment_read(v return SUCCESS; } -static zend_result php_uri_parser_rfc3986_fragment_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_rfc3986_fragment_write(void *uri, const zval *value, zval *errors) { UriUriA *uriparser_uri = get_uri_for_writing(uri); int result; @@ -537,7 +537,7 @@ static zend_result php_uri_parser_rfc3986_fragment_write(void *uri, zval *value, } } -static php_uri_parser_rfc3986_uris *uriparser_create_uris(void) +php_uri_parser_rfc3986_uris *uriparser_create_uris(void) { php_uri_parser_rfc3986_uris *uriparser_uris = ecalloc(1, sizeof(*uriparser_uris)); uriparser_uris->normalized_uri_initialized = false; @@ -545,12 +545,35 @@ static php_uri_parser_rfc3986_uris *uriparser_create_uris(void) return uriparser_uris; } -php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_parse_ex(const char *uri_str, size_t uri_str_len, const php_uri_parser_rfc3986_uris *uriparser_base_urls, bool silent) +static zend_result php_uri_parser_rfc3986_add_base_url( + UriUriA *tmp, const UriUriA *uri, const php_uri_parser_rfc3986_uris *uriparser_base_urls, const bool silent +) { + const int result = uriAddBaseUriExMmA(tmp, uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm); + if (result != URI_SUCCESS) { + if (!silent) { + switch (result) { + case URI_ERROR_ADDBASE_REL_BASE: + zend_throw_exception(php_uri_ce_invalid_uri_exception, "The specified base URI must be absolute", 0); + break; + default: + /* This should be unreachable in practice. */ + zend_throw_exception(php_uri_ce_error, "Failed to resolve the specified URI against the base URI", 0); + break; + } + } + + return FAILURE; + } + + return SUCCESS; +} + +php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_parse_ex(const char *uri_str, const size_t uri_str_len, const php_uri_parser_rfc3986_uris *uriparser_base_urls, const bool silent) { UriUriA uri = {0}; /* Parse the URI. */ - int result = uriParseSingleUriExMmA(&uri, uri_str, uri_str + uri_str_len, NULL, mm); + const int result = uriParseSingleUriExMmA(&uri, uri_str, uri_str + uri_str_len, NULL, mm); if (result != URI_SUCCESS) { if (!silent) { switch (result) { @@ -572,20 +595,7 @@ php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_parse_ex(const char *uri_str /* Combine the parsed URI with the base URI and store the result in 'tmp', * since the target and source URLs must be distinct. */ - int result = uriAddBaseUriExMmA(&tmp, &uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm); - if (result != URI_SUCCESS) { - if (!silent) { - switch (result) { - case URI_ERROR_ADDBASE_REL_BASE: - zend_throw_exception(php_uri_ce_invalid_uri_exception, "The specified base URI must be absolute", 0); - break; - default: - /* This should be unreachable in practice. */ - zend_throw_exception(php_uri_ce_error, "Failed to resolve the specified URI against the base URI", 0); - break; - } - } - + if (php_uri_parser_rfc3986_add_base_url(&tmp, &uri, uriparser_base_urls, silent) == FAILURE) { goto fail; } @@ -619,7 +629,7 @@ php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_parse_ex(const char *uri_str return NULL; } -void *php_uri_parser_rfc3986_parse(const char *uri_str, size_t uri_str_len, const void *base_url, zval *errors, bool silent) +static void *php_uri_parser_rfc3986_parse(const char *uri_str, const size_t uri_str_len, const void *base_url, zval *errors, const bool silent) { return php_uri_parser_rfc3986_parse_ex(uri_str, uri_str_len, base_url, silent); } @@ -637,7 +647,7 @@ ZEND_ATTRIBUTE_NONNULL static void *php_uri_parser_rfc3986_clone(void *uri) return new_uriparser_uris; } -ZEND_ATTRIBUTE_NONNULL static zend_string *php_uri_parser_rfc3986_to_string(void *uri, php_uri_recomposition_mode recomposition_mode, bool exclude_fragment) +ZEND_ATTRIBUTE_NONNULL static zend_string *php_uri_parser_rfc3986_to_string(void *uri, const php_uri_recomposition_mode recomposition_mode, const bool exclude_fragment) { php_uri_parser_rfc3986_uris *uriparser_uris = uri; const UriUriA *uriparser_uri; @@ -683,6 +693,190 @@ static void php_uri_parser_rfc3986_destroy(void *uri) efree(uriparser_uris); } +static zend_always_inline zend_result php_uri_parser_rfc3986_validate_component_result(const bool well_formed, const char *component_name) +{ + if (well_formed) { + return SUCCESS; + } + + zend_throw_exception_ex(php_uri_ce_invalid_uri_exception, 0, "The specified %s is malformed", component_name); + return FAILURE; +} + +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_scheme(const zend_string *scheme) +{ + const char *p = ZSTR_VAL(scheme); + const size_t len = ZSTR_LEN(scheme); + const bool well_formed = uriIsWellFormedSchemeA(p, p + len) == URI_TRUE; + + return php_uri_parser_rfc3986_validate_component_result(well_formed, "scheme"); +} + +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_userinfo(const zend_string *userinfo) +{ + const char *p = ZSTR_VAL(userinfo); + const size_t len = ZSTR_LEN(userinfo); + const bool well_formed = uriIsWellFormedUserInfoA(p, p + len) == URI_TRUE; + + return php_uri_parser_rfc3986_validate_component_result(well_formed, "userinfo"); +} + +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_host(const zend_string *host) +{ + const char *p = ZSTR_VAL(host); + const size_t len = ZSTR_LEN(host); + + if (len == 0) { + return SUCCESS; + } + + if (p[0] == '[') { + if (p[len - 1] != ']') { + return php_uri_parser_rfc3986_validate_component_result(false, "host"); + } + + if (len >= 2 && (p[1] == 'v' || p[1] == 'V')) { + return php_uri_parser_rfc3986_validate_component_result( + uriIsWellFormedHostIpFutureA(p + 1, p + len - 1) == URI_SUCCESS, + "host" + ); + } + + return php_uri_parser_rfc3986_validate_component_result( + uriIsWellFormedHostIp6A(p + 1, p + len - 1) == URI_SUCCESS, + "host" + ); + } + + if (uriIsWellFormedHostIp4A(p, p + len) == URI_TRUE) { + return SUCCESS; + } + + return php_uri_parser_rfc3986_validate_component_result( + uriIsWellFormedHostRegNameA(p, p + len) == URI_TRUE, + "host" + ); +} + +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_port(const zend_long port) +{ + zend_string *tmp = zend_long_to_str(port); + const char *p = ZSTR_VAL(tmp); + const size_t len = ZSTR_LEN(tmp); + const bool well_formed = uriIsWellFormedPortA(p, p + len); + zend_string_release(tmp); + + return php_uri_parser_rfc3986_validate_component_result(well_formed, "port"); +} + +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_path(const zend_string *path) +{ + const char *p = ZSTR_VAL(path); + const size_t len = ZSTR_LEN(path); + /* The build() method checks whether the path begins with a "/" when there's a host. + * In order to skip doing the same check, a false hasHost argument is passed to uriIsWellFormedPathA(). */ + const bool well_formed = uriIsWellFormedPathA(p, p + len, /* hasHost */ false); + + return php_uri_parser_rfc3986_validate_component_result(well_formed, "path"); +} + +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_query(const zend_string *query) +{ + const char *p = ZSTR_VAL(query); + const size_t len = ZSTR_LEN(query); + const bool well_formed = uriIsWellFormedQueryA(p, p + len); + + return php_uri_parser_rfc3986_validate_component_result(well_formed, "query"); +} + +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_fragment(const zend_string *fragment) +{ + const char *p = ZSTR_VAL(fragment); + const size_t len = ZSTR_LEN(fragment); + const bool well_formed = uriIsWellFormedFragmentA(p, p + len); + + return php_uri_parser_rfc3986_validate_component_result(well_formed, "fragment"); +} + +ZEND_ATTRIBUTE_NONNULL_ARGS(2,3,4,5,6,7,8) php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_build_from_zval( + const php_uri_parser_rfc3986_uris *uriparser_base_uris, + const zval *scheme, const zval *userinfo, const zval *host, const zval *port, + const zval *path, const zval *query, const zval *fragment +) { + php_uri_parser_rfc3986_uris *uriparser_uris = uriparser_create_uris(); + + if (Z_STRLEN_P(path) > 0) { + /* The first segment of the path must not contain ":" if the URI doesn't contain a scheme */ + if (Z_TYPE_P(scheme) == IS_NULL) { + const char *p = Z_STRVAL_P(path); + while (*p != '\0' && *p != '/') { + if (*p == ':') { + zend_throw_exception(php_uri_ce_invalid_uri_exception, "The path must not begin with \":\" when the URI doesn't contain a scheme", 0); + goto failure; + } + + p++; + } + } + + /* The path must not begin with "//" if the URI doesn't contain a host */ + if (Z_TYPE_P(host) == IS_NULL && zend_string_starts_with_literal(Z_STR_P(path), "//")) { + zend_throw_exception(php_uri_ce_invalid_uri_exception, "The path must not begin with \"//\" when the URI doesn't contain a host", 0); + goto failure; + } + } + + zend_result result = php_uri_parser_rfc3986_scheme_write(uriparser_uris, scheme, NULL); + if (result == FAILURE) { + goto failure; + } + result = php_uri_parser_rfc3986_host_write(uriparser_uris, host, NULL); + if (result == FAILURE) { + goto failure; + } + /* Intentionally writing userinfo after host to avoid error when the userinfo is set but the host is missing */ + result = php_uri_parser_rfc3986_userinfo_write(uriparser_uris, userinfo, NULL); + if (result == FAILURE) { + goto failure; + } + /* Intentionally writing userinfo after host to avoid error when the port is set but the host is missing */ + result = php_uri_parser_rfc3986_port_write(uriparser_uris, port, NULL); + if (result == FAILURE) { + goto failure; + } + result = php_uri_parser_rfc3986_path_write(uriparser_uris, path, NULL); + if (result == FAILURE) { + goto failure; + } + result = php_uri_parser_rfc3986_query_write(uriparser_uris, query, NULL); + if (result == FAILURE) { + goto failure; + } + result = php_uri_parser_rfc3986_fragment_write(uriparser_uris, fragment, NULL); + if (result == FAILURE) { + goto failure; + } + + if (uriparser_base_uris != NULL) { + UriUriA tmp = {0}; + + if (php_uri_parser_rfc3986_add_base_url(&tmp, &uriparser_uris->uri, uriparser_base_uris, false) == FAILURE) { + goto failure; + } + + uriMakeOwnerMmA(&tmp, mm); + uriFreeUriMembersMmA(&uriparser_uris->uri, mm); + uriparser_uris->uri = tmp; + } + + return uriparser_uris; + +failure: + uriFreeUriMembersMmA(&uriparser_uris->uri, mm); + efree(uriparser_uris); + return NULL; +} + PHPAPI const php_uri_parser php_uri_parser_rfc3986 = { .name = PHP_URI_PARSER_RFC3986, .parse = php_uri_parser_rfc3986_parse, diff --git a/ext/uri/uri_parser_rfc3986.h b/ext/uri/uri_parser_rfc3986.h index 633dd72062f2..9a9f0b38a0c4 100644 --- a/ext/uri/uri_parser_rfc3986.h +++ b/ext/uri/uri_parser_rfc3986.h @@ -25,8 +25,22 @@ ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_uri_type_read(php_uri_parser_ ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_host_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval); zend_result php_uri_parser_rfc3986_userinfo_read(php_uri_parser_rfc3986_uris *uri, php_uri_component_read_mode read_mode, zval *retval); -zend_result php_uri_parser_rfc3986_userinfo_write(php_uri_parser_rfc3986_uris *uri, zval *value, zval *errors); +zend_result php_uri_parser_rfc3986_userinfo_write(php_uri_parser_rfc3986_uris *uri, const zval *value, zval *errors); php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_parse_ex(const char *uri_str, size_t uri_str_len, const php_uri_parser_rfc3986_uris *uriparser_base_url, bool silent); +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_scheme(const zend_string *scheme); +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_userinfo(const zend_string *userinfo); +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_host(const zend_string *host); +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_port(zend_long port); +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_path(const zend_string *path); +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_query(const zend_string *query); +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_fragment(const zend_string *fragment); + +ZEND_ATTRIBUTE_NONNULL_ARGS(2,3,4,5,6,7,8) php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_build_from_zval( + const php_uri_parser_rfc3986_uris *uriparser_base_uris, + const zval *scheme, const zval *userinfo, const zval *host, const zval *port, + const zval *path, const zval *query, const zval *fragment +); + #endif diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c index f4e148704004..2a10eba96fa3 100644 --- a/ext/uri/uri_parser_whatwg.c +++ b/ext/uri/uri_parser_whatwg.c @@ -28,7 +28,7 @@ ZEND_TLS lxb_unicode_idna_t lexbor_idna = {0}; static const size_t lexbor_mraw_byte_size = 8192; -static zend_always_inline void zval_string_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str) +ZEND_ATTRIBUTE_NONNULL static zend_always_inline void zval_string_or_null_to_lexbor_str(const 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); @@ -40,12 +40,12 @@ static zend_always_inline void zval_string_or_null_to_lexbor_str(zval *value, le } } -static zend_always_inline void zval_long_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str) +ZEND_ATTRIBUTE_NONNULL static zend_always_inline void zval_long_or_null_to_lexbor_str(const 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); + zend_string *tmp = zend_long_to_str(Z_LVAL_P(value)); + lexbor_str_init_append(lexbor_str, lexbor_parser.mraw, (const lxb_char_t *) ZSTR_VAL(tmp), ZSTR_LEN(tmp)); + zend_string_release(tmp); } else { ZEND_ASSERT(Z_ISNULL_P(value)); lexbor_str->data = (lxb_char_t *) ""; @@ -58,15 +58,8 @@ static zend_always_inline void zval_long_or_null_to_lexbor_str(zval *value, lexb * https://url.spec.whatwg.org/#writing to a Uri\WhatWg\UrlValidationErrorType enum. * The result is passed by reference to the errors parameter. */ -static const char *fill_errors(zval *errors) +ZEND_ATTRIBUTE_NONNULL static const char *fill_errors_inner(zval *errors) { - size_t log_len; - if (lexbor_parser.log == NULL || (log_len = lexbor_plog_length(lexbor_parser.log)) == 0) { - ZVAL_EMPTY_ARRAY(errors); - return NULL; - } - - array_init_size(errors, log_len); const char *result = NULL; lexbor_plog_entry_t *lxb_error; @@ -213,6 +206,24 @@ static const char *fill_errors(zval *errors) return result; } +/** + * Creates a Uri\WhatWg\UrlValidationError class by mapping error codes listed in + * https://url.spec.whatwg.org/#writing to a Uri\WhatWg\UrlValidationErrorType enum. + * The result is passed by reference to the errors parameter. + */ +ZEND_ATTRIBUTE_NONNULL static const char *fill_errors(zval *errors) +{ + size_t log_len; + if (lexbor_parser.log == NULL || (log_len = lexbor_plog_length(lexbor_parser.log)) == 0) { + ZVAL_EMPTY_ARRAY(errors); + return NULL; + } + + array_init_size(errors, log_len); + + return fill_errors_inner(errors); +} + static void throw_invalid_url_exception_during_write(zval *errors, const char *component) { zval err; @@ -257,13 +268,15 @@ static zend_result php_uri_parser_whatwg_scheme_read(void *uri, php_uri_componen return SUCCESS; } -static zend_result php_uri_parser_whatwg_scheme_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_whatwg_scheme_write(void *uri, const zval *value, zval *errors) { lxb_url_t *lexbor_uri = uri; lexbor_str_t str = {0}; zval_string_or_null_to_lexbor_str(value, &str); + lxb_url_parser_clean(&lexbor_parser); + if (lxb_url_api_protocol_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { throw_invalid_url_exception_during_write(errors, "scheme"); @@ -297,13 +310,15 @@ static zend_result php_uri_parser_whatwg_username_read(void *uri, php_uri_compon return SUCCESS; } -static zend_result php_uri_parser_whatwg_username_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_whatwg_username_write(void *uri, const zval *value, zval *errors) { lxb_url_t *lexbor_uri = uri; lexbor_str_t str = {0}; zval_string_or_null_to_lexbor_str(value, &str); + lxb_url_parser_clean(&lexbor_parser); + if (lxb_url_api_username_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { throw_invalid_url_exception_during_write(errors, "username"); @@ -326,13 +341,15 @@ static zend_result php_uri_parser_whatwg_password_read(void *uri, php_uri_compon return SUCCESS; } -static zend_result php_uri_parser_whatwg_password_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_whatwg_password_write(void *uri, const zval *value, zval *errors) { lxb_url_t *lexbor_uri = uri; lexbor_str_t str = {0}; zval_string_or_null_to_lexbor_str(value, &str); + lxb_url_parser_clean(&lexbor_parser); + if (lxb_url_api_password_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { throw_invalid_url_exception_during_write(errors, "password"); @@ -411,13 +428,15 @@ ZEND_ATTRIBUTE_NONNULL void php_uri_parser_whatwg_host_type_read(const lxb_url_t } } -static zend_result php_uri_parser_whatwg_host_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_whatwg_host_write(void *uri, const zval *value, zval *errors) { lxb_url_t *lexbor_uri = uri; lexbor_str_t str = {0}; zval_string_or_null_to_lexbor_str(value, &str); + lxb_url_parser_clean(&lexbor_parser); + if (lxb_url_api_hostname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { throw_invalid_url_exception_during_write(errors, "host"); @@ -440,13 +459,15 @@ static zend_result php_uri_parser_whatwg_port_read(void *uri, php_uri_component_ return SUCCESS; } -static zend_result php_uri_parser_whatwg_port_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_whatwg_port_write(void *uri, const zval *value, zval *errors) { lxb_url_t *lexbor_uri = uri; lexbor_str_t str = {0}; zval_long_or_null_to_lexbor_str(value, &str); + lxb_url_parser_clean(&lexbor_parser); + if (lxb_url_api_port_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { throw_invalid_url_exception_during_write(errors, "port"); @@ -469,13 +490,15 @@ static zend_result php_uri_parser_whatwg_path_read(void *uri, php_uri_component_ return SUCCESS; } -static zend_result php_uri_parser_whatwg_path_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_whatwg_path_write(void *uri, const zval *value, zval *errors) { lxb_url_t *lexbor_uri = uri; lexbor_str_t str = {0}; zval_string_or_null_to_lexbor_str(value, &str); + lxb_url_parser_clean(&lexbor_parser); + if (lxb_url_api_pathname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { throw_invalid_url_exception_during_write(errors, "path"); @@ -498,13 +521,15 @@ static zend_result php_uri_parser_whatwg_query_read(void *uri, php_uri_component return SUCCESS; } -static zend_result php_uri_parser_whatwg_query_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_whatwg_query_write(void *uri, const zval *value, zval *errors) { lxb_url_t *lexbor_uri = uri; lexbor_str_t str = {0}; zval_string_or_null_to_lexbor_str(value, &str); + lxb_url_parser_clean(&lexbor_parser); + if (lxb_url_api_search_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { throw_invalid_url_exception_during_write(errors, "query string"); @@ -527,13 +552,15 @@ static zend_result php_uri_parser_whatwg_fragment_read(void *uri, php_uri_compon return SUCCESS; } -static zend_result php_uri_parser_whatwg_fragment_write(void *uri, zval *value, zval *errors) +static zend_result php_uri_parser_whatwg_fragment_write(void *uri, const zval *value, zval *errors) { lxb_url_t *lexbor_uri = uri; lexbor_str_t str = {0}; zval_string_or_null_to_lexbor_str(value, &str); + lxb_url_parser_clean(&lexbor_parser); + if (lxb_url_api_hash_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { throw_invalid_url_exception_during_write(errors, "fragment"); @@ -590,7 +617,7 @@ ZEND_MODULE_POST_ZEND_DEACTIVATE_D(uri_parser_whatwg) return SUCCESS; } -lxb_url_t *php_uri_parser_whatwg_parse_ex(const char *uri_str, size_t uri_str_len, const lxb_url_t *lexbor_base_url, zval *errors, bool silent) +lxb_url_t *php_uri_parser_whatwg_parse_ex(const char *uri_str, const size_t uri_str_len, const lxb_url_t *lexbor_base_url, zval *errors, const bool silent) { lxb_url_parser_clean(&lexbor_parser); @@ -656,6 +683,206 @@ static void php_uri_parser_whatwg_destroy(void *uri) lxb_url_destroy(lexbor_uri); } +static zend_always_inline zend_result php_uri_parser_whatwg_validate_component_result(const bool well_formed, const char *component_name) +{ + if (well_formed) { + return SUCCESS; + } + + zend_throw_exception_ex(php_uri_ce_whatwg_invalid_url_exception, 0, "The specified %s is malformed", component_name); + return FAILURE; +} + +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_whatwg_validate_none(const zend_string *component) +{ + return SUCCESS; +} + +static zend_always_inline bool php_uri_parser_whatwg_is_alpha(unsigned char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static zend_always_inline bool php_uri_parser_whatwg_is_digit(unsigned char c) +{ + return c >= '0' && c <= '9'; +} + +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_whatwg_validate_scheme(const zend_string *scheme) +{ + const char *p = ZSTR_VAL(scheme); + const size_t len = ZSTR_LEN(scheme); + bool seen_first = false; + + for (size_t i = 0; i < len; i++) { + const unsigned char c = (unsigned char)p[i]; + + if (c == '\t' || c == '\n' || c == '\r') { + continue; + } + + if (!seen_first) { + if (!php_uri_parser_whatwg_is_alpha(c)) { + return php_uri_parser_whatwg_validate_component_result(false, "scheme"); + } + seen_first = true; + } else { + if (!php_uri_parser_whatwg_is_alpha(c) && !php_uri_parser_whatwg_is_digit(c) && c != '+' && c != '-' && c != '.') { + return php_uri_parser_whatwg_validate_component_result(false, "scheme"); + } + } + } + + if (!seen_first) { + return php_uri_parser_whatwg_validate_component_result(false, "scheme"); + } + + return true; +} + +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_whatwg_validate_port(const zend_long port) +{ + const bool well_formed = port >= 0 && port <= 65535; + + return php_uri_parser_whatwg_validate_component_result(well_formed, "port"); +} + +static bool php_uri_parser_whatwg_is_special_scheme(const zval *scheme) +{ + ZEND_ASSERT(Z_TYPE_P(scheme) == IS_STRING); + + const zend_string *str = Z_STR_P(scheme); + + switch (ZSTR_LEN(str)) { + case 2: + return zend_string_equals_literal_ci(str, "ws"); + case 3: + return zend_string_equals_literal_ci(str, "ftp") + || zend_string_equals_literal_ci(str, "wss"); + case 4: + return zend_string_equals_literal_ci(str, "http") + || zend_string_equals_literal_ci(str, "file"); + case 5: + return zend_string_equals_literal_ci(str, "https"); + default: + return false; + } +} + +static void php_uri_parser_whatwg_build_errors(zval *errors) +{ + if (errors == NULL) { + return; + } + + size_t log_len; + + if (lexbor_parser.log == NULL || (log_len = lexbor_plog_length(lexbor_parser.log)) == 0) { + return; + } + + if (Z_TYPE_P(errors) != IS_ARRAY) { + zval_ptr_dtor(errors); + ZVAL_EMPTY_ARRAY(errors); + array_init_size(errors, log_len); + } + + fill_errors_inner(errors); +} + +ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_whatwg_build_from_zval( + lxb_url_t *lexbor_base_url, const zval *scheme, const zval *username, const zval *password, + const zval *host, const zval *port, const zval *path, const zval *query, const zval *fragment, + zval *errors_zv +) { + lxb_url_parser_clean(&lexbor_parser); + + lxb_url_t *lexbor_url = lexbor_mraw_calloc(lexbor_parser.mraw, sizeof(lxb_url_t)); + if (lexbor_url == NULL) { + zend_throw_exception(php_uri_ce_whatwg_invalid_url_exception, "Memory allocation error", 0); + return NULL; + } + + lexbor_url->mraw = lexbor_parser.mraw; + + /* + * The URL is initialized as LXB_URL_SCHEMEL_TYPE__UNDEF but this would prevent from the scheme to be updated + * in case of non-special schemes due to https://github.com/php/php-src/blob/27d7b799c0a13578ee0506b428b8ddc209ffb010/ext/lexbor/lexbor/url/url.c#L1402 + */ + if (php_uri_parser_whatwg_is_special_scheme(scheme)) { + lexbor_url->scheme.type = LXB_URL_SCHEMEL_TYPE__UNDEF; + } else { + lexbor_url->scheme.type = LXB_URL_SCHEMEL_TYPE__UNKNOWN; + } + + zval errors; + ZVAL_UNDEF(&errors); + + zend_result result = php_uri_parser_whatwg_scheme_write(lexbor_url, scheme, NULL); + php_uri_parser_whatwg_build_errors(&errors); + if (result == FAILURE) { + goto failure; + } + + result = php_uri_parser_whatwg_host_write(lexbor_url, host, NULL); + php_uri_parser_whatwg_build_errors(&errors); + if (result == FAILURE) { + goto failure; + } + + /* Intentionally writing username after host to avoid error when the username is set but the host is missing */ + result = php_uri_parser_whatwg_username_write(lexbor_url, username, NULL); + php_uri_parser_whatwg_build_errors(&errors); + if (result == FAILURE) { + goto failure; + } + + /* Intentionally writing password after host to avoid error when the password is set but the password is missing */ + result = php_uri_parser_whatwg_password_write(lexbor_url, password, NULL); + php_uri_parser_whatwg_build_errors(&errors); + if (result == FAILURE) { + goto failure; + } + + /* Intentionally writing port after host to avoid error when the port is set but the host is missing */ + result = php_uri_parser_whatwg_port_write(lexbor_url, port, NULL); + php_uri_parser_whatwg_build_errors(&errors); + if (result == FAILURE) { + goto failure; + } + + result = php_uri_parser_whatwg_path_write(lexbor_url, path, NULL); + php_uri_parser_whatwg_build_errors(&errors); + if (result == FAILURE) { + goto failure; + } + result = php_uri_parser_whatwg_query_write(lexbor_url, query, NULL); + php_uri_parser_whatwg_build_errors(&errors); + if (result == FAILURE) { + goto failure; + } + result = php_uri_parser_whatwg_fragment_write(lexbor_url, fragment, NULL); + php_uri_parser_whatwg_build_errors(&errors); + if (result == FAILURE) { + goto failure; + } + + if (lexbor_base_url != NULL) { + /* TODO */ + } + + if (php_uri_pass_errors_by_ref_and_free(errors_zv, &errors) == FAILURE) { + goto failure; + } + + return lexbor_url; + +failure: + zval_ptr_dtor(&errors); + lxb_url_destroy(lexbor_url); + return NULL; +} + PHPAPI const php_uri_parser php_uri_parser_whatwg = { .name = PHP_URI_PARSER_WHATWG, .parse = php_uri_parser_whatwg_parse, diff --git a/ext/uri/uri_parser_whatwg.h b/ext/uri/uri_parser_whatwg.h index f714ee483680..8d9ea5d4b242 100644 --- a/ext/uri/uri_parser_whatwg.h +++ b/ext/uri/uri_parser_whatwg.h @@ -25,7 +25,18 @@ ZEND_ATTRIBUTE_NONNULL void php_uri_parser_whatwg_host_type_read(const lxb_url_t lxb_url_t *php_uri_parser_whatwg_parse_ex(const char *uri_str, size_t uri_str_len, const lxb_url_t *lexbor_base_url, zval *errors, bool silent); +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_whatwg_validate_none(const zend_string *component); +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_whatwg_validate_scheme(const zend_string *scheme); +ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_whatwg_validate_port(zend_long port); + PHP_RINIT_FUNCTION(uri_parser_whatwg); + +ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_whatwg_build_from_zval( + lxb_url_t *lexbor_base_url, const zval *scheme, const zval *username, const zval *password, + const zval *host, const zval *port, const zval *path, const zval *query, const zval *fragment, + zval *errors_zv +); + ZEND_MODULE_POST_ZEND_DEACTIVATE_D(uri_parser_whatwg); #endif