To první pořídí lépe skripty pro server, to druhé skripty na klientovi. K tvorbě skriptů lze pořídit kupříkladu rozhraní CGI, či jazyk PHP.
Zásadní informace je, že skript zahajuje výstup zásahem do hlavičky stránky. Je povinný sdělit typ dat, která pošle(!), případně může ještě poslat volitelné informace jako například zákaz cachování stránky (Pragma: no-cache), stav spojení (Status: 204 No response)...
Stejně jako v HTTP je hlavička od dat oddělena novým (prázdným) řádkem. Naučení je tedy takové, že první prázdný řádek, který skript vypíše, odděluje hlavičku od těla. Člověk mnohdy na tento prázdný řádek zapomene a diví se, proč padají pětistovkové stavy, tedy chyby na straně serveru. Kromě zapomenutého oddělovače v tyto stavy ústí i situace, kdy člověk zapomene na celou hlavičku.
Za hlavičku skript vypíše celý obsah stránky, která se má poslat
klientovi (od !DOCTYPE
po /HTML
). Jak
jsme řekli výše, programovací
jazyk pro rozhraní CGI není předem specifikován, platí, že můžete
použít binární program, který se má na hardwaru serveru spustit
(který získáte třeba z překladače jazyka C pro prostředí používané
na serveru), pokud soubor začíná sekvencí #!
,
má se za to, že
následuje jméno interpretu (tedy třeba #!/bin/bash
spustí jako
interpret Bourne-again-shell v adresáři bin
, jehož
výuka, předpokládám, bude předmětem výkladu o UNIXu). K předvádění
příkladu prvního příkladu použijeme kromě tohoto shellu ještě Pascal,
jehož překladač gpc
lze použít k vygenerování skriptu
a který předpokládáme, že čtenář aspoň částečně zná.
Příklad:
#!/bin/bash echo 'Content-type: text/html' echo echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">' echo '<title>Pokusná stránka</title> echo '<p>Pokus s CGI... a nic než pokus' echo '</html>Mimochodem sekvence
#!
vychází z toho, že křížkem se
ve skriptovacích jazycích obvykle zahajuje komentář.program pokus begin writeln('Content-type: text/html'); writeln; writeln('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">'); writeln('<title>Pokusná stránka</title>'); writeln('<p>Pokus s CGI... a nic jiného'); writeln('</HTML>' end.Cvičení:
writeln
proceduru write
? Proč?
Jelikož skripty povětšinou reagují na data poslaná ve
formuláři, je zejména potřeba se k těmto datům dostat.
Rozhraní CGI definuje, že pošlou-li se data metodou POST,
skript je najde na standardním vstupu (ze kterého se
v Shellu i Pascalu čte příkazem read
(pokaždé
s jinou syntaxí), tudíž uvedeme-li následující příklad
jen v Shellu, nikoho snad nezmateme:
Příklad:
#!/bin/bash echo 'Content-type: text/html' echo echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> echo '<title>Výpis metodou POST</title>' echo '<p>Napsal jsi: ' while read a; do echo "$a" '<br>' done echo '...A to je všechno.'Pošleme-li data metodou GET, objeví se v proměnné prostředí QUERY_STRING, tedy vypsat data je v tomto případě (alespoň v shellu) ještě jednodušší, nežli s metodou POST (není mi známo, jak s proměnnými prostředí zachází Pascal, proto příklad v Pascalu bude opět chybět.
Příklad:
echo "Napsal jsi: $QUERY_STRING"Nahradíme-li tímto řádkem
while
-cyklus minulého
příkladu, získáme skript, který vypíše data poslaná metodou
GET.
Metodu, jejíž pomocí byla data odeslána, zjistíme z proměnné prostředí REQUST_METHOD.
Příklad:
#!/bin/bash echo 'Content-type: text/plain' echo read pocet <citac pocet=`expr $pocet + 1` echo "Jsi $pocet. návštěvník." echo "$pocet" >citacPříklad si trochu rozeberme. Za hlavičkou načteme ze souboru
citac
první řádek do proměnné počet. Tuto hodnotu
následně zvýšíme o jedna (pokud byla numerická, jinak nám vyjde
nesmysl). Tuto zvýšenou hodnotu vypíšeme na standardní výstup
(tedy do stránky) a také do souboru citac
.
Striktně vzato PHP je obyčejný interpretovaný programovací jazyk
z rodiny programovacího jazyka C. Syntakticky a sémanticky je poměrně
blízký Javascriptu. Hned zkraje upozorněme, že jazyk je case-sensitive,
tedy záleží na velikosti písmen.
Tedy chceme-li programovat v PHP, stačí napsat příslušné zdrojové
texty. Co je na PHP netypické, je, že v souboru zdrojového textu
nemusí být jen tento zdrojový text (v jazyku PHP), ale primárně
jde o webovou stránku obsahující HTML. Zdrojové kódy PHP skriptů
jsou ve stránce v dobře definovaném metatagu. Tento metatag je:
<?PHP ....?>
. Tedy před, resp. za špičatou závorku
dáme znak otazník. Tento metatag lze povětšinou zkrátit (jelikož
řetězec PHP říká, že půjde o skript interpretovaný interpretem
jazyka PHP; že se má použít právě tento interpret je však zpravidla
jasné z názvu souboru. Proto většinou stačí PHP-skript umístit
do takovéhoto metatagu: <? ?>
.
Souborům obsahujícím PHP-skripty je zpravidla potřeba přiřadit
koncovku php
. Pokud soubory pojmenujeme jinak, webový
server neví, že se má vůbec zabývat možností, že by příslušné stránky
mohly obsahovat PHP-skripty a zpravidla by takto schovaný skript
nebyl zinterpretován. Záleží ovšem zejména na konfiguraci webového
serveru.
První věcí, kterou budeme pomocí PHP chtít provádět, je výpis (textu,
čísel...). Výpis (do zdrojového kódu stránky) zajistíme pomocí klíčového
slova echo
. Ovládá se (syntakticky) stejně jako
return
, tedy za ně klademe výraz, který se má vypsat.
Příklad:
... <body> <? echo "nazdar"; ?> <? echo '10 + 15 ='; echo 10+15; ?> ...V příkladu vidíme trojí provedení příkazu
echo
. Poprvé
vypisujeme textový řetězec, podruhé vypisujeme aritmetický výraz (tedy
také řetězec), potřetí jde o číselný výraz (jehož výsledkem je číslo).
Povšimněme si, že příklad ukazuje dva skripty (vidíme dva metatagy).
V prvním je jeden (samostatný) příkaz, ve druhém jsou dva příkazy (oba
ukončeny středníkem stejně jako v jakémkoliv jiném jazyku z rodiny jazyku C).
Na příkladu též rovnou vidíme, jak v PHP zacházet s čísly a jak s řetězci. Jak jsme řekli na začátku, jde o jazyk podobný Javascriptu, a tedy číselné a znakové konstanty chovají velmi podobně. Čísla prostě zapíšeme do zdrojového kódu, textové řetězce ohraničujeme uvozovkami nebo apostrofy. Zda použijeme apostrofy nebo uvozovky není zcela lhostejné, za nedlouho si ukážeme rozdíl, k tomu ovšem budeme potřebovat pracovat s proměnnými:
Proměnné v PHP mají jedno velké specifikum. Jejich jméno musí začínat
znakem $
. Tedy platným jménem proměnné může být například
$cislo
nebo $a
, ale ne kupříkladu
vysledek
. Proměnné není potřeba definovat a proměnné
mohou měnit datový typ (co do nich přiřadíme, to tam budeme mít).
Jinak se s proměnnými pracuje vpodstatě shodně jako v ostatních
programovacích jazycích, tedy lze je předávat jako
parametry funkcím a lze je používat ve výrazech (jejichž hodnotu chceme
vyčíslit). Základní operátory k tomu používané jsou prakticky totožné
s Javascriptem:
+
-
*
/
%
.
&&
||
!
>
<
>=
<=
==
!=
=
++
--
Dříve či později budeme potřebovat zdrojové texty okomentovat anebo část zdrojových předmětů zakomentovat. Ani na komentářích nás ovšem nic nepřekvapí. Komentáře existují dvou druhů. Buďto komentář do konce řádku zahájený dvěma lomítky:
// tady je komentář tady už ne.Druhý druh komentářů je na předem neomezený počet řádků. Zahájíme sekvencí
/*
, ukončíme */
. Tedy takto:
/* Zde je komentář bez ohledu na to, zda sem umístím konec
řádku nebo ne. Ovšem v komentáři se nám nesmí objevit symbol konec
komentáře, tedy posloupnost hvězdička-lomeno. */
Nyní se vraťme k rozdílům mezi apostrofy a uvozovkami:
Příklad:
<?php $titulek = 'Pokus'; $jazyk = 's PHP.'; ?> <html> <head> <title><?php echo $titulek; ?></title> </head> <body> <?php echo "Delame pokusy $jazyk"; ?> </body> </html>
V příkladu vidíme tři skripty. V prvním přiřazujeme do proměnných, v dalších dvou s těmito proměnnými pracujeme. Vidíme tedy mezi řečí, že po konci skriptu se obsahy proměnných neruší, ale zůstávají takové, jak jsme je nastavili. Obsah proměnné je zrušen až poté, co je vygenerována celá stránka. Tedy pokud bychom doufali, že do další stránky budeme schopni zanést data upočítaná na jedné stránce, mýlíme se a bylo by třeba použít něco jiného.
Dále si v příkladu povšimněme třetího řádku od konce. Vidíme něco jako textový řetězec, ovšem v textovém řetězci je proměnná. Toto je právě rozdíl mezi řetězci ohraničenými uvozovkami a apostrofy. Řetězec ohraničený apostrofy se dále neinterpretuje a je ponechán přesně tak, jak byl napsán. Oproti tomu řetězec ohraničený uvozovkami podléhá interpretaci, a tedy kromě escapových sekvencí (shodných ve všech jazycích - \n, \t, \r,...) je možné do řetězce ohraničeného uvozovkami napsat jméno proměnné a tato proměnná se (při vyhodnocování řetězce) zapíše na příslušné místo. Abychom zabránili zmatení, escapové sekvence taktéž fungují jen v řetězcích ohraničených uvozovkami.
Cvičení: V minulém příkladu změňte uvozovky na apostrofy a pozorujte změnu chování skriptu.
Základní řídící struktury jsou totožné prakticky ve všech jazycích z rodiny jazyka C. Ani zde nás nečeká překvapení, tedy:
if(podmínka)příkaz_nebo_blok
Kód v těle cyklu
se vykoná právě když je podmínka splněna. Podotkněme, že příkaz
se ukončuje středníkem, blok ne.
Příklad
if($a>10) echo "$a je větší než deset!\n";
if(podmínka) příkaz_nebo_blok else jiný_příkaz_nebo_blok
-
není-li podmínka splněna, provedeme jiný_příkaz_nebo_blok.
if($a>10) echo "$a je větší než deset!\n"; else{ echo '$a není větší než deset!'; $a%=10; // ale teď už ano! }Příklad ukazuje rovnou několik věcí najednou. Blok (jak jste jistě očekávali) uzavíráme do složených závorek, v řetězci vypisujeme konec řádku, kromě toho
$a
bude v prvním případě nahrazeno
hodnotou této proměnné (kdežto v else-větvi ne). A v závěru else-větve
vidíme komentář.
while(podmínka) příkaz_nebo_blok
Cyklus while snad už nikoho
ani nepřekvapuje.
Příklad
$a=0; while(++$a<= 10) echo "$a\n";
for(inicializace;podmínka;inkrementační_výraz)příkaz_nebo_blok
Opět to samé, jako v ostatních jazycích.
Příklad
for($a=0;$a<10;$a++) echo "$a\n";
do... while(podmínka)
Opakujeme cyklus, dokud je podmínka
splněna. Podmínka je poprvé vyhodnocena až po prvním průchodu cyklem.
Víme tedy, že takovýmto cyklem se alespoň jednou projde.
switch(výraz)/
case hodnota: kód_vykonatelný_pro_hodnotu_hodnota...}
Tuto řídící strukturu si nastudujte samostatně, chová se stejně
jako v ostatních jazycích.
Cvičení:
++$a
) změníme na postinkrementaci (
$a++
)? A co se stane, pokud tuto inkrementaci zkusíme provést na
řádku výpisu, tedy kód změníme na: echo "$a++\n";
?
$veta='jedna '.'a'.' jedna '."jsou". " dve";
Druhý zajímavý operátor je ternární operátor otazníku a dvojtečky.
Často se stává, že do proměnné chceme přiřadit jednu ze dvou hodnot
v závislosti na nějaké podmínce. Dosud jsme problém řešili řídící
strukturou if
ve stylu:
if($a==1) $b=1; else $b=2;Pro tento případ (kdy chceme v každém případě přiřazovat do téže proměnné podle nějaké podmínky) existuje ternární operátor, kdy za podmínku napíšeme otazník, za otazník napíšeme výraz, který chceme vyhodnotit (případně přiřadit) v případě, že podmínka platí. Za tento výraz napíšeme dvojtečku a zapíšeme výraz, který chceme vyhodnotit v případě, že podmínka neplatí. Tedy výše zmíněný příklad bychom zapsali asi takto:
$b=($a==1?1:2);
Výraz v závorce
je příkladem použití zmíněného ternárního operátoru. Pokud je podmínka
($a==1)
splněna, "vyhodnotíme" jedničku, jinak vyhodnotíme
dvojku. Výsledek tohoto výrazu přiřadíme do proměnné b
.
Pokud chceme z nějakého důvodu práci PHP-skriptu ukončit (například
zjistíme, že data nám nedávají dobrý smysl a nechceme nadělat zbytečnou
škodu) a chceme-li při té příležitosti ohlásit chybu, můžeme využít
funkci die
. Této funkci jako parametr předáme řetězec
s chybovým hlášením. Tedy například máme-li vydělit dvě čísla a zjistíme-li,
že jmenovatel je nulový, není moc co dělat jiného, než ohlásit chybu
(a čísla nedělit). Tedy kód by mohl vypadat například takto:
if($jmenovatel==0) die("Nulou není zdravé dělit!"); $podil=$citatel/$jmenovatel; //Víme, že za funkcí die skript už nepokračuje.
Chceme-li, aby PHP-skript zasahoval do hlavičky, s výhodou využijeme
volání funkce header
, které jako jeden textový argument
dáme řetězec, který chceme přidat do hlavičky. Tedy například
header("Content-type: octet/stream");
Funkci header
je potřeba zavolat ještě před tím, než
server začne posílat data (tedy ještě před generováním výstupu).
Jinak skript spadne s hlášením, že hlavička už byla odeslána.
Dalším problémem, na který každou chvíli narážíme, je generování
náhodných (resp. pseudonáhodných)
čísel. Tento problém snadno vyřešíme
zavoláním funkce rand
. Tato funkce bere 0 - 2 parametry.
Bez parametru generuje celá čísla mezi 0 na hodnotou vracenou funkcí
getrandmax()
. Funkce (rand
) zavolaná se dvěma
parametry min
a max
generuje celá čísla od
min
do max
včetně obou mezí.
Generátor můžeme chtít zinicializovat pomocí funkce srand
(která bere 0 nebo 1 argument), ale není to nutné. Pokud funkci dáme
argument, je to tzv. seed
(česky prý překládáme jako
semínko). Tuto možnost využijeme, pokud chceme ladit padající
aplikaci. Pokud zadáme pevně stanovený seed
, pokaždé budeme
generovat tatáž čísla. Pokud nám tedy aplikace spadne, můžeme se snadno
podívat, co za čísla jsme jí nagenerovali.
Cvičení:
function
, za ním následuje jméno funkce, kulaté
závorky, v nich případně seznam formálních argumentů a za kulatými
závorkami závorky složené obsahující definici těla funkce. Návratová
hodnota se vrací pomocí klíčového slova return
. Tedy
například funkci, která vezme několik argumetů (všimněte si, že
v našem případě vezme argumenty právě tři) a vrátí první z nich,
bychom napsali asi takto:function prvni($arg1,$arg2,/*...*/$argn) { return $arg1; }Všimněme si, že tato funkce může snadno vracet hodnoty různých typů (v závislosti na tom jakého typu je první argument).
Cvičení: Napište si funkci, která bude vracet hodnoty různých typů nezávisle na parametrech. Ne že by taková funkce měla být k něčemu dobrá, ale je užitečné si uvědomit, že i to se může při programování v PHP snadno stát.
Podobně jako v Pascalu je i v PHP třeba funkce napřed definovat a pak až použít. V PHP je ovšem tento princip doveden do absurdního tvaru: Je třeba, aby před zavoláním funkce interpret přes definici dané funkce "přeběhl". Tedy pokud máme plochou funkční strukturu (nemáme jednu funkci definovanou uvnitř druhé), znamená to opravdu totéž, co v Pascalu. PHP nám umožňuje vložit definici jedné funkce do těla funkce jiné. V takovém případě je ona "vnitřní" funkce definována až poté, co interpret přes její definici proběhne. Zkusme si situaci vysvětlit na následujícím příkladu se jmény jen náhodně se podobajícími jménům jisté postavy v jistém filmu:
function f() { function g() { echo "Je tam pan Vrana?\n"; } } f(); g();Tento příklad bez potíží proběhne a vypíše: "Je tam pan Vrána?" Ovšem pokud bychom volání funkce
f
zrušili, skript spadne, jelikož
funkce g
nebude definována. My ovšem můžeme příklad ještě
poněkud upravit:function f() { function g() { echo "Je tam pan Vrana?\n"; } } function h() { function g() { echo "Nejsem Vrana, jsem Kralik!\n"; } } h(); g();Tento příklad také úspěšně proběhne a vypíše: "Nejsem Vrana, jsem Kralik!" Ovšem pokud bychom mezi volání funkcí
h
a g
vložili ještě volání funkce f
, skript spadne, jelikož
jednou definované funkce je zakázáno předefinovávat. PHP konečně
nepodporuje přetěžování funkcí (dvě funkce téhož jména lišící se
nějakým způsobem v parametrech). Proč tomu tak je, objasní následující
odstavce:
Funkce se za jistých okolností mohou zavolat s méně parametry, než kolik
jsme jich definovali. U některých parametrů můžeme říci, že mohou být
implicitní. Uděláme to tak, že do nich v definici funkce přiřadíme
implicitní hodnotu: function implicitni($a=1){}
Tuto funkci půjde zavolat jak s jedním parametrem, tak bez parametru.
Zavoláme-li funkci implicitni
bez parametru, v těle funkce
bude tato situace neodlišitelná od situace, kdy do dané proměnné dosadíme
implicitní hodnotu, tedy jedničku.
Příklad:
function implicitni($a=1) { if($a!=1) echo "Funkci jsme zavolali s parametrem!"; else echo "Funkci jsme budto zavolali bez parametru, nebo s parametrem 1!"; } implicitni(10);// Vypise: "Funkci jsme zavolali s parametrem!" implicitni();// Vypise: "Funkci jsme zavolali bez parametru, nebo s..." implicitni(1);// "Funkci jsme zavolali... s parametrem 1!"
Cvičení: Co se stane, pokud nedáme jako implicitní parametry ty poslední, ale například ty počáteční (function f($a=1,$b,$c){}
)?
array
asi takto:
$pole=array();
a ještě k tomu můžeme jako argumenty
zadat jednotlivé prvky pole, čili například takto:$pole=array("jedna","dve","tri","ctyri","pet");Pak získáme pole již zinicializované (tak, že první prvek má index 0) a k jednotlivým jeho položkám přistupujeme pomocí operátoru hranatých závorek:
$pole[0]
tedy bude mít hodnotu "jedna". Povšimněte si, že v jednom poli můžeme
skladovat proměnné různých typů. Pro tuto vlastnost mě sice nenapadá
rozumné využití, ale potřeba takovou možnost očekávat při čtení
cizího kódu. Oproti mnoha jazykům je novinkou tzv.
asociativní pole, které neindexujeme čísly, ale řetězci. Chceme-li
vyrobit asociativní pole, postupujeme takto:$garaz=array(prvni=>'Audi', druhe => 'Mercedes');Jak bylo napsáno, toto pole není indexováno čísly, ale pomocí řetězců:
$garaz[prvni]
(jméno prvku můžeme dát do
uvozovek nebo apostrofů, tedy též $garaz['prvni']
nebo $garaz["prvni"]
). Vypisovat obsahy proměnných
již umíme, činíme tak pomocí klíčového slova echo
.
Taktéž si všimněte, že můžeme udělat děravé pole a navíc pole
hybridní (kdy některé prvky budou indexovány asociativně, tedy
řetězcem, kdežto jiné budou indexovány číslem). Například můžeme
k předešlému příkladu s auty připojit řádek:
$garaz[1000]='Zigulik';
V tom případě by nebyl
definován žádný prvek s indexem menším než 1000.
S poli lze provádět i některé další lumpárny. Například lze
přidat k poli: $garaz[]='Skodovka';
v našem příkladu
na pokračování definuje prvek s indexem 1001 (protože zatím poslední
prvek měl index 1000) a příslušným způsobem ho zinicializuje.
Prvek v poli lze taktéž zrušit zavoláním funkce unset
,
které jako parametr předáme rušený prvek. Tedy například:
unset($garaz[1000]);
zrušíme Žigulík.
Pokud máme v poli takový nepořádek, že už se v něm sami nevyznáme,
můžeme dát pole "setřepat", tedy nechat vyrobit pole, ve kterém
budou jednotlivé prvky indexovány od nuly bez chybějících indexů.
Toto nám umožňuje funkce array_values
, které jako
parametr předáme setřásané pole. Funkce nám vrátí nové pole.
Funkci bychom tedy mohli využít například takto:
$nova_garaz=array_values($garaz);
Prvky jsou funkcí array_values
seřazeny v pořadí,
v jakém byly přidávány.
Cvičení: Jakým způsobem se dostaneme k hodnotě 8 v následujícím
poli? $a=array(0 => 0, 1 => 1, "pepicek" => 7, 8);
Nápověda: Pokud nevíte, podívejte se do slidů.
print_r
a var_dump
Snadno se může stát (a většině z nás se také stane), že ztratíme
orientaci v obsazích vlastních proměnných. Z nějakého důvodu nebude
fungovat to, co by dle našeho názoru fungovat mělo. Například nebude
definován prvek pole s námi očekávaným indexem. Pokud se chceme podívat,
co se s proměnnou děje, můžeme k tomu použít funkce print_r
nebo var_dump.
Obě funkce se ovládají podobně, ta druhá
podává poněkud obsáhlejší informace. Je ovšem potřeba dát pozor na to,
že výstup těchto funkcí by měl být prohlížen jako holý text (protože
se v něm mohou vyskytovat znaky 62, tedy >
- postačí
jen si nechat vypsat pole). Tedy příklad:
<? header('Content-type: text/plain'); $a=array('Mercedes','Rolls-Royce',"pepicek"=>"Skodovka","Zigulik"); print_r($a); echo "Tohle byla funkce print_r, ted pojede var_dump:\n"; var_dump($a); ?>Výstup bude vypadat asi takto:
Array ( [0] => Mercedes [1] => Rolls-Royce [pepicek] => Skodovka [2] => Zigulik ) Tohle byla funkce print_r, ted pojede var_dump: array(4) { [0]=> string(8) "Mercedes" [1]=> string(11) "Rolls-Royce" ["pepicek"]=> string(8) "Skodovka" [2]=> string(7) "Zigulik" }
Jelikož PHP je jazyk navrhovaný speciálně pro interakci s HTML, byl by velmi vhodný nějaký interface, který by nám zpřístupnil údaje zaslané prostřednictvím webových formulářů. Ačkoliv jsme si již ukázali poměrně rozsáhlou část PHP, dosud není jasné jakým způsobem skriptu předávat vstup. To napravíme v této kapitole.
Jednou (a zároveň nejjednodušší) možností, jak se dostat k datům zaslaným
prostřednictvím formuláře, je tzv. globální resoluce. Ta odkazuje k tomu,
že pro prvek formuláře daného jména PHP vytvoří proměnnou stejného jména
(ovšem se znakem $ na začátku). Tedy pokud ve formuláři máme například:
<input type=text name=pepicek>
, při zapnuté globální
resoluci ve skriptu můžeme použít proměnnou $pepicek
a v ní
bude hodnota, kterou jsme ve formuláři do příslušného textového políčka
vyplnili. Tato globální resoluce se nezřídka považuje za bezpečnostní
riziko (jelikož se snadno může stát, že nebude existovat nějaká proměnná,
na kterou se budeme ptát, případně se někdo může tímto způsobem pokusit
změnit obsahy již existujících proměnných). Proto bývá většinou vypnuta
a je potřeba použít jiný způsob.
Jinou metodou je naparsovat si řetězec zaslaný metodou GET. K tomuto
řetězci se dostaneme prostřednictvím užitečného asociativního pole
$_SERVER
konkrétně přístupem k položce QUERY_STRING
.
Řetězec poslaný na server metodou GET (tedy část URI za otazníkem, který
následuje za jménem skriptu) tak získáme jako
$_SERVER['QUERY_STRING']
. Pokud bychom byli masochisti, můžeme
si tento řetězec naparsovat.
V dalším způsobu přístupu k submitnutým datům je potřeba rozlišit, zda data
přišla metodou GET nebo POST. Podle toho používáme dvě z následujících čtyř
asociativních polí: $_GET, $_POST, $HTTP_GET_VARS, $HTTP_POST_VARS
.
Druhé dvě možnosti fungovaly v PHP verze 4, první dvě se používají
ve verzích 3 a 5 (v době používání verze 4 se v $_GET
a
$_POST
našly chyby a tato asociativní pole bylo důrazně
nedoporučeno používat, v PHP 5 naopak zmizela pole $HTTP_GET_VARS
a $HTTP_POST_VARS
. Pokud budeme používat PHP verze 5, budeme
tedy používat první dvě jmenovaná asociativní pole a výklad na ně tedy
nadále omezíme.
Jak jsme řekli, jde o asociativní pole indexovaná jmény jednotlivých prvků
formuláře. Příslušné hodnoty pak jsou hodnotami pole. Tedy chceme-li přiřadit
do proměnné $pepicek
obsah výše uvedeného stejnojmenného
textového pole, uděláme to takto: $pepicek=$_GET['pepicek'];
Pole $_POST
funguje úplně stejně, jen je třeba myslet na to
jakou metodou data na server posíláme.
Teď už sice víme, jak se dostat k uživatelem zadaným datům, ale je třeba
dát pozor na předčasnou radost. Uživatelé totiž nejsou dobráci a snadno
nám mohou poslat řetězec, kterým nám budou chtít uškodit. Konkrétně mohou
mít ambice koukat na obsahy našich proměnných (a to zejména těch obsahující
přístupová jména a hesla). Taktéž (až se budeme učit o práci s MySQL) mohou
chtít ukončit jeden SQLský dotaz a zahájit jiný (začínající pokud možno
příkazem drop
). Pokud nám přijde řetězec, je vhodné jej tzv.
oescapovat, tedy deaktivovat všechny potenciálně nebezpečné znaky. Zejména
se jedná o ASCII-znaky 39 (apostrof) a 92 (zpětné lomítko, backslash
alias zvrhlítko). Před každý z nich tak budeme chtít pro jistotu umístit
jednou znak 92 (\). Buďto použijeme nějakou již existující funkci (kterou
si najdeme v manuálu k PHP), nebo si ji vyrobíme sami například na způsob
toho,
jak je uvedena na roscripts.com:
<?php function escape_string($str) { if ($str !== null) { $str = str_replace(array('\\','\''),array('\\\\','\\\''),$str); $str = "'".$str."'"; } else { $str = "null"; } return $str; } ?>Příklad nám ukazuje použití funkce
str_replace
. Tato funkce
bere tři parametry. Třetí je řetězec, ve kterém chceme nahrazovat. První
parametr je pole určující které podřetězce budeme nahrazovat, druhý parametr
je pole stejné délky a říká čím budeme nahrazovat. Tedy znak \ nahradíme
dvěma takovými a znak ' nahradíme posloupností \'. Z existujících knihovních
funkcí řetězec "obrní" například funkce mysql_escape_string
.
Této funkci se jako parametr předá řetězec a funkce nám vrátí takový
řetězec, který lze bez obav poslat funkcím pracujícím s MySQL. Pozor, pro
jiné účely takový řetězec v pořádku být nemusí.Příklad:
$text=mysql_escape_string($text);Příklad ukazuje použití funkce
mysql_escape_string
. Jako
parametr jí dáváme proměnnou $text
, výsledek přiřadíme do téže
proměnné.
Nyní již sice víme, jak se dostat k datům z prvků formuláře, nevíme však,
co máme čekat od jednotlivých datových typů ve formuláři. Přesněji víme
to u textového políčka. V zásadě je to
jednoduché - vrací se nám hodnota atributu value
. Z této
informace je jasné, co čekat od textového políčka, políčka na heslo a prvku
hidden. Pro checkboxy a radiobuttony si situaci raději rozeberme podrobněji.
U checkboxů je situace jednodušší: Pokud checkbox není zatržen, vrátí se
prázdný řetězec. Pokud checkbox zatržen je, vrátí se nám obsah jeho atributu
value
. Pokud není atribut value
nastaven, vrací
se řetězec "on"
.
Radiobutton je z těchto prvků asi
nejzrádnější, ačkoliv v něm platí totéž, co pro checkbox. Tedy máme-li
radiobutton sestávající z několika možností, je nám vrácen atribut
value
pro příslušnou možnost. Pokud atribut value
nedefinujeme, vrátí se nám hodnota on
. Asi je jasné, že bude
legrační, pokud zapomeneme radiobuttonu definovat atribut value
pro více než jednu možnost (takové možnosti pak od sebe neodlišíme).
Příklad:
<form action="nic.php"> <input type="text" name="text1"> <input type="checkbox" name="chkbox"> <input type="radio" name="televize" value="prvni"> <input type="radio" name="televize" value="druhy"> <input type="radio" name="televize"> </form>Skript může následně získat hodnoty takto:
$text=$_GET["text1"]; $chkbox=$_GET["chkbox"]; $televize=$_GET["televize"];V jednotlivých proměnných potom bude:
$text
$chkbox
on
(jelikož jsme
nespecifikovali atribut value
),
$televize
prvni, druhy
nebo on
podle toho, který z těch tří prvků je vybrán. Všimněme si, že třetímu jsme nedefinovali atribut value
, proto, pokud
je vybrán, bude vrácena hodnota on
stejně jako v případě
checkboxu.
Cvičení: Vyzkoušejte si zasílání dat na server pomocí formulářů pro různé typy vstupních prvků (text, radio, select a podobně).
Zatím jsme se poměrně důsledně vyhýbali zpracování jednoho prvku
formuláře, a to souboru. Soubor lze nahrát metodou POST, ovšem je zapotřetí
udělat to rafinovaně. Vše začíná už u příslušného formuláře, který nám
bude odesílání zajišťovat. Tomu je potřeba nastavit atribut enctype
na hodnotu multipart/form-data
. Formulář by pak mohl vypadat
asi takto:
<form enctype="multipart/form-data" action="skript.php" method=POST> Tady se nahraje soubor: <input type=file name=soubor> <input type=submit> </form>PHP nám nahrané soubory zpřístupní v asociativním poli
$_FILES
(v PHP verze 4 se jmenovalo $HTTP_POST_FILES
).
Toto pole je opět indexováno jmény prvků ve formuláři (tedy v našem
případě bychom hledali položku "soubor"
. Každý prvek tohoto
asociativního pole (tedy například $_FILES[soubor]
) je též
asociativní pole několika prvků a můžeme v něm najít zejména:
tmp_name
name
size
type
image/png
).
Napřed (před zpracováním souboru) je vhodné se ujistit, zda byl soubor
opravdu nahrán (zda nedošlo k chybě). To uděláme zavoláním funkce
is_uploaded_file
, které jako parametr dáme jméno souboru
(který má existovat ve filesystému), tedy:
is_uploaded_file($_FILES[jmeno_prvku_formulare][tmp_name]);
Funkce vrací true
, pokud je soubor nahrán, false
jinak.
Chceme-li soubor zpracovat, chceme jej buďto přečíst (což se budeme učit
za nedlouho (a v takovém případě použijeme jako jméno souboru
$_FILES[jmeno_html_prvku][tmp_name]
) anebo jej chceme
okopírovat na stálé místo (kde nebude po konci skriptu smazán). V tom
případě můžeme použít funkci copy(odkud,kam);
Jméno
odkud
bude odkazovat opět k tmp_name
.
$fp=fopen($jmeno,rezim);
rezim
(jak se popisuje režim, viz dále a je
to velmi podobné otevírání souborů v jazyce C. Návratovou hodnotu
funkce fopen
pak používáme na odkazování k otevřenému
souboru (viz dále, kdykoliv používáme proměnnou $fp
,
jedná se o výsledek volání funkce fopen
.
$text=fread($fp,$delka);
$fp $delka
bytů.
$a=fputs($fp,$obsah);
obsah*/
(do souboru $fp
).
close($fp);
fp.
Příklad (odesílající formulář):
<FORM submit="skript_zapis.php"> <input type="text" name="textik"> <input type="submit"> </form>
Příklad (zapíšeme na konec souboru):
$soub=fopen("soubor.txt","a"); puts($soub,$textik); fclose($soub);
Příklad (vypíšeme soubor):
$soub=fopen("soubor.txt","r"); if($soub) { $puvobs=fread($soub , filesize ($filen)); fclose($soub); } $soub=fopen($filen,"rw+") or die("<b>Nelze otevřít soubor!</b>"); fputs($soub, "<span class=\"jmeno\">$jmeno</span><br>"); fputs($soub, "<span class=\"obsah\">$text</span><p>"); fwrite($soub, $puvobs); fclose($soub);
PHP je výkonný skriptovací jazyk, který mimo jiné umožňuje komunikovat s databázemi (jelikož většina skriptů, které potřebujeme, beztak reprezentují jen nějakou manipulaci s databázemi). My si popíšeme rozhraní, kterého lze využít při spolupráci s MySQL-serverem. MySQL se používá zejména proto, že je poměrně jednoduchý. Použití MySQL v PHP není samozřejmé, musí být zapnuta podpora jeho interpretace.
Od PHP verze 6 byla podpora MySQL přepsána na tzv. MySQL Improved.
Tato implementace je objektová (operuje s třídou mysqli
reprezentující jedno připojení k databázi). Jelikož jsme se objekty
dosud nezabývali (a budeme se o nich bavit jen okrajově, jelikož
objekty v PHP nejsou příliš vzorem toho, jak by měly objekty vypadat),
budeme využívat funkce volající jednotlivé metody třídy mysqli
.
Služeb serveru tedy využíváme pomocí zvláštních funkcí. Těmto funkcím
zadáváme jako argumenty řetězce obsahující SQL-dotazy, například
select * from telef;
$conn=mysqli_connect("adresa_stroje_s_db","login","heslo");
nebo
$conn=mysqli_connect("adresa","login","heslo","databaze");
připojí "skript" k serveru dle zadaných údajů. Funkce bere čtyři
parametry, z nichž čtvrtý je implicitní (tedy není třeba jej nastavovat).
Pokud jej nastavíme, rovnou vybereme databázi, se kterou budeme pracovat
(pak nemusíme volat mysqli_select_db
).
Nejspíše se používá připojení k místnímu stroji, jelikož připojení po
síti bývá zakázané). Funkce vrací objekt popisující připojení. Tento
objekt reprezentuje naše spojení a je třeba jej předávat téměř všem
následujícím funkcím (podobně jako pracujeme-li se soubory, předáváme
objekt popisující otevřený soubor).
mysqli_close($conn);
odpojí od serveru - konec SQL,
mysqli_select_db($conn,"jmeno_databaze");
vybere databázi,
$vysledek=mysqli_query($conn,"pozadavek");
provede požadavek,
výsledkem je objekt popisující odpověď (SQL-serveru). Tento objekt je osazen
metodami a atributy umožňujícími nám získat o odpovědi konkrétní informace:
$pocet=mysqli_num_rows($vysledek);
zjistí počet serverem vrácených řádek, ekvivalentně tuto informaci získáme:
$pocet=$vysledek->num_rows;
tedy přečteme objektu reprezentujícímu odpověď atribut num_rows
$asoc_pole=mysqli_fetch_assoc($vysledek);
odebere z odpovědi serveru první řádek a vytvoří z něj asociativní pole. Prvky pole jsou indexovány názvy sloupců (které odpověď obsahuje). Při dalším zavolání zpracuje (tedy vydá) další řádek odpovědi (jde tedy o funkci dotyčný objekt postupně destruující). Jde o ekvivalent zavolání metody fetch_assoc
objektu reprezentujícímu odpověď:
$asoc_pole=$vysledek->fetch_assoc();
$pole=mysqli_fetch_array($vysledek);
odebere z odpovědi serveru první řádek a vytvoří z něj pole (neasociativní, tedy prvky jsou indexovány přirozenými čísly - od nuly včetně). Ekvivalentní zavolání metody fetch_array
objektu reprezentujícímu odpověď:
$pole=$vysledek->fetch_array();
$prvek=mysqli_fetch_field($vysledek);
odebere z odpovědi serveru jeden prvek. Ekvivalentní zavolání metody fetch_field
objektu reprezentujícímu odpověď:
$prvek=$vysledek->fetch_field();
Příklad: Chceme z tabulky adresy
v databázi zakaznici
získat všechny prvky jmeno
a setříděné podle abecedy vypsat do tabulky (s jedním sloupcem):
<table> <caption>Jména zákazníků</caption> <tr><th>Jmeno <? @$conn=mysqli_connect("localhost","root","heslo","zakaznici"); $r=mysqli_query($conn,"select jmeno from adresy order by jmeno;"); $num_rows=mysqli_num_rows($r); for($i=0;$i<$num_rows;$i++) echo "<tr><td>".mysqli_fetch_assoc($r)["jmeno"]; mysqli_close($conn); ?>V příkladu vidíme (kromě toho, co vidět máme) na prvním řádku skriptu (volání funkce
mysql_connect
) využití unárního prefixního
operátoru zavináče (česky "obchodní a" - @). Tento operátor lze použít
k potlačení chybového hlášení. Pokud tedy matně tušíme, že bychom neradi
návštěvníkům webu prozradili přihlašovací údaje od databáze (jelikož
pak by se mohli zbytečně snažit naši databázi přijít osobně navštívit).
Toto by se
snadno mohlo stát, jen nám databáze vypadne, přesněji jen funkce
mysql_connect
z nějakého důvodu selže (interpret by
mohl ohlásit chybu na příslušném řádku a jeho obsah vypsat).
Proto jsme před
její volání dali operátor zavináče, který říká, že i když v následujícím
výrazu dojde k chybě, chybové hlášení se má potlačit (ovšem skript
spadne tak jako tak).
Často se stává, že z odpovědi od serveru potřebujeme sestrojit pole, jehož prvky jsou jednotlivé řádky. Prvky tohoto pole tedy budou opět pole, tentokrát asociativní (abychom v rámci řádku mohli hledat podle názvu položky). Problém vyřešíme asi takto:
function result($res) { $result=array(); for($i=mysqli_num_rows($res);$i>0;$i--) $result[]=$res->fetch_assoc(); return $result; }
Cvičení: Jak byste řešení modifikovali, aby byly jednotlivé řádky reprezentovány jako pole (neasociativní), tedy abychom k nim mohli přistupovat indexem požadovaného sloupce?
Cvičení:
castka
jehož hodnoty
chcete posčítat (select sum(castka) ...
) Jak bude vypadat
volání funkce mysql_result
v takovém případě?
Cookies lze použít k uschování informace na straně klienta. Můžeme si tak v principu ukládat data na disk klienta. To by nám v zásadě umožňovalo rozmigrovat si data po discích našich návštěvníků, má to však jeden háček: K těmto datům se nedostaneme, dokud dotyčný návštěvník znovu nepřijde. Cookies se tedy používají ke zrádnému šmírování nebohých uživatelů. Ovládají se až nečekaně jednoduše. Cookie se dá buďto zanést, nebo číst. Sám od sebe se může cookie zkazit (expiruje a klient jej nadále nepoužívá a nejspíše ho v rámci úspory místa na disku smaže). Každý cookie vypadá trochu jako HTML-atribut, tedy má jméno a jemu přiřazenou hodnotu.
Začneme tím jednodušším, tedy přístupem ke Cookies. Ten se děje přes
asociativní pole $_COOKIE.
Toto pole je (jistě k našemu
nemalému překvapení) indexováno jmény příslušných cookies. Tedy pokud
jsme zanesli cookie se jménem user,
jeho hodnotu zjistíme
jako $_COOKIE[user].
Snadno se ovšem může stát, že nevíme,
zda je cookie nastaven. K tomu použijeme obecnou funkci pracující nad
poli isset,
které předáme prvek pole a funkce rozhodne,
zda je prvek pole definován nebo ne. Zeptali bychom se tedy
isset($_COOKIE['user']).
Těžké není ani nastavování cookies. Ovládá se jednou (byť zběsile
vyhlížející) funkcí:
setcookie($jmeno,$hodnota,$cas_expirace,$cesta,$domena,$secure,$httponly)
Parametrů je úctyhodné množství, ale od druhého (včetně) jsou implicitní
a tedy jich lze vyplnit libovolné (nenulové) množství. Smysl a význam
parametrů je tento:
$jmeno
$hodnota
$cas_expirace
time()
,
$cesta
$domena
$secure
$httponly
Pozor, funkce setcookie
musí být zavolána před začítkem
vypisování těla stránky (tedy před doctypem)!
Příklad:
<? $jmeno=$_GET["jmeno"]; setcookie("jmeno",$jmeno,time()+3600); ?>Takto jsme nastavili cookie, který jménu
jmeno
přiřadí obsah
proměnné $jmeno,
která nám byla zaslána formulářem metodou
GET.
Tento cookie se zkazí za hodinu (3 600 sekund od teď).
Pokud bychom čas nenastavili (nebo jej nastavili na nulu),
cookie platí do konce session. Pokud bychom chtěli cookie smazat, nastavíme
mu čas expirace v minulosti. Implicitní hodnoty pro doménu a adresář jsou
jméno současného stroje a současný adresář.
Nazdar, <? if(isset($_COOKIE["jmeno"])) echo "ze ty jsi ".$_COOKIE["jmeno"]."!? Vidis, my tu vsechno pozname,"; ?> takze k veci...Tady zkusíme uživatele identifikovat podle dříve zaneseného cookie.
Cvičení: Do souvislého příkladu Kafomat implementujte identifikaci uživatele pomocí cookies (pokud uživatele neznáte, zobrazte mu registrační formulář, kde musí oznámit své jméno, pod kterým ho buďto najdete v databázi, nebo do ní zanesete. Cookie používejte ke sledování učiněných objednávek.
session_start
) a na běžící session
je třeba (voláním též funkce) upozorňovat na každé stránce (kde session
budeme používat). Funkci session_start
je třeba zavolat ještě
před výpisem HTML (před doctypem).
Session se ovládá jako (asociativní) pole $_SESSION
, do kterého
si můžeme ukládat hodnoty vcelku libovolného typu pod klíči, které chceme.
Můžeme tedy počítat, kolikrát člověk během současné návštěvy kliknul, nebo
si v tomto poli můžeme pamatovat přihlašovací údaje. Pole
$_SESSION
se zkrátka používá jako jakékoliv jiné (asociativní)
pole.
Příklad
<? session_start(); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <title>Kontrola</title> <p> Vy jste <? echo (isset($_SESSION['byltu'])?"tu uz byl, tak to tu znate...": "jste ted prisel, takze vitejte!"); $_SESSION['byltu']=true; //udelame si boolskou promennou, ktera je budto true, nebo neexistuje. ?>
V příkladu si všimněme využití ternárního operátoru otazník - dvojtečka. Tedy
pokud je v poli $_SESSION
nastaven prvek byltu
,
vypíšeme řetězec před dvojtečkou, jinak vypíšeme ten za dvojtečkou.
Pokud chceme session explicitně ukončit, můžeme po zavolání funkce
session_start
stejným způsobem zavolat funkci
session_destroy.
Tato funkce se taktéž volá bez parametrů.
Cvičení: Do souvislého příkladu Kafomat přidejte sessiony. Využijte je k počítání položek, které člověk během současné session objednal (a jejich počet při vhodných příležitostech vypisujte).
Pokud chceme uživatele autentizovat (přihlásit, abychom jim povolili nějakým způsobem speciální přístup), můžeme s výhodou využít cookies, resp. session (podle okolností). Session nám umožní propasírovat vždy na klienta jméno a heslo, Cookies můžeme použít podobně (nastavíme jeden na jméno, druhý na heslo), což ovšem není příliš bezpečné (cookies se porůznu ukládají na disk). Proto můžeme na nějakou dobu vygenerovat cookie, o kterém si poznamenáme, že ho drží tento uživatel a pokud nám někdo tento cookie předloží, budeme mít za to, že jde o dotyčného uživatele.
Zbývá maličkost: Jak dostat přihlašovací údaje z klienta k nám. K tomuto buďto můžeme použít již známé HTML-formuláře, anebo můžeme nasadit HTTP-autentizaci podle RFC 2617.Nás bude tato autorizace zajímat v souvislosti s využitím v rámci PHP. Jak tedy tuto autorizaci použijeme:
Druhů autentizace je několik, my si ukážeme napřed tzv. Basic Authentication:
Pokud se klient chce autentizovat, objevíme v již známém asociativním
poli $_SERVER
prvky PHP_AUTH_USER
a
PHP_AUTH_PW.
Tyto prvky obsahují (v tomto pořadí) přihlašovací
jméno a heslo. Přihlašovací stránku tedy můžeme pojmout například takto:
<? if(!isset($_SERVER['PHP_AUTH_USER'])) { header('Status: 401 Unauthorized'); header('WWW-Authenticate: Basic realm="Jmeno aplikace"'); echo 'Tak takhle by to neslo!';//toto se zobrazi pri zruseni. }else{ echo ' <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">'; echo '<title>Chranena stranka</title>'; echo '<p>Zde bychom mohli provest overeni jmena a hesla. Tyto udaje jsou:<p>'; echo '<em>Jmeno:</em> '.$_SERVER['PHP_AUTH_USER'].' <em>Heslo:</em> '; echo $_SERVER['PHP_AUTH_PW']; } ?>Existují i další typy autentizací, nicméně nám bude pro začátek stačit ta základní (například Digest zřejmě není zdaleka vždy podporována).
Cvičení: Do souvislého příkladu Kafomat přidejte přihlašování. Jména a hesla udržujte v MySQL-databázi a kontrolujte.