Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

Úvod do PHP Labsky@vse.cz IZI 228.

Podobné prezentace


Prezentace na téma: "Úvod do PHP Labsky@vse.cz IZI 228."— Transkript prezentace:

1 Úvod do PHP IZI 228

2 HTML CSS Javascript DHTML Napište jednoduchou knihu hostů! PHP model klient/server & HTTP Cookies SQL SQL z PHP Zabezpečení na serveru eso.vse.cz Paralelní přístup k aplikaci

3 Jazyk PHP PHP: Hypertext Preprocessor
Interpretovaný skriptovací jazyk (jako Javascript) Používaný na serveru pro dynamické generování stránek server klient <html> <head> <title>Aktuální datum</title> </head> <body> <p> <? $dneska = date("j.n.Y"); print ("Dnes je $dneska."); ?> </p> </body> </html> <html> <head> <title>Aktuální datum</title> </head> <body> <p> Dnes je </p> </body> </html> processing instruction

4 Příklad php skript HTTP požadavek TCP db HTTP odpověď - hlavičky
HTTP odpověď - tělo eso.vse.cz:80 eso.vse.cz:3306 HTTP server (Apache, Tomcat, IIS) db server (MySQL, Oracle) klient

5 PHP - možnosti Přístup na informace z HTTP požadavků
Detailní vytváření HTTP odpovědí Přístup do databází tj. knihovny poskytující API pro přístup na (příp. vzdálený) databázový server Autentizace Velké množství knihoven XML parsery, včetně DOMu API pro SOAP Generování obrázků Sockety (TCP spojení) Posílání mailu, stahování mailu (SMTP, POP3, IMAP), ...

6 PHP – jazyk Inkluze do HTML pomocí processing instructions: Komentáře:
<? print ("Ahoj!"); ?> <?= "Ahoj!" ?> <?php print ("Ahoj!"); ?> Komentáře: ; <? print ("Ahoj!"); // komentář /* komentář na víc řádek */ ?> Case sensitive Řada nastavení (php.ini) debugovací výpisy (error, warning, notice), max. doba běhu skriptu, bufferování výstupu, zabezpečení, vlastní processing instructions...

7 Proměnné – datové typy Deklarace Primitivní datové typy:
prvním přiřazením hodnoty: $jmeno = 'Josef'; dynamické datové typy lze měnit typ proměnné proměnná se chová dle kontextu (automatický cast) (!) Primitivní datové typy: boolean integer float string Speciální datové typy: resource NULL Složené datové typy: array object true false 10 10.5 'Josef' "Houdek"

8 " ' . .= Stringy $jmeno = 'Josef'; $prijmeni = "Houdek";
print("jmeno\t$jmeno\nprijmeni\t$prijmeni"); print('Zde se proměnné jako $jmeno nenahradí hodnotou a \n zůstane \n...'); $veta = $jmeno.' '.$prijmeni; $veta .= ' vlastní hospodu.'; $str = <<<KONEC Toto je string zapsaný v tzv. "heredoc" syntaxi. Chová se jako v případě dvojitých uvozovek: Vrchní se jmenuje $jmeno. KONEC; tabelátor nový řádek " ' . konkatenace (!) Operátor + je pouze numerický a operandy se automaticky castují na integery nebo floaty... .=

9 Stringy – některé funkce
$jmeno = 'Josef'; $prijmeni = "Houdek"; echo $jmeno; print $prijmeni; $prvni_znak = $jmeno{0} $posledni = $jmeno{ strlen($jmeno)-1 } $cena = 17.5; $vyveska = sprintf("Pivo je za %.2f", $cena); $serial = sscanf("SN/ ","SN/%d"); $pole = explode(' ', 'Josef Houdek'); split('/s+', 'Josef Houdek'); $retezec = implode(',', $pole); // join() stejne jako implode() $retezec = trim("\n\t\t Tady je teprv Maso.. \r\n"); ltrim() rtrim() $lcase = strtolower($str); $ucase = strtoupper($str); $viktor = substr("Nad Viktorkou", 4, 6); // Viktor $at_vse_cz = $idx = // 6 (odzadu strrpos()) $s = htmlspecialchars('Odkaz se píše <a href="url">'); // <  < $s = addslashes('něco v "uvozovkách"'); quotemeta() jazykové konstrukty, ne funkce  nejsou třeba závorky kolem argumentů 1 nebo více bílých znaků [ \t\r\n]

