124

I want to add GET parameters to URLs that may and may not contain GET parameters without repeating ? or &.

Example:

If I want to add category=action

$url="http://www.acme.com";
 // will add ?category=action at the end

$url="http://www.acme.com/movies?sort=popular";
 // will add &category=action at the end

If you notice I'm trying to not repeat the question mark if it's found.

The URL is just a string.

What is a reliable way to append a specific GET parameter?

1
  • Simply use: echo http_build_url($url, array("query" => "the=query&parts=here"), HTTP_URL_JOIN_QUERY);. But you'll need pecl install pecl_http or install jakeasmith/http_build_url via composer. Commented Jan 4, 2017 at 21:24

18 Answers 18

232

Basic method

$query = parse_url($url, PHP_URL_QUERY);

// Returns a string if the URL has parameters or NULL if not
if ($query) {
    $url .= '&category=1';
} else {
    $url .= '?category=1';
}

More advanced

$url = 'http://example.com/search?keyword=test&category=1&tags[]=fun&tags[]=great';

$url_parts = parse_url($url);
// If URL doesn't have a query string.
if (isset($url_parts['query'])) { // Avoid 'Undefined index: query'
    parse_str($url_parts['query'], $params);
} else {
    $params = array();
}

$params['category'] = 2;     // Overwrite if exists
$params['tags'][] = 'cool';  // Allows multiple values

// Note that this will url_encode all values
$url_parts['query'] = http_build_query($params);

// If you have pecl_http
echo http_build_url($url_parts);

// If not
echo $url_parts['scheme'] . '://' . $url_parts['host'] . $url_parts['path'] . '?' . $url_parts['query'];

You should put this in a function at least, if not a class.

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

9 Comments

also dont't forget to append the value of category
@DougT. not all parameters need a value. For instance, ?logout can be checked with isset($_GET["logout"])
This approach is good when you know that 'category' parameter is not already in the URL. If the parameter is in a URL already then PHP should take the value of the last occurrence of the parameter in the URL, so the solution of @andrewtweber still works. Yet, I like the following solution stackoverflow.com/a/4101638/99256 better.
-1 for not replacing a parameter if it already exists. It's not explicitly mentioned in the question, but it's logically and semantically mandatory.
Simply use: echo http_build_url($url, array("query" => "the=query&parts=here"), HTTP_URL_JOIN_QUERY);. But you'll need pecl install pecl_http or install jakeasmith/http_build_url via composer.
|
82

Here's a shorter version of the accepted answer:

$url .= (parse_url($url, PHP_URL_QUERY) ? '&' : '?') . 'category=action';

Edit: as discussed in the accepted answer, this is flawed in that it doesn't check to see if category already exists. A better solution would be to treat the $_GET for what it is - an array - and use functions like in_array().

9 Comments

