Manipulate a url string by adding GET parameters

PhpUrlString

Php Problem Overview


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?

Php Solutions


Solution 1 - Php

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.

Solution 2 - Php

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().

Solution 3 - Php

$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

Solution 4 - Php

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 ) );
}

Solution 5 - Php

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;
    }
}

Solution 6 - Php

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;
    }

Solution 7 - Php

 /**
 * @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;
}

Solution 8 - Php

<?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";

Solution 9 - Php

$parameters = array();

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

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

Solution 10 - Php

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;

Solution 11 - Php

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 "&"

Solution 12 - Php

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

Solution 13 - Php

 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;
}

Solution 14 - Php

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;
}

Solution 15 - Php

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>

Solution 16 - Php

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/

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCodeOverloadView Question on Stackoverflow
Solution 1 - PhpandrewtweberView Answer on Stackoverflow
Solution 2 - Phprybo111View Answer on Stackoverflow
Solution 3 - PhpTom ClausView Answer on Stackoverflow
Solution 4 - Phpuser2171027View Answer on Stackoverflow
Solution 5 - PhpDoug T.View Answer on Stackoverflow
Solution 6 - PhpАртур КурицынView Answer on Stackoverflow
Solution 7 - PhpLonderenView Answer on Stackoverflow
Solution 8 - Phpshengbin_xuView Answer on Stackoverflow
Solution 9 - PhpTaylorOtwellView Answer on Stackoverflow
Solution 10 - PhpHammeriteView Answer on Stackoverflow
Solution 11 - PhpTarikView Answer on Stackoverflow
Solution 12 - Phpthe_nutsView Answer on Stackoverflow
Solution 13 - PhpMaor KavodView Answer on Stackoverflow
Solution 14 - PhpRoman86View Answer on Stackoverflow
Solution 15 - Phpcsandreas1View Answer on Stackoverflow
Solution 16 - PhpkhandanielView Answer on Stackoverflow