PHP kurs 2


Celem tego kursu nie jest przekazanie wiedzy encyklopedycznej na temat PHP, ale praktycznych porad, które każdemu pozwolą na tworzenie ciekawych i funkcjonalnych aplikacji. Dlatego na początku powiedzmy tylko, że PHP (akronim rekursywny „PHP: Hypertext Preprocessor”) to język skryptowy wykonywany po stronie serwera (przez co oferuje większe możliwości niż np. JavaScript) o składni podobnej do C/C++, za pomocą którego tworzymy zaawansowane programy wykorzystywane głównie na stronach WWW.

Atutem PHP jest zaś to, że programista nie musi się tutaj przejmować np. lokowaniem pamięci pod zmienne czy konwersją typu. Dodatkowo możliwości PHP można rozszerzyć, instalując na serwerze gotowe moduły lub pisząc własne. Obecnie rozwijana jest wersja 5.x, a rozwój wersji 4.xx (długo współistniejącej z nowszą „piątką”) zatrzymał się w sierpniu 2007 roku na edycji 4.32. Obie wersje różnią się znacznie pod względem zaawansowanych funkcji obsługi baz danych, plików XML czy klas, które zostały w PHP5 przepisane od nowa, przez co są dużo prostsze i łatwiejsze w użyciu. Niemniej większość skryptów pisanych w PHP4 może pokazywać błędy w PHP5, dlatego starsza edycja była prawie przez dwa lata podtrzymywana i rozwijana.



Użycie PHP jest bardzo łatwe i sprowadza się do wstawienia kodu bezpośrednio do pliku ze stroną WWW. Jednakże pliki zawierające skrypty PHP powinny mieć najczęściej rozszerzenie PHP, chyba że konfiguracja serwera wskazuje inaczej. Jeśli serwer napotka taki plik, to wysyła go do parsera PHP, który przetwarza skrypt w nim zawarty i odsyła gotowy kod HTML. Skrypty można wstawiać na cztery sposoby:


0x01 graphic


Teraz możemy już napisać pierwszy skrypt w PHP. Jego postać jest następująca:

<?php echo "Hello World!"; ?>

Plik zapisujemy jako pierwszy.php i umieszczamy na naszym koncie WWW (koniecznie z obsługą PHP). Otwierając go w przeglądarce, zobaczymy tylko tekst Hello World!. A teraz mała analiza:

1. elementy <?php oraz ?> to odpowiednio otwarcie i zakończenie bloku kodu PHP,
2. element echo „Hello World!”; to niby-funkcja zwracająca do przeglądarki podany w cudzysłowie tekst.

Procedury echo można użyć na dwa sposoby: echo ("tekst”); oraz echo "tekst”;, przy czym różnią się one tylko sposobem wyświetlania tekstu pochodzących z wielu źródeł. W wypadku echo („tekst”); treści możemy wstawić, oddzielając je przecinkiem, np. echo („tekst1”, „tekst2”, „tekst3”);, a w echo „tekst”;, łącząc je kropkami, np. echo „tekst1”.”tekst2”.”tekst3”;. Oba te sposoby dają taki sam wynik, czyli:tekst1tekst2tekst3.

Jeżeli chcemy oddzielić teksty spacjami, należy je dodać do funkcji echo, na przykład tak:

echo "tekst1 "."tekst2 "."tekst3"

Pomyłki zdarzają się każdemu. Jeśli zrobimy jakąś literówkę lub poważniejszy błąd składniowy, parser poinformuje nas o tym następującym komunikatem: parse error: parse error, unexpected T_ECHO, expecting ',' or ';' in /var/www/kurs_php/kurs.php on line 3.

W informacji tej zawierają się informacje o rodzaju błędu oraz pliku i linii kodu, w którym się on znalazł.
Prostą i skuteczną metodą „debugowania” jest wstawianie w kluczowych miejscach skryptu procedury echo wyświetlającej wartości ważnych zmiennych. W ten sposób można się łatwo zorientować, czy skrypt w określonym miejscu zawiera dane, których oczekiwaliśmy. Przed pisaniem skryptów dla danego
serwera warto się też zapoznać z funkcją phpinfo();. Pokazuje ona pełną konfigurację parsera PHP na serwerze, listę zmiennych środowiskowych, listę zainstalowanych modułów oraz wiele innych potrzebnych danych. Wystarczy wpisać w pustym pliku <? phpinfo(); ?> i wysłać ten plik na serwer.

Każdą instrukcję w PHP (tak jak w wielu innych językach) należy zakończyć znakiem ; (średnik). Każde zakończenie instrukcji bez średnika spowoduje, że skrypt nawet nie zacznie być interpretowany przez parser. Od tej reguły jest tylko jeden wyjątek - średnikiem nie trzeba kończyć ostatniej instrukcji w bloku kodu PHP:

<?
echo "Tekst pierwszy";
echo "tekst drugi";
echo "tekst trzeci i ostatni"
?>

Komentarze to specjalnie oznaczone fragmenty kodu, które są omijane przez parser. Służą do różnych celów - można w nich umieszczać np. uwagi do danego fragmentu skryptu, znaki copyright czy uwagi do innych członków grupy roboczej. Istnieją trzy sposoby oznaczania komentarzy:

Na razie tylko wyświetlaliśmy tekst za pomocą procedury echo. Teraz PHP pokaże, na co go naprawdę stać, czyli przyszedł czas na obliczenia. Porcje danych są przechowywane w zmiennych. Są to lokowane na czas wykonywania skryptu fragmenty pamięci, w których można przechowywać praktycznie dowolne dane.

Zmienną w PHP oznaczamy znakiem $ (dolar). Rozróżniana jest wielkość znaków, a więc $zmienna, $ZmIeNNa i $ZMIENNA nie przechowują tych samych danych. Jest jeszcze jedna zasada nazywania zmiennych - nazwa musi się zaczynać od litery (wielkiej lub małej) lub znaku _ (podkreślenie). Dalej mogą wystąpić także cyfry. Oto kilka przykładowych nazw zmiennych:

Zgodnie z przyjętą konwencją, jeśli w nazwie zmiennej ma wystąpić kilka wyrazów, nie powinno się ich oddzielać znakiem podkreślenia ('_'), ale napisać każdy z nich (oprócz pierwszego) wielką literą (np. $liczbaWszystkichWpisów). W PHP nie musimy się martwić strukturami używanymi w innych językach programowania, takimi jak np. deklarowanie zmiennej czy jej typu. Wystarczy napisać po prostu:

<?
$text = "Ala ma kota, kot ma Alę";
$cyferka = 15;
?>

I już tworzona jest nowa zmienna (o ile taka już nie istnieje) i zapisywana jest jej wartość.

Zmienna jest strukturą, która (o ile przechowuje liczby) może być traktowana właśnie jak liczba. PHP pozwala na wykonywanie na nich standardowych działań dodawania, odejmowania, mnożenia i dzielenia. Dla przykładu:

<?
$a = 15;
$b = 5;
$c = $a+$b;
$d = $a-$b;
$e = $a*$b;
$f = $a/$b;
$g = $a%$b;
?>

W tym przykładzie wartość zmiennej $c wyniesie 20 (suma), $d = 10 (różnica), $e = 75 (mnożenie) i $f = 3 (dzielenie). Wytłumaczenia wymaga znak '%' (modulo) - jest to operator reszty z dzielenia. W tym wypadku $g wyniesie 0, bo reszta z dzielenia 15 przez 3 wynosi właśnie zero.

W PHP istnieje pięć podstawowych typów zmiennych:

PHP, kiedy zachodzi taka potrzeba, automatycznie zamienia rodzaje zmiennych. Zdarza się jednak, że nie jesteśmy zadowoleni z wyniku takiej zamiany. Pozostaje wtedy jedno wyjście - rzutowanie. W PHP mamy do czynienia z dwoma typami tej procedury: cast oraz settype(). Pierwszy służy do jednorazowej zamiany typu zmiennej. Wystarczy dopisać przed nazwą zmiennej typ w nawiasie, aby uzyskać pożądany wynik. Dla przykładu:

<?
$liczbaRzeczywista = (real) $liczbaCalkowita;
?>

W tym wypadku dozwolone typy rzutowań to:

Rzutowanie cast stosuje się także w zabezpieczaniu skryptu przed możliwymi atakami. Więcej o tym w rozdziale „Bezpieczeństwo skryptów”.

Druga możliwość to użycie funkcji settype(), która pobiera dwa argumenty. Pierwszy to nazwa zmiennej, której typ chcemy ustalić, a drugi to docelowy typ zmiennej. (dozwolone to „integer”, „double”, „string”, „array” i „object”). Dla przykładu:

<?
$liczba = 10.3;
settype($liczba, "integer");
echo $liczba;
?>

Po wykonaniu tego przykładu pokaże się liczba 10, ponieważ liczba 10,3 została zamieniona na liczbę całkowitą.

W każdym skrypcie możemy użyć kilku zdefiniowanych i gotowych do użycia zmiennych. Ich wartości zależą od ustawień serwera. Oto najważniejsze z nich:

Powyższe zmienne wyświetlamy w następujący sposób:

<?
echo $_SERVER['nazwa_zmiennej_predefiniowanej'] ;
?>

W PHP istnieją też obiekty, które nazywamy „stałymi” - są to rodzaje zmiennych, które przechowują pewne określone dane i nie można zmienić ich wartości w trakcie wykonywania skryptu. W zapisie różnią się one od tradycyjnych zmiennych tym, że nie mają znaku dolara na początku. PHP tworzy automatycznie kilka stałych gotowych do wykorzystania w skryptach:

Własne stałe można definiować funkcją define(), która pobiera dwa argumenty - nazwę stałej i jej wartość. Dla przykładu:

<?
define(HELLO, "Witam!")
echo HELLO; //Wyświetli: Witam!
?>

Operatory to specjalne znaki, pozwalające na wykonywanie operacji na zmiennych. Pomagają one też wyłapywać błędy w kodzie, jak również porównywać zmienne.



Poznaliśmy je już w poprzednim rozdziale. Są to: +, -, *, / i %, czyli odpowiednio: dodawanie, odejmowanie, mnożenie, dzielenie i dzielenie modulo (zwraca resztę z dzielenia).

Operator przypisania wygląda jak znak 'równa się' (=). Jest to przypisanie wartości wyrażenia z jego prawej strony (wyrażenie, czyli zmienna lub funkcja zwracająca jakąś wartość) do zmiennej z jego lewej strony. Dla przykładu:

<?
$a = 5; //przypisuje liczbę 5 do zmiennej $a
?>

Służą do porównywania wartości stojących po ich lewej i prawej stronie. Poniższa tabela zawiera kompletną listę operatorów porównania w PHP.

Przykład

Nazwa

Opis

$a == $b

Równy

Zwraca TRUE, jeśli $a jest równe $b.

$a === $b

Identyczny

Zwraca TRUE, jeśli $a jest równe $b i obie zmienne są tego samego typu (tylko w PHP 4).

$a != $b

Różny

Zwraca TRUE, jeśli $a nie jest równy $b.

$a <> $b

Różny

Zwraca TRUE, jeśli $a nie jest równy $b.

$a !== $b

Nie identyczny

Zwraca TRUE, jeśli $a nie jest równy $b lub nie są tego samego typu (tylko w PHP 4).

$a < $b

Mniejszy niż

Zwraca TRUE, jeśli $a jest mniejszy od $b.

$a > $b

Większy niż

Zwraca TRUE, jeśli $a jest większy od $b.

$a <= $b

Mniejszy lub równy

Zwraca TRUE, jeśli $a jest mniejszy lub równy $b.

$a >= $b

Większy lub równy

Zwraca TRUE, jeśli $a jest większy lub równy $b.

W PHP4 istnieje operator wyciszający błędy - jest to @ (małpa). Umieszczony przed dowolnym wyrażeniem zapobiega wyświetleniu komunikatu o błędzie. Jeśli jest to błąd typu fatal error, to skrypt zostanie zatrzymany, ale treść błędu i tak nie zostanie wyświetlona. W komercyjnych aplikacjach bardzo często stosuje się operator @, gdyż wręcz niedopuszczalne jest pojawienie się błędu (na przykład typu parse error) w programie.

Jeśli zachodzi potrzeba podniesienia lub zmniejeszenia wartości danej zmiennej o 1, nie musimy od razu pisać $a=$a+1; etc. Poniższa tabela pokazuje, w jaki sposób wykorzystać do tego celu wbudowane w PHP mechanizmy.

Przykład

Nazwa

Opis

++$a

Preinkrementacja

Najpierw zwiększa wartość $a o jeden, a potem zwraca $a.

$a++

Postinkrementacja

Najpierw zwraca $a, a potem zwiększa $a o jeden.

--$a

Predekrementacja

Najpierw zmniejsza wartość $a o jeden, a potem zwraca $a.

$a--

Postdekrementacja

Najpierw zwraca $a, a potem zmniejsza $a o jeden.

Operatory te wykorzystuje się głównie w instrukcjach warunkowych typu if, jeśli umieszczony został więcej niż jeden warunek.

Przykład

Nazwa

Opis

$a and $b

I

Zwraca TRUE, jeśli zarówno $a, jak i $b są TRUE.

$a or $b

Lub

Zwraca TRUE, jeśli $a lub $b są TRUE.

$a xor $b

Wyłącznie-Lub

Zwraca TRUE, jeśli $a lub $b jest TRUE, ale nie jednocześnie.

! $a

Nie

Zwraca TRUE, jeśli $a nie jest TRUE.

$a && $b

I

Zwraca TRUE, jeśli zarówno $a, jak i $b są TRUE.

$a || $b

Lub

Zwraca TRUE, jeśli $a lub $b jest TRUE.

Istnieją dwa takie operatory . (kropka) oraz .= (kropka+znak równości). Pierwszy z nich łączy dwa ciągi z jego lewej i prawej strony, a następnie zapisuje wynik do zmiennej. Dla przykładu:

<?
$ciag1 = "Hello";
$ciag2 = " world";
$razem = $ciag1.$ciag2; // $razem zawiera teraz Hello world
?>

Drugi operator łańcuchowy (.=) dopisuje wartość ciągu z jego prawej strony do zmiennej z jego lewej strony. Dla przykładu:

<?
$razem = "Hello";
$ciag2 = " world";
$razem .= $ciag2; //$razem teraz również zawiera Hello world
?>

Czas zająć się tworzeniem bardziej rozbudowanych programów - takich, które odpowiednio reagują np. na dane wprowadzone przez użytkownika. Służą do tego pewne fragmenty kodu zwane strukturami kontrolnymi.

0x01 graphic


Instrukcje warunkowe są znane ze wszystkich języków programowania. Używa się ich do sprawdzenia tego, czy dany warunek jest prawdziwy, i na podstawie wyniku takiego zapytania wykonania określonego fragmentu kodu. Jeśli dane sprawdzenie nie przynosi oczekiwanego rezultatu, sprawdzany jest kolejny warunek (i ewentualnie uruchamia przypisany mu blok kodu). W wypadku gdy nie jest spełniony żaden warunek, wykonuje się jeszcze inny blok instrukcji. Dla przykładu:

<?
if (pierwszy warunek do spełnienia) {
instrukcje do wykonania, gdy pierwszy warunek jest prawdą
}
else if (drugi warunek do spełnienia) {
instrukcje do wykonania, gdy drugi warunek jest prawdą
}
//dowolna ilość warunków else if...
else if (n-ty warunek do spełnienia) {
instrukcje do wykonania, gdy n-ty warunek jest prawdą
}
else {
instrukcje do wykonania, gdy żaden warunek nie jest prawdą
}
?>

Oczywiście nie wszystkie bloki struktury kontrolnej IF muszą być zawsze wykorzystywane. Konieczny jest jedynie blok if..,, a bloki else if i else można umieścić lub nie (w zależności od aktualnej potrzeby).

Za warunek można uznać wszystko, co zwraca jakąś wartość, czyli zmienną, instrukcję warunkową lub funkcję. Za prawdę uznawane jest wszystko, co ma wartość większą od 0. Dla przykładu:

<?
if ($a>$b) {
echo "a jest większe niż b";
}

if ($ble) {
echo "Wartość zmiennej ble to".$ble;
}
else {
echo "Zmienna ble jest pusta"
}

if ($row=mysql_fetch_array($res)) {
echo "Kolejny rekord bazy danych.";
}
?>

Instrukcje mogą być też lokowane jedna w drugiej. Dla przykładu:

<?
if ($a>$b) {
if ($a>$c) {
echo "Zmienna a jest większa od zmienych b i c";
}
}
?>

Jednak powyższy przykład można zastąpić innym - mniej skomplikowanym - łącząc wyrażenia operatorami logicznymi. Dla przykładu:

<?
if ($a>$b && $a>$c) {
echo "Zmienna a jest większa od zmienych b i c";
}
?>

Wyrażenia logiczne da się łączyć w jeszcze bardziej skomplikowane struktury, dodając nawiasy i znak ! (wykrzyknik) przed nimi, czyli zaprzeczając wyrażeniu w nawiasie lub pojedynczemu wyrażeniu. Oto dłuższy przykład do przeanalizowania:

<?
if (!($a>$b && $a>$c) && ($d<$e || $e<$h)) {
echo "Prawda tylko wtedy, gdy $a<$b, $a<$c oraz $d<$e lub $e<$h";
}
?>

Pętla FOR jest używana tylko wtedy, gdy zachodzi potrzeba wykonania jakiegoś kodu określoną liczbę razy (założoną z góry przez autora lub pochodzącą ze zmiennej). Składnia tej pętli przedstawia się następująco:

<?
for (inicjalizacja zmiennych; sprawdzenie warunku;
modyfikacja zmiennych) {
//instrukcje do wykonania
}
?>

Przeanalizujmy następujący przykład, aby się przekonać, jak prosta w użyciu jest pętla FOR:

<?
for($i=10; $i<20 ; $i++) {
echo "<span style=\"font-size:".$i."px\"> tekst </span><br />"
}
?>

W powyższym kodzie $i=10 oznacza początkową wartość licznika pętli. Najpopularniejszym sposobem jest oznaczenie jej jako i, ale można ją nazwać dowolnie. Wyrażenie $i<20 oznacza, że pętla jest wykonywana, dopóki ten warunek jest prawdą. Wyrażenie $i++ podnosi wartość licznika o 1. Równie dobrze można by wpisać tu $i+=2.

Kolejnym rodzajem pętli jest pętla WHILE. W tym wypadku nie jest z góry narzucone to, ile razy dany blok instrukcji zostanie wykonany. Jest on wykonywany, dopóki podany warunek jest prawdziwy. Schemat działania tej instrukcji wygląda następująco:

<?
while (warunek) {
//instrukcje do wykonania
}
?>
W ten sposób można zapisać przykład dotyczący pętli FOR:
<?
$i=10;
while ($i<20)
echo "<span style=\"font-size:".$i."px\"> tekst </span><br />"
$i++;
}
?>

