Get PHP to stop replaying'.'znaki w tablicach $ GET czy $ POST?

Jeśli podam zmienne PHP z . w ich nazwach przez $_GET PHP automatycznie zastąpi je znakami _. Na przykład:

<?php
echo "url is ".$_SERVER['REQUEST_URI']."<p>";
echo "x.y is ".$_GET['x.y'].".<p>";
echo "x_y is ".$_GET['x_y'].".<p>";

... wyjście:

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

... moje pytanie brzmi: czy jest jakiś sposób, żeby to powstrzymać? Nie mogę na całe życie dowiedzieć się, co zrobiłem, aby na to zasłużyć

Wersja PHP, z którą pracuję to 5.2.4-2ubuntu5.3.

Author: eLRuLL, 2008-09-16

13 answers

Oto PHP.net wyjaśnienie dlaczego to robi:

Kropki w nazwach zmiennych przychodzących

Zazwyczaj PHP nie zmienia nazwy zmiennych, gdy są / align = "left" / Jednak to należy zauważyć, że kropka (kropka, kropka) nie jest prawidłowym znakiem w nazwa zmiennej PHP. "For the reason", Zobacz też:

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

Teraz, co parser widzi zmienną o nazwie $varname, po którym następuje łańcuch operator konkatenacji, a następnie barestring (tj. unquoted string który nie pasuje do żadnego znanego klucza lub słowa zastrzeżone) "ext". Oczywiście, to nie ma zamierzonego rezultatu.

Z tego powodu ważne jest, aby zauważ, że PHP będzie automatycznie zamień kropki w zmiennej przychodzącej nazwiska z podkreśleniem.

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

Również, zgodnie z ten komentarz pozostałe znaki są konwertowane na podkreślniki:

Pełna lista nazw pól znaki, które PHP konwertuje na _ (podkreślenie) są następujące (nie tylko kropka):

  • chr (32) () (spacja)
  • chr(46) (.) (kropka)
  • chr (91) ([) (otwarty wspornik kwadratowy)
  • chr(128) - chr (159) (różne)

Więc wygląda na to, że utknąłeś z nim, więc będziesz musiał przekonwertować podkreślniki z powrotem na kropki w swoim skrypcie za pomocą sugestii dawnerda (użyłbym tylko str_replace.)

 66
Author: Jeremy Ruten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Dawno temu odpowiedział na pytanie, ale w rzeczywistości jest lepsza odpowiedź (lub obejście). PHP pozwala ci na surowy strumień wejściowy , więc możesz zrobić coś takiego:

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

Który da ci tablicę $_POST w formacie ciągu zapytania, kropki tak jak powinny być.

Możesz je następnie przeanalizować, jeśli potrzebujesz (zgodnie z komentarzem Postera )

<?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'); }
?>

Bardzo przydatne dla parametrów OpenID, które zawierają oba '.'i'_', każdy z określonym znaczeniem!

 59
Author: crb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-23 19:35:39

Podkreślając rzeczywistą odpowiedź Johana w komentarzu powyżej - właśnie owinąłem cały mój post w tablicę najwyższego poziomu, która całkowicie omija problem bez konieczności ciężkiego przetwarzania.

In the form you do

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

Zamiast

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

I w programie obsługi postów, po prostu go rozpakuj:

$posdata = $_POST['data'];

Dla mnie była to Dwuliniowa zmiana, ponieważ moje poglądy były całkowicie wzorowane.

Dla twojej wiadomości. Używam kropek w nazwach pól do edycji drzew zgrupowanych danych.

 27
Author: scipilot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-04 01:36:13

Działanie tej funkcji to świetny pomysł, który wymyśliłem podczas moich wakacji 2013 roku.

Jest zgodny ze standardami i obsługuje deep array, na przykład a.a[x][b.a]=10. Wykorzystuje parse_str() Za kulisami z pewnym ukierunkowanym przetwarzaniem wstępnym.

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

A następnie możesz zastosować go w ten sposób, w zależności od źródła:

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

Dla PHP poniżej 5.4: użyj base64_encode zamiast bin2hex i base64_decode zamiast hex2bin.

 20
Author: Rok Kralj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-15 21:24:08

Dzieje się tak, ponieważ kropka jest nieprawidłowym znakiem w nazwie zmiennej, powód , dla którego leży bardzo głęboko w implementacji PHP, więc nie ma łatwych poprawek (jeszcze).

W międzyczasie możesz obejść ten problem przez:

    W celu uzyskania dostępu do surowych danych zapytania za pomocą php://input dla danych POST lub $_SERVER['QUERY_STRING'] dla danych GET
  1. za pomocą funkcji konwersji.

Poniższa funkcja konwersji (PHP > = 5.4) koduje nazwy każdej wartości klucza sparuj w reprezentację szesnastkową, a następnie wykonaj regularną parse_str(); gdy to zrobisz, przywróci nazwy szesnastkowe z powrotem do ich pierwotnej formy:

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']);

Lub:

// handle posted data (this only works with application/x-www-form-urlencoded)
$data = parse_qs(file_get_contents('php://input'));
 7
Author: Ja͢ck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-05 23:35:57

To podejście jest zmienioną wersją roku Kralj ' s, ale z pewnymi poprawkami do pracy, w celu poprawy wydajności (uniknięcie niepotrzebnych wywołań zwrotnych, kodowania i dekodowania na klawiszach nienaruszonych) i poprawnej obsługi kluczy tablicowych.

A gist z testami jest dostępny i wszelkie opinie lub sugestie są mile widziane tutaj lub tam.

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
Author: El Yobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-10 15:31:14

Dzieje się tak z powodu starej funkcjonalności PHP register_globals. The . znak nie jest prawidłowym znakiem w nazwie zmiennej, więc PHP zakrywa go podkreśleniem, aby upewnić się, że istnieje kompatybilność.

Krótko mówiąc, nie jest dobrą praktyką Robienie okresów w zmiennych URL.

 4
Author: Jeremy Privett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 01:56:15

If looking for any way to dosłownie get PHP to stop replaying '.'znaki w tablicach $_GET lub $_POST, wtedy jednym z takich sposobów jest modyfikacja źródła PHP (i w tym przypadku jest to stosunkowo proste).

Uwaga: modyfikacja źródła PHP C jest opcją zaawansowaną!

Zobacz również ten raport o błędzie PHP, który sugeruje tę samą modyfikację.

Aby zwiedzić musisz:

  • Pobierz PHP ' S C source kod
  • Wyłącz sprawdzanie wymiany .
  • ./ konfiguracja, tworzenie i wdrażanie niestandardowej kompilacji PHP

Sama zmiana źródła jest trywialna i polega na aktualizacji tylko jednej połowy jednego wiersza W 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='_';
....

Uwaga: w porównaniu do oryginału || *p == '.' został skomentowany


Przykładowe Wyjście:

Podano QUERY_STRING z a.a[]=bb&a.a[]=BB&c%20c=dd, running <?php print_r($_GET); now produkuje:

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

    [c_c] => dd
)

