PHP Solutions 03 2007 PL

84

description

* MySQL* Eclipse* Framework P4A* osCommerce* Scaffolding* CakePHP* JavaScript* WordPress 2.x* Steganografia* Kent Michell – dyrektor Marketingu Produktu firmy Zend

Transcript of PHP Solutions 03 2007 PL

4 5

6 AKTUALNOŚCIKrzysztof Trynkiewicz

8 OPIS CDDorota Pączka

DLA POCZĄTKUJĄCYCH10 MySQLŁukasz SkowrońskiŁukasz nauczy Cię, w jaki sposób zbudować zapytania MySQL, wy-korzystując łączenie tabel oraz aliasy. Jeśli wcześniej miałeś do czy-nienia z MySQL, ale nie wiesz jak stosować łączenie tabel – przeczy-taj ten artykuł.

14 EclipseGuillaume FouquetGuillaume pokazuje, jak integrować, konfigurować i używać środowi-ska programistycznego Eclipse w Windows, jego modułu PHPEclipse i stosu XAMPP. Artykuł polecamy tym, którzy nie mieli wcześniej z nim styczności.

26 Framework P4AMichał GajekMichał pokazuje, w jaki sposób można wykorzystać framework P4A. Dowiesz się, jak bardzo jest on przydatny - dzięki niemu możesz stwo-rzyć sprawnie działający katalog płyt w bardzo krótkim czasie.

PHP Solutions jest wydawany przez Software-Wydawnictwo Sp. z o.o.

Dyrektor wydawniczy: Sylwia Pogroszewska

Redaktor naczelny: Patrycja Wądołowska [email protected]

Redaktor prowadzący: Dorota Pączka [email protected]

Korekta: Mateusz Lipiński [email protected]

Kierownik produkcji: Marta Kurpiewska [email protected]

Projekt okładki: Agnieszka Marchocka

Skład i łamanie: Robert Zadrożny [email protected]

Dział reklamy: [email protected]

Prenumerata: Marzena Dmowska [email protected]

Nakład: 6 000 egz.

SPIS TREŚCI

3/2007 (20)

4 5

PRAKTYKA

30 osCommerceMagdalena MaryańskaMagdalena pokaże Ci parę przydatnych rozwiązań, dzięki którym po-lepszysz funkcjonowanie swojego sklepu internetowego. Nauczysz się m. in. stworzenia skryptu wypisującego zawartość całego sklepu.

NARZĘDZIA36 ScaffoldingPiotr GapińskiPiotr pokaże Ci, jak przygotować własne aplikacje internetowe, dzia-łające w oparciu o tryb scaffold CakePHP. Dzięki temu artykułowi po-znasz zasady propotypowania aplikacji wykorzystując CakePHP.

40 CakePHP Piotr GapińskiPiotr opisuje zasady funkcjonowania nowoczesnego frameworka opartego o architekturę MVC. Po przeczytaniu artykułu będziesz w stanie stworzyć aplikacje internetowe działające działające w opar-ciu o CakePHP.

54 JavaScriptJakub SachaJakub opisuje kilka najpopularniejszych i najciekawszych zestawów skryptów oraz frameworków, które ułatwią Ci pracę z JavaScript. Z artykułu dowiesz się też, jak zastosować gotowe rozwiązania ty-pu Dojo.

58 WordPress 2.xŁukasz SosnaŁukasz nauczy Cię, jak instalować i konfigurować system WordPress. Jeśli chcesz stworzyć swój własny pamiętnik internetowy zdecydowa-nie ułatwi Ci to zastosowanie opisanego tu systemu.

BEZPIECZEŃSTWO66 SteganografiaPiotr GapińskiPiotr pokaże Ci, w jaki sposób ukrywać informacje w plikach graficz-nych. Po przeczytaniu artykułu nie będziesz miał problemów z przygo-towaniem plików, które zawierają ukryte wiadomości tekstowe.

70 WYWIADKent Michell – dyrektor Marketingu Produktu firmy Zend

TESTY KONSUMENCKIE72 Serwery dedykowane

76 RECENZJEŁukasz Skowroński, Łukasz Witczak

FELIETON78 Zatrudnię informatyka...Łukasz Skowroński

Adres korespondencyjny:Software-Wydawnictwo Sp. z o.o., ul. Bokserska 1, 02-682 Warszawa, Polskatel. +48 22 887 13 35, fax +48 22 887 10 11www.phpsolmag.org [email protected]

Dołączoną do magazynu płytę CD przetestowano programem AntiVirenKit firmy G DATA Software Sp. z o.o.

Redakcja dokłada wszelkich starań, by publikowane w piśmie i na towarzyszących mu nośnikach informacje i programy były poprawne, jednakże nie bierze odpowiedzialności za efekty wykorzystania ich; nie gwarantuje także poprawnego działania programów shareware, freeware i public domain.

Uszkodzone podczas wysyłki płyty wymienia redakcja.

Wszystkie znaki firmowe zawarte w piśmie są własności odpowiednich firm zostały użyte wyłącznie w celach informacyjnych.

Redakcja używa systemu automatycznego składu

Do tworzenia wykresów i diagramów wykorzystano program firmy

Osoby zainteresowane współpracą prosimy o kontakt:[email protected]

Druk: ArtDruk

Wysokość nakładu obejmuje również dodruki. Redakcja nie udziela pomocy technicznej w instalowaniu i użytkowaniu programów zamieszczonych na płycie CD-ROM dostarczonej razem z pismem.

Sprzedaż aktualnych lub archiwalnych numerów pisma po innej cenie niż wydrukowana na okładce – bez zgody wydawcy – jest działaniem na jego szkodę i skutkuje odpowiedzialnością sądową.

Pismo ukazuje się w następujących wersjach językowych:

polskiej , francuskiej

www.phpsolmag.org

3/2007

Aktualności

6 www.phpsolmag.org

Aktualności

7

