Красивый способ удалить GET-переменные с помощью PHP?

94

У меня есть строка с полным URL, включая переменные GET. Как лучше всего удалить переменные GET? Есть ли хороший способ удалить только один из них?

Это код, который работает, но не очень красив (я думаю):

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

Приведенный выше код просто удаляет все переменные GET. URL-адрес в моем случае создается из CMS, поэтому мне не нужна информация о переменных сервера.

Йенс Торнелл
источник
1
Я бы придерживался того, что у вас есть, если только производительность не является проблемой. Решение для регулярных выражений, поставляемое Gumbo, будет настолько красивым, насколько возможно.
MitMaro,
Она не должна быть красивой , если это происходит в functions.php или везде , где вы скрываете свои уродливые биты, вам нужно только увидеть qs_build () , чтобы назвать это
знак вопроса
Вот способ сделать это с помощью красивой анонимной функции. stackoverflow.com/questions/4937478/…
doublejosh
Как насчет фрагмента url? Решения, которые я вижу ниже, также отбрасывают фрагмент, как и ваш код.
Marten Koetsier

Ответы:

236

Хорошо, чтобы удалить все переменные, может быть, самый красивый

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

Смотрите примерно strtokздесь .

Это самый быстрый (см. Ниже) и обрабатывает URL без символа '?' должным образом.

Чтобы взять url + querystring и удалить только одну переменную (без использования замены регулярного выражения, что в некоторых случаях может быть быстрее), вы можете сделать что-то вроде:

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

Замена регулярного выражения для удаления одной переменной может выглядеть так:

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

Вот тайминги нескольких разных методов, обеспечивающие сброс тайминга между прогонами.

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

показывает

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 побеждает, и это, безусловно, самый маленький код.

Джастин
источник
Хорошо, я передумал. кстати strtok выглядит даже лучше. Остальные функции работали не так хорошо. Я пробовал использовать функции для этих переменных get? Cbyear = 2013 & test = value и написал echo removeqsvar ($ current_url, 'cbyear'); и получил результат: amp; test = value
Jens Törnell
ах да ... регулярное выражение не завершено - ему нужно будет заменить конечный разделитель и пропустить ведущий (написал его вслепую). Однако более длинная функция должна работать нормально. preg_replace ('/([?&ght)'.$ varname.' = [^ &] + (& | $) / ',' $ 1 ', $ url) должно работать
Джастин
1
PHP 5.4, похоже, жалуется на @unset - как ни странно, ему не нравится символ @.
Артем Русаковский 07
1
не удивительно - оператор @ (скрытие ошибок) в любом случае является своего рода злом - вероятно, лучший способ сделать это в PHP 5.4 сейчас, но я не писал PHP почти 2 года, поэтому я немного не в себе практика.
Джастин
strtok rocks, +1
FrancescoMM
33

Как насчет:

preg_replace('/\\?.*/', '', $str)
Гамбо
источник
1
Определенно красивее. Интересно, какой из них будет работать лучше. +1
MitMaro
Это сэкономило мне несколько строк, и для меня это коротко и красиво. Спасибо!
Йенс Торнелл,
5
Используйте /(\\?|&)the-var=.*?(&|$)/для удаления только определенной переменной ( the-varздесь).
10

Если URL-адрес, из которого вы пытаетесь удалить строку запроса, является текущим URL-адресом PHP-скрипта, вы можете использовать один из ранее упомянутых методов. Если у вас есть только строковая переменная с URL-адресом в ней, и вы хотите удалить все, что находится за "?" ты можешь сделать:

$pos = strpos($url, "?");
$url = substr($url, 0, $pos);
Мэтт Бриджес
источник
+1, потому что это единственный другой ответ здесь, который отвечает на вопрос и предлагает альтернативу.
MitMaro
2
Следует учитывать, что URL-адрес может не содержать расширение ?. После этого ваш код вернет пустую строку.
Гамбо,
Да, чтобы поддержать то, что сказал @Gumbo, я бы изменил вторую строку на:$url = ($pos)? substr($url, 0, $pos) : $url;
CenterOrbit
7

