html_entity_decode s UTF-8 v PHP 4
Funkce html_entity_decode od PHP 5 podporuje vícebajtové znakové stránky, tedy i UTF-8. Pokud potřebujeme řetězec v tomto kódování převést v PHP 4, musíme si funkci napsat sami. Pro získání převodní tabulky by se dala použít funkce get_html_translation_table, ta ale vrátí pouze znaky z kódování ISO-8859-1. Pokud máme zájem o všechny znaky, nezbývá nic jiného, než je získat přímo z HTML specifikace:
<?php /** Obdoba funkce chr() pro kódování UTF-8 * @param int pozice znaku * @return string UTF-8 reprezentace kódu */ function chr_utf8($code) { if ($code < 0) { return false; } elseif ($code < 128) { return chr($code); } elseif ($code < 2048) { return chr(192 | ($code >> 6)) . chr(128 | ($code & 63)); } elseif ($code < 65536) { return chr(224 | ($code >> 12)) . chr(128 | (($code >> 6) & 63)) . chr(128 | ($code & 63)); } else { return chr(240 | ($code >> 18)) . chr(128 | (($code >> 12) & 63)) . chr(128 | (($code >> 6) & 63)) . chr(128 | ($code & 63)); } } /** Callback pro náhradu HTML entit za UTF-8 znaky * @param array pole vzniklé z regulárního výrazu '&(#(x?))?([^;]+);' * @return string UTF-8 reprezentace entity, false v případě neznámé entity */ function html_entity_replace($matches) { if ($matches[2]) { return chr_utf8(hexdec($matches[3])); } elseif ($matches[1]) { return chr_utf8($matches[3]); } switch ($matches[3]) { case "nbsp": return chr_utf8(160); case "iexcl": return chr_utf8(161); case "cent": return chr_utf8(162); case "pound": return chr_utf8(163); case "curren": return chr_utf8(164); case "yen": return chr_utf8(165); case "brvbar": return chr_utf8(166); case "sect": return chr_utf8(167); case "uml": return chr_utf8(168); case "copy": return chr_utf8(169); case "ordf": return chr_utf8(170); case "laquo": return chr_utf8(171); case "not": return chr_utf8(172); case "shy": return chr_utf8(173); case "reg": return chr_utf8(174); case "macr": return chr_utf8(175); case "deg": return chr_utf8(176); case "plusmn": return chr_utf8(177); case "sup2": return chr_utf8(178); case "sup3": return chr_utf8(179); case "acute": return chr_utf8(180); case "micro": return chr_utf8(181); case "para": return chr_utf8(182); case "middot": return chr_utf8(183); case "cedil": return chr_utf8(184); case "sup1": return chr_utf8(185); case "ordm": return chr_utf8(186); case "raquo": return chr_utf8(187); case "frac14": return chr_utf8(188); case "frac12": return chr_utf8(189); case "frac34": return chr_utf8(190); case "iquest": return chr_utf8(191); case "Agrave": return chr_utf8(192); case "Aacute": return chr_utf8(193); case "Acirc": return chr_utf8(194); case "Atilde": return chr_utf8(195); case "Auml": return chr_utf8(196); case "Aring": return chr_utf8(197); case "AElig": return chr_utf8(198); case "Ccedil": return chr_utf8(199); case "Egrave": return chr_utf8(200); case "Eacute": return chr_utf8(201); case "Ecirc": return chr_utf8(202); case "Euml": return chr_utf8(203); case "Igrave": return chr_utf8(204); case "Iacute": return chr_utf8(205); case "Icirc": return chr_utf8(206); case "Iuml": return chr_utf8(207); case "ETH": return chr_utf8(208); case "Ntilde": return chr_utf8(209); case "Ograve": return chr_utf8(210); case "Oacute": return chr_utf8(211); case "Ocirc": return chr_utf8(212); case "Otilde": return chr_utf8(213); case "Ouml": return chr_utf8(214); case "times": return chr_utf8(215); case "Oslash": return chr_utf8(216); case "Ugrave": return chr_utf8(217); case "Uacute": return chr_utf8(218); case "Ucirc": return chr_utf8(219); case "Uuml": return chr_utf8(220); case "Yacute": return chr_utf8(221); case "THORN": return chr_utf8(222); case "szlig": return chr_utf8(223); case "agrave": return chr_utf8(224); case "aacute": return chr_utf8(225); case "acirc": return chr_utf8(226); case "atilde": return chr_utf8(227); case "auml": return chr_utf8(228); case "aring": return chr_utf8(229); case "aelig": return chr_utf8(230); case "ccedil": return chr_utf8(231); case "egrave": return chr_utf8(232); case "eacute": return chr_utf8(233); case "ecirc": return chr_utf8(234); case "euml": return chr_utf8(235); case "igrave": return chr_utf8(236); case "iacute": return chr_utf8(237); case "icirc": return chr_utf8(238); case "iuml": return chr_utf8(239); case "eth": return chr_utf8(240); case "ntilde": return chr_utf8(241); case "ograve": return chr_utf8(242); case "oacute": return chr_utf8(243); case "ocirc": return chr_utf8(244); case "otilde": return chr_utf8(245); case "ouml": return chr_utf8(246); case "divide": return chr_utf8(247); case "oslash": return chr_utf8(248); case "ugrave": return chr_utf8(249); case "uacute": return chr_utf8(250); case "ucirc": return chr_utf8(251); case "uuml": return chr_utf8(252); case "yacute": return chr_utf8(253); case "thorn": return chr_utf8(254); case "yuml": return chr_utf8(255); case "fnof": return chr_utf8(402); case "Alpha": return chr_utf8(913); case "Beta": return chr_utf8(914); case "Gamma": return chr_utf8(915); case "Delta": return chr_utf8(916); case "Epsilon": return chr_utf8(917); case "Zeta": return chr_utf8(918); case "Eta": return chr_utf8(919); case "Theta": return chr_utf8(920); case "Iota": return chr_utf8(921); case "Kappa": return chr_utf8(922); case "Lambda": return chr_utf8(923); case "Mu": return chr_utf8(924); case "Nu": return chr_utf8(925); case "Xi": return chr_utf8(926); case "Omicron": return chr_utf8(927); case "Pi": return chr_utf8(928); case "Rho": return chr_utf8(929); case "Sigma": return chr_utf8(931); case "Tau": return chr_utf8(932); case "Upsilon": return chr_utf8(933); case "Phi": return chr_utf8(934); case "Chi": return chr_utf8(935); case "Psi": return chr_utf8(936); case "Omega": return chr_utf8(937); case "alpha": return chr_utf8(945); case "beta": return chr_utf8(946); case "gamma": return chr_utf8(947); case "delta": return chr_utf8(948); case "epsilon": return chr_utf8(949); case "zeta": return chr_utf8(950); case "eta": return chr_utf8(951); case "theta": return chr_utf8(952); case "iota": return chr_utf8(953); case "kappa": return chr_utf8(954); case "lambda": return chr_utf8(955); case "mu": return chr_utf8(956); case "nu": return chr_utf8(957); case "xi": return chr_utf8(958); case "omicron": return chr_utf8(959); case "pi": return chr_utf8(960); case "rho": return chr_utf8(961); case "sigmaf": return chr_utf8(962); case "sigma": return chr_utf8(963); case "tau": return chr_utf8(964); case "upsilon": return chr_utf8(965); case "phi": return chr_utf8(966); case "chi": return chr_utf8(967); case "psi": return chr_utf8(968); case "omega": return chr_utf8(969); case "thetasym": return chr_utf8(977); case "upsih": return chr_utf8(978); case "piv": return chr_utf8(982); case "bull": return chr_utf8(8226); case "hellip": return chr_utf8(8230); case "prime": return chr_utf8(8242); case "Prime": return chr_utf8(8243); case "oline": return chr_utf8(8254); case "frasl": return chr_utf8(8260); case "weierp": return chr_utf8(8472); case "image": return chr_utf8(8465); case "real": return chr_utf8(8476); case "trade": return chr_utf8(8482); case "alefsym": return chr_utf8(8501); case "larr": return chr_utf8(8592); case "uarr": return chr_utf8(8593); case "rarr": return chr_utf8(8594); case "darr": return chr_utf8(8595); case "harr": return chr_utf8(8596); case "crarr": return chr_utf8(8629); case "lArr": return chr_utf8(8656); case "uArr": return chr_utf8(8657); case "rArr": return chr_utf8(8658); case "dArr": return chr_utf8(8659); case "hArr": return chr_utf8(8660); case "forall": return chr_utf8(8704); case "part": return chr_utf8(8706); case "exist": return chr_utf8(8707); case "empty": return chr_utf8(8709); case "nabla": return chr_utf8(8711); case "isin": return chr_utf8(8712); case "notin": return chr_utf8(8713); case "ni": return chr_utf8(8715); case "prod": return chr_utf8(8719); case "sum": return chr_utf8(8721); case "minus": return chr_utf8(8722); case "lowast": return chr_utf8(8727); case "radic": return chr_utf8(8730); case "prop": return chr_utf8(8733); case "infin": return chr_utf8(8734); case "ang": return chr_utf8(8736); case "and": return chr_utf8(8743); case "or": return chr_utf8(8744); case "cap": return chr_utf8(8745); case "cup": return chr_utf8(8746); case "int": return chr_utf8(8747); case "there4": return chr_utf8(8756); case "sim": return chr_utf8(8764); case "cong": return chr_utf8(8773); case "asymp": return chr_utf8(8776); case "ne": return chr_utf8(8800); case "equiv": return chr_utf8(8801); case "le": return chr_utf8(8804); case "ge": return chr_utf8(8805); case "sub": return chr_utf8(8834); case "sup": return chr_utf8(8835); case "nsub": return chr_utf8(8836); case "sube": return chr_utf8(8838); case "supe": return chr_utf8(8839); case "oplus": return chr_utf8(8853); case "otimes": return chr_utf8(8855); case "perp": return chr_utf8(8869); case "sdot": return chr_utf8(8901); case "lceil": return chr_utf8(8968); case "rceil": return chr_utf8(8969); case "lfloor": return chr_utf8(8970); case "rfloor": return chr_utf8(8971); case "lang": return chr_utf8(9001); case "rang": return chr_utf8(9002); case "loz": return chr_utf8(9674); case "spades": return chr_utf8(9824); case "clubs": return chr_utf8(9827); case "hearts": return chr_utf8(9829); case "diams": return chr_utf8(9830); case "quot": return chr_utf8(34); case "amp": return chr_utf8(38); case "lt": return chr_utf8(60); case "gt": return chr_utf8(62); case "OElig": return chr_utf8(338); case "oelig": return chr_utf8(339); case "Scaron": return chr_utf8(352); case "scaron": return chr_utf8(353); case "Yuml": return chr_utf8(376); case "circ": return chr_utf8(710); case "tilde": return chr_utf8(732); case "ensp": return chr_utf8(8194); case "emsp": return chr_utf8(8195); case "thinsp": return chr_utf8(8201); case "zwnj": return chr_utf8(8204); case "zwj": return chr_utf8(8205); case "lrm": return chr_utf8(8206); case "rlm": return chr_utf8(8207); case "ndash": return chr_utf8(8211); case "mdash": return chr_utf8(8212); case "lsquo": return chr_utf8(8216); case "rsquo": return chr_utf8(8217); case "sbquo": return chr_utf8(8218); case "ldquo": return chr_utf8(8220); case "rdquo": return chr_utf8(8221); case "bdquo": return chr_utf8(8222); case "dagger": return chr_utf8(8224); case "Dagger": return chr_utf8(8225); case "permil": return chr_utf8(8240); case "lsaquo": return chr_utf8(8249); case "rsaquo": return chr_utf8(8250); case "euro": return chr_utf8(8364); } return false; } // použití echo preg_replace_callback('~&(#(x?))?([^;]+);~', 'html_entity_replace', "a–b&c–d\n"); ?>
Diskuse
dgx:
Ten switch je docela brutální brzda (vyhodnucuje se jako if (...) elseif (...) elseif (...) atd.)
Doporučuji tedy neprve vytvořit párové pole "jmenná entita" => "UTF" a provést záměnu pomocí strtr(). A pak už jen číselné entity zaměňovat přes callback. Konkrétní ukázku najdete v Texy 1.0 final (http://www.texy.info/download?texy-1.0-final.tar.gz), soubor texy-entity.php.
p.s. ve verzi 1.1 to už funguje trošku jinak...

Lukas:
to dgx: Hele ja mel za to ze switch je daleko lepsi a rychlejsi nez if (...) elseif (...) elseif ...nebo ne?
Mordae:
Ne v PHP, tady vytvoris brutalne hluboke zanoreni podminek (jak psal dgx).RATMex B:
Citát z nedávneho článku http://php.vrana.cz/skladba-ctenaru.php hovorí: "Stránky mají být semeništěm nápadů a rozborů řešení, které lze použít pro vytvoření vlastního kódu, ale je k tomu potřeba studium a vlastní píle."Tento článok však spomínanú myšlienku dosť popiera, nakoľko uvádza iba konkrétne riešenie, ktoré je vyslovene určené na to, aby ho niekto skopíroval a priamo použil. Hlavne mi chýba onen faktor "rozboru".
Ako prvé spomeniem, že funkcia chr_utf8() nemá uvedenú bázu vedomostí (napríklad http://tools.ietf.org/html/3629 ), čo v kombinácii s tým, že obsahuje chybu (píšem nižšie) nie je práve najšťastnejšie pre "lepiča". To isté platí aj pre samotnú funkciu html_entity_replace() u ktorej nie je presne objasnená implementácia a tým pádom, že obsahuje chyby, je jej skopírovanie a použitie bezvýznamné.
OK, k veci: Cieľom je získať funkcionalitu html_entity_decode() pre znakovú sadu utf-8 z PHP5, t.j. nahradiť všetky HTML entity znakmi zo sady utf-8 a ostatné entity nezmeniť. Síce funkcia nahrádza, pre ňu, neznázme entity hodnotou false, ktorá sa prevádza do prázdeho stringu a teda ich odstraňuje a nezachováva, ale to je minimálny problém, nakoľko sa dá odstrániť vrátením $matches[0] v konkrétnych prípadoch. Týmto sa zaoberať nechcem. Čo je však horšie je, že mnohé entity, ktoré by sa mali zachovať, sa aj napriek tomu "preložia" a naopak entity, ktoré by sa mali preložiť, sa zachovajú. Predstav si napr. SGML dokument, v ktorom chceš preložiť všetky a iba HTML entity (ostatné zachovať).
Chyby:
1. V SGML je možné entitu neuzavrieť znakom ";" a ukončí sa automaticky znakom, ktorý sa v mene entity nemože nachádzať. Túto funkcionalitu samozrejme nespĺňa ani html_entity_decode() v PHP5, ale bolo by slušné, aby sa "nekonzumovala" nasledovná entita. Teda napr. "< lorem > ipsum" by sa malo preložiť ako "< lorem > ipsum".
Oprava: Do množiny uzatváracích znakov v regulárnom výraze pridať "&": "~&(#(x?))?([^&;]+);~".
2a. Ako numerická entita s hexa hodnotou znaku sa prekladá aj entita s nevalidnými hexa hodnotami, čo pramení zo správania funkcie hexdec(). Napr. "_this_is_not_right_56;" sa spracuje ako entita "ሴ".
2b. Ako numerické entity sa prekladajú aj entity, ktoré numerické nie sú, pričom sa preložia ako byte s hodnotou 0. Napr. "&#text;" alebo "&#xul;".
Oprava: Kontrolovať obsah $matches[3] pred spracovaním numerickej entity, alebo upraviť regulárny výraz.
3. Numerické entity s hodnotou väčšou ako 21 bitov sa prekladajú ako 21 bitové, pričom horné bity pretekajúv prvom byte-e utf znaku (chr(240 | ($code >> 18))), čo produkuje chybný (utf-8 nevalidný) text.
Oprava: Ako 21 bitové hodnoty nespracovávať všetky tie, ktoré sú väčšie ako 16 bitov:
} elseif ($code < 65536) { ...
} else { ... }
ale tie, ktoré majú iba veľkosť 21 bitov a ostatné vracať nezmenené:
} elseif ($code < 65536) { ...
} elseif ($code < 2097152) { ...
} else { return false; }
Jakub Vrána
:
Díky za připomínky, všechny považuji za oprávněné. Funkce s těmito okrajovými možnostmi skutečně nepočítá a předpokládá, že vstup je zkontrolovaný a obsahuje jen správné entity. To se týká především bodu 2, ale i bodu 3, protože znaky delší než 21 bitů pokud vím zatím neexistují - funkce by s nimi ale jistě mohla počítat do budoucna alespoň vrácením chybového stavu.
Co se bodu 1 týče, funkce skutečně očekává přísné uzavírání entit známé z XML. Tvou navržená oprava je ale nedostatečná, způsobí jen to, že dojde k nepřeložení některých neuzavřených entit.


