Zápisky z roku 2008

Neděle, 22. červen 2008

JavaScript: Převod barevných složek RGB do tvaru RRGGBB

Při tvorbě jakékoli aplikace je dobré vyvažovat přehlednost zápisu kódu s jeho efektivitou. Protože interprety JavaScriptu bohužel nejsou z nejrychlejších, přichází velmi často ke slovu právě faktor druhý, na úkor intuitivnosti zápisu. Jako třeba u následujícího problému:

Máme barevné složky R, G a B, z nichž každá je vyjádÅ™ena desetinným číslem v intervalu <0; 1>. Z tÄ›ch potÅ™ebujeme získat Å™etÄ›zec ve tvaru RRGGBB. Složky bude tedy tÅ™eba zvÄ›tÅ¡it do rozsahu 0–255, zaokrouhlit, pÅ™evést na čísla Å¡estnáctková a jednoznakové výsledky zleva doplnit nulou, aby sestavený Å™etÄ›zec mÄ›l vždy právÄ› Å¡est znaků. Jak ale splnit poslední požadavek co nejrychleji a bez podmínÄ›ného pÅ™idávání nuly?

Metoda číslo.toString(16) sice převede číslo do šestnáctkové soustavy, ale k doplnění počáteční nuly ji přimět nelze. Na sprintf() můžeme zapomenout a co se týče regulárních výrazů, jejich vyhodnocení je pomalé. Nabízí se vkládat nulu vždy a potom z řetězce zprava vytáhnout dva znaky, tedy ('0'+cislo).substr(-2), jenže IE záporný offset nepodporuje.

Nedávno mě trklo zajímavé řešení. K číslům nejprve přičteme 256, čímž po převodu získáme vždy nejméně 10016 a nejvíce 1FF16. Pak zbývá jen oříznout počáteční jedničku. Jak jsem zmínil na začátku, přehlednost takového algoritmu je na pováženou, ale za tu efektivitu jistě stojí:

function rgbToHex(R, G, B) {
    return (
        Math.round(256 + 255*R).toString(16).substr(1) +
        Math.round(256 + 255*G).toString(16).substr(1) +
        Math.round(256 + 255*B).toString(16).substr(1)
    )
}

Pátek, 29. únor 2008

70 řádkové Smarty – nač dÄ›lat ze Å¡ablon vÄ›du?

Šablony jsou běžnou součástí dynamických webů. Není proto divu, že dosud vzniklo nemalé množství šablonovacích systémů, více či méně vydařených. Smarty osobně řadím do té druhé skupiny, myslím, že se jedná o nepraktický molouch spatlaných regulárních výrazů. Místo dalšího hanobení Smarty zneužiji jeho nestydaté velikosti a dám ji do kontrastu se svým skromným šablonovacím uzlíčkem o cca 70 řádcích.

Žádná odlehčená syntaxe, nic se nekompiluje, šablonu představují běžné PHP soubory, které bych se koderovi bez znalosti PHP zdráhal svěřit. Nezní to příliš přesvědčivě, že? Přinejmenším pro vlastní projekty je to ale báječná věc. Ono to totiž má i výhody.

Pořád ještě ho chcete vidět?

Ten šablonovací skript jestli chcete vidět. Ano? Tak tady je (pro PHP5).

A tuhle je ukázka, jak se s ním bezva pracuje:

require_once('Template.inc.php');

$doc = new Template('tpl/'); // Å¡ablony jsou v adresáři tpl/
$doc->nazev 'Hudební nástroje'// nastavíme nadpis stránky

$nastroje[] = array('nazev'=>'Harmonika''ladeni'=>'C#');
$nastroje[] = array('nazev'=>'Příčná flétna''ladeni'=>'F');
// zparsujeme seznam nástrojů
foreach($nastroje as $nastroj) {
    
$doc->parse('nastroj.php''.NASTROJE'$nastroj);
}
// tisknout Å¡ablonu stranka.php
$doc->parse('stranka.php'Template::dstPrint);