To specyficzna odmiana pętli WHILE, bo jeśli we WHILE warunek jest na starcie fałszywy, to pętla ani razu nie wykona bloku instrukcji. W wypadku użycia DO…WHILE pętla wykona blok instrukcji przynajmniej raz (nawet wtedy, gdy warunek jest od początku fałszywy). Schemat działania tej instrukcji wygląda następująco:

<?
do {
//instrukcje do wykonania
} while (warunek);
?>

Pętla ta jest zaczerpnięta z innego języka programowania - Perla. Ułatwia ona obsługę tablic i tablic asocjacyjnych. Poniższy przykład pokazuje, jak łatwo zamienić pętlę FOR na FOREACH w wypadku korzystania z tablic.
Blok instrukcji:

<?
for($i=0;$i<sizeof($kolory);$i++) {
echo "Kolorek: ".$kolory[$i]
}
?>

jest równoważny zapisowi:

<?
foreach ($kolory as $kolor) {
echo "Kolorek: ".$kolor
}
?>

Atuty FOREACH stają się jeszcze bardziej oczywiste w wypadku obsługi tablic asocjacyjnych.

Blok instrukcji:

<?
while( list($klucz, $wartosc) = each($tabliczka) )
echo "$klucz => $wartosc<br />";
}
?>

jest równoważny zapisowi:

<?
foreach ($tabliczka as $klucz => $wartosc) {
echo "$klucz => $wartosc <br />";
}
?>

Switch jest rodzajem skondensowanej instrukcji warunkowej, którą zazwyczaj zastępujemy rozbudowane i wielokrotne użycia ELSE IF. Dla przykładu:

<?
switch ($miasto) {
case 'warszawa': echo "Pochodzisz ze stolicy?"; break;
case 'hel': echo "Mieszkasz nad morzem?"; break;
case 'sanok': echo "A może w Bieszczadach?"; break;
default: echo "Miasto nierozpoznane.";
}
?>

W pierwszym nawiasie podaje się nazwę zmiennej, której wartość chcemy sprawdzać. Następnie sprawdzamy, czy wartość tej zmiennej to „warszawa”. Jeśli tak jest, skrypt ma wyświetlić zdanie: „Pochodzisz ze stolicy?”. Jeżeli nie, skrypt sprawdza kolejny warunek itd. Bardzo ważne jest słowo BREAK na końcu każdej instrukcji. Jeśli o nim zapomnimy, parser wykona cały kod, aż do kolejnego BREAK lub końca SWITCH. Czasami ominięcie BREAK jest założone przez programistę. Na koniec wyjaśnijmy jeszcze DEFAULT - tutaj należy określić, który blok instrukcji ma zostać wykonany, gdy wartość zmiennej nie zgodzi się z żadnym CASE.

Może się zdarzyć, że będziemy chcieli przerwać wykonywania pętli. Służą do tego instrukcje BREAK oraz CONTINUE. Pierwsza z nich powoduje przerwanie wykonywania pętli, a co za tym idzie - dalszej części zawartego w niej kodu. Funkcja CONTINUE powoduje natomiast przerwanie aktualnej iteracji (przebiegu) pętli i wykonanie jej od nowa.

Istnieją też składnie alternatywne do tych przedstawionych powyżej. Pierwsza z nich to skrócona wersja instrukcji warunkowej IF. Jej schemat jest następujący:

<?
((warunek)? instrukcje jeśli warunek jest
prawdziwy : instrukcje w przeciwnym wypadku)
?>

W ten sposób możemy np. umieszczać instrukcje warunkowe wewnątrz tekstu wyświetlanego za pomocą funkcji ECHO. Dla przykładu:

echo "Liczba 5 jest ".((5>$a)"
"większa" : "mniejsza").' od zmiennej $a';

Koleją możliwością jest niewykorzystywanie nawiasów klamrowych w instrukcjach IF, FOR, SWITCH, WHILE oraz FOREACH. Zamiast klamry otwierającej należy umieścić dwukropek, a na końcu słowo ENDIF, ENDSWITCH bądź inny END... - odpowiedni do użytej pętli. Taka składnia obejmuje też użycie ELSEIF i ELSE:

<?
if ($a<5 && $b<5) :
echo "przypadek pierwszy";
elseif ($a<5 && $b>5) :
echo "przypadek drugi";
else:
echo "ostatnia możliwość";
endif;
?>

Tablice to typy zmiennych, które służą do przechowywania innych zmiennych. Używanie tablic pomaga np. w przeglądaniu wpisów pobranych z bazy danych lub odczytanej listy plików z foldera na serwerze, gdyż można się do nich odwoływać po ich indeksach (liczby całkowite) lub identyfikatorach tekstowych (w tablicach asocjacyjnych). W wypadku identyfikatorów tekstowych należy pamiętać, że zaczynają się one od zera, gdyż często młodzi programiści popełniają tu błąd. Oto przykład przypisania trzech kolejnych wartości do tablicy $tablica.


<?
$tablica[0] = 8;
$tablica[1] = 9;
$tablica[2] = 1;
?>

Kiedy chcemy po prostu dodać nową pozycję do tablicy, wystarczy zastosować powyższy zapis, pomijając numer klucza w nawiasie kwadratowym. Dla przykładu:

<?
$tablica[] = 8;
$tablica[] = 9;
$tablica[] = 1;
?>

Elementem tablicy może być dowolna zmienna, nawet inna tablica. Gdy powstanie taka konstrukcja, tablicę nazywamy wielowymiarową i do elementu odwołujemy się poprzez zapis uwzględniający liczbę wymiarów. Dla przykładu:

<?
$tablica[0][7] = "18 małych kotków";
echo $tablica[0][7];
$tablica[0][7]["tytuł"][2] = "Ala ma kota, a sierotka ma rysia";
//można też wprowadzać indeksy tekstowe (patrz niżej)
i więcej niż dwa wymiary tablicy
?>

Tablice asocjacyjne to odmiany tablic, w których zamiast indeksów liczbowych występują indeksy tekstowe. Najczęstsze zastosowanie tej struktury danych prezentuje poniższy przykład. Tablica reprezentuje konkretną osobę, a wiersze to jej dane osobowe.

<?
echo $kowalski["imie"] = "Jan";
echo $kowalski["miasto"] = "Warszawa";
echo $kowalski["ulica"] = "Polna";
echo $kowalski["wiek"] = 19;
echo $kowalski["kawaler"] = true;
?>

Czasem będziemy potrzebowali wykonać jakąś operację na wszystkich elementach tablicy. np. porównać ze wzorcem. W wypadku zwykłej tablicy (gdy znamy liczbę jej elementów) wystarczy użyć pętli FOR i następującej konstrukcji:

<?
for ($i=0;$i<10;$i++) {
echo $tablica[$i];
}
?>

Lecz jeśli nie znamy liczby elementów tablicy, bo jest ona zmienna i zależy np. od wprowadzonych wcześniej informacji, należy posłużyć się funkcją sizeof(). Podając jako argument nazwę tablicy, otrzymamy liczbę jej elementów. Dla przykładu:

<?
for ($i=0;$i<sizeof($tablica);$i++) {
echo $tablica[$i];
}
?>

Nieco bardziej skomplikowane jest przeglądanie tablicy asocjacyjnej, ale i tutaj użyteczne są dwie konstrukcje. Dla przykładu:

<?
foreach ($tablica as $klucz => $wartosc) {
echo "$klucz => $wartosc <br />";
}
?>

<?
while( list($klucz, $wartosc) = each($tabliczka) )
echo "$klucz => $wartosc<br />";
}
?>

W pierwszym przykładzie kod zwraca dla każdej iteracji pętli parę zmiennych: $klucz i $wartosc. Zawierają one odpowiednio nazwę klucza i jego wartość. W drugiej konstrukcji należy wyjaśnić funkcje list() i each().

Zatem each() zwraca tablicę, w której na pierwszej pozycji jest klucz, a na drugiej wartość, list() natomiast przechwytuje ją i zapisuje do dwóch zmiennych.

Zdarza się, że ciąg znaków trzeba zamienić na tablicę (chociażby przy odczytywaniu danych z pliku). Potrzebna jest wówczas funkcja explode(). Jak sama nazwa wskazuje, funkcja ta rozdziela określony ciąg znaków i tworzy z powstałych elementów tablicę. Funkcja pobiera dwa argumenty: ciąg znaków lub znak, który stanowi element rozdzielający, a także zmienną zawierającą ciąg, który chcemy rozdzielić. Dla przykładu:

<?
$wpisy = explode("|", $odczyt);
?>

Wywołanie takie zwróci tablicę o nazwie $wpisy z pojedynczymi elementami, które były w oryginalnym ciągu rozdzielone symbolem |.
Elementy tablicy można również tablicę połączyć określonym znakiem (aby je np. zapisać w pliku). Służy do tego funkcja implode(). Pobiera ona dwa argumenty. Pierwszy to ciąg, którymi mają być połączone elementy tablicy, a drugi to nazwa zmiennej tablicy z danymi.

<?
$doZapisu = implode("|", $wpisy);
?>

Do sortowania tablic - zarówno zwykłych, jak i asocjacyjnych - napisano wiele funkcji. Poniżej opisujemy sposób działania najważniejszych.

W tej części zajmiemy się metodami pozwalającymi na przechowywanie danych w plikach, czyli tzw. płaskimi bazami danych. Opiszemy tutaj, jak otwierać plik oraz czytać i zapisywać do nich informacje.
Wszystkie funkcje obsługujące pliki jako pierwszy parametr pobierają zmienną - tzw. uchwyt do pliku. Jest to specjalna nazwa zwracana przez funkcję otwierającą plik - fopen() - która jednoznacznie identyfikuje dany zbiór. Aby otworzyć plik, należy użyć funkcji fopen(). Pobiera ona dwa parametry. Pierwszy to nazwa pliku do otwarcia, a drugi to tryb otwarcia. Ten ostatni określa cel, w jakim otworzyliśmy zbiór. Istnieje pięć takich trybów:


Wewnętrzny wskaźnik pliku określa miejsce, w którym mają być zapisywane dane lub (przy procedurze odczytującej po jednym znaku) który znak ma być odczytany.
Aby otworzyć plik do odczytu, wystarczy wpisać kod:

<?
$plik = fopen("dane/imiona.txt", "r");
?>

Po zakończeniu używania pliku można (ale nie trzeba) go zamknąć. Służy do tego funkcja fclose(wskaznik_do_pliku). Dla przykładu:

<?
fclose($plik);
?>

Wewnętrzny wskaźnik pliku określa, skąd w pliku mają być odczytywane dane lub gdzie mają być zapisywane. Przesuwa się on automatycznie dalej po każdej procedurze odczytania określonej porcji danych z pliku. Czasem jednak chcemy odczytać np. 25. znak z pliku. Nie trzeba wtedy czytać po kolei każdego znaku, aby dojść do tego właściwego.

Wystarczy użyć funkcji fseek(). Pobiera ona trzy argumenty. Pierwszy to oczywiście uchwyt do pliku, kolejny to przesunięcie, a trzeci (opcjonalny) to rodzaj przesunięcia. Dostępne są trzy wartości przesunięcia:

Istnieje też funkcja rewind(), która pobiera jako argument uchwyt do pliku i przesuwa wskaźnik na jego początek.

Jest kilka możliwości odczytu danych z pliku. Pierwsza to odczyt po kolei po jednym znaku. Służy do tego funkcja fgetc(). Wystarczy jako argument podać tylko uchwyt do pliku. Zwraca ona jeden odczytany znak lub false, jeśli natrafimy na koniec zbioru.

Ważne jest to, że aby sprawdzić, czy funkcja oddała wartość false, należy użyć operatora === lub !==, ponieważ przy użyciu == lub != wartości „„ (pusty string), lub 0 dzięki zamianie typów zmiennych w PHP też będą interpretowane jako false. Dla przykładu:

<?
while (false !== ($char = fgetc($file))) {
echo "Aktualny znak to $char <br />";
}
?>

Kolejną możliwością jest odczyt linijka po linijce. Służy do tego funkcja fgets(). Jako parametry należy podać uchwyt do pliku i opcjonalnie maksymalną długość odczytanej linii. Czytanie linii kończy się, gdy funkcja dojdzie do końca linii (znak nowej linii jest dołączany do zwróconej wartości) lub do końca pliku. W PHP do 4.3 wartością domyślną było 1024 znaki, od wersji 4.3.0, gdy nie podany jest parametr określający długość, czytane są całe linijki (do końca).

Można też odczytać od razu cały plik. Wystarczy użyć funkcji fread(). Jej argumentami są: uchwyt do pliku i liczba znaków, jaka ma być odczytana. Często jako liczbę znaków podaje się całkowitą długość pliku, obliczoną funkcją filesize(nazwa_pliku). Dla przykładu:

<?
$plik = fopen("plik.txt", "r");
$dane = fread($plik, filesize("plik.txt"));
?>

Jeśli podana ilość danych do odczytania jest mniejsza niż długość pliku, to po ponownym wywołaniu tej funkcji zwróci ona dane, od momentu gdzie zakończyło się poprzednie czytanie z pliku.
Kolejna możliwość to odczyt całego pliku za pomocą tylko jednej linijki kodu:

<?
$dane = fread(fopen("plik.txt", "r"), filesize("plik.txt"));
?>

Ale już od PHP w wersji 4.3 istnieje dla powyższej instrukcji dużo lepsza alternatywa - funkcja file_get_contents(). Wystarczy podać jako argument nazwę pliku - tym razem nie jest to uchwyt, czyli pliku nie trzeba otwierać, używając fopen() 0 - a funkcja zwróci całą zawartość pliku. Dla przykładu:

<?
$dane = file_get_contents("plik.txt");
?>

Kolejną (już ostatnią) metodą odczytywania zawartości pliku jest przeniesienie jego zawartości do tablicy, gdzie każdy wpis odpowiada jednej linii z pliku. Służy do tego funkcja file(), której wystarczy podać tylko nazwę pliku. Zbiór w tym wypadku znów nie musi być otwarty funkcją fopen().

Z zapisem sprawa jest o tyle ułatwiona, że do dyspozycji jest tylko jedna funkcja - fwrite(), chociaż często w różnych kursach czy nawet skryptach jest używany alias do niej - fputs(). Problem z zapisywaniem jest taki, że nie ma możliwości zapisu na końcu czy w środku pliku.

Można dopisywać tylko na początku. Jeśli więc chcemy coś zapisać na początku już istniejącego zbioru, należy najpierw otworzyć dany plik, odczytać jego zawartość i dopiero wtedy zapisać całość od nowa. Dla przykładu:

<?
$file = fopen("plik.txt", "r");
$dane = fread($file, filesize("plik.txt"));
fclose($file);
$noweDane = "Coś do dodania na początku pliku";
$noweDane .= $dane;
$file = fopen("plik.txt", "w");
fwrite($plik, $noweDane);
fclose($file);
?>

Aby zaś dodać dane na końcu pliku, wystarczy otworzyć go z argumentem a.

Podczas używania plików w większym serwisie (o dużej liczbie odwiedzin) może się zdarzyć, że w tym samym czasie dwa procesy będą coś próbowały zapisywać do pliku. Jeśli odbędzie się to dokładnie w tym samym czasie, to z zawartością pliku mogą się zdarzyć różne nieprzewidywalne rzeczy. Po to też istnieją blokady pliku. W PHP znajdziemy dwa rodzaje blokad:

Aby założyć blokadę, należy użyć funkcji flock(), która pobiera dwa argumenty: uchwyt do pliku i rodzaj blokady. Oto trzy dostępne rodzaje blokady:

Jeśli zakładanie blokady się powiedzie, to funkcja zwraca wartość true, w przeciwnym wypadku zwracana jest wartość false.

<?
$file = fopen("dane\plik.txt", "w+");
if (flock($file, LOCK_EX)) { // zakładanie blokady wyłącznej
fwrite($file, "Wartość dopisana do pliku");
flock($file, LOCK_UN); // zdjęcie blokady
} else {
echo "Błąd - plik zablokowany";
}
fclose($file);
?>

W PHP istnieje też kilka funkcji, które zwracają informacje o pliku. Należą do nich:

Oprócz tego istnieje też kilka funkcji zwracających wartości true lub false. Są to:

Do kopiowanie plików służy funkcja copy(). Zwraca ona wartość true, jeśli plik zostanie poprawnie skopiowany, lub false, jeżeli coś pójdzie nie tak. Dla przykładu:

<?
if (!copy("tymczasowy/plik.txt", "dane/plik.txt"))
echo "Błąd podczas kopiowania";
?>

Aby zmienić nazwę zbioru, należy użyć funkcji o nazwie rename(). Może ona też służyć do kopiowania plików i zwraca true lub false, tak samo jak w wypadku copy().

