webew
Войти » Регистрация
 
PHP

Обработка GET-запроса в строке URL средствами PHP

2 июля 2008, 11:55

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

Данный материал устарел. За актуальной информацией рекомендуется обратиться к статье «Средства навигации по сайту: генерация с помощью PHP».

Вообще говоря, любая строка URL в терминах PCRE 1 может быть нестрого описана вот таким регулярным выражением:

/^([^?]+)(\?.*?)?(#.*)?$/

Приведенное выше регулярное выражение описывает GET-параметры как фрагмент строки адреса, начинающийся со знака вопроса и продолжающийся до конца строки или до первой решётки (#), если таковая имеется 2; про якоря (anchors) в адресах часто забывают — именно они объявляются с помощью решётки.

Вооружившись функциями для работы с регулярными выражениями, несложно проводить с GET-параметрами различные манипуляции.

Получение строки GET-запроса.

Для начала поставим самую простую задачу — получить часть URL, содержащую GET-параметры.

function ggp($url) { // get GET-parameters string
    preg_match('/^([^?]+)(\?.*?)?(#.*)?$/', $url, $matches);
    $gp = (isset($matches[2])) ? $matches[2] : '';
    return $gp;
}

Не стоит забывать, что адрес может вовсе не содержать никакого GET-запроса, и массив совпадений может не иметь второго элемента 3.

Исключение GET-запроса из URL.

Иногда нужно получить URL без GET-параметров (например, при перенаправлении запросов с помощью mod_rewrite зачастую требуется проводить анализ URL, чтобы сформировать ответ клиенту; нередко для анализа нужна только статическая часть URL, а часть, где передается GET-запрос, не нужна и даже мешает). Эта операция занимает фактически одну строку, но, чтобы не писать каждый раз однотипный код, удобно вынести его в функцию 4:

function rgp($url) { // remove GET-parameters from URL
    return preg_replace('/^([^?]+)(\?.*?)?(#.*)?$/', '$1$3', $url);
}

Замена содержимого GET-параметров.

Нередко требуется, имея адрес страницы, добавить к нему какой-нибудь GET-запрос. Как правило, это делается простым дописыванием к адресу строки вида ?имя_переменной=значение. Однако не всегда бывает заранее известно, не содержит ли уже адрес какого-нибудь GET-запроса — если вдруг содержит, то простое дописывание приведет к адресу, содержащему два знака вопроса (вида '?имя1=значение1?имя2=значение2') и потому некорректному, что приводит к необходимости на этот счет строку адреса специально проверять.

Еще более сложная ситуация возникает в случае, когда в URL уже может находиться нужный к передаче GET-параметр (т.е. с тем же именем) — в этом случае нужно даже не просто корректно дописать адрес, а найти там нужную переменную и переписать её значение. Например, было

/article.php?view=flat&page=3&mode=1#note_1

, а нужно

/article.php?view=flat&page=4&mode=1#note_1

Все эти задачи может решить функция sgp():

$url = '/article.php?view=flat&page=3&mode=1#note_1';
echo sgp($url, 'page', 4); // выведет '/article.php?view=flat&page=4&mode=1#note_1'
echo sgp($url, 'view', 'tree'); // выведет '/article.php?view=tree&page=3&mode=1#note_1'
echo sgp($url, 'view', ''); // выведет '/article.php&page=3&mode=1#note_1'

За один вызов функции можно менять и несколько переменных:

sgp(
        $url,
        array('page', 'view'),
        array(4, 'normal')
    ); // ..?view=normal&page=4&mode=1#note_1
   
sgp(
        $url,
        array('page', 'view'),
        NULL // или просто опустить третий аргумент
    ); // ..?mode=1#note_1

В примерах для краткости изложения приведены относительные имена. Функция, разумеется, может работать и с абсолютными адресами, подчиняющимися стандарту URI. Код функции достаточно прост:

function sgp($url, $varname, $value) // substitute get parameter
{
     if (is_array($varname)) {
         foreach ($varname as $i => $n) {
            $v = (is_array($value))
                  ? ( isset($value[$i]) ? $value[$i] : NULL )
                  : $value;
            $url = sgp($url, $n, $v);
         }
         return $url;
     }
     
    preg_match('/^([^?]+)(\?.*?)?(#.*)?$/', $url, $matches);
    $gp = (isset($matches[2])) ? $matches[2] : ''; // GET-parameters
    if (!$gp) return $url;
   
    $pattern = "/([?&])$varname=.*?(?=&|#|\z)/";
    if (preg_match($pattern, $gp)) {
        $substitution = ($value !== '') ? "\${1}$varname=" . preg_quote($value) : '';
        $newgp = preg_replace($pattern, $substitution, $gp); // new GET-parameters
        $newgp = preg_replace('/^&/', '?', $newgp);
    }
    else    {
        $s = ($gp) ? '&' : '?';
        $newgp = $gp.$s.$varname.'='.$value;
    }
   
    $anchor = (isset($matches[3])) ? $matches[3] : '';
    $newurl = $matches[1].$newgp.$anchor;
    return $newurl;
}

Ниже в статье приводится также версия функции без интенсивного использования регулярных выражений.

Для начала получаем GET-параметры адреса (код, аналогичный функции ggp()):

preg_match('/^([^?]+)(\?.*?)?(#.*)?$/', $url, $matches);
$gp = (isset($matches[2])) ? $matches[2] : ''; // GET-parameters

Если GET-параметры отсутствуют — заменять негде, переданный адрес заведомо останется неизменным и его можно сразу вернуть, завершив работу функции:

if (!$gp) return $url;

Далее нужно сконструировать регулярное выражение, описывающее указание в адресе на данную переменную GET-запроса. Это регулярное выражение должно ловить строку вида имя_переменной=значение, которой предшествует знак вопроса или амперсанд, и вслед за которой следует конец строки (самый, наверное, частый случай), амперсанд (если начинается объявление следующей GET-переменной) или же решётка (если адрес содержит якорь):

$pattern = "/([?&])$varname=.*?(?=&|#|\z)/";

Далее логика программы такая: если функции передается новое значение переменной, то производится замена старого значения на новое (при этом используется фрагмент строки, захваченный первой подмаской — это будет знак вопроса или амперсанд). Если же новое значение переменной — ни что иное, как пустая строка, то следует совсем исключить упоминание об этой переменной из строки запроса (т.е. получить адрес даже не вида /adress.php?v1=&v2=asdf, а просто /adress.php?v2=asdf). Такое поведение не является полностью строгим, поскольку может приводить к изменению количества передаваемых параметров GET-запроса, однако, автор считает такой вариант работы наиболее удобным, т.к. при этом происходит также очищение запроса от пустых переменных, которые в большинстве случаев являются просто мусором:

$substitution = ($value !== '') ? "\${1}$varname=" . preg_quote($value) : '';
$newgp = preg_replace($pattern, $substitution, $gp); // new GET-parameters

Нужно не забывать и о ситуации, в которой переменная запроса, которую решили убрать, следовала в запросе первой. В таком случае из адреса вида /adress.php?v1=&v2=asdf&v3=1 получится некорректный адрес: /adress.php&v2=asdf&v3=1. В этом адресе первый амперсанд нужно заменить на знак вопроса, а поскольку располагаем мы не только целым адресом, но и отдельно строкой его GET-запроса (в примере — &v2=asdf&v3=1), сделать это особенно легко:

$newgp = preg_replace('/^&/', '?', $newgp);

Если оказалось, что URL не содержит упоминания данной переменной, нужно эту переменную в него дописать, причем сделать это корректно: если URL вовсе не содержит GET-запроса (в этом случае переменная $gp будет содержать пустую строку), декларация переменной должна начинаться со знака вопроса, в противном случае — с амперсанда:

$s = ($gp) ? '&' : '?';

Далее окончательно формируем обновленную строку GET-запроса:

$newgp = $gp.$s.$varname.'='.$value;

Обрабатываем возможное наличие якоря:

$anchor = (isset($matches[3])) ? $matches[3] : '';

И, наконец, конструируем конечный адрес полностью, дописывая статическую часть адреса страницы и якорь, после чего возвращаем полученный результат:

$newurl = $matches[1].$newgp.$anchor;
return $newurl;

Следует отметить, что эту задачу можно решить и без интенсивного использования регулярных выражений:

function sgp(
        $url,
        $varname,
        $value = NULL, // если NULL - убираем переменную совсем
        $clean = TRUE // превращать ли ?one=&two=
    ) {              // в ?one&two (так адрес красивее)
   
    // Версия функции "substitue get parameter" без регулярных выражений
   
    if (is_array($varname)) {
        foreach ($varname as $i => $n) {
            $v = (is_array($value))
                 ? ( isset($value[$i]) ? $value[$i] : NULL )
                 : $value;
            $url = sgp($url, $n, $v, $clean);
        }
        return $url;
    }
   
    $urlinfo = parse_url($url);
   
    $get = (isset($urlinfo['query']))
           ? $urlinfo['query']
           : '';
   
    parse_str($get, $vars);
   
    if (!is_null($value))        // одновременно переписываем переменную
        $vars[$varname] = $value; // либо добавляем новую
    else
        unset($vars[$varname]); // убираем переменную совсем
       
    $new_get = http_build_query($vars);
   
    if ($clean)
        $new_get = preg_replace( // str_replace() выигрывает
                '/=(?=&|\z)/',     // в данном случае
                '',                // всего на 20%
                $new_get
            );
   
    $result_url =   (isset($urlinfo['scheme']) ? "$urlinfo[scheme]://" : '')
                  . (isset($urlinfo['host']) ? "$urlinfo[host]" : '')
                  . (isset($urlinfo['path']) ? "$urlinfo[path]" : '')
                  . ( ($new_get) ? "?$new_get" : '')
                  . (isset($urlinfo['fragment']) ? "#$urlinfo[fragment]" : '')
                  ;
    return $result_url;
}

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

1. Работа с PCRE-совместимыми регулярными выражениями описана на соответствующей странице руководства по PHP.

2. Более строгое регулярное выражение, описывающее URL, можно найти в стандарте, описывающем URI (и, соответственно, URL как его частный случай), однако, в данном случае работать оно будет аналогично приводимому в данной статье: модификатор ? в первой подмаске делает квантификатор + нежадным, поэтому совпадение с первой подмаской продолжается вплоть до первого знака вопроса, после чего задействуется уже вторая подмаска. По той же причине совпадение со второй подмаской прекращается, как только встречается решётка, в противном случае продолжается до конца строки.

3. С переменным количеством подмасок приходится иметь дело довольно-таки часто. В регулярном выражении /^([^?]+)(\?.*?)?(#.*)?$/ гарантировано совпадает только первая подмаска. Вторая же может совсем отсутствовать в случае статического (без GET-запроса) адреса без якоря - именно для описания такого случая и служит код

$gp = (isset($matches[2])) ? $matches[2] : '';

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

4. Вообще говоря, если положиться на корректность URL (т.е. допустить в т.ч., что знак вопроса встречается в адресе только один раз и решётка, если присутствует, следует за ним, а не перед ним), то можно использовать более простой код:

function rgp($url) { // remove GET-parameters from URL
    return preg_replace('/\?.*?(?=#|\z)/', '', $url);
    // конструкция вида (?=что_то) - т.н. "заглядывание назад" - проверка последующего текста на совпадение со строкой 'что_то'
    // данная строка совпадет, если за ней следует либо решётка, либо конец данных, обозначаемый служебной последовательностью \z
}

В принципе, уязвимость такого кода достаточно низкая: GET-параметры он успешно уберет, просто может затронуть находящуюся за решёткой-якорем часть строки, если она будет иметь вид ?что_то#что_то_еще?еще_что_то#и_т_д.... В большинстве случаев эта особенность поведения не имеет никакого значения, однако, о ней лучше знать.


© Все права на данную статью принадлежат порталу webew.ru. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.
Добавить комментарий
Отображение комментариев: Древовидное | Плоское

bur

В качестве замены функции ggp и для упрощения sgp не удобнее ли пользоваться масивом $_GET ?
02.07.2008, 12:21
Ответить

1234ru

Как мне видится, зачастую упрощения не получится, а будет, напротив, серьезное усложнение.

Представь себе, например, такую модельную задачу: есть форум, у него два (как минимум) параметра - режим просмотра (view) и что-нибудь (например, еще один режим просмотра).
Получается URL вида /showforum.php?topicid=123?view=flat&showads=1#post789

Так вот. И где-нть у тебя есть "карман" с какими-нибудь похожими темами (ну, и т.п.), причем надо, чтобы с этого места человек попадал на такой же режим просмотра темы, который ты задаешь в т.ч. с помощью GET-параметров. При формировании ссылок ты столкнешься с тем, что статическую часть (плюс, возможно, якорь) тебе нужно будет менять, а строку GET оставлять неизменной.
Вряд ли в таком случае будет удобнее руками перебирать ключи массива $_GET и лепить из них строку, предварительно еще и вылавливая из них нужные, чем в одну-две коротких строки сделать то же самое с использованием ggp().

Что там было бы с заменой параметров, я себе представляю уже достаточно смутно, но мне кажется, что еще более полная ж...

Еще не стоит забывать про случаи, при которых строка GET-запроса не соответствует текущему содержимому массива $_GET - в таком случае для обсуждаемой задачи он становится совершенно непригодным (например, обрабатываемый URL относится не к текущей, а к какой-то другой странице т др.).
То, что не убивает нас, делает нас инвалидами.
02.07.2008, 13:34
Ответить
NO USERPIC

aego

Имхо ужос =)
Гораздо проще и интуитивно понятнее реализовать это на parse_url -> explode|implode -> http_build_query
Автор фанат регэкспов?
03.07.2008, 12:42
Ответить

1234ru

Автор действительно очень большой фанат регэкспов :). Поэтому насчет интуитивной понятности кода Вы, наверное, правы (не очень много народу любят регулярные выражения).

С другой стороны, http_build_query() и parse_url() спасают убоявшихся регэкспов только на стадии получения GET-параметров.
Ну хорошо, вместо
preg_match('/^(.+?)(\?.*?)?(#.*)?$/', $url, $matches);
return $matches[2];

напишем

return parse_url($url, PHP_URL_QUERY);

Да, пока, возможно, короче.

Затем, как я понимаю, $vars = explode('&', $query). А дальше как?
foreach ($vars as $str) опять explode() и там проверять по имени ключа?
Не знаю, не знаю, я бы не сказал, что сильно удобнее. Скорее, кто как привык.

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

Плюс еще вот что:

- Ни parse_url(), ни http_build_query() не работают с относительными URL (это если вдруг кому понадобится)
- http_build_query() в PHP не ниже 5 (это мало кому будет помехой, но мало ли).
То, что не убивает нас, делает нас инвалидами.
08.07.2008, 16:40
Ответить
NO USERPIC

Sign

Цитата:
Автор действительно очень большой фанат регэкспов
и за это не может не быть уважаем)
Но по статистике независимо от используемого языка количество ошибок в коде это почти константная величина (естественно для каждого она разная), поэтому regexp-ы юзать необходимо, но и именно по этому в данном случае они не нужны, imho

