Beautiful way to remove GET-variables with PHP?

168,487

Solution 1

Ok, to remove all variables, maybe the prettiest is

$url = strtok($url, '?');

See about strtok here.

Its the fastest (see below), and handles urls without a '?' properly.

To take a url+querystring and remove just one variable (without using a regex replace, which may be faster in some cases), you might do something like:

function removeqsvar($url, $varname) {
    list($urlpart, $qspart) = array_pad(explode('?', $url), 2, '');
    parse_str($qspart, $qsvars);
    unset($qsvars[$varname]);
    $newqs = http_build_query($qsvars);
    return $urlpart . '?' . $newqs;
}

A regex replace to remove a single var might look like:

function removeqsvar($url, $varname) {
    return preg_replace('/([?&])'.$varname.'=[^&]+(&|$)/','$1',$url);
}

Heres the timings of a few different methods, ensuring timing is reset inbetween runs.

<?php

$number_of_tests = 40000;

$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
$starttime = $mtime;

for($i = 0; $i < $number_of_tests; $i++){
    $str = "http://www.example.com?test=test";
    preg_replace('/\\?.*/', '', $str);
}
$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
$endtime = $mtime;
$totaltime = ($endtime - $starttime);
echo "regexp execution time: ".$totaltime." seconds; ";

$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
$starttime = $mtime;
for($i = 0; $i < $number_of_tests; $i++){
    $str = "http://www.example.com?test=test";
    $str = explode('?', $str);
}
$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
$endtime = $mtime;
$totaltime = ($endtime - $starttime);
echo "explode execution time: ".$totaltime." seconds; ";

$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
$starttime = $mtime;
for($i = 0; $i < $number_of_tests; $i++){
    $str = "http://www.example.com?test=test";
    $qPos = strpos($str, "?");
    $url_without_query_string = substr($str, 0, $qPos);
}
$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
$endtime = $mtime;
$totaltime = ($endtime - $starttime);
echo "strpos execution time: ".$totaltime." seconds; ";

$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
$starttime = $mtime;
for($i = 0; $i < $number_of_tests; $i++){
    $str = "http://www.example.com?test=test";
    $url_without_query_string = strtok($str, '?');
}
$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
$endtime = $mtime;
$totaltime = ($endtime - $starttime);
echo "tok execution time: ".$totaltime." seconds; ";

shows

regexp execution time: 0.14604902267456 seconds; explode execution time: 0.068033933639526 seconds; strpos execution time: 0.064775943756104 seconds; tok execution time: 0.045819044113159 seconds; 
regexp execution time: 0.1408839225769 seconds; explode execution time: 0.06751012802124 seconds; strpos execution time: 0.064877986907959 seconds; tok execution time: 0.047760963439941 seconds; 
regexp execution time: 0.14162802696228 seconds; explode execution time: 0.065848112106323 seconds; strpos execution time: 0.064821004867554 seconds; tok execution time: 0.041788101196289 seconds; 
regexp execution time: 0.14043688774109 seconds; explode execution time: 0.066350221633911 seconds; strpos execution time: 0.066242933273315 seconds; tok execution time: 0.041517972946167 seconds; 
regexp execution time: 0.14228296279907 seconds; explode execution time: 0.06665301322937 seconds; strpos execution time: 0.063700199127197 seconds; tok execution time: 0.041836977005005 seconds; 

strtok wins, and is by far the smallest code.

Solution 2

How about:

preg_replace('/\\?.*/', '', $str)

Solution 3

If the URL that you are trying to remove the query string from is the current URL of the PHP script, you can use one of the previously mentioned methods. If you just have a string variable with a URL in it and you want to strip off everything past the '?' you can do:

$pos = strpos($url, "?");
$url = substr($url, 0, $pos);

Solution 4

Another solution... I find this function more elegant, it will also remove the trailing '?' if the key to remove is the only one in the query string.

