Na navigaci | Klávesové zkratky

Translate to English… Ins Deutsche übersetzen…

PHP triky: include, require a cesty

Vkládání skriptů přes include nebo require není vždy transparentní, protože programátoři často neví, z jakého adresáře se skript načte. Pravidla jsou následující:

  1. pokud je cesta absolutní, nic se nedohledává
  2. pokud cesta začíná na ./ nebo ../, hledá se vůči aktuálnímu adresáři (neplést s adresářem s právě prováděným skriptem!)
  3. v ostatních případech:
    1. prohledají se všechny cesty z include_path relativně vztažené k aktuálnímu adresáři
    2. hledá se od adresáře s právě prováděným skriptem

„Na jistotu“ lze obecně vkládat pouze přes absolutní cesty. K tomu se užívá tato formule:

require_once dirname(__FILE__) . '/libs/TexyBase.php';

Následující trik ukazuje, jak se lze volání funkce dirname zbavit (nedoporučuju používat)

require_once __FILE__ . '/../libs/TexyBase.php';

PHP 5.3 má novou magickou konstantu __DIR__, která nahradí volání dirname(__FILE__):

require_once __DIR__ . '/libs/TexyBase.php';

Komentáře

  1. veena #1

    Supr, dík za vysvětlení těch inkludů cest a trik bez dirname. V angličtině mi to pořád nesecvakávalo. Ale ještě mi zbývají nejasnosti.

    1. prohledají se všechny cesty z include_path relativně vztažené k aktuálnímu adresáři
    Že to je relativně k aktuálnímu adresáři asi neplatí vždy, ne? V include_path taky můžou být cesty zadány trojím způsobem (absolutně, s ./, „normálně“)

    A jak je to prosím tě s tím aktuálním adresářem? Který to je, nebo podle čeho se nastaví?
    Rád bych si už konečně chtěl z hlavy dokázat představit, kde se vlastně ten soubor hledá, když ho inkluduju.

    před 9 lety | reagoval [3] Jakub Vrána [6] David Grudl
  2. Poša #2

    avatar

    Davide, jsem jen lama, ale bylo by asi vhodné u těch triků říct, jaké jsou vady stávajícího řešení (proč se tedy vlastně ten trik dělá a co přinese). Nějak mi to např. u triku s dirname chybí.

    Proč je varianta s dirname horší než navržený trik?

    před 9 lety
  3. Jakub Vrána http://php.vrana.cz/ #3

    #1 veeno, Aktuální adresář je ten, který vrací funkce getcwd(). Na webu to je obvykle adresář spuštěného skriptu, ale dá se změnit i funkcí chdir(). Každá cesta se dá vztáhnout relativně k adresáři – absolutní zůstane nezměněná.

    před 9 lety
  4. Tomáš Fejfar http://blog.red-pill.cz #4

    avatar

    Jen možná ještě doplnit, že pokud includujete z nějaké asbolutní URL (http://www.example.com/foo.php), tak se nenačtou ani proměnné ani nic dalšího a použije se jen výstup. Což je sice logické, ale nedávno sem si to neuvědomil a řešil sem to půl dne.

    před 9 lety | reagoval [9] Jakub Vrána
  5. Jan Tichý http://www.phpguru.cz/ #5

    avatar

    Ahoj, co je špatného na dirname()? Subjektivně mi přijde použití dirname() čistší, než dávat název neadresářového souboru do cesty.

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

    avatar

    #1 veeno, Jakub to vysvětlil, jen dodám, že přímo aktuální adresář se prohledává jen tehdy, pokud jej tam include_path zavede, tedy třeba když obsahuje . (tečku).

    #5 Jane Tichý, na dirname není špatného vůbec nic, to je prostě jen taková frajeřina ;)

    před 9 lety | reagoval [9] Jakub Vrána
  7. Josef Průša http://josef-prusa.eu #7

    avatar

    O kolik je includovani souboru rychlejsi, kdyz se uvede celá cesta?

    před 9 lety | reagoval [8] veena
  8. veena #8

    #7 Josefe Průšo, + by mě zajímalo jak dlouho trvá vyzkoušení jednoho adresáře v include path. A jak jsou ty rychlosti vzhledem k rychlosti otestování pomocí funkce file_readable()

    před 9 lety
  9. Jakub Vrána http://php.vrana.cz/ #9

    #6 Davide Grudle, Přímo aktuální adresář se prohledává vždy. Pokud include_path neobsahuje tečku, tak jako poslední.

    #4 Tomáši Fejfare, Proměnné se načtou v případě, že jejich definici vkládaný skript vypíše (třeba kdyby měl koncovku .txt, ale může to samozřejmě udělat i PHP skript). Pro obyčejné vložení obsahu vzdáleného souboru bez jeho vykonání je lepší použít funkci readfile().

    před 9 lety | reagoval [10] David Grudl [12] Tomáš Fejfar
  10. David Grudl http://davidgrudl.com #10

    avatar

    #9 Jakube Vráno, nene, pokud include_path tečku neobsahuje, aktuální adresář se neprohledává vůbec. Prohledává se adresář s aktuálně prováděným skriptem (bod c.2), ten však s include_path nijak nesouvisí.

    před 9 lety
  11. Dundee http://blog.milde.cz #11

    avatar

    V případě, že je v includu na začátku / vyhledává se v adresáři nastaveném v doc_root?

    Nejlepší je mít jeden centrální controller, to pak starosti o to, odkud mě volající skript načetl, úplně odpadají.

    před 9 lety
  12. Tomáš Fejfar http://blog.red-pill.cz #12

    avatar

    #9 Jakube Vráno, To sem nějak nepochopil. Pokud někde ve skryptu udělám echo $foo; tak potom budu moct proměnou $foo požít i v souboru, ze kterého sem includoval?

    To mi nepřijde možné. Už jen z toho principu, že při přístupu k souboru přes HTTP:// se PHPčko vykoná a vrátí se výsledek a že by se prováděla kontrola, jestli ta která http adresa není náhodou na tomtéž serveru se mi teda moc nezdá.

  13. Marek Dušek http://www.stringdata.cz #13

    #12 Tomáši Fejfare, Presne tak, jakmile se vklada pres ‚http://‘, cilovy webserver skript normalne zpracuje a vrati vysledek, nikoli zdrojovy kod (at se jedna fakticky o ten samy server ci nikoli).

    Moznost vkladat takto soubory se ridi direktivou ‚allow_url_fopen‘ (tedy tou, ktera hlavne ridi totez pro fci fopen()).

    (vse psano AFAIK, prirozene ;)

    PS mozna to nekomu prijde zbytecne resit, ale taky znam pripad, kdy se u pocitace sesla postupne cela kancelar a zkoumala, proc po require souboru nejsou tridy v nem pristupne, ackoli kazde ‚print 123‘ videt bylo ;)))

    před 9 lety
  14. Vedouci #14

    hm, a do jake míry je tohle rešení platformově nezávislé?
    (a/b/c.php vs. a\b\c.php)

    pamatuju se, že sem s tím měl kdysi problémy…

    před 9 lety | reagoval [16] David Grudl
  15. jaro #15

    Programoval som pár rokov v PHP. Keď sa však musím pre načítanie hodnôt konfiguračných premenných uchyľovať k použitiu regulárnych výrazov, buď nie je niečo v poriadku s PHP alebo bude chyba medzi monitorom a operadlom stoličky. Ináč dobrý článok :-)

    před 9 lety
  16. David Grudl http://davidgrudl.com #16

    avatar

    #14 Vedouci, obyčejná lomítka / jsou platformově nezávislé.

    před 9 lety | reagoval [18] Vedouci
  17. Jan Tichý http://www.phpguru.cz/ #17

    avatar

    #12 Tomáši Fejfare, Tomáši, opravdu to tak je. Když budeš mít například ve svém skriptu následující kód:

    $foo = 'bar';
    include 'http://www.example.com/index.php';

    a v onom vzdáleném index.php budeš mít třeba něco takového:

    echo '<?php echo $foo; ?>';

    tak se Ti obsah proměnné $foo vypíše. To je hlavní důvod, proč je na spoustě serverů zakázáno allow_url_fopen respektive nově allow_url_include.

    před 9 lety
  18. Vedouci #18

    #16 Davide Grudle, no, jde o to, ze pak v kombinaci s navratovyma hodnotama php fci to pak moc nezavisle neni:

    <?php
    
    include $_SERVER [ 'DOCUMENT_ROOT' ] . 'kulovy/test.php';
    Mozna by to stalo za poznamenani ;-)
    před 9 lety | reagoval [19] David Grudl
  19. David Grudl http://davidgrudl.com #19

    avatar

    #18 Vedouci, nerozumím. Co konkrétně nefunguje?

    Jen bacha na to, že DOCUMENT_ROOT nemusí vpravo obsahovat lomítko, vlastně vůbec nemusí obsahovat očekávanou hodnotu a rozhodně bych podle něj neinkludoval. Správný postup se odvíjí od __FILE__.

    před 9 lety
  20. Dundee http://blog.milde.cz #20

    avatar

    #12 Tomáši Fejfare, Myslim, že Jakub to myslel trochu jinak. Říkal, že pokud includovaný skript má jako výstup definici proměnné, ta se pak dá ve volajícím skriptu vypsat.

    Např:

    <?php
    echo "$prom = 'nejaka kravovina'";
    ?>
    <?php
    include ("http://skript.php");
    echo $prom; //vypise 'nejaka kravovina'
    ?>

    Na tomhle principu je založen PHP Injection.

    před 9 lety | reagoval [23] Jan Tichý
  21. Mordae #21

    avatar

    require_once __FILE__ . '/../libs/TexyBase.php';

    eh… tohle funguje ale jen díky tomu, že PHP dělá realpath() na tu cestu kvůli té kontrole dvojího vkládání, případně kvůli open_basedir. Zkus to na skutečném filesystému a máš smůlu, protože hardlink ../ z něčeho, co není adresář, asi jen těžko vydoluješ.

    před 9 lety
  22. Mordae #22

    avatar

    Ah a jěště se doplním. PHP má vlastní realpath(3), podle všeho, protože ten BSDčkový (alespoň ten z Glibc) tohle taky nezkousne.

    před 9 lety
  23. Jan Tichý http://www.phpguru.cz/ #23

    avatar

    #20 Dundee, Hmm, tohle ledaže bys měl v tom prvním skriptu jednoduché uvozovky – v opačném případě dojde už přímo v něm k substituci $prom – v lepším případě za nějakou hodnotu, v horším to vyhodí undefined variable ;).

    před 9 lety
  24. optik #24

    jen k rychlosti prohledavani include_path, je to pomerne rychle, zejmena na unixech, na win nicmoc, ja osobne nejradeji pouzivam include_path a jednoduchy __autoload a pak uz se s include/require vubec nezabyvam, jinak misto /
    osobne pouzivam konstatnu DIRECTORY_SEPARATOR

    před 9 lety | reagoval [25] David Grudl [26] v6ak
  25. David Grudl http://davidgrudl.com #25

    avatar

    #24 optiku, DIRECTORY_SEPARATOR je sice jakože čisté řešení, ale kdo ho používá důsledně? A není-li používám důsledně, pak je zbytečné ho používat vůbec.

    Třeba kód Zend_Loader ze Zend Framework:

    • na jednom řádku str_replace('_', DIRECTORY_SEPARATOR, $class);
    • o pár řádků níže: require_once 'Zend/Exception.php';
    před 9 lety
  26. v6ak #26

    #24 optiku, Pokud vím, tak lomítko je taky čisté. DIRECTORY_SEPARATOR je systémový oddělovač. Je vhodné použít str_replace s ním např. po volání realpath.

    před 9 lety
  27. Luděk Pechanec #27

    avatar

    Ahoj, vytvořil jsem si dynamický stránky v PHP a používám tam klasické volání příkazu INCLUDE.
    Teď mám stránky zablokovaný, protože mi přes ně někdo posílá spamy atd. Údajně mám zabezpečit příkazy INCLUDE, POST a MAIL.

    Používám script v tomto tvaru:
    <?
    if ($stranka != "") {
    include („$stranka.php“);
    } else {
    include ‚main-center.php‘;
    }
    ?>

    Může mi někdo s tímto problémem poradit?
    díky

    před 9 lety | reagoval [28] v6ak
  28. v6ak http://v6ak.profitux.cz/ #28

    #27 Luděku Pechanci, Toto použití include je skutečně děravé a ‚hacknutelné‘, zvlášť v php5, kde přibyl pseudoprotokol data.

    před 9 lety
  29. Jarda #29

    avatar

    __autoload je skvělá věc, ale pokud vložený script (knihovna) z jiného adresáře používá taky __autoload, tak má pak logicky špatně cestu a script padne. A pak musím modifikovat cizí knihovnu a ten druhý __autoload smazat. Myslíte, že to jde řešit nějak elegantněji?

    před 9 lety | reagoval [30] v6ak
  30. v6ak http://v6ak.profitux.cz/ #30

    #29 Jardo, Žeby spl_autoload?

    před 9 lety
  31. David Grudl http://davidgrudl.com #31

    avatar

    Díval jsem se do zdrojových kódů PHP 5.3 a objevila se tam nová magická konstanta __DIR__, která nahradí volání dirname(__FILE__).

    před 9 lety

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