Ááá

Další web používající WordPress

Srp

8

Mizérie Java hostingů 3

Trochu jsem bádal nad tím, proč se Java nehodí na malé webové projekty. Jediný důvod je podle mě hosting, protože jinak aplikace v čistém PHP+PDO nebo čistém JSP+JDBC se v nákladech na údržbu moc lišit nebudou (obdobně Prado vs. JSF, Zend Framework vs. Spring MVC apod.).

Potíž je v nasazení na sdílený hosting. V případě PHP je tohle vcelku pěkně vyřešené: Nastaví se safe_mode, open_basedir, max_execution_time a hlavně memory_limit a je to. Pořád lze snadno zařídit shození nebo přetížení serveru, ale dost těžko omylem.

Jinými slovy – můžete hostovat 100 špatně napsaných aplikací na jedné mašině a ona to nějak ustojí: Když se aplikace moc roztahuje, PHP ji prostě utne. Blbuvzdorné.

Ohledně Javy jsem zkoušel hledat nějaké dobré zprávy na toto téma a našel jsem akorát povzdechnutí, že to je opravdu problém, hlavně správa paměti. Je ještě možné pro každého uživatele spouštět samostatné JVM, ale to pak náklady na provoz posouvá někam k VPS…

Na druhou stranu – existují hostingy, které nabízí sdílený Tomcat (vím o Savvy a G-hostingu), takže to možná nějak lze zařídit a jenom jsem to nenašel. Spíš to na mě ale působí tak, že ty hostingy prostě doufají, že u nich nikdy nikdo nebude hostovat špatně napsané aplikace…

Srp

4

První krůčky v Javě 2

První metou je pro mě teď základní orientace v Java SE, to je základ. Koukal jsem po různých publikacích, ale nakonec mi stejně nejvíc sedly tutoriály přímo od Sunu.

Abych byl docela přesný, úplně první krůčky jsem podstoupil už před několika lety, když jsem zkoušel psát midlety pro svůj telefon (Java 1.4 a MIDP1, pokud si pamatuju).

Od té doby se ale jazyk vyvíjel a když jsem někde náhodou narazil na javové zdrojáky, rozumněl jsem jim čím dál tím míň. Najednou se to všude hemžilo zavináči a špičatými závorkami… Už mám za sebou jazykový tutoriál, osvěžil jsem si pozapomenuté znalosti, získal nové (anotace a generiky) a ani to moc nebolelo. :-)

Teď si procházím tutoriály na základní třídy. Některé věci beru více hopem (práci se znakovým terminálem třeba), do jiných se zabírám pečlivěji – takovým tématem jsou i vlákna, u kterých se zdržuju právě teď. Jednak proto, že je to pro mě do jisté míry nové, jednak mi to přijde i docela zajímavé (já vím, jsem divný) a hlavně si uvědomuji, že tohle je fakt důležité.

Občas narazím na historicky dané nedostatky, ale jenom v nepodstatných prkotinách. Například java.io.File by se podle všech pouček měla nazývat spíše FileName nebo FilePath, metoda main zase nerespektuje konvenci začínat název metody slovesem… fakt jenom prkotiny. Největší zděděnou nepříjemností jsou asi pravidla pro názvy balíčků…

Baví mě to čím dál víc a skoro začínám litovat toho, že jsem u PHP vydržel tak dlouho. Dnes bych titulek u svého předchozího článku napsal asi tak o dvě slova kratší. ;-)

Srp

2

PHP je i není na hovno 7

(Šokující titulek vyjadřuje moje aktuální ambivalentní pocity.)

Trpělivost se svým vlastním PHP frameworkem jsem už dávno ztratil. Pomalu se rozhlížím po něčem, kde se sice web neskládá ze stránek, ale z metod nějakých objektů, ale za to se vývojem nebudu muset zabývat já.

Jedním z takových frameworků je i Nette, jehož některé části už používám a za jehož vývojem stojí programátor, což je významné plus (většinu PHPkařů, včetně sebe, považuju spíše za kodéry, než programátory). Nesporné mínus je ovšem dokumentace – informace je potřeba hledat převážně metodami reverzního inženýrství, navíc se Nette pořád hodně mění (to jde ruku v ruce – než stihne David nějakou fíčuru popsat, tak ji dvakrát předělá).

