Wenn ich PHP-Variablen mit .
ihren Namen über $ _GET übergebe, ersetzt PHP sie automatisch durch _
Zeichen. Zum Beispiel:
<?php
echo "url is ".$_SERVER['REQUEST_URI']."<p>";
echo "x.y is ".$_GET['x.y'].".<p>";
echo "x_y is ".$_GET['x_y'].".<p>";
... gibt Folgendes aus:
url is /SpShipTool/php/testGetUrl.php?x.y=a.b
x.y is .
x_y is a.b.
... meine Frage lautet: Gibt es eine Möglichkeit, dies zu stoppen? Ich kann nicht für mein Leben herausfinden, was ich getan habe, um dies zu verdienen
Die PHP-Version, mit der ich arbeite, ist 5.2.4-2ubuntu5.3.
Antworten:
Hier ist die Erklärung von PHP.net, warum es das tut:
Das ist von http://ca.php.net/variables.external .
Gemäß diesem Kommentar werden diese anderen Zeichen auch in Unterstriche umgewandelt:
Es sieht also so aus, als ob Sie daran festhalten , also müssen Sie die Unterstriche nach dem Vorschlag von dawnerd wieder in Punkte in Ihrem Skript konvertieren (ich würde jedoch nur str_replace verwenden .)
quelle
Die Frage wurde schon lange beantwortet, aber es gibt tatsächlich eine bessere Antwort (oder Umgehung). Mit PHP können Sie den rohen Eingabestream bearbeiten , sodass Sie Folgendes tun können:
$query_string = file_get_contents('php://input');
Dadurch erhalten Sie das Array $ _POST im Abfragezeichenfolgenformat in den angegebenen Zeiträumen.
Sie können es dann bei Bedarf analysieren (gemäß POSTer-Kommentar ).
<?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'); } ?>
Sehr nützlich für OpenID-Parameter, die beide '.' Enthalten. und '_', jeweils mit einer bestimmten Bedeutung!
quelle
file_get_contents("php://input")
durch$_SERVER['QUERY_STRING']
.$_SERVER['COOKIES']
Hervorheben einer tatsächlichen Antwort von Johan in einem Kommentar oben - Ich habe gerade meinen gesamten Beitrag in ein Array der obersten Ebene eingewickelt, das das Problem vollständig umgeht, ohne dass eine umfangreiche Verarbeitung erforderlich ist.
In der Form, die Sie tun
<input name="data[database.username]"> <input name="data[database.password]"> <input name="data[something.else.really.deep]">
anstatt
<input name="database.username"> <input name="database.password"> <input name="something.else.really.deep">
und im Post-Handler einfach auspacken:
$posdata = $_POST['data'];
Für mich war dies eine zweizeilige Änderung, da meine Ansichten vollständig vorlagen.
Zu Ihrer Information. Ich verwende Punkte in meinen Feldnamen, um Bäume mit gruppierten Daten zu bearbeiten.
quelle
Die Funktionsweise dieser Funktion ist eine nette Idee, die ich während meiner Sommerferien 2013 hatte.
Es ist standardkonform und bietet beispielsweise umfassende Array-Unterstützung
a.a[x][b.a]=10
. Es wirdparse_str()
hinter den Kulissen mit einer gezielten Vorverarbeitung verwendet.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; }
Und dann können Sie es je nach Quelle folgendermaßen anwenden:
$_POST = fix(file_get_contents('php://input')); $_GET = fix($_SERVER['QUERY_STRING']); $_COOKIE = fix($_SERVER['HTTP_COOKIE']);
Für PHP unter 5.4: Verwenden Sie
base64_encode
anstelle vonbin2hex
undbase64_decode
anstelle vonhex2bin
.quelle
a[2][5]=10
produziertarray(1) { ["a"]=> array(1) { [2]=> array(1) { [5]=> string(2) "10" } } }
.Dies liegt daran, dass ein Punkt ein ungültiges Zeichen im Namen einer Variablen ist, dessen Grund sehr tief in der Implementierung von PHP liegt, sodass es (noch) keine einfachen Korrekturen gibt.
In der Zwischenzeit können Sie dieses Problem umgehen, indem Sie:
php://input
für POST-Daten oder$_SERVER['QUERY_STRING']
für GET-DatenDie folgende Konvertierungsfunktion (PHP> = 5.4) codiert die Namen jedes Schlüssel-Wert-Paares in eine hexadezimale Darstellung und führt dann eine reguläre Darstellung durch
parse_str()
. Sobald dies erledigt ist, werden die hexadezimalen Namen wieder in ihre ursprüngliche Form zurückgesetzt: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']);
Oder:
// handle posted data (this only works with application/x-www-form-urlencoded) $data = parse_qs(file_get_contents('php://input'));
quelle
bin2hex()
Idee von mir zu übernehmen. Können wir diese sinnlose Fehde einfach fallen lassen?Dieser Ansatz ist eine geänderte Version von Rok Kraljs, jedoch mit einigen Optimierungen, um die Effizienz zu verbessern (vermeidet unnötige Rückrufe, das Codieren und Decodieren nicht betroffener Schlüssel) und um Array-Schlüssel korrekt zu handhaben.
Eine Übersicht mit Tests ist verfügbar und Feedback oder Vorschläge sind hier oder da willkommen.
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; } } }
quelle
parse_str
abzufangen. Wenn Ihre Quelle jedoch noch nicht URL-codiert ist, treten wahrscheinlich andere Probleme auf, da die Behandlung Zeichenfolgen immer decodiert und codiert. Der Aufruf wird dann erneut URL-Code. Was versuchst du zu analysieren, das noch nicht codiert ist?Der Grund dafür ist die alte Funktionalität von PHP register_globals. Das . Zeichen ist kein gültiges Zeichen in einem Variablennamen, daher wird es von PHP in einen Unterstrich umgewandelt, um die Kompatibilität sicherzustellen.
Kurz gesagt, es ist keine gute Praxis, Punkte in URL-Variablen zu machen.
quelle
Wenn Sie nach einer Möglichkeit suchen , PHP buchstäblich dazu zu bringen, das Ersetzen von '.' Zeichen in $ _GET- oder $ _POST-Arrays. Eine Möglichkeit besteht darin, die PHP-Quelle zu ändern (und in diesem Fall ist dies relativ einfach).
WARNUNG: Das Ändern der PHP C-Quelle ist eine erweiterte Option!
Siehe auch diesen PHP-Fehlerbericht , der dieselbe Änderung vorschlägt.
Um zu erkunden, müssen Sie:
.
ErsatzprüfungDie Quellenänderung selbst ist trivial und beinhaltet das Aktualisieren von nur einer Hälfte einer Zeile in
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='_'; ....
Hinweis: Im Vergleich zum Original
|| *p == '.'
wurde auskommentiertBeispielausgabe:
Bei einem QUERY_STRING von erzeugt das
a.a[]=bb&a.a[]=BB&c%20c=dd
Ausführen<?php print_r($_GET);
jetzt:Anmerkungen:
parse_str()
(falls nicht verfügbar) Rückgriff auf langsamere Methoden.quelle
Meine Lösung für dieses Problem war schnell und schmutzig, aber ich mag es immer noch. Ich wollte einfach eine Liste von Dateinamen veröffentlichen, die auf dem Formular überprüft wurden. Ich habe
base64_encode
die Dateinamen innerhalb des Markups codiert und sie dann einfach dekodiert,base64_decode
bevor ich sie verwendet habe.quelle
Nachdem ich mir Roks Lösung angesehen habe, habe ich eine Version entwickelt, die die Einschränkungen in meiner Antwort unten, die von crb oben und die Lösung von Rok ebenfalls behandelt. Siehe meine verbesserte Version .
Die Antwort von @ crb oben ist ein guter Anfang, aber es gibt ein paar Probleme.
Die folgende Lösung behebt diese beiden Probleme jetzt (beachten Sie, dass sie seit der ursprünglichen Veröffentlichung aktualisiert wurde). Dies ist ungefähr 50% schneller als meine Antwort oben in meinen Tests, behandelt jedoch keine Situationen, in denen die Daten denselben Schlüssel haben (oder ein Schlüssel, der gleich extrahiert wird, z. B. foo.bar und foo_bar, werden beide als foo_bar extrahiert).
<?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]); } } } }
quelle
%20
ist auch ein Sonderzeichen, das in Unterstriche umgewandelt wird. 2. Ihr Code stellt alle Daten vor, dapreg_match_all
alles gescannt werden muss, obwohl Sie sagen, dass Sie dies nicht tun. 3. Ihr Code schlägt bei Beispielen wie folgt fehl :a.b[10]=11
.preg_match_all
muss eine Zeichenfolge "verarbeiten", nicht alle nicht betroffenen Schlüssel und Werte extrahieren und erneut verarbeiten, damit Sie auch dort ein wenig aus der Bahn geraten. Das heißt, Ihr Ansatz mitparse_string
sieht aus wie ein interessanter Ansatz, der mit ein wenig Optimierung besser sein könnte :)O(n)
. Tatsächlich verschlechtern Sie die Komplexität, indem Sie die oben beschriebenein_array()
Funktion verwenden.in_array
ohnehin nicht verwendet . Wenn Sie einige Tests durchführen möchten, werden Sie feststellen, dass die oben genannten Schritte immer noch erheblich schneller sind. nicht O (n) gegen O (n ^ 2), aber ein linearer Ansatz kann immer noch schneller sein als ein anderer ... und dieser ist;)Nun, die Funktion "getRealPostArray ()", die ich unten einbinde, ist keine schöne Lösung, aber sie verarbeitet Arrays und unterstützt beide Namen: "alpha_beta" und "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>
während var_dump ($ _ POST) erzeugt:
'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 ()) erzeugt:
'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)
Die Funktion für das, was es wert ist:
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; }
quelle
Mit crbs wollte ich das
$_POST
Array als Ganzes neu erstellen , aber denken Sie daran, dass Sie immer noch sicherstellen müssen, dass Sie sowohl auf dem Client als auch auf dem Server korrekt codieren und decodieren. Es ist wichtig zu verstehen, wann ein Charakter wirklich ungültig und wirklich gültig ist . Darüber hinaus sollten Benutzer weiterhin und immer Client-Daten maskieren, bevor sie ausnahmslos mit einem Datenbankbefehl verwendet werden .<?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); ?>
Ich empfehle, dies nur für Einzelfälle zu verwenden. Ich bin mir nicht sicher, welche negativen Punkte es hat, wenn Sie dies oben in Ihre primäre Header-Datei einfügen.
quelle
Meine aktuelle Lösung (basierend auf den Antworten des vorherigen Themas):
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']);
quelle