Neboli:

  • Vytvoříme instanci $doc. Potom můžeme nastavit její vlastnosti – říkejme jim z pohledu Å¡ablony globální promÄ›nné, i když tento termín má obecnÄ› jiný význam. Globální proto, že jsou přístupné ve vÅ¡ech Å¡ablonách parsovaných tímto objektem.
  • Následuje jednoduchý seznam hudebních nástrojů reprezentovaný asociativními poli. Ta dále použijeme pÅ™i parsování jako lokální promÄ›nné. Lokální proto, že budou dostupné jen v dané Å¡ablonÄ› a taky že mají pÅ™ednost pÅ™ed globálními.
  • Cyklem foreach dvakrát zparsujeme Å¡ablonu tpl/nastroj.php. Výsledek se pÅ™ipojí do promÄ›nné $doc->NASTROJE (PÅ™ipojí se díky té tečce na začátku, kdyby tam nebyla, tak tu promÄ›nnou pÅ™epíšeme.)
  • Nakonec parsujeme Å¡ablonu tpl/stranka.php, pÅ™ičemž ji pomocí speciální konstanty Template::dstPrint namísto uložení tiskneme. NÄ›kdy se může hodit Template::dstReturn, čímž, jak jistÄ› tušíte, obdržíme výsledek návratovou hodnotou.

Å ablona tpl/stranka.php

<html>
<head>
    <title><?=htmlspecialchars($nazev)?></title>
</head>
<body>
<h1><?=htmlspecialchars($nazev)?></h1>
<ul>
<?=$NASTROJE?>
</ul>
</body>
</html>

Å ablona tpl/nastroj.php

<li><strong><?=htmlspecialchars($nazev)?></strong> - ladÄ›ní <?=$ladeni?></li>

V obou šablonách jsem záměrně použil stejnou proměnnou $nazev, aby bylo vidět, že lokální proměnné mají přednost.

Příklad: Parsování vrácených řádků MySQL

$res mysql_query('SELECT jmeno, prijmeni FROM uzivatele');

while(
$row mysql_fetch_assoc($res)) {
    
$doc->parse('uzivatel.php''.UZIVATELE'$row);
}

Metody init(), check() a clear()

Metodu Template::init() voláme na začátku šablony, když je třeba určit, které rozšiřující funkce šablona používá (třeba pro práci s URL). Nedělá nic jiného než require_once všech skriptů, které jsou uvedeny jako parametry. Hledá je ve stejném adresáři, kde je umístěna třída a na konec automaticky doplňuje koncovku .inc.php. Je-li např. třída umístěna v adresáři lib/, pak Template::init('string')
provede require_once('lib/string.inc.php')

Template->check() ověřuje dostupnost šablony. Vrací TRUE pokud daná šablona existuje a lze ji přečíst.

Template->clear() čistí všechny nastavené vlastnosti objektu.

A ty výhody?

Zpracování Å¡ablon je pekelnÄ› rychlé (hlavnÄ› proto, že se nemusejí kompilovat). Dále, spousta Å¡ablonovacích systémů používá k nastavení promÄ›nných nÄ›jakou funkci, nejčastÄ›ji assign(). Nemohou tak dÄ›lat psí kusy jako tÅ™eba list($doc->id$doc->nazev) = mysql_fetch_row($res), což tady možné je.

Zde popsaný bastl nepovažuji za průlomové dílo, ostatně pár hodně podobných jsem už viděl. Na svou jednoduchost mi ale přijde nesmírně efektivní a jistě bude pro někoho alespoň inspirací.

V rubrice PHP, 00.33 | Trvalý odkaz | Komentáře (677)

Pondělí, 18. únor 2008

jscolor – Co mi na formulářích chybÄ›lo a teď už ne

HTML definuje celou Å™adu formulářových prvků, nÄ›které situace ale vyžadují komfortnÄ›jší ovládání než tato základní sada nabízí. Ke slovu se pak dostává DHTML, které se se základními prvky vhodnÄ› smísí. ÄŒasto ve formulářích postrádám textové pole výhradnÄ› pro celočíselnou hodnotu, se Å¡ipkami pro inkrementaci a dekrementaci po pravé stranÄ› (v Å™eči Visual Basicu by to byl TextBox provázaný s objektem UpDown). To ale nechme na jiný článek…

