Oskar Skibski, grupa nr 3, sala 2043

Bazy danych 2015/16

Laboratorium 12 (11.01.2016) - PHP / PODSTAWY

Na dzisiejszych zajęciach poznamy podstawy PHP. Skrót PHP rozwija się PHP: Hypertext Preprocessor, służy on bowiem do generowania dynamicznie stron HTML. Wszystko co trzeba wiedzieć o PHP zawarte jest na domowej stronie PHP.net.

Jeżeli ktoś chce używać Javy w projekcie zaliczeniowym, odsyłam do notatek Pana Zbigniewa Jurkiewicza o JDBC.

Prosty przykład

Ściągnij plik hello.php i wrzuć do katalogu public_html na swoim koncie students. Odpal stronę http://students.mimuw.edu.pl/~xy123456/hello.php podmieniając ciąg xy123456 na swój identyfikator. Naszym oczom powinna ukazać się strona HTML (pokaż źródło) z nagłówkiem Hello World. Nie byłoby w niej nic szczególnego, gdyby nie to, że napis wypisaliśmy za pomocą komendy echo w PHP. Więcej o niej dowiemy się za chwilę.

W taki sam sposób będziemy odpalać wszystkie skrypty PHP (są one przetwarzane przez serwer), dlatego kolejne zadania warto wykonywać tworząc skrypty w katalogu publicznym public_html.

Zmienne i typy

Pierwszą najbardziej widoczną różnicą pomiędzy PHP a innymi znanymi nam językami programowania jest poprzedzanie każdej zmiennej znakiem dolara:

<?php
$liczba = 5 + 10;
echo $liczba;
?>

Zmiennych nie trzeba deklarować, nie trzeba też określać im typów. Ponadto typ zmiennych może się zmieniać w czasie:

<?php
$nibyTablica = array("jestem", "tablica", "i", "jestem", "z tego", "dumna");
$nibyTablica[3] = "NIE jestem"; // numeracja jest od zera
print_r($nibyTablica);
echo "<br>";
$nibyTablica = "HAHA! Jestes napisem!";
print_r($nibyTablica);
?>

Typy w PHP są bardzo "elastyczne", tzn. liczba umie sama rzutować się na napis albo napis na wartość logiczną. Nie jest jednak prawdą, że zmienne nie mają typów.

Napisy łączymy za pomocą kropki. Ponadto w środku napisu umieszczonego w cudzysłowach można umieszczać zmienne - albo po prostu pisząc ich nazwę albo umieszcając je w nawiasach klamrowych. Istnieje cały szereg funkcji do obróbki napisów, przystępną lista znajduje się tutaj: lista funkcji napisowych.

<?php
$nazwa = "jakis";
$rozszerzenie = "txt";
$napis = "{$nazwa}_plik.$rozszerzenie";
echo $napis."<br>";
echo substr($napis, 6, 4)."<br>";
echo strrchr($napis, '.')."<br>";
?>

Kolejną przydatną rzeczą są tablicę asocjacyjne w których kluczem może być dowolny napis:

<?php
$peter = array('imie' => 'Peter', 'nazwisko' => 'Griffin', 'wiek' => 38);
$peter['wiek'] += 5;
$peter['imiePsa'] = 'Brian';
print_r($peter);

$osoby['FG'] = $peter;
echo $osoby['FG']['wiek'];
?>

Jak widzimy tabele w PHP są bardzo wygodne - mogą zawierać dowolne typy danych, możemy swobodnie do tabeli dodawać elementy nie martwiąc się o wcześniejsze deklaracje wielkości.

Składnia

Podstawowe konstrukcje znane z innych języków - if/else, while i for istnieją także w PHP.

<?php
$i = 0;
while ($i < 10) {
  if ($i > 7) $i--;
  else $i += 3;
  
  echo "Teraz i jest równe $i. <br>";
}
?>

Do iterowania po tablicach asocjacyjnych (ale nie tylko) używa się pętli foreach($array as $key => $value). W składni tej można pominąć $key => (jeżeli klucz nie jest potrzebny) albo dodać znak '&' przed $value - wówczas modyfikując zmienną $value modyfikujemy także odpowiednie pole tabeli.

<?php
$odglosyPaszcza = array('kot' => 'Miau', 'pies' => 'Hau', 'kogut' => 'Kukuryku');
foreach($odglosyPaszcza as $zwierze => $odglos) {
  echo "$zwierze robi \"{$odglos}!\", <br>";
}
?>

Definiując funkcję nie musimy podawać typów argumentów ani typu zwracanej przez funkcję danej:

<?php
function silnia($n) {
  if ($n == 0) return 1;
  else return $n * silnia($n-1);
}
echo "silnia z liczby 5 jest równa ".silnia(5);
?>

W powyższych przykładach często używaliśmy komend echo oraz print_r. Echo jest najczęściej używaną funkcją do wypisywania na wyjście, print_r jest bardzo przydatne podczas debugowania kodu.