Цитата:
Ни parse_url(), ни http_build_query() не работают с относительными URL

не могу согласиться ибо
[php:]
$url = 'path/qwe/asd.php?arg1=value1&arg2=value2&arr[]=foo+bar&arr[]=baz#anchor';
$parsed_url = parse_url($url);
if($parsed_url['query'])
    parse_str($parsed_url['query'], $parsed_url['query']);
print_r($parsed_url);

[Output:]
Array
(
    [path] => path/qwe/asd.php
    [query] => Array
        (
            [arg1] => value1
            [arg2] => value2
            [first] => value
            [arr] => Array
                (
                    [0] => foo bar
                    [1] => baz
                )

        )

    [fragment] => anchor
)

А с абсолютными путями всё ещё удобней: Вы также получаете протокол, порт, юзера и пароль
14.08.2008, 15:24
Ответить

1234ru

Проглядел ваш комментарий, поэтому отвечаю не совсем своевременно :).

Sign
> Ни parse_url(), ни http_build_query() не работают с относительными URL

не могу согласиться


Да, вижу; действительно работает. Ну, это они написали, а я им поверил:
http://ru2.php.net/parse_url
Цитата:
This function doesn't work with relative URLs.


Вообще, конечно, регулярные выражения мучают компьютер сильно. Поэтому, например, моя функция работает где-то в 25 раз медленнее, чем просто parse_url($url, PHP_URL_QUERY). Признаю своё решение непроизводительным.

