Vkládání souborů

Školení, která pořádám

V PHP jsou k dispozici 4 funkce pro vložení jiného PHP skriptu – include, include_once, require a require_once. Rozdíl mezi include a require je ten, že require skončí při nenalezení souboru fatální chybou, kdyžto include jenom vyvolá varování (v PHP 3 sloužila jedna funkce k nepodmíněnému vložení souboru a druhá k podmíněnému, ale to už neplatí). Varianty _once slouží k zajištění pouze jednoho vložení onoho souboru.

include a require nejsou skutečné funkce, ale jsou to (podobně jako např. echo) jazykové konstrukce, takže jejich parametr není nutné uzavírat do závorek.

Velký zmatek panuje mezi PHP programátory v tom, odkud se bere cesta při vícenásobném vložení souborů do sebe. To, že existuje direktiva include_path většinu lidí netrápí, protože ve výchozím nastavení obsahuje pouze aktuální adresář. Ale přesně popsat, odkud se bere vkládaný soubor při vložení více souborů v různých adresářích do sebe, podle mě svede málokdo. Přitom to je poměrně jednoduché:

<?php
// soubor a.php
include "include/b.inc.php";

// soubor include/b.inc.php
include "c.inc.php"; // nejdříve se vyzkouší c.inc.php a pak include/c.inc.php
?>

Soubor c.inc.php se nejprve hledá v aktuálním adresáři (který např. v PHP-cli nebo po použití funkce chdir nemusí odpovídat adresáři skriptu a.php), pak v adresáři souboru, ve kterém se include provádí. To vše ještě samozřejmě se zohledněním include_path. Pokud ale vkládaný soubor začíná ./ nebo ../, tak se hledá pouze relativně vzhledem k současnému adresáři.

Složitým pravidlům používání relativní cesty se dá samozřejmě vyhnout používáním absolutní cesty např. s využitím proměnné $_SERVER["DOCUMENT_ROOT"]. Problém může nastat v případě, kdy se cesta ke knihovnám změní např. kvůli změně webhostingu nebo přesunu adresáře webu.

Pokud musíte vkládat soubor na základě hodnoty proměnné zvenku, nezapomeňte na důkladnou kontrolu, protože bez ní hrozí závažné narušení bezpečnosti. Lepší je ale úplně se tomu vyhnout.

<?php
// velmi nebezpečné
include $_GET["file"];

// lepší
include basename($_GET["file"]);

// ještě lepší
$povolene = array("a.php", "b.php");
if (in_array($_GET["file"], $povolene)) {
    include $_GET["file"];
}
?>

Nejlepší je se vkládání souborů na základě hodnoty proměnné úplně vyhnout. První způsob je nebezpečný už jenom např. při předání hodnoty /etc/passwd. Pořádná katastrofa ale teprve vznikne, když je zapnutá direktiva allow_url_fopen (od PHP 5.2.0 allow_url_include) a útočník předá hodnotu http://example.com/utok.txt, na které bude škodlivý kód, který se spustí v kontextu vaší webové aplikace!

Doplnění

Přijďte si o tomto tématu popovídat na školení Bezpečnost PHP aplikací (13.3.2018, Praha).

Jakub Vrána, Výuka, 25.5.2005, diskuse: 23 (nové: 0)

Diskuse

ikona dgx:

Související: http://www.majda.cz/zapisnik/permalink.php?idart=136

http://www.martincohen.info/forum/viewtopic.php?t=274

Andrej:

Include pouzivam k modularnemu navrhu prezentacie, daju sa s tym robit celkom dobre kusky a aplikacia / prezentacia je potom celkom logicky rozclenitelna. Napr. hlavicka, menu, footer a pod. su cez include proste krasne.

ikona Jakub Vrána OpenID:

Také jsem dřív používal <?php include "hlavicka.inc.php"; ?>, ale přešel jsem na funkce:
<?php
include "design.inc.php";
hlavicka("Titulek");
?>

Výhoda je zřejmá - funkce vidí jen ty proměnné, které jí jsou předané v parametru a tím pádem ani nemůže ovlivňovat své okolí. Navíc jí můžu snadno předat parametry ovlivňující její chování (zde např. titulek).

Andrej:

Myslim ze to zalezi od typu includovaneho suboru a operacie. Includy je dobre pouzivat aj napr. na konstanty (menu a pod.).
<?
$title
='Fedora Core 3';
require
'header.php';
?>
  <div class="menu">
    <ul class="tabnav">
      <? showMenuSlice($menuTree, array(), 'linux'); ?>
    </ul>
  </div>
  <div class="content">
    <ul class="submenu">
      <? showMenuSlice($menuTree, array('linux'), 'fedora'); ?>
    </ul>
    Page content...
  </div>
...
<?
require 'footer.php';
?>

ikona llook:

DOCUMENT_ROOT nebrat! Třeba na webzdarma je to "/3w" a to opravdu není cesta k rootu mého webu.
Lepší je odvozovat to z __FILE__, třeba takhle:
<?php
define
('CLASSPATH', __FILE__.'/../classes/');
require_once(
CLASSPATH.'MyClass.php');
//...
?>

Jo a nemělo by se zapomínat na readfile. Když někdo potřebuje jenom vkládat hlavičku a patičku, tak to často stačí.

ikona llook:

