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
rADo #1
Co podpora pro PHP 4.x?
David Grudl #2
#1 rADo, Řeším to explicitním voláním
__autoload
. Například__autoload("BaseClass");
před každýmclass 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.
medden #3
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é 🙂
Honza V. #4
Doprkýnka, to je tak strašně jednoduchý a přitom naprosto geniální, že mě to nenapadlo dřív…
Honza V. #5
Jo, nechceš už dát ke stažení aspoň ten modul, co umí tady tohlencto?
medden #6
#3 meddene,
No ten príklad mal byť takto:
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ý.
mrzout #7
Tenhle genialni postup jsem kdysi cetl v nejakem clanku a hned se mi zalibil. (mozna: https://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.
johno #8
#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äť?
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 :)
halogan #10
#9 Pavle, Mohu se zeptat, ktery framework Vas oslnil?
medden #11
#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 …
Jakub Podhorský #12
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 :)
johno #13
#11 meddene, No ešteže tak.
DGX: Čo ak niekto používa
autoload
vo svojej/inej knižnici? Na to si myslel?Borek #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 :)
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?
llook #16
#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.
johno #17
#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ť.
David Grudl #18
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 ;)
David Grudl #19
#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:bleak #20
#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.
FrozenDog #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… :)
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
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. 🙂
jules #24
#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 https://framework.zend.com/, zatial velmi cerstve…
zoznam frameworkov
a samozrejme – ocakavam nette :)
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 :(
Pavel #26
Why I hate frameworks – :)
Filda #27
Takže jestli tomu dobře rozumim, tak v celym projektu nemůžou bejt dve ruzný třídy se stejným názvem?
David Grudl #28
#27 Fildo, jistě, to by způsobilo Fatal error: Cannot redeclare class XXX
llook #29
#28 Davide Grudle, Je fakt, že už jsem se párkrát s něčím podobným setkal:
Dá se to řešit vzorem Factory, ale ne u cizích knihoven.
BoneFlute #30
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() `.
noone #31
A kdy bude nette venku
a3x #32
Nemáte skúsenosti s IDE EngInSite PHP Editor? Ako by obstál v konkurencii?
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.