<?
if (!rename("/tmp/tmp_file.txt", "/home/user/
login/docs/my_file.txt");)
echo "Błąd podczas kopiowania";
?>

Do usuwanie plików służy funkcja o nazwie unlink(). Jako parametr wystarczy podać nazwę pliku. Ważne jest to, by uprawnienia zbioru pozwalały na jego usunięcie, w przeciwnym razie zostanie wyświetlony błąd.

Aby utworzyć katalog, należy wywyołać funkcję mkdir() i jako parametry podać kolejno: nazwę katalogu do utworzenia i jego prawa dostępu (np. 0777). Funkcja (tak jak prawie wszystkie inne z tej grupy) wartości true lub false w zależności od powodzenia operacji.
Do przeglądania zawartości katalogów służy mechanizm pseudoobiektowy. Przeglądanie zaczynamy funkcją dir(nazwa_folderu), która zwraca obiekt-uchwyt do katalogu. Kolejne pozycje z katalogu pobieramy za pomocą metody read(). Pracę z folderem kończymy metodą close(). Dla przykładu:

<?
$dir=dir("logs");
while($entry=$dir->read()) {
echo "Kolejna pozycja z tego folderu to $entry<br />";
}
$dir->close();
?>

Nieco komplikacji pojawia się w trakcie procesu usuwania katalogów. Służy do tego funkcja rmdir(), której argumentem jest nazwa folderu przeznaczonego do usunięcia. Warunkiem koniecznym do tego, by wszystko się powiodło, jest to, aby usuwany katalog był pusty. Poniżej przykład funkcji rekurencyjnej do usuwania całego katalogu (wraz z plikami i podkatalogami):

<?
function deltree($dirn) {
$dir = @dir($dirn);
while ($entry = $dir->read()) {
if (is_dir($dirn.'/'.$entry)&&$entry!='.'&&$entry!='..') {
@deltree($dirn.'/'.$entry);
}
else if ($entry!='.'&&$entry!='..') {
@unlink($dirn.'/'.$entry);
}
}
$dir->close();
@rmdir($dirn);
}
?>

Unix i jego pochodne to systemy przeznaczone z założenia dla współpracy wielu użytkowników, dlatego wprowadzono coś takiego jak prawa dostępu. Są to zezwolenia dla konkretnych użytkowników lub całych ich grup na wykonanie operacji na danym pliku.

Każdemu plikowi i katalogowi w systemie można przypisać trzy komplety praw. Pierwszy z tych kompletów dotyczy właściciela pliku, drugi grupy użytkowników, a trzeci użytkowników, którzy ani nie są właścicielami zbioru, ani nie należą do grupy. Pojedynczy komplet to suma praw: uruchomienia lub w wypadku katalogu: otwarcia (wartość 1), odczytu (wartość 4) i zapisu (wartość 2). Aby nadać właścicielowi komplet praw, grupie odczyt i zapis, a pozostałym tylko odczyt, należy ustawić prawa „0764” (cyfra zero na początku służy do poinformowania PHP, że liczba zapisana jest w formacie ósemkowym).

Do ustawiania praw dostępu służy funkcja chmod($nazwa_pliku, $tryb), gdzie drugi parametr to prawa dostępu zapisane w formacie ósemkowym. Inne funkcje przydatne przy pracy z systemem zabezpieczeń to chown($nazwa_pliku, $user), zmieniająca właściciela pliku, i chgrp($nazwa_pliku, $grupa), zmieniająca grupę.

Ustawienie odpowiednich praw dostępu to sprawa podstawowa przy tworzeniu skryptów zapisujących coś do pliku lub operujących na katalogach. Bardzo często wyświetla się błąd „Permission denied”, którego poprawienie to właśnie kwestia ustawienia odpowiednich praw dostępu.

Cały skrypt nie zawsze musi się znajdować w jednym pliku. Jeśli budujemy rozbudowany serwis, na pewno są w nim elementy, które powtarzają się we wszystkich zbiorach. Nie muszą być one umieszczane w każdym pliku ze skryptem - wystarczy stworzyć jeden plik z daną funkcją lub klasą i załączać go do pozostałych. Służą temu cztery funkcje w PHP - na pierwszy rzut oka identyczne, ale różniące się kilkoma szczegółami.



Najbardziej znanymi funkcjami są include() i require(). Często przez początkujących programistów są używane zamiennie, gdyż na pierwszy rzut oka wydają się działać w sposób identyczny. Ale jest między nimi kilka różnic.

Dla odmiany require() od razu próbuje wczytać żądany plik, nawet jeśli jego wywołanie znajduje się w instrukcji warunkowej (może nie zostać wywołane). Ponadto jeśli funkcja require() znajduje się w pętli, to niezależnie od tego, ile razy byłaby wywoływana, plik zostanie wczytany tylko raz.

Obie te instrukcje mają też dwa ograniczenia:

Dla przykładu:

<?
//to NIE jest poprawny zapis:
if ($a>5)
include ("plik1.php");
else
include ("plik2.php");
//to jest poprawny zapis:
if ($a>5) {
include ("plik1.php");
}
else {
include ("plik2.php");
}
?>

Te dwie funkcje dodano w PHP 4.0.1. Działają podobnie jak include() i require(), z tą jedną różnicą, że nawet wielokrotne wywołanie includowania danego pliku poprzez include_once() lub require_once() nie spowoduje wczytania go kilkakrotnie. Rozwiązuje to problem przypadkowego redefiniowania funkcji lub nadpisywania zmiennych przez powtórne wczytanie pliku.

Pamiętajmy, że w systemach nierozróżniających wielkości znaków - np. Windows - poniższy kod spowoduje dwukrotne wczytanie pliku a.php.

<?
require_once("a.php"); //wczyta a.php
require_once("A.php"); //a Windows znowu wczyta a.php
?>

Po co w ogóle przekazywać zmienne? I gdzie? Wbrew pozorom jest to dość przydatny proces. Bardzo rzadko zdarza się, że plik PHP uruchamiany jest bez żadnych argumentów (czyli właśnie zmiennych z zewnątrz), bo w zasadzie język PHP został stworzony, by przetwarzać wprowadzone dane i zwracać na ich podstawie odpowiedni kod HTML. Istnieją dwa sposoby na przekazanie zmiennej między dwoma plikami PHP - metody GET i POST.


reklama

0x01 graphic


0x01 graphic


Za pomocą metody GET można przekazać kilka zmiennych, przechowujących małą ilość danych. GET polega na wywołaniu pliku PHP z dodaniem po jego nazwie następującej części:

?zmienna1=wartosc1&zmienna2=wartosc2&zmienna3=wartosc3

Oczywiście nazwy zmiennych i ich liczba może być dowolna. Jedynym ograniczeniem jest tutaj to, że większość starszych przeglądarek akceptuje adresy tylko do 1024 znaków. Ale metoda GET to nie tylko same plusy. Należy pamiętać, że wysyłanie zmiennych widocznych w URL prowadzi do sytuacji, w której ktoś może łatwo podmienić ich wartość i np. odczytać plik z hasłami umieszczony na serwerze.

Metoda POST służy raczej do przekazywania dużej liczby zmiennych przy zachowaniu podstawowych zasad poufności. Nazwy zmiennych i ich wartości nie są bowiem widoczne, ponieważ są wysyłane jako część nagłówka pliku. Minusem tej metody jest to, że strony z wysłanymi danymi metodą POST nie można np. dodać do Ulubionych.

Zmienne przekazane jako POST lub GET trafiają w najnowszych i tych trochę starszych wersjach PHP (od 4.10) do tablic superglobalnych $_POST, $_GET, $_REQUEST, $_SESSION, $_ENV, $_SERVER oraz $_COOKIE. Wykorzystywane dotychczas tablice o długich nazwach $HTTP_*_VARS są nadal dostępne, aczkolwiek niezalecane w użyciu.

Jednocześnie wycofywana jest obsługa registered_globals, co oznacza, że każda zmienna - niezależnie, czy z metody GET, POST czy z sesji - nie jest już rejestrowana po prostu jako zmienna. Dotychczas było to niebezpieczne i stwarzało możliwości oszukiwania skryptu. Tablice superglobalne, o których wspomnieliśmy ($_GET, $_POST i inne), są asocjacyjne i nazwą danego klucza jest nazwa zmiennej, a wartością - wartość tej zmiennej. Poniżej znajdują się wszystkie tablice superglobalne dostępne w PHP od wersji 4.10.

Oto przykład formularza wysyłającego dane o użytkowniku do innego pliku.
plik formularz.html:

<form action="dane.php" method="POST">
<input type="text" name="imie" size="15" /><br />
<input type="text" name="nazwisko" size="16" /><br />
<input type="radio" name="plec" value="m" /> Mężczyzna<br />
<input type="radio" name="plec" value="k" /> Kobieta<br />
<input type="submit" value="Wyślij dane" /><br />
</form>

plik dane.php:

<?
echo "Witaj ".$_POST['imie']." ".$_POST['nazwisko']."<br />";
echo "Jesteś ".(($_POST['plec']=="m")?"mężczyzną":"kobietą")
." <br />";
?>

Plik dane.php pokaże wpisane imię i nazwisko oraz wyświetli płeć w zależności od wartości zmiennej $_POST['plec']. Poszczególne elementy formularza nie wymagają komentarza.

Cookies, czyli tzw. ciasteczka, to małe pliki tekstowe przechowywane na komputerze odbiorcy, w których przetrzymywane są informacje np. o dacie jego ostatniej wizyty lub jego nazwie użytkownika na forum. PHP ma możliwość zapisu ciasteczek dzięki funkcji setcookie(). Argumenty, jakie należy podać, to setcookie(nazwa_zmiennej, wartosc_zmiennej, czas_wygaśnięcia, ścieżka, domena, bezpieczeństwo).

Czas wygaśnięcia to czas, po którym ciastko nie będzie dostępne do odczytu. Podaje się go w formacie timestamp. Przykładowa wartość, jaką można wpisać do czasu_wygasniecia, to np. time()+3600 (ważne godzinę od czasu ustawienia ciastka), time()+3600*24 (ważne dobę) czy time()+3600*24*365 (ważne cały rok).

Jeśli czas nie zostanie podany, to ciastko będzie ważne do zamknięcia przeglądarki. Ścieżka i domena określa kolejno ścieżkę na serwerze i domenę, z których ciastko może zostać odczytane. Jeśli wartość bezpieczenstwo wyniesie 1, to ciastko będzie można odczytać, tylko jeśli połączenie będzie szyfrowane (połączenie SSL).

Domyślnie w ciastkach zapisuje się tylko ciągi znaków. Istnieje jednak możliwość umieszczenia również całej tablicy zapełnionej danymi. Służy do tego funkcja serialize(), która jako parametr pobiera tablicę, a zwraca przekonwertowany ciąg znaków. Odwrotną operację wykonuje funkcja unserialize(), która pobiera ciąg znaków (tablicę zamienioną funkcją serialize()), a zwraca zmienną z tablicą.

Często pisząc rozbudowany skrypt, wykorzystuje się pewne fragmenty kodu (np. pobranie porcji danych z bazy i odpowiednie przetworzenie jej) wiele razy. Zamiast wielokrotnie pisać ten sam kod, można stworzyć funkcję, czyli coś w rodzaju podprogramu wykonującego odpowiednie zadania. Często funkcje pisane są w taki sposób, że pobierają argumenty i zwracają odpowiedni wynik. Jedną z prostszych jest funkcja sumująca dwie liczby (podane właśnie jako argumenty) i zwracająca wynik.



<?
function suma($liczba1, $liczba2=0) {
$suma = $liczba1 + $liczba2;
return $suma;
}
?>

Powyższy kod to deklaracja funkcji o nazwie suma. Gdy chcemy zadeklarować własną funkcję, musimy najpierw napisać słowo kluczowe function, a następnie nazwę funkcji. Nazwa nie może być taka jak już istniejące w PHP funkcje, ponadto musi się zaczynać od litery (później mogą wystąpić cyfry). Rozróżniane są również wielkości liter, tak więc suma() i SuMa() to dwie różne funkcje.

Następnie w nawiasie podajemy parametry funkcji (w tym wypadku są to $liczba1 i $liczba2), a na końcu w nawiasach klamrowych jej treść. Bardzo ważna jest linia zaczynająca się od słowa return. To ona przypisuje wynik działania $suma do wartości, jaką przekazuje sama funkcja.

Dodajmy, że dzięki zapisanemu na końcu drugiego argumentu wyrażeniu =0 wartość drugiej zmiennej nie musi być podana przez użytkownika i przyjmuje zero. Argumenty, których wartość nie musi być podana, koniecznie pobierane są na końcu listy argumentów. Niewłaściwe jest coś takiego jak:

function robKawe($kawa="cappucino", $wielkosc, $typKubka))

Aby wyświetlić wynik, należy teraz napisać odpowiedni kod:

<?
$wynik = suma(55, 75);
echo $wynik;
?>

lub taki:

<?
echo suma(55, 75);
?>

Return można również użyć do zakończenia działania funkcji w jej trakcie. Dla przykładu:

<?
function suma($liczba1, $liczba2) {
if ($liczba1=='' || $liczba2=='')
return false;
$suma = $liczba1 + $liczba2;
return $suma;
}
?>

W powyższym wypadku funkcja zakończy się, gdy nie zostanie podany chociażby jeden parametr. Gdy tak się stanie, dobrze jest zwracać wartość false, gdyż programista może w ten sposób sprawdzić poprawność obliczeń instrukcją warunkową if. Dla przykładu:

<?
if (! suma(55, 75)) {
echo "Wystąpił błąd!";
}
?>

Ale funkcja nie zawsze musi pobierać i zwracać dane. Można też napisać funkcję (w innych językach - np. w Delphi zwaną procedurą) niepobierającą argumentów i niezwracającą danych. Przykładem może być poniższa funkcja:

<?
function wypisz() {
for ($i=10; $i<=20; $i++) {
echo "<span style=\"font-size:".$i."pt\"> tekst $i</span><br />"
}
}
?>

Możliwe jest też stworzenie funkcji pobierającej nieokreśloną liczbę argumentów. Przykładem może być funkcja unset(), usuwająca zmienne, dla której można podać dowolną liczbę argumentów. Aby osiągnąć coś takiego w samodzielnie napisanej funkcji, należy się posłużyć funkcją func_get_args(), zwracającą w tablicy wszystkie argumenty wysłane do funkcji.

<?
function sumaMulti() {
foreach (func_get_args() as $val) {
$suma+=$val;
}
return $suma;
}
echo sumaMulti(1, 2, 3, 4);
?>

W PHP nie ma bezpośredniego dostępu do zmiennych globalnych poprzez funkcje. Aby użyć w ciele funkcji zmiennej globalnej, należy posłużyć się operatorem global. Dla przykładu:

<?
$liczba1 = 55;
function dodajDoLiczby($liczba2) {
global $liczba1;
$suma = $liczba1+$liczba2;
return $suma;
}
?>

Gdyby nie użycie global, nie byłoby dostępu do zmiennej $liczba1 w funkcji, w wyniku czego nie działałaby ona poprawnie.

Tym razem zacznijmy od przykładu:

<?
function foo() {
$a=0;
echo $a;
$a++;
}
foo();
foo();
foo();
?>

Jak pewnie wszyscy zauważyli, nie ma on zbytniego sensu, gdyż za każdym wywołaniem funkcji foo() wartość zmiennej jest zerowana i wyświetlane jest zero. Aby ten przykład działał poprawnie, powinien wyglądać tak:

<?
function foo() {
static $a=0;
echo $a;
$a++;
}
?>

Wtedy zmienna $a jest zmienną statyczną i za każdym wywołaniem funkcji sprawdzane jest, czy zmienna nie była już wcześniej zadeklarowana. Jeśli tak się stało, to do jej wartości nie jest już przypisywane zero i funkcja zadziała wtedy zgodnie z założeniem (wyświetli po kolei 0, 1, 2).

Funkcje zmienne służą do wywołania funkcji o nazwie takiej jak wartość zmiennej. Stanie się tak, gdy po nazwie zmiennej dodamy nawiasy (puste lub z argumentami).

<?
function foo($liczba) {
echo "argumentem jest $liczba";
}
$funkcja = "foo";
$funkcja(15); //wywoła funkcję foo() z argumentem 15
//co da w wyniku "argumentem jest 15"
?>

Pomiędzy PHP4 i PHP5 istnieją znaczne różnice, jeśli chodzi o tworzenie skryptów bazujących na klasach. Warto jest jednak znać oba modele obiektowe, gdyż wiele skryptów (bazujących na obiektach) nie ma jeszcze swoich wersji na PHP5. Ponadto administratorzy serwerów nie spieszą się z instalowaniem PHP w wersji 5 na swoich serwerach, a z powodu dużej liczby różnic istnieje prawdopodobieństwo, że skrypty obiektowe z PHP4 nie zadziałają pod PHP5 i na odwrót. Jeśli mamy dostęp do serwera z zainstalowanym interpreterem PHP5, nauczmy się od razu modelu klas z PHP5, warto jednak zapoznać się również z ogólnym wstępem do programowania z wykorzystaniem klas.


0x01 graphic


Klasy to swego rodzaju szablony, oferujące zbiór zmiennych i funkcji na nich operujących. Dostarczają one pewnej abstrakcji, gdyż tworząc np. klasę zarowka, nie tworzymy konkretnej żarówki, a jedynie ogólne założenie np. odnośnie do jej wyglądu i właściwości. Dopiero gdy gotowa jest deklaracja klasy, ze wszystkimi funkcjami i zmiennymi, można utworzyć nowy obiekt na jej podstawie. Dla przykładu:

<?
class zarowka {
var $jasnosc; //deklaracje zmiennych
var $wlaczona;
var $moc;
var $sprawnosc;
function zarowka($moc = 60) {
$this->wlaczona = false;
$this->moc = $moc;
$this->sprawnosc = 15;
$this->jasnosc = $this->moc * $this->sprawnosc /100;
$echo "Zarówka świeci z jasnością ".$this->jasnosc."<br />";
}
function wlacz() {
$this->wlaczona=true;
echo "Zarówka włączona<br />";
}
function wylacz() {
$this->wlaczona=false;
echo "Zarówka wyłączona<br />";
}
}
?>

Omówmy powyższy przykład. Otwieramy klasę (class zarowka) i zaczynamy od deklaracji zmiennych wewnątrzklasowych. Muszą one być zadeklarowane przy użyciu słowa kluczowego var i nie wolno im przypisywać na początku żadnej wartości. Następnie mamy do czynienia z funkcją (konstruktorem) - ma ona taką samą nazwę jak sama klasa i uruchamia się zaraz po zadeklarowaniu obiektu. Tutaj właśnie można przypisać wartości do zmiennych.

Zmienne klasy są dostępne poprzez następującą konstrukcję: $nazwaObiektu->nazwa_zmiennej. Jednak podczas pisania klasy nie zawsze wiadomo, jakie będziemy tworzyli obiekty. Należy się wówczas posłużyć pseudozmienną $this, która odwołuje się do aktualnej klasy. Na końcu znajdują się już tylko dwie funkcje do włączania i wyłączania żarówki. Klasa gotowa!

Gdy mamy już klasę zarowka z jej funkcjami i zmiennymi, warto utworzyć obiekt klasy zarowka:

<?
$zarowka = new zarowka(80);
$malaZarowka = new zarowka(15);
?>

Utworzone zostały teraz dwa obiekty klasy zarowka: $zarowka o mocy 80 i $malaZarowka o mocy 15. Są to dwa odrębne obiekty, tak więc wywołanie $zarowka->wlacz() spowoduje tylko włączenie się mocniejszej żarówki, gdy $malaZarowka pozostanie wyłączona. Może się tak zdarzyć (przy odpowiednim kodzie - w naszym przykładzie w konstruktorze jest wartość domyślna), że powstaną jakieś błędy podczas użycia klasy. Możliwe jest ich wyciszenie operatorem kontroli błędów - napiszmy po prostu @new.

Gdy trzeba rozszerzyć klasę o nowe funkcje, zmienne lub tylko zmienić działanie już istniejących funkcji, nie musimy definiować od nowa całej klasy. Wystarczy utworzyć klasę potomną i wpisać tylko te funkcje lub zmienne, które się zmieniły lub zostały dodane. Dziedziczenie jest osiągane dzięki słowu extends. Dla przykładu:

<?
class zarowkaOsram extends zarowka {
function zarowkaOsram($moc) {
$this->wlaczona = false;
$this->moc = $moc;
$this->sprawnosc = 90;
$this->janosc = $this->moc * $this->sprawnosc /100;
$echo "Zarówka Osram świeci z jasnością "
.$this->jasnosc."<br />";
}
}
?>

Przy dziedziczeniu wystarczy tylko wypisać te funkcje, których działanie chcemy zmienić - w tym wypadku jest to konstruktor, gdyż dla żarówki energooszczędnej definiowana jest nowa sprawność.

Jeśli w klasie potomnej nie zdefiniujemy konstruktora (funkcji o nazwie takiej jak klasa pochodna), parser będzie próbował uruchomić konstruktor klasy bazowej (mimo że ma on inną nazwę niż klasa pochodna). UWAGA! Klasy muszą być zdefiniowane, zanim będą użyte. Nie można tworzyć klasy zarowkaOsram rozszerzającej klasę zarowka bez uprzedniej deklaracji klasy zarowka.

Operator :: pozwala odnosić się do funkcji klasy z pominięciem samego obiektu. Można dzięki niemu uruchamiać funkcje z wewnątrz klasy, bez deklarowania obiektu na niej opartego.

<?
class A {
function napis() {
echo "funkcja napis() klasy A";
}
}
A::napis();
//wyświetli "funkcja napis() klasy A" mimo że nie został
//zadeklarowany żaden obiekt klasy A
?>

Pokaże to też na przykładzie klas żarówek. Możesz wyświetlić napis: „Żarówka włączona” z funkcji wlacz() klasy zarowka, nie deklarując żadnego obiektu tej klasy. Po prostu wywołaj kod zarowka::wlacz(), a wyświetli się potwierdzenie włączenia żarówki, nawet jeśli nie istnieje jeszcze nawet obiekt klasy zarowka.

Kiedy piszemy rozbudowane klasy i klasy pochodne, może zajść potrzeba wywołania oryginalnych funkcji z klas nadrzędnych. Służy do tego operator parent. Dla przykładu:

<?
class A {
function napis() {
echo "funkcja napis() klasy A<br />';
}
}
class B extends A {
function napis() { echo "funkcja napis() klasy B<br />";
parent::napis();
}
}
$b = new B;
$b->napis();
//wyświetli "funkcja napis() klasy B", a potem wywoła
//funkcję napis z klasy A i wyświetli się
//"funkcja napis() klasy A"
?>

W nowym PHP nie wprowadzono wiele zmian. Poprawiono obsługę plików XML, dodano obsługę wyjątków i - co najważniejsze - przebudowano model obiektowy (obsługę klas), który można teraz porównać do tego z C++ czy Javy.


Wiele osób piszących skrypty oparte na obiektach umieszcza po jednej definicji klasy w pojedynczym „includowanym” pliku. Jedną z najbardziej irytujących rzeczy jest napisanie listy includes na początku skryptu. Aby to rozwiązać, dodano funkcję __autoload(), która automatycznie wykona zaplanowanie zadania podczas tworzenia obiektu. Wyjątki typu fatal_error zwracane przez __autoload() nie mogą być wyłapane przez blok catch. Dla przykładu:

<?
function __autoload($klasa) {
require_once $klasa . '.php';
}

$obj = new klasa1();
$obj2 = new klasa2();
?>

W poprzednich wersjach konstruktor musiał nazywać się tak samo jak klasa. Prowadziło to do pomyłek w tworzeniu klas pochodnych i ich kontruktorach. Teraz konstruktor każdej klasy nazywa się po prostu __construct() - pamiętajmy o dwóch znakach podkreślenia na początku.

Destruktory są natomiast funkcjami uruchamianymi podczas niszczenia obiektu - zazwyczaj jest to właśnie koniec skryptu. Destruktor ma postać __destruct() - również zaczyna się od dwóch znaków podkreślenia. Dla przykładu:

<?
class zarowka {
function __construct() {
echo "żarówka włączona<br />";
}
function __destruct() {
echo "zarowka wyłączona<br />";
}
}
$zar1 = new zarowka;
?>

Skrypt ten da w wyniku następujący tekst:
żarówka włączona
żarowka wyłączona
Tekst z destruktora wyświetli się w tym wypadku, ponieważ nastąpi zniszczenie obiektu (czyli zazwyczaj dzieje się to przy zakończeniu skryptu, chociaż można też to zrobić ręcznie).

W PHP4 tworzenie referencji do obiektu odbywało się za pomocą operatora &. Należało wykonać następujące przypisanie: $obiekt2 =& $obiekt1 (przy czym $obiekt2 = $obiekt1 tworzy po prostu kopię danego obiektu). W PHP5 zmieniono tę kwestię i teraz wyrażenie $obiekt2 = $obiekt1 tworzy referencję. Aby skopiować obiekt, należy użyć funkcji clone(obiekt), która jako parametr pobiera obiekt do skopiowania, a zwraca nowy obiekt ($obiekt2 = clone($obiekt1)). Dla przykładu:

<?
class zarowka {
var $wlaczona;
function __construct() {
$this->wlaczona='wlaczona';
}
function pokaz() {
return $this->wlaczona;
}
}
$zarowka1 = new zarowka;
$zarowka2 = $zarowka1;
$zarowka1->wlaczona="wylaczona";
}
echo "zarowka2 jest ".$zarowka2->pokaz();
?>

Powyższy kod wyświetli wyrażenie „zarowka2 jest wylaczona”, ponieważ $zarowka2 jest referencją do $zarowka1, a więc odnosi się do tych samych wartości zmiennych. Jeśli natomiast ktoś chciałby skopiować obiekt, należy zamiast linii $zarowka2 = $zarowka1 wstawić $zarowka2 = clone($zarowka1). Wtedy skrypt zwróci „zarowka2 jest wlaczona”, ponieważ $zarowka2 będzie zupełnie innym obiektem, operującym na innych zmiennych.

W PHP5 każda metoda klasy lub jej zmienna może być zadeklarowana za pomocą jednego z trzech słów kluczowych:

  1. public(publiczna) - zmienna lub metoda jest widoczna z całego skryptu,

  2. protected(chroniona) - zmienna lub metoda jest widoczna tylko z obiektu, w którym się znajduje, bądź z jego obiektu podrzędnego,

  3. private(prywatna) - zmienna lub metoda jest widoczna tylko z obiektu, w którym się znajduje.

Dla przykładu:

<?
class klasa1 {
public $public = 'public';
protected $protected = 'protected';
private $private = 'private';
function pokazZmienne() {
echo $this->public.'<br />';
echo $this->protected.'<br />';
echo $this->private.'<br />';
}
}

$obj1 = new klasa1;
echo $obj1->public; //działa, z zewnątrz
tylko public jest dostępna
echo $obj1->protected; //fatal error
echo $obj1->private; //fatal error
$obj1->pokazZmienne(); //pokaże wszystkie
zmienne, z wewnątrz klasy wszystkie są dostępne

class klasa2 extends klasa1 {}
$obj2 = new klasa2;
echo $obj2->public; //działa
echo $obj2->protected; //fatal error
echo $obj2->private; //zmienna niezdefiniowana
$obj2->pokazZmienne(); //pokaże $public i $protected
// ale nie $private, gdyż nie została ona odziedziczona z klasa1
?>

