Door
Johan K
op 24-05-2015 18:09
gewijzigd op 24-05-2015 18:10
5.136 views
Ik loop niet echt tot een probleem aan, maar ben wel benieuwd waar dit vandaan komt.
Ik draai op mijn computer een LAMP server, en alles werkt perfect als ik mijn *.php documenten als ISO-8859-[1-15] op sla. Maar als ik dit niet doe en de standaard UTF-8 encoding gebruik dan word mijn php code als HTML comments weergeven.
Ik heb niets aangepast in de .ini bestand, behalve error_reporting.
De uitkomst is altijd hetzelfde, ongeacht of je het scriptje nu wel of niet als UTF-8 met of zonder BOM opslaat.
Ergo: ga er nooit impliciet van uit dat PHP altijd feilloos met UTF-8 uit de voeten kan, maar maak dat expliciet in je code door die op de verwerking van UTF-8 in te richten.
@An tje: (nav relaas hierboven) daarom zou je altijd aan moeten geven wat voor content(type) je serveert, en met welke character encoding, want het zal PHP worst wezen.
@Johan K: heb je al gekeken naar de broncode van je output (in je browser)? Wat wordt uiteindelijk uitgespuugd?
De broncode as-is: dan wordt UTF-8 encoded data op een of andere manier opgepikt en anders behandeld (bijvoorbeeld door Apache?). Ook interessant om uit te vinden hoe dit dan gedetecteerd wordt.
Een HTML-safe variant van je broncode (& in plaats van & etc.): dat vindt er ergens een vertaalstap plaats, maar dat zou een beetje vreemd zijn.
Hoe dan ook: iets neemt op een of andere manier de beslissing om de PHP-code op een afwijkende manier te behandelen. Dit gegeven vormt de leidraad voor je onderzoek.
Ik heb overigens nog nooit het hierboven beschreven gedrag meegemaakt (UTF-8 encoded PHP-code die genegeerd wordt, als dat inderdaad het euvel is).
Ik vond het zelf ook al vreemd, schijnbaar heb ik *iets* verkeerds gedaan tijdens de installatie want ik ben nog nooit eerder tegen deze LAMP aangelopen. Zojuist even in Archlinux getest, en daar 't werkt prima.
Als ik er achter kom wat precies 't euvel is zal ik wel laten weten. Ik dacht het zal wel iets simpels zijn wat ik over het hoofd heb gezien, een flag in de config, maar dat is schijnbaar dus niet het geval.
@Ivo P: Ik heb de desbetreffende code even door de parser gehaald, en de output is UTF-8 maar volgens mij is dat het resultaat van de locale standaard ingebakken in de Ubuntu installatie.
@An tje, Thomas: Na wat kleine testjes op het werkende Archlinux en UTF-8 encoding word prima uitegelezen. Zelfs met karakters die niet ondersteund worden in UTF-8, its wat hij op deze instlalatie gewoon niet doet.
Voorlopig ben ik er even klaar mee, als ik dit zo lees (en niet alleen hier) hoort dit gewoon niet en denk dat als dit echt een probleem voor mij gaat worden is dat ik dingen opnieuw ga installeren, desnoods het O.S. Maar ik ben een simpele ziel, het werkt met ISO-8859-1? Prima... a.u.b. parser.
?Onbekende gebruiker
26-05-2015 19:27
gewijzigd op 26-05-2015 19:29
@Thomas, het HTTP 'content-type' slaat op de _output_ van PHP. Terwijl de .php-bestanden juist _input_ zijn voor PHP. Ik lees dat iedereen maar aannames doet zonder mijn vragen te beantwoorden of mijn stelling te ontkrachten. Leuk dat HHVM, maar we hebben het hier over PHP, dat is toch echt iets anders. Wat als enthousiastelingen bijvoorbeeld emoji in PHP code verwerken? Wie kan mij garanderen dat die 4 bytes per icoontje door PHP niet opgemerkt gaan worden als 4 losse 8-bit karakters in Latin1-encoding?
Even off-topic: wat is de relevantie van PHP code opslaan als UTF-8? Is het geen prakken als je data en code door elkaar gaan lopen, en kun je dan niet beter code en data scheiden (zoals vaak aanbevolen)?
Mijn devies: gewoon php als Latin1 laten inlezen door PHP. Vervolgens $_REQUEST-achtige data als UTF-8 behandelen, en die mb_* -functies aanspreken om UTF-8 af te handelen, UTF-8 in de HTTP header 'Content-Type: text/plain; charset=utf-8' zetten voor de output, en klaar.
Tenzij; maar dat is de uitdaging, iemand een stuk documentatie kan linken van een bron met autoriteit met iets van een tabel waarin staat welke versie van PHP met welke versie(s) en coderingsschema's van Unicode gaat werken.
Gezien de populariteit van functies als utf8_encode() verwacht ik dat PHP scriptbestanden als met de encoding ISO/IEC 8859-1 (Latin1) moeten worden opgeslagen.
Omdat vertalen efficienter is dan opslaan in native UTF-8? -_-
Waarom UTF-8? Standaardisatie misschien? De wereld is groter dan de VS/Europa.
Eenduidigheid?
Een zinnige default character encoding?
En als je echt/toch wilt nitpicken, weet niet of het in PHP ook zo is / je het ook zo gebruikt, maar in MySQL is het zelfs zo dat "latin1" gebaseerd is op cp1252 West European, die afwijkt van iso-8859-1. cp1252 is geen (ANSI) standaard (ben bron kwijt, als iemand dit kan bevestigen of ontkennen).
Wat als enthousiastelingen bijvoorbeeld emoji in PHP code verwerken?
Het is moeilijk (zoniet onmogelijk) om dingen idiot proof te maken, want idioten zijn vindingrijk.
Ik kan de logica die je hanteert ook tegen je argumentatie gebruiken: waarom zou ik ISO-8859-1 gebruiken? Tis maar net wat je afspreekt, en voor wie het moet werken.
Ooit iets op github gezet? Niet bedoeld als een pissing contest (ik doe zelf niets met github) maar meer in de zin van: heb je ooit code met de rest van de wereld gedeeld?
Bevatten je regelovergangen ook nog CR+LF?
Gebruik je een versioning systeem voor je code?
Wat voor IDE gebruik je?
Al die dingen bepalen mede hoe je met jouw code omgaat. En andermans code. Gebruik je libraries of andere zaken? Dat is ook altijd? vaak? meestal? UTF-8. Ga je dan werken met 2 standaarden?
Het is altijd beter om expliciet aan te geven wat je gebruikt (wat dan ook, UTF-8 of anderszins) dan uit te gaan van een default. Dit kun je ook doen middels DOCUMENTATIE. En ja, het kan problematisch zijn om te detecteren wat voor character encoding er gebruikt wordt. Maar als jij een stuk PHP-code hebt van onbekende origine, waar zou jij je geld dan op zetten?
Je moet trouwens argumenten niet tellen maar wegen. Je weet er ongetwijfeld het eea van af, maar omdat niemand een weerwoord lijkt te hebben op je epistels heb je niet ineens meer gelijk dan een ander.
Daarbij moet je het praktische aspect van je oplossingen niet uit het oog verliezen.
"Alles UTF-8" lijkt mij een simpele en (dus) goede vuistregel. Daar valt je broncode ook onder.
En als jij een andere oplossing hebt die voor jou werkt, doe je ding. De rest / het overgrote deel van de wereld / een heleboel mensen gebruiken inmiddels UTF-8 en ik hoop van harte dat deze trend zich voortzet, in plaats van dat iedereen zijn eigen (obscure) standaard gebruikt.
@an tje, UTF-8 is netzoals Thomas vermeld een standaard geworden, het heeft beter support en je zou toch wel verwachten dat vooral php met de trend mee gaat en hun parser daarop aan past om er meer performance uit te drukken.
Met dit gezegd te hebben ben ik bezig met een CMS systeem die gewoon volledig 100% UTF-8 pagina's verwerkt, waaronder ook de database. Geen poes pas met andere character encodings zodat ik me daar geen zorgen over hoef te maken en ik maak het dan voor mezelf alleen maar makkelijker. Als je andere encoderingen gaat gebruiken kunnen er rare dingen gebeuren.
Een parser kan alleen maar gokken wat de encoding is van het document dat het leest. Dit gaat in 99.999% van de gevallen goed en mocht je input krijgen waarvan de encoding anders is kan altijd de parser overrulen met die mb_* functies (zoals bestanden die via de FTP server zijn geupload in ASCII mode).
Dus ik raad jouw echt aan om gewoon lekker altijd UTF-8 te gebruiken. Als er veel Chinese, Japanse, etc tekens op de websites komen te staan dan kan je beter UTF-16 gebruiken omdat het document dat je verzend dan kleiner is dan dat je UTF-8 gebruikt.
@Thomas, effe rustig neem een bakkie koffie.
- Als ik een onbekende origine php bestand heb zal ik waarschijnlijk mijn editor erop los laten gokken (wat hij toch altijd wel doet) en het desnoods laten opslaan als UTF-8.
- CR + LF? dit heeft meer te maken met het O.S. / IDE dan de encodatie van een document.
- Git download alles lekker in UTF-8, bron https://github.com/gitextensions/gitextensions/wiki/Git-Encoding. Dit is, natuurlijk te overrulen.
- IDE? een beetje IDE staat een gebruiker toe om een document op te slaan naar andere encodering maar staat standaard op de O.S. default.
Thomas van den Heuvel op 25/05/2015 19:23:31
[quote="An tje op 24/05/2015 19:59:09"]Je PHP-code zelf moet _NIET_ als UTF-8 worden opgeslagen.
Volgens mij is dat (idd) BS. Je kunt PHP-bestanden prima UTF-8 encoded opslaan (minus BOM).[/quote]
Volgende keer even wat subtieler aanpakken, je bereikt er niets mee om zo te reageren zowel hier niet als in het echte leven.
Al met al...
Ik maar hopen dat het toch die ene flag was... ;)
?Onbekende gebruiker
27-05-2015 11:01
gewijzigd op 27-05-2015 13:08
Bij het maken van een gedachtenexperiment met de nodige bewijslast kwam bij mij een 'aha erlebnis', dat zowel Thomas als ik gelijk hebben.
1.) PHP doet helemaal niets met UTF-8 in de broncode. PHP is agnostisch tov. encoding en kijkt alleen naar de bytes. Het maakt bv. onderscheid tussen normale quotes en de quotes bekend van Word.
2.) Gebruik van UTF-8 in PHP als onderdeel van data (zoals in strings) gaat altijd goed door de aard van de encodering van UTF-8. Het is geen verdienste van PHP.
Het werd duidelijk toen ik op een willekeurige site (http://www.utf8-chartable.de) zocht naar een UTF-8 encoded karakter dat de byte 0x22 (dubbele quote) had in de byte sequence. Gewoon om te kijken of dat PHP zou breken met escapen van strings met dubbele quotes. Maar, dat karakter BESTAAT NIET in UTF-8! Wel in UTF-16 en UTF-32 waar je ook een BOM nodig hebt voor de endianness.
De crux is dat UTF-8 niet volledig ASCII-compatibel is. Het is alleen compatibel met de eerste 128 karakters, en niet met de Extended ASCII reeks (de overige 128 karakters) die je in 1 byte kunt proppen. PHP en ook andere talen maken voor hun control-karakters, variabelenamen en functienamen _uitsluitend_ gebruik van de eerste 128 karakters. Dat is geen toeval, want de meeste variatie in de verschillende ASCII code pages zit juist in de Extended ASCII set. (De Extended ASCII-set kwam beschikbaar toen de eerste bit van een byte, dat aanvankelijk gebruikt werd voor controle van pariteit, vrij kwam.) Zo hoeven quotes nooit ge-escaped te worden vanwege hun codering; de codepoints zitten op respectievelijk 0x22 en 0x27.
Omdat de aanvullende karakters van UTF-8 alleen gebruik maken van de range 0x80 t/m 0xFF zal dat DUS geen problemen geven.
Ik vind het veelzeggend dat niemand dat antwoord paraat had.. ;-)
Ofwel, PHP code kán niet in UTF-8. Maar omdat je PHP code dwars door HTML-bestanden (met een eigen codering) kunt gebruiken, en omdat je data al dan niet in UTF-8 rechtstreeks in een .php-bestand kunt gebruiken ontstaat gemakkelijk babelonische spraakverwarring.
Het werd duidelijk toen ik op een willekeurige site (http://www.utf8-chartable.de) zocht naar een UTF-8 encoded karakter dat de byte 0x22 (dubbele quote) had in de byte sequence. Gewoon om te kijken of dat PHP zou breken met escapen van strings met dubbele quotes. Maar, dat karakter BESTAAT NIET in UTF-8! Wel in UTF-16 en UTF-32 waar je ook een BOM nodig hebt voor de endianness.
Ik volg je niet helemaal, 0x22 oftewel de unicode benoeming U+0022 / U+0027 zit natuurlijk gewoon in utf-8 verwerkt.
UTF-8 (1-4 bytes) is volledig backwards compatible met ASCII (1 byte), en inderdaad als je over een andere encoding praat (ASCII Extended) beginnen inderdaad de verschillen. ASCII was een basis waar andere coderingen zichzelf overheen hebben gebouwd. En nee, het is inderdaad geen toeval dat de tekens die gebruikt worden in parsers altijd in de 128 reeks zitten, gewoon voor de compatibiliteit.
De link die je hebt meegestuurd gaat over een project van 10 jaar geleden! Een tijd waar IE6 nog heerste, waar webdesigners vrolijk een pagina bouwde voor IE6 zonder ook maar aan compatibiliteit te denken, een tijd waar alleen Mozilla hun best deed om zich aan standaarden te houden, een tijd waar mensen screeuwden voor betere ondersteuning met special karakters.. en toen was Unicode geboren..
?Onbekende gebruiker
27-05-2015 14:46
gewijzigd op 27-05-2015 17:55
We zijn het dus eens over de eerste 128 karakters van ASCII. Ik heb het niet gecontroleerd maar ik neem aan dat de verschillende codepages onderling grotendeels gelijk zijn.
UTF-8 maakt juist gebruik van het feit dat de overige karakters (128 t/m 255) verschillen, door de overige miljoenen Unicode codepoints vast te leggen in byte reeksen die maximaal 4 bytes per karakter bevatten.
De clou is dat UTF-8 het conflict met de standaard ASCII set mijdt door een combinatie te maken met bytes die standaard de eerste bit op 1 hebben staan. Als je bijvoorbeeld kijkt naar het Unicode karakter 'TAG QUOTATION MARK' op: http://www.fileformat.info/info/unicode/char/e0022/index.htm
dan zie je dat een schrijfwijze als:
U+E0022
wordt vertaald naar:
0xF3 0xA0 0x80 0xA2
Ik nam aan dat PHP het conflict ook mijdt door in functienamen en variabelenamen alleen standaard ASCII karakters toe te staan. Dat is in ieder geval niet zo voor zelf gedefineerde functies. Een simpele test:
function ?nabla() {
header('content-type:text/plain');
print "test: ?";
}
?nabla();
Voor de parser van PHP zijn extended ASCII tekens ook toegestaan. PHP hoeft ook niet eens te kijken naar het Nabla teken (U+2207), omdat het in UTF-8 wordt opgeslagen als 0xE2 0x88 0x87. Het maakt natuurlijk ook niet uit, als de functienaam maar hetzelfde is als de aanroep ervan. Zolang je in je IDE of editor maar de juiste encoding hebt ingesteld, anders kan het zijn dat je zelf niet meer weet waar de functie voor is.
Hier staat het allemaal uitgelegd, inclusief wat het voor PHP betekent. Het had me een paar posts en toegegeven, foute aannames kunnen schelen als ik me die link naar (onofficiële) documentatie eerder had herinnerd.
Wat we er uit kunnen leren is dat PHP met ELKE encoding overweg kan, ZOLANG het maar ASCII-compatible is.
Aangezien UTF-8 ASCII compatible is (zie het verhaal over de 1e bit in een byte) levert het geen problemen op met PHP.
Aangezien EBCDIC niet ASCII compatible is zal EBCDIC niet werken met PHP.
Het enige dat nog wel eens problemen levert is manipulatie van Unicode in PHP, hiervoor moet je vrij oplettend omgaan met mb_* functies. Die set is heel beperkt, en er zijn op internet meerdere libraries te vinden die het manipuleren van Unicode uitgebreider kunnen.