Dokumentaci aplikace nelze považovat za kompletní bez dokumentace jejího frameworku, do dokumentace Nette se pouštět nechci a do psaní aplikace bez dokumentace taky ne. Sice kóduju pouze sám pro sebe, ale jsem náročný zadavatel…

Pak přichází do úvahy Zend Framework nebo Symfony, s dokumentací jsou na tom dobře, nechybí ani návody typu „jak udělat přihlášení“, nebo „jak se používá ACL“. Od té doby, co jsem opustil ZF (tehdy verze 1.0), oba tyto projekty hodně vyrostly, ale pořád to není ono.

Nějak se mi do nich nechce a nedokážu popsat proč, nemůže za to samotné PHP?

Proč použít PHP?

Doteď jsem uvažoval hrozně omezeně, pouze PHP + MySQL. Pro mě to má pár velkých výhod, hlavně asi tyto (v tomto pořadí):

  1. Je všude a konkurence na poli PHP hostingů je ohromná. Jenom zkuste najít k Hostmonster nějakou alternativu s JSP nebo ASP.NET…
  2. PHP docela umím, MySQL jakž takž.
  3. Je to lingua franca webových vývojářů. Referenční implementace různých webových API bývají často v PHP, méně často i v něčem jiném (české příklady: Pipay, Invia).

(Když koukám na bod 2, nechce se mi věřit, že jsem to napsal opravdu já. Kdy jsem takhle zpohodlněl?)

Proč ne PHP?

A pak jsou tu ty nevýhody…

Pokud teď čekáte nějaký seznam, koukněte se třeba k Ondrovi Žižkovi na Výhody a nevýhody PHP, nebo pár let staré (a stále z velké části aktuální) What I don't like about PHP a I’m sorry, but PHP sucks.. Na netu se toho válí mraky.

Co jiného?

Rails, Django a další jsou taková exotika. Navíc se moc nehodí k ničemu jinému, než k psaní webových aplikací jedním určitým způsobem. Pokud mám začít odznova, vyplatí se podle mě pouze jedna ze dvou možností: .NET nebo Java.

Když se dívám na nabídku hostingů JSP a ASP.NET, připomíná mi nabídku PHP hostingů před nějakými pěti lety. Pomalu jich přibývá a ceny jsou čím dál přijatelnější. Předpokládám, že tento trend bude pokračovat a PHP během pár let ztratí svoji hlavní výhodu.

Java mě láká víc. Připadá mi, že více věcí přebírá .NET od Javy, než naopak. Třeba různé knihovny – dříve byl jUnit než NUnit, Hibernate vzniklo před NHibernat apod. Taky větší nabídka vývojových i běhových prostředí je pro mě plus. Zase na dotnetu se mi líbí C# a možnost používat i jiné jazyky, to je prostě super…

Věcně vzato mi ale obě volby přijdou stejně správné a proto nezbývá, než vybrat čistě pocitově. Dám nějakou dobu šanci Javě a bude se vidět. Třeba se ještě k PHP vrátím… a třeba taky ne.

Kvě

31

__PHP_Incomplete_Class 4

Pokud deserializujete objekt a PHP nenajde jeho třídu, vznikne objekt třídy __PHP_Incomplete_Class. Zkuste například:

$foo = unserialize('O:3:"Foo":1:{s:3:"bar";s:9:"something";}');
var_dump($foo);

Dostanete takový výsledek:

object(__PHP_Incomplete_Class)#2 (2) {
  ["__PHP_Incomplete_Class_Name"]=>
  string(3) "Foo"
  ["bar"]=>
  string(9) "something"
}

Tento objekt je tak trochu k ničemu, neumožňuje ani přístup ke svým vlastnostem:

Notice: Unknown: The script tried to execute a method or access a property of an
 incomplete object. Please ensure that the class definition "Foo" of the object
you are trying to operate on was loaded _before_ unserialize() gets called or pr
ovide a __autoload() function to load the class definition  in E:\phell.php(148)
 : eval()'d code on line 1