W powyższym przykładzie wykorzystaliśmy zmienne, ale rzecz ma się tak samo w wypadku metod w klasie. Metoda publiczna jest widoczna nawet spoza obiektu, chronione i prywatne mogą być natomiast wywołane tylko z wnętrza obiektu. Dla przykładu: $klasa->metoda() wyświetli błąd fatal terror, jeśli metoda() jest chroniona bądź prywatna.

Klasy mogą dziedziczyć tylko z jednej klasy nadrzędnej. Prowadziło to czasami do nadmiernego komplikowania drzewa klas. Aby temu zapobiec, w PHP5 wprowadzono interfejsy, które w rzeczywistości nie mają żadnych metod. Interfejs to tylko definicja metody i parametrów, które ona pobiera.

Do klasy można później zaimplementować dowolną liczbę interfejsów, pod warunkiem że w klasie zostaną zadeklarowane metody z interfejsów. Jeśli nie będzie zadeklarowana jakaś metoda wymagana przez zaimplementowany interfejs, wyświetli się fatal error. Aby dołączyć interfejs, należy się posłużyć słowem implements. Dla przykładu:

<?
interface jasnosc {
function rozjasnij($wartosc);
function sciemnij($wartosc);
}
interface dzialanie {
function wlacz();
function wylacz();
}
class zarowka implements jasnosc, dzialanie {
function rozjasnij($wartosc) {
echo "rozjaśniam o ".$wartosc."<br />";
}
function sciemnij($wartosc) {
echo "ściemniam o ".$wartosc."<br />";
}
function wlacz() {
echo "wlączam<br />";
}
function wylacz() {
echo "wylączam<br />";
}
?>

W PHP5 wprowadzono abstrakcyjne klasy i metody. Są to specjalne klasy, na podstawie których nie można utworzyć obiektu, ale da się z nich dziedziczyć (to jest właśnie ich podstawowe zadanie). Służą one przede wszystkim jako bazy do tworzenia klas pochodnych. Abstrakcyjne metody to tylko ich deklaracje. Nie mogą zawierać kodu do wykonania, muszą być natomiast rozbudowane w każdej klasie potomnej.

Klasę lub metodę deklaruje się jako abstrakcyjną, wstawiając przed jej nazwę i typ (private, public, protected) słowo kluczowe abstract. W klasie, w której zaimplementowano metodę abstrakcyjną, musi ona być deklarowana z tym samym (bądź niższym) poziomem widoczności. Oznacza to, że jeśli w klasie abstrakcyjnej metoda była chroniona (protected), to w klasie potomnej może ona być chroniona lub publiczna. W przeciwnym wypadku wygenerowany zostanie błąd fatal terror - Access level to klasa2::metoda() must be protected (as in class klasa1) or weaker. Dla przykładu:

<?
abstract class klasaBazowa {
abstract protected function func(); //wymuś
//rozbudowę metody
//w klasach pochodnych
protected function func2 () {//zwykła funkcja
echo "jakiś tekst";
}
}
class klasa2 extends klasaBazowa {
protected function func() {
echo "inny tekst";
}
}
?>

Metody i zmienne statyczne to takie, do których dostęp można uzyskać z zewnątrz (bez konieczności definiowania obiektu danej klasy). Elementy statyczne definiowane są za pomocą operatora static, a dostęp do nich musi być ustawiony na publiczny. Nie można też używać wyrażenia $this-> jako odniesienia do aktualnej klasy.

Zamiast tego używajmy self::. Zmienne z metody statycznej nie mogą być pobierane za pomocą operatora ->. Zamiast tego używajmy operatora Paamayim Nekudotayim, czyli :: (podwójnego dwukropka). Dla przykładu:

<?
class klasa {
public static $statyczna = 'coś';

public function wartosc() {
return self::$statyczna;
}
}
$klasa = new klasa;
echo klasa::$statyczna;
echo klasa::wartosc();
echo $klasa->statyczna; //nie zadziała
?>

W wypadku wywołania metody z klasy, która nie istnieje, uruchomiona zostanie metoda __call() (o ile została zadeklarowana). Może ona wykonać dowolny kod, pokazać błąd, zapisać log lub poinformować o tym, że dana metoda nie istnieje. Jeśli metoda __call nie zostanie zadeklarowana, pokaże się fatal error o treści call to an undefinied function. Dla przykładu:

<?
class klasa {
function __call ($name, $params) {
echo 'Wywołałeś metodę '.$name.'.';
}
}
$klasa = new klasa;
$klasa->jakastam();//wyświetli 'Wywołałeś metodę
jakastam, która nie istnieje'
?>

Metoda __set() służy do kontroli procesu zmiany wartości zmiennej w klasie. Ma dwa argumenty: nazwę zmiennej oraz jej nowej wartości. Dla przykładu:

<?
class klasa {
function __set ($name, $value) {
echo 'Probujesz nadać zmiennej '
.$name.' wartość '.$value;
}
}
$klasa = new klasa;
$klasa->zmienna = 'cośtam';
//wyświetli 'Próbujesz nadać zmiennej zmienna wartość cośtam'
?>

Metoda __get() przechwytuje próby uzyskania dostępu do wartości zmiennej. Dla przykładu:

<?
class klasa {
function __get ($name) {
echo 'Probujesz odczytać znienną '.$name;
}
}
$klasa = new klasa;
echo $klasa->zmienna;//wyświetli 'Probujesz
odczytać zmienną zmienna'
?>

Dzięki słowu kluczowemu final nie jest możliwe redefiniowanie metody w klasie pochodnej. Jeśli nastąpi próba redefiniowania metody finalnej, wyświetli się błąd. Dla przykładu:

final function funkcja() {/*instrukcje*/}

Operator instanceof pozwala na sprawdzenie, czy obiekt jest powiązany z daną klasą lub interfejsem. Jeśli dany obiekt jest utworzony na bazie podanej klasy, jej klasy bazowej lub klasa implementuje dany interfejs, zwracana jest wartość TRUE. Dla przykładu:

<?
interface zarowki{}
//interfejs zarowki
}
class zarowkaBazowa implements zarowki {}
//klasa zarowkaBazowa
}
class zarowka extends zarowkaBazowa {}
//klasa zarowkaBazowa
}
$zarowka = new zarowka;
if ($zarowka instanceof zarowka)
//prawda - $zarowka to obiekt klasy zarowka
echo '$zarowka jest powiązana z klasą zarowka';
if ($zarowka instanceof zarowkaBazowa)
//prawda - $zarowka to obiekt klasy zarowka
//(dziedziczonej po zarowkaBazowa)
echo '$zarowka jest powiązana z klasą zarowkaBazowa';
if ($zarowka instanceof zarowki)
//prawda - klasa zarowkaBazowa implementuje
//interfejs zarowki
echo '$zarowka jest powiązana z interfejsem zarowki';
?>

W PHP istnieje bardzo dużo funkcji pozwalających na swobodne operowanie datą i czasem. Podstawowym formatem daty jest timestamp, czyli liczba sekund, która upłynęła od 1 stycznia 1970 roku. Taki zapis daje bardzo dużo możliwości, ponieważ pozwala np. na łatwe porównywanie dwóch dat poprzez ich odjęcie. Łatwo też jest przechowywać taki zapis w bazie danych czy w pliku, a dzięki odpowiednim funkcjom PHP można go bardzo prosto zamienić na format czytelny dla użytkownika.


Najprostszą akcją jest uzyskanie aktualnej daty i czasu w formacie timestamp. Wystarczy użyć funkcji time(). Dla przykładu:

<?
$teraz = time();
?>

Czasem zachodzi potrzeba sprawdzenia szybkości jakiegoś rozwiązania, np. fragmentu kodu PHP, i porównania jego wydajności z innym. W takim wypadku oczywiście nie ma sensu używanie funkcji time(), ponieważ zazwyczaj wykonanie całego skryptu PHP trwa niecałą setną sekundy. Z pomocą przychodzi funkcja microtime(). Zwraca ona jednak dane w nieco dziwny sposób - część milisekundowa jest oddzielona spacją od części sekundowej. Poniżej zamieszczamy funkcję interpretującą dane z microtime() i zamieniającą je na coś bardziej czytelnego dla użytkownika:

<?
function getmicrotime(){
list($usec, $sec) = explode(" ",microtime());
return ((float)$usec + (float)$sec); //zwróć uwagę
na rzutowanie settype:)
}
?>

Powyższą funkcję można użyć do sprawdzenia czasu wykonywania skryptu. Dla przykładu:

<?
$start = getmicrotime();
//cały skrypt - jakieś instrukcje
$koniec = getmicrotime();
echo "Skrypt wykonał się w "
.round($koniec-$start, 5)." sekund.";
?>

Aby sprawdzić, czy data wprowadzona np. w formularzu jest poprawna, należy posłużyć się funkcją checkdate(). Pobiera ona kolejno miesiąc, dzień i rok (kolejność zapisu daty w USA) i zwraca wartość TRUE, jeśli data jest poprawna, lub FALSE w wypadku błędu. Funkcja uwzględnia takie kwestie, że np. w roku przestępnym w lutym może być 29 dni.

<?
if (checkdate($_POST[month], $_POST[day],
$_POST[year])) {
//data jest poprawna - dalsze instrukcje
}
else {
//data niepoprawna - wprowadz jeszcze raz
Header("Location:podaj_date.php?blad=1");
}
?>

Jeśli dysponujemy już zapisem daty np. wprowadzonej przez użytkownika w formularzu (dzień, miesiąc, rok, godziny, minuty i sekundy osobno), można ją łatwo zamienić na timestamp. Należy wtedy użyć funkcji mktime() i podać w jej parametrach kolejno: godzinę, minuty, sekundy, miesiąc, dzień i rok. Jako ostatni (niewymagany) parametr można podać 1 (jeśli jest to czas letni), 0 (jeżeli jest to czas zimowy) lub -1 bądź nie podawać niczego (wtedy interpreter PHP będzie sam zgadywał, jaki to czas). Dla przykładu:

<?
$time = mktime(23, 24, 11, 12, 4, 1999);
?>

Funkcja mktime() nadaje się także do poprawiania błędów w datach i wyliczania ostatnich dni miesiąca. Dla przykładu:

<?
$time = mktime(1, 1, 1, 12, 32, 1998);
$time = mktime(1, 1, 1, 13, 1, 1998);
$time = mktime(1, 1, 1, 1, 1, 1999);
$time = mktime(1, 1, 1, 1, 1, 99);
?>

Wszystkie powyższe przykłady utworzą datę 1 stycznia 1999, a błędy typu 32 grudnia czy 1 dnia 13. miesiąca roku zostaną automatycznie poprawione. Aby wyliczyć ostatni dzień danego miesiąca, należy podać jako argument dzień 0 (zero), a numer miesiąca o jeden większy. Poniższy przykład wyliczy ostatni dzień dla lutego 2000 roku.

<?
$ostatni = mktime (0,0,0,3,0,2000);
?>

W poprzednim paragrafie pokazywaliśmy, jak utworzyć datę w formacie timestamp. Jednak normalni ludzie mają niestety taką wadę, że nie potrafią czytać daty w tym formacie. Należy im wówczas zaserwować datę w formacie hh:mm:ss dd-mm-rrrr. Aby uzyskać taki efekt z formatu timestamp, należy użyć funkcji date(). Jako pierwszy argument pobiera ona format, w którym zwrócona ma być data, jako drugi zaś (opcjonalnie) datę w formacie timestamp. Funkcja zwraca datę sformatowaną zgodnie z szablonem w argumencie format. W szablonie podaje się specjalne znaki, które są później zamieniane na odpowiednie elementy daty lub czasu. Na przykład G jest zamieniane na godzinę w formacie 24-godzinnym. Oto lista wszystkich znaków do użycia w szablonie w funkcji date:

Jeśli nie chcemy, aby jakiś znak był zamieniony na swój odpowiednik, trzeba przed nim dać znak \. Jeżeli ten znak razem z \ tworzy jakiś znak specjalny, to trzeba go poprzedzić podwójnym \ (np. \t to znak tabulacji, a to znak nowej linii). Dla przykładu:

<?
echo date(?l \\t\h\e jS?);
// wyświetla tekst typu 'Saturday the 8th'
?>

Warto dodać, że choć na razie część znaków nie jest używana przez date(), to powinno się cytować wszystkie znaki, które nie mają być zamienione. Dzieje się tak, ponieważ w nowych wersjach PHP do date() wprowadzane będą coraz to nowe symbole. Dla przykładu:

<?
$today = date('\i\t \i\s \t\h\e jS \d\a\y.');
// It is the 10th day.
?>

Poniżej inny przykład użycia date(), zaczerpnięty ze strony php.net:

<?
$today = date("F j, Y, g:i a"); // March 10, 2001, 5:16 pm
$today = date("m.d.y"); // 03.10.01
$today = date("j, n, Y"); // 10, 3, 2001
$today = date("D M j G:i:s T Y"); // Sat Mar 10 15:16:08 MST 2001
$today = date("H:m:s \m \i\s\ \m\o
\t\h"); // 17:03:17 m is month
$today = date("H:i:s"); // 17:16:17
?>

Jeśli dysponujemy kontem WWW np. w Interii lub Lycos, na pewno spotkaliśmy się z klientem FTP dostępnym z poziomu przeglądarki WWW. Nie jest to jednak zbyt praktyczne rozwiązanie, szczególnie gdy chcemy umieszczać na stronie pliki o dużym rozmiarze, ale świetnie się ono sprawdza w wypadku małych zbiorów, takich jak pliki HTML. Spróbujmy coś takiego napisać.


0x01 graphic


Należy zacząć od przygotowania formularza, w którym odwiedzający wskaże pliki do wysłania. W znaczniku form należy umieścić informację, że wysyłane będą dane (enctype=„multipart/form-data”); ważne jest również to, by wartość metody ustawić na post. Na końcu należy dodać komponent typu file, dzięki któremu gość wskaże plik do wysłania.

Plik formularz.html:

<form enctype="multipart/form-data" action="up.php" method="post">
<input type="file" name="plik">
<input type="submit" value="Wyślij na serwer">
</form>

Po wysłaniu danych w pliku up.php dostępna będzie wielowymiarowa tablica $_FILES - pierwszym wymiarem będą nazwy pól file z formularza, a drugim następujące dane o wysłanym pliku:

Wyjaśnić należy chyba tylko błąd numer 2. W formularzu, z którego wysyłane będą pliki, można umieścić pole typu hidden o nazwie MAX_FILE_SIZE, gdzie należy podać maksymalną wielkość pliku do wysłania. Dla przykładu:

<input type="hidden" name="MAX_FILE_SIZE" value="30000" />

MAX_FILE_SIZE zależne jest od przeglądarki i nie można na nim polegać w stu procentach. Na przykład wczesne wersje Firefoksa nic sobie nie robiły z obecności takiego pola i pozwalały wysłać plik. PHP sprawdza jednak wielkość pliku i wyświetla błąd nr 2.

Przyszedł czas na zbiór up.php - dzięki niemu plik jest odbierany i zapisywany na serwerze.

<?
$uploaded = "var/www/uploady/". $_FILES['plik']['name'];
if (is_uploaded_file($_FILES['plik']['tmp_name'])) {
if ($_FILES['plik']['size'] > 1024*1024*1024) {
echo 'Plik jest za duży';
} else {
move_uploaded_file($_FILES['plik']['tmp_name'],$uploaded);
echo "Operacja przebiegła pomyślnie";
}
} else {
echo 'Błąd przy przesyłaniu danych!';
}
?>

Najpierw należy sprawdzić, czy plik został na pewno wysłany przez formularz. W przeciwnym wypadku istnieje możliwość ominięcia zabezpieczeń i uruchomienia np. dowolnego skryptu PHP. Jeśli plik nie został wysłany, pokazywany jest błąd. Następnie sprawdzana jest wielkość pliku, która na poniższym przykładzie nie może przekroczyć 1 MB - 1073741824 bajtów. Jeśli jego wielkość jest większa, pokazywany jest błąd. Jeżeli nie, to plik zostaje przeniesiony na właściwe miejsce i wypisywane jest potwierdzenie, że operacja przebiegła pomyślnie.
Skrypt, który odbiera plik, powinien zawierać kod określający, co stanie się z wysłanym plikiem. Możemy więc usunąć pliki za duże lub za małe. Dla przykładu:

if ($_FILES['plik']['size'] > 1073741824

lub odebrać tylko pliki graficzne:

if ($_FILES['plik']['type']=="image/jpeg"
|| $_FILES['plik']['type']=="image/gif"))

Pamiętajmy, że od PHP 4.2.0 możemy używać funkcji $_FILES['plik']['error'], aby sprawdzić, co poszło nie tak.
Jeśli podczas uploadu plików odkryjemy, że tablica $_FILES jest pusta, to oznacza, iż wielkość przesyłanego pliku przekracza wielkość ustawioną w dyrektywie post_max_size pliku php.ini.

Jeśli potrzebujemy wysłać więcej niż jeden plik z formularza, możemy się posłużyć jedną z dwóch metod: nadać każdemu komponentowi file inną nazwę lub wykorzystać tablicę. Oto zmodyfikowany plik formularz.html:

<form enctype="multipart/form-data" action="up.php" method="post">
<input type="file" name="plik[]">
<input type="file" name="plik[]">
<input type="file" name="plik[]">
<input type="submit" value="Wyślij na serwer">
</form>

A oto up.php po drobnych przeróbkach:

<?
for ($i=0; $i<sizeof($_FILES['plik']['size']); $i++) {
if ($_FILES['plik']['size'][$i] !=0) {
$uploaded = "var/www/uploady/". $_FILES['plik']['name'][$i];
if (is_uploaded_file($_FILES['plik']['tmp_name'][$i])) {
if ($_FILES['plik']['size'][$i] > 1024*1024*1024) {
echo "Plik numer $i jest za duży<br />";
} else {
move_uploaded_file($_FILES['plik']['tmp_name'][$i],
$uploaded);
echo "Operacja wysyłania pliku $i
przebiegła pomyślnie<br />";
}
} else {
echo "Błąd przy przesyłaniu danych w pliku $i!<br />";
}
}
}
?>

Dzięki sesjom możemy przechowywać pewne dane (takie jak identyfikator użytkownika czy jego login) podczas następujących po sobie wywołań strony. Działa to podobnie jak ciasteczka bez ustawionego czasu wygaśnięcia (expire) - przy czym dostęp do danych znika przy wylogowaniu się (dzięki specjalnemu przyciskowi na stronie) lub zamknięciu wszystkich okien przeglądarki. Sesje od ciastek odróżnia również to, że wszystkie dane są przechowywane nie u klienta, ale na serwerze. Na komputerze gościa trzymany jest jedynie identyfikator sesji, który znajduje się w ciasteczku lub przekazywany jest przez adres strony metodą GET.


reklama

0x01 graphic


0x01 graphic


Sesje rozpoczynamy, uruchamiając na stronie funkcję session_start(). Sprawdza ona, czy nie istnieje już rozpoczęta sesja. Jeśli tak, to pobiera identyfikator sesji z URL lub z ciastka i odtwarza wszystkie zmienne przechowywane w sesji, a jeżeli nie, to tworzy nową sesję i nadaje unikatowy identyfikator. Dostęp do zmiennych sesyjnych odbywa się poprzez obecną w PHP w wersji od 4.0.6 tablicę superglobalną $_SESSION. Dla przykładu:

<?
session_start();
if (isset($_SESSION['licznik']))
$_SESSION['licznik']++;
else
$_SESSION['licznik']=1;
echo "Oglądasz tą stronę już ".$_SESSION['licznik']." raz";
?>

Jeśli nie potrzebujemy już danej zmiennej zapisanej w sesji, używamy funkcji unset(), a zostanie ona „wymazana” z sesji. Dla przykładu:

<?
session_start();
unset($_SESSION['licznik']);
?>

Jak już wspomnieliśmy, ID sesji może być przekazane na dwa sposoby: za pomocą ciastka lub URL-u. Jako że niektóre przeglądarki mogą nie zaakceptować ciasteczek, nie należy na nich polegać w 100%. Trzeba wówczas wstawić też ID sesji do adresu strony. PHP może to zrobić sam - jeśli był kompilowany z opcją--enable-trans-sid - ale jeżeli tak się nie stało, należy ręcznie umieścić ID w adresie. Dla przykładu:

<a href="jakasstronka.php?<?=SID?>">Idź dalej</a>
<a href="jakasstronka.php?<?echo SID?>">Idź dalej</a>

Te dwie metody mają podobne działanie, chociaż pierwsza wydaje się łatwiejsza w zapisie. Tej pierwszej można jednak użyć tylko wtedy, gdy została włączona możliwość używania krótkich tagów. Dlatego pisząc skrypty pod kątem serwera, którego konfiguracji nie znamy, używajmy tej drugiej metody.

Dzięki użyciu obecnej domyślnie w PHP biblioteki GD istnieje możliwość dynamicznego tworzenia plików graficznych. Grafika taka jest tworzona przez parser PHP, a następnie wysyłana do przeglądarki tak jak zwykły plik graficzny. Dzięki GD można utworzyć grafikę w różnych formatach, m.in. JPG, GIF, WBMP i PNG, przy czym zalecane jest używanie właśnie PNG, który ostatnio zaczyna wypierać GIF-y z Sieci.


reklama

0x01 graphic


0x01 graphic


Dynamiczną grafikę wstawia się do dokumentu tradycyjnie, używając znacznika <img>. Jedyną różnicą jest to, że atrybut src wskazuje na plik z rozszerzeniem PHP. Możliwe jest nawet uruchamianie skryptu tworzącego grafikę z parametrami podanymi jako zmienne GET. Oto, jak należy wstawić przykładowy obrazek do dokumentu:

<?
echo"<img src=\"licznik.php?wizyty=".$licz."\" />";
?>

Najważniejsze jest ustawienie Content-type w nagłówku, tak aby przeglądarka mogła odpowiednio zinterpretować plik. Dla przykładu:

<?
header("Content-type: image/png");
?>

W zależności od założonego typu pliku należy ustawić odpowiedni Content-type na image/png, image/gif lub image/jpeg. Następnie należy utworzyć nowy, pusty obrazek za pomocą funkcji imagecreate(). Zwraca ona uchwyt do obrazka, coś na wzór uchwytu do pliku funkcji fopen(). Jako parametry tej funkcji trzeba podać wysokość i szerokość obrazka. Dla przykładu:

$img=imagecreate(200,200);

Można też utworzyć nowy obrazek na podstawie wzorca za pomocą funkcji imagecreatefrompng(). Jest to bardzo dobra metoda do tworzenia np. przycisków na stronie. Wystarczy utworzyć jeden obrazek z tłem i później stworzyć tylko skrypt wstawiający tekst z parametru GET na gotowe tło. Dla przykładu:

$img=imagecreatefrompng("buttony/podklad1.png");

Po zakończeniu tworzenia obrazka trzeba go wysłać do przeglądarki. Służą do tego funkcje imagepng(), imagejpeg(), imagegif() i imagewbmp(). Pobierają one dwa argumenty. Jako pierwszy należy podać wskaźnik do obrazka, a drugi (opcjonalny) to nazwa pliku (używamy go w momencie, gdy chcemy zapisać obrazek na serwerze).

W nazwach kilku funkcji obsługi grafiki występuje również format pliku, np. imagecreatefrompng czy imagepng. Oznacza to, że funkcja ta służy do obsługi danego formatu pliku. Do obsługi innych formatów służą inne funkcje, z innymi nazwami, np. imagecreatefromjpeg czy imagegif. Należy o tym pamiętać i podczas tworzenia obrazka trzymać się zawsze jednego formatu. Tworząc cały serwis, najlepiej tworzyć pliki graficzne w jednym wybranym formacie, chociaż najbardziej przyszłościowym wydaje się właśnie PNG.

Jeśli mamy już gotowy plik, warto teraz coś w nim narysować. Najpierw jednak należy zadeklarować kolory, które będą użyte podczas malowania. Do deklarowania koloru służy funkcja imagecolorallocate(), która pobiera cztery argumenty. Należy podać kolejno zmienną-obiekt z obrazkiem oraz wartości składowe: czerwoną, zieloną i niebieską wybranego koloru (od 0 do 225). Funkcja zwraca zmienną-identyfikator koloru. Dla przykładu:

<?
$orange=imagecolorallocate($img,220,210,60);
$white=imagecolorallocate($img,225,225,225);
$black=imagecolorallocate($img,0,0,0);
?>

Do wypełniania zwartych powierzchni służy funkcja imagefill(). Należy podać cztery argumenty, zmienną id obrazka, współrzędną początkową x (left), współrzędną początkową y (top) oraz id koloru. Dla przykładu:

<?
imagefill($img,0,0,$black);
?>

Aby narysować linię, należy użyć funkcji imageline(). Argumenty, które należy podać, to imageline(obrazek, x_poczatkowe, y_poczatkowe, x_koncowe, y_koncowe, kolor). Na rysunku obok widać narysowaną linię i opisane współrzędne. Dla przykładu:

<?
imageline($img,120,120,190,190,$white);
?>

Aby narysować kwadrat, należy użyć funkcji imagerectangle(). Argumenty, które podajemy, to imagerectangle(obrazek, x_poczatkowe, y_poczatkowe, x_koncowe, y_koncowe, kolor), przy czym x_początkowe i y_poczatkowe to współrzędne lewego górnego wierzchołka kwadratu, a x_koncowe i y_koncowe to współrzedne prawego dolnego wierzchołka. Istnieje też funkcja imagefilledrectangle() (te same argumenty), rysująca wypełniony kwadrat. Dla przykładu:

<?
imagerectangle($img,10,10,30,30,$orange);
imagefilledrectangle($img,30,30,50,50,$orange);
?>

Do rysowania elipsy należy użyć funkcji imageellipse(). Argumenty, które podajemy, to imageellipse(obrazek, x_srodka, y_srodka, x_szerokosc, y_wysokosc, kolor). Istnieje też funkcja imagefilledellipse() (te same argumenty), rysująca wypełnioną elipsę. Dla przykładu:

<?
imageellipse($img,180,20,20,20,$orange);
imagefilledellipse($img,20,180,20,20,$orange);
?>

Jeśli musimy narysować nieregularny kształt, należy się posłużyć funkcją imagepolygon(). Argumenty, które podajemy, to imagepolygon(obrazek, tablica_wierzcholkow, ilosc_wierzcholkow, kolor). Tablica wierzchołków to tablica przygotowana według poniższego wzorca:

<?
$wartosci=array(
50,50,// punkt 1 (x, y)
90,60,// punkt 2 (x, y)
80,100,// punkt 3 (x, y)
40,60,// punkt 4 (x, y)
);
?>

Pamiętajmy, żeby zmienna liczba_wierzchołkow miała taką samą wartość jak liczba, którą zadeklarowaliśmy w tablicy. W przeciwnym wypadku wystąpi błąd. Oczywiście, tak jak w innych sytuacjach, istnieje też funkcja tworzącą wypełniony wielobok - imagefilledpolygon(). Efekt taki jak na obrazku (biały wielobok z pomarańczowym obrysem) uzyskaliśmy, wywołując dwa razy tworzenie wieloboku o tych samych wierzchołkach: raz białego z wypełnieniem, a drugi raz pomarańczowego. Dla przykładu:

<?
imagefilledpolygon($img,$wartosci,4,$white);
imagepolygon($img,$wartosci,4,$orange);
?>

Schemat działania imagearc()
i kontynuacja 'malowanki':
Aby narysować część okręgu, należy posłużyć się funkcją imagearc(). Argumenty, które podajemy, to imagearc(obrazek, x_srodka, y_srodka, szerokosc, wysokosc, kat_poczatka, kat_konca, kolor). Łuk jest zawsze rysowany od poziomu (jak na schemacie) zgodnie z ruchem wskazówek zegara. Dla przykładu:

<?
imagearc($img,120,120,100,100,90,360,$orange);
?>

Łuki wypełnione
IMG_ARC_PIE IMG_ARC_CHORD IMG_ARC_NOFILL
IMG_ARC_EDGED IMG_ARC_EDGED+IMG_ARC_NOFILL
W poprzednich funkcjach, żeby otrzymać figurę wypełnioną, wystarczyło zmienić nazwę funkcji, dodając filled. Aby narysować wypełniony łuk, należy się również posłużyć funkcją imagefilledarc(). Oprócz parametrów opisanych przy imagearc() musimy również na końcu dodać jeszcze styl wypełnienia. Cztery dostępne style to:

Dla przykładu:

<?
imagefilledarc($img,40,30,100,100,30,120,$orange,IMG_ARC_PIE);
?>

Style można łączyć operatorem + (tworząc w ten sposób nowe style). Najlepszym przykładem na to jest połączenie IMG_ARC_NOFILL oraz IMG_ARC_EDGED, dzięki któremu otrzymujemy sam łuk i linie łączące jego końce ze środkiem (bez wypełnienia). Warto też samemu eksperymentować z różnymi kombinacjami stylów, które dają czasem dosyć ciekawe efekty.

Do dodawania do obrazka napisów służy kilka funkcji. Pierwsza z nich to imagechar(). Dodaje ona do obrazka tylko jeden znak. Jej argumenty to imagechar(obrazek, wielkosc, x_poczatkowe, y_poczatkowe, znak, kolor). Wielkość to cyfra od 1 do 5 - 1 to najmniejsza, 5 to największa. Dla przykładu:

<?
imagechar($img,3,80,80,'P',$orange);
?>

Istnieje też druga funkcja dodająca po jednym znaku do obrazka. To imagecharup(). Pobiera ona te same parametry, a różni się od poprzedniej tylko tym, że rysuje znak pionowo.

Aby dodać jakiś tekst do obrazka, nie trzeba bawić się w dodawanie po kolei każdego znaku. Wystarczy uzyć funkcji imagestring(). Parametry, które podajemy, są takie same jak w wypadku imagechar(), tylko zamiast pojedynczego znaku podaje się cały ciąg. Dla przykładu:

<?
imagestring($img,5,80,80,'PHP jest OK',$orange);
?>

Aby dodać tekst pionowo, wystarczy (podobnie jak w wypadku jednego znaku) użyć funkcji imagestringup().

Domyślna czcionka w imagestring() nie jest zbyt ciekawa. Czasem zachodzi potrzeba wypisania czegoś bardziej ciekawym fontem. Z pomocą przychodzi wtedy funkcja imagettftext(), dzięki której możemy tworzyć napisy pisane dowolną czcionką True Type. Oto argumenty, które trzeba podać do funkcji:

imagettftext(obrazek, wielkosc,
kat, x_poczatkowe, y_poczatkowe,
kolor, plik_z_czcionka, tekst)

Wielkość to tym razem nie liczba od 1 do 5, a liczba od 5 do 150 reprezentująca wielkość w pikselach, taką jaką znamy z np. Worda. Kąt to kąt nachylenia tekstu. Wyrażenie 0 stopni to tekst pisany normalnie - poziomo. Tym razem jednak tekst jest obracany o dany kąt przeciwnie do ruchu wskazówek zegara. Zmienne x_poczatkowe i y_poczatkowe to współrzędne lewego (dolnego) wierzchołka tekstu (a nie lewego górnego, tak jak w imagestring()!). W parametrze plik_z_czcionka należy zaś podać adres do pliku .TTF z czcionką True Type. Czcionki True Type to czcionki używane w systemie - wystarczy skopiować taki plik z folderu C:\WINDOWS\FONTS i wgrać go na własne konto ze stroną. Dla przykładu:

<?
imagettftext($img,10,315,30,
30,$orange,"folder/acmesa.ttf","czcionka A.C.M.E - test");

imagettftext($img,10,20,30,
140,$white,"folder/bluehigb.ttf","czcionki True Type");
?>

Pokażemy teraz, jak utworzyć plik licznik.php pobierający liczbę odwiedzin z parametru wizyty i wyświetlający graficznie liczbę odwiedzin. Najpierw należy ustawić odpowiednią wartość Content-type. Później kopiujemy zawartość zmiennej wizyty z tablicy _GET do zmiennej $wizyty, a w zmiennej $dl zapisujemy długość ciągu zmiennej $wizyty. Kolejno tworzymy obrazek o odpowiedniej szerokości (liczba cyfr*20) i definiujemy kolory. Pokrywamy tło na czarno i w pętli for wyświetlamy po kolei każdą cyferkę, używając funkcji imagettftext(). Na koniec wysyłamy gotowy obrazek, używając imagepng(). Dla przykładu:

<?php
header("Content-type: image/png");
$wizyty=$_GET['wizyty'];
$dl=strlen($wizyty);
$img=imagecreate(($dl*20),40);//szerokość zależna od liczby cyfr
$orange=imagecolorallocate($img,220,210,60);//deklaracja kolorów
$black=imagecolorallocate($img,0,0,0);
imagefill($img,0,0,$black);//wypełnienie tła
for($i=0;$i$dl;$i++) {
imagettftext($img,15,0,$i*20+5,27,$orange,"gd/bluehigb.ttf",
substr($wizyty,$i,1));
}
imagepng($img);//wysłanie obrazka do przeglądarki
?>

Aby teraz wstawić to do dokumentu, należy użyć znacznika <img>. Dla przykładu:

<img src="licznik.php?wizyty=12345" />

a tam, gdzie jest wpisane 12345, wpisać nazwę zmiennej z liczbą do wyświetlenia.

Referencje pozwalają na utworzenie dwóch zmiennych o tej samej wartości i odwołujących się do jednego miejsca w pamięci. Jest to rozwiązanie problemu dublowania pamięci podczas procesu zwykłego przypisania (poniżej pokazujemy przypisanie i referencję). Dla przykładu:



<?
$b = $a; //zwykłe przypisanie
$b = & $a; //utworzenie referencji
?>

W referencji podczas zmiany wartości zmiennej $b zmienia się też $a i na odwrót, podczas gdy po zwykłym przypisaniu zmienne $a i $b mogą przyjmować inne wartości.

Kolejnym plusem referencji jest możliwość odwoływania się do zmiennych globalnych w funkcji bez użycia słowa global. Dla przykładu:

<?
function podniesOJeden (&$zmienna) {
$zmienna++;
}
$a=5;
podniesOJeden($a);
?>

Po takiej operacji wartość zmiennej $a będzie wynosiła 6. Pomimo że w funkcji zmienna $zmienna nie jest globalna, to jest ona referencją do zmiennej $a i obie odwołują się do tej samej wartości.

Czasem zachodzi potrzeba usunięcia referencji. Poniższy kod może zostać uruchomiony bez obawy o wartość zmiennej $b, ponieważ niszczona jest właśnie referencja - dowiązanie zmiennej do jej wartości, a nie jej zawartość, która dalej zostaje dostępna poprzez $b. Dla przykładu:

<?
$a=1;
$b = & $a;
unset($a);
?>

W PHP5 dodano obsługę błędów podobną do tej np. z Javy, czyli bazującą na wyjątkach. Opiera się ona na trzech słowach kluczowych: try, catch i throw. Obsługa wyjątków zapisana jest w klasie Exception. Dzięki takiej obsłudze można zapisać błędy do logów albo wyświetlić wszystkie naraz na końcu działania skryptu. Dla przykładu:


reklama

0x01 graphic


0x01 graphic


<?
function dziel($a, $b) {
if ($b==0) {
throw new Exception("niedozwolone dzielenie przez zero");
}
return (int)$a/(int)$b;
}
try {
dziel(5, 0);
}
catch (Exception $e) {
echo "Błąd to: ".$e->getMessage();
}
?>

W powyższym przykładzie zadeklarowaliśmy funkcję dziel, która (jak łatwo się domyślić) służy do dzielenia dwóch liczb całkowitych przez siebie. Pobiera ona dwa argumenty (liczby) i jeśli drugi z nich wynosi zero, zwraca błąd. Ale to nie jest po prostu błąd typu echo „błąd!”.

W przykładzie w bloku try zamykamy taki kod, który może zwrócić błąd (w naszym wypadku wywołanie funkcji dziel(), która może wygenerować wyjątek). Zaraz po try jest catch, w którym decydujemy, co mamy zrobić z wyjątkiem (o ile wystąpił). W zwykłym nawiasie (po catch) podajemy nazwę klasy obsługi wyjątków i zmienną, która stanie się obiektem tej klasy.

Klasa Exception przedstawia się następująco (za php.net):

<?
class Exception {
protected $message = 'Nieznany wyjątek'; // treść błędu
protected $code = 0; // kod błędu
protected $file; // nazwa pliku z wyjątkiem
protected $line; // linia z wyjątkiem

function __construct($message = null, $code = 0);

final function getMessage(); // wyświetla błąd
final function getCode(); // wyświetla kod błędu
final function getFile(); // wyświetla nazwę pliku z błędem
final function getLine(); // wyświetla linię z błędem
final function getTrace(); // więcej tutaj
final function getTraceAsString(); // getTrace() jako string

function __toString(); // sformatowany błąd do wyświetlenia
}
?>

Klasę Exception można rozbudować o własne metody lub nadpisać już w niej istniejące (można nadpisać __construct() i __toString()). Jeśli nadpisujemy oryginalny konstruktor klasy Exception, powinniśmy wywołać parent::__construct(), aby być pewnym, że wszystkie zmienne zostały poprawnie przydzielone. Oto przykład pokazujący, jak można rozbudować klasę Exception:

<?
class extendedException extends Exception {
function __toString() {
echo "Błąd to: ".$this->getMessage()."<br />";
echo "Wystąpił w linijce ".$this->getLine().?<br />";
echo "i w pliku ".$this->getFile();
}
}
?>

Teraz przerabiamy kod z powyższego przykładu, ale korzystamy z klasy extendedException:

<?
function dziel($a, $b) {
if ($b==0) {
throw new extendedException("niedozwolone
dzielenie przez zero");
}
return (int)$a/(int)$b;
}
try {
dziel(5, 0);
}
catch (extendedException $e) {
echo $e->__toString();
}
?>

Powyższy kod wyświetli następujący błąd:
Błąd to: niedozwolone dzielenie przez zero
Wystąpił w linijce 9
i w pliku /var/www/kurs/kurs.php

Na zakończenie tego rozdziału powiedzmy jeszcze kilka słów na temat metody getTrace(). Zwraca ona bardzo rozbudowane statystyki dotyczące błędu w postaci wielowymiarowej tablicy asocjacyjnej. Pokażmy za jej pomocą statystyki błędu w dzieleniu przez 0, wyświetlone przy użyciu funkcji print_r($e->getTrace()).

Array
(
[0] => Array
(
[file] => c:\usr\krasnal\www\webplanet\kurs\test5.php
[line] => 14
[function] => dziel
[args] => Array
(
[0] => 5
[1] => 0
}
}
}

Jeden odpowiednio wykorzystany przez hakera błąd w naszym skrypcie może pozwolić na odczytanie haseł użytkowników bazy danych czy zniszczenie całej struktury plików na serwerze. Jeśli nie chcemy, aby tak się stało i nasz serwis został zaatakowany po dwóch miesiącach żmudnego programowania, stosujmy się do poniższych rad.

reklama

0x01 graphic


0x01 graphic


Najbardziej niebezpieczne są funkcje include() oraz require(), gdy są użyte w nieodpowiedni sposób. W skrypcie najczęściej zawarty jest taki oto kod:

<?
include($_GET['podstrona']);
?>

Teraz wystarczy, że (przy ustawieniu safe_mode na off - zazwyczaj tak właśnie jest) wywołamy adres index.php?file=/etc/passwd i... nazwa plików mówi sama za siebie - na serwerze unikowym wyświetli się plik z hasłami użytkowników. Aby zachować podstawowy poziom bezpieczeństwa, należy zrobić coś takiego:

<?
include("includes/".$_GET['nazwa'].".php");
?>

Wtedy rozszerzenie będzie na pewno .php i plik będzie „includowany” z foldera ze skryptem. Nie jest to jednak rozwiązanie najlepsze, więc dobrze jest w takim wypadku korzystać ze switch. Dla przykładu:

<?
switch($_GET['nazwa']) {
case "index": include("index.php");break;
case "o_mnie": include("omnie.php");break;
case "download": include("download.php");break;
default: echo "Błąd - taki plik nie istnieje";
}
?>

W poprzedniej części wspomnieliśmy o trybie safe_mode. Jest to specjalna dyrektywa ustawiana w pliku php.ini (konfiguracja środowiska PHP na serwerze), która określa, czy PHP ma być uruchamiane w trybie bezpiecznym czy nie. Jeśli tylko mamy dostęp do tego pliku, warto ustawić tryb safe_mode na on. Jeśli safe_mode jest włączony, to użytkownik nie ma dostępu do plików, których nie jest właścicielem (tak więc problem tego nieszczęsnego /etc/passwd jest rozwiązany - nikt nie odczyta haseł z serwera).

Druga dyrektywa to open_basedir - określa ona folder, powyżej którego w drzewie folderów nie mamy dostępu. Jeżeli więc ustawione jest np. /var/www/users, to skrypt nie będzie miał dostępu np do /var/www. Najlepiej jest mieć te dwie dyrektywy ustawione (włączone), a jeśli nie jest to możliwe, to należy włączyć safe_mode.

Najbardziej zagrożonymi na ataki są te fragmenty skryptów, które operują na danych wprowadzonych przez użytkownika w formularzach lub przekazywanych metodą $_GET. Niezbędnym minimum jest w takim wypadku sprawdzenie, czy typ danych jest zgodny z tym, którego oczekujemy.

Jeśli czekamy na liczbę całkowitą, aby wykorzystać ją np. do wygenerowania zapytania do bazy danych, ważne jest, aby była to właśnie liczba, a nie ciąg tekstu, który może nam poważnie namieszać w bazie danych (atak SQL Injection). W tym wypadku należy daną - pobraną z formularza lub przesłaną za pomocą GET - rzutować do oczekiwanego typu zmiennej. Dla przykładu:

<?
$doBazy = (int) $_GET['zFormularza'];
?>

Użycie bazy danych to dobra alternatywa wobec plików, zwłaszcza przy budowaniu dużych serwisów czy złożonych statystyk. Pobieranie i zapisywania informacji w bazach danych opiera się na wysyłaniu do serwera bazy zapytań zapisywanych w języku SQL (Structured Query Language) zawartych w kodzie PHP, na które serwer odpowiada, pobierając z bazy odpowiednie dane i wysyłając je do nas. Zrozumienie tego na pewno ułatwi poniższy schemat:


reklama

0x01 graphic


0x01 graphic


  1. Klient wysyła do serwera zapytanie o stronę WWW,

  2. Serwer WWW wysyła do parsera kod PHP,

  3. Parser wysyła do serwera baz danych zapytanie o jakieś dane,

  4. Serwer baz zwraca dane, które są używane dalej w skrypcie,

  5. Parser wysyła gotowy kod HTML do serwera WWW,

  6. Serwer WWW wysyła kod HTML do przeglądarki klienta.

Przy pisaniu kodu PHP z użyciem baz danych trzeba pamiętać, że jeśli serwer PHP nie znajduje się (fizycznie) na tym samym serwerze (czyli nie ma możliwości odwołania się do niego poprzez localhost), tylko np. w innej części Internetu, może to znacznie zwolnić pracę skryptu. Podczas naszych standardowych testów po uruchomieniu strony z połączeniem do serwera bazy MySQL na innym hoście czas generowania strony wyniósł około 2,5 sekundy, a podczas gdy baza znajdowała się na serwerze lokalnym, nie trwało to nawet 0,01 sekundy.

Schemat obrazuje strukturę bazy danych. Najważniejszym elementem jest oczywiście sama baza - zazwyczaj jedna przydzielana jest dla każdego użytkownika, chociaż na serwerach komercyjnych dostaje się czasem nawet i dziesięć baz. Ingerować w bazy (tworzyć, usuwać) może zazwyczaj tylko administrator.

Kolejnym elementem są tabele - to są dopiero struktury przechowujące dane. Są one dowolnie modyfikowane przez użytkownika i może ich być dowolna liczba, bo ograniczenia są zazwyczaj nakładane przez firmy hostingowe na wielkość całej bazy, a nie na liczbę tabel.
Aby ułatwić naukę obsługi bazy MySQL-a i tworzenia własnych przykładów, we wszystkich przykładach tworzona będzie i rozbudowywana baza danych fikcyjnych uczniów jakiejś klasy oraz skrypty ją obsługujące.

Przed wykonywaniem operacji na bazie danych należy się z nią połączyć. Trzeba użyć funkcji mysql_connect(host[:port], nazwa użytkownika, hasło). Funkcja ta łączy się z serwerem podanym jako argument host. Opcjonalnie, jeśli MySQL pracuje na innym porcie niż 3306, należy też to podać. Dla przykładu:

<?
$sql = mysql_connect('serwer_baz.org', 'login', 'moje_haslo');
?>

Zmienna $sql podana na początku to uchwyt (handle) do połączenia z bazą. Ponownie potrzebny będzie tylko na końcu wykonywania skryptu, aby rozłączyć się z bazą:

<?
mysql_close($sql);
?>

Jeśli chcemy utworzyć duży serwis, korzystający z bazy i często odświeżany, warto połączyć się z bazą funkcją mysql_pconnect (permanent connect - stałe połączenie), dzięki któremu nie trzeba się za każdym razem łączyć z bazą. Wszystkie argumenty są te same. Występuje tutaj tylko jedna różnica: połączenia pconnect nie trzeba zamykać na końcu skryptu funkcją mysql_close.

Z bazą „kontaktujemy się” za pomocą zapytań. Są to polecenia pisane w języku SQL (bardzo proste) wysyłane dzięki funkcji mysql_query, która zwraca zmienną z wynikami zapytania. Najczęściej używane tutaj nazwy zmiennych są widoczne na przykładzie, ale oczywiście możemy ustalić swoje własne. Dla przykładu:

<?
$query="ZAPYTANIE";
$res = mysql_query($query);
?>

Jeśli mamy dostęp do serwera, możemy sobie założyć bazę. Jedyną sprawą, o której należy pamiętać, jest wywołanie funkcji mysql_create_db(nazwa), która tworzy bazę o nazwie podanej w argumencie. Po utworzeniu i przed rozpoczęciem pracy należy pamiętać, aby też wybrać bazę, z którą chcemy pracować. Robimy to za pomocą polecenia mysql_select_db(nazwa). Dla przykładu:

<?
mysql_create_db("klasa");
mysql_select_db("klasa");
?>

Gdy założyliśmy już bazę danych, kolejnym krokiem jest utworzenie tabel. W tym celu budujemy zapytanie o następującej składni:

"CREATE TABLE nazwa (nazwa1 typ1, nazwa2 typ2,
nazwa3 typ3, nazwa4 typ4, PRIMARY KEY(nazwa))"

Możemy teraz utworzyć tabelę według podanego powyżej zapytania.

"CREATE TABLE uczniowie (id char(2) NOT NULL
AUTO_INCREMENT, imie char(20) NOT NULL,
nazwisko char(40) NOT NULL, urodzony date,
PRIMARY KEY(id) )"