Subversion dla każdegoNa serwisie jakilinux.org powstał już jakiś czas temu obszerny wpis na temat systemu kontro-li wersji SubVersion. Dowiemy się z niego, jak zainstalować i używać tego systemu, gene-rować certyfikat SSL przy pomocy Apache2, z którego SubVersion korzysta, przedstawio-ne zostały także nakładki na konsolę SubVer-sion (kdesvn, Rapid SVN, eSVN, WebSVN, Tor-toiseSVN. Więcej na temat samego projektu znajdziemy w Wikipedii i na oficjalnej stronie, podajemy jednak także linki do konkurencyj-nych systemów kontroli wersji oraz kilka przy-datnych odnośników co do samego SVN.http://subversion.tigris.org

Najpopularniejsze ataki w PHPOchrona skryptów PHP jest zagadnieniem bardzo obszernym, jednak niezwykle istot-nym. Do podobnego wniosku doszedł autor bloga z http://albi.xve.pl, robiąc podsumo-wanie luk bezpieczeństwa w aplikacjach PHP. Jego wpis opisuje metody ataków i zabezpieczenia skryptów przeciwko: SQL Injection, Shell Injection, Cross-Site Scripting (XSS), Code Injection, HTTP Response Split-ting, Directory Traversal, Session Fixation, Session Injection, Session Poisoning, E-mail Injection. Jeśli któreś z tych określeń nie jest Tobie znane, polecam przyjrzeć się temu opracowaniu. Podobne informacje znajdzie-my także w niewielkim PDF-ie przygotowa-nym przez Patryka Zawadzkiego, opubliko-wanego na jego blogu.http://albi.xve.pl/2007/02/21/najpopularniejsze-ataki-w-phphttp://room-303.com/blog/2007/01/10/php-bezpieczniej

Kompendium informacji o AJAX-ie“Hey, JavaScript is pretty cool, who knew?” -- tak wita nas slogan opisywanej witryny, sta-nowiącej blog pełen informacji o nowościach związanych z AJAX-em. Faktycznie, do niedaw-na używaliśmy przecież JavaScriptu do pod-mian obrazków na stronach po najechaniu kursora, zaś wszelkie bardziej zaawansowane techniki nazywaliśmy już dumnie dHTML (od Dynamic HTML). Dziś, jesteśmy o lata świetlne do przodu – AJAX zrewolucjonizował Internet, wprowadzając Web 2.0. Na prowadzonym od dwóch lat blogu znajdziemy tysiące wpisów dotyczących tej technologii.http://ajaxian.com

Dokumentacja Prototype – już jest!W poprzednim numerze pisaliśmy na temat zbierania informacji do dokumentacji przez autorów frameworka AJAX-owego Prototy-pe. Od tego czasu, witryna projektu przeszła gruntowne przebudowanie. Obecnie stano-wi miły dla oka, ogromny zbiór informacji. Sam Prototype jest już w wersji 1.5.0 i uka-zuje wiele bardzo przydatnych funkcjonalno-ści. Dzięki temu, na światło dzienne wyszła nowa wersja script.aculo.us – 1.7.0. Mamy więc urodzaj na nowości w świecie AJAX-a. Dodatkowo, Sam Stephenson zarejestrował domenę i uruchomił listę dyskusyjną.http://prototypejs.orghttp://script.aculo.us

WikiCalcp

WikiCalc to darmowy arkusz kalku-lacyjny o otwartym kodzie, który może zostać uruchomiony tak na

serwerze, jak na komputerze domowym użyt-kownika (jego trzon jest bowiem napisany w Perlu). Rozwiązanie drugie pozwala na pracę w trybie offline i publikowanie zmian do ko-pii WikiCalc w Internecie przy użyciu wbu-dowanego klienta FTP. Jeśli więc nie zawsze mamy dostęp do Internetu, by skorzystać np. z Google Documents & Spreadsheets (http://docs.google.com), możemy pokusić się o wy-korzystanie tego rozwiązania. System w sty-lu Wiki gwarantuje, że możliwa będzie edycja arkusza przez wielu użytkowników. Arkusz kalkulacyjny jest bardzo rozbudowany – po-siada ponad 100 wbudowanych funkcji, refe-rencje w formułach mogą odpowiadać warto-ściom innych arkuszy. Ponadto mamy do dys-pozycji wiele typów danych: czasowe, finanso-we, a nawet definiowane przez użytkownika. Formatowanie kolumn odzwierciedla pełen za-kres opcji oferowanych przez inne aplikacje te-go typu: modyfikować możemy czcionkę, ko-lor, obramowanie, margines, wypełnienie, wy-sokość i szerokość kolumn. Kolumny możemy także łączyć. Dodatkowo, możemy wprowadzić ukryte komórki, wiersze, czy kolumny. Do dys-pozycji mamy także sortowania, wypełnienia, masowe operacje na zbiorach komórek. Import i eksport możliwy jest przy użyciu kilku for-

matów, z których najważniejszym jest oczywi-ście CVS i tzw. Tab-delimited text, co gwaran-tuje, że nasze dane będą akceptowane bez pro-blemu przez inne systemy arkuszy kalkulacyj-nych. Po arkuszu możemy poruszać się spraw-nie przy użyciu myszki lub klawiatury. Peł-ną listę funkcjonalności znajdziemy na http://www.softwaregarden.com/products/wikicalc/features.html – jest ona bardzo duża i można w niej dostrzec kilka innowacyjnych rozwiązań (kanały RSS, wbudowana funkcja wckHTTP do komunikacji ze zdalnymi skryptami itp.). Warto się jej przyjrzeć nie tylko ze względu na sam WikiCalc – niektóre solucje możemy wy-korzystać z powodzeniem przy naszych projek-tach. Software Garden w ostatnich tygodniach opublikowało pierwszą stabilną wersję (1.0) aplikacji, której prezentację możemy obejrzeć w screencast’ie trwającym aż 15 minut, na oficjal-nej stronie. Tam też znajdziemy wiele informa-cji na temat załączania linków typu “Edytuj ten arkusz”, dołączania samych arkuszy do witryn, funkcjonalności LiveView (podgląd na żywo, w trakcie edycji przez użytkownika) i wstęp “dla początkujących”. Cały projekt jest bardzo do-brze rozwinięty, udokomentowany i udostęp-niany na licencji GPL 2.0.

http:/ /www.softwaregarden.com/products/wikicalchttp://docs.google.com

3/2007

Aktualności

6 www.phpsolmag.org

Aktualności

7

Innowacyjny chat AJAX--owyW poprzednim numerze opisywaliśmy two-rzenie innowacyjnych systemów rankingo-wych przy użyciu Script.Aculo.Us oraz Proto-type. Autor wpadł na kolejny pomysł, który od razu zrealizował. Efektem jest chat o otwartym kodzie, udostępniany za darmo do zastosowań niekomercyjnych. Internau-ta może za pomocą kliknięcia rozwinąć lub schować chat - nie przeszkadza mu w prze-glądaniu witryny, będąc domyślnie scho-wanym. Internatura może sprawnie poru-szać się po witrynie mając rozwinięty chat: nie jest to pop-up, lecz stanowi integralną część strony, będąc wraz z nią przesuwal-nym suwakami. Sprawne poruszanie ozna-cza także, że przy przejściu do innej pod-strony, chat zostanie automatycznie otwar-ty. Chat jest estetyczny: nie zawiera żad-nych grafik, jednak świetnie komponuje się z witrynami społecznościowymi - głów-nie forami, czy stronami hobbystycznymi. Adresy są automatycznie zamieniane na odnośniki. Przewijanie okna jest podobne do znanego z Gmaila: chat sprawdza, czy użytkownik nie czyta wcześniejszych wia-domości, zanim przesunie się, pokazując nową treść. Dodatkowo dodany jest przy-jemny dla oka efekt znikania i pojawiania się chata. Z punktu widzenia developera, stano-wi w miarę kompaktowy skrypt: całość zaj-muje 31KB (po usunięciu niektórych zbęd-nych części pakietu Script.Aculo.Us). Oparty jest na Prototype i MySQL, a kod jest na tyle przejrzysty, że zbędne są komentarze. Natu-ralnie, jest odporny na XSS i jemu podob-ne. Skrypt nie generuje dużego obciążenia: aktualizuje treść jedynie przy rozwiniętym oknie, z czasem ustawionym domyślnie na 2 sekundy. Pobiera generowany przy wysy-łaniu wiadomości plik stały z odpowiedni-mi nagłówkami, które gwarantują korzysta-nie z cache przeglądarki w przypadku braku zmiany treści.http://ajax.eldoras.com

jQuery – AJAXowy frameworkNie każdemu odpowiada Prototype, czy to ze względu na jego rozmiar (70KB), czy brak zaimplementowanych efektów, które trzeba dołączać dzięki Script.Aculo.Us (które także swoje zajmuje). Dobrą alternatywą wydaje się jQuery, które, skompresowane, zajmuje 19KB (warto nadmienić, że skompresowany Proto-type ma 13KB – wpis na ten temat znajdzie-my na wyżej opisywanym serwisie Ajaxian). jQuery zawiera obszerną dokumentację i ma przyjemną (aczkolwiek inną od tej znanej z Pro-totype) składnię. Opiera się na pluginach – są ich dziesiątki. Służą do usprawniania nawiga-cji, animacji, efektów związanych z grafikami, sprawdzaniu formularzy... Lista jest długa i cie-kawa. Na witrynie projektu znajdziemy także informacje, jak używać jQuery z innymi biblio-tekami (np. Prototype), choć z oczywistych względów, rozważniej skupić się na jednym rozwiązaniu. Kilkadziesiąt tutoriali w różnych językach pomoże zrozumieć, jak działa frame-work i jak go używać.http://jquery.com

Identity 2.0, czyli nowa tożsamość w Internecie

Zagadnienie Identity 2.0 to ostatnio bardzo popularny temat poruszany przy okazji zagadnień Web 2.0. Do

niedawna była to tylko teoria, jednak w cią-gu kilku ostatnich miesięcy w Internecie po-jawiły się praktyczne implementacje cyfrowej tożsamości drugiego stopnia. Na świecie dzia-ła już kilka serwisów typu OpenID, w Pol-sce – dwa. Pierwszym jest OpenID.pl, dru-gim – MyID.pl. Zasada działania tego typu systemów jest bardzo podobna, dlatego war-to przytoczyć opis działania systemu Ope-nID.pl:

“OpenID to zbiór protokołów i specyfika-cji, umożliwiających tworzenie zdecentralizo-wanych systemów obsługi tożsamości cyfro-wej kontrolowanej przez użytkownika. Jest on oparty na adresach internetowych URI. Wersja 2.0 dopuszcza także stosowanie i-names w ro-li identyfikatorów. Podstawową usługą OpenID jest uwierzytelnianie/identyfikacja.

Pierwszym krokiem jest założenie konta na dowolnie wybranym serwerze OpenID peł-niącym rolę dostawcy tożsamości. Od dostaw-cy tożsamości otrzymujemy adres interneto-wy (URI), będący zarówno naszym loginem w serwisach korzystających z OpenID jak i adre-sem strony, na której będziemy podawać dane uwierzytelniające (np. hasło, hasło jednorazo-we, liczba odczytana z tokenu).

Aby „zalogować się” do serwisu, używające-go OpenID podajemy zamiast nazwy użytkow-nika adres internetowy (URI), który w OpenID spełnia rolę loginu. Serwis następnie przekiero-wuje nas pod ten adres, który obsługiwany jest przez dostawcę tożsamości. Ten żąda od nas ha-sła , a następnie, jeśli hasło się zgadza, komu-nikuje się z serwisem, z którego przyszliśmy i uwierzytelnia tam nas.

Ten modelowy proces może być nieco zmo-dyfikowany (w wersji 2.0 specyfikacji Ope-nID):

l Adres, który jest naszym identyfikato-rem, niekoniecznie musi być adresem do-stawcy tożsamości. Może być np. adres bloga, czy innej strony, na której dopiero (w nagłówku strony lub w postaci pliku Yadis) jest zawarty adres dostawcy tożsa-mości. To rozwiązanie pozwala na unie-zależnienie się od konkretnego dostaw-cy tożsamości (w przypadku, gdy np. do-stawca tożsamości zbankrutuje, umiesz-czamy w nagłówku strony lub pliku Yadis inny adres -- nasz identyfikator pozostaje bez zmian).

l Identyfikator może być adresem stro-ny głównej dostawcy tożsamości. Wów-czas dopiero na niej podajemy swój URI. W ten sposób serwis, który nas uwierzy-telnia nie zna nawet naszego OpenID, co może mieć znaczenie w niektórych sytu-acjach.

l Identyfikator może mieć postać XRI.

Dzięki tym możliwościom OpenID stanowi wygodną platformę do realizacji pojedynczej rejestracji.

Poza tą podstawową usługą, OpenID obej-muje (lub będzie obejmować w przyszło-ści) inne usługi takie jak udostępnianie pro-filu (nasz dostawca tożsamości może prze-kazać za naszą zgodą podstawowe dane oso-bowe) czy ocenę jakości otrzymywanych da-nych (strona, na której podajesz OpenID bę-dzie mogła spytać się, czy użytkownik zakła-dając tożsamość u danego dostawcy tożsamo-ści zweryfikował swój e-mail lub wprowadził captcha).“

Deweloperzy PHP prowadzący blogi już zaczęli implementować obsługę OpenID na swoich platformach, możemy więc spodzie-wać się, że na dniach rozwiązanie pojawi się w systemach CMS i forach dyskusyjnych. Wiele informacji znajdziemy na oficjalnej stronie projektu oraz na wiki i blogu serwi-su Identity2.0.pl.

http://wiki.identity20.plhttp://blog.identity20.plhttp://openid.plhttp://myid.plhttp://openid.nethttp://en.wikipedia.org/wiki/OpenID

3/20078 www.phpsolmag.org 9

HateML Pro 1.0.Program służący do tworzenia stron internetowych (PHP, XTML, CSS, XML, JavaScript, VBScript, SQL). Funkcje ofero-wane przez program to m. in.: podświetlenie składni, weryfika-cja błędów, autouzupełnianie kodu, podpowiedzi do kodu PHP, wsparcie dla Unicode, inspektor właściwości tagów (podobny do Delphi lub VS). Dodatkowym atutem programu jest jego inter-fejs: dzięki zakładkom, można edytować kilka plików naraz. Po-nadto wbudowany klient FTP pomoże aktualizowaniu zmian w projektach. To program dla każdego webmastera: początkujące-go i zaawansowanego. Główne cechy programu:

• obsługa debuggera z breakpointami, podglądem zmiennych globalnych i lokalnych, uruchamianiem krok po kroku i wie-loma innymi możliwościami;

• zdalne debugowanie PHP;• profiler kodu PHP• wbudowany klient FTP z możliwością zdalnej edycji;• Code Folding – zwijanie (ukrywanie) bloków kodu;• wsparcie dla Unicode;• automatyczne wstawianie, podświetlanie i wcięcie dla nawia-

sów;• automatyczne sprawdzanie składni PHP• PHP Parser – wyświetla strukturę dokumentu• i wiele innych

który zdecydowanie ułatwia pracę użytkownikom. Najważniej-sze części:

• Framework (do rozwijania podstawowych aplikacji szablonu)• PHP Code Generator• PHP Template engine

Opis CD

TemplateTamerNarzędzie do tworzenia i zarządzania stroną internetową bazu-jące na webowej aplikacji PHP, łatwo i kompletnie oddzielające kod PHP od projektowania w HTML. Jest to bardzo przydatne narzędzie, ponieważ zapewnia kompleksowe rozwiązanie: dla jednoczesnej pracy programistów i projektantów serwisów in-ternetowych.

TemplateTamer utrzymuje rozdzielnie pliki html i php, a przechodzenie z jednego rodzaju plików do drugiego odbywa się poprzez proste klikanie na poszczególne zakładki, co w re-zultacie bardzo ułatwia pracę z narzędziem. Ponadto stworzo-ne przez Ciebie szablony możesz ułożyć w formie drzewka, dzię-ki czemu stworzy ono prostą do odczytania strukturę. Template Tamer posiada Code Generator, który generuje niezbędny kod,

PostgreSQL 8.2.3Jest to jeden z najpopularniejszych systemów zarządzania relacyj-nymi bazami danych.

MySQL 5.0.27Chyba najpopularniejsza darmowa baza danych używana przy tworzeniu aplikacji Webowych.

Joomla 1.0.12Wywodzący się z Mambo System portalowy (CMS) napisany w php. Dane mogą być przechowywane w bazie danych MySQL.

phpbbSkrypt najpopularniejszego, darmowego forum dyskusyjnego. Ob-sługuje system skórek, dzięki któremu możemy indywidualnie do-pasować wygląd do swoich potrzeb za pomocą gotowych skórek bądz stworzyć własny. Dzęki panelowi administracyjnemu, mo-żemy definiować grupy użytkowników, nadawać im uprawnie-nia, itp.

3/20078 www.phpsolmag.org 9

Redakcja nie udziela pomocy technicznej w instalowaniu

i użytkowaniu programów zamieszczonych na płytach

CD-ROM dostarczonych razem z pismem.

Jeśli nie możesz odczytać zawartości płyty CD, a nie

jest ona uszkodzona mechanicznie, sprawdź ją na co

najmniej dwóch napędach CD. W razie problemów

z płytą, prosimy pisać pod adres: [email protected]

3/2007

Dla początkujących

10

Pierwsze pytanie, które się nasuwa to: co to jest to łączenie tabel? Wytłuma-czenie tego bez konkretnego przykła-

du graniczy z cudem. Dlatego w dalszej czę-ści artykułu poznając różne sposoby łącze-nia tabel postaram się przedstawiać jasne przykłady, które ułatwią zrozumienie. Bę-dziemy mówić o łączeniu tabel, w których możemy dopatrzyć się wspólnych danych jak i łączeniu tabel, w których dane są zu-pełnie niespokrewnione. Na koniec powie-my też o aliasach, które naprawdę ułatwiają życie każdemu programiście i pozwolą opty-malniej wykorzystać zdobytą wiedzę. Zapra-szam dalej.

Łączymy tabele ze wspólnymi danymiWyobraźmy sobie, że mamy bazę danych fir-my zajmującej się sprzedażą płyt CD. Baza ta składa się z trzech tabel tj. bills, cd oraz com-position. W tabeli bills będziemy przechowy-wać id_bill, sumę cen zakupionych płyt oraz informację – kto sprzedał płyty. Dla czytelno-ści przykładu pracownik będzie identyfikowa-ny po imieniu i nazwisku, chociaż optymal-niej byłoby wprowadzić kolejną tabelę maga-zynującą informacje o pracownikach, a w ta-beli bills przechowywać tylko id pracownika. W tabeli cd znajdą się podstawowe informa-cje o produkcie. Natomiast w tabeli compo-sition przechowywać będziemy informacje o

tym, jakie produkty zostały przypisane do da-nego paragonu. Utworzymy je poprzez polece-nia widoczne w Listingu 1.

Po utworzeniu tabel, nadszedł czas na wprowadzenie paru rekordów, na których bę-dziemy mogli ćwiczyć. W pierwszej kolejności wprowadzimy trzy płyty CD. Dwie z nich są tego samego wykonawcy, ale noszą inne tytu-ły. Trzecia płyta jest innego wykonawcy i in-nego gatunku.

INSERT INTO cd VALUES(null,'nazwa_zespolu',

'plyta1','rock',10);

INSERT INTO cd VALUES(null,'nazwa_zespolu',

'plyta2','rock',10);

INSERT INTO cd VALUES(null,'inny_zespol',

'inna_plyta','klasyczna',10);

Następnie wprowadzamy dwa paragony, któ-re zostały wystawione przez sprzedawcę Ja-na Kowalskiego. Pierwszy z nich jest na kwo-tę 10 a drugi na kwotę 20 – pominiemy sys-tem walutowy.

INSERT INTO bills VALUES(null,10,

'Jan Kowalski');

INSERT INTO bills VALUES(null,20,

'Jan Kowalski');

Na koniec dodamy 3 rekordy, które wskażą nam, do którego paragonu należy przypisać, jakie pły-ty. Łatwo możemy zauważyć ze do pierwszego paragonu przypisano jedną płytę CD, natomiast na drugim paragonie znajdują się dwie płyty.

INSERT INTO composition VALUES(null,1,1);

INSERT INTO composition VALUES(null,2,1);

INSERT INTO composition VALUES(null,2,2);

Utworzyliśmy już tabele i wypełniliśmy je da-nymi. Załóżmy, że chcemy wyszukać ile sprze-

MySQL

Jeżeli uważasz, że znasz MySQL, ale nie korzystałeś nigdy z łączenia tabel, bo po prostu tego nie potrafisz lub nie widzisz miejsca, w którym można to zastosować ten artykuł jest dla Ciebie. Wprowadzimy Cię w ten temat w prosty i przystępny sposób opisując każdy problem zrozumiałym przykładem.

Co obiecujemy...l Nauczymy Cię budowy zapytań MySQL wyko-

rzystujących łączenie tabel oraz aliasy.

Co należy wiedzieć..l Powinieneś znać podstawy języka MySQL i

PHP.

Poziom trudności

Łączenie tabel

Listing 1. Tworzymy tabele w naszej bazie danych

CREATE TABLE bills (

id_bill int(5) unsigned not null auto_increment primary key,

price float(6,2) unsigned not null,

worker char(50) not null

);

CREATE TABLE cd (

id_cd int(5) unsigned not null auto_increment primary key,

group char(30) not null,

title char(30) not null,

type char(15) not null,

price float(6,2) unsigned not null

);

CREATE TABLE composition (

id int(5) unsigned not null auto_increment primary key,

id_bill int(5) unsigned not null,

id_cd int(5) unsigned not null

);

3/2007

Dla początkujących

12

daliśmy już płyt zespołu nazwa_zespolu. W pierwszym wariancie zrobimy to bez łączenia tabel (Listing 2).

Zajęło nam to 15 linijek kodu wykonywa-nego przez serwer, w którym dwa razy wysłali-śmy zapytanie sql do bazy danych oraz wykona-liśmy w najlepszym przypadku zero razy w naj-gorszym nawet kilkadziesiąt razy pętle while.

Teraz zrobimy to samo za pomocą łączenia ta-bel (Listing 3).

Skróciliśmy to do 11 linijek wykonywane-go kodu ,jednego zapytania sql do bazy da-nych oraz zlikwidowaliśmy pętlę while. W obu przypadkach wynikiem powinna być liczba 3 ponieważ płyty zespołu nazwa_ze-spolu znalazły się na dwóch paragonach – na

pierwszym jedna płyta, a na drugim dwie płyty. Na tak krótkim przykładzie zaosz-czędziliśmy trochę pracy oraz czasu. Jednak gdybyśmy mieli zamiar połączyć duże tabele mające po kilka/kilkanaście tysięcy rekordów lub więcej to proces łączenia tych tabel praw-dopodobnie pochłonąłby dużo mocy oblicze-niowej procesora i w efekcie byłby wolniejszy od wykonania dwóch oddzielnych zapytań. Warto więc sprawdzić czas wykonania skryp-tu dla konkretnego przypadku i określić, któ-ry sposób będzie korzystniejszy. Teraz po ko-lei omówimy zapytanie sql, które skonstru-owaliśmy w poprzednim listingu.

SELECT composition.id ...

Za pomocą tej części wybieramy, co ma zostać zwrócone jako odpowiedź z bazy danych. Tu-taj wybraliśmy, że będą zwracane id z tablicy composition. Więc ogólny schemat wygląda następująco nazwa _ tabeli.nazwa _ kolumny. Dalej mamy:

... FROM composition, cd ...

Tutaj następuję łączenie tabel. Dokonuje-my tego poprzez wymienienie łączonych tabel i oddzielenie ich przecinkiem. Na-stępnie mamy warunki, które konstruuje-my w sposób analogiczny do wyboru zwra-canego wyniku oraz ogólnie przyjętych za-sad, czyli :

... WHERE composition.id_cd=cd.id_cd AND

cd.group='nazwa_zespolu';

Tutaj znowu mamy schemat nazwa _

tabeli.nazwa _ kolumny. Ważną rzeczą pod-czas tworzenia warunków jest pamiętanie o tym, aby stworzyć połączenie pomiędzy jed-ną, a drugą tabelą poprzez którąś z kolumn. Dzięki temu będziemy mogli dokonać porów-nania. W naszym przypadku porównujemy id_cd z dwóch tabel. Wynikiem naszego zapy-tania będą id dla których id_cd z dwóch tabel są sobie równe i są to id_cd płyty zespołu o na-zwie nazwa_zespolu.

Co by było gdybyśmy chcieli sprawdzić ile płyt danego gatunku muzycznego sprze-dał nasz pracownik Jan Kowalski? Sprawdze-nie tego bez łączenia tabel zajęłoby nam spo-ro czasu i wymagało napisania większego ka-wałka skryptu. Dzięki łączeniu tabel zrobimy to szybko i sprawnie za pomocą zapytań z Li-stingu 4.

Wynikiem będą trzy rekordy id.bills, ponie-waż sprzedaliśmy tylko trzy płyty jednego roc-kowego zespołu nazwa_zespolu. Warto zauwa-żyć ze tabela bills i tabela cd nie mają ze sobą bezpośredniego połączenia. Naszym spoiwem jest tabela composition. Jak widać zapytanie to jest bardziej skomplikowane i może wymagać chwilę analizy, aby je w pełni zrozumieć. Jed-

Listing 2. Wyszukanie ilości sprzedanych płyt zespołu nazwa_zespolu bez łączenia tabel

$db=mysql_connect('host','login','haslo');

mysql_select_db('nazwa_bazy',$db);

$question=" SELECT id_cd FROM cd WHERE group='nazwa_zespolu'";

// znajdujemy id produktu po jego nazwie

$id=mysql_query($question,$db);

// jako że zespół może mieć więcej niż jedna płyta trzeba zliczyć wszystkie wyniki do

// tego celu użyjemy zmiennej pomocniczej $number

$number=0;

while($row=mysql_fetch_assoc($id)) {

// kiedy mamy już id możemy wyszukać ile płyt zostało sprzedanych za pomocą

// kolejnego zapytania sql

$question='SELECT id FROM composition WHERE id_cd='.$row['id_cd'];

$answer=mysql_query($question,$db);

// musimy jeszcze tylko sprawdzić ile otrzymaliśmy wyników i dodać do zmiennej

$number +=mysql_num_rows($answer);

}

// wyświetlenie wyniku

echo $number;

mysql_close($db);

Listing 3. Wyszukanie ilości sprzedanych płyt zespołu nazwa_zespolu z wykorzystaniem łączenia tabel

$db=mysql_connect('host','login','haslo');

mysql_select_db('nazwa_bazy',$db);

$question=" SELECT composition.id FROM composition, cd WHERE

composition.id_cd=cd.id_cd AND cd.group='nazwa_zespolu'";

$answer=mysql_query($question,$db);

$number=mysql_num_rows($answer);

echo $number;

mysql_close($db);

Listing 4. Łączenie trzech tabel w celu znalezienia ilości płyt sprzedanych przez Jana Kowalskiego

SELECT bills.id_bill FROM bills, composition, cd WHERE cd.type='rock'

AND worker='Jan Kowalski' AND cd.id_cd=composition.id_cd

AND bills.id_bill=composition.id_bill;

Listing 5. Wysłanie zapytania do bazy danych

$db=mysql_connect('host','login','haslo');

mysql_select_db('nazwa_bazy',$db);

// nasze zapytanie sql wraz z łączeniem tabel przy pomocy left join

$question=" SELECT cd.group , cd.title , composition.id_bill FROM cd LEFT JOIN

composition ON cd.id_cd=composition.id_cd WHERE composition.id_bill IS null";

$answer=mysql_query($question,$db);

while($row=mysql_fetch_assoc($answer)){

echo '<br />Zespół: '.$row['group'].'<br />Tytuł: '.$row['title'];

}

mysql_close($db);

MySQL

www.phpsolmag.org 13

nak od razu możemy stwierdzić, że dzięki te-mu zaoszczędzimy trochę czasu w trakcie wy-konywania skryptu, bo do bazy wyślemy tylko jedno zapytanie zamiast kilku. Dlatego warto, abyś zatrzymał się nad tym chwilę oraz trochę to poćwiczył.

Łączymy tabele z danymi niepołączonymi ze sobąTabele, w których nie możemy doszukać się spo-sobu połączenia zwykłym sposobem musimy łą-czyć inną metodą, aby móc wykonać zapytanie. Używamy do tego polecenia join . Występuje one w różnych składniach np. full join, cross join, in-ner join oraz rigth lub left join. Za chwilę zajmie-my się tym ostatnim, ponieważ jest on najczę-ściej używany. Jeżeli zaś jesteś zainteresowany działaniem pozostałych zajrzyj na stronę: http://dev.mysql.com/doc/refman/5.1/en/join.html.

Aby omówić działanie left join skorzysta-my z bazy danych z poprzedniego przykładu. Tym razem będziemy jednak szukać informa-cji nigdzie niezapisanych. Wydaje się to nie-możliwe, bo przecież jak możemy znaleźć coś, czego nie ma, ale left join otwiera przed nami nowe możliwości. Naszym zadaniem będzie udzielenie odpowiedzi na pytanie czy istnie-je w naszym sklepie płyta cd, która nie zosta-ła sprzedana w ani jednym egzemplarzu. Aby udzielić na to odpowiedzi musimy wykonać kod z Listingu 5.

Ten skrypt wyświetli nam nazwy zespo-łów i nazwy płyt, które nie zostały sprzeda-ne w ani jednym egzemplarzu. Wynikiem bę-dzie napis:

Zespół: inny_zespol

Tytuł: inna_plyta

To, że otrzymamy taki wynik zawdzięczamy działaniu left join, które po dołączeniu tabe-li composition w zwracanej kolumnie compo-sition.id_bill wstawia wartość null wszędzie tam, gdzie w tabeli composition nie istnieje wartość id_cd równa id_cd z tabeli cd. Najle-piej zobrazuje to schemat tabeli z wprowadzo-nymi przykładowymi danymi – Tabela 1.

Jak widzimy w powyższej tabeli, nie znalezio-no id_bill, na którym znajdowałby się id_cd ze-społu trzeciego i dlatego pole composition.id_bill przyjęło wartość null.

Omówimy teraz krok po kroku zapytanie sql. Wybieramy, które pola będą zwracane ja-ko wynik:

SELECT cd.group , cd.title ...

Następnie standardowo wskazujemy pierwszą tabelę, która będzie brała udział w łączeniu:

... FROM cd ...

Teraz czas wybrać tabelę, która zostanie dołą-czona, robimy to w sposób następujący:

... LEFT JOIN composition ...

Jako warunek połączenia poprzez left join sto-sujemy klauzulę on, która określa wybór po-równywanych kolumn z dwóch tabel, co wi-dać poniżej:

... ON cd.id_cd=composition.id_cd ...

Pozostała już tylko końcówka zapytania, któ-rej interpretacja nie powinna sprawić niko-mu problemu:

... WHERE composition.id_bill IS null;

Zapytanie to możemy jeszcze delikatnie zmodyfikować w sposób, który wpłynie na czytelność kodu. Otóż zamiast sformu-łowania on możemy zastosować polecenie using(), które jako parametr przyjmuje na-zwę kolumny, która będzie porównywana. Zapytanie po tej modyfikacji będzie miało następującą postać:

SELECT cd.group , cd.title ,

composition.id_bill FROM

cd LEFT JOIN composition

USING(id_cd) WHERE

composition.id_bill IS null;

Jak widać zasada działania jest bardzo podob-na. Należy jednak pamiętać , że using() wy-musza, aby obie kolumny w obu tabelach mia-ły tą samą nazwę, co w przypadku on nie jest wymagane.

Optymalizacja składni zapytań SQL – AliasyOdpowiedzmy sobie na pytanie, czym są alia-sy i do czego są nam potrzebne. Otóż aliasami nazywamy zazwyczaj krótsze nazwy za pomo-cą których odwołujemy się do konkretnych ta-bel. Aliasów używamy, aby uczynić nasz kod czytelniejszym i skrócić czas potrzebny na wpisanie zapytania SQL. Dla przykładu za-stosowania aliasu użyjemy trochę skrajnego przykładu, ale dzięki temu nie będziesz miał problemu ze zrozumieniem idei. Do tego celu

stworzymy dwie tabelki o bardzo długich na-zwach. Obie tabelki będą miały po jednej ko-lumnie.

CREATE TABLE

pierwsza_tabela_o_dlugiej_nazwie(

id_p int(3) unsigned not null primary

key auto_increment

);

CREATE TABLE

druga_tabela_o_dlugiej_nazwie(

id_d int(3) unsigned not null

primary key auto_increment

);

Przy budowie tego przykładu nie bierzemy pod uwagę funkcjonalności tabel, bo nie o to tutaj chodzi. Spróbujmy teraz wyświetlić wszystkie id_p pierwszej tabeli, które są róż-ne od id_d drugiej tabeli. Zapytanie wygląda-łoby następująco:

SELECT

pierwsza_tabela_o_dlugiej_nazwie.id_p

FROM

pierwsza_tabela_o_dlugiej_nazwie,

druga_tabela_o_dlugiej_nazwie

WHERE

pierwsza_tabela_o_dlugiej_nazwie.

id_p !=

druga_tabela_o_dlugiej_nazwie.id_d;

Całe to sformułowanie wygląda bardzo znie-chęcająco, a przeanalizowanie go może zająć dłuższą chwile. Aliasy bardzo uproszczą nam to zadanie. Oto ten sam przykład z wykorzy-staniem aliasów:

SELECT p.id_p FROM

pierwsza_tabela_o_dlugiej_nazwie

AS p , druga_tabela_o_dlugiej_nazwie

as d WHERE p.id_p != d.id_d;

Całość jest dużo krótsza i znacznie bardziej zrozumiała. Aliasy tworzymy przy pomo-cy słowa as. Schemat wygląda w sposób na-stępujący:

nazwa_tabeli AS alias

Jak widzicie tworzenie aliasów jest bardzo proste , a zarazem bardzo przydatne. Pomimo-,że znajdzie się grupa ludzi, którzy uznają alia-sy za zbędny wynalazek – warto je znać. Pole-cam zapoznać się z nimi, gdyż ich znajomość ułatwi zrozumienie podzapytań, które z pew-nością znajdą w przyszłości zastosowanie w waszych projektach.

Tabela 1. Przykład łączenia tabel poprzez left join

cd.group cd.title composition.id_bill

Zespół pierwszy Płyta 1 34

Zespół drugi Płyta 2 123

Zespół trzeci Płyta 3 null

ŁUKASZ SKOWROŃSKIAutor jest studentem III roku informatyki na Uni-wersytecie w Białymstoku.Kontakt z autorem: [email protected]

3/2007

Dla początkujących

14

Eclipse

www.phpsolmag.org 15

Używanie środowiska programistyczne-go ma, poza debugowaniem, inne za-lety, na przykład kolorowanie składni,

podpowiadanie kodu, itp. W tym artykule, pro-ponuję Ci zrobić pierwsze kroki w Eclipse. Wraz ze stosem XAMPP przygotujesz kompletny pa-kiet składający się z serwera i środowiska progra-mistycznego. Główną zaletą proponowanej plat-formy jest jej koszt, gdyż większość narzędzi jest bezpłatnych. Drugą ważną zaletą jest fakt, że ta platforma nie wymaga instalacji, a jedynie skopio-wania plików na twardy dysk. Można więc ją sko-piować do pamięci USB, co umożliwia dewelope-rowi dysponowanie środowiskiem przez cały czas. Aby zilustrować ten temat, proponuje ci oprzeć się na programie używającym bazy przykłado-wych danych XAMPP jak również silnika szablo-nów Smarty. Program ten będzie przedstawiony szczegółowo w drugiej części tego artykułu.

Stos XAMPPXAMPP jest kompletną platformą WWW prze-znaczoną całkowicie do programowania. Pakiet ten zawiera między innymi: Apache, MySQL, PHP 4.3.X i 5.0.X, Perl, PEAR itp. Jest dostępny w wersji kompletnej i w mniejszej wersji bez nie-których narzędzi. XAMPP jest dostępny dla róż-nych systemów operacyjnych, np. Linux, Win-

dows lub wersji Beta systemu Solaris i MAC OS X. Aby XAMPP działał, wystarczy skopiować da-ne z pakietu. Nie ma plików do edytowania! Inną zaletą jest jego przenośność. W Windows XAMPP nie zapisuje klucza w bazie rejestrującej (nie doty-czy to wersji z instalacją). Można więc go rozpako-wać na jakimkolwiek dysku twardym lub pamię-ci USB. Można też go łatwo przenieść przy mini-malnej konfiguracji. Instalacja nie jest potrzebna, więc większość narzędzi działa prawie tylko ze ścieżkami względnymi. Można zainstalować kil-

ka platform jednocześnie na jednym kompute-rze, co umożliwia na przykład deweloperowi ła-twe przechodzenie z PHP4 na PHP5. XAMPP integruje także bazy danych i aplikacje przykła-dowe, umożliwiające początkującym zapoznanie się z tym typem platformy. Wybrałem tę platfor-mę WWW z jednej strony ze względu na jej prze-nośność i łatwość stosowania, a z drugiej strony, gdyż jest znana ze swojego modułu PHPEclipse, umożliwiającego wspólne działanie XAMPP i Ec-lipse. Proszę zwrócić uwagę na fakt, że w XAMPP, większość ochron nie włącza się domyślnie, gdyż ta platforma jest głównie przeznaczona dla po-trzeb programowania, a nie produkcji (przynajm-niej w swej wcześniejszej konfiguracji). Dowodem na to jest fakt, że domyślnie konto root w MySQL nie ma hasła!

EclipseStworzony przez IBM w 2001r., który zaofero-wał swój pierwszy kod, projekt Eclipse wciąż ro-

Eclipse

Kto z nas nie rozpoczynał debugowania swojego kodu za pomocą funkcji echo? Po zatwierdzeniu takiej funkcji, trzeba jeszcze spędzić trochę czasu na usuwaniu odwoływań do funkcji, które stały się przestarzałe. Okazuje się wtedy, że praktycznym wyjściem jest kodowanie i debugowanie w środowisku programistycznym.

Co obiecujemy...• W jaki sposób integrować, konfigurować i uży-

wać środowiska programistycznego Eclipse w Windows, jego modułu PHPEclipse i stosu XAMPP.

• W jaki sposób uzyskać aplikację wielojęzyczną i wielo-aspektową.

Co należy wiedzieć...• Powinieneś znać podstawy języka PHP i orien-

tować się ogólnie w temacie środowisk progra-mistycznych.

Poziom trudności

Programowanie

Rysunek 1. Włączenie DLL debugowania w PHP

3/2007

Dla początkujących

14

Eclipse

www.phpsolmag.org 15

śnie dzięki partnerstwu i modularności. Napisany w Javie za pomocą graficznej biblioteki SWT fir-my IBM, Eclipse stał się projektem open-source. Ce-lem Eclipse było na samym początku dostarczenie podstawy napisanej w Javie, umożliwiającej stwo-rzenie środowisk programistycznych wokół tej podstawy. Jego główna zaletą jest jego rozszerzal-ność, czyli możliwość dorzucenia do tej podsta-wy pewnej ilości modułów (napisanych w więk-szości również w Javie), umożliwiających wyjście naprzeciw potrzeb użytkowników. Można w ten sposób programować prawie w jakimkolwiek ję-zyku. Jest wszechstronny i wychodzi naprzeciw różnorodnym potrzebom w różnych domenach. Znajdziesz tu na przykład moduły przeznaczo-ne dla deweloperów (C, C++, PHP etc.), moduły do modelowania (UML itp.), biblioteki graficzne itp. Eclipse był zaakceptowany przez większość deweloperów i firm, które programowały rów-nież moduły i uczestniczą, zwłaszcza finansowo, w rozwoju Eclipse. Wymieńmy jeszcze kilku stra-tegicznych deweloperów Eclipse jak Sybase, Com-puter Associates, Borland, Scapa i Zend, który pi-lotuje projekt modułu PHP i, który zamierza na-wet przenieść swoje środowisko programistyczne Zend Studio na Eclipse. Istnieją różne typy aplika-cji, komercyjnych i nie, w Eclipse. Znajdziesz apli-kacje WWW geograficzne, ochrony, kontroli, apli-kacje w dziedzinie chemii, medycyny, energii nu-klearnej czy też aeronautyki.

Wymagania programoweOto lista potrzebnych narzędzi, aby przygoto-wać zaproponowaną platformę:

• XAMPP Lite wersja v1.5.2, która będzie twoją lokalną platformą WWW,

• Eclipse SDK wersja v3.1.2, która będzie twoim osadzonym środowiskiem progra-mistycznym,

• Eclipse wymaga środowiska wykonawcze-go Java, będziesz więc potrzebować Java Runtime Environment,

• moduł PHPEclipse wersja v1.1.8, która umożliwia używanie Eclipse, jako środo-wiska programistycznego PHP,

• DLL debugowania DBG dla Twojej wersji PHP (wersja XAMPP) dla wersji v5.1.2.

Wybór wersjiNiektórzy z was zadają sobie pewnie to pyta-nie: dlaczego platforma nie opiera się na ostat-nich wersjach PHP, Eclipse, itp. Jest to rozmyśl-ny wybór z mojej strony dla następujących po-wodów: jeśli chodzi o wersję PHP, DLL debugo-wania są bezpłatne tylko do wersji v5.1.2 PHP, wybrałem więc wersję XAMPP zawierającą PHP v5.1.2 i XAMPP Lite v1.5.2. Jeśli chodzi o Eclipse, aktualna wersja SDK to v3.2.1, ale moduł PHPEclipse w swojej wersji v1.1.8 nie jest dobrze odbierany przez tę wersję Eclipse (wersja v1.1.9 de PHPEclipse jest właśnie rozwijana). Wybrałem więc wersję Eclipse kompatybilną z ostatnią stabil-ną wersją modułu PHPEclipse.

Instalacja i konfiguracjaW tym rozdziale zobaczysz, jak instalować i konfigurować zaproponowaną platformę pro-gramistyczną. Najpierw zobaczysz, jak zainsta-lować stos XAMPP, a następnie platformę Ec-lipse z modułem PHPEclipse. Po zakończeniu tych instalacji przejdziemy do używania plat-formy z programem demo znajdującym się na płycie CD załączonej do tego numeru.

XAMPPPo ściągnięciu XAMPP, odpakuj pakiet w c:/xampp. Uruchom plik setup_xampp.bat, któ-ry skonfiguruje różne wewnętrzne ścieżki do XAMPP dla twojego katalogu instalacyjnego. Uru-chom teraz xampp-control.exe oraz serwery Apache i MySQL. Uwaga: zapora sieciowa Windows XP może blokować domyślnie serwer Apache dla ce-lów bezpieczeństwa! Przez przeglądarkę Interne-tową idź więc na stronę http://localhost lub http://127.0.0.1. Po wybraniu języka, wybierz wyświe-tlenie informacji związanych z platformą WWW: w tym celu wybierz phpinfo() i zapisz następu-jące ścieżki:

• ścieżkę pliku inicjalizującego PHP odpowia-dającego Configuration File (php.ini)

Path, która jest następująca c:/xampp/apache/bin/php.ini,

• ścieżkę katalogu rozszerzeń PHP odpowia-dającego zmiennej extension _ dir, która jest następująca c:/xampp/php/ext/.

Rozpakuj pakiet DLL DBG. Każda nazwa pliku DLL zawiera wersję PHP, która odpowiada DLL. W naszym przypadku chodzi więc o DLL z na-zwą php_dbg.dll-5.1.2. Zmień nazwę php_dbg.dll i umieść w katalogu rozszerzeń PHP c:/xampp/php/ext/. Edytuj teraz plik c:/xampp/apache/bin/php.ini, w którym zaznaczysz na końcu pliku opcje konfiguracyjne DLL debugowania:

extension=php_dbg.dll

[debugger]

debugger.enabled = true

debugger.profiler_enabled = true

debugger.JIT_host = clienthost

debugger.JIT_port = 7869

Zmień również następujące opcje:

implicit_flush = On

Zmienna implicit _ flush musi obowiązkowo przejść do Off , gdyż inaczej wydajność syste-mu znacznie się zmniejszy. Wyłącz moduł Eac-celerator jeśli jest zainstalowany i włączony:

eaccelerator.enable="0"

Zmień moduł Zend Extension Manager je-śli jest zainstalowany i włączony za pomocą zmiennej zend _ extension _ ts, która będzie wskazywać na bezwzględna ścieżkę DLL:

zend_extension_ts =

"c:/xampp/php/ext/php_dbg.dll"

Po wykonaniu tych zmian, zresetuj serwer Apache za pomocą xampp-control.exe. Jeśli od-mówi zresetowania, oznacza to, że poprzed-nie wskazówki były źle przestrzegane. Po-twierdź dodanie modułu debugowania do

Tabela 1. Ścieżki, które warto poznać w XAMPP

Ścieżki Zawartość

./xampp/apache/bin/ Zawiera pliki wykonawcze związane z Apache i w naszym przypadku, plik php.ini.

./xampp/apache/conf/ Zawiera plik konfiguracyjny Apache httpd.conf jak również inne przykłady konfiguracji w podkatalogu extra.

./xampp/htdocs/ Jest to korzeń serwera WWW (Document Root). W tym katalogu należy umieścić aplikacje chyba, że są one obiek-tem alias w Apache w ten sam sposób jak przestrzeń pracy w Eclipse.

./xampp/htdocs/xampp/ Zawiera kody aplikacji przykładowych dostarczonych z pakietem.

./xampp/mysql/bin/ Zawiera pliki wykonawcze związane z MySQL w tym program użytkowy kopii bezpieczeństwa mysqldump.

./xampp/mysql/data/ Zawiera pliki bazy danych MySQL.

./xampp/php/ Zawiera pliki wykonawcze związane z PHP.

./xampp/php/ext/ Zawiera rozszerzenia związane z PHP. Ten katalog będzie użyty dla DLL debugowania.

./xampp/phpmyadmin/ Aplikacja WWW administracyjna MySQL.

./xampp/tmp/ Katalog przechowywania danych sesji kiedy są używane.

3/2007

Dla początkujących

16

PHP. W tym celu wybierz ponownie funkcję phpinfo() w XAMPP. Moduł debugowania powinien się wtedy wyświetlić, jak to przed-stawia Rysunek 1 (część DBG).

Konfiguracja XAMPP jest więc zakończo-na. Zachęcam was do przyjrzenia się później szczegółowo stosowi Apache-PHP-MySQL-Pear (AMPP). Tabela 1 przedstawia ścieżki, które warto poznać w XAMPP.

EclipseZanim przejdziemy do konfiguracji Eclipse, nale-ży zainstalować Java Runtime Environment. Po wy-konaniu tej instalacji, rozpakuj archiwa Eclipse do katalogu c:/xampp/eclipse. Rozpakuj również za-wartość modułu PHPEclipse. Znajdziesz tam dwa foldery features i plugins, które umieść w katalogu Eclipse, to znaczy c:/xampp/eclipse. Uruchom eclip-se.exe z tego katalogu. Eclipse prosi o zdefiniowa-

nie katalogu roboczego. Wpisz ścieżkę względną ./workspace. Twoim katalogiem roboczym jest od tej pory c:/xampp/eclipse/workspace. W tym katalo-gu będziesz umieszczać swoje projekty. Przy uru-chomieniu Eclipse można nie wyświetlać okienka dialogowego umożliwiającego wybór przestrzeni pracy zaznaczając Use this as the default and do not ask again. Aby ponownie włączyć okienko dialo-gowe, należy przejść do Window >Preferences > Ge-neral >Startup and Shutdown i zaznaczyć Prompt for workspace on startup.

Teraz stworzymy alias w Apache do kata-logu roboczego Eclipse. Edytuj plik c:/xampp/apache/conf/httpd.conf. Na końcu pliku wpisz kod przedstawiony na Listingu 1.

Po wykonaniu tego etapu zresetuj ponownie serwer Apache, aby uwzględnić alias. W Eclipse wykonaj Window > Preferences > PHPEclipse Web Development > Project Defaults aby zapisać infor-macje o aliasie. Wpisz Localhost : http://localhost/eclipse. Dział Document Root odpowiada działo-wi twojego katalogowi pracy. Parametr ten bę-dzie stosowany do wszystkich projektów. Aby zastosować inną konfigurację w projekcie, nale-ży zmienić własności odpowiedniego projektu.

Niektórzy deweloperzy używają innego katalo-gu niż zaproponowany, aby przechowywać swo-je pliki i aplikacje. Na przykład, deweloper, któ-ry używa XAMPP od dłuższego czasu. Jest moż-liwe, że jego Document Root to c:/xampp/htdocs/ i że chciałby on zachować ten katalog. Wystarczy w ta-kim przypadku dostosować poprzednią opcję Do-cument Root w Eclipse i stworzyć ewentualnie od-powiedni alias. Można także stworzyć kilka prze-strzeni pracy (przyporządkowane do kilku alias) i można w każdej chwili wybrać jedną z nich za po-mocą File > Switch Workspace ... Można wtedy wy-brać istniejącą przestrzeń pracy lub stworzyć no-wą. Sprawdź teraz czy moduł PHPEclipse został właściwie zainstalowany w Eclipse za pomocą Help > Software Updates > Manage configuration. Przeglądając strukturę drzewiastą, powinieneś zo-baczyć Disable dla modułu PHPEclipse, co ozna-cza, że Eclipse uwzględnił go i, że jest włączony.

Jak już to wyjaśniliśmy wcześniej, Eclipse może współpracować z XAMPP. Będziesz miał możliwość sprawdzenia tego już teraz. Wyko-naj w Eclipse Window > Preferences > PHPEc-lipse Web Development > PHP External Tools > XAMPP. Znajdziesz ścieżki domyślne modułu PHPEclipse do XAMPP. Ścieżki te odpowiada-ją twojej instalacji.

Trochę praktyki!Przedstawmy teraz program, który zanalizuje-my aby obserwować użycie Eclipse i jego debu-gera. Celem naszego programu jest wyświetlenie zawartości tablicy bazy danych za pomocą sil-nika szablonów Smarty. Użyta baza danych jest jedną z przykładowych baz z XAMPP i zawiera charakterystyczne zapisy płyt CD, tzn. identy-fikator,imię artysty, nazwa albumu i rok. Użyt-kownik będzie również mógł wybrać język tłu-maczenia (polski lub angielski), aby zilustrować

Listing 1. Dodanie aliasu w Apache do przestrzeni pracy Eclipse

Alias /eclipse "C:/xampp/eclipse/workspace"

<Directory "C:/xampp/eclipse/workspace">

DirectoryIndex index.php

AllowOverride All

Order allow,deny

Allow from all

</Directory>

Listing 2. Plik config.inc.php

### ZMIENNE UZYTKOWNIKA ###

# Domyślny szablon

$defaultTemplate = "template1";

# Domyślny język

$defaultLanguage = "fr";

# Połączenie z bazą danych

# Zmienne nie znajdujące się w sesji dla bezpieczeństwa

$dbServer = "localhost";

$dbUsername = "root";

$dbPassword = "";

$db = "cdcol";

### KONIEC ZMIENNYCH UZYTKOWNIKA ###

# Korzeń aplikacji

define ('APP_ROOT_PATH', "./");

# Definicja ścieżek szukania plików

$paths = array (INI_PATH => get_include_path(),

LANGUAGE => APP_ROOT_PATH."translations/",

SMARTY => APP_ROOT_PATH."smarty/",

TEMPLATES => APP_ROOT_PATH."templates/",

TEMPLATES_C => APP_ROOT_PATH."smarty/templates_c/"

);

set_include_path(implode(PATH_SEPARATOR,$paths));

# Uwzględnienie zmiennych użytkownika w sesji

$_SESSION['defaultLanguage'] = $defaultLanguage;

$_SESSION['defaultTemplate'] = $defaultTemplate;

$_SESSION['paths'] = $paths;

# dodatki

require_once('Smarty.class.php');

require_once('functions.php');

require_once('db.class.php');

Listing 3. Plik pl.lang.php

# Zmienne tłumaczenia na polski

$language['encoding'] = "ISO-8859-15";

$language['pageTitle'] = "Lista płyt CD";

$language['id'] = "Identyfikator";

$language['year'] = "Rok";

$language['interpret'] = "Artysta";

$language['title'] = "Tytuł";

$language['templateLabel'] = "Aspekt";

$language['languageLabel'] = "Język";

$language['noResults'] = "Nie ma rezultatów";

3/2007

Dla początkujących

18

sposób uzyskania aplikacji wielojęzykowej. Użyt-kownik będzie również mógł wybrać dwa aspek-ty graficzne, aby zilustrować sposób ustawiania różnych tematów graficznych w jednej aplikacji. Całość opiera się na używaniu sesji w PHP. Płyta CD dołączona do numery zawiera ten program. Hierarchia projektu jest następująca:

• folder Smarty (wersja v2.6.14) zawiera wszystkie klasy silnika szablonów jak rów-nież folder templates_c, który będzie za-wierał pliki skompilowane przez Smarty,

• folder translations zawiera pliki tłumaczeń w formacie xx.lang.php gdzie xx jest skró-tem języka kraju,

• podfolder flags zawiera piktogramy flag krajów,

• folder templates zawiera różne szablony w formacie HTML/Smarty, jak również przyporządkowane arkusze stylów CSS,

• oraz, w korzeniu foldera aplikacja, znajdu-je się plik konfiguracyjny config.inc.php, plik db.class.php, który jest bardzo prosta klasą umożliwiającą przedstawienie uwzględnie-nia klas w Eclipse, plik functions.php, który jest biblioteką funkcji użytecznych w apli-kacji i plik index.php, który jest punktem wejściowym aplikacji.

Na początek zanalizujemy plik config.inc.php, któ-rego zawartość znajduje się na Listingu 2. W tym pliku są najpierw zdefiniowane zmienne, umoż-liwiające zindywidualizowanie aplikacji domyśl-nie z różnymi wartościami jak te z parametrami

połączenia z bazą danych. Tabela $paths umoż-liwia odzyskanie domyślnych ścieżek poszukiwa-nia PHP i dodanie tam ścieżek do różnych kata-logów aplikacji. Taka czynność umożliwia, przy wywołaniu funkcji require_once(), nieprecy-zowanie ścieżek katalogów z plikami mającymi być dołączone. Duża część zmiennych znajdu-je się w sesji poza parametrami połączenia do ba-zy danych z powodów związanych z bezpieczeń-stwem. Różne dodatki niezbędne dla uruchomie-nia programy są następnie wykonane, Umożli-wia to zmniejszyć kod, gdyż wystarczy później dodać do stron plik konfiguracyjny, aby dyspono-wać wszystkimi plikami, które należy dodać i bez precyzowania ścieżki poszukiwania. Zanalizujmy teraz dwa pliki tłumaczenia, których zawartość znajduje się na Listingach 3. i 4. Przypomnijmy, że ta aplikacja jest dwujęzyczna dla polskiego i an-gielskiego. Jasne jest, że jak tylko zmienna znajdzie się w pliku tłumaczenia, musi być ona przetłuma-czona na wszystkich plikach językowych aplika-cji. Każdy plik tłumaczenia kieruje tabelą nazwa-ną $language. Za każdym razem jak strona apli-kacji jest ładowana, tabela $language przyporząd-kowana do wybranego języka jest ładowana w se-sji. Używanie zmiennych sesji do kierowania ję-zykami nie jest potrzebne w tej aplikacji. W więk-szej aplikacji może to być interesującą zaletą, jeśli chodzi o skuteczność! Wyobraź sobie, że przy każ-dym ładowaniu strony, masz w pamięci (więc w sesji) język użyty poprzednio. Jeśli przy zmianie strony język nie został zmieniony, nie ma potrze-by ładowania wszystkich etykiet. Uwaga: jeśli ist-nieje tysiące etykiet do ładowania dla jednej apli-kacji, użycie sesji nie będzie wtedy być może zale-cane, jeśli fizyczny serwer nie ma dobrej wydajno-ści. Aby dorzucić nowy wiersz do aplikacji, wystar-czy dorzucić obraz flagi w katalogu ./translations/flags i nazwać plik językowy w ten sam sposób jak plik obrazkowy w katalogu ./translations. Wy-obraź sobie, że chcesz dorzucić francuskie tłu-maczenie do tej aplikacji. Będziesz miał z jed-nej obrazek flagi francuskiej fr.png w katalogu ./translations/flags, a z drugiej strony plik tłumacze-nia fr.lang.php w folderze ./translations. Funkcja DisplayLanguagesList() biblioteki przeszuka katalog ./translations/flags i sprawdzi czy dla każ-dego pliku obrazka znalezionego istnieje plik języ-kowy w katalogu ./translations. Dla plików istnieją-cych, funkcja ta generuje ciąg HTML wyświetlają-cy flagę każdego języka. Każda flaga jest hipertek-stem do strony PHP, która właśnie się uruchamia ($_SERVER['PHP_SELF']) wzbogacona zmienną odpowiadającą językowi, który użytkownik chce przetłumaczyć. Daje to na przykład taki wynik: http://localhost/myApp/page_xx.php?language=pl. Zanalizujmy teraz plik db.class.php, którego kod jest przedstawiony na Listingu 5.

Plik ten przedstawiony jest w formie bardzo prostej klasy, umożliwiającej połączenie i zada-nie pytania bazie danych. Zwróćcie uwagę, że w naszej aplikacji taka klasa nie jest zbytnio ciekawa. W większej aplikacji można by było stworzyć kla-sę abstrakcyjną niezależną od SGBD, która by by-

Listing 4. Plik uk.lang.php

# Zmienne tłumaczenia na angielski

$language['encoding'] = "CP1252";

$language['pageTitle'] = "Compact Discs List";

$language['id'] = "Identifiant";

$language['year'] = "Year";

$language['interpret'] = "Interpret";

$language['title'] = "Title";

$language['templateLabel'] = "Template";

$language['languageLabel'] = "Language";

$language['noResults'] = "No results";

Listing 5. Plik db.class.php

class db {

private $dbId = null;

public function cd () {}

public function dbConnection ($dbServer, $dbUsername, $dbPassword, $db) {

if (!$this->dbId = @mysql_connect($dbServer, $dbUsername, $dbPassword))

return false;

else {

if (!@mysql_select_db($db, $this->dbId)) return false;

else return true;

}

}

public function query($request) {

$results = array();

if (($request != "") && ($queryResult = @mysql_query($request, $this->dbId))) {

while ($myRow = @mysql_fetch_array($queryResult)) $results =

array_merge($results, $myRow);

return $results;

}

else return false;

}

}

Listing 6. Funkcja ConfigureTemplate() pliku functions.php

function ConfigureTemplate($o) {

$o->debugging = FALSE;

$o->security = TRUE;

# Umożliwia szybką zmianę szablonu

$o->compile_check = FALSE;

$o->force_compile = TRUE;

return $o;

}

3/2007

Dla początkujących

20

ła używana przez klasy pochodne uwzględniające SGBD. Klasa taka zawierałaby oczywiście więcej metod niż te, które przedstawiamy. Klasa db (któ-ra może się połączyć jedynie z bazą danych My-SQL) jest mimo tego użyteczna, gdyż umożli-wia, jak już to wyjaśniłem wcześniej, przedstawie-nie uwzględnienia klas przez Eclipse. Klasa db za-wiera dwie metody. Pierwsza umożliwia połącze-nie z bazą danych MySQL. Po połączeniu, iden-tyfikator połączenia jest zapisywany w prywatnej

zmiennej klasy dbId. Zmienna ta używana jest w funkcji query odpowiedzialnej za zapytania. Wyniki są łączone i zapisane w tablicy za pomocą funkcji array_merge, a następnie odesłane. Wy-bór łączenia danych jednych po drugich w tabeli nie jest bez znaczenia. Umożliwia on użycie funk-cji charakterystycznej dla silnika szablonów Smar-ty, który przyjmuje w argumencie tabele danych i generuje odpowiedni kod HTML. Tabela jest wte-dy przekształcona w arkusz stylów CSS.

Zanalizujemy teraz każdą funkcję pliku functions.php.Funkcja ConfigureTemplate(), której zawartość przedstawiona jest na Listingu 6., umożliwia, jak sama jej nazwa mówi, konfigurację obiektu sza-blonu Smarty. Kod ten jest zawarty w funkcji, gdyż musi być uruchomiony przy każdej konfigu-racji obiektu szablonu. Metoda force_compile() umożliwia ponowną kompilację przy każdym uruchomieniu szablonu (zajmując miejsce funk-cji compile_check(), którą pozostawiam w ko-dzie specjalnie, aby pokazać jej istnienie). Nie jest ona zalecana w produkcji z powodu wydajności (jeśli szablony są znaczne) i czasu ponownej kom-pilacji, ale umożliwia ona natychmiastową zmianę szablonu i języka aplikacji.

Wspominaliśmy już wcześniej o funkcji DisplayLanguagesList(). Umożliwia ona wy-świetlenie listy dostępnych języków sprawdza-jąc obecność pliku tłumaczenia dla każdej flagi kraju. Jej kod zawarty jest na Listingu 7. Funk-cja ManageTemplateVariables(), której kod przedstawia Listing 8., umożliwia przyporząd-kowanie większości zmiennych szablonu.

Funkcja ManageTemplate() z Listingu 9. umożliwia, podczas ładowania strony, określe-nie języka i szablonu, który należy użyć w zależ-ności od zmiennych zawartych ewentualnie w parametrach. Funkcja wybiera żądany język i szablon jedynie wtedy, gdy są one dostępne, a je-śli nie są, wtedy będą użyte domyślne wartości.

Zauważcie, że w kodzie tej funkcji nie spraw-dziłem celowo zawartości danych, aby zmniej-szyć przedstawiony kod w tym artykule. Trze-ba jednak to wykonać, jeśli chcesz skorzystać z przedstawionych przykładów. Jeśli opcja magic_quote_gpc twojego serwera jest włączona, nie ma potrzeby uwalniania danych, ale jest to dosyć zła zasada programowania, gdyż należy próbować pozostać jak najbardziej niezależnym od platfor-my programowej. W dniu kiedy aplikacja zosta-nie przeniesiona z jednego serwera na inny, opcja ta może być wyłączona. Jeśli kod nie jest wtedy uwolniony, może to mieć konsekwencje zwią-zane z bezpieczeństwem aplikacji. Należy więc przetestować i uwolnić w razie potrzeby dane za pomocą funkcji get_magic_quotes_gpc(). Dzięki niej, można dowiedzieć się, czy opcja jest włączona, czy też nie na serwerze. Poza tym, PHP 6 może usunąć po prostu opcję magic quote.

Tak samo jak w pliku tłumaczenia, funkcja TemplatesList() umożliwia wyświetlenie li-sty różnych szablonów dostępnych w folderze ./templates/. JFunkcja nie sprawdza, czy pliki skła-dające się na szablony są obecne. W tym przy-padku należałoby także zabezpieczyć tę funk-cję, której kod jest widoczny na Listingu 10.

Funkcja TemplatesURL() przypomina bar-dzo poprzednią funkcję. Jej celem jest umożli-wienie użytkownikowi wybranie szablonu w li-ście rozwijalnej do ponownego automatycznego załadowania strony za pomocą kodu Javascript z wybranym szablonem jako aspektem graficz-nym. Istnieje możliwość klikania na obrazki

Listing 7. Funkcja DisplayLanguagesList() pliku functions.php

function DisplayLanguagesList() {

$path = $_SESSION['paths']['LANGUAGE']."flags";

$flags = NULL;

if ($folderId = opendir($path)) {

while (($f = readdir($folderId)) != FALSE) {

$tab = split('[.]', $f);

if (($f != ".") && ($f != "..") && (is_file($_SESSION['paths']['LANGUAGE'].

$tab[0].'.lang.php'))) {

$flags .= '<a href="'.$_SERVER['PHP_SELF'];

$flags .= '?language='.$tab[0].'">';

$flags .= '<img src="'.$path."/".$f.'"/>';

$flags .= '</a>&nbsp;';

}

}

}

return $flags;

}

Listing 8. Funkcja ManageTemplateVariables() pliku functions.php

function ManageTemplateVariables($o, $templatePath) {

$arrayTemplatesURL = TemplatesURL();

$o->assign("path", $templatePath);

$o->assign("encoding", $_SESSION['language']['encoding']);

$o->assign("language", $_SESSION['activeLanguage']);

$o->assign("flags", DisplayLanguagesList());

$o->assign("templateOutput", TemplatesList());

$o->assign("templateValues", $arrayTemplatesURL);

$o->assign("templateSelected", $arrayTemplatesURL[$_SESSION['activeTemplate']]);

$o->assign("pageTitle", $_SESSION['language']['pageTitle']);

$o->assign("languageLabel", $_SESSION['language']['languageLabel']);

$o->assign("templateLabel", $_SESSION['language']['templateLabel']);

return $o;

}

Listing 9. Funkcja ManageTemplate() pliku functions.php

function ManageTemplate() {

# Zarządzanie językiem

if (isset($_GET['language'])) $_SESSION['activeLanguage'] = $_GET['language'];

if (!is_file($_SESSION['paths']['LANGUAGE'].$_SESSION['activeLanguage'].

'.lang.php')) require($_SESSION['defaultLanguage'].'.lang.php');

else require($_SESSION['activeLanguage'].'.lang.php');

# Aktualizacja zmiennych sesji ze zmiennymi pliku językowego

$_SESSION['language'] = $language;

# Zarządzanie szablonem

if (isset($_GET['template'])) {

if (is_dir($_SESSION['paths']['TEMPLATES'].$_GET['template']."/"))

$_SESSION['activeTemplate'] = $_GET['template'];

else $_SESSION['activeTemplate'] = $_SESSION['defaultTemplate'];

}

}

Eclipse

www.phpsolmag.org 21

flag. Funkcja ta wyświetla tabelę stron URL z nazwą szablonu w parametrze. Kod tej funkcji znajduje się w Listingu 11.

Zanalizujemy teraz kod głównej strony pro-gramu przedstawionej na Listingu 12. Biorąc pod uwagę, że używamy sesje, należy uruchomić jedną z nich wywołując session_start(). Sesja została specjalnie nazwana gdyż bez nazwy, jeśli taka aplikacja jest skopiowana na tym samym ser-werze dla różnych zespołów, kiedy jeden z użyt-kowników połączy się z kilkoma aplikacjami, bę-dzie on dzielił tylko jeden plik sesji. Każda apli-kacja dostarczy wtedy zmienne sesji pochodzą-cych z pierwszej aplikacji, z którą połączył się użytkownik. Aby rozwiązać ten problem, nale-ży nadać różną nazwę sesji każdej aplikacji za po-mocą funkcji session_name(). Po uruchomie-niu sesji dołącza się plik konfiguracyjny. Domyśl-ne wartości dla języka i szablonu są używane tyl-ko przy pierwszym połączeniu. Jest to wykonane za pomocą zmiennej sesji APP_INSIDE.

Następuje później wywołanie funkcji ManageTemplate(), odpowiedzialnej za zmia-nę języka i szablonu w razie potrzeby. Po określe-niu szablonu, jego ścieżka jest zapisana w zmien-nej $templatePath. Instancja obiektu szablonu Smarty jest następnie stworzona, konfigurowa-na a następnie dodaje się do niej różne zmien-ne, które będą wyświetlone w ostatecznej apli-kacji. Następuje instancja obiektu klasy db. Naj-pierw realizowane jest połączenie. Następnie sprawdzany jest status połączenia. W razie nie-powodzenia, aplikacja nagle się zatrzyma. W ra-zie powodzenia, funkcja query wykonuje zapy-tanie. Zauważmy, że nie poznaliśmy identyfika-tora połączenia dbId, gdyż był zarządzany przez klasę w obiekcie; ta klasa zachowuje się wtedy jak czarna skrzynka naszego pliku index.php. Na-stępnie jest stworzona tabela danych, której czte-ry pierwsze elementy, zawierają nazwę kolumn tabeli do wyświetlenia. W tej tabeli są łączone dane pochodzące z zapytania w bazie danych. Smarty generuje kod HTML umożliwiający wyświetlenie linearnych zawartości tabeli, aby otrzymać wszystkie wyniki. Wywołanie funkcji display() obiektu szablonu Smarty wywoła po-nowną kompilację szablonu (nie zapomnijcie, że ta ponowna kompilacja była celowo wymuszona) i wyświetli dane w przeglądarce klienta.

W tej aplikacji, dwa podobne szablony różnią się tylko kilkoma kolorami i elementami układu strony. Umożliwiają one jednak jasną ilustrację zmiany aspektu graficznego dzięki Smarty i PHP oraz pokazanie mocy arkuszy stylów, jeśli wiel-kość jest dobrze sformatowana. Ich użyteczność jest często nieznana. Poza tym, te arkusze stylów są bardzo zwięzłe i należałoby je ulepszyć (eks-perci CSS mogą sami się o tym przekonać).

Zanalizujemy teraz krótko zawartość szablo-nu. Każdy szablon składa się z trzech plików .tpl i z arkusza stylów CSS. Plik header.tpl zawiera nagłówek pliku HTML. Na kompletnej stronie internetowej byłby to baner strony z ewentu-alnym poziomym menu. Plik footer.tpl zawiera

Listing 10. Funkcja TemplatesList() pliku functions.php

function TemplatesList() {

$path = $_SESSION['paths']['TEMPLATES'];

$templatesList = array();

if ($folderId = opendir($path)) {

while (($f = readdir($folderId)) != FALSE) {

if (($f != ".") && ($f != "..") && (is_dir($path.$f)))

$templatesList [] = $f;

}

}

return $templatesList;

}

Listing 11. Funkcja TemplatesURL() pliku functions.php

function TemplatesURL() {

$path = $_SESSION['paths']['TEMPLATES'];

$urls = array();

if ($folderId = opendir($path)) {

while (($f = readdir($folderId)) != FALSE) {

if (($f != ".") && ($f != "..") && (is_dir($path.$f))) {

$var = $_SERVER['PHP_SELF'];

$var .= '?template='.$f;

$urls [$f] = $var;

}

}

}

return $urls;

}

Listing 12. Plik index.php

session_name('myApp');

session_start();

require_once('config.inc.php');

# Inicjalizacja

if (!isset($_SESSION['APP_INSIDE'])){

$_SESSION['APP_INSIDE'] = TRUE;

$_SESSION['activeLanguage'] = $_SESSION['defaultLanguage'];

$_SESSION['activeTemplate'] = $_SESSION['defaultTemplate'];

}

ManageTemplate();

$templatePath = $_SESSION['paths']['TEMPLATES'].$_SESSION['activeTemplate']."/";

# Stworzenie i ustawianie szablonu

$oSmarty = new Smarty();

$oSmarty = ConfigureTemplate($oSmarty);

$oSmarty->template_dir = $templatePath;

$oSmarty->compile_dir = $_SESSION['paths']['TEMPLATES_C'];

$oSmarty = ManageTemplateVariables($oSmarty, $templatePath);

# Odzyskanie danych do wyświetlenia

$obj= new db();

if ($obj->dbConnection($dbServer, $dbUsername, $dbPassword, $db)) {

$results = $obj->query(

"select id, jahr, interpret, titel from cds order by id asc");

$data = array($_SESSION['language']['id'], $_SESSION['language']['year'],

$_SESSION['language']['interpret'], $_SESSION['language']['title']);

if (is_array($results)) $data = array_merge($data, $results);

else $data [] = $_SESSION['language']['noResults'];

$oSmarty->assign('arrayData', $data);

$oSmarty->display("index.tpl");

}

else die ("Critical Error ! Connection failed !");

3/2007

Dla początkujących

22

końcówkę pliku HTML. Na stronie Interneto-wej, informacje mogłyby stanowić link do praw-nych komunikatów, copyright, daty ostatniej ak-tualizacji, itp. Plik index.tpl wywołuje jedynie pliki header.tpl i footer.tpl. Między tymi dwo-ma wywołaniami, widzisz kod umożliwiający wyświetlenie linearne tabeli danych dostarczo-nych przez zapytanie. Kody plików .tpl znajdu-ją się w Listingach 13., 14. i 15. Aby zmniejszyć ilość kodu w tym artykule, ominęliśmy kod ar-kuszu stylów CSS, jest on dosyć objętościowy jeśli chodzi o wiersze kodu.

Użycie Eclipse z naszym programemStwórz pierwszy projekt w Eclipse wykonując File > New > PHP Project. Okienko dialogowe in-formuje, żebyś wpisał nazwę i miejsce projektu. Wywołaj TestProject i wstaw go do swojego kata-logu roboczego zaznaczając Use Default. Będzie on teraz dostępny w przeglądarce Internetowej via stworzony alias. Projekt wyświetla się teraz w przeglądarce projektów Eclipse. Aby utwo-rzyć plik PHP, wykonaj File > New > PHP File. Wystarczy sprecyzować nazwę pliku i projekt dołączony do niego, aby dorzucić go do projek-tu. Zawartość domyślna pliku znajduje się na Listingu 16. Zawartość ta może być zmieniona, jak to wyjaśnia komentarz w pliku. Aby stwo-rzyć prosty plik o jakimkolwiek rozszerzeniu, powinniście wykonać File > New > File, pamię-tając przy tym o sprecyzowaniu rozszerzenia.

Jeśli chcesz importować istniejące pliki do projektu, istnieją po temu różne metody. Pierw-sza polega na wybraniu katalogu źródłowego i plików do importu. W tym celu, kliknij File > Import > File System a następnie sprecyzuj ścież-kę katalogu, np. katalogu silnika szablonów Smarty. Można wtedy importować zawartości katalogów i podkatalogów lub wybrać jedynie niektóre pliki. Eclipse wykona kopie plików w katalogu projektu tworząc strukturę drzewia-stą katalogu źródłowego bez wpływu na orygi-nalne pliki. Funkcja importu jest również do-stępna za pomocą prawego przycisku myszy na projekcie w przeglądarce Eclipse.

Druga metoda polega na połączeniu katalogu z projektem bez fizycznego kopiowania plików w katalogu roboczym. W tym celu kliknij File > New > Folder > Advanced i zaznacz Link to folder in the file system. Wybierz projekt do dodania i na-daj nazwę katalogowi wirtualnemu, który stwo-rzysz a następnie potwierdź. Pojawia się wtedy

symbol dodania w postaci piktogramu w prze-glądarce Eclipse, który przedstawia katalog ze strzałka. Rysunek 2. przedstawia widok Eclipse.

Edytor EclipseJak już o tym wspomnieliśmy Eclipse jest kom-pletnym środowiskiem programistycznym. Nie chodzi więc o prostą platformę, która służy do de-bugowania kodu nawet jeśli jest to jedna z jej głów-nych zalet. Moduł PHPEclipse dodaje do Eclipse pewną ilość funkcjonalności dotyczących edycji kodu. Popatrzmy teraz na kilka z nich, które mo-gą okazać się użyteczne i potrzebne w każdym śro-dowisku programistycznym. Na przykład, edy-tuj plik db.class.php. Zauważ, że Eclipse proponu-je kolorowanie składni kodu. Atrybuty funkcji są zaznaczone na fioletowo. Metody PHP są zazna-czone na szaro, itp. Te domyślne kolory mogą być

zmienione w Window > Preferences... > PHPeclipse Web Development > PHP > Onglet Syntax. Zakład-ka ta umożliwia spersonalizowanie środowiska edycyjnego. Polecam dokładniejsze przyjrzenie się możliwościom oferowanych przez tę zakładkę. Możesz na przykład poprosić o wyświetlenie nu-merów wierszy w plikach kodu...

Eclipse proponuje domyślnie pomoc w edy-cji wykonując czynności za Ciebie, kiedy wpisu-jesz pewne znaki kluczowe. Na przykład, kiedy otwierasz nawias, Eclipse zamyka automatycz-nie ten nawias za Ciebie, dotyczy to również apostrofów i cudzysłowów. Te jokers mogą być także wyłączone dzięki zakładce Syntax.

Masz również dostęp do prototypów więk-szości funkcji PHP. Jeśli na przykład najedziesz kursorem na funkcję mysql_select_db pliku db.class.php gdzie znajduje się wiersz @mysql_

Listing 13. Plik header.tpl

<?xml version="1.0" encoding="{$encoding}"?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//{$language}"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{$language}">

<head>

<title>{$pageTitle}</title>

<style type="text/css" title="currentStyle" media="screen">

@import "{$path}styles.css";

</style>

</head>

<body>

<div id="container">

<div id="pageHeader">

<p class="pTitle">{$pageTitle}</p>

<p class="pOptions">{$languageLabel} : {$flags} &nbsp;&nbsp;&nbsp;&nbsp;

&nbsp; {$templateLabel} : <select name="myTemplate" size="1"

class="template" onChange="location = this.options[this.selecte

dIndex].value;" > {html_options values=$templateValues

selected=$templateSelected output=$templateOutput}</select></p>