/**
 * Remove a query string parameter from an URL.
 *
 * @param string $url
 * @param string $varname
 *
 * @return string
 */
function removeQueryStringParameter($url, $varname)
{
    $parsedUrl = parse_url($url);
    $query = array();

    if (isset($parsedUrl['query'])) {
        parse_str($parsedUrl['query'], $query);
        unset($query[$varname]);
    }

    $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
    $query = !empty($query) ? '?'. http_build_query($query) : '';

    return $parsedUrl['scheme']. '://'. $parsedUrl['host']. $path. $query;
}

Tests:

$urls = array(
    'http://www.example.com?test=test',
    'http://www.example.com?bar=foo&test=test2&foo2=dooh',
    'http://www.example.com',
    'http://www.example.com?foo=bar',
    'http://www.example.com/test/no-empty-path/?foo=bar&test=test5',
    'https://www.example.com/test/test.test?test=test6',
);

foreach ($urls as $url) {
    echo $url. '<br/>';
    echo removeQueryStringParameter($url, 'test'). '<br/><br/>';
}

Will output:

http://www.example.com?test=test
http://www.example.com

http://www.example.com?bar=foo&test=test2&foo2=dooh
http://www.example.com?bar=foo&foo2=dooh

http://www.example.com
http://www.example.com

http://www.example.com?foo=bar
http://www.example.com?foo=bar

http://www.example.com/test/no-empty-path/?foo=bar&test=test5
http://www.example.com/test/no-empty-path/?foo=bar

https://www.example.com/test/test.test?test=test6
https://www.example.com/test/test.test

» Run these tests on 3v4l

Solution 5

Inspired by the comment of @MitMaro, I wrote a small benchmark to test the speed of solutions of @Gumbo, @Matt Bridges and @justin the proposal in the question:

function teststrtok($number_of_tests){
    for($i = 0; $i < $number_of_tests; $i++){
      $str = "http://www.example.com?test=test";
      $str = strtok($str,'?');
    }
}
function testexplode($number_of_tests){
    for($i = 0; $i < $number_of_tests; $i++){
      $str = "http://www.example.com?test=test";
      $str = explode('?', $str);
    }
}
function testregexp($number_of_tests){
    for($i = 0; $i < $number_of_tests; $i++){
      $str = "http://www.example.com?test=test";
      preg_replace('/\\?.*/', '', $str);
    }
}
function teststrpos($number_of_tests){
    for($i = 0; $i < $number_of_tests; $i++){
      $str = "http://www.example.com?test=test";
      $qPos = strpos($str, "?");
      $url_without_query_string = substr($str, 0, $qPos);
    }
}

$number_of_runs = 10;
for($runs = 0; $runs < $number_of_runs; $runs++){

  $number_of_tests = 40000;
  $functions = array("strtok", "explode", "regexp", "strpos");
  foreach($functions as $func){
    $starttime = microtime(true);
    call_user_func("test".$func, $number_of_tests);
    echo $func.": ". sprintf("%0.2f",microtime(true) - $starttime).";";
  }
  echo "<br />";
}
strtok: 0.12;explode: 0.19;regexp: 0.31;strpos: 0.18;
strtok: 0.12;explode: 0.19;regexp: 0.31;strpos: 0.18;
strtok: 0.12;explode: 0.19;regexp: 0.31;strpos: 0.18;
strtok: 0.12;explode: 0.19;regexp: 0.31;strpos: 0.18;
strtok: 0.12;explode: 0.19;regexp: 0.31;strpos: 0.18;
strtok: 0.12;explode: 0.19;regexp: 0.31;strpos: 0.18;
strtok: 0.12;explode: 0.19;regexp: 0.31;strpos: 0.18;
strtok: 0.12;explode: 0.19;regexp: 0.31;strpos: 0.18;
strtok: 0.12;explode: 0.19;regexp: 0.31;strpos: 0.18;
strtok: 0.12;explode: 0.19;regexp: 0.31;strpos: 0.18;

