Proměnné zvenku
Školení, která pořádám
Ve svých skriptech se nikdy nespoléhám na to, že bude zapnutá direktiva register_globals a pokud to mohu ovlivnit, tak ji v konfiguraci vypnu. Nebojím se ani tak podstrčení hodnoty interní proměnné útočníkem (protože proměnné důsledně inicializuji), ale pole $_GET, $_POST a podobné používám hlavně kvůli přehlednosti kódu. Když se podívám do některých svých starších skriptů a vidím změť proměnných, u kterých není na první pohled patrné, jestli jsou interní, přišly v URL a nebo jsou to třeba cookies, tak mám co dělat, abych se v tom vyznal. Z tohoto důvodu nepoužívám ani zkopírování těchto polí do obyčejných proměnných, jak to ukazuje následující kód:
<?php
// nedoporučený kód
$id = $_POST["id"];
$nazev = $_POST["nazev"];
$email = $_POST["email"];
// ...
?>
To jednak kód zbytečně zamořuje a jednak způsobuje popsaný problém nejasnosti původu proměnných při pozdější úpravě kódu.
Používání polí $HTTP_GET_VARS a podobných bych se nebál označit jako archaismus (proměnné $_GET a podobné byly zavedeny v PHP 4.1.0, tedy v prosinci 2001).
Přijďte si o tomto tématu popovídat na školení Bezpečnost PHP aplikací.
Diskuse
RejpalCZ:
Vám příjde zápis $_POST("promenna") v kódu přehlednější než prostě $promenna?
Není lepší do $var vložit hodnotu _POST / _GET pomocí funkce? Na začátku každého skriptu a funkce zinicializovat proměnné a pak používat kratší a imho přehlednější zápis?
<?php
function getP ($what) // returns _POST[$what]. Inicialize when empty
{
if (isset($_POST[$what]))
{ return $_POST($what); }
return '';
}
function MyFunction ($args)
{
// inicializace proměnných
$MyVar1 = getP('myvar1');
$MyVar2 = getP('myvar2');
// funkční část
...
...
// návrat hodnoty
return $out;
}
?>
Zabijete tím několik much jednou ranou, jednak máte po problémech s inicializací, druhak ve fci getP (příp. getG) i jednoduše ošetříte magic_quotes a další nepříjemnosti.
Peta:
Kdyz uz, tak $_POST('promenna')
Dale musim souhlasit s Jakubem - ja take radeji pisi o neco dele, ale skripty jsou citelnejsi...
RejpalCZ:
Narážel jsem na příklad, kde je taky použit tvar $_POST["promenna"].
Tady jde o to, že kupa $_POST['neco'] v kódu mi zrovna čitelnější nepřijde :/, zvlášť když to máte ještě ošetřit fcemi Xslashes.
Pavel Zbytovský:
A zvlášť když to chcete psát do řetězce... co je lepší:
<?php
$str = "<b>".$_POST["promenna"]."</b>\n";
?>
nebo
<?php
$str = "<b>$promenna</b>\n";
?>
<?php
$str = "<b>$_POST[promenna]</b>\n";
?>
je stoprocentně správný způsob.
Lukoko:
<?php
$str = "<b>$_POST[promenna]</b>\n";
?>
nebude fungovat u dvouromerneho pole
pokud mam neco jako
<input type="text" name="neco[]" >
<input type="text" name="neco[]" >
...
tak potom dostanu
$_POST[neco][0] a to takto nelze zapsat, je nutné
"<b>".$_POST[neco][0]."</b>\n";
Kody:
<?php
$str = "<b>{$_POST['promenna'][0]}</b>\n";
?> Tohle funguje i u vícerozměrných polí.
xxx:
spravny zpusob ?? jak si to vubec dovolujete tvrdit ???? hm taky nekdo snedl Salamounovo hovno, ze ??!!
tark:
Tady vidíme krásný příklad blbečka. :-)
xxx:
Tady vidíme krásný příklad blbečka. :-)
Radim:
Jakube, pokud neco nedoporucujete, je jiste i neco, co doporucujete. Dik.
Doporučuji používat všude v kódu plný zápis, tedy např. $_GET["id"]. Psaní navíc je minimum a v kódu je pak na první pohled patrné, co pochází odkud.
Andrew:
Určitě to je správnější používat, jak píšeš, ale souhlasím také s níže uvedeným příspěvkem, že jakmile to začneš spracovávat, tak by se to mělo hodit jinam a nepředělávat to v superglobálních proměnných.
Já mám ale příklad, kde to potřebuji hned na začátku dostat z $_GET. Je to pro výpis stavových hlášení, které ale mohou být poslána zvenčí, nebo ze samotného skriptu a jsou si rovnocena.
Tak ty promenne muzes zpracovavat uvnitr nejake funkce ktera si vezme GET/POST jako vstupni parametr... pak je jasne, ze se zpracuje ve funkci to co se do ni posle.
Souhlasim, naopak mnoho lidi pouziva kopirovani promenych $_POST a $_GET do jinych, staci odradkovat o stranku a v kodu se nikdo nevyzna.
Krispin:
no..ja teda vsude v kodu nepouzivam $_POST['promenna'] / $_GET['promenna']...neprijde mi tot aky moc prehledny...mam uz zazitej zvyk, ze na zacatku stranky si zkontroluju ocekavana data pomoci Ereg(I) a vlozim je do jejich promennych ekvivalentu: $_POST['promenna'] => $_prommenna -- jediny co nevim odkud pochazi, jeslti z POST, nebo GET, ale to uz neni takova traga...hlavni je ze vim, ze je z venku, ze je na 100% spravna a obsahuje pouze ocekavana data a ze nemusim vypisovat tu dlouhou variantu...:)
musim souhlasit, ze napr $_promenna je prehlednejsi varianta :)). Ostatne, snad je i skoro jedno, jestli hned vidime get nebo post, a reknu si aha, get. Aha post. Nenapada me, proc by to melo bejt tak uzitecny.
Hlavni je, kdyz s superglobalnimi poli pocitame a kontrolujeme.
Pro neznalého člověka to přehlednější není. Když v cizím kódu vidím $_promenna, tak si řeknu - aha to bude nějaká konstanta/globální proměnná/lokální proměnná - na první pohled není patrné, co je to zač. Navíc je kód zamořen balastem, jak je zmíněno v článku (pokud je použita funkce extract, je balastu méně).
Při psaní kódu by měly objektivní výhody (na první pohled je patrné, že je proměnná zvenku) převažovat nad subjektivními (zápis $_promenna se mi zdá přehlednější než $_GET["promenna"]).
Lord Mystic:
Musím jen souhlasit. Začínal jsem s php jako samouk a naučil jsem se spousty zlozvyků. Ale ještě před tím jsem se učil asp a tam se něco jako $_GET muselo zapisovat vždy když se načítala proměnná z venku. Když jsem pak začal s php tak jsem byl šťastný jak blecha že je to super jednoduché a že stačí zapsat jen $promena, ale dnes po několika letech programování i při tvorbě složitějších webu musím jednoznačně říct, že používám výhradně $_GET všude a nepřiřazuji to jednoduché proměnné - ty jsou jen pro daný script lokálně.
A věřte mi, php směřuje přesně tímto směrem, hodně k bezpečnosti. A ty složené závorky i elegantně vyřeší problém s uvozovakami.
Squad_Lead:
Presne toto se mi libi pouzivat.
U jednoduchych kodu $promenna = $_POST['jmeno'];
se zacatecnikovi (me) toto libilo, pak ale nastala ouha ouha. kod mel 1600 radku a musel jsem to upravovat.
"mix $_GET a $_POST je $_REQUEST. Pokud nevím (a je to jedno) odkud data pocházejí, můžeme použít i toto superglobální pole.
Pro psaní kvalitního a přehledného kódu by se měli dodržovat tyto zásady:
1) pro přístup k syrovým vstupním proměnným používat $_GET, $_POST, $_REQUEST (nebo také $_FILES)
2) v těchto polích proměnné neměňte (kromě samozřejmého odstranění nesmyslu jménem slashes)
3) Jakmile pole ověříte a převedete na správný typ, můžete je zkopírovat do globální/lokální proměnné $xxx a s tou dále pracovat.
a ještě bonus, funkce na odstranění slashes ze superglobálních polí. Funkce dávám k dispozici pod licencí GNU, je tedy třeba, aby každá aplikace, kde se použije, byla také pod GNU a obsahovala příslušný copyright :-)))
<?php
/***
* Recursive stripSlashes
***/
function stripSlashes_r(&$arr) {
foreach (array_keys($arr) as $key)
if (is_array($arr[$key])) stripSlashes_r($arr[$key]);
else $arr[$key] = stripSlashes($arr[$key]);
}
// normalize GPC:
if (get_magic_quotes_gpc()) {
stripSlashes_r($_GET);
stripSlashes_r($_POST);
stripSlashes_r($_COOKIE);
}
?>
Já obvykle používám tohle:
<?php
if (get_magic_quotes_gpc()) {
$_GET = array_map('stripslashes', $_GET);
$_POST = array_map('stripslashes', $_POST);
$_COOKIE = array_map('stripslashes', $_COOKIE);
$_REQUEST = array_map('stripslashes', $_REQUEST);
}
?>
Ale pokud někdo chce předávat pole ve stylu index.php?a[]=b&c[]=d, pak může použít něco takovýhodle:
<?php
if (get_magic_quotes_gpc()) {
function s($val) {
if (is_array($val)) {
return array_map('s', $val);
}
}
$_GET = array_map('s', $_GET);
$_POST = array_map('s', $_POST);
$_COOKIE = array_map('s', $_COOKIE);
$_REQUEST = array_map('s', $_REQUEST);
}
?>
Sakra, mám tam chybu:
<?php
function s($val) {
if (is_array($val)) {
return array_map('s', $val);
}
return stripslashes($val);
}
?>
Tak to dopadá, když píšu skripty z hlavy...
Ale llooku, nazvat funkci jako "s" ? A deklarovat ji navíc podmíněčně? To se pak divíš, proč na jednom hostingu aplikace běží, a na jiném háže chybu. :-)
Lépe snad takto
<?php
function stripSlashes_r($array) {
return is_array($array) ? array_map('stripSlashes_r', $array) : stripslashes($array);
}
// normalize GPC:
if (get_magic_quotes_gpc()) {
$_GET = stripSlashes_r($_GET);
$_POST = stripSlashes_r($_POST);
$_COOKIE = stripSlashes_r($_COOKIE);
}
?>
p.s. občas se mi komentář nedostane na správné místo, takže kdyby něco, toto je reakce na llooka 05.03.2005 15:30:49
maro:
Pochopil jsem to správně, že když používám _REQUEST, tak mi stačí změnit _GET a _POST? Nebo tam musím přidat i _REQUEST?
Přinejmenším vyzkoušením bys zjistil, že $_GET a ostatní s $_REQUEST nejsou nijak provázané a změnou jednoho neovlivníš druhé.
RejpalCZ:
Ad dgx: Když tu zveřejním svou úžasnou část kódu
<?php
echo $promenna;
?>
pod licencí GNU, budou muset být všechny projekty tento řádek obsahující taktéž pod licencí GNU? (je to trošku provokace, ale vážně to nevím a zajímalo by mně, kde je ta hranice)
Jinak s vaším postupem výše uvedeným souhlasím, dodal bych jen, že je IMHO lepší k těm syrovým datům z _GET a _POST přistupovat ne přímo, ale pomocí ověřovací fce (viz výše).
pokud by ho někdo zkopíroval přímo odsud a pastnul do své aplikace tak ano :-)
Michal:
Tak takhle to skutecne neni. Je uplne jedno odkud se ten kod ve clipboardu ocitnul. Dulezite je, ze oba vase GPL-pokusy jsou natolik kratke a obecne, ze se proste copyrightem chranit nedaji :-)
Stano:
No, ak si dobre pametam, tak pri kode ktory chce autor zverejnit pod licenciou GPL musi byt pridelena aj cela verzia tejto licencie - co je niekolko stran textu, a co v tomto pripade nebolo uskutocnene, takze kupirujte kolko len chcete.
honza:
no dobre, ale urcite se nevyhnu prepisovani do vlastni promene v tomto pripade
v html kdyz je input text
<input type="text" name="pole[jmeno]" value="Jmeno">
tak v v php musim udelat
<?php
$pole = $_POST["pole"];
$jmeno = $pole["jmeno"];
?>
- nebo snad tohle jde take nejak obejit?
Krispin:
toto je vicerozmerne pole...data z nej vydolujes takto: $_POST['pole']['jmeno']
Dave:
Ahoj, rad bych se zeptal ohledne tech promennych. Potreboval bych Javascriptem otestovat rozliseni a predat výsledek testu do nejake promenne v PHP abych s ni mohl dal pracovat, nemohl by mi nekdo poradit pls?
Predem diky.
Krispin:
musit to PHP nejak poslat...PHP je serverovy skript, takze jestlize mu chces dat vedet rozliseni, tak mu to musis dat v nejakym odkazu, aby se reloadla stranka...
Bjarne:
Tak tenhle tip podle me neni vubec dobrej, aspon pro me ne. Ja xmrti nesnasim psani silenosti typu $_POST['promenna']... proto mam fce podobne tem nahore getPost getGet getSession apod.
argument ze pak neni poznat co to vubec je za promennou a kde se vzala neberu... spravnej programator ma poradek v nazvu promennych a pomoci vhodny konvence vi co je co zac.
analagie, ktera me napada je napriklad "madarska notace" pri programovani ve WIN32 :)
Milan:
Zacinam s PHP a neprenasi se mi promene, netusite cim to je? V php.ini mam nastevno: register_globals = on i tak mi to nejde? Netusim, kde muze byt problem - jedu na Apache. Dik za radu.
Škoda, u téhle diskuze řešící jednu z elementárních věcí mi trochu chybí Jakubova reakce na postoj DGX.
skybedy:
Jasné, ale hlavním důvodem je tedy přehlednost, což je celkem subjektivní věc, nikoli důvody třeba bezpečnostní, je to tak?
Denis:
V mých aplikacích mám na práci s $_POST, $_GET atd. třídu, která se inicializuje hned na začátku programu. Po inicializaci této třídy již proměnné $_POST a $_GET neexistujou, jelikož třída je unsetne. To proto, abych měl jistotu, že je nějaký "blbec" nebude nikde dále v programu používat.
Tato třída si hodnoty z GET a POST nakopíruje do svého pole a pokud někdo chce k těmto hodnotám přistupovat, pak jedině pomoci této třídy.
Přístup k hodnotám pak provádím např. takto:
$InstanceTridy->GetValue(PARAMNAME_USERID);
Pokud se stane, že potřebuju nějaké paramtery z POSTu nebo GETu nějak kontrolovat, upravovat atd., vše se děje v oné třídě, takže na jednom místě.
Za aplikace, kde se všude v kódu povalují $_POST['name'] atd. bych dával dvacet na holou :D
Jakub Vrána :
Tu třídu bych chtěl vidět. Z tohoto popisu mi přijde, že to žádný valný smysl nemá.
Dvain:
Někde se bohužel používá, resp. používalo i tohle:
<?php
$get = array_keys ($_GET);
for ($i=0; $i<count ($get); $i++) {
${$get[$i]} = $_GET[$get[$i]];
}
?>
Diskuse je zrušena z důvodu spamu.