Co mi vpravdÄ› až donedávna chybÄ›lo víc byl dialog pro výbÄ›r barvy. Velká část uživatelů hexadecimální zápis barvy neovládá a je dobré jim výbÄ›r odstínu usnadnit nÄ›jakým jednoduchým rozhraním, jednodušším než Vyber barvu v Photoshopu a zkopíruj ji. Tedy, ne že by takových rozhraní k dneÅ¡nímu dni už desítky neexistovaly, povÄ›tÅ¡inou ale bývají realizována jako klikatelné matice 216 web-safe barev, což je hodnÄ› málo – proč nedopřát uživateli více volnosti? A tak, hnán moudrým heslem, rozhodl jsem se vytvoÅ™it color picker vlastní, minimalistický, avÅ¡ak s volbou barvy velejemnou.

Do světa tedy dnes vypouštím nástroj jscolor. Nechť má spokojených uživatelů, jako je konkurenčních projektů, a ať poskytne hezkých barev alespoň z půlky tolik, kolik při ladění padlo nadávek na IE.

Pozadí: Text:

jscolor.com


Neděle, 27. leden 2008

Regulární výraz - MySQL TIMESTAMP

Tímhle článkem bych rád započal svou občasnou knihovnu regulárních výrazů, i když vím že podobné projekty se už nějaký ten pátek po internetu válejí.

Na úvod jsem si šetřil časovou známku. Výraz propustí téměř všechny platné formáty, kterými lze v MySQL nastavovat sloupec TIMESTAMP. Kromě formátu kontroluje také platost data a času, a věřte nebo ne, poradí si i s přestupnými roky. Možná proto je tak nevkusně dlouhý. Zadané datum a čas mohou být v rozmezí 1970-01-01 00:00:00 až 2037-12-31 23:59:59. Výraz pečlivě zkontrolován cyklem v celém rozsahu :-)

Regexp

/^((((19)?[7-9][0-9]|(20)?([0-2][0-9]|3[0-7]))[[:punct:]]+(0?2[[:punct:]]+(0?[1-9]|1[0-9]|2[0-8])|
(0?[469]|11)[[:punct:]]+(0?[1-9]|[12][0-9]|30)|(0?[13578]|10|12)[[:punct:]]+(0?[1-9]|[12][0-9]|3[01])))|
((19)?(8[048]|[79][26])|(20)?([02][048]|[13][26]))[[:punct:]]+0?2[[:punct:]]+29)([[:punct:]\s]+([01]?[0-9]|2[0-3])
([[:punct:]]+([0-5]?[0-9])([[:punct:]]+([0-5]?[0-9]))?)?)?$/

Souhlasí

1970-01-21
2037-3#24  16&39
08-3-24 16:39:42
2008?03+24@!_16%39?42

Nesouhlasí

200803-24-16-39-42
20080324163942


Sobota, 19. leden 2008

Převracení boolean sloupce v administraci

U administrace je třeba ve většině případů počítat s tím, že ji může používat i více uživatelů naráz. Je zřejmé, že provedou-li změnu stejné databázové buňky současně, poslední zápis přepisuje ostatní. O tomhle problému se v souvislosti s PHP rozepsal Jakub Vrána.

Jsou ale situace, kdy pravidlo poslední vyhrává je tím nejmenším zlem. Jednou takovou je triviální převrácení boolean sloupce. Mějme například výpis článků u nichž je tlačítko publikovat, které se kliknutím přepíná na nepublikovat a zase zpět. URL takového tlačítka může vypadat takto:

  1. ?clanek=70&akce=prohodPublik
  2. ?clanek=70&akce=publikovat pro nepublikovaný článek
    ?clanek=70&akce=nepublikovat pro publikovaný článek