</div>

Listing 14. Plik index.tpl

{include file="header.tpl"}

<div id="results">

{html_table loop=$arrayData cols=4 td_attr='class="pTd"'}

</div>

{include file="footer.tpl"}

Listing 15. Plik footer.tpl

</div>

</body>

</html>

Listing 16. Zawartość domyślna pliku PHP stworzona w Eclipse

<?php

/*

* Created on 26 nov. 2006

*

* To change the template for this generated file go to

* Window - Preferences - PHPeclipse - PHP - Code Templates

*/

?>

TerminologiaPodpowiadanie: w oprogramowaniu : podpo-wiadanie umożliwia użytkownikowi ograni-czenie wpisywania słów. Opcja ta proponu-je wybór słów w liście opartej na pierwszych literach wpisanego słowa. Na przykład, w ję-zyku PHP, jeśli wpiszesz ciąg str, system za-proponuje ci listę słów kluczowych PHP za-czynających się na str, na przykład funkcje strlen, strtolower, strpos itp. Nie mu-sisz wtedy wpisywać ich w całości.

3/2007

Dla początkujących

24

select_db($db, $this->dbId), Eclipse wymie-nia bool mysql_select_db (string database_name ...) zaznaczając, co funkcja PHP ma w ar-gumencie oraz rodzaj wysłanych danych. Jeśli na-jedziesz kursorem na zmienną $db, otrzymasz li-stę plików używających zmiennej o tej samej na-zwie (w naszym przypadku pliki config.inc.php i index.php oraz plik db.class.php). Zostań przy pli-ku db.class.php i przyjrzyj się części Outline inter-fejsu (który możesz wyświetlić za pomocą Win-dows > Show View > Outline). Część ta umożliwia wizualizację w jednej chwili różnych zmiennych

i metod pliku. Publiczna zmienna będzie mia-ła symbol małej zielonej wklęsłej kropki w prze-ciwieństwie do zmiennej prywatnej, której sym-bolem będzie mały czerwony wklęsły kwadra-cik. Metoda publiczna będzie miała symbol zie-lonej pełnej kropki w przeciwieństwie do metody prywatnej, której symbolem będzie czerwony peł-ny kwadracik. Podwójne kliknięcie na obiekt czę-ści Outline przeniesie Cię automatycznie do defi-nicji obiektu, który może być użyteczny dla kla-sy mającej setki wierszy kodu jak na przykład plik ./smarty/Smarty.class.php. Masz także widok na

wszystkie dodatki wykonane w pliku. Są one wy-mienione ponownie w części Outline, a dokład-niej w pod części include declarations. Jeśli wywołasz plik, który nie istnieje w projekcie, wy-świetlony zostanie czarny wykrzyknik na żółtym tle (ostrzeżenie). Podobnie jak wcześniej, podwój-ne kliknięcie na błąd w części Outline przeniesie cię do definicji błędu. Jeśli najedziesz kursorem na błąd, otrzymasz więcej informacji na jego temat.

Eclipse jest również użyteczny przy skład-ni, pomagając w poprawianiu błędów. Jeśli na przykład zapomnisz wstawić średnik w wier-szu kodu, w którym jest to niezbędne (błąd na poziomie parsera PHP), Eclipse zaznaczy to miejsce czerwoną falą. Czerwony kwadrat tak-że będzie widoczny na prawym marginesie pli-ku PHP. Kliknięcie na czerwony kwadrat prze-niesie cię do błędnej części kodu. Zauważ, że wi-dok Problems dostępny w Window > Show View > Problems zbierze wszystkie błędy i ostrzeżenia obecne w kodzie. Wystarczy później kliknąć je-den z nich aby dotrzeć do definicji.