Call Stack:
    0.0058      87880   1. {main}() E:\phell.php:0
   25.8032     104144   2. eval(''echo $foo->bar\r\n;'') E:\phell.php:148

Dobrá zpráva je, že se PHP pokusí třídu nejdřív najít přes autoload. Já na to narazil díky menšímu refaktoringu, po kterém mě nenapadlo pročistit cache…

Přijde mi to chování dost podivné. Rozumné by mi přišlo buďto jít cestou starého benevolentního PHP a vytvořit instanci stdClass, nebo cestou nového přísnějšího PHP a vyhodit catchable fatal error. Vytvoření budižkničemu-objektu je fakt zvláštní nápad.

Dub

19

Co je nového 5

Mám-li to stručně shrnout, nic.

Jak to vypadá s mým frameworkem? Nevím, nějak na to nemám náladu a to zpravidla znamená, že se mi na tom něco podvědomně nelíbí a až přijdu na to, co to je, tak to zase nějak celý překopu…

Trochu začínám tušit, že to souvisí se správou obsahu, protože právě na tom jsem se zasekl.

Ne že bych nevěděl, jak na to, kdybych musel. Dál bych si ručně vytvářel formuláře, které víceméně kopírují strukturu databáze. Přidání sloupce do tabulky by si vždy vyžádalo také ruční přidání dalšího ovládacího prvku do formuláře a naopak, odebrání odebrání…

Zásek je způsoben tím, že nemusím. Můžu si dovolit klidně na měsíc zmrazit celý projekt a čekat, až mě napadne nejlepší možné řešení. Řešení, o němž nejsem přesvědčen, mě totiž nebaví a jako amatér (od latinského amare) ho proto nemůžu přijmout.

A co že mi to furt vrtá tou hlavou? Jak odstranit duplicitu mezi strukturou databázové tabulky a formulářem, kterým se editují její záznamy. Jak zobecnit běžné úkony (známé pod zkratkou CRUD) a zachovat si přitom možnost cokoli snadno měnit.

Rýsuje se mi v hlavě něco trochu na způsob scaffoldingu, ale furt to nemůžu uchopit. To je fakt k nevydržení…

Dub

13

Nette je venku 0

Včera proběhla slezina PHP frameworky jaro 2008. Já nakonec bohužel do té prdele světa nejel a teď mě to docela mrzí.

Konečně byl zveřejněn framework Nette, čímž se potvrdila domněnka, že opravdu existuje. Existují i dvě ukázkové aplikace, akorát na ně odnikud z nettephp.com nevedou odkazy:

  • Akrabat – známý ZF tutoriál předělaný do Nette.
  • Patnáctka – hra, kde šoupáním patnácti kostiček skládáte obrázek krávy.

Jmenné prostory

Pokud už nějaké části Nette používáte, první čeho si všimnete, je odstranění N prefixu. Takže NDebug → Debug, NForm → Form, NHtml → Html apod. Je to příprava na PHP 5.3, kde se k tomu přidají jmenné prostory (Html → Nette::Web::Html apod.), konečně.

Vizte jmenné prostory v PHP >= 5.3.0

Nette::Forms::Form

Příklady použití Nette::Forms najdete v adresáři /tests/Forms/, takže tady se omezím na novinky.

Proti dříve omylem zveřejněné verzi je tu pár rozdílů. Změny jsou ale spíše pod povrchem. Vstup se už například nezpracovává v konstruktoru, ale až při volání isSubmitted(), nebo explicitním zavoláním populate() (což je přejmenovaná processHttpQuery).

Přibyla metoda isValid(), která vstup zkontroluje pouze v případě, že ještě zkontrolovaný nebyl. Naproti tomu validate() validuje vždy.

Jo a konstanty pro validační pravidla se přesunuly do (zatím falešného) jmenného prostoru Forms, takže místo NForm::REGEXP budete psát Forms::REGEXP apod.

Nejpodstatnější změnou ve vykreslovacích funkcích je, že metoda renderEnd() teď před zavírací značkou ještě vykreslí skrytá pole (metodou renderHiddens()).

Autoloader

Nette nabízí hned tři různé autoloadery:

// use Nette::Loaders;

  • SimpleLoader – pro třídu Foo::Bar zkusí vložit soubor Foo/Bar.php.
  • BotAutoLoader – vizte vkládání souborů v Nette.
  • NetteLoader – slouží pouze pro vkládání tříd z Nette frameworku podle jednoduchého pole 'třída' => 'soubor'.

tools.php

Několik užitečných funkcí:

  • Nette::uniqueId() generuje unikátní ID.
  • Nette::checkUTF($s) validuje UTF-8 řetězec.
  • Nette::iniFlag($var) zjišťuje logickou hodnotu konfiguračních voleb (narozdíl od ini_get(), které vrací koniny typu „on“ nebo „off“, tohle vrací TRUE nebo FALSE).
  • Nette::defaultize(&$var, $default) je ifsetor.
  • Nette::arrayGet(array $array, $key, $default = NULL) je ifsetor pro prvky pole. Funkce Nette::defaultize má tu nepříjemnou vlastnost, že když ta proměnná neexistuje, tak ji to vytvoří a to u polí nebývá záhodno.
  • Nette::promo($xhtml = TRUE) vloží tohle: Powered by Nette Framework>

Testování

David používá takový zajímavý postup.

Každou funkci prověří krátkým skriptem, jestli funguje tak, jak očekává a pokud ano, tak si uloží výstup toho skriptu jakožto referenční výstup. Když pak chce ověřit, jestli se něco nezměnilo, tak opět spustí tyto testovací skripty a jejich výstupy porovná (nejspíš nějakým diffem).

Mnohem víc

Je tam toho mnohem víc, třeba taky implementace access contol listů. Ještě jsem to celý neprozkoumal…

Dub

8

Testování databáze 9

Testování objektů, které pracují s databázovými daty, to byl vždycky trochu oříšek.

Mock objekty

Dají se používat tzv. mock objekty, které fungují tak, že místo databáze podstrčíte jakousi falešnou databázi, která vrací pevně nastavený výstup a navíc můžou testovat i svůj vstup. V PHPUnit můžete mock připravit nějak takhle (pseudokód):

class MyTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $mock = $this->getMock("Database");
        $mock->expects($this->once())
            ->method("query")
            ->will($this->returnValue(array("nějaký", "výstup")));

        ... kód, u kterého testujeme volání query
    }
}

Tím vytvoříte objekt, který navenek vypadá jako Database (ve skutečnosti se vytvoří nová třída, která je potomkem Database, ale překrývá všechny její metody, ale to už zabýhám do zbytečných detailů), který dělá tohle:

  • Sám testuje, že se právě jednou (->expects($this->once())) zavolá jeho metoda query() (->method("query")).
  • Na to volání query() vrátí array("nějaký", "výstup") (->will(array("nějaký", "výstup"))).

Nebo to samé v Simpletest:

Mock::generate("Database");

class MyTest extends UnitTestCase
{
    public function testSomething()
    {
        $mock = new MockDatabase;
        $mock->expectOnce("query");
        $mock->setReturnValue("query", array("nějaký", "výstup"));

        ... kód, u kterého testujeme volání query
    }
}

Někdy jsou mocky k nezaplacení, jindy je ale potřeba operovat nad skutečnou databází.

Skutečná databáze, neskutečná data

Jak třeba zjistíte, že metoda pro načtení posledních X článků z blogu opravdu načte posledních X článků z blogu? Že jí podstrčíte mocka, který bude očekávat „správný“ select a vracet „správnou“ odpověď?

To asi ne, za prvé to jsou takové implementační podrobnosti, které do testu nepatří a za druhé to je právě to, co potřebujeme otestovat → že metoda položí takový dotaz, který opravdu vrátí to, co potřebujeme. Výstup této metody se ale v produkčním prostředí mění, co vlastně v testu očekávat?

Řešení je takové, že test probíhá nad skutečnou databází, ve které jsou ale testovací, předem známá, data. Před každým testem si databázi připravíme a nasypeme do ní tato data. Díky tomu můžeme docela přesně říct, že první tři články jsou „Ahoj Světe“, „Nazdar lidi“ a „blablabla“, v tomto pořadí.

