Skip to content

Commit 3f285f4

Browse files
authored
Implement "Followup improvements for ext/uri" RFC - RFC 3986 URI building (#22173)
RFC: https://wiki.php.net/rfc/uri_followup#uri_building
1 parent 60aab0f commit 3f285f4

50 files changed

Lines changed: 1679 additions & 44 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ PHP NEWS
299299
(kocsismate)
300300
. Added Uri\Rfc3986\Uri:getHostType() and Uri\WhatWg\Url:getHostType().
301301
(kocsismate)
302+
. Added Uri\Rfc3986\UriBuilder.
303+
(kocsismate)
302304

303305
- Zip:
304306
. Fixed ZipArchive callback being called after executor has shut down.

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ PHP 8.6 UPGRADE NOTES
254254
RFC: https://wiki.php.net/rfc/uri_followup#uri_type_detection
255255
. Added Uri\Rfc3986\Uri:getHostType() and Uri\WhatWg\Url:getHostType().
256256
RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection
257+
. Added Uri\Rfc3986\UriBuilder.
258+
RFC: https://wiki.php.net/rfc/uri_followup#uri_building
257259

258260
========================================
259261
3. Changes in SAPI modules

ext/uri/php_uri.c

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "php_uri_arginfo.h"
3131
#include "uriparser/Uri.h"
3232

33+
zend_class_entry *php_uri_ce_rfc3986_uri_builder;
3334
zend_class_entry *php_uri_ce_rfc3986_uri;
3435
zend_class_entry *php_uri_ce_rfc3986_uri_type;
3536
zend_class_entry *php_uri_ce_rfc3986_uri_host_type;
@@ -46,13 +47,33 @@ zend_class_entry *php_uri_ce_whatwg_url_validation_error;
4647
static zend_object_handlers object_handlers_rfc3986_uri;
4748
static zend_object_handlers object_handlers_whatwg_uri;
4849

50+
typedef zend_result (*php_uri_component_validator_string)(const zend_string *component);
51+
typedef zend_result (*php_uri_component_validator_long)(zend_long component);
52+
4953
static const zend_module_dep uri_deps[] = {
5054
ZEND_MOD_REQUIRED("lexbor")
5155
ZEND_MOD_END
5256
};
5357

5458
static zend_array uri_parsers;
5559

60+
static zend_always_inline zval *php_uri_deref(zval *zv)
61+
{
62+
if (UNEXPECTED(Z_TYPE_P(zv) == IS_REFERENCE)) {
63+
return Z_REFVAL_P(zv);
64+
}
65+
66+
return zv;
67+
}
68+
69+
#define Z_RFC3986_URI_PROP_SCHEME_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 0))
70+
#define Z_RFC3986_URI_PROP_USERINFO_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 1))
71+
#define Z_RFC3986_URI_PROP_HOST_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 2))
72+
#define Z_RFC3986_URI_PROP_PORT_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 3))
73+
#define Z_RFC3986_URI_PROP_PATH_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 4))
74+
#define Z_RFC3986_URI_PROP_QUERY_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 5))
75+
#define Z_RFC3986_URI_PROP_FRAGMENT_P(zv) php_uri_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 6))
76+
5677
static HashTable *uri_get_debug_properties(php_uri_object *object)
5778
{
5879
const HashTable *std_properties = zend_std_get_properties(&object->std);
@@ -1044,6 +1065,186 @@ PHP_METHOD(Uri_WhatWg_Url, __debugInfo)
10441065
RETURN_ARR(uri_get_debug_properties(uri_object));
10451066
}
10461067

1068+
PHP_METHOD(Uri_Rfc3986_UriBuilder, reset)
1069+
{
1070+
ZEND_PARSE_PARAMETERS_NONE();
1071+
1072+
convert_to_null(Z_RFC3986_URI_PROP_SCHEME_P(ZEND_THIS));
1073+
convert_to_null(Z_RFC3986_URI_PROP_USERINFO_P(ZEND_THIS));
1074+
convert_to_null(Z_RFC3986_URI_PROP_HOST_P(ZEND_THIS));
1075+
convert_to_null(Z_RFC3986_URI_PROP_PORT_P(ZEND_THIS));
1076+
zval_ptr_dtor(Z_RFC3986_URI_PROP_PATH_P(ZEND_THIS));
1077+
ZVAL_EMPTY_STRING(Z_RFC3986_URI_PROP_PATH_P(ZEND_THIS));
1078+
convert_to_null(Z_RFC3986_URI_PROP_QUERY_P(ZEND_THIS));
1079+
convert_to_null(Z_RFC3986_URI_PROP_FRAGMENT_P(ZEND_THIS));
1080+
1081+
RETVAL_COPY(ZEND_THIS);
1082+
}
1083+
1084+
ZEND_ATTRIBUTE_NONNULL static void php_uri_builder_set_component_string(
1085+
INTERNAL_FUNCTION_PARAMETERS, const char *name, const size_t name_length,
1086+
const php_uri_component_validator_string validator
1087+
) {
1088+
zend_string *component;
1089+
1090+
ZEND_PARSE_PARAMETERS_START(1, 1)
1091+
Z_PARAM_STR(component)
1092+
ZEND_PARSE_PARAMETERS_END();
1093+
1094+
if (validator(component) == FAILURE) {
1095+
RETURN_THROWS();
1096+
}
1097+
1098+
zend_update_property_str(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length, component);
1099+
1100+
RETVAL_COPY(ZEND_THIS);
1101+
}
1102+
1103+
ZEND_ATTRIBUTE_NONNULL static void php_uri_builder_set_component_string_or_null(
1104+
INTERNAL_FUNCTION_PARAMETERS, const char *name, const size_t name_length,
1105+
const php_uri_component_validator_string validator
1106+
) {
1107+
zend_string *component;
1108+
1109+
ZEND_PARSE_PARAMETERS_START(1, 1)
1110+
Z_PARAM_STR_OR_NULL(component)
1111+
ZEND_PARSE_PARAMETERS_END();
1112+
1113+
if (component == NULL) {
1114+
zend_update_property_null(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length);
1115+
} else {
1116+
if (validator(component) == FAILURE) {
1117+
RETURN_THROWS();
1118+
}
1119+
1120+
zend_update_property_str(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length, component);
1121+
}
1122+
1123+
RETVAL_COPY(ZEND_THIS);
1124+
}
1125+
1126+
ZEND_ATTRIBUTE_NONNULL_ARGS(1) static void php_uri_builder_set_component_long_or_null(
1127+
INTERNAL_FUNCTION_PARAMETERS, const char *name, const size_t name_length,
1128+
const php_uri_component_validator_long validator
1129+
) {
1130+
zend_long component;
1131+
bool component_is_null;
1132+
1133+
ZEND_PARSE_PARAMETERS_START(1, 1)
1134+
Z_PARAM_LONG_OR_NULL(component, component_is_null)
1135+
ZEND_PARSE_PARAMETERS_END();
1136+
1137+
if (component_is_null) {
1138+
zend_update_property_null(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length);
1139+
} else {
1140+
if (validator(component) == FAILURE) {
1141+
RETURN_THROWS();
1142+
}
1143+
1144+
zend_update_property_long(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length, component);
1145+
}
1146+
1147+
RETVAL_COPY(ZEND_THIS);
1148+
}
1149+
1150+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setScheme)
1151+
{
1152+
php_uri_builder_set_component_string_or_null(
1153+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1154+
ZEND_STRL("scheme"),
1155+
php_uri_parser_rfc3986_validate_scheme
1156+
);
1157+
}
1158+
1159+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setUserInfo)
1160+
{
1161+
php_uri_builder_set_component_string_or_null(
1162+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1163+
ZEND_STRL("userinfo"),
1164+
php_uri_parser_rfc3986_validate_userinfo
1165+
);
1166+
}
1167+
1168+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setHost)
1169+
{
1170+
php_uri_builder_set_component_string_or_null(
1171+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1172+
ZEND_STRL("host"),
1173+
php_uri_parser_rfc3986_validate_host
1174+
);
1175+
}
1176+
1177+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setPort)
1178+
{
1179+
php_uri_builder_set_component_long_or_null(
1180+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1181+
ZEND_STRL("port"),
1182+
php_uri_parser_rfc3986_validate_port
1183+
);
1184+
}
1185+
1186+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setPath)
1187+
{
1188+
php_uri_builder_set_component_string(
1189+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1190+
ZEND_STRL("path"),
1191+
php_uri_parser_rfc3986_validate_path
1192+
);
1193+
}
1194+
1195+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setQuery)
1196+
{
1197+
php_uri_builder_set_component_string_or_null(
1198+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1199+
ZEND_STRL("query"),
1200+
php_uri_parser_rfc3986_validate_query
1201+
);
1202+
}
1203+
1204+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setFragment)
1205+
{
1206+
php_uri_builder_set_component_string_or_null(
1207+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1208+
ZEND_STRL("fragment"),
1209+
php_uri_parser_rfc3986_validate_fragment
1210+
);
1211+
}
1212+
1213+
PHP_METHOD(Uri_Rfc3986_UriBuilder, build)
1214+
{
1215+
zval *base_url = NULL;
1216+
1217+
ZEND_PARSE_PARAMETERS_START(0, 1)
1218+
Z_PARAM_OPTIONAL
1219+
Z_PARAM_OBJECT_OF_CLASS_OR_NULL(base_url, php_uri_ce_rfc3986_uri)
1220+
ZEND_PARSE_PARAMETERS_END();
1221+
1222+
const zval *scheme = Z_RFC3986_URI_PROP_SCHEME_P(ZEND_THIS);
1223+
const zval *userinfo = Z_RFC3986_URI_PROP_USERINFO_P(ZEND_THIS);
1224+
const zval *host = Z_RFC3986_URI_PROP_HOST_P(ZEND_THIS);
1225+
const zval *port = Z_RFC3986_URI_PROP_PORT_P(ZEND_THIS);
1226+
const zval *path = Z_RFC3986_URI_PROP_PATH_P(ZEND_THIS);
1227+
const zval *query = Z_RFC3986_URI_PROP_QUERY_P(ZEND_THIS);
1228+
const zval *fragment = Z_RFC3986_URI_PROP_FRAGMENT_P(ZEND_THIS);
1229+
1230+
php_uri_parser_rfc3986_uris *base_uris = NULL;
1231+
if (base_url != NULL) {
1232+
base_uris = Z_URI_OBJECT_P(base_url)->uri;
1233+
}
1234+
1235+
php_uri_parser_rfc3986_uris *uriparser_uris = php_uri_parser_rfc3986_build_from_zval(
1236+
base_uris, scheme, userinfo, host, port, path, query, fragment
1237+
);
1238+
if (uriparser_uris == NULL) {
1239+
RETURN_THROWS();
1240+
}
1241+
1242+
object_init_ex(return_value, php_uri_ce_rfc3986_uri);
1243+
php_uri_object *uri_object = Z_URI_OBJECT_P(return_value);
1244+
uri_object->parser = &php_uri_parser_rfc3986;
1245+
uri_object->uri = uriparser_uris;
1246+
}
1247+
10471248
PHPAPI php_uri_object *php_uri_object_create(zend_class_entry *class_type, const php_uri_parser *parser)
10481249
{
10491250
php_uri_object *uri_object = zend_object_alloc(sizeof(*uri_object), class_type);
@@ -1113,6 +1314,8 @@ PHPAPI zend_result php_uri_parser_register(const php_uri_parser *uri_parser)
11131314

11141315
static PHP_MINIT_FUNCTION(uri)
11151316
{
1317+
php_uri_ce_rfc3986_uri_builder = register_class_Uri_Rfc3986_UriBuilder();
1318+
11161319
php_uri_ce_rfc3986_uri = register_class_Uri_Rfc3986_Uri();
11171320
php_uri_ce_rfc3986_uri->create_object = php_uri_object_create_rfc3986;
11181321
php_uri_ce_rfc3986_uri->default_object_handlers = &object_handlers_rfc3986_uri;

ext/uri/php_uri.stub.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,35 @@ enum UriHostType
4545
case RegisteredName;
4646
}
4747

48+
final class UriBuilder
49+
{
50+
private ?string $scheme = null;
51+
private ?string $userinfo = null;
52+
private ?string $host = null;
53+
private ?int $port = null;
54+
private string $path = "";
55+
private ?string $query = null;
56+
private ?string $fragment = null;
57+
58+
public function reset(): static {}
59+
60+
public function setScheme(?string $scheme): static {}
61+
62+
public function setUserInfo(#[\SensitiveParameter] ?string $userInfo): static {}
63+
64+
public function setHost(?string $host): static {}
65+
66+
public function setPort(?int $port): static {}
67+
68+
public function setPath(string $path): static {}
69+
70+
public function setQuery(?string $query): static {}
71+
72+
public function setFragment(?string $fragment): static {}
73+
74+
public function build(?\Uri\Rfc3986\Uri $baseUrl = null): \Uri\Rfc3986\Uri {}
75+
}
76+
4877
/** @strict-properties */
4978
final readonly class Uri
5079
{

0 commit comments

Comments
 (0)