Stworzona zostanie tabela o nazwie uczniowie z polami id, imie, nazwisko, urodzony, przy czym id będzie kluczem podstawowym, czyli indeksem, i nie będzie się mógł powtarzać. Oprócz tego, jeśli dodamy NOT NULL, to klucz nie będzie mógł być pusty. Opcja AUTO_INCREMENT sprawia natomiast, że jeśli nie podamy wartości id do wpisania, to zostanie ona automatycznie wpisania (wartością stanie się wartość poprzedniego rekordu id podniesiona o jeden).
Dostępnych jest wiele rodzajów pól - oto kilka najważniejszych:

Aby wstawić jakieś dane do tabeli, należy posłużyć się instrukcją INSERT. Jej schemat jest następujący:

"INSERT INTO nazwa_tabeli (pole1, pole2, pole3)
VALUES (wart_pola1, wart_pola2, wart_pola3)"

Jako pole1, pole2 itd. wpisujemy nazwy pól, które chcemy wypełnić, a w wart_pola1 itd. w odpowiedniej kolejności wartości do dodania. Dla przykładu:

"INSERT INTO uczniowie (imie, nazwisko, dziennik)
VALUES ('Kornel', 'Kornelowicz', '1989-12-12')"

W powyższym wypadku wartość pola id zostanie ustalona na 1, ponieważ jest to pierwszy rekord w tabeli. Jeśli wstawialibyśmy to samo jeszcze raz, to miałby on cały czas wartość id o jeden większą od poprzedniej. Wstawmy do bazy kilka pojedynczych rekordów z danymi uczniów:

