Odpovědi na test znalosti konfiguračních direktiv PHP

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

Minulý týden jsem zveřejnil Test znalostí konfiguračních direktiv PHP, dnes přináším jeho řešení:

  1. Ve stejném adresáři jako právě spuštěný skript je soubor connect.inc.php. Co je špatného na kódu include "connect.inc.php" a jak ho opravit?

    Kód se spoléhá na to, že direktiva include_path bude obsahovat . (aktuální adresář) na prvním místě seznamu (pokud by v jiném adresáři určeném pro vkládání existoval soubor connect.inc.php, použil by se ten). Kód se tedy dá opravit pomocí include "./connect.inc.php", čímž se i nepatrně zrychlí. Pokud by se jednalo o skript spouštěný z příkazové řádky z jiného adresáře, bylo by nutné psát include rtrim(dirname(__FILE__), "\\/") . "/connect.inc.php".

  2. Co je špatného na kódu if (isset($_GET["search"])) echo htmlspecialchars($_GET["search"])?

    Kód předpokládá vypnutou direktivu magic_quotes_gpc. Dále předpokládá, že kódování dokumentu nebude UTF-7 nebo UTF-16.

  3. Za jakých okolností nebude fungovat kód fopen("http://www.example.com/favicon.ico", "r") a čím se dá nahradit?

    Kód nebude fungovat při vypnuté direktivě allow_url_fopen a dá se nahradit funkcí fsockopen:

    <?php
    $fp = fsockopen("www.example.com", 80);
    fwrite($fp, "GET /favicon.ico\r\n");
    fwrite($fp, "Host: www.example.com\r\n");
    fwrite($fp, "Connection: close\r\n");
    fwrite($fp, "\r\n");
    ?>
    

    Při zpracování se ale budeme muset poprat s hlavičkami, především Transfer-Encoding: chunked. Na některých hostinzích je také funkce fsockopen zakázaná. Další možnost je použít rozšíření CURL.

  4. Na co může doplatit kód header("Content-Type: image/png"); echo file_get_contents("obrazek.png")?

    Může doplatit na zbytečnou direktivu magic_quotes_runtime.

  5. Jaké je riziko následujícího kódu?
    <?php
    if (isset($_POST["nazev"]) && isset($_GET["id"])) {
        $nazev = (get_magic_quotes_gpc() ? $_POST["nazev"] : addslashes($_POST["nazev"]));
        mysql_query("UPDATE tabulka SET nazev = '$nazev' WHERE id = " . intval($_GET["id"]));
    }
    ?>
    

    Kód se spoléhá na to, že je vypnutá direktiva magic_quotes_sybase a správně by měl vypadat takto:

    <?php
    if (isset($_POST["nazev"]) && isset($_GET["id"])) {
        $nazev = mysql_real_escape_string(get_magic_quotes_gpc() ? stripslashes($_POST["nazev"]) : $_POST["nazev"]);
        mysql_query("UPDATE tabulka SET nazev = '$nazev' WHERE id = " . intval($_GET["id"]));
    }
    ?>
    
  6. Jaký je nejlepší způsob uzavírání PHP kódu a proč?
    1. <% %>
    2. <? ?>
    3. <?php ?>
    4. <script language="php"> </script>

    Nejpřenositelnější způsob zápisu kódu je <?php ?> nebo <script language="php"> </script>, protože první dva se dají zakázat direktivami asp_tags resp. short_open_tag.

  7. Co by měl udělat skript před tím, než začne vypisovat jakýkoliv text?

    Měl by informovat o výstupním kódování. Stará se o to direktiva default_charset nebo funkce header("Content-Type: text/html; charset=utf-8").

  8. Co může způsobit kód echo "Vítejte"; session_start()?

    Pokud není zapnutá direktiva output_buffering, tak je nutné všechny hlavičky odeslat ještě před začátkem výstupu. Funkce session_start hlavičky posílá, proto může způsobit chybu.

  9. Na co může doplatit kód if (isset($HTTP_GET_VARS["id"])) { }?

    Může doplatit na vypnutou direktivu register_long_arrays.

  10. Proč kód echo $PHP_SELF nejspíš nic nevypíše?

    Je to proto, že direktiva register_globals bývá obvykle vypnutá.

  11. Co na URL hledat.php?jmeno=Franta vypíše kód echo $_REQUEST["jmeno"], pokud bylo na předchozí stránce zavolané setcookie("jmeno", "Pepa")?

    Záleží to na hodnotě direktivy variables_order a samozřejmě také na tom, jestli má uživatel povolené cookies.

  12. Jaký je nedostatek hlášky Maximální velikost souboru je <?php echo ini_get("upload_max_filesize"); ?> B?

    Maximální velikost souboru se bohužel určuje složitěji.

  13. Na co spoléhá následující kód?
    <?php
    $row = mysql_fetch_assoc(mysql_query("SELECT jmeno FROM uvizvatele"));
    if ($row) {
        $jmeno = $row[jmeno];
    }
    ?>
    

    Spoléhá se na vypnutou úroveň chyb E_NOTICE a neexistenci konstanty jmeno.

  14. Proč nejspíš nebude fungovat, když na samém začátku skriptu provedeme $_SESSION["navsteva"] = (isset($_SESSION["navsteva"]) ? $_SESSION["navsteva"] + 1 : 1);?

    Pokud není zapnutá direktiva session.auto_start, tak je potřeba práci se session proměnnými nejprve zapnout funkcí session_start.

  15. Jak musíme upravit kód header("Location: http://$_SERVER[SERVER_NAME]/hotovo.php"), pokud používáme session proměnné a chceme podporovat i uživatele bez cookies?

    Do parametrů URL musíme přidat konstantu SID.

  16. Může následující kód za nějakých podmínek skončit chybou, pokud existuje tabulka tabulka?
    <?php
    $result = mysql_query("SELECT * FROM tabulka");
    while ($row = mysql_fetch_assoc($result)) {
        // zpracování
    }
    ?>
    

    Pokud je zapnutá direktiva mysql.trace_mode, tak se musí všechny výsledky zavřít funkcí mysql_free_result.

  17. Uživatel pomocí formuláře odešle obrázek, který testujeme kódem getimagesize($_FILES["obrazek"]["tmp_name"]). Za jakých okolností se to nemusí povést a jak to napravit?

    Pokud je direktiva upload_tmp_dir nastavena na cestu mimo open_basedir, tak funkce skončí chybou. Napravit se to dá přesunutím nahraného souboru pomocí move_uploaded_file a teprve následným testováním obrázku.

  18. Co je špatného na heredoc syntaxi?

    Oddělovač řádků je platformově závislý, takže nemusí fungovat na Apple.

  19. Jak se dá z PHP poslat e-mail obsahující na prvním řádku Ahoj! a na druhém Povedlo se to?

    Problém je v tom, že řádky v e-mailu musí být oddělené znaky \r\n, ale některé Unixové programy na tuto posloupnost automaticky překládají znak \n. Pokud tedy použijeme samotné \n, tak může skončit i na výstupu, pokud použijeme \r\n, může na výstupu skončit \r\r\n. Pro přenositelné posílání e-mailů tedy na funkci mail bohužel musíme zapomenout a použít přímo SMTP třeba přes PEAR Mail.