A teď přijde to zábavné, jak na to.

Více databází

Tento přístup je upřednostňován v Ruby on Rails a myslím, že i v Symfony. Nadefinujete si (v základu) tři databáze:

  • Vývojová. Tohle je databáze, se kterou si zkoušíte web používat a blbnout.
  • Testovací. Ta se používá pro automatické testy, před každým testem se naplní předem známými testovacími daty.
  • Produkční. Jakmile projekt upnete na ostrý server, bude používat tuto databázi.

Pokud budete chtít někdy pouštět své automatické testy i na ostrém serveru, vytvoříte si ještě jednu databázi tam. Tento přístup zajistí naprosto spolehlivě, že se testy nikdy ani omylem nedotknou opravdových dat.

Mě ale vždycky přišlo pohodlnější ke každému projektu provozovat jenom dvě databáze – jednu na localhostu, druhou na serveru. Navíc momentálně spouštím testy takovým způsobem, že může nastat více přístupů k databázi zároveň a pak bych asi musel celý test obalit transakcí a už bych nemohl transakce používat v samotné aplikaci… Zkrátka nic pro mě.

TEMPORARY tabulky

Tohle je jenom pro MySQL a je to moje volba.

MySQL umožňuje vytvářet tzv. dočasné tabulky, které se od normálních tabulek liší v pár detailech:

  • Existují pouze v databázovém spojení, ve kterém byly vytvořeny a spolu s jeho uzavřením zanikají.
  • Nenajdete je příkazem SHOW TABLES.
  • Můžou „přebít“ normální tabulky. Máte v databázi tabulku „tab1“ a vytvoříte si dočasnou tabulku se stejným názvem. Jakýkoli dotaz nebo příkaz, odkazující se na „tab1“, se odteď odkazuje na naši dočasnou tabulku.
  • Asi ještě něco, na co jsem doteď nenarazil.

Schválně si to zkuste:

CREATE TABLE `tab1` (`a` INT);
CREATE TEMPORARY TABLE `tab1` (`b` INT);
SHOW CREATE TABLE `tab1`;

Pak si otevřete druhou konzoli a vyjeďte si SHOW CREATE TABLE taky tam.

Jak tedy připravím databázi pro testování? Ten kousek kódu vypadá takhle (ano, přešel jsem na dibi):

dibi::query("DROP TABLE IF EXISTS [tmp];");
foreach (dibi::query("SHOW TABLES;") as $row) {
    $table = array_shift($row);
    dibi::query("CREATE TEMPORARY TABLE [tmp] LIKE %n", $table);
    dibi::query("CREATE TEMPORARY TABLE %n LIKE [tmp]", $table);
    dibi::query("DROP TABLE [tmp];");
}
dibi::loadFile(PZ_ROOT . "/tests/fixtures/data.sql");

Pokud používáte některý xUnit, tohle patří někam do metody setUp(). Zbývá už jenom vytvořit si ten soubor s testovacími daty (viz poslední řádku → data.sql) a je hotovo.

Metoda tearDown() v mém případě není nutná, protože každý test jede ve vlastním threadu a má svoje vlastní spojení, které ty tabulky uklidí samo, ale pro úplnost, představoval bych si ji takhle:

foreach (dibi::query("SHOW TABLES;") as $row) {
    $table = array_shift($row);
    $createTable = dibi::query("SHOW CREATE TABLE %n", $table)->fetch();
    $createTable = array_pop($createTable);
    if (preg_match("~^CREATE TEMPORARY TABLE ~", $createTable)) {
        dibi::query("DROP TABLE %n", $table);
    }
}

Pozor musíte dávat akorát s příkazy jako DROP TABLE nebo CREATE TABLE, těmi si můžete do databáze zanést bordel. To se mi ale ještě nepovedlo. ;-) (Jak často v aplikacích používáte CREATE/DROP TABLE? Já přibližně nikdy.)

No není ta MySQL skvělá?

Bře

29

__autoload 6

Napsal jsem si vlastní __autoload, protože Zend_Loader mi přijde zbytečně pomalý. Přitom mě pár věcí překvapilo.

