Fura PHP karakterkészlet probléma

Van egy régi PHP kód, ami adatbázisból generál tartalmat.

A tartalom generálásához a következő feltételt használja: "WHERE statusz = 'Aktív'". Igen, ékezetes karakter van a feltételben. A PHP forráskód ISO-8852 kódolású, mint ahogy az adatbázisban is latin2 karakterkészlettel tárolódnak az adatok, latin2_hungarian_ci használatával. A generált HTML tartalom is ISO-8859-2 kódolású.

Minden tesztelésre használt böngészőnkben rendben lefut a kód, és helyes kódolású tartalmat generál.

De a naplóban ritkán GET kérelmek esetén megjelenik a következő hibaüzenet:

"PDOException: SQLSTATE[HY000]: General error: 1267 Illegal mix of collations (latin2_hungarian_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '=' ... "

Elképzelni sem tudom, hogy milyen kérelmeket küldhetnek ilyenkor a böngészők. A napló alapján az URL ugyanaz, mint a tesztelésre használt URL, tehát gondolom, valamilyen spéci header-t küldenek, vagy épp valamit nem küldenek ... nem tudom. Azt meg végképp nem, hogy ez hogyan befolyásolhatja a fixen lekódolt PHP-ból indított SQL kérés karakterkészletét.

Az SQL kérelemben nem szerepel semmilyen egyéb paraméter, amit a kérelem küldhetne.

Ha bárkinek van használható ötlete, örömmel olvasnám.

Hozzászólások

Nem megoldás a karakterkódolásra, de érdemes lehet átgondolni ezt a  statusz mezőt, mondjuk egy int segítségével kódolni az értékeket. Ez nem csak ezt a problémát oldaná meg, hanem az a kellemetlen helyzet is elkerülhető, hogy hibás eredményt kaptok, ha "Aktív" és "Aktiv" vegyesen szerepel.

Debian - The "What?!" starts not!
http://nyizsa.blogspot.com

A hiba valódi oka az el...ott adatszerkezet, tehát azt kell megjavítani. Vagy minimum a lekérdezést, ami az ékezetes mezővel operál átvakarni - mondjuk WHERE  UPPER(statusz) LIKE 'AKT%'
Ami a hibaüzenetből nekem lejön, az az, hogy az egyik az multibyte, a másik meg nem, és ezekre nem vizsgálhatsz egyenlőséget, ami valahol jogos is... Hogy miért érkezik az "=" egyik oldalára multibyte-os string, na az jó kérdés...

Hányszor éltem át én is azt, hogy órákon keresztüli sikertelen hibakeresés végén végül betűről betűre elolvastam a hibaüzenetet, és az vezetett nyomra ... :)

De megnyugtatlak, megnéztem a hibában jelzett sor, és ott nem volt más, mint a PHP kódban közvetlenül megadott SQL lekérdezés, ami a hibaüzenetben is szerepelt.

Igen, én is ezt tippelem, csak ugye nem ez a kérdés, hanem az, hogy 1000-ből miért csak 1-szer rossz a fixen beírt karakterlánc kódolása, a többi 999 esetben miért nem rossz? Ugyanis mind ékezetes karakter, ami az SQL-ben szerepel, az ott van közvetlenül megadva a PHP kódban. Nem kívülről jön, nem más PHP fájlok include-jával kerül be, hanem abban a PHP fájlban van konstansként megadva.

Köszönöm, ez egy olyan lehetőség, amire nem is gondoltam eddig.

Ha a hibaüzenet következetesen sorolja fel az illesztéseket, akkor az adatbázisbeli collation rendben van, és az = jobb oldalán lévő string konstanst illesztené utf-8 szerint. Ebből arra gondolok, hogy nem az adatbázissal lesz a gond. Ennek ellenére legalább egy új irány, amerre keresgélhetek.

Szerkesztve: 2022. 10. 17., h – 15:38

A böngésző nem áll kapcsolatban a mysql-szerverrel (vagy ki tudja, lassan a CPU mikrokódja is JavaScript), szóval inkább PHP-oldalon kellene nézelődni. Kezdetnek: ugye volt set names 'latin2' (vagy mysqli_set_charset('latin2')) ?

Szerk: azt írja az internet, hogy az '=' nem feltétlenül '='-t jelent, más esetben is előfordulhat, hogy két mező egyezését vizsgálja a szerver, mondjuk egy "UNION" esetén (a duplaszűrés miatt).

