Technologie ADOdb kluczem do każdej bazy Jak pisać uniwersalne skrypty PHP 08 2004
CMYK NA CD NEWSY Z OKŁADKI FIRMA MAGAZYN PROGRAMY WARSZTAT technologie ADOdb kluczem do każdej bazy danych Tworzenie skryptów niezależnych { switch($db) { od bazy danych należy case mysql : $connection = mysql_connect($host, $user, $pass); do najtrudniejszych zadań mysql_select_db($database, $db); programisty. Aby stworzone break; case pg : programy mogły współpracować $connection = pg_connect( host=$host port=5432 z każdą bazą danych, potrzeba dbname=$database user=$user password=$pass ); break; spójnego interfejsu. case sqlite : Dostarczy go biblioteka ADOdb, $connection = sqlite_open( $database.sqlite ); break; której przyjrzymy się bliżej. } return $connection; Paweł Grzesiak } ?> ykupując miejsce na serwerze mamy dostęp do jednej, maksymal- Funkcja connect() tworzy interfejs, warstwę abstrakcyjną służącą do połą- nie dwóch baz danych. Bardzo często jesteSmy skazani na My- czenia z bazą danych. Lecz by mogła działać, potrzebuje wprowadzenia na- WSQL. I tak powstają skrypty, które pracują wyłącznie na tej bazie. stępujących informacji: typ bazy danych (MySQL, PostgreSQL, czy SQLi- Problem pojawia się, gdy MySQL przestaje spełniać oczekiwania. Trudno te), nazwa bazy danych, adres serwera, nazwa użytkownika, hasło. Gdy te przecież przewidzieć jak nasza strona się rozroSnie i czego będziemy po- dane zostaną przekazane do funkcji, nastąpi próba połączenia z bazą danych: trzebowali w przyszłoSci. Gdy zdecydujemy się na zmianę bazy danych, $db = connect( mysql , baza , localhost , staniemy przed koniecznoScią zmiany skryptów. Zwykle jednak okazuje uzytkownik , haslo ); się to nierealne jest tyle zmian do dokonania, że lepiej byłoby stworzyć Takim sposobem stworzyliSmy najprostszą z możliwych abstrakcję słu- oprogramowanie całkowicie od nowa. Takich sytuacji można uniknąć, żącą do nawiązania połączenia z bazą danych. W powyższym przykładzie wprowadzając poSrednika pomiędzy naszym skryptem a bazą danych. próbujemy połączyć się z bazą MySQL. Funkcja connect() na podstawie Przyjrzymy się prostemu przykładowi. Połączymy się z trzema bazami zmiennej $db wybiera sekcję kodu, którą ma uruchomić. Jeżeli wszystko się danych, by zobaczyć, jak wygląda nawiązywanie połączenia w przypadku powiedzie, funkcja w zmiennej $db zwróci identyfikator połączenia. Czy (kolejno) MySQL, PostgreSQL i SQLite: spełniliSmy założony cel? Czy skrypt jest na etapie połączenia przenoSny? // MySQL Tak, ponieważ by połączyć się z inną, dowolną bazą danych, wystarczy mysql_connect( localhost , uzytkownik , haslo ); jedynie zmodyfikować parametry przekazywane funkcji connect(). Idąc da- mysql_select_db( baza ); lej z naszym systemem abstrakcyjnym, należałoby stworzyć obsługę błę- dów i dalsze funkcje (do wykonywania zapytań, zwracania wyników itd.). // PostgreSQL Są to już jednak znacznie trudniejsze zadania. Takim sposobem powstałby pg_connect ( host=localhost port=5432 dbname=baza kompletny system abstrakcyjny do przełączania się pomiędzy obsługiwany- user=uzytkownik password=baza ); mi bazami danych. Jednak można sądzić, że produkt końcowy byłby bardzo niedoskonały i wymagałby jeszcze wielu poprawek. Tak trafiamy na // SQLite ADOdb, czyli coS, co już jest gotowe i z czego możemy korzystać od zaraz. sqlite_open( baza.sqlite ); Znaczne różnice są widoczne już na pierwszy rzut oka. Począwszy O ADOdb od nazw funkcji, po przekazywane parametry to samo zadanie wygląda ADOdb (Active Data Objects Data Base) to oprogramowanie, które niwe- skrajnie różnie. Jak rozwiązać ten problem? Jak napisać skrypt, który luje setki różnic występujących pomiędzy obsługiwanymi przez tę aplika- prawidłowo połączy się z wybraną bazą? Należy stworzyć warstwę abs- cję bazami danych. A obsługuje niemało, bo kilkadziesiąt rozwiązań bazo- trakcyjną służącą do połączenia z poszczególnymi bazami danych: danowych, a wSród nich: MySQL, PostgreSQL, Interbase, Informix, Orac- function connect($db, $database, $host = , funkcji, za pomocą których wykonuje się wszystkie operacje na bazie da- $user = , $pass = ) nych. W efekcie wszystkie skrypty tworzone w oparciu o ADOdb stają się Przykłady i programy opisane w tym artykule znajdują się na dołączonej 84 INTERNET.sierpień.2004 płycie CD w folderze Warsztat_ADOdb Jak pisać uniwersalne skrypty PHP? CMYK WARSZTAT PROGRAMY MAGAZYN FIRMA Z OKŁADKI NEWSY NA CD technologie bardziej przenoSne niezależne od bazy danych. Są także i inne zalety, jak ADOdb w różnych formatach (CHM, HTML). Wszystkie pliki dokumen- możliwoSć eksportowania wyników do formatu CSV, czy rozbudowana tacji są tradycyjnie w języku angielskim. diagnostyka ułatwiająca usuwanie błędów i monitorowanie zapytań wyko- nywanych na bazie danych. Połączenie z bazą danych Obsługa i korzystanie z ADOdb nie są wcale trudniejsze od pracy ze By rozpocząć pracę z bazą danych, należy się najpierw z nią połączyć. standardowymi rozszerzeniami PHP. Analogii i podobieństw ADOdb moż- Jakby to wyglądało w bazie MySQL? na także szukać w standardzie ADO Microsoftu. W ADOdb znajdziemy wiele ułatwień, których brakuje w standardowych API do obsługi baz da- $db = mysql_connect( localhost , uzytkownik , nych dostępnych w PHP. haslo ); WydajnoSć komunikacji z bazami danych ma znaczny wpływ na mysql_select_db( baza , $db); szybkoSć działania skryptów. W ADOdb szybkoSć wykonywania pole- ?> ceń jest bardzo duża. Na podstawie testów wydajnoSci Podajemy podstawowe dane, wybieramy bazę danych, łączymy się (http://phplens.com/lens/adodb/) można stwierdzić, że ADOdb przewyższa wiele z MySQL. Teraz dokładnie to samo, stosując już ADOdb: podobnych rozwiązań dostępnych na rynku. Warto dodać, że ADOdb jest lepszą, szybszą i bogatszą w opcje alternatywą dla PEAR DB include( /adodb/adodb.inc.php ); (http://pear.php.net/package/DB), konkurencyjnej biblioteki wpieranej przez or- $db = &NewADOConnection( mysql ); ganizację PHP w ramach projektu PEAR. ADOdb jednak ustępuje wy- $db->Connect( localhost , uzytkownik , haslo , dajnoScią bibliotekom wbudowanym wprost w język PHP. To logiczne, baza ); bo jest tylko skryptem PHP, który korzysta z dostępnych w PHP biblio- ?> tek, by stworzyć wokół nich warstwę abstrakcyjną. Nie może więc prze- Tabela 1: wybrane bazy danych obsługiwane przez ADOdb wyższać rozwiązań, z których sam korzysta. Skrót ADOdb Baza Danych Wymagany sterownik System operacyjny Wreszcie, największym atutem ADOdb jest wbudowany system buforo- access Microsoft Access/Jet ODBC Windows wania zapytań. Zapisuje on do osobnych plików na dysku twardym wyniki ado ADO, bez wskazania ADO lub OLEDB Windows pochodzące z zapytań kierowanych do bazy danych. Gdy ponownie zosta- na konkretną bazę db2 DB2 interfejs DB2 CLI/ODBC Unix/Windows nie wykonane to samo zapytanie na bazie danych, ADOdb pobierze wynik vfp Microsoft Visual FoxPro ODBC Windows zapytania bezpoSrednio z pliku. Nietrudno dojSć do wniosku, że takie roz- ibase Interbase 6 lub wcześniejszy klient Interbase Unix/Windows wiązanie znacznie zwiększa wydajnoSć budowanych na ADOdb aplikacji. firebird Firebird klient Interbase Unix/Windows ldap sterownik LDAP rozszerzenie LDAP Unix/Windows A przy tym tworzenie zapytań buforowanych jest tak proste jak zwykłych. mssql Microsoft SQL Server 7 i starszy klient Mssql Unix/Windows mysql MySQL bez obsługi transakcji klient MySQL Unix/Windows mysqlt MySQL z obsługą transakcji. klient MySQL Instalacja lub maxsql Aktualną wersję ADOdb można pobrać z oficjalnej strony biblioteki. Instala- oci8 Oracle 8/9 klient Oracle Unix/Windows cja wymaga od nas posiadania dystrybucji PHP w wersji 4.0.4 lub starszej. Na odbc Standardowy ODBC bez wskazania ODBC Unix/Windows na konkretną bazę dołączonej płycie CD zamieSciliSmy aktualną wersję (4.2.2) biblioteki odbc_mssql MSSQL (używa ODBC by połączyć) ODBC Unix/Windows ADOdb. Choć trudno tu mówić o instalacji, ponieważ ADOdb to zbiór pli- odbc_oracle Oracle (używa ODBC by połączyć) ODBC Unix/Windows ków PHP, należy zastosować się do kilku uwag. Po rozpakowaniu archiwum oracle Oracle 7 (interfejs oci8 lepszy klient Oracle ? większa wydajność) katalog adodb należy wgrać na serwer w miejsce niedostępne (ze względów postgres PostgreSQL (obecnie identyczny klient PostgreSQL Unix/Windows bezpieczeństwa katalog z adodb powinien znalexć się w niewidzialnej z ze- ze sterownikiem postgres7) wnątrz strukturze serwera) poprzez wpisanie adresu URL. Dla większoSci ser- postgres64 PostgreSQL 6.4 i wcześniejsze, klient PostgreSQL Unix/Windows nieobsługujące LIMIT werów za katalog bezpieczny uważa się katalog równorzędny z public_html. postgres7 PostgreSQL z obsługą LIMIT klient PostgreSQL Unix/Windows Obok katalogu public_html powinien znalexć się więc katalog adodb. i zachowaną funkcjonalnością wersji 7 Ponadto należy stworzyć w tym samym miejscu nowy katalog, do które- sapdb SAP DB klient SAP ODBC ? go będą trafiały zapytania buforowane. Może on mieć co prawda dowolną sqlanywhere Sybase SQL Anywhere klient SQL Anywhere ODBC Unix/Windows nazwę, lecz my nazwiemy ten katalog: adodbcache. Nowo utworzony kata- sqlite SQLite SQLite (nie dot. PHP5) Unix/Windows sybase Sybase Sybase client Unix/Windows log powinien posiadać prawa dostępu również i do zapisu (najlepiej chmod 777). Tak przygotowana instalacja pozwala w pełni korzystać z ADOdb. W pierwszej linii stosując Scieżkę bezwzględną dołączamy bibliotekę Jeżeli zależy nam na małych rozmiarach instalacji, do poprawnego urucho- ADOdb, która znajduje się w katalogu adodb, patrząc od głównego katalogu mienia ADOdb w wersji minimalnej potrzebne są następujące pliki: konta. Następnie tworzymy instancję $db klasy NewADOConnection, jako pa- adodb.inc.php rametr podając typ bazy, skrót odpowiadający sterownikowi do konkretnej ba- adodb-lib.inc.php zy danych (patrz tabela 1). Kolejno, korzystając z metody Connect, wprowa- adodb-time.inc.php dzamy wszystkie dane (host, użytkownika, hasło oraz podajemy nazwę bazy). adodb-php4.inc.php W efekcie otrzymujemy zwrotny identyfikator połączenia ($db). Gdy będzie- adodb-iterator.inc.php my chcieli zmienić aktualnie obsługiwaną bazę danych na całej stronie, zmia- Z katalogu drivers (sterowniki) należy też wybrać systemy bazodano- nie podlegają tylko 2 ostatnie linijki powyższego kodu. Wszystkie pozostałe we, z których mamy zamiar korzystać. Znajdują się tu sterowniki do elementy i funkcje są uniwersalne dla każdej obsługiwanej bazy danych. Gdy- wszystkich baz danych obsługiwanych przez ADOdb. Gdy zdecydujemy bySmy zatem chcieli przejSć na bazę danych SQLite, powinniSmy zrobić to tak: się tylko na MySQL, powinniSmy usunąć wszystkie pliki poza adodb-my- sql.inc.php (do dyspozycji mamy jeszcze parę sterowników pobocznych include( /adodb/adodb.inc.php ); do obsługi MySQL o tym w dokumentacji). Przy zmianie bazy danych, $db = &NewADOConnection( sqlite ); np. na SQLite, do tego katalogu powinniSmy także wrzucić odpowiedni $db->Connect( /bazy/baza.sqlite ); tej bazie sterownik, czyli adodb-sqlite.inc.php. ?> W dystrybucji ADOdb w katalogu docs znajdziemy pełną dokumenta- Tu tkwi największa zaleta ADOdb. Wystarczy modyfikacja dwóch cję opisującą poszczególne funkcje i sposób ich użycia. Na stronie interne- linii kodu, by zmienić bazę danych, na której często pracuje cała strona towej projektu oraz na dołączonej płycie CD znajduje się dokumentacja internetowa! INTERNET.sierpień.2004 85 CMYK NA CD NEWSY Z OKŁADKI FIRMA MAGAZYN PROGRAMY WARSZTAT technologie Asocjacyjna czy numeryczna Instalacja ADOdb jako rozszerzenia PHP W powyższym przykładzie (wykonanym na bazie MySQL) tablica wynikowa zawierała elementy tablicy asocjacyjnej (gdzie indeksami Istnieje możliwość zainstalowania ADOdb również jako modułu rozszerza- były nazwy pól tabeli) i numerycznej (gdzie indeksami tablicy były jącego PHP. To rozwiązanie jest dużo wydajniejsze od wersji ADOdb pracują- cej pod kontrolą PHP. Kod modułu napisany jest (zresztą jak całe PHP) w języ- kolejne liczby, odzwierciedlające kolejne pola tablicy począwszy od ku C++. Korzystanie z ADOdb jako modułu PHP jest nieco wygodniejsze (nie zera). W ADOdb wyboru tablicy wynikowej dokonujemy w następują- trzeba np. każdorazowo dołączać pliku biblioteki). Pojawia się jednak problem cy sposób: z przenośnością. O ile pliki ADOdb w formie skryptów PHP przenieść łatwo, to echo
; może się okazać, że na innym serwerze nie ma możliwości zainstalowania tego $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; modułu rozszerzającego. Moduł jest również w fazie eksperymentalnej. Można $rs = $db->GetRow( SELECT * FROM premiery WHERE id=1 ); go pobrać z oficjalnej strony ADOdb. Zamieściliśmy go także na dołączonej print_r($rs); płycie. Aktualna wersja ADOdb 3.3.2ext znajduje się w pliku o nazwie adodb- echo
; -ext-332.zip. Po rozpakowaniu archiwum, w katalogu php-win-4.3 znajdziemy żądaną bibliotekę dll. By zainstalować rozszerzenie w Windows, bibliotekę php_adodb.dll należy przenieść do katalogu extensions, który mieści się w podkatalogu instalacji PHP. Następnie należy otworzyć plik php.ini, który kryje w sobie całą lokalną konfigurację PHP. Tam znajduje się sekcja nazwana Dynamic Extensions . Na końcu tej listy rozszerzeń należy dodać linijkę: extension=php_adodb.dll Wystarczy już tylko uruchomić ponownie serwer, aby ADOdb był standar- dowo dostępny z poziomu PHP. Informacje na temat instalacji w systemach uniksowych są dostępne w pliku readme.txt. n Tablica wynikowa GetRow() jako tablica asocjacyjna Tabela 2: tabela Premiery , na której będziemy operować Wyróżniamy trzy wartoSci zmiennej $ADODB_FETCH_MODE: id tytul_pl tytul data_premiery 1 Dirty Dancing 2 Dirty Dancing: 2004-06-04 ADODB_FETCH_BOTH (zarówno asocjacyjna, Havana Nights jak i numeryczna; niektóre bazy nie obsługują tego trybu), 2 Zgromadzenie The Gathering 2004-06-04 ADODB_FETCH_ASSOC (tablica asocjacyjna), 3 Świt żywych trupów Dawn of the Dead 2004-06-11 4 Efekt motyla The Butterfly Effect 2004-06-11 ADODB_FETCH_NUM (tablica numeryczna). 5 The Punisher The Punisher 2004-06-18 6 Ladykillers The Ladykillers 2004-06-18 Istnieje także elegantsza konstrukcja obiektowa: Przyjrzyjmy się danym w tabeli 2. Jest to tabela o nazwie premiery z da- $db->SetFetchMode(ADODB_FETCH_NUM); nymi na temat kilku filmów. Zawiera szeSć rekordów i cztery kolumny. Ma- my identyfikator liczbowy wiersza (zwiększający się o jeden wraz z kolejnym Buforowanie zapytań wierszem), polski tytuł filmu (ciąg znaków, VARCHAR), tytuł oryginalny Podczas instalacji ADOdb utworzyliSmy folder adodbcache, który po- (również VARCHAR) oraz datę premiery (pole typu DATE). Będziemy ko- służy nam teraz do przechowywania danych z zapytań. Gdy wykona- rzystać z tej tabeli, wykonując na niej wszystkie przykłady. Ze względu na my zapytanie buforowane, wynik tej operacji zostanie zapisany w po- oszczędnoSć miejsca w wielu przykładach zostaną pominięte trzy pierwsze staci pliku do katalogu adodbcache. Gdy powtórnie wykonamy to sa- linie skryptu. Dołączamy w nich bibliotekę ADOdb, tworzymy instancję klasy mo zapytanie, zapytanie zamiast trafić powtórnie do bazy danych, zo- oraz łączymy się z bazą danych (patrz: połączenie z bazą danych). stanie odnalezione w katalogu z plikami buforu. ADOdb pobierze wyniki operacji wprost z pliku, pomijając całkowicie bazę danych. Pobieranie jednego wiersza Dzięki temu nasze aplikacje będą pracowały ze zwielokrotnioną pręd- Za wykonanie zapytania i pobranie do tablicy danych z jednego wiersza koScią. Systemu cachingu używa się tam, gdzie kieruje się do bazy odpowiada w ADOdb metoda GetRow(). Jej działanie jest analogiczne bardzo dużo identycznych zapytań. Buforowanie ma sens, gdzie dane do funkcji mysql_fetch_row() i zbliżone do pg_fetch_row(). Poniższy pobierane z buforu nie ulegają zbyt częstej aktualizacji (lub wpływ przykład pobiera z tabeli premiery wiersz o id równym 1, zapisując go tych aktualizacji nie jest ważny). Stworzymy więc przykładowe zapy- do tablicy $rs. Następnie za pomocą funkcji PHP print_r() zostaje gra- tanie buforowane: ficznie wySwietlony wynik działania skryptu tablica z danymi. echo
; echo
; $ADODB_CACHE_DIR = /adodbcache ; $rs = $db->GetRow( SELECT * FROM premiery WHERE id=1 ); $rs = $db->CacheGetRow( SELECT * FROM premiery print_r($rs); WHERE id=1 ); echo
; print_r($rs); echo
; W powyższym kodzie pojawiła się nowa zmienna. $ADODB_CA- CHE_DIR informuje bibliotekę o tym, gdzie znajduje się katalog z pli- kami buforu. Dodatkowo zmianie uległa nazwa metody GetRow(). Przybył jej przyrostek Cache, ostatecznie mamy więc metodę Cache- GetRow(). Podobnie będzie z innymi funkcjami. Gdy pierwszy raz zo- stanie wykonane to zapytanie, w katalogu adodbcache powstanie plik z wynikami tej operacji. Gdy powtórnie uruchomimy program, dane powędrują już prosto z pliku. Pliki w buforze przedawniają się jednak po pewnym czasie standardowo po 3600 sekundach. Wtedy ADOdb powtórnie pobiera dane z bazy, by utworzyć nowy, Swieży cache. Wynik działania metody GetRow() Standardowy czas życia pliku bufora można zmienić, dodając do me- 86 INTERNET.sierpień.2004 CMYK WARSZTAT PROGRAMY MAGAZYN FIRMA Z OKŁADKI NEWSY NA CD technologie tody CacheGetRow() dodatkowy parametr. Jest to wartoSć liczbowa Jeżeli chcemy, by powyższy przykład działał na zasadzie zapytania wyrażana w sekundach. Jeżeli więc chcemy odSwieżyć cache po buforowanego (czyli był wydajniejszy), należy zmienić nazwę metody 15 minutach, powinniSmy wpisać jako pierwszy parametr wartoSć 900: Execute() na CacheExecute(): $rs = $db->CacheGetRow( 900 , SELECT * FROM $rs = $db->CacheExecute( SELECT * FROM premiery ); premiery WHERE id=1 ); Wprowadzanie danych Pobieranie wielu wierszy By wprowadzić dane do bazy danych, najpierw należy zanegować Gdy z tabeli chcieliSmy pobrać pojedynczy wiersz, korzystaliSmy kłopotliwe znaki. I tak wszystkie kłopotliwe znaki zostaną poprze- z funkcji GetRow(). Zajmijmy się teraz sytuacją, gdy z tabeli chcemy dzone odpowiednim dla konkretnej bazy danych symbolem. W My- pobrać więcej niż jeden wiersz. Kolejny przykład będzie wykorzysty- SQL korzystaliSmy z mysql_escape_string() czy addslashes(). wał metodę Execute(). Jest ona odzwierciedleniem mysql_query() czy W SQLite używaliSmy sqlite_escape_string(), a użycie addslashes() pg_query() wykonuje dowolne zapytanie na bazie danych. Gdy ope- groziło błędami. W ADOdb mamy jedną funkcję, która odpowiada racja się powiedzie, zwraca prawdę, gdy coS się nie powiedzie fałsz. za przygotowanie ciągu znaków do użycia z metodą Execute(). Me- Zapytanie wykonane poprawnie należy następnie wySwietlić. Skorzy- tody qstr() należy używać zawsze wtedy, gdy chcemy do bazy stamy w tym celu z dwóch pętli. Pierwsza zajmie się kolejnymi wier- danych wprowadzić ciąg znaków. Ta funkcja jest o tyle nietypowa, szami (rekordami), druga kolejnymi komórkami. Skorzystamy także że dodatkowo na końcu i na początku ciągu znaków dodaje znak po- z metody MoveNext(), która przenosi wewnętrzny wskaxnik wyników jedynczego cudzysłowu . To zaleta, która nieco skraca zapytanie o jeden wiersz do przodu. Spójrzmy zatem na przykład: kierowane do bazy. Zaprezentowany poniżej przykład dodaje do ta- $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; beli premiery nowy wiersz: $rs = $db->Execute( SELECT * FROM premiery ); // dane do dodania if (!$rs) $tytul_pl = Shrek 2 ; echo $db->ErrorMsg(); $tytul = Shrek 2 ; else $data_premiery = 2004-07-02 ; while (!$rs->EOF) { $query = INSERT INTO premiery (tytul_pl, tytul, foreach($rs->fields as $d){ data_premiery) VALUES ( .$db->qstr($tytul_pl). , echo $d. ; ; .$db->qstr($tytul). , .$db->DBDate($data_premiery). ) ; } $rs = $db->Execute($query); $rs->MoveNext(); if($rs) echo ; echo dodano poprawie! ; } else echo rekordów: .$rs->RecordCount(); // zwraca: 6 echo $db->ErrorMsg(); echo kolumn: .$rs->FieldCount(); // zwraca: 4 echo $db->Insert_ID(); // zwróci: 7 W pierwszej linii deklarujemy wynikową tablicę asocjacyjną. echo $db->Affected_Rows(); // zwróci: 1 W następnej linii wykonujemy zwykłe zapytanie na bazie, przypisując Jak widzimy, konstrukcja zapytania niczym specjalnym się nie wynik do zmiennej $rs. Sprawdzamy, czy zapytanie przebiegło po- wyróżnia. Do zmiennej $query wędruje zapytanie typu INSERT, prawnie. Jeżeli nie, metoda ErrorMsg() wySwietli informację tekstową które następnie wykonujemy przy użyciu metody Execute(). Jeżeli na jakie problemy napotkano. Jeżeli zapytanie zostanie wykonane po- operacja się powiedzie, wySwietlany jest komunikat o sukcesie, je- prawnie, wykonywana jest pętla while. Instrukcje w niej zawarte wy- żeli operacja się nie powiedzie, skrypt informuje o napotkanym błę- konywane są dotąd, aż wyrażenie !rs->EOF nie zwróci fałszu. Sygnali- dzie. Oprócz sposobu zastosowania metody qstr(), należy również zator EOF (podobnie jak w operacjach na plikach tekstowych) zwraca zwrócić uwagę na nową metodę, a mianowicie na DBDate(). Auto- prawdę, gdy pobrane zostaną wszystkie wyniki. Pętla while w naszym rzy ADOdb musieli w jakiS sposób rozwiązać problem wynikający przypadku wykona się szeSć razy, dla każdego rekordu w tabeli. Pętla z tego, że każda baza danych zapisuje datę w nieco innym formacie. foreach operuje już na konkretnym wierszu tabeli, na tablicy Tak powstała metoda DBDate(), której jako parametr możemy po- $rs->fields[]. W każdym wierszu znajdują się cztery komórki, stąd ma- dać datę w dwóch formatach. Może to być (tak jak w przykładzie) my cztery elementy w tablicy asocjacyjnej $rs->fields[]. WySwietlamy data w standardzie ISO: YYYY-MM-DD lub data w formie unikso- więc po kolei wszystkie komórki, a ich zawartoSć tkwi w zmiennej $d. wego wskaxnika czasu (timestamp). Data wprowadzona przy użyciu Wychodząc z pętli foreach widzimy jeszcze metodę MoveNext(). tej funkcji nie powinna rodzić konfliktów i problemów, gdy zmieni- Pominięcie jej spowoduje, że pętla while nigdy się nie zatrzyma. Pętla my system bazodanowy. ta odpowiada bowiem za przesunięcie tablicy wyników do następnego wiersza. Typy danych Na końcu listingu widoczne są jeszcze dwie metody. Pierwsza Czasami potrzebna jest możliwoSć pobrania informacji o danym polu z nich, RecordCount() wySwietla liczbę zwróconych wierszy w wyni- w tabeli. Chcemy np. wiedzieć jakiego typu dane tam powinny trafiać ku. Metoda FieldCount() zwraca liczbę kolumn wynikowych. (jakiego typu danych), czy też chcemy znać maksymalną liczbę zna- ków jaką można wprowadzić do konkretnego pola. W ADOdb do po- bierania informacji na temat kolumn w tabeli służy metoda Fetch- Field(). Przejdxmy do przykładu: $rs = $db->Execute( SELECT * FROM premiery ); echo
; print_r($rs->FetchField(1)); echo
; W pierwszej linii wykonujemy zapytanie na bazie danych. Pobiera- my informacje na temat wszystkich pól w tabeli ( * ). Następnie ko- Pobieranie wielu wierszy z tabeli rzystamy z metody FetchField, jako parametr podając numer kolumny INTERNET.sierpień.2004 87 CMYK NA CD NEWSY Z OKŁADKI FIRMA MAGAZYN PROGRAMY WARSZTAT technologie (począwszy od zera). WySwietlamy informacje na temat 1 kolumny ta- echo rs2csv($rs, false); beli, czyli nie na temat kolumny ID, a na temat kolumny tytul_pl. Parametr fałszu w przypadku obu funkcji spowoduje, że wynikowe Funkcja print_r() wySwietli graficznie zawartoSć wynikowego obiektu. dane nie będą zawierały nazw kolumn tabeli. Nie zobaczymy więc pierw- Są tu informacje na temat nazwy pola, jego maksymalnej długoSci, ty- szego wiersza, gdzie wymienione zostały kolejno: id, tytul_pl, tytul, da- pu obsługiwanych danych itp. ta_premiery. Podobnie jak z datami, także z typami danych są problemy co do zgodnoSci pomiędzy poszczególnymi bazami danych. W jednej ba- Eksportowanie danych do pliku zie spotkamy się z zapisem typu danych int , w drugiej integer . Zapiszemy teraz wynik działania funkcji rs2csv() do pliku. Następnie po- By w programie mieć pewnoSć o jaki konkretnie typ chodzi, mamy staramy się wydobyć dane z zapisanego uprzednio pliku csv, by wprowa- do dyspozycji specjalną funkcję. MetaType() na podstawie wprowa- dzić je do bazy danych. W tej sposób najpierw wyeksportujemy dane, dzonych parametrów: typu danych (np. timestamp, timedate) i mak- a następnie zaimportujemy je do bazy (co doSć często się przydaje). symalnej długoSci pola (np. 127, 65535) decyduje o jaki typ danych chodzi. Metoda zwraca jedną z dziewięciu wartoSci liter, które include( adodb/adodb.inc.php ); symbolizują konkretne typy danych (patrz tabela 3). Spójrzmy na include( adodb/toexport.inc.php ); przykład: $db = &NewADOConnection( mysql ); $rs = $db->Execute( SELECT * FROM premiery ); $db->Connect( localhost , uzytkownik , haslo , $inf = $rs->FetchField(1); baza ); echo $rs->MetaType($inf->type, $inf->max_length); $rs = $db->Execute( SELECT * FROM premiery ); Wykonujemy operacje analogiczne z przedstawionymi w poprzed- $f_loc = premiery.csv ; nim przykładzie. Przybywa jedna linia, czyli sposób użycia metody Me- $file = fopen($f_loc, w ); taType(). W zmiennej $inf znajdują się dane na temat pola tytul_pl. Aby if ($file AND $rs) { otrzymać informacje na temat typu danych, wprowadzamy jako parame- rs2csvfile($rs, $file, false); try $inf->type (w tym przykładzie jest to string), $inf->max_length (tu fclose($file); 60 znaków). W efekcie otrzymujemy wynik: C. Przyglądając się tabeli } stwierdzamy, że pole to musi być typu char lub varchar. Zgadza się. Po- ?> dobnie można postąpić z pozostałymi kolumnami tabeli. Podobnie jak w poprzednim przykładzie dołączamy potrzebne bibliote- ki, łączymy się z bazą i wykonujemy zapytanie pobierające do zmiennej Tabela 3: ustandaryzowane typy danych ADOdb $db zawartoSć tabeli premiery . Otwieramy do zapisu plik o nazwie za- wartej w zmiennej $f_loc (prermiery.csv). W przypadku gdy plik nie istnie- Litera Typ danych Komentarz C char lub varchar krótkie pola tekstowe; do 255 znaków je, skrypt utworzy nowy. Jeżeli wszystko przebiegło poprawnie (plik został X clob lub text duże i bardzo duże pola tekstowe otworzony do zapisu i zapytanie zostało wykonane poprawnie), funkcja D date data lub czas rs2csvfile() załaduje dane do pliku, a następnie plik zostanie zamknięty. T timestamp uniksowy wskaznik czasu L boolean lub bitowe wartości 0 (fałsz) lub 1 (prawda) Funkcja rs2csvfile() to odmiana funkcji rs2csv() przygotowana specjalnie N decimal, numeric, float, real liczby zmiennoprzecinkowe do wgrywania danych do plików. Takim sposobem został utworzony plik I integer liczba naturalna R autoincrement lub inny licznik pole musi być numeryczne premiery.csv, który można bez problemu otworzyć np. w Excelu. B blob lub inne obiekty binarne np. obrazy Eksportowanie danych Standardowo w ADOdb wbudowane są dwie funkcje służące do ekspor- towania danych pochodzących z wyników zapytań. Możemy przenieSć nasze dane do dwóch formatów: CSV (gdzie kolejne komórki oddzielone są przecinkami) oraz formatu tabularycznego (gdzie kolejne komórki od- dzielone są znakiem tabulatora). Spójrzmy więc na przykład: include( adodb/adodb.inc.php ); include( adodb/toexport.inc.php ); $db = &NewADOConnection( mysql ); $db->Connect( localhost , uzytkownik , haslo , baza ); $rs = $db->Execute( SELECT * FROM premiery ); echo
; Plik premiery.csv można otworzyć m.in. w Excelu echo rs2tab($rs); echo
; Importowanie danych z pliku ?> By załadować dane z pliku, musimy go najpierw otworzyć, potem W powyższym przykładzie należy zwrócić uwagę na to, że ładu- przeanalizować, by wreszcie wrzucić dane do bazy za pomocą zapytań jemy dodatkową bibliotekę. Biblioteka toexport.inc.php zawiera typu INSERT. Zobaczmy przykład: dwie funkcje: rs2tab() oraz rs2csv(). Kolejno wykonujemy zapyta- nie pobierające wszystkie dane z tabeli premiery , a następnie ko- include( adodb/adodb.inc.php ); rzystamy z funkcji rs2tab(), która zwraca nam dane w ustalonym $db = &NewADOConnection( mysql ); formacie. $db->Connect( localhost , uzytkownik , haslo , Teraz przyjrzymy się funkcji rs2csv(). Działa ona dokładnie na ta- baza ); kiej samej zasadzie, jak ta w powyższym przykładzie. Zarówno dla $query = INSERT INTO premiery (id, tytul_pl, rs2csv(), jak i rs2tab() istnieje jeszcze drugi, opcjonalny parametr: tytul, data_premiery) VALUES (?, ?, ?, ?) ; 88 INTERNET.sierpień.2004 CMYK WARSZTAT PROGRAMY MAGAZYN FIRMA Z OKŁADKI NEWSY NA CD technologie $prepared = $db->Prepare($query); tu value odpowiada drugiej kolumnie zapytania. Drugi parametr $f_loc = premiery.csv ; przekazany metodzie GetMenu jest pusty, ponieważ ten parametr usta- $file = file($f_loc); wia domySlnie wybrany element listy wyboru (gdybySmy chcieli usta- foreach($file as $line){ wić film The Punisher jako domySlny, należałoby w tym miejscu $record = explode( , , $line); wpisać jego nazwę). Trzeci parametr odpowiada za wySwietlenie do- $rs = $db->Execute($prepared, $record); datkowego pustego pola do wyboru (my wybraliSmy fałsz, co oznacza, } że na początku listy nie pojawi się puste pole). rs2html($db->Execute( SELECT * FROM premiery )); ?> Wynik do tabeli HTML W tym przykładzie spotykamy się z nową metodą. Prepare() nie wy- W ADOdb napotkamy na wiele ułatwień programistycznych. Jednym konuje zapytania na bazie, lecz informuje bazę danych, że będziemy wy- z takich ułatwień jest funkcja rs2html(). Na podstawie wyników za- woływać wielokrotnie to samo zapytanie (lecz zmieniały się będą dane pytania automatycznie wygeneruje tabelę HTML. Jednak by skorzy- oznaczone znakiem zapytania). Takie rozwiązanie pozwala przyspieszyć stać z tej funkcji, musimy dołączyć do wykonywanego skryptu do- nieco dodawanie rekordów, lecz tylko w przypadku, gdy obsługuje nas datkową bibliotekę: tohtml.inc.php. Szybkie tworzenie tabel ułatwia baza danych Interbase lub Oracle. W przypadku baz danych nie obsługu- pisanie programów oraz służy do usuwania błędów, a nie do genero- jących zapytań typu prepare (np. MySQL) nie odczujemy żadnej różni- wania tabel na strony internetowe. Rzućmy okiem na kompletny cy w wydajnoSci. Przy użyciu funkcji file() pobieramy do tablicy nume- przykład: rycznej zawartoSć pliku premiery.csv . Uzyskaną w ten sposób tablicę wrzucamy w pętle foreach. Spójrzmy co znajduje się w zmiennej $line: include( adodb/adodb.inc.php ); 1,Dirty Dancing 2,Dirty Dancing: Havana Nights,2004-06-04 include( adodb/tohtml.inc.php ); Zmienna kryje pierwszą linię, którą znajdziemy w pliku premie- $db = &NewADOConnection( mysql ); ry.csv . Należy ją teraz podzielić, by kolejnymi elementami tablicy były $db->Connect( localhost , uzytkownik , haslo , poszczególne komórki, które będziemy mogli wrzucić do tablicy. Zrobi- baza ); my to korzystając z funkcji explode(), która rozdziela ciąg znaków we- $rs = $db->Execute( SELECT * FROM premiery ); dług okreSlonego wzorca i tworzy tablicę z podzielonych skrawków. Na- rs2html($rs); stępnie przy użyciu metody Execute() wykonujemy zapytanie do bazy. ?> W tym miejscu pojawia się nowy, drugi parametr. Jest to tablica $record, która zawiera wynik działania funkcji explode(). ADOdb z tablicy $re- cord pobierze kolejne jej elementy i zastąpi nimi pytajniki widoczne przy metodzie Prepared(). Utworzy się tym samym kompletne zapytanie, a da- ne trafią do bazy. W przykładzie operowaliSmy co prawda na tabeli pre- miery , lecz przed wykonaniem przykładu została ona oczyszczona z re- kordów. W przeciwnym wypadku żadne dane nie trafiłyby do bazy. Dla- czego? Ponieważ zduplikowalibySmy pola ID, które już istnieją (a takie zapytanie baza zwyczajnie odrzuci). rs2html() wynik zapytania prosto do tabeli HTML Tworzenie list wyboru Kolejnym ułatwieniem na które natkniemy się korzystając z ADODb jest WICEJ O ADODB W SIECI możliwoSć automatycznego tworzenia HTML-owych list wyboru. Listy http://php.weblogs.com/ADODB oficjalna strona projektu ADOdb typu SELECT tworzy się prosto z wyniku zapytania, co ilustruje poniższy http://phplens.com/adodb/adodb_tutorial_pl.html wprowadzenie przykład (pominięto połączenie z bazą): do ADOdb w języku polskim $rs = $db->Execute( SELECT tytul_pl, id FROM http://php.weblogs.com/portable_sql jak tworzyć uniwersalne zapytania SQL premiery ); http://php.weblogs.com/adodb#downloads aktualna wersja ADOdb do pobrania echo pokaż datę premiery filmu: ; http://phplens.com/lens/adodb testy wydajności ADOdb w stosunku echo
; Podsumowanie Biblioteka ADOdb jest obecnie najpopularniejszym i najbardziej za- awansowanym rozwiązaniem oferującym możliwoSć tworzenia skryp- tów PHP niezależnych od używanego systemu bazodanowego. Umie- jętnie zaciera różnice występujące pomiędzy dostępnymi na rynku tech- nologiami bazodanowymi. Obsługuje pokaxną liczbę baz danych, co daje programiScie duże pole manewru. Niewątpliwą zaletą ADOdb jest ciągłe udoskonalanie oraz wiele nieoficjalnych rozszerzeń dostępnych w Internecie. Rozległa i dobra dokumentacja sprawia, że korzystanie Szybkie tworzenie list wyboru SELECT z ADOdb jest bardzo wygodne. W powyższym przykładzie pojawia się jedna nowa metoda. GetMe- ADOdb oferuje ciekawe funkcje w zakresie dynamicznego limitowa- nu() wykonujemy na zbiorze wyników. By lista wyboru mogła zostać pra- nia i segmentowania wyników. Rozwiązanie to kierowane jest do pro- widłowo utworzona, musimy dostarczyć dwie wartoSci: gramistów piszących kod wielokrotnego użytku. Tu na mySl przychodzi zastosowanie ADOdb w Srednich i dużych serwisach internetowych, Tytuły filmów potrzebne do stworzenia listy są danymi pochodzą- aplikacjach typu CMS czy CRM, gdzie od aplikacji webowej wymaga cymi z pierwszej kolumny zapytania. Analogicznie zawartoSć elemen- się wieloplatformowoSci i niewielkich wymagań technicznych. n INTERNET.sierpień.2004 89