Nutnost validace vstupu

Pokud provedete tohle:

class_exists("http://www.pokerzpravy.cz/", true);

Tak se zavolá autoload a jako argument dostane http://www.pokerzpravy.cz/. Až potom se vyhodnotí, že taková třída neexistuje, ale žádné ověření předem, že ani existovat nemůže, není.

Výjimky

Je vcelku známé, že výjimka vyhozená z autoloadu je PHPkem ignorována. Proto mě mile překvapilo, že v případě class_exists() tomu tak není. Jinými slovy, následující test funguje tak, jak potřebuju:

try {
    class_exists("http://www.pokerzpravy.cz/", true);
    $t->fail("Autoload should validate its input.");
} catch (Exception $e) {
}

Výsledek

function __autoload($className)
{
    if (!preg_match("~^[A-Za-z_][a-zA-Z0-9_]*\$~", $className)) {
        throw new Exception("$className is not a valid class name.");
    }
    require_once str_replace("_", "/", $className) . ".php";
}

Bře

25

Framework: Radikální zjednodušení 0

Základní myšlenky mého vznikajícího frameworku jsem už popsal dříve. Čím se liším od zbytku světa lze hrubě zjednodušeně popsat tímto obrázkem:

Pokud jde o konkrétní implementaci, momentálně mě dost láká tohle: Požadavky routovat přímo pomocí mod_rewrite podle návodu z La Trine a různé inicializace provádět v souboru předpojeném přes auto_prepend_fi­le.

Jediná věc mě mrzí, že se připravím o PATH_INFO. Ačkoli pro těch pár stránek, které by PATH_INFO využily, bych mohl vytvořit další zvláštní mod_rewrite pravidla. Vždyť mi přece nejde o nějakou univerzálnost…

Nakonec, když už mod_rewrite skýtá takové ohromné možnosti, není to škoda využívat ho jenom k hloupému přepisování všeho na hlavní index.php?

Bře

20

Prvnííí! 4

Vítejte na mém novém mocinky cupa blogísku. ;-)

Důvody k přesunu byly v podstatě dva: Jednak chci ještě občas něco napsat a potom o tom třeba i diskutovat (a na to už ten starý server Webzdarma jaksi nestačí) a jednak bych za tím rád udělal tlustou čáru:


Ten starý weblog se mnou vyrůstal, začal jsem ho psát ještě jako uhrovitý teenager a z některých článků je mi teď trochu i trapně. Ale takový jsem zkrátka byl a nebudu předstírat, že ne (díky Internet Archive ani nemůžu ;-) ).

Pro nový weblog jsem vymyslel ještě pitomější název než kdy dřív, vybral hezkou šablonu a komentáře obdařil Texylou. A nakonec jsem si já, kritik technologie IDN, zaregistroval doménu xn--1caaa.info…

Taky jsem narazil na to, čeho jsem se obával už když jsem prvně viděl „kompaktní“ verzi Texy:

Warning: is_file() [function.is-file]: open_basedir restriction in effect. File(/icon_win­k.gif) is not within the allowed path(s): (/home/www/xn–1caaa.info:/tmp:/var­/lib/php5) in /home/www/xn–1caaa.info/sub­domeny/www/wp-content/plugin­s/texy2-plugin-wordpress/texy­.compact.php on line 782

Zkuste hádat, co dělá pětisetznaková řádka 782. Oprava byla nakonec jednoduchá, v texy-plugin.php stačilo správně nastavit $texy->emoticonModule->fileRoot, ale najít to…

Dosti bylo keců. Tady máte odkaz na RSS článků a RSS komentářů, krom toho je pod každým článkem odkaz na komenty k tomu kterému článku.

Next Entries »
Další možnosti...

Blogroll

  • Blog vývojářů
  • Česká verze
  • Dokumentace
  • Fórum podpory
  • Motivy vzhledu
  • Pluginy
  • Vaše nápady
  • WordPress Planet
  • Pages

    • O těchto stránkách

Your List

  • Your list items
  • Your list items
  • Your list items
  • Your list items
  • Your list items

© Ááá * WordPress * LoseMyMind * Feed feed