А вообще в вашем сообщении есть шокировавшая меня информация: я-то жил и не знал, что в GET-параметрах можно массивы передавать :-O (первый раз увидел это в вашем примере).
Для массивов моя функция работать корректно не сможет, т.к. там надо еще и выяснять, который из элементов хотят заменить, что с точки зрения удобства использования имхо уже граничит с безумием.
В общем, как-то я щас подумал, функция получается, во-первых, медленная, во-вторых, неуниверсальная. Но я уж, наверное, не буду статью убирать - пускай потомки познакомятся с примером использования рег. выражений, пусть и не очень, как оказалось, жизненным.
То, что не убивает нас, делает нас инвалидами.
13.10.2008, 05:21
Ответить

bur

С массивами в get-параметрах приходится сталкиваться, когда через гет отправляется форма с чекбоксами.
К примеру у тебя есть набор чекбоксов с одинаковыми именами и разными значениями:

<input type="checkbox" name="arr" value="1" />
<input type="checkbox" name="arr" value="2" />
<input type="checkbox" name="arr" value="3" />


Чтобы чекбоксы не перезаписывали друг друга к имени добавляются квадратные скобки, которые явно указывают, что значения нужно собрать в массив:

<input type="checkbox" name="arr[]" value="1" />
<input type="checkbox" name="arr[]" value="2" />
<input type="checkbox" name="arr[]" value="3" />


