Hlavní navigace

PHP okénko: Kontrola pravopisu v HTML dokumentu

Jakub Vrána 30. 5. 2005

Dnešní PHP okénko na příkladu kontroly pravopisu ukazuje, jak rozebrat HTML dokument, a představuje dva nové obraty v PHP 5.

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 $check řetězec ke zkontrolování v kódování UTF-8
@param string $lang kód jazyka používaný knihovnou PSpell
@return array slova nenalezená ve slovníku, false v případě chyby
*/
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 $code 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));
    }
}

define('WORD_BOUNDARY', '[^A-Za-z' . chr_utf8(192) . '-' . chr_utf8(563) . ']');
?> 

Znaky s kódy 192 až 563 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ů:

Využít regulární výrazy
Hledání značek obsahujících daný atribut a odpovídajících uzavíracích značek je mnohokrát zpracovaná úloha, která se pomocí regulárních výrazů neřeší zrovna pohodlně.
Využít rozšíření XML
HTML dokumenty nejsou XML, takže bychom je nejprve museli převést – nejsnáze s využitím rozšíření Tidy. Toto rozšíření ostatně HTML dokument do struktury objektů dokáže rozebrat samo o sobě.
Využít rozšíření DOM
Standard DOM je k rozebírání struktury HTML a XML dokumentů přímo navržen, ale v PHP je toto rozšíření k dispozici až od verze 5.

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 $node kontrolovaný DOM objekt
@param string $lang kód jazyka používaný knihovnou PSpell
@return array slova nenalezená ve slovníku
*/
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::lo­adHTMLFile 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ů.


Podobně laděné texty můžete najít i na autorově weblogu PHP triky.

Našli jste v článku chybu?

30. 5. 2005 9:47

| je bitový OR, & je bitový AND a >> je bitový posun vpravo. Pokud c = a | b, tak c bude mít nastavené ty bity, které má nastavené a nebo b, pokud d = a & b, tak d bude mít nastavené ty bity, které má nastavené a i b.

Proč jsou použity zrovna takhle je dobře vidět z popisu kódování UTF-8

19. 6. 2005 23:45

HejTi (neregistrovaný)
Chtěl jsem se zeptat, jestli je někde možné sehnat český slovník pro kontrolu pravopisu? Pokud není nikde zdarma (což předpokládám), tak bych snad byl ochoten snad i něco zaplatit - nemáte představu kolik by to potom mohlo stát?
Lupa.cz: Slevové šílenství je tu. Kde nakoupit na Black Friday?

Slevové šílenství je tu. Kde nakoupit na Black Friday?

Vitalia.cz: Říká amoleta - a myslí palačinka

Říká amoleta - a myslí palačinka

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

DigiZone.cz: Česká televize mění schéma ČT :D

Česká televize mění schéma ČT :D

DigiZone.cz: Sony KD-55XD8005 s Android 6.0

Sony KD-55XD8005 s Android 6.0

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Podnikatel.cz: EET zvládneme, budou horší zákony

EET zvládneme, budou horší zákony

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Podnikatel.cz: Na poslední chvíli šokuje vyjímkami v EET

Na poslední chvíli šokuje vyjímkami v EET

DigiZone.cz: NG natáčí v Praze seriál o Einsteinovi

NG natáčí v Praze seriál o Einsteinovi

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

Root.cz: Vypadl Google a rozbilo se toho hodně

Vypadl Google a rozbilo se toho hodně

Vitalia.cz: Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Vitalia.cz: Baletky propagují zdravotní superpostel

Baletky propagují zdravotní superpostel

Vitalia.cz: To není kašel! Správná diagnóza zachrání život

To není kašel! Správná diagnóza zachrání život

DigiZone.cz: Recenze Westworld: zavraždit a...

Recenze Westworld: zavraždit a...

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

120na80.cz: Rakovina oka. Jak ji poznáte?

Rakovina oka. Jak ji poznáte?