4. diel - Knižnica StringUtils pre prácu s textom v PHP
V minulej lekcii, Dokončenie knižnice DateUtils v PHP , sme dokončili knižnicu DateUtils, ktorá uľahčuje formátovanie a parsování českého dátumu a času. V dnešnom dieli si naprogramujeme knižnicu StringUtils.
Motivácia
PHP poskytuje bohatú škálu funkcií pre prácu s textovými reťazcami. Niektoré dôležité funkcie tu však úplne chýba a my ich preto musíme implementovať sami a stále znovu a znovu.
StringUtils
Do knižnice StringUtils vložíme niekoľko užitočných funkcií, ktoré PHP chýba. Trieda je pomocný charakter, nebude mať žiadny vnútorný stav a jej funkcie budeme opäť používať často a na mnohých miestach našich aplikácií. Preto budú všetky jej metódy statické.
class StringUtils
{
}
Triedu si môžete vložiť do menného priestoru Utility. Hoci je to snáď samozrejmé, radšej pripomeniem, že pre vnútorné implementáciu budeme používať výhradne PHP funkcie s prefixom mb_, ktoré podporujú unicode.
StartsWith () a endsWith ()
Často by sme potrebovali vedieť, či nejaký text začína alebo končí určitým podreťazcom. Napr. dekóduje nejaké položky a práve podľa častí ich názvu sa chceme orientovať. EndsWith () sa hodí najmä pre autoloader, kde podľa koncovky názvu triedy určujeme z akej zložky sa má načítať.
public static function startsWith($haystack, $needle) { return (mb_strpos($haystack, $needle) === 0); } public static function endsWith($haystack, $needle) { return ((mb_strlen($haystack) >= mb_strlen($needle)) && ((mb_strpos($haystack, $needle, mb_strlen($haystack) - mb_strlen($needle))) !== false)); }
Metódy berú v prvom parametri prehľadávaný text a v druhom hľadaný podreťazec. V anglickej literatúre sa pri problémoch prehľadávaní používajú označenie kôpka sena (haystack) a ihla (needle). Asi tušíte prečo
Na startsWith () nie je nič zložité, len musíme použiť trojrovnítko, pretože mb_strpos () vracia false pri neúspechu a 0 pri nájdení na prvej pozícii.
EndsWith () je už zaujímavejšie. Kupka musí byť dlhšia alebo aspoň rovnako dlhá ako ihla, inak v nej nemá zmysel hľadať. Ďalej necháme funkciu mb_strpos () prehľadať kôpku až od tej pozície, kde by mohla ihla začínať (dĺžka kôpky - dĺžka ihly), predchádzajúce výskyty nás nezaujímajú.
Funkcia vyskúšajme, nezabudnite nastaviť interné kódovanie na UTF-8:
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ýsledok:
Capitalize () a uncapitalize ()
Vyššie uvedené funkcie prevedú začiatočné písmeno reťazca na veľký alebo na malý tvar. Capitalize () sa nám hodí pri výpise napr. Tabuľkových dát, kde chceme, aby užívateľ videl hodnoty s počiatočným veľkým písmenom, ale vnútorne sa s nimi nejako pracuje a preto sú malými písmenami. Uncapitalize () prvé písmeno naopak zmenší. Obe funkcie budeme využívať ešte pre vnútorné potreby knižnice.
Pozn. uncapitalize je pravdepodobne nasprávný anglický tvar, však mi pripadá zrejmejšie, než napr. lowerFirst alebo niečo podobné.
public static function capitalize($text) { return mb_strtoupper(mb_substr($text, 0, 1)) . mb_substr($text, 1, mb_strlen($text)); } public static function uncapitalize($text) { return mb_strtolower(mb_substr($text, 0, 1)) . mb_substr($text, 1, mb_strlen($text)); }
Kód je zrejmý, poďme ho teda vyskúšať:
echo(StringUtils::capitalize('žluťoučký kůň') . '<br />'); echo(StringUtils::uncapitalize('Žluťoučký kůň') . '<br />');
výsledok:
Skrátenie textu
Často vypisujeme nejaké popisky napr. K položkám v eshope a chceme, aby mal popisok nejaký maximálny počet znakov, pretože dlhší text sa k tovaru v danej šablóne nezmestí. Ak je text dlhší, než určitá maximálna dĺžka, potrebujeme ho skrátiť a pridať na koniec tri bodky, aby bolo jasné, že časť chýba. Ak bol kratší, nepridávame nič. Maximálna dĺžka sa počíta už s prípadnými bodkami.
public static function shorten($text, $length) { if (mb_strlen($text) - 3 > $length) $text = mb_substr($text, 0, $length - 3) . '...'; return $text; }
Metódu niekedy nájdete vo frameworkoch pod názvom truncate. Môžeme vyskúšať:
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, WiFi, Bluetooth, Webkamera, USB 3.0, Windows 8 64-bit', 71) . '<br />'); echo(StringUtils::shorten('Notebook - Intel Pentium 2020M', 71) . '<br />');
Výsledok som schválne odťal u znaku s diakritikou, všetko funguje správne:
Odstránenie diakritiky
Dnešný diel zakončíme odstránením diakritiky. To je často potrebné, keď vyrábame napr. Z mena emailovú adresu daného človeka, z názvu článku jeho URL adresu, ak chceme overiť, že používateľ nezadal do hesla diakritiku (heslo musí byť zhodné s heslom bez diakritiky, inak je v ňom diakritika) a podobne.
Priznám sa, že kód metódy som vygooglil, presnejšie ho vygooglil sczdavos . Ukážme si ho:
public static function removeAccents($text) { $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); }
Ide vlastne len o výpočet diakritických znakov, ktoré sú v UTF-8 reprezentované ako niekoľko ASCII znakov za sebou (napr. Háčik ac). Výpočet je zapísaný ako pole, aby pomocou neho mohla následne PHP funkcie strtr () vykonať nahradenie.
Kód je súčasťou metódy nejakého modulu WordPress (konkrétne tu: https://core.trac.wordpress.org/...rmatting.php), kde je výpočet ešte oveľa dlhší, nám však bude tento stačiť.
Metódu vyskúšajme:
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ýsledok:
To by bolo pre dnešok všetko. Nabudúce, v lekcii Dokončenie knižnice StringUtils v PHP , StringUtils dokončíme.