Тогда и имеем урл из примера, который отлично обрабатывается в PHP, по-моему даже в массиве $_GET[] всё адекватно.
13.10.2008, 11:14
Ответить

1234ru

Ясно. Хорошо, что есть такая возможность.
Мне кажется, бывает осмысленно делать даже не name="arr[]", а name="arr[1]", name="arr[2]" и т.п.
То, что не убивает нас, делает нас инвалидами.
13.10.2008, 17:31
Ответить
NO USERPIC

rgbeast

При использовании регэкспов сохраняется порядок следования GET-параметров. Иногда это удобно, так как поисковики считают различный порядок параметров - различными URL.
08.07.2008, 22:45
Ответить

1234ru

aego
Имхо ужос =)
Гораздо проще и интуитивно понятнее реализовать это на parse_url -> explode|implode -> http_build_query


explode|implode - это грубо. Лучше parse_str. В общем, без регулярных выражений - вот (надо сказать, что без регулярных выражений выглядит как-то правильнее, хотя в скорости разницы почти нет).
То, что не убивает нас, делает нас инвалидами.
26.12.2011, 12:01
Ответить
NO USERPIC

AlexNZ

На первый взгляд статья более академического плана, нежели практического, однако приведеные функции удобны в использовании. Еще один важный момент. Regexp нужно знать обязательно, прятать тут голову в песок всё равно что поставить крест на своей работе (конечно, если вы заняты программированием серьёзно).