První zápis je jednoduchý a univerzální, protože ve výpisu měníme jenom ID článku a akce zůstává stejná. Převrátit boolean sloupec v databázi je také snadné. Přesto takové řešení nelze doporučit, protože převracejí-li uživatelé A a B hodnotu současně, vše se takhle zamotá:

  1. A i B vidějí článek nepublikováný
  2. A chce článek publikovat a prohodí jeho stav
  3. článek je nyní publikovaný, B který neobnovil stránku ho vidí jako nepublikovaný
  4. B chce článek publikovat a prohodí jeho stav
  5. článek je nyní nepublikovaný, což ani jeden z uživatelů nechtěl, navíc A ho vidí jako publikovaný

Druhý zápis si ve stejné situaci vede znamenitě. Uplatňuje ono pravidlo poslední vyhrává:

  1. A i B vidějí článek nepublikováný
  2. A chce článek publikovat a nastaví ho jako publikovaný
  3. článek je nyní publikovaný, B který neobnovil stránku ho vidí jako nepublikovaný
  4. B chce článek publikovat a nastaví ho jako publikovaný
  5. článek je nyní publikovaný, A i B ho vidějí správně
V rubrice PHP, 11.29 | Trvalý odkaz | Komentáře (482)

Pátek, 18. leden 2008

Vypnutí PHP v určitém adresáři

Neboli PHP, dost! Možná to bude pro někoho novinka, ale PHP se dá taky vypnout. V takovém případě server odešle skripty tak jak leží a běží. Jde mimochodem o skvělý způsob, jak zajistit bezpečnost při uploadování souborů formulářem. Doteď, když jsme se chtěli vyhnout spuštění cizího PHP skriptu na serveru, museli jsme (někteří) precizně kontrolovat koncovku či vyhodnocovat obsah. Ale ono stačí udělat v daném adresáři soubor .htaccess obsahující:

php_flag engine off

Všechny PHP skripty kolem takového .htaccess (včetně podadresářů) ztrácejí na své skriptovitosti. Stávají se z nich obyčejné textové soubory které nikomu ani v nejmenším nevadí. Tři věci je třeba mít na zřeteli:

  1. nemusí to fungovat na každém serveru (ani v takovém případě ovšem nehrozí riziko spuštění cizího PHP skriptu, protože server odmítne poslušnost se slovy Internal Server Error)
  2. pozor, aÅ¥ uživatel neuploaduje vlastní .htaccess namísto původního… to by byl docela trapas :-)
  3. server může obsluhovat i jiné skriptovací jazyky, např. Python či Ruby
V rubrice PHP, 16.23 | Trvalý odkaz | Komentáře (344)

Úterý, 8. leden 2008

Gradient v CSS za pomoci PHP a mod_rewrite

To jsem jednou potřeboval udělat barevný přechod. Ale ne GIMPárnu na jedno použití, nóbrž přechod dynamický, něco jako že by se zavolal soubor a vono se to vygenerovalo a tvářilo jako obrázek. S PHP a knihovnou GD je věc snadná a přidá-li se i mod_rewrite, pak i velmi slušivá. Laťku jsem si nastavil zhruba do téhle výše:

  1. možnost prolnutí více než dvou barev v libovolných vzdálenostech
  2. výběr mezi vertikální a horizontální orientací
  3. ukládání obrázku do cache
  4. bezpečnost z hlediska zatížení serveru či přeplnění cache

Co se týče bodu 1, skript dostane seznam klíčových barev a velikosti mezer které má mezi nimi proložit pÅ™echodem. Jako oddÄ›lovač takového seznamu jsem zvolil znak + který asi nejlépe znázorňuje spojování. VaÅ¡e komentáře mÄ› pÅ™imÄ›ly zmÄ›nit oddÄ›lovač na spojovník "-" Klíčové barvy se zapisují Å¡esti velkými hexadecimálními číslicemi, mezery desítkovÄ›. Aby se mezera odliÅ¡ila od barvy, může mít maximálnÄ› pÄ›t cifer – v praxi se použijí nanejvýš čtyÅ™i. Zbývá jeÅ¡tÄ› rozliÅ¡it orientaci (bod 2). Tak co tÅ™eba jedním malým písmenem na začátku? A bude to buď x pro horizontálnÄ› se opakující pÅ™echod nebo y pro pÅ™echod s opakováním vertikálním.