Result: @justin's strtok is the fastest.

Note: tested on a local Debian Lenny system with Apache2 and PHP5.

Share:
168,487

Related videos on Youtube

Jens Törnell
Author by

Jens Törnell

Updated on April 23, 2020

Comments

  • Jens Törnell
    Jens Törnell about 4 years

    I have a string with a full URL including GET variables. Which is the best way to remove the GET variables? Is there a nice way to remove just one of them?

    This is a code that works but is not very beautiful (I think):

    $current_url = explode('?', $current_url);
    echo $current_url[0];
    

    The code above just removes all the GET variables. The URL is in my case generated from a CMS so I don't need any information about server variables.

    • MitMaro
      MitMaro over 14 years
      I would stick with what you have unless performance is not an issue. The regex solution supplied by Gumbo is going to be be as pretty as it gets.
    • Question Mark
      Question Mark over 14 years
      It Doesn't need to be beautiful if it's going in functions.php or whereever you hide your ugly bits, you'll only need to see qs_build() to call it
    • doublejosh
      doublejosh almost 11 years
      Here's a way to do this via a nice anonymous function. stackoverflow.com/questions/4937478/…
    • Marten Koetsier
      Marten Koetsier almost 5 years
      How about the url fragment? The solutions I see below all discard the fragment as well, just as your code does.
  • MitMaro
    MitMaro over 14 years
    This assumes of course that the url he is parsing is the page doing the parsing.
  • MitMaro
    MitMaro over 14 years
    Definitely prettier. I wonder which one would perform better though. +1
  • MitMaro
    MitMaro over 14 years
    +1 because its the only other answer here that answers the question and provides an alternative.
  • Jens Törnell
    Jens Törnell over 14 years
    This saved me a few rows and for me this is short and beautiful. Thank you!
  • Gumbo
    Gumbo over 14 years
    You should consider that the URL might not contain a ?. Your code will then return an empty string.
  • Justin
    Justin over 14 years
    regexp execution time: 0.14591598510742 seconds; explode execution time: 0.07137393951416 seconds; strpos execution time: 0.080883026123047 seconds; tok execution time: 0.042459011077881 seconds;
  • Jens Törnell
    Jens Törnell over 14 years
    Very nice! I think speed is important. It's not the only thing that is going to happen. A web application could have hundreds of functions. "It's all in the details". Thanks, vote up!
  • Scharrels
    Scharrels over 14 years
    Justin, thanks. The script is now cleaned up and takes your solution into account.
  • Jens Törnell
    Jens Törnell over 14 years
    Ok, I changed my mind. strtok way looks even better. The other functions did not work that well. I tried the functions on these get variables ?cbyear=2013&test=value and wrote echo removeqsvar($current_url, 'cbyear'); and got the result: amp;test=value
  • Justin
    Justin over 14 years
    ah yeah... the regex is not complete - it'll need to replace the trailing delimiter and miss the leading one (wrote it blind). The longer function should still work fine though. preg_replace('/([?&])'.$varname.'=[^&]+(&|$)/','$1',$url) should work
  • Artem Russakovskii
    Artem Russakovskii over 10 years
    PHP 5.4 seems to complain about @unset - it doesn't like the @ symbol, oddly.
  • Justin
    Justin over 10 years
    not surprising - the @ operator (hide errors) is sort of evil anyway - theres probably a better way of doing it in PHP 5.4 now, but I haven't been writing PHP for almost 2 years now so I'm a bit out of practice.
  • Admin
    Admin over 9 years
    Use /(\\?|&)the-var=.*?(&|$)/ to remove only a specific variable (the-var here).
  • FrancescoMM
    FrancescoMM about 7 years
    strtok rocks, +1
  • CenterOrbit
    CenterOrbit over 5 years
    Yeah to back what @Gumbo said, I would change the second line to: $url = ($pos)? substr($url, 0, $pos) : $url;