Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 152 additions & 71 deletions ext/standard/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
encoded_data = php_url_encode(Z_STRVAL_P(scalar), Z_STRLEN_P(scalar));
}
smart_str_append(form_str, encoded_data);
zend_string_free(encoded_data);
zend_string_release_ex(encoded_data, false);
break;
}
case IS_LONG:
Expand All @@ -77,8 +77,8 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
encoded_data = php_url_encode(ZSTR_VAL(tmp), ZSTR_LEN(tmp));
}
smart_str_append(form_str, encoded_data);
zend_string_free(tmp);
zend_string_free(encoded_data);
zend_string_release_ex(tmp, false);
zend_string_release_ex(encoded_data, false);
break;
}
case IS_FALSE:
Expand All @@ -92,11 +92,135 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
}
}

static HashTable* php_object_get_visible_properties(/* const */ zend_object *object)
{
/* const */ HashTable *properties = object->handlers->get_properties(object);
//HashTable *visible_properties = zend_new_array(zend_hash_num_elements(properties));
HashTable *visible_properties = zend_array_dup(object->handlers->get_properties(object));
zend_string *property_name;
zval *value;

ZEND_HASH_FOREACH_STR_KEY_VAL(properties, property_name, value) {
bool is_dynamic = true;
if (Z_TYPE_P(value) == IS_INDIRECT) {
value = Z_INDIRECT_P(value);
if (Z_ISUNDEF_P(value)) {
continue;
}
is_dynamic = false;
}

if (zend_check_property_access(object, property_name, is_dynamic) == FAILURE) {
/* property not visible in this scope */
zend_hash_del(visible_properties, property_name);
continue;
}

/* handling for private & protected object properties */
if (UNEXPECTED(ZSTR_VAL(property_name)[0] == '\0')) {
zend_hash_del(visible_properties, property_name);
const char *tmp;
const char *unmangled_name;
size_t unmangled_name_len;
zend_unmangle_property_name_ex(property_name, &tmp, &unmangled_name, &unmangled_name_len);
zend_hash_str_add(visible_properties, unmangled_name, unmangled_name_len, value);
}
} ZEND_HASH_FOREACH_END();
return visible_properties;
}

static zend_string* php_url_encode_get_new_prefix(
int encoding_type, zend_ulong index_int,
const char *index_string, size_t index_string_len,
const char *num_prefix, size_t num_prefix_len,
const zend_string *key_prefix
) {
zend_string *new_prefix;

if (index_string) {
zend_string *encoded_key;
if (encoding_type == PHP_QUERY_RFC3986) {
encoded_key = php_raw_url_encode(index_string, index_string_len);
} else {
encoded_key = php_url_encode(index_string, index_string_len);
}

if (key_prefix) {
new_prefix = zend_string_concat3(ZSTR_VAL(key_prefix), ZSTR_LEN(key_prefix), ZSTR_VAL(encoded_key), ZSTR_LEN(encoded_key), "%5D%5B", strlen("%5D%5B"));
} else {
new_prefix = zend_string_concat2(ZSTR_VAL(encoded_key), ZSTR_LEN(encoded_key), "%5B", strlen("%5B"));
}
zend_string_release_ex(encoded_key, false);
} else { /* is integer index */
char *index_int_as_str;
size_t index_int_as_str_len;

index_int_as_str_len = spprintf(&index_int_as_str, 0, ZEND_LONG_FMT, index_int);

if (key_prefix && num_prefix) {
/* zend_string_concat4() */
size_t len = ZSTR_LEN(key_prefix) + num_prefix_len + index_int_as_str_len + strlen("%5D%5B");
new_prefix = zend_string_alloc(len, 0);

memcpy(ZSTR_VAL(new_prefix), ZSTR_VAL(key_prefix), ZSTR_LEN(key_prefix));
memcpy(ZSTR_VAL(new_prefix) + ZSTR_LEN(key_prefix), num_prefix, num_prefix_len);
memcpy(ZSTR_VAL(new_prefix) + ZSTR_LEN(key_prefix) + num_prefix_len, index_int_as_str, index_int_as_str_len);
memcpy(ZSTR_VAL(new_prefix) + ZSTR_LEN(key_prefix) + num_prefix_len +index_int_as_str_len, "%5D%5B", strlen("%5D%5B"));
ZSTR_VAL(new_prefix)[len] = '\0';
} else if (key_prefix) {
new_prefix = zend_string_concat3(ZSTR_VAL(key_prefix), ZSTR_LEN(key_prefix), index_int_as_str, index_int_as_str_len, "%5D%5B", strlen("%5D%5B"));
} else if (num_prefix) {
new_prefix = zend_string_concat3(num_prefix, num_prefix_len, index_int_as_str, index_int_as_str_len, "%5B", strlen("%5B"));
} else {
new_prefix = zend_string_concat2(index_int_as_str, index_int_as_str_len, "%5B", strlen("%5B"));
}
efree(index_int_as_str);
}

return new_prefix;
}