Erre írtam, hogy minden általunk futtatott tesztben helyesen fut a kód. Ez egy fix url, még csak egyedi paramétereket sem kap.

Én is azt hittem, hogy a böngésző nem hat a szerveroldali szkript karakterkódolására, de ha ezerből 1-szer ilyen hibát ad úgy, hogy nincsenek kívülről jövő paraméterek, akkor mégsem tudok másra gondolni.

Amúgy PDO és nem mysqli, de igen, beállítottuk a mysql felé a karakterkészletet.

Konkrétan a csatlakozás parancs a következő:

$options = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
                , PDO::ATTR_PERSISTENT => true
                , PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'latin2' COLLATE 'latin2_hungarian_ci'"
                );
$dbh = new PDO( 'mysql:host=localhost;dbname=dbname;charset=latin2', user, pwd, $options );

em lehet hogy valamelyik tábla mégis más kódolású és némely lekérésnél van csak behivatkozva péládul joinnal?

Szerkesztve: 2022. 10. 18., k – 08:05

A web szerverről nem volt szó, annak is van default és vhost esetén is karakter kódolás beállítása. Olyat láttam régebben, hogy több vhost volt, és egyiken nem volt jól beállítva minden, hasonló hibát hozott.

Apache esetén .htaccess file a site rootban is tartalmazhat ilyet.

Másik ok, ami okozhatja: kézzel a Firefoxban például át tudom állítani az enkódolást menüből, és akkor a form azzal a karakterkészlettel küld adatokat. Ez is lehet.

Tippre állami projekt vagy a fejlesztő egyik első munkája ez. Én csak állami projektekben láttam hasonlókat, de hát valakinek azt is el kell készíteni.

Ha még nincs itt, eljön majd az, hogy indiai, filippínó juniorok írják a Magyarország.hu új verzióját, akik a vállalkozói lánc negyedik, ötödik szintjén lesznek csak. (ahol régen a legolcsóbb informatikus volt)

A webszerver apache, és a .htaccess fájlok nincsenek engedélyezve.

Az űrlap karakterkészletével már nekem is volt gondom, és ha ez egy POST kérelem lenne, vagy legalább egy GET FORM url, akkor ki is tudnám próbálnám, de itt nincs űrlap. Azonban ahogyan az űrlap elküldi a karakterkészletet, gondolom a böngésző is küldhet valamilyen accept header-t, amiben jelzi, hogy milyen karakterkészletben várná az url tartalmát, és amit a webszerver még a PHP futtatása előtt értelmez.

A PHP generál saját header-t a válaszhoz, amiben beállítja a karakterkódolást, tehát az Apache AddDefaultCharset értéke elvileg nem juthat érvényre. Azért a biztonság kedvéért most ezt is beállítottam, de logikusan ez nem lehet a megoldás.

[off]

Az állami tipp nem jött be. Meglepő módon ez egy élő piaci vállalkozás, ahol az eredeti tulaj anno a "legolcsóbb" megoldást választotta, egy önjelölt programozó személyében...

"egy önjelölt programozó" kontra ,,"legolcsóbb" megoldás'' - a kettő csak első látásra van pariban... Most például kiderült, hogy az "olcsó" megoldáshoz még kell x óra debug/fixálás, hogy rendesen működjön, illetve utána ennek - és a vélhetően szép számmal a kódban lévő hasonló "egyszerű megoldások"-nak - a kigyomlálása is szép és az időt bőségesen zabáló feladat lesz...

[on]

akkor én klónoznám a rendszert és teszt környezetben próbálnám a hibát előhozni

ha valami oknál ott is előfordul, és nem tudni, mitől, akkor teljes mysql logolást kapcsolnék be, és ott látszódna, hogy mi után jön ez, akkor a kódban is megkereshető, hogy merre lehet

az élő rendszeren is növelném a logolást, hátha kiderül valami, access log, error log, bármi

megnézném a sémában vagy bárhol máshol, hogy hol van utf8_general_ci

honnan jön az "Aktív" szó? hány helyről? ott mi az enkódolás? minden esetben jó? ha megnyitod latin2 módolású editorral, mindenhol jól látszódik az "Aktív" szó? Lehet olyan is, hogy Latin2 kódolású fájlban utf-8-ként szerepel ez a szó, mert olyan editorral nyúltak hozző, ahol ez volt az encoding. Az is okoz hasonlót. Kb ezt fogod ott látni: "AktĂ­v". Ha pedig fordítva: "Akt"[xED]"v". Az [xED] egy nem megjeleníthető karakter.

 

