Web

Как заставить PHP перестать заменять символы «.» в массивах $_GET или $_POST

Если я передаю переменные PHP с «.» в имени через массив $_GET, PHP автоматически заменяет их символами «_». Например:

<?php

echo "url is ".$_SERVER['REQUEST_URI']."<p>";

echo "x.y is ".$_GET['x.y'].".<p>";

echo "x_y is ".$_GET['x_y'].".<p>";

 Вывод будет таким:

url is /SpShipTool/php/testGetUrl.php?x.y=a.b

x.y is .

x_y is a.b.

 ... мой вопрос заключается в следующем: есть ли способ заставить это прекратиться? Не могу понять, что мне сделать для этого. Версия PHP, с которой я работаю, — 5.2.4.

 

Ответ 1

Вот объяснение PHP.net, почему это происходит:

Точки в именах входящих переменных

Как правило, PHP не изменяет имена переменных, когда они передаются в скрипт. Однако следует отметить, что точка не является допустимым символом в имени переменной PHP. Чтобы понять причину, посмотрите на этот код:

<?php

$varname.ext; /* недопустимое имя переменной */

?>

Теперь парсер видит переменную с именем $varname, за которой следует оператор конкатенации строк, а затем чистая строка (т. е. строка без кавычек, в которой нет ни одного известного ключевого или зарезервированного слова) «ext». Очевидно, что это не дает желаемого результата.

 По этой причине важно отметить, что PHP автоматически заменяет любые точки в именах входящих переменных на символы подчеркивания.

Это описано в документации здесь:

 http://ca.php.net/variables.external

 Также, согласно документации, есть другие символы, которые преобразуются в символы подчеркивания. Полный список символов имени поля, которые PHP преобразует в «_» (знак подчеркивания), следующий (не только точка):

  1. chr(32) ( ) (пробел)

  2. chr(46) (.) (точка)

  3. chr(91) ([) (открытая квадратная скобка)

  4. chr(128) - chr(159) (непечатные символы)

Похоже, что вам придется преобразовать подчеркивания обратно в точки в вашем скрипте, используя функцию str_replace.

 

Ответ 2

Это известный вопрос в PHP, но на самом деле есть лучший ответ (или обходной путь). PHP позволяет вам работать с необработанным входным потоком, поэтому вы можете сделать что-то вроде этого:

$query_string = file_get_contents('php://input');

 Это даст вам массив $_POST в формате строки запроса. Затем вы можете разобрать его, если вам это нужно:

<?php

// Функция для исправления того, что PHP путает ввод, содержащий точки и т. д.

// `$source` может быть либо 'POST', либо 'GET'

function getRealInput($source) {

    $pairs = explode("&", $source == 'POST' ? file_get_contents("php://input") : $_SERVER['QUERY_STRING']);

    $vars = array();

    foreach ($pairs as $pair) {

        $nv = explode("=", $pair);

        $name = urldecode($nv[0]);

        $value = urldecode($nv[1]);

        $vars[$name] = $value;

    }

    return $vars;

}

// Функции-обертки специально для GET и POST:

function getRealGET() { return getRealInput('GET'); }

function getRealPOST() { return getRealInput('POST'); }

?>

Очень полезно для параметров OpenID, которые содержат «.» и «_», каждый из которых имеет определенное значение!

 

Ответ 3

Вам нужно решение, соответствующее стандартам и работающее с большими массивами (например: ?param[2][5]=10)?

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

$_GET        = fix( $_SERVER['QUERY_STRING'] );

$_POST      = fix( file_get_contents('php://input') );

$_COOKIE = fix( $_SERVER['HTTP_COOKIE'] );

 Работа данной функции — это изящная идея, которая пришла мне в голову во время летнего отпуска. Пусть вас не смущает простой regex, он всего лишь берет все имена запросов, кодирует их (так что точки сохраняются), а затем использует обычную функцию parse_str().

function fix($source) {

    $source = preg_replace_callback(

        '/(^|(?<=&))[^=[&]+/',

        function($key) { return bin2hex(urldecode($key[0])); },

        $source

    );

    parse_str($source, $post);

    $result = array();

    foreach ($post as $key => $val) {

        $result[hex2bin($key)] = $val;

    }

    return $result;

}

 

 Ответ 4

Это происходит потому, что точка является недопустимым символом в имени переменной, причина этого лежит очень глубоко в реализации PHP, поэтому простых решений пока не существует (пока). Тем временем вы можете обойти эту проблему:

  1. Получить доступ к необработанным данным запроса через php://input для POST-данных или $_SERVER['QUERY_STRING'] для GET-данных.

  2. Использовать функцию преобразования.

Приведенная ниже функция преобразования (PHP >= 5.4) кодирует имена каждой пары ключ-значение в шестнадцатеричное представление и затем выполняет обычный parse_str(); после этого она возвращает шестнадцатеричные имена в их исходную форму:

function parse_qs($data) {

    $data = preg_replace_callback('/(?:^|(?<=&))[^=[]+/', function($match) {

        return bin2hex(urldecode($match[0]));

    }, $data);

    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);

}

