Článek vyšel v rámci PHP okénka na serveru Root.cz.
Pokud chceme zkontrolovat pravopis jednojazyčného HTML dokumentu, je to s využitím rozšíření PSpell poměrně jednoduché. Funkci, která bude kontrolu zajišťovat, navrhneme s využitím statické proměnné rovnou tak, aby se inicializace slovníku nemusela vykonávat při každém zavolání:
<?php /** Kontrola pravopisu pro všechna slova v textu * @param string řetězec ke zkontrolování v kódování UTF-8 * @param string kód jazyka používaný knihovnou PSpell * @return array slova nenalezená ve slovníku, false v případě chyby * @copyright Jakub Vrána, https://php.vrana.cz/ */ function spell_check($check, $lang) { static $pspell = array(); // pole s inicializovanými slovníky pro jednotlivé jazyky if (!isset($pspell[$lang])) { pspell_config_create($lang); // bez vytvoření konfigurace nemusí pspell_new() najít slovník $pspell[$lang] = @pspell_new($lang, "", "", "utf-8"); } if (!$pspell[$lang]) { // nepodařilo se načíst slovník pro daný jazyk return false; } $return = array(); $words = array_unique(preg_split('~' . WORD_BOUNDARY . '+~u', $check, -1, PREG_SPLIT_NO_EMPTY)); foreach ($words as $word) { if (!pspell_check($pspell[$lang], $word)) { $return[$word] = true; } } return $return; } $file = file_get_contents($filename); $file = strip_tags($file); // pozor, může spojit dvě slova do jednoho $errors = spell_check($file, 'cs'); ?>
Funkce předpokládá, že dokument bude v kódování UTF-8, což v reálných podmínkách často nebude splněno. Pro skutečné nasazení tedy budeme muset kódování detekovat a např. funkcí iconv převést. V kódování UTF-8 nefunguje správně regulární výraz \W
, takže slova rozdělujeme pomocí vlastní konstanty WORD_BOUNDARY
, kterou můžeme definovat např. takto:
<?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)); } } */ define('WORD_BOUNDARY', '[^A-Za-z\\x{C0}-\\x{233}]'); ?>
Znaky s kódy C0 až 233 hexadecimálně jsou vyhrazeny znakům odvozeným z Latinky, funkce .chr_utf8
potom vrací reprezentaci těchto znaků v kódování UTF-8
S vynaložením určitého úsilí by se dal kód např. s využitím regulárních výrazů vylepšit tak, aby kontroloval kupříkladu i atributy alt a title nebo aby ignoroval obsah značek <script>
a <style>
. Mnohem zajímavější ale bude kód upravit tak, aby respektoval atribut lang ve vícejazyčných HTML dokumentech. Tento atribut určuje jazyk hodnot textových atributů a obsahu značek a můžou ho používat skoro všechny HTML značky. Abychom atribut dokázali zpracovat, budeme muset HTML dokument rozebrat některým z následujících způsobů:
Nejčistší řešení bude přes rozšíření DOM:
<?php $SPELL_ATTRS = array("abbr", "alt", "label", "prompt", "standby", "summary", "title"); /** Kontrola pravopisu DOM objektu a jeho potomků na základě hodnoty atributu $lang * @param DOMNode kontrolovaný DOM objekt * @param string kód jazyka používaný knihovnou PSpell * @return array slova nenalezená ve slovníku * @copyright Jakub Vrána, https://php.vrana.cz/ */ function spell_check_childs($node, $lang) { $return = array(); // nastavení jazyka if ($node->getAttribute("lang")) { $lang = $node->getAttribute("lang"); } // kontrola textových atributů foreach ($GLOBALS["SPELL_ATTRS"] as $attr) { if ($node->getAttribute($attr)) { $return += spell_check($node->getAttribute($attr), $lang); } } // kontrola dětí foreach ($node->childNodes as $child) { if ($child instanceof DOMText) { $return += spell_check($child->nodeValue, $lang); } elseif ($child instanceof DOMElement && !in_array($child->nodeName, array("script", "style"))) { $return += spell_check_childs($child, $lang); } } return $return; } $dom = DOMDocument::loadHTMLFile($filename); $html = $dom->getElementsByTagName("html")->item(0); $errors = spell_check_childs($html, 'cs'); ?>
Funkcí DOMDocument::loadHTMLFile se načte HTML dokument a na kořenovou značku <html>
se spustí rekurzivní kontrola pravopisu. Ta v případě nastaveného atributu lang změní aktuální jazyk, zkontroluje atributy obsahující textovou informaci a prochází děti DOM objektu – v textových objektech kontroluje pravopis, značky <script>
a <style>
a komentáře ignoruje a na ostatní objekty volá rekurzivně sama sebe. Chyby se vracejí nadřazené funkci v návratové hodnotě, alternativním řešením by bylo předávat je v parametru funkce volaném referencí.
U kontroly dětí si lze všimnout toho, že vlastnost childNodes je ve skutečnosti objekt typu DOMNodeList. Díky iteraci objektů zavedené v PHP 5 je ale možné tento objekt přímo procházet konstrukcí foreach.
Zdánlivě jednoduchý příklad kontroly pravopisu v HTML dokumentu se zvrtl na rozebírání dokumentu rozšířením DOM. Využili jsme při tom dva nové obraty v PHP 5 – operátor instanceof a iteraci objektů.
Diskuse je zrušena z důvodu spamu.