[off]

Kb ugyanezek a személyek dolgoznak sokszor az állami projekteken is, mert más nem vállalja az adott áron/feltételekkel. Tehát a "személyt" kb eltaláltam, a megrendelőt nem.

Tesztelni azért nehéz teszt környezetben, mert semmilyen módon nem tudtuk még reprodukálni a hibát. Próbáltuk az access.log-ban a User-Agent alapján beazonosított böngészőkkel, de azokkal sem reprodukálható a hiba. Sőt, nem is csak egyféle böngészőben jön elő. Generálta már I-phone kliens, de Windows böngésző is.

Tehát tesztelném, ha tudnám...

Emiatt is nyitottam ezt a témát, már több ember több extrém esetbe ütközött már. Hátha valaki valamikor látott már hasonlót, vagy van ötlete, mi lehet az a környezeti paraméter, amivel érdemes lenne tesztelni. Elvégre a hiba nem lehet túl bonyolult. Valahogy egy GET kérést kell elindítani, hogy a PHP-ban foglalt string konstans karakterkódolása befolyásolható legyen.

Állami projekteknél, feltörekvő vállalkozóknál az ékezet használata hasonló módon teljesen megszokott, az a szint. Meg hogy egy óriás queryben hívnak le mindent, így nem tud működni a cache. Ezért egy lekérdezés másodpercek a sok left join meg hasonlók miatt. Meg nincsenek indexek, stb. 

Alvállalkozó alvállakozójának alvállalkozójának az ismerőse készítette a kódot, aki még gimibe jár, de már készített több honlapot! :)

Sajnos nincs megfejtés azóta sem. Egyelőre együtt élünk a hibaüzenettel, mivel ritka. Valós ügyfél még nem jelezte, pedig reméltem, mert akkor lenne infó a kliens környezetről.

Amúgy nem állami szféra, hanem olcsóban dolgozó - valószínűleg önképzett - vállalkozó műve volt, aki azóta már eltűnt.

Haladt valamit mostanában? Még mindig nincs jobb gondolatom, mint hogy a táblák között van olyan ritkán használt nünüke, aminek a charset/collate értéke különbözik a többiétől.

Az OP nem leszarja, hanem már elmondta, hogy olyan fix kérelmekről van szó, amik mindig ugyanazon a táblák ugyanazon mezőit használják. Nem tábladefiníciós problémáról van szó, mivel a kérelmek 99%-ában hibaüzenetek nélkül lefutnak ugyanazok a kérelmek, amik - és megint ismételem - ugyanazokból a táblákból ugyanazokat a mezőket használják.

Tippre a futtató rendszeren a lokalizációs változóknál a LANG vagy LC rész nem latin2-re van konfigurálva, vagyis a latin2 nincs a felsorolt lehetőségek között, hanem default UTF8-at használ. Próbaképp megpróbálnám a szervert LANG="hu_HU.ISO-8859-2" /path/binary formában futtatni, megnézni ilyenkor is sír-e a logban.

A computer is like air conditioning – it becomes useless when you open Windows.” (Linus Torvalds)

A kérdésből nem derült ki a "PHP / MySQL" verzió.

Bár már több mint 10 éve nem PHP-zok, de van pár dolog amire már akkor is érdemes volt oda figyelni:

 

A PHP file-ok kódolására. Azaz, hogy egységes legyen minden file-ban, pláne ha az egyik beölti ("include"-olja) a másikat.
Ha az egyik ilyen (ISO-8852), a másik olyan (utf8, stb.) akkor az pont ilyen hibákat tud okozni ha emlékeim nem csalnak.

Pl.: Előfordulhat az, hogy ha ez a logika egy megosztott (több helyről is hivatkozott) fájlban van akkor a betöltőtől is függhet a viselkedés ha annak eltérő a kódolása.

file1.php (ISO) -> shared.php (ISO) (jó)

file2.php (UTF8) -> shared.php (ISO) (rossz és csak ennél az ágnál jön a hiba)

Ez mindenképp a szerveren érdemes ellenőrizni, mert lehetett hiba a fájl átvitelben (deployment) is ha az nem olyan módon történt.

 

Én még körülnéznék az index-ek körül, hogy nincs-e valami corruption.
A másik MySQL kapcsolat kódolása, de ha félszemmel jól láttam itt a hozzászólásoknál akkor az megfelelően került beállításra.