Skip to content

Commit ae4c905

Browse files
committed
Review fixes + optimize property reads + fix leak
1 parent f99c5ee commit ae4c905

5 files changed

Lines changed: 117 additions & 84 deletions

File tree

ext/uri/php_uri.c

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ zend_class_entry *php_uri_ce_whatwg_url_validation_error;
4747
static zend_object_handlers object_handlers_rfc3986_uri;
4848
static zend_object_handlers object_handlers_whatwg_uri;
4949

50-
typedef bool (*php_uri_string_component_validator)(const zend_string *component);
51-
typedef bool (*php_uri_long_component_validator)(zend_long component);
50+
typedef bool (*php_uri_component_validator_string)(const zend_string *component);
51+
typedef bool (*php_uri_component_validator_long)(zend_long component);
5252

5353
static const zend_module_dep uri_deps[] = {
5454
ZEND_MOD_REQUIRED("lexbor")
@@ -57,6 +57,14 @@ static const zend_module_dep uri_deps[] = {
5757

5858
static zend_array uri_parsers;
5959

60+
#define Z_RFC3986_URI_PROP_SCHEME_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 0)
61+
#define Z_RFC3986_URI_PROP_USERINFO_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 1)
62+
#define Z_RFC3986_URI_PROP_HOST_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 2)
63+
#define Z_RFC3986_URI_PROP_PORT_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 3)
64+
#define Z_RFC3986_URI_PROP_PATH_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 4)
65+
#define Z_RFC3986_URI_PROP_QUERY_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 5)
66+
#define Z_RFC3986_URI_PROP_FRAGMENT_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 6)
67+
6068
static HashTable *uri_get_debug_properties(php_uri_object *object)
6169
{
6270
const HashTable *std_properties = zend_std_get_properties(&object->std);
@@ -1057,19 +1065,20 @@ PHP_METHOD(Uri_Rfc3986_UriBuilder, reset)
10571065
{
10581066
ZEND_PARSE_PARAMETERS_NONE();
10591067

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"));
1068+
ZVAL_NULL(Z_RFC3986_URI_PROP_SCHEME_P(ZEND_THIS));
1069+
ZVAL_NULL(Z_RFC3986_URI_PROP_USERINFO_P(ZEND_THIS));
1070+
ZVAL_NULL(Z_RFC3986_URI_PROP_HOST_P(ZEND_THIS));
1071+
ZVAL_NULL(Z_RFC3986_URI_PROP_PORT_P(ZEND_THIS));
1072+
ZVAL_EMPTY_STRING(Z_RFC3986_URI_PROP_PATH_P(ZEND_THIS));
1073+
ZVAL_NULL(Z_RFC3986_URI_PROP_QUERY_P(ZEND_THIS));
1074+
ZVAL_NULL(Z_RFC3986_URI_PROP_FRAGMENT_P(ZEND_THIS));
10661075

10671076
RETVAL_COPY(ZEND_THIS);
10681077
}
10691078

10701079
ZEND_ATTRIBUTE_NONNULL static void php_uri_builder_set_component_string(
10711080
INTERNAL_FUNCTION_PARAMETERS, const char *name, size_t name_length,
1072-
const php_uri_string_component_validator validator
1081+
const php_uri_component_validator_string validator
10731082
) {
10741083
zend_string *component;
10751084

@@ -1089,7 +1098,7 @@ ZEND_ATTRIBUTE_NONNULL static void php_uri_builder_set_component_string(
10891098

10901099
ZEND_ATTRIBUTE_NONNULL static void php_uri_builder_set_component_string_or_null(
10911100
INTERNAL_FUNCTION_PARAMETERS, const char *name, size_t name_length,
1092-
const php_uri_string_component_validator validator
1101+
const php_uri_component_validator_string validator
10931102
) {
10941103
zend_string *component;
10951104

@@ -1113,7 +1122,7 @@ ZEND_ATTRIBUTE_NONNULL static void php_uri_builder_set_component_string_or_null(
11131122

11141123
ZEND_ATTRIBUTE_NONNULL_ARGS(1) static void php_uri_builder_set_component_long_or_null(
11151124
INTERNAL_FUNCTION_PARAMETERS, const char *name, size_t name_length,
1116-
const php_uri_long_component_validator validator
1125+
const php_uri_component_validator_long validator
11171126
) {
11181127
zend_long component;
11191128
bool component_is_null;
@@ -1208,16 +1217,13 @@ PHP_METHOD(Uri_Rfc3986_UriBuilder, build)
12081217
Z_PARAM_OBJECT_OF_CLASS_OR_NULL(base_url, php_uri_ce_rfc3986_uri)
12091218
ZEND_PARSE_PARAMETERS_END();
12101219

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);
1220+
const zval *scheme = Z_RFC3986_URI_PROP_SCHEME_P(ZEND_THIS);
1221+
const zval *userinfo = Z_RFC3986_URI_PROP_USERINFO_P(ZEND_THIS);
1222+
const zval *host = Z_RFC3986_URI_PROP_HOST_P(ZEND_THIS);
1223+
const zval *port = Z_RFC3986_URI_PROP_PORT_P(ZEND_THIS);
1224+
const zval *path = Z_RFC3986_URI_PROP_PATH_P(ZEND_THIS);
1225+
const zval *query = Z_RFC3986_URI_PROP_QUERY_P(ZEND_THIS);
1226+
const zval *fragment = Z_RFC3986_URI_PROP_FRAGMENT_P(ZEND_THIS);
12211227

12221228
zend_string *uri_str = php_uri_parser_rfc3986_recompose_from_zval(scheme, userinfo, host, port, path, query, fragment);
12231229
if (uri_str == NULL) {

ext/uri/tests/rfc3986/builder/all_success_basic.phpt

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
--TEST--
2+
Test Uri\Rfc3986\UriBuilder all components - success - calling reset() afterwards
3+
--FILE--
4+
<?php
5+
6+
$builder = new Uri\Rfc3986\UriBuilder()
7+
->setScheme("https")
8+
->setUserInfo("user:info")
9+
->setHost("example.com")
10+
->setPort(443)
11+
->setPath("/foo/bar/baz")
12+
->setQuery("foo=1&bar=baz")
13+
->setFragment("fragment");
14+
$uri = $builder->build();
15+
16+
var_dump($uri->toRawString());
17+
var_dump($uri);
18+
19+
$uri = $builder->reset()->build();
20+
21+
var_dump($uri->toRawString());
22+
var_dump($uri);
23+
24+
?>
25+
--EXPECTF--
26+
string(68) "https://user:info@example.com:443/foo/bar/baz?foo=1&bar=baz#fragment"
27+
object(Uri\Rfc3986\Uri)#%d (%d) {
28+
["scheme"]=>
29+
string(5) "https"
30+
["username"]=>
31+
string(4) "user"
32+
["password"]=>
33+
string(4) "info"
34+
["host"]=>
35+
string(11) "example.com"
36+
["port"]=>
37+
int(443)
38+
["path"]=>
39+
string(12) "/foo/bar/baz"
40+
["query"]=>
41+
string(13) "foo=1&bar=baz"
42+
["fragment"]=>
43+
string(8) "fragment"
44+
}
45+
string(0) ""
46+
object(Uri\Rfc3986\Uri)#%d (%d) {
47+
["scheme"]=>
48+
NULL
49+
["username"]=>
50+
NULL
51+
["password"]=>
52+
NULL
53+
["host"]=>
54+
NULL
55+
["port"]=>
56+
NULL
57+
["path"]=>
58+
string(0) ""
59+
["query"]=>
60+
NULL
61+
["fragment"]=>
62+
NULL
63+
}

ext/uri/tests/rfc3986/builder/path_error_special_char.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Test Uri\Rfc3986\UriBuilder::setUserInfo() - error - contains invalid special character
2+
Test Uri\Rfc3986\UriBuilder::setPath() - error - contains invalid special character
33
--FILE--
44
<?php
55

ext/uri/uri_parser_rfc3986.c

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -723,19 +723,20 @@ ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_rfc3986_validate_port(zend_long port)
723723
const char *p = ZSTR_VAL(tmp);
724724
const size_t len = ZSTR_LEN(tmp);
725725

726-
return uriIsWellFormedPortA(p, p + len);
726+
bool result = uriIsWellFormedPortA(p, p + len);
727+
zend_string_release(tmp);
728+
729+
return result;
727730
}
728731

729732
ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_rfc3986_validate_path(const zend_string *path)
730733
{
731734
const char *p = ZSTR_VAL(path);
732735
const size_t len = ZSTR_LEN(path);
733736

734-
/*
735-
It's checked during the build() method whether the path begins with a "/" when there's a host
736-
that's why a false hasHost argument is passed to uriIsWellFormedPathA() for now.
737-
*/
738-
return uriIsWellFormedPathA(p, p + len, false);
737+
/* The build() method checks whether the path begins with a "/" when there's a host.
738+
* In order to skip doing the same check, a false hasHost argument is passed to uriIsWellFormedPathA(). */
739+
return uriIsWellFormedPathA(p, p + len, /* hasHost */ false);
739740
}
740741

741742
ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_rfc3986_validate_query(const zend_string *query)
@@ -756,18 +757,26 @@ ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_rfc3986_validate_fragment(const zend_
756757

757758
ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_rfc3986_recompose_from_zval(
758759
const zval *scheme, const zval *userinfo, const zval *host, const zval *port,
759-
const zval *path, const zval *query, const zval *fragment)
760-
{
761-
/* The userinfo and port components can only ever be present if the host is present */
762-
if ((Z_TYPE_P(userinfo) != IS_NULL || Z_TYPE_P(port) != IS_NULL) && Z_TYPE_P(host) == IS_NULL) {
763-
zend_throw_exception(php_uri_ce_invalid_uri_exception, "The URI must contain a host if either the userinfo or the port component is present", 0);
764-
return NULL;
765-
}
760+
const zval *path, const zval *query, const zval *fragment
761+
) {
762+
if (Z_TYPE_P(host) == IS_NULL) {
763+
/* The path must not begin with "//" if the URI doesn't contain a host */
764+
if (zend_string_starts_with_literal(Z_STR_P(path), "//")) {
765+
zend_throw_exception(php_uri_ce_invalid_uri_exception, "The path must not begin with \"//\" when the URI doesn't contain a host", 0);
766+
return NULL;
767+
}
766768

767-
/* The path must be either empty or begin with a "/" if the URI contains a host */
768-
if (Z_TYPE_P(host) != IS_NULL && (Z_STRLEN_P(path) > 0 && Z_STRVAL_P(path)[0] != '/')) {
769-
zend_throw_exception(php_uri_ce_invalid_uri_exception, "A path must be either empty or begin with \"/\" when the URI contains a host", 0);
770-
return NULL;
769+
/* The userinfo and port components must not be present if the URI doesn't contain a host */
770+
if (Z_TYPE_P(userinfo) != IS_NULL || Z_TYPE_P(port) != IS_NULL) {
771+
zend_throw_exception(php_uri_ce_invalid_uri_exception, "The URI must contain a host if either the userinfo or the port component is present", 0);
772+
return NULL;
773+
}
774+
} else {
775+
/* The path must be either empty or begin with a "/" if the URI contains a host */
776+
if (Z_STRLEN_P(path) > 0 && Z_STRVAL_P(path)[0] != '/') {
777+
zend_throw_exception(php_uri_ce_invalid_uri_exception, "A path must be either empty or begin with \"/\" when the URI contains a host", 0);
778+
return NULL;
779+
}
771780
}
772781

773782
/* The first segment of the path must not contain ":" if the URI doesn't contain a scheme */
@@ -783,12 +792,6 @@ ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_rfc3986_recompose_from_zval(
783792
}
784793
}
785794

786-
/* The path must not begin with "//" if the URI doesn't contain a host */
787-
if (Z_TYPE_P(host) == IS_NULL && Z_STRLEN_P(path) >= 2 && Z_STRVAL_P(path)[0] == '/' && Z_STRVAL_P(path)[1] == '/') {
788-
zend_throw_exception(php_uri_ce_invalid_uri_exception, "The path must not begin with \"//\" when the URI doesn't contain a host", 0);
789-
return NULL;
790-
}
791-
792795
/* result = "" */
793796
smart_str uri_str = {0};
794797

0 commit comments

Comments
 (0)