Использование $_GET не приносит проблем на практике, поскольку в грамотно спроектированном приложении всё должно быть "по полочкам". В таком срипте точно известно что и откуда ожидается, всё остальное нужно отфильтровать. Если у вас возникает путаница, задумайтесь, быть может стоит изменить структуру приложения.

Не стоит таскать в URL режимы просмотров и им подобные понятия, используйте сессии. Используйте для сессий cookie и перестанем уже говорить, что cookie у кого-то там отключены. Вы пишете форум для параноиков?

В чем недостаток regexp? (Это риторический вопрос). Они работают медленно, но за гибкость, которую они предоставляют надо платить, таков наш мир. Используйте другие средства по возможности.

Выражаю автору благодарность за статью, а если вы еще слабы в regexp, обязательно займитесь ими прямо сейчас. :-)
06.08.2008, 09:19
Ответить

1234ru

AlexNZ, спасибо на добром слове. Рад, что Вам понравилось.
То, что не убивает нас, делает нас инвалидами.
06.08.2008, 18:25
Ответить

Ed

Никто не может претендовать на идеальное решение. Но на решение текущей задачи - это позволительно.
Данное решение решает именно таковую.
Спасибо за статью она позволила мне двигатся дальше.

исправил маленький баг в функции sgp:
при ситуации
$url = '/article.php';
echo sgp($url, 'view', ''); // выведет '/article.php&page=3&mode=1#note_1'

выводится /article.php?view=

решение:
preg_match('/^(.+?)(\?.*?)?(#.*)?$/', $url, $matches);
$gp = (isset($matches[2])) ? $matches[2] : ''; // GET-parameters
//проверяем если GET существует
if (!isset($matches[2])) { return $url;}
11.07.2009, 17:21
Ответить

1234ru

Да, действительно. Спасибо за замечание. Внес в статью исправление.
То, что не убивает нас, делает нас инвалидами.
12.07.2009, 05:56
Ответить
NO USERPIC

smart1k

Два дня искал что-то подобное. Огромное спасибо за скрипт!
23.09.2009, 02:47
Ответить

1234ru

Добавлена поддержка списков имен и значений переменных, что позволяет менять несколько переменных за один вызов функции:

$url = '/article.php?view=flat&page=3&mode=1#note_1';

sgp(
        $url,
        array('page', 'view'),
        array(4, 'normal')
    ); // ..?view=normal&page=4&mode=1#note_1
   
sgp(
        $url,
        array('page', 'view'),
        NULL // или просто опустить третий аргумент
    ); // ..?mode=1#note_1

То, что не убивает нас, делает нас инвалидами.
31.12.2011, 16:44
Ответить
NO USERPIC

bradvad

