Skip to content

Commit 9ffcb5d

Browse files
committed
Review fixes + optimize property reads + fix leak
1 parent 306220c commit 9ffcb5d

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
@@ -733,19 +733,20 @@ ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_rfc3986_validate_port(zend_long port)
733733
const char *p = ZSTR_VAL(tmp);
734734
const size_t len = ZSTR_LEN(tmp);
735735

736-
return uriIsWellFormedPortA(p, p + len);
736+
bool result = uriIsWellFormedPortA(p, p + len);
737+
zend_string_release(tmp);
738+
739+
return result;
737740
}
738741

739742
ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_rfc3986_validate_path(const zend_string *path)
740743
{
741744
const char *p = ZSTR_VAL(path);
742745
const size_t len = ZSTR_LEN(path);
743746

744-
/*
745-
It's checked during the build() method whether the path begins with a "/" when there's a host
746-
that's why a false hasHost argument is passed to uriIsWellFormedPathA() for now.
747-
*/
748-
return uriIsWellFormedPathA(p, p + len, false);
747+
/* The build() method checks whether the path begins with a "/" when there's a host.
748+
* In order to skip doing the same check, a false hasHost argument is passed to uriIsWellFormedPathA(). */
749+
return uriIsWellFormedPathA(p, p + len, /* hasHost */ false);
749750
}
750751

751752
ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_rfc3986_validate_query(const zend_string *query)
@@ -766,18 +767,26 @@ ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_rfc3986_validate_fragment(const zend_
766767

767768
ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_rfc3986_recompose_from_zval(
768769
const zval *scheme, const zval *userinfo, const zval *host, const zval *port,
769-
const zval *path, const zval *query, const zval *fragment)
770-
{
771-
/* The userinfo and port components can only ever be present if the host is present */
772-
if ((Z_TYPE_P(userinfo) != IS_NULL || Z_TYPE_P(port) != IS_NULL) && Z_TYPE_P(host) == IS_NULL) {
773-
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);
774-
return NULL;
775-
}
770+
const zval *path, const zval *query, const zval *fragment
771+
) {
772+
if (Z_TYPE_P(host) == IS_NULL) {
773+
/* The path must not begin with "//" if the URI doesn't contain a host */
774+
if (zend_string_starts_with_literal(Z_STR_P(path), "//")) {
775+
zend_throw_exception(php_uri_ce_invalid_uri_exception, "The path must not begin with \"//\" when the URI doesn't contain a host", 0);
776+
return NULL;
777+
}
776778

777-
/* The path must be either empty or begin with a "/" if the URI contains a host */
778-
if (Z_TYPE_P(host) != IS_NULL && (Z_STRLEN_P(path) > 0 && Z_STRVAL_P(path)[0] != '/')) {
779-
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);
780-
return NULL;
779+
/* The userinfo and port components must not be present if the URI doesn't contain a host */
780+
if (Z_TYPE_P(userinfo) != IS_NULL || Z_TYPE_P(port) != IS_NULL) {
781+
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);
782+
return NULL;
783+
}
784+
} else {
785+
/* The path must be either empty or begin with a "/" if the URI contains a host */
786+
if (Z_STRLEN_P(path) > 0 && Z_STRVAL_P(path)[0] != '/') {
787+
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);
788+
return NULL;
789+
}
781790
}
782791

783792
/* The first segment of the path must not contain ":" if the URI doesn't contain a scheme */
@@ -793,12 +802,6 @@ ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_rfc3986_recompose_from_zval(
793802
}
794803
}
795804

796-
/* The path must not begin with "//" if the URI doesn't contain a host */
797-
if (Z_TYPE_P(host) == IS_NULL && Z_STRLEN_P(path) >= 2 && Z_STRVAL_P(path)[0] == '/' && Z_STRVAL_P(path)[1] == '/') {
798-
zend_throw_exception(php_uri_ce_invalid_uri_exception, "The path must not begin with \"//\" when the URI doesn't contain a host", 0);
799-
return NULL;
800-
}
801-
802805
/* result = "" */
803806
smart_str uri_str = {0};
804807

0 commit comments

Comments
 (0)