static void php_url_encode_object(zend_object *object, smart_str *form_str,
int encoding_type, zend_ulong index_int,
const char *index_string, size_t index_string_len,
const char *num_prefix, size_t num_prefix_len,
const zend_string *key_prefix,
const zend_string *arg_sep)
{
if (GC_IS_RECURSIVE(object)) {
/* Prevent recursion */
return;
}

HashTable *properties = php_object_get_visible_properties(object);
if (zend_hash_num_elements(properties) == 0) {
zend_array_destroy(properties);

zval tmp;
/* If the data object is stringable without visible properties handle it like a string instead of empty array */
if (object->handlers->cast_object(object, &tmp, IS_STRING) == SUCCESS) {
php_url_encode_scalar(&tmp, form_str,
encoding_type, index_int,
index_string, index_string_len,
num_prefix, num_prefix_len,
NULL,
arg_sep);
zval_ptr_dtor(&tmp);
}
return;
}

GC_TRY_PROTECT_RECURSION(object);
php_url_encode_hash_ex(properties, form_str, num_prefix, num_prefix_len, key_prefix, NULL, arg_sep, encoding_type);
GC_TRY_UNPROTECT_RECURSION(object);
zend_array_destroy(properties);
}

/* {{{ php_url_encode_hash */
PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
const char *num_prefix, size_t num_prefix_len,
const zend_string *key_prefix,
zval *type, const zend_string *arg_sep, int enc_type)
ZEND_ATTRIBUTE_UNUSED zval *type, const zend_string *arg_sep, int enc_type)
{
zend_string *key = NULL;
const char *prop_name;
Expand All @@ -118,84 +242,32 @@ PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
}

ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, zdata) {
bool is_dynamic = 1;
if (Z_TYPE_P(zdata) == IS_INDIRECT) {
zdata = Z_INDIRECT_P(zdata);
if (Z_ISUNDEF_P(zdata)) {
continue;
}

is_dynamic = 0;
}

/* handling for private & protected object properties */
if (key) {
prop_name = ZSTR_VAL(key);
prop_len = ZSTR_LEN(key);

if (type != NULL && zend_check_property_access(Z_OBJ_P(type), key, is_dynamic) != SUCCESS) {
/* property not visible in this scope */
continue;
}

if (ZSTR_VAL(key)[0] == '\0' && type != NULL) {
const char *tmp;
zend_unmangle_property_name_ex(key, &tmp, &prop_name, &prop_len);
} else {
prop_name = ZSTR_VAL(key);
prop_len = ZSTR_LEN(key);
}
} else {
prop_name = NULL;
prop_len = 0;
}

ZVAL_DEREF(zdata);
if (Z_TYPE_P(zdata) == IS_ARRAY || Z_TYPE_P(zdata) == IS_OBJECT) {
zend_string *new_prefix;
if (key) {
zend_string *encoded_key;
if (enc_type == PHP_QUERY_RFC3986) {
encoded_key = php_raw_url_encode(prop_name, prop_len);
} else {
encoded_key = php_url_encode(prop_name, prop_len);
}

if (key_prefix) {
new_prefix = zend_string_concat3(ZSTR_VAL(key_prefix), ZSTR_LEN(key_prefix), ZSTR_VAL(encoded_key), ZSTR_LEN(encoded_key), "%5D%5B", strlen("%5D%5B"));
} else {
new_prefix = zend_string_concat2(ZSTR_VAL(encoded_key), ZSTR_LEN(encoded_key), "%5B", strlen("%5B"));
}
zend_string_release_ex(encoded_key, false);
} else { /* is integer index */
char *index_int_as_str;
size_t index_int_as_str_len;

index_int_as_str_len = spprintf(&index_int_as_str, 0, ZEND_LONG_FMT, idx);

if (key_prefix && num_prefix) {
/* zend_string_concat4() */
size_t len = ZSTR_LEN(key_prefix) + num_prefix_len + index_int_as_str_len + strlen("%5D%5B");
new_prefix = zend_string_alloc(len, 0);

memcpy(ZSTR_VAL(new_prefix), ZSTR_VAL(key_prefix), ZSTR_LEN(key_prefix));
memcpy(ZSTR_VAL(new_prefix) + ZSTR_LEN(key_prefix), num_prefix, num_prefix_len);
memcpy(ZSTR_VAL(new_prefix) + ZSTR_LEN(key_prefix) + num_prefix_len, index_int_as_str, index_int_as_str_len);
memcpy(ZSTR_VAL(new_prefix) + ZSTR_LEN(key_prefix) + num_prefix_len +index_int_as_str_len, "%5D%5B", strlen("%5D%5B"));
ZSTR_VAL(new_prefix)[len] = '\0';
} else if (key_prefix) {
new_prefix = zend_string_concat3(ZSTR_VAL(key_prefix), ZSTR_LEN(key_prefix), index_int_as_str, index_int_as_str_len, "%5D%5B", strlen("%5D%5B"));
} else if (num_prefix) {
new_prefix = zend_string_concat3(num_prefix, num_prefix_len, index_int_as_str, index_int_as_str_len, "%5B", strlen("%5B"));
} else {
new_prefix = zend_string_concat2(index_int_as_str, index_int_as_str_len, "%5B", strlen("%5B"));
}
efree(index_int_as_str);
}
if (Z_TYPE_P(zdata) == IS_ARRAY) {
zend_string *new_prefix = php_url_encode_get_new_prefix(enc_type, idx, prop_name, prop_len,
num_prefix, num_prefix_len, key_prefix);
GC_TRY_PROTECT_RECURSION(ht);
php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, new_prefix, (Z_TYPE_P(zdata) == IS_OBJECT ? zdata : NULL), arg_sep, enc_type);
php_url_encode_hash_ex(Z_ARRVAL_P(zdata), formstr, NULL, 0, new_prefix, NULL, arg_sep, enc_type);
GC_TRY_UNPROTECT_RECURSION(ht);
zend_string_release_ex(new_prefix, false);
} else if (Z_TYPE_P(zdata) == IS_OBJECT) {
zend_string *new_prefix = php_url_encode_get_new_prefix(enc_type, idx, prop_name, prop_len,
num_prefix, num_prefix_len, key_prefix);
php_url_encode_object(Z_OBJ_P(zdata), formstr,
enc_type, idx,
prop_name, prop_len,
num_prefix, num_prefix_len,
new_prefix,
arg_sep);
zend_string_release_ex(new_prefix, false);
} else if (Z_TYPE_P(zdata) == IS_NULL || Z_TYPE_P(zdata) == IS_RESOURCE) {
/* Skip these types */
continue;
Expand Down Expand Up @@ -230,7 +302,16 @@ PHP_FUNCTION(http_build_query)
Z_PARAM_LONG(enc_type)
ZEND_PARSE_PARAMETERS_END();

php_url_encode_hash_ex(HASH_OF(formdata), &formstr, prefix, prefix_len, /* key_prefix */ NULL, (Z_TYPE_P(formdata) == IS_OBJECT ? formdata : NULL), arg_sep, (int)enc_type);
if (Z_TYPE_P(formdata) == IS_OBJECT) {
php_url_encode_object(Z_OBJ_P(formdata), &formstr,
(int) enc_type, /* int_index */ 0,
/* string_index */ NULL, 0,
prefix, prefix_len,
NULL,
arg_sep);
} else {
php_url_encode_hash_ex(Z_ARRVAL_P(formdata), &formstr, prefix, prefix_len, /* key_prefix */ NULL, NULL, arg_sep, (int)enc_type);
}

RETURN_STR(smart_str_extract(&formstr));
}
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/http_build_query/bug26817.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ $obj->foo();
var_dump(http_build_query($obj));
?>
--EXPECT--
string(27) "foo=lala&bar=meuh&test=test"
string(27) "test=test&foo=lala&bar=meuh"
string(9) "test=test"
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,30 @@ class StringableObject {

$o = new StringableObject();

var_dump(http_build_query(['hello', $o]));
var_dump(http_build_query($o));
var_dump(http_build_query(['hello', $o], numeric_prefix: 'prefix_'));
var_dump(http_build_query($o, numeric_prefix: 'prefix_'));

class StringableObjectWithNonInternedString {
public function __toString() : string {
return str_repeat("abcd", 3);
}
}

$o = new StringableObjectWithNonInternedString();

var_dump(http_build_query(['hello', $o]));
var_dump(http_build_query($o));
var_dump(http_build_query(['hello', $o], numeric_prefix: 'prefix_'));
var_dump(http_build_query($o, numeric_prefix: 'prefix_'));
?>
--EXPECT--
string(7) "0=hello"
string(0) ""
string(14) "prefix_0=hello"
string(0) ""
string(20) "0=hello&1=Stringable"
string(12) "0=Stringable"
string(34) "prefix_0=hello&prefix_1=Stringable"
string(19) "prefix_0=Stringable"
string(22) "0=hello&1=abcdabcdabcd"
string(14) "0=abcdabcdabcd"
string(36) "prefix_0=hello&prefix_1=abcdabcdabcd"
string(21) "prefix_0=abcdabcdabcd"
8 changes: 1 addition & 7 deletions run-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -857,13 +857,7 @@ function write_information(): void
$php_cgi_info = '';
}

if ($phpdbg) {
$phpdbg_info = shell_exec("$phpdbg $pass_options $info_params $no_file_cache -qrr \"$info_file\"");
$php_info_sep = "\n---------------------------------------------------------------------";
$phpdbg_info = "$php_info_sep\nPHP : $phpdbg $phpdbg_info$php_info_sep";
} else {
$phpdbg_info = '';
}
$phpdbg_info = '';

if (function_exists('opcache_invalidate')) {
opcache_invalidate($info_file, true);
Expand Down