Спасибо за полезную функцию.!
А не подскажите почему получается так, что если в строке несколько get-параметров и несколько ссылок для изменения этих параметров (каждая ссылка для своего параметра), то при переходе по второй и последующих ссылках происходит не замена/удаление) значения/параметра, а добавляется ещё один такой же параметр , но с новым значением?
Пример имеем url: site.ru/?param1=0&param2=3&para3=6
Формируем ссылки для изменения каждого параметра:
<a href="<?=sgp($stroka_get, 'param1', 2);?>">param1</a>
<a href="<?=sgp($stroka_get, 'param2', 0);?>">param2</a>
<a href="<?=sgp($stroka_get, 'param3', 1);?>">param3</a>
Кликаем по первыой ссылке получаем адрес:site.ru/?param1=2&param2=3&para3=6
Кликаем по второй получаем: site.ru/?param1=0&param2=3&para3=6&param2=0
Кликаем по третьей получаем site.ru/?param1=0&param2=3&para3=6&param2=0&para3=1
Ещё раз кликаем по второй получаем site.ru/?param1=0&param2=3&para3=6&param2=0&para3=1&param2=0
Клиакем по первой - меняется только первый site.ru/?param1=0&param2=3&para3=6&param2=0&para3=1&param2=0, т.е. не добавляется новый param1. А вот любой клик по НЕ ПЕРВОЙ ссылке приводит к появлению дубля в строке.
Как это поправить?
Спасибо!
05.12.2015, 20:56
Ответить

1234ru

bravad, код в статье несколько устарел (вот-вот выйдет статья, где будет опубликован обновленный код).
Попробуйте вот такую функцию, воспроизведется ли с ней ошибка?
(Обратите внимание, что изменился порядок аргументов - теперь параметр, значение, url (если не текущий))
    function substitute_url_get_parameter(
        $name, // $name и $value могут быть как строками, так и массивами
        $value = NULL, // если NULL - убираем переменную совсем
        $url = FALSE,
        $clean = TRUE // превращать ли ?one=&two= в ?one&two (так адрес красивее)
    )
    {              
        // sgp() из статьи http://webew.ru/articles/743.webew#sgp
        // (только там url первый, а не последний)
       
        if (!$url)
            $url = $_SERVER['REQUEST_URI'];
       
        if (is_array($name)) {
            $fn = __FUNCTION__;
            foreach ($name as $i => $n) {
                $v = (is_array($value))
                      ? ( isset($value[$i]) ? $value[$i] : NULL )
                      : $value;
                $url = $fn($n, $v, $url, $clean);
            }
            return $url;
        }
       
        $urlinfo = parse_url($url);
       
        $get = (isset($urlinfo['query']))
                 ? $urlinfo['query']
                 : '';
       
        parse_str($get, $vars);
       
        if (!is_null($value))        // одновременно переписываем переменную
            $vars[$name] = $value; // либо добавляем новую
        else
            unset($vars[$name]); // убираем переменную совсем
           
        $new_get = http_build_query($vars);
       
        if ($clean)
            $new_get = preg_replace( // str_replace() выигрывает
                    '/=(?=&|\z)/',     // в данном случае
                    '',                // всего на 20%
                    $new_get
                );
       
        $result_url =   (isset($urlinfo['scheme']) ? "$urlinfo[scheme]://" : '')
                          . (isset($urlinfo['host']) ? "$urlinfo[host]" : '')
                          . (isset($urlinfo['path']) ? "$urlinfo[path]" : '')
                          . ( ($new_get) ? "?$new_get" : '')
                          . (isset($urlinfo['fragment']) ? "#$urlinfo[fragment]" : '')
                          ;
        return $result_url;
    }

То, что не убивает нас, делает нас инвалидами.
07.12.2015, 19:33
Ответить
NO USERPIC

bradvad

Спасибо!
Но что-то опять не так.
site.ru/?param1=0&param2=3
Формируем ссылки для изменения каждого параметра:
<a href="<?=substitute_url_get_parameter('param1', 1, $stroka_get);?>">param1</a>
<a href="<?=substitute_url_get_parameter( 'param2', NULL, $stroka_get);?>">param2</a>
получаем в адресной строке следующий url^
site.ru/?param1=1&amp%3Bamp%3Bparam2=1&amp%3Bparam2=1
мои скрипты видят, что param2 стал пустым
Откуда беруться &amp%3B не понятно.
Функция сохранена в кодировке UTF-8 (без BOM)
23.12.2015, 17:32
Ответить
NO USERPIC

bradvad

Вообще хотел реализовать следующее:
<a href="<?=substitute_url_get_parameter( 'param1', 2);?>">добавить param1 со значением 2</a><a href="<?=substitute_url_get_parameter('param1', NULL);?>">Удалить param1</a>
<a href="<?=substitute_url_get_parameter( 'param2', 4);?>">Добавить param2 со значением 4</a><a href="<?=substitute_url_get_parameter('param2', NULL);?>">Удалить param2</a>
<a href="<?=substitute_url_get_parameter('param3', 1);?>"> Добавить param3 со значением 1</a><a href="<?=substitute_url_get_parameter( 'param3', NULL);?>">Удалить param3</a>

