{"id":225,"date":"2010-05-10T08:45:06","date_gmt":"2010-05-10T07:45:06","guid":{"rendered":"http:\/\/eriz.pcinside.pl\/weblog\/flash-unicodeutf-8-php-fail-225.html"},"modified":"2024-04-09T22:18:16","modified_gmt":"2024-04-09T21:18:16","slug":"flash-unicodeutf-8-php-fail","status":"publish","type":"post","link":"https:\/\/eriz.pcinside.pl\/weblog\/flash-unicodeutf-8-php-fail-225.html","title":{"rendered":"Flash + Unicode\/UTF-8 + PHP = fail?"},"content":{"rendered":"<p>Ostatnio musia\u0142em si\u0119 zmierzy\u0107 z pewnym algorytmem, kt\u00f3ry wymienia\u0142 dane z aplikacj\u0105 klienck\u0105 pisan\u0105 we Flashu pomi\u0119dzy PHP. Niby nic skomplikowanego, przesy\u0142anie znak\u00f3w z kodami ASCII uzyskiwanych flashowym odpowiednikiem metody urlencode(). Jednak pomimo prawid\u0142owej realizacji algorytmu, nadal uzyskiwa\u0142em dziwne wyniki.<\/p>\n<p>Jakie\u017c by\u0142o moje zdziwienie, gdy odkry\u0142em, co by\u0142o grane.<\/p>\n<p><!--more--><\/p>\n<p>Wpis ten dedykuje ku pami\u0119ci, bo delikatna poprawka okaza\u0142a si\u0119 nie lada wyzwaniem, kt\u00f3re chodzi\u0142o po g\u0142owie przez kilka dni. Dopiero szczeg\u00f3\u0142owa analiza poszczeg\u00f3lnych etap\u00f3w wysy\u0142ania danych da\u0142a jednoznaczn\u0105 i \u2013 mo\u017cna powiedzie\u0107 \u2013 zaskakuj\u0105c\u0105 odpowied\u017a, co tak naprawd\u0119 by\u0142o nie tak.<\/p>\n<p>A okaza\u0142o si\u0119, \u017ce winne jest samo dzia\u0142anie interpretera. Nie od dzisiaj wiadomo, \u017ce obs\u0142uga UTF-8\/Unicode w PHP niestety kuleje, ale czasem cz\u0142owiek po prostu o tym zapomina.<\/p>\n<h2>Do rzeczy<\/h2>\n<p>Zacznijmy od tego, w jaki spos\u00f3b transmitowane jest \u017c\u0105danie typu POST (GET w\u0142a\u015bciwie te\u017c; nie do ko\u0144ca, ale&#8230; ;)). Dane przesy\u0142ane s\u0105 w formacie <em>klucz=wartosc<\/em>, oddzielane przez znak <em>&#038;<\/em>. Znaki, kt\u00f3re nie mieszcz\u0105 si\u0119 w standardowym zestawie znak\u00f3w ASCII oraz znaki specjalne, s\u0105 kodowane poprzez znak procentu oraz heksadecymalnego kodu znaku. W przypadku kod\u00f3w przekraczaj\u0105cych zakres warto\u015bci mieszcz\u0105cych si\u0119 w ramach jednego bajtu, jest on kodowany dwubajtowo, co umo\u017cliwia zakodowanie jednego z 65536 znak\u00f3w.<\/p>\n<p>I zgodnie <a href=\"http:\/\/www.adobe.com\/support\/flash\/action_scripts\/actionscript_dictionary\/actionscript_dictionary689.html\">z dokumentacj\u0105<\/a>, Flash zwraca liczb\u0119 w\u0142a\u015bnie z zakresu 0-65536, kt\u00f3ra jest kodowana do postaci <em>%XX%YY<\/em> (dla jednego znaku). W tym w\u0142a\u015bnie miejscu zaczynaj\u0105 si\u0119 tzw. &#8222;cyrki&#8221;, je\u015bli chodzi o PHP&#8230;<\/p>\n<p>Dlaczego? Przy przetwarzaniu \u017c\u0105dania, PHP propaguje tablic\u0119 superglobaln\u0105 $_POST w\u0142a\u015bnie przy pomocy metody wykorzystywanej w funkcji <a href=\"http:\/\/php.net\/urldecode\">urldecode<\/a>. S\u0119k w tym, \u017ce jest ona pozbawiona obs\u0142ugi wielobajtowego przetwarzania znak\u00f3w. I tak zamiast np. ci\u0105gu 4-znakowego wyprodukowanego przez Flasha, otrzymujemy znak 8-znakowy, co powoduje zupe\u0142nie odwrotne do zamierzonych skutki.<\/p>\n<p>Najlepiej jest przesy\u0142a\u0107 takie dane zakodowane przez algorytm base64, ale je\u015bli ju\u017c stoimy przed problemem, w kt\u00f3rym nie mamy mo\u017cliwo\u015bci zmiany sposobu transportu ci\u0105gu znak\u00f3w&#8230;?<\/p>\n<p>Skoro PHP nie potrafi rozkodowa\u0107 tych danych, jak by\u0107 powinno, to musimy zrealizowa\u0107 to samodzielnie.<\/p>\n<p>Problemem pozostaje kwestia dobrania si\u0119 do danych wej\u015bciowych, kt\u00f3re pochodz\u0105 z \u017c\u0105dania. O ile w starszych wersjach PHP by\u0142a tablica superglobalna <em>$HTTP_RAW_POST_DATA<\/em>, to ju\u017c od jakiego\u015b czasu nie jest ona najcz\u0119\u015bciej dost\u0119pna. G\u0142\u00f3wnie ze wzgl\u0119du na pami\u0119cio\u017cerno\u015b\u0107 (jak podaje dokumentacja).<\/p>\n<p>Jednak nie jeste\u015bmy kompletnie na lodzie i damy rad\u0119. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_wink.png\" alt=\";)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> Mianowicie \u2013 PHP udost\u0119pnia kilka wrapper\u00f3w do systemu plik\u00f3w, kt\u00f3re \u2013 mo\u017cna powiedzie\u0107 \u2013 s\u0105 odpowiednikiem pseudosystemu plik\u00f3w <em>\/proc<\/em> w systemach uniksowych. Zaczynaj\u0105 si\u0119 one od prefiksu <em>php:\/\/<\/em>. Szczeg\u00f3\u0142y mo\u017cna odnale\u017a\u0107 w dokumentacji.<\/p>\n<p>Interesuje nas konkretnie <em>php:\/\/input<\/em>. Zawiera on surowe dane POST przes\u0142ane przez przegl\u0105dark\u0119. Niestety, posiada on jedn\u0105 wad\u0119 \u2013 strumie\u0144 pozostanie pusty, je\u015bli formularz posiada atrybut enctype ustawiony z cechami wielocz\u0119\u015bciowo\u015bci, co jest wykorzystywane przy przesy\u0142aniu plik\u00f3w. W\u00f3wczas trzeba skorzysta\u0107 z <em>php:\/\/stdin<\/em> i zrealizowa\u0107 ca\u0142\u0105 obs\u0142ug\u0119 strumienia samodzielnie.<\/p>\n<p>Jednak w wi\u0119kszo\u015bci przypadk\u00f3w wystarczy przetwarzanie danych tekstowych, wi\u0119c wystarczy poradzi\u0107 sobie mniej-wi\u0119cej tym kodem:<\/p>\n<pre lang=\"php\"><?PHP\n$data = file_get_contents('php:\/\/input');\n$data = utf8_decode(urldecode($data));\nparse_str($data, $_POST);\n?><\/pre>\n<p>Wystarczy u\u017cy\u0107 tego kodu gdzie\u015b na pocz\u0105tku skryptu i wszystko dzia\u0142a jak nale\u017cy. Banalne, nie?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ostatnio musia\u0142em si\u0119 zmierzy\u0107 z pewnym algorytmem, kt\u00f3ry wymienia\u0142 dane z aplikacj\u0105 klienck\u0105 pisan\u0105 we Flashu pomi\u0119dzy PHP. Niby nic skomplikowanego, przesy\u0142anie znak\u00f3w z kodami ASCII uzyskiwanych flashowym odpowiednikiem metody urlencode(). Jednak pomimo prawid\u0142owej realizacji algorytmu, nadal uzyskiwa\u0142em dziwne wyniki. Jakie\u017c by\u0142o moje zdziwienie, gdy odkry\u0142em, co by\u0142o grane.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[48,30,111],"_links":{"self":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/posts\/225"}],"collection":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/comments?post=225"}],"version-history":[{"count":1,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/posts\/225\/revisions"}],"predecessor-version":[{"id":0,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/posts\/225\/revisions\/0"}],"wp:attachment":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/media?parent=225"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/categories?post=225"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/tags?post=225"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}