Kurs języka HTML i CSS

Poradnik webmastera

  • Zwiększ rozmiar czcionki
  • Domyślny  rozmiar czcionki
  • Zmniejsz rozmiar czcionki

Przetwarzanie danych

Email Drukuj PDF

Tematy: PostgreSQL | Połączenie z bazą danych | Pobieranie danych | Dodawanie i modyfikacja danych | Usuwanie danych | Parę słów na temat bezpieczeństwa


Poznane w rozdziale 6. operacje na plikach umożliwiają wprowadzenie do naszych aplikacji najprostszego mechanizmu lub raczej sposobu na magazynowanie danych. W nieskomplikowanych przypadkach (pojedyncze dane, tekst do wyświetlenia w oknie przeglądarki itp.) jest to sposób bardzo dobry i wydajny. Często jednak mamy do czynienia z serwisami WWW przetwarzającymi bardzo duże ilości danych, generującymi strony zawierające dane uzależnione od różnych kryteriów, dla wielu użytkowników itp. W przypadku tego typu aplikacji zwykłe pliki przestają wystarczać. Pomijając aspekty związane z bezpieczeństwem (bezpośredni odczyt i zapis na dysku serwera…), mamy tutaj jeden bardzo istotny argument przemawiający przeciwko stosowaniu plików w zaawansowanych aplikacjach: problem wydajności. Magazynując duże ilości danych (od kilku megabajtów do kilkudziesięciu gigabajtów, a czasem i więcej) i zapewniając do nich dostęp w dowolnym czasie z dowolnego miejsca na świecie, musimy mieć do dyspozycji oprogramowanie pozwalające w szybki sposób wydobyć z naszego zbioru danych informacje, których żąda użytkownik. Informacje te mogą być przez użytkownika ograniczone za pomocą określonych warunków, np. znaleźć wszystkie informacje sprzed miesiąca, ale nie starsze niż z roku 2009. O ile tego typu problem jest jeszcze w miarę łagodny (choć można sobie wyobrazić czas, jaki skrypt PHP przeznaczy na wertowanie pliku o rozmiarze np. 1 GB), to prawdziwa „zabawa” zaczyna się, gdy użytkownik będzie chciał zmodyfikować dane już znajdujące się w naszym zbiorze danych, ustalając określone warunki, które powinny być spełnione przez te dane…

Tak czy inaczej, chcąc magazynować dane w plikach, musimy mieć świadomość, że czeka nas tworzenie skryptów, które w sposób nieraz dynamiczny będą musiały analizować polecenia użytkownika i szybko je wykonywać, czyli przeszukiwać pliki i zwracać żądane dane. Czy jest to trudne? Cóż, jeśli ktoś już napisał system zarządzania bazami danych, stwierdzi pewnie, że nie… A czy jest jakiś sposób, żeby zapewnić efektywność serwisom przetwarzającym duże ilości danych? Owszem — słowo kluczowe to bazy danych.

Zanim jednak zgłębi się tematykę baz danych (a tematyka ta jest szalenie obszerna i znacznie wykraczająca poza ten kurs), należy poznać kilka ważnych terminów:

  • Baza danych — nie jest to program, który umożliwia gromadzenie i przetwarzanie danych, choć tego typu programy określane są często w ten właśnie sposób; baza danych to po prostu zbiór danych dotyczących pewnego, określonego zjawiska, np. baza danych wypożyczalni DVD.
  • System zarządzania bazami danych (SZBD, ang. DBMS — DataBase Management System) — to jest właśnie oprogramowanie służące do magazynowania danych oraz — przede wszystkim — umożliwiające manipulowanie danymi (wyszukiwanie, wprowadzanie, modyfikowanie itp.).
  • Zapytanie — jak zostanie to poniżej wyjaśnione, współczesne SZBD udostępniają mechanizm pozwalający wykorzystywać do manipulacji danymi wysokopoziomowe języki zapytań, z których jeden — SQL — jest najpopularniejszy i jest standardem dotyczącym systemów baz danych.

Na rynku informatycznym dostępnych jest wiele ciekawych systemów zarządzania bazami danych, od komercyjnych zaczynając (Microsoft SQL Server, Oracle, DB2 itp.), a na darmowych kończąc (MySQL, PostgreSQL, Firebird itp.). Większość firm produkujących systemy komercyjne, np. Microsoft, Oracle czy IBM, udostępnia również zubożone, darmowe wersje swoich produktów. Są to w pełni funkcjonalne systemy baz danych, mające jedynie ograniczenia co do rozmiaru obsługiwanych danych oraz liczby obsługiwanych procesorów. Spośród darmowych systemów zarządzania bazami danych najpopularniejszy jest MySQL. Jest darmowy w przypadku zastosowań niekomercyjnych, jednak posiada wiele wad, głównie związanych z wydajnością w przypadku przetwarzania dużych ilości danych. MySQL zyskał sobie dużą popularność wśród użytkowników być może dlatego, że jest wygodny w użyciu, ma wygodny język zapytań, od dawna istnieje możliwość instalowania go i używania w systemach Windows, a nie tylko w Linuksie, i w końcu od dawna jest obsługiwany przez PHP. Ponieważ MySQL opisany został w setkach opracowań na temat tworzenia stron WWW w PHP, w tym kursie zajmiemy się innym systemem baz danych — PostgreSQL. PostgreSQL jest projektem open source, darmowym do zastosowań zarówno komercyjnych, jak i niekomercyjnych, działającym bez problemów na platformach Linux i Windows, wspierającym aktualne standardy języka SQL. Jak się okazuje, PostgreSQL nie jest trudny do zainstalowania i obsługi, a duża wydajność i niezawodność rekompensuje drobne niewygody związane np. z brakiem typu autoincrement (choć istnieje pseudotyp serial) itp.

Systemy zarządzania bazami danych umożliwiają nam to wszystko, o co musielibyśmy zadbać sami, pracując z wykorzystaniem plików. Zapewniają szybki dostęp do danych dzięki implementacji najlepszych algorytmów magazynowania i wyszukiwania danych i działają najczęściej w architekturze klient-serwer, dzięki czemu odpadają wszystkie operacje dyskowe, które musiałby wykonywać skrypt podczas przeszukiwania plików. Technologia klient-serwer to jednak przede wszystkim możliwość pobierania danych i przechowywania danych na zdalnym serwerze, niejednokrotnie dedykowanym (100% mocy obliczeniowej komputera przeznaczonej do obsługi serwera baz danych), można więc zacząć rozmawiać o bezpieczeństwie.

W rozdziale tym przedstawione zostały (na poziomie wystarczającym do tworzenia skryptów PHP wykorzystujących dane przechowywane w bazach danych) serwer PostgreSQL, techniki pobierania danych, modyfikowania ich i dodawania do bazy z poziomu skryptu, jak również podstawowe zagrożenia związane z przetwarzaniem danych.

PostgreSQL

PostgreSQL to profesjonalny system zarządzania bazami danych rozpowszechniany jako oprogramowanie open source i dostępny zarówno na platformy linuksowe, jak i systemy rodziny Windows NT. Bez zbędnego rozpisywania się na temat zalet PostgreSQL zajmiemy się od razu instalacją tego oprogramowania w systemie Windows i Linux.

W przypadku systemów Windows najbardziej odpowiednią formą instalacji PostgreSQL jest pobranie i uruchomienie programu instalacyjnego w wersji skompilowanej i przygotowanej do „realiów” systemów Windows (uruchamianie PostgreSQL jako usługi itp.). Instalacja PostgreSQL w systemach Windows przebiega bezproblemowo — każdy użytkownik, który instalował kiedykolwiek jakieś poważniejsze programy, bez problemów sobie poradzi.

Podstawową formą instalacji PostgreSQL w systemach Linux jest kompilacja źródeł i ten sposób zostanie szerzej przedstawiony. Niektóre dystrybucje, obsługujące specjalnie przygotowane pakiety binarne oprogramowania (np. Fedora Core — format rpm), udostępniają wersję PostgreSQL dostosowaną do danej dystrybucji, wstępnie skonfigurowaną i bardzo łatwą do zainstalowania (o ile nie instalowaną domyślnie).

Żeby rozpocząć proces instalacji PostgreSQL w systemach Linux, należy pobrać ze strony http://www.postgresql.org źródła najnowszej, stabilnej wersji serwera. Będzie to plik o nazwie podobnej do postgresql-8.X.X.tar.bz2 lub postgresql-8.X.X.tar.gz, gdzie X oznacza kolejne numery podwersji. W czasie pisania tego kursu aktualną wersją stabilną była 8.4.1. Pobrany plik należy rozpakować, wydając w konsoli polecenia (zależnie od typu pobranego archiwum):

bunzip2 postgresql-8.X.X.tar.bz2

tar -zvf postgresql-8.X.X.tar

lub:

gzip –d postgresql-8.X.X.tar.bz2

tar -zvf postgresql-8.X.X.tar

i zmienić katalog bieżący na postgresql-8.X.X:

cd ./postgresql-8.X.X

W następnym kroku konfigurujemy źródła:

./configure

Warto pamiętać, że polecenie configure z parametrem --help wyświetla listę możliwych opcji konfiguracji PostgreSQL. Poza tym kompilację najlepiej przeprowadzić jako użytkownik root.

Jeżeli konfiguracja źródeł przebiegła pomyślnie (wszystkie potrzebne do kompilacji komponenty zostały odnalezione), kompilujemy i instalujemy oprogramowanie:

make

make install

PostgreSQL jest już zainstalowany i gotowy do użycia, ale musimy wykonać jeszcze kilka niezbędnych czynności. Na początek dodajemy w systemie użytkownika postgres, niezbędnego do prawidłowego działania serwera — możemy to zrobić np. poleceniem useradd lub za pomocą jednej z graficznych nakładek systemowych.

Kolejnym krokiem jest utworzenie katalogu, w którym przechowywane będą bazy danych. Może to być dowolny katalog — w naszym przypadku wybierzemy lokalizację domyślną, czyli /usr/local/pgsql. W tym katalogu za pomocą polecenia mkdir tworzymy podkatalog data:

mkdir /usr/local/pgsql/data

lub jeśli /usr/local/pgsql jest katalogiem bieżącym:

mkdir data

Ustalamy właściciela tego podkatalogu — będzie to użytkownik postgres:

chown postgres /usr/local/pgsql/data

Następnie przełączamy się na użytkownika postgres:

su - postgres

i inicjujemy utworzony wcześniej magazyn danych:

/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data

W zasadzie to jest już wszystko — pozostaje tylko uruchomić serwer:

/usr/local/pgsql/bin/postmaster -i -D /usr/local/pgsql/data 2>&1 &

Opcja -i jest konieczna, jeśli chcemy umożliwić korzystanie z serwera zdalnie za pomocą protokołu TCP/IP. Dla testów możemy jeszcze utworzyć bazę danych (cały czas z poziomu użytkownika postgres):

/usr/local/pgsql/bin/createdb baza

gdzie baza jest nazwą naszej bazy danych, i przejść do interpretera poleceń:

/usr/local/pgsql/bin/psql baza

Ponieważ jesteśmy zalogowani jako użytkownik postgres (w systemie), a domyślnie PostgreSQL skonfigurowany jest tak, że istnieje jeden użytkownik bazy danych (postgres właśnie), a logowanie do serwera następuje z tej samej maszyny, na której serwer jest uruchomiony, jest tzw. logowaniem zaufanym, nie zostaniemy zapytani o hasło. Zostajemy zalogowani do serwera PostgreSQL z bazą danych baza i możemy rozpocząć pracę, oczywiście w języku SQL.

Zaczniemy od rzeczy najważniejszej: zmiany hasła użytkownika postgres (na serwerze PostgreSQL). W tym celu wydajemy polecenie:

alter user postgres password 'nowetajnehaslo';

Ponieważ dostępne są dobre programy do zarządzania serwerem PostgreSQL działające w trybie graficznym (np. pgAdmin), darujemy sobie interpreter poleceń (wydajemy polecenie \q) i opuszczamy powłokę użytkownika postgres. Dalsze operacje na bazach danych w tym rozdziale wykonywać będziemy już z poziomu programu pgAdmin.

Po poprawnej instalacji serwera PostgreSQL trzeba jeszcze przekompilować PHP z odpowiednią opcją, umożliwiającą PHP wymianę danych z tym serwerem (opcja --with-pgsql[=DIR]). Warto jeszcze dodać polecenie uruchamiające PostgreSQL do któregoś ze skryptów startowych Linuksa (zależnych od dystrybucji), tak żeby PostgreSQL uruchamiał się automatycznie po uruchomieniu systemu.

Żeby mieć dostęp do serwera PostgreSQL z innych komputerów w sieci, powinniśmy jeszcze wprowadzić niezbędne zmiany w pliku pg_hba.conf, znajdującym się w katalogu /usr/local/pgsql/data.

Połączenie z bazą danych

Najwyższy czas na pierwsze połączenie z serwerem baz danych z poziomu skryptu PHP. Poniższy przykład realizuje zadanie polegające na nawiązaniu połączenia z serwerem PostgreSQL, pobraniu informacji o wersji serwera i zamknięciu połączenia (listing 7.1). Wersja serwera zostanie wyświetlona w oknie przeglądarki (rysunek 7.1).

Listing 7.1. Połączenie z serwerem baz danych

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Połączenie z bazą danych</title>

</head>

<body>

<div>

<?php

  $uchwyt_polaczenia = pg_connect("dbname=template1 user=postgres password=test");

  if (!$uchwyt_polaczenia) {

    echo "Błąd połączenia z PostgreSQL";

  } else {

    $rezultat = pg_query($uchwyt_polaczenia, "select version()");

    $wersja = pg_fetch_result($rezultat, 0, 0);

    echo $wersja;

    pg_close($uchwyt_polaczenia);

  }

?>

</div>

</body>

</html>

Rysunek 7.1. Wynik działania skryptu z listingu 7.1

Przeanalizujmy skrypt z listingu 7.1. Pierwszy wiersz tego skryptu:

$uchwyt_polaczenia = pg_connect("dbname=template1 user=postgres");

zawiera wywołanie funkcji pg_connect(). Funkcja ta ma za zadanie nawiązanie tymczasowego (nietrwałego) połączenia z serwerem PostgreSQL i przyjmuje jako parametr (argument) tzw. ciąg połączenia (ang. connection string) — w przypadku naszego skryptu ciąg ten zawiera nazwę bazy danych (dbname) i nazwę użytkownika (user). Baza danych, którą wskazaliśmy, jest jedną z domyślnie zakładanych w PostgreSQL wzorcowych baz danych. Tego typu połączenie możliwe jest, gdy PostgreSQL ufa wszystkim połączeniom nawiązywanym z lokalnego komputera (w naszym przypadku zarówno serwer WWW, jak i PostgreSQL działają na tej samej maszynie). Jeżeli konieczne jest podanie hasła użytkownika bazy danych, wywołanie pg_connect należy zmodyfikować następująco:

$uchwyt_polaczenia = pg_connect("dbname=template1 user=postgres password=hasło");

Więcej informacji na temat konstrukcji ciągu połączenia znaleźć można w dokumentacji funkcji pg_connect(): http://www.php.net/manual/pl/function.pg-connect.php.

Dalej skrypt zawiera znaną Ci już konstrukcję, mającą na celu sprawdzenie, czy nawiązano połączenie — funkcja pg_connect() zwraca wartość typu resource (umownie możemy ją nazwać uchwytem połączenia) lub false, jeśli połączenie nie zostanie nawiązane.

Pierwszy wiersz w klauzuli else:

$rezultat = pg_query($uchwyt_polaczenia, "select version()");

zawiera wywołanie funkcji pg_query() — funkcja ta służy do wykonywania zapytań w języku SQL (a w zasadzie do wysyłania zapytań do serwera). Wartość zwracana to identyfikator (uchwyt) wyniku lub false w przypadku niepowodzenia. Pierwszym parametrem tej funkcji jest uchwyt (identyfikator) połączenia, drugim — zapytanie w języku SQL. Zapytanie użyte w tym przypadku zwraca ciąg znaków oznaczający wersję serwera PostgreSQL (rysunek 7.1). Wynik tego zapytania jest tzw. skalarem (nie tablicą), ale możemy go potraktować jak tablicę o rozmiarach 1 na 1, dlatego też wygodnie jest użyć do odczytu tej wartości funkcji pg_fetch_result(), która jako parametry przyjmuje identyfikator wyniku zapytania oraz współrzędne elementu tablicy (wyniku zapytania) i zwraca wartość znajdującą się w określonej współrzędnymi komórce tablicy:

$wersja = pg_fetch_result($rezultat, 0, 0);

Dalej w skrypcie następuje wypisanie zawartości zmiennej $wersja i zamknięcie połączenia z serwerem baz danych — za pomocą funkcji pg_close(), która jako parametr przyjmuje identyfikator połączenia z serwerem baz danych. Użycie tej funkcji w zasadzie jest opcjonalne, ponieważ tymczasowe połączenia z serwerem PostgreSQL są zamykane automatycznie po zakończeniu wykonywania skryptu, ale warto sobie wyrobić nawyk zamykania połączeń za pomocą funkcji pg_close().

Pobieranie danych

W sumie poprzedni punkt pokazał to, co najważniejsze — tam też pobieraliśmy dane. Jednak w tym punkcie spróbujemy pobrać dane z prawdziwej tabeli, którą najpierw utworzymy, używając np. programu pgAdmin.

Baza ta będzie potrzebna pod koniec kursu — wykorzystamy ją do stworzenia prostego systemu aktualności dla pewnej firmy.

Po połączeniu się z serwerem baz danych wybieramy opcję tworzenia bazy danych i jako nazwę nowej bazy podajemy SerwisWWW. Mając nową bazę danych, tworzymy w niej sekwencję (będzie ona generowała kolejne wartości pola klucza głównego tabeli), wykonując zapytanie z listingu 7.2.

Listing 7.2. Skrypt SQL tworzący sekwencję

CREATE SEQUENCE aktualnosci_sq

  INCREMENT 1

  MINVALUE 1

  MAXVALUE 65535

  START 1

  CACHE 1;

ALTER TABLE aktualnosci_sq OWNER TO postgres;

Następnie, wykonując kod z listingu 7.3, tworzymy nową tabelę i wprowadzamy do niej dane.

Listing 7.3. Skrypt SQL tworzący tabelę i wprowadzający do niej dane

CREATE TABLE aktualnosci

(

  aktualnosci_id int4 NOT NULL DEFAULT nextval('aktualnosci_sq'::regclass),

  aktualnosci_tytul text NOT NULL,

  aktualnosci_tresc text,

  aktualnosci_data date NOT NULL,

  CONSTRAINT aktualnosci_pkey_constraint PRIMARY KEY (aktualnosci_id)

)

WITHOUT OIDS;

ALTER TABLE aktualnosci OWNER TO postgres;

INSERT INTO aktualnosci VALUES(nextval('aktualnosci_sq'::regclass), 'Programowanie w PHP', 'Nowa wersja PHP już dostępna!', '2009-06-20');

INSERT INTO aktualnosci VALUES(nextval('aktualnosci_sq'::regclass), 'Systemy baz danych', 'PostgreSQL - jak się okazuje - jest całkiem łatwy w obsłudze.', '2009-06-21');

INSERT INTO aktualnosci VALUES(nextval('aktualnosci_sq'::regclass), 'Przełom w naszym serwisie', 'Od dziś naszym serwisem WWW zarządzają nowi webmasterzy.', '2009-05-02');

INSERT INTO aktualnosci VALUES(nextval('aktualnosci_sq'::regclass), 'Programowanie w PHP', 'Spore zmiany zapowiadają się w wersji 5 naszego ulubionego PHP.', '2007-03-10');

INSERT INTO aktualnosci VALUES(nextval('aktualnosci_sq'::regclass), 'Awaria systemu', 'Wczorajszy przestój systemu spowodowany został awarią serwera. Przepraszamy!', '2006-01-12');

INSERT INTO aktualnosci VALUES(nextval('aktualnosci_sq'::regclass), 'Serwis aktualności naszej firmy - Start!', 'Witamy w nowym serwisie WWW naszej firmy! Mamy nadzieję, że spełni on Państwa oczekiwania', '2006-11-23');

Napiszmy teraz skrypt, który pobierze dane ze zdefiniowanej przez nas bazy danych i przedstawi je w przejrzystej formie. O ile w przypadku prostego spodziewanego wyniku wygodnie było użyć funkcji pg_fetch_result(), o tyle w przypadku zdefiniowanej przez nas tabeli najodpowiedniejszą funkcją będzie jedna z tych, które pobierają z tabeli stanowiącej wynik zapytania wiersz po wierszu aż do końca tabeli. Żeby móc używać nazw kolumn (a nie numerów — w przypadku modyfikacji tabeli indeksowanie numerami kolumn może dawać błędne rezultaty), użyjemy funkcji pg_fetch_assoc(), która jako parametr przyjmuje identyfikator wyniku zapytania, natomiast wartością zwracaną jest tablica asocjacyjna zawierająca dane z jednego wiersza tabeli.

Na listingu 7.4 przedstawiono skrypt, który pobiera dane z bazy i wyświetla w oknie przeglądarki w czytelnej postaci.

Listing 7.4. Przykładowy skrypt pobierający i prezentujący dane z bazy

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Aktualności</title>

</head>

<body>

<h1>Aktualności Serwisu WWW</h1>

<hr>

<div>

<?php

  $uchwyt_polaczenia = pg_connect("dbname=SerwisWWW user=postgres password=test");

  if (!$uchwyt_polaczenia) {

    echo "Problem z połączeniem z bazą danych...<br />";

  } else {

    $rezultat = pg_query($uchwyt_polaczenia, "SELECT * FROM aktualnosci");

     

    while ($wiersz = pg_fetch_assoc($rezultat)) {

      echo "<h3>$wiersz[aktualnosci_tytul]</h3>";

      echo "<p>$wiersz[aktualnosci_tresc]";

      echo "<br /><i>$wiersz[aktualnosci_data]</i></p>";

      echo "<hr />";

    }

 

    pg_close($uchwyt_polaczenia);

  }

?>

Serwis WWW &copy; 2009

</div>

</body>

</html>

Rysunek 7.2 przedstawia wynik działania skryptu z listingu 7.4.

Rysunek 7.2. Wynik działania skryptu pobierającego i prezentującego dane z bazy

Jedyną nowością w skrypcie z listingu 7.4 w stosunku do skryptu z listingu 7.1, nie licząc formatowania strony, jest kod:

    while ($wiersz = pg_fetch_assoc($rezultat)) {

      echo "<h3>$wiersz[aktualnosci_tytul]</h3>";

      echo "<p>$wiersz[aktualnosci_tresc]";

      echo "<br /><i>$wiersz[aktualnosci_data]</i></p>";

      echo "<hr />";

    }

wykonujący najważniejszą pracę: w pętli while pobierane są kolejne wiersze tabeli zwróconej przez zapytanie wybierające (SELECT * FROM aktualnosci — to zapytanie oznacza: „pobierz wszystkie wiersze z tabeli aktualnosci”) i umieszczane w tablicy asocjacyjnej $wiersz. Warunek zawiera wyrażenie, które zwraca wartość true, jeżeli wiersz został pobrany, natomiast gdy tabela nie zawiera już wierszy, wygenerowana zostanie wartość false i pętla zakończy działanie. Ciało pętli stanowią instrukcje echo, które — wykorzystując formatowanie HTML — wyświetlają w oknie przeglądarki zawartość kolejnych elementów tabeli.

W zasadzie listing 7.4 zawiera uniwersalny kod pobierania danych z bazy — o tym, jaki kształt i formę będzie miała tabela wynikowa, decyduje zapytanie, które możemy ułożyć w dowolny sposób, zgodnie z zasadami języka SQL.

Zauważmy jeszcze, że dane wyświetlane w oknie przeglądarki (rysunek 7.2) niekoniecznie muszą być ułożone w kolejności, w której były wprowadzane — może to być spowodowane np. modyfikacją któregoś z pól tabeli itp. Aby zapobiec takiej sytuacji, możemy np. sortować dane według klucza głównego, którego wartości rosną dla nowo dodawanych wierszy, lub — jeszcze lepiej — według daty (czyli pola aktualnosci_data). Możemy w skrypcie wprowadzić następującą modyfikację:

$rezultat = pg_query($uchwyt_polaczenia, "SELECT * FROM aktualnosci ORDER BY aktualnosci_data DESC");

Wykonanie tego zapytania spowoduje wygenerowanie tabeli wynikowej zawierającej wiersze posortowane według daty, od najnowszego do najstarszego — kolejność taka jest najbardziej odpowiednia z logicznego punktu widzenia, ponieważ wyświetlamy aktualności.

Dodawanie i modyfikacja danych

Dodawanie i modyfikacja danych to zadania realizowane przez odpowiednie zapytania SQL — stosujemy dokładnie te same metody komunikacji z bazą danych, jak w przypadku odczytu danych.

Dodawanie danych

Do dodawania danych do tabeli służy zapytanie SQL INSERT INTO, o ogólnej postaci:

INSERT [INTO] nazwa_tabeli [(kolumna1, kolumna2, ..., kolumnaN)]

VALUES (wartość1, wartość2, ..., wartośćN)

Powoduje ona wprowadzenie do tabeli nowego wiersza, w którym w polu kolumna1 została zapisana wartość wartość1, w polu kolumna2 — wartość wartość2 itd. Elementy instrukcji ujęte w nawias kwadratowy są opcjonalne.

Na listingu 7.5 przedstawiono prosty skrypt (prostota i pominięcie kilku operacji mają na celu ułatwienie analizy skryptu) realizujący operację dodawania nowych wpisów do tabeli aktualnosci naszej bazy danych. Podobnie jak w przykładach z rozdziału omawiającego obsługę formularzy założeniem jest, że w pliku konfiguracyjnym php.ini włączona została opcja short_open_tags.

Listing 7.5. Dodawanie danych do bazy

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Dodawanie danych</title>

</head>

<body>

<?php

  // Funkcja wyświetlająca formularz pozwalający na wprowadzenie danych

  function form($tytul = "", $tresc = "", $data = "") {

?>

  <form action="dod.php" method="POST">

  <div>

  Data:<br />

  <input name="data" value="<?= $data; ?>"/><br />

  Tytuł:<br />

  <input name="tytul" value="<?= $tytul; ?>"/><br />

  Treść:<br />

  <input name="tresc" value="<?= $tresc; ?>"/><br />

  <input type="submit" name="submit" value="Dodaj" />

  </div>

  </form>

<?php

  }

  if (!isset($_POST["submit"])) {

    // Wyświetlamy formularz z aktualną datą wprowadzoną domyślnie

    date_default_timezone_set("Europe/Warsaw&;quot;);

    form("", "", date("Y-m-d"));

  } else {

    // Dane zostały przesłane - sprawdzamy, czy pola zostały wypełnione

    if (empty($_POST['data']) || empty($_POST['tytul']) || empty($_POST['tresc'])) {

      // Któreś z pól jest puste (wyświetlamy formularz i komunikat)

      form($_POST['tytul'], $_POST['tresc'], $_POST['data']);

      echo "<p>Należy wypełnić wszystkie pola formularza!</p>";

    } else {

      // Wszystko jest w porządku - wyświetlamy formularz

      form($_POST['tytul'], $_POST['tresc'], $_POST['data']);

      // Wprowadzamy dane do bazy

      $tytul_filtr = addslashes(trim($_POST['tytul']));

      $tresc_filtr = addslashes(trim($_POST['tresc']));

      $data_filtr = addslashes(trim($_POST['data']));

      $zapytanie = "INSERT INTO aktualnosci VALUES(nextval('aktualnosci_sq'::regclass), '$tytul_filtr', '$tresc_filtr', '$data_filtr')";

      $uchwyt_polaczenia = pg_connect("dbname=SerwisWWW user=postgres");

      if (pg_query($uchwyt_polaczenia, $zapytanie))

        echo "<p>Dane zostały wprowadzone.</p>";

      else

        echo "<p>Błąd podczas wprowadzania danych!</p>";

      pg_close($uchwyt_polaczenia);

    }

  }

?>

</body>

</html>

Skrypt ten przy pierwszym uruchomieniu (odwołaniu się do niego przez przeglądarkę) wyświetla formularz do wprowadzania danych (aktualna data jest wprowadzana domyślnie). Po wprowadzeniu i wysłaniu danych (kliknięcie przycisku Dodaj) skrypt sprawdza, czy wszystkie pola zostały zapełnione. Jeśli nie, wyświetlany jest formularz i odpowiedni komunikat — użytkownik otrzymuje szansę dopisania brakujących wartości. Jeśli wszystkie pola są zapełnione, skrypt próbuje wykonać zapytanie wstawiające dane do tablicy aktualności. O ile wszystkie wymienione wcześniej operacje są znane m.in. z rozdziału o formularzach i wcześniejszych punktów, o tyle sam sposób generowania zapytania jest w pewnym sensie nowością. Każda informacja przekazana przez użytkownika poddawana jest filtracji pod kątem białych spacji na początku i na końcu ciągu znaków (funkcja trim()). Wynik filtracji poddawany jest obróbce przez funkcję addslashes(), która opisana zostanie w punkcie poświęconym bezpieczeństwu, na końcu tego rozdziału.

Skrypt z listingu 7.5 jest prosty, ponieważ dopuszcza wiele niejasnych sytuacji — udoskonalenie tego skryptu może być dobrym tematem ćwiczenia do samodzielnego wykonania.

Jak wynika z listingu 7.5, do wykonywania zapytań wstawiających dane do tablicy wykorzystywana jest funkcja pg_query(), ta sama, którą stosowaliśmy w przypadku zapytań wybierających.

Rysunek 7.3 przedstawia skrypt z listingu 7.5 w akcji (po wprowadzeniu danych do bazy), natomiast rysunek 7.4 przedstawia stronę wyświetlającą aktualności z nowym wpisem.

Rysunek 7.3. Skrypt dodający dane do bazy w akcji

Rysunek 7.4. Prezentacja aktualności z nowym wpisem

Modyfikacja danych

Modyfikacja danych jest zadaniem trochę trudniejszym od dodawania nowych danych — najpierw trzeba wybrać z tabeli wiersz, w którym chcemy wprowadzić zmiany. Żeby wybrać taki wiersz, musimy mieć jakieś kryterium wyszukiwania. Do modyfikacji służy instrukcja SQL UPDATE, która ma ogólną postać:

UPDATE tabela

SET kolumna1=wartość1, kolumna2=wartość2, ..., kolumnaN=wartośćN

[WHERE warunek]

Oznacza ona: zmień w tabeli tabela — w wierszach spełniających warunek warunek — pole kolumna1 na wartość1, pole kolumna2 na wartość2 itd. Klauzula WHERE jest opcjonalna, jeśli jednak zostanie pominięta, zmianie ulegną wszystkie wiersze w tabeli.

Przykładowy skrypt z listingu 7.6 jako kryterium (element, którego wartość wyznacza wiersz do modyfikacji) wykorzystuje klucz główny tabeli — chodzi o to, żeby przykładowe skrypty nie były trudne do analizy, jak zresztą bywało w rozdziałach i punktach wcześniejszych.

Listing 7.6. Skrypt modyfikujący dane w zależności od wartości parametru (tutaj klucza głównego — identyfikatora wiersza)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Modyfikacja danych</title>

</head>

<body>

<?php

 

  // Funkcja wyświetlająca formularz wyboru pola

  function form_id() {

?>

  <form action="mod.php" method="post">

  <div>

  Podaj identyfikator wiersza:<br />

  <input name="id" value=""><br />

  <input type="submit" name="submit_id" value="Wyślij" />

  </div>

  </form>

<?php

  }

 

  // Funkcja wyświetlająca formularz modyfikacji danych

  function form_mod($id = "", $tytul = "", $tresc = "", $data = "") {

?>

  <form action="mod.php" method="POST">

  <div>

  <input type="hidden" value="<?= $id; ?>" name="id" />

  Data:<br />

  <input name="data" value="<?= $data; ?>"/><br />

  Tytuł:<br />

  <input name="tytul" value="<?= $tytul; ?>"/><br />

  Treść:<br />

  <input name="tresc" value="<?= $tresc; ?>"/><br />

  <input type="submit" name="submit_mod" value="Zmień" />

  </div>

  </form>

<?php

  }

 

 

  if (!isset($_POST["submit_id"]) && !isset($_POST["submit_mod"])) {

    // Wyświetlamy formularz wprowadzania identyfikatora wiersza

    form_id();

  } else if (isset($_POST["submit_id"])) {

    // Pobranie danych na podstawie identyfikatora

    $uchwyt_polaczenia = pg_connect("dbname=SerwisWWW user=postgres password=test");

    $rezultat = pg_query($uchwyt_polaczenia, "SELECT * from aktualnosci WHERE aktualnosci_id=".trim($_POST['id']));

    if (pg_num_rows($rezultat) != 0) {

      // Wyświetlenie formularza z pobranymi danymi

      $wiersz = pg_fetch_assoc($rezultat);

      form_mod($wiersz['aktualnosci_id'], $wiersz['aktualnosci_tytul'], $wiersz['aktualnosci_tresc'], $wiersz['aktualnosci_data']);

    } else {

      echo "<p>Nie ma wiersza o podanym identyfikatorze.</p>";

    }

    pg_close($uchwyt_polaczenia);

  } else {

      // Wprowadzenie danych do bazy (modyfikacja)

      $id = $_POST['id'];

      $tytul_filtr = addslashes(trim($_POST['tytul']));

      $tresc_filtr = addslashes(trim($_POST['tresc']));

      $data_filtr = addslashes(trim($_POST['data']));

      $zapytanie = "UPDATE aktualnosci SET aktualnosci_tytul='$tytul_filtr', aktualnosci_tresc='$tresc_filtr', aktualnosci_data='$data_filtr' WHERE aktualnosci_id=$id";

      $uchwyt_polaczenia = pg_connect("dbname=SerwisWWW user=postgres password=test");

      if (pg_query($uchwyt_polaczenia, $zapytanie))

        echo "<p>Dane zostały wprowadzone.</p>";

      else

        echo "<p>Błąd podczas wprowadzania danych!</p>";

      pg_close($uchwyt_polaczenia);

  }

?>

</body>

</html>

Skrypt z listingu 7.6 sprawia wrażenie skomplikowanego, ale po bliższym przyjrzeniu się kodowi można znaleźć wiele znanych już konstrukcji. Skrypt działa według następującego algorytmu: pierwsze odwołanie do skryptu spowoduje wyświetlenie formularza służącego do podania wartości identyfikatora wiersza (funkcja form_id()). Jeśli użytkownik kliknie przycisk Wyślij (submit_id), skrypt pobierze z bazy wiersz o wartości pola aktualnosci_id równej wartości przekazanej przez użytkownika — gdyby się okazało, że nie ma takiego wiersza (sprawdzamy ilość pobranych wierszy funkcją pg_num_rows()), wyświetlony zostanie odpowiedni komunikat. Po pobraniu wiersza z bazy wartości poszczególnych pól zostaną wyświetlone w formularzu modyfikacji realizowanym przez funkcję form_mod() — identyfikator modyfikowanego wiersza zostaje „zapamiętany” jako ukryte pole formularza. Użytkownik może dokonać modyfikacji danych i kliknąć przycisk Zmień — dane zostaną wprowadzone do bazy (dla uproszczenia z przykładowego skryptu usunięty został kod sprawdzający, czy wszystkie pola zostały wypełnione).

Poszczególne kroki modyfikacji wybranego wiersza tabeli pokazują rysunki od 7.5 do 7.7.

Rysunek 7.5. Modyfikacja danych — wybór wiersza (krok 1.)

Rysunek 7.6. Modyfikacja danych — zmiana wartości poszczególnych pól (krok 2.)

Rysunek 7.7. Strona prezentująca dane po wprowadzonych zmianach

Weźmy pod uwagę fakt, że skrypt z listingu 7.6, jak i skrypty wcześniejsze, może nie być optymalny — chodzi przede wszystkim o wyjaśnienie pewnych mechanizmów, dlatego pominiętych zostało wiele aspektów związanych z zabezpieczeniami przed nieuprawnionym wywołaniem itp.

Usuwanie danych

Proces usuwania danych jest bardzo podobny do modyfikacji danych — tutaj również musimy wiedzieć, co chcemy usunąć.

Zła konstrukcja zapytania usuwającego dane może spowodować utratę nawet wszystkich danych!

Do usuwania danych służy instrukcja SQL DELETE o schematycznej postaci:

DELETE FROM tabela

[WHERE warunek]

Oznacza ona: usuń z tabeli tabela wszystkie wiersze spełniające warunek warunek. Jeśli warunek zostanie pominięty, zostaną usunięte wszystkie dane (podobnie jak w przypadku instrukcji UPDATE, gdzie pominięcie warunku powoduje modyfikację wszystkich wierszy tabeli).

Na listingu 7.7 przedstawiono skrypt, który powstał jako przeróbka przykładu z listingu 7.6 — użytkownik podaje identyfikator wiersza i po wysłaniu formularza (przycisk Wyślij) wiersz o podanej wartości pola aktualnosci_id zostaje usunięty (o ile istniał). Ponieważ skrypt zawiera konstrukcje doskonale nam już znane, listing przedstawiany jest bez dodatkowego opisu działania.

Listing 7.7. Skrypt usuwający dane z tabeli

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Usuwanie danych</title>

</head>

<body>

<?php

  // Funkcja wyświetlająca formularz wyboru pola

  function form_id() {

?>

  <form action="del.php" method="post">

  <div>

  Podaj identyfikator wiersza:<br />

  <input name="id" value=""><br />

  <input type="submit" name="submit_id" value="Wyślij" />

  </div>

  </form>

<?php

  }

  if (!isset($_POST["submit_id"])) {

    // Wyświetlamy formularz wprowadzania identyfikatora wiersza

    form_id();

  } else {

    // Usuwamy dane z tablicy

    $id = addslashes(trim($_POST['id']));

    $zapytanie = "DELETE FROM aktualnosci WHERE aktualnosci_id=$id";

    $uchwyt_polaczenia = pg_connect("dbname=SerwisWWW user=postgres password=test");

    pg_query($uchwyt_polaczenia, $zapytanie);

    echo "<p>Operację usuwania zakończono.</p>";

  }

?>

</body>

</html>

Parę słów na temat bezpieczeństwa

Zaprezentowano już podstawowe funkcje i metody dostępu do baz danych z poziomu skryptów PHP. Funkcje te nieznacznie różnią się od siebie (przeważnie przedrostkiem nazwy) w zależności od systemu baz danych, natomiast przedstawione algorytmy np. odczytu danych z bazy pozostają niezmienne.

Ostatnią rzeczą do przedstawienia, choć nie mniej ważną niż wszystko, co zostało opisane wcześniej, są problemy zagrażające aplikacjom WWW z dostępem do baz danych. W zasadzie problem jest jeden: dane przekazywane przez użytkownika (np. przez formularze HTML). Zasada ogólna mówi, że przed wprowadzeniem do bazy dane tego typu powinno się odpowiednio filtrować. Dlaczego? Ponieważ odpowiednia konstrukcja treści wprowadzonej przez dociekliwego i ciekawskiego użytkownika może doprowadzić do wykonania przez serwer baz danych zapytania, którego w ogóle nie planowaliśmy (np. DELETE FROM aktualnosci — wiadomo, czym to grozi). Zapytanie to może mieć drastyczne skutki — od utraty danych po przejęcie kontroli nad serwerem, dlatego też nie powinniśmy dopuszczać do wprowadzania do bazy danych pochodzących bezpośrednio np. z formularza. Takie preparowanie danych, które mają za zadanie różne od zamierzonego wykonanie zapytania, nazywane jest iniekcją (wstrzykiwaniem) SQL.

Najprostszym sposobem filtrowania danych jest używanie funkcji porządkujących ciągi znaków, np. funkcji trim() do wycinania początkowych i końcowych białych znaków. Jednak jedną z ważniejszych funkcji jest addslashes(). Funkcja ta dodaje lewe ukośniki przed znakami, które w ciągu znaków powinny być umieszczane jako sekwencje ucieczki (znane m.in. z języka C/C++). Dopiero dane wyposażone w ukośniki powinny być zapisywane w bazie. W przypadku zastosowania funkcji addslashes() po odczycie danych z bazy powinniśmy „potraktować” je funkcją stripslashes(), która ma odwrotne działanie do addslashes().

Jeśli nie chcemy, żeby w bazie zapamiętywany był kod HTML, możemy użyć funkcji np. htmlentities().

Zagadnienia związane z bezpieczeństwem zostały opisane w rozdziale 8.

Literatura

Współpraca PHP z bazami danych, a także tworzenie baz i obsługa systemów RDBMS to szerokie zagadnienia, których nie sposób dogłębnie omówić w krótkim rozdziale kursu. Czytelnikom zainteresowanym tą tematyką można jednak polecić m.in. następujące publikacje:

  • PostgreSQL 8.3. Ćwiczenia (http://helion.pl/ksiazki/cposq9.htm),
  • SQL. Ćwiczenia praktyczne (http://helion.pl/ksiazki/cwsql2.htm),
  • PHP i MySQL. Dla każdego (http://helion.pl/ksiazki/phsqdk.htm).
 

PHP i HTML. Tworzenie dynamicznych stron WWW

PHP i HTML.
Tworzenie dynamicznych stron WWW

PHP 5. Narzędzia dla ekspertów

PHP 5. Narzędzia dla ekspertów

PHP. Praktyczne projekty

PHP. Praktyczne projekty

Nowości Helionu

Statystyki

Użytkowników : 766
Artykułów : 513
Zakładki : 28
Odsłon : 15226333