Skip to content

Commit f99c5ee

Browse files
committed
Implement "Followup improvements for ext/uri" RFC - RFC 3986 URI building
RFC: https://wiki.php.net/rfc/uri_followup#uri_building
1 parent a7aacec commit f99c5ee

43 files changed

Lines changed: 1429 additions & 28 deletions

Some content is hidden

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

ext/uri/php_uri.c

Lines changed: 199 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,6 +47,9 @@ 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 bool (*php_uri_string_component_validator)(const zend_string *component);
51+
typedef bool (*php_uri_long_component_validator)(zend_long component);
52+
4953
static const zend_module_dep uri_deps[] = {
5054
ZEND_MOD_REQUIRED("lexbor")
5155
ZEND_MOD_END
@@ -1044,6 +1048,199 @@ PHP_METHOD(Uri_WhatWg_Url, __debugInfo)
10441048
RETURN_ARR(uri_get_debug_properties(uri_object));
10451049
}
10461050

1051+
PHP_METHOD(Uri_Rfc3986_UriBuilder, __construct)
1052+
{
1053+
ZEND_PARSE_PARAMETERS_NONE();
1054+
}
1055+
1056+
PHP_METHOD(Uri_Rfc3986_UriBuilder, reset)
1057+
{
1058+
ZEND_PARSE_PARAMETERS_NONE();
1059+
1060+
zend_update_property_null(php_uri_ce_rfc3986_uri_builder, Z_OBJ_P(ZEND_THIS), ZEND_STRL("scheme"));
1061+
zend_update_property_null(php_uri_ce_rfc3986_uri_builder, Z_OBJ_P(ZEND_THIS), ZEND_STRL("userinfo"));
1062+
zend_update_property_null(php_uri_ce_rfc3986_uri_builder, Z_OBJ_P(ZEND_THIS), ZEND_STRL("host"));
1063+
zend_update_property_stringl(php_uri_ce_rfc3986_uri_builder, Z_OBJ_P(ZEND_THIS), ZEND_STRL("path"), "", 0);
1064+
zend_update_property_null(php_uri_ce_rfc3986_uri_builder, Z_OBJ_P(ZEND_THIS), ZEND_STRL("query"));
1065+
zend_update_property_null(php_uri_ce_rfc3986_uri_builder, Z_OBJ_P(ZEND_THIS), ZEND_STRL("fragment"));
1066+
1067+
RETVAL_COPY(ZEND_THIS);
1068+
}
1069+
1070+
ZEND_ATTRIBUTE_NONNULL static void php_uri_builder_set_component_string(
1071+
INTERNAL_FUNCTION_PARAMETERS, const char *name, size_t name_length,
1072+
const php_uri_string_component_validator validator
1073+
) {
1074+
zend_string *component;
1075+
1076+
ZEND_PARSE_PARAMETERS_START(1, 1)
1077+
Z_PARAM_STR(component)
1078+
ZEND_PARSE_PARAMETERS_END();
1079+
1080+
if (!validator(component)) {
1081+
zend_throw_exception_ex(php_uri_ce_invalid_uri_exception, 0, "The specified %s is malformed", name);
1082+
RETURN_THROWS();
1083+
}
1084+
1085+
zend_update_property_str(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length, component);
1086+
1087+
RETVAL_COPY(ZEND_THIS);
1088+
}
1089+
1090+
ZEND_ATTRIBUTE_NONNULL static void php_uri_builder_set_component_string_or_null(
1091+
INTERNAL_FUNCTION_PARAMETERS, const char *name, size_t name_length,
1092+
const php_uri_string_component_validator validator
1093+
) {
1094+
zend_string *component;
1095+
1096+
ZEND_PARSE_PARAMETERS_START(1, 1)
1097+
Z_PARAM_STR_OR_NULL(component)
1098+
ZEND_PARSE_PARAMETERS_END();
1099+
1100+
if (component == NULL) {
1101+
zend_update_property_null(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length);
1102+
} else {
1103+
if (!validator(component)) {
1104+
zend_throw_exception_ex(php_uri_ce_invalid_uri_exception, 0, "The specified %s is malformed", name);
1105+
RETURN_THROWS();
1106+
}
1107+
1108+
zend_update_property_str(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length, component);
1109+
}
1110+
1111+
RETVAL_COPY(ZEND_THIS);
1112+
}
1113+
1114+
ZEND_ATTRIBUTE_NONNULL_ARGS(1) static void php_uri_builder_set_component_long_or_null(
1115+
INTERNAL_FUNCTION_PARAMETERS, const char *name, size_t name_length,
1116+
const php_uri_long_component_validator validator
1117+
) {
1118+
zend_long component;
1119+
bool component_is_null;
1120+
1121+
ZEND_PARSE_PARAMETERS_START(1, 1)
1122+
Z_PARAM_LONG_OR_NULL(component, component_is_null)
1123+
ZEND_PARSE_PARAMETERS_END();
1124+
1125+
if (component_is_null) {
1126+
zend_update_property_null(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length);
1127+
} else {
1128+
if (!validator(component)) {
1129+
zend_throw_exception_ex(php_uri_ce_invalid_uri_exception, 0, "The specified %s is malformed", name);
1130+
RETURN_THROWS();
1131+
}
1132+
1133+
zend_update_property_long(Z_OBJCE_P(ZEND_THIS), Z_OBJ_P(ZEND_THIS), name, name_length, component);
1134+
}
1135+
1136+
RETVAL_COPY(ZEND_THIS);
1137+
}
1138+
1139+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setScheme)
1140+
{
1141+
php_uri_builder_set_component_string_or_null(
1142+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1143+
ZEND_STRL("scheme"),
1144+
php_uri_parser_rfc3986_validate_scheme
1145+
);
1146+
}
1147+
1148+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setUserInfo)
1149+
{
1150+
php_uri_builder_set_component_string_or_null(
1151+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1152+
ZEND_STRL("userinfo"),
1153+
php_uri_parser_rfc3986_validate_userinfo
1154+
);
1155+
}
1156+
1157+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setHost)
1158+
{
1159+
php_uri_builder_set_component_string_or_null(
1160+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1161+
ZEND_STRL("host"),
1162+
php_uri_parser_rfc3986_validate_host
1163+
);
1164+
}
1165+
1166+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setPort)
1167+
{
1168+
php_uri_builder_set_component_long_or_null(
1169+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1170+
ZEND_STRL("port"),
1171+
php_uri_parser_rfc3986_validate_port
1172+
);
1173+
}
1174+
1175+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setPath)
1176+
{
1177+
php_uri_builder_set_component_string(
1178+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1179+
ZEND_STRL("path"),
1180+
php_uri_parser_rfc3986_validate_path
1181+
);
1182+
}
1183+
1184+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setQuery)
1185+
{
1186+
php_uri_builder_set_component_string_or_null(
1187+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1188+
ZEND_STRL("query"),
1189+
php_uri_parser_rfc3986_validate_query
1190+
);
1191+
}
1192+
1193+
PHP_METHOD(Uri_Rfc3986_UriBuilder, setFragment)
1194+
{
1195+
php_uri_builder_set_component_string_or_null(
1196+
INTERNAL_FUNCTION_PARAM_PASSTHRU,
1197+
ZEND_STRL("fragment"),
1198+
php_uri_parser_rfc3986_validate_fragment
1199+
);
1200+
}
1201+
1202+
PHP_METHOD(Uri_Rfc3986_UriBuilder, build)
1203+
{
1204+
zval *base_url = NULL;
1205+
1206+
ZEND_PARSE_PARAMETERS_START(0, 1)
1207+
Z_PARAM_OPTIONAL
1208+
Z_PARAM_OBJECT_OF_CLASS_OR_NULL(base_url, php_uri_ce_rfc3986_uri)
1209+
ZEND_PARSE_PARAMETERS_END();
1210+
1211+
zend_object *obj = Z_OBJ_P(ZEND_THIS);
1212+
zval tmp;
1213+
1214+
const zval *scheme = zend_read_property(obj->ce, obj, ZEND_STRL("scheme"), false, &tmp);
1215+
const zval *userinfo = zend_read_property(obj->ce, obj, ZEND_STRL("userinfo"), false, &tmp);
1216+
const zval *host = zend_read_property(obj->ce, obj, ZEND_STRL("host"), false, &tmp);
1217+
const zval *port = zend_read_property(obj->ce, obj, ZEND_STRL("port"), false, &tmp);
1218+
const zval *path = zend_read_property(obj->ce, obj, ZEND_STRL("path"), false, &tmp);
1219+
const zval *query = zend_read_property(obj->ce, obj, ZEND_STRL("query"), false, &tmp);
1220+
const zval *fragment = zend_read_property(obj->ce, obj, ZEND_STRL("fragment"), false, &tmp);
1221+
1222+
zend_string *uri_str = php_uri_parser_rfc3986_recompose_from_zval(scheme, userinfo, host, port, path, query, fragment);
1223+
if (uri_str == NULL) {
1224+
RETURN_THROWS();
1225+
}
1226+
1227+
php_uri_parser_rfc3986_uris *base_uri = NULL;
1228+
if (base_url != NULL) {
1229+
base_uri = Z_URI_OBJECT_P(base_url)->uri;
1230+
}
1231+
1232+
php_uri_parser_rfc3986_uris *uri = php_uri_parser_rfc3986_parse_ex(ZSTR_VAL(uri_str), ZSTR_LEN(uri_str), base_uri, false);
1233+
zend_string_release(uri_str);
1234+
if (uri == NULL) {
1235+
RETURN_THROWS();
1236+
}
1237+
1238+
object_init_ex(return_value, php_uri_ce_rfc3986_uri);
1239+
php_uri_object *uri_object = Z_URI_OBJECT_P(return_value);
1240+
uri_object->parser = &php_uri_parser_rfc3986;
1241+
uri_object->uri = uri;
1242+
}
1243+
10471244
PHPAPI php_uri_object *php_uri_object_create(zend_class_entry *class_type, const php_uri_parser *parser)
10481245
{
10491246
php_uri_object *uri_object = zend_object_alloc(sizeof(*uri_object), class_type);
@@ -1113,6 +1310,8 @@ PHPAPI zend_result php_uri_parser_register(const php_uri_parser *uri_parser)
11131310

11141311
static PHP_MINIT_FUNCTION(uri)
11151312
{
1313+
php_uri_ce_rfc3986_uri_builder = register_class_Uri_Rfc3986_UriBuilder();
1314+
11161315
php_uri_ce_rfc3986_uri = register_class_Uri_Rfc3986_Uri();
11171316
php_uri_ce_rfc3986_uri->create_object = php_uri_object_create_rfc3986;
11181317
php_uri_ce_rfc3986_uri->default_object_handlers = &object_handlers_rfc3986_uri;

ext/uri/php_uri.stub.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,37 @@ 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 __construct() {}
59+
60+
public function reset(): static {}
61+
62+
public function setScheme(?string $scheme): static {}
63+
64+
public function setUserInfo(#[\SensitiveParameter] ?string $userInfo): static {}
65+
66+
public function setHost(?string $host): static {}
67+
68+
public function setPort(?int $port): static {}
69+
70+
public function setPath(string $path): static {}
71+
72+
public function setQuery(?string $query): static {}
73+
74+
public function setFragment(?string $fragment): static {}
75+
76+
public function build(?\Uri\Rfc3986\Uri $baseUrl = null): \Uri\Rfc3986\Uri {}
77+
}
78+
4879
/** @strict-properties */
4980
final readonly class Uri
5081
{

0 commit comments

Comments
 (0)