Eclipse umożliwia także podpowiadanie ko-du (zob. Część Terminy w tym artykule). Pod-powiadanie może być używane na poziomie ta-gów HTML, PHP jak również PHPDoc. Jeśli na przykład wpiszesz ciąg /** i naciśniesz po nim Enter, Eclipse będzie symbolizować blok ko-mentarzy PHPDoc. Jeśli wpiszesz symbol @ w środku tego bloku komentarzy, pojawi się lista tagów PHPDoc. Teraz wystarczy ich używać.

Konfiguracja i używanie debugeraJeszcze o tym nie wspominałem ale zauważ, że po-jawiły się w Eclipse dwa przyciski przedstawiają-ce X z XAMPP. Przycisk nie przekreślony umoż-liwia uruchomienie serwerów Apache i MySQL z XAMPP, a przekreślony przycisk – ich zatrzyma-nie. Jeśli serwery nie zostały jeszcze uruchomio-ne, to jest dobra chwila, żeby to zrobić, gdyż te-raz skonfigurujesz debugera aby go później użyć w aplikacji. Aby debugować kod PHP, trzeba wcze-śniej stworzyć konfigurację debugowania. W tym celu, kliknij Run > Debug > podwójne kliknięcie na PHP DBG Script. Nowa pusta konfiguracja pojawi się z zestawem zakładek. Widok okienka przedsta-wiony jest na Rysunku 3.

Nazwij swoją konfigurację na przykład DBG-Conf. W okienku File, wybierz projekt i plik do debugowania, tutaj index.php projektu Test-Project. W Environment, należy sprecyzować w pod-zakładce Interpreter lokalizację interprete-ra PHP, którego ścieżka jest w naszym przypad-ku następująca c:/xampp/php/php.exe. W pod-za-kładce Remote Debug, zaznacz Remote Debug i Open with DBGSession URL in internal Browser, a wtedy wyświetli się aplikacja w trakcie debugo-wania w wewnętrznej przeglądarce Eclipse. Po-le Remote Sourcepath musi mieć w wartości bez-względną ścieżkę do twojego projektu, to znaczy c:/xampp/eclipse/workspace/TestProject. Jeśli jakaś informacja jest nieważna, Eclipse zaznacza to na górze okienka konfiguracyjnego debugera a przy-cisk Debug nie będzie włączony. Po konfiguracji Rysunek 3. Konfiguracja debugera.

Rysunek 2. Widok Eclipse.

Eclipse

www.phpsolmag.org 25

debugowania, możesz kliknąć Debug aby włączyć perspektywę debugowania i zacząć operacje.

W Eclipse perspektywa jest widokiem in-terfejsu oprogramowania z różnymi podległy-mi elementami perspektywy. Perspektywa De-bug zawiera na przykład część przeznaczoną do nadzoru zmiennych, inna część umożliwia ope-racje debugowania krok po kroku, itp. W każ-dej chwili możesz przejść pomiędzy perspek-tywami PHP i Debug klikając na odpowiadają-cy im przycisk.

Wywołanie debugera może być wykonane z każdej perspektywy. W perspektywie PHP, wy-starczy wykonać Run powiązane z konfiguracją debugowania, aby przejść do perspektywy Debug (po zatwierdzeniu przez użytkownika) z uru-chomieniem debugowania i wykonaniem kodu. Jeśli pułapka została postawiona w kodzie, debu-ger zatrzyma się na pierwszej spotkanej pułapce. Pułapka, na której jest obecnie zatrzymany debu-ger, jest symbolizowana strzałką, jak w większo-ści środowisk programistycznych.

Aby zastawić pułapkę, wystarczy kliknąć dwa razy na marginesie wiersza, w którym chcesz ją ustawić. Ta sama czynność umożli-wia jej usunięcie. Rysunek 4. przedstawia pu-łapkę umieszczoną w pliku index.php. Zmienna $templatePath jest wtedy widoczna i ma war-tość ./templates/template1/. Łatwiej jest więc de-bugować za pomocą środowiska programistycz-nego niż funkcji echo lub var_dump().

W perspektywie Debug, można wykonać pewną ilość czynności na pułapkach, np. Wyłą-czyć je czasowo lub lepiej, ustawić warunkowe pułapki. Nie ma potrzeby używania warunko-wych pułapek w naszej aplikacji, ale zilustruje-my jednak ich użyteczność w przykładzie. Jeśli pułapka jest ustawiona w pętli for, nie ma po-trzeby, aby się na niej zatrzymać przy każdym przejściu pętli, ale jedynie przy wartości okre-ślonej przez dewelopera. Jeśli $i jest licznikiem pętli możemy na przykład włączyć pułapkę tyl-ko wtedy kiedy $i jest równe 11. W tym celu w Eclipse, w perspektywie Debug, ustaw pułapkę w miejscu wybranym przez siebie a następnie w okienku zarządzania pułapek, kliknij na prawa dostępu> właściwości. Wpisz $i == 11. Kiedy $i będzie miał wartość 11 i tylko wtedy, debu-ger zatrzyma się na pułapce.

Kontrola i nadzór za pomocą pułapek jest groźnym narzędziem i niezbędnym zwłaszcza w środowisku programistycznym. Istnieją dwa rodzaje nadzoru zmiennej. Pierwszy polega na umieszczeniu kursora myszki na zmiennej kie-dy debuger zatrzyma się na pułapce. Jest to naj-prostszy sposób przejrzenia zawartości zmien-nej. Drugi sposób polega na znalezieniu żądanej zmiennej w widoku Variables (przypomnij so-bie przykład zmiennej $templatePath ma Ry-sunku 4.) i na żądaniu od Eclipse nadzoru tej zmiennej za pomocą prawego przycisku my-szy a następnie Watch. W ten sposób, zatrzy-

manie się w jakimś punkcie kodu umożliwi au-tomatyczne wyświetlenie zawartości zmiennej bez potrzeby najechania na nią kursorem mysz-ki. Możemy w ten sposób obserwować dziesiąt-ki zmiennych i spokojnie debugować kod, krok po kroku, nadzorując jednocześnie zawartość zmiennych.

ZakończenieMam nadzieję, że artykuł ten przedstawił wam zalety zintegrowanych środowisk programi-stycznych. Poza łatwością i wygodą tego typu narzędzi przy edycji kodu, ich siła na etapie de-bugowania i testów okazuje się bardzo skutecz-na, czy to w małym projekcie czy też w większej infrastrukturze profesjonalnej.

Jak już wspomniałem wcześniej, główną za-letą proponowanej platformy jest z jednej stro-ny jej koszt a z drugiej strony trwałość w tym sensie, że wszystkie składniki platformy stano-wią część aktywnego projektu w Internecie. Są one nadzorowane przez dużą społeczność, która chce zaproponować narzędzia bezpłat-ne, które są w stanie rywalizować z produkta-mi płatnymi.

W tym artykule przedstawiłem głównie konfigurację i wdrożenie środowiska progra-mistycznego, co umożliwi wam teraz szybkie rozpoczęcie pierwszego projektu. Przedstawi-łem jedynie 5% funkcjonalności tych różnych elementów. Zachęcam was więc do zaintereso-wania się nimi szczegółowo, jeśli chcecie uży-wać tej platformy w przyszłych projektach. XAMPP proponuje na przykład dużą ilość pro-gramów użytkowych i moduł, których używa-nie nie było opisane w tym artykule (moduł bench w Apache, różne programy użytkowe MySQL, rozszerzenie WebDav itp.). Jeśli cho-dzi o Eclipse, którego stosowanie zostało opi-sane na podstawie modułu PHPEclipse, wiele rzeczy pozostaje jeszcze do odkrycia na jego te-mat. Wasza ciekawość będzie więc dużą zale-tą. Temat Smarty był już poruszany w wielu artykułach (między innymi, numer specjalny z listopada 2005r.), jego możliwości są olbrzy-mie i skorzystaliśmy tylko z ułamka jego funk-cjonalności.

Artykuł ten nie wyczerpuje więc tematu ale umożliwi, przede wszystkim początkują-cym, pracę na bezpłatnej platformie programi-stycznej i zapoznanie się z tego typem narzę-dzi, często stosowanych w środowisku profe-sjonalnym.

Rysunek 4. Perspektywa debugowania

W Sieci

• http://java.sun.com/javase/downloads/index.jsp – Strona do ściągnięcia maszyny wirtualnej Java SUN,

• http://www.eclipse.org/ – Główna strona frameworka Eclipse,• http://www.phpeclipse.de/ – Główna strona wtyczki PHPEclipse,• http://www.apachefriends.org/ – Główna strona stosu XAMPP,• http://dd.cron.ru/dbg/downloads.php – Strona do ściągnięcia DLL debugowania PHP.

GUILLAUME FOUQUETAutor ukończył szkołę inżynierii elektronicznej w Paryżu IFITEP. Trzy lata temu zdecydował się za-jąć programowaniem informatycznym, a zwłasz-cza aplikacjami PHP/MySQL. Od tej pory rozwi-ja aplikacji WWW dla francuskiej firmy aeronau-tycznej. Oprócz języka PHP autor interesuje się językami programowania WWW i pisze również programy w C#.NET.

3/2007

Dla początkujacych

26

P4A (PHP for Applications) jest fra-meworkiem typu RAD służącym do szybkiego tworzenia zorientowanych

obiektowo aplikacji w języku PHP. Wspie-ra on kodowanie UTF-8, pozwala na łącze-nie się ze wszystkimi ważniejszymi typami baz danych (używając PEAR::DB) oraz ła-twą prezentację pobranych z bazy danych. Dzięki wbudowanej dużej ilości kompo-nentów napisanie sprawnej aplikacji nie wy-maga od nas w zasadzie większej znajomo-ści języka PHP, natomiast wiedza dotyczą-ca (X)HTML, CSS czy MySQL jest niemal zbędna. Proces tworzenia aplikacji ogranicza się do napisania klasy aplikacji, klasy interfej-su użytkownika (jedna klasa – jedna podstro-na) a następnie utworzenia odpowiednich komponentów.

Pierwszy programPowiedzieliśmy już trochę na temat P4A, czas zapoznać się z nim bliżej.

Zacznijmy, więc od instalacji (patrz ram-ka Instalacja). Zanim jednak przejdziemy do pisania kodu, musimy poznać szkielet aplika-cji – pierwszy program będzie od nas wyma-gał trzech plików i dwóch folderów. W kata-logu gdzie skopiowaliśmy P4A, tworzymy no-wy folder i nazywamy go HelloWorld. W folde-rze tym utworzymy plik index.php oraz folder objects. Ten z kolei będzie zawierał plik hello-

world.php (kod główny aplikacji) oraz hello.php (kod strony hello).

Ponieważ struktura jest dosyć skomplikowa-na, przedstawiam ją na Rysunku 1.

Teraz wyjaśnijmy działanie konkretnych plików. Plik index.php w głównym katalogu dołącza silnik p4a i uruchamia aplikację za-sadniczą (w naszym przypadku jest to /objects/helloworld.php). Plik helloworld.php zawiera kla-sę helloworld dziedziczącą po klasie p4a, czyli de facto główną aplikację, która np. załaduje odpo-wiedni plik interfejsu (u nas - /objects/hello.php). Dopiero ten ostatni plik odpowiedzialny jest za tworzenie (generowanie) danej podstrony naszej aplikacji. Zacznijmy od wypełnienia pliku in-dex.php kodem (Listing 1). Jak widać, w pliku tym tworzymy tylko instancję klasy aplikacji. Przejdźmy zatem do kodu samej aplikacji. W pliku helloworld.php będzie znajdowała się klasa helloworld dziedzicząca po p4a. W konstrukto-rze klasy musimy umieścić obowiązkową linie: parent::p4a(); czyli wywołanie konstrukto-ra klasy p4a. W naszej prostej aplikacji, drugą i ostatnią linią będzie załadowanie interfejsu (hel-lo.php) : $this -> openMask('hello'); Kod pliku helloworld.php możemy zobaczyć na Li-stingu 2. Ostatnim krokiem jest napisanie kodu

odpowiedzialnego za wyświetlenie strony głów-nej – hello.php.

Znów znajdzie się tu klasa, tym razem roz-szerzająca p4a_mask. To właśnie w jej kon-struktorze, poza obowiązkowym parent::

p4a_mask();, umieścimy kod odpowiedzialny za tworzenie obiektów.

Tutaj powinniśmy zatrzymać się na chwilę, aby poznać sposób tworzenia obiektów w p4a. W przeciwieństwie do np. frameworka PRA-DO, tutaj obiekty nie są tworzone automatycz-nie na podstawie szablonu – tworzymy je po-przez kod, następnie przypisując im właściwo-ści tą samą drogą. Przykład utworzenia przyci-sku jest banalnie prosty:

$button1 =& $this->build(

'p4a_button', 'button1');

$button1->setLabel('test przycisku');

$this->display('main', $button1);

W pierwszej linii tworzymy obiekt typu p4a _ button i przypisujemy go do zmien-nej $button1. Druga linia to przypisanie etykiety tekstowej do przycisku, w trze-ciej zaś linii informujemy klasę interfejsu, że chcielibyśmy wyświetlić obiekt button1. Parametr main oznacza w tym przypadku, że chcemy to zrobić w głównej części strony (domyślny szablon posiada trzy strefy: ma-in, menu i top).

Stworzyliśmy zatem pierwszy program – wyświetlający przycisk. Cóż, logicznym kro-kiem jest teraz oprogramowanie kliknięcia w

Framework P4A

O użyteczności frameworków nikogo przekonywać nie trzeba. W tym artykule skupimy się na jednym z nich – P4A – dzięki któremu w 15 minut możemy stworzyć sprawnie działający katalog płyt.

Co obiecujemy...• Dowiesz się, w jaki sposób zainstalować frame-

work P4A oraz jak szybko i efektywnie skonfigu-rować go do własnych potrzeb.

Co należy wiedzieć...• Powinieneś znać podstawy języka PHP, MySQL

i orientować się ogólnie w tematyce tworzenia stron internetowych.

Poziom trudności

Wprowadzenie

Instalacja P4AInstalacja wybranego przez nas frameworka jest bardzo prosta i w zasadzie ogranicza się tylko do rozpakowania pobranych przez nas plików. Na potrzeby artykułu skopiujmy następujące pli-ki i foldery: icons/, p4a/, themes/, .htaccess i p4a.php do katalogu dostępnego dla serwera Apache (np. htdocs/p4a/). W tym artykule będziemy używać P4A w wersji 1.0.0 (do pobrania z http://p4a.crealabsfoundation.org/)Należy pamiętać, że P4A wymaga PHP w wersji 4.3.x/4.4.x/5.x oraz Apache 1.3.x/2.0.x. Dodatko-wo baza płyt CD, którą stworzymy w tym artykule, wymagać będzie bazy MySQL.

v

Pren

umer

ata

PRO

Wię

cej i

nfor

mac

ji: patrycja.wadolowska@

software.com.pl

tel.:

022

887

-13-

45

v

3/2007

Dla początkujacych

28

ów przycisk. W tym celu, powyżej linii odpo-wiedzialnej za wyświetlenie obiektu dodajmy następujący kod:

$this->intercept($button1,

'onClick', 'onButton1Click');

Kod ten jest odpowiedzialny za przypisa-nie reakcji na zdarzenie dla danego obiektu.

Pierwszy parametr to obiekt, któremu chce-my przypisać zdarzenie, drugi to nazwa zda-rzenia, na które chcemy zareagować. Trze-ci parametr decyduje, która z funkcji na-szej klasy hello zostanie wywołana. Zgod-nie z powyższym kodem, musimy teraz stworzyć funkcję onButton1Click(). W jej kodzie zmienimy etykietkę przycisku oraz wyłączymy go, tak by więcej nie można by-

ło w niego kliknąć. Kod funkcji wygląda na-stępująco:

$this->button1->setLabel('kliknąłeś

w przycisk');

$this->button1->enable(false);

Zwróćmy uwagę, że jesteśmy wewnątrz me-tody klasy, więc wywołanie $button1 ->

setLabel skończyłoby się błędem. Dlatego też odwołujemy się do przycisku przez $this->button1->setLabel. W tym prostym przy-kładzie HelloWorld zapoznaliśmy się z podsta-wami tworzenia aplikacji w P4A. Pełny kod pli-ku hello.php jest na Listingu 3. Czas przejść do pisania czegoś bardziej użytecznego...

Katalog płyt CDPo zapoznaniu się z frameworkiem możemy już napisać funkcjonalną aplikację – będzie nią ba-za płyt CD. Program będzie chroniony hasłem, a dane przechowywane będą w bazie MySQL. Za-cznijmy od stworzenia szkieletu aplikacji w opar-ciu o przykład HelloWorld. Tym razem struktura plików i katalogów będzie następująca:

/cd_base/index.php

/cd_base/objects/cd_base.php

/cd_base/objects/login.php

/cd_base/objects/cd_list.php

W pliku index.php umieścimy dobrze znany nam już kod tworzący główną aplikację, tym razem nazwa klasy to cd _ base. Plik cd_base.php rów-nież zawiera klasę cd _ base. W jej konstrukto-rze umieścimy kod, który załaduje interfejs lo-gowania. Tyle, jeśli chodzi o główny kod aplikacji, przejdźmy teraz do pliku login.php dzięki które-mu zapewnimy dostęp do strony tylko osobom znającym hasło. W pliku tym umieścimy inter-fejs logowania, tak więc znów będzie zawierał kla-sę dziedziczącą po P4A_Mask. Tym razem poza przyciskiem służącym do logowania umieścimy tam pole tekstowe typu password:

$pwd =& $this -> build('p4a_field', 'pwd');

$pwd -> setType('password');

Listing 1. Plik index.php dla HelloWorld

<?php

// dołącz niezbędny plik

require_once dirname(__FILE__) . "/../p4a.php";

// utwórz nową instancję klasy helloworld (jeśli jeszcze nie istnieje)

$app =& p4a::singleton('helloworld');

// uruchom aplikację

$app -> main();

?>

Listing 2. Plik helloworld.php

<?php

class helloworld extends p4a {

function helloworld(){

parent::p4a(); // konstruktor p4a

$this->openMask('hello'); //załadowanie interfejsu

}

}

?>

Listing 3. Plik hello.php

<?php

class Hello extends p4a_mask {

function Hello() {

parent::P4A_Mask();

// nowy obiekt p4a_button

$button1 =& $this->build('p4a_button', 'button1');

// przypisanie etykiety tekstowej

$button1->setLabel('test przycisku');

// dodanie obsługi zdarzenia onClick

$this->intercept($button1, 'onClick', 'onButton1Click');

// wreszcie wyświetlamy obiekt

$this->display('main', $button1);

}

// funkcja wywoływana po kliknięciu w przycisk

function onButton1Click() {

// zmieniamy etykietę przycisku...

$this->button1->setLabel('kliknąłeś w przycisk');

// ... i wyłączamy go

$this->button1->enable(false);

}

}

?>Rysunek 2. Hello World. Efekt końcowy nie jest może imponujący, ale poznaliśmy zasadę działania frameworka

Rysunek 1. Struktura plików i katalogów

P4A

www.phpsolmag.org 29

$pwd -> addAction('onReturnPress');

$this -> intercept($pwd,

'onReturnPress', 'checkPwd');

Dodatkowo powinniśmy przypisać etykie-tę tekstową dla pola hasła, inaczej zostanie wyświetlona domyślna (czyli Pwd): $pwd -

> setLabel('Hasło'); W powyższym kodzie zadeklarowaliśmy użycie funkcji checkPwd, bę-dzie on wywoływana po wciśnięciu klawisza [Enter]. Dodatkowo stworzymy przycisk, po którego kliknięciu zostanie ona wywołana:

$btn =& $this -> build(

'p4a_button', 'btn');

$btn -> setLabel('Zaloguj');

$this -> intercept($btn, 'onClick',

'checkPwd');

Aby uporządkować stworzone przez nas kon-trolki, utworzymy obiekt typu p4a _ fieldset. Utworzenie tego obiektu wygląda następująco:

$fset =& $this -> build('p4a_fieldset',

'fset');

$fset -> setTitle('Logowanie');

$fset -> setWidth(350);

To jednak nie wszystko, musimy teraz połą-czyć utworzone przez nas wcześniej obiekty (przycisk i pole edycyjne) z ramką fieldset. Po-nadto przycisk logowania powinien być wy-środkowany i znajdować się niżej niż pole tek-stowe. Trudne? Niekoniecznie! Dzięki P4A zrobimy to w trzech linijkach:

$fset -> anchor($pwd);

$fset -> newRow();

$fset -> anchorCenter($btn);

Ostatnią czynnością, którą musimy wy-konać jest wyświetlenie ramki: $this-

>display('main', $fset);

Przyszła pora, abyśmy zaimplementowa-li funkcję sprawdzającą hasło checkPwd. Mu-simy w niej sprawdzić czy nowa wartość po-la $pwd jest taka, jakiej oczekiwaliśmy (krót-ko mówiąc, czy użytkownik wpisał właściwe hasło). Musimy przy tym pamiętać, że dane z pól typu password są automatycznie hasho-wane algorytmem md5, dlatego też ich wartość należy porównywać z hashem prawidłowego hasła. W funkcji tej potrzebujemy dostępu do utworzonej na samym początku instancji apli-kacji (by na wypadek podania poprawnego ha-sła móc załadować właściwą stronę) – dlatego też umieszczamy taką linię:

$p4a =& p4a::singleton();

Najważniejsze dopiero przed nami – pobranie hasła wpisanego przez użytkownika. Dokonamy tego używając metody getNewValue naszego po-la $pwd. Ostateczny kod powinien więc wyglądać tak: $pass = $this->pwd->getNewValue(); Następnie, w przypadku gdy wartość pola jest ta-ka sama jak hash prawidłowego hasła, wywołuje-my metodę $p4a -> openMask('cd_list');, aby załadować listę płyt CD.

Nadeszła pora napisać klasę wyświetlającą listę płyt oraz pozwalającą na edycję wpisów. Zrobimy to tak samo jak w przypadku pod-rzędnych klas dziedziczących z P4A_Mask, je-dyną nowością będzie tu dostęp do bazy da-nych. Będziemy też potrzebować bazy da-nych z utworzoną wcześniej tabelą (więcej in-formacji znajduje się w ramce Baza Danych). Aby połączyć się z bazą musimy wcześniej po-dać login i ewentualne hasło oraz nazwę bazy

której będziemy używać. W tym celu zdefi-niujemy wartość stałej P4A_DSN. Dane poda-je się w formie mysql://login:hasło@host/nazwa_bazy. Przykładowo, może być to mysql://root:123456@localhost/cdbase. Po utworzeniu prawidłowego łańcucha mo-żemy połączyć się z bazą danych. W tym ce-lu, w konstruktorze klasy cd_list tworzymy obiekt typu p4a_db_source o nazwie source. Następnie, wywołując jego metody setTable, setPK, addOrder i load ustawimy kolejno: nazwę tabeli (plyty), nazwę pola kluczowe-go (id_plyty), pole według którego dane bę-dą sortowane (nazwa) i wreszcie załadujemy dane. Aby program mógł działać prawidłowo, musimy też dodać linię $source->fields->id_plyty->setSequence('id_plyty'); dzięki czemu kolejne wpisy będą miały zwiększające się wartości id_plyty. Kolej-nym krokiem który musimy wykonać jest pobranie pierwszego rekordu przez wywoła-nie metody firstRow(); Następnie, do obec-nej strony interfejsu przypiszemy utworzo-ne przez nas źródło danych, używając meto-dy $this->setSource($source); Aby zapre-zentować dane, utworzymy znaną nam już metodą obiekty: p4a_table o nazwie table któremu przypiszemy źródło danych ($ta-ble->setSource($source)) oraz obiekt do którego zakotwiczymy naszą tabelę – będzie nim p4a_frame. Prezentowanie danych jest już wykonane, teraz musimy umożliwić do-dawanie, usuwanie i edycję rekordów. W tym celu do naszej ramki zakotwiczymy pola id_plyty ($frm->anchor($this->fields->id_plyty);) oraz, analogicznie, pole nazwa. Aby uniemożliwić zmianę wartości pola id_plyty musimy wywołać metodę enable, z pa-rametrem false. Ostatnim krokiem będzie utworzenie paska narzędziowego do zarzą-dzania rekordami – w tym celu utworzymy obiekt p4a_simple_toolbar i nazwiemy go tool. Musimy przypisać mu jeszcze podstronę do której należy: $tool->setMask($this);. Nareszcie, możemy wyświetlić już pasek na-rzędzi i ramkę:

$this->display('top', $tool);

$this->display('main', $frm);

PodsumowanieStworzony przez nas program nie znajdzie ra-czej praktycznego zastosowania, lecz dzięki nie-mu nauczyliśmy się jak w niezwykle łatwy spo-sób zarządzać danymi z bazy MySQL. Dzięki temu w krótkim czasie możemy stworzyć na-prawdę użyteczną aplikację – na przykład li-stę kontaktów.

MICHAŁ GAJEKAutor jest programistą – amatorem, od kilku lat tworzącym również w języku PHP.Kontakt z autorem: [email protected]

Baza DanychPotrzebna nam będzie baza danych (w artykule jej nazwa to cdbase). W bazie tej utworzymy tabe-lę plyty z trzeba polami:

id_plyty INT NOT NULL,

nazwa TEXT

przy czym polem PRIMARY KEY musi być id_plyty.

Rysunek 3. Baza płyt. Ostateczny efekt naszej pracy

3/2007

Praktyka

30

osCommerce

www.phpsolmag.org 31

Jedną z głównych zalet osCommerce, naj-bardziej znanego oprogramowania do prowadzenia sklepu internetowego, jest

licencja GNU GPL, dzięki której można nie tylko bezpłatnie korzystać ze sklepu, ale rów-nież wprowadzać własne modyfikacje w je-go kodzie.

Przepraszamy remanent – czyli blokujemy składanie zamówieńZ różnych względów w naszym sklepie inter-netowym mogą zdarzyć się okresy, w których chcielibyśmy tymczasowo wstrzymać przyj-mowanie nowych zamówień. Problem ten mo-żemy rozwiązać na kilka sposobów.

Pierwszy (drastyczny) to zamiana strony naszego sklepu na inną, informującą o chwi-lowym wstrzymaniu zamówień. Pomysł ten jednak nie jest zbyt szczęśliwy, ponieważ za-zwyczaj właściciel sklepu chciałby, aby od-wiedzający nadal mogli przeglądać całą je-go ofertę.

Sposób drugi (pośredni i często stosowany) to wysyłanie e-maili z odpowiednią informacją po otrzymaniu zamówienia, co z kolei może zi-rytować klientów, którzy po przebrnięciu przez wszystkie formularze są przekonani, że ich za-mówienie zostało przyjęte.

Poniżej prezentujemy rozwiązanie, które nie zmniejszając funkcjonalności naszego skle-pu umożliwi nam tymczasowo zablokować składanie zamówień.

Klient nadal będzie mógł przeglądać zawar-tość sklepu, korzystać z wyszukiwarki oraz wszystkich informacji serwisu, jednak po klik-nięciu na przycisk do koszyka, pokaż koszyk lub kup teraz - zamiast formularza zamówień zoba-czy przygotowany przez nas komunikat (Ry-sunek 1.).

Zaczynamy od skopiowania pliku checko-ut_shipping.php (katalog główny) oraz shop-ping_cart.php (katalog główny).

Pliki te odpowiadają za składanie zamówie-nia oraz wyświetlenie produktów w koszy-ku. Zmienimy ich zawartość, więc kopie bę-dą nam potrzebne - kiedy postanowimy przy-wrócić możliwość składania zamówień (wy-starczy wtedy podmienić te dwa pliki na ser-werze).

Kopie zapisujemy w bezpiecznym miejscu na dysku i tworzymy stronę, która będzie się wyświetlać zamiast dotychczasowych formu-larzy zamówień.

Na wzór weźmiemy jeden z istniejących pli-ków - privacy.php (katalog główny). Otwieramy plik i zapisujemy go jako checkout_shipping.php. Dotychczasowy plik zostanie zastąpiony no-wym. Teraz modyfikujemy w nim dwa wpi-sy. Wiersz:

require(DIR_WS_LANGUAGES . $language . '/'

. FILENAME_PRIVACY);

zmieniamy na:

require(DIR_WS_LANGUAGES . $language . '/'

. FILENAME_BLOK);

a wiersz:

$breadcrumb->add(NAVBAR_TITLE,

tep_href_link(FILENAME_PRIVACY));

zmieniamy na:

$breadcrumb->add(NAVBAR_TITLE,

tep_href_link(FILENAME_BLOK));

Po modyfikacji początek pliku powinien wy-glądać następująco:

<?php

require('includes/application_top.php');

require(DIR_WS_LANGUAGES

. $language . '/'

. FILENAME_BLOK);

$breadcrumb->add(NAVBAR_TITLE,

tep_href_link(FILENAME_BLOK));

?>

Resztę pliku pozostawiamy bez zmian. Tą sa-mą operację wykonujemy dla pliku shopping_cart.php (ponieważ te dwa pliki będą iden-tyczne, więc możemy skopiować plik checko-ut_shipping.php i zapisać go jako shopping_cart.php).

Następnie tworzymy nowy pusty plik blok.php i umieszczamy w nim kod:

<?

define('NAVBAR_TITLE',

'Informacja o składaniu

zamówień');

define('HEADING_TITLE',

'Wymiana asortymentu');

define('TEXT_INFORMATION',

'W związku z obecną wymianą

osCommerce

W tym artykule przedstawimy trzy przydatne rozwiązania, które pomogą początkującym użytkownikom dopasować sklep do własnych potrzeb.

Co obiecujemy...• Zaprezentujemy rozwiązania, które pomogą

początkującym użytkownikom osCommerce dopasować sklep do własnych potrzeb. Pokaże-my m.in., jak zablokować możliwość składania zamówień oraz jak stworzyć skrypt wypisujący zawartość całego sklepu.

Co należy wiedzieć...• Powinieneś umieć zainstalować sklep osCom-

merce oraz posiadać podstawową znajomosć języka PHP.

Poziom trudności

praktyczne rozwiązania

3/2007

Praktyka

30

osCommerce

www.phpsolmag.org 31

asortymentu,

wstrzymaliśmy tymczasowo

składanie zamówień.

Za utrudnienia przepraszamy.');

?>

Wynik działania tego skryptu obrazuje Rysu-nek 2.

Ostatnią rzeczą, jaką musimy zrobić to dodać wpis do pliku filenames.php (katalog includes):

define('FILENAME_BLOK' , 'blok.php');

Umieszczamy plik blok.php w katalogu includes/languages/polish/ Plik checkout_ship-ping.php, shopping_cart.php oraz filenames.php

umieszczamy w odpowiednich katalogach na serwerze, zastępując pliki dotychczasowe (pli-ki checkout_shipping.php oraz shopping_cart.php w katalogu głównym a plik filenames.php w ka-talogu includes).

Od tej pory odwiedzający nie mają moż-liwości składania zamówień. Żeby przywró-cić sklep do stanu wyjściowego wystarczy za-stąpić pliki checkout_shipping.php oraz shop-ping_cart.php przygotowanymi wcześniej ko-piami.

Blokujemy zamówienia – sposób drugiPrzedstawione rozwiązanie, oprócz tego, że jest proste w wykonaniu, ma również tę zale-tę, że kiedy raz przygotujemy odpowiednie pli-ki, nie musimy już ich później edytować - tzn. żeby przywrócić/ponownie zablokować skła-danie zamówień, nie trzeba otwierać żadne-go pliku i szukać w nim zmiennych, wystarczy tylko zastąpić dwa pliki na serwerze innymi. Może to zrobić samodzielnie również osoba, nieznająca PHP (np. nasz klient), której udo-stępnimy te pliki, wystarczy, tylko że prześle je na serwer.

Jeśli natomiast nie chcemy obarczać się two-rzeniem plików zapasowych, ich późniejszym przechowywaniem oraz przesyłaniem, mo-żemy zastosować nieco inną metodę i posłu-żyć się zmienną, która będzie decydować o zawartości naszych plików. Żeby zablokować/odblokować sprzedaż trzeba będzie wtedy edy-tować jeden plik i przypisać nową wartość do tej zmiennej. Poniżej prezentujemy ten nieco inny sposób zablokowania możliwości składa-nia zamówień.

Zaczynamy od przygotowania pliku blok.php oraz filenames.php tak jak w rozwiązaniu zapre-zentowanym powyżej.

Nową zmienną stworzymy w pliku applica-tion_top.php (katalog includes). W pliku tym znaj-duje się wiele ustawień oraz funkcji i jest on do-łączany do wszystkich potrzebnych nam plików, więc nasza zmienna będzie w nich również do-stępna. Otwieramy plik application_top.php i umieszczamy w nim, na początku, wpis:

$blok='tak';

Ta zmienna będzie decydować, czy blokowa-nie zamówień ma być włączone. Zapisujemy

Rysunek 1. Wygląd strony informującej o wstrzymaniu możliwości składania zamówień

Rysunek 3. Efekt działania skryptu wypisującego zawartość sklepu z uwzględnieniem poszczególnych kategorii

Rysunek 2. Opis elementów strony zdefiniowanych w pliku blok.php

Rysunek 4. Domyślna zawartość boxu Informacje

3/2007

Praktyka

32

Listing 1. Skrypt wypisujący całą zawartość sklepu

<?

require('includes/configure.php');

$dbhost=DB_SERVER;

$default_dbname=DB_DATABASE;

$dbusername=DB_SERVER_USERNAME;

$dbuserpassword=DB_SERVER_PASSWORD;

function db_connect($dbname='') {

$dbhost=$GLOBALS["dbhost"];

$default_dbname=$GLOBALS["default_dbname"];

$dbusername=$GLOBALS["dbusername"];

$dbuserpassword=$GLOBALS["dbuserpassword"];

$MYSQL_ERRNO=$GLOBALS["MYSQL_ERRNO"];

$MYSQL_ERROR=$GLOBALS["MYSQL_ERROR"];

$link_id=mysql_connect(

$dbhost,$dbusername,$dbuserpassword);

if(!$link_id) {

$MYSQL_ERRNO=0;

$MYSQL_ERROR=

"Nie udalo się nawiazac polaczenia z $dbhost";

return 0;

} else if(!empty($dbname) && !mysql_select_db($dbname)) {

$MYSQL_ERRNO=mysql_errno();

$MYSQL_ERROR=mysql_error();

return 0;

} else return $link_id;

}

function sql_error() {

$MYSQL_ERROR=$GLOBALS["MYSQL_ERROR"];

$MYSQL_ERRNO=$GLOBALS["MYSQL_ERRNO"];

if (empty($MYSQL_ERROR)) {

$MYSQL_ERRNO=mysql_errno();

$MYSQL_ERROR=mysql_error();

} return "$MYSQL_ERRNO: $MYSQL_ERROR";

}

function error_message($msg) {

echo"<script>alert(\"Error: $msg\"); history.go(-1)

"</script>";

exit();

}

function check($nr, $text, $od) {

$od+=50;

$query="select categories_name

from categories_description where categories_id=$nr";

$result=mysql_query($query);

if(!$result) error_message(sql_error());

$category_name=mysql_fetch_row($result);

echo"<span style=\"font-weight: bold; padding-left:

".$od." px;\">$text: $category_name[0]

</span><br>";

$query="select products_id from products_to_categories

where categories_id=$nr";

$result=mysql_query($query);

if(!$result) error_message(sql_error());

$od2=$od+10;

while($query_data=mysql_fetch_array($result)) {

$product_nr=$query_data["0"];

$query2="select products_name

from products_description

where products_id=$product_nr";

$result2=mysql_query($query2);

if(!$result2) error_message(sql_error());

$name_pr=mysql_fetch_row($result2);

$product=$name_pr[0]; {

echo "<span style=\"padding-left: ".$od2."\">$product

</span><br>";

}

$query="select categories_id from categories

where parent_id=$nr";

$result=mysql_query($query);

if(!$result) error_message(sql_error());

while($query_data=mysql_fetch_array($result)) {

$subcategory=$query_data["categories_id"];

check($subcategory, "<i>podkategoria </i>", $od);

}

}

$link_id=db_connect($default_dbname);

if(!$link_id) error_message(sql_error());

$query="select categories_id from categories

where parent_id='0'";

$result=mysql_query($query); if(!$result) error_message(sql_error());

while($query_data=mysql_fetch_array($result)) {

$category=$query_data["categories_id"];

check($category, "<span style=\"color: #CC0000

\">Kategoria główna </span>", 0)

}

?>

zmiany i zamykamy plik. Następnie w ory-ginalnych plikach checkout_shipping.php oraz shopping_cart.php umieszczamy po linii:

require("includes/application_top.php");

następujący wpis:

if($blok= ='tak') {

include('zablokuj.php');

} else {

//tutaj cała oryginalna zawartość danego

//pliku

<?

}

?>

Teraz w zależności od zmiennej $blok, albo zo-stanie wyświetlona przygotowana przez nas strona z odpowiednim komunikatem o zablo-kowaniu zamówień (plik zablokuj.php, który przygotujemy), albo oryginalna zawartość stro-ny (formularz zamówienia). Ważne jest, aby nasz warunek znalazł się po dołączeniu pliku application_top.php, ponieważ tam znajduje się zmienna $blok, która jest następnie sprawdza-na. Cała dalsza oryginalna zawartość pliku zo-staje ujęta w klamry po wyrażeniu else, pamię-tajmy więc o znacznikach na końcu pliku:

<?

}

?>

Zapisujemy zmiany w plikach checkout_ship-ping.php oraz shopping_cart.php.

Ostatnią rzeczą, jaka nam pozostała, to przy-gotowanie pliku zablokuj.php. Tworzymy go na wzór pliku privacy.php tak, jak w opisanym po-wyżej pierwszym sposobie, zapisując zmienio-ny plik jako zablokuj.php (wcześniej zastępowa-liśmy nim pliki checkout_shipping.php oraz shop-ping_cart.php)

Umieszczamy plik zablokuj.php na serwe-rze w katalogu głównym, pozostałe pliki na ich miejscach (pliki checkout_shipping.php oraz shopping_cart.php w katalogu głównym, plik ap-plication_top.php oraz filenames.php w katalogu includes, a plik blok.php w katalogu includes/languages/polish/).

3/2007

Praktyka

34

Listing 2. Funkcja wypisującą zawartość sklepu

Efekt działania obu przedstawionych spo-sobów jest taki sam. Różni się natomiast spo-sób późniejszego włączania i wyłączania moż-liwości składania zamówień; albo, jak w roz-wiązaniu pierwszym, podmieniamy dwa pliki na serwerze (checkout_shipping.php oraz shop-ping_cart.php), albo, jak w rozwiązaniu dru-gim, edytujemy jeden plik (application_top.php) i zmieniamy wartość zapisanej w nim zmien-nej. Wybór sposobu zależy tylko od naszych po-trzeb i preferencji.

Inwentaryzacja – zobacz wszystko, co mam w ofercieKażdy właściciel sklepu chciałby, aby istnia-ła możliwość wglądu w całość oferty – na po-trzeby własne lub klientów. Do tego celu przy-dałaby się opcja wyświetlania wszystkich pro-duktów w jednym miejscu, bez wyszukiwania, czyli tak jak znajdują się w sklepie, z zachowa-niem podziału na poszczególne kategorie i pod-kategorie.

Istnieje dodatek do osCommerce, który umożliwia wyświetlenie cennika wszystkich produktów, jednak tworzy on listę bez rozróż-nienia poszczególnych podkategorii.

W tej części artykułu prezentujemy skrypt, który wypisuje drzewo wszystkich katego-rii wraz z przypisanymi do nich produktami. Wcięcia obrazują zagnieżdżenie poszczegól-nych działów. Efekt działania skryptu obrazu-je Rysunek 3.

Skrypt zawiera funkcje obsługujące połącze-nia z bazą danych sklepu oraz obsługujące błę-dy. Cały kod znajduje się na Listingu 1. Na Li-

stingu 2. przedstawiona jest natomiast funk-cja, której zadaniem jest odpowiednie wypisa-nie drzewa kategorii i produktów.

Funkcja po raz pierwszy w skrypcie wywo-ływana jest w pętli z identyfikatorami katego-rii głównych. Jej działanie polega na iteracyj-nym sprawdzaniu kolejnych podkategorii i wypisywaniu ich nazw, z odpowiednim wcię-ciem oraz wypisaniu produktów, które się w nich znajdują.

Skrypt ten można łatwo dostosować do wła-snych potrzeb, zmieniając tylko sposób wypi-sywania informacji.

Nowe menu, czyli jak dodać własne linki i treści do działu InformacjeDomyślnie dział Informacje wygląda tak, jak na Rysunku 4. Znajdują się w nim linki do działów: Wysyłka i Zwroty, Bezpieczeństwo, Korzystanie z ser-wisu, Program lojalnościowy, Kontakt. W tej części artykułu pokażemy, jak umieścić tam przykłado-wy nowy link Strony polecane oraz jak przygotować odpowiednią podstronę. Linki z działu Informa-cje są zgrupowane w tabeli przypisanej do zmien-nej $info_box_contents w pliku information.php (katalog includes/boxes/). Dopisujemy nasz link na końcu tabeli w postaci:

.'<br><a href="' . tep_href_link

(FILENAME_POLECANE)

. '">'BOX_INFORMATION_POLECANE

. '</a>'

Zawartość tabeli powinna teraz wyglądać jak na Listingu 2.

Nazwy linków, widoczne na stronie, znaj-dują się w pliku polish.php (katalog includes/languages/). Odnajdujemy w nim komentarz: // information box text in includes/

boxes/information.php i dopisujemy własną definicję do pozostałych:

define('BOX_INFORMATION_POLECANE',

'Strony polecane');

Musimy następnie powiązać link z naszą pod-stroną. W tym celu otwieramy plik filena-mes.php (katalog includes/) i dopisujemy w nim wiersz: define('FILENAME _ POLECANE',

'polecane.php');

Ostatnia rzecz to przygotowanie podstro-ny. W przypadku większości podstron skle-pu nagłówek, tytuł oraz treść podstrony są zapisane w osobnym pliku w katalogu includes/languages/polish/ (np. w pliku privacy.php, w tym katalogu, znajduje się nagłówek, tytuł oraz treść strony przypisanej do linku Bezpie-czeństwo), natomiast sama strona znajduje się w katalogu głównym (plik o tej samej nazwie). Dlatego musimy teraz przygotować dwa pliki o nazwie polecane.php. Najpierw przygotuje-my plik z tytułem, nagłówkiem oraz treścią. Tworzymy plik polecane.php i umieszczamy w nim kod:

<?php

define('NAVBAR_TITLE',

'Strony polecane');

define('HEADING_TITLE',

'Zapraszamy na strony:');

function check($nr, $text, $od) {

$od+=50;

//zwiększenie wcięcia

$query="select categories_name

from categories_description where categories_id=$nr";

//pobranie nazwy kategorii z bazy danych

$result=mysql_query($query);

if(!$result) error_message(sql_error());

$category_name=mysql_fetch_row($result);

//odczytanie nazwy kategorii

echo"<span style=\"font-weight: bold; padding-left: "

.$od." px;\">$text: $category_name[0]</span><br>";

//wypisanie nazwy kategorii z odpowiednim wcięciem ($od)

$query="select products_id from products_to_categories

where categories_id=$nr";

//pobranie nazwy produktów z bazy danych

$result=mysql_query($query);

if(!$result) error_message(sql_error());

$od2=$od+10;

//głębsze wcięcie dla produktu

while($query_data=mysql_fetch_array($result)) {

$product_nr=$query_data["0"];

$query2="select products_name from products_description

where products_id=$product_nr";

$result2=mysql_query($query2);

if(!$result2) error_message(sql_error());

$name_pr=mysql_fetch_row($result2);

$product=$name_pr[0]; //odczytanie nazwy produktu

echo "<span style=\"padding-left: "

.$od2."\">$product</span><br>";

//wypisanie produktu z odpowiednim wcięciem ($od2)

}

$query="select categories_id from categories

where parent_id=$nr";

//pobranie identyfikatorów podkategorii danych kategorii

$result=mysql_query($query);

if(!$result) error_message(sql_error());

while($query_data=mysql_fetch_array($result)) {

$subcategory=$query_data["categories_id"];

//odczytanie identyfikatora podkategorii

check($subcategory, "<i>podkategoria </i>", $od);

//ponowne wywołanie funkcji

}

}

osCommerce

www.phpsolmag.org

define('TEXT_INFORMATION',

'<a href="http://www.pierwsza.pl">

Przykładowa strona</a>');

?>

Działanie skryptu obrazuje Rysunek 5. Plik należy umieścić w katalogu includes/languages/polish/ Stronę przeznaczoną do umieszczenia w katalogu głównym zrobimy wzorując się na pliku privacy.php (katalog główny). Otwieramy plik i zapisujemy go w katalogu głównym, jako polecane.php. Następnie modyfikujemy w nim dwa wpisy. Wiersz:

require(DIR_WS_LANGUAGES . $language . '/'

. FILENAME_PRIVACY);

zamieniamy na:

require(DIR_WS_LANGUAGES . $language . '/'

. FILENAME_POLECANE);

a wiersz:

$breadcrumb->add(NAVBAR_TITLE,

tep_href_link(FILENAME_PRIVACY));

zamieniamy na:

$breadcrumb->add(

NAVBAR_TITLE,

tep_href_link(FILENAME_PRIVACY)

);

Po dokonaniu zmian początek pliku powinien wyglądać następująco:

<?php

require('includes/application_top.php');

require(DIR_WS_LANGUAGES . $language

. '/' . FILENAME_POLECANE);

$breadcrumb->add(NAVBAR_TITLE,

tep_href_link(FILENAME_PRIVACY));

?>

Resztę pliku pozostawiamy bez zmian. Po umieszczeniu plików na serwerze zobaczymy, że box Informacje zawiera teraz nowy link Strony po-lecane, który (po kliknięciu) otwiera przygotowa-ną przez nas podstronę. Wiedząc, w których miej-scach umieszczone są poszczególne elementy opi-sanego menu, możemy już łatwo dopasować całą jego zawartość do własnych potrzeb.

PodsumowanieW tym artykule pokazaliśmy, jak można sa-modzielnie rozbudowywać gotowe rozwiąza-nie, jakim jest osCommerce, poszerzając w ten sposób jego możliwości i dopasowując sklep do własnych potrzeb. Sposobów jest wiele ,jak wi-dać na naszym pierwszym przykładzie, a wybór metody zależy tylko od własnych preferencji. W Internecie znajdziemy wiele gotowych rozwią-zań, które możemy również przerabiać, dosto-sowując do naszych potrzeb. W ramce Przydat-ne linki podajemy kilka adresów, pod którymi można znaleźć m.in. tego typu skrypty.

MAGDALENA MARYAŃSKAAutorka pracuje w firmie a3a jako projektant stron internetowych.Kontakt z autorką: [email protected]

Rysunek 5. Opis elementów strony zdefiniowanych w pliku polecane.php

Przydatne linki

• Główna strona osCommerce - informacje, skrypty, skórki oraz wiele różnych dodatków: – http://www.oscommerce.com

• Przykładowe polskie podstrony na oscommerce.com: – http://www.oscommerce.com/community/contributions,1376/page,2

• http://www.oscommerce.com/community/contributions,457• http://www.oscommerce.com/community/contributions,1130• http://www.oscommerce.com/community/contributions,573• Polskie forum poświęcone osCommerce - porady, dyskusje, moduły i dodatki: – http://

www.oscommerce.pl• Generator przycisków osCommerce: – http://generator.oscpremium.com• Kilka bezpłatnych dodatków do osCommerce: – http://oscommerceguides.com/index.php?cPath=3

R E K L A M A

3/2007

Narzędzia

36

Scaffolding

www.phpsolmag.org 37

Kluczowym elementem frameworku jest możliwość prostego definiowania modeli danych oraz powiązania ich z

interfejsem użytkownika. Model, to nic innego jak komponent odpowiedzialny za zarządzanie stanem aplikacji (np. informacjami przetwarza-nymi w bazie danych). Model odpowiada tak-że za logikę, jaka kryje się w powiązaniach mię-dzy różnymi danymi - dba by dane były spój-ne. Pozostałe elementy interfejsu użytkowni-ka (kontroler i widok) dbają o prawidłową pre-zentację danych. Dzięki takiemu podejściu, np. zmiana sposobu przechowywania danych, nie spowoduje konieczności zmiany pozostałych komponentów aplikacji, ponieważ są one odse-parowane od siebie i współdziałają według ści-śle określonych zasad. Oczywistym utrudnie-niem, przy takim podejściu do tworzenia apli-kacji, jest konieczność zbudowania wszystkich komponentów już na etapie prototypu. Dopie-ro wtedy możliwe jest sprawdzenie, czy opra-cowany model danych poradzi sobie w prakty-ce. Ewentualne zmiany najczęściej muszą być wprowadzane we wszystkich komponentach aplikacji.

Naturalnym rozwiązaniem powyższych niedogodności jest możliwość automatycz-nego generowania interfejsu użytkownika, na

podstawie modelu danych. Technika ta zosta-ła nazwana scaffolding i jest niezastąpiona w początkowej fazie budowania aplikacji. Sche-mat danych zmienia się wraz z rozwojem pro-jektu, co jest zjawiskiem jak najbardziej nor-malnym. Ma to jednak negatywne skutki - do-piero co opracowane formularze czy proce-

dury zapisu danych, mogą się szybko zdeak-tualizować. Nikt nie lubi tworzyć kodu, któ-ry później nie będzie dołączony do aplikacji. Scaffolding pozwala na analizę tabel w bazie danych i automatyczne tworzenie formula-rzy służących do edycji, usuwania i przeglą-dania danych.

Główną zaletą scaffoldingu jest to, że auto-matycznie wygenerowany interfejs użytkow-nika, uwzględnia relacje między modelami da-nych aplikacji oraz ograniczenia nałożone za po-mocą reguł walidacji. Weźmy na przykład rela-cję między tabelami osoby i telefony. Intuicyj-nie wiemy, że z każdą osobą mogą być skojarzo-ne różne numery telefonów (domowy, służbo-wy itp.) oraz, że każdy numer telefonu należy do pewnej osoby. Jeżeli zdefiniujemy taką rela-

Scaffolding

CakePHP został, przez autorów, wyposażony w bardzo elastyczny mechanizm MVC, ułatwiający tworzenie własnych aplikacji interneto-wych. Z artykułu dowiesz się, jak go wykorzystać.

Co obiecujemy...• Poznasz zasady prototypowania aplikacji wy-

korzystujących CakePHP. Po analizie przedsta-wionego w artykule kodu, będziesz mógł przy-gotowywać własne aplikacje internetowe, dzia-łające w oparciu o tryb scaffold CakePHP.

Co należy wiedzieć...• Wymagana jest znajomość frameworku Cake-

PHP oraz umiejętność samodzielnego skonfi-gurowania środowiska Apache, MySQL oraz PHP. Przydatna będzie również znajomość języ-ka SQL i umiejętność administrowania bazą da-nych MySQL.

Poziom trudności

Użycie trybu CakePHP

Relacje między modelami danychFramework pozwala na zdefiniowanie czterech rodzajów relacji:

• hasOne (pol.1 ma jeden) - relacja 1:1,• hasMany (pol. ma wiele) - relacja 1:n,• hasAnsBelongsToMany (pol. ma i należy do wielu) - relacja n:m,• belongsTo (pol. należy do) - relacja 1:1 będąca uzupełnieniem relacji hasMany.

Aplikacje w środowisku produkcyjnymScaffolding został pomyślany, jako narzędzie podczas prototypowania aplikacji. Za jego pomocą można szybko sprawdzić, czy model i reguły walidacji danych działają zgodnie z intencjami. Auto-matycznie generowany kod nie zawiera np. zabezpieczeń przed nieautoryzowanymi zmianami czy wręcz usunięciem niektórych danych. Z tego powodu stosowanie scaffoldingu, np. do edycji da-nych w środowisku produkcyjnym nie jest zalecane, choć czasem bywa przydatne na etapie uru-chamiania aplikacji.

Szybki startDo przetestowania trybu scaffold w CakePHP można wykorzystać przykładową aplikację, znajdu-jącą się na płycie CD. Cała operacja ogranicza się do wykonania trzech prostych kroków:

• rozpakuj plik archiwum,• skonfiguruj Apache tak, by DocumentRoot wskazywał na katalog app/webroot/,• uruchom przeglądarkę WWW i wprowadź adres IP serwera CakePHP.

3/2007

Narzędzia

36

Scaffolding

www.phpsolmag.org 37

cję, to zapytanie o dowolną osobę zwróci także listę powiązanych numerów telefonów. Tak sa-mo relacja zadziała w drugą stronę. Zapytanie o numer telefonu zwróci dodatkowe informacje o właścicielu.

Automatyczne funkcjonowanie framewor-ku, bez skomplikowanej konfiguracji, wyma-ga stosowania się do pewnych reguł. CakePHP zakłada, że nazwy kolumn kończące się znaka-mi _id, są kluczami obcymi o nazwach pasu-jących do szablonu "nazwa tabeli obcej w licz-bie pojedynczej"_id (podstawowy klucz tabeli musi nazywać się id). Dodatkowo, nazwy ta-bel, powinny być w liczbie mnogiej, bez pol-skich znaków np.: people, phones. W naszym przykładzie z książką telefoniczną kolumna people.phone_id jest kluczem obcym do tabe-li phones.id.

Zalety scaffoldingu sprawdzimy przy tworze-niu aplikacji, która będzie wykorzystywała klu-czowe zalety CakePHP.

Książka telefonicznaZałóżmy, że tworzymy prostą aplikację, która będzie umożliwiała gromadzenie informacji o numerach telefonów naszych znajomych. Do-celowo użytkownicy naszej książki telefonicz-nej będą mogli wyszukiwać dane według na-zwiska lub numeru telefonu. Teraz jednak zale-ży nam na sprawdzeniu funkcjonowania mode-lu danych oraz zweryfikowaniu reguł walidacji numerów telefonów (docelowo system będzie np. sprawdzał unikalność danych, jednak scaf-folding w CakePHP obsługuje tylko walidację za pomocą wyrażeń regularnych).

Model danychFizyczny model danych jest przedstawiony na rysunku 1. Informacje będziemy przechowywa-li w dwóch tabelach (people oraz phones), dla których stworzymy niezależne modele Cake-PHP (odpowiednio: person oraz phone). Rela-cje między modelami możemy opisać w nastę-pujący sposób:

• każda osoba może mieć wiele numerów te-lefonów,

• numer telefonu należy do osoby.

Plik app/models/person.php:

class Person extends AppModel {

var $hasMany = array(

'Phones' => array(

'className' => 'Phone',

'foreignKey' => 'person_id'

) );

...

}

Musimy pamiętać, by w klasie Phone dodać re-lację $belongsTo. Jeżeli o tym zapomnimy, otrzymamy powiązanie, które nie umożliwi zadawanie zapytań o osobę będącą właścicie-lem danego numeru telefonu. Plik app/models/phone.php:

class Phone extends AppModel {

var $belongsTo = array(

'Person' => array(

'className' => 'Person',

'foreignKey' => 'person_id'

) );

...

}

Kontroler aplikacjiUruchomienie trybu scaffold w aplikacji wy-maga, by w kontrolerze została zdefiniowana zmienna $scaffold. Nie ma potrzeby defi-niowania funkcji obsługi modelu danych, po-nieważ CakePHP zrobi to za nas automatycz-nie. Utworzymy dwa kontrolery, odpowiadają-ce poszczególnym modelom danych (People-Controller oraz PhonesController). Plik app/controllers/people_controller.php:

class PeopleController extends

AppController {

// jeżeli używamy PHP4 musimy zdefiniować

// zmienną zawierającą nazwę klasy

var $scaffold;

var $name = 'People';

}

Plik app/controllers/phones_controller.php:

class PhonesController extends

AppController {

// jeżeli używamy PHP4 musimy zdefiniować

// zmienną zawierającą nazwę klasy

var $scaffold;

var $name = 'Phones';

}

Nasza aplikacja jest już gotowa (Rysunek 2). Wystarczy uruchomić przeglądarkę interneto-wą i wpisać adres IP serwera CakePHP razem z nazwą kontrolera (np. http://127.0.0.1/people). Możemy już w prosty sposób tworzyć i usuwać rekordy w bazie danych, jednak ze względu na brak zdefiniowanych reguł walidacji, możliwe jest zapisanie w bazie dowolnych informacji.

Walidacja danychPonieważ zależy nam na jakości danych w na-szej „książce telefonicznej”, wprowadzimy pew-ne ograniczenia. Będziemy akceptowali tylko numery telefonów stacjonarnych, zaczynające się numerem kierunkowym (dwie cyfry), po którym wystąpi myślnik lub spacja i 7 dalszych cyfr. Wyrażenie regularne, weryfikujące taki za-pis danych, ma postać '/[0-9]{2}[\- ][0-9]{7}$/i' i wprowadzamy je do definicji modelu Phone. Plik app/models/phone.php:

class Phone extends AppModel {

var $validate = array(

'number' => '/[0-9]{2}[\- ]

[0-9]{7}$/i'

);

...

}

Rysunek 2. Książka telefoniczna działająca w oparciu o CakePHP Rysunek 1. Fizyczny model danych książki telefonicznej

3/2007

Narzędzia

38

beli people (np. nową kolumnę przechowują-cą komentarz).

mysql> use notes;

mysql> alter table people add column

(comment text);

Wykonanie polecenia odśwież w przeglądar-ce, spowoduje natychmiastową aktualizację strony www i od razu będziemy mogli korzy-stać z dodatkowego pola przy edycji danych (Rysunek 5). Dodanie nowego modelu danych wymaga już nieco więcej zabiegów, ponieważ oprócz definicji klasy CakePHP, musimy utwo-rzyć tabelę w bazie danych i odpowiednie re-lacje. Na płycie CD, dołączonej do czasopi-sma, znajduje się przykład zawierający aplika-cję książki telefonicznej rozbudowanej o moż-liwość przechowywania adresu zamieszkania (relacja: osoba ma jeden adres).

Zaawansowana walidacja danychPrzykłady, które omówiliśmy powyżej, nie oddają pełni możliwości mechanizmów walidacji danych w CakePHP, ponieważ walidacja w trybie scaffold jest ograniczona tylko do wyrażeń regularnych. Te-raz zajmiemy się techniką, która umożliwi nam np. unikanie wprowadzania do systemu dublują-cych się danych (co w przypadku książki telefo-nicznej jest wysoce pożądane). Standardowo, regu-ły walidacji zdefiniowane w tablicy $validate (kla-sa AppModel) są sprawdzane podczas zapisu danych (tzn. podczas wywołania metody Mode::save()) za pomocą funkcji Model::validates(), Model::invalidate() oraz Model::invalidFields(). Pierwsza z nich jest odpowiedzialna za sprawdze-nie poprawności danych. Druga i trzecia funkcja ma za zadanie zwrócić komunikat powiązany z kolum-ną tabeli, która naruszyła reguły. Za wyświetlenie komunikatu o błędzie odpowiada funkcja $html->tagErrorMsg() zawarta w helperze HTML. Wy-korzystując te informacje zbudujemy funkcję, któ-ra przed zapisem danych, wprowadzonych przez użytkownika, sprawdzi ich unikalność (Listing 1). Kod formularza współpracującego z funkcją walida-cji znajduje się na Listingu 2.

PodsumowanieScaffolding, w CakePHP, to doskonałe narzędzie, pozwalające na szybkie tworzenie prototypów aplikacji oraz manipulacje na danych. Dzięki nie-mu możliwe jest łatwe dostosowywanie modelu danych do potrzeb zmieniających się wraz z roz-wojem projektu. W niektórych przypadkach, taki tryb wprowadzania danych, z powodzeniem mo-że być użyty do wprowadzania danych do środo-wiska produkcyjnego aplikacji.

Listing 1. Funkcja weryfikująca unikalność danych

class PeopleController extends AppController {

function create() {

// sprawdź czy w formularzu są dane o nowym kontakcie

if (!empty($this->data['Person'])) {

// sprawdź czy w bazie jest podobny rekord

$person = $this->Person->findByName($this->data['Person']['name']);

// jeżeli taki kontakt już istnieje wyświetl komunikat o błędzie

if (!empty($person['Person']['name'])) {

// zaznacz, że kolumna 'name' ma niepoprawne dane

// informacja o błędzie będzie wyświetlana za pomocą funkcji

// $html->tagErrorMsg('Person/name','komunikat o błędzie')

$this->Person->invalidate('name');

$this->render();

}

// analogicznie postępujemy z pozostałymi modelami dla którch

// dane zostały przekazane w formularzu

else {

// zapisz dane i przejdź do głównej strony serwisu

$this->User->save($this->data);

this->redirect('/');

}

}

}

}

Listing 2. Formularz współpracujący z funkcją weryfikującą unikalność danych

<h2>Dodaj kontakt do książki telefonicznej</h2>

<form action="<?php echo $html->url('/people/add')?>" method="post">

<div class="add">

<p>Kontakt:

<?php echo $html->input('Person/name', array('size'=>'50'))?>

<?php echo $html->tagErrorMsg('Person/name',

'wprowadzone dane nie są unikalne!')?>

</p>

<p>Body

<?php echo $html->textarea('Phone/number') ?>

<?php echo $html->tagErrorMsg('Phone/number',

'numer telefonu już jest w bazie danych!')?>

</p>

<p><?=$html->submit('Zapisz')?></p>

</div>

</form>

PIOTR GAPIŃSKIAutor w wolnych chwilach zajmuje się programo-waniem w różnych językach (głównie Rebol, Ruby, PHP i AmigaE).Kontakt z autorem: [email protected].

W sieci

• http://www.cakephp.org – strona domowa projektu,• http://cakeforge.org/projects/cakephp – repozytorium stabilnych wersji frameworku,• http://groups.google.com/group/cake-php/ – lista dyskusyjna poświęcona CakePHP.

Teraz próba wprowadzenia błędnego numeru spowoduje wyświetlenie odpowiedniego ko-munikatu (Rysunek 3). Dane zostaną zapisa-ne dopiero, gdy będą miały wymagany format (Rysunek 4). Podobne ograniczenie nałożymy na dane w tabeli people - aplikacja nie będzie akceptowała braku informacji o osobie. Plik app/models/person.php:

class Person extends AppModel {

var $validate = array(

'name' => VALID_NOT_EMPTY

);

...

}

ModyfikacjeDotychczas nie wykorzystaliśmy głównej cechy trybu scaffold, czyli automatycznej aktualizacji formularzy w przypadku zmian fizycznego mo-delu danych. Sprawdźmy, co się stanie z naszą aplikacją, jeżeli dodamy kolejną kolumnę w ta-

3/2007

Narzędzia

40

CakePHP

www.phpsolmag.org 41

Od pierwszej wersji wydanej w 2005 roku CakePHP zjednuje sobie coraz większe grono osób zainteresowanych

jego dalszym rozwojem. Wśród wielu języków umożliwiających programowanie aplikacji in-ternetowych (Java, .Net, Perl, Ruby itp.) PHP ma ugruntowaną pozycję. Jest szeroko wykorzysty-wany i wspierany przez wielu dostawców inter-netu. Dla języka PHP powstało wiele pakietów wspierających programistów w tworzeniu apli-kacji: Prado, SmartPHP, Mojavi, CakePHP itp. Co powoduje, że CakePHP jest wyjątkowy?

Wielu programistów jest zmęczonych istnieją-cymi technologiami zmuszającymi ich do ciągłe-go tworzenia kodu od początku, do samodzielne-go dbania o pobieranie i prezentowania danych na stronie WWW. Wiele pakietów, podobnie jak CakePHP opartych o model MVC (więcej na ten temat w następnych rozdziałach) likwi-dowało powyższe niedogodności, ale w zamian zmuszały programistów do żmudnego konfigu-rowania framework'u. CakePHP zachowuje się inaczej. Zastosowano w nim zasadę konwencja ponad konfigurację dzięki czemu każdy element tworzonej aplikacji ma swoje miejsce a wszystkie elementy aplikacji oddziaływają na siebie wza-jemnie w standardowy, określony sposób. Po-mysłem na ograniczenie liczby błędów popełnia-

nych przez programistów jest uwolnienie ich od nudnego tworzenia interfejsu WWW (najwięcej błędów powstaje w kodzie, który musi powstać, a nie niesie ze sobą wyzwań dla programisty) oraz przez zasadę DRY (ang. don't repeat yourself; pol. nie powtarzaj się). Informacje o zachowaniu apli-kacji (tzw. logika biznesowa) powinny być zdefi-niowane w jednym miejscu. Konieczność powie-lania tego samego kodu w wielu miejscach pro-gramu może być źródłem błędów, a już na pew-no świadczy o wadliwej konstrukcji aplikacji.

Model, View, ControllerWczesne aplikacje internetowe były monolitem w którym kod obsługujący model danych (zarządza-jący stanem aplikacji) był przemieszany z kodem odpowiedzialnym za wyświetlanie danych w prze-glądarce użytkownika. Prowadziło to oczywiście

do trudności w zarządzaniu aplikacją, gdyż zmia-na np. układu strony WWW mogła prowadzić do trudnych do wykrycia błędów w pozostałych czę-ściach programu. Poza tym tak przygotowany kod utrudniał jego ponowne wykorzystanie w innych projektach. Na początku lat 80 XX wieku zaczęto stosować nową architekturę w której aplikacje by-ły podzielone na trzy typy komponentów: model, view oraz controller (Rysunek 1.). Model jest od-powiedzialny za zarządzanie stanem aplikacji (np. przechowywaniem danych poza aplikacją – w ba-zie danych). Nie oznacza to oczywiście, że kompo-nent ten odpowiada tylko za pobranie/zapisanie danych z zewnętrznego źródła. Model odpowia-da także za logikę, jaka kryje się w powiązaniach między różnymi danymi – dba by dane były spój-ne. Dzięki takiemu podejściu np. zmiana sposo-bu przechowywania danych (inny model bazy da-nych) nie powoduje konieczności zmiany wszyst-kich komponentów aplikacji.

View (pol. widok) jest odpowiedzialny za gene-rowanie interfejsu użytkownika w oparciu o da-ne dostarczane przez Model. View nie przetwa-rza danych, które otrzymuje - po prostu prezen-tuje je w określony (zdefiniowany) sposób. Oczy-wiście może być wiele widoków dla tych samych danych. Weźmy na przykład aplikację wyświetla-jącą dane wprowadzone przez użytkownika. Za-

CakePHP

CakePHP to framework wzorowany na pakiecie Ruby On Rails (http://www.rubyonrails.org). Ułatwia on tworzenie, wdrażanie oraz opiekę nad kodem aplikacji internetowych (ang. web applications).

Co obiecujemy...• Poznasz zasady funkcjonowania nowoczesnego

framework'u opartego o architekturę MVC.• Po analizie przedstawionego kodu będziesz

mógł przygotowywać własne aplikacje interne-towe działające w oparciu o CakePHP.

Co należy wiedzieć...• Wskazana jest znajomość zasad programowa-

nia obiektowego oraz umiejętność samodziel-nego skonfigurowania środowiska Apache, My-SQL, PHP.

• Niezbędna jest znajomość języka SQL i admini-stracji bazą danych MySQL.

Poziom trudności

zaawansowany framework MVC

Rysunek 1. Architektura MVC

������

�����

����������

3/2007

Narzędzia

40

CakePHP

www.phpsolmag.org 41

rządzanie danymi oraz dbanie o ich spójność to zadanie komponentu Model. Wyświetlanie da-nych to zadanie komponentu View, który może je wyświetlić jako np. tabelę lub jako listę.

Controller (pol. kontroler) to serce aplika-cji. Komponent otrzymuje informacje o zdarze-niach generowanych przez użytkownika i na tej podstawie łączy we właściwy sposób komponen-ty Model z komponentami View. Aplikacja mo-że mieć wiele kontrolerów obsługujących okre-śloną, specyficzną funkcjonalność w odmienny sposób. O tym, który kontroler ma być wywoła-ny decyduje kluczowy moduł CakePHP: Router. Dla lepszego zrozumienia roli routera przeanali-zujmy jak zareaguje aplikacja na wywołanie adre-su http://www.strona/forum/view/1. Router przyj-mie pierwszą część adresu WWW (forum) za nazwę kontrolera, który należy uruchomić, dru-gą część adresu (view) jako nazwę funkcji obsłu-gującej zdarzenie, a ostatni element (1) jako para-metr funkcji (mówimy do domyślnej konfigura-cji - moduł routera jest konfigurowalny).

Początkowo wymyślony do obsługi GUI (ang. graphics user interface; pol. graficzny inter-

fejs użytkownika) MVC szybko zadomowił się w aplikacjach internetowych. Aktualnie więk-szość framework'ów działa w oparciu o tę archi-tekturę: Java Struts, PHP Prado, SmartPHP itp.

Model danychNowoczesne aplikacje często muszą przechowy-wać swoje informacje w zewnętrznych źródłach danych. Nawet najprostsza aplikacja obsługująca konta użytkowników musi na boku zapisywać in-formacje o loginach i hasłach. Sklepy internetowe muszą przechowywać informacje o dostępnych artykułach, zamówieniach, adresach, pod które należy dostarczyć towar. Jak widać potrzeba prze-chowywania danych jest dosyć powszechna. PHP od dawna oferuje mechanizmy umożliwiające obsługę różnych baz danych co umożliwia mani-pulowanie danymi za pomocą języka SQL (ang. structured query language). Z łatwością można je wykorzystać w aplikacjach jednak możemy napo-tkać na dwa rodzaje problemów:

• różnice w API (ang. application programming interface; pol. interfejs programowania apli-

kacji) powodują konieczność tworzenia ko-du do obsługi każdej bazy oddzielnie,

• każde zapytanie SQL może zwrócić róż-ne dane, które należy przekazać do apli-kacji we właściwy sposób. Ponadto zapyta-nie może zależeć od różnych parametrów, które trzeba przekazać z aplikacji.

Powyższe ograniczenia mogą prowadzić do po-wielania kodu co w miarę rozwoju aplikacji mo-że prowadzić do powstawania błędów (DRY!). Jeżeli zmieni się fizyczny model danych (np. pojawi się dodatkowa kolumna w tabeli) trzeba będzie poprawić całą aplikację! We wszystkich miejscach w których odwoływaliśmy się do sta-rych danych... Niestety najpierw trzeba te miej-sca odszukać. Naturalnym rozwiązaniem po-wyższych niedogodności jest dostęp do danych poprzez warstwę obiektów pośredniczących ukrywających przed programistą niskopoziomo-we szczegóły związane z konkretną bazą danych. W bardziej zaawansowanych framework'ach sto-suje się technologie mapowania relacyjnych da-nych na obiekty i struktury języka programo-wania: ORM (ang. object-relational mapping). Na przykład jeżeli w bazie danych znajduje się tabe-la messages to program musi mieć zdefiniowaną klasę Message. Poszczególne wiersze tabeli są au-tomatycznie zamieniane na obiekty klasy Messa-ge. ORM umożliwia także określanie relacji mię-dzy poszczególnymi tabelami. CakePHP (wer-sja 1.1.x) oferuje rozwiązanie pośrednie wzglę-dem ORM. Dane zwrócone w wyniku zapyta-nia nie są obiektami klasy modelu danych ale za-gnieżdżoną strukturą tablic asocjacyjnych w któ-rej kluczami są nazwy kolumn tabeli (Listing 1). Wróćmy do przykładu z tabelą messages i klasą Message (zasady nazywania tabel i klas CakePHP poznamy w dalszej części artykułu). Zapytanie o pierwszy rekord tabeli będzie miało postać:

$dat = $this->Message->find(1);

echo $dat['Message']['title'];

// nazwa tabeli, nazwa kolumny

Kluczowe cechy CakePHP

• Działa zarówno z PHP4 jak i PHP5.• Działa w oparciu o architekturę Model,

View, Controller (MVC).• Umożliwia obsługę łatwych do zapamię-

tania (i ładnie wyglądających) adresów WWW.

• Posiada wbudowane mechanizmy wali-dacji danych oraz cache.

• Udostępnia szablony stron WWW wyko-rzystujące składnię PHP.

• Nie wymaga skomplikowanej konfigura-cji serwera HTTP (Apache).

• Umożliwia tworzenie nowoczesnych ser-wisów działających w oparciu o techno-logię AJAX.

• Posiada zintegrowany interfejs dostępu do baz danych.

• Dostępny na elastycznej licencji MIT.

Listing 1. Wynik mapowania relacyjnych danych na zagnieżdżone tablice asocjacyjne

Array (

[Topic] => Array (

[id] => 1

[forum_id] => 1

[topic_id] => 0

[title] => Nagłówek wiadomości

[author] => admin

[email] => [email protected]

[content] => Pierwsza wiadomość

[created_at] => 2007-01-30 22:33:27

) [Forum] => Array (

[id] => 1

[name] => Forum testowe

[description] => Testujemy CakePHP

) [Comments] => Array (

[0] => Array (

[id] => 2

[forum_id] => 1

[topic_id] => 1

[title] => Pierwsza odpowiedź

[author] => user

[email] => [email protected]

[content] => Pierwsza odpowiedź na pierwszą wiadomość

[created_at] => 2007-01-30 22:35:04

) [1] => Array (

[id] => 3

[forum_id] => 1

[topic_id] => 1

[title] => Druga odpowiedź

[author] => user

[email] => [email protected]

[content] => Druga odpowiedź na pierwszą wiadomość

[created_at] => 2007-01-30 22:35:50

)

)

)

3/2007

Narzędzia

42

Do poszczególnych danych odwołujemy się poprzez indeks nazwy klasy (Message) i wła-ściwej kolumny (title). Jeżeli zapytanie zwróci więcej niż jeden rekord dostęp do danych bę-dzie możliwy przez dodatkowy indeks będący numerem rekordu:

$dat = $this->Message->findAll();

foreach ($dat as $no => $row){

echo "rekord ${no}: " .

$row['Message']['title'];

}

Pozostałe komponentyPrzy opisie architektury MVC mówiliśmy także o komponentach View i Controller. View jest odpo-wiedzialny za zbudowanie struktury informacji,

która zostanie wyświetlona w przeglądarce użyt-kownika. W praktyce jest to szablon wyglądu stro-ny, który będzie wypełniony danymi. Może to być fragment kodu HTML wyświetlający pewien nie-zmienny tekst lub dynamiczna treść generowana w przez odpowiednie funkcje kontrolera. To wła-śnie dzięki tej właściwości możliwe jest wyświetla-nie dynamicznie zmieniających się danych. Ponadto możemy posługiwać się gotowymi modułami (ang. helpers) ułatwiającymi formatowanie danych i two-rzenie kodu HTML, Javascript itp. Wiele takich mo-dułów jest dostarczanych razem z CakePHP. Frag-ment kodu szablonu wyświetlającego wiersze z ta-beli messages:

<h2>

Tytuły wiadomości z tabeli

<i>messages</i>

</h2>

<ol>

<?php foreach ($dat as $row): ?>

<li>

<?php

echo $row['Message']['title'];

?>

</li>

<?php endforeach; ?>

</ol>

Kontroler to serce aplikacji odpowiedzialne za logikę biznesową, interakcję z użytkowni-kiem, modelem i widokiem. Oprócz opisane-go wcześniej rutingu (decydowania w jaki spo-sób będą obsługiwane adresy WWW) kontro-ler odpowiada także za:

• zarządzanie cache wyświetlanych stron,• walidację danych,• zarządzanie modułami rozszerzającymi

funkcjonalność CakePHP,• tworzenie logu zdarzeń.• Specyficznymi dla CakePHP są następują-

ce komponenty:• Helpers - klasy grupujące funkcje wykorzysty-

wane przy formatowaniu danych wyświetla-nych w widokach (Ajax, Html, Javascript itp),

• Components - niewielkie fragmenty ko-du zawierającego logikę biznesową, które mogą być współdzielone między różnymi aplikacjami.

Instalacja i UruchomienieKorzystanie z CakePHP wymaga spełnienia po-niższych wymagań:

• serwer HTTP obsługujący sesje i mod_rewrite (np. Apache),

• PHP w wersji 4.3.2 lub nowszej (PHP5 jest obsługiwany),

• baza danych (MySQL, PostgreSQL, SQLi-te lub inne, dostępne poprzez interfejs ADOdb).

Do poprawnego funkcjonowania CakePHP w środowisku PHP4 interpreter języka musi być skompilowany z opcją --enable-overload. Począwszy od PHP 4.3.0 opcja ta jest domyśl-nie włączona.

Najnowsze wersje CakePHPStabilne wersje kodu są dostępne na stro-nach WWW projektu, pod adresem http://cakeforge.org/projects/cakephp/. Możliwe jest tak-że pobranie najnowszych wersji developerskich bezpośrednio z repozytorium SVN znajdujące-go się pod adresem https://svn.cakephp.org/repo/trunk/cake/.

Instalacja pakietu sprowadza się do pobra-nia odpowiedniego archiwum ze strony WWW projektu (szczegóły w ramce Najnowsze wersje CakePHP) i rozpakowania w wybranym katalo-

Listing 2. Struktura katalogów po instalacji CakePHP

./katalog_cakephp

/app <-- MVC tworzonej aplikacji

/config <-- konfiguracja routera i połączeń do baz danych

/controllers <-- kontrolery

/components <-- komponenty współdzielone między różnymi aplikacjami

/models <-- modele danych

/plugins <-- pakiety rozszerzające CakePHP

/tmp <-- pliki tymczasowe aplikacji (logi, sesje itp.)

/vendors <-- pakiety nie należące do CfakePHP (lokalne dla aplikacji)

/views <-- szablony stron WWW

/elements <-- szablony wyświetlające niewielkie, powtarzające się porcje danych

/helpers <-- klasy grupujące funkcje wykorzystywane przy formatowaniu danych

/layouts <-- szablony wyglądu serwisu (definiujące styl wyglądu serwisu)

/webroot <-- główny katalog aplikacji - dostępny przez serwer WWW (dostęp do

pozostałych katalogów został zablokowany przez pliki .htaccess)

/css <-- szablony CSS

/images <-- grafika strony WWW

index.php <-- punkt wejścia do aplikacji

index.php

/cake <-- biblioteki systemowe

/docs <-- pliki README

/vendors <-- pakiety nie należące do CakePHP (globalne)

index.php

VERSION.txt <-- informacje o wersji pakietu

Listing 3. Przykładowy fragment kodu kontrolera funkcjonalności forum

class ForumsController extends AppController{

// jeżeli używamy PHP4 musimy zdefiniować zmienną zawierającą nazwę klasy

var $name = 'Forums';

// definiujemy, który szablon serwisu będziemy wykorzystywali

// wpis możemy pominąć jeżeli korzystamy ze standardowego szablonu

var $layout = 'default';

// definiujemy, które klasy pomocnicze mają być dostępne w widokach

// związanych z kontrolerem

var $helpers = array('Html', 'Time');

// definiujemy, które modele danych będziemy wykorzystywać

var $uses = array('Forum','Topic','Comment');

function index() {

// pobierz listę wszystkich działów wraz z informacjami

// o tematach dyskusji; przekaż zmienną $forums do widoku

$forums = $this->Forum->findAll();

$this->set('forums', $forums);

}

3/2007

Narzędzia

44

gu na serwerze. Przyjrzyjmy się strukturze po-wstałych plików i katalogów (Listing 2) :

• app/ – Miejsce na kod naszej aplikacji i towa-rzyszące jej moduły zewnętrzne. Poszczegól-ne komponenty architektury MVC będzie-my umieszczali w podkatalogach controllers/, models/, views/. Pliki graficzne, szablony CSS oraz inne dane dostępne bezpośrednio przez przeglądarkę będziemy umieszczali w katalo-gu webroot/ (katalog ten będzie udostępniany przez Apache). Kod nie będący komponen-tem MVC naszej aplikacji będziemy umiesz-czali w katalogu vendors/.

• app/config/ – znajdują się tutaj pliki konfiguru-jące CakePHP (w kontekście naszej aplikacji).

Przydatne opcje omówimy w dalszej części ar-tykułu, przy okazji tworzenia aplikacji obsłu-gującej internetowego forum :

• app/controllers/ – Kontroler aplikacji to plik z rozszerzeniem php zawierający definicję kla-sy i metod (akcji) sterujących logiką bizneso-wą aplikacji (Listing 3). Najczęściej każdy ze zdefiniowanych kontrolerów zarządza dany-mi z jednego modelu.

• app/controllers/components/ – Komponen-ty to fragmenty kodu wspólne dla wielu aplikacji (takim komponentem może być np. moduł zarządzania kontami użytkow-ników lub moduł obsługujący wysyłanie poczty email). Komponenty wbudowane w CakePHP są umieszczone w katalogu cake/libs/controller/components/.

• app/models/ – Model to plik z rozszerzeniem php zawierający definicję klasy i metod ope-rujących na danych. Najczęściej model od-nosi się do pojedynczej tabeli w bazie danych umożliwiając pobieranie, zapisywanie i wali-dację zawartych w niej informacji (Listing 4).

• app/views/ – Widoki to pliki z rozszerze-niem thtml zawierające szablon wyglądu da-nych związanych z konkretną metodą (akcją) kontrolera. Jeżeli mamy kontroler dedykowa-ny obsłudze forum (ForumsController), w któ-rym znajduje się metoda view() to właściwą dla tej akcji lokalizacją widoku jest plik app/views/forums/view.thtml.

• app/views/layouts/ – Elementy wspólne dla wyglądu strony WWW (np. nagłówek i stopkę, która jest niezmienna dla całej aplikacji) możemy zdefiniować w szablo-nach serwisu. To jakby kontener w któ-rym będą wyświetlane pozostałe wido-ki związane z poszczególnymi funkcjami aplikacji. Każdy szablon to plik z rozsze-rzeniem thtml (domyślny szablon nazywa się default.thtml). Przykładowe szablony znajdują się na Listingach 5. i 6.

• app/views/elements/ – W wielu aplikacjach znajdują się bloki informacji, które muszą być wyświetlane na wielu stronach. Takie frag-menty aplikacji (formularze, przyciski nawi-

gacyjne, dodatkowe menu itp.) mogą być wy-dzielone ze struktury widoku. Dzięki temu nie będziemy niepotrzebnie powielali kodu (DRY!). Innymi słowy elementy to mini-wi-doki, które mogą być dynamicznie dołącza-ne do widoków i tak jak one operować na da-nych dostarczonych przez pozostałe kompo-nenty aplikacji. Każdy element to plik z roz-szerzeniem thtml. Przykładowy kod znajduje się na Listingu 7.

• app/views/helpers/ – Bardzo często przed wy-świetleniem w widoku dane muszą być od-powiednio prze formatowane. Nie jest to za-

danie dla modelu danych gdyż często będzie-my chcieli by te same dane były wyświetla-ne w różny sposób. Modyfikacje powinny być przeprowadzane w kontrolerze o ile do-tyczą one zmiany struktury danych. Proste modyfikacje formatu (np. zmiana sposobu wyświetlania liczb lub formatu daty) mogą być przeprowadzane w widokach - za pomo-cą funkcji zdefiniowanych w obiektach kla-sy Helper. Każdy helper to plik php zawierają-cy definicję klasy i funkcji, które mają być do-stępne w widokach. Przykładowy kod znaj-duje się na Listingu 8.

Listing 4. Przykładowy kod modelu zarządzający danymi o tematach forum (ang. topic)

// jeżeli używamy PHP4 musimy zdefiniować zmienną zawierającą nazwę klasy

var $name = 'Topic';

// domyślnie nazwa klasy odpowiada nazwie tabeli w liczbie pojedynczej

// jeżeli jest inaczej i np. zamierzamy wykorzystać inną tabelę z bazy danych

// musimy zdefiniować jej nazwę

var $useTable = 'messages';

// definiujemy zasady walidacji danych w określonych kolumnach tabeli

var $validate = array('author' => VALID_NOT_EMPTY, 'email' => VALID_EMAIL,

'title' => VALID_NOT_EMPTY, 'content' => VALID_NOT_EMPTY);

// definiujemy relacje między modelem Topic i innymi modelami aplikacji

// ... każdy temat "należy" do forum

var $belongsTo = array( 'Forum' => array('className' => 'Forum'));

// ... na każdy temat forum może być wiele odpowiedzi/komentarzy

var $hasMany = array('Comments' => array('className' => 'Comment',

'foreignKey' => 'topic_id', 'conditions' => 'Comments.topic_id<>0' ) );

}

Listing 5. Przykładowy plik szablonu serwisu wyświetlającego danej jako HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<title><?php echo $title_for_layout?></title>

<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">

</head>

<body>

<!-- Nagłówek strony -->

<div id="header">...</div>

<!-- W tym miejscu będą wyświetlane poszczególne widoki aplikacji -->

<?php echo $content_for_layout ?>

<!-- Stopka strony -->

<div id="footer">...</div>

</body>

</html>

Listing 6. Przykładowy plik szablonu serwisu wyświetlającego dane jako XML

<?php header('Content-type: text/xml'); ?>

<!-- W tym miejscu będą wyświetlane poszczególne widoki aplikacji -->

<?php echo $content_for_layout; ?>

Listing 7. Przykładowy kod wydzielony z aplikacji jako powtarzający się element

<?php

$today = date ("Y-m-d", time()); // YYYY-MM-DD

// dzień roku liczony od 1 do 366

$day_of_year = intval (date ('z',time())) + 1;

?>

<h2><?php echo "$today&nbsp;::&nbsp;$day_of_year dzień roku"; ?></h2>

3/2007

Narzędzia

46

• app/webroot/ – Główny katalog naszej apli-kacji do którego możliwy jest dostęp z in-ternetu (dostęp do pozostałych katalogów jest zablokowany przez pliki .htaccess).

• cake/ – Katalog zawiera biblioteki niezbędne do funkcjonowania pakietu oraz moduły uła-twiające programistom tworzenie własnych aplikacji. Znajdziemy tutaj między innymi te-stową aplikację (pages), która została wbudo-wana w CakePHP, domyślne szablony stron (katalog cake/libs/views/layouts/) oraz pozosta-łe pliki Helpers (Ajax, Cache, Form, Html, Java-script, Number, Session, Text, Time) i Components (Acl, Request_handler, Security, Session).

Konfiguracja i uruchomienieCakePHP został tak przygotowany by był go-towy do uruchomienia bez żadnych dodatko-wych zabiegów. Wystarczy konfiguracja serwe-ra HTTP (dla serwera Apache plik konfiguracyj-ny nazywa się httpd.conf):

• ustawienie DocumentRoot na katalog app/webroot/,

• ustawienie AllowOverride All dla właści-wych katalogów,

• upewnienie się, że mod_rewrite jest inicja-lizowany poprawnie,

• jeżeli CakePHP został zainstalowany na prywatnym koncie (np.: http://serwer.com/~mojekonto/) konieczne jest zmodyfikowa-nie pliku app/webroot/.htaccess przez doda-nie linii RewriteBase /~mojekonto/.

Przykładowa konfiguracja Apache 1.3.X znajdu-je się na Listingu 9. Do sprawdzenia czy instala-cja CakePHP zakończyła się sukcesem wystarczy uruchomić Apache (zmiany wprowadzone w kon-figuracji będą widoczne dopiero po ponownym uruchomieniu serwera), uruchomić przeglądar-kę internetową i wpisać adres IP serwera. Jeżeli in-stalowaliśmy pakiet lokalnie to powinniśmy wpi-sać adres http://127.0.0.1/ (lub http://cake.forum/). Nie musimy pisać żadnej aplikacji testowej - zosta-ła ona wbudowana we framework przez twórców CakePHP! Rezultat jaki powinniśmy zobaczyć jest przedstawiony na Rysunku 2.

Konwencja ponad konfiguracjęZanim spróbujemy napisać pierwszą aplikację musimy najpierw zaznajomić się z zasadami na-zywania plików, klas i modułów. Przyzwyczaili-

śmy się, że język PHP nie narzuca żadnego stan-dardu w tym zakresie a tymczasem CakePHP wy-maga by stosować się do pewnych reguł. Ma to na celu nie tylko wprowadzenie porządku ale przede wszystkim umożliwia automatyczne funkcjono-wanie framework'u bez skomplikowanej konfigu-racji. Na przykład CakePHP zakłada, że nazwy ta-bel w bazie danych są zapisane z małej litery i za-wierają znak podkreślenia _ w miejscu spacji. Do-datkowo nazwy tabel powinny być w liczbie mno-giej (bez polskich znaków) np.: messages, forums, user_accounts. Dzięki tym zasadom możliwa jest np. automatyczna zamiana nazwy tabeli w ba-zie danych na nazwę pliku zawierającego definicję modelu. Model :

• nazwa klasy jest w liczbie pojedynczej, bez spacji między wyrazami, każdy wyraz jest za-pisany z dużej litery (Forum, UserAccount).

• nazwa pliku jest zapisana małymi litera-mi, ze znakiem podkreślenia _ zamiast spacji (np.: forum.php, user_account.php).

• nazwa tabeli w bazie danych jest w liczbie mnogiej, zapisana małymi literami ze zna-kiem podkreślenia _ zamiast spacji.

• podstawowy klucz tabeli (ang. primary key) musi nazywać się id.

• nazwy kluczy obcych (ang. foreign key) mu-szą pasować do szablonu "nazwa tabeli

obcej w liczbie pojedynczej" _ id (np.: tabela messages może zawierać klucz obcy forum _ id).

Widok :

• nazwa pliku jest taka sama jak nazwa me-tody kontrolera, którą widok obsługuje (jeżeli mamy kontroler dedykowany ob-słudze forum - ForumsController, w któ-rym znajduje się metoda view() to właści-wą dla tej akcji lokalizacją widoku jest plik app/views/forums/view.thtml).

• nazwa pliku jest zapisana małymi literami, ze znakiem podkreślenia _ zamiast spacji.

Kontroler :

• nazwa klasy jest w liczbie mnogiej, bez spacji między wyrazami, każdy wyraz jest zapisany z dużej litery.

• nazwa klasy kończy się słowem Controller (np.: ForumsController, UserAccountsCon-troller),

• nazwa pliku jest zapisana małymi literami, ze znakiem podkreślenia _ zamiast spacji,

• nazwa pliku kończy się słowem controller (np.: forums_controller.php, topics_control-ler.php, user_accounts_controller.php).

Liczba pojedyncza, liczba mnogaJedynym powodem dla którego CakePHP musi umieć odmieniać nazwy klas przez liczby jest intuicyjność. Mówimy przecież wiadomość na-leży do forum oraz wiadomość ma wiele ko-mentarzy. Pomaga to zrozumieć zależność między obiektami oraz krotność relacji.Lista słów o nieregularnej odmianie znajduje się w pliku cake/libs/inflector.php.

Listing 8. Przykładowy plik pomocniczy zawierający funkcję tworzącą link

class linkHelper extends Helper {

function makeLink($title, $url) {

// kod umożliwiający utworzenie linku na podstawie

// przekazanych argumentów

}

}

Listing 9. Przykładowa konfiguracja wirtualnego serwera Apache obsługującego framework CakePHP

NameVirtualHost *

<VirtualHost *>

ServerAdmin admin@home

ServerName cake.forum

# katalog w którym znajdują się publiczne pliki naszej aplikacji

DocumentRoot /www/forum/app/webroot

<Directory /www/forum/app/webroot>

DirectoryIndex index.php index.html

Options FollowSymLinks

Options +Includes

# obsługa plików .htaccess

AllowOverride All

# zezwalamy tylko na połączenia z naszego komputera i lokalnej podsieci

# w miejsce 10.0.0. należy wpisać własną podsieć

Order deny,allow

Deny from all

Allow from 127.0.0.1 10.0.0.

</Directory>

# pliki logów Apache będą zapisywane w katalogu aplikacji

ErrorLog /www/forum/app/tmp/logs/error_log

CustomLog /www/forum/app/tmp/logs/access_log combined

</VirtualHost>

CakePHP

www.phpsolmag.org 47

Helper :

• nazwa klasy jest w liczbie pojedynczej, bez spacji między wyrazami, każdy wyraz jest zapisany z dużej litery,

• nazwa klasy kończy się słowem Helper (np. LinkHelper),

• nazwa pliku jest zapisana małymi literami, ze znakiem podkreślenia _ zamiast spacji,

• nazwa pliku kończy się słowem helper (np. app/views/helpers/link_helper.php).

Pierwsza aplikacjaUruchomienie testowej aplikacji nie było trud-ne i nie wymagało od nas znajomości frame-work'u. Teraz spróbujemy napisać prostą apli-kację wyświetlającą krótki tekst w przeglądar-ce internetowej. Jak wiemy z wcześniejszego opisu - CakePHP działa w oparciu o architek-turę MVC. Oznacza to iż framework pobiera żądania z przeglądarki WWW, dekoduje by znaleźć kontroler i wywołuje właściwą akcję (metodę) w nim zawartą. Kontroler zatroszczy się o powiązanie modelu danych z widokiem i wysłanie rezultatu do przeglądarki użytkow-nika. Dobra wiadomość jest taka, że zadania te są w większości realizowane automatycz-nie. Nie musimy się skupiać na technicznych aspektach funkcjonowania CakePHP - może-my zająć się funkcjonalnością tworzonej apli-kacji. Ponieważ w naszym przykładzie nie bę-dziemy operowali na danych tak więc model będzie maksymalnie uproszczony. Plik app/models/display.php :

class Display extends AppModel {

// jeżeli używamy PHP4 musimy

// zdefiniować zmienną zawierającą

// nazwę klasy

var $name = 'Display';

// nie korzystamy z bazy danych

var $useTable = false;

}

Teraz zajmiemy się kontrolerem. Plik app/controllers/display_controller.php :

class DisplayController extends

AppController {

// jeżeli używamy PHP4 musimy zdefiniować

// zmienną zawierającą nazwę klasy

var $name = 'Display';

// definiujemy, które modele danych

// będziemy wykorzystywać

var $uses = array('Display');

// definiujemy, które klasy pomocnicze

// mają być dostępne w widokach

// związanych z kontrolerem

var $helpers = array('Time');

}

Powyższa klasa nie zawiera akcji obsługu-jących żądania napływające z przeglądarki WWW. Zanim przystąpimy do ich utworze-

nia musimy sobie przypomnieć opis zadań głównego modułu CakePHP - routera. Ro-uter pobiera adres WWW i mapuje go na na-zwę kontrolera i akcje za pomocą prostych re-guł zdefiniowanych w pliku konfiguracyjnym app/config/routes.php. Działanie to zostało zi-lustrowane na Rysunku 3. Wróćmy teraz do naszego kontrolera i zdefiniujmy dwie metody.

class DisplayController extends

AppController {

// jeżeli używamy PHP4 musimy zdefiniować

// zmienną zawierającą nazwę klasy

var $name = 'Display';

// definiujemy, które modele danych

// będziemy wykorzystywać

var $uses = array('Display');

// definiujemy, które klasy pomocnicze

// mają być dostępne w widokach

// związanych z kontrolerem

var $helpers = array('Time');

function hello() {

}

function goodbye() {

}

}

Jak widać metody nie zawierają kodu gdyż wy-świetlany tekst będzie statyczny i umieszczo-ny w widoku powiązanym z odpowiednią ak-cją. Dokończmy nasz przykład i utwórzmy brakujące pliki w katalogu app/views/display/. Plik app/views/display/hello.thtml :

<h1>Witaj!</h1>

<p>Właśnie wywołałeś/-aś akcję hello.</p>

Relacje między modelamiJedną z najistotniejszych możliwości CakePHP jest tworzenie relacji między modelami. Takie pół-wiązania powodują, że zapytanie o dane z jednego modelu powoduje automatyczne pobranie do-datkowych informacji z innych modeli. Weźmy na przykład relację między forum i tematami dys-kusji. Intuicyjnie rozumiemy, że każde forum ma wiele tematów dyskusji (każdy temat dyskusji na-leży do forum). Jeżeli zdefiniujemy taką relację to zapytanie o dowolne forum zwróci (oprócz infor-macji o forum) także listę należących do niego tematów dyskusji. Tak samo relacja zadziała w dru-gą stronę. Zapytanie o temat dyskusji zwróci dodatkowe informacje o powiązanym z nim forum.Framework pozwala na zdefiniowanie czterech rodzajów relacji:

• hasOne (pol. ma jeden) – relacja 1:1,• hasMany (pol. ma wiele) – relacja 1:n,• hasAnsBelongsToMany (pol. ma i należy do wielu) – relacja n:m,• belongsTo (pol. należy do) – relacja 1:1 będąca uzupełnieniem relacji hasMany.

Pobieranie dodatkowych informacji nie zawsze jest pożądane. Czasem chcemy po prostu odczy-tać dane z pojedynczego modelu. Na szczęście CakePHP umożliwia określenie jak głęboko ma działać przeszukiwanie powiązań (do ich zablokowania wystarczy w modelu zdefiniować zmienną $recursive i nadać jej wartość -1).

Rysunek 2. Aplikacja testowa wbudowana w CakePHP

3/2007

Narzędzia

48

Plik app/views/display/goodbye.thtml :

<h1>Żegnaj!</h1>

<p>Właśnie wywołałeś/-aś akcję goodbye.</p>

Możemy teraz przetestować zachowanie apli-kacji modyfikując adres WWW w przeglą-darce internetowej. Wpiszemy na przykład adres http://127.0.0.1/display/hello. Rezultat ja-ki powinniśmy zobaczyć jest przedstawio-ny na Rysunku 4. Wyświetlona strona WWW jest statyczna i przez to mało atrakcyjna. Może-my to szybko zmienić dołączając do szablonu kod PHP, który będzie wyświetlał dynamicz-nie zmieniające się dane. Zmodyfikujmy nasz przykład by oprócz tekstu wyświetlał także ak-tualną datę i czas. Plik app/controllers/display_controller.php :

...

function hello() {

// zmień tytuł okienka przeglądarki

$this->pageTitle = 'CakePHP';

// przekaż zmienną datetime do widoku

$this->set("datetime",

Najpopularniejsze funkcje CakePHPCakePHP zawiera wiele przydatnych klas, które ułatwiają pisanie aplikacji interneto-wych. Poniżej znajduje się, krótka lista naj-częściej wykorzystywanych funkcji.

Kontroler (klasa AppController)Przekazywanie zmiennych do widoku:

set($var, $value);

render($action, $layout, $file);

Przekierowanie do innego adresu:

redirect($url);

flash($message, $url, $pause);

Walidacja danych:

validateErrors();

validate();

Model (klasa AppModel)Pobieranie danych:

find($conditions, $fields, $order,

$recursive);

findAll($conditions, $fields, $order,

$limit, $page, $recursive);

findAllBy<fieldName>($value);

Operacje na danych:

del($id, $cascade);

save($model, $validate);

query($sql);

execute($query);

WidokDołączanie elementów do widoku:

renderElement($element, $args)

Rysunek 3. Adres WWW zostanie zamieniony na kontroler i akcję

Listing 10. Plik app/controllers/messages_controller .php

function add($forum_id=null, $topic_id=null) {

$rc = false;

if (!empty($this->data)) {

// zapis danych z formularza

$data = $this->data;

$data['Message']['author'] = strip_tags($data['Message']['author']);

$data['Message']['title'] = strip_tags($data['Message']['title']);

$data['Message']['content'] = strip_tags($data['Message']['content']);

$data['Message']['created_at'] = date('Y-m-d H:i:s', time());

$topic_id = $data['Message']['topic_id'];

$forum_id = $data['Message']['forum_id'];

$rc = $this->Message->save($data['Message']);

}

if (empty($this->data) || (!$rc)) {

// wyświetl formularz z ewentualnymi komunikatami walidatora

$this->Forum->recursive = -1;

$forum = $this->Forum->findById(intval($forum_id));

if (!empty($forum)) $this->set('forum', $forum['Forum']);

$this->Topic->recursive = -1;

$topic = $this->Topic->findById(intval($topic_id));

if (!empty($topic)) $this->set('topic', $topic['Topic']);

}

if ($rc) {

// jeżeli zapis rekordu powiódł przejdź do następnej strony

$this->flash('Dane zostały zapisane!', ($topic_id)?

'/forums/view/'.$forum_id: '/messages/view/'.$topic_id, 1);

exit();

}

}

3/2007

Narzędzia

50

date('r', time()));

}

...

Plik app/views/display/hello.thtml :

<h1>Witaj!</h1>

<p>Właśnie wywołałeś/-aś akcję hello.</p>

<h2>Data zdarzenia:

<?php

echo $time->niceShort($datetime);

?>

</h2>

Jeżeli teraz odświeżymy stronę wyświetlaną w przeglądarce WWW to zmiany powinny być od razu widoczne (Rysunek 5.). W jaki sposób informacje o dacie zostały przekazane z kontro-lera do widoku? W CakePHP nie ma automa-tycznego mechanizmu powodującego udostęp-nianie zmiennych zainicjowanych w kontrole-rze. Każdorazowo musimy korzystać z meto-dy $this->set(). Dlaczego jednak umieścili-śmy kod w kontrolerze a nie bezpośrednio w wi-doku? Z prostej przyczyny. Takie rozwiązanie jest bardziej elastyczne (i zgodne z architekturą MVC). W widokach powinniśmy co najwyżej modyfikować format danych. Dzięki temu unik-niemy powielania kodu i zgrupujemy funkcje za-rządzające danymi w kontrolerze (DRY!). Można powiedzieć, że poznaliśmy zasady rządzące Cake-PHP. Pora na zbudowanie aplikacji, która będzie wykorzystywała kluczowe zalety framework'u.

Tworzymy forum internetoweW poprzedniej części artykułu zajmowaliśmy się testowaniem framework'u i utworzeniem prostej aplikacji ilustrującej zasady funkcjonowa-nia CakePHP. Teraz zbudujemy proste forum in-ternetowe i w praktyce wykorzystamy wiele za-awansowanych rozwiązań takich jak tworzenie modeli danych i relacji między nimi, pobieranie, walidację i zapisywanie informacji do bazy da-nych czy obsługę formularzy WWW.

Oczywiście nie należy oczekiwać, że stworzy-my konkurencję dla phpBB lub innych, zaawan-sowanych systemów. Parafrazując znane powie-dzenie można stwierdzić, że forum jakie jest, każdy wie. Zakładamy, że użytkownicy będą odwiedzali nasz serwis by przeglądać poszcze-gólne działy w poszukiwaniu interesujących te-matów. Po ich znalezieniu będą mogli wyświe-tlić dostępne komentarze i zabrać głos w dysku-sji. Niewiele ale pamiętajmy, że tak jak poprzed-nie przykłady kod ma nam służyć do ilustracji zagadnień związanych z CakePHP.

Utworzymy dwie, dynamicznie generowane strony WWW zawierające odpowiednio spis działów oraz listę i tematów i komentarzy. Dru-gą stronę wyposażymy dodatkowo w przycisk tworzenia nowego wątku dyskusji oraz przycisk powrotu do pierwszej strony. Do przechowywa-nia informacji wprowadzanych przez użytkow-ników wykorzystamy bazę danych MySQL.

Rysunek 5. Działanie aplikacji wyświetlającej dynamicznie zmieniającą się datę

Rysunek 4. Działanie aplikacji wyświetlającej tekst w przeglądarce internetowej

Rysunek 6. Połączenie tabel topics i comments do wspólnej tabeli messages

Model danychW bazie danych będziemy przechowywali informa-cje o działach forum, tematach dyskusji i komenta-rzach jednak zamiast trzech tabel utworzymy tylko dwie. Część danych jest wspólna (pierwsza wiado-mość w wątku dyskusji określa jej temat) i może być umieszczona we wspólnej tablicy messages (Rysunek 6). Ostatecznie utworzymy dwie tabele (forums, messages) oraz cztery modele (Forum, Topic, Com-ment i Message). Model danych jest przedstawiony na Rysunku 7. Relacje między poszczególnymi mo-delami możemy opisać w następujący sposób:

• forum ma wiele tematów dyskusji,• temat dyskusji należy do forum,• wątek dyskusji ma wiele komentarzy/

odpowiedzi,• komentarze należą do wątku dyskusji,• wiadomość (temat lub komentarz) należy

do forum.

Zdefiniowanie powyższych powiązań umożli-wi nam automatyczne pobranie dodatkowych informacji podczas przeszukiwania bazy da-nych (patrz ramka obok). Do opisu relacji w CakePHP są używane tablice asocjacyjne przy-pisywane do zmiennych o nazwach odpowia-dających rodzajowi relacji. Powiązanie między modelami Forum i Topic musimy zapisać za po-mocą tablicy $hasMany zawierającej parametry relacji. Plik app/models/forum.php :

class Forum extends AppModel {

var $hasMany = array(

'Topics' => array(

'className' => 'Topic',

'foreignKey' => 'forum_id',

// tematy dyskusji (w odróżnieniu

// od komentarzy) mają topic_id =0

'conditions' => "Topics.topic_id

is null",

3/2007

Narzędzia

52

CakePHP

www.phpsolmag.org 53

'order' => 'Topics.created_at' )

)

...

}

Musimy pamiętać by w drugiej klasie (Topic) dodać relację $belongsTo. Jeżeli o tym zapo-mnimy otrzymamy powiązanie działające tyl-ko w jedną stronę (Forum -> Topic) co może prowadzić do powstawania błędów w aplika-cji. Plik app/models/topic.php :

class Topic extends AppModel {

var $belongsTo = array(

'Forum' => array(

'className' => 'Forum',

'foreignKey' => 'forum_id')

);

...

}

Powyższe reguły tworzenia relacji dotyczą oczywiście wszystkich modeli, które utworzy-liśmy. Przykładowy wynik zapytania uwzględ-niającego relacje między modelami Forum, To-pic i Comment znajduje się na Listingu 1.

Pobieranie danychZ wcześniejszych opisów, że nasza aplikacji, jak każda aplikacja MVC, potrzebuje kontrolera, który skoordynuje wszystkie komponenty i umożliwi wy-świetlanie danych w przeglądarce internetowej. Na początek utworzymy kontroler wyświetlający listę dostępnych działów w forum ForumsController). Plik app/controllers/forums_controller .php

function index() {

$forums = $this->Forum->findAll();

$this->set('forums', $forums);

}

W kodzie wykorzystaliśmy wbudowaną w każ-dy model funkcję AppModel::findAll() zwraca-jącą pobrane dane w formie tablic asocjacyjnych. Dzięki relacjom między modelami Forum oraz To-pic zapytanie zwróci informacje o forum i tema-tach dyskusji. W bardzo podobny sposób zrealizu-jemy wyświetlanie informacji o tematach dyskusji należących do określonego forum. Mając do dyspo-zycji kolumnę forums.id możemy zawęzić zapyta-nie do bazy danych za pomocą funkcji AppModel::findById($id) wyszukującej tylko rekordy pa-sujące do przekazanego parametru. Zabezpiecze-

Konfiguracja dostępu do bazy danychCakePHP umożliwia trzymanie informacji o połączeniu do bazy danych (adres IP serwe-ra, login, hasło) w pliku konfiguracyjnym app/config/database.php. Możemy zdefiniować wiele połączeń do różnych baz danych (np. niezależne połączenia do bazy zawierającej dane testowe i dane produkcyjne).

Rysunek 7. Fizyczny model danych forum

niem przed błędnym (np. nie istniejącym) parame-trem jest funkcja AppController::redirect(), która w takim przypadku przekieruje przeglądar-kę użytkownika na listę działów. Plik app/controllers/forums_controller .php :

function view($id) {

$forum = $this->Forum->findById($id);

if (empty($forum)) {

$this->redirect('/forums/index');

exit();

}

$this->set('topics', $forum['Topics']);

$this->set('forum', $forum['Forum']);

}

Drugi z kontrolerów, który zbudujemy bę-dzie przetwarzał informacje dotyczące ko-mentarzy należących do wątku dyskusji (MessagesController). Tym razem wykorzy-stamy funkcję AppModel::find(), która zwra-ca tylko jeden rekord z bazy danych (temat

Rysunek 8. Przykładowe komunikaty wyświetlane przy błędnym wypełnieniu formularza dodawania komentarzy

3/2007

Narzędzia

52

CakePHP

www.phpsolmag.org 53

Rysunek 9. Forum internetowe działające w oparciu o CakePHP

dyskusji). Podobnie jak w poprzednim przy-padku relacje między modelami spowodu-ją, że CakePHP automatycznie dołączy wła-ściwe komentarze. Nie możemy tutaj użyć funkcji AppModel::findById($id) ponieważ w tej samej tabeli mamy rekordy modelu To-pic oraz Comment – rozróżniamy je dzięki drugiemu parametrowi funkcji (Topic.to-pic _ id=0). Plik app/controllers/messages_con-troller .php :

function view($id) {

$topic = $this->Topic->find(array(

'Topic.id'=>$id, 'Topic.

topic_id'=>0));

if (empty($topic)) {

$this->redirect('/forums/index');

exit();

}

$this->set('topic', $topic);

}

Zapisywanie danychKontroler MessagesController pełni jeszcze jedną istotną funkcję. Umożliwia zapisywane do bazy danych informacji wprowadzonych przez użyt-kowników. Ponieważ przetwarzanie takich da-nych jest potencjalnie niebezpieczne (użytkow-

nik może próbować przesłać dane, które zakłó-cą funkcjonowanie naszego forum) przed zapi-sem usuwane są wszystkie znaczniki HTML (funkcja strip_tags()) a same dane są podda-ne walidacji.

W funkcji MessagesController::add() nie znajdziemy reguł walidacji – są one zdefiniowa-ne w modelu danych Message. Ograniczenia są nakładane na poszczególne kolumny tabeli (zo-bacz też plik cake/libs/validators.php). Plik app/models/message.php :

var $validate = array(

'author' => VALID_NOT_EMPTY,

'email' => VALID_EMAIL,

'title' => VALID_NOT_EMPTY,

'content' => VALID_NOT_EMPTY

);

Funkcja AppModel::save() zapisze dane tyl-ko wtedy gdy zostaną spełnione wszystkie reguły walidacji. Dopełnieniem funkcji zapi-su jest funkcja tagErrorMsg() zawarta w kla-sie Html helper. W przypadku błędów walida-cji wyświetla informacje o przyczynie po-wstawania problemów. Na Rysunku 8., wi-dać przykładowe komunikaty wyświetlane przy błędnym wypełnieniu formularza do-

dawania komentarzy. Plik app/views/elements/message.thtml :

...

<tr>

<td align="right">Temat:&nbsp;</td>

<td align="left">

<?php echo $html->input('Message/

title',array('size'=>40)); ?>

<?php echo $html->tagErrorMsg(

'Message/title', 'Wpisz

temat');

?>

</td>

</tr>

...

Testowanie i podglądanie wartości zmiennychNasze forum jest już gotowe do publicznej pre-zentacji (Rysunek 9). Teraz przydadzą się nam sprawdzone sposoby na weryfikację funkcjono-wania naszej aplikacji. Niestety CakePHP nie zo-stał wyposażony w zintegrowane narzędzia do testów – sprawdźmy co dostajemy w zamian. Standardowa konfiguracja CakePHP ograni-cza możliwość testowania się do wyświetlania zmiennych za pomocą funkcji e() - echo(), debug() oraz AppController::log(). Zmiana trybu pracy framework'a jest możliwa poprzez modyfikację stałej DEBUG w pliku konfigura-cyjnym app/config/core.php. Pozwala to na okre-ślenie ilości informacji jakie CakePHP może wy-świetlać podczas działania aplikacji. Oczywiście zwiększanie poziomu szczegółowości raportów odbywa się kosztem wydajności. Dostępne po-ziomy szczegółowości raportów:

• produkcyjny (brak informacji o błędach),• developerski (ostrzeżenia i informacje o

błędach),• developerski2 (developerski + informacje

zapytaniach SQL),• developerski3 (developerski2 + zawartość

obiektu kontrolera).

PodsumowaniePrzedstawiona w artykule budowa CakePHP oraz przykłady wykorzystania framework'u po-kazują, że jest to elastyczne środowisko budo-wy aplikacji internetowych zarówno dla ama-torów jak i osób z większym doświadczeniem w programowaniu. Amatorzy prawdopodob-nie polubią CakePHP za prostotę i intuicyjność funkcjonowania – eksperci za modularną bu-dowę, łatwość rozbudowy i szybkość tworze-nia aplikacji.

PIOTR GAPIŃSKIAutor w wolnych chwilach zajmuje się programo-waniem w różnych językach (głównie Rebol, Ruby, PHP i AmigaE). Kontakt z autorem: [email protected]

W sieci

• http://www.cakephp.org – strona domowa projektu• http://cakeforge.org/projects/cakephp – repozytorium stabilnych wersji framework'u• http://groups.google.com/group/cake-php/ – lista dyskusyjna poświęcona CakePHP

3/2007

Narzędzia

54

JavaScript

www.phpsolmag.org 55

Wraz z rozwojem technologii interne-towych, doczekaliśmy się tak roz-budowanych narzędzi, że w dzi-

siejszych czasach tworzenie aplikacji interneto-wych z interfejsem podobnym do interfejsów systemów operacyjnych nie stanowi najmniej-szego problemu. Przyjrzyjmy się kilku najpopu-larniejszym i najciekawszym, moim zdaniem, zestawom skryptów oraz frameworkom uła-twiającym nam pracę z JavaScript.

DojoDojo Toolkit jest potężnym narzędziem, skrypt wraz ze wszystkimi bibliotekami zajmuje 5MB, jednak znajdziemy w nim dosłownie wszystko, od prostych bibliotek pozwalających na rozjaśnienia, płynne przesunięcia czy zmianę rozmiaru, do za-awansowanych, wspomnianych we wstępnie, dra-g&drop'ów, drzewek czy edytora WYSIWYG. Za-wiera on całą gamę elementów pozwalających na budowę interfejsu aplikacji: zakładki, okienka, ta-kie same jak te znane nam z Microsoft Windows. Elementy te zawierają się jednak w większości omawianych dzisiaj frameworków. Czym więc wyróżnia się Dojo? Dojo Toolkit wyróżnia się mo-dułem zwanym dojo.storage. Przeglądarki interne-towe pozawalają witrynom przechowywać dane po stronie użytkownika, różnego rodzaju infor-macje można przechowywać w cookies (do 4KB), w cache Flash Playera (do 100KB), userData Inter-

net Explorera (z limitem do 1MB), DOM Stora-ge (do 5MB) w MozilliFireFox. Programiści wpa-dli, więc na pomysł, aby wykorzystać te miejsca, do przechowywania danych, na których operuje użytkownik. Pozwala to na utworzenie aplikacji, która daje możliwość pracy w trybie offline, cał-kowicie bez dostępu do Internetu, w której syn-chronizacja danych odbywa się dopiero po uzy-skaniu połączenia. Na stronie domowej Dojo w dziale Demos znajduje się właśnie aplikacja, któ-ra demonstruje działanie tej biblioteki. Na pew-no warto na nią zwrócić uwagę tworząc wszel-kiego rodzaju systemy zarządzania treścią. Spró-bujmy w Dojo wykonać element drag&drop, czy-li coś, co umożliwi nam przenoszenie z jednego do drugiego elementu jakichś przedmiotów, sy-mulację naszego sklepu z koszykiem. Pobieramy dojo i do utworzonego przez nas folderu kopiu-jemy pliki dojo.js, iframe_history.htm oraz cały fol-der src – znajdują się w nim wszystkie biblioteki, które możemy załadować poprzez skrypt. Stwórz-my teraz przykładowy skrypt - jego treść zobaczyć można na pierwszym Listingu. (Wszystkie skryp-ty zamieszczone są również na płycie CD dołączo-nej do czasopisma). W treści strony deklarujemy 3 sekcje div. Jedną odpowiadającą za listę produk-tów, następną za koszyk oraz kolejną w celach te-stowych i aby, wyświetlała nam, jakie informacje należałoby np. zapisać w bazie danych, czyli status koszyka. Dojo posiada funkcję odpowiedzialną za ładowanie części swoich bibliotek, aby niepotrzeb-nie nie ładować całości, co zajmowałoby dość dużo pamięci i niepotrzebnie obciążałoby przeglądarkę. My potrzebujemy jedynie obiektu event oraz dnd. Za pośrednictwem pierwszego z nich obsługuje-my wydarzenia, czyli ładujemy skrypt po załado-

waniu strony oraz wywołujemy funkcję update. Drugi dostarcza nam funkcji odpowiedzialnych za znany z systemów operacyjnych drag&drop. Aby skrypt zadziałał poprawnie należy zdefinio-wać obiekty, które można upuszczać oraz obiekty, które można przenosić. Dojo pozwala na zdefinio-wanie kilku rodzajów elementów podnoszonych oraz elementów, które można upuszczać w odpo-wiednie miejsca. Uzyskujemy w ten sposób duże pole działania. Dojo, jako jedyny z opisywanych frameworków pozwala na asynchroniczny upload plików, czy też obsługę Ajaxowego wstecz. Jako minusy Dojo uznać musimy jego rozmiar oraz do-kumentację średniej jakości. Trzeba się dobrze na-gimnastykować, aby coś w niej odnaleźć. Plusy to z pewnością zakres bibliotek, jakie dostarcza oraz wspomniany dojo.storage.

PrototypePrototype jest jedynie frameworkiem, który możemy wykorzystać podczas budowy naszych bibliotek. Dostarcza on również wygodnego in-terfejsu do obsługi zapytań Ajaxowych i wyda-rzeń (eventów). Na Listingu 3. zaprezentowa-ny został przykład użycia tego interfejsu. Jako pierwszy parametr funkcji observe podajemy id elementu, do którego się odnosi, jako drugi akcję, po której ma zostać uruchomiona funk-cja z parametru trzeciego. Podstawowym i bar-dzo częstym błędem popełnianym przez pro-gramistów jest odwoływanie się do niezałado-wanych elementów DOM. Dodanie eventu ob-serve do myForm może nastąpić dopiero po je-go załadowaniu (wczytaniu przez przeglądar-kę całego kodu html). Można temu zaradzić na dwa sposoby. Pierwszy, mało elegancki, to usta-wienie eventu w treści strony, dopiero po zała-dowaniu formularza. Drugi sposób polega na pewnego rodzaju wstawieniu eventa w event. Listing 4. opisuje właśnie taki przykład. W mo-mencie załadowania obiektu window, czyli po wczytaniu strony, ustawiane są elementy odpo-wiedzialne za całą resztę zdarzeń – w tym przy-padku obsługę formularza. Prototype dostarcza

JavaScript

Sklep internetowy, na którym chwytamy produkt niczym folder w popularnym systemach operacyjnych i przesuwamy do koszyka, jakiś czas temu znajdował się jeszcze w sferze naszych marzeń. Rozwój technologii zdecydowanie ułatwił nam życie.

Co obiecujemy...l Nazuczysz się tworzyć skrypty z wykorzysta-

niem funkcji drag&drop w Dojo.l Dowiesz się jakie daje możliwości biblioteka

Scribus.aculo.us.

Co należy wiedzieć...l Powinieneś znać podstawy JavaScriptl Powinieneś umieć korzystać z gotowych roz-

wiązań typu Dojo

Poziom trudności

Biblioteki i frameworki

3/2007

Narzędzia

54

JavaScript

www.phpsolmag.org 55

Listing 1. Przykładowy skrypt drag&drop wykonany w dojo

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

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

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl"

lang="pl">

<head>

<title>

Dojo drag&amp;drop test

</title>

<script type="text/javascript" src="dojo.js"></script>

<script type="text/javascript">

// ładowanie bibliotek

// obiekt pozwalający nam obsłużyć eventy

dojo.require("dojo.event.*");

// drag & drop

dojo.require("dojo.dnd.*");

// w dojo przyjęło się używać tego aliasu funkcji

function byId(id) {

return document.getElementById(id);

}

function init() {

// pobranie diva z produktami

var products = dojo.byId("products");

// przygotowanie tablicy na obiekty dojo,

// zawierające dane o produktach

var product = new Array();

// pobranie diva z zamowieniami

var shoppingcart = dojo.byId("shoppingcart");

/* ustalamy miejsca gdzie można upuszczać elementy,

jako drugi parametr podaje się typy elementow,

jakie można upuscic */

