0

Let's say the current query string is the following

$_SERVER['QUERY_STRING']:
apple=green&banana=yellow2&navi=blue&clouds=white&car=black

I need a function which can add and remove several parameters from the query string. For example:

echo ChangeQueryString('eyes=2&fingers=10&car=purple', 'clouds&apple');

should give

banana=yellow2&navi=blue&car=purple&eyes=2&fingers=10

So, not only should the function be able to add new parameters (eyes=2&fingers=10), but it should also change the ones which are already present (car=black => car=purple).

All these new and changed parameters could be passed in the first argument of the function. Separated by "&".

The second argument of the function should pass all the keys of the parameters which should be removed from the query string, if present.

I only managed the first part which can add and replace parameters. But maybe somebody has a more efficient way, including the second argument for removing things from the query string.

Actually in the end I need the complete current URL. That's why I added PHP_SELF. Though the removing part is not there yet...

function ChangeQueryString ($add, $remove) {

    if (empty($_SERVER['QUERY_STRING'])){

        $final_url = $_SERVER['PHP_SELF']."?".$add;

    } else {

        $query_string_url_addition = $_SERVER['QUERY_STRING'].'&'.$add;

        parse_str($query_string_url_addition, $query_array);

        $final_url = $_SERVER['PHP_SELF']."?".http_build_query($query_array);

    }

    return $final_url;

}
Dharman
  • 30,962
  • 25
  • 85
  • 135
DanielM
  • 317
  • 2
  • 11
  • 3
    would it not be MUCH easier to manipulate the `$_GET` array instead of the raw query string? – rickdenhaan Aug 15 '19 at 20:28
  • If you really want to process a query string, turn it into an associative array with `parse_str()`, manipulate that, then use `http_build_query()` to turn it back into a string. – Barmar Aug 15 '19 at 20:43

6 Answers6

3
function changeQueryString($queryStr, $updateStr, $removeStr) {
    parse_str($queryStr, $queryArr);
    parse_str($updateStr, $updateArr);
    parse_str($removeStr, $removeArr);
    $changedArr = array_merge($queryArr, $updateArr);
    foreach($removeArr as $k => $v) {
        if(array_key_exists($k, $changedArr)) {
            unset($changedArr[$k]);
        }
    }
    return http_build_query($changedArr);
}

$str = 'apple=green&banana=yellow2&navi=blue&clouds=white&car=black';
$changedQuery = changeQueryString($str, 'eyes=2&fingers=10&car=purple', 'clouds&apple');
var_dump($changedQuery);

This should work for you, utilizing parse_str(), array_merge() and http_build_query()

2

As the first comment pointed out, if you work with the $_GET global array, then you're just working with an array in which you can add, remove and generally manipulate in any fashion.

<?php
// Sample query string: ?apple=green&banana=yellow2&navi=blue&clouds=white&car=black

// You can either copy the query string to a new variable or work with the $_GET array directly. 
// In this case, I am copying it to a new array to preserve the original. 
$queryParams = $_GET;

// Get the value of clouds
$cloudColour = $queryParams ['clouds'];

// Change car from black to purple
$queryParams['car'] = 'purple';

// Change apple to red
$queryParams['apple'] = 'red';

// Remove banana
unset($queryParams['banana']);

// Add person=Mark
$queryParams['person'] = 'Mark';

Finally, as you already know, you can turn the array back into a string with http_build_query($queryParams).

waterloomatt
  • 3,662
  • 1
  • 19
  • 25
1

After you've parsed the query strings to arrays, you can merge and diff those to produce the result you want.

function ChangeQueryString($original, $add, $remove) {
    $new = [];
    parse_str($original, $new[0]);
    parse_str($add, $new[1]);
    parse_str($remove, $delete);
    return http_build_query(array_diff_key(array_merge(...$new), $delete));
}

This will only handle simple query strings. If there are any nested elements you'll need a recursive solution. But that's true for any of these answers as well as your original function.