Вдохновленный комментарием @MitMaro, я написал небольшой тест для проверки скорости решений @Gumbo, @Matt Bridges и @justin предложения в вопросе:

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;

Результат: strtok @ justin - самый быстрый.

Примечание: протестировано в локальной системе Debian Lenny с Apache2 и PHP5.

Шаррелс
источник
время выполнения регулярного выражения: 0,14591598510742 секунды; время выполнения взрыва: 0,07137393951416 секунд; Время выполнения strpos: 0,080883026123047 секунд; время выполнения токена: 0,042459011077881 секунды;
Джастин,
Очень хорошо! Я считаю, что скорость важна. Это не единственное, что произойдет. Веб-приложение может иметь сотни функций. «Все дело в деталях». Спасибо, проголосуйте!
Йенс Торнелл,
Джастин, спасибо. Теперь сценарий очищен и учитывает ваше решение.
Шаррелс,
7

Другое решение ... Я считаю эту функцию более элегантной, она также удалит завершающий символ '?' если удаляемый ключ является единственным в строке запроса.

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

Тесты:

$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/>';
}

Выведет:

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

»Запустите эти тесты на 3v4l

Катушка
источник
3

Не могли бы вы использовать для этого серверные переменные?

Или это сработает ?:

unset($_GET['page']);
$url = $_SERVER['SCRIPT_NAME'] ."?".http_build_query($_GET);

Просто мысль.

Боберт
источник
2

Вы можете использовать переменные сервера для этого, например $_SERVER['REQUEST_URI'], или даже лучше: $_SERVER['PHP_SELF'].

Шаррелс
источник
4
Это, конечно, предполагает, что URL-адрес, который он разбирает, является страницей, выполняющей анализ.
MitMaro,
0

Как насчет функции для перезаписи строки запроса, перебирая массив $ _GET

! Примерный план подходящей функции

function query_string_exclude($exclude, $subject = $_GET, $array_prefix=''){
   $query_params = array;
   foreach($subject as $key=>$var){
      if(!in_array($key,$exclude)){
         if(is_array($var)){ //recursive call into sub array
            $query_params[]  = query_string_exclude($exclude, $var, $array_prefix.'['.$key.']');
         }else{
            $query_params[] = (!empty($array_prefix)?$array_prefix.'['.$key.']':$key).'='.$var;
         }
      }
   }

   return implode('&',$query_params);
}

Что-то вроде этого было бы полезно держать под рукой для ссылок на страницы и т. Д.

<a href="?p=3&<?= query_string_exclude(array('p')) ?>" title="Click for page 3">Page 3</a>
Вопросительный знак
источник
0

basename($_SERVER['REQUEST_URI']) возвращает все после и включая '?',

В моем коде иногда мне нужны только разделы, поэтому разделите их, чтобы я мог получить ценность того, что мне нужно, на лету. Не уверен в скорости работы по сравнению с другими методами, но для меня это действительно полезно.

$urlprotocol = 'http'; if ($_SERVER["HTTPS"] == "on") {$urlprotocol .= "s";} $urlprotocol .= "://";
$urldomain = $_SERVER["SERVER_NAME"];
$urluri = $_SERVER['REQUEST_URI'];
$urlvars = basename($urluri);
$urlpath = str_replace($urlvars,"",$urluri);

$urlfull = $urlprotocol . $urldomain . $urlpath . $urlvars;
Сидупак
источник
0

На мой взгляд, лучше всего было бы так:

<? if(isset($_GET['i'])){unset($_GET['i']); header('location:/');} ?>

Он проверяет наличие параметра GET «i» и удаляет его, если он есть.

Джошуа Андерсон
источник
0

просто используйте echo'd javascript, чтобы избавить URL-адрес от любых переменных с помощью самоотправляемой пустой формы:

    <?
    if (isset($_GET['your_var'])){
    //blah blah blah code
    echo "<script type='text/javascript'>unsetter();</script>"; 
    ?> 

Затем сделайте эту функцию javascript:

    function unsetter() {
    $('<form id = "unset" name = "unset" METHOD="GET"><input type="submit"></form>').appendTo('body');
    $( "#unset" ).submit();
    }
План B
источник