new dojo.dnd.HtmlDropTarget(products,

["products"]);

new dojo.dnd.HtmlDropTarget(shoppingcart,

["products"]);

// pobieramy wszystkie elementy - produkty

var lis = products.getElementsByTagName("div");

for(var x=0; x<lis.length; x++) {

// ustalenie źródeł - elementów które można

// podnieść, i ich typów

product[x] = new dojo.dnd.HtmlDragSource(

lis[x], "products");

// dodanie wydażenia, po upuszczeniu elementu

// wywołuje funkcję update

dojo.event.connect(product[x], "onDragEnd",

"update");

}

}

/* Funkcja, która mówi nam ze cos sie stało. Docelowo

można nią obsłużyć zapis w bazie dotyczący zamówień

itp. */

function update(e){

byId('debug').innerHTML += "Wykonanie operacji na

bazie danych dotyczącej id:"

+ e.dragObject.domNode.id+"<br />";

}

dojo.event.connect(dojo, "loaded", "init");

</script>

<style type="text/css">

#products, #shoppingcart {

padding:5px; border: 1px solid blue;

height: 100px;

margin-bottom:10px;

}

#products div, #shoppingcart div {

cursor: pointer;

margin:5px;

}

#products div {

border: 1px solid green;

float: left;

width:90px;