Jakub Vrána, Výuka, 20.6.2007, diskuse: 19 (nové: 0)

Diskuse

Ditto:

19. Hmm.. pride mi to trosku tazkopadne. Ja posielam maily tak, ze v hlavicke definujem te ide o text/hmtl a ako oddelovac riadkov pouzivam <br />. Je to jednoduche a naviac syntax html ponuka mnozstvo dalsich funkcii pri formatovani mailu.

ikona Jakub Vrána OpenID:

To s tím ale nijak nesouvisí. Řádky v e-mailu nemůžou být nekonečně dlouhé, takže je potřeba je oddělit i ve zdrojovém kódu. A tím se dostáváme tam, kde jsme byli.

hmm:

"Řádky v e-mailu nemůžou být nekonečně dlouhé, takže je potřeba je oddělit i ve zdrojovém kódu."

Myslíte si, že rozdělením nekonečně dlouhé řádky v e-mailu na fragmenty něco vyřešíte? Ve skutečnosti po každém dělení nekonečné entity zůstane alespoň jeden z fragmentů nekonečný, takže dělením se nekonečna nezbavíte... :-)

Megaloman:

Podle RFC specifikace je maximální délka řádku v těle e-mailu 998 znaků + CR a LF znaky.

Takže pokud chce hmm poslat nekonečný validní mail, tak bude muset řádkovat :))

ikona Jakub:

"Co je špatného na heredoc syntaxi?
Oddělovač řádků je platformově závislý, takže nemusí fungovat na Apple."

-> Má to ještě nějaké další nevýhody? HEREDOC využívám jen na vypisování html kódu, a tam je mi celkem jedno jak bude odřádkovaný.

ikona Jakub Vrána OpenID:

Problém není v odřádkování, ale v tom, že ukončovací identifikátor se vůbec nenajde, takže skript skončí syntaktickou chybou.

ikona Jakub:

Tzn. že chyba se projeví pouze pokud budu mít web na webhostingu s apple platformou? To v ČR není příliš obvyklé.

Martin Grames:

Mohl bys blíže vysvětlit tu dvojku - s tím UTF jsem to nějak nepochopil. Díky.

Jan Tichý:

Tato kódování nemají zpětnou sedmibitovou kompatibilitu. Může se tedy stát, že jakýkoliv běžný ASCII znak (například ampersand nebo ostré závorky) bude vlastně jen součást nějakého vícebajtového znaku. Nicméně htmlspecialchars funguje pouze jednobytově, takže všechny takové byty přeloží na textové entity.

Jan Tichý:

"7. Co by měl udělat skript před tím, než začne vypisovat jakýkoliv text?"

Tahle otázka je, upřímně řečeno, dost nejednoznačná. Daleko spíše než odeslat hlavičky s kódováním by mi přišlo logické na ni odpovědět ve smyslu "odeslat všechny potřebné HTTP hlavičky, včetně nastartování sessions apod."

"13. Na co spoléhá následující kód?"

Uvedený kód ale navíc chybně spoléhá i na to, že jsem správně připojený k databázi, že databáze jede atd apod. Prostě tam chybí ošetření pro případ, že mysql_query nevrátí z mnoha různých důvodů žádný platný result.

"16. Může následující kód za nějakých podmínek skončit chybou, pokud existuje tabulka tabulka?"

Totéž jako výše. Minimální ošetření by mělo vypadat alespoň takto:

<?php
$result
= mysql_query("SELECT * FROM tabulka");
while (
$result && $row = mysql_fetch_assoc($result)) {
    // zpracování
}
?>

Ale je fakt, že test byl o konfiguračních direktivách, zatímco mé připomínky jsou z jiného soudku. Ale jinak moc hezký test, díky.

Čelo:

Vím, že nic nevím.

ikona Arcao:

3. Hm, a nema být to spíš takhle?

<?php
$fp
= fsockopen("www.example.com", 80);
fwrite($fp, "GET /favicon.ico HTTP/1.0\r\n");
fwrite($fp, "Host: www.example.com\r\n");
fwrite($fp, "Connection: close\r\n");
fwrite($fp, "\r\n");
?>

Tím že určíme protokol 1.0 nám to zpět nepoleze chunked, protože Transfer-Encoding a chunked je znám až od verze HTTP 1.1.

ikona Jakub Vrána OpenID:

Protokol HTTP/1.0 bohužel nezná hlavičku Host.

Ondrej Ivanic:

to ze nieco poslem v HTTP 1.0 neznamena ze spat dostnem HTTP 1.0. O tomto som sa uz viacktrat presvedcil pri jednoucelovych webserveroch...

ikona dgx:

ad 1: myslíš, že je skutečně potřeba po dirname() volat rtrim(... "\\/") ?

ad 2, 5: kód také předpokládá, že $_GET['search'] není pole. Pravda, nesouvisí to přímo s direktivami, nicméně je to asi pravděpodobnější, než stránka v UTF-7 :)

ad 16: tak to jsem fakt netušil!

ad 19: fajnový je taky Zend_Mail v Zend Frameworku

ikona Jakub Vrána OpenID:

1. Je to kvůli případu, kdy by byl skript v kořenovém adresáři. Nicméně Linux i Windows se s cestami //connect.inc.php a C:\/connect.inc.php vypořádají, takže alespoň pro tyhle dvě platformy to potřeba skutečně není.

ikona D1ce:

Musíte být mistr ve svém oboru.

A jak jinak mám drobný dotaz. :) Když var_dump(ini_get('memory_limit')); nevypíše nic smysluplného, znamemá to, že mi paměť omezuje jen OS?

paranoiq:

výborný článek!
vztahuje se short_open_tag i na zkrácené <?= ?> ?

ikona Jakub Vrána OpenID:

Ano.

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-2017 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.