RATMex B:
Áno, neuzavreté entity sa nepreložia, dôležite je ale, že sa preloží najbližšia uzavretá entita. Že je úprava nedostatočná? Nevadí, pretože úprava spôsobí identické správanie s html_entity_decode() v PHP5 a to bolo jej cieľom.Avšak podstata mojej pripomienky netkvela v tom, či sa v kóde vyskytujú, alebo nevyskytujú chyby, ale v tom, že článok je bez, Tebou propagovanej, myšlienky a kód padol z neba. Živná pôda pre "lepičov", ktorými tak opovrhuješ. :-\
Jakub Vrána
:
Ano, i toto spadá do připomínek, které jsem uznal za oprávněné...


@ss@ssIn:
Neviem presne ako sa chova case '...' resp. jeho casesensitivita, ale ja by som radsej vsetko dal do strtolowercase...no uz som kopec krat videl &NBSP; a bolo by hezke aby to za ziadnych okolnosti nemohlo odignorovat...
dgx:
Samozřejmě je nutné rozlišovat velikost písmenek. &NBSP; není totéž co .

@ss@ssIn:
myslel som si, ale nebol som si isty...vsetko by do lowercasue trebalo dat... a bol by pokoj...
dgx:
Třeba velké Á má entitu Á a malé á zase á. Na velikosti holt záleží.

@ss@ssIn:
Na to som zabudol...ale aj tak AACUTE a Aacute platne su obidve... to je na dlho riesenie...
Asi nejak tak, ze 2 polia jedno uplne incase sensitive
<?php $pole['nazov'] = char; ?>
a druhe tez tak isto, ale
<?php
$pole2['nazov'] = array( 'lowercase', 'uppercase');
if( isset( $pole2[ strtolower( $nazov)])){
return $lowercase ? $pole2[ strtolower( $nazov)][0] : $pole2[ strtolower( $nazov)][1];
}
?>
lucas:
nevíte někdo jak vyparzovat z utf znak ´ , pri ulozeni do db se s nim sveze jeste znak  ,.. nejak mi to nejdeDiskuse je zrušena z důvodu spamu.

