Der Beweis ist im Quellcode von PHP.
Ich werde Sie durch einen kurzen Prozess führen, wie Sie diese Art von Dingen in Zukunft jederzeit selbst herausfinden können. Denken Sie daran, es wird eine Menge C-Quellcode geben, den Sie überfliegen können (ich erkläre es). Wenn Sie etwas C auffrischen möchten, ist unser SO-Wiki ein guter Anfang .
Laden Sie die Quelle herunter (oder verwenden Sie http://lxr.php.net/, um sie online zu durchsuchen), durchsuchen Sie alle Dateien nach dem Funktionsnamen. Sie finden Folgendes:
PHP 5.3.6 (letzter zum Zeitpunkt des Schreibens) beschreibt die beiden Funktionen in ihrem nativen C - Code in der Datei url.c .
RawUrlEncode ()
PHP_FUNCTION(rawurlencode)
{
char *in_str, *out_str;
int in_str_len, out_str_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &in_str,
&in_str_len) == FAILURE) {
return;
}
out_str = php_raw_url_encode(in_str, in_str_len, &out_str_len);
RETURN_STRINGL(out_str, out_str_len, 0);
}
UrlEncode ()
PHP_FUNCTION(urlencode)
{
char *in_str, *out_str;
int in_str_len, out_str_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &in_str,
&in_str_len) == FAILURE) {
return;
}
out_str = php_url_encode(in_str, in_str_len, &out_str_len);
RETURN_STRINGL(out_str, out_str_len, 0);
}
Okay, was ist hier anders?
Beide rufen im Wesentlichen zwei verschiedene interne Funktionen auf: php_raw_url_encode und php_url_encode
Suchen Sie also nach diesen Funktionen!
Schauen wir uns php_raw_url_encode an
PHPAPI char *php_raw_url_encode(char const *s, int len, int *new_length)
{
register int x, y;
unsigned char *str;
str = (unsigned char *) safe_emalloc(3, len, 1);
for (x = 0, y = 0; len--; x++, y++) {
str[y] = (unsigned char) s[x];
#ifndef CHARSET_EBCDIC
if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||
(str[y] < 'A' && str[y] > '9') ||
(str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||
(str[y] > 'z' && str[y] != '~')) {
str[y++] = '%';
str[y++] = hexchars[(unsigned char) s[x] >> 4];
str[y] = hexchars[(unsigned char) s[x] & 15];
#else /*CHARSET_EBCDIC*/
if (!isalnum(str[y]) && strchr("_-.~", str[y]) != NULL) {
str[y++] = '%';
str[y++] = hexchars[os_toascii[(unsigned char) s[x]] >> 4];
str[y] = hexchars[os_toascii[(unsigned char) s[x]] & 15];
#endif /*CHARSET_EBCDIC*/
}
}
str[y] = '\0';
if (new_length) {
*new_length = y;
}
return ((char *) str);
}
Und natürlich php_url_encode:
PHPAPI char *php_url_encode(char const *s, int len, int *new_length)
{
register unsigned char c;
unsigned char *to, *start;
unsigned char const *from, *end;
from = (unsigned char *)s;
end = (unsigned char *)s + len;
start = to = (unsigned char *) safe_emalloc(3, len, 1);
while (from < end) {
c = *from++;
if (c == ' ') {
*to++ = '+';
#ifndef CHARSET_EBCDIC
} else if ((c < '0' && c != '-' && c != '.') ||
(c < 'A' && c > '9') ||
(c > 'Z' && c < 'a' && c != '_') ||
(c > 'z')) {
to[0] = '%';
to[1] = hexchars[c >> 4];
to[2] = hexchars[c & 15];
to += 3;
#else /*CHARSET_EBCDIC*/
} else if (!isalnum(c) && strchr("_-.", c) == NULL) {
/* Allow only alphanumeric chars and '_', '-', '.'; escape the rest */
to[0] = '%';
to[1] = hexchars[os_toascii[c] >> 4];
to[2] = hexchars[os_toascii[c] & 15];
to += 3;
#endif /*CHARSET_EBCDIC*/
} else {
*to++ = c;
}
}
*to = 0;
if (new_length) {
*new_length = to - start;
}
return (char *) start;
}
Ein schnell bisschen Wissen , bevor ich vorwärts zu bewegen, EBCDIC ist ein weiterer Zeichensatz , ähnlich wie ASCII, aber insgesamt Wettbewerber. PHP versucht mit beiden umzugehen. Aber im Grunde bedeutet dies, dass das Byte EBCDIC 0x4c Byte nicht das L
in ASCII ist, sondern tatsächlich ein <
. Ich bin sicher, Sie sehen die Verwirrung hier.
Beide Funktionen verwalten EBCDIC, wenn der Webserver es definiert hat.
Außerdem verwenden beide ein Array von Zeichen (Think String Type) hexchars
, um einige Werte abzurufen. Das Array wird als solches beschrieben:
/* rfc1738:
...The characters ";",
"/", "?", ":", "@", "=" and "&" are the characters which may be
reserved for special meaning within a scheme...
...Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
reserved characters used for their reserved purposes may be used
unencoded within a URL...
For added safety, we only leave -_. unencoded.
*/
static unsigned char hexchars[] = "0123456789ABCDEF";
Darüber hinaus sind die Funktionen sehr unterschiedlich und ich werde sie in ASCII und EBCDIC erklären.
Unterschiede in ASCII:
URLENCODE:
- Berechnet eine Start- / Endlänge der Eingabezeichenfolge und weist Speicher zu
- Geht durch eine while-Schleife und erhöht sich, bis das Ende der Zeichenfolge erreicht ist
- Greift den gegenwärtigen Charakter
- Wenn das Zeichen gleich ASCII Char 0x20 ist (dh ein "Leerzeichen"), fügen Sie
+
der Ausgabezeichenfolge ein Vorzeichen hinzu.
- Wenn es nicht ein Raum, und es ist auch nicht alphanumerische Zeichen (
isalnum(c)
), und auch nicht und _
, -
oder .
Zeichen, dann wir, gibt ein %
Zeichen Feldposition 0, tun einen Array Blick auf die oben hexchars
für eine Referenz für die Array - os_toascii
Anordnung ( Ein Array von Apache, das char in Hex-Code übersetzt) für den Schlüssel von c
(das vorliegende Zeichen), verschieben wir dann bitweise um 4 nach rechts, weisen diesen Wert dem Zeichen 1 zu und zu Position 2 weisen wir die gleiche Suche zu, außer wir formen vor eine logische und um zu sehen, ob der Wert 15 (0xF) ist, und geben Sie in diesem Fall eine 1 oder eine 0 zurück. Am Ende erhalten Sie etwas Codiertes.
- Wenn es am Ende kein Leerzeichen ist, es ist alphanumerisch oder eines der
_-.
Zeichen, gibt es genau das aus, was es ist.
RAWURLENCODE:
- Ordnet Speicher für die Zeichenfolge zu
- Iteriert darüber basierend auf der im Funktionsaufruf angegebenen Länge (nicht in der Funktion wie bei URLENCODE berechnet).
Hinweis: Viele Programmierer haben wahrscheinlich noch nie gesehen, dass eine for-Schleife auf diese Weise iteriert. Sie ist etwas hackig und nicht die Standardkonvention, die bei den meisten for-Schleifen verwendet wird. Achten Sie darauf, sie weist zu x
und y
prüft, ob sie bei len
Erreichen von 0 beendet wird, und erhöht sowohl x
als auch y
. Ich weiß, es ist nicht das, was Sie erwarten würden, aber es ist gültiger Code.
- Weist das aktuelle Zeichen einer übereinstimmenden Zeichenposition in zu
str
.
- Es prüft , ob das vorliegende Zeichen alphanumerische oder eines der
_-.
Zeichen, und wenn dies nicht der Fall, haben wir fast die gleiche Belegung wie bei urlencode wo es Lookups Preforms jedoch erhöhen wir anders verwenden , y++
anstatt to[1]
, ist dies , weil die Saiten werden auf unterschiedliche Weise gebaut, erreichen aber am Ende trotzdem das gleiche Ziel.
- Wenn die Schleife fertig ist und die Länge weg ist, wird die Zeichenfolge tatsächlich beendet und das
\0
Byte zugewiesen.
- Es gibt die codierte Zeichenfolge zurück.
Unterschiede:
- UrlEncode sucht nach Leerzeichen, weist ein + -Zeichen zu, RawURLEncode nicht.
- UrlEncode weist
\0
der Zeichenfolge kein Byte zu, RawUrlEncode jedoch (dies kann ein strittiger Punkt sein).
- Sie iterieren unterschiedlich, man kann dazu neigen, mit fehlerhaften Zeichenfolgen überzulaufen. Ich schlage dies lediglich vor und habe es nicht wirklich untersucht.
Sie iterieren grundsätzlich anders, man weist bei ASCII 20 ein + -Zeichen zu.
Unterschiede in der EBCDIC:
URLENCODE:
- Gleiches Iterationssetup wie bei ASCII
- Das Leerzeichen wird immer noch in ein + -Zeichen übersetzt. Hinweis - Ich denke, dies muss in EBCDIC kompiliert werden, oder Sie werden mit einem Fehler enden? Kann jemand dies bearbeiten und bestätigen?
- Es prüft , ob die vorliegende char , bevor ein Zeichen ist
0
, mit Ausnahme eines Wesens .
oder -
, oder weniger als , A
aber größer als char 9
, OR größer Z
und kleiner als a
eine , aber nicht _
. ODER größer als z
(ja, EBCDIC ist irgendwie durcheinander, um damit zu arbeiten). Wenn es mit einem dieser Elemente übereinstimmt, führen Sie eine ähnliche Suche wie in der ASCII-Version durch (es ist lediglich keine Suche in os_toascii erforderlich).
RAWURLENCODE:
- Gleiches Iterationssetup wie bei ASCII
- Dieselbe Prüfung wie in der EBCDIC-Version von URL Encode beschrieben, mit der Ausnahme, dass
z
sie ~
von der URL-Codierung ausgeschlossen wird , wenn sie größer als ist .
- Gleiche Zuordnung wie der ASCII RawUrlEncode
- Hängt das
\0
Byte vor der Rückkehr immer noch an die Zeichenfolge an.
Große Zusammenfassung
- Beide verwenden dieselbe Hexchars-Nachschlagetabelle
- URIEncode beendet eine Zeichenfolge nicht mit \ 0, raw nicht.
- Wenn Sie in EBCDIC arbeiten, würde ich die Verwendung von RawUrlEncode vorschlagen, da es das verwaltet
~
, was UrlEncode nicht tut ( dies ist ein gemeldetes Problem ). Es ist erwähnenswert, dass ASCII und EBCDIC 0x20 beide Leerzeichen sind.
- Sie iterieren unterschiedlich, man kann schneller sein, man kann anfällig für speicher- oder stringbasierte Exploits sein.
- URIEncode macht ein Leerzeichen in
+
, RawUrlEncode macht ein Leerzeichen in %20
über Array-Lookups.
Haftungsausschluss: Ich habe C seit Jahren nicht mehr berührt und mich EBCDIC schon lange nicht mehr angesehen. Wenn ich irgendwo falsch liege, lass es mich wissen.
Vorgeschlagene Implementierungen
Basierend auf all dem ist Rawurlencode die meiste Zeit der richtige Weg. Wie Sie in Jonathan Finglands Antwort sehen, bleiben Sie in den meisten Fällen dabei. Es befasst sich mit dem modernen Schema für URI-Komponenten, bei dem Urlencode die Dinge auf die alte Art und Weise erledigt, wobei + "Raum" bedeutet.
Wenn Sie versuchen, zwischen dem alten und dem neuen Format zu konvertieren, stellen Sie sicher, dass Ihr Code nicht fehlerhaft ist, und verwandeln Sie etwas, das ein dekodiertes + Zeichen ist, in ein Leerzeichen, indem Sie es versehentlich doppelt codieren, oder ähnliche "oops" -Szenarien Platz / 20% / + Ausgabe.
Wenn Sie auf einem älteren System mit älterer Software arbeiten, die das neue Format nicht bevorzugt, bleiben Sie bei Urlencode. Ich glaube jedoch, dass% 20 tatsächlich abwärtskompatibel ist, da% 20 unter dem alten Standard einfach nicht funktioniert hat bevorzugt. Probieren Sie es aus, wenn Sie zum Herumspielen bereit sind. Lassen Sie uns wissen, wie es für Sie funktioniert hat.
Grundsätzlich sollten Sie bei raw bleiben, es sei denn, Ihr EBCDIC-System hasst Sie wirklich. Die meisten Programmierer werden auf keinem System, das nach dem Jahr 2000, vielleicht sogar 1990, hergestellt wurde, auf EBCDIC stoßen (das ist drängend, aber meiner Meinung nach immer noch wahrscheinlich).