Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 4 - Knihovna StringUtils pro práci s textem v PHP

V předešlém cvičení, Řešené úlohy k 1.-3. lekci knihovny v PHP, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

V dnešním dílu si naprogramujeme knihovnu StringUtils.

Motivace

PHP poskytuje bohatou škálu funkcí pro práci s textovými řetězci. Některé důležité funkce zde však zcela chybí a my je proto musíme implementovat sami a stále znovu a znovu.

Knihovna StringUtils

Do knihovny StringUtils vložíme několik užitečných funkcí, které PHP postrádá. Třída je pomocného charakteru, nebude mít žádný vnitřní stav a její funkce budeme opět používat často a na mnoha místech našich aplikací. Proto budou všechny její metody statické.

class StringUtils
{

}

Třídu si můžete vložit do jmenného prostoru Utility. Ačkoli je to snad samozřejmé, raději připomenu, že pro vnitřní implementaci budeme používat výhradně PHP funkce s prefixem mb_, které podporují znakovou sadu Unicode.

Metody startsWith() a endsWith()

Často bychom potřebovali vědět, zda nějaký text začíná nebo končí určitým podřetězcem. Např. parsujeme nějaké položky a právě podle částí jejich názvu se chceme orientovat. Metoda endsWith() se hodí zejména pro autoloader, kde podle koncovky názvu třídy určujeme z jaké složky se má načíst:

public static function startsWith(string $haystack, string $needle): bool
{
    return (mb_strpos($haystack, $needle) === 0);
}

public static function endsWith(string $haystack, string $needle): bool
{
    return ((mb_strlen($haystack) >= mb_strlen($needle)) && ((mb_strpos($haystack, $needle, mb_strlen($haystack) - mb_strlen($needle))) !== false));
}

Metody berou v prvním parametru prohledávaný text a v druhém hledaný podřetězec. V anglické literatuře se při problémech prohledávání používají označení kupka sena (haystack) a jehla (needle). Asi tušíte proč :)

Na metodě startsWith() není nic složitého, pouze musíme použít trojrovnítko ===, jelikož funkce mb_strpos() vrací hodnotu false při neúspěchu a hodnotu 0 při nalezení na první pozici.

Metoda endsWith() je již zajímavější. Kupka musí být delší nebo alespoň stejně dlouhá jako jehla, jinak v ní nemá smysl hledat. Dále necháme funkci mb_strpos() prohledat kupku až od té pozice, kde by mohla jehla začínat (délka kupky - délka jehly), předchozí výskyty nás nezajímají.

Funkce vyzkoušejme, nezapomeňte nastavit interní kódování na UTF-8 a cestu ke knihovně:

require_once('models/System/Utility/StringUtils.php');
mb_internal_encoding('utf-8');
var_dump(StringUtils::startsWith('Čtvrtletní hodnocení', 'Čtvrtletní'));
var_dump(StringUtils::startsWith('Pololetní hodnocení', 'Čtvrtletní'));
var_dump(StringUtils::startsWith('Pololetní a čtvrtletní hodnocení', 'Čtvrtletní'));
var_dump(StringUtils::endsWith('ArticleController', 'Controller'));
var_dump(StringUtils::endsWith('ArticleHelperController', 'Helper'));

Výsledek:

Metoda startsWith() a endsWith()
localhost

Metody capitalize() a uncapitalize()

Výše uvedené funkce převedou počáteční písmeno řetězce na velký nebo na malý tvar. Metoda capitalize() se nám hodí při výpisu např. tabulkových dat, kde chceme, aby uživatel viděl hodnoty s počátečním velkým písmenem, ale vnitřně se s nimi nějak pracuje a proto jsou malými písmeny. Metoda uncapitalize() první písmeno naopak zmenší. Obě funkce budeme využívat ještě pro vnitřní potřeby knihovny.

Metoda uncapitalize() má pravděpodobně nesprávný anglický tvar, nicméně mi připadá zřejmější, než např. lowerFirst() nebo něco podobného.