Nie jest to jednak jedyny sposób tworzenia wyjściowej strony HTML. Jak widzieliśmy już w przykładzie z Hello World kod PHP może być mieszany z kodem HTML - w dowolnym miejscu strony HTML w którym potrzebujemy dynamicznych danych znacznikiem <?php możemy włączyć przetwarzanie PHP i znacznikiem ?> wyłączyć.

W PHP można także tworzyć klasy, nie będziemy jednak tego zagadnienia omawiać.

Zadanie 1.

Zadanie o krasnoludkach. Postaraj się zapisać je jak najkrócej.

  • Napisz funkcję która stworzy tablicę zawierającą imiona siedmiu krasnoludków. Dla ułatwienia, jak rasowi informatycy, przyjmijmy że nazywały się krasnoludek1, krasnoludek2, ..., krasnoludek7.
  • Korzystając z powyższej funkcji stwórz zmienną $imionaKrasnoludkow na której wypisane będą imiona krasnoludków rozdzielone przecinkiem i spacją, a po ostatnim krasnoludku będzie kropka.
  • W powyższym napisie znajdź imię pierwszego krasnoludka (czyli pierwszy wyraz).
  • Korzystając z funkcji z pierwszego podpunktu stwórz tabelę asocjacyjną zawierającą wysokości krasnoludków w centymetrach. Przyjmijmy, że krasnoludki wpisały sobie 194cm wysokości. Zmniejsz wysokość krasnoludka o imieniu 'krasnoludek6' o 20.
  • Napisz pętlę, która zamieni wysokości krasnoludków na ich reszty z dzielenia przez 100 i wypisze je w formacie 'IMIĘ_KRASNOLUDKA: wysokość krasnoludka'.

Łączenie z bazą

Wszystkie funkcje potrzebne do łączenia się z bazą danych Oracle znajdują się tu: funkcje oci8. Przykład łączenia wygląda tak:

$conn = oci_connect("scott", "tiger", null, 'AL32UTF8'); // trzeba zmienic na wlasne dane
if (!$conn) {
  $e = oci_error();
  echo "Koniec swiata, baza danych nie dziala! ({$e['message']})";
  exit;
}

// pobieranie calej tabeli na raz
$r = oci_parse($conn, "SELECT * FROM emp");
oci_execute($r);
$rowCount = oci_fetch_all($r, $all, null, null, OCI_FETCHSTATEMENT_BY_ROW + OCI_ASSOC);

Operacja oci_connect łączy z bazą danych i zwraca identyfikator połączenia. Do późniejszych operacji będziemy musieli go zawsze podawać. oci_parse przygotowuje zapytanie do wykonania i zwraca do niego "uchwyt". Dopiero komenda oci_execute wykonuje dane zapytanie. Jeżeli naszym zapytaniem był SELECT, do wyniku odnosimy się zwykle za pomocą oci_fetch_all, albo oci_fetch_row. Pierwsza komenda -- oci_fetch_all -- pobiera od razu wszystkie wiersze i umieszcza je w tabeli $all. Podanie flagi OCI_FETCHSTATEMENET_BY_ROW powoduje, że pod kolejnymi numerami ($all[0], $all[1],...) znajdują się kolejne wiersze z zapytania. Każdy wiersz jest tablicą, jednak od kolejnej flagi zależy jaką -- jeżeli podamy flagę OCI_ASSOC to będzie to tablica asocjacyjna w której klucze to nazwy kolumn, np. $all[0]['EMPNO'] to numer pracownika. Jeżeli podamy flagę OCI_NUM, to kolumny będą pod kolejnymi numerami -- 0,1,.... Flagi te można łączyć. Alternatywnie możemy przeglądać wyniki wiersz po wierszu za pomocą oci_fetch_array (lub oci_fetch_assoc czy oci_fetch_row).

Wypisanie tablicy $all z powyższego przykładu da taki efekt:

Array
(
    [0] => Array
        (
            [EMPNO] => 7369
            [ENAME] => SMITH
            [JOB] => CLERK
            [MGR] => 7902
            [HIREDATE] => 17-DEC-80
            [SAL] => 800
            [COMM] => 
            [DEPTNO] => 20
        )

    [1] => Array
        (
            [EMPNO] => 7499
            [ENAME] => ALLEN
            [JOB] => SALESMAN
            [MGR] => 7698
            [HIREDATE] => 20-FEB-81
            [SAL] => 1600
            [COMM] => 300
            [DEPTNO] => 30
        )
	...
)

Przykładowy skrypt łączacy się z bazą danych i pobierający dane: laczenie.php.

Zadanie 2.

Stwórz skrypt PHP który wygeneruje stronę HTML z tabelą w której będą dane wszystkich pracowników tabeli EMP. Przetłumacz stanowiska na język polski, jeżeli pracownik nie ma prowizji (comm) wypisz w tej komórce myślnik.

Zmienne przedefiniowane w PHP