10 Regulární výrazy I. /vzor/i Viz PHP manuál:
- Regular Expression Functions (Perl / POSIX) - Pattern Syntax - Pattern Modifiers // extrahuj hostname z URL preg_match('/^      " $matches); $cely_match = $matches[0]; // $hostname = $matches[1]; // // extrahuj ove odkazy z HTML stranky '<a <a $matches); $cely_match_1 = $matches[0][0]; // $ _1 = $matches[0][1]; // $cely_match_2 = $matches[1][0]; // $ _2 = $matches[1][1]; // /vzor/i

11 Regulární výrazy II. // nahrad stringy jinymi stringy $string = "Project members are Karel and Josef"; $vzory[0] = "/Karel/"; $vzory[1] = "/Jan/"; $prepisy[0] = "Charlie"; $prepisy[1] = "Joseph"; print preg_replace($vzory, $prepisy, $string); // nahrad vsechny miry v cm mirami v mm $nahrazeno = preg_replace('/([0-9.]+)\s*cm/e', ' (\1 * 10) . " mm" ',      "Míry jsou 20 cm x 5 cm"); // prevod retezce na pole hodnot $pole = preg_split('/\s*[,;]\s*', "Josef Vyskočil, Pepa, Franta");

12 Pole I. slouží jako: klasická pole, asociativní pole (mapy, hash tabulky), zásobníky, fronty, seznamy, mnohorozměrná pole... $zver = array('kocka', 'pes'); $zver[0] = 'kocka'; $zver[] = 'kocka'; $zver[1] = 'pes'; $zver[] = 'pes'; $delka = count($zver); array_push($zver,'lev'); $lev = array_pop($zver); array_unshift($zver,'lev'); $lev = array_shift($zver); $zvireDne = array_rand($zver); $extrahovanePrvky = array_splice ($zver, 1, 1); kocka index (klíč) hodnota totéž 1 pes jako zásobník (LIFO) FIFO

13 Pole II. cat kocka dog pes citron 1 kiwi 1 agave 1 grep
$zver = array('cat'=>'kocka', 'dog'=>'pes'); $zver['cat'] = 'kocka'; $zver['dog'] = 'pes'; $poleInv = array_flip($pole); // hodnoty <-> klice $klice = array_keys($pole); // pole klicu 0..n $hodnoty = array_values($pole); // pole hodnot 0..n /* mnohorozměrné pole */ $a = array( array('citron','kiwi'), array('agave','grep') ); print $a[1][0]; // agave asoc. pole (uspořádaná hash tabulka) cat kocka dog pes citron 1 kiwi 1 agave 1 grep

14 Pole – některé funkce cat kocka dog pes
while (list($klic, $hodnota) = each($zver)) { print ("na indexu $klic je $hodnota\n"); } reset($zver); sort($zver); // sort dle hodnot ksort($zver); // sort dle klicu usort($zver, "srovnejObracene"); uksort... function srovnejObracene ($a, $b) { if ($a == $b) return 0; return ($a > $b) ? -1 : 1; array_walk($prispevky, "sprostaSlovaVen"); iterátor, vrátí 2-prvkové pole s klíčem a hodnotou aktuálního prvku a posune se na další prvek, nakonec vrátí False cat kocka dog pes hromadné přiřazení prvků pole do více nezávislých proměnných (konstrukt) aplikuje zadanou funkci na každý prvek pole