Bod 3: Skript na konci, pokud je to možné, uloží výsledek do souboru. Název souboru má stejný tvar s jakým pracuje mod_rewrite, takže pÅ™i příštím požadavku už soubor fyzicky existuje a má pÅ™ednost. Když vím, že obrázek potÅ™ebuju jenom jednou a je zbytečné ho cachovat (tÅ™eba jako v testovacím rámečku níž), pÅ™idám na konec parametr ?tmp. Jelikož parametry nejsou součástí názvu souboru, mají cachované soubory opÄ›t pÅ™ednost. A proč je to cachování tak nechutnÄ› důmyslné? Protože pÅ™episování URL je nastaveno case-sensitive, takže v cache nevzniknou duplicity – každý pÅ™echod je jednoznačnÄ› identifikován. Nuže a bod 4 vyÅ™ešíme omezením délky celého pÅ™echodu – dejme tomu na 5000px?

Zápis gradientu

Jak je popsáno výše, na začátku musí být malé písmeno x/y, za nímž následují klíčové barvy a číselně vyjádřené mezery mezi nimi. Pokud mezi barvami prolnutí být nemá (tedy nulová mezera), pak se zapíše ihned další barva, např. yFF00FF-00FF00 jsou fialový a zelený pixel vedle sebe. Jednoduchý gradient pro svislé opakování (y) získáme třeba pod tímhle názvem:

Způsob zápisu gradientu

Délku celého pruhu spočítáme součtem klíčových barev (3) a mezer mezi nimi (5+2). Pruh je tedy dlouhý 10px.

Jak se to teda používá

  1. adresář gradient/ si zkopírujete kam je pohodlno
  2. má-li fungovat cachování, je třeba povolit do něj zápis
  3. teď už v adresáři najdete jakýkoli obrázek gradientu, např. gradient/y00FF00-300-0000FF.png je 302px dlouhý přechod od zelené k modré

Kdo náhodou nemá k dispozici mod_rewrite, může skript volat přímo: gradient/gradient.php?repeat=y&grad=00FF00-300-0000FF

A proč se pruh roztahuje na 5px

… když by stačil 1px? Tučňákům nestačí. A možná i tygrům, pÅ™iznám se, že jsem po tom příliÅ¡ nepátral. Problém je v hladkosti pÅ™echodu. Zatímco pod Windows je opakovaný pÅ™echod stejný pÅ™i jakékoli šířce pruhu, FF/Linux ukáže 1px proužek pÅ™i nižší barevné hloubce jako nekoukatelnÄ› ohyzdné čáry. ZjevnÄ› nejprve obrázek proloží a teprve potom ho opakuje. Je tedy jasné, že jednorozmÄ›rný pÅ™echod nemůže proložit nikterak rozmanitÄ›, ovÅ¡em dostane-li vÄ›tší plochu, stane se s ním pÅ™ibližnÄ› tohle:

Opakovaný černobílý přechod při 8-bitové hloubce (FF/Linux)
Opakovaný černobílý přechod při 8-bitové hloubce (FF/Linux)

K pÄ›ti pixelům jsem doÅ¡el po sáhodlouhém zkouÅ¡ení. VÅ¡e do 3px je bída, 4px začínají být pÅ™ijatelné a pÄ›t – bingo! Vyšší hodnoty samosebou pÅ™inášejí výsledek o Å¡petku lepší, ale to už je příliÅ¡ na úkor velikosti souboru (ačkoli nijak drasticky, PNG komprese se celkem vytáhla). A protože tÄ›ch 5px je krásná a vždy použitelná hodnota, dal jsem ji na pevno do hlavičky skriptu namísto do URL, které je tím jednodušší. PÅ™ipomínám, že fujtajblproužkový problém se týká jen nížších barevných hloubek (do 16 bitů).

Zkouška

gradient/.png

Předvolby: B/W B/W/B R/G/B Černý panel Aqua moje :-)

A proč je v nadpisu "CSS"?

body {
  background:url('gradient/x664477-600-CC99FF.png') left top repeat-x fixed #CC99FF;
}

Download

gradient.zip

V rubrice PHP, 02.47 | Trvalý odkaz | Komentáře (375)

Rubriky

Navigace

Hledání