В результате получаем следующее:
Кликаем по первыой ссылке в первой строке - получаем адрес:site.ru/?param1=2
Кликаем по первой ссылке во второй строке - получаем адрес:site.ru/?param1=2&param2=4
Кликаем по первой ссылке в третьей строке - получаем адрес:site.ru/?param1=2&amp%3Bparam2=4&param3=1

Дальше кликаем, например, по второй ссылке во второй строке (удалить второй параметр) - получаем адрес: site.ru/?param1=2&amp%3Bamp%3Bparam2=4&amp%3Bparam3=1

Как сделать ссылки на добавление/изменение и удаление параметров в строку?
23.12.2015, 21:11
Ответить

1234ru

Цитата:
Откуда беруться &amp%3B не понятно.

Такое впечатление, что ваш движок прогоняет переменную $_SERVER['REQUEST_URI'] через htmlspecialchars(). В результате & превращается в &amp;, в котором точка с запятой претерпевает url-кодирование в %3B (на какой стадии - тоже вопрос) и в итоге получается &amp%3B.

Видоизмененный таким образом параметр, разумеется, функцией не распознается и замену не претерпевает.

Попробуйте куда-нибудь рядом с заменами вставить вот такой код:

$u[] = substitute_url_get_parameter('param1', 2);
$u[] = substitute_url_get_parameter('param1', NULL);
$u[] = substitute_url_get_parameter('param2', 4);
$u[] = substitute_url_get_parameter('param2', NULL);
$u[] = substitute_url_get_parameter('param3', 1);
$u[] = substitute_url_get_parameter('param2', NULL);

echo "<b>$_SERVER[REQUEST_URI]</b>";
echo '<pre>' . print_r($u, 1) . '</pre>';

Что получится?
То, что не убивает нас, делает нас инвалидами.
24.12.2015, 14:47
Ответить
NO USERPIC

bradvad

при открытии страницы с адресом site.ru/?param1=2&param2=4&param3=1
Получилось вот что
?param1=2&param2=4&param3=1

Array
(
   [0] => site.ru/?param1=2&amp%3Bparam2=4&amp%3Bparam3=1
   [1] => site.ru/?amp%3Bparam2=4&amp%3Bparam3=1
   [2] => site.ru/?param1=2&amp%3Bparam2=4&amp%3Bparam3=1&param2=4
   [3] => site.ru/?param1=2&amp%3Bparam2=4&amp%3Bparam3=1
   [4] => site.ru/?param1=2&amp%3Bparam2=4&amp%3Bparam3=1&param3=1
   [5] => site.ru/?param1=2&amp%3Bparam2=4&amp%3Bparam3=1
)

Да, именно. Появление в строке & приводит к его замене на amp%3B
24.12.2015, 19:09
Ответить

1234ru

А покажите результат print_r($_SERVER)
(доменное имя для анонимности можно заменить на site.ru)
То, что не убивает нас, делает нас инвалидами.
25.12.2015, 16:01
Ответить
NO USERPIC

bradvad