height:90px;

}

#shoppingcart div{

border: 1px solid green;

}

</style>

</head>

<body>

<div id="products">

<div id="1">Pierwszy produkt</div>

<div id="2">Drugi produkt</div>

<div id="3">Trzeci produkt</div>

</div>

<div id="shoppingcart"></div>

<div id="debug"></div>

</body>

</html>

3/2007

Narzędzia

56

dzamy galerią obrazków. Zaraz po rozpako-waniu źródeł widzimy, iż scriptaculous jest dużo lżejszy od dojo. Całość (wraz z wymaga-nym Prototype) zajmuje około 200KB. Potrze-bujemy folderu lib (który zawiera Prototype) oraz src, gdzie znajdują się wszystkie bibliote-ki. Script.aculo.us podobnie jak dojo pozwala załadować część z nich. Robimy to poprzez do-danie parametru ?load w ścieżce – nazwy po-dajemy po przecinku. Potrzebne nam będą ef-fects oraz dragdrop. Na Listingu 2. znajduje się przykładowy skrypt.

Metoda onUpdate umożliwia sprawdzenie kolejności ułożenia elementów, możemy np. na bieżąco w tle wysyłać Ajaxem informacje do serwera, aby w bazie zapisać kolejność ele-mentów. Konstruktor Sortable przyjmuje kil-kanaście ciekawych parametrów takich jak: tag – tag elementów, jakie mają być sortowa-ne między sobą, overlap – parametr określają-cy czy lista ma się sortować poziomo czy pio-nowo oraz handle – definicję obszaru, za któ-ry można złapać przenoszone elementy. Cieka-wostką jest, że jedynie Scriptaculous radzi so-bie (i to również nie do końca, bo tylko w Fire-foksie) z takimi listami, które zostały umiesz-czone w elementach z ustawionym overflow:scroll.

Scriptaculus pozwala również tworzyć ko-lejki efektów – można łączyć elementy i two-rzyć skomplikowane animacje. Bardzo dużym plusem jest czytelna dokumentacja oraz ła-twość implementacji kodu. W Internecie zna-leźć można również wersję lite (lekką) biblio-teki Prototype – co pozwala dodatkowo odchu-dzić całość kodu.

MooFxAutorzy promują tę bibliotekę, jako ultralek-ki zestaw efektów JavaScript. Dostępne są dwie jego wersje. Jedna oparta podobnie do Scriptaculousa o Prototype, druga o Mooto-ols framework. Wersja oparta o Mootools za-wiera wszystkie moduły (można wybrać je-dynie wymagane) i zajmuje 36KB, co jest du-żym osiągnięciem. Biblioteka zawiera wszyst-kie podstawowe elementy, takie jak obsłu-ga zdarzeń, Ajaxa, efektów wizualnych ty-pu płynne przesuwanie, tooltipów i całej ma-sy innych. Wszystkie osoby uczulone na roz-miar swoich skryptów powinny zwrócić uwa-gę na tę właśnie bibliotekę. Dużym minusem jest brak przykładów do większości z dostęp-nych funkcji – wiadomo, że najłatwiej przy-swoić sobie właśnie w ten sposób ich działa-nie. Pomimo tego, dość dużo informacji zna-leźć można na forum MooFx. Na Listingu 5. znajduje się przykładowa implementacja. Zwykły element blokowy, który po załadowa-niu strony rozszerza się, oraz można go prze-nosić po ekranie. Implementacja całości jest ła-twa, kod jest przyjemny, problemem jest jedy-nie znalezienie w dokumentacji odpowiedniej składni poleceń.

Listing 2. Sortowalne obrazki w Script.aculo.us

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/

xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>

<title>Sortable test</title>

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

<script src="lib/prototype.js" type="text/javascript"></script>

<script src="src/scriptaculous.js?load=effects,dragdrop" type="text/

javascript"></script>

<style type="text/css" media="screen">

#images {

padding:5px;

}

#images img {

margin:5px;

border: 2px solid #dedede;

}

</style>

</head>

<body>

<div id="images">

<img id="image_1" src="1.jpg" />

<img id="image_2" src="2.jpg" />

<img id="image_3" src="3.jpg" />

<img id="image_4" src="2.jpg" />

<img id="image_5" src="3.jpg" />

<img id="image_6" src="2.jpg" />

<img id="image_7" src="3.jpg" />

</div>

<div id="debug"></div>

<script type="text/javascript" language="javascript">

// <![CDATA]

// tworzenie listy sortowalnej

Sortable.create("images", {

tag:'img',overlap:'horizontal',constraint: false, onUpdate:function(){

$('debug').innerHTML = (Sortable.serialize('images'));

}

});

// ]]>

</script>

</body>

</html>

Listing 4. Jak ustawić event, do niezaładowanego obiektu?

Event.observe(window, 'load', function() {

Event.observe('myForm', 'submit', doSomething);

});

Listing 3. Obsługa wydarzeń w Prototype.

<form action=”form.php” id=”myForm” method=”post”>

...

</form>

...

Event.observe('myForm', 'submit', doSomething);

również znakomitych narzędzi do Ajaxowej ob-sługi formularzy.

Script.aculo.usScriptaculous to biblioteka skryptów oparta o Prototype. Podobnie do Dojo zawiera ma-

sę modułów ułatwiających pracę z JavaScrip-tem. Wykonajmy przykładowy skrypt oparty o Scriptaculous. Niech będzie odpowiedzial-ny za przenoszenie, zmianę kolejności, obraz-ków w galerii. Może być to przydatne w sys-temie zarządzania treścią, w którym zarzą-

JavaScript

Yahoo User Interface LibraryYahoo również przygotowało interfejs wspo-magający budowę aplikacji opartych o techno-logie takie jak DOM, DHTML czy AJAX. Naj-większym plusem tego interfejsu jest na pewno ogromne zaplecze Yahoo – najlepiej przygoto-wane przykłady, świetna dokumentacja (na stro-nie internetowej dział YUI Theater z wykładami

i całą otoczka związana z biblioteką). Nie zmie-nia to faktu, iż podobnie do Dojo, pomimo funk-cjonalności na najwyższym poziomie oraz bar-dzo dobrego wsparcia Yahoo, całość zajmuje po-nad 3,5MB. Na szczęście odpowiednia konfigu-racja całości może pozwolić nam na osiągnięcie kompromisu. Nawet, jeśli paczka nie przypadnie nam do gustu, to ogrom informacji, jakie można

znaleźć na stronie domowej projektu jest godny przyswojenia.

Po stronie phpJest kilka bibliotek napisanych po stronie php, uła-twiających tworzenie aplikacji opartych o technolo-gię Ajax. Za najpopularniejszy uznać możemy Sa-jax, godnym uwagi jest również Pear::Html::Ajax. Dostarczają one jednak jedynie wygodnego interfej-su do komunikacji pomiędzy php i JavaScriptem, a nie tak jak reszta biblioteki efektów wizualnych.

JAKUB SACHAAutor z technologiami internetowymi ma stycz-ność od około 4-5 lat. Początkowo programowa-nie traktował jako pasję, ale aktualnie pracuje ja-ko programista w bytomskiej firmie NylonCoffee.Kontakt z autorem: [email protected]

Listing 5. Przykładowy skrypt efektów wizualnych wykonany za pomocą MooFx

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>

<title>movment test</title>

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

<script src="mootools.js" type="text/javascript"></script>

<script type="text/javascript" language="javascript">

window.onload= function(){

$('element').makeDraggable();

var wEffect = $('element').effect('width', {duration: 1000,

transition: Fx.Transitions.linear});

var hEffect = $('element').effect('height', {duration: 1000,

transition: linear});

wEffect.start(10,300);

hEffect.start(10,300);

};

</script>

<style type="text/css" media="screen">

#element {

width:10px;

height:10px;

border: 1px solid blue;

}

</style>

</head>

<body><div id="element"></div></body>

</html>

Ciekawe linki

• http://ajaxian.com/ - skarbnica wiedzy dotyczącej JavaScript'u,

• http://www.prototypejs.org/ - strona do-mowa framework'a Prototype,

• http://dojotoolkit.org/ - strona domowa kolejnego świetnego zestawu narzędzi JavaScript,

• http://script.aculo.us/ - biblioteka efek-tów napisana na podstawie Prototype,

• http://moofx.mad4milk.net/ - biblioteka efektów, w której autorzy postawili na lekkość kodu,

• http://developer.yahoo.com/yui/ - Yahoo User Interface Library,

• http://codinginparadise.org/projects/dojo_offline/screencast_02_26_2007/screencast_02_26_2007.html – wersja demonstracyjna aplikacji opartej o do-jo.storage.

R E K L A M A

3/2007

Narzędzia

58

WordPress 2.x

www.phpsolmag.org 59

Wpisy mogą być ogólnodostępne, jak i zastrzeżone, czyli takie, do któ-rych przeczytania potrzebne bę-

dzie hasło. Wpisy w takim pamiętniku mogą zostać także skomentowane przez osoby od-wiedzające Twojego bloga. Dzięki temu możesz poznać opinię innych osób, o tym co napisałeś. WordPress jest obecnie najbardziej popularnym darmowym skryptem bloga, który możesz po-brać i zainstalować samodzielnie na swoim ser-werze. Skrypt do działania potrzebuje interpre-tera PHP zainstalowanego na serwerze oraz ba-zy danych MySQL, czyli standardowych skład-ników, dostępnych nawet w przypadku korzy-stania z darmowego konta hostingowego.

Pobieranie skryptu WordPressPierwszą czynnością, którą musisz wykonać, będzie pobranie skryptu WordPress ze strony domowej. Aby tego dokonać uruchom przeglądarkę interne-tową, za pomocą której wejdziesz na stronę WWW systemu WordPress. Następnie wpisz w pasku adre-su http://wordpress.org/. Teraz kliknij odnośnik DO-WNLOAD znajdujący się po prawej stronie menu. Następnie wybierz, w jakim formacie chcesz pobrać skrypty bloga. W przypadku formatu ZIP kliknij Do-wnload .zip, w przypadku formatu GZIP kliknij Do-wnload .tar.gz. W tym momencie rozpocznie się po-bieranie, udostępnionej przez autorów, aktualnej i stabilnej wersji skryptu. Po rozpakowaniu pobrane-go archiwum uzyskasz katalog, którego zawartość będzie można wgrać na serwer WWW.

Pobieranie polskich plików językowych dla WordPressaPobrany system nie jest niestety w języku pol-skim. Nasz język, tak jak każdy inny poza angiel-skim, musisz samodzielnie pobrać z serwera i za-instalować w systemie. W tym celu otwórz ulu-bioną przeglądarkę internetową. W pasku adresu wpisz: http://codex.wordpress.org/WordPress_in_Your_Language#Polish. Na podanej stronie do-stępne są dwa rodzaje kodowania znaków dla języka polskiego. Pierwszym jest UTF, a dru-gim ISO. Proponuję, abyś pobrał pliki w forma-cie UTF, dzięki czemu Twój blog będzie zgod-ny z najnowszymi standardami Internetu. Klik-nij na odnośnik pliki .mo i .po, a wówczas zosta-niesz przekierowany na stronę, z której będziesz mógł je pobrać. Pobierz pliki .mo i .po na swój dysk twardy. Teraz pozostała już tylko instalacja polskiej lokalizacji.

Instalacja języka polskiego w WordPressieW celu instalacji języka polskiego w systemie Word-Press, należy wgrać pliki pobrane z serwera do od-powiedniego katalogu naszego systemu, a następ-nie ustawić odpowiednie opcje w pliku konfigura-cyjnym serwisu. Pierwszym krokiem jest wejście do katalogu wp-includes, w katalogu z WordPressem. Utwórz w nim katalog o nazwie languages. Wgraj pliki z polską wersją językową: pl_PL.mo i pl_PL.so. Następnie przejdź do głównego katalogu systemu. Odnajdź plik wp-config.php lub wp-config.php-sample i otwórz go w dowolnym edytorze tekstu. Odnajdź linię define ('WPLANG', ''); i popraw ją tak, aby wyglądała następująco: define ('WPLANG', 'pl_PL');. Następnie zapisz plik.

Instalacja WordPressaW momencie, gdy WordPress został już przez Cie-bie spolonizowany, możesz wreszcie go zainstalo-wać. Pierwsze co powinieneś zrobić, to wgranie sys-temu na serwer za pomocą narzędzi do przesyłania plików protokołem FTP. Znajdź w głównym kata-logu systemu plik wp-config.php lub wp-config.php-smaple. Jeżeli plik ma sufix sample wówczas usuń

WordPress 2.x

WordPress to system ułatwiający prowadzenie bloga, czyli własnego pamiętnika internetowego. Za jego pomocą możesz bardzo łatwo dodawać do swojego serwisu nowe wpisy ułożone w daty oraz kategorie.

Co obiecujemy...• Nauczysz się instalować i konfigurować system

WordPress.

Co należy wiedzieć...• Wiedza podstawowa na temat systemów do

prowadzenia blogów.

Poziom trudności

Stwórz własny blog

Rysunek 1. Strona WordPressa z odnośnikami do pobrania skryptu

3/2007

Narzędzia

58

WordPress 2.x

www.phpsolmag.org 59

go tak, aby plik nazywał się wp-config.php. Otwórz plik w dowolnym edytorze tekstu. Odnajdź linię define('DB_NAME', 'wordpress'); // The name

of the database i wpisz swoją nazwę bazy danych w miejscu wordpress. Odnajdź linię define('DB_USER', 'username'); // Your MySQL username i wpisz nazwę użytkownika mającego prawo dostę-pu do bazy danych w miejscu username. Odnajdź linię define('DB_PASSWORD', 'password'); // ...and password i wpisz hasło użytkownika dostę-pu do bazy danych w miejscu password. Odnajdź li-nię define('DB_HOST', 'localhost'); // 99% chance you won't need to change this value i w miejscu localhost wpisz numer IP bądź nazwę serwera, na którym pracuje Twoja baza danych. Za-pisz i zamknij plik. Następnie uruchom przeglądar-kę i wpisz w niej adres swojego serwera, na którym postanowiłeś zainstalować system. Powinna pojawić się strona z komunikatem It doesn't look like you've in-stalled WP yet. Try running install.php. Kliknij na od-nośniku install.php. Pojawi się strona z informacją, że można zainstalować system WordPress. Kliknij na odnośniku Krok pierwszy ». W polu Nazwa blo-ga wpisz swoją nazwę bloga. Będzie ona jednocze-śnie tytułem, wyświetlanym w pasku tytułu prze-glądarki, jak i nazwą Twojego bloga. Pole Twój e-mail wypełnij wpisując w nim swój adres e-mail. Pamię-taj, aby wpisać poprawny adres, dzięki czemu bę-dziesz informowany o nowych komentarzach poja-wiających się na Twoim blogu. Umożliwi Ci to rów-nież zmianę hasła w przypadku jego zapomnienia. Po wypełnieniu wszystkich pól, kliknij na przycisku Przejdź do drugiego kroku ». Na następnej stronie zo-staniesz poinformowany o udanej instalacji systemu na Twoim serwerze. Zostaną również podane infor-macje potrzebne do zalogowania się do panelu admi-nistratora. Kliknij na łączu wp-login.php, aby przejść do okna logowania do panelu administratora.

Logowanie do panelu administratoraPodstawową rzeczą, którą możesz zrobić po insta-lacji WordPressa, jest zalogowanie się do panelu ad-ministratora. Dzięki temu będziesz mógł dodawać wpisy i strony do swojego systemu oraz modyfiko-wać jego ustawienia. Aby tego dokonać otwórz swo-ją przeglądarkę internetową i po wpisaniu adresu bloga, dodaj nazwę piku wp-login.php. Zostałeś teraz przeniesiony do strony zawierającej formularz logo-wania. W pole Nazwa użytkownika wpisz swoją na-zwę użytkownika - admin. W pole Hasło wpisz ha-sło dostępu do panelu administracji, które zostało Ci przydzielone podczas instalacji systemu. Aby za-

Rysunek 3. Edycja pliku konfiguracyjnego

Rysunek 2. Polskie pliki językowe

Rysunek 5. Informacje o koncie administratoraRysunek 4. Instalacja bloga na własnym serwerze

3/2007

Narzędzia

60

Rysunek 6. Logowanie do panelu administratora Rysunek 10. Opcje publikacji poprzez e-mail

Rysunek 9. Opcje publikacji wpisów

Rysunek 8. Konfiguracja daty i czasu

Rysunek 7. Konfiguracja opcji ogólnych

kończyć proces logowania kliknij przycisk Login. Je-żeli login i hasło wpisałeś poprawnie, to zostałeś wła-śnie zalogowany do panelu administracji.

Konfiguracja systemuKonfiguracja systemu pozwoli Ci na dostosowa-nie go do własnych potrzeb i ustawienie co na da-nym blogu można, a czego nie można zobaczyć.