public static function capitalize(string $text): string
{
    return mb_strtoupper(mb_substr($text, 0, 1)) . mb_substr($text, 1, mb_strlen($text));
}

public static function uncapitalize(string $text): string
{
    return mb_strtolower(mb_substr($text, 0, 1)) . mb_substr($text, 1, mb_strlen($text));
}

Kód je zřejmý, pojďme ho tedy vyzkoušet:

echo(StringUtils::capitalize('žluťoučký kůň') . '<br />');
echo(StringUtils::uncapitalize('Žluťoučký kůň') . '<br />');

Výsledek:

Capitalize a Uncapitalize
localhost

Zkrácení textu

Často vypisujeme nějaké popisky např. k položkám v eshopu a chceme, aby měl popisek nějaký maximální počet znaků, jelikož delší text se ke zboží v dané šabloně nevejde. Pokud je text delší, než určitá maximální délka, potřebujeme ho zkrátít a přidat na konec tři tečky, aby bylo jasné, že část chybí. Pokud byl kratší, nepřidáváme nic. Maximální délka se počítá již s případnými tečkami:

public static function shorten(string $text, int $length): string
{
    if (mb_strlen($text) - 3 > $length)
        $text = mb_substr($text, 0, $length - 3) . '...';
    return $text;
}

Metodu někdy naleznete ve frameworcích pod názvem truncate. Můžeme vyzkoušet:

echo(StringUtils::shorten('Notebook - Intel Pentium 2020M Ivy Bridge, 15.6" LED 1366x768 lesklý, RAM 4GB, Intel HD Graphics, HDD 500GB 5400 otáček, DVD, Wi-Fi, Bluetooth, Webkamera, USB 3.0, Windows 8 64-bit', 71) . '<br />');
echo(StringUtils::shorten('Notebook - Intel Pentium 2020M', 71) . '<br />');

Výsledek jsem schválně usekl u znaku s diakritikou, vše funguje správně:

Zkrácení textu
localhost

Odstranění diakritiky

Dnešní díl zakončeme odstraněním diakritiky. To je často potřeba, když vyrábíme např. ze jména emailovou adresu daného člověka, z názvu článku jeho URL adresu, pokud chceme ověřit, že uživatel nezadal do hesla diakritiku (heslo musí být shodné s heslem bez diakritiky, jinak je v něm diakritika) a podobně.

Přiznám se, že kód metody jsem vygooglil, přesněji ho vygooglil sczdavos. Ukažme si ho:

public static function removeAccents(string $text): string
{
    $chars = array(
        // Decompositions for Latin-1 Supplement
        chr(195) . chr(128) => 'A', chr(195) . chr(129) => 'A',
        chr(195) . chr(130) => 'A', chr(195) . chr(131) => 'A',
        chr(195) . chr(132) => 'A', chr(195) . chr(133) => 'A',
        chr(195) . chr(135) => 'C', chr(195) . chr(136) => 'E',
        chr(195) . chr(137) => 'E', chr(195) . chr(138) => 'E',
        chr(195) . chr(139) => 'E', chr(195) . chr(140) => 'I',
        chr(195) . chr(141) => 'I', chr(195) . chr(142) => 'I',
        chr(195) . chr(143) => 'I', chr(195) . chr(145) => 'N',
        chr(195) . chr(146) => 'O', chr(195) . chr(147) => 'O',
        chr(195) . chr(148) => 'O', chr(195) . chr(149) => 'O',
        chr(195) . chr(150) => 'O', chr(195) . chr(153) => 'U',
        chr(195) . chr(154) => 'U', chr(195) . chr(155) => 'U',
        chr(195) . chr(156) => 'U', chr(195) . chr(157) => 'Y',
        chr(195) . chr(159) => 's', chr(195) . chr(160) => 'a',
        chr(195) . chr(161) => 'a', chr(195) . chr(162) => 'a',
        chr(195) . chr(163) => 'a', chr(195) . chr(164) => 'a',
        chr(195) . chr(165) => 'a', chr(195) . chr(167) => 'c',
        chr(195) . chr(168) => 'e', chr(195) . chr(169) => 'e',
        chr(195) . chr(170) => 'e', chr(195) . chr(171) => 'e',
        chr(195) . chr(172) => 'i', chr(195) . chr(173) => 'i',
        chr(195) . chr(174) => 'i', chr(195) . chr(175) => 'i',
        chr(195) . chr(177) => 'n', chr(195) . chr(178) => 'o',
        chr(195) . chr(179) => 'o', chr(195) . chr(180) => 'o',
        chr(195) . chr(181) => 'o', chr(195) . chr(182) => 'o',
        chr(195) . chr(182) => 'o', chr(195) . chr(185) => 'u',
        chr(195) . chr(186) => 'u', chr(195) . chr(187) => 'u',
        chr(195) . chr(188) => 'u', chr(195) . chr(189) => 'y',
        chr(195) . chr(191) => 'y',
        // Decompositions for Latin Extended-A
        chr(196) . chr(128) => 'A', chr(196) . chr(129) => 'a',
        chr(196) . chr(130) => 'A', chr(196) . chr(131) => 'a',
        chr(196) . chr(132) => 'A', chr(196) . chr(133) => 'a',
        chr(196) . chr(134) => 'C', chr(196) . chr(135) => 'c',
        chr(196) . chr(136) => 'C', chr(196) . chr(137) => 'c',
        chr(196) . chr(138) => 'C', chr(196) . chr(139) => 'c',
        chr(196) . chr(140) => 'C', chr(196) . chr(141) => 'c',
        chr(196) . chr(142) => 'D', chr(196) . chr(143) => 'd',
        chr(196) . chr(144) => 'D', chr(196) . chr(145) => 'd',
        chr(196) . chr(146) => 'E', chr(196) . chr(147) => 'e',
        chr(196) . chr(148) => 'E', chr(196) . chr(149) => 'e',
        chr(196) . chr(150) => 'E', chr(196) . chr(151) => 'e',
        chr(196) . chr(152) => 'E', chr(196) . chr(153) => 'e',
        chr(196) . chr(154) => 'E', chr(196) . chr(155) => 'e',
        chr(196) . chr(156) => 'G', chr(196) . chr(157) => 'g',
        chr(196) . chr(158) => 'G', chr(196) . chr(159) => 'g',
        chr(196) . chr(160) => 'G', chr(196) . chr(161) => 'g',
        chr(196) . chr(162) => 'G', chr(196) . chr(163) => 'g',
        chr(196) . chr(164) => 'H', chr(196) . chr(165) => 'h',
        chr(196) . chr(166) => 'H', chr(196) . chr(167) => 'h',
        chr(196) . chr(168) => 'I', chr(196) . chr(169) => 'i',
        chr(196) . chr(170) => 'I', chr(196) . chr(171) => 'i',
        chr(196) . chr(172) => 'I', chr(196) . chr(173) => 'i',
        chr(196) . chr(174) => 'I', chr(196) . chr(175) => 'i',
        chr(196) . chr(176) => 'I', chr(196) . chr(177) => 'i',
        chr(196) . chr(178) => 'IJ', chr(196) . chr(179) => 'ij',
        chr(196) . chr(180) => 'J', chr(196) . chr(181) => 'j',
        chr(196) . chr(182) => 'K', chr(196) . chr(183) => 'k',
        chr(196) . chr(184) => 'k', chr(196) . chr(185) => 'L',
        chr(196) . chr(186) => 'l', chr(196) . chr(187) => 'L',
        chr(196) . chr(188) => 'l', chr(196) . chr(189) => 'L',
        chr(196) . chr(190) => 'l', chr(196) . chr(191) => 'L',
        chr(197) . chr(128) => 'l', chr(197) . chr(129) => 'L',
        chr(197) . chr(130) => 'l', chr(197) . chr(131) => 'N',
        chr(197) . chr(132) => 'n', chr(197) . chr(133) => 'N',
        chr(197) . chr(134) => 'n', chr(197) . chr(135) => 'N',
        chr(197) . chr(136) => 'n', chr(197) . chr(137) => 'N',
        chr(197) . chr(138) => 'n', chr(197) . chr(139) => 'N',
        chr(197) . chr(140) => 'O', chr(197) . chr(141) => 'o',
        chr(197) . chr(142) => 'O', chr(197) . chr(143) => 'o',
        chr(197) . chr(144) => 'O', chr(197) . chr(145) => 'o',
        chr(197) . chr(146) => 'OE', chr(197) . chr(147) => 'oe',
        chr(197) . chr(148) => 'R', chr(197) . chr(149) => 'r',
        chr(197) . chr(150) => 'R', chr(197) . chr(151) => 'r',
        chr(197) . chr(152) => 'R', chr(197) . chr(153) => 'r',
        chr(197) . chr(154) => 'S', chr(197) . chr(155) => 's',
        chr(197) . chr(156) => 'S', chr(197) . chr(157) => 's',
        chr(197) . chr(158) => 'S', chr(197) . chr(159) => 's',
        chr(197) . chr(160) => 'S', chr(197) . chr(161) => 's',
        chr(197) . chr(162) => 'T', chr(197) . chr(163) => 't',
        chr(197) . chr(164) => 'T', chr(197) . chr(165) => 't',
        chr(197) . chr(166) => 'T', chr(197) . chr(167) => 't',
        chr(197) . chr(168) => 'U', chr(197) . chr(169) => 'u',
        chr(197) . chr(170) => 'U', chr(197) . chr(171) => 'u',
        chr(197) . chr(172) => 'U', chr(197) . chr(173) => 'u',
        chr(197) . chr(174) => 'U', chr(197) . chr(175) => 'u',
        chr(197) . chr(176) => 'U', chr(197) . chr(177) => 'u',
        chr(197) . chr(178) => 'U', chr(197) . chr(179) => 'u',
        chr(197) . chr(180) => 'W', chr(197) . chr(181) => 'w',
        chr(197) . chr(182) => 'Y', chr(197) . chr(183) => 'y',
        chr(197) . chr(184) => 'Y', chr(197) . chr(185) => 'Z',
        chr(197) . chr(186) => 'z', chr(197) . chr(187) => 'Z',
        chr(197) . chr(188) => 'z', chr(197) . chr(189) => 'Z',
        chr(197) . chr(190) => 'z', chr(197) . chr(191) => 's',
        // Euro Sign
        chr(226) . chr(130) . chr(172) => 'E',
        // GBP (Pound) Sign
        chr(194) . chr(163) => ''
    );
    return strtr($text, $chars);
}

