HiveBrain v1.2.0
Get Started
← Back to all entries
patternphpMinor

Remove a parameter and its value from URL's query string

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
queryvalueremoveitsandfromstringurlparameter

Problem

I'm coding an algorithm to remove a parameter (let's call it foo) from URL strings.

Of course, after the foo parameter removal, the query string should remain valid (with a leading ? and remaining parameters separated by &).

I'd also like to remove the leading ? if foo was the only parameter.

Details:

  • Domain and pathname should be preserved.



  • The URLs may not contain a query string. Expected output is the same as input.



  • The URLs may contain a query string which does not contain the foo parameter. Expected output is the same as input.



  • The URLs are already properly URL-encoded.



  • Fragments (hashes) don't necessarily need to be kept, but it would be a nice little extra.



Input examples:

http://example.com/?foo=42
http://example.com/?foo=42&bar=43
http://example.com/?bar=43&foo=42
http://example.com/?bar=43&foo=42&baz=44
http://domain.com.uk/pathname?foo=42&bar=bar%20value
http://yahoo.com/mail
http://nofoo.com/?bar=43


Expected output:

http://example.com/
http://example.com/?bar=43
http://example.com/?bar=43
http://example.com/?bar=43&baz=44
http://domain.com.uk/pathname?bar=bar%20value
http://yahoo.com/mail
http://nofoo.com/?bar=43


My initial attempt:

preg_replace_callback('/([?&])foo=[^&]+(&|$)/', function($matches) {
    return $matches[2] ? $matches[1] : '';
}, $url);


The regex itself is rather simple. The callback logic is as follows:

  • If foo is not the last parameter (2nd capturing group is not end of string), then the whole match is replaced by the first capturing group (? or &). This handles:



  • ?foo=valuefoo&bar -> ?bar



  • &foo=valuefoo&bar -> &bar



  • If foo is the last parameter then the whole match is replaced by an empty string. This handles:



  • ?bar=valuebar&foo=valuefoo -> ?bar=valuebar



  • ?foo=valuefoo -> (empty string)



This logic seemed rather complicated, hence I rewrote it into a single regex:

preg_replace('/[?&]foo=[^&]+$|([?&])foo=[^&]+&/', '$1', $url);


Now

Solution

I would also avoid using regexes or any kind of manual string parsing, you can do it all with parse_str() and http_build_query(), thusly:

function removeAndReturn(&$url, $toRemove)
{
    $parsed = [];
    parse_str(substr($url, strpos($url, '?') + 1), $parsed);
    $removed = $parsed[$toRemove];
    unset($parsed[$toRemove]);
    $url = 'http://example.com/';
    if(!empty($parsed))
    {
        $url .= '?' . http_build_query($parsed);
    }
    return $removed;
}


Then with a simple script to test it:

$input = ['http://example.com/?foo=42',
         'http://example.com/?foo=42&bar=43',
         'http://example.com/?bar=43&foo=42',
         'http://example.com/?bar=43&foo=42&baz=44'];

$expected = ['http://example.com/',
           'http://example.com/?bar=43',
           'http://example.com/?bar=43',
           'http://example.com/?bar=43&baz=44'];

$count = count($input);
for($i = 0; $i ' .
          'URL: ' . $input[$i] . '';
    if($input[$i] === $expected[$i])
        echo 'Match';
}


You get:

Foo: 42
URL: http://example.com/
Match
Foo: 42
URL: http://example.com/?bar=43
Match
Foo: 42
URL: http://example.com/?bar=43
Match
Foo: 42
URL: http://example.com/?bar=43&baz=44
Match

Code Snippets

function removeAndReturn(&$url, $toRemove)
{
    $parsed = [];
    parse_str(substr($url, strpos($url, '?') + 1), $parsed);
    $removed = $parsed[$toRemove];
    unset($parsed[$toRemove]);
    $url = 'http://example.com/';
    if(!empty($parsed))
    {
        $url .= '?' . http_build_query($parsed);
    }
    return $removed;
}
$input = ['http://example.com/?foo=42',
         'http://example.com/?foo=42&bar=43',
         'http://example.com/?bar=43&foo=42',
         'http://example.com/?bar=43&foo=42&baz=44'];

$expected = ['http://example.com/',
           'http://example.com/?bar=43',
           'http://example.com/?bar=43',
           'http://example.com/?bar=43&baz=44'];


$count = count($input);
for($i = 0; $i < $count; $i++)
{
    $foo = removeAndReturn($input[$i], 'foo');
    echo 'Foo: ' . $foo . '<br />' .
          'URL: ' . $input[$i] . '<br />';
    if($input[$i] === $expected[$i])
        echo 'Match<br />';
}
Foo: 42
URL: http://example.com/
Match
Foo: 42
URL: http://example.com/?bar=43
Match
Foo: 42
URL: http://example.com/?bar=43
Match
Foo: 42
URL: http://example.com/?bar=43&baz=44
Match

Context

StackExchange Code Review Q#38070, answer score: 5

Revisions (0)

No revisions yet.