Получить 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.

597   13  

13 ответов:

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

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

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

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

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

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

это от http://ca.php.net/variables.external.

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

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

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

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

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

$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, которые содержат оба '.'и '_', каждый с определенным смыслом!

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

в той форме, которую вы делаете

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

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

$posdata = $_POST['data'];

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

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

работа этой функции-гениальный хак, который я придумал во время своих летних каникул в 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.

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

тем временем вы можете обойти эту проблему с помощью:

  1. доступ к данным запроса с помощью php://input для почтовых данных или $_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'));

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

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

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

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

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

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

внимание: изменение источника PHP C является расширенной опцией!

также смотрите это PHP отчет об ошибке что предполагает ту же модификацию.

чтобы исследовать вам понадобится к:

  • скачать исходный код PHP C
  • отключить . замена проверки
  • ./настроить,сделать и разверните свою индивидуальную сборку 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); в настоящее время производит:

Array
(
    [a.a] => Array
        (
            [0] => bb
            [1] => BB
        )

    [c_c] => dd
)

Примечания:

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

после просмотра решения Rok я придумал версию, в которой рассматриваются ограничения в моем ответе ниже, crb выше и решение Rok. Вижу моя улучшенная версия.


@ЦРБ по выше Это хорошее начало, но есть несколько проблем.
  • он перерабатывает все, что является излишним; только те поля, которые имеют "."в названии нужно переработать.
  • это не обрабатывайте массивы так же, как это делает собственная обработка PHP, например, для таких ключей, как "foo.бар."][

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

<?php

public function fix2(&$target, $source, $keep = false) {                       
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    preg_match_all(                                                            
        '/                                                                     
        # 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',                                                                   
        $source,                                                               
        $matches                                                               
    );                                                                         

    foreach (current($matches) as $key) {                                      
        $key    = urldecode($key);                                             
        $badKey = preg_replace('/(\.| )/', '_', $key);                         

        if (isset($target[$badKey])) {                                         
            // Duplicate values may have already unset this                    
            $target[$key] = $target[$badKey];                                  

            if (!$keep) {                                                      
                unset($target[$badKey]);                                       
            }                                                                  
        }                                                                      
    }                                                                          
}                                                                              

мое решение этой проблемы было быстрым и грязным, но мне все равно нравится. Я просто хотел опубликовать список имен файлов, которые были проверены на форме. Я использовал base64_encode чтобы закодировать имена файлов в разметке, а затем просто декодировать его с помощью base64_decode перед их использованием.

ну, функция, которую я включаю ниже, " getRealPostArray ()", не является красивым решением, но она обрабатывает массивы и поддерживает оба имени:" alpha_beta "и" alpha.бета":

  <input type='text' value='First-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='Second-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='First-_' name='alpha_beta[a.b][]' /><br>
  <input type='text' value='Second-_' name='alpha_beta[a.b][]' /><br>

в то время как var_dump($_POST) производит:

  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=4)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
          2 => string 'First-_' (length=7)
          3 => string 'Second-_' (length=8)

var_dump (getRealPostArray ()) производит:

  'alpha.beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-_' (length=7)
          1 => string 'Second-_' (length=8)

функция, для чего она стоит:

function getRealPostArray() {
  if ($_SERVER['REQUEST_METHOD'] !== 'POST') {#Nothing to do
      return null;
  }
  $neverANamePart = '~#~'; #Any arbitrary string never expected in a 'name'
  $postdata = file_get_contents("php://input");
  $post = [];
  $rebuiltpairs = [];
  $postraws = explode('&', $postdata);
  foreach ($postraws as $postraw) { #Each is a string like: 'xxxx=yyyy'
    $keyvalpair = explode('=',$postraw);
    if (empty($keyvalpair[1])) {
      $keyvalpair[1] = '';
    }
    $pos = strpos($keyvalpair[0],'%5B');
    if ($pos !== false) {
      $str1 = substr($keyvalpair[0], 0, $pos);
      $str2 = substr($keyvalpair[0], $pos);
      $str1 = str_replace('.',$neverANamePart,$str1);
      $keyvalpair[0] = $str1.$str2;
    } else {
      $keyvalpair[0] = str_replace('.',$neverANamePart,$keyvalpair[0]);
    }
    $rebuiltpair = implode('=',$keyvalpair);
    $rebuiltpairs[]=$rebuiltpair;
  }
  $rebuiltpostdata = implode('&',$rebuiltpairs);
  parse_str($rebuiltpostdata, $post);
  $fixedpost = [];
  foreach ($post as $key => $val) {
    $fixedpost[str_replace($neverANamePart,'.',$key)] = $val;
  }
  return $fixedpost;
}

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

<?php
unset($_POST);
$_POST = array();
$p0 = explode('&',file_get_contents('php://input'));
foreach ($p0 as $key => $value)
{
 $p1 = explode('=',$value);
 $_POST[$p1[0]] = $p1[1];
 //OR...
 //$_POST[urldecode($p1[0])] = urldecode($p1[1]);
}
print_r($_POST);
?>

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

мое текущее решение (на основе ответов на предыдущие темы):

function parseQueryString($data)
{
    $data = rawurldecode($data);   
    $pattern = '/(?:^|(?<=&))[^=&\[]*[^=&\[]*/';       
    $data = preg_replace_callback($pattern, function ($match){
        return bin2hex(urldecode($match[0]));
    }, $data);
    parse_str($data, $values);

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

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

Comments

    Ничего не найдено.