15 Řídící struktury – podmínky
Logické operátory: && || ! or and xor $link = mysql_connect("db.cz", "user", "pass") or die("Sory eror"); if ($a > $b) { print "a je větší než b"; }elseif ($a == $b) { print "a rovná se b"; }else { print "a je menší než b"; } $jmeno = "Karel"; $vaha = 129; echo $jmeno.( ($vaha>130)? " je" : " není" )." tlusťoch"; switch ($piti) { case "pivo": case "cola": print "$piti máme"; break; case "tonic": print "$piti došel"; break; default: print "$piti nevedeme";

16 Některé cykly /* vypiš kontakty */ $vypis = "<table>\n";
for ($i=0; $i<count($poleKontaktu); $i++) { $vypis .= "<tr><td>$poleKontaktu[$i]</td></tr>\n"; } $vypis = "\n</table>\n"; /* pošli zprávy ze seznamu Karlovi */ foreach ($poleZprav as $zprava) { "Dalsi zprava pro Karla", $zprava); /* vytiskni slovnik */ foreach ($slovnik as $anglicky => $cesky) { print "<tr> <td>$anglicky</td> <td>$cesky</td> </tr>\n"; /* stáhni určitý dokument a vypiš ho i s čísly řádků */ $poleRadek = file(' while ( list ($cis_radky, $radka) = each ($poleRadek) ) { echo "<b>Line $cis_radky:</b> ".htmlspecialchars($radka)."<br>\n";

17 Předdedinované proměnné I. Informace o spojení klient-server
Asociativní pole $_SERVER IP adresa klienta, vybrané hlavičky ze zpracovávaného požadavku, jméno běžícího skriptu, jméno serveru... /* info o klientovi, serveru a jejich spojení */ $_SERVER['REMOTE_ADDR'] $_SERVER['HTTP_USER_AGENT'] $_SERVER['PHP_SELF'] ... Vypište celé pole $_SERVER pomocí foreach cyklu do tabulky: REMOTE_ADDR HTTP_USER_AGENT Mozilla ...

18 Předdefinované proměnné II Čtení HTTP Parametrů
Demo /* 1. čtení HTTP parametrů z požadavků typu GET */ <a href=" Karla</a> nebo <form action=" method="GET"> <input type="text" name="search" value="Karel"> <input type="submit" name="submit" value="Najdi"> </form> print $_GET['search']; /* pro oba případy vytiskne Karel */ /* 1. čtení HTTP parametrů z požadavků typu POST */ <form action=" method="POST">...</form> $_POST['jmeno'] /* totéž jako $_GET, ale přistupuje k POST param. */ $_POST['heslo'] GET POST

19 Chybové výpisy error_reporting (E_ALL);
error_reporting (E_ALL ^ E_NOTICE); error_reporting (0); - potlaceni varovani pro zavolani funkce $poleRadek ('nexistujici_soubor'); if( ! $poleRadek) { die ("Problém s otevřením souboru: '$php_errormsg'"); }

20 Inkluze jiných souborů
require "common.php"; require $soubor; include "specialni_nastaveni.php"; include $soubor; $pocet_bytu = readfile($jmeno); vloží místo sebe obsah požadovaného souboru a provede jej – tj. scope při provádění požadovaného souboru je stejný jako na volajícím místě. pokud se soubor nenajde, pokračuje se dál (ne tak require) obrátí obsah souboru na výstup (neinterpretuje)

21 Tisk hlaviček do HTTP odpovědi
HTTP/ OK Date: Tue, 21 Oct :11:07 GMT Server: Apache/ (Win32) PHP/4.3.0 Last-Modified: Mon, 04 Aug :48:54 GMT Content-Length: 1969 Content-Type: text/html; charset=iso [CRLF] <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html><head>... headery je třeba vypsat před začátkem těla ...! (tj. před prvními printy a ne-PHP daty) header ("Content-type: application/pdf"); header ("Content-Disposition: attachment; filename=downloaded.pdf"); // 1. speciální význam: změna typu odpovědi header("HTTP/ Not Found"); header("HTTP/ Unauthorized"); // 2. speciální význam: přesměrování header("Location: exit; lze opětovně měnit dokud nejsou hlavičky odeslány automaticky změní typ odpovědi na HTTP/ Moved Temporarily přesměrovací odpověď je obvykle bez těla (ale HTTP/1.1 doporučuje „jste přesměrováni...“)

22 Hlavičky – typická použití
dle HTTP 1.1 je potřeba absolutní URL Přesměrování header('Location: .dirname($_SERVER['PHP_SELF']).'/login.php'); exit; Zabránění cachování (na prohlížeči nebo po cestě) header("Expires: Mon, 26 Jul :00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); Nastavení Content-type header("Content-type: image/png"); readfile("obrazek.png"); header("Content-type: text/html; encoding=iso "); Cookies (viz dále) Autentizace (předposlední seminář)

23 Funkce a scope proměnných
function Soucet($a, $b) { return $a + $b; } $tips = ('Tip 1','Tip 2'); function TipOfTheDay1() { global $tips; return array_rand($tips); function TipOfTheDay2() { return array_rand($GLOBALS['tips']); print 'Tip dne: <em>'.TipOfTheDay1().'</em>'; function Counter() { static $pocet = 0; return ++$pocet; $i = Counter(); // 1 $j = Counter(); // 2 pole $tips je definováno v globálním variable scopu lokální scope unvitř funkcí Pokud chci použít proměnnou z globálního scopu, musím ji importovat pomocí global (!) nebo použít pole $GLOBALS, viditelné všude (pozor, bez _ ). statická proměnná, inicializována jen 1x, zachovává hodnotu mezi voláními

24 Objekty I. class Clovek { var $jmeno;
var $prijmeni; function Clovek($jm = 'neznamy', $pr = 'clovek') { $this->jmeno = $jm; $this->prijmeni = $pr; } function Jmenovka() { return $this->jmeno.' <em>'.$this->prijmeni.'</em>'; $kontakt = new Clovek('Smil','Flek'); $kontakt->prijmeni = 'Flek z Nohavic'; print $kontakt->Jmenovka(); // Smil Flek z Nohavic class Rytir extends Clovek { var $zbran; function Rytir($jm = 'neznamy', $pr = 'rytir', $zb = 'mec') { $this->Clovek($jm, $pr); $this->zbran = $zb; implicitní hodnota pokud argument chybí

25 Objekty II. // volání statické metody (vázána na třídu, ne na instanci) // definice metody stejná, pouze nesmí používat atributy instance Rytir::VsechnyZbrane(); // mec, bidlo // volání metody předka v těle metody potomka: parent::Funkce(); // přímý předek Clovek::Funkce(); // konkrétní předek $kontakt = new Clovek('Smil','Flek z Nohavic'); $str = serialize($kontakt); ... // např. uložit do souboru, poslat přes síť... $opetKontakt = unserialize($str);

26 Reference Stejný obsah pod různými jmény, aliasy (nejde o pointery)
$a = 'Maso'; $b = & $a; /* Předání parametru referencí: 1. nevytvoří se kopie 2. funkce může originál měnit */ function NactiObrazek($filename, &$data, &$errMsg) { ... /* pokus se otevřít filename, načíst data, a podle chyby naplň $errMsg a $errCode */ return $errCode; } $chyba = ''; $binData = ''; if( NactiObrazek('Mapa280_450.png', $binData, $chyba) != OK ) { die ("Nepovedlo se načíst obrázek: $chyba"); header ("Content-type: application/png"); print $binData; proměnné $a a $b obsahují fyzicky stejná data, není mezi nimi rozdíl ($b neukazuje na $a, jsou to aliasy pro totéž) předání hodnotou (kopie) předání referencí

27 Reference II // Vrácení reference z funkce – typicky pro výběr z N objektů, kde vybraný objekt budu chtít v místě editovat function & getNextMessageForSpellCheck ($kriteria) { ... // najdi dalsi prispevek dle kriterii return $nextMessage; } $message = & getNextMessageForSpellCheck('anglicke prispevky'); spellCheckAndRepair($message);

28 Předdefinované proměnné III Session proměnné (1)
Proměnné skladovány mezi HTTP transakcemi stejného klienta session_start(); ... $_SESSION['uzivatel'] = new Uzivatel($userName, $preferences); $_SESSION['nakupniKosik'] = new Array('klobasa'=>5, 'horcice'=>1); vytvoří session (pokud neexistuje) nebo obnoví obsah session proměnných session_start(); $kosik = & $_SESSION['nakupniKosik']; $kosik['chleba'] = 1; server klient 1 SID 1 session klienta 1 SID 1 session klienta 2 SID 1 ...

29 Session proměnné (2) PHP je obnovuje pro každého klienta zvlášť
/* obnovení session proměnných z konce zpracování minulého požadavku, pokud je klient nový, založí novou session */ session_start(); if(!isset($_SESSION["PocetPristupu"])) { /* poprvé */ $_SESSION["PocetPristupu"]=1; }else { $_SESSION["PocetPristupu"]++; } print "Počet přístupů během session: ".$_SESSION["PocetPristupu"]; zapisuje do hlaviček, třeba zavolat před začátkem těla @ potlačí varovný notice při prvním přístupu k poli $_SESSION, kdy čteme prvek s indexem "PocetPristupu", který ještě není definován. V takovém případě dostáváme NULL, který je pro numerický operátor ++ castován na 0. @$_SESSION["PocetPristupu"]++; // totéž jako

30 Session proměnné (3) session_start();
// session proměnné jsou klasické prvky pole $_SESSION $_SESSION["kosik"] = array("parek"=>2, "pivo"=>7); $_SESSION["kosik"]["chleba"] = 1; $_SESSION["kosik"]["parek"]++; /* přehlednější a pohodlnější – použít lokální proměnnou s referencí na často používanou session proměnnou */ $kosik = & $_SESSION["kosik"]; $kosik["chleba"] = 1; $kosik["parek"]++; // odstranění jedné session proměnné unset($_SESSION["kosik"]); // odstranění všech session proměnných $_SESSION = array(); // zničení session (úložiště) a zneplatnění klientova klíče (SID) session_destroy();

31 Předdefinované proměnné IV Cookies (1)
Předvyplnění uživ. jména, čas poslední návštěvy, nastavení... <form action=" method="POST"> Login name: <input type="text" name="user" value="Karel"> <input type="submit" name="submit" value="Login"> </form> /* cookies – informace v podobe (klic-hodnota), kterou klient posila serveru v hlavicce Cookie: jako soucast pozadavku. Cookie u klienta ulozil server behem minulych transakci. */ setcookie ("last_login_from_here", "Karel", time()+3600*24*365); /* Za týden, ve stejné aplikaci, při generování přihlašovacího formuláře předvyplníme naposledy zadané uživatelské jméno: print 'Login name: <input type="text" name="user" value="' . $_COOKIE['last_login_from_here'] . '" size="10">';

32 Cookies (2) Server uloží u klienta dvojice proměnná=>hodnota
setcookie ("ForumAutor", "Karel", time()+3600*24*365); setcookie ("Navstev", 20, time()+3600, "/~labsky/",".vse.cz"); // smazání setcookie ("TestCookie", "", time() ); ... (HTTP odpověd) Set-Cookie: ForumAutor=Karel; expires=Wed, 12-Nov-04 00:09:30 GMT Set-Cookie: Navstev=20; expires=Wed, 12-Nov-03 00:09:30 GMT; path=/~labsky/; domain=.vse.cz ... expires=0 (do zavření okna prohlížeče) nazýváno dočasné, session cookie Klient posílá cookie stejnému serveru v každém požadavku zpět ... (HTTP požadavek) Cookie: ForumAutor=Karel; Navstev=20 ... echo $_COOKIE["ForumAutor"]." tu byl ".$_COOKIE["Navstev"]."-krat.";

33 Předdefinované proměnné V
„Superglobals“ – pokračování /* seznam vsech globalnich promennych dle jejich jmen */ $GLOBALS['jmeno_glob_promenne'] /* info o prostredi na serveru */ $_ENV['promenna_prostredi'] /* info o uploadovaných souborech */ $_FILES['name_prvku_formulare']['jmeno_souboru'] $_FILES['name_prvku_formulare']['size'] $_FILES['name_prvku_formulare']['tmp_jmeno'] ... Další předdefinované proměné $php_errormsg /* popis poslední chyby skriptu (např. po neúspěšném pokusu o otevření souboru) */

34 Další datové typy Resource – reference na externí zdroj:
$link = mysql_connect("localhost", "mysql_user", "mysql_password"); ... mysql_close($link); NULL – datový typ o 1 možné hodnotě NULL ;) Všechny proměnné, kterým zatím nebyla přiřazena hodnota, jsou NULL, včetně ve skriptu zatím nezmíněných proměnných // NULL je typická negativní návratová hodnota $a = array(); $prvek = array_shift($a); // prvni prvek neni zadny, vrati se NULL // test na NULL a obrácený test: is_null($x); isset($x); // negeneruje notice když $x neexistuje // NULL proměnné lze vyrobit ručně např. takto: $a = NULL; $b = 5; unset($b);

35 Práce s datovými typy var_dump($x); print_r($x);
is_bool() is_numeric() is_float() is_int() is_string() is_object() is_array() is_integer() is_real() is_null() $pi = 3.14; print gettype($pi); /* vrátí jednu ze stringových hodnot: boolean integer double string array object resource */ print "15 jablek" + "10 hrušek"; // automatický cast – 25 (!) print (int) "15 jablek"; // ruční cast $a = "15 jablek"; settype($a, "integer"); // změna typu samotné proměnné $a = 0; if($a == False) { print "a lze přecastovat na false"; } if($a === False) { print "a je boolean false"; platí, int přecastován na bool (0  False) neplatí, operátor = = = vyžaduje i stejný datový typ (neprovádí cast jako u operátoru = =)

36 Některé důležité funkce
(viz manuál) String, Array, Variable Functions Date and Time Functions Regular Expression Functions Filesystem Functions Execution Operators URL Functions Iconv Functions MySQL functions

37 Stav serverové aplikace
HTTP je bezestavový protokol HTTP server nezná pojem „session s klientem X“ server pouze posílá HTTP odpověď na HTTP požadavek nějakého klienta Často potřeba držet stav aplikace pro klienty - kde? jak? příklady: nákupní košík, doba trvání session, přihlašovací informace (jestli je klient správně přihlášen, jméno), …

38 Udržení stavu na klientovi
„stránka s přesným časem“ – žádný stav „interface k databázi bez přihlášení“ stav = klientova pozice v aplikaci např. „programy kin – kino Aero – program na dnešek“ pozice v aplikaci je dána (vygenerovanými) HTML odkazy na aktuální stránce u klienta, na které může uživatel přejít: stav si ve své HTML stránce nese každý klient, server pouze standardně reaguje na požadavky obsahující správná URL (včetně GET parametrů). jinak lze držet stav u klienta zejména pomocí: skrytých formulářových polí, jejichž obsah se při submitu dostává zpět na server jako GET nebo POST parametry, cookies. <a href="kino.php?idKina=Aero">Zpět</a> <a href="program.php?idKina=Aero&datum=131104">Zítřejší program</a>

39 Udržení stavu na serveru
„interface k databázi s přihlášením“ přizpůsobení vzhledu rozhraní uživateli, zabezpečení... stav pozice v aplikaci (stále implicitně v HTML kódu na klientovi) informace o uživateli jméno, příjmení, obsah nákupního košíku, čas posledního přihlášení, čas od posledního HTTP požadavku... typicky velké množství informací, nevhodné nebo nemožné skladovat v GET parametrech, cookies nebe ve skrytých formulářových polích.  skladovat na serveru. nutná jednoznačná identifikace klienta (na serveru skladujeme data pro různé klienty) typicky se generuje pro každého klienta při prvním požadavku klíč, kterým se klient při všech dalších požadavcích představuje přesně toto zajišťují php session proměnné nebo lze zajistit vlastními silami

40 Identifikace klienta pomocí php session
Klient pošle požadavek (ev. může poslat username a heslo, např. jako POST parametry) Server (ev. posoudí správnost username a hesla) a vytvoří pro klienta jedinečný klíč – session identifikátor (SID) Server alokuje prostor pro skladování dat (session proměnných) pro session SID – např. dočasný soubor jménem SID.tmp SID je spolu s odpovědí poslán klientovi, např. jako dočasný cookie Klient nadále v každém požadavku posílá SID cookie (do doby než je prohlížeč vypnut) Server při zpracování každého požadavku od některého přihlášeného klienta obdrží jeho SID. Ten porovná s existujícími SIDy a v případě shody je klient identifikován (php obnoví session proměnné pro zpracování požadavku) Zničení session ze strany klienta – klient zapomene dočasný SID cookie (zavření prohlížeče) – server pak po timeoutu provede smazání session dat u sebe. Zničení session ze strany serveru – session_destroy() – smaže session data na serveru – při příštím požadavku téhož klienta nebude session nalezena.

41 Články o php a stáhnutelné aplikace
Manuál PHP Články o php a stáhnutelné aplikace ... a materiály na


Stáhnout ppt "Úvod do PHP Labsky@vse.cz IZI 228."

Podobné prezentace


Reklamy Google