Jde vlastně jen o výčet diakritických znaků, které jsou v UTF-8 reprezentovány jako několik ASCII znaků za sebou (např. háček a c). Výčet je zapsán jako pole, aby pomocí něj mohla následně PHP funkce strtr() provést nahrazení.

Kód je součástí metody nějakého modulu WordPressu, kde je výčet ještě mnohem delší, nám však bude tento stačit.

Metodu vyzkoušejme:

echo(StringUtils::removeAccents('Příliš žluťoučký kůň úpěl ďábelské ódy. PŘÍLIŠ ŽLUŤOUČKÝ KŮŇ ÚPĚL ĎÁBELSKÉ ÓDY. <br />'));
echo(StringUtils::removeAccents('Příliš žluťoučký kůň úpěl ďábelské ódy. PŘÍLIŠ ŽLUŤOUČKÝ KŮŇ ÚPĚL ĎÁBELSKÉ ÓDY. <br />'));

Výsledek:

Odstranění diakritiky
localhost

To by bylo pro dnešek vše.

Příště, v lekci Dokončení knihovny StringUtils v PHP, v kniihovně StringUtils zajistíme převod na pomlčky mezi snake_case a camelCase. Také si vygenerujeme hesla.


 

Předchozí článek
Řešené úlohy k 1.-3. lekci knihovny v PHP
Všechny články v sekci
Knihovny pro PHP
Přeskočit článek
(nedoporučujeme)
Dokončení knihovny StringUtils v PHP
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
15 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity