PHP/Studium przypadku - Księga gości

Z Wikibooks, biblioteki wolnych podręczników.
< PHP
Poprzedni rozdział: Korzystanie z dokumentacji
Następny rozdział: Ćwiczenia

Studium przypadku: Księga gości[edytuj]

Dotychczasowe rozdziały miały w sobie więcej teorii i nie ukazywały prawdziwej istoty tworzenia skryptów dla stron WWW. Najlepszym rodzajem nauki jest praktyczne stworzenie skryptu z prawdziwego zdarzenia i pokazanie w ten sposób, jak poszczególne elementy języka ze sobą się łączą. W tym rozdziale stworzymy samodzielnie funkcjonalną księgę w PHP. Wykorzystamy w niej pętle, tablice, instrukcje warunkowe, formularze oraz dołączanie plików zewnętrznych. Ponadto poznamy kilka interesujących funkcji.

Plan[edytuj]

Księgę gości oprzemy o pliki tekstowe. Każdy wpis będzie charakteryzowany przez:

  • Tytuł
  • Autora
  • Datę
  • Adres WWW (opcjonalny)
  • Treść

Wszystkie te dane zostaną zapisane w pliku w pojedynczym wierszu, a oddzielone będą znakiem "|". Aby wyeliminować sytuację, gdy ktoś wpisze np. w treść taki znak, albo (co gorsza) naciśnie enter, każda kolumna zostanie zakodowana w formacie Base64. Oczywiście nie będziesz musiał sam pisać odpowiedniego algorytmu - PHP posiada odpowiednie funkcje. Base64 został pierwotnie zaprojektowany do przesyłania 8-bitowych wiadomości w 7-bitowych protokołach. Do zapisu są tu używane wyłącznie litery, cyfry, ukośnik oraz znak równości, zatem nie ma obawy o uszkodzenie formatu pliku księgi. Zakodowana wiadomość jest o ok. 33% dłuższa od oryginału.

Księga nie będzie zapisywać i odczytywać z pliku bezpośrednio. W zewnętrznym pliku napiszemy sobie dwie funkcje zajmujące się wyłącznie tym. Księga będzie miała dostęp do danych jedynie za ich pomocą. Rozwiązanie to jest bardzo użyteczne. Kiedy poznasz już bazy danych, z pewnością zapragniesz przesiąść się właśnie na nie. Nie będziesz musiał przepisywać całej księgi. Po prostu napiszesz do tych dwóch funkcji nowy kod. Dzięki takiemu rozwiązaniu źródła skryptu będą uporządkowane i czytelne, a ponadto bardzo łatwe do modyfikacji.

dane.php[edytuj]

Plik dane.php zawierać będzie dwie funkcje: dodajWpis() oraz pobierzWpisy() zajmujące się wyłącznie operowaniem na danych. Będziemy go pisać po kawałku.

<?php
 	define('WPISY', './wpisy.txt');
 
 	function dodajWpis($tytul, $autor, $www, $tresc)
 	{
 		// Ucinanie bialych znakow
 		$tytul = trim($tytul);
 		$autor = trim($autor);
 		$www = trim($www);
 		$tresc = trim($tresc);

Nazwę pliku zapisaliśmy za pomocą stałej. To na wypadek, gdyby zachciało nam się go kiedyś przenieść albo zmienić mu nazwę. Pierwszy etap obrabiania danych to ucięcie białych znaków (spacji, tabulatorów itp.) z początku i końca każdego ciągu. Nie są nam one do niczego potrzebne, a jedynie utrudniają nam sprawę.

		// Kontrola danych
 		
 		if(strlen($tytul) < 3)
 		{
 			return false;
 		}
 		
 		if(strlen($autor) < 3)
 		{
 			return false;
 		}
 		
 		if(strlen($tresc) < 10)
 		{
 			return false;
 		}
 
 		if(strlen($www) > 0)
 		{
 			// Jesli adres nie zaczyna sie od http:// to dodaj to
 			if(strpos($www, 'http://') !== 0)
 			{
 				$www = 'http://'.$www;
 			}		
 		}

Tutaj zajmujemy się kontrolą danych. Tytuł i autor muszą mieć przynajmniej trzy znaki, a treść 10. Długość pobieramy funkcją strlen(). Jeżeli podaliśmy adres WWW, za pomocą funkcji strpos() sprawdzamy, czy na jego początku jest na pewno dodany identyfikator protokołu. Zwróć uwagę na użyty operator: !==. Wspomniana funkcja zwraca pozycję pierwszego znaku szukanego ciągu (liczoną od zera), a jeżeli go nie znajdzie, zwraca false. Normalnie 0 i false są równoważne, dlatego musimy wymusić sprawdzenie także typu, bowiem nam chodzi o 0 jako pozycję identyfikatora w adresie, a nie informację, że go nie ma.

Jeżeli któraś z informacji nie będzie prawidłowo podana, funkcja zwróci do skryptu wartość false.

		// Dodawanie
 	
 		$f = fopen(WPISY, 'a');
 		
 		$dane = array(0 =>
 			base64_encode(htmlspecialchars($tytul)),
 			base64_encode(htmlspecialchars($autor)),
 			time(),
 			base64_encode(htmlspecialchars($www)),
 			base64_encode(nl2br(htmlspecialchars($tresc)))		
 		);
 		
 		fwrite($f, implode('|', $dane)."\r\n");
 		fclose($f);
 		return true;
 	} // end dodajWpis();

Ostatni akord to zapisanie wpisu w pliku. Na początek otwieramy go w trybie dopisywania (parametr a), następnie budujemy tablicę z danymi wpisu. Parametry przybyłe z formularza kodujemy w Base64, środkowa kolumna to czas dodania wpisu w sekundach od 1.1.1970. Dzięki takiemu jego zapisowi, będziemy go mogli później dowolnie formatować funkcją date(). Przy przetwarzaniu tekstu użyliśmy jeszcze kilku funkcji:

  • htmlspecialchars() - wszystkie wprowadzone tagi HTML są zamieniane na zwykły tekst (ograniczniki są zastępowane odpowiadającymi im encjami, np. < zmienia się w &lt;). W ten sposób nikt nie rozwali nam księgi złośliwym kodem.
  • nl2br() - zamienia znaki nowej linii na znaczniki <br/>.
  • implode() - łączy tablicę w ciąg tekstowy, wstawiając pomiędzy poszczególne elementy podany w pierwszym parametrze znak. Na końcu utworzonego przez nią rekordu wpisu musimy dodać samodzielnie znaki zejścia do nowej linii: \r\n, koniecznie w cudzysłowach, a nie w apostrofach.

Zwróć uwagę na rozbudowane wywołania funkcji:

base64_encode(nl2br(htmlspecialchars($tresc)))

Oznacza ona, że najpierw zawartość zmiennej $tresc trafia do funkcji najbardziej w prawo, tj. htmlspecialchars(). Z niej przechodzi do nl2br(), a z niej do base64_encode(). Alternatywne kursy preferują w tym miejscu czytelną, ale mniej wydajną formę zapisu:

$tresc = htmlspecialchars($tresc);
$tresc = nl2br($tresc);
$tresc = base64_encode($tresc);

Nasz kod ma tę przewagę, że wynik jednej funkcji od razu trafia do drugiego, tymczasem powyżej po drodze trafia do zmiennej, co ma szczególnie negatywny wpływ na wydajność przy dużych danych. Ponadto zapis ten często nie jest opatrzony stosownym komentarzem i wyrabia złe nawyki tworzenia mnóstwa niepotrzebnych zmiennych tymczasowych, o czym już wspominaliśmy. Trzeba zapamiętać, że nawet podawany przez nas wariant konstrukcji księgi nie jest "tym jedynym słusznym". Istnieje jeszcze wiele innych sposobów jej zaprogramowania.

Przyszła kolej na funkcję pobierającą wpisy.

	function pobierzWpisy()
 	{
 		$wpisy = array_reverse(file(WPISY));
 		
 		$i = 1;
 		$rezultat = array();

Wpisy pobieramy funkcją file(), która automatycznie rozbija nam plik na tablicę względem wierszy. Tam jednak najnowsze wpisy są na końcu, a my chcemy je wyświetlić w kolejności odwrotnej: najnowsze na górze. Dlatego od razu przepuszczamy wynik przez funkcję array_reverse() odwracającą tablicę. Następnie inicjujemy licznik wpisów $i oraz tablicę $rezultat, do której będziemy pakować sformatowane odpowiednio wpisy. Ją później przekażemy księdze do wyświetlenia.

		foreach($wpisy as $wpis)
 		{
 			$wpis = explode('|', trim($wpis));
 			
 			$rezultat[] = array(
 				'id' => $i,
 				'tytul' => base64_decode($wpis[0]),
 				'autor' => base64_decode($wpis[1]),
 				'data' => date('d.m.Y, H:i', $wpis[2]),
 				'www' => base64_decode($wpis[3]),
 				'tresc' => base64_decode($wpis[4])		
 			);
 			$i++;		
 		}
 		return $rezultat;	
 	} // end pobierzWpisy();
 
 ?>

Po kolei formatujemy każdy wpis. Do ich pobierania wykorzystaliśmy pętlę foreach(). Na wejściu przepuszczamy je przez funkcję trim(), bowiem file() pozostawia w tablicach znaki zejścia do nowej linii. Dopiero po ich usunięciu możemy funkcją explode() rozbić ciąg z powrotem na tablicę (czyli wykonać operację odwrotną do implode()). Wpis przepuszczamy przez base64_decode(), aby zdekodować informacje, formatujemy datę i pakujemy to do tablicy $rezultat. Później zwracamy ją.

ksiega.php[edytuj]

W pliku tym znajdzie się główna część funkcjonalna księgi. To jego powinniśmy uruchamiać z poziomu przeglądarki. Zaczynamy jego pisanie od nagłówka HTML oraz dołączenia uprzednio napisanego pliku:

<!DOCTYPE html 
	PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl" lang="pl">
   <head>
   <meta http-equiv="content-type" content="text/html; charset=utf-8">
   <title>Księga gości Wikibooks</title>
   </head>
   <body>
   	<h1>Księga gości Wikibooks</h1>
 <?php
 
 	require('./dane.php');

Właściwy kod rozpocznie się instrukcją warunkową, która zadecyduje, czy wysłany został formularz dodawania, czy też wystarczy wyświetlić aktualną zawartość księgi. PHP zapisuje informację o użytej metodzie wysyłania żądania w specjalnej zmiennej $_SERVER['REQUEST_METHOD'], która może przyjąć wartości POST (formularz) albo GET (zwyczajne żądanie). Z niej właśnie skorzystamy:

	if($_SERVER['REQUEST_METHOD'] == 'POST')
 	{
 		// Dodawanie wpisu
 		if(dodajWpis($_POST['tytul'], $_POST['autor'], $_POST['www'], $_POST['tresc']))
 		{
 			echo '<p>Dziękujemy, wpis został dodany prawidłowo.</p>';
 		}
 		else
 		{
 			echo '<p>Proszę wypełnić prawidłowo formularz.</p>';
 		}
 		echo '<p><a href="ksiega.php">Powrót</a></p>';	
 	}

Kontrola danych jest realizowana w pliku dane.php i nie ma potrzeby jej tu powtarzać. Wprowadzamy do funkcji dodajWpis() poszczególne pola formularza i ifem sprawdzamy, jaki jest rezultat, aby móc wygenerować stosowny komunikat.

Jeżeli nie dodajemy aktualnie żadnego nowego wpisu, wyświetlamy te, które już mamy:

	else
 	{
 		// Wyświetlanie wpisów
 		$wpisy = pobierzWpisy();
 		
 		foreach($wpisy as $wpis)
 		{
 			echo '<hr /><p><b>Tytuł: <i>'.$wpis['tytul'].'</i>; 
 				Autor: '.$wpis['autor'].'; Data: '.$wpis['data'];
 			if(strlen($wpis['www']) > 0)
 			{
 				echo '; <a href="'.$wpis['www'].'" target="_blank">Strona WWW</a>';
 			}
 			echo '</b></p>';
 			echo '<p>'.$wpis['tresc'].'</p>';		
 		}
 		
 ?><hr />

Tablicę z wpisami dostajemy z funkcji pobierzWpisy(). Skanujemy ją pętlą foreach i wyświetlamy każdy z elementów. Zauważ, że skrypt potrafi ominąć pole z adresem WWW, jeśli ten nie został podany. Po prostu funkcją strlen() sprawdzamy, czy jego długość jest większa od zera.

Ostatnim akordem będzie dodanie formularza HTML:

 <form method="post" action="ksiega.php">
 <table border="0" width="50%">
 	<tr>
 		<td>Tytuł</td>
 		<td><input type="text" name="tytul"/></td>
 	</tr>
 	<tr>
 		<td>Autor</td>
 		<td><input type="text" name="autor"/></td>
 	</tr>
 	<tr>
 		<td>WWW</td>
 		<td><input type="text" name="www"/></td>
 	</tr>
 	<tr>
 		<td>Treść</td>
 		<td><textarea name="tresc" rows="4" cols="50"></textarea></td>
 	</tr>
 	<tr>
 		<td></td>
 		<td><input type="submit" value="Dodaj"/></td>
 	</tr>
 </table>
 </form>
 <?php
 	
 	}
 
 ?>
   </body>
 </html>

Formularz wysyłamy metodą POST do pliku ksiega.php. Jego wysłanie spowoduje uaktywnienie się dodawania wpisów. Przed wysłaniem ostatnich znaczników musimy dodać jeszcze klamrę kończącą instrukcję warunkową, która decyduje, co należy wykonać. Umieszczenie jej w tym miejscu gwarantuje, że formularz pokaże się tylko podczas wyświetlania wpisów. Jeśli skrypt będzie wysyłać komunikat o dodaniu wpisu, nie zostanie on już dołączony.

To już koniec naszej księgi gości. Utwórz teraz pusty plik wpisy.txt i zacznij korzystanie.

Co dalej?[edytuj]

Napisana tutaj księga nie jest wyrafinowanym szczytem techniki. Jej głównym zadaniem było pokazanie podstawowych mechanizmów tworzenia dynamicznych stron WWW w praktyce. Wraz z powiększaniem się twojej wiedzy o języku PHP możesz rozszerzać ją o nowe możliwości:

  • Blokadę przed floodem.
  • Kasowanie wpisów.
  • Dzielenie wyników na strony.
  • Łatwy w modyfikacji wygląd.

Niektóre z tych rzeczy będą znacznie łatwiejsze do wykonania, kiedy poznasz zasady pracy z bazami danych dającymi nieporównywalnie większe możliwości, niż pliki tekstowe. Na bazach danych oparta jest obecnie zdecydowana większość istniejących aplikacji internetowych. Dzięki temu, że odseparowaliśmy pobieranie danych od ich wyświetlania, przepisanie księgi na MySQL będzie jedynie zabiegiem kosmetycznym. Zdecydowanie polecamy korzystanie z takiego rozwiązania, gdyż daje programiście sporą elastyczność. Nie zawsze bowiem zdarza się, że organizacja naszych danych będzie dokładnie taka, jakiej życzyłby sobie klient. Programując wszystko "na sztywno" wraz z wyglądem i kodem funkcjonalnym aplikacji narażamy się na większe ryzyko popełnienia błędu, zwiększamy rozmiar kodu i czynimy go mniej czytelnym.

Na tym kończymy poznawanie podstaw języka PHP. Przyszła pora na dokładniejsze poznanie kilku użytecznych funkcji przydatnych podczas tworzenia własnych skryptów.