regex — заставить 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-2ubuntu5.3.

66

Решение

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

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

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

<?php
$varname.ext;  /* invalid variable name */
?>

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

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

Это из http://ca.php.net/variables.external.

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

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

  • chr (32) () (пробел)
  • chr (46) (.) (точка)
  • chr (91) ([) (открытая квадратная скобка)
  • chr (128) — chr (159) (разные)

Похоже, вы застряли с этим, поэтому вам придется преобразовать подчеркивания обратно в точки в вашем скрипте, используя предложение Доунерда (Я бы просто использовал str_replace хоть.)

62

Другие решения

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

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

который даст вам массив $ _POST в формате строки запроса, точки, как и должно быть.

Затем вы можете разобрать его, если вам нужно (согласно Комментарий ПОСТЕРА)

<?php
// Function to fix up PHP's messing up input containing dots, etc.
// `$source` can be either 'POST' or '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;
}

// Wrapper functions specifically for GET and POST:
function getRealGET() { return getRealInput('GET'); }
function getRealPOST() { return getRealInput('POST'); }
?>

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

54

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

В форме вы делаете

<input name="data[database.username]">
<input name="data[database.password]">
<input name="data[something.else.really.deep]">

вместо

<input name="database.username">
<input name="database.password">
<input name="something.else.really.deep">

и в обработчике почты просто разверните его:

$posdata = $_POST['data'];

Для меня это было изменение в две строки, так как мои взгляды были полностью шаблонными.

FYI. Я использую точки в именах полей для редактирования деревьев сгруппированных данных.

24

Работа этой функции — гениальный хак, который я придумал во время своих летних каникул в 2013 году. Я когда-нибудь напишу об этом в блоге.

Это исправление работает универсально и имеет, например, глубокую поддержку массивов. a.a[x][b.a]=10, Оно использует 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;
}

И тогда вы можете вызвать эту функцию следующим образом, в зависимости от источника:

$_POST   = fix(file_get_contents('php://input'));
$_GET    = fix($_SERVER['QUERY_STRING']);
$_COOKIE = fix($_SERVER['HTTP_COOKIE']);

Для PHP ниже 5.4: использование base64_encode вместо bin2hex а также base64_decode вместо hex2bin,

17

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

Тем временем вы можете обойти эту проблему:

  1. Доступ к необработанным данным запроса через php://input для данных POST или $_SERVER['QUERY_STRING'] для получения данных
  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);
}

// work with the raw query string
$data = parse_qs($_SERVER['QUERY_STRING']);

Или же:

// handle posted data (this only works with application/x-www-form-urlencoded)
$data = parse_qs(file_get_contents('php://input'));
6

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

суть с тестами доступна и любые отзывы или предложения приветствуются здесь или там.

public function fix(&$target, $source, $keep = false) {
if (!$source) {
return;
}
$keys = array();

$source = preg_replace_callback(
'/
# Match at start of string or &
(?:^|(?<=&))
# Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
[^=&\[]*
# Affected cases: periods and spaces
(?:\.|%20)
# Keep matching until assignment, next variable, end of string or
# start of an array
[^=&\[]*
/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) {
// Only unprocess encoded keys
if (!in_array($key, $keys)) {
$target[$key] = $val;
continue;
}

$key = base64_decode($key);
$target[$key] = $val;

if ($keep) {
// Keep a copy in the underscore key version
$key = preg_replace('/(\.| )/', '_', $key);
$target[$key] = $val;
}
}
}
5

Причина этого заключается в старой функциональности PHP register_globals. . символ не является допустимым символом в имени переменной, поэтому PHP закрывает его подчеркиванием для обеспечения совместимости.

Короче говоря, не рекомендуется делать периоды в переменных URL.

4

Если ищете любой путь к в прямом смысле заставить PHP перестать заменять ‘.’ символы в массивах $ _GET или $ _POST, тогда одним из таких способов является изменение исходного кода PHP (и в этом случае это относительно просто).

ВНИМАНИЕ: Модификация исходного кода PHP C — это расширенный вариант!

Также увидеть это Отчет об ошибках PHP который предлагает такую ​​же модификацию.

Для изучения вам необходимо:

  • скачать PHP C исходный код
  • отключить . проверка замены
  • ./ Configure, делать и разверните свою индивидуальную сборку PHP

Само изменение источника тривиально и требует только обновления одна половина одной линии в main/php_variables.c:

....
/* ensure that we don't have spaces or dots in the variable name (not binary safe) */
for (p = var; *p; p++) {
if (*p == ' ' /*|| *p == '.'*/) {
*p='_';
....

Примечание: по сравнению с оригиналом || *p == '.' был закомментирован


Пример вывода:

заданный QUERY_STRING из a.a[]=bb&a.a[]=BB&c%20c=dd,
Бег <?php print_r($_GET); сейчас производит:

массив
(
[a.a] => Массив
(
[0] => бб
[1] => BB
)

[c_c] => дд
)

Заметки:

  • этот патч касается только исходного вопроса (он останавливает замену точек, а не пробелов).
  • запускать этот патч будет быстрее, чем решения на уровне сценариев, но ответы с чистым .php по-прежнему предпочтительнее (поскольку они избегают изменения самого PHP).
  • в теории здесь возможен подход с полифилом, который может сочетать подходы — тест на изменение уровня C с использованием parse_str() и (если он недоступен) использовать более медленные методы.
3