Każdy skrypt PHP uruchomiony na serwerze posiada pewne przedefiniowane zmienne superglobalne. Są one dostępne w dowolnym miejscu skryptu. Są to:

Parametry $_GET

Najprostszą metodą przekazywania argumentów do skryptu PHP sa parametry GET. Przekazanie parametrów polega na wywołaniu adresu skryptu z dodatkowym ciągiem odpowiadającym za zmienne GET; na przykład w skrypcie kategorie.php wywołanym poprzez link kategorie.php?idkategorii=7 wartość $_GET['idkategorii'] będzie równa 7.

<?php
$parametry = array('jedenklucz' => 2, 'drugi' => 'tralala', 'trzeci' => 'a b c');
$nazwaSkryptu = "index.php";

$url = "{$nazwaSkryptu}?".http_build_query($parametry);
echo "<a href='{$url}'>link</a>";
// url = index.php?jedenklucz=2&drugi=tralala&trzeci=a+b+c
?>

Jak widzimy po nazwie skryptu musi znajdować się znak zapytania, a następnie rozdzielone & pary klucz=wartosc, przy czym wartość musi być zakodowana (funkcja urlencode). Przykładowo spacja kodowana jest za pomocą plusa.

Przekazywanie parametrów metodą GET jest bardzo często wykorzystywane do tworzenia nawigacji po stronie lub w formularzach którymi wysyłamy niedużo mało ważnych informacji (na przykład przy wyszukiwaniu). Jeżeli potrzebujemy przesłać dużo danych lub zależy nam na tym aby nie były one widoczne (na przykład przy logowaniu) używamy metody POST.

Formularze

Oczywiście nikt nie wymaga od użytkownika aby zmieniał adres url. Za interakcję między użytkownikiem a skryptem odpowiadają formularze. Pod podanym linkiem możemy obejrzeć podstawowe elementy jakie możemy używać w formularzu.

W atrybutach formularza ustalamy adres skryptu obsługującego formularz ("action") oraz metodę obsługującą ten formularz ("method" - GET lub POST).

Każdy element (input) formularza powinien mieć nazwę. Po stronie skryptu stworzy ona odpowiednie pole w tabeli $_GET lub $_POST. Sama wartość zależy od rodzaju pola - w przypadku <input type='text'> będzie to wartość wpisana przez użytkownika, w przypadku <input type='checkbox'> będzie to tablica zaznaczonych wartości lub nic kiedy nic nie zostanie zaznaczone.

Przykładowy plik z formularzem i obsługą: formularz.php.

Zadanie 3.

Znajdź w zmiennych globalnych zmienną odpowiadającą za adres IP łączącego się użytkownika oraz pełen adres dyskowy do pliku PHP który obsługuje żądanie i wypisz je na ekran.

Zadanie 4.

Stwórz formularz do dodawania pracownika do tabeli emp. Pomiń datę zatrudnienia (będzie ustawiona na dzisiaj), niech szefa wybiera się z listy rozwijalnej, a departament za pomocą radio buttonów.

SQL Injection

SQL Injection jest dobrze opisane na wikipedii: SQL Injection.

Jak można zapobiec SQL Injection? W innych językach musimy bardzo dbać o "escapowanie" znaków. Używając PHP z Oraclową bazą danych mamy do tego funkcję oci_bind_by_name.

<?php
$r = oci_parse($conn, "SELECT * FROM emp WHERE ename = :ename");
oci_bind_by_name($r, ":ename", $_GET['ename']);
oci_execute($r);
?>
Zadanie 5.

Dodaj obsługę formularza z powyższego zadania. Przetestuj działanie.

Transakcje

Domyślnie wszystkie nasze operacje na bazie danych wykonywane w PHP są automatycznie commitowane. Nie musi tak jednak być. Jeżeli chcemy wykonać szereg operacji atomowo, możemy funkcji oci_execute jako drugi parametr podać flagę OCI_NO_AUTO_COMMIT. Wówczas wszystkie akcje (aż do pierwszego commita, funkcją oci_commit lub oci_execute bez tej flagi) będą traktowane atomowo (wykonają się wszystkie lub żadne). Przykładowo jeżeli w trakcie transkacji zerwie nam się połączenie z bazą danych nie dostaniemy niespójnej sytuacji w bazie - nasze zmiany zostaną wycofane. Wycofanie zmian możemy wywołać także ręcznie komendą oci_rollback.

<?php
$conn = oci_connect("scott", "tiger");

$r = oci_parse($conn, "INSERT INTO dept VALUES (50, 'IT', 'WARSAW')");
oci_execute($r, OCI_NO_AUTO_COMMIT);

$r = oci_parse($conn, "INSERT INTO emp (empno, ename) VALUES (9000, 'FRANEK')");
oci_execute($r, OCI_NO_AUTO_COMMIT);

oci_commit($conn);
?>

W powyższej sytuacji możemy mieć pewność, że pusty departament na pewno nie powstanie.


Oskar Skibski (oski@mimuw.edu.pl), Wydział Matematyki, Informatyki i Mechaniki