W jaki sposób PHP wewnętrznie reprezentuje łańcuchy?

18

UTF8?
UTF16?

Czy łańcuchy w PHP śledzą również używane kodowanie?

Spójrzmy na przykład na ten skrypt. Powiedz, że biegnę:

$original = "शक्नोम्यत्तुम्";

Co się właściwie dzieje?

Oczywiście myślę, że $originalnie będzie zawierać tylko 7 znaków. Te glify muszą być reprezentowane przez kilka bajtów.

Następnie robię:

$converted = mb_convert_encoding ($original , "UTF-8");

Co się stanie $converted? Czym będzie $convertedsię różnić $original?

Czy będzie to dokładnie ta sama sekwencja bajtów, $originalale z innym kodowaniem?

użytkownik4951
źródło
1
Która wersja PHP? PHP <6 nie obsługuje natywnego UTF-8. Istnieją jednak pakiety i metody, które pomagają / rozwiązują ten problem. Google fun z utf-8 i php. Następnie przełącz się na inną platformę zamiast PHP. :)
Andrew T Finnell,
4
PHP <6?
Obejmowałoby
1
Ponadto PHP może obsługiwać UTF-8, po prostu nie ma dedykowanego typu danych, więc musisz uważać na to, co robisz.
tdammers

Odpowiedzi:

22

Łańcuch PHP jest tylko sekwencją bajtów, bez żadnego kodowania. Wartości łańcuchowe mogą pochodzić z różnych źródeł: klienta (przez HTTP), bazy danych, pliku lub literałów łańcuchowych w kodzie źródłowym. PHP czyta je wszystkie jako sekwencje bajtów i nigdy nie wyodrębnia żadnych informacji o kodowaniu.

Tak długo, jak wszystkie źródła danych i miejsca docelowe używają tego samego kodowania, najgorsze, co może się zdarzyć, to nieprawidłowe pozycje pozycji (jeśli używasz kodowania wielobajtowego), ponieważ PHP będzie liczyć bajty, a nie znaki.

Ale jeśli kodowania się nie zgadzają (np. Piszesz literał łańcuchowy w pliku źródłowym przechowywanym jako UTF-8, a następnie wysyłasz go do bazy danych, która oczekuje Latin-1), PHP nie wykona dla ciebie żadnej konwersji: wykona szczęśliwie skopiuj bajty na raw.

Najlepszym rozwiązaniem jest:

  • Ustaw wewnętrzne kodowanie PHP na UTF-8.
  • Zapisz wszystkie pliki źródłowe jako UTF-8.
  • Użyj UTF-8 jako kodowania wyjściowego (nie zapomnij wysłać odpowiednich Content-typenagłówków).
  • Ustaw połączenie z bazą danych, aby używało UTF-8 ( SET NAMES UTF8w MySQL).
  • Skonfiguruj wszystko inne, aby było UTF-8, jeśli to w ogóle możliwe.
  • W przypadku czegokolwiek, czego nie możesz kontrolować (np. Usług internetowych stron trzecich), upewnij się, że znasz kodowanie, i przekonwertuj na UTF-8 tak wcześnie, jak to możliwe, a następnie wróć do innego kodowania tak późno, jak to możliwe.

Dlaczego UTF-8? Ponieważ może reprezentować wszystkie znaki Unicode, a tym samym zastępuje wszystkie istniejące kodowania 7-bitowe i 8-bitowe oraz ponieważ jest binarnie zgodny z ASCII, to znaczy, każdy prawidłowy ciąg ASCII jest również prawidłowym ciągiem UTF-8 (ale nie vv .).

W twoim przykładzie tak się dzieje.

Najpierw zapisz plik źródłowy; Twój edytor tekstowy jest prawdopodobnie skonfigurowany do używania UTF-8, więc literał łańcuchowy kończy się kodowaniem UTF-8 na dysku. PHP czyta ten plik, interpretując ciąg znaków jako ciąg bajtów; $originalteraz zawiera ciąg znaków zakodowany w UTF-8 składający się z 7 znaków, który jest tylko sekwencją bajtów (chociaż zawiera więcej niż 7 bajtów, ponieważ każdy znak jest reprezentowany przez dwa lub więcej bajtów). Jeśli następnie zadzwonisz echo $original, zakodowany ciąg zostanie wysłany do klienta w niezmienionej postaci; jeśli powiedziałeś klientowi, aby spodziewał się UTF-8, wszystko jest w porządku, ale jeśli nie, PHP nie ma sposobu na odróżnienie, a skończysz na śmieciach w przeglądarce. W ramach eksperymentu spróbuj tego:

$original = "शक्नोम्यत्तुम्";
echo strlen($original);

strlen jest agnostyczny dla kodowania i zakłada 8-bitowe kodowanie o stałej szerokości, to znaczy jeden bajt na znak, więc będzie liczyć bajty, a nie znaki.

tdammers
źródło
Więc $ convert będzie reprezentował ten sam ciąg znaków, ale w innym kodowaniu. Rzeczywiste surowe kodowanie, jakim jest sklep PhP, będzie inne.
user4951
2
Powtórzę to dla ciebie: PHP przechowuje bajty, a nie znaki, i wcale nie wie o kodowaniu (chociaż niektóre funkcje biblioteczne tak robią.
tdammers
1
Aha, i to jest „PHP”, a nie „PhP”.
tdammers
2
jeśli nieprzetworzone bajty są takie same, jaka jest różnica między $ oryginalnym a $ przekonwertowanym, to. O to pytam.
user4951
2
Och, OK, o to ci chodzi. Tak, nieprzetworzone bajty zmieniają się zgodnie z konwersją kodowania. PHP jednak nie pamięta kodowania, więc jeśli przekonwertujesz ciąg znaków, powiedzmy, z utf-8 na latin-1, a następnie potraktujesz wynik jako utf-8, zobaczysz dziwne wyniki.
tdammers