Commit d8a5aec1cc9 for php.net
commit d8a5aec1cc93e0fd41e8641091c32aace2a73059
Author: Máté Kocsis <kocsismate@woohoolabs.com>
Date: Sun May 24 23:54:03 2026 +0200
Implement "Followup improvements for ext/uri" RFC - Host type detection (#22100)
RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection
diff --git a/NEWS b/NEWS
index bb873c2619d..474698303b5 100644
--- a/NEWS
+++ b/NEWS
@@ -244,6 +244,8 @@ PHP NEWS
- URI:
. Added Uri\Rfc3986\Uri:getUriType() and Uri\WhatWg\Url:isSpecialScheme().
(kocsismate)
+ . Added Uri\Rfc3986\Uri:getHostType() and Uri\WhatWg\Url:getHostType().
+ (kocsismate)
- Zip:
. Fixed ZipArchive callback being called after executor has shut down.
diff --git a/UPGRADING b/UPGRADING
index 38858a27ea9..d9bf1fc79ac 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -214,6 +214,8 @@ PHP 8.6 UPGRADE NOTES
- URI:
. Added Uri\Rfc3986\Uri:getUriType() and Uri\WhatWg\Url:isSpecialScheme().
RFC: https://wiki.php.net/rfc/uri_followup#uri_type_detection
+ . Added Uri\Rfc3986\Uri:getHostType() and Uri\WhatWg\Url:getHostType().
+ RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection
========================================
3. Changes in SAPI modules
diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c
index 007517ae04d..58f34a37015 100644
--- a/ext/uri/php_uri.c
+++ b/ext/uri/php_uri.c
@@ -32,11 +32,13 @@
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;
zend_class_entry *php_uri_ce_comparison_mode;
zend_class_entry *php_uri_ce_exception;
zend_class_entry *php_uri_ce_error;
zend_class_entry *php_uri_ce_invalid_uri_exception;
+zend_class_entry *php_uri_ce_whatwg_url_host_type;
zend_class_entry *php_uri_ce_whatwg_invalid_url_exception;
zend_class_entry *php_uri_ce_whatwg_url_validation_error_type;
zend_class_entry *php_uri_ce_whatwg_url_validation_error;
@@ -622,6 +624,16 @@ PHP_METHOD(Uri_Rfc3986_Uri, getRawHost)
php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_HOST, PHP_URI_COMPONENT_READ_MODE_RAW);
}
+PHP_METHOD(Uri_Rfc3986_Uri, getHostType)
+{
+ ZEND_PARSE_PARAMETERS_NONE();
+
+ php_uri_object *uri_object = Z_URI_OBJECT_P(ZEND_THIS);
+ ZEND_ASSERT(uri_object->uri != NULL);
+
+ php_uri_parser_rfc3986_host_type_read(uri_object->uri, return_value);
+}
+
PHP_METHOD(Uri_Rfc3986_Uri, withHost)
{
php_uri_property_write_str_or_null_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_HOST);
@@ -924,6 +936,16 @@ PHP_METHOD(Uri_WhatWg_Url, getUnicodeHost)
php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_HOST, PHP_URI_COMPONENT_READ_MODE_NORMALIZED_UNICODE);
}
+PHP_METHOD(Uri_WhatWg_Url, getHostType)
+{
+ ZEND_PARSE_PARAMETERS_NONE();
+
+ php_uri_object *uri_object = Z_URI_OBJECT_P(ZEND_THIS);
+ ZEND_ASSERT(uri_object->uri != NULL);
+
+ php_uri_parser_whatwg_host_type_read(uri_object->uri, return_value);
+}
+
PHP_METHOD(Uri_WhatWg_Url, getFragment)
{
php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_FRAGMENT, PHP_URI_COMPONENT_READ_MODE_NORMALIZED_UNICODE);
@@ -1100,6 +1122,7 @@ static PHP_MINIT_FUNCTION(uri)
object_handlers_rfc3986_uri.clone_obj = php_uri_object_handler_clone;
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 = register_class_Uri_WhatWg_Url();
php_uri_ce_whatwg_url->create_object = php_uri_object_create_whatwg;
@@ -1114,6 +1137,7 @@ static PHP_MINIT_FUNCTION(uri)
php_uri_ce_error = register_class_Uri_UriError(zend_ce_error);
php_uri_ce_invalid_uri_exception = register_class_Uri_InvalidUriException(php_uri_ce_exception);
php_uri_ce_whatwg_invalid_url_exception = register_class_Uri_WhatWg_InvalidUrlException(php_uri_ce_invalid_uri_exception);
+ php_uri_ce_whatwg_url_host_type = register_class_Uri_WhatWg_UrlHostType();
php_uri_ce_whatwg_url_validation_error = register_class_Uri_WhatWg_UrlValidationError();
php_uri_ce_whatwg_url_validation_error_type = register_class_Uri_WhatWg_UrlValidationErrorType();
diff --git a/ext/uri/php_uri.stub.php b/ext/uri/php_uri.stub.php
index 03e8a483b0b..b0b83fcf83e 100644
--- a/ext/uri/php_uri.stub.php
+++ b/ext/uri/php_uri.stub.php
@@ -37,6 +37,14 @@ enum UriType
case Uri;
}
+ enum UriHostType
+ {
+ case IPv4;
+ case IPv6;
+ case IPvFuture;
+ case RegisteredName;
+ }
+
/** @strict-properties */
final readonly class Uri
{
@@ -70,6 +78,8 @@ public function getHost(): ?string {}
public function getRawHost(): ?string {}
+ public function getHostType(): ?\Uri\Rfc3986\UriHostType {}
+
public function withHost(?string $host): static {}
public function getPort(): ?int {}
@@ -162,6 +172,15 @@ enum UrlValidationErrorType
public function __construct(string $context, \Uri\WhatWg\UrlValidationErrorType $type, bool $failure) {}
}
+ enum UrlHostType
+ {
+ case IPv4;
+ case IPv6;
+ case Domain;
+ case Opaque;
+ case Empty;
+ }
+
/** @strict-properties */
final readonly class Url
{
@@ -191,6 +210,8 @@ public function getAsciiHost(): ?string {}
public function getUnicodeHost(): ?string {}
+ public function getHostType(): ?\Uri\WhatWg\UrlHostType {}
+
/** @implementation-alias Uri\Rfc3986\Uri::withHost */
public function withHost(?string $host): static {}
diff --git a/ext/uri/php_uri_arginfo.h b/ext/uri/php_uri_arginfo.h
index e88d439fa16..0fb464ee74a 100644
Binary files a/ext/uri/php_uri_arginfo.h and b/ext/uri/php_uri_arginfo.h differ
diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h
index 4c0ac466da7..c9f5e81075f 100644
--- a/ext/uri/php_uri_common.h
+++ b/ext/uri/php_uri_common.h
@@ -19,6 +19,7 @@
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;
extern zend_class_entry *php_uri_ce_comparison_mode;
extern zend_class_entry *php_uri_ce_exception;
@@ -27,6 +28,7 @@ extern zend_class_entry *php_uri_ce_invalid_uri_exception;
extern zend_class_entry *php_uri_ce_whatwg_invalid_url_exception;
extern zend_class_entry *php_uri_ce_whatwg_url_validation_error_type;
extern zend_class_entry *php_uri_ce_whatwg_url_validation_error;
+extern zend_class_entry *php_uri_ce_whatwg_url_host_type;
typedef enum php_uri_recomposition_mode {
PHP_URI_RECOMPOSITION_MODE_RAW_ASCII,
diff --git a/ext/uri/php_uri_decl.h b/ext/uri/php_uri_decl.h
index 947858515db..d1fd58d04b2 100644
Binary files a/ext/uri/php_uri_decl.h and b/ext/uri/php_uri_decl.h differ
diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_ip_future.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_ip_future.phpt
new file mode 100644
index 00000000000..0ac7ea3f7ef
--- /dev/null
+++ b/ext/uri/tests/rfc3986/getters/host_type_success_ip_future.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\Rfc3986\Uri component retrieval - host type - IP future
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://[vF.addr]");
+
+var_dump($uri->getHostType());
+
+?>
+--EXPECT--
+enum(Uri\Rfc3986\UriHostType::IPvFuture)
diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_ipv4.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_ipv4.phpt
new file mode 100644
index 00000000000..1c0c4448b95
--- /dev/null
+++ b/ext/uri/tests/rfc3986/getters/host_type_success_ipv4.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\Rfc3986\Uri component retrieval - host type - IPv4
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://192.168.0.1");
+
+var_dump($uri->getHostType());
+
+?>
+--EXPECT--
+enum(Uri\Rfc3986\UriHostType::IPv4)
diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_ipv6.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_ipv6.phpt
new file mode 100644
index 00000000000..5f3d826b412
--- /dev/null
+++ b/ext/uri/tests/rfc3986/getters/host_type_success_ipv6.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\Rfc3986\Uri component retrieval - host type - IPv6
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://[2001:0db8:3333:4444:5555:6666:7777:8888]");
+
+var_dump($uri->getHostType());
+
+?>
+--EXPECT--
+enum(Uri\Rfc3986\UriHostType::IPv6)
diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_none.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_none.phpt
new file mode 100644
index 00000000000..3a0e0205bef
--- /dev/null
+++ b/ext/uri/tests/rfc3986/getters/host_type_success_none.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\Rfc3986\Uri component retrieval - host type - none
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("/foo/bar");
+
+var_dump($uri->getHostType());
+
+?>
+--EXPECT--
+NULL
diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_registered_name.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_registered_name.phpt
new file mode 100644
index 00000000000..31d4aca36c8
--- /dev/null
+++ b/ext/uri/tests/rfc3986/getters/host_type_success_registered_name.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\Rfc3986\Uri component retrieval - host type - registered name
+--FILE--
+<?php
+
+$uri = Uri\Rfc3986\Uri::parse("https://example.com");
+
+var_dump($uri->getHostType());
+
+?>
+--EXPECT--
+enum(Uri\Rfc3986\UriHostType::RegisteredName)
diff --git a/ext/uri/tests/whatwg/getters/host_type_success_domain.phpt b/ext/uri/tests/whatwg/getters/host_type_success_domain.phpt
new file mode 100644
index 00000000000..21fc961e554
--- /dev/null
+++ b/ext/uri/tests/whatwg/getters/host_type_success_domain.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\WhatWg\Url component retrieval - host type - registered name
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("https://example.com");
+
+var_dump($url->getHostType());
+
+?>
+--EXPECT--
+enum(Uri\WhatWg\UrlHostType::Domain)
diff --git a/ext/uri/tests/whatwg/getters/host_type_success_empty.phpt b/ext/uri/tests/whatwg/getters/host_type_success_empty.phpt
new file mode 100644
index 00000000000..41351350fb6
--- /dev/null
+++ b/ext/uri/tests/whatwg/getters/host_type_success_empty.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\WhatWg\Url component retrieval - host type - empty
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("file:///foo/bar");
+
+var_dump($url->getHostType());
+
+?>
+--EXPECT--
+enum(Uri\WhatWg\UrlHostType::Empty)
diff --git a/ext/uri/tests/whatwg/getters/host_type_success_ipv4.phpt b/ext/uri/tests/whatwg/getters/host_type_success_ipv4.phpt
new file mode 100644
index 00000000000..f2f63caabb5
--- /dev/null
+++ b/ext/uri/tests/whatwg/getters/host_type_success_ipv4.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\WhatWg\Url component retrieval - host type - IPv4
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("https://192.168.0.1");
+
+var_dump($url->getHostType());
+
+?>
+--EXPECT--
+enum(Uri\WhatWg\UrlHostType::IPv4)
diff --git a/ext/uri/tests/whatwg/getters/host_type_success_ipv6.phpt b/ext/uri/tests/whatwg/getters/host_type_success_ipv6.phpt
new file mode 100644
index 00000000000..ce5bb00b93d
--- /dev/null
+++ b/ext/uri/tests/whatwg/getters/host_type_success_ipv6.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\WhatWg\Url component retrieval - host type - IPv6
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("https://[2001:0db8:3333:4444:5555:6666:7777:8888]");
+
+var_dump($url->getHostType());
+
+?>
+--EXPECT--
+enum(Uri\WhatWg\UrlHostType::IPv6)
diff --git a/ext/uri/tests/whatwg/getters/host_type_success_none.phpt b/ext/uri/tests/whatwg/getters/host_type_success_none.phpt
new file mode 100644
index 00000000000..07d54412a23
--- /dev/null
+++ b/ext/uri/tests/whatwg/getters/host_type_success_none.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\WhatWg\Url component retrieval - host type - none
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("email:john.doe@example.com");
+
+var_dump($url->getHostType());
+
+?>
+--EXPECT--
+NULL
diff --git a/ext/uri/tests/whatwg/getters/host_type_success_opaque.phpt b/ext/uri/tests/whatwg/getters/host_type_success_opaque.phpt
new file mode 100644
index 00000000000..844cba82d51
--- /dev/null
+++ b/ext/uri/tests/whatwg/getters/host_type_success_opaque.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Test Uri\WhatWg\Url component retrieval - host type - opaque
+--FILE--
+<?php
+
+$url = Uri\WhatWg\Url::parse("scheme://example.com");
+
+var_dump($url->getHostType());
+
+?>
+--EXPECT--
+enum(Uri\WhatWg\UrlHostType::Opaque)
diff --git a/ext/uri/uri_parser_rfc3986.c b/ext/uri/uri_parser_rfc3986.c
index cdf88013252..ad47aa1946c 100644
--- a/ext/uri/uri_parser_rfc3986.c
+++ b/ext/uri/uri_parser_rfc3986.c
@@ -271,6 +271,30 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_host_read(void
return SUCCESS;
}
+ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_host_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval)
+{
+ const UriUriA *uriparser_uri = get_uri_for_reading(uri, PHP_URI_COMPONENT_READ_MODE_RAW);
+
+ if (!has_text_range(&uriparser_uri->hostText)) {
+ ZVAL_NULL(retval);
+ return;
+ }
+
+ const char *type;
+
+ if (uriparser_uri->hostData.ip4 != NULL) {
+ type = "IPv4";
+ } else if (uriparser_uri->hostData.ip6 != NULL) {
+ type = "IPv6";
+ } else if (has_text_range(&uriparser_uri->hostData.ipFuture)) {
+ type = "IPvFuture";
+ } else {
+ type = "RegisteredName";
+ }
+
+ 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)
{
UriUriA *uriparser_uri = get_uri_for_writing(uri);
diff --git a/ext/uri/uri_parser_rfc3986.h b/ext/uri/uri_parser_rfc3986.h
index bf8a0733fc8..633dd72062f 100644
--- a/ext/uri/uri_parser_rfc3986.h
+++ b/ext/uri/uri_parser_rfc3986.h
@@ -22,6 +22,7 @@ PHPAPI extern const php_uri_parser php_uri_parser_rfc3986;
typedef struct php_uri_parser_rfc3986_uris php_uri_parser_rfc3986_uris;
ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_uri_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval);
+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);
diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c
index 4ca7073847e..f4e14870400 100644
--- a/ext/uri/uri_parser_whatwg.c
+++ b/ext/uri/uri_parser_whatwg.c
@@ -386,6 +386,31 @@ static zend_result php_uri_parser_whatwg_host_read(void *uri, php_uri_component_
return SUCCESS;
}
+ZEND_ATTRIBUTE_NONNULL void php_uri_parser_whatwg_host_type_read(const lxb_url_t *lexbor_uri, zval *retval)
+{
+ switch (lexbor_uri->host.type) {
+ case LXB_URL_HOST_TYPE_IPV4:
+ ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "IPv4"));
+ return;
+ case LXB_URL_HOST_TYPE_IPV6:
+ ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "IPv6"));
+ return;
+ case LXB_URL_HOST_TYPE_DOMAIN:
+ ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "Domain"));
+ return;
+ case LXB_URL_HOST_TYPE_EMPTY:
+ ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "Empty"));
+ return;
+ case LXB_URL_HOST_TYPE_OPAQUE:
+ ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "Opaque"));
+ return;
+ case LXB_URL_HOST_TYPE__UNDEF:
+ ZVAL_NULL(retval);
+ return;
+ default: ZEND_UNREACHABLE();
+ }
+}
+
static zend_result php_uri_parser_whatwg_host_write(void *uri, zval *value, zval *errors)
{
lxb_url_t *lexbor_uri = uri;
diff --git a/ext/uri/uri_parser_whatwg.h b/ext/uri/uri_parser_whatwg.h
index 1ed2b4ac0e1..f714ee48368 100644
--- a/ext/uri/uri_parser_whatwg.h
+++ b/ext/uri/uri_parser_whatwg.h
@@ -21,6 +21,7 @@
PHPAPI extern const php_uri_parser php_uri_parser_whatwg;
ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_whatwg_is_special(const lxb_url_t *lexbor_uri);
+ZEND_ATTRIBUTE_NONNULL void php_uri_parser_whatwg_host_type_read(const lxb_url_t *lexbor_uri, zval *retval);
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);