uwagi:

  • ta łatka odnosi się tylko do pierwotnego pytania (zatrzymuje wymianę kropek, nie spacji).
  • uruchomienie tego patcha będzie szybsze niż rozwiązania na poziomie skryptów, ale te czyste-.odpowiedzi php są nadal ogólnie preferowane (ponieważ unikają zmiany samego PHP).
  • w teorii podejście polyfill jest możliwe tutaj i może łączyć podejścia -- test dla zmiany poziomu C za pomocą parse_str() i (jeśli niedostępne) powrót do wolniejszych metod.
 3
Author: humbletim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-15 00:28:57

Moje rozwiązanie tego problemu było szybkie i brudne, ale i tak mi się podoba. Chciałem po prostu opublikować listę nazw plików, które zostały sprawdzone w formularzu. Użyłem base64_encode do zakodowania nazw plików wewnątrz znaczników, a następnie po prostu dekodowałem je za pomocą base64_decode przed ich użyciem.

 2
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-11 18:17:40

Po przyjrzeniu się rozwiązaniu roku wymyśliłem wersję, która odnosi się do ograniczeń w mojej Odpowiedzi poniżej, crb powyżej i rozwiązanie roku, jak również. Zobacz Moja ulepszona wersja .


ODPOWIEDŹ@crb powyżej to dobry początek, ale jest kilka problemów.

  • przetwarza wszystko, co jest przesadą; tylko te pola, które mają"."w imię trzeba ponownie przetworzyć.
  • nie obsługuje tablic w taki sam sposób, jak natywne przetwarzanie PHP robi, np. dla kluczy typu " foo.bar []".

Poniższe rozwiązanie rozwiązuje oba te problemy (zauważ, że zostało zaktualizowane od momentu opublikowania). Jest to o około 50% szybsze niż moja odpowiedź powyżej w moich testach, ale nie poradzi sobie z sytuacjami, w których dane mają ten sam klucz (lub klucz, który jest wydobywany tak samo, np. foo.bar i foo_bar są wyodrębniane jako 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]);                                       
            }                                                                  
        }                                                                      
    }                                                                          
}                                                                              
 2
Author: El Yobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-13 11:56:14

Cóż, funkcja, którą załączam poniżej," getRealPostArray ()", nie jest ładnym rozwiązaniem, ale obsługuje tablice i obsługuje obie nazwy: "alpha_beta "I" alpha.beta": {]}

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

Podczas gdy var_dump ($_POST) generuje:

  '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 ()) generuje:

  '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)

Funkcja, o ile jest warta:

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;
}
 0
Author: ChrisNY,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-12 01:27:53

Używając crb chciałem odtworzyć tablicę $_POST jako całość, ale pamiętaj, że nadal będziesz musiał upewnić się, że kodujesz i dekodujesz poprawnie zarówno na kliencie, jak i na serwerze. Ważne jest, aby zrozumieć, kiedy postać jest naprawdę nieważna i jest naprawdę ważna . Dodatkowo ludzie powinni nadali Zawszeprzed użyciem danych Klienta z dowolnąkomendą bazy danych bez wyjątku.

<?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);
?>

Polecam używanie to tylko dla pojedynczych przypadków, poza tym nie jestem pewien, co do negatywnych punktów umieszczania tego na górze głównego pliku nagłówkowego.

 0
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-05 20:49:47

Moje obecne rozwiązanie (na podstawie odpowiedzi z poprzedniego tematu):

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']);
 0
Author: sasha-ch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-16 13:25:33