Uwaga: zakładam u czytelników pewną minimalną znajomość HTML. Z wyjatkiem formularzy nie jest to dokumentacja HTML --- jest mnóstwo tutoriali w sieci.
Program CGI jest to program wywoływany przez serwer WWW. Program CGI otrzymuje dane od serwera i produkuje w wyniku stronę, wysyłaną przez serwer do przeglądarki. Interfejs używany przy komunikacji między takimi programami a serwerem WWW nosi nazwę CGI (Common Gateway Interface).
Serwer WWW interpretuje komunikaty HTTP pochodzące z przeglądarki. Niektóre w tych komunikatów zawierają żądanie uruchomienia programu (CGI). Część komunikatu określa nazwę programu i jego położenie, a inna część jego argumenty.
To, jakie adresy URL są traktowane jako programy CGI, zależy od serwera WWW
i jego administratora. Czasem muszą one być zapisane w ustalonym katalogu,
np. cgi-bin
, a ich adres ma wtedy postać np.
http://www.foo.org/cgi-bin/nazwa-programu
.
Takie programy często są uruchamiane z prawami użytkownika nobody
,
a więc nie wolno w nich używać ścieżek względnych.
Model komunikacji oparty na WWW jest bardzo ograniczony:
Programy CGI działają jak typowe filtry w systemie Unix: wczytuja dane ze standardowego wejścia i wypisują wyniki na standardowe wyjscie.
Programy CGI można pisać w każdym języku programowania, nasze przykłady zapiszemy w PHP. Jest to skryptowy język programowania, którego bloki można zanurzać bezpośrednio w strony HTML (trochę podobnie do ASP Microsofta). Jest to produkt bezpłatny, główna strona to www.php.net.
Krótki opis:
static
, aby
były wspólne dla wszystkich bloków PHP na danej stronie (w przeciwnym
razie będą lokalne).
$POST[]
.
Program w PHP może być wykonywany w dwóch trybach, zależnie od użytego interpretera. Serwer WWW automatycznie wywołuje interpreter dla interfejsu CGI, automatycznie generującego nagłówki stron itp. Będziemy o tym mówić dalej.
Na razie zajmiemy się interpreterem dla wiersza poleceń (CLI - Command Line
Interface). Ułatwia on testowanie programów PHP. Aby sprawdzić, czy
jest on zainstalowany w naszym systemie, możemy wywołać program
php
z opcją -v
:
[zbyszek@katastrofa2 ~]$ php -v PHP 5.3.2 (cli) (built: Mar 6 2010 08:33:02) Copyright (c) 1997-2010 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
Prosty program możemy wpisać bezpośrednio. Należy pamiętać, że musi on
być otoczony znacznikami <?php
i ?>
, np.
[zbyszek@katastrofa2 ~]$ php <?php echo "Witaj świecie\n"; exit(0); ?> Witaj świecie
Po wpisaniu programu i naciśnięciu CTRL-D zostanie on wykonany.
Najprostszy przykład to skrypt nie pobierający żadnych danych wejściowych. Powinien on wypisać na standardowe wyjście nagłówek HTTP, a następnie zawartość strony zapisaną w HTML.
Jeśli nie zażądamy inaczej, to PHP wypisze automatycznie standardowy nagłówek HTTP. Zamiast tego możemy w skrypcie umieścić instrukcję
<?php header("Content-type: image/png"); ?>
Musi to być pierwsza linia skryptu, nie może być nawet poprzedzona pustą linią.
Zawartość strony można generować w różny sposób. Najprościej jest użyć standardowych funkcji wyjścia w PHP. W pierwszym przykładzie wygenerujemy stronę z nieśmiertelnym komunikatem.
<html> <?PHP echo "<html><head>"; echo "<title>Pierwszy przykład</title></head>"; echo "<body><h1>Witaj świecie!</h1></body>"; ?> </html>Do wypisywania poszczególnych wierszy HTML użyliśmy funkcji
echo
.
Użycie wyrażeń PHP pozwala na prostsze generowanie skomplikowanych stron. Popatrzmy na prostą pętlę zwiększającą licznik i wypisującą każdorazową wartość licznika:
<html> <?PHP echo "<html><head>"; echo "<title>"Ślad licznika"</title></head>"; echo "<body>"; for ($n = 0; $n <= 10; $n++) { echo "<P>Bieżąca wartość n wynosi $n</P>"; } echo "</body>"; ?> </html>
Programy nie pobierające danych wejściowych są zwykle bezużyteczne. Z programami CGI sprawa ma się inaczej, ponieważ mogą one wyświetlać informację, która ulega częstym zmianom, na przykład pochodzić z bazy danych czy skrytki pocztowej użytkownika.
Napiszemy teraz program podający, jakie gatunki zwierzaków znajdują się w naszej zoologicznej bazie danych. Użyjemy wbudowanego w PHP interfejsu do Postgresa:
<html> <head> <title>Test</title> </head> <body bgcolor="white"> <?php $link = pg_connect("host=lkdb dbname=bd user=scott password=tiger"); $result = pg_query($link, "select distinct gatunek from Zwierzaki"); $numrows = pg_numrows($result); ?> <h2 align=center>Nasze gatunki</h2> <table border="1" align=center> <tr> <th>Gatunek</th> </tr> <?php // Przechodzimy po wierszach wyniku. for($ri = 0; $ri < $numrows; $ri++) { echo "<tr>\n"; $row = pg_fetch_array($result, $ri); echo " <td>" . $row["gatunek"] . "</td> </tr> "; } pg_close($link); ?> </table> </body> </html>
Warto dodawać poniższą instrukcję
error_reporting(E_ALL)
do skryptów PHP, żeby serwer WWW raportował wszystkie błędy.
Konstrukcje sterujące PHP mogą być dzielone na części. Pozwala to na przykład warunkowo umieszczać na wyjściu fragmenty w HTML
<?php if (2 > 3) { ?> <p>co to jest</p> <?php } else { ?> <p>albo to</p>
Standardowe biblioteki PHP obejmują wiele funkcji do komunikacji z bazami danych. W naszych przykładach ograniczymy się do PostgreSQL.
Zaczynamy jak zwykle od połączenia się z bazą danych. Służy do tego funkcja
pg_connect
$link = pg_connect("host=lkdb dbname=mrbd user=scott password=tiger");
Zwraca ona deskryptor połączenia, który należy zachować na zmiennej.
Gdy dostęp do bazy danych przestanie nam być potrzebny, należy zamknąć
połączenie funkcją pg_close
.
pg_close($link);
Pora na zadawanie zapytań. Funkcja pg_query
wykonuje
polecenie SQL i zwraca deskryptor wyniku.
$result = pg_query($link, "select * from Gatunki");
Po sprawdzenie, czy nie było błędu, poszczególne wiersze można pobierać
funkcją pg_fetchrow
. Liczbę wierszy w wyniku podaje funkcja
pg_numrows
.
$numrows = pg_numrows($result); for($ri = 0; $ri < $numrows; $ri++) { $row = pg_fetch_array($result, $ri); echo $row["gatunek"] . "<br>" }
Znacznie ciekawsze są programy CGI, których wyniki zależą od otrzymanych danych wejściowych. Najczęściej użytkownik wypełnia formularz WWW i naciskając przycisk potwierdzenia powoduje wysłanie tej informacji (odpowiednio zapisanej). Serwer przekazuje tę informację do programu CGI określonego w formularzu.
Dane do programów CGI pobiera się używając formularzy HTML. Mogą one zawierać pola tekstowe, rozmaite przyciski, przewijane tablice itp. Pobieranie informacji składa się z dwóch kroków: należy przygotować dokument HTML zawierający odpowiednie formularze, a następnie w programie CGI zanalizować wczytane dane i określić akcję do wykonania.
Formularze umieszcza się w dokumencie HTML używając elementu
<form>
:
<FORM METHOD="POST" ACTION="http://form.url.com/cgi-bin/cgiprogram"> ... Zawartość formularza ... </FORM>Adres URL podany atrybutem
ACTION
powinien wskazywać na program CGI.
Atrybut METHOD
podaje sposób przekazania danych z formularza
do programu CGI. W powyższym przykładzie użyto zalecanej metody
"POST"
, inna możliwość to metoda "GET"
, lecz zwykle
jej użycie jest bardziej kłopotliwe.
W metodzie GET
informacje z formularza przekazuje się do programu
CGI poprzedzając je znakiem zapytania i doklejając do podanego adresu akcji.
Dla metody POST
są one umieszczane w treści komunikatu HTTP i
programu CGI będzie mógł jest odczytać ze standardowego wejścia.
W przypadku metody POST
zamiast uruchamiać program CGI możemy
zawartość formularza przesłać pocztą elektroniczną. Atrybut ACTION
powinien wtedy mieć postać
mailto:ktoś-tam@gdzieś-tam
Wewnątrz formularza można umieścić dowolny element z wyjątkiem innego
formularza. Elementy służace do komunikacji z użytkownikiem to
INPUT
, SELECT
i TEXTAREA
.
Najprostsze są elementy INPUT
:
<INPUT TYPE="text" NAME="napis" VALUE="domyślna" SIZE=10 MAXLENGTH=20> |
|
<INPUT TYPE="checkbox" NAME="thisbox" VALUE="on" CHECKED> |
|
<INPUT TYPE="radio" NAME="radio1" VALUE="1"> |
|
<INPUT TYPE="radio" NAME="radio1" VALUE="2" CHECKED> |
|
<INPUT TYPE="submit" VALUE="gotowe"> |
Atrybut TYPE
określa typ obiektu używanego do komunikacji,
dozwolone wartości to "text"
, "password"
,
"checkbox"
, "radio"
, "image"
,
"hidden"
, "submit"
i "reset"
.
Dwa ostatnie pełnią role przycisków wysyłających zawartość formularza
do serwera i czyszczących formularz.
Każdy z pozostałych typów ma atrybut NAME
podający nazwę
,,zmiennej'', z którą zostanie związana wartość w polu podczas
przekazywania zawartości do programu CGI. Poszczególne typy mają
następującą postać
"text"
--- proste pole do wprowadzania tekstu. Atrybut
VALUE
podaje wartość domyślną (początkową),
atrybut SIZE
to rozmiar pola (domyślnie 20), zaś atrybut
MAXLENGTH
podaje maksymalną liczbę znaków do wpisania
w pole (domyślnie bez ograniczeń).
"password"
--- działa tak samo jak pole tekstowe, ale nie
wyświetla wpisywanych znaków.
"checkbox"
--- określa przełącznik, który może zostać
wciśnięty (,,włączony''). Atrybut VALUE
określa napis
przekazywany do programu CGI gdy przełącznik jest wciśnięty (wartość
domyślna to "on"
).
"radio"
--- określa przełącznik, który może być grupowany
z innymi dając listę alternatywnych wyborów. Grupę tworzą przełączniki
o tej samej wartości atrybutu NAME
.
Element SELECT
służy do tworzenia menu i przewijalnych
list wyboru.
<SELECT NAME="menu"> <OPTION>wybór 1 <OPTION>wybór 2 <OPTION>wybór 3 <OPTION SELECTED>wybór 4 <OPTION>wybór 5 <OPTION>wybór 6 <OPTION>wybór 7 </SELECT> |
|
<SELECT NAME="scroller" MULTIPLE SIZE=7> <OPTION SELECTED>wybór 1 <OPTION SELECTED>wybór 2 <OPTION>wybór 3 <OPTION>wybór 4 <OPTION>wybór 5 <OPTION>wybór 6 <OPTION>wybór 7 </SELECT> |
Atrybut SIZE
określa, czy będzie to rozwijalne menu czy lista
wyboru. Jeśli jest pominięty lub ma wartość 1, to domyślnie jest to menu.
Jeśli jest większy niż 1, to otrzymamy listę przewijaną z widocznymi
SIZE
elementami. Obecność opcji MULTIPLE
automatycznie wybiera listę przewijaną z wieloma wyborami.
Element OPTION
podaje nazwy i wartości dla każdego pola
i pozwala określić opcją SELECTED
, czy jest ono domyślnie wybrane.
Element TEXTAREA
służy do wprowadzania tekstów
w wielu wierszach:
<TEXTAREA NAME="area" ROWS=5 COLS=30> Mary had a little lamb. A little lamb? A little lamb! Mary had a little lamb. It's fleece was white as snow. </TEXTAREA> |
Atrybut NAME
to nazwa ,,zmiennej'', zaś ROWS
i
COLS
określają rozmiar widocznej częsci tekstu.
Początkową (domyślną) zawartość podaje się wewnątrz elementu. Spacje są
respektowane, podobnie znaki nowej linii (jak w elemencie
<PRE>
).
Spróbujmy napisać usługę, pobierającą nazwę użytkownika i hasło, a następnie sprawdzającą ich poprawność. Zaczniemy od formularza:
<html> <title>Drzwi wejściowe</title> <body> <h3 align="center">Logowanie</h3> <form action=".../loguj.php" method="post"> <center> Login: <input type="text" name="kto"><br> Hasło: <input type="password" name="klucz"><br> <input type="submit" value="Loguj"> </center> </form> </body> </html> |
Logowanie |
Strona ta oprócz tytułu zawiera jedynie formularz
(element form
), zawierający trzy elementy input
.
Pierwsze dwa określają pola tekstowe do wprowadzenia identyfikatora
użytkownika (w postaci linii tekstu) i jego hasła (bez ujawniania). Trzeci
element to przycisk zatwierdzenia, po jego naciśnięciu zawartość formularza
jest przesyłana do programu, którego nazwa jest podana jako atrybut
action
formularza.
Oprócz nazwy programu formularz powinien podawać metodę przekazania
wartości zmiennych (w tym przykładzie są dwie zmienne kto
i
klucz
), pobranych z formularza. Dwie główne metody to
get
(domyślna, informacje są przekazywane przez zmienną
środowiskową QUERY_STRING
) i post
(informacje są
przekazywane przez standardowe wejście programu CGI). Do określenia metody
służy atrybut method
--- w tym przypadku jego wartością jest
post
.
Informacja o użytej metodzie zostaje przez serwer WWW zapisana w zmiennej
środowiskowej REQUEST_METHOD
i może być odczytana przez program CGI
(wartości to "GET"
i "POST"
).
Zawartość formularza jest przesyłana w postaci zapytania --
napisu o specjalnej postaci. Dla metody GET
napis ten staje
się wartością zmiennej środowiska QUERY_STRING
. Serwer
otrzymuje go bowiem jako fragment URL, na przykład:
http://students.mimuw.edu.pl/~ab123456/aa?thisinput=default&thisbox=on&radio1=2Zapytanie znajduje się po znaku zapytania ('?').
Niezależnie od przyjętej metody, wartości zmiennych formularza sa
przekazywane jako ciąg par nazwa/wartość, oddzielanych znakiem '&
'.
Nazwy od wartości oddziela się znakiem równości '=
'. Ewentualne
spacje w wartościach są zastępowane znakiem '+
', zaś wszystkie
inne znaki nie alfanumeryczne trójznakową sekwencją zaczynająca się
znakiem procenta '%
', po którym zapisany jest dwoma cyframi
szesnastkowymi kod znaku, np. napis
"20% + 30% = 50%, &c."
"20%25+%2b+30%25+%3d+50%25%2c+%26c%2e"
W metodzie POST
postać zapytania jest taka sama, ale
program CGI otrzymuje je przez standardowe wejście. Nie otrzymamy jednak
końca pliku. Zamiast tego długość napisu zapytania podawana jest w
zmiennej środowiska CONTENT_LENGTH
, którą można odczytać
funkcją getenv
.
Pora na program CGI do naszego przykładu.
<?php $kto = $_POST["kto"]; $klucz = $_POST["klucz"]; $link = pg_connect("host=lkdb dbname=mrbd user=scott password=tiger"); $wynik = pg_query_params($link, "SELECT upraw FROM users WHERE login = $1 AND password = $2", array($kto,$klucz)); $ile = pg_numrows($wynik); if ($ile == 0) { echo "<center><strong>Nie ma takiego użytkownika</strong></center>"; } else { echo "<center><strong>OK</strong></center>"; } ?>
Uwaga: programu zapisanego w powyższej uproszczonej wersji nie
należy umieszczać na serwerze --- nie jest on bezpieczny. Złośliwy
użytkownik mógłby wpisać w przeglądarce po kluczu apostrof, a następnie
dowolne wyrażenie SQL lub średnik i dowolne polecenia SQL
(np. ' OR 'a' = 'a
). Poprawna pełna wersja powinna najpierw
sprawdzić wprowadzone wartości, np. usuwając niedozwolone znaki.
Dokument HTML stanowiący odpowiedź tworzy się dynamicznie na podstawie odpowiedzi na zapytanie użytkownika. Można wykorzystać dowolne techniki formatowania dostępne w HTML.
echo
.