Don't Panic
  • 41,125
  • 10
  • 61
  • 80
0

You should not alter the PHP magic GLOBALS.

Using arrays is more readable than strings. So try this modification which may fit your needs.

$queryString = 'apple=green&banana=yellow2&navi=blue&clouds=white&car=black';

echo changeQueryString($queryString, ['eyes' => 2, 'fingers' => 10], ['clouds', 'apple']);


function changeQueryString(string $queryString, array $add, array $remove): string
{
    $queryValues = [];
    parse_str($queryString ?? '', $queryValues);

    foreach ($remove as $key) {
        if (isset($queryValues[$key])) unset($queryValues[$key]);
    }

    return http_build_query(array_merge($queryValues, $add));
}
Markus Zeller
  • 8,516
  • 2
  • 29
  • 35
0

My function satisfies your request, although it does not using strings, but rather array.

function change_query( $arguments=array(), $url=false )
{
    // url
    if( $url===false ) $url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http"). "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
    
    // hash
    $hash = explode( '#', $url );
    $url = $hash[0];
    $hash = ( isset($hash[1]) )? '#'.$hash[1] : '';
    
    // query
    $query = explode( '?', $url );
    $url = $query[0];
    $query = ( isset($query[1]) )? $query[1] : '';
    parse_str( $query, $query );
    
    // remove or replace
    foreach( $arguments as $k=>$v )
    {
        if( $v==null ) unset( $query[$k] );
        else $query[$k] = $v;
    }
    
    // finalize query
    $query = ( !empty( $query ) )? '?'.http_build_query( $query ) : '' ;
    
    // return
    return $url.$query.$hash;
    
}

Please note:

  • The $url variable is built using a snippet taken from (this answer)[https://stackoverflow.com/a/6768831/162049]
  • If you pass an $url to the function, it will use that, otherwise it will use the current url of the page
  • The $arguments array has elements like =>
  • If you set => null, that variable will be removed from the query string

Usage:

echo change_query(
    array(
        'foo' => 'changed',
        'bar' => null,
        'lorem' => "Ipsum"
    ),
    'https://stackoverflow.com/?foo=test&bar=10'
);

// output
// https://stackoverflow.com/?foo=changed&lorem=Ipsum
Marco Panichi
  • 1,068
  • 1
  • 18
  • 31
-2

Since there are 2 different separator characters, that makes it a bit simpler. What we can do is first split the string by the "&", then split the parts by the "=" so it's a 2 dimensional array or a 1D array of what are effectively the key/value pairs. (In C#, it could be inserted into a Dictionary, if it helps you to think like that. Otherwise ignore what I just said.)

This would need to be done on both the existing query string and the first param of your ChangeQueryString method. Since you only have a key in the 2nd param, you can just split on the "&".

I would loop through the query array looking for the existence of the keys in the replacement array, and update based on that. Afterwards, I'd loop backwards through the query array looking for the keys in the removal array and delete those instances from the query array.

Because the 2nd one has to be done backwards to prevent issues in indexes, you can't do both loops at the same time. If you try this, or even try to do the 2nd one forwards, you need more code and it becomes convoluted very quickly. I suggest doing the removal last, since you apparently want to keep the order of existing keys, and it's only a minimal decrease in performance to do it last. This performance opinion assumes that you are not doing 10's of thousands (or more) of these params, and are sticking to web standardized allowable amounts of characters in your URL/URI.

If you decide to use the $GET array like rickdenhaan mentioned, you simply don't have to decode the query string. The rest still applies.

Of course, there are many different ways to code this, as well as different methods than I describe to do what you want. I'm simply describing what I'd do. If other people have other opinions, I recommend you post your own answer, instead of down voting or critically commenting on this answer.

As this seems possible it could be a homework question, I'm not going to code this for the OP. If it's not a homework question, I would think they should be able to figure this out on their own from what I've already given.

Hopefully I've been helpful and good luck!

computercarguy
  • 2,173
  • 1
  • 13
  • 27