Array ( [REDIRECT_UNIQUE_ID] => Vn-kWwVlmSUAAHpwD6QAAAA2 [REDIRECT_GEOIP_ADDR] => xxx.xxx.xxx.xxx [REDIRECT_GEOIP_CONTINENT_CODE] => EU [REDIRECT_GEOIP_COUNTRY_CODE] => RU [REDIRECT_GEOIP_COUNTRY_NAME] => Russian Federation [REDIRECT_GEOIP_REGION] => 48 [REDIRECT_GEOIP_REGION_NAME] => Moscow City [REDIRECT_GEOIP_CITY] => Moscow [REDIRECT_GEOIP_DMA_CODE] => 0 [REDIRECT_GEOIP_METRO_CODE] => 0 [REDIRECT_GEOIP_AREA_CODE] => 0 [REDIRECT_GEOIP_LATITUDE] => 55.752201 [REDIRECT_GEOIP_LONGITUDE] => 37.615601 [REDIRECT_GEOIP_POSTAL_CODE] => 101194 [REDIRECT_HTTP_IF_MODIFIED_SINCE] => [REDIRECT_HTTP_IF_NONE_MATCH] => [REDIRECT_STATUS] => 200 [UNIQUE_ID] => Xx-xXxXXXXXXXX [GEOIP_ADDR] => xxx.xxx.xxx.xxx [GEOIP_CONTINENT_CODE] => EU [GEOIP_COUNTRY_CODE] => RU [GEOIP_COUNTRY_NAME] => Russian Federation [GEOIP_REGION] => 48 [GEOIP_REGION_NAME] => Moscow City [GEOIP_CITY] => Moscow [GEOIP_DMA_CODE] => 0 [GEOIP_METRO_CODE] => 0 [GEOIP_AREA_CODE] => 0 [GEOIP_LATITUDE] => 55.752201 [GEOIP_LONGITUDE] => 37.615601 [GEOIP_POSTAL_CODE] => 101194 [HTTP_IF_MODIFIED_SINCE] => [HTTP_IF_NONE_MATCH] => [HTTP_HOST] => site.ru [HTTP_X_SERVER_ADDR] => xxx.xxx.xxx.xxx [HTTP_X_FORWARDED_PROTO] => http [HTTP_X_REAL_IP] => xxx.xxx.xxx.xxx [HTTP_USER_AGENT] => Mozilla/5.0 (Windows NT 5.1; rv:43.0) Gecko/20100101 Firefox/43.0 [HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 [HTTP_ACCEPT_LANGUAGE] => ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 [HTTP_ACCEPT_ENCODING] => gzip, deflate [HTTP_COOKIE] => CookieID=xxxxxxxxxxXXXXXXXXXXXXXXX; nc_welcome_is_showed=1; SCROLLER_X=304; QUICK_BAR_CLOSED=-1; sid=YYYYYYYYYYYYYYYYYYYYY; PHP_AUTH_SID=ZZZZZZZZZZZZZZZZZZZZZZZ; PHP_AUTH_LANG=Russian [PATH] => /usr/local/bin:/usr/bin:/bin [LD_LIBRARY_PATH] => /opt/apache/2.4.10/lib [SERVER_SIGNATURE] =>
Apache/2.4.10-1-beget-reuseport (Unix) Server at site.ru Port 80
[SERVER_SOFTWARE] => Apache/2.4.10-1-beget-reuseport (Unix) [SERVER_NAME] => site.ru [SERVER_ADDR] => xxx.xxx.xxx.xxx [SERVER_PORT] => 80 [REQUEST_SCHEME] => http [REMOTE_ADDR] => xxx.xxx.xxx.xxx [DOCUMENT_ROOT] => /home/s/XXX/site.ru/public_html [CONTEXT_PREFIX] => [CONTEXT_DOCUMENT_ROOT] => /home/s/XXX/bsite.ru/public_html [SERVER_ADMIN] => webmaster@site.ru [SCRIPT_FILENAME] => /home/s/XXX/site.ru/public_html/cms/require/e404.php [REMOTE_PORT] => 19434 [REDIRECT_QUERY_STRING] => param1=1&param2=2&param3=2 [REDIRECT_URL] => / [GATEWAY_INTERFACE] => CGI/1.1 [SERVER_PROTOCOL] => HTTP/1.1 [REQUEST_METHOD] => GET [QUERY_STRING] => param1=1&param2=2&param3=2 [REQUEST_URI] => /?param1=1&param2=2&param3=2 [SCRIPT_NAME] => /CMS/require/e404.php [PHP_SELF] => /CMS/require/e404.php [REQUEST_TIME] => 1451222107 [argv] => Array ( [0] => param1=1&param2=2&param3=2 ) [argc] => 1 ) 1
27.12.2015, 16:22
Ответить

1234ru

Странно. У вас echo $_SERVER['REQUEST_URI'] дает адрес без слэша - ?param1=2&param2=4&param3=1, а print_r($_SERVER показывает, что REQUEST_URI содержит адрес со слэшом: /?param1=2&param2=4&param3=1.
Попробуйте в одном скрипте сразу код из предыдущего сообщения, а затем print_r($_SERVER) (весь вывод сюда копировать необязательно - посмотрите только то, что в REQUEST_URI).
То, что не убивает нас, делает нас инвалидами.
28.12.2015, 12:05
Ответить

1234ru

Кстати. Опубликована статья, посвященная генерации HTML-кода средств навигации.
Взгляните, возможно, вам будет интересно: http://webew.ru/articles/5510.webew
То, что не убивает нас, делает нас инвалидами.
25.12.2015, 16:03
Ответить
Добавить комментарий
Отображение комментариев: Древовидное | Плоское
© 2007—2016 webew.ru, связаться: x собака webew.ru
Сайт использует Flede и соответствует стандартам WAI-WCAG 1.0 на уровне A.
Rambler's Top100

Реклама: