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.
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.)
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!
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.
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
.
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ą
- za pomocą funkcji konwersji.
php://input
dla danych POST lub $_SERVER['QUERY_STRING']
dla danych GET
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'));
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;
}
}
}
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.
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.
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.
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]);
}
}
}
}
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;
}
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.
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']);
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