Na navigaci | Klávesové zkratky

Translate to English… Ins Deutsche übersetzen…

Vkládání souborů v Nette

Programový kód zpřehledníme rozdělením do více souborů. V případě OOP se nabízí ukládání každé třídy/interface (resp. několika úzce souvisejících tříd) do samostatného souboru. Tyto soubory je vhodné pojmenovávat podle nějaké své konvence. Následně je vkládáme do skriptů konstrukcí require_once. Opět, kvůli přehlednosti, bývá zvykem všechna vkládání umísťovat na začátky skriptů.

Odbočka: pokud použijete relativní cestu k souboru, require_once jej dohledá možná trošku nečekaným způsobem. Na include_path se také nerad spoléhám, proto se kloním k používání absolutních cest tímto způsobem:

require_once dirname(__FILE__).'/soubor.php';

Slabá místa

Tohle všechno sice přispívá ke zpřehlednění kódu, ale má to i slabé stránky:

  • vkládáme soubory, které třeba nebudeme potřebovat
  • ke každé třídě si musíme pamatovat název souboru

Obzvláště ta dualita třída ↔ soubor mi hodně vadí. Hledal jsem tedy nějaké flexibilní řešení, které by jednak odstranilo obě slabá místa, zároveň co nejvíce zjednodušilo programátorovi život a hlavně nekladlo nová omezení či pravidla.

Řešení

Řešením je Nette\Loaders\RobotLoader. Základem je magická funkce __autoload. Díky ní se soubor s definicí třídy vloží až ve chvíli, kdy je skutečně potřeba.

Nette má vlastní obsluhu __autoload(). Jejím jádrem je vcelku jednoduchá funkce, která v adresáři webové aplikace proběhne všechny PHP skripty (tedy i podadresářích) a pomocí funkce token_get_all v nich vyhledá definice tříd a rozhraní. Výsledkem je tabulka identifikátorů a relativních cest k souborům. Nette pak přesně ví, který soubor při požadavku na konkrétní třidu vložit. Je to velice rychlé. Tabulka se samozřejmě uchovává v cache.

Při nahrání nové verze aplikace na web lze jedním příkazem tabulku vygenerovat znovu, nebo ještě jednodušeji – stačí smazat příslušný soubor a vygeneruje se sama.

aktualizace: Nette může běžet v tzv. vývojářském režimu (DEVELOPMENT). Pokud v tomto režimu není třída nalezena, provede se automaticky regenerace cache. Nepomůže-li to, ohlásí se error.

Výhody řešení

  • zbavíte se všech volání require_once
  • vkládají se jen potřebné soubory
  • bez striktních konvencí pojmenování souborů
  • možno mít více tříd v jednom souboru
  • není třeba ručně udržovat tabulku
  • Nette již při generování odhalí konflikty názvů
  • připadáte si jako v kompilovaném jazyce

Je to prostě velmi pohodlné a krutě návykové :-)