Opcje ogólneOpcje ogólne panelu administracji to jedno z naj-ważniejszych miejsc w konfiguracji całego syste-mu, ponieważ właśnie tutaj możemy zmieniać podstawowe aspekty blogu. Aby przejść do od-powiedniej sekcji w panelu administrcji, kliknij na Opcje, a z menu, które się ukaże, wybierz Ogól-ne. W tym momencie możesz już dowolnie kon-figurować następujące opcje swojego bloga. Pole Nazwa bloga to miejsce w którym możesz zmie-nić nazwę swojego bloga. W polu Opis bloga mo-żesz wpisać tematykę, którą będziesz tam poru-szał. Pole Adres URI WordPress określa adres, pod którym zainstalowano system WordPress. W polu Adres URI bloga możesz podać swój własny adres, pod którym chcesz, aby blog był widziany w sieci. Pole Adres e-mail i jego funkcje zostały opisane już wyżej. W polu Uprawnienia użytkowników decy-dujesz, jakie chcesz im nadać prawa. Opcja Każdy może się zarejestrować – decyduje o tym, czy użyt-kownik może zarejestrować swoje konto w syste-mie, czy też nie. Natomiast opcja Użytkownicy mu-szą być zarejestrowani i zalogowani, by móc komento-wać – decyduje o tym czy użytkownik musi być zalogowany, aby móc dodać komentarz do Twoje-go wpisu. W przypadku niezaznaczenia tej opcji, każdy użytkownik, nawet bez założonego konta w systemie, będzie mógł dodawać komentarze do Twoich publicznych wpisów. Pole Domyślna funk-cja nowego użytkownika decyduje o tym, jakie pra-wa zostaną przyznane każdemu użytkownikowi, który zarejestruje się w systemie. Tutaj najlepiej pozostawić Abonent, co oznacza, że domyślnie za-rejestrowany użytkownik będzie miał jedynie pra-wo do komentowania wpisów. Kolejną opcją, ja-ką dysponuje administrator, jest zmiana daty oraz czasu. W polu Czas tego bloga ma się różnić od cza-su UTC o należy wpisać, o jaką ilość godzin czas wyświetlany na blogu ma się różnić od czasu uni-

wersalnego. Pole Domyślny format daty decyduje o tym, jaki będzie używany na blogu domyślny for-mat daty. Analogicznie pole Domyślny format cza-su decyduje o tym jaki będzie stosowany domyśl-ny format czasu. (Format akceptowanej przez sys-tem daty oraz czasu znaleźć można na stronie http://codex.wordpress.org/Formatting_Date_and_Time) Ustawienie pola Pierwszy dzień tygodnia decydu-je, jaki dzień jest początkiem tygodnia. Po wypeł-nieniu wszystkich wymaganych pól, kliknij przy-cisk Zapisz zmiany w opcjach. Jeżeli wszystko po-szło dobrze, to Twoje ustawienia zostały zapisane i będą używane przez system jako domyślne.

Opcje publikacjiOpcje publikacji pozwalają na dostosowanie wbudo-wanego edytora WYSIWYG (What You See Is What You Get) do swoich wymagań oraz zmianę opcji związanych z publikacją wpisów bloga za pomo-cą listów e-mail. Aby do nich dotrzeć, w głównym menu kliknij na odnośnik Opcje i z podmenu wy-bierz Publikacja. Zawarte w tej sekcji pole Wielkość okna edycji określa ile linii tekstu będzie domyślnie przeznaczone na treść nowego komentarza lub wpi-su do bloga. Gdy chcemy używać do edycji edytora typu WYSIWIG musimy zaznaczyć pole Włączaj domyślnie edytor WYSIWYG. Pole “Zmieniaj emoti-

3/2007

Narzędzia

62

ży do zdefiniowania portu, na którym WordPress będzie się łączył z danym kontem pocztowym. W polu Login należy wpisać nazwę użytkownika, któ-ra będzie loginem do skojarzonego z systemem kon-ta poczty e-mail. Pole Hasło należy wypełnić hasłem dostępu do konta e-mail. W polu Domyślna katego-ria wpisów wysyłanych e-mailem można określić ka-tegorię, w której pojawią się wszystkie wpisy odebra-ne za pośrednictwem Twojego e-maila. W polu Serwi-sy aktualizacji należy wpisać serwisy, które zostaną powiadomione o nowych wpisach na Twoim blogu, pamiętając jednocześnie, że każdy nowy serwis nale-ży wpisać w nowej linii. W celu zapisania ustawień z tej zakładki należy kliknąć Zapisz zmiany w opcjach.

Wyświetlanie wpisówW opcjach wyświetlania wpisów można zdecydo-wać o tym, ile wpisów ma zostać pokazanych na stronach bloga, ile wątków ma zostać wyekspor-towanych do formatu RSS oraz o domyślnym ko-dowaniu stron bloga. Aby tego dokonać, kliknij w głównym menu systemu na Opcje i wybierz z pod-menu Wyświetlanie wpisów. Pole Pokaż najwyżej de-finiuje ile wpisów lub ile dni ma być wyświetlane na jednej stronie Twojego blogu. Pole Pokaż najnowsze decyduje o tym, ile wpisów, udostępnianych z Two-jej strony, zostanie pokazane jako zawartość kana-łu RSS. W polu Dla każdej publikacji pokazuj moż-na zdecydować o tym, czy chcemy pokazać Cały tekst czy też jego Streszczenie. Pole Kodowanie stron i wątków służy do określenia domyślnego kodowa-nia wątków i stron Twojego bloga. Pole WordPress ma kompresować publikacje na żądanie przeglądarek (gzip) należy zaznaczyć, w przypadku, gdy chcemy żeby, strony były kompresowane na żądanie prze-glądarki WWW. Kompresja znacznie przyspiesza transfer z serwera do klienta, lecz powoduje także większe obciążenie serwera ze względu na potrzebę kompresji strony WWW. Aby zapisać ustawienia należy kliknąć przycisk Zapisz zmiany w opcjach.

Dyskusja międzyblogowaOpcje zawarte w tej zakładce pozwalają na kontro-lę współpracy własnego bloga z innymi, znajdujący-mi się w sieci Internet. W głównym menu admini-stratora kliknij na Opcje. Z podmenu wybierz Dys-kusja międzyblogowa. Opcja Próbuj wysłać sygnały ping/TrackBack do wszystkich blogów, które są linko-wane w tekście (wydłuża czas publikacji), po zazna-czeniu, wyśle sygnał do wszystkich blogów lub stron internetowych, których użyłeś w tekście. W opcji Ze-zwalaj na powiadomienia o linkowaniu na innych blo-gach (sygnały ping/TrackBack), po zaznaczeniu, zo-staniesz poinformowany, jeżeli na którymś blogu pojawi się link do Twojej publikacji. Opcja Włącz ko-mentarze do publikacji dołącza do treści Twojej in-formacji, zapisanej na blogu, także treść komentarza. W polu Wyślij do mnie wiadomość e-mail, gdy decy-dujesz o tym, kiedy chcesz otrzymać wiadomość e-mail. Masz do wyboru Ktoś doda komentarz – zosta-niesz wówczas poinformowany o tym listem e-ma-il, Komentarz czeka na sprawdzenie – jest to sytuacja w której wybrałeś sprawdzanie komentarzy osobi-ście. Wówczas także przyjdzie do Ciebie list e-mail,

Rysunek 11. Serwisy aktualizacji

Rysunek 13. Konfiguracja powiadamiania

Rysunek 12. Opcje wyświetlania wpisów

Rysunek 14. Informacje o publikacji komentarzy

kony, takie jak :-) i :-P” na obrazki, w przypadku za-znaczenia, będzie automatycznie zamieniało tego typu tekstowe znaczki na obrazki. Uaktywniona opcja Automatycznie poprawiaj nieprawidłowo za-gnieżdżony XHTML, będzie automatycznie popra-wiała źle zagnieżdżone elementy języka XHTML. Pole Domyślna kategoria wpisów służy do wybrania domyślnej kategorii, do której wpisy będą automa-tycznie dodawane. Dzięki temu, będziesz mógł au-

tomatycznie wybrać kategorię, o ile będą te zazna-czone, do której nowy wpis zostanie automatycznie przyporządkowany. Dzięki grupie opcji publikacji, poprzez e-mail możesz skonfigurować WordPres-sa tak, aby automatycznie umieszczał wszystki li-sty e-mail, pojawiające się na danym koncie , na swo-ich stronach. Pole Nazwa serwera mail musi w tym przypadku zawierać nazwę serwera na którym zo-stało utworzone konto poczty e-mail. Pole Port słu-

3/2007

Narzędzia

64

rzem. W polu Blokuj komentarze z publicznych i nieza-bezpieczonych serwerów proxy decydujesz, czy chcesz zablokować komentarze pochodzące od osób korzy-stających z publicznych serwerów proxy. Po zakoń-czeniu konfiguracji kliknij na przycisk Zapisz zmia-ny w opcjach.