<?
$res = mysql_query("INSET INTO uczniowie
(imie, nazwisko, dziennik) VALUES ('Wacek', 'Kapusta', '2')");
$res = mysql_query("INSET INTO uczniowie
(imie, nazwisko, dziennik) VALUES ('Jurek', 'Ogórek', '3')");
$res = mysql_query("INSET INTO uczniowie
(imie, nazwisko, dziennik) VALUES ('Wojtek', 'Jarząbek', '4')");
$res = mysql_query("INSET INTO uczniowie
(imie, nazwisko, dziennik) VALUES ('Sebastian', 'Warzywo', '5')");
?>

Aby wybrać z bazy danych jakieś dane, należy posłużyć się operatorem SELECT. Jego składnia to:

SELECT pole1, pole2, pole_n FROM nazwa_tabeli
WHERE warunek1 AND warunek2 ORDER BY
nazwa_klucza rodzaj_sortowania LIMIT
klucz_poczatkowy, ilosc_rekordów

Jest to dość zawiłe, dlatego teraz po kolei wytłumaczymy całe zapytanie.

Na schemacie pokazujemy przykład użycia LIMIT. W wynikach jest 16 rekordów, potrzebne są rekordy od 3 do 14. Ważne jest, aby się nie pomylić i w ilosc_rekordow podać całkowitą liczbę potrzebnych rekordów, a nie id ostatniego (tak jak w większości baz danych czy na adresowaniu komórek w Excelu). Dla przykładu:

<?
$res = mysql_query("SELECT * FROM uczniowie
ORDER BY dziennik ASC");
$res = mysql_query("SELECT imie FROM uczniowie
WHERE nazwisko = 'Kapusta'");
$res = mysql_query("SELECT * FROM uczniowie
WHERE dziennik > 5");
//a oto popularny przykład na wyświetlanie porcji
//wyników, np z księgi gości
$res = mysql_query("SELECT * FROM księga
ORDER BY id ASC LIMIT $start, 10");
?>

W warunkach do przeszukiwania mogą też wystąpić takie operatory, jak IN lub BETWEEN. Dla przykładu:

<?
$res = mysql_query("SELECT * FROM uczniowie
WHERE id=5 OR id=7 OR id=10");
//te dwa przykłady to to samo
$res = mysql_query("SELECT imie FROM uczniowie
WHERE id IN(5,7,10)");
$res = mysql_query("SELECT * FROM uczniowie
WHERE dziennik > 5 AND dziennik < 20");
//i to też to samo - between określa zakres
$res = mysql_query("SELECT * FROM uczniowie
WHERE dziennik BETWEEN 5 AND 20");
?>

Zastępcą znanego z instrukcji warunkowych wykrzyknika jest NOT. Można go użyć zarówno przed pojedynczą instrukcją, jak i przed całym nawiasem. Dla przykładu:

<?
$res = mysql_query("SELECT imie FROM uczniowie
WHERE id NOT IN(5,7,10)");
//zwraca wszystkich uczniów o id różnym od 5, 7 lub 10
$res = mysql_query("SELECT * FROM uczniowie
WHERE dziennik NOT BETWEEN 5 AND 20");
//zwraca wszystkich uczniów o id mniejszym od 5 i większym od 20
?>

Nie zawsze da się dokładnie porównać dwa ciągi znaków. Czasem należy posłużyć się operatorem LIKE. Porównuje on konkretną wartość ze wzorcem. Zaznaczmy przy tym, że % (procent) zastępuje w wyrażeniu dowolną liczbę znaków, a _ (podkreślenie) zastępuje dokładnie jeden znak. Dla przykładu:

<?
$res = mysql_query("SELECT * FROM uczniowie
WHERE imie LIKE '%ek' AND nazwisko LIKE '_apusta'");
?>

Czasem zachodzi potrzeba poprawienia jakiegoś rekordu. Błąd może być wynikiem nieuwagi przy wprowadzaniu danych, ale możemy też chcieć zmienić adres klienta w bazie danych sklepu internetowego. Służy do tego polecenie UPDATE. Jego składnia to:

UPDATE nazwa_tabeli SET pole1='xyz',
pole2 = 'abc', polen = 'bcd' WHERE warunki

Przyjrzyjmy się bliżej temu zapytaniu:

Dla przykładu:

<?
$res = mysql_query("UPDATE uczniowie
SET dziennik = 11 WHERE id = 3");
$res = mysql_query("UPDATE uczniowie
SET dziennik = 12 WHERE id = 2");
?>

Do usuwania jednego, kilku określonych rekordów lub zawartości całej tabeli służy DELETE. Jego składnia to:

DELETE FROM nazwa_tabeli WHERE warunki

Oznaczenia są takie same jak w poprzednich poleceniach i nie trzeba ich jeszcze raz opisywać. Warto zaś zaznaczyć, że jeśli niepodany zostanie warunek WHERE, to tabela zostanie wyczyszczona. Dla przykładu:

<?
$res = mysql_query("DELETE
FROM uczniowie WHERE id = 3");
$res = mysql_query("DELETE
FROM uczniowie WHERE id > 15");
?>

W PHP istnieje wiele funkcji do kontaktu z bazą danych MySQL i obsługi zapytań. Podstawowe już znamy:

Funkcja mysql_num_rows(rezultat) zwraca liczbę rekordów z wyniku zapytania skierowanego do serwera bazy. Poniższy przykład wyświetla liczę rekordów w tabeli uczniowie:

<?
$res = mysql_query("SELECT * FROM
uczniowie");
$ile = mysql_num_rows($res);
echo $ile;
?>

Funkcja mysql_fetch_array(rezultat [,typ]) zwraca przy każdym wywołaniu kolejny wiersz z wyniku zapytania w postaci tablicy zwykłej lub asocjacyjnej (lub jednej i drugiej). Typ tablicy zależy właśnie od parametru typ. Może on przybrać wartość MYSQL_ASSOC (zwraca wynik w postaci tablicy asocjacyjnej), MYSQL_NUM (w postaci zwykłej tablicy) albo (domyślnie) MYSQL_BOTH (tablicy indeksowanej asocjacyjnie i numerycznie). Dla przykładu:

<?
$res = mysql_query("SELECT
* FROM uczniowie");
while($row =
mysql_fetch_array($res, MYSQL_BOTH)) {
echo ?ID to ?.$row[id].? a jego imie to: ?.$row[1];
}
?>

Funkcja mysql_insert_id() zwraca ID wygenerowane dla pola z własnością AUTO_INCREMENT. Uwaga! Funkcja mysql_insert_id() zwróci 0, gdy rekord był dodawany do tabeli bez pola AUTO_INCREMENT. Dla przykładu:

<?
$res = mysql_query("INSERT INTO
uczniowie (imie, nazwisko, dziennik)
VALUES ('Justyna', 'Koma', 13)");
$id = mysql_insert_id();
echo "ID ostatnio dodanego rekordu to: ".$id;
?>

mysql_result(rezultat ,wiersz [,kolumna])- Bardzo przydatna funkcja, gdy trzeba się odwołać do konkretnego pola w konkretnym rzędzie wyniku. Wystarczy podać jako pierwszy argument zmienną z rezultatem zapytania, jako drugi wiersz, a jako trzeci (opcjonalnie)pole w danym wierszu (domyślne to 0).

<?
$res = mysql_query("SELECT * FROM uczniowie");
echo mysql_result($res, 0, 1);
//wyświetli imię pierwszego ucznia w tabeli
?>

Funkcja mysql_error() pokazuje treść ostatniego błędu wygenerowanego przez serwer MySQL. Warto wywołać tę funkcję po zapytaniu do bazy, które powoduje błędy. Dla przykładu:

<?
$res = mysql_query("SELECT * FROM ucnziowie");
echo mysql_error();
?>

wyświetli coś w stylu „#1146 - Table 'baza.ucnziowie' doesn't exist”.

Częstym błędem początkującego programisty jest próba wykonania następującego kodu:

echo $res;

Niestety, $res jest niejako obiektem zawierającym zgrupowane dane, a więc za pomocą echo nie da się ich bezpośrednio wyświetlić. Istnieją dwie podstawowe metody przeglądania danych z zapytania do MySQL-a. Pierwsza - wiersz po wierszu - i druga - bezpośrednio odwołując się do danego wiersza lub kolumny.

1. Pętla while i mysql_fetch_array
Dla przykładu:

<?
$res = mysql_query("SELECT * FROM config");
while($row = mysql_fetch_array($res, MYSQL_BOTH)) {
echo "config_name to ".$row[0]." a jego wartość to:
".$row[config_value];
}
?>

Przy każdej iteracji pętli zmienna $row staje się czteroelementową tablicą. Dzięki ustawionemu MYSQL_BOTH do rekordów mamy dostęp zarówno poprzez nazwy indeksów (np. config_value), jak i poprzez kolejne cyfry (w danym wypadku config_name to 0, a config_value to 1). Ważne jest to, by wybrać wygodniejszy dla siebie sposób, pamiętając o wadach każdego z nich (MYSQL_ASSOC to więcej pisania, przy MYSQL_NUM natomiast podczas zmiany zapytania może się „posypać” cały skrypt).

2. Funkcja mysql_result()
Metoda ta jest dobra przy pobieraniu pojedynczego elementu z dużego zapytania do bazy. Jej zaletą jest to, że nie trzeba przechodzić przez całą bazę, wadą jest natomiast konieczność znajomości dokładnej lokalizacji wyniku w zapytaniu (współrzędne wiersz i kolumna).
Dla przykładu:

<?
$res = mysql_query("SELECT * FROM config");
echo mysql_result($res,3,1); //wyświetli po prostu '25'
?>

Do administrowania swoją bazą danych, a w szczególności całym serwerem i wszystkimi bazami, polecamy używania darmowego skryptu phpMyAdmin. Pozwala on na bardzo szybki i sprawny nadzór nad bazami, tabelami i użytkownikami baz.

Smarty to system szablonów przygotowany dla PHP. Jego głównym zadaniem jest oddzielenie logiki skryptu (obliczeń, pobierania danych z bazy) od treści wyświetlanych na stronie. Po pierwsze, zwiększa to czytelność samego kodu - bardzo denerwujące jest, gdy fragmenty kodu są poprzedzielane blokami echo wyświetlającymi kilka znaków - a ponadto ułatwia pisanie aplikacji w zespołach. Załóżmy, że nad stroną gazety internetowej pracują dwie osoby: programista i grafik. Jeśli grafik chce zmienić layout strony wyświetlającej artykuły, nie musi zmieniać jej kodu. Wystarczy, że zmieni szablon, i gotowe. I sprawa odwrotna: programista, chcąc zmienić skrypt, nie musi się bać, że precyzyjnie zaprojektowana strona przygotowana przez grafika się rozsypie - zmienia kod PHP, nie ingerując przy tym w layout.

reklama

0x01 graphic


0x01 graphic


Aby zainstalować Smarty, trzeba przekopiować folder libs z archiwum smarty do folderu ze stroną. Następnie należy założyć cztery foldery: templates, templates_c, cache i configs, a później w pliku, gdzie chcemy wykorzystać Smarty, „includujemy” klasę libs/Smarty.class.php (uwaga na wielkość liter w systemach uniksowych!). Na końcu wystarczy utworzyć nowy obiekt na bazie klasy Smarty. Dla przykładu:

<?
require('libs/Smarty.class.php');
$smarty = new Smarty;
?>

System szablonów nie może się obyć bez… szablonu. Tworzymy więc pierwszy szablon i zapisujemy go w folderze templates. Dla przykładu:

Plik index.tpl:

Moje imię to:
{$imie}

Przypisujemy nową zmienną szablonu poprzez metodę assign() klasy Smarty. Pobiera ona dwa argumenty: nazwę zmiennej i jej wartość. Następnie wyświetlamy szablon za pomocą display(). Dla przykładu:

<?
require('libs/Smarty.class.php');
$smarty = new Smarty;
$smarty->assign('imie', 'Jacuś');
$smarty->display('index.tpl');
?>

Po uruchomieniu pliku index.php zobaczymy następującą treść:

Moje imię to:
Jacuś

Domyślnie wszystkie tagi zamykają się w znakach { } (nawiasy klamrowe). Wszystko, co znajduje się poza nimi, wyświetlane jest bez zmian, tagi są natomiast parsowane przez Smarty.

Komentarze mieszczą się pomiędzy tagami {* i *}. Służą do organizowania struktury szablonu i nie są później w żaden sposób widoczne w kodzie gotowej strony. Dla przykładu:

{* Pokazuje imię ze zmiennej z pliku index.php *}
Moje imię to:
Jacuś

Zmienne są oznaczane, podobnie jak w PHP, symbolem dolara. Można je wyświetlać na kilka sposobów:

Każdy tag Smarty albo wyświetla jakąś zmienną, albo uruchamia funkcję. Zmienne już znamy - teraz czas na funkcje. Dla przykładu:

{config_load file="kolorki.conf"}
{include file="gora.tpl"}
{if $podswietlenie}
Witaj, <font color="{#kolorek1#}">{$imie}!</font>
{else}
Witaj, {$imie}!
{/if}
{include file="dol.tpl"}

Są dwa rodzaje funkcji: wbudowane (np. if), których nie można modyfikować, i funkcje modyfikowalne, które są dodawane poprzez system plug-inów (wtyczek). Możemy je łatwo modyfikować, dostosowywać do swoich potrzeb i tworzyć nowe. Są to np. html_options czy html_select_date.

Delimiterem domyślnie są znaki: { oraz }. Problem zaczyna się, gdy chcemy używać w szablonie np. JavaScriptu, który też korzysta z nawiasów klamrowych. Aby zmienić domyślne delimitery, podmieńmy zmienne left_delimiter i right_delimiter klasy Smarty. Dla przykładu:

<?
require('libs/Smarty.class.php');
$smarty = new Smarty;
$smarty->left_delimiter= '<!--{';
$smarty->right_delimiter= '}-->';
$smarty->assign('tekst', 'cośtam');
$smarty->display('index.tpl');
?>

plik index.tpl:

{* Inny delimiter *}
<script type="text/javascript">
<!--
if (warunek_spełniony){
alert("<!--{$tekst}-->"); //zadziała
}
//-->
</script>