A prova está no código fonte do PHP.
Vou levá-lo através de um processo rápido de como descobrir esse tipo de coisa por conta própria no futuro a qualquer momento que você quiser. Tenha paciência comigo, haverá muito código-fonte C em que você pode ler (eu explico). Se você deseja atualizar um pouco de C, um bom lugar para começar é o nosso SO wiki .
Faça o download da fonte (ou use http://lxr.php.net/ para navegar online), grep todos os arquivos para o nome da função, você encontrará algo como isto:
PHP 5.3.6 (mais recente no momento da escrita) descreve as duas funções em seu código nativo C no arquivo 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);
}
Ok, então o que há de diferente aqui?
Ambos estão essencialmente chamando duas funções internas diferentes, respectivamente: php_raw_url_encode e php_url_encode
Então vá procurar essas funções!
Vamos olhar para php_raw_url_encode
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);
}
E, claro, 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;
}
Um conhecimento rápido antes de avançar, EBCDIC é outro conjunto de caracteres , semelhante ao ASCII, mas um concorrente total. O PHP tenta lidar com ambos. Mas basicamente, isso significa que o byte EBCDIC 0x4c não é o L
ASCII, na verdade é um <
. Tenho certeza que você vê a confusão aqui.
Ambas as funções gerenciam o EBCDIC se o servidor da web o definiu.
Além disso, ambos usam uma pesquisa de caracteres chars (tipo de cadeia de caracteres) hexchars
para obter alguns valores; a matriz é descrita da seguinte forma:
/* 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";
Além disso, as funções são realmente diferentes, e eu vou explicá-las em ASCII e EBCDIC.
Diferenças em ASCII:
URLENCODE:
- Calcula um comprimento inicial / final da sequência de entrada, aloca memória
- Anda através de um loop while, incrementa até chegar ao final da string
- Agarra o personagem atual
- Se o caractere for igual ao ASCII Char 0x20 (ou seja, um "espaço"), adicione um
+
sinal à string de saída.
- Se não é um espaço, e também não é alfanumérico (
isalnum(c)
), e também não é e _
, -
ou .
caractere, emitimos um %
sinal para a posição 0 da matriz, fazemos uma matriz procurar na hexchars
matriz uma procura por os_toascii
matriz ( uma matriz do Apache que converte char para código hexadecimal) para a chave dec
(o caractere presente), então deslocamos bit a bit para a direita em 4, atribuímos esse valor ao caractere 1 e à posição 2 atribuímos a mesma pesquisa, exceto que executamos um lógico e para ver se o valor é 15 (0xF) e retorne 1 nesse caso ou 0 caso contrário. No final, você terminará com algo codificado.
- Se acabar, não é um espaço, é alfanumérico ou um dos
_-.
caracteres, gera exatamente o que é.
RAWURLENCODE:
- Aloca memória para a sequência
- Repete sobre ele com base no comprimento fornecido na chamada de função (não calculado na função como com URLENCODE).
Nota: Muitos programadores provavelmente nunca viram um loop for iterar dessa maneira, é um tanto hackiano e não a convenção padrão usada com a maioria dos loops for, preste atenção, ele atribui x
e y
verifica a saída ao len
atingir 0 e incrementa ambos x
e y
. Eu sei, não é o que você esperaria, mas é um código válido.
- Atribui o caractere atual a uma posição de caractere correspondente em
str
.
- Ele verifica se o caractere atual é alfanumérico ou um dos
_-.
caracteres, e se não for, fazemos quase a mesma atribuição que com URLENCODE onde ele realiza pesquisas, no entanto, incrementamos de maneira diferente, usando em y++
vez de to[1]
, isso ocorre porque o as strings estão sendo construídas de maneiras diferentes, mas atingem o mesmo objetivo no final.
- Quando o loop termina e o comprimento acaba, na verdade termina a string, atribuindo o
\0
byte.
- Retorna a string codificada.
Diferenças:
- UrlEncode verifica se há espaço, atribui um sinal de +, RawURLEncode não.
- UrlEncode não atribui um
\0
byte à cadeia de caracteres, RawUrlEncode (isso pode ser um ponto discutível)
- Eles iteram de maneira diferente, é possível que você transborde com seqüências de caracteres malformadas, estou apenas sugerindo isso e na verdade não investiguei.
Basicamente, iteram de maneira diferente, atribui-se um sinal de + no evento ASCII 20.
Diferenças no EBCDIC:
URLENCODE:
- Mesma configuração de iteração que com ASCII
- Ainda traduzindo o caractere "espaço" para um sinal de + . Nota-- Acho que isso precisa ser compilado no EBCDIC ou você vai acabar com um bug? Alguém pode editar e confirmar isso?
- Ele verifica se o caractere atual é um caractere anterior
0
, com exceção de ser um .
ou -
, OR menor que A
mas maior que char 9
, OR maior que Z
e menor que a
mas não a _
. OU maior que z
(sim, EBCDIC está meio bagunçado para trabalhar). Se corresponder a qualquer um desses, faça uma pesquisa semelhante à encontrada na versão ASCII (apenas não requer uma pesquisa em os_toascii).
RAWURLENCODE:
- Mesma configuração de iteração que com ASCII
- A mesma verificação descrita na versão EBCDIC do URL Encode, com exceção de que, se for maior que
z
, exclui ~
da codificação de URL.
- Mesma atribuição que o ASCII RawUrlEncode
- Ainda anexando o
\0
byte à string antes de retornar.
Grande resumo
- Ambos usam a mesma tabela de pesquisa de hexchars
- URIEncode não finaliza uma string com \ 0, não processado.
- Se você estiver trabalhando no EBCDIC, sugiro usar o RawUrlEncode, pois ele gerencia o
~
que o UrlEncode não faz ( esse é um problema relatado ). Vale a pena notar que ASCII e EBCDIC 0x20 são ambos espaços.
- Eles iteram de maneira diferente, um pode ser mais rápido, um pode ser propenso a explorações baseadas em memória ou em strings.
- URIEncode cria um espaço
+
, RawUrlEncode cria um espaço %20
através de pesquisas de matriz.
Isenção de responsabilidade: não toquei em C há anos e não vejo o EBCDIC há muito tempo. Se eu estiver errado em algum lugar, me avise.
Implementações sugeridas
Com base em tudo isso, rawurlencode é o caminho a seguir na maioria das vezes. Como você vê na resposta de Jonathan Fingland, fique com ela na maioria dos casos. Ele lida com o esquema moderno de componentes de URI, onde como urlencode faz as coisas da maneira antiga, onde + significava "espaço".
Se você estiver tentando converter entre o formato antigo e os novos, certifique-se de que seu código não aconteça e transforme algo que é um sinal + decodificado em um espaço por meio de codificação dupla acidental ou cenários semelhantes de "ops" espaço / 20% / + problema.
Se você estiver trabalhando em um sistema mais antigo com software mais antigo que não prefere o novo formato, fique com o código de URL, no entanto, acredito que% 20 será realmente compatível com versões anteriores, pois no antigo padrão% 20 funcionou, simplesmente não era preferido. Dê uma chance se quiser brincar, deixe-nos saber como funcionou para você.
Basicamente, você deve ficar com o raw, a menos que seu sistema EBCDIC realmente o odeie. A maioria dos programadores nunca encontrará o EBCDIC em qualquer sistema criado após o ano de 2000, talvez até 1990 (isso é bom, mas ainda é provável na minha opinião).