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í:
- 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"
.
- 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.
- 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.
- 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.
- 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"]));
}
?>
- Jaký je nejlepší způsob uzavírání PHP kódu a proč?
<% %>
<? ?>
<?php ?>
<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.
- 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")
.
- 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.
- Na co může doplatit kód
if (isset($HTTP_GET_VARS["id"])) { }
?Může doplatit na vypnutou direktivu register_long_arrays.
- Proč kód
echo $PHP_SELF
nejspíš nic nevypíše?Je to proto, že direktiva register_globals bývá obvykle vypnutá.
- 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.
- 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.
- 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
.
- 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.
- 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.
- 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.
- 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.
- Co je špatného na heredoc syntaxi?
Oddělovač řádků je platformově závislý, takže nemusí fungovat na Apple.
- 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.
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.
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 :))
"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ý.
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.
Tzn. že chyba se projeví pouze pokud budu mít web na webhostingu s apple platformou? To v ČR není příliš obvyklé.
Mohl bys blíže vysvětlit tu dvojku - s tím UTF jsem to nějak nepochopil. Díky.
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.
"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.
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.
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...
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
Jakub Vrána :
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í.
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é
<?= ?> ?
Jarmila Kučerová:
Prosím, co to znamená "dlouho spuštěný skript" a jak ho zastavím. Nevím vůbec, co je skriptem myšleno a tudíž nemohu použít Seznam atd.
Děkuji Vám mnohokrát za radu.
Diskuse je zrušena z důvodu spamu.