OdnośnikiOpcja, która pozwala na lepsze dostosowanie swojego bloga dla potrzeb użytkowników i wy-szukiwarek internetowych. Możesz dzięki tej opcji polepszyć nawet pozycję w wyszukiwar-kach, dzięki temu, że treści będą wyglądały tak, jakby były w katalogach, co znacznie poprawi po-zycję Twojej strony w wynikach wyszukiwania. Kliknij w menu głównym na Opcje, a z podmenu wybierz Odnośniki. Z listy Ustawienia ogólne wy-bierz jaki schemat chcesz mieć w swoim blogu. Czy ma to być schemat Domyślny, czy Oparty na dacie i nazwie publikacji, Numeryczny czy też może Własny (określ poniżej) (Więcej informa-cji na temat wpisywania formatu odnośników znajdziesz na stronie http://codex.wordpress.org/Using_Permalinks). W polu Prefiks kategorii mo-żesz podać swój własny prefiks, którym będą po-przedzane wszystkie kategorie w Twoim blogu. Po zakończeniu wybierania i wypełniania pól, kliknij na przycisk Zmień schemat odnośników.

System będzie działał tylko i wyłącznie na ser-werze Apache, gdyż ten serwer posiada moduł rewrite, służący do przepisywania adresów URL. Jeżeli po zmianie ustawień pojawi się pole z infor-macją, że musisz zapisać odpowiednie dane w pli-ku .htaccess, skopiuj dany fragment kodu i zapisz do tego pliku. Komunikat ten oznacza, że Word-Press nie mógł samodzielnie zapisać wartości w tym pliku, ponieważ nie posiada praw do zapisu.

Inne opcjeW innych opcjach są zawarte wszystkie inne opcje, które nie odpowiadały pozostałym kategoriom. Kliknij w menu głównym na Opcje. Z podmenu wybierz Inne. W polu Zapisuj wysłane pliki w tym folderze wpisz nazwę folderu, w którym mają zo-stać zapisane pliki wgrywane na serwer. Pole Or-ganizuj wysłane pliki w folderach miesięcznych i rocz-nych, po zaznaczeniu, będzie organizowało pliki w folderach z nazwami miesięcy i lat. W polu Notuj datę aktualizacji linków po zaznaczeniu, wszystkie linki będą miały automatyczną aktualizację dat. Pole Użyj rozwiązań indywidualnych z pliku my-hacks.php pozwala na użycie dodatkowych skryp-tów znajdujących się w pliku my-hacks.php.

Rysunek 17. Konfiguracja innych opcji

Rysunek 16. Konfiguracja adresów wpisów w blogu

ŁUKASZ SOSNAAutor od 4 lat zajmuje się informatyką dzięki czemu napisał kilkanaście książek oraz kilka artykułów. Mię-dzy innymi książki: phpMyAdmin – proste zarządza-nie bazą MySQL, Sekrety pozycjonowania w Google oraz bestseller wydawnictwa Helion jakim jest książ-ka: Linux. Komendy i polecenia.Strona WWW: http://linuxmag.plKontakt z autorem: [email protected]

Rysunek 15. Czarna lista komentarzy

z informacją o tym, że komentarze na Twoim blogu oczekują na sprawdzenie. Opcję Administrator musi zaakceptować komentarz (niezależnie od opcji wybra-nych poniżej) zaznacz wtedy, kiedy chcesz akcepto-wać wszystkie komentarze dodawane do Twojego bloga. Tylko po Twojej akceptacji komentarz pojawi się na stronach. W opcji Autor komentarza musi wpi-sać swój identyfikator i adres e-mail decydujesz czy au-tor komentarza będzie musiał podać swoje prywat-ne dane, takie jak Nick i adres e-mail. Opcja Autor ko-mentarza musi posiadać co najmniej jeden zaakcepto-wany komentarz przy zaznaczeniu będzie publiko-wała komentarze, w momencie kiedy autor będzie miał przynajmniej jeden komentarz zaakceptowany w systemie. W opcji Hold a comment in the queue if it contains możesz określić, aby komentarz zawierający daną ilość odnośników został zatrzymany i nie był

publikowany na stronach bloga. Opcja Jeżeli treść, au-tor, adres URI, e-mail lub IP komentarza zawiera jedno z poniższych słów, komentarz zostanie przeniesiony do poczekalni (wpisz każde słowo w nowej linii) pozwala na określenie słów kluczowych, według których ko-mentarz nie będzie publikowany, tylko przeniesio-ny do poczekalni, a Ty będziesz mógł go samodziel-nie zaakceptować. Pole To jest lista słów, które chcesz umieścić na czarnej liście twojego bloga. Dodawaj nowe słowa z dużą ostrożnością, bo jeśli w jakimś komentarzu znajdzie się jedno z nich, to ten komentarz zostanie za-blokowany bez powiadomienia cię o tym. Jeżeli istnie-je szansa, że dozwolony komentarz może zawierać takie słowo, to lepiej będzie, jeśli umieścisz je w polu komenta-rzy do sprawdzenia (powyżej) służy do umieszczania listy słów, które w momencie użycia w treści dane-go wpisu, zostaną usunięte wraz z całym komenta-

3/2007

Bezpieczeństwo

66

Steganografia

www.mscoder.eu 67

Informacje można ochronić na dwa sposo-by: kryptograficznyprzez przetworzenie danych w taki sposób by stały się nieczytel-

ne dla osób postronnych lub steganograficzny-przez utajnienie faktu istnienia wiadomości.

Najbezpieczniejszą metodą przechowywania prywatnych danych jest umieszczenie ich w ta-kim miejscu gdzie nikt się ich nie spodziewa. Plik zakodowany programami takimi jak PGP (ang. Pretty Good Privacy) lub GnuPG (ang. Privacy Guard) jest bezpieczny ale przez swo-ją nieczytelność niesie informację iż są w nim ja-kieś zakodowane dane. Idealnym rozwiązaniem jest takie ukrycie wiadomości by miały one po-stać np. niewinnego zdjęcia rodzinnego. Nikt nie będzie podejrzewał, że w obrazku znajduje się ukryta wiadomość zakodowana na poziomie pojedynczych pikseli.

Takie samo, a jednak innePomysł jest prostyskoro cyfrowy obraz jest zbiorem punktów o określonych kolorach, to modyfikując je w niewielkim stopniu można uzyskać obraz niewiele różniący się od orygi-nału i jednocześnie zawierający tajne infor-macje. Jest to możliwe ponieważ każdy punkt obrazu (piksel, ang. pixel) jest określany za pomocą trzech barw składowych: czerwonej, zielonej i niebieskiej (RGBang. Red, Green, Blue). Z kolei liczba barw możliwych do uzy-

skania z połączenia poszczególnych składo-wych to tzw. "głębia koloru" (Rysunek 1.). W przypadku popularnego formatu zdjęć "true color" każda składowa RGB jest reprezento-wana za pomocą 8 bitów. Inaczej mówiąc in-formacje o kolorze każdego punktu są prze-chowywane w 24 bitach.

Do zakodowania bajtu danych wystarczy więc zmodyfikować wartość składowej jednego z kolorów (lub wszystkichdla zdjęć w skali sza-rości) na wartość odpowiadającą bajtowi ukry-

wanej wiadomości.Aby zilustrować ten sposób ukrywania danych załóżmy, że mamy 3 sąsia-dujące ze sobą piksele (9 bajtów) o kolorze bia-łym (wszystkie składowe RGB przyjmują war-tość dziesiętną 255) – Tabela 1.

Zakodowanie znaku A (o kodzie dziesiętnym 65) w składowej G w trzecim pikselu spowodu-je następujące zmiany – Tabela 2.

Tak uproszczona metoda szyfrowania ma swo-je wadymodyfikacja jednej składowej koloru mo-że powodować pojawianie się na zdjęciu punk-tów o kolorach znacznie odbiegających od punk-tów sąsiadujących (jest to szczególnie widocz-ne przy jednolitym tle obrazka – Rysunek 2.). Oczywiście można odpowiednio dobrać obra-zek i ukryć kolorowe punkty. Co jednak zrobić gdy chcemy by jakość obrazka nie zdradzała fak-tu iż zawiera on ukryte przez nas dane? Jedną z możliwości jest modyfikowanie nie całej składo-

Steganografia

Każdy z nas na pewno stanął przed problemem ochrony danych przed wzrokiem niepożądanych osób. Każdy z nas chciałby pewne wiadomości ukrywać i udostępniać tylko wybranym osobom.

Co obiecujemy...• Poznasz praktyczne metody ukrywania infor-

macji w plikach graficznych. Będziesz mógł sa-modzielnie przygotować pliki zawierające ukry-te wiadomości tekstowe.

Co należy wiedzieć...• Wskazana jest znajomość zasad obsługi plików

graficznych za pomocą funkcji PHP.

Poziom trudności

Informacja, której nie widać

Rysunek 1. Model barw RGB przedstawiony jako sześcian (rysunek pochodzi z serwisu http://de.wikipedia.org)

Rysunek 2. Przykład deformacji wprowadzonych do obrazka przy modyfikacji jednej składowej koloru

3/2007

Bezpieczeństwo

66

Steganografia

www.mscoder.eu 67

wej koloru punktu ale np. tylko jednego, ostat-niego bitu (ang. least significant bit). Oznacza to iż do zakodowania np. 10 bajtów danych będzie-my potrzebowali zmodyfikować 10 (bajtów) * 8 (bitów/bajt) / 3 (bity/piksel) = 27 (pikseli). Taki sposób ukrywania danych (nazywany LSB) pro-wadzi do tak nieznacznej modyfikacji koloru po-szczególnych punktów iż dostrzeżenie zmian jest w zasadzie niemożliwe.

Wróćmy do przykładu z trzema sąsiadujący-mi ze sobą pikselami o kolorze białym (wszyst-kie składowe RGB przyjmują wartość binarną "11111111"255 w kodzie dziesiętnym) i prze-analizujmy jak zmienią się dane w przypad-ku kodowania metodą LSB: Zakodowanie zna-ku "A" (o kodzie binarnym 0100000165 w ko-dzie dziesiętnym) spowoduje następujące zmia-ny – Tabela 4.

Zakłócenia wprowadzone do obrazu, mi-mo iż nieznaczne, są jednak nadal możliwe do wychwycenia poprzez analizę histogramu po-szczególnych składowych barw. Każdy obraz posiada charakterystyczny rozkład kolorów. Je-go zaburzenie może wskazywać na fakt umiesz-czenia dodatkowych informacji. Problem ten można obejść poprzez "rozstrzelenie" ukrywa-nego tekstu w obrazie. Modyfikowane piksele nie będą położone obok siebie ale ich pozycja będzie obliczana za każdym razem w oparciu np. o tajny klucz.

Dzięki takiej operacji zaburzenia statystyk kolorów nie będą aż tak wyraźne. Kolejną tech-niką, którą można wykorzystać przy ukrywa-niu danych w plikach graficznych jest fakt iż inaczej reagujemy na nasycenie poszczególnych składowych modelu RGB. Okazuje się, że ludz-kie oczy są najbardziej wrażliwe na zmiany ko-loru zielonego a najmniej na zmiany koloru nie-bieskiego i czerwonego (dla RGB stosunek ten wynosi 3:6:1). Stosując tę zasadę można znacz-nie zwiększyć ilość ukrywanych informacji w jednym pikselu obrazu.

Skrypty PHPKlasy PHP umożliwiające kodowanie i wyświe-tlanie ukrytych informacji zostały przedstawio-ne na Listingu 1. (klasa Image) oraz Listingu 2. (klasa Steganography). Klasa Image pełni funk-cję pomocniczą ułatwiającą manipulowanie po-szczególnymi pikselami obrazu. Procedury ko-dowania i dekodowania danych są zawarte z klasie Steganography i to właśnie nimi zajmiemy się podczas analizy kodu.

Steganografia(słowo z języka greckiego oznaczające ukry-te pismo) dział wiedzy zajmujący się utaj-nianiem informacji poprzez ukrywanie ich w innych informacjach, np. w obrazach te-lewizyjnych, plikach graficznych itp. Hasło opracowane na podstawie Wielkiej Interak-tywnej Encyklopedii Multimedialnej http://wiem.onet.pl

Listing 1. Klasa Image zawierająca funkcje pomocnicze ułatwiające operowanie na poszczególnych pikselach obrazu

class Image {

var $im;

var $im_width;

var $im_height;

var $greyscale = false;

function load_or_create_png($imgname) {

$im = @imagecreatefrompng($imgname);

if (!$im) {

$im = imagecreatetruecolor(300, 300);

$bgc = imagecolorallocate($im, 255, 255, 255);

imagefilledrectangle($im, 0, 0, 300, 300, $bgc);

}

$this->im_width = imagesx($im);

$this->im_height = imagesy($im);

$this->im = $im;

return $im;

}

function unload() {

if ($this->im) {

@imagedestroy($this->im);

}

}

function save_png($imgname) {

if ($this->im) {

@imagepng($this->im, $imgname);

}

}

function get_pixel($index) {

$x = $this->pixel_x($index);

$y = $this->pixel_y($index);

$color = imagecolorat($this->im, $x, $y);

return array( ($color >> 16) & 0xFF, # R($color >> 8)

& 0xFF, # G

$color & 0xFF # B );

}

function set_pixel($index, $color) {

$x = $this->pixel_x($index);

$y = $this->pixel_y($index);

imagesetpixel($this->im, $x, $y, $color);

}

function pixel_color($r, $g, $b) {

return ($r << 16) | ($g << 8) | $b;

}

function change_color_component($rgb, $component, $value) {

$rgb[$component] = $value;

return $this->pixel_color($rgb[0], $rgb[1], $rgb[2]);

}

function pixel_index($x, $y) {

return $y * $this->im_width + $x + 1;

}

function pixel_x($index) {

return ($index - 1) % $this->im_width;

}

function pixel_y($index) {

return intval(($index - 1) / $this->im_width);

}

}

3/2007

Bezpieczeństwo

68

Steganografia

www.mscoder.eu 69

Listing 2. Klasa Steganography zawierająca funkcje kodowania i dekodowania danych

class Steganography

class Steganography {

function encode($text, $key, $image) {

# coder la longueur du message dans le premier pixel

if (empty($text) || empty($key)) return false;

$text_len = strlen($text);

$pixel = 1;

$r = $text_len >> 2;

$g = ($text_len - ($r << 2)) >> 1;

$b = $text_len - ($r << 2) - ($g << 1);

$color = $image->pixel_color($r, $g, $b);

$image->set_pixel($pixel, $color);

$key_len = strlen($key);

$key_index = $color_index = $i = 0;

while ($i < $text_len) {

# trouvez le pixel suivant

$pixel += ord($key[$key_index]);

$rgb = $image->get_pixel($pixel);

# codez l'octet des données à l'aide d'une clé privée

$crypted_value = ord($text[$i] ^ $key[$key_index]);

if ($image->greyscale) {

$color = $image->pixel_color($crypted_value, $crypted_value, $crypted_

value);

} else {

$color = $image->change_color_component($rgb, $color_index, $crypted_

value);

}

$image->set_pixel($pixel, $color);

$i += 1;

$key_index = ($i % $key_len);

$color_index = ($i % 3);

}

return true;

}

function decode($key, $image) {

# la longueur du message est codée dans le premier pixel

$pixel = 1;

$rgb = $image->get_pixel($pixel);

$text = "";

$text_len = ($rgb[0] << 2) + ($rgb[1] << 1) + $rgb[2];

$key_len = strlen($key);

$key_index = $color_index = $i = 0;

while ($i < $text_len) {

# trouvez le pixel suivant

$pixel += ord($key[$key_index]);

$rgb = $image->get_pixel($pixel);

# décodez l'octet des données à l'aide d'une clé privée

$byte = chr($rgb[$color_index]) ^ $key[$key_index];

$text .= $byte;

$i += 1;

$key_index = ($i % $key_len);

$color_index = ($i % 3);

}

return $text;

}

}

Musimy się upewnić, że nasz interpreter PHP jest odpowiednio skonfigurowany. Obsługę plików graficznych umożliwia biblioteka GD, która może być wbudowana w PHP lub dołą-czona jako oddzielny moduł (ang. Extension). Obecność biblioteki najprościej sprawdzić za pomocą polecenia php -i, które wyświetla in-formacje o konfiguracji interpretera. W wy-świetlonym tekście powinny znajdować się poniższe informacje:

• GD Support => enabled• PNG Support => enabled

W przypadku systemu Windows w pliku konfiguracyjnym php.ini powinien znaleźć się wpis:

• extension=php _ gd2.dll

KodowanieProces kodowania zaczyna się od zapisania dłu-gości wiadomości w pierwszym pikselu obrazka. Nie jest to niezbędne ale znacznie ułatwi później-sze dekodowanie tekstu. Pierwszy piksel możne także posłużyć jako znacznik mówiący czy obra-zek zawiera zakodowane dane czy też nie.

$text_len = strlen($text);

$pixel = 1;

$r = $text_len >> 2;

$g = ($text_len($r << 2)) >> 1;

$b = $text_len($r << 2)($g << 1);

$color = $image->pixel_color($r, $g, $b);

$image->set_pixel($pixel, $color);

Dla każdego bajtu wiadomości określamy, któ-ry piksel będzie modyfikowany oraz szyfru-jemy dane prywatnym kluczem. W naszym przypadku piksele są wybierane na podstawie kodu ASCII poszczególnych znaków klucza szyfrowania ale nic nie stoi na przeszkodzie by zastosować własną funkcję powodującą rozpro-szenie zmodyfikowanych pikseli. Do zaszyfro-wania wiadomości wykorzystujemy funkcję różnicy symetrycznej xor.

$pixel += ord($key[$key_index]);

$rgb = $image->get_pixel($pixel);

$crypted_value = ord($text[$i] ^ $key[

$key_index]);

Zaszyfrowane dane zapisujemy w odpowiedniej składowej koloru wybranego punktu (zmienna $color_index będzie wskazywała kolejno na R, G i B). Jeżeli mamy do czynienia z obrazkiem w skali szarości musimy zmodyfikować wszystkie składowe ponieważ w przeciwnym przypadku na ekranie pojawią się kolorowe piksele.

if ($image->greyscale) {

$color = $image->pixel_color(

$crypted_value, $crypted_value,

$crypted_value);

} else {

3/2007

Bezpieczeństwo

68

Steganografia

www.mscoder.eu 69

$color = $image->change_color_

component($rgb, $color_index,

$crypted_value);

}

$image->set_pixel($pixel, $color);

Praktyczne wykorzystanie klas Image i Stegano-graphy zawiera Listing 3. Skrypt wczytuje ory-ginalny plik PNG, koduje tekst i zapisuje wy-nik do pliku zakodowany.png Rezultat działa-nia można zobaczyć w dowolnej przeglądarce plików graficznych.

DekodowaniePodobnie jak przy kodowaniu procedurę zaczy-namy od pobrania długości wiadomości prze-chowywanej w pierwszym pikselu.

$pixel = 1;

$rgb = $image->get_pixel($pixel);

$text_len = ($rgb[0] << 2) + ($rgb[1] << 1)

+ $rgb[2];

Posługując się tą samą metodą jak przy kodo-waniu odszukujemy kolejne piksele, które by-ły modyfikowane i rozkodowujemy ukryte w nich informacje.

$pixel += ord($key[$key_index]);

$rgb = $image->get_pixel($pixel);

$byte = chr($rgb[$color_index]) ^

$key [$key_index];

Połączenie rozkodowanych bajtów daje nam w rezultacie oryginalną wiadomość. Listing 4. zawiera skrypt wczytujący plik PNG i wy-świetlający informację, którą w nim zakodo-wano.

PodsumowanieZastosowany algorytm kodowania jest uprosz-czony i może powodować pojawianie się kolo-rowych punktów w zakodowanym obrazku. Jednak przy zastosowaniu odpowiedniej me-tody szyfrowania (np. opisywanej wcześniej LSB) dodatkowe informacje są praktycznie nie do wykrycia a ich rodzaj jestnieograniczony. Tak jak w naszym przykładzie, tak i w prakty-ce dla zwiększenia bezpieczeństwa stosuje się połączenie steganografii i kryptografii (przed ukryciem tekst jest kodowany). Omawiane w artykule przykłady znajdują się na płycie kom-paktowej dołączonej do czasopisma. Znajdu-ją się tam także przykładowe pliki graficzne zawierające zakodowaną wiadomość. Zachę-camy do podjęcia próby rozkodowania tek-stu (klucz szyfrowania ma postać ciągu zna-ków "1234").

Tabela 1. Trzy sąsiadujące piksele (9 bajtów) o kolorze białym – wartości dziesiętne

Kolor Piksel 1 Piksel 2 Piksel 3

R 255 255 255

G 255 255 255

B 255 255 255

Tabela 2. Zakodowanie znaku A w składowej G w trzecim pikselu

Kolor Piksel 1 Piksel 2 Piksel 3

R 255 255 255

G 255 255 65

B 255 255 255

Tabela 3. Trzy sąsiadujące piksele (9 bajtów) o kolorze białym – wartości binarne

Kolor Piksel 1 Piksel 2 Piksel 3

R 11111111 (255) 11111111 (255) 11111111 (255)

G 11111111 (255) 11111111 (255) 11111111 (255)

B 11111111 (255) 11111111 (255) 11111111 (255)

Tabela 4. Zakodowanie znaku A o kodzie binarnym 0100000165 w kodzie dziesiętnym

Kolor Piksel 1 Piksel 2 Piksel 3

R 11111110 (254) 11111110 (254) 11111110 (254)

G 11111111 (255) 11111110 (254) 11111111 (255)

B 11111110 (254) 11111110 (254) 11111111 (255)

Listing 3. Przykładowy skrypt służący do dekodowania tekstu zawartego w pliku graficznym

include "image.inc.php";

include "steganography.inc.php";

$key = "tajny klucz";

$image = new Image;

$image->greyscale = false;

$cipher = new Steganography;

$image->load_or_create_png('zakodowany.png');

echo $cipher->decode($key, $image)."\n";

$image->unload();

Listing 4. Przykładowy skrypt służący do kodowania tekstu

include "image.inc.php";

include "steganography.inc.php";

$message = "wiadomość do zakodowania";

$key = "tajny klucz";

$image = new Image;

$image->greyscale = false;

$cipher = new Steganography;

$image->load_or_create_png('oryginalny.png');

$cipher->encode($message, $key, $image);

$image->save_png('zakodowany.png');

$image->unload();

PIOTR GAPIŃSKIAutor w wolnych chwilach zajmuje się programo-waniem w różnych językach. Kontakt z autorem: [email protected]

W Sieci

l http://pl.wikipedia.org/wiki/Steganografial http://www.binary-universe.net/przykładowe algorytmy kodowania danychl http://www.steganografia.wsi.edu.pl/witryna poświęcona steganografii komputerowej

Wywiad

3/200770

PHP Solutions: Jak przekonałby Pan poten-cjalnego klienta do zakupu tego produktu, podczas gdy istnieje dużo darmowych rozwią-zań lub rozwiązań typu open-source? Jakie są cechy, których nie można znaleźć nigdzie in-dziej, może pomijając łatwość w obsłudze?KM: Głównym atutem Zend Core (oprócz ła-twości w obsłudze) jest to, iż Zend certyfikuje na-sze rozwiązanie i zapewnia całkowite wsparcie. Przyznając certyfikaty, Zend wybiera odpowied-nie wersje technologii open-source, aby zagwa-rantować możliwie najlepsze rozwiązanie. Na przykład, PHP 5.2.1 jest najbardziej niezawod-ną i bezpieczną wersją PHP dostępną na rynku i stanowi fundament dla Zend Core. Jest połą-czony z Zend Framework, wyłaniającym się stan-dardem dla struktur PHP. Ta kombinacja razem z Apache i innymi oprogramowaniami stanowi obecnie najlepsze dostępne rozwiązanie open-source. Wciąż usprawniając te oprogramowa-nie, Zend współpracuje z naszymi partnerami, aby dołączać najlepszą wersję oprogramowa-nia wspierającego, która jest zasadniczą czę-ścią środowiska programistycznego. To może być tak proste, jak dostarczenie prawidłowego IBM DB oraz sterowników lub na tyle złożone, że nasze partnerstwo z Microsoft w celu dostarcze-nia PHP na najwyższym poziomie staje się sto-sowniejsze. Podsumowując, Zend zapewnia bez-awaryjny start środowiska PHP. Jeśli chodzi o wsparcie, to pomoc Zend dla Core nie polega je-dynie na podnoszeniu słuchawki. Oferując stan-dardowe usługi pomocy, nasi klienci doceniają to, że Zend jest w stanie utrzymać zdrowe środo-wisko PHP. Monitorujemy strony PHP open-sour-ce i nadzorujemy uaktualnienia, aby wszystkie wprowadzane przez nas udoskonalenia opro-gramowania dostarczać w szacunku do czasu naszych klientów. Dla większości z nich byłaby to czasochłonna i droga czynność, którą z przy-jemnością oddają w ręce firmy Zend. Ponad-to php.net wprowadza nakładki tylko na bieżą-ce wersje PHP, co dla wielu klientów jest nie do przyjęcia. Zawsze, gdy uaktualniają PHP, muszą przetestować ich wszystkie aplikacje tracąc przy tym czas i pieniądze. Zend dostarcza uspraw-nień dla naszych klientów, pracujących na star-szych wersjach Zend Core. Unikają problemu poprzez automatyczną aktualizację starszych wersji przy każdej modyfikacji PHP w Zend Core. Na przykład, ostatnio Zend dostarczył uspraw-nień dla klientów Zend Core 1.0 (obsługują-cych PHP 5.1.6), które są jedynie dostępne w

PHP 5.2.1. Wszystko to zostało osiągnięte dzię-ki mechanizmowi uaktualniania, który dostarczy i zainstaluje te uaktualnienia automatycznie. W ocenie naszych klientów ta stabilność jest głów-ną zaletą usług wspierających Zend Core.

PS: Jeśli zechciałbym pozostać przy serwe-rach linuxa – czy jest sens przechodzenia na Core 2.0?KM: Odpowiedź na zadane przez Pana pytanie, zawiera się w mojej wcześniejszej wypowiedzi.

PS: Co stałoby się, gdybym potrzebował jakiegoś zewnętrznego oprogramowania, które nie jest częścią Zend Framework? Czy będą one ze sobą funkcjonować?KM: Oczywiście, Zend Framework jest napisa-na w całości w PHP i nie będzie kolidować z ja-kimkolwiek innym kodem PHP lub oprogramo-waniem zewnętrznym.

PS: Czy mógłby Pan powiedzieć nam o głównych różnicach pomiędzy Zend Core a Eclipse IDE dla PHP?KM: Zend Core to środowisko wykonawcze PHP (Web Server, PHP engine, itd.), a Eclipse PDT (IDE dla PHP) to struktura IDE (środowisko do tworze-nia aplikacji PHP). Zend Studio jest w pełni wypo-sażonym środowiskiem programistycznym zinte-growanym ze wszystkimi produktami Zend.

PS: Czy możemy używać obydwu produk-tów razem?KM: Tak.

PS: Na czym polega główna różnica pomiędzy Zend Core i innymi podobnymi produktami?KM: Odpowiedź na zadane przez Pana py-tanie, zawiera się w mojej wcześniejszej wy-powiedzi.

PS: Jakiego rodzaju nowe rozwiązania do-starcza Zend Core programistom PHP?KM: Zend Core zawiera Zend Framework, któ-ra jest wspaniałą, nową strukturą PHP i znaczą-co ułatwia tworzenie aplikacji PHP. Dodatkowo, dostarczając pojedynczy i spójny pakiet dla PHP, programiści wiedzą, że używają tej samej wer-sji PHP i oprogramowania wspierającego, któ-ry ma być użyty podczas programowania. To eli-minuje wiele problemów, które mogą wystąpić podczas późniejszego programowania.

Wywiad z Kentem Mitchellem

Zend jest producentem wiodącego na rynku oprogramowania dla platform internetowych. Świadczy teżusługi związane z aplikacjami PHP.

Kent Mitchell jest

dyrektorem Marketingu

Produktu firmy Zend

3/2007

Testy konsumenckie

72

Firma Done świadczy usługi hostingowe pod marką Ultra.pl od 2001 roku. Nasza oferta jest skierowana do firm, które oprócz usłu-

gi hostingowej oczekują również wsparcia tech-nicznego. Mimo posiadania całkowicie zautomaty-

zowanego systemu zarządzania kontami POK (Pa-nel Obsługi Konta), dzięki któremu klient nie mu-si w ogólne się z nami kontaktować, zawsze służy-my pomocą np. przy instalacji programów narzę-dziowych na komputerze klienta (konfiguracji kont

pocztowych czy ftp), możliwa jest nawet wizyta w lokalu klienta.

Wielu klientów decyduje się na przeniesie-nie swojej strony www na nasz serwer, w ce-lu podniesienia standardu obsługi, w takim przypadku zapewniamy bez problemową mi-grację na nasze serwery, wyręczając klienta z konieczności zapoznawania się z techniczny-mi zawiłościami tego procesu.

Współpracujemy z wieloma webmastera-mi, którzy od lat wieszają strony swoich klien-tów na naszych serwerach, chwaląc sobie in-dywidualne podejście do klienta oraz specjal-ny program partnerski. Od 4 lat świadczymy także usługi związane z kolokacją serwerów. Oferujemy zarówno możliwość wynajęcia na-szych komputerów jak i wstawienia do serwe-rowi komputera klienta. Poza pełnym wspar-ciem technicznym w procesie instalacji i ad-ministracji serwerami oferujemy na życzenie klienta system backupów, pule adresów IP oraz systemy podtrzymania napięcia. Oferta nasza jest bardzo elastyczna a ceny wyjątkowo atrak-cyjne – podstawowa wersja serwera dedykowa-nego to tylko 199 PLN miesięcznie. Skierowana jest ona do małych i średnich firm, a także klien-tów indywidualnych, którym zależy na wydzie-lonym serwerze przy minimalizacji kosztów ta-kiej usługi. Dodatkowa możliwość wykupienia usług naszych administratorów zapewnia bez-problemową pracę serwerów i systematyczny upgrade zainstalowanego oprogramowania.

Ofertę na serwery dedykowane dla klienta przygotowujemy zawsze w oparciu o analizę aktualnych potrzeb, sugerując najlepsze dla klienta rozwiązanie.

Robert Papis

5 lat temu poszukiwaliśmy dostaw-cy usług internetowych. Firmę Done, świadczącą usługi hostingowe pod mar-

ką Ultra.pl, wybraliśmy spośród kilku kandy-datów. DotychczasoOd dostawcy oczekujemy

świadczenia wysokiej jakości usług przy jed-noczesnym zapewnieniu indywidualnego po-dejścia do klienta. Nie byliśmy zainteresowani współpracą z koncernami hostingowymi mię-dzy innymi ze względu na zbyt duże, naszym zdaniem, ryzyko mało elastycznej współpracy. Obawialiśmy się kłopotów z uzyskaniem peł-nej informacji na tematy nas interesujące oraz fachowej pomocy, zwłaszcza jeśli nasze oczeki-wania wykraczałyby poza standardowe proce-dury komunikacji z klientem. Dlatego poszu-kiwaliśmy niewielkiej firmy, która miałaby do-świadczenie, działała na rynku od kilku lat oraz posiadała wykwalifikowany personel, zawsze gotowy rozwiązać problemy i, w razie potrze-by, spotkać się z nami, żeby przedyskutować sposoby rozwiązania problemów w bezpośred-niej rozmowie.

Ponieważ w tym samym czasie planowali-śmy modernizację naszego portalu interne-towego, chcieliśmy mieć pewność, że serwer, na którym go umieścimy, spełni nasze ocze-kiwania pod względem technologicznym oraz będzie wyposażony w mechanizmy nie-zbędne do poprawnego funkcjonowania stro-ny. Najlepszym w tym przypadku rozwią-

zaniem było skorzystanie z usług webagen-cji, która mogła utworzyć stronę i posiadała sprawdzone zaplecze hostingowe. Firma Do-ne również pod tym względem spełniła na-sze oczekiwania.

Od firmy hostingowej oczekujemy ponad-to umożliwienia nam nieskomplikowanego za-rządzania dostarczonymi usługami (dodawa-nie aliasów, domen, kont pocztowych, okre-ślanie pojemności skrzynek, itp.). Udostępnio-ny nam przez firmę Done, Panel Obsługi Kon-ta Ultra.pl „POK” jest takim centrum dowodze-nia, gdzie bez kłopotu możemy konfigurować usługi z poziomu przeglądarki internetowej. Dostęp do „POK” nie oznacza braku wsparcia ze strony operatora – pracownicy Done w każ-dej chwili są gotowi do rozwiązania ewentual-nych problemów.

W ostatnich latach dużą niedogodnością w korzystaniu z kont pocztowych jest spam. Dzięki temu, że nasz dostawca – Done – trzy-ma rękę na pulsie i systematycznie wprowadza nowe rozwiązania programistyczne problem ten jest ograniczony do minimum.

Sylwia MorstinPenomocnik ds. Zarzdzania Systemem JakociMC Kontrakty Budowlane Sp. z o.o.

Ocena: «««««

Serwery dedykowane

www.phpsolmag.org 73

Współpraca spółki Michalczyk i Pro-kop z firmą Done zaczęła się w końcu 2004 roku. Szukaliśmy

wtedy firmy, która w krótkim czasie przygotu-je serwis dla loterii promocyjnej Oplem pod pal-my. Tak nazywała się akcja wspierająca sprze-daż kalendarzy książkowych, których jeste-śmy wydawcą. Serwis produktowy oraz kam-pania reklamowa na jednym z portali, miały być elementami tego wspólnego projektu. Za-przyjaźniona firma poleciła nam właśnie Do-ne. Już podczas pierwszego spotkania doszli-śmy do porozumienia. Zależało nam nie tylko

na stworzeniu serwisu, ale również na usługach hostingowych oraz wypracowaniu koncepcji re-klam, które miały pojawić się na stronach One-tu. Muszę przyznać, że całość wypadła dość do-brze i co warto dodać - za rozsądne pieniądze. Strona umieszczona na serwerze Ultra, ładowa-ła się bardzo szybko i nie zaliczyliśmy najmniej-szego postoju. To był punkt krytyczny projektu. Nie mieliśmy możliwości próby. Nie sposób by-ło zaaranżować obciążenia serwisu w skali, ja-kiej się spodziewaliśmy. Z tego, co pamiętam Done również nie posiadała doświadczeń w te-go rodzaju kampaniach. Kolejnym elementem przedsięwzięcia był monitoring ruchu genero-wanego przy pomocy banerów, wyświetlanych na stronach Onetu. Done zaproponowała nam wtedy dobre rozwiązanie, dzięki któremu mie-liśmy dostęp do obszernych statystyk. Widzie-liśmy, który baner zapewnia wizyty i czy klik-nięcia na portalu przekładają się na prawdzi-wych gości. Całość prac informatycznych zo-stała oparta na systemie Moebius - autorskim rozwiązaniu firmy Done. To była nasza pierw-sza kampania oparta na reklamie internetowej i muszę przyznać, że Done bardzo pomogła w jej przeprowadzeniu. Dużym plusem było kre-atywne zaangażowanie w projekt. Graficy Do-ne opracowali kilka animacji, które osiągnęły wskaźnik CTR (Click Through Ratio - liczba kliknięć w reklamę do liczby jej wyświetleń) na dość dobrym poziomie, przewyższającym prze-ciętne wyniki tego rodzaju reklam, i dużo lep-sze od banera, który miał być początkowo fa-worytem.

Udana współpraca przy pierwszym projekcie sprawiła, że wykonanie kolejnych również po-wierzyliśmy Done. Ostatnim z nich było zbu-dowanie serwisu dla marki Tetis oraz jego ho-sting. Tetis to najnowsza linia produktów spół-ki Michalczyk i Prokop, obejmuje galanterię biurową i artykuły piśmiennicze. W tym pro-jekcie zależało nam na sprawnej obsłudze ak-tualizacji baz danych. Kilka razy w roku ofer-ta jest powiększana o kolejne pozycje. Obawiali-śmy się, że ze względu na częstotliwość i zakres zmian, czynności te będą zabierać dużo czasu. Dlatego zdecydowaliśmy się na wygodną usłu-gę serwisową realizowaną przez Done. Po kil-ku miesiącach funkcjonowania strony przyzna-ję, że to był trafny wybór. Jesteśmy zadowole-ni z witryny, a jej aktualizacje przeprowadza-ne są sprawnie. Można powiedzić, że równocze-śnie z drukiem katalogów i co ważne, na pod-stawie tego samego kompletu plików pdf, które otrzymuje drukarnia. Minusem może być dość skomplikowana obsługa panela administracyj-nego, choć w kontekście liczby parametrów cha-rakteryzujących produkt, trudno byłoby zorga-nizować to inaczej.

Podsumowując, jesteśmy zadowoleni ze współpracy z Done i w dalszym ciągu zamie-rzamy korzystać z usług tej firmy. W perspek-tywie najbliższych tygodni będziemy wspólnie realizować dwa nowe serwisy. Jednym z nich będzie korporacyjna strona dla spółki Michal-czyk i Prokop.

Mariusz KasprzakSpecjalista ds. marketinguMichalczyk i Prokop Sp. z o.o.

Ocena: «««««

Jesteśmy niezależnym dostawcą usług ko-lokacji, serwerów dedykowanych oraz centrum przetwarzania danych, Firma

Network Communication zorientowana jest na ciągły rozwój i otwarta na nowe rynki. Wybiegając myślą w przyszłość gwarantuje-my stabilne rozwiązania na dziś i jutro.

Wieloletnie doświadczenie i innowacyjne rozwiązania telekomunikacyjne pozwalają ofe-rować Państwu usługi z uwzględnieniem naj-wyższego poziomu bezpieczeństwa oraz nieza-wodności. Jakość i konkurencyjność naszych usług została doceniona przez wielu renomo-wanych klientów, dla których nowoczesne i

niezawodne kanały komunikacji są podstawą funkcjonowania na rynku. Jesteśmy jedną z pierwszych firm zajmujących się sprzedażą ser-werów dedykowanych zlokalizowanych w Pol-sce. Posiadamy ponad 6 letnie doświadczenie na rynku usług hostingowych oraz ponad 9 letnie związane z usługami teleinformatycznymi.

Od samego początku działalności stawiali-śmy ogromny nacisk na świadczenie usług na najwyższym poziomie. Dzięki bogatemu do-świadczeniu wypracowaliśmy unikalne roz-wiązania gwarantujące naszym klientom wy-soką niezawodność oferowanych rozwiązań.

Network Communication to obecnie zespół wykwalifikowanych pracowników w skład któ-rych wchodzą administratorzy, technicy, pra-cownicy helpdesku oraz pracownicy działu marketingu.

Stale podnosząc swoją kwalifikacje dążymy do zapewnienia obsługi klienta wychodzą-cej na przeciw rosnącym wymaganiom dy-namicznie zmieniającego się rynku. Network Communication to także nowoczesna infra-struktura, jako jedna z nielicznych firm tego sektora posiadamy własne Centrum Danych obsługujące na powierzchni ponad 300m2 ponad trzy tysiące klientów z sektora małych i średnich przedsiębiorstw.

Strona domowa Network Communication

3/2007

Testy konsumenckie

74

Z Network Communication współpracu-jemy prawie rok, a firma ta jest mi oso-biście znana już od końcówki 2004 ro-

ku, kiedy wykupiłem tam, mały prywatny ser-wer dedykowany.

Po wybitnie nieudanej współpracy z po-przednim usługodawcą, zależało nam przede wszystkim na jakości – cena była drugorzęd-ną sprawą. Zaczęliśmy intensywnie przeszuki-wać zasoby internetu. Głównie szukaliśmy na przeróżnych forach internetowych gdyż tam opisanych jest najwięcej firm tego typu. W zakresie serwerów dedykowanych wybór jest dosyć ubogi – większość firm pośredniczy w sprzedaży serwerów zlokalizowanych za gra-nicą i nie ma własnego zaplecza. Nie braliśmy pod uwagę kupowania u polskiego pośrednika. zagranicznej serwerowni, gdyż to wiązałoby się z gorszą jakość pomocy technicznej. Priory-tetem dla nas było to, aby dzierżawiona maszy-na znajdowała się w polskiej serwerowni, gdyż przeciętny klient na pewno odczuje różnicę w czasie dostępu do serwera (ping) między Pol-ską, a np. USA. Poza tym mając Polskiej do-stawce nie istnieje żadna bariera językowa, a wszelkie prośby i problemy można dokładniej i szybciej przekazywać.

Decyzja o wyborze usługodawcy okazała się stosunkowo prosta. Generalnie NcGro-up.pl miało (i z tego co się orientuje dalej ma) bezkonkurencyjną ofertę – największą ilość maszyn w ofercie, oraz wszelkiego typu usług dodatkowych, więc decyzja o wyborze usłu-godawcy był stosunkowo prosta. Na oku mie-liśmy też inną dosyć sporą firmę, ale ta od-straszyła nas prowizorką rozwiązań technicz-nych i słabymi zabezpieczeniami w swojej serwerowni, które nie umywały się do wszel-kich systemów przeciwpożarowych, oraz an-tywłamaniowych zastosowanych w Network Communication. Zniechęciła nas również strona tamtej firmy, która wykonana była na szablonie, który widzieliśmy już nie raz w In-

ternecie. Wychodzimy z założenie, że jeże-li ktoś nie inwestuje w swój wizerunek, to prawdopodobnie nie dba również o jakość swoich usług. Jednogłośnie stwierdziliśmy, że NcGroup.pl jest jedyną firmą tego typu w Polsce, mające tak zaawansowane własne za-plecze technologiczne. Do wyboru skłoniła nas jeszcze jedna bardzo istotna sprawa – do-stęp do serwera, na który możemy wgrywać kopie danych (backupy). Korzystamy z tego i co 24 godziny robimy kopie zapasowe danych naszych klientów, oraz całego systemu, dzię-ki czemu zarówno nasi klienci jak i my śpimy spokojnie. Na razie pisałem, dlaczego Jor.pl wybrało NcGroup.pl, pisząc tylko o jakości i mnogości usług, ale nie ukrywamy, że kal-kulacja kosztów współpracy była dla nas mi-łą niespodzianką. W tej kwestii NC też zosta-wiło konkurencję w tyle. Ceny nam zaofero-wane były bardzo atrakcyjne. Na dodatek mo-gliśmy podać parametry serwera, jaki chcieli-byśmy otrzymać, a którego nie było w ofercie, wtedy jeden z pracowników firmy szybko za-proponował nam ciekawą ofertę w dobrej ce-nie. Po dokładnej analizie oferty, bez żadnych wątpliwości kupiliśmy nasz pierwszy firmy serwer w NcGroup.pl.

Już po pierwszych 2 tygodniach przeko-naliśmy się o tym, że dokonaliśmy słusznego wyboru, gdyż zostaliśmy zaatakowani z kil-kudziesięciu serwerów atakiem typu DDOS. Nasz serwer nie wyrabiał z obsługą tak wie-lu połączeń i zapytań, więc korzystanie z nie-go było praktycznie nie możliwe (przeciętna strona ładowała się 20 minut). Nasi admini-stratorzy mimo starań nie byli w stanie zablo-kować wszystkich ataków, więc wykonaliśmy telefon do NC i z prośbą o pomoc. Mimo iż była wtedy niedziela wieczorem, dwóch ad-ministratorów z serwerowni zostało zaanga-żowanych w nasz problem. Pracowali oni do późnych godzin nocnych, ale udało im się za-blokować ruch ze wszystkich atakujących ser-werów, oraz cały ruch z USA (spora część ata-kujących maszyn była właśnie z stamtąd). Dzięki tym „Serwerowym gwiezdnym woj-nom”, które były dosyć niecodzienną próbą przekonaliśmy się, że współpracujemy z pro-fesjonalistami i że nasz serwer jest bezpiecz-ny pod każdym względem. Zdarzyło nam się również pewien problem z dyskiem, któ-ry zachowywał się dosyć dziwnie i jeden z na-szych administratorów stwierdził, że może on się wkrótce zepsuć. Mimo iż była godzi-na 3 w nocy (nie mogliśmy sobie pozwolić na awarię dysku o żadnej porze dnia i nocy) za-dzwoniliśmy do serwerowni i poinformowa-liśmy o problemie. Jakie było moje zdziwie-nie gdy w ciągu niespełna godziny w naszym serwerze był już nowy dysk wraz ze wszystki-mi danymi skopiowanymi z wadliwego nośni-ka! Te dwie wymienione wyżej sytuacje prze-konały nas, że nie mogliśmy dokonać lepsze-go wyboru. Jednak, aby tak bardzo nie sło-

dzić muszę napisać o jednej awarii, jaka mia-ła miejsce. Otóż kilka miesięcy temu nasz ser-wer był niedostępny z powodu krótkiej awa-rii zasilania, przez którą spalił się nasz zasi-lacz. Od około północy do 8 rano (o tej godzi-nie zasilacz został wymieniony) serwer nie działał. W tej sytuacji firma jak zwykle zacho-wała klasę i otrzymaliśmy stosowną rekom-pensatę w postaci bezpłatnego przedłużenia naszego abonamentu. Jeżeli już piszę o re-kompensacie, to NcGroup.pl oferuje również ciekawą opcje ubezpieczenia danych serwera na dowolną kwotę! Dzięki temu w razie utra-ty danych otrzymujemy odszkodowanie, pro-porcjonalne do składki ubezpieczenia.

Pragnę również pochwalić samego właści-ciela firmy, Pana Przemysława Soleckiego, z którym miałem okazje kilka razy rozmawiać przez telefon. Jest to bardzo kompetentna osoba, która zawsze załatwia wszystko „od rę-ki”, a zarazem potrafi rozluźnić nieco atmos-ferę w sytuacjach stresowych (ataki DDOS). Zresztą również inny pracownicy byli rów-nież bardzo mili i pomocni, co jest ważnym plusem.

We wrześniu 2006 przejęliśmy markę Ne-tiro.pl i wtedy oczywiście bez wahania kupi-liśmy kolejną maszynę w tej samej serwerow-ni, w której znajduje się Jor.pl. Aktualnie w NcGroup.pl posiadamy 2 serwery udostęp-nione dla naszych klientów, 2 serwery na kopie danych, oraz 1 serwer dodatkowy dla klientów którzy mają nieco większe witryny/portale, generujące spore obciążenie i co za tym idzie nie mogące współdzielić jednej z dwóch głównych maszyn.

Ogólnie jesteśmy bardzo zadowoleni ze współpracy z Network Communication i mo-żemy tą firmę z czystym sumieniem każdemu polecić. Już teraz przymierzamy się do kupna kolejnej maszyny do użytku dla naszych klien-tów, a w nieco dalszej przyszłości do wykupie-nia usługi kolokacji, gdyż obserwując dynamicz-ny rozwój naszej firmy, taka usługa będzie nie-zbędna.

Jesteśmy spokojni o jakości usług, planujemy rozszerzyć dalej współpracę z NC, oferując na-szym klientom serwery dedykowane, oraz ser-wery VPS, w oparciu o ich zaplecze technolo-giczne. Poza tym wkrótce otwieram spory por-tal biznesowy - wtedy również będę potrzebo-wał kolejnej prywatnej maszyny i również nie mam wątpliwości gdzie ją kupię.

Reasumując dochodzę do konkluzji, iż znaleźliśmy usługodawcę, który zapew-ni nam stabilność i bezpieczeństwo serwe-ra, oraz danych na nim zawartych, a jedno-cześnie sprawną, fachową pomoc technicz-ną. Niewątpliwie Network Communication jest świetnym partnerem w biznesie - poma-ga rozwijać mi zarówno Jor.pl, jak i prywat-ne projekty.

Kacper Bisanz

Jor.pl

Ocena: «««««

Serwery dedykowane

Jesteśmy małą, lecz dynamicznie rozwijają-cą się firmą hostingową. Naszą ofertę kieruje-my do małych i średnich przedsiębiorstw. Wy-

bór kluczowego partnera i odpowiedniego centrum danych nie był dla nas łatwy, a pod uwagę braliśmy bardzo wiele czynników. Główne z nich to oczy-wiście jakość, cena oraz poziom obsługi technicz-nej. Ponieważ nie obsługujemy klientów indywidu-alnych, lecz klientów biznesowych - to właśnie sto-sunek cena/jakość miał dla nas kluczowe znaczenie. Nie na wszystkie z dostępnych na rynku rozwiązań było nas stać, chociaż w poszukiwaniu odpowied-niej serwerowni braliśmy pod uwagę oferty tych

największych w kraju, zlokalizowanych w Warsza-wie, Wrocławiu oraz Gdańsku. Poszukiwaliśmy ela-stycznych rozwiązań, a po długotrwałych negocja-cjach, wybór padł właśnie na Łódź i Network Com-munication. Interesowała nas nie tylko oferta serwe-rów dedykowanych, ale także możliwość kolokacji posiadanego już przez nas sprzętu. Odległość samej serwerowni od naszej firmy (45km), a także moż-liwości szybkiego dojazdu okazały się jednym z do-datkowych plusów. Już na samym początku zostali-śmy mile zaskoczeni. Firma Network Communica-tion wykazała się niezwykle indywidualnym podej-ściem do klienta. Określiliśmy dokładnie nasze po-trzeby i oczekiwania i dostaliśmy czego chcieliśmy.

Jak wyglądały nasze pierwsze dni współpracy z firmą Network Communication? Zacznę od same-go początku, czyli od dostarczenia serwera do cen-trum danych. O ile dojazd do serwerowni, dzięki jej atrakcyjnej lokalizacji okazał się bardzo prosty, to znalezienie właściwego wejścia do budynku stało się już dla mnie małym problemem. Telefon do biura rozwiązał jednak mój kłopot i kiedy udało mi się już dotrzeć na odpowiednie piętro do odpowiednich drzwi, powitał mnie w nich uśmiechnięty człowiek- jak się okazało, był to Przemek Solecki, czyli właści-ciel firmy. Podpisanie przygotowanej już wcześniej umowy stało się w zasadzie tylko formalnością. Od razu udało się także podpiąć do sieci przywieziony przeze mnie serwer, w przygotowanym dla niego miejscu, w jednym z pomieszczeń serwerowni. Do-stępność usługi od zaraz to pierwszy miły ukłon fir-my w stronę klienta. Rozpoczął się więc pierwszy, wedle umowy- testowy miesiąc korzystania z usług kolokacji. Nieuczciwie byłoby z mojej strony, gdy-

bym napisał, że obyło się bez problemów. Podczas pierwszych dni mieliśmy wyraźnego pecha i załapa-liśmy się na 3 krótkotrwałe awarie łącza - w dodatku niestety w środku dnia. Co prawda, plusem jest to, że bez problemu udawało się dodzwonić do admi-nistratora i uzyskać stosowne wyjaśnienia, ale mimo tego pierwszy miesiąc zakończyliśmy z niezbyt do-brymi wrażeniami i mieszanymi uczuciami. W ra-mach przeprosin i wedle wynegocjowanych indywi-dualnie warunków umowy- otrzymaliśmy fakturę za pierwszy miesiąc kolokacji opiewającą na kwotę symbolicznej złotówki netto. Mimo niezbyt udane-go początku, postanowiliśmy zostać i zobaczyć jak będzie dalej... nie żałujemy. Dziś śmiało możemy powiedzieć, że dokonaliśmy dobrego wyboru.

Awarie zdarzają się wszędzie i ważne jest by być na nie przygotowanym. Nie tak dawna przecież ogólnopolska awaria sieci GTS Energis w zasadzie nas nie dotknęła, gdyż dzięki BGP cały ruch prze-łączony został na łącze awaryjne zestawione nam przez Crowley Data Poland. Jak każda firma, tak-że i Network Communication ma swoje plusy i mi-nusy, a także dobre i złe opinie. Ja proponuję jednak sprawdzić zawsze osobiście. Jednym z największych plusów jest tu indywidualne podejście do klienta, ale także całodobowy dostęp do serwerowni, możli-wość kontaktu bezpośrednio z właścicielem firmy, oraz... naprawdę dobra kawa. Są to cechy, których trudno szukać w dużych korporacjach. Mam więc nadzieję, że profesjonalizm i miła atmosfera współ-pracy jakiej doznałem, znajdzie także swoje odzwier-ciedlenie przy obsłudze innych klientów.

Krzysztof Kaczyński

P.H.U. DustNet

Ocena: «««««

R E K L A M A

76 3/2007

Recenzje

Systemy zarządzania treścią (CMS-y) cieszą się niesłabnącą popu-larnością. Umożliwiają stworzenie profesjonalnego serwisu interne-towego, zarówno dla firmy jak i prywatnego użytkownika, bez po-święcania dużej ilości czasu czy też pieniędzy. System Joomla! jest jednym z nich. Książka “Joomla! System zarządzania treścią” jest pozycją dla każdego, kto chce poznać zasady działania systemów CMS. Autor zaczyna od historii Joomli! i muszę przyznać, że cho-ciaż jest krótka, to jednak bardzo interesująca. Następnie omówio-na jest, krok po kroku, instalacja systemu na lokalnym serwerze. Pro-ces ten opisany jest w jasny sposób, nikt więc nie powinien mieć pro-blemów z przebrnięciem przez tą część książki. Po instalacji przecho-dzimy do najobszerniejszej części, czyli do opisu modułów. Przedsta-wione tu zostały podstawowe opcje konfiguracyjne, zmieniające bu-dowę strony, jak i oddzielne moduły typu forum, galeria czy nawet sklep internetowy. Wszystkie większe moduły są rzeczowo opisane. Po tym wszystkim dochodzimy do, teoretycznie najciekawszej, czę-

ści książki tzn. do tworzenia własnych szablonów i rozszerzeń. Autor postanowił przeznaczyć na to 50 stron, co zdecydowanie nie wystar-cza na omówienie tego tematu. Poza tym, aby skorzystać z wiedzy przekazywanej w tej części, musielibyśmy też dysponować progra-mem Macromedia Dreamweaver (w wersji MX 2004 lub wyższej), co z pewnością stanowi barierę dla niektórych osób. Należy odnoto-wać, że konstrukcja poszczególnych działów może sprawiać proble-my przy próbie szybkiego odnalezienia szukanej informacji. Naduży-wane są linie oddzielające treść, przez co w konstrukcji stron panuje spory bałagan. Książka przypomina trochę poradnik encyklopedycz-ny, a nie każdemu to odpowiada. Każdy kolejny dział jest potraktowa-ny jako oddzielny element, dzięki czemu nie musimy wertować całej książki, aby zrozumieć interesujący nas fragment. Takie podejście do tematu sprawia, że nie uczymy się, jak stworzyć stronę, ale poznaje-my, z czego jest ona zbudowana.

Zrecenzował: Łukasz Skowroński

Autor: Hagen GrafISBN: 83-246-0643-2Ocena: 4

Książka napisana jest przez polskich autorów z uwzględnieniem polskich realiów i rynku. Nie jest to pozycja dla programistów, a więc, dla kogo? Większa część książki adresowana jest do właści-cieli sklepów lub osób, które taki sklep chciałyby posiadać. W czę-ści poświęconej marketingowi znajdziemy wiedzę przydatną przy prowadzeniu sklepu, podaną jednak przystępniej niż w książkach do marketingu. Autor powie nam tu, o specyficznych dla sklepów internetowych rzeczach, takich jak np. zaufanie klienta do sklepu (jak je zbudować i utrzymać). Autor wtajemnicza czytelnika w spo-soby zdobywania klientów i sprzedawania produktów m.in. po-przez wyszukiwarki internetowe, programy partnerskie, katalo-gi sklepów, porównywarki, aukcje internetowe. Każdy z punków jest omówiony, w kontekście sklepu internetowego, z ujęciem je-go znaczenia obecnie i w przyszłości. Czytelnik dowiaduje się, ja-kie znaczenie mają dane statystyczne, skąd je czerpać i jak skutecz-nie wykorzystać. W książce została również omówiona personali-

zacja sklepu, co warto personalizować i jakie to może mieć konse-kwencje (niekoniecznie pozytywne). Dobrym pomysłem jest osza-cowanie kosztów i zysków hipotetycznego sklepu, pomoże to przy-szłym właścicielom uświadomić sobie, z jakimi kosztami mają do czynienia. Dowiadujemy się również jak obsługiwać klienta, aby był zadowolony i jakimi kanałami możemy się z nim komuniko-wać z uwzględnieniem kosztów i wygody obu stron transakcji. Dużo miejsca poświęcono również działaniom promocyjnym, ja-kie można, a nawet trzeba, podjąć prowadząc sklep internetowy. Książka zawiera również część, poświęconą użyteczności sklepów internetowych. Ten fragment, moim zdaniem, skierowany jest ra-czej do projektantów sklepów niż do samych właścicieli. Znajdzie-my tutaj wskazówki m.in. jak i gdzie rozmieścić elementy sklepu, jak zbudować koszyk i przyjazny proces składania zamówienia, jak stworzyć intuicyjną pomoc.

Zrecenzował: Łukasz Witczak

Joomla! System zarządzania treścią

Autorzy: Wojciech Kyciak, Karol PrzeliorzISBN: 83-246-0300-XOcena: 4

Jak założyć skuteczny i dochodowy sklep internetowy

Felieton

3/200778

Ogłoszeń o pracę dla informatyków nie brakuje. Na każdym kroku natrafiamy na oferty kuszące dobrymi zarobkami i możliwościami rozwoju. Rzekomo wszędzie otwierają się

przed nami najszersze perspektywy i rzeki pieniędzy, ale zdrowy roz-sądek podpowiada, że wszystko to może okazać się tylko zwyczajową polską reklamą. W końcu pracodawcy zależy na zdobyciu najlepszego pracownika, więc czasem musi trochę przebarwić, aby pozyskać zain-teresowanie ogłoszeniem. W serwisach pośredniczących w zatrudnie-niu aż roi się od ofert dla informatyków. Niektórzy pracodawcy wycią-gają rękę do absolwentów uczelni kształcących się na kierunkach infor-matycznych, chcąc zwerbować ludzi, którzy znają się na rzeczy, choć może brakować im praktyki w profesjonalnej firmie. Należy w tym mo-mencie podkreślić, że informatykiem nie musi być wcale osoba z infor-matycznym wykształceniem oraz że nie każdy, kto ukończył studia in-formatyczne od razu staje się informatykiem. Na rynku pracy nie bra-kuje ofert, pomimo to stan wiedzy każdego z nas zostanie odpowiednio zweryfikowany przed zatrudnieniem, ponieważ musimy spełniać okre-ślone wymagania, ale z drugiej strony czy na każdą ofertę warto od-powiedzieć?

Na mojej uczelni ogłoszenia związane z pracą dla informatyków nie są rzadkością, ale jasno można stwierdzić, że duża część z nich jest po prostu śmieszna i zajmuje tylko miejsce na uczelnianej tablicy ogło-szeń. Jak można poważnie potraktować ogłoszenie typu: “Zatrudnię in-formatyka. Wymagana bardzo dobra znajomość programów C++ i Ja-va. Zarobki 10tys. zł. Praca w Holandii”. Dobre na poprawienie humo-ru – prawda? Co to za pracodawca, który nawet nie wie, czego szuka! Programy C++ i Java? To chyba coś nowego. Takim ogłoszeniem nikt rozsądny się nie zainteresuje. Praca za granicą to już kwestia indywi-dualna każdego z nas. Jednej osobie to pasuje inna powie, że może za-robić tyle nie wyjeżdżając z kraju. Moim zdaniem wyjazd za granice do pracy organizowanej przez tak wyspecjalizowanego pracodawcę to jak wyjazd w ciemno.

Kolejnym przykładem “ciekawego” ogłoszenia jest treść typu: ” Zatrudnię informatyka. Wymagana znajomość: PHP, MySQL, XHTML, CSS, Flash, JavaScript, C++, Corel, Photoshop, Linux. Umie-jętność postawienia i administrowania siecią. Kontakt ...”. Tutaj pra-codawca się chyba lekko zagalopował. Albo może nie starczyło mu już kartki do dopisanie kolejnych wymogów? Pewnie znajdzie się omnibus, który będzie posiadać dogłębną wiedze z każdego zagad-nienia, ale czy można to nazwać realnymi wymogami? Poza tym, jak dobrze wiemy, jeżeli coś jest od wszystkiego to jest do niczego. Jak można nadążyć za nowościami z dziesięciu różnych technologii? Co chwila pojawiają się nowe wersje oprogramowania, trochę rzadziej zmieniają się standardy.

Ale czy nas wszechwiedzący da radę? Moim zdaniem nie. Jeże-li chcemy być w czymś dobrzy dążymy do tego żeby się wyspecjali-

zować w konkretnym kierunku. Jak tak dalej pójdzie to Informatyk będzie musiał umieć dodatkowo wykopać rów na okablowanie, na-prawić hydraulike albo może nawet szydełkować?! Nigdy nie wia-domo, co ktoś wymyśli. Pracodawcy – co wcale nie dziwi – szuka-ją oszczędności starając się zatrudnić ludzi, którzy zrobią wszystko i to jak najtaniej.

Zasadniczo taniej wychodzi zatrudnić jednego człowieka, który umie zrobić podstawowe rzeczy niż np. dwóch lub trzech specjali-stów, ale czy w dalszej perspektywie się to opłaci? Część polskich firm (w szczególności te większe) oraz zagraniczne koncerny, które tworzą swoje placówki w Polsce, już dawno zmieniły sposób werbo-wania kadry.

Poszukują specjalistów z dwóch, trzech dziedzin, dzięki czemu mają pewność, że zadania zostaną wykonane z dużą dbałością o szczegóły i w parze z najnowszymi technologiami. Poważne firmy zapewniają dobre zarobki, możliwości rozwoju w konkretnym kie-runku tak, aby być jeszcze lepszym, a co za tym idzie przynosić większe zyski nie tylko sobie, ale i pracodawcy. Dodatkowo w takich firmach możemy liczyć na szkolenia, które dodatkowo uczynią z nas jeszcze lepszych znawców tematu.

Początkujący informatyk chwyta się każdej nadarzającej okazji, aby zdobyć doświadczenie. Warto jednak przed podjęciem pracy zadać sobie pytanie: czy nie zmarnujemy swojego potencjału tracąc czas na praktykę w nie rozwojowym projekcie czy też prace u osoby nierozróżniającej programowania w C++ od programu w C++? Gdy uznamy, że jednak nie warto, a sądzę, że tak odpowie większość, trzeba zastanowić się jak zaistnieć w dużej firmie. Większe koncer-ny bardzo często oferują staże i praktyki. Pomimo, że często nie są one płatne, warto spróbować. Może dzięki takiemu stażowi otwo-rzymy sobie drzwi do pracy, a może dojdziemy do wniosku, że to jednak zajęcie nie dla nas i wrócimy do punktu wyjścia.

Doświadczeni informatycy – najczęściej programiści, dobrze wie-dzą gdzie chcą pracować i za jakie pieniądze. Faktem jest, że te ce-le osiągają. Dzieje się tak, bo jasno określili sobie dziedzinę, w której chcą być najlepsi. Dążą do tego ceniąc swoją prace i czas, który prze-cież w sposób prosty i przyjemny przeliczają na pieniądze.

Jestem pewien, że temat pracodawcy dla każdego z nas wygląda in-aczej. Polemika na ten temat jest nieunikniona. Każdy z nas jest inny i ma inne wymagania. Jeżeli nie znajdziemy pracodawcy dla siebie, za-wsze zostaje nam jeszcze samo zatrudnienie ... chociaż znając polskie realia, to raczej mało prawdopodobna droga.

Zatrudnię informatyka ...

ŁUKASZ SKOWROŃSKIAutor jest studentem III roku informatyki na Uniwersytecie w Białymstoku.Kontakt z autorem: [email protected]

Zaprenumeruj swoje ulubione magazyny i zamów archiwalne numery!

Już teraz w kilka minut możesz zaprenumerować swoje ulubione pismo.Gwarantujemy:- preferencyjne ceny- bezpieczną płatność on-line- szybką realizację Twojego zamówienia Bezpieczna prenumerata on-line wszystkich tytułów Wydawnictwa Software!

www.buyitpress.com zamówienie prenumeraty

Prosimy wypełnić czytelnie i przesłać faksem na numer: (22) 887 10 11 lub listownie na adres: Software-Wydawnictwo Sp. z o.o., Bokserska 1, 02-682 Warszawa, e-mail: [email protected]. Przyjmujemy też zamówienia telefoniczne: (22) 887 14 44

Imię i nazwisko............................................................................................ ID kontrahenta..........................................................................................

Nazwa firmy................................................................................................. Numer NIP firmy.......................................................................................

Dokładny adres....................................................................................................................................................................................................................

Telefon (wraz z numerem kierunkowym)................................................... Faks (wraz z numerem kierunkowym) ....................................................

E-mail (niezbędny do wysłania faktury)............................................................................................................................................................................

zamówienie prenumeraty

1 Cena prenumeraty rocznej dla osób prywatnych 2 Cena prenumeraty rocznej dla osób prenumerujących już Software Developer’s Journal lub Linux+3 Cena prenumeraty dwuletniej Aurox Linux

Jeżeli chcesz zapłacić kartą kredytową, wejdź na stronę naszego sklepu internetowego:

www.buyitpress.com

automatyczne przedłużenie prenumeraty

Suma

Tytuł Ilość numerów

Ilość zamawianych prenumerat

Od numeru pisma lub miesiąca

Opłata w zł

z VATSoftware Developer’s Journal (1 płyta CD)– dawniej Software 2.0Miesięcznik profesjonalnych programistów

12 250/1801

SDJ Extra (od 1 do 4 płyt CD lub DVD)– dawniej Software 2.0 Extra!Numery tematyczne dla programistów

6 150/1352

Linux+DVD (2 płyty DVD)Miesięcznik o systemie Linux 12 199/1791

Linux+Extra! (od 1 do 7 płyt CD lub DVD)Numery specjalne z najpopularniejszymi dystrybucjami Linuksa 8 232/1982

PHP Solutions (1 płyta CD)Dwumiesięcznik o zastosowaniach języka PHP 6 135

hakin9, jak się obronić (1 płyta CD)Miesięcznik o bezpieczeństwie i hakingu 12 1991/219

.psd (1 płyta CD + film instruktażowy)Dwumiesięcznik użytkowników programu Adobe Photoshop 6 140

.psd numery specjalne (.psd Extra + .psd Starter Kit) 6 140