Sakra, zapoměl jsem na dirname. Samozřejmě ten příklad měl vypadat takhle:
<?php
define
('CLASSPATH', dirname(__FILE__).'/../classes/');
require_once(
CLASSPATH.'MyClass.php');
//...
?>
Na dirname(__FILE__) se lze spolehnout vždy.

Standa:

Dobrý den, mám problém s dirname(__FILE__). Po citaci

"Na dirname(__FILE__) se lze spolehnout vždy." nemůžu souhlasit (možná špatné nastavení v php).

Při otestování tohoto příkazu na našem webu se mi zobrazí následující cesta  "/web/htdocs1/i_wupscom/home/www/xxx/", ale dibi tohle nezná.

Děkuji za odpověď.

ikona spaze:

nejsem si uplne jist, jestli jsem pochopil dobre vetu "Pokud ale vkládaný soubor začíná ../, tak se hledá pouze relativně vzhledem k současnému adresáři. To vše ještě samozřejmě se zohledněním include_path."

Podle me, pokud urceni souboru ma v sobe jakykoliv naznak cesty, i relativni a i "./" (tedy aktualni adesar), tak se v include_path uz nehleda.

ikona Jakub Vrána OpenID:

Díky za upozornění, poznámka o include_path se vztahuje k textu před větou s ../ - přesunul jsem ji.

kr4UT1k:

A co <?php include('./neco/'."$_GET[necojinyho]".'.php');?> ?
Jak je na tom s bezpecnosti?

ikona Jakub Vrána OpenID:

Není to ta úplně nejhorší varianta, ale k bezpečnosti má pořád dost daleko - nic útočníkovi nezabrání použít např. "../../utocnik/hack" - na webhostingu, kde má web víc lidí (velmi běžné) a není zapnutá direktiva safe_mode nebo open_basedir (jsou takové) to je velmi reálná hrozba.

Gioel:

Velmi dobra pripomienka. Inak perfektny clanok, velmi chvalim :)

BOPE:

Tak jsme použil

<? include "foto/spgm.php";?>

jenže mi to při otevření vyhazuje chybu:

unable to load themes/default/spgm.thm: insufficient permissions (644 required)

ale když jsem tento soubor spgm.php testoval samostatně bez volžení v jiné stránce tak mi to běželo bez problémů.
Něco se tam tluče ale nevím co.

Yankee:

Chyba je v atributech includovanéh osouboru.

xxx:

Dotaz. Potřebuju vložit například adresu www.seznam.cz. Ale adresa má určené cesty na server seznamu jako "./pictures/bla.gif", například. Jak zařídit to, aby se mi stránky správně zobrazily u mě? Funkce include je zobrazuje špatně.

xxx:

jde o to, že mám třeba www.neco.cz/neco.php...a potřebuju to includovat aby se mi zobrazhovaly dobře i obrázky...

xxx:

Ha, tak jsem si našel, že soubor končící *.php, který načítáme z jiného webu, nebude přečten zprávně:o/

Something not previously stated here - but found elsewhere - is that if a file is included using a URL and it has a '.php' extension - the file is parsed by php - not just included as it would be if it were linked to locally.

This means the functions and (more importantly) classes included will NOT work.

for example:

include "http://MyServer.com/MyInclude.php";

would not give you access to any classes or functions within the MyInclude.php file.

to get access to the functions or classes you need to include the file with a different extension - such as '.inc' This way the php interpreter will not 'get in the way' and the text will be included normally.

Nevíte někdo jak to ošetřit, když nemám přístup s souborům na cílovém serveru?

ikona dgx:

PHP triky - Weblog o elegantním programování v PHP pro mírně pokročilé.

Podstatné jsou ty poslední dvě slova. Při vší úctě, zkuste si najít diskusní fórum pro začátečníky a vraťte se až budete mírně pokročilý.

xxx:

Asi takhle, nejsem profesionál, ba ani pokročilý. Takže si tady na nic nebudu hrát. Toto fórum jsem našel náhodou a zdálo se mi plné odborníků, kteří dokáží poradit, proto jsem sem dotaz napsal. Hraju si s php jen tak a tohle mi zrovna napadlo.

ikona dgx:

Problémy, se kterými se potýkáš, jsou způsobeny neznalostí základních principů HTML (to je v pořádku, tím jsme si někdy prošli všichni). Určitě ti pomůžou ve fóru třeba na http://diskuse.jakpsatweb.cz nebo http://interforum.interval.cz/. Tohle není fórum, ale diskuse pod článkem. 

Jan Kudělka:

Dobrý den, chtěl jsem se zeptat jak je to se SEO optimalizací? Mám v kódu řekněme include footer.php společný a důležitý pro několik stránek. Chci aby jej vyhledávače indexovali spolu s tím ostatním obsahem, ale samotný soubor footer.php byl nepřístupný. Stačí to zapsat do robots.txt jako disalow?

ikona Jakub Vrána OpenID:

K souboru je lepší zakázat přístup na úrovni webového serveru. To znamená umístit ho mimo DocumentRoot nebo znepřístupnit direktivou Deny (pod webovým serverem Apache). Dá se to řešit i na úrovni PHP, ale to je krkolomné.

Miloš Brecher:

Já používám důsledně pro vkládání všech souborů absolutní cestu z konstanty společné pro celý web ROOT_DIR, která se nastaví podle kontextu - na testovacím serveru doma mám webový projekt v podadresáří, na provozním serveru v rootu. Pak nemusím řešit chaos okolo cest include, které se v PHP nepovedlo.

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.