Если я передаю переменные 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 преобразует в «_» (знак подчеркивания), следующий (не только точка):
chr(32) ( ) (пробел)
chr(46) (.) (точка)
chr(91) ([) (открытая квадратная скобка)
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, поэтому простых решений пока не существует (пока). Тем временем вы можете обойти эту проблему:
Получить доступ к необработанным данным запроса через php://input для POST-данных или $_SERVER['QUERY_STRING'] для GET-данных.
Использовать функцию преобразования.
Приведенная ниже функция преобразования (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
Ответ выше — хорошее начало, но есть пара проблем:
Он перерабатывает все, что является лишним; перерабатывать нужно только те поля, которые имеют «.» в имени.
Он не может обрабатывать массивы так, как это делает родная обработка 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