83

Is there a way to use http_build_query() without having it URL encode according to some RFC standard?

Why I don't want to URL encode everything: I'm querying the Ebay API.. They honestly insist on parameter names not being URL encoded, as far as commas in parentheses. E.g. DomainName(0) is a parameter, and the query fails if those parens are encoded.

0

10 Answers 10

170

You can use urldecode() function on a result string which you get from http_build_query()

Sign up to request clarification or add additional context in comments.

3 Comments

You could create your own function for it, http_build_query_decoded(). You would have to copy the parameters, though, since you need to pass them to http_build_query.
This problem no longer exists in PHP 5.4 and above, the ampersand does not get url encoded.
@BasilMusa The keys and values still get url encoded. This solution decodes those too.
8

PHP 5.3.1 (Buggy Behavior) http_build_query DOES escape the '&' ampersand character that joins the parameters. Example: user_id=1&setting_id=2.

PHP 5.4+ http_build_query DOES NOT escape the '&' ampersand character that joins the parameters. Example: user_id=1&setting_id=2

Example:

$params = array(
    'user_id' => '1', 
    'setting_id' => '2'
);
echo http_build_query($params);

// Output for PHP 5.3.1:
user_id=1&setting_id=2   // NOTICE THAT: '&' character is escaped

// Output for PHP 5.4.0+:
user_id=1&setting_id=2       // NOTICE THAT: '&' character is NOT escaped

Solution when targeting multiple version

Option #1: Write a wrapper function:

/** 
 * This will work consistently and will never escape the '&' character
 */
function buildQuery($params) {
    return http_build_query($params, '', '&');
}

Option #2: Ditch the http_build_query function and write your own.

2 Comments

How about the php.ini core setting arg_separator.output link? I'm using PHP 5.4.34, and it has the output separator set to & while the input separator is &. Seems very strange to me.
@sootsnoot a bit late but for anyone reading your question: "arg_separator: arg_separator.output is used to separate arguments but may be overridden by specifying this parameter."
6

Nope, it appears to always want to encode (which it should, it is meant to URL encode when building a list of params for a URL).

You could make your own...

$params = array('a' => 'A', 'b' => 'B');

$paramsJoined = array();

foreach($params as $param => $value) {
   $paramsJoined[] = "$param=$value";
}

$query = implode('&', $paramsJoined);

CodePad.

1 Comment

this one will not handle nested arrays.
4

You might want to try their JSON API instead. I tried to get a working sample, but I don't have an app name so I can't verify the result. Here's the code:

<?php
$appName = "Your App Name Here";

$post_data = array(
  'jsonns.xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  'jsonns.xs' => 'http://www.w3.org/2001/XMLSchema',
  'jsonns.tns' => 'http://www.ebay.com/marketplace/search/v1/services',
  'tns.findItemsByKeywordsRequest' => array(
    'keywords' => 'harry potter pheonix'
  )
);

$headers = array(
  "X-EBAY-SOA-REQUEST-DATA-FORMAT: JSON", 
  "X-EBAY-SOA-RESPONSE-DATA-FORMAT: JSON",
  "X-EBAY-SOA-OPERATION-NAME: findItemsByKeywords",
  "X-EBAY-SOA-SECURITY-APPNAME: $appName"
);

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'http://svcs.ebay.com/services/search/FindingService/v1');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$result = curl_exec($ch);
if($result) {
  $response = json_decode($result);
}

curl_close($ch);
?>

You'll need to to fill $appName with whatever the name of the app is. Also the X-EBAY-SOA-OPERATION-NAME will need to be set to the actual call, and the JSON modified if the call is different.

Comments

4

http_build_query() is INVALID without urlencoding. Maybe you accidentally double-encode it? For example, try to build http query for this array:

$params = array('a' => 'a&b=b');

Without encoding, you would get

a=a&b=b

When properly encoded, you would get

a=a%26b%3Db

which is correct. If you skip encoding, be sure to avoid URL injection attacks.

Comments

3

I was trying to create a GET string to attach it to the end of a url like this -

$get_params = array(
    'include' => array(
        'enrollments',
        'current_grading_period_scores'
    ),
    'enrollment_type' => array(
        'student',
    ),

);
$get_params = http_build_query($get_params);
$get_params = urldecode($get_params);
$url = $domain.$slug;
$url = $url.'?'.$get_params;
echo $url;

which prints out

include[0]=enrollments&include[1]=current_grading_period_scores&enrollment_type[0]=student

But my api didn't like the numbers in the square brackets, so I found a regular expression that removes the numbers -

preg_replace('/[[0-9]+]/', '[]', $get_params);

The final code, and the result -

$get_params = array(
    'include' => array(
        'enrollments',
        'current_grading_period_scores'
    ),
    'enrollment_type' => array(
        'student',
    ),

);
$get_params = http_build_query($get_params);
$get_params = urldecode($get_params);
$get_params = preg_replace('/[[0-9]+]/', '[]', $get_params);
$url = $domain.$slug;
$url = $url.'?'.$get_params;
echo $url;

Prints out -

include[]=enrollments&include[]=current_grading_period_scores&enrollment_type[]=student

If someone knows of a better regex let me know, I'm kind of a noob with it.

Comments

3

You can use urldecode(). Or use http_build_query with the $arg_separator argument.

$query_data= $data = array('bar', 'baz'=>'boom');
$numeric_prefix= 'test_';
$arg_separator = '&';
$http_query = http_build_query ( $query_data, $numeric_prefix, $arg_separator );
var_dump( $http_query );

the output of the above is

string 'test_0=bar&baz=boom' (length=19)

numeric_prefix: if the array indexes are numbers, this string is added as prefix in each index. In this case the 'test_0=bar'.

arg_separator : is used to separate arguments. If non is given php use the arg_separator.output difined in php.ini

See php http_build_query

Comments

0

my take on alex's answer but is faster

$params = array('a' => 'A', 'b' => 'B');
$query = '';

foreach ($params as $param => $value) {
   $query .= $param.'='.$value .'&';
}

echo substr($query, 0, -1);

1 Comment

You can use the second parameter of trim() also to specify characters to be trimmed. E.g. I would use trim($query, '&') instead of substr($query, 0, -1).
0

This could also work

$fields = array(
    'a' => 'A', 
    'b' => 'B'
 );

$separator = '';
foreach($fields as $key=>$value) {
    $fields_string .= $separator . $key . '=' . $value; 
    $separator = '&'; 
}

Comments

0

I tried do this recently https://gist.github.com/marcocesarato/4293986fe93139f04d8269aa3fbd47e9 the difference is that this function is array recursive and compatible with PHP4. You can manage here the urlencode

<?php
/**
 * Build URL query params
 * as http_build_query build a query url the difference is 
 * that this function is array recursive and compatible with PHP4
 *
 * @author Marco Cesarato <[email protected]>
 * @param $query
 * @param string $parent
 * @return string
 *
 * @example
 * $p = array('abbreviations' => array('google' => 'ggle', 'facebook' => array('abb_key' => 'fbook', 'fcbk')), 'key' => 'value');
 * echo url_build_query($p);
 */
function url_build_query($query, $parent = null){
    $query_array = array();
    foreach($query as $key => $value){
        $_key = empty($parent) ?  urlencode($key) : $parent . '[' . urlencode($key) . ']';
        if(is_array($value)) {
            $query_array[] = url_build_query($value, $_key);
        } else {
            $query_array[] = $_key . '=' . urlencode($value);
        }
    }
    return implode('&', $query_array);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.