Note this will only work with a single added parameter, otherwise it will append a ? if there was no original query.
@Mgamerz unless I'm misunderstanding you, that is what the OP asked for. It works multiple times, because of the . before the =.
But wont query not exist if you add a parameter to url (hence adding a ?), and if you do it again it will add another ? (Unless you're supposed to run the first line again)? How does $query get updated?
@Mgamerz I see what you mean now. Yes, you would need to repeat the first line each time to check whether $url already has parameters. Edited.
Doesn't handle URLs with #.
|
26
$data = array('foo'=>'bar',
              'baz'=>'boom',
              'cow'=>'milk',
              'php'=>'hypertext processor');

$queryString =  http_build_query($data);
//$queryString = foo=bar&baz=boom&cow=milk&php=hypertext+processor

echo 'http://domain.com?'.$queryString;
//output: http://domain.com?foo=bar&baz=boom&cow=milk&php=hypertext+processor

1 Comment

Simply use: echo http_build_url($url, array("query" => "the=query&parts=here"), HTTP_URL_JOIN_QUERY);. But you'll need pecl install pecl_http or install jakeasmith/http_build_url via composer.
10

One-liner:

$url .= (strpos($url, '?') ? '&' : '?') . http_build_query($additionalParams);

using http_build_query is recommended because it encodes special characters (for example spaces or @ in email addresses)


Improved version for 2022

This allows existing parameters to be replaced, and also preserves existing URL fragment (the part after # at the end of an URL)

function addParametersToUrl(string $url, array $newParams): string
{
    $url = parse_url($url);
    parse_str($url['query'] ?? '', $existingParams);

    $newQuery = array_merge($existingParams, $newParams);

    $newUrl = $url['scheme'] . '://' . $url['host'] . ($url['path'] ?? '');
    if ($newQuery) {
        $newUrl .= '?' . http_build_query($newQuery);
    }

    if (isset($url['fragment'])) {
        $newUrl .= '#' . $url['fragment'];
    }

    return $newUrl;
}

Testing:

$newParams = [
    'newKey' => 'newValue',
    'existingKey' => 'new',
];

echo addParametersToUrl('https://www.example.com', $newParams);
// https://www.example.com?newKey=newValue&existingKey=new
echo addParametersToUrl('https://www.example.com/', $newParams);
// https://www.example.com/dir/?newKey=newValue&existingKey=new
echo addParametersToUrl('https://www.example.com/dir/', $newParams);
// https://www.example.com/dir/?newKey=newValue&existingKey=new
echo addParametersToUrl('https://www.example.com/dir/file?foo=bar', $newParams);
// https://www.example.com/dir/file?foo=bar&newKey=newValue&existingKey=new
echo addParametersToUrl('https://www.example.com/dir/file?foo=bar&existingKey=old', $newParams);
// https://www.example.com/dir/file?foo=bar&existingKey=new&newKey=newValue
echo addParametersToUrl('https://www.example.com/dir/file?foo=bar&existingKey=old#hash', $newParams);
// https://www.example.com/dir/file?foo=bar&existingKey=new&newKey=newValue#hash
echo addParametersToUrl('https://www.example.com/dir/file#hash', $newParams);
// https://www.example.com/dir/file?newKey=newValue&existingKey=new#hash
echo addParametersToUrl('https://www.example.com/dir/file?foo=bar#hash', $newParams);
// https://www.example.com/dir/file?foo=bar&newKey=newValue&existingKey=new#hash

Comments

9

Use strpos to detect a ?. Since ? can only appear in the URL at the beginning of a query string, you know if its there get params already exist and you need to add params using &

function addGetParamToUrl(&$url, $varName, $value)
{
    // is there already an ?
    if (strpos($url, "?"))
    {
        $url .= "&" . $varName . "=" . $value; 
    }
    else
    {
        $url .= "?" . $varName . "=" . $value;
    }
}

2 Comments

Good and easy approach if you are concerned about performance, since this will be a lot faster than parse_url and also less cpu intensive. Tradeoff is that it is not checking for an existing $varName.
Classic mistake is to use strpos() return in if clause directly. $url .= (strpos($url, '?') === false ? '?' : '&').'category=action'; And performance wise would be better to use single quotes. Other than that, this solution fits the requirements better.
9

This function overwrites an existing argument

function addToURL( $key, $value, $url) {
    $info = parse_url( $url );
    parse_str( $info['query'], $query );
    return $info['scheme'] . '://' . $info['host'] . $info['path'] . '?' . http_build_query( $query ? array_merge( $query, array($key => $value ) ) : array( $key => $value ) );
}

2 Comments

What happens if there is no $info['query'] set? You haven't accounted for that.
If not set, $info['query'] becomes NULL and $query becomes an empty array. So its not an issue.
9

Example with updating existent parameters.

Also url_encode used, and possibility to don't specify parameter value

    <?
    /**
     * Add parameter to URL
     * @param string $url
     * @param string $key
     * @param string $value
     * @return string result URL
     */
    function addToUrl($url, $key, $value = null) {
        $query = parse_url($url, PHP_URL_QUERY);
        if ($query) {
            parse_str($query, $queryParams);
            $queryParams[$key] = $value;
            $url = str_replace("?$query", '?' . http_build_query($queryParams), $url);
        } else {
            $url .= '?' . urlencode($key) . '=' . urlencode($value);
        }
        return $url;
    }

Comments

5
<?php
$url1 = '/test?a=4&b=3';
$url2 = 'www.baidu.com/test?a=4&b=3&try_count=1';
$url3 = 'http://www.baidu.com/test?a=4&b=3&try_count=2';
$url4 = '/test';
function add_or_update_params($url,$key,$value){
    $a = parse_url($url);
    $query = $a['query'] ? $a['query'] : '';
    parse_str($query,$params);
    $params[$key] = $value;
    $query = http_build_query($params);
    $result = '';
    if($a['scheme']){
        $result .= $a['scheme'] . ':';
    }
    if($a['host']){
        $result .= '//' . $a['host'];
    }
    if($a['path']){
        $result .=  $a['path'];
    }
    if($query){
        $result .=  '?' . $query;
    }
    return $result;
}
echo add_or_update_params($url1,'try_count',1);
echo "\n";
echo add_or_update_params($url2,'try_count',2);
echo "\n";
echo add_or_update_params($url3,'try_count',3);
echo "\n";
echo add_or_update_params($url4,'try_count',4);
echo "\n";

1 Comment

great work, only replace the line $query = $a['query'] ? $a['query'] : ''; by $query = isset($a['query']) ? $a['query'] : '';
5
 /**
 * @example addParamToUrl('/path/to/?find=1', array('find' => array('search', 2), 'FILTER' => 'STATUS'))
 * @example addParamToUrl('//example.com/path/to/?find=1', array('find' => array('search', 2), 'FILTER' => 'STATUS'))
 * @example addParamToUrl('https://example.com/path/to/?find=1&FILTER=Y', array('find' => array('search', 2), 'FILTER' => 'STATUS'))
 *
 * @param       $url string url
 * @param array $addParams
 *
 * @return string
 */
function addParamToUrl($url, array $addParams) {
  if (!is_array($addParams)) {
    return $url;
  }

  $info = parse_url($url);

  $query = array();

  if ($info['query']) {
    parse_str($info['query'], $query);
  }

  if (!is_array($query)) {
    $query = array();
  }

  $params = array_merge($query, $addParams);

  $result = '';

  if ($info['scheme']) {
    $result .= $info['scheme'] . ':';
  }

  if ($info['host']) {
    $result .= '//' . $info['host'];
  }

  if ($info['path']) {
    $result .= $info['path'];
  }

  if ($params) {
    $result .= '?' . http_build_query($params);
  }

  return $result;
}

Comments

4
$parameters = array();

foreach ($get as $key => $value)
{
     $parameters[] = $key.'='.$value;
}

$url = 'http://example.com/movies?'.implode('&', $parameters);

Comments

3

I think you should do it something like this.

class myURL {
    protected $baseURL, $requestParameters;

    public function __construct ($newURL) {
        $this->baseurl = $newURL;
        $this->requestParameters = array();
    }

    public function addParameter ($parameter) {
        $this->requestParameters[] = $parameter;
    }

    public function __toString () {
        return $this->baseurl.
               ( count($this->requestParameters) ?
                 '?'.implode('&', $this->requestParameters) :
                 ''
                 );
    }
}

$url1 = new myURL ('http://www.acme.com');
$url2 = new myURL ('http://www.acme.com');
$url2->addParameter('sort=popular');
$url2->addParameter('category=action');
$url1->addParameter('category=action');

echo $url1."\n".$url2;

Comments

1

After searching for many resources/answers on this topic, I decided to code my own. Based on @TaylorOtwell's answer here, this is how I process incoming $_GET request and modify/manipulate each element.

Assuming the url is: http://domain.com/category/page.php?a=b&x=y And I want only one parameter for sorting: either ?desc=column_name or ?asc=column_name. This way, single url parameter is enough to sort and order simultaneously. So the URL will be http://domain.com/category/page.php?a=b&x=y&desc=column_name on first click of the associated table header row.

Then I have table row headings that I want to sort DESC on my first click, and ASC on the second click of the same heading. (Each first click should "ORDER BY column DESC" first) And if there is no sorting, it will sort by "date then id" by default.

You may improve it further, like you may add cleaning/filtering functions to each $_GET component but the below structure lays the foundation.

foreach ($_GET AS $KEY => $VALUE){
    if ($KEY == 'desc'){
        $SORT = $VALUE;
        $ORDER = "ORDER BY $VALUE DESC";
        $URL_ORDER = $URL_ORDER . "&asc=$VALUE";
    } elseif ($KEY == 'asc'){
        $SORT = $VALUE;
        $ORDER = "ORDER BY $VALUE ASC";
        $URL_ORDER = $URL_ORDER . "&desc=$VALUE";
    } else {
        $URL_ORDER .= "&$KEY=$VALUE";
        $URL .= "&$KEY=$VALUE";
    }
}
if (!$ORDER){$ORDER = 'ORDER BY date DESC, id DESC';}
if ($URL_ORDER){$URL_ORDER = $_SERVER[SCRIPT_URL] . '?' . trim($URL_ORDER, '&');}
if ($URL){$URL = $_SERVER[SCRIPT_URL] . '?' . trim($URL, '&');}

(You may use $_SERVER[SCRIPT_URI] for full URL beginning with http://domain.com)

Then I use resulting $ORDER I get above, in the MySQL query:

"SELECT * FROM table WHERE limiter = 'any' $ORDER";

Now the function to look at the URL if there is a previous sorting and add sorting (and ordering) parameter to URL with "?" or "&" according to the sequence:

        function sort_order ($_SORT){
            global $SORT, $URL_ORDER, $URL;
            if ($SORT == $_SORT){
                return $URL_ORDER;
            } else {
                if (strpos($URL, '?') !== false){
                    return "$URL&desc=$_SORT";
                } else {                        
                    return "$URL?desc=$_SORT";
                }
            }
        }

Finally, the table row header to use the function:

        echo "<th><a href='".sort_order('id')."'>ID</a></th>";

Summary: this will read the URL, modify each of the $_GET components and make the final URL with parameters of your choice with the correct form of usage of "?" and "&"

Comments

0
 public function addGetParamToUrl($url, $params)
{
    foreach ($params as $param) {
         if (strpos($url, "?"))
        {
            $url .= "&" .http_build_query($param); 
        }
        else
        {
            $url .= "?" .http_build_query($param); 
        }
    }
    return $url;
}

Comments

0

another improved function version. Mix of existing answers with small improvements (port support) and bugfixes (checking keys properly).

/**
 * @param string $url original url to modify - can be relative, partial etc
 * @param array $paramsOverride associative array, can be empty
 * @return string modified url
 */
protected function overrideUrlQueryParams($url, $paramsOverride){
    if (!is_array($paramsOverride)){
        return $url;
    }

    $url_parts = parse_url($url);

    if (isset($url_parts['query'])) {
        parse_str($url_parts['query'], $params);
    } else {
        $params = [];
    }

    $params = array_merge($params, $paramsOverride);

    $res = '';

    if(isset($url_parts['scheme'])) {
        $res .= $url_parts['scheme'] . ':';
    }

    if(isset($url_parts['host'])) {
        $res .= '//' . $url_parts['host'];
    }

    if(isset($url_parts['port'])) {
        $res .= ':' . $url_parts['port'];
    }

    if (isset($url_parts['path'])) {
        $res .= $url_parts['path'];
    }

    if (count($params) > 0) {
        $res .= '?' . http_build_query($params);
    }

    return $res;
}

Comments

0

Try this function to add URL parameters.

Then you can disable the link when parameter is set so there is no url parameter duplicate.

<?php
  function addQueryString($a)
                {
             if (empty($_SERVER['QUERY_STRING']))
               return '?' . $a;
             else if (!empty($_SERVER['QUERY_STRING']))
              return '?' . $_SERVER['QUERY_STRING'] . '&' . $a;
                }
?>

 <a href="<?php echo addQueryString('lang=en'); ?>">test</a>
 <a href="<?php echo addQueryString('category=5'); ?>">sat</a>

Comments

0

In case you are using WordPress you can simply use

    add_query_args(['sort' => 'asc'], 'http:/example.com/?search=news')

Docs https://developer.wordpress.org/reference/functions/add_query_arg/

Comments

0

Solution for Laravel

If you are using Laravel you can use a function off of the Request object.

// In a controller function
function foo(Request $request) {
    // will be www.laravel.test?hi=there
    $urlWithNewQueryParam = $request->fullUrlWithQuery(['hi' => 'there']);
}

// Outside of a controller function (like in a blade)
request()->fullUrlWithQuery(['hi' => 'there']); // will be www.laravel.test?hi=there

This will also correctly add & and overwrite query params with the same name if they already exist.

You can read more in the docs here https://laravel.com/docs/10.x/requests#retrieving-the-request-url.

Comments

0

Set and remove url query params using PHP:

<?php
function build_url(array $parts): string {
    $url = '';

    $delimiters = [
        'scheme'   => [
            'value' => '://',
            'after' => true
        ],
        'port'     => ['value' => ':'],
        'query'    => ['value' => '?'],
        'fragment' => ['value' => '#']
    ];

    foreach ($parts as $name => $part) {
        if (isset($delimiters[$name])) {
            if (!isset($delimiters[$name]['after'])) {
                $url .= $delimiters[$name]['value'] . $part;
            } else {
                $url .= $part . $delimiters[$name]['value'];
            }
        } else {
            $url .= $part;
        }
    }

    return $url;
}

function set_query_params(array $values, string $url): string {
    $parts = parse_url($url);
    $params = [];
    if (isset($parts['query'])) {
        parse_str($parts['query'], $params);
    }

    $params = array_merge($params, $values);
    $parts['query'] = http_build_query($params);

    return build_url($parts);
}

function remove_query_params(string | array $name, string $url): string {
    $parts = parse_url($url);
    if (!isset($parts['query'])) {
        throw new Exception('Query not found.');
    }
    parse_str($parts['query'], $params);

    if (is_array($name)) {
        if (empty(array_intersect($name, array_keys($params)))) {
            throw new Exception('Any params not found.');
        }

        foreach ($name as $param) {
            if (!isset($params[$param])) {
                continue;
            }

            unset($params[$param]);
        }
    } elseif (!isset($params[$name])) {
        throw new Exception("Param with name - $name not found.");
    } else {
        unset($params[$name]);
    }

    if (!empty($params)) {
        $parts['query'] = http_build_query($params);
    } else {
        unset($parts['query']);
    }

    return build_url($parts);
}

$url= set_query_params(['a' => 'b', 'c' => 'd', 'e' => 'f'], 'https://example.com');
echo "$url\n";  // output - https://example.com?a=b&c=d&e=f
echo remove_query_params(['a', 'c'], $url) . "\n"; // output - https://example.com?e=f

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.