Komentáře

  1. rADo http://radekhulan.cz/ #1

    avatar

    Co podpora pro PHP 4.x?

    před 11 lety | reagoval [2] David Grudl [15] Ivan
  2. David Grudl http://davidgrudl.com #2

    avatar

    #1 rADo, Řeším to explicitním voláním __autoload. Například __autoload("BaseClass"); před každým class MyClass extends BaseClass {...} lze vložit automaticky PHP 5 → 4 převodníkem.

    Pravda je, že teď, co mám na všech oblíbených hostinzích PHP5, tak jsem na podporu PHP4 vnitřně rezignoval. Tedy alespoň pro nové projekty. Tudíž tento návod klidně ber jako PHP5 only.

    před 11 lety | reagoval [15] Ivan
  3. medden #3

    avatar

    Tabulka se samozřejmě uchovává na disku, v podobě INI souboru.

    Takže si to dobre predstavujem, ak si myslím, že to bude takto? (ini súbor)
    MenoTriedy = cesta
    MenoTriedy2 = cesta2

    Nebolo by potom jednoduchšie (a rýchlejšie) to nechávať v php súboroch? Mimochodom, vo svojom projekte používam niečo podobné :-)

    před 11 lety | reagoval [6] medden
  4. Honza V. #4

    Doprkýnka, to je tak strašně jednoduchý a přitom naprosto geniální, že mě to nenapadlo dřív…

    před 11 lety
  5. Honza V. #5

    Jo, nechceš už dát ke stažení aspoň ten modul, co umí tady tohlencto?

    před 11 lety
  6. medden #6

    avatar

    #3 meddene,
    No ten príklad mal byť takto:

    MenoTriedy = cesta
    MenoTriedy2 = cesta2
    …

    Skúsil som si spraviť benchmark (predpokladám, že používaš parse_ini_file) a bol som šoknutý:

    INI: avg = 0.00327613067627 dev = 0.000486483699644<br />
    PHP: avg = 0.00415405726433 dev = 0.00078431006504

    Použil som svoj php.ini súbor, mierne modifikovaný pre php použitie ;-), 1000 iterácií

    Aj keď ono to je viac-menej pochopiteľné, prečo je ini rýchlejšie: php musí robiť aj syntatický rozbor a veci okolo toho… Ale aj tak som dosť prekvapený.

    před 11 lety | reagoval [8] johno
  7. mrzout http://www.abclinuxu.cz/blog/mrzutej #7

    Tenhle genialni postup jsem kdysi cetl v nejakem clanku a hned se mi zalibil. (mozna: http://www.sitepoint.com/…or-autoload/ )

    Lze to vylepsit tak, aby __autoload pokud nenalezne v cache cestu k souboru, provede sam pokus o jeho dohledani v adresarove strukture a pokud ho nalezne, do cache doplni. Naopak i pri nalezeni testovat jeho existenci a pokud neni, z cache vyhodit.

    Tim se uplne vypusti nutnost „pracne“ generovat cache.

    před 11 lety | reagoval [14] Borek [18] David Grudl
  8. johno http://johno.jsmf.net/ #8

    avatar

    #6 meddene, Prepáč, ale takéto testy sú na smiech. Kedy budeš parsovať 1000 ini súborov? A kedy ti tam bude vadiť ten rozdiel v tisícine sekundy? Oznamujem Ti, že robíš v zlom jazyku ak ti toto vadí.

    Vieš koľko trvá napríklad zaslanie typického SELECTu do DB až po získanie dát naspäť?

    před 11 lety | reagoval [11] medden
  9. Pavel #9

    Nechci byt protivny :), ale tento zpusob predstavovani Nette vedl jen k tomu, ze jsem zatim narazil na jiny framework, ktery se mi velice zalibil a ktery uz mohu zacit pouzivat na novem projektu… Takze popisy hezke, ale nakonec koncim u jineho :(

    snad priste, pokud tou dobou jen nebude 50. dil povidani o Nette – coz doufam, ze ne :)

    před 11 lety | reagoval [10] halogan [20] bleak
  10. halogan http://halogan.blog.lupa.cz #10

    #9 Pavle, Mohu se zeptat, ktery framework Vas oslnil?

    před 11 lety
  11. medden #11

    avatar

    #8 johno, Mne je jasné, že tisícina sekundy je oproti iným veciam úplne zanedbateľná. Ja som sa pozastavoval iba nad tým, že parsovanie .ini pomocou štandartných funkcií je rýchlejšie ako parsovanie .php. Je mi jasné, že ak chcem niečo optimalizovať, tak sa musím zamerať na najslabšie (najpomalšie) časti, ako je zväčša databáza, a takýmito vecami vlastne strácam čas. Ja si to uvedomujem. No niekedy je dôležité poznať aj postupy, ktoré sú všeobecne rýchlejšie.

    Viem, že Premature optimization is the root of all evil …

    před 11 lety | reagoval [13] johno
  12. Jakub Podhorský #12

    avatar

    pěkný řešení :) tohle mě nikdy nenapadlo…holt zkusím si s tím asi pohrát :) a nějak to zakomponovat do toho mojeho :)

    před 11 lety
  13. johno http://johno.jsmf.net/ #13

    avatar

    #11 meddene, No ešteže tak.

    DGX: Čo ak niekto používa autoload vo svojej/inej knižnici? Na to si myslel?

    před 11 lety | reagoval [16] llook
  14. Borek http://borber.com/blog/ #14

    #7 mrzoute, To je určitě podstatné vylepšení, není moc dobré, když se musí ručně editovat něco, co může být uděláno automaticky. Zde ovšem výměnou za drobnou výkonnostní úlitbičku.

    P.S. Dobrá captcha :)

    před 11 lety
  15. Ivan #15

    #1 rADo, #2 David Grudl Taky bych nejraději na verzi 4 rezignoval, ale..

    Bohužel nenalezení třídy vyhodí fatal error, tak to nelze obejít ani přes set_error_handler. Tak to taky řeším explicitně – v každé třídě napíšu co používá: Cl::req(‚Class1, Class2, Class3‘). A pak postup podobný tomu v článku. Nebyl by lepší nápad?

    před 11 lety
  16. llook http://llook.wz.cz/weblog/ #16

    avatar

    #13 johno, To by mělo být jednoduchý – prostě se v té knihovně __autoload zakomentuje a o načítání tříd knihovny se postará Nette.

    Problém by mohl nastat, kdyby autoload té knihovny třídy neincludoval, ale vytvářel. Ale to bude asi velmi okrajový problém.

    před 11 lety | reagoval [17] johno
  17. johno http://johno.jsmf.net/ #17

    avatar

    #16 llooku, To je síce pekný nápad, ale nebude fungovať ak tá knižnica nie je v adresári, ktorý ten skript prehľadáva.

    Možno by stálo za to tam dať ešte nejaký zoznam adresárov, ktoré má prehľadávať.

    před 11 lety | reagoval [19] David Grudl
  18. David Grudl http://davidgrudl.com #18

    avatar

    llook to popisuje přesně, tento způsob je použitelný napříč knihovnami, proto je možné ostatní __autoload vyřadit.

    #7 mrzoute, wow, to je skutečně podobný způsob. Jen princip parsování přes regexp, ukládání do globální proměnné a používání absolutní cest jsou cestou zpět.

    Pokud jde o znovuvytvoření cache pokaždé, když se třída nenajde, Nette to dělá, ale jen v ladícím režimu (doplnil jsem článek).

    Jinak v praxi se mi ukázalo, že tabulku na hostingu nikdy negeneruji. Rovnou ji totiž s aktualizací webu překopíruju z lokálu – tím, že se ukládají jen relativní cesty, v tom není problém. Taktéž se nemusím bát nějakého „refactoringu“ na hostingu ;)

    před 11 lety
  19. David Grudl http://davidgrudl.com #19

    avatar

    #17 johno, je tam pevně daný kořenový adresář a seznam podadresářů k prohledávání (nemá třeba smysl prohledávat cache atd.)

    Ale pokud to nevyhovuje, je možné __autoload dále rozšířit, neboť Nette::autoload vrací boolean, nevidím v tom žádný problém:

    function __autoload($class)
    {
        $result = Nette::autoload($class);
        if ($result == false)
            nejaky_jiny_autoload($class);
    }
    před 11 lety
  20. bleak #20

    avatar

    #9 Pavle, Tento způsob představování Nette mne taky moc nebaví. Ale zrovna by se mi nějaký framework hodil – jsem amatérský příležitostný a líný tvůrce PHP skriptů.
    Tak tedy také prosím Pavla o tip na framework, který začal používat.

    před 11 lety | reagoval [21] FrozenDog [24] jules
  21. FrozenDog http://frozendog.blognito.sk #21

    #20 bleaku, len si pekne pockaj s nami ostatnymi. Mne uz tiez tecu sliny na klavesnicu vzdy ked vidim dalsi clanok o Nette (ako ten Pavlovov pes), ale vies aky to bude pocit, ked sa ti po vsetkych tych mukach dostane konecne Nette do ruk? Teda za predpokladu, ze bude naozaj take, ako sa teraz zda, vsakano… :)

    před 11 lety
  22. error414 #22

    Me by docela uspokojilo kdyby alespon napisal kolik procent uz toho ma napsano. Takhle to zni jako krasna pohadka (nechci tim rict ze nas DGX taha za nos).

    Jestli tohle bude fungovat jak pises a nenarazi se na ostram provozu nejakou zasadni krpu, bude to super.

    Bude treba nejake beta testovani?
    Uz se moc tesim

    před 11 lety
  23. spud #23

    Zajimavy reseni, jsem moc zvedavej na hotovy Nette. Zkusil jsem si udelat podle tvyho navodu univerzalni autoload … asi nebude tak genialni, ale treba se nekomu hodi, nez vytasis ten svuj komplet framework. :-)

    před 11 lety
  24. jules http://www.kapibara.sk #24

    avatar

    #20 bleaku, pre rýchly vývoj webových aplikácií odporúčam CakePHP – framweork išpirovaný RubyOnRails, automatické generovanie editovateľných formularov – scaffolding, silná diskusná skupina, podpora php4, php5

    stoji za pozretie http://framework.zend.com, zatial velmi cerstve…

    zoznam frameworkov

    a samozrejme – ocakavam nette :)

    před 11 lety | reagoval [25] Pavel
  25. Pavel #25

    #24 julesi, ten seznam je nejaky oblibeny, prohlizel jsem ten samy :) kdyz jsem probral vsechny podminky, ktere musi byt splneny, vysel mi z toho CodeIgniter, na ktery momentalne koukam a ve volne chvili bych ho chtel zkusit.

    Mozna ze i to Nette bude driv, to se neda vyloucit :) kazdopadne jak rikam, cele Nette me nakoplo jen k tomu, ze se koukam jinde :(

    před 11 lety
  26. Pavel #26

    před 11 lety
  27. Filda http://vobloz.xtr.cz #27

    Takže jestli tomu dobře rozumim, tak v celym projektu nemůžou bejt dve ruzný třídy se stejným názvem?

    před 11 lety | reagoval [28] David Grudl
  28. David Grudl http://davidgrudl.com #28

    avatar

    #27 Fildo, jistě, to by způsobilo Fatal error: Cannot redeclare class XXX

    před 11 lety | reagoval [29] llook
  29. llook http://llook.wz.cz/weblog/ #29

    avatar

    #28 Davide Grudle, Je fakt, že už jsem se párkrát s něčím podobným setkal:

    if (phpversion() < 5) {
        require_once 'Trida_php4.php';
    } else {
        require_once 'Trida_php5.php';
    }

    Dá se to řešit vzorem Factory, ale ne u cizích knihoven.

    před 11 lety
  30. BoneFlute #30

    avatar

    Mám dotaz. Pokud nahraji definici třídy až v okamžiku použití, platí to i na session?
    Používám stále php4ku. A tam je nutné mít nahranou definici třídy dříve než se zavolá ` session_start() `.

    před 11 lety
  31. noone #31

    A kdy bude nette venku

    před 11 lety
  32. a3x #32

    Nemáte skúsenosti s IDE EngInSite PHP Editor? Ako by obstál v konkurencii?

    před 10 lety

Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.