// работа с необработанной строкой запроса

$data = parse_qs($_SERVER['QUERY_STRING']);

 

Или:

// обрабатываем размещенные данные (это работает только с application/x-www-form-urlencoded)

$data = parse_qs(file_get_contents('php://input'));

 

 Ответ 5

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

public function fix(&$target, $source, $keep = false) {                        

    if (!$source) {                                                            

        return;                                                                

    }                                                                          

    $keys = array();                                                           

    $source = preg_replace_callback(                                           

        '/                                                                     

        # Искать в начале строки или &                                        

        (?:^|(?<=&))                                                           

        # Исключить случаи, когда точка находится в скобках, например, foo[bar.blarg]

        [^=&\[]*                                                               

        # Затронутые случаи: точки и пробелы                                   

        (?:\.|%20)                                                             

        # Продолжайте поиск до присвоения, следующей переменной, конца строки или   

        # начала массива                                                    

        [^=&\[]*                                                               

        /x',                                                                   

        function ($key) use (&$keys) {                                         

            $keys[] = $key = base64_encode(urldecode($key[0]));                

            return urlencode($key);                                            

        },                                                                     

    $source                                                                    

    );                                                                         

    if (!$keep) {                                                              

        $target = array();                                                     

    }                                                                          

    parse_str($source, $data);                                                 

    foreach ($data as $key => $val) {                                          

        // Только необработанные кодированные ключи                                      

        if (!in_array($key, $keys)) {                                          

            $target[$key] = $val;                                              

            continue;                                                          

        }                                                                      

        $key = base64_decode($key);                                            

        $target[$key] = $val;                                                  

        if ($keep) {                                                           

            // Храните копию в версии ключа подчеркивания                       

            $key = preg_replace('/(\.| )/', '_', $key);                        

            $target[$key] = $val;                                              

        }                                                                      

    }                                                                          

}   

 

 Ответ 6

Ответ выше хорошее начало, но есть пара проблем:

  1. Он перерабатывает все, что является лишним; перерабатывать нужно только те поля, которые имеют «.» в имени.

  2. Он не может обрабатывать массивы так, как это делает родная обработка PHP, например, для ключей типа «foo.bar[]».

Приведенное ниже решение справляется с обеими этими проблемами (обратите внимание, что оно было обновлено с момента первоначального размещения). Это решение примерно на 50% быстрее, но оно не справляется с ситуациями, когда данные имеют одинаковый ключ (или ключ, который извлекается одинаково, например, foo.bar и foo_bar извлекаются как foo_bar).

<?php

public function fix2(&$target, $source, $keep = false) {                       

    if (!$source) {                                                            

        return;                                                                

    }                                                                          

    preg_match_all(                                                            

        '/                                                                     

        # Искать в начале строки или &                                        

        (?:^|(?<=&))                                                           

        # Исключить случаи, когда точка находится в скобках, например, foo[bar.blarg]

        [^=&\[]*                                                               

        # Затронутые случаи: точки и пробелы                                   

        (?:\.|%20)                                                             

        # Продолжайте поиск до присвоения, следующей переменной, конца строки или   

        # начала массива                                                    

        [^=&\[]*                                                               

        /x',                                                                   

        $source,                                                               

        $matches                                                               

    );                                                                         

    foreach (current($matches) as $key) {                                      

        $key    = urldecode($key);                                             

        $badKey = preg_replace('/(\.| )/', '_', $key);                         

        if (isset($target[$badKey])) {                                         

            // Дублирующие значения, возможно, уже сняли эту настройку                    

            $target[$key] = $target[$badKey];                                  

            if (!$keep) {                                                      

                unset($target[$badKey]);                                       

            }                                                                  

        }                                                                      

    }                                                                          

 

Схожие статьи

Web

Как преобразовать объект в массив

Web

Как определить язык браузера в PHP

Web

Как выполнить цикл по массиву в php с форматированным html оформлением?

Web

Получение элементов DOM по имени класса