You are on page 1of 84

SPIS TREŚCI

06 AKTUALNOŚCI
Daniel Ancuta

08 OPIS CD

10 TAM BYLIŚMY
Relacja z polskiej premiery C++Buildera 2007

DLA POCZĄTKUJĄCYCH
12 MySQL
Krzysztof Trynkiewicz
Krzysztof opisuje bazę danych – MySQL. Dzięki autorowi poznasz spo-
soby wykonywania większości najpotrzebniejszych operacji na bazie
danych MySQL. Po przeczytaniu tego artykułu dowiesz się wszystkie-
go o instalacji, tworzeniu warstw i tabel, aktualizacji oraz optymaliza-
cji danych MySQL.

22 WordPress
Konrad Gołuchowski
Konrad pokaże w jaki sposób zbudować własną skórkę do systemu
WordPress. W przystępny sposób przedstawia jak zbudowane są sza-
blony oraz jakie możliwości daje nam stworzenie własnej skórki. Za-
prezentuje najczęściej używane funkcje i nauczy Cię wykorzystywać
komentarze.

30 Symfony Framework
Łukasz Klejnberg
Łukasz pokaże Ci w przystępny sposób, jak zainstalować i skonfiguro-
wać Symfony, a na przykładzie prostej strony WWW zapoznasz się z
możliwościami tego frameworka. Między innymi dowiesz się jak wyko-
nać rejestrację nowego użytkownika oraz w jaki sposób można spraw-
dzić dane przesłane z formularza.

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

Dyrektor wydawniczy: Sylwia Pogroszewska

Redaktor naczelny: Patrycja Wądołowska patrycja.wadolowska@software.com.pl

Redaktorzy: Magdalena Sobiś magdalena.sobis@software.com.pl


Anna Kozioł anna.koziol@software.com.pl

Korekta: Przemka Skudniewska przemka@gmail.com

Kierownik produkcji: Marta Kurpiewska marta.kurpiewska@software.com.pl

Projekt okładki: Agnieszka Marchocka

Skład i łamanie: Robert Zadrożny robert.zadrozny@software.com.pl

Dział reklamy: adv@software.com.pl

Prenumerata: Marzena Dmowska pren@software.com.pl

Nakład: 6 000 egz.

4 05/2007
PRAKTYKA NARZĘDZIA
38 Własny system statystyk 56 SciTE
Łukasz Borchmann Robert Zajda
Łukasz przedstawi Ci sposób na szybkie pobieranie informacji doty- Robert pokaże Ci, jak wykorzystać zaawansowane możliwości edy-
czących gościa, jego oprogramowania oraz ustawień. Pokaże Ci, jak tora SciTE oraz jak skonfigurować go do pracy z językiem PHP. Mu-
sprawdzić źródła ruchu, dokonać segregacji ze względu na typ (odno- sisz posiadać podstawową znajomość PHP oraz narzędzi do tworze-
śnik,wyszukiwarka, bezpośredni). Dowiesz się również, jak wyciągać nia kodu.
słowa kluczowe oraz zbadać pozycję w wynikach organicznych wy-
szukiwarek. 60 jQuery
Dariusz Duszyński
Dariusz opisuje bibliotekę javascript, która m.in. tworzy dynamiczne
TECHNIKA efekty graficzne na stronie internetowej oraz daje możliwość wykony-
42 Operacje na tekście wania zapytań asynchronicznych.
Michał Gacki
Michał opisuje podstawy wyświetlania tekstu w PHP oraz funkcje, któ- 66 WordPress i punBB
re umożliwiają zamianę pewnych fragmentów tekstu lub samych kon- Piotr Maliński
kretnych znaków. Pokaże Ci, jak sprawdzić czy w tekście istnieje dany Piotr w pierwszym artykule z serii „Warsztat Programisty” przedsta-
ciąg znaków według podanych kryteriów, zmienić kodowanie tekstu wi Ci sposoby integrowania ze sobą dwóch skryptów na przykładzie
itp. Ponadto znajdziesz tu kilka ciekawostek związanych z tematem. punBB i CMSa WordPress. Zaprezentuje Ci „klasyczny” sposób inte-
gracji oraz integracje za pomocą wtyczki WordPress.
46 Formularz internetowy
Tomasz Roszko
Tomek pomoże Ci w przystępny sposób stworzyć interfejs. Poznasz TESTY KONSUMENCKIE
technologię Ajax w jednym z jej praktycznych zastosowań. Będziesz 70 Statystyki zewnętrzne
wiedział jak wysłać i sprawdzić formularz bez przeładowania strony.
75 Recenzje
50 CakePHP Łukasz Skowroński, Sławomir Zadrożny
Piotr Gapiński
Piotr opisuje różne techniki buforowania stron WWW. Jeśli tylko znasz 78 Felieton
Framework CakePHP, język SQL oraz potrafisz administrować bazę Wojciech Sapiechowski
MySQL. Po przeczytaniu tekstu poznasz możliwości ich praktycznego Witryny internetowe – Tylko dla zarobku?
zastosowania w CakePHP.

Adres korespondencyjny:
Software-Wydawnictwo Sp. z o.o., Redakcja używa systemu automatycznego składu
ul. Bokserska 1, 02-682 Warszawa, Polska
tel. +48 22 887 13 45, fax +48 22 887 10 11 Do tworzenia wykresów i diagramów wykorzystano program firmy
www.phpsolmag.org cooperation@software.com.pl

Osoby zainteresowane współpracą prosimy o kontakt:


Dołączoną do magazynu płytę CD przetestowano programem AntiVirenKit firmy cooperation@software.com.pl
G DATA Software Sp. z o.o.
Druk: ArtDruk
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 Wysokość nakładu obejmuje również dodruki. Redakcja nie udziela pomocy
za efekty wykorzystania ich; nie gwarantuje także poprawnego działania programów technicznej w instalowaniu i użytkowaniu programów zamieszczonych na płycie
shareware, freeware i public domain. CD-ROM dostarczonej razem z pismem.

Sprzedaż aktualnych lub archiwalnych numerów pisma po innej cenie niż


Uszkodzone podczas wysyłki płyty wymienia redakcja. wydrukowana na okładce – bez zgody wydawcy – jest działaniem na jego
szkodę i skutkuje odpowiedzialnością sądową.

Wszystkie znaki firmowe zawarte w piśmie są własności odpowiednich firm zostały Pismo ukazuje się w następujących wersjach językowych:
użyte wyłącznie w celach informacyjnych.
polskiej , francuskiej

www.phpsolmag.org 5
Aktualności

Poprawki bezpieczeństwa
WordPress 2.2.1
eZ Componnents 2007.1
e
Została wydana nowa wersja WordPress, Z Componnents to zbiór open sourco- flow. Warto zapoznać się z dokumentacją http://
która to zawiera kilka poprawek błędów wych bibliotek które zna za pewne każ- ez.no/doc/components/overview/latest która poka-
– także tych krytycznych, jak SQL Injection
dy programista PHP. Komponenty zosta- że wam możliwości nowych bibliotek.
czy Shell Injection. Wobec tego zalecamy
aktualizację systemu. ły rozszerzone o dwie nowe biblioteki Authenti-
Źródło: http://wordpress.org/development/ cation który pozwala nam na uwierzytalnianie
2007/06/wordpress-221/ użytkowników na kilka sposobów (poprzez ba- Źródło: http://ez.no/ezcomponents/news/ez_com-
zę danych, LDAP czy też Open ID) oraz Work- ponents_2007_1

HTML Purifier v 2.0.0

Nowa wersja PHP-GTK: 2 Beta


Developerzy PHP-GTK poinformowali na blogu
o wydaniu nowej wersji beta tego rozszerzenia
do PHP. Oto kilka z nowości:

• Wynik pokrycia kodu API przez testy to


teraz 90 procent, z ponad 95-procen-
towym pokryciem tylko dla Gtk+ APIl;
• GtkTreeView widget został zauważalnie
rozbudowany dzięki własnemu dostoso-
walnemu modelowi i wsparciu dla znane-
go nam systemu „przeciągnij i upuść”;
• Bardzo ciekawe nowe cross-platformo-
we widgety już dostępne poprzez Gtk-
Spell, GtkExtra, GtkHTML3, libsexy i roz-
szerzenia Scintilla;
• Wersje 2.8 i 2.10 Gtk+ są teraz wspiera-
ne, dzięki czemu dostajemy wiele nowych
użytecznych funkcji takich, jak: rozbudo- Rysunek 1. Demo przedstawiające podstawową funkcjonalność
wane wsparcie dla drukowania, odmło-

J
dzone GtkIconView i GtkNotebook. ak wiemy, największym wrogiem programi-
sty jest użytkownik. Jego nieświadome błę-
Otrzymujemy także oczekiwane przez nie- dy lub też świadome próby ataku mogą nara-
których wsparcie GtkPlug/Socket dla sys-
zić nas na duże kłopoty. Głównym zagrożeniem
temów Win32. Wśród ciekawszych nowo-
ści znajdziemy także wiele najróżniejszych są formularze. Tutaj przychodzi nam z pomocą
widgetów, jak GtkStatusIcon i GtkAssistant. HTML Purifier, który jest biblioteką wartą zainte-
Dodatkowo poprawiono wiele znanych resowania każdego programisty PHP. Umożliwia
błędów oraz dodano kilka pomniejszych ona programiście kontrolowanie wprowadzanego
usprawnień. Rozszerzenie możecie ściągnąć
przez użytkownika kodu HTML. Funkcje biblio-
z strony http://www.gtk.php.net.
Źródło: http:/www.gtk.php.net teki, o których warto wspomnieć, to m.in.:
Rysunek 2. Strona główna, biblioteki
Symfony 1.0.5 • usuwanie kodu XSS; nie jej do swoich potrzeb. Jjeśli mimo to za-
Wydana została nowa wersja jednego z naj- • uzupełnianie niezamkniętych tagów; braknie nam jakiejś funkcjonalności, zawsze
popularniejszych frameworków – Symfony,
• zamienienie przestarzałych elementów na możemy ją zaimplementować, gdyż HTML
w wersji 1.0.5 zawiera ona tylko poprawki
znalezionych błędów. zgodne z XHTML; Purifier jest projektem OpenSource (na li-
Źródło: http://www.symfony-project.com/ • uporządkowanie elementów – nie do- cencji LGPL). Na stronie WWW projektu
weblog/2007/06/25/symfony-1-0-5-rele- puszczenia do nieprawidłowego zagnież- znajdziemy sporą dokumentację, która po-
ased-security-fix.html dżania. zwoli nam w konfortowy sposób korzystać z
biblioteki. Jeśli jesteś użytkownikiem CMS-a
Jak widać, biblioteka ta nie tylko zapewnia Drupal czy też MODx, możesz ściągnąć
nam bezpieczeństwo aplikacji, pozwala także wtyczkę do obsługi tej biblioteki.
na zachowanie czystości kodu, dzięki czemu
nie musimy się już bać, że użytkownik znisz- Demo: http://htmlpurifier.org/demo.php
czy nasz kod XHTML czy też wstrzyknie Download: http://htmlpurifier.org/
nam XSS. Aplikacja posiada rozwinięty sys- download.html
tem konfiguracji, co umożliwiadostosowa- Źródło: http://htmlpurifier.org/

6 05/2007
Aktualności

Zend Framework 1.0.0 Zostań członkiem zarządu PHP.pl


Ktoś kiedyś powiedział: „nie wstąpię do klubu,

Z
end już od ponad roku rozwija Swój produkt za najlepszy tego typu na rynku, cóż który nazywa mnie członkiem”, dzisiaj jednak
framework. Spore grono użytkowni- oby im ich pycha nie zaślepiła oczu. każdy z nas ma okazję zostać członkiem zarzą-
du wortalu PHP.pl. Wortal ten znany jest w spo-
ków tej aplikacji doczekało się w koń- łeczności developerów, a istnieje już od wielu
cu wersji stabilnej oznaczonej numerem 1.0.0. lat. Aktualnie serwis poszukuje kandydatów
Wersja ta zawiera sporo poprawek już istnie- Źródło: http://devzone.zend.com/article/2262- do nowo tworzonego zarządu. Do głównych
jących modułów. Sami autorzy uwarzają Swój Zend-Framework-1.0.0-production-release zadań zarządu należeć będzie między innymi:

• reprezentacja PHP.pl – podejmowanie

Agavi RC 5
współpracy z serwisami, firmami;
• nadzorowanie projektów rozwijanych
przez PHP.pl;
• kontrolowanie administratorów;
• organizacja konkursów;
• pomoc w realizacjach podejmowanych
przez członków grupy PHP.pl.

Z niecierpliwością czekamy na roztrzygnię-


cie konkursu, a wszystkim biorącym udział
życzymy powodzenia.
Więcej informacji na temat zasad konkursu
znajdziesz na forum.PHP.pl.
Źródło: http://forum.php.pl/Zmiany-na-
gorze-t70961.html

Rysunek 1. Agavi w serwisie ohloh.net Konferencja PHP w Holandii


Amsterdam stał się ostatnio stolicą PHP.

W
ydana została kolejna wersja Rele- Odbyła się tam konferencja poświęcona roz-
ase Candidate o numerze 5, jed- wojowi języka PHP na świecie. Kilka z tema-
tów poruszanych na konferencji to m.in.:
nego z najlepszych frameworków
PHP, jakim jest Agavi. Jest to ostatnia wersja • Zend Framework – przedstawienie funk-
przed wydaniem finalnym, które ukaże się w lip- cjonalności oraz pokazanie przykłado-
cu. Każda z wersji Agavi utwierdza nas w prze- wych aplikacji;
konaniu, że w PHP także powstają aplikacje w • PHP i Oracle;
• PDO – metody bardziej efektywnej,
pełnym tego słowa znaczeniu. Wraz z kolejny-
przenośnej i bezpiecznej implementacji;
mi wersjami dostajemy dawkę kodu, który zde- • CodeGear Delphi dla PHP;
cydowanie ułatwia nam pracę z tym framewor- • Symfony Framework.
Rysunek 2. Strona www Agavi
kiem lub też dodaje nowe funkcjonalności. Tak
samo jest tym razem, developerzy przygotowa- Jak widzimy, teraz obsługa SOAP staje się dzie- Więcej informacji na temat konferencji można
znależć na stronie: http://devzone.zend.com/
li nam kilka nowych narzędzi oraz usprawnień. cinnie prosta. Wersja ta została wzbogacona:
article/2217-Dutch-PHP-Conference-2007-
Pierwszą rzeczą, o której na pewno warto wspo- Wrap-Up
mnieć, jest dodanie obsługi SOAP. Stworzenie • wsparcie dla przestrzeni nazw w XML; Źródło: http://devzone.zend.com/article/
akcji dostępnej przez SOAP jest naprawdę pro- • wsparcie dla UTF-8 w StringValidator; 2217-Dutch-PHP-Conference-2007-Wrap-Up,
ste, a opiera się na kilku czynnościach: • wsparcie dla XSL stylesheets w plikach kon- http://www.phpconference.nl/
figuracyjnych;
• Dodajemy nowy router zawierający elemen- • parser konfiguracji został przepisany.
ty <wsdl:input> (dla wejścia) oraz <wsdl:out-
put> (dla wyjścia) – z definicją parametrów; Jest to tylko kilka z nowości, po więcej infor-
• Jeśli zdefiniujemy nagłówki w WSDL, macji odsyłam do changeloga.
Agavi doda je automatycznie;
• Z danych ustawionych w routerze Agavi Agavi w Ohloh: http://www.ohloh.net/projects/5907
automatycznie wygeneruje WSDL, który Changelog: http://trac.agavi.org/browser/
może być wykorzystywany przez innych branches/0.11/CHANGELOG
klientów obsługujących protokół. Źródło: http://agavi.org/

www.phpsolmag.org 7
Opis CD

System Statystyk 7point


Płyta zawiera dwa pliki flash z prezentacjami. Pierwsza z nich poka-
zuje proces instalacji statystyk 7point, natomiast druga zawiera de-
monstrację działania samego systemu statystyk. Pokaz można oglą-
dać w dwóch rozdzielczościach 800x600 lub 1024x768.

Prezentacja procesu instalacji


statystyk 7point
Przewodnik, pozwalający prześledzić krok po kroku czynności ko-
nieczne do zainstalowania systemu statystyk 7point. Otwierające się
kolejno okna prezentacji pozwolą łatwo i szybko prześledzić proces in-
stalacji v statystyk od momentu logowania do sytemu, przez konieczne
ustawienia, po proces pobierania i wklejania kodu. Kolejne kroki to po-
branie i wklejenie kodu.Należy pobrać kod danej strefy i wkleić go na Rysunek 1. Pierwszy krok – logowanie ze strony internetowej
odpowiednich pod stronach serwisu. Ostatnie okno prezentacji zawie- www.7point.pl
ra informacje końcowe – użyteczne wskazówki dla każdego Użytkow-
nika systemu statystyk 7point.

Prezentacja systemu statystyk 7point


Podobnie jak pierwsza, stanowi przewodnik dla Użytkowników
systemu 7point – demonstruje szereg jego możliwości. Pokaz zo-
stał podzielony na 7 rozdziałów,

• Użytkownicy strony w liczbach;


• Funkcjonowanie strony w sieci;
• Jakość i atrakcyjność strony;
• Strona w wyszukiwarkach;
• Schematy zachowań w serwisie;
• Serwis na mapie świata;
• Tygodniowy raport.

W każdym z 7 rozdziałów znajduje się szczegółowa prezentacja


funkcji i zastosowań systemu:

• Pierwszy rozdział to informacje na temat użytkowników; Rysunek 2. Po zalogowaniu do systemu należy przejść do ustawień
• Podsumowanie ogólne zawierające zestawienie wybranych
danych z ostatnich 7 dni;
• Szczegółowe informacje na temat użytkowników, wizyt i odsłon.
Ponadto widoczna jest również opcja wybierania zakresu czasu;
• Podział użytkowników wg ilości powrotów;
• Podział użytkowników wg czasu ostatniego powrotu.

Prezentacja kolejnych rozdziałów przebiega analogicznie. Użyt-


kownik może więc zobaczyć kolejne możliwości i zastosowania
systemu statystyk 7point, np.:

• Serwisy linkujące;
• Ścieżki;
• Szczegóły odsłon dla danej strony;
• Słowa kluczowe.

Dodatkowo wszystkie prezentacje wizualne dotyczące statystyk zo-


stały opatrzone komentarzem lektora, co znacznie ułatwia odbiór Rysunek 3. Użytkownik może utworzyć własne strefy i pogrupować
pokazu. strony swojego serwisu

8 05/2007
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: cd@software.com.pl

Redakcja nie udziela pomocy technicznej w instalowaniu


i użytkowaniu programów zamieszczonych na płytach
CD-ROM dostarczonych razem z pismem
Tam byliśmy

Polska premiera C++Buildera 2007


i nie tylko!
W pierwszej połowie czerwca w trzech miastach Polski – Poznaniu, Warszawie
i Krakowie – odbył się cykl bezpłatnych konferencji poświęconych najnowszym
rozwiązaniom firmy CodeGear, w tym środowiska C++Builder® 2007.

O
prócz premierowych pokazów nowego C++Builder® 2007 pozwala tworzyć aplikacje resowanie wśród programistów Delphi. Code-
narzędzia IDE dla programistów C++ Web 2.0 wykorzystujące technologię AJAX Gear nie zamierza poprzestać na pierwszej edy-
zaprezentowano także dostępne na (ang. Asynchronous JavaScript and XML). Nowy cji oprogramowania. Delphi dla PHP wpisze się
rynku od kilku miesięcy środowiska Delphi 2007 zestaw komponentów INDY 10 Internet Proto- na stałe do gamy oferowanych produktów. Pod-
i Delphi for PHP 2007 oraz serwer bazodano- col dostarcza nowe możliwości programistom czas ostatniej części konferencji Bogdan Polak za-
wy InterBase 2007. W pierwszej części konferen- aplikacji internetowych. W kolejnej części kon- prezentował możliwości serwera bazodanowego
cji konsultant techniczny BSC Polska – Bogdan ferencji przedstawiono nowe możliwości środo- InterBase 2007. Nowy InterBase zapewnia wie-
Polak – przedstawił ogólne perspektywy rozwo- wiska Delphi 2007 for Win32, a w szczególno- le korzyści związanych z architekturą wielowąt-
ju produktów firmy CodeGear oraz zaprezento- ści wsparcie dla platformy Microsoft Windows kową. Zastosowanie systemu rejestracji zdarzeń
wał nowe możliwości środowiska C++Builder® Vista®, nową architekturę dostępu do baz da- i procedur przywracania do pracy po awa-
2007. Pod koniec każdego bloku tematycznego nych DBX4, możliwości zarządzania buildami rii gwarantuje wysoki stopień bezpieczeństwa.
przewidziano czas na sesje pytań i odpowiedzi. w oparciu o MSBuild oraz ulepszoną technolo- InterBase jest jednocześnie jedną z najszybszych
C++Builderc 2007 jest jedynym natywnym śro- gię instalacyjną. Delphi 2007 for Win32 wspie- baz danych, która dzięki obsłudze SMP może
dowiskiem typu RAD do programowania w ję- ra wykorzystanie technologii AJAX, pozwalając pracować z wieloma procesorami, w tym wie-
zyku C++ dla systemu Windows. Oferuje pełne na szybkie i wizualne tworzenie interaktywnych lordzeniowymi. InterBase uzyskał certyfikację
wsparcie dla Windows Vista® oraz pozwala pro- aplikacji internetowych. Po raz kolejny duże za- do pracy na systemach Windows, Linux i Solaris.
gramistom na łatwe dostosowanie istniejących już interesowanie uczestników wzbudziło narzę- Pożegnanie z uczestnikami uwieńczyło loso-
aplikacji C++ do wymogów tego systemu. Umoż- dzie Delphi dla PHP – pierwsze narzędzie RAD wanie nagród. Po konferencji każdy z uczestni-
liwia tworzenie nowych aplikacji Windows do programowania aplikacji w języku PHP. Pod- ków mógł skorzystać z unikalnej oferty ceno-
wykorzystujących możliwości interfejsu Vista czas wykładu słuchacze mogli zapoznać się mię- wej przygotowanej specjalnie na tą okazję. Wiele
Aero. Nowa edycja komponentów bazodano- dzy innymi z możliwościami nowego edytora, osób skorzystało także z zaproszenia na bezpłat-
wych dbExpress (DBX4) umożliwia łączenie debuggera oraz zintegrowanej biblioteki VCL ne seminaria on-line dotyczące programowania
się z najnowszymi wersjami serwerów SQL. dla PHP. Nowy produkt wzbudził duże zainte- w Delphi dla PHP.

10 05/2007
Dla początkujących

MySQL
Bazy danych od podstaw

Stworzenie funkcjonalnej i szybkiej aplikacji w dowolnym języku wymaga


sprawnego systemu przechowywania niezbędnych dla niej danych. PHP
obsługuje wiele systemów bazodanowych (Oracle, PostgreSQL, SQLite,
Access itd.), z których niewątpliwie najpopularniejszym jest MySQL.

mu Linux, najprościej skorzystać z plików


Dowiesz się ... Powinieneś wiedzieć... RPM (najlepiej pobrać je z witryny http://
• Dowiesz się, jak wykonywać większość naj- • Przydatna będzie znajomość podstaw PHP www.mysql.com, bowiem te dostarczane z dys-
potrzebniejszych operacji na bazie danych trybucjami często są nieaktualne). Mniej wię-
MySQL cej w tym samym czasie, gdy PHP wkroczyło
w wersję 5, MySQL także opublikowano pod
tym numerem. Zmian w stosunku do wersji 4
baz danych. Jest to niewątpliwą zaletą w jest wiele, dlatego zalecam instalację najnow-
stosunku do języków programistycznych, szej wersji. Pobieramy pliki o nazwach zbli-
Poziom trudności które mają osobne style, składnie i nazwy żonych do MySQL-server-WERSJA.i386.rpm,
funkcji. MySQL-Max-WERSJA.i386.rpm, MySQL-
client-WERSJA.i386.rpm. Instalujemy ser-
Instalacja wer wraz z aplikacją kliencką przez wpisa-

P
ierwszym nasuwającym się pytaniem Podobnie jak w przypadku PHP, MySQL zo- nie w powłoce:
jest: dlaczego właśnie MySQL? Ta baza stał zaprojektowany do działania zdalnego, na
danych oferuje wysoką wydajność, po- serwerze. Do działania nie potrzebuje jednak rpm -i MySQL-server-WERSJA.i386.rpm
twierdzoną licznymi testami – jest to jeden z serwera HTTP, bowiem sam sobie jest serwe- MySQL-client-WERSJA.i386.rpm
najszybciej działających produktów na rynku. rem. Oznacza to, że instalując MySQL, insta-
Jego licencję (GPL) można streścić następują- lujemy aplikację, która działa w tle i oczeku- Nie zapomnijmy zastąpić słowa WERSJA od-
co: jeśli pobieramy opłaty za używanie pro- je na połączenia przez jej protokół. Łączyć z powiednim numerem. MySQL zostanie za-
duktu korzystającego z MySQL, musimy wy- serwerem MySQL możemy się na wiele spo- instalowane, uruchomiony zostanie ser-
kupić płatną licencję. Oznacza to, że do zasto- sobów (przez PHP, phpMyAdmin, czy konso- wer „mysqld” oraz dodany zostanie wpis w /
sowań niekomercyjnych można stosować My- lę kliencką dostarczaną z pakietem instalacyj- etc/init.d/ uruchamiający serwer przy każ-
SQL bez ograniczeń. Już same te dwa argu- nym), niezależnie, czy serwer działa na kom- dym rozruchu komputera. Procedura instala-
menty przemawiają zdecydowanie za używa- puterze lokalnym, czy zdalnym. Mamy więc cji MySQL bez udziału plików RPM jest bar-
niem MySQL. Oczywiście, jest to baza stabil- ponownie do wyboru dwie możliwości: ko- dziej skomplikowana, jednak dobrze opisana
na i łatwa w użyciu (obsługuje składnię poda- rzystać z usług hosting providera (w Interne- w manualu.
ną w standardzie SQL 92, dzięki czemu ucząc cie można znaleźć darmowe i płatne oferty) W przypadku instalacji MySQL na Win-
się jej obsługi, uczymy się także obsługi więk- lub zainstalować MySQL u siebie. W pierw- dowsie, logujemy się jako administrator i po-
szości innych baz danych), posiadająca wie- szym przypadku, będziemy tworzyć użyt- bieramy pakiet. Do wyboru mamy samoin-
le funkcjonalności, która jest cały czas w roz- kownika (dane do logowania) przy pomo- stalujący się plik *.exe lub spakowane archi-
woju. Konkurencja nie pozostaje daleko w ty- cy interfejsu dostarczanego przez provide- wum. Oczywiście, najłatwiej wybrać pierw-
le, jednak praktycznie każdy produkt ma ja- ra (najczęściej przez panel WWW). Otrzy- szą możliwość – przez proces instalacji zo-
kąś wadę: mamy także zapewne zainstalowany system staniemy przeprowadzeni szybko i sprawnie,
phpMyAdmin (dostęp do bazy danych przez wszystkie ustawienia zaaplikuje nam instala-
• PostgreSQL – szybkość; stronę WWW), co znacznie ułatwi sprawdza- tor. W drugim przypadku, możemy wypako-
• SQLite – brak dostępu przez protokół nie wyników wykonywania zapytań. Jeśli ma- wać archiwum i własnoręcznie umieścić my-
zdalny (operuje na plikach lokalnych); my taką możliwość, najprościej będzie z niej sqld-max.exe w autostarcie (to nasz serwer).
• Access – cenę i niską ekonomię. skorzystać – możemy przejść do kolejnej czę- Możemy także zainstalować MySQL jako
ści artykułu. usługę przechodząc przy pomocy wiersza po-
Niemniej nie należy się martwić – systemy W przypadku instalacji MySQL na wła- lecenia do katalogu instalacji, a potem katalo-
baz danych podlegają w większości standar- snym komputerze, niezbędnych będzie kil- gu bin i wydając polecenie mysql-max –stan-
dom, dzięki czemu ucząc się składni MySQL, ka działań. Najpierw musimy zainstalować dalone a następnie mysql-max –install. Pro-
będziemy znać podstawy składni innych serwer MySQL (tzw. daemon). Dla syste- cedura ta mogła jednak zmienić się od czasu

12 05/2007
MySQL od podstaw

napisania artykułu, dlatego zalecam zajrze- blic wielowymiarowych, zmiennych itd.), trze- my wtedy możliwości modyfikacji danych w
nie do manuala w przypadku instalacji z ar- ba więc wiedzieć, jakich komend użyć, by je wy- przypadku przejęcia przez intruza danych do
chiwum. świetlić. Dwie pierwsze możliwości zapewnia- cześciej używanego konta, służącego do odczy-
Manual MySQL zawiera dokładne instruk- ją interfejs, który automatycznie formatuje i tu. Ustanawianie przywilejów jest jednak kwe-
cje instalacji dla wielu systemów (Solaris, wyświetla wyniki zapytań (komend). Narazie stią „dmuchania na zimne” i ma sens jedynie
MacOS itp.) i jest bardzo przystępnie na- więc polecam korzystanie z aplikacji klienckiej w przypadku ważnych danych, nie jest więc w
pisany, dlatego warto się do niego odwoły- (konsoli), ewentualnie panelu phpMyAdmin. gestii tego artykułu – odsyłam do świetnego
wać. Manuale stanowią ogromny atut PHP Po zapoznaniu się z podstawowymi zapytania- opisu polecenia GRANT na http://dev.mysql.com/
i MySQL – są to niewątpliwie jedne z najle- mi, przejdziemy do opisu ich implementacji w doc/refman/5.0/en/grant.html. Dodajmy jesz-
piej opisanych produktów służących do two- PHP. Warto jednak już teraz wspomnieć, że do cze, że w MySQL nie są rozróżniane wielkości
rzenia oprogramowania. W przeciwieństwie niektórych zapytań PHP się po prostu nie nada- liter w poleceniach, jednak w niektórych sys-
do manuali innych języków, czytając je, nie je (właśnie z racji konieczności własnoręcznego temach operacyjnych rozróżniane są wielkości
czujemy, że czytamy encyklopedię lub słow- formatowania wyników). Takie operacje to na liter w nazwach baz danych i tabelach. War-
nik, lecz normalną książkę. przykład: tworzenie bazy danych wraz z tabe- to więc przyjąć stałą konwencję w przypadku
Po instalacji MySQL sprawdzamy jego działa- lami (czynność jednokrotna – nie warto dla niej nazw. Często stosowanym pomysłem jest wy-
nie komendą wpisaną do konsoli (w Windowsie budować skryptu PHP, o ile nie tworzymy in- dawanie poleceń pisanych wielkimi literami
Start -> Uruchom): stalatora do naszego skryptu), wyświetlanie do- z nazwami własnymi pisanymi małymi. Czas
stępnych tabel, zmiana ustawień bazy lub ta- już przejść do praktyki. Bazę danych tworzy-
mysql -u root beli. Do debuggowania (w tym wypadku np. my poleceniem:
W przypadku systemu Windows podamy także sprawdzania, czy w tabelach znajdują się po-
ścieżke do katalogu z MySQL, np. prawne wartości) lepiej skorzystać z konsoli lub CREATE DATABASE test;
phpMyAdmin. Jeśli używamy porządnego edy-
c:\mysql\bin mysql -u root tora PHP (np. polecany w artykule „Wstęp do Gdy stworzymy bazę danych (nasz „katalog”
PHP” Zend Developer Environment), mamy do dla tabel), przed stworzeniem tabel, musimy
Powinniśmy zobaczyć powitanie w Monitorze dyspozycji wbudowanego klienta SQL, który zdeklarować, że właśnie jej chcemy użyć po-
MySQL, w którym możemy wydawać komen- także formatuje wyniki zapytań. leceniem:
dy. Opuścić go można przy pomocy komendy
\q. Jeśli napotkamy error can't connect to Tworzenie baz i tabel USE test;
MySQL server, oznacza to, że serwer nie został Zapytania w MySQL kończymy znakiem śred-
uruchomiony. Możemy ponownie uruchomić nika. Jest to ważne, ponieważ możemy dzię- Jeśli chcemy usunąć bazę danych (wraz z jej ta-
system lub uruchomić serwer wpisując: ki temu formatować zapytania. W systemie belami i danymi w nich zawartymi!), używa-
MySQL możemy stworzyć wiele baz danych, my komendy:
mysqld –standalone a w nich – wiele tabel. To w tabelach zawarte
Przy logowaniu do mysql użyliśmy nazwy użyt- są dane, natomiast bazy danych służą do kata- DROP DATABASE test;
kownika root. Ze względów bezpieczeństwa, logowania tabel. Dobrze jest przyjąć konwen-
powinniśmy ustanowić hasło w Monitorze ko- cję tworzenia jednej bazy danych dla każdego W MySQL można używać przydatnych opcjo-
mendą: projektu, a także dwóch kont użytkowników nalnych if exists i if not exists. Oznacza
o ograniczonych prawach – za pomocą jed- to, że np. zapytanie:
set password for root@localhost=password( nego konta możliwy byłby jedynie odczyt da-
'hasło'); nych, za pomocą drugiego – zapis. Ogranicza- CREATE DATABASE IF NOT EXISTS test;

Jak widać, komendy w MySQL są dosyć in-


tuicyjne i opierają się na języku angielskim.
W powyższej komendzie słowo hasło zamie-
niamy na nasze hasło. Działanie hasła spraw-
dzamy wylogowując się, a następnie logując
komendą:

mysql -u root -p

Powinniśmy zostać zapytani o hasło.

Używanie MySQL
Jak wspomniałem, komendy MySQL można
wydawać na kilka sposobów. Możemy w tym
celu użyć aplikacji phpMyAdmin (jeśli mamy ją
zainstalowaną), jak widać na Rysunku 1. Może-
my także wydać komendę bezpośrednio w Mo-
nitorze MySQL (plik mysql.exe w Windowsie i
mysql w Linuxie) – przestawia to Rysunek 2.
W końcu, mamy dostęp do MySQL z pozio-
mu PHP. Teoretycznym minusem ostatniego
rozwiązania, w przypadku nauki, jest fakt, że
wyniki otrzymujemy w różnych formach (ta- Rysunek 1. PhpMyAdmin w akcji

www.phpsolmag.org 13
Dla początkujących

nie będzie próbowało utworzyć bazy o nazwie tuje, że każdy kolejny wiersz będzie miał która przyspiesza wyszukiwanie po tym
test, jeśli już taka istnieje. Szczególnie przydat- w tej kolumnie wartość o jeden większą polu). Znakomita większość tabel po-
ne jest if not exists w przypadku tworze- niż poprzedni (to pole będzie więc nu- siada takie pole jako pierwsze – w celu
nia tabel. Tworzymy tabelę „tabela” (jeśli jesz- merem wiersza, który w przypadku baz identyfikacji wierszy;
cze nie istnieje) z polami: danych nazywamy rekordem); primary • nazwa: wartości tej kolumny będą składać
key oznacza, że będzie to klucz główny się z maksymalnie 30 znaków;
• id_produktu: wartości tej kolumny bę- naszej tabeli (klucz główny oznacza, że • opis: pole podobne do powyższego, jed-
dą liczbami całkowitymi (int), wartość wartości w tej kolumnie nigdy się nie nak może zawierać do 65 535 znaków (są
tej kolumny nie może być pusta (not powtarzają, są więc unikalne; jednocze- też większe i mniejsze typy łańcuchowe
null) – inaczej próba dodania nie po- śnie, klucz główny implikuje indeksowa- pól, jednak te są najczęściej wykorzysty-
wiedzie się; auto _ increment gwaran- nie, czyli tworzenie tabeli pomocniczej, wane);

Listing 1. Struktura bazy danych http://eldoras.com

# Struktura tabeli dla `accounts` `video_link` text NOT NULL,


`image_link` varchar(255) NOT NULL default '',
CREATE TABLE `accounts` ( PRIMARY KEY (`video_id`)
`account_id` int(11) NOT NULL auto_increment, ) ENGINE=InnoDB;
`nick` varchar(15) NOT NULL default '',
`pass` varchar(32) NOT NULL default '', # --------------------------------------------------------
`email` varchar(128) NOT NULL default '', # Struktura tabeli dla `performers`
PRIMARY KEY (`account_id`)
) ENGINE=InnoDB; CREATE TABLE `performers` (
`performer_id` int(11) NOT NULL auto_increment,
# -------------------------------------------------------- `performer_name` varchar(25) NOT NULL default '',
# Struktura tabeli dla `added` PRIMARY KEY (`performer_id`)
) ENGINE=InnoDB;
CREATE TABLE `added` (
`video_id` int(11) NOT NULL auto_increment, # --------------------------------------------------------
`stamp` timestamp NOT NULL default CURRENT_TIMESTAMP on # Struktura tabeli dla `songs`
update CURRENT_TIMESTAMP,
PRIMARY KEY (`video_id`) CREATE TABLE `songs` (
) ENGINE=InnoDB; `song_id` int(11) NOT NULL auto_increment,
`song_title` varchar(30) NOT NULL default '',
# -------------------------------------------------------- `song_artist` varchar(20) NOT NULL default '',
# Struktura tabeli dla `comments_en_0` PRIMARY KEY (`song_id`)
) ENGINE=InnoDB;
CREATE TABLE `comments_en_0` (
`comment_id` int(11) NOT NULL auto_increment, # --------------------------------------------------------
`stamp` timestamp NOT NULL default CURRENT_TIMESTAMP on # Struktura tabeli dla `stats`
update CURRENT_TIMESTAMP,
`poster_id` int(11) NOT NULL default '0', CREATE TABLE `stats` (
`content` text NOT NULL, `video_id` int(11) NOT NULL auto_increment,
PRIMARY KEY (`comment_id`) `views_count` int(11) NOT NULL default '0',
) ENGINE=InnoDB; `rate` float(4,2) NOT NULL default '0.00',
`votes_count` int(11) NOT NULL default '0',
# -------------------------------------------------------- `rating_sum` int(11) NOT NULL default '0',
# Struktura tabeli dla `comments_pl_0` PRIMARY KEY (`video_id`)
) ENGINE=InnoDB;
CREATE TABLE `comments_pl_0` (
`comment_id` int(11) NOT NULL auto_increment, # --------------------------------------------------------
`stamp` timestamp NOT NULL default CURRENT_TIMESTAMP on # Struktura tabeli dla `videos`
update CURRENT_TIMESTAMP,
`poster_id` int(11) NOT NULL default '0', CREATE TABLE `videos` (
`content` text NOT NULL, `video_id` int(11) NOT NULL auto_increment,
PRIMARY KEY (`comment_id`) `song_id` int(11) NOT NULL default '0',
) ENGINE=InnoDB; `performer_id` int(11) NOT NULL default '0',
`proposed_by_id` int(11) NOT NULL default '0',
# -------------------------------------------------------- PRIMARY KEY (`video_id`)
# Struktura tabeli dla `links` ) ENGINE=InnoDB;

CREATE TABLE `links` (


`video_id` int(11) NOT NULL auto_increment,

14 05/2007
MySQL od podstaw

• data_dodania: kolumna ta posiada for- konkretnej wartości głosu w drugiej tabeli. Uwaga: polecenie ALTER modyfikuje jedy-
mat datetime, będzie domyślnie wypeł- Podobnie, nie możemy dodać wartości głosu nie definicję tabeli, a nie wartości w niej
niana aktualnym czasem, a zapytania bę- nie zwiększając sumy – prowadziłoby to do zapisane. Powyższe polecenie nie zmieni
dą zwracać datę w formacie RRRR-MM- zamieszania i sfałszowania wyników. Trans- wartości wierszy – do tego służy polece-
DD GG:MM:SS. Istnieją także inne typy akcja gwarantuje, że zajdą obydwie możliwo- nie UPDATE, które omówimy w dalszej czę-
dat (np. datetime, date, time, year). ści, lub żadna. Wspomnieć należy, że typ ta- ści artykułu.
beli można zmienić – jeśli więc zauważymy, MySQL oferuje wiele typów pól. Warto po-
W końcu, po zdefiniowaniu pól (kolumn), że jeden typ okazuje się niewydajny, może- znać te mniej popularne, by nie „strzelać z ar-
jakie mają znaleźć się w tablicy, określamy my zastosować inny. maty do muchy”, co często dzieje się, gdy pro-
(nieobowiązkowo) typ tworzonej tabeli. Możemy zobaczyć dostępne bazy danych i jektant użyje TEXT zamiast VARCHAR dla samej
MySQL obsługuje kilka typów tabel, z któ- tabele (po wybraniu bazy danych przy pomocy nazwy produktu. Oczywiście, jedyną różni-
rych zdecydowanie najpopularniejszymi są USE) wykonując odpowiednio komendy: cą będzie czas wykonania zapytania, a więc
MyISAM i InnoDB. Nie wdając się w szcze- kluczowa sprawa, jeśli idzie o bazy danych.
góły, różnica między nimi polega na struk- SHOW DATABASES; Wspomniane polecenia mają wiele dodatko-
turze przechowywania danych i funkcjo- SHOW TABLES; wych opcji, którym warto się przyjrzeć, my
nalności. MyISAM stosuje się, jeśli tabe- jednak na pewno chcemy już operować na da-
la zawiera dane, do których potrzebny jest Jeśli chcemy obejrzeć zapytanie, które utwo- nych.
szybki dostęp i nie są często modyfikowane rzy tabelę już istniejącą, wydajemy polecenie:
(jest to domyślne ustawienie – jeśli usunie- Wstawianie, usuwanie
my TYPE= MyISAM z powyższego zapytania, SHOW CREATE TABLE tabela; i aktualizacja danych
efekt będzie taki sam). Z kolei wolniejsze Operacje określone powyżej należą raczej do
InnoDB zawiera niewątpliwą zaletę w posta- Rezultat powinien dać polecenie zbliżone wolnych. Bazy danych nastawione są na szyb-
ci blokowania na poziomie wierszy (a nie ta- do tego, którym stworzyliśmy tabelę. Moż- kość wyszukiwania i zwracania informacji,
beli – jak MyISAM), obsługuje także trans- na spodziewać się kilku detalicznych zmian, dlatego musimy oszczędnie i rozmyślnie pro-
akcje. Wyjaśnię to na przykładzie: mamy które są automatycznie dodawane, więc za- jektować struktury baz danych, by ograniczyć
tabelę z tysiącami rekordów. Jeśli tabela zwyczaj pomijane, np. rozmiar pola int bę- ilość zapytań odpowiednio: INSERT, DELETE
jest typu MyISAM, przy dodawaniu nowe- dzie wynosił 11, kodowanie znaków zosta- i UPDATE.
go wiersza, zablokowane na ten czas zosta- nie dodane po definicji typu tabeli (w za- Polecenie INSERT występuje w różnych od-
nie jakiekolwiek inne działanie na tej tabeli leżności od ustawień domyślnych systemu mianach: możemy dodawać jeden lub kilka
(a więc także odczyt). bazodanowego) oraz nazwy tabel i kolumn wierszy, dodawać pewne warunki lub okre-
Przyjmijmy, że nasza witryna odczytuje zostaną ujęte w znaki `. Jeśli chcemy usunąć ślać jedynie wartości niektórych pól tabe-
z tabeli rekordy przy każdorazowym przeła- tabelę, stosujemy: li (ma to sens np. w przypadku, gdy więk-
dowaniu, zaś użytkowników mamy co naj- szość pól ma ustawione opcje not null oraz
mniej setki. Jako administratorzy, dodajemy DROP TABLE tabela; default – wtedy zostaną automatycznie uzu-
wiersz do tabeli (np. z nowym towarem). W pełnione). Dodajmy pierwsze wiersze do na-
czasie wykonania takiego zapytania (ułam- Natomiast do modyfikacji tabeli służy polece- szej tabeli:
ki sekundy, choć niekiedy nawet sekundy nie ALTER . W przypadku graficznych interfej- W tej formie zapytania podajemy wartości
– zależnie od wprowadzanych danych) po- sów, jak np. phpMyAdmin, nie musimy znać wszystkich pól oddzielone przecinkiem (tekst
bieranie danych przez użytkowników zosta- tego polecenia, jednak sposób tworzenia ta- musi być zawarty w apostrofach), zaś kolejne
nie wstrzymane – załadowanie naszej witry- beli będzie miał wpływ na to, jak zbudujemy wiersze oddzielone są przecinkami i ujęte w na-
ny będzie przedłużone o ten czas oczekiwa- zapytanie dodające, czy wyszukujące rekordy wiasy. W pierwszym wierszu dla pola id_pro-
nia. Jeśli rzadko dodajemy do tabeli nowe da- – warto więc znać tekstowe wersje poleceń. duktu podajemy wartość NULL. Jego deklaracja
ne, problem będzie niezauważony (dodatko- Dla przykładu, aby dodać kolumnę na końcu zakłada, że taka wartość nie może wystąpić, więc
wo MyISAM oferuje kompresje i działa szyb- tabeli, używamy: MySQL automatycznie przypisze temu polu ko-
ciej niż InnoDB). lejną wartość z auto_increment (będzie to 1).
Jeśli jednak dodawanie wiersza będzie od- ALTER TABLE tabela ADD COLUMN kolumna Podobnie sprawa ma się z ostatnim polem
bywało się automatycznie i często, różnica varchar(20); – zostanie mu przypisana wartość CURRENT_
może być zauważalna. W tym celu stosuje
się tabele InnoDB – choć wolniejsze, bloku-
ją przed odczytem nie całą tabelę, lecz jedy-
nie aktualnie modyfikowany wiersz. W tym
czasie inne rekordy z tabeli mogą być pobra-
ne przez użytkowników. InnoDB obsługuje
także transakcje.
Transakcje można najogólniej objaśnić jako
zbiory kilku dowolnych poleceń, które albo
wykonają się wszystkie poprawnie, albo ba-
za danych wróci do stanu sprzed wykonania
pierwszego polecenia.
Jest to szczególnie przydatne przy do-
dawaniu danych do wielu tabel – jeśli np.
w jednej przechowujemy wartości głosów,
a w drugiej ich sumę, nie możemy pozwolić,
by suma głosów zwiększyła się bez dodania Rysunek 2. Klient tekstowy mysql

www.phpsolmag.org 15
Dla początkujących

TIMESTAMP, czyli aktualny znacznik czasu. Operacja SELECT jest najważniejszą w ca- Jak widać, zmieniamy wartość pola opis.
W drugim wierszu robimy coś, czego raczej łym systemie bazodanowym i omówimy ją Kluczowym wyrażeniem jest tu WHERE
nie powinno się robić w przypadku pól auto_ szerzej później. Polecenie UPDATE jest szcze- – określone są po nim warunki, jakie mu-
increment – podajemy jego wartość. Nie jest to gólnie przydatne w przypadku przechowy- si spełniać wiersz, by do niego została za-
zabronione, jest więc możliwe, ale ma sens jedy- wania danych statystycznych wewnątrz ta- stosowana zmiana. W naszym przypadku
nie, gdy usuniemy rekord ze „środka” tablicy i bę- bel. Zmieńmy lekko treść rekordu numer określamy id_produktu (które jest unikalne
dziemy chcieli ponownie zająć jego numer. Efekt dwa: z założenia tabeli), zmiana będzie więc do-
możemy zobaczyć, wykonując zapytanie: tyczyła jednego wiersza. Jeśli usuniemy wa-
UPDATE tabela SET opis='Napoj gazowany o runek, zmiana będzie dotyczyć wszystkich
SELECT * FROM tabela; smaku coli' WHERE id_produktu='3'; rekordów:

UPDATE tabela SET opis='Napoj gazowany o


Listing 2. Wyświetlanie najnowszych filmów
smaku coli';
<?
mysql_connect('localhost', 'nazwa_bazy', 'haslo_bazy'); Wyrażenie WHERE jest niezwykle istotne i sto-
mysql_select_db('numa'); suje się je także w innych poleceniach. Aby
usunąć wiersze, stosujemy:
//select videos
DELETE FROM tabela WHERE id_produktu='3'
$query_videos=mysql_query('SELECT links.image_link, links.video_link, LIMIT 1;
performers.performer_name AS performer, songs.song_title AS title,
songs.song_artist AS artist, lastadded.video_id FROM links, performers, Ponownie, polecenie będzie dotyczyło tyl-
songs, videos, ( SELECT video_id, stamp FROM added ORDER BY stamp DESC LIMIT '. ko jednego wiersza. Dodaliśmy wyraże-
(($page-1)*8) .', 8) AS lastadded WHERE videos.video_id = lastadded.video_id AND nie LIMIT jedynie w celu demonstracyj-
links.video_id = lastadded.video_id AND performers.performer_id = nym. Często jednak stosuje się je „na wszel-
videos.performer_id AND songs.song_id = videos.song_id ORDER ki wypadek”. Praktyczne zastosowanie ma
BY lastadded.stamp DESC'); natomiast, jeśli nie określimy jednoznacz-
nie wierszy. Na przykład, posiadamy tabe-
//count all videos lę, która zawiera historię cen produktów.
Niech ma taką strukturę:
$query_count=mysql_query('SELECT count(*) AS count from added'); Pierwsze pole zawiera identyfikator jed-
$count=mysql_fetch_assoc($query_count); noznaczny każdego rekordu. Drugi – numer
$videos_count=$count['count']; produktu, którego dotyczy (wartości tego po-
la mogą się więc powtarzać – np. dwa wpi-
if($count['count']<8) sy mające ten sam id_produktu będą ozna-
{ czać dwie jego ceny, określone w czasie przez
$total_pages=1; data_dodania). Załóżmy, że jeden z produk-
} tów bardzo często zmienia cenę. Chcemy
elseif ($count['count']%8==0) więc usunąć jego 15 najstarszych cen z hi-
{ storii. Niech jego id_produktu będzie wyno-
$total_pages=$count['count']/8; siło 20. Aby określić, że usunięte mają być
} najstarsze dane, musimy je najpierw posorto-
else wać. Służy do tego wyrażenie ORDER BY:
{
$total_pages=(intval($count['count']/8))+1; DELETE FROM ceny WHERE id_produktu='20'
} ORDER BY data_dodania ASC LIMIT 15;

if($page>$total_pages) Po określeniu, którego pola ma dotyczyć


{ sortowanie, podajemy DESC lub ASC – odpo-
die(); wiednio malejąco i rosnąco. Limit 15 okre-
} śla, że maksymalnie usuniętych zostanie 15
rekordów.
//count all users Sortowanie będzie wykonane jedynie dla
$query_users_count=mysql_query('SELECT count(*) AS count from accounts'); tego zapytania – po jego wykonaniu, da-
$users_counter=mysql_fetch_assoc($query_users_count); ne będą w takiej kolejności, jak przedtem.
$users_count=$users_counter['count']; Silnik MySQL wykona więc kolejno nastę-
pujące czynności: wyselekcjonuje rekordy,
mysql_query('UPDATE overall_stats SET views_count=views_count+1'); których id_produktu równa się 20, posortu-
$query_views_count=mysql_query('SELECT views_count from overall_stats'); je je rosnąco według daty, a następnie usunie
$views_counter=mysql_fetch_assoc($query_views_count); maksymalnie 15 pierwszych rekordów z ta-
$views_count=$views_counter['views_count']; kiej posortowanej listy.
Do większości wyrażeń (np. WHERE lub
?> ORDER BY ) można dodawać kilka warunków
i łączyć je (np. w celu wyszukiwania frag-

16 05/2007
MySQL od podstaw

mentu tekstu). Najczęściej stosuje się jed-


nak operator AND: Listing 3. Wyświetlanie wyników zapytań
<div id="main">
UPDATE ceny SET cena='5' WHERE <h1>
id_produktu='4' AND id='5'; <?=$lang_latest_videos?>
</h1>
Takie zapytanie ma sens jedynie wtedy, gdy <h2 class="count">
chcemy być pewni, czy numer wiersza o id <?
5 na pewno odpowiada produktowi o id 4. if($page!=1){
Później przekonamy się jednak, że WHERE print '<a href="index.php?page='. ($page-1) .'">'.$lang_previous.'</a> | ';
w połączeniu z AND tworzą trzon zapytań w }
MySQL. Jeśli chcemy usunąć wszystkie wier-
sze z tabeli, wystarczy wydać polecenie: print $lang_page.' '.$page.' '.$lang_of.' '.$total_pages;

TRUNCATE TABLE tabela; if($page<$total_pages){


print ' | <a href="index.php?page='. ($page+1) .'">'.$lang_next.'</a>';
Trzeba być jednak bardzo pewnym tego, co }
się robi.
?></h2><ul><?
Zapytania wybierające w MySQL $i=1;
Wreszcie, docieramy do tego, co najważniejsze while($video=mysql_fetch_assoc($query_videos)){
– sposobu pobierania danych z bazy. Do wykona- ?><li>
nia zapytania służy instrukcja SELECT. Raz już jej <a href="view.php?id=<?=$video['video_id']?>">
użyliśmy. W jej najprostszej postaci, wyświetlanie <span>
wszystkich danych z tablicy uzyskamy dzięki: <img src="<?=$video['image_link']?>" width="105px" height="105px"
alt="<?=stripslashes($video['performer'])?>" />
SELECT * FROM tabela; p><?=stripslashes($video['performer']).' '.$lang_in.'<br />'.stripslashes(
$video['artist']).' - '.stripslashes($video['title'])?>
Oczywiście, sensu nabiera to zazwyczaj dopie- </span>
ro po dodaniu klauzuli WHERE : </p>
</a>
SELECT * FROM tabela WHERE cena<500; </li>
<?
Symbol gwiazdki oznacza pobranie wartości
wszystkich kolumn dla danych wierszy. Mo- if($i==4)
żemy także pobrać jedynie określone kolum- {
ny używając: ?></ul><ul><?
}
SELECT cena, nazwa FROM tabela WHERE $i++;
cena<500; }

Oczywiście, jeśli mamy kilka tabel, możemy while($i<=8){


użyć jednego zapytania do pobrania danych z if($i==5){
wszystkich z nich: ?>

SELECT tabela.cena, tabela.nazwa, </ul>


kontrahenci.nazwa FROM tabela, <ul>
kontrahenci; <?
}
W powyższym zapytaniu wybieramy kolum- ?>
ny: cena i nazwa z tabeli tabela oraz nazwa <li>
z tabeli kontrahenci. By uniknąć problemu <img src="images/box.jpg" width="105px" height="105px"
tych samych nazw kolumn, poprzedzamy ich alt="Image unavailable" class="box" />
nazwy nazwą tabeli, z której mają być pobra- <h2>
ne, i kropką. Prawdziwą siłą zapytania SELECT &nbsp;
jest połączenie z WHERE w celu określenia relacji </h2>
między tabelami: </li>
<?
SELECT tabela.cena, tabela.nazwa, $i++;
kontrahenci.nazwa FROM tabela, }
kontrahenci WHERE tabela. ?>
id_zamawiajacego=kontrahenci. </ul>
id_kontrahenta AND tabela. </div>
id_zamowienia=15;

www.phpsolmag.org 17
Dla początkujących

Jeśli założymy, że w tabeli tabela ma- Powyższe zapytanie zwróci tabelę, której ko- ilość zwróconych wyników. Klauzula LIMIT
my zamówienia produktów wraz z kolum- lumna nie będzie miała nazwy nazwa, lecz może jednak przyjmować także dwa parame-
ną id _ zamawiającego posiadającą numer pole1. Zmieniliśmy więc nazwę wynikowej try. W takim wypadku, pierwszy parametr
klienta, do którego są przypisane dane (np. kolumny. Będzie to miało znaczenie w połą- określa punkt startowy, od którego należy li-
nazwa) klienta w tabeli kontrahenci, takie czeniu z PHP, bowiem nazwy kolumn zwra- czyć ilość maksymalnych wyników określoną
zapytanie zwróci nam nazwę i cenę zamó- canych przez zapytanie są zamieniane na in- przez drugi parametr. Ma to sens, gdy stroni-
wienia numer 15 wraz z nazwą kontrahen- deksy tablic z wynikami w PHP. Przykład cujemy dane, np. po 50 na wyników na stro-
ta, który zamówienia dokonał. Takie po- możemy jednak podać także bez użycia PHP. nie. Dla trzeciej strony wyników, wykonamy
wiązanie dwóch lub więcej tabel nazywa MySQL posiada wiele wbudowanych funkcji, zapytanie:
się relacją. których często nie używa się, chociaż są szyb-
MySQL posiada tak że użyteczną funkcjo- sze i równie sprawne, jak te zaimplemento- SELECT * FROM tabela ORDER BY cena
nalność zwaną aliasami. Jeśli nie podoba nam wane w PHP: DESC LIMIT 2*50, 50;
się nazwa tabeli, np. z powodu jej długości, mo-
żemy na czas wykonania zapytania przypisać jej SELECT count(*) AS rekordow FROM tabela; Takie zapytanie posortuje tabelę względem
inną nazwę: ceny (malejąco), a następnie zwróci pozycje
Zapytanie takie zwróci wartość funkcji od 100 do 150, a więc maksymalnie 50 wyni-
SELECT t1.nazwa FROM tabela AS t1; count(*), która to z kolei zwraca ilość rekor- ków, lecz odpowiednich dla trzeciej strony.
dów w tablicy. Posługiwanie się jednak na- Jeśli za drugi parametr podamy -1, otrzy-
Tabeli tabela nadaliśmy alias t1, którego na- stępnie wynikami takiego zapytania z ko- mamy wszystkie wyniki począwszy od punktu
stępnie używamy w zapytaniu SELECT. Efekt lumną o nazwie count(*) byłoby co najmniej określonego przez pierwszy parametr.
jest taki sam, jak zapytania: niewygodne. Dlatego, przypisujemy jej alias Zanim przejdziemy do przykładu z ży-
rekordow. Wynikiem będzie jeden rekord z cia, nauczmy się jeszcze trochę teorii – cho-
SELECT tabela.nazwa FROM tabela; liczbą wierszy tabeli tabela. Funkcji count() ciaż może niezbyt ciekawej, jednak niezwy-
za argument możemy podać także nazwę ta- kle istotnej. Jeśli nie opanujemy zagadnienia
W tym konkretnym przypadku, skrót nie ma beli lub pewne zapytanie. Na przykład, je- normalizacji bazy danych, już przy pierw-
sensu, jednak przydaje się, jeśli używamy za- śli poprzedzimy nazwę kolumny słowem szym projekcie będziemy zmuszeni ponow-
pytań zagnieżdżonych. Po klauzuli WHERE distinct, wszystkie duplikaty zostaną zre- nie projektować bazę danych, ponieważ zapy-
możemy bowiem umieścić nie tylko nazwy dukowane do jednej pozycji: tania będą wykonywać się zbyt długo i będą
istniejących tabel, ale także kolejne zapyta- zbyt skomplikowane.
nie SELECT (w nawiasie). Wynikiem tego po- SELECT count(distinct nazwa) FROM tabela;
dzapytania będzie oczywiście tabela, do któ- Optymalizacja bazy danych
rej pól możemy odwoływać się przypisując Jeśli w tabeli tabela będziemy mieć kilka re- W przypadku używania bazy danych, istot-
jej wcześniej alias. Zapytania zagnieżdżone są kordów o tej samej wartości w polu nazwa, ny jest czas wykonywania zapytań. Jest on
niezwykle istotne przy większych projektach zwrócona zostanie ilość tylko rekordów uni- w większości przypadków zależny od pro-
i zademonstrujemy je później na przykładzie kalnych. Wszelkie duplikaty nie zostaną doli- jektu (architektury) bazy danych – rozkładu
z życia wziętym. Wróćmy jednak do aliasów. czone do wyniku. danych między tabelami (czy nawet całymi
Możemy je przypisać nie tylko tabelom, ale tak- Kolejną przydatną klauzulą jest LIMIT. Jak bazami danych na różnych serwerach). Klu-
że wierszom: już pokazaliśmy, określa, ilu maksymalnie re- czowe są więc dwa składniki: dobrze zapro-
kordów ma dotyczyć zapytanie. Jeśli użyje- jektowana baza oraz dobrze skonstruowane
SELECT tabela.nazwa AS pole1 FROM tabela; my jej w SELECT, będzie określać maksymalną zapytania. Jakkolwiek to drugie użytkownik
musi sam zagwarantować (MySQL umożli-
wia podgląd wolnych zapytań w dzienniku),
tak przy projektowaniu bazy warto trzymać
się pewnych zasad. Jeśli baza jest z nimi zgod-
na, zwie się ją znormalizowaną. Zanim wyja-
śnię to znaczenie, dodam, że prędkość dzia-
łania bazy danych można zwiększyć przy po-
mocy polecenia:

OPTIMIZE TABLE tabela;

oraz tworząc indeksy (warto z nich korzy-


stać, jednak jeśli użyjemy ich do nieodpo-
wiednich danych, tzn. nie będą wykorzysty-
wane, będą jedynie zajmować zasoby) oraz
stosując minimalistyczne typy danych dla
kolumn.

Pierwsza postać normalna


Zwana także 1NP, określa, że dane w kolum-
nie muszą być niepodzielne. Dla przykładu
mamy tabelę z pracownikami (pracownicy).
W jednej z jej kolumn (języki) mamy nazwy
Rysunek 3. Widok strony głównej http://eldoras.com języków, które umie dany pracownik. Dla

18 05/2007
MySQL od podstaw

rekordu Jana Kowalskiego wartość tego po- nym i wykraczającym poza granice tego arty- rozdzielone na dwie tabele, by przyspie-
la będzie miała postać Angielski, Niemiecki, kułu. Jeśli chcemy projektować bazy danych szyć wyszukiwanie. Przy większym obcią-
Polski. Taka baza danych jest nieznormalizo- używane często, powinniśmy zaznajomić się żeniu, można bardziej rozszerzyć podział.
wana – jeśli bowiem będziemy chcieli póź- z tymi terminami. Występuje tu tzw. „klucz obcy” – poster_
niej wyszukać pracowników znających an- id. Jest to numer konta osoby, która umie-
gielski, będziemy musieli wykonać skompli- Baza filmów na przykładzie ściła komentarz (jej nick pobierany jest z
kowane i długie czasowo zapytanie. Aby ta- http://www.eldoras.com tabeli accounts);
ka baza (w tym wypadku tabela) była zgodna Przeanalizujmy teraz strukturę bazy da- • links – zawiera odnośniki do miniatur
z pierwszą postacią normalną, musimy roz- nych serwisu skupiającego filmy – http:// i filmów dla każdego rekordu będącego
dzielić języki – po jeden, dla każdego rekor- www.eldoras.com. Dobra architektura pozwoli- osobnym filmem;
du. Oznacza to, że po znormalizowaniu bę- ła w tym wypadku na generowanie bardzo róż- • performers – zawiera nazwę wykonawcy,
dziemy mieć trzy wpisy (dla każdego z języ- norodnych statystyk przy ogromnych możliwo- wraz z jego identyfikatorem (NIE jest to
ków) dla Jana Kowalskiego! Pozostałe warto- ściach wyszukiwania. Oczywiście, każda baza identyfikator konta, bowiem serwis zakła-
ści pól będą więc się powtarzać dla każdego danych da się bardziej zoptymalizować, jed- da, że wykonawca nie musi być zarejestro-
wpisu – uzyskaliśmy znormalizowanie, ale nak ta struktura okazuje się bardzo wydajna na- wanym użytkownikiem);
kosztem zwiększenia ilości danych. Opty- wet w przypadku 3000 unikalnych odwiedzin • songs – zawiera nazwy piosenek oraz ich
malniej będzie więc, jeśli utworzymy osob- dziennie. Kod odpowiedzialny za stworzenie wykonawców wraz z ich identyfikatora-
ną tabelę jezyki, a w niej pola id_pracownika tabel widać na Listingu 1. mi. Podział na wykonawców i tytuły po-
oraz jezyk. Będziemy więc mieć trzy wpi- Listing został wygenerowany przez phpMy- zwala na generowanie statystyk według
sy dla Jana Kowalskiego w tej tabeli, jednak Admin, ma więc trochę inną strukturę od tej, wykonawców, natomiast identyfikator
jej zawartość będzie niewielka – pierwsze której się uczyliśmy (np. klucz podstawowy jest jednoznacznie określa jedną piosenkę.
pole będzie zawierało zawsze jego identyfi- definiowany dopiero po kolumnach) – efekt Przyspiesza to także wyszukiwanie sa-
kator (taki sam, jak w macierzystej tabeli), jest jednak taki sam. Przeanalizujmy po kolei mych wykonawców lub samych tytu-
natomiast drugie – pojedynczy język. Warto za- wszystkie tabele: łów;
znaczyć, że ponieważ wartość id_pracownika • stats – zawiera dane statystyczne dla każ-
będzie mogła być powielana, nie może on • accounts – zawiera dane użytkowników, dego filmu: ilość oglądnięć, ocenę, ilość
być kluczem głównym tabeli. W macierzystej odpowiednio zakodowane; głosów oraz ich sumę (przydatne w gene-
tabeli pozostanie natomiast jeden wiersz dla • added – posiada id filmu oraz datę jego rowaniu nowej oceny);
Jana Kowalskiego, z którego całkowicie usu- dodania do serwisu; • videos – jest kluczową tabelą, określają-
niemy kolumnę języki. Od teraz, by znaleźć • comments_en_0 oraz comments_pl_0 – ko- cą pewne relacje. Dla każdego rekordu
jego umiejętności językowe, wykonywać bę- mentarze w różnych językach zostały, (filmu), przypisana jest piosenka (a więc
dziemy zapytanie:
Listing 4. Tworzenie tabeli o nazwie tabela
SELECT jezyki.jezyk FROM jezyki,
pracownicyWHERE pracownicy. CREATE TABLE IF NOT EXISTS tabela (
id_pracownika=13 AND jezyki.id id_produktu int not null auto_increment primary key,
pracownika=pracownicy.id_pracownika; nazwa varchar(30),
opis text,
Oczywiście, moglibyśmy od razu zapisać data_dodania timestamp not null default CURRENT_TIMESTAMP
... AND jezyki.id _ pracownika=13, jed- ) TYPE=MyISAM;
nak jeśli zapytanie będzie bardziej rozbu-
dowane, zmiana numeru będzie konieczna Listing 5. Dodanie pierwszych wierszy w tabeli
we wszystkich miejscach, warto więc od ra- INSERT INTO tabela VALUES
zu posłużyć się relacją. W klauzuli FROM wy- (
mieniliśmy tabelę pracownicy, chociaż z niej NULL,
nie pobieramy bezpośrednio danych. Słu- 'Cola',
ży nam ona jednak do wykonania zapytania 'Napoj gazowany',
przy budowie relacji, musimy więc ją wy- NULL
mienić. Tym sposobem znormalizowaliśmy ),
względem pierwszej zasady bazę danych. (
Dla przykładu, jeśli teraz będziemy szukać 3,
anglojęzycznych pracowników, wykonamy 'Pepsi',
zapytanie: 'Napoj gazowany',
NULL
SELECT pracownicy.imie, pracownicy.nazwisko );
FROM pracownicy, jezyki WHERE
jezyki.jezyk='angielski' AND Listing 6. Tworzenie tabeli o nazwie ceny
jezyki.id_pracownika=pracownicy. CREATE TABLE IF NOT EXISTS ceny (
id_pracownika; id int not null auto_increment primary key,
id_produktu int not null,
Postaci normalnych jest kilka, z należy znać cena int not null,
pierwszą, a warto – trzy pierwsze. Kolejne data_dodania timestamp not null default CURRENT_TIMESTAMP
postacie są jednak związane z kluczami tabel, ) TYPE=MyISAM;
są więc zagadnieniem bardziej skomplikowa-

www.phpsolmag.org 19
Dla początkujących

tytuł i artysta), wykonawca oraz konto wspólne powiązania dla wszystkich danych sji językowej serwisu, wczytywane są róż-
użytkownika, który film dodał. z tabel wymienionych w FROM – video_id ne dane. Następnie napotykamy instrukcję
musi być wspólny, zaś tabela videos zawiera warunkową, która określa, że jeśli jesteśmy
Jak widzimy, zastosowany został silnik InnoDB id odpowiadające id piosenek i wykonawców na pierwszej lub ostatniej stronie wyników,
w celu blokowania na poziomie wierszy w tabelach z ich danymi tekstowymi, więc nie pojawią się napisy Dalej i Wstecz.
– MyISAM wyraźnie nie zdawał egzaminu. głównie z nią tworzymy powiązania. Powią- Dalej, deklarujemy zmienną $i , która bę-
zanie pierwsze (z lastadded.video_id) gwa- dzie oznaczać ilość filmów wyświetlonych
PHP i MySQL rantuje, że pod uwagę bierzemy tylko osiem w jednym wierszu (cztery) na stronie z wy-
PHP w wersji 5 oferuje kilka sposobów po- ostatnich rekordów i dane z nimi związane. nikami. Używamy pętli while, która przej-
łączenia się z bazą danych MySQL. Najko- Na końcu, ponownie sortujemy dane wzglę- dzie przez każdy rekord wyniku. Jest to
rzystniejszymi rozwiązaniami wydają się dem daty dodania. Wynik tego zapytania typowa konstrukcja w połączeniu z mysql_
PDO i mysqli, które jednak wymagają zna- przechowywany jest w $query_videos. fetch_assoc() , która zawiera wielowymia-
jomości zagadnienia programowania zo- Kolejne zapytanie zwraca łączną ilość fil- rową tablicę (osiem rekordów z filmami,
rientowanego obiektowo (opartego na kla- mów w serwisie. Dalszy kod służy do okre- każdy z nich ma swoje dane – tablica więc
sach), które z kolei jest techniką dosyć za- ślenia, jaka liczba ma być przypisana do wy- jest dwuwymiarowa).
awansowaną i nieprzydatną w przypadku rażenia na stronie Strona X z Y – X jest zdefi- Każde wywołanie pętli while przypisze
małych projektów. Po opanowaniu najważ- niowane w odfiltrowanym $page, natomiast zmiennej $video wartość rekordu z jednym
niejszych funkcji PHP jest to jednak nie- Y jest właśnie określane w zmiennej $total_ filmem. Dalej znajduje się kod odpowiedzial-
zbędny przystanek na drodze do zostania pages – jest to ilość wyników podzielona ny za wyświetlenie danych w odpowiednich
poważnym deweloperem. Kod serwisu http:// przez osiem. Intval() służy do zaokrągle- znacznikach (które są potem formatowane
www.eldoras.com został jednak napisany nia wartości – jeśli reszta z dzielenia równa pod kątem wyglądu za pomocą arkuszy sty-
pod PHP 4 (działa także pod PHP 5) i z tego się 0, to przypisanie Y jest łatwe. W innym li CSS). Jeśli pętla while przechodzi czwarty
względu używa wciąż przydatnych i ułatwiają- przypadku filmów jest mniej niż wielokrot- raz, oznacza to, że należy przejść do kolejnego
cych naukę funkcji. ność 8, więc stosujemy widoczne obliczenie wiersza – kończymy więc listę UL i rozpoczy-
Przypatrzmy się, jak wygląda obsługa serwi- by określić prawidłową ilość stron. Użyliśmy namy nową. Na końcu inkrementujemy $i.
su z poziomu PHP. Na Listingu 2. widać kod funkcji mysql_fetch_assoc() z argumentem Nie jest jednak powiedziane, że zostanie
odpowiedzialny za wyświetlanie najnowszych będącym wynikiem zapytania MySQL – wy- zwróconych dokładnie 8 wyników – jeśli
filmów na stronie głównej serwisu. nikiem tego jest z kolei tablica o indeksach bowiem jesteśmy na ostatniej stronie, mo-
Jak widać, najpierw jest inicjowane połącze- będących nazwami kolumn w MySQL (lub że ich być np. 6. Wtedy pętla while zakoń-
nie z bazą danych znajdującą się pod adresem ich aliasów, jeśli zostały przypisane). czy się z $i równym 6. Dlatego definiuje-
localhost (czyli na komputerze, na którym uru- Jeśli ktoś próbował być sprytny i podał w my kolejną pętlę while – jeśli zostało wy-
chomiony jest serwer HTTP). Nazwa i hasło do- adresie strony podstronę z wynikami, która świetlonych mniej niż 8 miniatur z przypi-
stępu do bazy zostały wycięte z oczywistych nie ma prawa istnieć z racji braku filmów, sami, zapełniamy pozostałe miejsca obraz-
względów. Następnie, wybieramy bazę danych skrypt kończy działanie. Kolejne zapytania kami „Brak grafiki”. Efekt tego kodu widać
(nasz katalog tabel) przy pomocy kolejnej wbu- odpowiedzialne są za zliczenie ilości zareje- na Rysunku 3.
dowanej funkcji. strowanych użytkowników dodanie jednego
Wykonanie zapytania MySQL zlecamy wyświetlenia do statystyk oraz pobranie ilo- Podsumowanie
funkcji mysql_query, która zwraca wyniki ści wyświetleń. Czas na zaprezentowanie wy- MySQL oferuje ogromne możliwości, które
do zmiennej $query_videos (i kilku innych ników zapytań. Odpowiedzialny za to kod połączone z PHP tworzą to, co widzimy każ-
w dalszych poleceniach). Kod wygląda na nie- widać na Listingu nr 3. dego dnia w Internecie – Wikipedię, Yahoo
zmiernie skomplikowany, jest jednak bardzo Widzimy tu niezbyt estetyczny, pomiesza- itd... Trudno o lepsze argumenty przema-
intuicyjny. Po SELECT określamy, co chce- ny kod PHP i xHTML. Wprawdzie wygląda wiające za nauką tworzenia stron w ramach
my otrzymać. Do wyświetlenia miniatur fil- to fatalnie, jednak przy odpowiednim forma- pakietu AMP (Apache + MySQL + PHP).
mów, niezbędne będą: linki do miniatury towaniu i podświetlaniu składni jest całkiem Tak PHP, jak MySQL zawiera jeszcze mnó-
i filmu, nazwa wykonawcy, tytuł piosenki czytelne. stwo przydatnych funkcjonalności, jednak
i nazwa artysty (przypisywane są od razu alia- Trzeba jednak zaznaczyć, że pisząc kod w już informacje zawarte w tych dwóch arty-
sy, by mieć wygodniejsze nazwy indeksów projekcie kilkuosobowym, należy podzielić kułach mogą wystarczyć do stworzenia wła-
tablic w PHP) oraz id danego filmu – do stwo- aplikację na kilka „warstw". Na przykład, sche- snej wideogralerii, jak na http://eldoras.com.
rzenia linka do obejrzenia filmu. mat MVC (ang. Model – View – Controller) za- Zachęcamy do nauki i realizowania swoich
Dalej mamy klauzulę FROM – musimy kłada podział na warstwę biznesową, wyglądu pomysłów!
zawrzeć w niej wszystkie tabele, z których ko- i kontrolną. Dzięki takiemu podziałowi, kod
rzystamy do pobrania powyższych danych. odpowiedzialny za obliczenia nie jest zmiesza-
Sprawdzamy więc, gdzie leżą powyższe da- ny z kodem odpowiedzialnym za wygląd itd...
ne i dopisujemy je. Jedną z tabel jest wy- Przydatne w tym celu jest także opanowanie
nik podzapytania, któremu dajemy nazwę użycia szablonów, jest to jednak przydatne w
lastadded. większych, zespołowych projektach. Najpierw
Jest ono odpowiedzialne za pobranie iden- napotykamy na dziwny zlepek: KRZYSZTOF TRYNKIEWICZ
tyfikatorów ośmiu ostatnio dodanych fil- Studiuje Informatykę na Uniwersytecie Jagielloń-
mów (stronicowanie realizowane jest co 8 <?=$lang_latest_videos?> skim. Od wielu lat zajmuje się tworzeniem witryn
filmów, więc zmienna $page określa, na której w technologii PHP oraz Flash. Obecnie rozwija kil-
stronie wyników jesteśmy; jest ona wcześniej Jest to skrócony odpowiednik <? print ka równoległych projektów autorskich dostęp-
odpowiednio filtrowana – domyślnie jest to $lang _ latest _ videos; ?>. Zmienne o nych na witrynach http://eldoras.com i http://
zmienna $_GET['page']). Dalej, określone przedrostku $lang oznaczają tekst zależny ajax.eldoras.com
są relacje w klauzuli WHERE. Ustalamy więc od języka – w zależności od wybranej wer- Kontakt z autorem: chris.trynkiewicz@gmail.com

20 05/2007
Dla początkujących

WordPress
Tworzymy skórkę

WordPress jest skryptem bloga oferującym ogromne możliwości tworzenia


i przystosowania strony do swoich potrzeb. W Internecie jest wiele
szablonów gotowych do wykorzystania na naszym blogu. Co jednak,
gdy żaden nam nie odpowiada? Pozostaje nam wykonanie własnego.

blony, które znajdują się w katalogu naszej


Dowiesz się... Powinieneś wiedzieć... skórki, zostaną zignorowane.
• W jaki sposób zbudować własną skórkę do sys- • Czytelnik powinien znać podstawy języków
temu WordPress. PHP i SQL. Przydatna też będzie podstawowa Hierarchia szablonów
• Jak zbudowane są szablony oraz jakie możliwo- znajomość systemu WordPress. Szablony odgrywają w skórkach kluczową ro-
ści daje nam stworzenie własnej skórki. lę. Jedne z nich, takie jak nagłówek czy stopkę,
• Poznamy najczęściej używane funkcje i nauczy- wykorzystujemy wielokrotnie. Inne natomiast
my się wykorzystywać komentarze. służą nam do wyświetlenia tylko poszczegól-
nych stron. Projektując szablony, musimy wie-
dzieć, jakiego pliku użyje WordPress do wy-
go oczywiście, że definiuje wygląd strony, świetlenia konkretnej podstrony.
zawiera także informacje o skórce. Metada- Na początku WordPress używa danych
Poziom trudności ne o skórce umieszczamy w komentarzu w przesłanych przez przeglądarkę do zidenty-
pliku style.css. WordPress użyje tych danych fikowania typu zapytania. W tym momen-
do wyświetlenia informacji o skórce w panelu cie dowiaduje się, co chcemy wyświetlić: czy
administratora. Komentarz rozpoczynamy uży- jest to strona (Page), post, kategoria czy może

T
worząc szablon dla nowego bloga, zo- wając /*, a kończymy używając */. Metadane inna podstrona. Później WordPress wyszuku-
baczymy, że WordPress oferuje bar- określamy, używając formatu: nazwa: wartość. je kolejno szablony, które może wykorzystać.
dzo wiele funkcji. Nasze szablony Do określenia nazwy skórki używamy identy- Jeżeli szablon o zadanej nazwie istnieje, to on
mogą mieć nie tylko inny wygląd niż domyśl- fikatora: Theme Name. Jeżeli chcemy nazwać zostaje wybrany.
ne, ale także większą funkcjonalność. W ni- naszą skórkę „Moja pierwsza skórka”, odpo- Dla strony głównej naszego bloga Word-
niejszym artykule będziemy wykorzystywać wiednia linia w pliku style.css będzie wyglą- Press w pierwszej kolejności użyje pliku
wersję 2.2, która w chwili pisania artykułu dała jak poniżej. home.php. Jeżeli taki plik nie istnieje, poszuka
była najnowszą wersją. index.php. Pojedynczy post w pierwszej kolej-
Theme name: Moja pierwsza skórka ności będzie szukał pliku single.php, a jeśli ta-
Jak działa system szablonów Używając podobnych identyfikatorów, może- kiego nie ma, zostanie użyty index.php. Stro-
System skórek w WordPressie oparty jest o my dodać następujące informacje: ny (Page) dają nam większe możliwości kon-
szablony i style CSS. Każda skórka posiada figuracji. W pierwszej kolejności WordPress
własny podkatalog w katalogu wp-content/ • adres strony domowej skórki – (Theme URI); będzie szukał pliku szablonu wybranego w pa-
themes/, w którym znajdują się wszyst- • opis skórki – (Description); nelu administratora. Abyśmy mogli wybrać
kie pliki skórki. Gdy wchodzimy na stronę, • wersje – (Version) – opcjonalnie; szablon z panelu administratora, musimy na
skrypt wybiera odpowiedni plik, a następnie • autora – (Author); początku pliku szablonu (np custom.php) w ko-
go wyświetla. Obrazki, style i inne dodatko- • adres strony autora – (Author URI); mentarzu po identyfikatorze Template Name:
we pliki także powinny być zawarte w tym • skórka nadrzędna – (Template) – opcjo- umieścić nazwę naszego szablonu, np.:
katalogu. nalnie.
Pliki szablonów są zwykłymi plikami PHP, /*
w których wymieszany jest kod xHTML Przypatrzmy się uważniej identyfikato- Template Name: My custom template
z kodem PHP. Dzięki licznym funkcjom udo- rowi Template. Jeżeli chcemy, aby nasza */
stępnianym przed WordPress możemy w skórka dziedziczyła szablony z określo-
bardzo prosty sposób wyświetlać różne ele- nej skórki, jako wartość po identyfikato- Jeżeli taki plik nie zostanie znaleziony,
menty na stronie. Nasza skórka musi posia- rze umieszczamy nazwę katalogu wybra- WordPress będzie szukał pliku page.php,
dać przynajmniej 2 pliki: index.php i style.css. nej skórki. Określenie skórki nadrzędnej a na końcu index.php. Dla każdej katego-
Pierwszy posłuży jako szablon domyśl- spowoduje użycie wszystkich szablonów rii możemy użyć osobnego pliku szablonu.
ny wszystkich podstron, a drugi oprócz te- wybranego schematu, co oznacza, że sza- Jeżeli WordPress znajdzie plik o nazwie

22 05/2007
WordPress

category-numer_kat.php, gdzie numer_kat jest szablon, który jest używany, gdy nie znaleziono kod możemy zobaczyć na Listingu 1. Szablon
numerem ID kategorii (np category-3.php), bardziej szczegółowego szablonu. Wiemy już, ten jest bardzo wybrakowany. Brakuje w nim
to ten plik zostanie wykorzystany. Jeżeli jak wyświetlić nagłówek i stopkę. Wykorzysta- nawigacji między poszczególnymi strona-
nie utworzyliśmy takiego pliku, WordPress my do tego funkcje get_header i get_footer. mi, nawigacji między postami ijakiejkolwiek
będzie sprawdzał kolejno pliki: category.php, W tym momencie domyślne pliki są wystar- obsługi komentarzy. Odpowiednie funkcje
archive.php, a na końcu index.php. Do wyświe- czająco dobre. Domyślny plik z nagłówkiem do wykonania tych zadań poznamy w dalszej
tlenia strony o autorze WordPress kolejno wyświetli nam oprócz odpowiednich tagów części artykułu.
sprawdza szablony: author.php, archive.php oraz meta tytuł i opis bloga. Domyślna stopka wy-
index.php. Do wyświetlenia postów z okre- świetli nam oprócz informacji niezbędnych w Przydatne funkcje
ślonej daty WordPress będzie sprawdzał pli- stopce liczbę zapytań oraz czas generowania Poznamy teraz wybrane funkcje, które po-
ki date.php, archive.php i na końcu index.php. strony. zwolą nam tworzyć szablony. Pierwszą będzie
W przypadku wyszukiwania zostanie użyty WordPress przed wyświetleniem szablonu wy- bloginfo. Wyświetla ona opcje ustawione
plik search.php. Jeśli takiego pliku nie znale- wołuje zapytanie do bazy, pobierając odpowied- w panelu admina. Jako argument przyjmu-
ziono, WordPress użyje domyślnego szablonu nie dane za bazy danych. Sam analizuje infor- je nazwę opcji, której wartość ma wyświetlić.
index.php. Jeżeli dana podstrona nie została zna- macje przesłane do skryptu i tworzy odpowied- Jako parametr możemy podać:
leziona, zostanie wykorzystany plik 404.php, nie zapytanie. Aby sprawdzić, czy dostępne są ja-
a jeśli takiego nie ma – index.php. kieś posty, używamy funkcji have_posts. Jeżeli • name – funkcja wyświetli nazwy blogu;
Umiejętne wykorzystanie hierarchii sza- są dostępne do wyświetlenia posty, zwraca true, • description– funkcja wyświetli opisu
blonów pozwala na budowę zaawansowa- jeśli nie ma zwraca false. Używając tej funkcji blogu;
nych i bardzo zróżnicowanych stron. W ram- w pętli while jako warunku, sprawdzamy, czy • url – funkcja wyświetli URL do strony
ce Linki znajduje się adres strony, na której dostępne są kolejne posty do wyświetlenia, a na- bloga;
znajdziemy diagram ilustrujący tę hierarchię. stępnie używając funkcji the_post, „pobieramy” • rdf _ url – funkcja wyświetli URL do
Oprócz hierarchii szablonów WordPress ofe- post. Automatycznie aktualizuje ona niezbęd- RDF/RSS 1.0;
ruje także specjalne funkcje, które pozwa- ne dane, tak abyśmy mogli wykorzystywać funk- • rss _ url – funkcja wyświetli URL do
lają na poziomie konkretnego pliku szablo- cje takie jak the_title czy the_ID. Tytuł posta RSS 0.92;
nu sprawdzić, która strona jest wyświetlana. wyświetlamy, używając funkcji the_post, a aby • rss2 _ url – funkcja wyświetli URL do
Z tymi funkcjami zapoznamy się w dalszej wyświetlić treść, używamy funkcję the_content. RSS 2.0;
części artykułu. Więcej przydatnych funkcji poznamy w dalszej • atom _ url – funkcja wyświetli URL do
części artykułu. Atom feed?;
Struktura strony Zbierzmy teraz wszystkie poznane infor- • comments _ rss2 _ url – funkcja wyświe-
Zastanówmy się nad tym, jak wygląda struk- macje i stwórzmy nasz pierwszy szablon. Jego tli URL do RSS 2.0 feed dla komentarzy;
tura pojedynczej podstrony bloga. Najprost-
sza strona zawiera takie elementy jak nagłó-
Listing 1. Pierwszy szablon
wek, treść i stopka strony. Na początku każ-
dego pliku musi znaleźć się wywołanie funk- <?php
cji get_header, która wyświetli nam odpo- //Wyświetlamy nagłówek
wiedni nagłówek. Jeżeli w katalogu skór- get_header();
ki znajduje się plik header.php, zostanie on ?>
wczytany. Jeżeli go nie stworzyliśmy, zosta- <?php
nie dołączony domyślny plik. Podobnie jak w //Sprawdzamy, czy są dostępne jakiekolwiek posty
przypadku nagłówka – jeżeli stworzyliśmy if (have_posts()) :
plik footer.php zostanie on dołączony zamiast //Wyświetlamy posty w pętli
domyślnej stopki. while (have_posts()) : the_post();
Większość stron zawiera także panel boczny. ?>
Jego kod znajduje się w pliku sidebar.php. Jeże- //Wyświetlamy tytuł i treść
li takiego pliku nie umieścimy w katalogu skór- <div class="post" id="post-<?php the_ID(); ?>">
ki, to ponownie zostanie użyty domyślny plik. <h3 class="title"><?php the_title(); ?></h3>
Aby dołączyć panel boczny, używamy funkcji <div class="content">
get_sidebar. <?php the_content(); ?>
Strona może zawierać także inne elemen- </div>
ty. Do ich dołączania stosujemy na przykład </div>
funkcję include. Gdybyśmy – dajmy na to <?php endwhile; else: ?>
– chcieli dołączyć formularz wyszukiwania <p>
zawarty w pliku searchform.php, musielibyśmy <?php
wywołać funkcję w następujący sposób: //Wyświetlamy informację o braku postów
echo 'Brak postów spełniających twoje kryteria';
include( TEMPLATEPATH .'/searchform.php' ); ?>
</p>
Stała TEMPLATEPATH zawiera ścieżkę do katalo- <?php endif; ?>
gu naszej skórki. <?php
//Wyświetlamy stopkę
Plik szablonu get_footer();
Stworzymy teraz nasz pierwszy plik szablonu. ?>
Index.php w katalogu naszej skórki to domyślny

www.phpsolmag.org 23
Dla początkujących

• pingback _ url – funkcja wyświetli URL • html _ type– funkcja wyświetli zawar- • stylesheet _ url – funkcja wyświetli
dla Pingback; tość „Content-type”; adres URL do pliku style.css w twoim
• – funkcja wyświetli adres
admin _ email • wpurl – funkcja wyświetli URL dla insta- szablonie;
e-mail administratora; lacji WordPressa; • stylesheet _ directory – funkcja wy-
• charset – funkcja wyświetli nazwę kodo- • template _ url – funkcja wyświetli URL świetli URL do katalogu ze stylami.
wania znaków; do używanego szablonu;
• version – funkcja wyświetli wersję • template _ directory – funkcja wyświetli Jeżeli zamiast wyświetlenia powyższych
WordPressa; URL do katalogu szablonu; informacji chcemy je pobrać do zmiennej, po-
winniśmy użyć funkcji get _ bloginfo.
Gdy będziemy chcieli wyświetlić tytuł
Listing 2. Plik sidebar.php. Kod panelu bocznego
bloga, zastosujemy wp_title. Jako pierwszy
<div id="sidebar"> argument możemy podać separator między
<ul> poszczególnymi fragmentami tytułu. Drugi
<li><h2>Archiwa</h2> parametr określa, czy chcemy wyświetlić ty-
<ul> tuł (true, wartość domyślna), czy też wolimy,
<?php aby funkcja zwróciła tytuł jako wynik działania
//Wyświetlamy archiwa funkcji (wartość false).
wp_get_archives('type=monthly'); ?> Poznamy teraz kilka funkcji, które są szcze-
</ul> gólnie przydatne przy budowie własnego
</li> panelu bocznego. Bardzo często pierwszym
<li><h2>Kategorie</h2> elementem, który zauważamy na panelu
<ul> bocznym, jest kalendarz. WordPress oferuje
<?php nam funkcję get_calendar, która pozwala
//Wyświetlamy kategorie go wyświetlić. Przyjmuje ona jeden parametr,
wp_list_categories('sort_column=name&optioncount=1&hierarchical=0'); ?> który określa, czy WordPress ma użyć tyl-
</ul> ko pierwszej litery nazwy tygodnia (wartość
</li> true, np. S zamiast Sun), czy skrótu (wartość
<?php false, np. Sun w niedziele). Przy wyświetla-
//Wyświetlamy linki niu dni tygodnia WordPress opiera się o wy-
get_links_list(); ?> brany język.
<li><h2>Meta</h2> Jeżeli zechcemy wyświetlić łącza do archi-
<ul> wum, najprawdopodobniej wykorzystamy do
<?php tego funkcję wp_get_archives. Domyślnie
//Wyświetlamy link do rejestracji (domyslnie wyświetla link pomiędzy <li> i </li> wyświetli ona łącza do archiwum danego mie-
wp_register(); ?> siąca, używając listy HTML (znaczniki <li>).
<li><?php Funkcja ta przyjmuje jednak parametry, uży-
//Wyświetlamy link logowania/wylogowania wając formatu query string, czyli podobnego
wp_loginout(); ?> do tego, w jakim przekazywane są zmien-
</li> ne w adresie URL metodą GET. Poszczególne
</ul> parametry rozdzielone są znakiem &, a wartość
</li> poprzedzona jest nazwą parametru i znakiem =.
</ul> Jeżeli chcemy wyświetlić linki określonego
</div> rodzaju, używamy identyfikatora type. War-
tościami, które możemy użyć dla tego argu-
Listing 3. Fragment kodu, który pozwala wyeksponować posty z kategorii 3 mentu. są:
<?php
if (have_posts()) : • yearly – wyświetla łącza do archiwum
while (have_posts()) : the_post(); grupowanego po latach;
//Sprawdzamy, czy post należy do kategorii 3. i wyświetlamy odpowiedni tekst • monthly – wyświetla łącza do archiwum
if( in_category(3) ) grupowanego po miesiącach (wartość
echo '<div class=”featured”>'; domyślna);
else • daily – wyświetla łącza do archiwum
echo '<div class=”post”>'; dziennego;
//Wyświetlamy post • weekly – wyświetla łącza do archiwum
?> tygodniowego;
<h1><?php the_title();?></h1> • postbypost – wyświetla łącza do postów.
<div class=”content”><?php the_content(); ?></div>
</div> Możemy także ograniczyć liczbę wyświetla-
<?php nych linków. W tym celu używamy identyfi-
endwhile; katora limit. Do wyświetlenia ostatnich 12
endif; miesięcy możemy użyć poniższego wywoła-
?> nia funkcji.

wp_get_archives('type=monthly&limit=12');

24 05/2007
WordPress

Kolejnym parametrem, który możemy podać, Identyfikator depth określa pokazywaną głę- katora order, podając jako wartość ASC (ko-
jest format. Identyfikator format może przyj- bokość drzewa strony. Ustawienie wartości lejność rosnąca) lub DESC (kolejność maleją-
mować następujące wartości: na 1 pokaże tylko strony najwyższego rzędu, ca). Aby pokazać puste kategorie, ustawiamy
wartości 2 – strony najwyższego rzędu i ich wartość hide_empty na 0 (domyślnie Word-
• html – domyślna wartość, używa listy podstrony. Domyślnie ustawiona jest wartość Press używa wartości 1). Do wyświetle-
HTML do wyświetlenia linków (znacznik 0, co sprawia, że wyświetlane jest pełne drze- nia podkategorii wybranej kategorii uży-
<li>); wo wraz z wcięciami. Ustawienie wartości na wamy identyfikatora child_of, który jako
• option – używany jest do budowania pól -1 powoduje wyświetlenie wszystkich stron wartość przyjmuje numer ID kategorii nad-
SELECT (znacznik <option>); bez wcięć. rzędnej. Podobnie jak w przypadku funkcji
• link – używa znacznika <link>; Gdy chcemy wyświetlić tylko podstrony wp_list_pages – możemy użyć identyfikato-
• custom – używa wartości podanych dla konkretnej strony, używamy identyfikatora rów title_li, include oraz exclude. Mamy
parametrów before i after. child_of. Jako wartość możemy podać nu- również możliwość ograniczenia liczby wy-
mer ID strony, której podstrony chcemy po- świetlanych kategorii, używając identyfikato-
Ostatnim identyfikatorem jest show _ post _ kazać. Kolejnym ważnym atrybutem jest echo, ra number i podając jako wartość maksymalną
count, który odpowiada za wyświetlenie licz- które określa, czy lista ma zostać wyświetlo- liczbę wyświetlanych kategorii.
by postów w archiwach. Działa ze wszyst- na (domyślnie wartość 1) czy zwrócona przez Aby wyświetlić listę autorów, używamy
kimi wartościami type oprócz postbypost. funkcję, (wartość 0). Funkcja ta jest odpowie- funkcji wp_list_authors. Przyjmuje ona jeden
Jeżeli wartość show _ post _ count wynosi 1, dzialna za jeszcze kilka innych atrybutów, np. parametr, który określa różne opcje. Format
WordPress wyświetli liczbę postów. Jeżeli nie wyświetlanie daty, obsługę pól Custom Field tego ciągu znaków jest identyczny jak w przy-
chcemy wyświetlenia tej informacji, podaje- Key i Custom Field Value. padku funkcji wp_list_pages. Najważniejszy-
my wartość zero. Wartość zero jest domyśl- Aby wyświetlić listę kategorii, powinni- mi parametrami, które możemy ustawić, są:
ną wartością, więc jeśli nie podamy tego para- śmy użyć wp_list_categories. Podobnie optioncount (wartości 1 lub 0) – umożliwiają-
metru, to WordPress domyślnie nie wyświetli jak przy kilku poprzednich funkcjach tu- ca pokazanie listy postów, oraz show_fullname
liczby postów. taj także przekazujemy argumenty, używa- (wartości 1 lub 0) – nakazująca wypisanie imie-
Gdy chcemy wyświetlić listę stron (Pages), jąc odpowiednio sformatowanego ciągu zna- nia i nazwiska autora zamiast nicka.
powinniśmy użyć funkcji wp_list_pages. ków. Do określenia kolejności wyświetlania Do zakończenia budowy naszego panelu bocz-
Funkcja ta przyjmuje parametry w sposób kategorii służy identyfikator orderby, który nego brakuje nam jeszcze tylko listy linków. Do
identyczny jak wp_get_archives, używając pozwala sortować według numeru ID (war- ich wyświetlenia służy funkcja get_links_list.
tak formatu zapytania (ang. query string). tość id), nazwy kategorii (wartość name) oraz Jako jedyny parametr możemy przekazać to, we-
Aby określić wyświetlany tytuł listy, usta- liczby postów (wartość count). Aby określić dług jakiej kolumny zostaną uporządkowane
wiamy odpowiednią wartość dla identyfika- kolejność wyświetlania, używamy identyfi- linki. Poprawnymi ergumentami są wartości:
tora title_li. Możemy także posortować
strony, używając identyfikatora sort_column
Listing 4. Dwie pętle z postami z użyciem rewind_post
oraz odpowiednich wartości:
<?php
• post _ title – domyślne, sortowanie if (have_posts()) :
według tytułu strony; while (have_posts()) : the_post();
• menu _ order – sortowanie według tzw. if( in_category(3) )
Page Order ustawianego w panelu admini- echo '<div class=”featured” >'.the_content().'</div>;
stratora; endwhile;
• post _ date – sortowanie według daty endif;
utworzenia; rewind_posts();
• post _ modified – sortowanie według daty if (have_posts()) :
ostatniej modyfikacji; while (have_posts()) : the_post();
• ID – sortowanie według numeru ID; if( !in_category(3) )
• post _ author – sortowanie według nu- echo '<div class=”post” >'.the_content().'</div>;
meru ID autora; endwhile;
• post _ name – sortowanie według tzw. endif;
Post slug. ?>

Kolejność sortowania ustawiamy, używając Listing 5. Wykorzystanie funkcji query_posts


identyfikatora sort _ order i wartości: <?php
//Zapytanie i dalej standardowe wyświetlenie treści
• asc – sortowanie rosnące; query_posts('cat=3&showposts=3');?>
• desc – sortowanie malejące. <?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
Używając identyfikatora exclude, możemy <div class="item">
wykluczyć pewne strony, podając jako war- <a href="<?php the_permalink() ?>" rel="bookmark" title="Stały link do <?php the_
tość rozdzielone przecinkami ich numery ID. title(); ?>"><?php the_title(); ?></a><br/>
Jeżeli chcemy wyświetlić tylko wybrane stro- <?php the+excerpt(); ?>
ny, możemy to uczynić, wykorzystując iden- <?php endwhile; ?>
tyfikator include. Jako wartość przyjmu- <?php endif; ?>
je on – podobnie jak identyfikator exclude </div>
– rozdzielone przecinkami numer ID stron.

www.phpsolmag.org 25
Dla początkujących

id (numer ID linku) oraz name (tytuł linku). Do- W poprzedniej części poznaliśmy kilka ta- tor kategorii, jako drugi sposób wyświetla-
myślnie linki zostaną wyświetlone w kolejności kich funkcji. Były to funkcje umożliwiające nia linków podkategorii);
rosnącej. Aby wyświetlić je w kolejności maleją- wyświetlenie ID posta (the_ID), tytuł posta • the _ permalink – adres trwałego adresu
cej, musimy poprzedzić nazwę kolumny znakiem (the_title) czy treść (the_title). WordPress do postu;
_ (np _id lub _name). Na Listingu 2. możemy zo- oferuje wiele podobnych funkcji. Do najważ- • the _ excerpt – wyświetla fragment tre-
baczyć przykładowy kod panelu bocznego. niejszych należą: ści posta;
Wśród licznych funkcji szablonów możemy • the _ autor – autor posta (warto poznać
znaleźć takie, które działają tylko w pętli z po- • the _ category – wyświetlenie kategorii także inne funkcje, jak np.: the _ author _
stami (w szczególności po użyciu the_post). (jako pierwszy parametr podajemy separa- firstname, the _ author _ lastname, the _
author _ ID, itd.);
• oraz the _ time – czas dodania posta
Listing 6. Przykład pliku szablonu komentarzy
(jako argument przyjmuje format czasu i
<h2 id="comments"><?php comments_number(__('Brak komentarzy'), __('1 komentarz'), daty zgodny z funkcją date).
__('% komentarzy')); ?>
<?php Większość tych funkcji ma także odpowied-
//Sprawdzamy, czy są komentarze niki, które zwracają dane zamiast je wyświe-
<?php if ( $comments ) : ?> tlać np. get _ the _ content, get _ the _
<ol id="comments"> title, itd. WordPress oferuje także funk-
<?php cje pozwalające na nawigację miedzy posta-
//Przechodzimy pętlą po komentarzach i wyświetlamy komentarze mi. Next _ post _ link wyświetla następny
foreach ($comments as $comment) : ?> post, a _ post _ link – poprzedni. Pierwszy
<li id="comment-<?php comment_ID() ?>"> parametr to format wyświetlania linku, a
<?php comment_text() ?> miejsce wstawienia znacznika <a> oznacza-
<p><cite>przez <?php comment_author_link() ?>, <?php comment_date() ?> my tekstem %link. Jeżeli podamy wartość
<?php comment_time() ?></cite> <?php edit_comment_link('Edytuj', ' |'); ?></p> Przejdź do %link, to WordPress wygeneruje
</li> kod Przejdź do <a href=”...”>...</a>. Do-
<?php endforeach; ?> myślnie skrypt przyjmuje format &raquo;
</ol> %link. Jako drugi parametr podajemy for-
<?php else : // Jeżeli nie ma jeszcze komentarzy ?> mat tytułu linku. Miejsce wstawienia
<p>Brak komentarzy</p> tytułu posta oznaczamy tekstem %title.
<?php endif; ?> Domyślnie wyświetlany jest tylko tytuł
<?php posta. Trzeci argument określa, czy na-
//Jeżeli można komentować stępny post ma być tylko z tej samej kate-
if ( comments_open() ) : ?> gorii (wartość true), czy także z innych
<h2 id="postcomment">Komentuj</h2> (wartość false). Domyślnie WordPress
<form action="<?php echo get_option('siteurl'); ?>/wp-comments-post.php" method="post" przyjmuje wartość false, jeżeli nie podamy
id="commentform"> wartości tego parametru. Ostatni parametr,
<?php jaki możemy przekazać, to numery ID kate-
//Jeżeli użytkownik jest zalogowany gorii, jakie chcemy wykluczyć. W wersji 2.2
if ( $user_ID ) : ?> numery ID powinny być rozdzielone prze-
<p>Zalogowany jako <a href="<?php echo get_option('siteurl'); ?>/wp-admin/ cinkiem (np. '4, 5, 6'). We wcześniejszych
profile.php"><?php echo $user_identity; ?></a>.</p> wersjach separatorem był tekst and (np.
<?php else : ?> '4 and 5 and 6').
<p><input type="text" name="author" id="author" value="<?php echo $comment_author; ?>" WordPress udostępnia jeszcze szereg innych
/> funkcji. Dodatkowo pewne rozszerzenia doda-
<label for="author"><small>Autor</small></label></p> ją własne funkcje, których możemy używać w
<p><input type="text" name="email" id="email" value="<?php echo $comment_author_email; szablonach. W tej części artykułu poznaliśmy
?>" /> te najczęściej wykorzystywane. W dalszej czę-
<label for="email"><small>Adres e-mail (nie zostanie opublikowany) </small></label></ ści artykułu pojawią się jeszcze inne przydat-
p> ne funkcje.
<p><input type="text" name="url" id="url" value="<?php echo $comment_author_url; ?>"
/> Pętla z postami i własne zapytania
<label for="url"><small>Strona WWW</small></label></p> Pętla z postami jest najważniejszym elemen-
<?php endif; ?> tem treści niemal każdej strony WordPressu.
<p><textarea name="comment" id="comment"></textarea></p> Wcześniej poznaliśmy kilka przydatnych funk-
<p><input name="submit" type="submit" id="submit" value="Komentuj" /> cji, które możemy wykorzystywać tylko w pętli.
<input type="hidden" name="comment_post_ID" value="<?php echo $id; ?>" /> Kluczowymi funkcjami dla pętli są have_posts
</p> i the_post, które zostały opisane już wcześniej.
<?php do_action('comment_form', $post->ID); ?> Teraz nauczymy się rozwiązywać przy pomocy
</form> wybranych funkcji różne problemy.
<?php else : // Komentowanie nie jest dozwolone ?> Załóżmy, że na stronie głównej nasze-
<p>Nie można komentować tego postu</p> go bloga chcielibyśmy wyeksponować w
<?php endif; ?> szczególny sposób posty z jednej kategorii.

26 05/2007
WordPress

W jaki sposób możemy to osiągnąć? Każ- query_posts('cat=3,5,7'); spowoduje pobranie posta o uproszczonej na-
dy post może znajdować się w kilku kate- zwie first-name. Możemy także chcieć pobrać
goriach. Wyobraźmy sobie, że dysponujemy Jeżeli chcemy wykluczyć pewną kategorię, wybraną stronę. W tym celu wykorzystamy
kategorią „Featured”, której posty chcemy dodajemy znak – przed numerem kategorii. page _ id.
wyeksponować. Załóżmy też, że kategoria ta Używając poniższego wywołania, wyświetlimy
ma ID równe 3. Do sprawdzenia, czy dany posty z wszystkich kategorii z wyjątkiem 3. query_posts('page_id=2');
post jest w naszej kategorii, użyjemy funk-
cji in_category. Przyjmuje ona jako para- query_posts('cat=-3'); Przy pomocy query _ posts możemy tak-
metr numer ID kategorii i sprawdza, czy że podać, ile postów chcemy pobrać (np.
aktualnie przetwarzany post w pętli należy Jeżeli chcemy zamiast ID kategorii podać showposts=3) oraz ile najnowszych postów
do tej kategorii. Na Listingu 3. możemy zoba- jej nazwę, musimy zamiast cat zastosować ma zostać pominiętych (offset=5). Wszyst-
czyć, jak wygląda odpowiedni fragment ko- category _ name. Poniższe wywołanie funkcji kie powyższe części możemy łączyć, używa-
du. Wszelkie elementy nawigacyjne zostały wyświetli posty z kategorii „Featured clips”. jąc znaku &. Aby pobrać np. 5 najnowszych
celowo pominięte. postów z kategorii 3., wywołamy funkcję
Warto wspomnieć jeszcze, że gdybyśmy query_posts('category_name=Featured clips') query _ posts w następujący sposób.
używali tego pliku jako szablonu nie tyl-
ko dla strony głównej, ale także dla innych Możemy także wyświetlić posty konkretne- query_posts('cat=3&showpost=5');
podstron, musimy sprawdzić, czy jesteśmy go autora. Używając author i podając ID au-
na stronie głównej. Aby to uczynić, używa- tora lub używając author _ name i podając Możemy także posortować posty względem
my funkcji is_home. Gdybyśmy o tym za- wartość z kolumny user _ nicename w tabeli innego parametru niż data dodania. Służą
pomnieli, nie moglibyśmy wyświetlić listy użytkowników. do tego orderby do określenia kolumny sor-
postów z kategorii „Featured”. Odpowiedni towania oraz order do określenia porządku
warunek powinien wyglądać tak: query_posts('author=3'); sortowania. Funkcja query _ posts ma jesz-
query_posts('author_name=Kodie'); cze kilka przydatnych opcji, z którymi war-
if( is _ home() && in _ category(3) ) to się zapoznać.
Możemy także pobrać pojedynczy post uży- Są to między innymi wyświetlenie po-
Wyobraźmy sobie, że chcielibyśmy wy- wając p i jako argument podając numer ID stów z wybranego roku ( year=2005), miesią-
świetlić najpierw najnowsze posty z kate- posta lub używając name i podając tzw. post ca (monthnum=6), dnia (day=15) oraz kilka in-
gorii „Featured”, a dopiero potem pozosta- slug, czyli uproszczoną tytuł posta. Poniższe nych. Wymienione zostały te najważniejsze
łe. Wykorzystamy tutaj ponownie funk- wywołanie funkcji spowoduje pobranie posta parametry, z tymi rzadziej wykorzystywa-
cję in _ category. Aby „przewinąć” posty o numerze ID równym 3. nymi możemy zapoznać się w Internecie na
i rozpocząć pętlę od nowa, musimy wyko- stronie: http://codex.wordpress.org/Template_
rzystać funkcję rewind _ posts. Dzięki te- query_posts('p=3') Tags/query_posts.
mu znowu będziemy mogli w taki sam spo- Wiemy już, w jaki sposób pobrać no-
sób jak w pierwszym przypadku wykorzy- Natomiast wywołanie we posty. Teraz zostaje tylko ponownie
stać funkcje have _ posts i the _ post. Na wykorzystać znane nam już dobrze funk-
Listingu 4. możemy zobaczyć kod wykonu- query_posts('name=first-post'); cje have_posts i the_post. Na Listingu 5.
jący dwie pętle.
Może się zdarzyć jednak tak, że oprócz naj-
Listing 7. Plik functions.php
nowszych postów będziemy chcieli wyświetlić
dokładnie 5 najnowszych postów z jednej wy- <?php
branej kategorii. Wtedy będziemy musieli – po- function my_get_the_image($content){
dobnie jak wcześniej – wykonać pętlę dwa razy. //Szukamy znacznika <img
Musimy jednak wywołać nowe zapytanie. W $img = strpos( $content, '<img');
tym celu wykorzystamy funkcję query_posts. //Jeżeli takiego nie ma, zwracamy pusty ciąg znaków
Jako jedyny parametr przyjmuje ona ciąg zna- if( $img === false ) return '';
ków określający, co ma zostać pobrane, ile po- //Szukamy ciąg src=”
stów i jak mają zostać uszeregowane ponow- $srcpos = strpos( $content. 'src=”', $img ) + 5; /* = strlen('src=”') */
nie, wykorzystując format query string. Z tych //Pobieramy adres obrazka
informacji WordPress sam tworzy zapytanie $path = substr($content, $srcpos, strpos($content, '”', $srcpos) - $srcpos);
SQL. Jakie zatem informacje możemy przeka- //Zwracamy nową wartość
zać tej funkcji? return '<img src=”'.$path.'” class=”thumb”/>';
Rozpoczniemy od podania kategorii, z któ- }
rej chcemy pobrać posty. Do tego służy identy- ?>
fikator cat. Odpowiednie wywołanie tej funk-
cji, aby pobrać tylko posty z kategorii 3., wyglą- Listing 8 Komentarze WordPress
dałoby następująco. $query = new WP_Query();
$query->query('cat=3');
query_posts('cat=3'); if( $query->have_posts() )
while( $query->have_posts() ){
Jeśli chcielibyśmy pobrać posty z kilku katego- $query->the_post();
rii, musimy oddzielić je przecinkami. Poniż- /* ... */
szy kod pozwala nam wyświetlić posty z kate- }
gorii 3., 5. oraz 7.

www.phpsolmag.org 27
Dla początkujących

możemy zobaczyć kod wykorzystujący funk- z nadpisywaniem wyników i używaniem wty- funkcję comment_author_link. Jeżeli nie
cję query_posts. czek, teraz zajmijmy się komentarzami. chcemy wyświetlać linku, a tylko samego au-
Co jednak stanie się, jeżeli zapragniemy tora, możemy posłużyć się funkcją comment_
wykorzystać query_post przed wywołaniem Komentarze author. Sam adres URL możemy wyświe-
pierwszej pętli? Nie będziemy mogli już wy- Nieodłączną częścią każdego bloga są komenta- tlić, używając funkcji comment_author_url,
korzystać wyniku pierwszego zapytania, gdyż rze. WordPress oferuje gotowe funkcje i rozwią- a cały link – comment_author_url_link.
zostanie nadpisany drugim. Jak wybrnąć z tej zania do wykorzystania. Szablon komentarzy Do wyświetlenia kolejno adresu e-mail i łącza
sytuacji? Są dwa rozwiązania. Musimy wie- znajduje się w pliku comments.php, jeśli jednak do niego używamy odpowiednio comment_
dzieć, że funkcje query_posts, have_posts i go nie stworzymy, WordPress użyje domyślne- author_email i comment_author_email_link.
the_post zapisu wykorzystują wyniki zapisa- go szablonu ze skórki default. Nie powinniśmy jednak wyświetlać adresu
ne w zmiennej $wp_query. Możemy zatem za- Aby dołączyć komentarze do naszej stro- e-mail autora komentarzy ze względu na
pisać kopię zmiennej $wp_query w zmiennej ny, musimy posłużyć się funkcją comments_ ochronę naszych użytkowników przed spa-
tymczasowej, wywołać zapytanie, a potem template. Jako jedyny parametr możemy po- mem. Możemy jeszcze pokazać link do edycji
przywrócić oryginalną zawartość $wp_query. dać jej nazwę naszego szablonu. Jeśli jej nie po- komentarza. W tym celu wykorzystamy funk-
W PHP 4 wystarczy przypisać zmienną damy, WordPress automatycznie uzna, że sza- cję edit_comment_link. Przyjmuje ona trzy
$wp_query do innej zmiennej. W ten sposób blon nazwaliśmy comments.php. Funkcja ta za- argumenty: tekst łącza, tekst wyświetlany
zostanie stworzona jej kopia: działa tylko, jeżeli nasza strona wyświetla stro- przed znacznikiem <a> oraz jako trzeci tekst
nę, post lub zmienna $withcomments ustawio- wyświetlany po znaczniku </a>. Funkcję tę
$temp_query = $wp_query; na jest na true. możemy wywołać w następujący sposób:
Rozpocznijmy tworzenie szablonu z ko-
Jednak w PHP 5 operator = przypisuje referen- mentarzami. Przechowywane są one w zmien- edit_comment_link('edycja komentarza',
cje, a nie kopie obiektu. Musimy zastosować nej $comments. Musimy także wiedzieć, że '<p>', '</p>');
nowy operator klonowania obiektu: clone. większość funkcji pobierająca lub wyświetla-
jąca dane wybranego komentarza pobiera je Najważnieszją funkcją jest wyświetlenie tre-
$temp_query = clone $wp_query; ze zmiennej $comment. W takim razie nasza ści komentarza. W tym celu posłużymy się
pętla wyświetlająca komentarze powinna wy- comment _ text. To są najważniejsze funk-
Następnie wystarczy po pętli przypisać do glądać tak: cje obsługi pojedynczego komentarza, warto
$wp _ query zawartość $temp _ query. Na początku tego kodu sprawdzamy, czy są też zapoznać się z tymi rzadziej używanymi.
Niestety, używając tej metody, musimy albo jakieś komentarze, a następnie w pętli wyświe- Informacje o nich możemy znaleźć w pliku
znać wersję PHP, na której będzie umieszczo- tlamy je. Teraz poznamy funkcje pozwalające wp-includes/comment_template.php oraz w doku-
na strona, albo sprawdzać wersję przed stworze- wyświetlić poszczególne elementy komenta- mentacji na stronie http://codex.wordpress.org/
niem kopii. Jest jednak inne rozwiązanie. Funk- rza. Są one bardzo podobne do funkcji wyświe- Template_Tags#Comment_tags.
cje query_posts, have_posts oraz the_post tlającej posty. Comment_ID wyświetla numer ID Wyświetlając posty, musimy także spraw-
są tylko funkcjami ułatwiającymi działanie na komentarza. Comment_date wyświetla datę do- dzić, czy dany post nie czeka w kolejce do
obiekcie WP_Query przechowywanym w zmien- dania komentarza. Jako jedyny parametr po- moderacji. Aby to uczynić, sprawdzamy
nej $wp_query. Zamiast używać tych funk- dajemy format daty zgodnie z formatem funk- wartość pola $comment->comment_approved.
cji możemy samodzielnie stworzyć obiekt WP_ cji date. Jeśli nie podamy formatu, WordPress Jeżeli wynosi ona zero, oznacza to, że komen-
Query i zamiast funkcji query_post wykorzy- pobierze format z ustawień bloga. Podobną tarz nie przeszedł jeszcze procesu moderacji.
stać metodę query. Zamiast funkcji have_posts funkcją jest comment_time, która wyświetla W tym wypadku powinniśmy wyświetlić sto-
i the_post używamy metod tego obiektu o tej sa- czas dodania posta. Jeżeli nie podamy żadne- sowną informację.
mej nazwie. Zatem wywołanie funkcji wraz z pę- go parametru lub przekażemy pusty ciąg zna- Zajmijmy się teraz innymi funkcjami.
tlą powinno wyglądać następująco: ków, WordPress ponownie pobierze formato- Funkcja comments_number oferuje nam moż-
Niektóre wtyczki mają problemy z radze- wanie czasu z ustawień bloga. Jako drugi para- liwość wyświetlenia liczmy komentarzy.
niem sobie z wieloma zapytaniami. Aby temu metr możemy podać, czy chcemy wyświetlić Przyjmuje ona dokładnie trzy argumenty:
zaradzić, powinniśmy wywołać update_post_ czas lokalny (wartość false), czy czas GMT tekst do wyświetlenia w przypadku braku
caches($posts) po wywołaniu metody lub (wartość true). Domyślnie wyświetlany jest komentarzy, tekst do wyświetlenia w przy-
funkcji the_post. czas lokalny. padku jednego komentarza oraz tekst do wy-
Wiemy już, jak wykonać osobne zapytania i Aby wyświetlić autora wraz z podanym świetlenia w przypadku wielu komentarzy.
jak poradzić sobie z problemami związanymi przez niego adresem URL, wykorzystujemy W tym ostatnim przypadku, aby wskazać
miejsce wystąpienia liczby, używamy znaku
% . Zatem wywołanie funkcji:
Listing 9. Wywoływanie metody the_post

while( $query->have_posts() ): comments_number('Brak komentarzy',


$query->the_post(); 'Tylko jeden komentarz', '% komentarzy');
update_post_caches($posts);
endwhile; spowoduje wyświetlenie tekstu „Brak komen-
tarzy”, jeżeli nie ma jeszcze żadnego komenta-
Listing 10. Pętla wyświetlająca komentarze rza, wyświetlenie tekstu „Tylko jeden komen-
if( $comments ): tarz”, jeżeli dodano tylko jeden komentarz
foreach( $comments as $comment ): oraz „15 komentarzy” jeśli dodano 15 komen-
//wyświetlenie danych tarzy. Inną przydatną funkcją jest comments_
endforeach; rss_link. Jak łatwo się domyślić, pozwala
endif; onawyświetlić łącze do kanału RSS z komen-
tarzami. Jako pierwszy parametr podajemy

28 05/2007
WordPress

tytuł łącza, jako drugi możemy podać skrypt wanych w szablonach albo do dodania strony akcji wp_head, która wywołana jest tuż przed
generujący plik RSS. Jeżeli nie podamy te- w panelu administratora. Kiedy możemy wy- zamknięciem znacznika head. Przyjmijmy,
go pliku, WordPress użyje domyślnego pliku korzystywać taki plik? Możliwości jest wiele. że nazwiemy naszą funkcję my_custom_head.
wp-commentsrss2.php. Przed wyświetleniem My wykorzystamy tę funkcjonalność, aby wy- Przykładowe wywołanie funkcji add_action
formularza dodającego komentarze musi- świetlić miniaturkę pierwszego obrazka postu może wyglądać tak jak kod poniżej.
my sprawdzić, czy możemy dodawać ko- (o ile istnieje) przed fragmentem tekstu.
mentarze. Jeśli tak, to wartość pola $post-> Zastanówmy się, w jaki sposób może dzia- add_action('wp_head', 'my_custom_head');
comment_status będzie wynosić “open”. łać taka funkcja? Przede wszystkim pobierze-
Jeśli można dodawać komentarze, to wyświe- my adres pierwszego obrazka z treści. Aby Nasza funkcja my _ custom _ head będzie wy-
tlamy formularz. Jako parametr action formu- pobrać treść, wykorzystamy funkcję get_the_ pisywała treść, używając funkcji echo. Jej kod
larza ustawiamy plik wp-comments-post.php. content. Należy pamiętać, że większość wty- możemy zobaczyć na Listingu 6. Warto wspo-
Aby podać dokładny adres, używamy funk- czek, które modyfikują treść posta, nie zadzia- mnieć, że do jednej akcji możemy przypisać
cji get_option podając jako parametr tekst ła, gdy używamy get_the_content. Gdy za- wiele funkcji, które mają zostać uruchomio-
siteurl. Znacznik otwierający formularz mo- leży nam na zadziałaniu wtyczek (np. wtycz- ne. Oprócz akcji możemy używać także fil-
że wyglądać następująco: ki galerii), to najłatwiejszym wyjściem będzie trów, które pozwalają nam zmodyfikować da-
użycie funkcji ob_start, wywołanie the_ ne przed wyświetleniem. Przykładem takie-
<form action="<?php echo get_option(' content, później zapisanie treści do zmien- go zastosowania może być na przykład filtr
siteurl'); ?>/wp-comments-post.php" nej używając funkcji ob_get_contents oraz zmieniający w treści posta emotikony na ob-
method="post" id="commentform"> użycie ob_end_clean do wyczyszczenia bufo- razki przed wyświetleniem. Najważniejszą
ra. Skupmy się teraz na odnalezieniu obrazka. różnicą między filtrami a akcjami jest to, że
Dalej wyświetlamy pola formularza. Pamię- Mając treść strony, wystarczy odnaleźć znacz- filtry przyjmują wartość do zmiany jako pa-
tajmy, że dla zalogowanych użytkowników nik <img> a w nim atrybut src. Dla uprosz- rametr i zwracają ją używając return. Akcje
nie musimy wyświetlać pól: autor, e-mail czenia przyjmijmy, że dokument jest sforma- natomiast nie przyjmują argumentów i wy-
i strona WWW. Aby to sprawdzić, spraw- towany poprawnie i wartość atrybutu znaj- świetlają wynik swojego działania bezpośred-
dzamy, czy $user _ ID nie jest fałszem lub duje się w cudzysłowie po znaku równości. nio na stronie (np. przez funkcję echo). Pełną
nie jest pusty. Pola formularza mają ściśle Takie zadanie można wykonać na wiele spo- listę filtrów i akcji znajdziemy pod adresami
określone nazwy. Zatem pole autor ma na- sobów. My wykorzystamy najprostszy z uży- podanymi w ramce. Filtry i akcje są przydat-
zwę author, pole e-mail nazwę email, pole ciem funkcji strpos i substr. Nazwiemy ne, lecz częściej niż w szablonach wykorzystu-
strony WWW nazwę url, a pole treści ko- naszą funkcję my_get_the_image i zapiszemy je się je przy budowie wtyczek. Pozostaje pyta-
mentarza nazwę comment. Musimy jeszcze ją w pliku functions.php. Jej kod możemy zoba- nie, kiedy używać wtyczek, a kiedy pliku func-
dodać ukryte pole o nazwie comment _ czyć na Listingu 7. tions.php. Używając obu rozwiązań, możemy
post _ ID o wartości $id . Jeśli chcemy wy- W kodzie pliku home.php w miejscu, gdzie stworzyć rozwiązania o podobnej funkcjonal-
świetlić listę znaczników, które mogą zostać powinien pojawić się adres obrazka, wstawia- ności. Musimy pamiętać jednak o tym, że plik
użyte w komentarzach, używamy funkcji my poniższą linię functions.php działa tylko dla określonego sza-
allowed _ tags. Przed znacznikiem zamy- blonu, a nie dla wszystkich, jak ma to miej-
kającym formularz powinniśmy jeszcze dać echo my_get_the_image( get_the_content() ); sce z wtyczkami. Plik functions.php jest tylko
wywołanie funkcji do _ action, co umożli- formą pomocy szablonom w wyświetlaniu
wi wtyczkom modyfikacje naszego formula- Oczywiście obrazek należy przeskalować. danych czy też stworzeniu pewnych opcji kon-
rza. Wywołanie tej funkcji powinno wyglą- Można w tym momencie skorzystać np. ze figuracji szablonu. Jednak do modyfikacji da-
dać następująco: skryptu phpThumb lub samodzielnie utworzyć nych powinniśmy raczej używać wtyczek.
miniaturkę i zapisać ją na dysku i zwrócić do
do_action('comment_form', $post->ID); niej ścieżkę. Zachęcam do samodzielnego wy- Podsumowanie
konania takiej funkcji. Dla naszych potrzeb W artykule starałem się pokazać, że tworzenie
Na Listingu 6. możemy zobaczyć przykłado- zadowolimy się przeskalowaniem za pomo- szablonów w WordPressie daje znacznie więk-
wy plik wyświetlający komentarze. Gdy pro- cą stylów CSS. Trzeba jednak pamiętać, że to sze możliwości niż tylko zmiana wyglądu. Od-
jektujemy własną skórkę, zwykle łatwiej jest rozwiązanie ma swoje wady. powiednio wykorzystana wiedza pozwala prze-
skopiować plik comments.php ze skórki default Używając pliku functions.php, możemy rów- kształcić nasz blog nie do poznania. Niniejszy
i zmodyfikować ten plik niż tworzyć całość od nież dodać własne funkcje do akcji. Służy do artykuł jest tylko wprowadzeniem w tematy-
początku. tego funkcja add_action. Dzięki niej mamy kę tworzenia skórek. Zapoznanie się ze strona-
ogromne możliwości modyfikacji wyświetla- mi podanymi w ramce na pewno pomoże jesz-
Functions.php nych elementów zarówno na stronie bloga, cze rozszerzyć wiedzę i zobaczyć więcej intere-
Poznaliśmy już, jak tworzyć pliki szablo- jak i w panelu administratora. Nasza funkcja sujących przykładów oraz rozwiązań.
nów. WordPress oferuje nam także dodatko- zostanie wywołana w odpowiednim momen-
wy plik functions.php, który jest wczytywany cie, tak że będziemy mogli dodać własne dane.
przed szablonami. Może on posłużyć np. do Zobaczymy, jak moglibyśmy dodać do sekcji
zdefiniowania własnych funkcji wykorzysty- HEAD naszej strony kod JS lub CSS. Użyjemy

KONRAD GOŁUCHOWSKI
W Sieci Autor od wielu lat zajmuje się programowaniem
w języku PHP. Jest administratorem i redaktorem
• http://codex.wordpress.org/ – strona dla administratorów i deweloperów WordPressa serwisów CompZone.Org i TryCMS.org. Progra-
• http://codex.wordpress.org/Template_Hierarchy – hierarchia szablonów
muje także w językach Java i C++.
• http://codex.wordpress.org/Plugin_API/Filter_Reference – filtry w WordPressie
• http://codex.wordpress.org/Plugin_API/Action_Reference – akcje w WordPressie Kontakt z autorem:
kodie@compzone.org, kodie@trycms.org

www.phpsolmag.org 29
Dla początkujących

Symfony Framework
Część pierwsza – Moduł Użytkownika

Frameworki wyrosły jak grzyby po deszczu, ciężko jest czasami dobrać


odpowiedni dla danego zadania. Prezentujemy szybki framework Symfony,
którego domeną jest to, że został wybrany przez Yahoo! Do stworzenia systemu
zakładek dla swoich użytkowników. Żmudne programowanie przechodzi
powoli do lamusa.
• pear install http://propel.phpdb.org/
Dowiesz się... Powinieneś wiedzieć... pear/propel_runtime-current.tgz;
• W jaki sposób zainstalować framework Symfony • Powinieneś znać podstawy programowania • aktualizujemy bibliotekę PEARA polece-
oraz jak skonfigurować go do swoich projektów. obiektowego w PHP 5 oraz podstawy Propela. niem pear upgrade PEAR,
• Poznasz modułową zasadę budowania aplika- Znajomość CSS może pomóc w analizie strony
cji. Przekonasz się, jak prosto możesz budować wizualnej. Umiejętność edycji pliku konfigura- podczas aktualizacji PEARA może poja-
i sprawdzać formularze. cyjnego Apache'a. wić się problem, jeśli mamy starą bibliotek
Archive_Tar (poniżej wersji 1.3.1), wtedy na-
leży najpierw aktualizować ten pakiet pole-
które będą miały za zadanie odpowiednie spraw- ceniem
dzanie formularzy.
Poziom trudności pear upgrade http://download.pear.php.net/
Instalacja frameworku Symfony package/Archive_Tar-1.3.1.tgz,
Do testów użyjemy wersji sf_sandbox. Jest to
pełen pakiet, który wystarczy skopiować na ser- kolejnym krokiem jest instalacja Symfony przy

S
ymfony jest frameworkiem typu RAD, wer i uruchomić w przeglądarce internetowej. pomocy następujących poleceń:
dzięki któremu można tworzyć w bardzo W tym celu:
szybki i przyjemny sposób aplikacje bazo- pear channel-discover pear.
danowe. Pozwala na łączenie się z ważniejszymi • Pobieramy sf_sandbox.tgz z http://www. symfony-project.com
typami bazy danych. Proces tworzenia przyszłej symfony-project.com/get/sf_sandbox.tgz, rozpa- pear install symfony/symfony
aplikacji polega na utworzeniu aplikacji frontend, kowujemy i kopiujemy do katalogu, z które- pear install http://phing.info/pear/
a następnie modułów użytkownik, posty, osym- go będzie miał dostęp Apache; phing-current.tgz
fony, kontakt. Podczas tworzenia modułu gene- • Konfigurujemy wirtualny host w pliku
rowana jest podstawowa klasa akcji, którą w póź- konfiguracyjnym serwera Apache, jak wi- (instalujemy pakiet, jeśli nie był wcześniej
niejszej fazie programowania będziemy systema- dać na Listingu 1; zainstalowany). Po wykonaniu polecenia
tycznie uzupełniać. Będziemy projektowali pro- • Przeładowujemy serwer Apache. symfony – V powinien pojawić się napis z
sty system rejestracji nowego użytkownika, logo- wersją Symfony, w moim przypadku Symfony
wania oraz dodawania przez niego postu z moż- Jest to bardzo dobre rozwiązanie do testów, nie version 1.0.4
liwością edycji, jak widać na Rysunku 1. Struk- jest jednak zalecane do aplikacji końcowych, Konfigurację wirtualnego hosta wykonu-
tura naszego projektu będzie składała się z nastę- które mają być dostępne szerszemu gronu użyt- jemy tak samo jak w przypadku sf_sandbox.
pujących katalogów apps, batch, cache, config, da- kowników. W tym celu lepiej zainstalować Jeśli wszystko przebiegło prawidłowo i instala-
ta, doc, lib, log, plugins, test, web. W tym artyku- symfony ręcznie, jak jest to opisane poniżej. cja Symfony zakończyła się sukcesem, to po wy-
le będziemy głównie zajmować się katalogiem wołaniu strony ukaże się nam informacja jak na
apps, który z kolei będzie składał się na katalog Instalacja pod Linuksem Rysunku 2.
frontend a w nim będą zawarte config, i18n, lib, Aby zainstalować poprawnie działające Sym-
modules, templates. Następnie katalog modules bę- fony z biblioteką Propel i Creole, należy wyko- Uwaga
dzie zawierał wygenerowane moduły m.in. uzyt- nać następujące czynności z poziomu shella z Bardzo ważne jest, aby serwer Apache miał
kownik. W module tym będą katalogi actions, uprawnieniami roota: możliwość zapisywania w katalogu cache i log.
config, lib, templates i validate. W actions zawar-
ta będzie klasa actions.class.php odpowiedzialna • instalujemy PEAR::Log poleceniem cffff; Konfigurujemy Symfony
za główną funkcjonalność modułu uzytkownik. • następnie przy pomocy PEARA instaluje- i bazę danych
Z kolei templates będzie zawierał szablony, które my Propel poleceniami: Tworzymy bazę danych tak jak na Listingu 2,
będą wykorzystywane w metodach klasy actions. • pear install http://propel.phpdb.org/ a następnie konfigurujemy pliki database.yml
Katalog validate służy do umieszczania plików, pear/propel_generator-current.tgz; i propel.ini, jak to widać na Listingu 3. i 4.

30 05/2007
Symfony Framework

Uwaga
Przy konfiguracji plików z rozszerzeniem yml Listing 1. Konfiguracja wirtualnego hosta w pliku httpd.conf Apache'a
należy używać wyłącznie spacji, nie można
używać tabulatora, w przeciwnym razie będzie <VirtualHost 127.0.0.1:80>
zgłaszany błąd. W przyszłości więc jeśli będzie- # nazwa hosta, po którym będzie wywołanie strony w przeglądarce internetowej
my edytować pliki register.yml i login.yml spraw- # http://symfony
dzanie formularza nie zadziała prawidłowo, ServerName symfony
należy najpierw sprawdzić plik yml, czy czasa- # w tym miejscu podajemy ścieżkę dostępu do katalogu symfony i podkatalogu web
mi nie ma gdzieś tabulatora. Następnie musimy DocumentRoot „/var/www/html/symfony/web”
założyć kilka tabel w bazie danych, które będą # definiujemy, że jako pierwszy ma być przeczytany index.php
niezbędne w naszym projekcie. Przede wszyst- DirectoryIndex index.php
kim należy przeanalizować problem. Ogólna # definiujemy alias do katalogu sf znajdującego się w katalogu web
funkcjonalność aplikacji będzie następująca: Alias /sf /var/www/html/symfony/web/sf
# umożliwiamy dostęp do katalogu web
• rejestracja nowego konta; <Directory „/var/www/html/symfony/web”>
• panel logowanie; AllowOvveride All
</Directory>
</VirtualHost>

Listing 2. Tworzenie nowej bazy danych i użytkownika.


Logujemy się jako administrator bazy danych MySQL
# mysql -u root -p
Enter password:
Tworzymy bazę danych Symfony
mysql> CREATE DATABASE projekt_symfony;
mysql> use mysql;
Tworzymy nowego użytkownika user bazy danych projekt_symfony o haśle haslo
mysql> GRANT select,insert,update,delete,create,drop,alter ON projekt_symfony.*
to user@localhost IDENTIFIED BY 'haslo';
Query OK, 0 rows affected (0.02 sec)
mysql> FLUSH PRIVILEGES;

Listing 3. Konfiguracja pliku database.yml. Domyślną zawartość pliku zastępujemy poniższymi


liniami kodu
All:
Rysunek 1. Nasza przyszła aplikacja, widok na propel:
formularz rejestracji class: sfPropelDatabase
param:
datasources: symfony
Wybieramy bazę danych MySQL
phptype: mysql
hostspec: localhost
Nazwa bazy danych
database: projekt_symfony
Nazwa użytkownika bazy danych
username:user
Hasło użytkownika user
password: haslo

Listing 4. Fragment pliku propel.ini. Ustawiamy następujące linie:


propel.target = lib.model
propel.packageObjectModel = true
;Ustawiamy nazwę taką samą jak baza danych
propel.project = symfony
;Wybieramy typ bazy danych
propel.database = mysql
;Dodajemy użytkownika, hasło i nazwę bazy danych, aby PROPEL mógł się połączyć
;z bazą danych
propel.database.createUrl = mysql://user:haslo@localhost
propel.database.url = mysql://user:haslo@localhost/projekt_symfony
propel.home = .
;Ustawiamy główny katalog naszego projektu
Rysunek 2. Widok po poprawnej instalacji propel.output.dir = /var/www/html/symfony
Symfony

www.phpsolmag.org 31
Dla początkujących

• edycja profilu;
Listing 5. Zapisanie kolumn w znacznikach <column /> • przypomnienie hasła (wygenerowanie no-
<column name=”id” type=”integer” required=”true” wego i przesłanie e-mailem);
primaryKey=”true” autoIncrement=”true” /> • pisanie nowych postów;
<column name=”created_at” type=”timestamp” /> • walidacja formularzy;
<column name=”sha1_password” type=”varchar” size=”40” /> • wysyłanie e-maila z potwierdzeniem zało-
<column name=”salt” type=”varchar” size=”32” /> żenia konta;
<column name=”nick” type=”varchar” size=”20” /> • edycja podstron serwisu przy pomocy apli-
<column name=”strona_www” type=”varchar” size=”100” /> kacji backend.
<column name=”email” type=”varchar” size=”100” />
<column name=”podpis” type=”varchar” size=”255” /> Będą zatem potrzebne cztery tabele: użytkow-
<column name=”potwierdzenie” type=”integer” size=”5” /> nik, posty, osymfony i kontakt. Wobec tego edy-
<column name=”status” type=”integer” size=”1” default=”0” /> tujemy plik schema.xml, który znajduje się w
katalogu config.
Listing 6. Metoda executeRegister(). Odpowiedzialna za wyświetlanie formularza rejestracji i
zapisywania nowego użytkownika do bazy danych Uwaga
public function executeRegister() { Jeśli w katalogu istnieje plik schema.yml, nale-
// jeśli został wysłany formularz, zapisujemy nowego użytkownika do bazy danych, ży go usunąć, gdyż będziemy korzystać z pli-
// jeśli nie to formularz rejestracji ku zapisanego w XML-u. Jeśli plik schema.xml
if ($this->getRequest()->getMethod() == sfRequest::POST) { nie istnieje, tworzymy go. Na początku musi-
// generowanie liczby z podanego przedziału, wymagane przy potwierdzeniu konta my zdefiniować typ dokumentu xml. Ustawia-
$potwierdzenie = mt_rand(10000,99999); my również odpowiednie kodowanie znaków:
// zapisywanie danych do tabeli uzytkownik <?xml version=”1.0” encoding=”ISO-8859-2”?>
// tworzymy nowy obiekt klasy Uzytkownik Strukturę bazy danych zawieramy w znaczni-
$uzytkownik = new Uzytkownik(); ku <database></database>:
$uzytkownik->setNick($this->getRequestParameter('nick'));
$uzytkownik->setStronaWww($this->getRequestParameter('strona_www')); <database package=”lib.model” name=”propel”
$uzytkownik->setEmail($this->getRequestParameter('email')); defaultIdMethod=”native” noxsd=”true”>
$uzytkownik->setPodpis($this->getRequestParameter('podpis')); </database>
$uzytkownik->setPotwierdzenie($potwierdzenie);
// zapisujemy dane do tabeli użytkownika Następnie dodajemy tabelę użytkownik
$uzytkownik->save(); między znacznikami <database></database>.
// wysyłanie e-maila z potwierdzeniem tworzymy nowy obiekt klasy sfMail Poszczególne kolumny będą zawarte w znacz-
$mail = new sfMail(); niku <table></table>, gdzie phpName oznacza
$mail->initialize(); nazwę, do której będziemy się odwoływać
// ustawiamy sendmail jako główny program do wysyłania poczty, w systemach Linux w języku PROPEL. Dla wygody phpName
// sendmail może być aliasem do np. Postfixa ustawiamy na nazwę tabeli z tą różnicą, że
$mail->setMailer('sendmail'); pierwsza litera jest duża. Dzięki temu nazew-
// ustawiamy kodowanie nictwu unikniemy wielu problemów, któ-
$mail->setCharset('iso-8859-2'); re w późniejszym okresie tworzenia aplika-
// ustawiamy nadawcę cji mogą okazać się trudne do zlokalizowania
$mail->setSender('lukasz@helionet.com.pl','Redaktor'); i naprawienia.
$mail->setFrom('lukasz@helionet.com.pl','Redaktor');
// dodajemy adres e-mail rejestrowanego użytkownika <table name=”uzytkownik”
$mail->addAddress($this->getRequestParameter('email')); phpName=”Uzytkownik”>
// ustawiamy temat e-maila </table>
$mail->setSubject('Rejestracja konta i potwierdzenie');
// ustawiamy treść e-maila Do tabeli dodajemy kolumny id, created_at,
$mail->setBody(' sha1_password, salt, nick, strona_www, email, pod-
Witamy w testowym projekcie opartym na frameworku Symfony,\n\n pis, potwierdzenie i status. Każda kolumna będzie
Prosimy o potwierdzenie konta. W tym celu kliknij w poniższy link:\n zapisana w znaczniku <column /> – Listing 5,
http://symfony/uzytkownik/potwierdzenie/kod/'.$potwierdzenie.'\n\n W podobny sposób dodajemy pozostałe tabele sto-
Pozdrawiamy,\n sując się do podanych wytycznych: tabela posty:
Redakcja projektu Symfony w akcji ');
// wysyłamy e-mail • kolumna id typu integer z zaznaczoną
$email->send(); opcją required, podstawowym kluczem
// ustawiamy nowy szablon z komunikatem dla użytkownika tuż po pomyślnym primaryKey oraz automatyczną numera-
wysłaniu formularza cją autoIncrement;
$this->setTemplate('nowy_uzytkownik');; • kolumna id_uzytkownika typu integer
} else { z zaznaczoną opcją required;
// wyświetlanie formularza z szablonu registerSuccess.php • kolumna created_at typu timestamp;
return sfView::SUCCESS; • kolumna tytul typu varchar o rozmiarze
} size równym 255 znakom;
• kolumna tresc typu longvarchar.

32 05/2007
Symfony Framework

Tabela osymfony: Kolejnym krokiem jest stworzenie tabel • osymfony;


w bazie danych. Dokonujemy tego przy pomo- • kontakt.
• kolumna tresc typu longvarchar. cy następujących poleceń:
Tworzymy moduły za pomocą poleceń:
Tabela kontakt: ./symfony propel-build-sql
./symfony propel-insert-sql ./symfony init-module frontend uzytkownik
• kolumna tresc typu longvarchar. ./symfony init-module frontend posty
Po tych operacjach możemy w końcu zabrać ./symfony init-module frontend osymfony
Kolejnym krokiem jest wywołanie polecenia: się za programowanie naszej aplikacji. Teraz ./symfony init-module frontend kontakt
będzie naprawdę ciekawie.
/symfony propel-build-model Każdy z modułów składa się na pięć katalo-
Aplikacja frontend i jej moduły gów: actions, config, lib, templates i validate.
w głównym katalogu projektu. Dzięki temu Aplikacja frontend będzie składała się na nastę- Najczęściej będziemy używać katalogów
zostanie utworzony podstawowy model klas pujące moduły: actions, templates i validate. W actions za-
obsługujących zapytania do poszczególnych pisana jest klasa actions.class.php odpowie-
tabel w bazie danych, tak jak jest to widocz- • użytkownik; dzialna za akcje modułu. Templates zawie-
ne na Rysunku 3. • posty; ra szablony poszczególnych metod z klasy
actions. Validate z kolei zawiera pliki służą-
ce do sprawdzania formularzy.

Moduł użytkownik
– klasa actions.class.php
Nasza pierwsza metoda executeRegister()
będzie umożliwiała wyświetlanie formula-
rza rejestracji oraz zapisanie nowego użyt-
kownika do tabeli uzytkownik. Metoda za-
mieszczona jest na Listingu 6. Z chwilą do-
dania użytkownika do bazy danych zo-
stanie wysłany e-mail z prośbą potwier-
dzenia konta. Wyświetlanie formularza
odbywa się za pomocą szablonu register-
Success.php, który należy utworzyć w katalo-
gu templates modułu użytkownik. Następnie
poddajemy go edycji. W pierwszej kolejno-
ści definiujemy możliwość sprawdzania for-
mularza poprzez plik register.yml umieszczo-
ny w katalogu validate. W tym celu plik ten
rozpoczynamy linią:

<?php use_helper('Validation') ?>.


Rysunek 3. Utworzenie modelu klas obsługi tabel
Następnie tworzymy nowy formularz przy
pomocy funkcji:

<?php echo form_tag('uzytkownik/register') ?>.

Kolejnym krokiem jest utworzenie pola nick:

<?php echo form_error('nick') ?>


<div class=”input-default”>
<label for=”nick”>Nick:</label>
<?php echo input_tag('nick') ?>
</div>.

Jak zwróciliśmy uwagę została wykorzysta-


na warstwa z klasą input-default, otóż w na-
stępnym artykule jak będziemy już kończyć
naszą aplikację zaczniemy pisać style CSS.
W identyczny sposób tworzymy pozostałe
pola formularza email, podaj hasło, powtórz
hasło, strona www i podpis stosując się do
następujących zaleceń (zmieniamy tylko
form_error i input_tag): pole email:
Rysunek 4. Widok na formularz rejestracji

www.phpsolmag.org 33
Dla początkujących

<?php echo form_error('email') ?>, methods: Następnie określamy wymogi dla zmiennej nick:
<?php echo input_tag('email') post: [nick, email, haslo, powtorz_haslo,
strona_www, podpis] names:
Pole haslo: nick:
Wszystkie definicje pól będą występować po sło- required: true
<?php echo form_error('haslo') ?>, wie kluczowym names (wpisujemy tylko raz). required_msg: to pole jest wymagane,
<?php echo
input_password_tag('haslo')
Listing 7. Dodanie nickValidator i checkdbnickValidator

Pole powtorz_haslo: nickValidator:


class: sfStringValidator
<?php echo form_error('powtorz_haslo') param:
?>, <?php echo min: 4
input_password_tag('powtorz_haslo') max: 20
min_error: nazwa użytkownika musi składać się przynajmniej z 4 znaków
Pole strona_www: max_error: nazwa użytkownika nie może mieć więcej niż 20 znaków
checkdbnickValidator:
<?php echo form_error('strona_www') ?>, class: myRegisternicklValidator
<?php echo input_tag('strona_www') ?> param:
nick: nick
Pole podpis: nick_error: nazwa użytkownika już istnieje

<?php echo form_error('podpis') ?>, Listing 8. Komunikaty zawarte w dyrektywach min_error i max_error
<?php echo input_tag('podpis') ?> strona_wwwValidator:
class: sfStringValidator
Następnie dodajemy ukryte pole referer funkcją: param:
min: 4
<?php echo input_tag('referer') ?>. max: 255
min_error: adres internetowy musi składać się przynajmniej z 4 znaków
Musimy również dodać przycisk umożliwia- max_error: adres internetowy nie może mieć więcej niż 255 znaków
jący wysłanie formularza. Użyjemy funkcji: podpisValidator:
<?php echo submit _ tag('zarejestruj') ?> class: sfStringValidator
i na samym końcu dodajemy oczywiście znacz- param:
nik </form>. W trakcie pracy nad różnymi for- min: 4
mularzami odkryjemy, że wbudowane funk- max: 255
cje w szablony Symfony są bardzo pomocne min_error: podpis musi składać się przynajmniej z 4 znaków
i często skracają czas pracy. W tabeli 1 przed- max_error: podpis nie może mieć więcej niż 255 znaków
stawiamy tagi html wykorzystywane przy
tworzeniu formularzy. Proszę zwrócić uwa- Listing 9. Zazwartość pliku myRegisternickValidator.class.php. Proszę zwrócić uwagę na proste
gę jak czytelne i krótkie są tagi Symfony. odwołania SQL w postaci języka PROPEL
W pierwszej kolumnie są przedstawione funk- <?php
cje umożliwiające tworzenie formularzy a w class myRegisternickValidator extends sfValidator {
drugiej zwracane wartości przez nie w posta- public function initialize($context, $parameter = null) {
ci kodu html. parent::initialize($context);
Mamy już formularz rejestracji. Teraz przy- $this->setParameter('nick_error', 'Invalid input');
dałaby się możliwość jego sprawdzania przy $this->getParameterHolder()->add($parameters);
wysyłaniu. Zatem tworzymy wcześniej wspo- return true;
mniany plik register.yml w katalogu validate. }
Zaczynamy od definicji zmiennych, które mają public function execute(&$value, &$error) {
być przekazywane z formularza nick, email, haslo, $nick = $value
powtorz_haslo, strona_www i podpis: // szukanie takiego samego nicka w tabeli uzytkownik, zapytanie w języku PROPEL
$c = new Criteria();
// odwołanie do tabeli uzytkownik do pola nick
$c->add(UzytkownikPeer::NICK, $nick);
$uzytkownik = UzytkownikPeer::doSelectOne($c);
// jeśli istnieje taki nick, to zostanie wywołany błąd
if ($uzytkownik) {
$error = $this->getParameter('nick_error');
return false;
}
return true;
}
Rysunek 5. Widok niewypełnionego formularza ?>
po przesłaniu

34 05/2007
Symfony Framework

wypełnij je ki dyrektywie validators możemy zdefiniować email:


validators: [nickValidator, na jednej zmiennej kilka możliwości testów. required: true
checkdbnickValidator] W przypadku zmiennej nick zostały wpro- required_msg: twój email jest wymagany
wadzone dwa validatory nickValidator oraz validators: [checkdbemailValidator]
Pole required określa czy zmienna jest wyma- checkdbnickValidator, które będą opisane w dal-
gana. Z kolei required_msg zwraca informa- szej części artykułu. Podobnie dodajemy wymo- Następnie ustawiamy wymagania dla zmien-
cję w przypadku gdy pole będzie puste. Dzię- gi dla zmiennej email: nych haslo i powtorz_haslo:

haslo:
Listing 10. Zawartość pliku myRegisteremailValidator.class.php. Proszę zwrócić uwagę na proste
odwołania SQL w języku PROPEL required: true
required_msg: twoje hasło jest wymagane
<?php powtorz_haslo:
class myRegisteremailValidator extends sfValidator { required: true
public function initialize($context, $parameter = null) { required_msg: ponowienie twojego hasła jest
parent::initialize($context); wymagane
$this->setParameter('email_error', 'Invalid input'); validators: [checkhasloValidator]
$this->getParameterHolder()->add($parameters);
return true; Definicja dla zmiennej haslo jest troszkę in-
} na w porównaniu do powtorz_haslo dlate-
public function execute(&$value, &$error) { go, że validator checkhasloValidator będzie
$email = $value sprawdzał zgodność tych dwóch zmiennych.
// szukanie takiego samego e-maila w tabeli uzytkownik, W momencie gdy hasła nie będą się zgadza-
// zapytanie w języku PROPEL ły zostanie zwrócona informacja o błędzie
$c = new Criteria(); z właśnie tego validatora.
// odwołanie do tabeli uzytkownik do pola email Następne zmienne strona_www i podpis rzą-
$c->add(UzytkownikPeer::EMAIL, $email); dzą się tymi samymi wymaganiami, z tą różni-
$uzytkownik = UzytkownikPeer::doSelectOne($c); cą, że używają innych nazw validatorów ale o
// jeśli istnieje taki e-mail, to zostanie wywołany błąd tych samych właściwościach zmienna strona:
if ($uzytkownik) {
$error = $this->getParameter('email_error'); strona_www:
return false; required: false
} validators: [strona_wwwValidator]
return true;
} zmienna podpis:
?>
podpis:
Listing 11. Metoda setPassword() odpowiedzialna jest za kodowanie hasła. Nową funkcjonalność required: false
dodajemy w pliku Uzytkownik.php, który znajduje się w katalogu symfony/lib/model validators: [podpisValidator]
public function setPassword($password) {
$salt = md5(rand(100000, 999999).$this->getNick().$this->getEmail()); W przypadku tych dwóch zmiennych dyrekty-
$this->setSalt($salt); wy required zaznaczone są na false. Dzięki temu
$this->setSha1Password(sha1($salt.$password)); ustawieniu sprawdzane są tylko i wyłącznie w
} momencie gdy zostaną wypełnione a validatory
będą zwracać stosowne informacje w przypad-
Listing 12. Metoda executePotwierdzenie(), dzięki której nowe konto użytkownika jest ku nie spełnienia ich zależności. Następnym
akceptowane lub nie
krokiem jest tworzenie odpowiednich validato-
public function executePotwierdzenie() { rów, i tak dla sprawdzania hasła wpisujemy:
// tworzenie nowego obiektu klasy Uzytkownik
$uzytkownik = new Uzytkownik(); checkhasloValidator:
$uzytkownik = UzytkownikPeer::retrieveByKod($this->getRequestParameter('kod')); class: sfCompareValidator
// gdy zostanie znaleziony użytkownik o podanym kodzie, jego status będzie param:
// zamieniony z 0 na 1 check: haslo
if($uzytkownik) { compare_error: hasła nie zgadzają się
$uzytkownik->setStatus(1);
$uzytkownik->save(); Prawda, że proste. W wymaganiach do zmien-
// ustawiamy szablon nowy_uzytkownik_okSuccess.php nej powtorz_haslo utworzyliśmy powyższy
$this->setTemplate('nowy_uzytkownik_okSuccess.php'); validator i przekazaliśmy do niego zmienną
} else { haslo, dzięki temu zostały porównane dwie
// w przeciwnym razie zostanie wyświetlony komunikat o błędzie zmienne. Dodajemy kolejne dwa validatory nick-
// ustawiamy szablon nowy_uzytkownik_errorSuccess.php Validator i checkdbnickValidator odpowiedzial-
$this->setTemplate('nowy_uzytkownik_errorSuccess.php'); ne za sprawdzanie zmiennej nick – Listing 7.
} Validator checkdbnickValidator używa kla-
} sy, która jeszcze nie istnieje. Będzie to nasze
pierwsze rozszerzenie, w którym zdefiniu-

www.phpsolmag.org 35
Dla początkujących

jemy metody do sprawdzania zmiennej nick


Listing 13. Metoda retrieveByKod(). Sprawdza, czy istnieje użytkownik o takim kodzie w polu w bazie danych. Jak tego dokonać, dowiemy
potwierdzenie tabeli uzytkownik się w dalszej części artykułu. Kolejne valida-
// metoda odpowiedzialna za sprawdzanie kodu potwierdzenia tory strona_wwwValidator i podpisValidator
public static function retrieveByKod($kod, $con=null) { różnią się tylko zmienną. Zasada działania
if($con === null) { ich jest taka, że sprawdzany jest ciąg zna-
$con = Propel::getConnection(self::DATABASE_NAME); ków. Minimalnie zmienna może zawierać
} 4 znaki a maksymalnie 255. W momencie
$criteria = new Criteria(UzytkownikPeer::DATABASE_NAME); gdy nie zostaną spełnione warunki pojawią
$criteria->add(UzytkownikPeer::POTWIERDZENIE, $kod); się odpowiednie komunikaty zawarte w dy-
$v = UzytkownikPeer::doSelect($criteria, $con); rektywach min_error i max_error – Listing
return !empty($v) > 0 ? $v[0] : null; 8. Ostatnim validatorem będzie checkdbema-
} ilValidator, w którym również została użyta
nasza własna klasa, dzięki której będziemy
Listing 14. Metoda executeLogin(). Odpowiedzialna jest za logowanie użytkownika mogli sprawdzić zawartość zmiennej email
public function executeLogin() { w bazie danych. Tego typu sprawdzanie jest
if ($this->getRequest()->getMethod() == sfRequest::POST) { potrzebne z oczywistych względów, nie mo-
$uzytkownik = new Criteria(); // Sprawdzanie loginu, hasła i statusu że być dwóch takich samych maili, tak samo
$uzytkownik->add(UzytkownikPeer::NICK, $this->getRequestParameter('login')); jak nicków.
$uzytkownik->add(UzytkownikPeer::STATUS, „1”);
$uzytkownik = UzytkownikPeer::doSelectOne($uzytkownik); checkdbemailValidator:
if ($uzytkownik) { class: myRegisteremailValidator
// Porównywanie hasła z formularza i bazy danych jeśli się zgadzają, następuje param:
// nadanie prawa subscriber oraz prawidłowa autoryzacja email: email
if (sha1($uzytkownik->getSalt().$this->getRequestParameter('password')) email_error: ten e-mail jest już wpisany
== $uzytkownik->getSha1Password()) do naszej bazy danych
$uzytkownik = $this->getUser();
$uzytkownik->addCredential('subscriber'); Jeśli chcemy zobaczyć dotychczasowy wynik na-
$uzytkownik->setAuthenticated(true); szej pracy, to należy w głównym katalogu Sym-
} else { fony uruchomić polecenie /symfony clear-cache,
// Wyświetlenie szablonu loginError.php w przypadku, gdy pola nie będą wyczyści ono utworzony cache pliku register.yml.
// spełniały warunków zawartych w pliku login.yml Następnie wchodzimy pod adres http://symfony/
$error = $this->getParameter('login_error'); frontend.php/uzytkownik/register, wyświetli się pro-
return false; sty formularz rejestracji nowego użytkownika, jak
} dotąd jeszcze nieopisany w CSS. Wygląd tego for-
} else { mularza przedstawiamy na Rysunku 4. Jeśli klik-
// Wyświetlenie szablonu login_uzytkownik_errorSuccess.php w momencie, niemy zarejestruj, pojawi się błąd, gdyż nie utwo-
// gdy zostanie podany zły login i hasło rzyliśmy nowych klas do sprawdzania pewnych
$this->setTemplate('login_uzytkownik_error'); elementów formularza. Niedługo je wprowadzimy
} i wtedy będziemy mogli przetestować rejestrację
} else { użytkownika. Jak pewnie zauważyliście odwołanie
return sfView::SUCCESS; // Wyświetlanie szablonu logowania do metody executeRegister() jest bardzo pro-
} ste, Symfony bowiem posiada wsparcie dla SEO
} (przyjaznych linków dla wyszukiwarek). Dobrze,
teraz uzupełnimy nasze klasy. Wprowadzimy kolej-
Listing 15. Zawartość pliku loginSuccess.php. Formularz logowania no myRegisternickValidator.class.ph oraz my
<?php use_helper('Validation') ?> RegisteremailValidator.class.php. Zasada jest
<?php echo form_tag('uzytkownik/login') ?> taka: abyśmy mogli korzystać z tych klas, należy jet
<!-- NICK (login) --> stworzyć w katalogu lib aplikacji frontend, więc
<?php echo form_error('login') ?> jeśli jesteśmy w module uzytkownik, wychodzi-
<div class=”input-default”> my z niego i wchodzimy do katalogu lib. Tworzy-
<label for=”login”>Podaj swój nick:</label> my dwa pliki myRegisternickValidator.class
<?php echo input_tag('login') ?> .php i myRegisteremailValidator.class.php,
</div> następnie uzupełniamy je w podany kod w Li-
<!-- HASŁO --> stingu 9. i 10. Jak widać, sprawdzanie formularzy
<?php echo form_error('password') ?> po krótkim zapoznaniu się ze strukturą plików
<div class=”input-default”> validate oraz nowych klas do sprawdzania w ba-
<label for=”password”>Podaj hasło:</label> zie danych jest przejrzyste i czytelne. Dzięki takiej
<?php echo input_password_tag('password') ?> metodzie programowania nasz czas pracy znacznie
</div> się skraca. Na koniec dodajmy jeszcze plik nowy_
<?php echo input_hidden_tag('referer') ?> uzytkownikSuccess.php z informacją dla użyt-
<?php echo submit_tag('zaloguj się ->') ?> kownika po wysłaniu poprawnego formularza.
</form> Oczywiście, plik tworzymy w katalogu templates
modułu uzytkownik.

36 05/2007
Symfony Framework

Tabela 1. Tagi html w szablonach Symfony


form _ tag('moduł/metoda') <form method=”post” action=”/frontend.php/moduł/metoda”>
input _ tag('nazwa','wartość') <input type=”text” name=”nazwa” id=”nazwa” value=”wartość” />
input _ tag('nazwa','wartość','maxlenght=30') <input type=”text” name=”nazwa” id=”nazwa” value=”” maxlenght=”30” />
textarea _ tag('nazwa','wartość','size=15x30') <textarea name=”nazwa” id=”nazwa” cols=”15” rows=”30”>wartość</textarea>
checkbox _ tag('opcja','wartość',true), <input type=”checkbox” name=”opcja” id=”opcja” value=”wartość” checked=”checked” />,
checkbox _ tag('opcja2','wartość2',false) <input type=”checkbox” name=”opcja2” id=”opcja2” value=”wartość2” />
radiobutton _ tag('opcja[]','wartość1',true), <input type=”radio” name=”opcja[]” id=”opcja _ wartość1” value=”wartość1”
radiobutton _ tag('opcja[]','wartość2', false) checked=”checked” />,
<input type=”radio” name=”opcja[]” id=”opcja _ wartość2” value=”wartość2” />
select _ tag('opcja','<option <select name=”opcja” id=”opcja”><option selected=”selected”>wartość1</
selected=”selected”> option><option>wartość2
wartość1</option><option>wartość2</option>') </option></select>
select _ tag('imie', options _ for _ <select name=”imie” id=”imie”><option value=”lukasz”>Łukasz</option><option
select(array('lukasz' => 'Łukasz','janek' => value=”janek”
'Janusz','jarek' => 'Jarek',' selected=”selected”>Janek</option><option value=”jarek”>Jarek</option><option
michal' => 'Michał'), 'janek')) value=”michal”>
Michał</option></select>
input _ file _ tag('nazwa') <input type=”file” name=”nazwa” id=”nazwa” value=”” />

input _ password _ tag('nazwa','wartość') <input type=”password” name=”nazwa” id=”nazwa” value=”wartość” />


input _ hidden _ tag('nazwa','wartość') <input type=”hidden” name=”nazwa” id=”nazwa” value=”wartość” />

submit _ tag('Zapisz'), <input type=”submit” name=”submit” value=”Zapisz” />, <input type=”image”


submit _ image _ tag('button.jpg') name=”submit”
src=”/images/button.jpg” />
select _ country _ tag('panstwo','PL') <select name=”panstwo” id=”panstwo”><option value=”AF”>Afghanistan</option>...<option
value=”PL”
selected=”selected”>Poland</option> ... </select>

Metoda setPassword() aktywacji. Nową metodę dodajemy na końcu uzytkownik_errorSuccess.php (są to pliki in-
Musimy jeszcze dodać jedną metodę set- klasy actions.class.php (Listing 12). formacyjne, na razie zostawiamy je puste) w ka-
Password(), gdyż została ona użyta podczas za- talogu templates oraz register.yml w katalo-
pisywania hasła nowego użytkownika. Metoda ta Metoda executePotwierdzenie() gu validate modułu uzytkownik. Gdy zaloguje-
ma na celu zapisanie hasła w kodowaniu MD5 Aby nasza nowa metoda działała po- my się poprawnie, zostanie wyświetlony szablon
(Listing 11). Jeśli chcemy użyć naszych metod w prawnie musimy, dodać kolejną o nazwie login_uzytkownik_okSuccess.php. Jest to dla
klasie akcji, należy umieścić je w odpowiednim retrieveByKod(), która będzie umieszczona nas potwierdzenie, że się udało. Jeśli chcielibyśmy
miejscu, w naszym przypadku jest to główny_ w pliku UzytkownikPeer.php znajdującym zobaczyć, czy rzeczywiście została utworzona sesja,
katalog_symfony/lib/model/Uzytkownik.php. się w katalogu /katalog_główny_projektu/ możemy to sprawdzić dzięki frontend_dev.php,
Sprawdzamy nasz formularz rejestracji. Aby wpro- lib/model/ (Listing 13.). gdzie dev jest skrótem od deweloper. Wybieramy
wadzone zmiany były aktywne, ponownie wyko- zakładkę vars&config (Rysunek 6.), następnie glo-
nujemy polecenie w głównym katalogu projektu Metoda retrieveByKod() bals i na samym dole odszukujemy linijkę zaczyna-
./symfony clear-cache. Wciśnijmy najpierw Teraz zatem sprawdźmy, jak działa akceptacja jącą się od session. Powinno wyglądać to tak:
button zarejestruj i zobaczmy, jakie komunikaty nowego konta. Patrzymy do bazy danych (ta-
się pojawią – Rysunek 5. Teraz dodajemy nowe- bela Uzytkownik -> pole Potwierdzenie) lub do e- session:
go użytkownika. Klikamy zarejestruj. Jeśli nie zo- maila, na którego został wysłany link z potwier- symfony/user/sfUser/attributes:
stały zgłoszone błędy, zostanie wywołany szablon dzeniem, i w oknie przeglądarki internetowej symfony/user/sfUser/authenticated: 1
nowy_uzytkownikSuccess.php, w którym będzie wklejamy link: http://symfony/uzytkownik/ symfony/user/sfUser/credentials:
informacja o przesłanym e-mailu z linkiem do po- potwierdzenie/kod/87286 Jeśli kod okazał się subscriber
twierdzenia konta. Jest to swego rodzaju zabez- poprawny, to konto zostało aktywowane i od tej
pieczenie przed spamerami. Dodaliśmy nowego pory użytkownik może się logować do systemu. Podsumowanie
użytkownika, sprawdźmy teraz, jak zachowa To już wszystko. Gratuluję, jeśli dotąd dotarłeś.
się formularz rejestracji w przypadku podania Metoda executeLogin() Dowiedzieliśmy się wiele z tego artykułu. Pozna-
tego samego e-maila i nicka. Zostaną wyświetlo- Skoro mamy już działający poprawnie formularz re- liśmy proste zastosowanie praw. W następnej czę-
ne ostrzeżenia o istniejącej nazwie i e-mailu w jestracji, który potrafi analizować pola poprzez plik ści zajmiemy się pozostałymi modułami oraz do-
bazie danych. Nasz formularz jest już gotowy. register.yml, oraz poprawnie działającą aktywa- damy możliwość wylogowania się i przypomnie-
Ale utworzone konto nie jest jeszcze aktywne. cję konta, to teraz czas najwyższy zająć się nową me- nia hasła. Dowiemy się również, jak należy przypi-
W liście wysłanym do użytkownika został za- todą odpowiedzialną za logowanie użytkownika i sać prawa do konkretnych modułów i metod.
warty link potwierdzający rejestrację, który był przypisanie mu prawa subscriber, aby miał do-
przedstawiony na Listingu 5.: http://symfony/ stęp do swojego menu i zamieszczonego w nim lin-
uzytkownik/potwierdzenie/kod/'.$potwierdzenie.' ku do profilu, który będzie mógł edytować. Użyt- ŁUKASZ KLEJNBERG
Musimy do klasy akcji dodać nową metodę kownik zalogowany będzie mógł również pisać no- Autor jest studentem informatyki w Wyż szej Szko-
executePotwierdzenie(), która będzie pobie- we komentarze. Na końcu plik actions.class.php le Informatyki i Zarządzania w Rzeszowie. Pracuje
rała unikalny dla każdego użytkownika kod dodajemy metodą executeLogin(), która przed- jako projektant stron WWW w firmie Helionet.pl.
z linku. Następnie po poprawnym zatwier- stawiona jest na Listingu 14. Następnie należy Kontakt z autorem: lukasz@helionet.com.pl
dzeniu zostanie wyświetlony szablon nowy_ utworzyć pliki loginSuccess.php (Listing 15.), Na potrzeby artykułu został opracowany layout
uzytkownik_okSuccess.php, w którym będzie loginError.php (kopia pliku loginSuccess.php), graficzny przez Bartłomieja Kina, który również
informacja o poprawnym dodaniu konta i o jego login_uzytkownik_okSuccess.php i login_ pracuje w firmie Helionet.pl.

www.phpsolmag.org 37
Praktyka

Własny system statystyk


Alternatywa dla rozbudowanych i uniwersalnych skryptów

Niniejszy artykuł prezentuje sposoby pozyskiwania danych, niezbędnych


do monitorowania ruchu na naszej witrynie internetowej oraz przedstawia
przykładową implementacje prostego systemu statystyk.

gę na ewentualny brak wsparcia dla JavaScriptu


Dowiesz się... Powinieneś wiedzieć... w przeglądarce, dlatego nie uzależniamy od nie-
• Jak pobrać dane dotyczące systemu, przeglą- • Czytelnik powinien posiadać podstawową go rejestracji pozostałych statystyk.
darki i rozdzielczości gościa. wiedze z zakresu obiektowego projektowania Warto zainteresować się także funkcją
• Jak oddzielić ruch pochodzący z wyszukiwarek aplikacji w PHP oraz rozumieć budowe wyra- get_browser(), dostarczającą w postaci ta-
od tego z innych witryn polecających. żeń regularnych. blicy takich informacji, jak: platforma sys-
• Jak śledzić słowa kluczowe, kierujące ruch temowa, typ przeglądarki, wersja oraz te
z wyników wyszukiwarek oraz pozycje zajmo- wynikające z ustawień przeglądarki. Wiedzę
wane pod nimi w wynikach organicznych. o tym, jak uzyskać dostęp do poszczególnych
• Jak zaimplementować prosty system statystyk. elementów, dostarczy nam wynik działania
skryptu, tworzącego tablice $params i zwra-
cającego wartości wraz z indeksami:
tową użytkownika. Na potrzeby naszej aplika-
cji przydatnymi mogą się okazać: $params=get_browser(null, true);
Poziom trudności print_r($params);
• – lista akcep-
$ _ SERVER['HTTP _ ACCEPT']
towanych przez przeglądarkę typów Dzięki jej zastosowaniu nie ma potrzeby ręcz-
MIME; nego parsowania nagłówków, niestety dla pra-

P
rzy poszukiwaniu – adekwatnych do • $ _ SERVER['HTTP _ ACCEPT _ LANGUAGE'] widłowego działania niezbędne jest istnienie
realizacji postawionych sobie zadań – preferencje użytkownika dotyczące do- pliku browscap.ini po stronie serwera. W nie-
– narzędzi, może okazać się, że te do- myślnego języka strony; których przypadkach jest to trudne do zre-
stępne na rynku, nie odpowiadają stawianym • $ _ SERVER['HTTP _ REFERER'] – adres alizowania, dlatego w dalszej części artykułu,
wobec nich oczekiwaniom, a ich budowa jest strony, za pośrednictwem której nastąpiło przyjęliśmy, iż nie istnieje możliwość zastoso-
zbyt skomplikowana do samodzielnej modyfi- odwołanie do naszej (np. poprzez kliknię- wania funkcji.
kacji. Może zdarzyć się, że zależy nam na śle- cie odnośnika);
dzeniu ruchu robotów sieciowych (wyszukiwa- • $ _ SERVER['HTTP _ USER _ AGENT'] – ciąg Funkcje rozszerzające możliwości
rek internetowych czy spambotów), który nie znaków identyfikujący przeglądarkę i sys- Przykładem oryginalnej i niewystępującej w ty-
zostanie zarejestrowany z poziomu JavaScript tem użytkownika; powych systemach statystyk opcji jest niewąt-
(a więc przez zewnętrzne serwisy, vide Google • $ _ SERVER['HTTP _ X _ FORWARDED _ FOR'] pliwie sprawdzanie aktualnej pozycji w wy-
Analytics). – jeżeli użytkownik łączy się za pośred- szukiwarkach pod hasła, na które pozycjo-
W takich sytuacjach jedyną alterna- nictwem proxy, a ustawienia serwera na nujemy naszą witrynę. Dzięki zestawieniu
tywą jest stworzenie własnego systemu to pozwalają, zawiera właściwe IP; tych statystyk ze zgromadzonymi do tej pory
statystyk, którego głównymi zaletami są • $ _ SERVER['REMOTE _ ADDR'] – adres IP możemy łatwo stwierdzić, w jaki sposób są
prostota, możliwość bezproblemowej rozbu- użykownika wyświetlającego stronę lub one ze sobą powiązane i czy faktycznie opła-
dowy i dostosowania do konkretnej strony proxy. ca się zabiegać o jak najwyższą lokatę pod da-
internetowej. ne słowo kluczowe w wynikach organicznych
PHP jako przedstawiciel języków server-side po- wyszukiwarek. Listing 1. przedstawia przykła-
Co należy wiedzieć? siada ograniczoną wiedzę o komputerze klien- dową implementację funkcji zwracającej pozycje
PHP daje możliwość dostępu do szeregu pre- ta, dlatego nie ma bezpośredniej możliwości w Google, która jest zachętą do dalszych prac
definiowanych zmiennych, wśród nich znaj- pobrania rozdzielczości ekranu przy jego uży- nad budowaniem naszego autorskiego systemu.
dują się superglobalne, wchodzące w skład ta- ciu. Jeśli taka informacja jest nam potrzebna, Analogicznie do podanego przykładu z ła-
blicy $_SERVER (zawierającej m. in. nagłówki pobieramy ją z poziomu JavaScriptu i przekazu- twością wykonamy podobne funkcje dla
HTTP), która dostarcza nam garść informacji jemy poprzez COOKIE, którego odczytanie nie innych wyszukiwarek. Niekiedy z jakiegoś po-
dotyczących odwiedzającego witrynę interne- sprawia problemów. Zwracamy szczególną uwa- wodu pragniemy zamaskować informacje

38 05/2007
Statystyki

o tym, że dane pobierane są z poziomu skryp- tu spełniającego zależność, z uwagi na przyję- get_all() tablicy budowane jest wyrażenie
tu przez parsowanie strony z wynikami. Może- te w bazie nazewnictwo (Rysunek 1.) domyślą regularne (odpowiadające strukturze lin-
my tego dokonać, zastępując funkcję file_get_ wartością jest „P99”. Taka operacja będzie wy- ków) w celu wyciągnięcia słowa kluczowe-
contents() naszą własną, bazującą na CURL-u, konywana niejednokrotnie, np. podczas przy- go, zaś samo ID wyszukiwarki zapisane zosta-
który daje możliwość ustawienia User Agenta porządkowywania typowi preglądarki czy wy- je jako kolejny z parametrów. W celu zabez-
(Listing 2.). szukiwarki nazwy odpowiedniego do uaktual- pieczenia przed atakami XSS zastosowaliśmy
nienia pola. funkcję strip_tags().
Przykładowy system statystyk
Zwracamy uwagę przede wszystkim na wyni- Pobieranie danych Zapis danych
ki organiczne wyszukiwarek (Google, Onet, To na pozór łatwe zadanie zrealizują trzy me- Na Listingu 5 widoczna jest przykładowa
Wirtualna Polska, MSN oraz Yahoo!) i anali- tody klasy userinfo. Pierwsza z nich – Listing 3 implementacja metody db(), odpowiedzial-
zę stron polecających. Prowadzenie pozosta- – odpowiedzialna jest za pobranie adresu IP lub nej za zapis pobranych wcześniej danych.
łych statystyk dotyczących przeglądarek czy wyciągnięcie go zza proxy, jeśli zajdzie taka ko- W przypadku konieczności utworzenia ko-
systemów operacyjnych na potrzeby niniej- nieczność. Ważnym elementem jest walidacja lejnych rekordów, równoznacznej z odwie-
szego artykułu zostanie zredukowane do mi- poprawności, której pominięcie nie jest rozsąd- dzinami pierwszego w ciągu danej doby
nimum. Skupiamy się na źródłach ruchu, nie ne ze względu na to, że dane te mogą zostać z ła- użytkownika, czyszczony jest rejestr adre-
badamy natomiast popularności poszczegól- twością zmanipulowane przez złośliwego użyt- sów IP.
nych podstron naszej witryny. Kod systemu kownika. Alternatywą jest automatyczne utworzenie
statystyk umieszczamy w czterech plikach we- Na potrzeby metody unique(), zwraca- niezbędnych wpisów za pośrednictwem unik-
dług schematu: jącej wartość typu bool, utworzyliśmy już sowego daemona cron. Ogranicza się to do usu-
tabelę „today”, zawierającą adresy IP osób zli- nięcia pierwszej instrukcji if z metody, utwo-
• functions.inc.php; czonych w danym dniu, co pozwala na branie rzenia odrębnego pliku cron.php oraz dodania
• class.inc.php; pod uwagę jedynie unikalnych wizyt. W celu do rejestru crontab:
• stats.inc.php; zmniejszenia liczby zapytań – jeśli ustawie-
• display.php. nia przeglądarki na to pozwalają – wysyłamy 0 0 * * * wget -q http://www.nasza-
plik COOKIE o ważności do północy trwają- strona.pl/sciezka/cron.php > /dev/null
Dysponujemy serwerem cego dnia.
bez wsparcia dla funkcji W przypadku zwrócenia wartości true Co zostanie umożliwone, poprzez wydanie
get_browser() przechodzimy do pobrania kolejnej daw- w lini poleceń, komendy:
Dane przechowujemy w bazie utworzonej ki informacji, a więc kolejno: detekcji ty-
zapytaniem przedstawionym na dołączonym pu przeglądarki, systemu operacyjnego oraz /usr/bin/crontab -e
do gazety nośniku. W zależności od ilości od- przeparsowania nagłówka HTTP_REFERER,
wiedzających witrynę przyjmujemy adekwat- zawierającego adres URL strony, za pośred- W celu przeglądu już istniejących wpisów
ne wartości maksymalne pól. nictwem której internauta trafił na naszą. użytkownika wystarczy:
Warto wspomnieć o budowie pierwszego z Jeśli jego zawartość sugeruje przejście z wy-
plików – functions.inc.php. Posiada on, oprócz szukiwarki, na podstawie znajdującej się w /usr/bin/crontab -l
podstawowych funkcji regulujących opera-
cje na bazie danych, inną nazwaną check().
Listing 1. Funkcja sprawdzająca pozycję strony w Google pod dane słowo kluczowe
Jej zadaniem jest zwrócenie nazwy pola tabe-
li na podstawie badania zawartości elementów function get_links ($url, $q) {
tablicy $array w ciągu $string. Zwracana jest // Budowanie prawidłowego URL-a (zamiana spacji na “+”)
wartość „PX”, gdzie „X” jest indeksem elemen- $q=str_replace(' ', '+', $q);
// Pobieranie 100 pierwszych witryn z wyników organicznych

���� ��� �� �� �� �� �� preg_match_all ('|href=(.*)>|U', file_get_contents('http://www.google.pl/ie?q='.$q.'


&hl=pl&num=100&start=0'), $table, PREG_PATTERN_ORDER);
�������� �� � �� � � �
// Znajdowanie na liście interesującego nas adresu
�������� �� � � � � � for ($n=0; $n<100; $n++)
�������� �� � � � � � if (ereg($url, $table[1][$n]))
break;
Rysunek 1. Przyjęte nazewnictwo pól i rekordy z return $n+1;
przykładową zawartością }

Listing 2. Alternatywa dla file_get_contents()


function file_get_plus($url) {
// User Agent IE 6.0
$ua='Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)';
$ch=curl_init($url)
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_useragent, $ua);
������ ������� �������
$content=curl_exec($ch);
�� �� � return $content;
curl_close($ch);
Rysunek 2. Wykres wygenerowany przy użyciu }
metody drawtab()

www.phpsolmag.org 39
Praktyka

Listing 3. Klasa userinfo (część pliku class.inc.php)

class userinfo { function db() {


var $ip, $br, $os, $gr, $se, $kw; $date=date("Ymd");
// Podstawowe dane // Czy strona została dziś odwiedzona
function get_basic() { if (!check_exist('cache', 'date', $date)) {
// Pobieranie IP // Tworzenie rekordów, jeśli to konieczne
$this->ip=$_SERVER['HTTP_X_FORWARDED_FOR'] ? $_SERVER[ mysql_query('INSERT INTO cache SET date="'.$date.'"');
'HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']; mysql_query('INSERT INTO browser SET date="'.$date.'"');
// Validacja IP mysql_query('INSERT INTO system SET date="'.$date.'"');
if (ereg("^[0-9]{1,3}(.[0-9]{1,3}){3}$",$this->ip)) // Kasowanie nieaktualnych IP
foreach(explode(".", $this->ip) as $block) { mysql_query('DELETE FROM today');
if( $block<0 || $block>255) { }
$this->ip='0.0.0.0';
break; // Cache
} if ($this->se!='P99') $t='o';
}else $this->ip='0.0.0.0'; else if ($this->hr!='') $t='r';
} else $t='d';
// Czy unikalny odwiedzający update ('cache', $t, 'date', $date);
function unique() {
if (!isset($_COOKIE['stats'])&&!check_exist('today', 'ip', // Zapis informacji o przeglądarce i systemie
$this->ip)) { update('browser', $this->br, 'date', $date);
// ile sekund do końca dnia update('system', $this->os, 'date', $date);
$waznosc = (23- date('H'))*3600 + (59-date('i'))*60 + 59- // Czy znany jest HR
date('s'); if ($this->hr!='') {
setcookie('stats', '1', time()+$waznosc); // Zapis informacji o wyszukiwace
mysql_query('INSERT INTO today VALUES ("'.$this->ip.'")'); if ($this->se!='P99') {
return true; // Zapis słowa kluczowego
}else return false; if (!check_exist('keywords', 'val', $this->kw))
} mysql_query('INSERT INTO keywords VALUES("", "'.$this->kw.'")');
// Pozostałe dane // Pobieranie ID
function get_all(){ $id=get_id('keywords', $this->kw);
// Pobieranie UA // Tworzenie rekordów, jeśli to konieczne
$ua=strtolower(strip_tags($_SERVER['HTTP_USER_AGENT'])); if (!check_exist('sengine', 'date', $date, 'word', $id,
// Detekcja przeglądarki 'engine', $this->se))
$br=array( 'opera', 'msie', 'konqueror', 'firefox', 'safari', mysql_query('INSERT INTO sengine VALUES("'.$date.'",
'netscape', 'aol', 'slurp', 'googlebot', 'msnbot', "'.$id.'", "'.$this->se.'", "")');
'onetszukaj', 'netsprint'); // Zapis informacji
$this->br = check($br, 11, $ua); update('sengine', 'val', 'date', $date, 'word', $id, 'engine',
// Detekcja systemu $this->se);
$os=array('windows', 'linux', 'mac', 'unix'); } else {
$this->os = check($os, 3, $ua);
// Pobieranie HR // Zapis informacji o HR, jeśli nie jest SE
$this->hr=strip_tags($_SERVER['HTTP_REFERER']); // Zapis referera
// Sprawdzanie, czy HR to wyszukiwarka; jeśli tak, to jaka if (!check_exist('referer', 'val', $this->hr))
$ref=array( array ( 'search.yahoo.com', 'google' , mysql_query('INSERT INTO referer VALUES("", "'.
'search.msn.com', 'live.com', 'szukaj.onet.pl', $this->hr.'")');
'szukaj.wp.pl'),
array ('p', 'q', 'q', 'q', 'qt', 'szukaj') ); // Pobieranie ID
$this->se = check($ref[0], 5, $this->hr); $id=get_id('referer', $this->hr);
// Wyciąganie słowa kluczowego
if ($this->se !='P99' ) { // Tworzenie rekordów, jeśli to konieczne
preg_match('/'.$ref[1][$this->se].'=(.*?)(&|$)/', if (!check_exist('refhosts', 'date', $date, 'referer', $id))
$this->hr,$arr); mysql_query('INSERT INTO refhosts VALUES("'.$date.'",
$this->kw=strtolower($arr[1]); "'.$id.'", "")');
$this->kw=str_replace('+', ' ', $this->kw); // Zapis informacji
} update('refhosts', 'val', 'date', $date, 'referer', $id);
// Zamienianie HR na informacje o hoście }
preg_match('|http://(.*)/|U',$this->hr, $arr); }
$this->hr = $arr[1]; }
} }
// Zapis danych

40 05/2007
Statystyki

Listing 4. Plik display.php Listing 5. Plik stats.inc.php

<?php // Połączenie z bazą


// Połączenie z bazą $db = mysql_connect('localhost',
$db = mysql_connect('localhost', 'admin', 'passwd') or die ('Błąd: '.mysql_error()); 'admin', 'passwd') or die
mysql_select_db('stats') or die ('Błąd: '.mysql_error()); ('Błąd: '.mysql_error());
mysql_select_db('stats') or die
// Załadowanie niezbędnych plików ('Błąd: '.mysql_error());
include ('functions.inc.php');
include ('class.inc.php'); // Załadowanie niezbędnych plików
include ('functions.inc.php');
// Tworzenie obiektu klasy include ('class.inc.php');
$view=new view; $info = new userinfo;
$info->get_basic();
// Przyjęcie danych podanych metodą $_GET lub domyślnych
$view->date1 = is_numeric($_GET['d1']) ? $_GET['d1'] : 00000000; if ($info->unique()) {
$view->date2 = is_numeric($_GET['d2']) ? $_GET['d2'] : 99999999; $info->get_all();
$info->db();
// Pobieranie danych metodą get_single }
$view->get_single('browser',13);
$view->arrayd=array('Nieznana', 'Opera', 'MSIE', 'Konqueror', 'Firefox', 'Safari', // Zamykanie połączenia
'Netscape', 'AOL', 'Yahoo', 'Google', 'MSN', 'Onet', 'Netsprint'); mysql_close($db);
$exit=$view->drawtab();
$view->get_single('system', 5);
$view->arrayd=array('Nieznany', 'Windows', 'Linux', 'Mac', 'UNIX'); (Listing 5) do strony serwisu, np. poprzez
$exit.=$view->drawtab(); include_once(). Od tej pory dane dotyczące
$view->get_single('cache', 3); źródeł ruchu na naszej WWW są śledzone.
$view->arrayd=array('Direct, 'Organic', 'Referal');
$exit.=$view->drawtab(); Obróbka statystyk
Po pobraniu i zapisaniu danych niezbędnych do
// Pobieranie danych metodą get_multi prowadzenia analizy przyszedł czas na ich pre-
$view->get_multi('sengine', 'word', 'val', 'keywords'); zentację. Warstwą odpowiedzialną za dostar-
$exit.=$view->drawtab(); czenie zgromadzonych informacji do aplikacji
$view->get_multi('refhosts', 'referer', 'val', 'referer'); jest klasa view z metodami get_single() oraz
$exit.=$view->drawtab(); get_multi(). Różnią się one jedynie sposobem
pobierania danych z bazy, cechą wspólną jest
// Zamykanie połączenia wyjście zgodne z przyjętym standardem – dwie
mysql_close($db); tablice; zawierajace wartości i opisy zmiennych.
?> W przypadku metody get_single tę drugą two-
rzymy ręcznie. Obsługę najłatwiej omowić na
<html><head><title>Przeglądanie statystyk</title> przykładzie, a zatem skupmy się na skrypcie dy-
<style type="text/css"> namicznie generującym wykresy – Listing 5.
body { W celu zachowania przejrzystości ko-
background : rgb(0, 0, 0); du HTML-ową tabelę, tworzoną metodą
font : 16px Trebuchet MS, Verdana, Arial, Helvetica, sans-serif; drawtab() , umieszczamy w zmiennej $exit ,
color : #cccccc; by na koniec zwrócić ją w odpowiednim
} miejscu szablonu strony.
td {
text-align: center; Podsumowanie
} Artykuł nie zamyka tematu, a dostarcza jedynie
</style></head><body> bazy, na której można zbudować własny system
statystyk, który z pewnością spełni nasze ocze-
<?php kiwania w stopniu większym niż „uniwersalne”
echo $exit; i publicznie dostępne rozwiązania.
?>

</body></html> ŁUKASZ BORCHMANN


Członek grupy Rainfox, oferującej usługi z zakre-
su tworzenia aplikacji webowych. Autor bloga do-
Drugie rozwiązanie jest dużo lepsze pod Integracja z witryną tyczącego zastosowania PHP i środowiska Apa-
względem wydajności, jednak z uwagi na za- W celu rozpoczęcia rejestrowania statystyk che do optymalizacji witryn pod kątem wyszuki-
mierzoną prostotę skryptu nie przyjęliśmy go za pośrednictwem napisanego do tej pory warek internetowych. Kontakt: vealks@gmail.com;
jako domyślne. kodu wystarczy załączenie pliku stats.inc.php Blog: http://www.veal.pl/.

www.phpsolmag.org 41
Technika

Operacje na tekście
Wprowadzenie

Operacje na tekście w locie są bardzo często potrzebne na naszych stronach


WWW. W innych językach programowania służą nam do tego zazwyczaj
standardowe funkcje jakiejś biblioteki. PHP ma ogromny zasób wbudowanych
funkcji, dzięki którym zrobimy z tekstem, co tylko chcemy.

Pierwszy parametr to nasz tekst, podajemy


Dowiesz się... Powinieneś wiedzieć... tam naszą zmienną. Drugi parametr określa,
• Jak dzielić fragmenty tekstu. • Należy znać podstawową składnię języka PHP. od którego znaku tekst ma się zaczynać (np.
• Jak usunąć niepotrzebne litery/ciągi, na pod- • Należy wiedzieć, co to jest zmienna i warunki jeśli wstawimy 1 [liczenie od 0] to tekst zosta-
stawie danych kryteriów. w PHP. nie ucięty do „la ma kota, a kot ma Alę”). Trzeci
• Jak przeszukiwać tekst. • Skrypty PHP uruchamiamy na serwerze (może parametr (długość) jest opcjonalny, jednak bar-
• Jak sprawdzić czy w tekście istnieje dany ciąg być lokalny) z zainstalowanym PHP. dzo często używany, gdyż to on potrafi skró-
znaków. • Należy wiedzieć, co to jest kodowanie strony cić tekst. Przykład – Listing 5. Przydaje się to
• Jak zamienić znaki w tekście. WWW. często, jeśli np. tworzymy system newsów na
• Jak zmienić kodowanie tekstu za pomocą funkcji stronę i chcemy skrócić treść newsu na stronie
głównej do określonej ilości znaków. Przykła-
dowo chcemy skrócić każdą wiadomość, która
przez PHP, dlatego też musieliśmy tekst podzielić ma treść dłuższą niż 200 znaków. Do spraw-
na dwie części, a między nimi „dokleić” zawartość dzenia długości tekstu służy funkcja strlen.
Poziom trudności zmiennej $cat. Jak widać, tekst łączy się znakiem Jej użycie jest proste – strlen(tekst). Zasto-
kropki. W piątej linijce naszego kodu zastosowano sujemy prosty warunek skracający news. Załóż-
skróconą wersję funkcji echo, za którą odpowiada my, że w zmiennej $news mamy treść newsa:
znak równości po pytajniku (<?=$cat?> wyświe-

P
o pierwsze, musimy wiedzieć, jak wyglą- tla więc tekst „kot”). Ostatnie linijki pokazują, że w if (strlen($news) > 200) {
da wyświetlanie tekstu w PHP. Wszelkie przypadku użycia podwójnego cudzysłowia zmien- echo substr($news, 0, 200).'[...]';
operacje na tekście będziemy wykonywać ne w nim zawarte są parsowane, jednak rozwiąza- }
za pomocą funkcji na zmiennej, zawierający da- nie to nie jest zalecane, gdyż wszystkie benchmar- else
ny tekst. Powinno być oczywiste, że do wyświetla- ki PHP pokazują, że jest to sposób najwolniejszy. {echo $news;
nia tekstu w PHP służą funkcje echo i print. Są to W tekście wyświetlanym z pojedynczym cudzysło- }
identyczne funkcje, więc dlaczego nie mogłaby być wem możemy bez problemu wstawić cudzysłów
tylko jedna? Dlatego, że w PHP wiele funkcji wystę- podwójny, jednak aby wstawić pojedynczy, musi- Jeśli news będzie dłuższy niż 200 znaków, zosta-
puje np. pod dwoma nazwami, aby upodobnić się my poprzedzić go znakiem backslash ( \ ). Analo- nie skrócony właśnie do 200 znaków, a na jego
do wielu języków programowania, by programi- gicznie wygląda sytuacja z użyciem wyświetlania końcu zostanie dodany kwadratowy nawias z wie-
stom było po prostu wygodniej nauczyć się PHP. tekstu w podwójnym cudzysłowie, tyle że odwrot- lokropkiem. Możemy także z tekstu wyciągnąć je-
Mniejsza o to. Wyświetlmy testowo tekst „Ala ma nie. Przykład:– Listing 3.Jak widać sytuacja ta nie den interesujący nas znak. Przydaje się to w róż-
kota, a kot ma Alę” na kilka możliwości – Listing.1. występuje, gdy wyświetlamy tekst bezpośrednio w nych sytuacjach, np. jeśli chcemy zakodować jakąś
Wszystkie te zapisy są poprawne, a dla wyświetla- dokumencie. Zajmijmy się podstawową „obróbką” informację i wplatamy interesujące nas znaki po-
nia tekstu nie zawierającego żadnych zmiennych tekstu. Najlepiej jest pracować z użyciem zmien- między znaki „śmieci", a nasz skrypt PHP będzie
najlepszym zapisem będzie ostatni – z użyciem za- nych. Zapisujemy więc nasze zdanie do zmiennej te znaki wyciągał i układał w logiczną całość. Uży-
mknięcia i ponownego otwarcia kodu PHP między $tekst. Jeśli nie mamy takiej możliwości (np. tekst wamy w tym przypadku klamr przy zmiennej, a
tekstem. W tym przypadku nie jest używana żadna strony jest generowany przez PHP, a chcemy prze- między nimi podajemy konkretny indeks znaku.
funkcja PHP, gdyż tekst stanowi wtedy część doku- robić cały tekst wynikowy), możemy użyć bufora. Przykład takiego zastosowania:
mentu. Jest to zatem zapis najbardziej optymalny. Wygląda to tak – Listing 4. Pierwsza funkcja, która
Oczywiście, do tekstu możemy także wprowadzić może nam się przydać, to substr. Pozwala ona na $kod = 'fksmajso3jr0lf0es3a32fsgc';
zmienne. Dodajmy zmienną $cat z tekstem „kot” i skrócenie tekstu do określonej liczby liter. Ponadto $klucz = $kod{4}.$kod{12}.$kod{18};
użyjmy ją w naszym kodzie. Dodajmy także zapis z pozwala obciąć tekst nie tylko z jednej strony, ale i z echo $klucz;
podwójnym cudzysłowiem ( “” ) – Listing 2. Przy- dwóch. Składnia funkcji wygląda tak:
kład ten pokazuje, że w przypadku użycia pojedyn- Powyższy przykład wyświetli imię „ala”. Jak
czego cudzysłowia ( ' ' ) zmienne nie są parsowane substr(tekst, start, [długość]); widać, wszystko jest małymi literami – co

42 05/2007
Operacje na tekście

teraz zrobić, aby daną literę bądź całe sło- $n; $i++) { je się dopóki warunek, iż $i jest mniejsze
wo zamienić na wersaliki? Z pomocą przy- echo $tekst{$i}.'<br />'; od $n jest spełniony. Za każdym przebie-
chodzi nam funkcja strtoupper oraz jej sio- } giem pętli zmienna $i zwiększana jest o 1.
stra strtolower, która zamienia litery na ma- } Zastosować funkcję można bardzo łatwo
łe. Przykład zastosowania – Listing 6. Klam- – ustaw _ tekst _ pionowo($zmienna) ub
ry możemy też wykorzystać do wyświetla- Jak widać zmienna-index $i jest ustawia- bezpośrednio, np.
nia tekstu pionowo. W tym celu najlepiej na na 0, a zmienna $n przyjmuje wartość
napisać własną funkcję. Nazwijmy ją po pro- długości zmiennej $tekst. Pętla wykonu- ustaw_tekst_pionowo('Mój tekst').
stu “ustaw_tekst_pionowo”. Głównym środ-
kiem użytym w naszej funkcji będzie pętla.
Listing 5. Zastosowanie funkcji substr
Funkcja najpierw policzy ile podany tekst
ma znaków, aby pętla mogła tyle razy prze- <?
biec i wyświetlać każdy znak z tagiem <br /> $tekst = 'Ala ma kota, a kot ma Alę';
(przejście do następnej linii w HTML). // Wyświetli “ ma kota, a kot ma “ - gdy obcinamy tekst z obu stron, należy
// w parametrze długość użyć znaku minus
function ustaw_tekst_pionowo($tekst) echo substr($tekst, 3, -3);
{ ?>
for ($i = 0, $n = strlen($tekst); $i <
Listing 6. Przykład zastosowania funkcji strtoupper oraz strtolower
<?
Listing 1. Kilka możliwości wyświetlenia
danego tekstu $kod = 'fksmajso3jr0lf0es3a32fsgc';
$klucz = $kod{4}.$kod{12}.$kod{18};
<? echo strtoupper($klucz); // Wyświetli ALA
echo 'Ala ma kota, a kot ma Alę'; echo strtoupper($kod{4}).$kod{12}.$kod{18}; // Wyświetli Ala
print 'Ala ma kota, a kot ma Alę'; echo strtolower(strtoupper($klucz)); // Wyświetli ala
?>Ala ma kota, a kot ma Alę<? ?>
?>
Listing 7. Przykład zastosowania funkcji strstr oraz stristr
Listing 2. Dodanie zmiennej $cat oraz <?
podwójnego cudzysłowia $tekst = 'Ala ma kota, a kot ma Alę';
<? // Zwróci “Wyraz 'ala' znajduje się w podanym tekście”
$cat = 'kot'; if (stristr($tekst, 'ala')) {
echo 'Ala ma kota, a '.$cat.' ma Alę'; ?>Wyraz 'ala' znajduje się w podanym tekście <?
print 'Ala ma kota, a '.$cat.' ma Alę'; } else {
?>Ala ma kota, a <?=$cat?> ma Alę<? ?>Wyraz 'ala' nie znajduje się w podanym tekście <?
echo “Ala ma kota, a $cat ma Alę”; }
print “Ala ma kota, a $cat ma Alę”; // Zwróci “Wyraz 'ala' nie znajduje się w podanym tekście” (małe litery)
?> if (strstr($tekst, 'ala')) {
?>Wyraz 'ala' znajduje się w podanym tekście<?
Listing 3. Wyświetlanie tekstu w } else {
podwójnym cudzysłowie ?>Wyraz 'ala' nie znajduje się w podanym tekście <?
<? }
echo 'Ala ma \'kota\', a “kot” ma Alę'; // Zwróci “Wyraz 'Ala' znajduje się w podanym tekście”
echo “Ala ma 'kota', a \“kot\” ma Alę”; if (strstr($tekst, 'Ala')) {
?>Ala ma 'kota', a “kot” ma Alę<? ?>Wyraz 'Ala' znajduje się w podanym tekście<?
?> } else {
?>Wyraz 'Ala' nie znajduje się w podanym tekście <?
Listing 4. Możemy użyć bufora }
<? ?>
// funkcja rozpoczynająca buforowanie // Przykład użycia funkcji eregi i ereg
ob_start(); <?
$tekst = 'Ala ma kota, a kot ma Alę';
/* if (ereg('ala', $tekst)) { // Wyświetli "Nie znaleziono 'ala'"
tutaj wszystkie funkcje wyświetlające ?>Znaleziono 'ala'<?
tekst wynikowy } else {
*/ ?>Nie znaleziono 'ala'<?
}
// pobieranie już sparsowanego wyniku if (eregi('ala', $tekst)) { // Wyświetli "Znaleziono 'ala'”
// powyższych operacji do zmiennej ?>Znaleziono 'ala'<?
$tekst = ob_get_contents(); } else {
// zamykanie i czyszczenie bufora ?>Nie znaleziono 'ala'<?
ob_end_clean(); }
?> ?>

www.phpsolmag.org 43
Technika

Warunki na podstawie danego rzeczy, np. do sprawdzania poprawności adre- nych nie będę się tu rozpisywał – jest to temat
tekstu sów e-mail podczas rejestracji użytkowników. rozległy i został opisany w numerze 6/2006 PHP
Jak sprawdzić, czy dany tekst zawiera podany W artykule nie będziemy podawać przykładów, Solutions w artykule „Przyjazne URLe w PHP" .
ciąg znaków? Możemy posłużyć się funkcjami które wymagają użycia dużo bardziej zaawanso- Dlatego też w przykładzie zostało zapisane tylko
stristr i strstr. Funkcje robią to samo z tym wanych wyrażeń regularnych. Funkcje te są zwy- jedne potrzebne nam wyrażenie – Listing 8.
wyjątkiem, że stristr nie zwraca uwagi na kłymi funkcjami pozwalającymi na sprawdzenie
wielkość liter. Odpowiednikami tych funkcji są tylko „na sztywno” podanego ciągu znaku. Co Przeszukiwanie i zamiana tekstu
także eregi i ereg z takim wyjątkiem, że funk- jednak, jeśli chcemy sprawdzić, czy w tekście PHP umożliwia także zamienianie pewnych
cje mogą, aczkolwiek nie muszą, zostać uży- znajduje się jakikolwiek wyraz zaczynający się fragmentów tekstu na inne. Najczęściej uży-
te z wyrażeniami regularnymi ( np. aby okre- np. na literę g, a kończący na literę a? Przykład wane, a zarazem najprostsze umożliwiające to
ślić konkretny zakres sprawdzania w danym – nie znamy treści tekstu, chcemy wiedzieć, czy funkcje to str_replace i preg_replace. Skład-
tekście, zamiast sprawdzać w całości). Funckja zawiera słowo według podanych kryteriów, mo- nia tych funkcji jest identyczna z tą różnicą, że
eregi nie zwraca uwagi na wielkość liter. że to być np. gitara czy glazura. Z pomocą przyj- preg_replace używa wyrażeń regularnych,
Funkcji możemy użyć zatem tak – Listing 7. dzie nam funkcja preg_match korzystająca z wy- dzięki czemu możemy tą funkcją zrobić wię-
Funkcje te można użyć także do wielu innych rażeń regularnych. Na temat wyrażeń regular- cej. Jednakże to ma też swoją wadę – funkcja
ta jest wolniejsza. Do zamiany stałych fragmen-
tów tekstu dużo lepsza jest str_replace. Oto
Listing 8. Przykład użycia funkcji preg_match
jej składnia: str_replace(szukaj, zamień,
<? tekst);Funkcje te są bardzo proste w użyciu i
$tekst = 'Gitarzysta gra na gitarze. Jego gitara jest stara'; pozwalają na podanie im tablic do przeszuka-
if (preg_match(“/\sg(.*?)a\s/s”, $tekst)) { nia i zamiany, co wpływa pozytywnie na wydaj-
?>W podanym tekście występują wyrazy ność (lepiej użyć raz funkcji z tablicą niż dzie-
zaczynające się na g i kończące na a<? sięć razy osobno).Przykład – Listing 9. Funkcję
} str_replace często wykorzystuje się także do
else { wycięcia danego fragmentu tekstu. Wtedy, jako
?>W podanym tekście nie występują wyrazy pierwszy parametr podajemy szukany ciąg, a ja-
zaczynające się na g i kończące na a<? ko drugi tylko pusty cudzysłów (''), np.
}
?> str_replace('Ala ma kota', '', $tekst)

Listing 9. Przykłady użycia funkcji str_replace spowoduje usunięcie wszystkich ciągów "Ala
<? ma kota" w zmiennej $tekst. Teraz bardziej
$szukaj = 'kot'; praktycznie rozwiązanie – przykładowo chce-
$zamien = 'kocur'; my do wszystkich linków w danym tekście do-
$tekst = 'Ala ma kota, a kot ma Alę'; dać klasę link, tak aby każdy link wyglądający
/* tak <a href=”...”>...</a> przyjął postać
Stosujemy funkcję str_replace bezpośrednio na zmiennej $tekst. Równie dobrze nie
musimy tego robić – wystarczy od razu wyświetlić funkcję (echo str_replace($szukaj, <a href=”...” class=”link”>...</a>
$zamien, $tekst); ) – Listing 10.
*/
$tekst = str_replace($szukaj, $zamien, $tekst); W szablonie wyrażenia regularnego przy prze-
echo $tekst; // Wyświetli "Ala ma kocura, a kocur ma Alę" szukiwaniu każdy odnaleziony ciąg znaków
?> (wyrażenie (.*?) ) odpowiada kolejnej cyfrze po
<? dwóch znakach slash w zamianie (np. \1, \2).
// Przykład z użyciem tablic
$szukaj = array('kota', 'kot '); Zmiana kodowania tekstu
$zamien = array('koteczka', 'kocur '); za pomocą funkcji
$tekst = 'Ala ma kota, a kot ma Alę'; Często bywa tak, że mamy problem z kodowa-
$tekst = str_replace($szukaj, $zamien, $tekst); niem znaków narodowych na naszych dynamicz-
echo $tekst; // Wyświetli “Ala ma koteczka, a kocur ma Alę” nych stronach. Na poprawne wyświetlanie takich
?> znaków na stronie wyświetlającej tekst z bazy
danych wpływa kodowanie bazy danych, kodo-
Listing 10. Dodanie klasy link do wszyskich linków w tekście wanie danej tabeli bazy danych, kodowanie pliku
<? wyświetlającego tekst oraz kodowanie samego
$linki = '<a href=”index.php”>Strona główna</a> <a href=”index.php?p=firma”>O firmie</a> dokumentu HTML. Gdy jeden wariant jest niepo-
| <a href=”index.php?p=kontakt”>Kontakt</a>'; prawny, znaki nie zawsze wyświetlają się popraw-
$linki = preg_replace(“#<a(.*?)href=\”(.*?)\”>(.*?)</a>#si”, ' nie (lub ich część). Ratować możemy się sami,
<a\\1 href=”\\2” class=”link”>\\3</a>', $linki); pisząc odpowiednią funkcję. Funkcja change_
echo $linki; encoding będzie zmieniała kodowanie danego
// Przykład wyświetli “<a href=”index.php” class=”link”>Strona główna</a> tekstu na podstawie znaków zawartych w tabli-
// | <a href=”index.php?p=firma” class=”link”>O firmie</a> <a href= cach (każdy znak ma swój kod, dlatego też funkcja
// ”index.php?p=kontakt” class=”link”>Kontakt</a>” działa w każdym kodowaniu – Listing 11.
?> Funcja działa na prostej zasadzie – jako
pierwszy parametr podajemy tekst do przeko-

44 05/2007
Operacje na tekście

Listing 11. Tworzenie funkcji change_encoding Listing 13. Użycie składni heredoc
<? <?
function change_encoding($string, $type) $a = 5;
{ $b = 'Ala ma kota';
$win2utf = array( $c = 'a kot ma Alę';
"\xb9" => "\xc4\x85", "\xa5" => "\xc4\x84", "\xe6" => "\xc4\x87", "\xc6" => "\ $d = array('test1', 'test2');
xc4\x86", "\xea" => "\xc4\x99", "\xca" => "\xc4\x98", echo <<<EOT
"\xb3" => "\xc5\x82", "\xa3" => "\xc5\x81", "\xf3" => "\xc3\xb3", "\xd3" => "\ “Witamy panie i panowie”
xc3\x93", "\x9c" => "\xc5\x9b", "\x8c" => "\xc5\x9a", 'To jest nasz test składni heredoc',
"\xbf" => "\xc5\xbc", "\x8f" => "\xc5\xbb", "\x9f" => "\xc5\xba", "\xaf" => "\ Teoretycznie nie ma tu ograniczeń.
xc5\xb9", "\xf1" => "\xc5\x84", "\xd1" => "\xc5\x83", 3 + $a = 8.
); $b,$cMożemy także wyświetlać zawartości
$iso2utf = array( tablic, np. $d[0] EOT;
"\xb1" => "\xc4\x85", "\xa1" => "\xc4\x84", "\xe6" => "\xc4\x87", "\xc6" => "\ ?>
xc4\x86", "\xea" => "\xc4\x99", "\xca" => "\xc4\x98",
"\xb3" => "\xc5\x82", "\xa3" => "\xc5\x81", "\xf3" => "\xc3\xb3", "\xd3" => "\ Listing 14. Przykład zastosowania znaków
xc3\x93", "\xb6" => "\xc5\x9b", "\xa6" => "\xc5\x9a", specjalnych
"\xbc" => "\xc5\xba", "\xac" => "\xc5\xb9", "\xbf" => "\xc5\xbc", "\xaf" => "\ <?
xc5\xbb", "\xf1" => "\xc5\x84", "\xd1" => "\xc5\x83" $tekst = “Ten tekst\n przechodzi
); do\n nowej linii. Mam dużo
if ($type == 'ISO-8859-2->UTF-8') { \$becho $tekst;
return strtr($string, $iso2utf); /*
} Przykład wyświetli:
else if ($type == 'UTF-8->ISO-8859-2') { Ten tekst
return strtr($string, array_flip($iso2utf)); przechodzi do
} nowej linii
else if ($type == 'WINDOWS-1250->UTF-8') { Mam dużo $
return strtr($string, $win2utf); */
} ?>
else if ($type == 'UTF-8->WINDOWS-1250') {
return strtr($string, array_flip($win2utf));
} Oto lista najczęściej używanych znaków spe-
else if ($type == 'ISO-8859-2->WINDOWS-1250') { cjalnych:
return strtr($string, "\xa1\xa6\xac\xb1\xb6\xbc", "\xa5\x8c\x8f\xb9\x9c\x9f");
} • \n — nowa linia;
else if ($type == 'WINDOWS-1250->ISO-8859-2') { • \r — powrót karetki;
return strtr($string, "\xa5\x8c\x8f\xb9\x9c\x9f", "\xa1\xa6\xac\xb1\xb6\xbc"); • \t — tabulacja pozioma;
} • \\ — odwrotny ukośnik;
} • \$ — znak dolara.
?>
Jak widać, znaki te zapisuje się poprzedzając
Listing 12. Zmiana kodowania je backslashem. Przykład zastosowania – Li-
<? sting 14.
$tekst = 'Tekst z polskimi znakami ęóąśłżźćń zapisany w ISO-8859-2';
$tekst = change_encoding($tekst, 'ISO-88592->WINDOWS-1250'); Podsumowanie
echo $tekst; Przedstawione tu informacje o operacjach na tek-
?> ście to tylko podstawy. Przy pomocy bardziej za-
awansowanych funkcji oraz wyrażeń regularnych
możemy z tekstem robić dużo więcej. Mamy
dowania, a jako drugi typ kodowania. W za- otwieramy operator <<< po którym umieszcza- nadzieję, że artykuł naprowadzi Cię na dalsze kro-
leżności od tego jak kodowany jest tekst i jaki my identyfikator składni (tym samym identy- ki w tej dziedzinie, która jest bardzo przydatna
chcemy otrzymać wynik, podajemy odpowied- fikatorem należy zamknąć tekst). W przypad- w większości skryptów PHP.
ni parametr. Do dyspozycji mamy zamianę ku prostszego tekstu posłuży nam identyfika-
ISO-8859-2 na UTF-8 i odwrotnie, WINDOWS- tor EOT. Przykład zastosowania – Listing 13.
1250 na UTF-8 i odwrotnie oraz ISO-8859-2 na Możemy także wyświetlać zawartości tablic.
WINDOWS-1250 i odwrotnie. Przykład użycia Pamiętajmy o tym, że identyfikator kończący MICHAŁ GACKI
funkcji – Listing 12. tekst musi zaczynać się w pierwszej kolumnie Jest programistą-samoukiem. Od najmłodszych
nowej linii, by nie spowodował błędu składni, lat interesuje się informatyką, a najbardziej
Ciekawostki a po nim może znajdować się tylko średnik. programowaniem, które jest jego pasją. Pracu-
Do zapisywania większej ilości tekstu wraz ze Warto wiedzieć, że wyświetlanie tekstu w je w założonej przez siebie grupie (obecnie nie
zmiennymi i znakami specjalnymi bez obaw podwójnym cudzysłowie pozwala na używa- tylko programistycznej) pod nazwą Bil Software
o cudzysłowy możemy użyć składni heredoc. nie znaków specjalnych (cytowanych). Może- (www.bilsoftware.com).
Po funkcji echo lub print zamiast cudzysłowu my zapisać przejście do nowej linii, akapit itp. Kontakt z autorem: michal@bilsoftware.com

www.phpsolmag.org 45
Technika

Formularz internetowy
Walidacja z wykorzystaniem AJAX

Ostatnimi czasy zaistniała potrzeba tworzenia coraz doskonalszych


aplikacji internetowych, w których interfejs usprawnia pracę internauty.
Kolejnym krokiem do zapewnienia lepszej funkcjonalności witryn WWW jest
zastosowanie połączenia kilku technologii: JavaScript, PHP lub innego języka
strony serwera, DOM, XMLHttpRequest.

script_name =”checker.php”; i tablica służą-


Dowiesz się... Powinieneś wiedzieć... ca jako pamięć podręczna żądania var memory
• Jak sprawdzić dane formularza bez przełado- • Znać HTML wraz z DOM, PHP, JavaScript. = new array. Niestety, funkcja createXmlHtt
wania witryny. pRequestObject(); nie należy do standardu i
trzeba samemu ją napisać. Pamiętając o obsłu-
dze błędów, np. za pomocą bloku try/catch,
wzrośnie obciążenie serwera i nadchodzące spróbujmy stworzyć obiekt XMLHttpRequest:
informacje przyjdą w niewłaściwej kolejności. xmlHttp = new XMLHttpRequest();. Takie wy-
Poziom trudności Dlatego stworzymy kolejkę, która będzie prze- wołanie powinno odnieść pożądany skutek we
chowywać zgłoszenia. wszystkich przeglądarkach oprócz magiczne-
go Internet Explorera od wersji szóstej w dół.
AJAX – asynchroniczny W przypadku IE skorzystamy z następującego

O
dpowiednie połączenie tych techno- JavaScript i XML kodu: xmlHttp = new ActiveXObject("Mic
logii, które potrafi asynchronicznie, Nadszedł długo oczekiwany moment. Stworzy- rosoft.XMLHTTP"); Oczywiście, powinniśmy
czyli niewidocznie dla użytkownika, my główną część naszej aplikacji, której zada- także sprawdzić, czy nasze działania zostały
wywoływać skrypty umieszczone na serwerze, niem będzie niewidoczne dla użytkownika wy- wykonane w poprawny sposób. Na Listingu 2.
nazywamy AJAX-em. Aby przybliżyć Ci za- wołanie skryptów PHP umieszczonych na ser- przedstawiliśmy, jak powinna wyglądać funk-
lety i tajniki AJAX-u, wykonamy formularz, werze. Na starcie stwórzmy zmienną przecho- cja createXmlHttpRequestObject();
w którym wprowadzone dane sprawdzimy już wującą obiekt XMLHttpRequest. Robimy to Następnym krokiem jest utworzenie funk-
w momencie jego wypełniania. w pliku index.html – dokładnie tym, w którym cji check. Na samym początku pamięta-
umieściliśmy nasz formularz. W tym momencie my oczywiście, aby dodać nowo wprowadzo-
Przygotowanie formularza HTML używać będziemy języka JavaScript, zatem waż- ne przez użytkownika dane do pamięci pod-
Załóżmy, że nasz formularz ma posłużyć ne jest, aby prawidłowo umieścić go w struktu- ręcznej. Robimy to, używając polecenia push:
do wysyłania wiadomości e-mail. Będziemy rze dokumentu HTML. JavaScript wstawiamy memory.push("inputValue="+inputValue
potrzebować pola: Imię i Nazwisko, Twój adres w sekcji HEAD pomiędzy znaczniki: +"&fieldID="+fieldID);. Po tym zabiegu
e-mail i Wiadomość. Na samym początku zaj- sprawdzamy już tylko, czy obiekt XMLHttp-
mijmy się podstawowym przygotowaniem for- <script language="javascript" Request jest wolny i czy nasza pamięć podręcz-
mularza przy wykorzystaniu tagów HTML-a. type="text/javascript">/* na nie jest pusta, gdyż w przeciwnym wypad-
Kod naszej propozycji formularza zaprezento- <![CDATA[ */ TU TU TU będzie nasz ku nie mielibyśmy czego sprawdzać. Teraz wy-
wany jest w Listingu 1. Oczywiście istnieje do- skrypt JavaScript /* ]]> */</script> starczy już pobrać dane oczekujące w kolejce i
wolność w formatowaniu wyglądu, należy jed- wykonać żądanie. Jak wykonać funkcję check,
nak zwrócić uwagę na pewne istotne punkty. Jeżeli istnieje taka potrzeba możemy również przedstawiliśmy na Listingu 3.
Kluczowymi elementami formularza są wczytać kod z innego pliku, właśnie tak: Tak naprawdę wykonaliśmy właśnie ser-
wywołania funkcji check w momencie, gdy ce aplikacji AJAX. Winni jesteśmy pań-
użytkownik opuści dane pole, i funkcja send_ <script type="text/javascript" stwu jeszcze jedno wyjaśnienie. Użyta meto-
message() w momencie, gdy wyśle wiado- src="nazwa_pliku.js"></script> da readyState zwraca status żądania, gdzie
mość. Założenie zatem jest dość proste: w mo- również w sekcji HEAD 0 oznacza, że nie zainicjalizowano żądania,
mencie, gdy użytkownik wpisze dane, spraw- 1 – trwa ładowanie, 2 – załadowano, 3 – in-
dzimy je i w razie potrzeby wyrazimy naszą Wracając jednak do deklaracji zmiennej prze- teraktywne, 4 – zakończono żądanie. Teraz
dezaprobatę. Tutaj jednak napotykamy na pe- chowującej obiekt XMLHttpRequest, wykonu- należy stworzyć skrypt, który odbierze przy-
wien problem. Cóż bowiem się stanie, jeżeli jemy to w następujący sposób: var xmlHttp = chodzące informacje. Wykona to funkcja han
internauta jest niebywałym mistrzem klawia- createXmlHttpRequestObject();. dleRequestStateChange. Nieocenioną umie-
tury i bardzo szybko przechodzi z jednego po- Potrzebna będzie także zmienna przecho- jętnością w tym przypadku jest poprawne po-
la do drugiego? Wtedy istnieje możliwość, że wująca adres wywoływanego skryptu var sługiwanie się strukturą XML. Spójrzcie, jak

46 05/2007
Formularz internetowy

Listing 1. Podstawowa konstrukcja formularza

<FORM id="form_kontakt"><!--Nie musimy podawać argumentu dla action, gdyż wywołamy odpowiednie pliki asynchronicznie (czyli w tle),
wtedy też podamy, jaki plik wywołujemy i jakiej metody użyjemy do przekazania danych-->
<div style="float:left; display:inline; padding-right:10px;"><!--diplasy:inline użyte tylko po to, aby naprawić błędne
wyświetlanie elementów float przez Internet Explorera-->
<label style="padding-bottom:2px;" for="nazwa_klienta">Imi&#281; i nazwisko:</label>
<label style="padding-bottom:2px;" for="adres_email">Adres e-mail:</label>
<label style="padding-bottom:2px;" for="wiadomosc">Wiadomo&#347;&#263;:</label>
</div>
<div style="float:right: display:inline; ">
<input style="padding-bottom:2px;" id="nazwa_klienta" name="nazwa_klienta" type="text" value="" onblur="check(this.va
lue,this.id)"/><!--wywołanie funkcji check spowoduje połączenie z odpowiednim skryptem na serwerze, który
sprawdzi dane i prześle odpowiedź-->
<input style="padding-bottom:2px;" id="adres_email" name="adres_email" type="text" value="" onblur="check(this.value,thi
s.id"/>
<textarea style="padding-bottom:2px;" name="wiadomosc" cols="19" rows="10" id="wiadomosc"></textarea>
</div>
<div style="padding-top:5px;padding-bottom:5px; font-size:14px;">
<div id="comunication"><!--Tutaj wyświetlimy komunikaty nadesłane z serwera--></div></div>
<div style="clear:both; padding-left:209px;">

<INPUT type="button" name="ok" value="Wy&#347;lij" id="wyslij" onclick="send_message();"><!--Funkcja send_message() uruchomi


odpowiedni skrypt na serwerze, który wyśle wiadomość-->
</div>
</FORM>

Listing 2. Sprawdzanie poprawności stworzenia obiektu XMLHttpRequest

var xmlHttp;
if (window.ActiveXObject)
{
try
{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); //w ten sposób robimy to specjalnie dla IE
}
catch (e)
{
xmlHttp = false; //jeżeli nie udało się stworzyć – z reguły znaczy to, że ktoś nie używa IE
}
}
else
{
try
{
xmlHttp = new XMLHttpRequest(); //tu powołujemy obiekt XMLHttpRequest dla wszystkich innych przeglądarek poza IE
}
catch (e)
{
xmlHttp = false;
}
}

if (!xmlHttp)
{
alert("Bе┌д┘d podczas tworzenia obiektu XMLHttpRequest.");
}
else
{

return xmlHttp;
}

www.phpsolmag.org 47
Technika

Listing 3. Funkcja check realizująca asynchroniczne wywołanie serwera

function check (inputValue,fieldID) //inputValue – wartość pola, fieldID – ID pola


{

//o ile obiekt jest stworzony


if (xmlHttp)
{
//o ile podaliśmy ID
if (fieldID)
{
inputValue = encodeURIComponent(inputValue);
fieldID = encodeURIComponent(fieldID);
memory.push("inputValue="+inputValue+"&fieldID="+fieldID);//dodajemy wprowadzone dane do kolejki
}
try
{
if ((xmlHttp.readyState == 4 || xmlHttp.readyState == 0) && memory.length > 0)
{
var EntryInformation = memory.shift(); //pobieramy dane z kolejki
xmlHttp.open("POST",script_name,true);
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//onreadystatechange użyliśmy do ustawienia funkcji zwrotnej, która obsługuje zmiany statusu żądania
xmlHttp.onreadystatechange = handleRequestStateChange;
xmlHttp.send(EntryInformation);
}
}
catch(e)
{
}
}
}

Listing 4. Odbieramy nadchodzące dane

// wcześniej sprawdzamy, czy dane już zostały dostarczone, robimy to dzięki xmlHttp.status, którego wartość powinna wynosić 200,
// i xmlHttp.readyState, którego wartość powinna wynosić 4
var hellomessage,myDiv;
//zwraca odpowiedź serwera w postaci dokumentu XML, możemy również użyć responseText, a jako zwrot otrzymay łańcuch znaków
xmlResponse = xmlHttp.responseXML;
xmlDocumentElement = xmlResponse.documentElement;
hellomessage = xmlDocumentElement.firstChild.data;
myDiv = document.getElementById("comunication");
//wstawiamy otrzymaną wiadomość do bloku opatrzonego id comunication
myDiv.innerHTML = hellomessage;
setTimeout(”check();”,500) //ponowne wywołanie funkcji check, żeby obsłużyła kolejne żądania oczekujące w kolejce

Listing 5. Zabezpieczenie przed wstrzyknięciem kodu


function form_cleaner()
{
$_POST['inputValue']=htmlspecialchars($_POST['inputValue']);
$_POST['fieldID']=htmlspecialchars($_POST['inputfieldID']);
}

Listing 6. Konstruowanie odpowiedzi XML

header('Content-Type: text/xml');
echo '<?xml version="1.0" encoding="utf-8" standalone="yes"?>';
echo '<response>';

//tutaj wykonujemy funkcje sprawdzające poprawność danych


echo'</response>';

48 05/2007
Formularz internetowy

śmy do sprawdzenia struktury adresu e-mail:


Listing 7. Zawartość nagłówka wiadomości e-mail. '^[0-9a-zA-Z_.-]+@([0-9a-zA-Z-]+.)+
[a-zA-Z]{2,4}$'.
$headears="From: Wiadomość od:".$email_od."\n"; Najważniejszym jednak elementem z punk-
$headears.="Reply-To:".$email_od."\n"; tu widzenia aplikacji AJAX jest teraz popraw-
$headears.="MIME-Version: 1.0\n"; ne skonstruowanie odpowiedzi w formacie do-
$headears.="Content-Type: multipart/alternative; boundary=\"$mime_boundary\"\n"; kumentu XML. Operacja ta przedstawiona jest
$message = "--$mime_boundary\n"; na Listingu 6.
$message .= "Content-Type: text/plain; charset=UTF-8\n"; Tak stworzony skrypt zapewni nam spraw-
$message .= "Content-Transfer-Encoding: 8bit\n\n"; dzenie i odebranie danych przez wcześniej na-
$message .= "$subject:\n\n"; pisaną aplikację. Ostatnim etapem będzie wy-
$message .= "E-mail z Twojej strony internetowej\n"; słanie wiadomości. Całą tę operację zapiszmy
w pliku mail.php. Aby wysłać e-mail, musimy
Listing 8. Funkcja send_message(). skorzystać z funkcji mail: @mail($email,
$subject, $message, $headears). Nie zapo-
function send_message() mnijmy także o ponownej walidacji danych for-
{ mularza. Jest to konieczne z uwagi na potrzebę
if (xmlHttp1.readyState == 4 || xmlHttp1.readyState == 0) wzmocnienia poziomu zabezpieczeń. W prze-
{ ciwnym wypadku jakiś internetowy złoczyń-
nazwa = encodeURIComponent(document.getElementById("nazwa_klienta").value); ca mógłby skorzystać z naszego formularza
mail = encodeURIComponent(document.getElementById("adres_email").value); w sobie tylko znanych niecnych zamiarach.
wiadomosc = encodeURIComponent(document.getElementById("wiadomosc").value); Jeżeli nigdy nie korzystaliście z funkcji @mail,
var parametry; to chcielibyśmy uczulić przede wszystkim na
parametry = "?nazwa="+nazwa+"&mail="+mail+"&wiadomosc="+wiadomosc; zawartość nagłówka. Istotnym jest, abyście
wprowadzili niezbędne tam informacje, tak jak
//tutaj dane przesy&#322;amy getem - w&#322;a&#347;ciwie dla tego tylko, na Listingu 7.
//&#380;eby pokaza&#263; &#380;e si&#281; da W pliku index.html dodajmy do naszego
skryptu JavaScript funkcję send_message. Tak
xmlHttp1.open("GET","mail.php"+parametry,true); naprawdę powinniście umieć już ją wykonać.
xmlHttp1.onreadystatechange = handleRequestStateChange1; Aby wywołać skrypt PHP w pliku mail.php w
xmlHttp1.send(null); sposób niewidoczny dla użytkownika, utwórz-
} cie nowy obiekt var xmlHttp1 = createXml
else HttpRequestObject(); i postępujcie identycz-
{ nie jak przy tworzeniu funkcji check, zmienia-
jąc tylko nazwę obiektu xmlHttp na xmlHttp1.
alert ('Serwer chwilowo zajęty'); Przy wysyłaniu wiadomości nie potrzebujemy
} pamięci podręcznej, gdyż mamy zamiar obsłu-
giwać tylko jedno żądanie. Na Listingu 8 poka-
zaliśmy funkcję send_message. Wysłaliśmy
odebraliśmy nadchodzące informacje na Li- lu użyjemy instrukcji switch lub zapytań wa- tam asynchroniczne żądanie i przesłaliśmy da-
stingu 4. runkowych if. Kiedy już to zrobimy, może- ne do podanego skryptu, ale tym razem meto-
Dokonaliśmy zatem niezbędnych czynności my tworzyć poszczególne funkcje dla każ- dą GET. Zabieg ten nie był podyktowany jakimiś
po stronie klienta. Teraz zajmiemy się walida- dego rodzaju przesyłanych danych. I tak: praktycznymi względami, chcieliśmy po pro-
cją wprowadzanych danych i stworzeniem od- dla pola Imię i Nazwisko stwórzmy funkcję stu pokazać, jak wysłać dane zarówno metodą
powiedzi w postaci struktury XML. Przedsta- check_name($value). Jego walidacja polegać POST, jak i GET.
wione wyżej skrypty nie są kompletne z uwagi będzie na sprawdzeniu, czy składa się wyłącz-
na ich rozpiętość. Zapraszamy jednak do sko- nie z liter. Musimy także sprawdzić, czy dany Podsumowanie
rzystania ze źródeł znajdujących się na płycie tekst nie jest za długi i czy w ogóle został wpi- W naszym artykule skupiliśmy się na poka-
dołączonej do wydania. sany. Aby sprawdzić, czy zmienna zawiera- zaniu, jak działają asynchroniczne wywołania
wyłącznie litery, użyliśmy funkcji ereg (lub serwera. Ważnym jest, abyś wiedział, jak wy-
PHP i walidacja danych preg_match) i kombinacji wyrażeń regular- słać i pobrać informacje ze skryptu PHP oraz
formularza nych: ereg('^[a-zA-Z]+$',$value). Do po- jak skonstruować przy pomocy tegoż skryptu
Stwórzmy teraz plik checker.php. Na wstę- równania długości z wartością minimalną i poprawną odpowiedź w formacie dokumentu
pie przetwarzania wpisanych przez użyt- maksymalną wystarczyła zwykła konstrukcja XML. Sama walidacja danych została przed-
kownika informacji należałoby przekształ- zapytania warunkowego. W przypadku po- stawiona w podstawowym zakresie, ale to już
cić znaki <,>,” i & na nieszkodliwe odpowied- myślnej walidacji funkcja nie powinna nic ro- jest temat na kolejny, sami przyznacie, bardzo
niki, przeciwdziałając w ten sposób możli- bić, jeżeli jednak wprowadzony tekst nie prze- ciekawy artykuł.
wym atakom. Użyjmy do tego celu funkcji szedł pomyślnie naszych testów, to zwracamy
htmlspecialchars, tak jak jest to zaprezento- komunikat o błędzie.
wane na Listingu 5. Podobnie ma się zachowywać funkcja spraw-
Następnie napiszmy skrypt, który wywo- dzająca poprawność wprowadzonego adre- TOMASZ ROSZKO
ła odpowiednią funkcję sprawdzająca w za- su e-mail. Tutaj również ograniczymy się Autor jest studentem III roku informatyki na Uniwer-
leżności od tego, jakie pole jest aktualnie pod- do sprawdzenia długości i struktury adresu. sytecie w Białymstoku.
dawane kontroli. Wystarczy, jeżeli w tym ce- Oto wyrażenie regularne, które zastosowali- Kontakt z autorem: tomaszroszko@gmail.comTeen

www.phpsolmag.org 49
Technika

CakePHP
Buforowanie stron

Cache jest mechanizmem umożliwiającym zredukowanie opóźnienia w


dostarczaniu danych do użytkownika oraz zmniejszenia obciążenia serwera.
W aplikacjach internetowych często zachodzi konieczność wyświetlania tych
samych informacji wielokrotnie. Np. sklep internetowy wyświetla listę dostępnych
produktów w odpowiedzi. na każde żądanie potencjalnych klientów.
stał umieszczony w cache, jest przypisanie da-
Dowiesz się... Powinieneś wiedzieć... ty, która minęła. Znacznik idealnie nadaje się
• Poznasz różne techniki buforowania stron • Wymagana jest znajomość framework Cake- do określania polityki tworzenia cache plików
WWW oraz możliwość ich praktycznego zasto- PHP. Przydatna będzie znajomość języka SQL graficznych, które bardzo rzadko ulegają zmia-
sowania w CakePHP. i umiejętność administrowania bazą danych nie – Listing 1.
MySQL. Cache-Control: DYREKTYWA – Znacznik
określa sposób zachowania mechanizmu bu-
forowania plików (zarówno serwera WWW,
che z pominięciem wszystkich etapów, dzię- jak i serwerów proxy) mówiącego, co powin-
ki którym strona została pierwotnie wygene- no być buforowane i co może być przechowy-
Poziom trudności rowana. W praktyce odbywa się to prawie wane w cache. Najbardziej przydatne są poniż-
tak szybko jak udostępnianie statycznych sze dyrektywy:
stron HTML.
• max-age=[sekundy] – określa maksy-

W
ielokrotne generowanie stron, Cache przeglądarki internetowej malny czas, w którym dane są traktowa-
których zawartość nie zmienia Zamiast za każdym razem generować tę sa- ne jako aktualne (liczony względem da-
się, powoduje zbędne obciążenie mą zawartość strony wystarczy przy pierw- ty określonej przez znacznik Last-Modi-
serwera WWW i bazy danych. Każde żąda- szym wyświetleniu zapamiętać ją i używać fied);
nie od użytkownika musi zostać przetworzo- przy kolejnych wywołaniach – Rysunek 1. • public – wymusza buforowane (przy-
ne, a wynik skierowany do przeglądarki in- Właściwie w każdej nowoczesnej przeglą- datne dla stron, które w normalnych
ternetowej, jednak za każdym razem serwer darce internetowej znajdują się ustawienia warunkach nie są umieszczane w ca-
pobiera te same dane. Nie stanowi to proble- związane z cache. Najczęściej jest to para- che);
mu, gdy strony przegląda kilku użytkowni- metr określający miejsce zarezerwowane
ków, ale kłopoty mogą się pojawić wraz ze na pliki tymczasowe przeglądanych stron Listing 1. Przykładowe żądanie i odpowiedź
wzrostem popularności serwisu. Im więcej WWW. Zasady aktualizacji cache są proste. serwera WWW
użytkowników, tym bardziej serwer będzie Przeglądarka za pomocą znaczników HTTP
obciążony wykonywaniem zadań, które pro- sprawdza, czy przechowywane dane są aktu- HEAD / HTTP/1.1
wadzą do wyświetlenia stron nieróżniących alne – Listing 1. i w miarę możliwości korzy- HOST: localhost
się od siebie wcale lub różniących się tylko sta z nich zamiast pobierać dane z Internetu. HTTP/1.1 200 OK
w niewielkim stopniu. Naturalnym rozwią- Dzięki temu powrót do poprzednio wyświe- Date: Mon, 02 Jul 2007 21:24:12 GMT
zaniem problemu spadku wydajności jest tlanej strony WWW w przeglądarce (przy- Server: Apache
zapisywanie „na boku” generowanych stron cisk „wstecz”) powoduje szybkie wyświetle- Accept-Ranges: bytes
i udostępnianie ich innym użytkownikom. nie informacji, najczęściej bez konieczności X-Powered-By: PHP/4.2.2
Zamiast za każdym razem generować tą samą pobierania danych z sieci. Set-Cookie: PHPSESSID=
zawartość strony wystarczy przy pierwszym Kontrola aktualności cache odbywa się ac35d9b842215f4fb23ca419337af4b8;
wyświetleniu zapamiętać ją i używać przy za pomocą znaczników: Cache - Control, path=/
kolejnych wywołaniach. Technika ta nazywa Expires, Last-Modified oraz ETag przesyła- Expires: Thu, 19 Nov 1981 08:52:00 GMT
się page caching (pol. buforowanie stron) i jest nych w nagłówku stron WWW: Cache-Control: no-store, no-cache,
najprostszą metodą przyspieszania działania Expires: DATA_GMT – Znacznik określa ter- must-revalidate
serwisu. Przy pierwszym wyświetleniu stro- min możliwej zmiany lub wygaśnięcia ważno- Pragma: no-cache
na zostaje zapisana do pliku HTML. Każde ści dokumentu. Po upływie określonego cza- Connection: close
następne odwołanie do tej samej strony po- su (liczonego względem GMT) dokument mo- Content-Type: text/html
woduje, iż do przeglądarki użytkownika zo- że ulec zmianie lub zostać usunięty. Najprost- Content-Language: pl
stanie wysłana zawartość bezpośrednio z ca- szym sposobem wymuszenia, by plik nie zo-

50 05/2007
CakePHP

• no-cache – wymusza pominięcie cache nerowany, co przy częstych zmianach sta- nić na TRUE wartość stałej CACHE_CHECK w
(przeglądarki i serwerów proxy) i pobiera- wia pod znakiem zapytania sens używania pliku /app/config/core.php. define ('CACHE_
nie danych bezpośrednio z Internetu; tej technologii. W praktyce jednak strony CHECK', true); W kontrolerze powiązanym
• no-store – wymusza usunięcie danych z WWW można podzielić na fragmenty, które z widokiem, dla którego włączamy cache, mu-
cache zaraz po przesłaniu ich do użytkow- są statyczne, oraz na takie, które zmieniają się simy dodać CacheHelper przez umieszczenie
nika; często, i poddać buforowaniu tylko te ostat- kodu: var $helpers = array('Cache');
• must-revalidate – wymusza sprawdzanie nie. Wynikowa strona jest wtedy „składana” Następnie należy określić, co chcemy umie-
stanu przedawnionych dokumentów znaj- z mniejszych bloków, z których część będzie ścić w cache. Do zmiennej $cacheAction mu-
dujących się w cache. znajdowała się w cache. Jest to bardzo intu- simy przypisać tablicę zawierającą akcje, któ-
icyjne – w przypadku sklepu internetowego re mają być buforowane, oraz czas (w sekun-
Last-Modified: DATA _ GMT– Znacznik okre- lista produktów jest fragmentem, który naj- dach), przez który cache ma być aktualny
śla datę ostatniej modyfikacji dokumentu – rzadziej ulega zmianom, a z kolei koszyk za- (można używać liczb lub zapisów „1 day” czy
Listing 1. ETag: ZNACZNIK – Znacznik jest kupów klienta może zmieniać się dynamicz- „60 seconds”) – Listing 3.
unikalnym identyfikatorem strony genero- nie. Wykorzystanie page cache w CakePHP W praktyce zdarza się, że pewne fragmen-
wanym przez serwer WWW, który zmie- zaczniemy od zapoznania się konfiguracją ty strony są wypełniane dynamicznymi dany-
nia się za każdym razem, gdy przesyłane da- serwera. Domyślnie cache widoków jest za- mi i nie mogą znaleźć się w cache ze względu
ne ulegną modyfikacji. Znaczniki możemy blokowane. By je aktywować, musimy zmie- na wyświetlane dane. Wystarczy wówczas, że
wysyłać do przeglądarki za pomocą funkcji
header() – Listing 2.
Listing 2. Przykładowe znaczniki wysyłane do przeglądarki za pomocą funkcji header()

Page cache // strona nie będzie umieszczona w cache przeglądarki


Mechanizm page cache jest powiązany z adre- // wymaga obsługi protokołu HTTP/1.1
sem URL. To na jego podstawie w kontrolerze
zapada decyzja, czy strona ma być wygenero- header("Cache-Control: no-cache, must-revalidate");
wana dynamicznie, czy też ma być użyty ca-
che. Jeżeli użytkownik już wcześniej odwoły- // data z przeszłości
wał się do określonego adresu WWW, to zo-
stał wygenerowany plik cache – o ile prezen- header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
towane informacje nie uległy zmianie, to po-
nowne odwołanie do tego samego adresu spo- Lisgin 3. Definiowanie akcji, które mają zostać buforowane przez CakePHP
woduje udostępnienie danych wcześniej wy- // Możemy zdefiniować cache dla wszystkich parametrów akcji
generowanych. Z tego faktu wynikają pewne var $cacheAction = array('view/' => 86400);
ograniczenia. Strony zależące od parametrów
przekazywanych w URL lub od informacji // lub dla każdego parametru oddzielnie.
przechowywanych w sesji nie będą mogły
skorzystać z page cache, podobnie jak strony, var $cacheAction = array(
których zawartość jest uzależniona od czasu. 'view/23/' => 21600,
Adres WWW takich witryn może być za każ- 'view/48/' => 21600
dym razem inny i w ten sposób ciągle byłyby );
tworzone kolejne pliki cache. Sposób działa-
nia page cache (buforowanie całej strony) do-
skonale sprawdza się w przypadku stron, któ-
�����������������������������������
rych zawartość nie zmienia się często. Jest to
trudne do osiągnięcia, jeżeli prezentowanych �����������
jest wiele danych z różnych źródeł. Jeżeli zaj-
dą zmiany w dowolnym źródle danych, plik ����������
cache będzie musiał zostać ponownie wyge-

QUERY-CACHE �����������������������������������������
Nowoczesne bazy danych są najczęściej wy-
posażone w mechanizm QUERY-CACHE po-
wodujący zapamiętanie zapytań kierowa-
nych do bazy oraz skojarzonych z nimi wyni- ����������������������������������
ków. Takie samo zapytanie SQL do bazy da-
nych spowoduje przekazanie wyników prze- �����������
chowanych w QUERY-CACHE bez koniecz-
ności odczytu informacji z plików bazoda- ����������
nowych. Cache jest czyszczony w przypad-
ku zmian w tabelach do których zapytania
się odwołują. MySQL4 posiada błąd, któ-
ry powoduje czyszczenie całego QUERY-CA-
CHE w przypadku zmian w dowolnej tablicy, �����
co niekorzystnie wpływa na czas odpowiedzi
serwera na te same zapytania.

Rysunek 1. Przesyłanie do użytkowników stron WWW

www.phpsolmag.org 51
Technika

fragment kodu, który nie powinien być bu- zentowane błędne nieaktualne dane. W naj- tlać dane z różnych modeli danych). Zmia-
forowany, określimy za pomocą znaczników prostszym przypadku w reakcji na zmiany na danych dowolnego modelu spowoduje
<cake:nocache> oraz </cake:nocache> – Li- możemy usunąć wszystkie pliki cache. Roz- więc automatyczne usunięcie cache dla całe-
sting 4. wiązanie to ma jednak tę wadę, że nawet nie- go widoku. CakePHP automatycznie usuwa
wielka zmiana spowoduje konieczność czaso- cache, gdy nastąpi zmiana w stanie aplikacji.
Czyszczenie cache chłonnego generowania cache na nowo. Zde- Jeżeli użytkownik spowoduje działanie, któ-
Przy korzystaniu z cache kluczowym momen- cydowanie lepiej jest usuwać tylko te pliki ca- re będzie skutkowało zmianami w bazie da-
tem w funkcjonowaniu serwisu jest sposób che, które prezentują dane, które uległy zmia- nych (INSERT, UPDATE, DELETE), zosta-
reakcji na zmiany w prezentowanych danych. nie. CakePHP wykorzystuje fakt, że prezen- nie usunięte cache dla widoku powiązane-
Jeżeli nie będzie sprawnego mechanizmu in- towane na stronach WWW dane są powią- go z kontrolerem odwołującym się do mode-
formowania aplikacji, które pliki cache nale- zane z modelem danych, a cache jest związa- li danych, które uległy zmianie. Istnieje moż-
ży usunąć, to użytkownikowi zostaną zapre- ny z widokiem (który z kolei może wyświe- liwość ręcznego sterowania cache i usuwania
nieaktualnych danych za pomocą funkcji cle-
arCache(). Jako parametr przekazujemy na-
Listing 4. Przykład wyłączenia buforowania fragmentu kodu widoku
zwę kontrolera, kontrolera i akcji lub kontro-
<h1> Ostatnie 10 wiadomości! </h1> lera, akcji i parametrów identyfikujących plik
<cake:nocache> cache – Listing 5.
<ul> W prosty sposób można zaimplemento-
<?php foreach($recentMessages as $message): ?> wać dodatkowy scenariusz czyszczenia cache
<li>$message['title']</li> sprawdzający się w sytuacjach, gdy nie może-
<?endforeach;?> my zapobiec cyklicznemu tworzeniu się no-
</ul> wych plików cache, ale jednocześnie chcemy
</cake:nocache> uniknąć ciągłego usuwania plików. Zamiast
spowalniać działanie aplikacji ciągłym testo-
Listing 5. Funkcja clearCache() usuwająca nieaktualne pliki cache waniem aktualności cache możemy urucho-
// Usuń wszystkie strony z cache, bazując na nazwie kontrolera mić dodatkowy proces działający w tle. Pro-
clearCache('controller'); ces ten będzie odpowiedzialny za cykliczne
czyszczenie cache co określony (konfiguro-
// Usuń wszystkie strony z cache, bazując na nazwie kontrolera i nazwie akcji walny) przedział czasowy niezależnie od ob-
ciążenia aplikacji.
clearCache('controller_action/');
Składowanie danych
// Usuń wszystkie strony z cache, bazując na nazwie kontrolera, nazwie akcji Najprostszym sposobem przechowywania
// i parametrze cache są pliki umieszczone bezpośrednio w
systemie operacyjnym (jest to jedyny sposób
// Do funkcji clearCache() można przekazywać wiele parametrów przechowywania cache obsługiwany automa-
tycznie przez CakePHP w wersji 1.1.xx). Wy-
clearCache(array('controller_action_params','controller2_action_params)); generowana strona HTML zostanie umiesz-
czona w publicznie dostępnym obszarze ser-
wera (w miejscu określonym ścieżką dostępu
wskazywaną przez stałą $CACHE) – każde od-
Memcached wołanie do tej samej strony będzie wówczas
System cache przechowujący dane w pamięci RAM, umożliwiający zapisywanie danych i obiek-
przekierowywane na plik cache. Rozwiąza-
tów. Wysoce wydajny i skalowalny umożliwia łączenie serwerów działających w oparciu o różne
architektury systemowe. Stosowany m.in. w serwisach LiveJournal i Wikipedia. System memca- nie takie oprócz oczywistych zalet, takich jak
ched jest dostępny w repozytoriach wielu dystrybucji Linuksa (kod źródłowy można pobrać z ad- prostota implementacji czy dobra wydajność,
resu http://www.danga.com/memcached). Obsługę memcached w PHP zapewnia binarne rozsze- ma podstawową wadę: nie jest rozwiązaniem
rzenie, dostępne na pecl.php.net. skalowalnym. Jeżeli nasz serwis będzie się
Krótki przegląd metod API:
rozwijał i zajdzie konieczność uruchomie-
• Memcache::add – dodaje element do serwera. nia dodatkowego serwera WWW, to stanie-
• Memcache::addServer – dDodaje serwer memcached do listy wykorzystywanych serwerów. my przed problemem związanym z dystry-
• Memcache::close – zamyka połączenie. bucją plików cache i ich synchronizacją. Za-
• Memcache::decrement – zmniejsza wartość elementu. pytania od użytkowników mogą być kierowa-
• Memcache::delete – usuwa element z serwera. ne do dowolnego serwera, każdy będzie więc
• Memcache::flush – usuwa wszystkie elementy z serwera.
• Memcache::get – zwraca element z serwera.
z nich tworzył pliki cache niezależnie. Poza
• Memcache::getExtendedStats – statystyki wszystkich serwerów memcached. tym usunięcie cache z jednego serwera nie
• Memcache::getServerStatus – zwraca stan serwerów memcached. spowoduje usunięcia plików z pozostałych
• Memcache::getStats – statystyki serwerów. serwerów. Można co prawda utworzyć jeden
• Memcache::getVersion – zwraca wersję serwera memcached. wspólny sieciowy filesystem na potrzeby ca-
• Memcache::increment – inkrementuje wartość elementu.
che wszystkich serwerów, ale rozwiązanie
• Memcache::pconnect – otwiera stałe połączenie.
• Memcache::replace – zmienia wartość podanego elementu. traci wtedy swoją prostotę. Innym rozwiąza-
• Memcache::set – zapisuje dane na serwerze. niem jest umieszczanie danych cache w bazie
• Memcache::setCompressThreshold – włącza automatyczną kompresję dużych wartości. danych. Jest to elastyczniejsze od plików ca-
• Memcache::setServerParams – zmienia parametry i stan serwera. che w systemie operacyjnym – głównie kosz-
tem dodatkowego obciążenia zasobów serwe-

52 05/2007
CakePHP

Listing 6. Struktury danych wykorzystane do przechowywania cache


mysql> desc dbcache; // metoda usuwająca z bazy danych wszystkie dane,
mysql> desc dbcache; // których termin ważności minął
+------------+------------------+------+-----+---------+------
----------+ function purge()
| Field | Type | Null | Key | Default | {
Extra | return $this->query("delete * from cache where
+------------+------------------+------+-----+---------+------ expires_at < NOW()");
----------+ }
| key | int(10) unsigned | NO | PRI | NULL | }
|
| value | text | YES | | NULL | // przykładowy kontroler wykorzystujących cache
| // klasa MessagesControler wyświetlająca wiadomości
| expires_at | datetime | YES | | NULL | // internetowego forum
|
+------------+------------------+------+-----+---------+------ class MessagesController extends AppController{
----------+ var $name = 'Messages';
var $helpers = array('Html','Time');
// przykładowy model danych obsługujący buforowanie stron var $layout = 'default';
// w bazie danych klasa Cache var $uses = array('Forum','Topic','Comment','Message',
'MyCache');
class MyCache extends AppModel
function view($id)
// jeżeli używamy PHP4, musimy zdefiniować zmienną {
// zawierającą nazwę klasy
var $name = 'MyCache'; // pobierz informacje o temacie dyskusji i powiązaną
// z nim listę komentarzy jeżeli temat nie istnieje,
// korzystamy z bazy danych – tabeli o nazwie cache // ponownie wyświetl listę działów

var $useTable = ‘cache’; $topic = $this->MyCache->find('topic'.$id);


if (empty($topic)) {
// metoda wyszukująca wartość skojarzoną z kluczem $topic = $this->Topic->find(array('Topic.id'=>
// i zwracająca dane jako zmienną PHP $id, 'Topic.topic_id'=>0));
if (! empty($topic)) {
function find($key)
{ // przechowuj przez tydzień
$rc = $this->query("select * from cache where key = $this->MyCache->store('topic'.$id, $topic, time()
'$key' and expires_at < NOW() limit 1"); + (7 * 24 * 60 * 60));
return unserialize($rc['cache'][0]['value']); }
} else
{
// metoda przechowująca w bazie danych klucz razem $this->redirect('/forums/index');
// z wartością exit();
}
function store($key, $value, $expires=null) }
{
// jutro // przekaż zmienną $topic do widoku
if (is_null($expires)) $expires = time()+(24 * 60 * 60));
$expires = date('Y-m-d H:i:s', $expires); $this->set('topic', $topic);
$value = serialize($value); }
return $this->query("replace cache set value = '$value',
$expires_at = '$expires' where key = '$key'); function remove($id)
} {

// metoda usuwająca przechowywane dane na podstawie klucza // usuń temat dyskusji wraz ze wszystkimi wiadomościami
// wyszukiwania // usuń zbędne pliki z cache

function delete($key) if ($this->Topic->drop($id, true)) $this->


{ MyCache->delete('topic'.$id);
return $this->query("delete * from cache where key = $this->redirect('/forums/index');
'$key'"); exit();
} }

www.phpsolmag.org 53
Technika

Memcached ma jeszcze jedną zaletę – moż-


Listing 7. Wykorzystanie memcached z poziomu PHP na go użyć jako mechanizm przechowują-
// utworzenie puli dostępnych serwerów memcached cy dane nie tylko na potrzeby buforowania
// każdy z nich będzie wykorzystywany proporcjonalnie całych stron (page cache). Możemy w cache
// do listy wag przekazanych jako prametr do metody Mamcache::addServer() umieszczać dowolne dane (wyniki obliczeń,
dynamicznie zmieniające się relacje między
$memcache = new Memcache; danymi) i na ich podstawie generować stro-
$memcache->addServer('memcache_host1', 11211, 50); ny WWW.
$memcache->addServer('memcache_host2', 11211, 25); Podobnie jak w przypadku page cache pro-
$memcache->addServer('memcache_host3', 11211, 25); blemem jest określenie, czy strony na których
$memcache = new Memcache; były prezentowane dane, które uległy mo-
$memcache->connect('localhost', 11211) or die ('Nie mogę się połączyć'); dyfikacji, znajdują się w cache. Memcached
umożliwia przechowywanie wyników obli-
if ($get_result = $memcache->get('key')) czeń, więc nie możemy polegać na prostej za-
{ leżności mówiącej, że w przypadku zmian w
modelu danych należy usunąć cache związa-
// jeżeli obiekt jest w cache, to skorzystaj z niego ny z widokiem prezentującym dane.Zamiast
tego można posłużyć się sztuczką polegają-
echo '<b>Dane z serwera</b>:<br/>'; cą na powiązaniu przechowywanych danych
echo $get_result->str_attr.'<br />'; przechowywanych w cache ze znacznikiem
echo $get_result->int_attr; sygnalizującym zmiany. Wystarczy, że utwo-
rzymy model danych, który będzie dostarczał
} informacje o stanie aplikacji, np. unikalny nu-
mer, niezmienny tak długo, jak długo nie na-
else stąpiły zmiany w bazie danych lub innych ob-
liczeniach.
{ Jeżeli tym numerem będziemy posługiwa-
li się przy obsłudze cache, to jego zmiana wy-
// w cache nie ma żądanych danych, zapisz dane musi ponowne przeliczenie zmienionych da-
nych. Jeżeli przestrzeń przeznaczona na bu-
$tmp_object = new stdClass; for będzie się zmniejszać, memcached auto-
$tmp_object->str_attr = 'test'; matycznie usunie dane, które przez określo-
$tmp_object->int_attr = time(); ny czas nie były aktywne.
$memcache->set('key', $tmp_object, false, 10) or die ( Rozwiązanie takie sprawdzi się przede
'Nie udało się zapisać elementu'); wszystkim przy skomplikowanych stronach
echo 'Zapisane dane zostaną usunięte po 10 sekundach<br/>'; WWW opartych o przechowywane w cache
echo 'Odśwież stronę, by zobaczyć dane zapisane na serwerze memcached'; dane pochodzące z czasochłonnych obliczeń.
Koszt zaangażowania zasobów serwera w wy-
} krycie zmian w stanie aplikacji będzie wów-
czas zrównoważony przez oszczędności wy-
nikające z braku potrzeby wykonywania cza-
ra mechanizmami zapisu i odczytu danych odłączać) bez konieczności przerywania dzia- sochłonnej generacji strony.
(zobacz ramka QUERY-CACHE). Oczywi- łania serwisu (obsługa memcached zostanie
ście istnieje możliwość skalowania takiego włączona do CakePHP począwszy od wer- Podsumowanie
rozwiązania, jednak nie jest ono proste po- sji 1.2.xx). Mechanizm page cache w CakePHP w połą-
nieważ wymaga żmudnej konfiguracji (repli- Typowa sesja połączenia z memcached z po- czeniu z umiejętnym wykorzystaniem cache
kacja baz danych) i nie jest standardowo ob- ziomu PHP została przedstawiona na Listin- przeglądarki internetowej może zredukować
sługiwane przez CakePHP 1.1.xx. Przykłado- gu 7. W pierwszej kolejności następuje zdefi- opóźnienia w dostarczaniu danych do użyt-
wy model danych obsługujący przechowywa- niowanie puli dostępnych serwerów pracują- kownika oraz zmniejszyć obciążenie serwera
nie cache w bazie danych został przedstawio- cych pod kontrolą memcached. Każdy z nich WWW i bazy danych. Warto zwrócić szcze-
ny w Listingu 6. będzie wykorzystywany proporcjonalnie do gólną uwagę na technologię memcached, któ-
Najbardziej elastycznym sposobem prze- wagi określonej podczas inicjalizacji połącze- ra zapewnia szybki dostęp do danych zawar-
chowywania danych jest mechanizm mem- nia i w razie awarii zastąpiony przez kolejny tych w cache, jest elastyczna i skalowalna,
cached przechowujący dane w pamięci RAM serwer z listy. dzięki czemu może „rosnąć” razem z naszym
serwera (zobacz ramka). W miarę, jak dane Każdy dostęp do danych poprzedzony jest serwisem.
będą zapełniały przydzieloną pamięć, system testem, czy informacje są dostępne w cache.
będzie automatycznie usuwał te, które były Tylko w przypadku negatywnym nastąpi ko-
najrzadziej używane (można też usuwać da- nieczność wykonania czasochłonnych obli-
ne za pomocą odpowiednich wywołań syste- czeń, po których wynik zostanie zapisany w
mowych). cache (kolejne zapytania będą więc korzysta- PIOTR GAPIŃSKI
Memcached został także wyposażony w ły z bufora). Memcached sam zatroszczy się o Autor w wolnych chwilach zajmuje się programo-
mechanizmy równoważące obciążenie, gdy usunięcie zbędnych (przestarzałych) danych waniem w różnych językach (głównie Rebol, Ruby,
wykorzystywanych jest kilka serwerów, któ- z pamięci – w naszym przykładzie po 10 se- PHP i AmigaE).
re można w prosty sposób przyłączać (lub kundach. Kontakt z autorem: narg@polbox.com

54 05/2007
Narzędzia

SciTE
„Lekki” edytor o dużych możliwościach

Wcześniej czy później każdy programista stanie przed koniecznością wyboru


edytora, pozwalającego na sprawne tworzenie kodu. Odpowiednie narzędzie
to nie tylko wygoda, ale przede wszystkim oszczędność czasu.

lu twórców będzie miłą wiadomością. Należy


Dowiesz się... Powinieneś wiedzieć... również wspomnieć że SciTE jest programem
• Jak wykorzystać zawansowane możliwości edy- • Podstawowa znajomość PHP i narzędzi do two- o otwartym kodzie źródłowym rozprowadza-
totora SciTE oraz jak skonfigurować go do pra- rzenia kodu. nym za darmo.
cy z językiem PHP. Instalacja edytora nie powinna przysporzyć
trudności. Binarne wersje zarówno dla systemu
Windows jak i Linux można ściągnąć ze strony
• możliwość podpięcia zewnętrznych pro- domowej programu http://scintilla.sourceforge.n
gramów (np. Debuggera); et/SciTEDownload.html
Poziom trudności • bufory pozwalające na pracę z kilkoma pli-
kami jednocześnie; Pierwsze uruchomienie
• mechanizm zapamiętywania sesji; Po pierwszym uruchomieniu edytora można
• dwupanelowa budowa (panel edycji i pa- się trochę rozczarować prostotą jego interfej-

P
HP jest bardzo popularnym językiem nel wyjścia). su. W najprostszej konfiguracji SciTE przy-
programowania, co przekłada się rów- pomina program Notatnik z systemu Win-
nież na liczbę dostępnych dla niego na- SciTE może pracować zarówno w środowisku dows. Nie dajmy się jednak zwieźć pozorom.
rzędzi programistycznych. Niestety, szeroka Windows, jak i pod kontrolą większości syste- Przy odrobinie wytrwałości to z pozoru ubo-
gama aplikacji tak naprawdę utrudnia wybór, mów unixowych (w tym Linuksa), co dla wie- gie narzędzie, może zmienić się w funkcjonal-
ponieważ sama znajomość funkcji progra-
mu nie wystarczy aby stwierdzić czy to ten
„właściwy”. W praktyce edytor można uznać
za sprawdzony dopiero po napisaniu kliku
skryptów podczas codziennej pracy.
SciTE to prosty, szybki i konfigurowalny edy-
tor tekstu wyposażony w wiele funkcji przydat-
nych programistom przy tworzeniu kodu. Po-
czątkowo był on tylko programem demonstru-
jącym możliwości komponentu Scintilla (na
którym bazuje), z czasem jednak rozrósł się tak,
że obecnie konkuruje z wieloma narzędziami
programistycznymi.
Edytor ten został wyposażony w wie-
le funkcji ułatwiających programowanie.
Trudno byłoby wymienić je wszystkie, dla-
tego poniżej znajdują się tylko ważniejsze
z nich:

• kolorowanie składni dla większości popu-


larnych języków;
• numerowanie linii;
• uzupełnianie składni języka;
• podpowiedzi;
• zwijanie kodu;
• tworzenie zakładek w kodzie; Rysunek 1. Okno edytora SciTE

56 05/2007
SciTE

ny i wygodny edytor kodu tak jak to przedsta- ków (np. nie posiadamy uprawnień zapisu) konfiguracyjnym użytkownika mógłby wyglą-
wia Rysunek 1. w katalogu instalacyjnym możemy utworzyć dać następująco:
plik z konfiguracją języka w innej lokalizacji.
Konfiguracja Na przykład w systemach uniksowych może- import .SciTE/html
Konfiguracja edytora SciTE nie należy do naj- my stworzyć w katalogu domowym folder o
prostszych, ponieważ polega na edycji plików nazwie .SciTE i w nim umieszczać pliki z wła- lub
tekstowych, a to z kolei wymaga studiowania snymi ustawieniami. Aby wczytać konfigura-
dokumentacji. Warto jednak poświęcić na te cję dla danego języka z innej lokalizacji niż ka- import /home/nazwauzytkownika/.SciTE/html
czynności trochę czasu, ponieważ efekt mo- talog instalacyjny należy skorzystać z opcji im-
że być imponujący. Podstawowa konfigu- port podając pełną ścieżkę do wczytywanego W domyślnej instalacji SciTE nie posiada
racja SciTE zawiera się w pliku SciTEGlo- pliku pomijając rozszerzenie .properties. Od- osobnego pliku konfiguracyjnego dla języka
bal.properties znajdującym się przeważnie powiedni wpis (np. dla języka HTML) w pliku PHP. Oczywiście można go utworzyć, jednak
w katalogu instalacyjnym aplikacji. Aby go
nie szukać możemy otworzyć plik wybiera-
jąc z menu programu Options | Open Glo-
bal Options File. Tutaj dokonujemy odpo-
wiednich poprawek zgodnie z dokumentacją
dostępną na stronie http://scintilla.sourceforg
e.net/SciTEDoc.html. SciTE oferuje również
możliwość stworzenia własnej konfiguracji
bez wprowadzania modyfikacji w pliku glo-
balnym. Dokonujemy tego poprzez utworze-
nie pliku użytkownika SciTEUser.properties
wybierając z menu programu opcję Options
| Open User Options File. Plik ten będzie pu-
sty, tak więc najlepszym rozwiązaniem bę-
dzie skopiowanie do niego ustawień global-
nych a następnie wykonanie potrzebnych
zmian. Wybrane opcje konfiguracyjne zosta-
ły przedstawione na Listingu 1.

Dwa panele
Edytor SciTE umożliwia korzystanie z dwóch
paneli: panelu edycji – w którym tworzony jest
kod, oraz panelu wyjścia – gdzie można obser-
wować komunikaty programów zewnętrznych
oraz uruchamiać polecenia systemowe. Jest to
bardzo przydatne gdy korzystamy z debugge- Rysunek 2. Uzupełnianie składni
ra lub programu sprawdzającego poprawność
składni. W dalszej części artykułu dowiemy
się jak skonfigurować sprawdzanie składni dla
języka PHP.

Obsługa języków programowania


SciTE obsługuje większość popularnych języ-
ków programowania w tym PHP, a każdy ob-
sługiwany język posiada swój własny plik kon-
figuracyjny w którym możemy modyfikować
ustawienia takie jak kolorowanie składni czy
listę funkcji oraz słów kluczowych. Pliki kon-
figuracyjne poszczególnych języków znajdu-
ją się w katalogu instalacyjnym edytora i po-
siadają rozszerzenie .properties. Jeśli nie chce-
my, bądź nie możemy modyfikować tych pli-

Listing 1. Wybrane opcje konfiguracyjne


edytora

command.name.13.*=example
command.mode.13.*=subsystem:lua,
savebefore:no,groupundo
command.shortcut.13.*=Ctrl+E
command.13.*=example
Rysunek 3. API PHP i podpowiedzi w edytorze SciTE

www.phpsolmag.org 57
Narzędzia

stania z podpowiedzi w postaci „dymków” oraz


Listing 2. Zawartość przykładowego pliku rozszerzeń – example.lua autouzupełniania w postaci listy pasujących
# ustaw szerokość edytora na starcie na 800 pikseli słów. Udogodnienia te najlepiej ilustrują Ry-
position.width=800 sunki 2 i 3.
# ustaw wysokość edytora na starcie na 600 pikseli Aby włączyć te funkcje należy zaopatrzyć
position.height=600 się w plik .api danego języka. Można go stwo-
# podziel okno na panele: 0 – w poziomie, 1 -w poziomie rzyć ręcznie lub pobrać ze strony http://scintilla
split.vertical=0 .sourceforge.net/SciTEExtras.html. Zakładając że
# ustal szerokość dla panelu wyjścia na 100 pikseli plik ten został umieszczony w podkatalogu
output.vertical.size=100 .SciTE pod nazwą php.api wystarczy dodać na-
# ustal szerokość dla panelu wyjścia na 100 pikseli stępujące linijki do pliku konfiguracyjnego
output.vertical.size=100 języka HTML:
# ustal wysokośćkość dla panelu wyjścia na 100 pikseli
output.horizontal.size=100 api.$(file.patterns.php)=
# pokaż pasek z buforami (otwarte pliki) $(SciteUserHome)/.SciTE/php.api
tabbar.visible=1 autocomplete.hypertext.start.characters=
# pokaż pasek narzędziowy _$(chars.alpha)
toolbar.visible=1
# pokaż pasek statusu Sprawdzanie składni
statusbar.visible=1 Sprawdzanie poprawności kodu na bieżąco po-
# włącz numerowanie linii zwala uchronić się przed błędami składniowy-
line.margin.visible=1 mi i dzięki temu znacznie skrócić pracę nad
# automatycznie zapisz pliki gdy okno traci aktywność skryptem. W PHP realizujemy to poprzez wy-
save.on.deactivate=1 wołanie progamu – interpretera „php” z opcją
# zapamiętaj sesję po zamknięciu programu – l nazwa pliku. SciTE może to robić za każ-
save.session=1 dym razem gdy naciśniemy przycisk Compile.
# włącz funkcję zwijania kodu Wystarczy dodać poniższą linijkę do pliku kon-
fold=1 figuracyjnego języka HTML:
# włącz kodowanie unicode
codepage=65001 command.compile.$(file.patterns.php)=
LC_CTYPE=pl_PL.UTF-8 php -l "$(FileNameExt)"
output.codepage=65001
Listing 2 Przy poprawnej konfiguracji rezultat pole-
cenia powinien wyświetlić się w panelu wyj-
function OnChar(c) ścia za każdym razem gdy naciśniemy przy-
if c == '"' or c == '\'' then cisk Compile.
editor:InsertText(editor.CurrentPos, c);
elseif c == '(' then Rozszerzenia w języku LUA
editor:InsertText(editor.CurrentPos, ')'); Jeżeli ktoś wciąż czuje niedosyt funkcjonal-
elseif c == '[' then ności edytora SciTE, mam wesołą wiadomość
editor:InsertText(editor.CurrentPos, ']'); - aplikację tą możemy rozszerzać przy pomocy
elseif c == '<' then editor:InsertText(editor.CurrentPos, '>'); języka LUA. Daje to ogromne możliwości takie
elseif c == '?' and string.char(editor.CharAt[editor.CurrentPos - 2]) == jak tworzenie makr i wyzwalaczy czy reakcji na
'<' then editor:InsertText(editor.CurrentPos, 'php ?'); określone zdarzenia. Tak więc należy utworzyć
editor:GotoPos(editor.CurrentPos + 4); plik o dowolnej nazwie np. extensions.lua i po-
informować SciTE o jego lokalizacji umieszcza-
elseif c == '{' then local indent = editor.LineIndentation[editor: jąc w pliku konfiguracyjnym linijkę, która mo-
LineFromPosition(editor.CurrentPos)] / editor.TabWidth; że wyglądać w następująco:
editor:InsertText(editor.CurrentPos, "\n\n" .. string.rep("\t",
indent + 1) .. "\n" .. string.rep("\t", indent) .. "}"); extension.*=$(SciteUserHome)/extensions.lua
editor:GotoPos(editor.CurrentPos + (indent + 3));
end Przykładowy fragment pliku lua znajduje się
return false; na Listingu 2. Skrypt ten jest oparty na zda-
rzeniu onChar edytora SciTE (fukcja wywo-
end ływana przy każdym wpisaniu znaku), a jego
zadanie polega na dopisywaniu zamykających
nawiasów, cudzusłowów etc. Oczywiście moż-
lepszym rozwiązaniem będzie skorzystanie z zmodyfikować ustawienia dla opcji keywordc- liwości edytora można rozbudowywać przy
pliku ustawień dla języka html, który posiada lass.php dodając oddzielone spacją nazwy inte- użyciu bardziej rozbudowanych skryptów
również odpowiednie instrukcje dla PHP. resujących nas funkcji. lua. Mamy na przykład możliwość tworzenia
Domyślnie nowo zainstalowany edytor bę- funkcji które zostaną wywołane z poziomu
dzie kolorował tylko słowa kluczowe języ- Podpowiedzi i autouzupełnianie menu Tools aplikacji lub przy użyciu skrótu
ka PHP. Jeśli chcielibyśmy dodać pozostałe Bardzo ciekawym i przydatnym rozszerzeniem klawiaturowego. Wystarczy w pliku konfigu-
funkcje dostępne w PHP należy odpowiednio w omawianym edytorze jest możliwość korzy- racyjnym dodać linie – Listing 1.

58 05/2007
Powyższy przykład spowoduje umieszczenie
w menu Tools pozycji o nazwie example, która
będzie odpowiedzialna za uruchomienie funk-
cji example z pliku rozszerzeń lua.
Szczególnie przydatna może okazać się moż-
liwość reakcji na zdarzenie onSave (podczas za-
pisywania pliku). Tworząc odpowiedni skrypt
możemy np. automatycznie wysyłać pliki na
zdalny serwer poprzez protokół FTP używając
zewnętrznych programów.
Pod adresem http://lua-users.org/wiki/SciteScripts
można znaleźć kilka gotowych do użycia skryp-
tów. Zachęcam również do zapoznania się z do-
kumentacją tworzenia rozszerzeń z użyciem
LUA pod adresem http://scintilla.sourceforge.net/
SciTELua.html.

SciTE w ojczystym języku


Wielu z pewnością zadowoli fakt możliwo-
ści spolszczenia interfejsu edytora SciTE. Wy-
starczy ze strony http://scintilla.sourceforge.net/
SciTETranslation.html pobrać odpowiedni plik,
zmienić jego nazwę na locale.properties i skopio-
wać do katalogu instalacyjnego.

Podsumowanie
Jak widać edytor SciTE jest bardzo zaawan-
sowanym narzędziem w swojej klasie. Choć
niektórym programistom będzie tu brakować
funkcji takich jak zarządzanie projektami czy
klient FTP. Można te braki oczywiście obejść
stosując dodatkowe oprogramowanie, bądź
pisząc rozszerzenia w języku LUA.
Niewątpliwie największą zaletą tej aplikacji
jest konfigurowalność, pozwalająca na dosto-
sowanie jej do własnych potrzeb programisty i
chyba właśnie to docenia większość użytkow-
ników najbardziej. Należy również wspomnieć
o stabilności programu i niskim zapotrzebowa-
niu na zasoby systemowe.
Poza stroną domową http://scintilla.sour-
ceforge.net/SciTE.html w sieci nie znajdzie-
my zbyt wiele na temat opisywanego edy-
tora – najprawdopodobniej początkujących
użytkowników odstrasza konfiguracja tego
narzędzia. Warto również zajrzeć na grupę
dyskusyjną http://mailman.lyra.org/mailman/
listinfo/scite-interest – można tu odnaleźć wie-
le odpowiedzi na pytania nurtujące nie tylko
początkujących użytkowników tego imponu-
jącego narzędzia.

ROBERT ZAJDA
Autor artykułu jest właścicielem firmy APISOFT
zajmującej się bezpieczeństwem systemów in-
formatycznych i wdrażaniem oprogramowania
open-source oraz nauczycielem przedmiotów in-
formatycznych w Zakładzie Doskonalenia Zawo-
dowego w Radomiu.
Kontakt z autorem: robert.zajda@gmail.com

www.phpsolmag.org
Narzędzia

jQuery
Pisz mniej, rób więcej

Co to jest jQuery? Wikipedia podaje następującą definicję: lekka Biblioteka


programistyczna dla języka JavaScript, ułatwiająca współdziałanie JavaScript
oraz HTML. Ale to nie wszystko. Jest to biblioteka, która wszystkie obiekty
strony HTML „ubiera” w dodatkowe zdarzenia, własności i metody.

Można się pokusić o stwierdzenie, że nie


Dowiesz się... Powinieneś wiedzieć... wydaje się to wielkim ułatwieniem, wszak
• Artykuł pokazuje jak w łatwy i szybki sposób • Wymagana jest dobra znajomość html, css, JavaScript sam w sobie daje możliwość wy-
dodać do strony zaawansowane efekty wizual- • Podstawowa znajomość JavaScript, php. selekcjonowania wybranego elementu, ale
ne, a także możliwości wykonywania asynchro- dla konstrukcji, która ma za zadanie wybrać
nicznych zapytań http. wszystkie elementy listy <li> dla obiektu o
wybranym ID, kod JavaScript będzie wy-
magał odrobiny programowania, a w przy-
jawi się komunikat (alert) „Hello Word”. padku jQuery wystarczy zastosować wy-
Ta konstrukcja odpowiada klasycznemu kodo- starczy zastosować konstrukcję prezento-
Poziom trudności wi HTML i JavaScript waną w listingu 8. Innym ciekawym przy-
kładem selekcji elementów strony HTML
<a href="" onclick="alert('Hello może być ujęcie w kolekcję wszystkich od-
world')">Link</a> nośników mających ustawiony parametr

D
odatkowo bardzo intuicyjne two- „name”. Kod w jQuery napisać można w na-
rzy grupy obiektów i kolekcje, dając z tą różnicą, że jeden wpis odnosi się automa- stępującej postaci:
możliwość iteracji po wybranych ele- tycznie do wszystkich linków na tworzonej
mentach grupy. Wyposażenie jQuery w możli- przez nas stronie. $("a[@name]").css("background", "#eee"
wość wykonywania zapytań asynchronicznych );
w połączeniu z łatwością manipulacji dowolny- Selektory HTML i CSS
mi obiektami lub grupami obiektów wewnątrz Pełne wsparcie dla CSS (1, 2, 3) oraz wszystkich Łatwo się domyślić, że wykonanie tego frag-
strony HTML daje silne i łatwe w użyciu narzę- selektorów HTML stanowi podstawę dobrego mentu kodu spowoduje zmianę koloru tła
dzie do tworzenia atrakcyjnych wizualnie stron imienia omawianej biblioteki. Możliwość defini- wyselekcjonowanych elementów. Bardziej
w technologii AJAX. cji kolekcji selektorów i iteracji po dowolnej z nich użytecznym selektorem jednak wydać się
jest dużym ułatwieniem w tworzeniu logiki apli- może taki, który wybiera linki zawierają-
Instalacja i konfiguracja kacji webowej po stronie klienta. Przypuśćmy że ce w swym adresie wskazanie do np. kata-
Instalacja jest bardzo prosta, a właściwie nie chcemy odwołać się do elementu o ID „ordere- logu „galery”.
ma jej wcale. Wystarczy ściągnąć plik jqu- dlist”. W klasycznym skrypcie JavaScript musie-
ery.js i włączyć go jako skrypt do własnej stro- libyśmy użyć konstrukcji: document.getElement $("a[@href*=/gallery]").click(function() {
ny. Od tego momentu można zacząć używać ById("orderedlist") by wydobyć interesujący // do something with all links that
funkcjonalności, jaką daje nam ta bibliote- nas element. Przy wsparciu jQuery mamy do dys- // point somewhere to /gallery
ka. Pobieramy plik jQuery.js z adresu: http:// pozycji konstrukcję: });
code.jquery.com/jquery-latest.js i umieszcza-
my go w katalogu roboczym razem z plikiem $("#orderedlist").addClass("red"); Można długo opisywać różne ciekawe kom-
tworzonej przez nas strony. Następnie włą- binacje metod selekcji elementów strony, za-
czamy w kod strony zawartość ściągniętego Przy czym możemy na wybranym obiekcie mieszczone tutaj przykłady zostały wybra-
pliku, deklarując w części nagłówkowej na- wywołać dowolne funkcje dostarczane z oma- ne, by pokazać spektrum możliwości, jakie na
szej strony: wianą biblioteką, w tym konkretnym przykła- tym polu daje nam jQuery.
dzie została wywołana funkcja
<script src="jquery-latest.js" type="text/ Walidacja formularzy
javascript"></script> addClass("red"); Częstym i bardzo eleganckim rozwiązaniem
stosowanym na różnego rodzaju stronach
Tworzymy kod powodujący, iż po kliknięciu Funkcja ta, jak łatwo się domyślić, doda jest walidacja formularzy. Nie trzeba chyba
na dowolny link w obrębie strony HTML po- klasę „red” do elementu o ID „orderedlist”. nikogo przekonywać, że duże formularze na

60 05/2007
jQuery

witrynach pełnych elementów graficznych działania danego zapytania. Pierwszą użytecz- załadowaniu danych ze skryptu PHP, załaduje
lepiej walidować bez kosztownego przełado- ną funkcją, związaną z dziedziną „ajax”, jest je do znacznika HTML o ID =”wynik” (<div
wywania całej strony, gdyż słabe łącze mo- funkcja get. Parametrami jej wywołania są: id=”wynik”><div>)
że spowodować, że wypełniający formularz .get(url, params, callback), gdzie url to W pliku test.php przetwarzamy zapyta-
zirytuje się długim oczekiwaniem na odpo- URL, params określa parametry przekazywa- nie oraz przekazane parametry – Listing 11.
wiedź serwera z informacją, że wpisana war- ne w zwykłym URL-u za znakiem zapytania, u Oczywiście, taki prosty przykład jest dobry,
tość jest niewłaściwa. Zacznijmy od przykła- nas będą w postaci: ale dla nas dobry to za mało, potrzebne nam
du, w którym będziemy walidować numer są zapytania, które odpowiednio zareagują w
PESEL, a w przypadku wpisania niewłaści- { name: "PHPSolution", id: "3/2007" } przypadku niepowodzenia zapytania, a tak-
wej wartości ostrzeżemy użytkownika, ale że – co być może ważniejsze – zrealizują od-
pozwolimy mu wypełniać dalej formularz Ostatnim parametrem może być nazwa funk- powiednie czynności po pełnym wykonaniu
i ostatecznie zaakceptujemy wadliwy nu- cji, którą chcemy wywołać po wykonaniu za- zapytania, czyli wtedy, kiedy „spłyną” już
mer PESEL. Najpierw przygotujemy funk- pytania, może być to funkcja formatująca wszystkie dane od serwera. Jest to potrzeb-
cję, która dokona sprawdzenia, czy przeka- treść stronę w zależności od uzyskanej odpo- ne w przypadku przetwarzania przez serwer
zana jako argument wartość jest poprawnym wiedzi. Dla przykładu podamy kod, który po skomplikowanych operacji bazodanowych.
numerem PESEL.
Część funkcji zmieniona w komen-
Listing 1. Funkcja sprawdzająca poprawność numeru PESEL
tarz umożliwia w szybki sposób rozszerze-
nie funkcjonalność testu o sprawdzenie po- function validPESEL(pesel)
prawności podanej w innym polu formula- {
rza płci, w tym celu należy dodać kolejny if(pesel == 0)return true;
argument funkcji o nazwie „plec” (dozwolo- if (pesel.length > 11) return false;
ne wartości to M lub K) i usunąć znaki ko- var rePesel = /(\d{11})/;
mentarza z odpowiedniego fragmentu kodu.
Mając tak przygotowaną funkcję, możemy if (rePesel.test(pesel)) pesel = RegExp.$1;
sprawić, by została wywołana przy wysłaniu else return false;
formularza. Aby na nasze żądanie mimo pesel = pesel.split("");
błędnego wpisu dane zostały wysłane, zasto-
sujemy jeszcze jedną funkcję pomocniczą. //if(plec)
Teraz szybie powiązanie funkcji do zdarzenia //{
wysłania formularza przy pomocy jQuery. // if(pesel[9]%2 && plec.search("K")!=-1) return false;
// else if (!pesel[9]%2 && plec.search("M")) return false;
$('#frmDaneOsobowe').ajaxForm( { //}
beforeSubmit: validate } );
var wagi = new Array(1,3,7,9,1,3,7,9,1,3);
Czegóż wybredny programista chciał- var suma=0;
by mieć więcej w tej sytuacji? Możliwość
sprawdzenia, czy dany numer PESEL istnie- for(i=0;i<=9;i++) suma += pesel[i]*wagi[i];
je w naszej lokalnej bazie danych? Wywoła- suma %= 10;
nie asynchronicznego zapytania get i uzy- var sumaKontrolna = (10-suma) % 10;
skanie odpowiedzi od przygotowanego od-
powiednio skryptu PHP wydaje się właści- if (pesel[10]==sumaKontrolna) return true;
we. W podobny sposób można sprawdzić else return false;
adres e-mail i w czasie, gdy użytkownik wy- }
pełnia resztę formularza, możemy przesłać
e-mail do przygotowanego skryptu PHP, Listing 2. Formularz HTML z logowaniem asynchronicznym
który nie tylko sprawdzi poprawność adre- <form method="get" class="cmxform" id="form" action="form.php">
su pod względem budowy (ciąg znaków, li- <fieldset>
terka at, kolejny ciąg znaków), lecz także <p>
odpyta serwery DNS o rekordy MX czy na- <label for="login">Twój login</label>
wet system pocztowy o poprawność adresu <input id="user" name="user" title="Podaj login, przynajmniej trzy znaki"
e-mail. Aby jednak to zrobić, zapoznajmy się class="{required:true,minLength:3}" />
z funkcjonalnością zapytań asynchronicz- </p>
nych oferowaną przez jQuery. <p>
<label for="pass">Hasło</label>
GET I POST asynchronicznie <input type="password" name="password" id="password" class="{
To, co czyni z omawianej biblioteki uniwersal- required:true,minLength:5}" />
ną do różnych zastosowań, to możliwość wy- </p>
syłania asynchronicznych zapytań. Biblioteka <p>
ta ma gotowy zbiór funkcji wspierających za- <input class="submit" type="submit" value="Login"/>
pytania asynchroniczne i – co bardzo użytecz- </p>
ne – funkcje te jako jeden z parametrów przyj- </fieldset>
mują tzw. callback, a dokładnie nazwę funk- </form>
cji, która ma być wywołana na zakończenie

www.phpsolmag.org 61
Narzędzia

Taką funkcją jest: $.ajax(params); Parame-


Listing 3. Wywołania funkcji dokonujących walidacji try przekazywane tej funkcji wymagają do-
kładniejszego omówienia.
<script type="text/javascript"> Przede wszystkim forma przekazywanych
jQuery(function() { parametrów jest w postaci par klucz: „war-
tość”. Nie wszystkie parametry są obowiąz-
// show loading indicator kowe, a te, które nie będą ustawione, zostaną
zastąpione wartościami domyślnymi. Pierw-
var loader = jQuery('<div id="loader"><img src="images/loading.gif" szym niezbędnym parametrem jest URL. Na-
alt="Czekaj..." /></div>') zwa skryptu bądź strony, która zostanie wy-
.css({position: "relative", top: "1em", left: "25em"}) wołana, np. url: „test.php”, Kolejnym jest
.hide() „type”, w naszym przypadku upewnimy się,
.appendTo("body"); że będzie to typ domyślny, czyli „GET”, type:
jQuery().ajaxStart(function() { „GET”, następnie czas na przesłanie danych do
loader.show(); skryptu:
}).ajaxStop(function() {
loader.hide(); data: „name=PHPSolution&id=3/2007” §
}); (znany jako $QUERY_STRING)

jQuery().ajaxError(function(a, b, e) { następnie funkcja, która zostanie wywoła-


throw e; na, w przypadku wykonania skryptu z suk-
}); cesem
jQuery.validator.setDefaults({
debug: true success: callback_success,
});
Po ukończeniu zapytania, czyli wtedy kie-
var v = jQuery("#form").validate({ dy spłynęły już wszystkie dane i serwer za-
submitHandler: function(form) { mknął połączenie, zostanie wywołana in-
jQuery(form).ajaxSubmit({ na funkcja:
dataType: "json",
after: function(result) { complete: callback_complete,

if(result.status) { całość będzie wyglądała zgodnie z listin-


v.showErrors(result.data); giem. Wartymi zauważenia funkcjami z dzia-
v.focusInvalid(); łu „ajax” są:
}
} • $.ajaxStart(callback);
}); • $.ajaxStop(callback);
} • $.ajaxSend(callback);
}); • $.ajaxSuccess(callback);
jQuery("#reset").click(function() { • $.ajaxComplete(callback).
v.resetForm();
}); Wszystkie wymienione tu funkcje związu-
}); ją wybraną przez nas funkcję oznaczoną ja-
</script> ko „callback” z odpowiednim zdarzeniem.
IAjaxStart spowoduje, że będzie wykonywa-
Listing 4. Przykładowy plik php symulujący logowanie użytkownika na wybrana przez nas funkcja zawsze, kiedy bę-
<?php // oczekiwanie, w celu sumulacji opóźnień w przetwarzaniu I przesyłaniu dzie zaczynało wykonywać się jakieś zapytanie
// danych „ajax”. AjaxStop spowoduje, że wykona się od-
powiednia funkcja w chwili zakończenia zapyta-
sleep(1); nia typu „ajax”. Funkcja ajaxSend wykona przy-
$user = $_REQUEST['user']; pisaną funkcję przed każdym wykonaniem za-
$pw = $_REQUEST['password']; pytania „ajax”, ajaxSuccess odpowiednio po
każdym zapytaniu zakończonym sukcesem, a
if($user && $pw && $pw == "pass") ajaxComplete będzie wywoływał funkcję za-
echo "{'status': 0, 'data':'Cześć $user, Witamy ponownie.'}"; wsze, kiedy zakończą napływać dane z serwera.
Ponadto funkcja $.ajax wspiera zapyta-
else nia, które jako odpowiedź przesyłają dane w
następującej postaci: XML, HTML, script,
echo "{'status': 1, 'data': {'password': JSON. Kiedy już poznaliśmy funkcję odpo-
'Przykro mi, Twoje hasło jest błędne.'}} ; wiedzialną za zapytania asynchroniczne, mo-
?> żemy napisać kod aplikacji webowej, realizu-
jącej zaawansowaną walidację formularza. Ni-
kogo nie trzeba przekonywać, że korzystanie

62 05/2007
jQuery

z gotowych funkcji i tego, co już zostało napi- chronicznie do przetestowania poprawności da- Akcją wykonywaną przy wysłaniu formula-
sane, znacznie przyśpiesza tworzenie własnej nych. Kod formularza zawarty jest w Listingu 2. rza jest asynchroniczne wywołanie skryptu
aplikacji. Z tego właśnie powodu sięgniemy Kodem zawarty w Listingu 3. oprogramuje- PHP form.php, w którym możemy umieścić
po gotowe funkcje przygotowane do walida- my elementy formularza, wykorzystując funk- uwierzytelnienie użytkownika, i włączenie
cji formularzy, a dostarczone jako plugin jQu- cje dostarczane przez jQuery. sesji, tak by dalsze wędrowanie po stronie
ery o nazwie: validation. Pobieramy plik z ad- odbywało się w spersonalizowany sposób.
resu: http://jquery.bassistance.de/validate/jqu- Współpraca jQuery zPHP Dla potrzeb testów ograniczymy się do pro-
ery.validate.zip Po rozpakowaniu włączamy Umieszczamy w kodzie HTML potrzebne ele- stej symulacji prawdziwego logowania. Kod
w kod naszej strony następujące pliki: menty, takie jak: pliku form.php zawiera Listing 4.To, co wy-
W katalogu “js” umieszczamy następujące pli- różnia jQuery od innych podobnych biblio-
ki pomocnicze: cmxforms.js, jquery.js, form.js, <button id="reset">Programowy reset formy tek, to fakt, że wokół jQuery nabudowało
test.js, testrunner.js. Zaczniemy od przygotowa- </button> się wiele ciekawych i darmowych bibliotek,
nia formularza logowania, który wyślemy asyn- <div id="result">Oczekiwany rezultat</div> które rozszerzają i wzbogacają jego funkcjo-
nalność. Jednym z takich przykładów jest
jQPie, który:
Listing 5. Strona z „jQuery callendar”

<HTML><HEAD> • w prosty sposób wykonuje i przetwarza


<script type="text/javascript" src="http://code.jquery.com/jquery-latest.pack.js"> dane ze skryptów PHP, używając metody
</script> $.getJSON,
<style type="text/css">@import url(jquery-calendar.css);</style> • włącza wygenerowany przez PHP kod
<script type="text/javascript" src="jquery-calendar.js"></script> HTML przy pomocy funkcji $.(element).
<script type="text/javascript"> load,
$(document).ready(function () { • wywołuje funkcje PHP bezpośrednio ze
$('.calendarFocus').calendar(); strony przy pomocy funkcji $.jqpie,
popUpCal.prevText = '<< '; • wywołuje funkcje jQuery z pozio-
popUpCal.nextText = '>> '; mu PHP w odpowiedzi na wywołania
popUpCal.minDate = new Date(2005, 1 - 1, 1); $.jqpie.
popUpCal.maxDate = new Date(2008, 12 - 1, 31);
Innym bardzo dobrym pluginem, integrują-
}); cym PHP z jQuery, jest „PQuery". Jest do kla-
</script> sa w PHP, która umożliwia korzystanie na po-
</HEAD> ziomu skryptów php w funkcjonalności jQu-
<body> ery. Aby zainstalować i używać tego modułu,
musimy włączyć plik
<form>
<input type=”text” class="calendarFocus" id=”MyInputData” name=”testData1”> <script src="pq/js/jquery.js" type="text/
</form> javascript"></script>

<body> w kod strony a także w skrypcie php włączyć plik

Listing 6. Przykład efektu graficznego w jQuery include("pquery/pquery.php");


<HTML><HEAD>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.pack.js">
Listing 7. „Helo world” w jQuery
</script>
<script type="text/javascript"> <script>
$(document).ready(function () { $(document).ready(function() {
$("input").mousedown(function() { $("a").click(function() {
//alert(""); alert("Hello world!");
$(this).animate({ });
opacity: 'hide' });
}, "slow");}); </script>
//$("input").mouseout(function(){
// $(this).animate({ Listing 8. Przykład iteracji po elementach
// opacity: 'show' strony
// }, "slow");;}); $("#orderedlist > li").
}); addClass("blue");
</script> // Kolejnym ułatwieniem jest możliwość
</HEAD> // iteracji po wszystkich tak
<body> // wyselekcjonowanych obiektach.
<form> $("#orderedlist > li").
<input type=”text” class="calendarFocus" id=”MyInputData” name=”testData1”> each(function(i) {
</form> $(this).append( " Test " + i );
<body> });

www.phpsolmag.org 63
Narzędzia

sktyptu php, a jego odpowiedź zmodyfikuje-


Listing 9. Funkcja pomocnicza do dolidacji numeru PESEL element http o id='idtoupdate'. ***
function validate(data, jqForm, opts) {
var form = jqForm[0]; Efekty
var pesel = form.pesel.value; Silną stroną biblioteki jQuery są efekty graficzne
var vl= validPESEL(pesel); uzyskiwane za jej pomocą. Mimo że samo jądro
dostarcza tylko podstawową i bardzo ubogą funk-
if (!vl) cjonalność w tej dziedzinie, to jednak narosło bar-
{ dzo wiele pluginów i dodatków, które możemy
włączyć w nasz kod i cieszyć się graficznie i dy-
return confirm(„PESEL nie poprawny, czy mimo to wysłać dane?”) namicznie zaawansowanym zachowaniem naszej
} strony. Przykładem efektownego wykorzystania
możliwości omawianej biblioteki może być wy-
else return true; godny sposób wstawiania daty w wymagane po-
} le tekstowe, za pomocą kalendarza jQuerycallen-
dar. Aby zaimplementować tą funkcjonalność,
Listing 10. Zapytanie asynchroniczne get należy pobrać i włączyć w kod HTML oprócz pli-
$.get("test.php", ku jquery dodatkowo http://marcgrabanski.com/
{ name: "PHPSolution", id: "3/2007" }, code/jquery-calendar/jquery-calendar.css oraz http:
//marcgrabanski.com/code/jquery-calendar/jquery-
function(data){ calendar.js Następnie na wybranym elemencie „in-
$("#wynik").html(data); put”, wyselekcjonowanym w jQuery, wywołuje-
} my metodę callendar(). W zdarzeniu:

); $('.callendarFocus').calendar();
A w kodzie HTML umieszczamy formularz z
Listing 11. Przykładowy plik php, generujący odpowiedź dla zapytania asynchronicznego elementem
<?php <input type=”text” class=”callendarFocus” §
name=”testData”>
If(isset($_GET[‘name’] && $_GET[‘name’] === „PHPSolution” ) echo $_GET[‘name’].
” To dobry magazyn”; Do pełnego zadowolenia brakuje nam jeszcze
konfiguracji kalendarza, tak by domyślna war-
If(isset($_GET[‘id’] && $_GET[‘id’] === „3/2007”) echo $_GET[‘id’]. tość była tą, którą chcemy widzieć w danym
” To ciekawy numer”; miejscu, i format też był odpowiedni.
JQuery ma zaimplementowanych wiele róż-
?> nych funkcji odpowiedzialnych za wizualne
efekty i animacje elementów strony. Prostym
Listing 12. Przykładowe zapytanie ajax przykładem może być tutaj stopniowe nada-
$.ajax( wanie przezroczystości dowolnemu elemento-
{ wi strony. Kod w Listingu 6. pokazuje, jak po
url: „test.php”, ustawieniu kursora w polu tekstowym formu-
data: „name=PHPSolution&id=3/2007”, larza sprawić, by pole to zniknęło.
type: „GET”,
success: function(data){ Podsumowanie
$("#wynik").html(data); jQuery daje możliwość zarówno budowania szyb-
}, kich i sprawnych efektów graficznych, jak i łatwe-
go generowania zapytań asynchronicznych i mo-
complete: callback_complete, dyfikacji treści strony po stronie przeglądarki.
}); Przytoczone tu przykłady stanowią cząstkę
możliwości jakie daje ta biblioteka. Celem te-
Listing 13. Włączenie potrzebnych plików js dla validatora jQuery go artykułu było jednak zapoznanie czytelni-
<script src="js/jquery.js" type="text/javascript"></script> ka z funkcjonowaniem i sposobem użycia tej bi-
<script src="js/cmxforms.js" type="text/javascript"></script> blioteki na własnej stronie i mamy nadzieję, że
<script src="jquery.metadata.js" type="text/javascript"></script> cel ten został osiągnięty. Mocną stroną bibliote-
<script src="jquery.validate.js" type="text/javascript"></script> ki jest mnogość pluginów i szeroka rzesza pro-
gramistów gotowa wesprzeć technicznie kiedy
mamy jakiś problem, tak więc warto wypróbo-
Po stworzeniu instancji klasy $pquery = new Podaj tekst : wać to co nam ta biblioteka oferuje.
PQuery(); możemy używać własności jQuery
w skrypcie php. <input type="text" name="field" /><br />
<input type="submit" /> </form> DARIUSZ DUSZYŃSKI
<?=$pquery->form_remote_tag( array Autor jest programistą w toruńskiej firmie JADE
('url'=>'index.php?task=ajax', Wartość wpisana do pola tekstowego zostanie Sp. z o.o.
'update'=>'#idtoupdate'));?> przekazana zapytaniem asynchronicznym do Kontakt z autorem: dduszynski@jade.pl

64 05/2007
Narzędzia

WordPress i punBB
Integrowanie

Seria artykułów „Warsztat Programisty” ma na celu przedstawienie rozwiązań


ciekawych i nietypowych problemów, o których nie piszą w podręcznikach.
Dowiesz się np., jak zintegrować użytkowników dwóch niezależnych skryptów, jak
stworzyć zaawansowany parser tagów i wiele więcej.

nieść kod integracji z kodu CMS-a do wtycz-


Dowiesz się... Powinieneś wiedzieć... ki. Zaprezentowana wtyczka pisana była z
• Jak rozwiązywć różne programistyczne pro- • Znajomość podstaw PHP i HTML. myślą o serwisie jakilinux.org – instalacji
blemy. Wordpressa z różnymi wtyczkami i istnie-
jącą pulą użytkowników. Wtyczka musia-
ła uwzględniać istniejących użytkowników
cy niegdyś jPortal Entropia zintegrowany z (po stronie Wordpressa i punBB), ingerencja
IPB). W naszym przypadku będzie to opcja w kod CMSa odpadała – znacznie utrudnia
Poziom trudności druga lub trzecia. Celem jest integracja, któ- to aktualizację, a także fakt iż Wordpress
ra nie wprowadza dużych zmian w kodzie haszuje hasła za pomocą MD5, a punBB za
skryptów, tak by nie było problemów z in- pomocą SHA1. Hasze haseł do synchroni-
stalacją rozszerzeń czy aktualizacją. PunBB zowanej tabeli użytkowników punBB moż-

W
ordpress to znany system CMS/ oferuje swój mechanizm do integracji, lecz na tworzyć jedynie przy logowaniu, gdy API
Blog, a punBB to popularne i bar- idealny nie jest i próba zastosowania go z wtyczek Wordpressa przekazuje do wtyczki
dzo funkcjonalne forum dysku- WordPressem skończyłaby się kolizją kodu hasło a nie jego hasz.
syjne. WordPress jest stosowany we wszyst- i błędnym działaniem CMS-a. Musimy więc
kich serwisach grupy jakilinux.org (jakili- niezależnym od punBB kodem zintegrować Integracja – wersja II
nux.org, polishlinux.org, wolnakultura.info i oba skrypty, modyfikując kod Wordpressa W katalogu WordPressu /wp-content/plugins/
inne), natomiast punBB jest wykorzystywa- (jednostronna integracja) – Listing 1. stwórz katalog jl-punbb, a w nim plik
ne jako forum dyskusyjne na jakilinux.org (w jl-punbb.php o kodzie przedstawionym na Li-
przyszłości także w innych serwisach). Ce- Integracja – wersja I stingu 2.
lem jest zintegrowanie systemu użytkowni- Dla tabeli użytkowników punBB dodajemy no- Teraz w Panelu Admina WordPressu
ków obu skryptów, tak by czynności jak lo- wą kolumnę „wp_id”, która przechowuje nu- w zakładce Plugins włącz wtyczkę i goto-
gowanie, rejestracja, wylogowanie, zmiana ha- mer ID danego użytkownika z tabeli użytkow- we. Dodatkowo nie wymaga ona edycji
sła i przypomnienie hasła wpływały na oba ników WordPressa. Kod SQL wygląda nastę- tabeli punBB.
skrypty. Integracje tego typu możemy prze- pująco:
prowadzać na kilka sposobów: Synchronizacja użytkowników
ALTER TABLE `pun_users` ADD `wp_id` Powyższa wtyczka zajmuje się rejestracją,
• zastąpić system użytkowników i upraw- INT UNSIGNED NOT NULL DEFAULT 0; logowaniem, wylogowaniem i zmianą has-
nień jednego skryptu systemem z dru- ła dla użytkowników, którzy zarejestrowa-
giego; Oczywiście prefiks „pun _ ” do tabel punBB li się w WP po jej zainstalowaniu. W punBB
• dokonać krzyżowej integracji (czynności może być inny i w takim przypadku edytu- nie będziemy mieć dostępu do istniejące-
jak logowanie wykonuje identyczną czyn- jemy nazwy tabel w zapytaniach z prezento- go konta admina. Musimy zsynchronizo-
ność dla drugiego skryptu); wanych w tym artykule fragmentów kodu. wać tabelę użytkowników punBB z tabelą
• dokonać jednostronnej integracji (jeden Zaprezentowane na listingu 1 zmiany skut- użytkowników WordPressu. Usuwamy ist-
skrypt przy np. logowaniu loguje również kują tym, że rejestracja, logowanie, wylogo- niejących użytkowników (poza „Gościem”
do drugiego skryptu). wanie, resetowanie hasła i zmiana hasła w DELETE FROM pun_users WHERE id > 1; Te-
WordPressie spowoduje dokładnie to samo raz musimy pobrać wszystkich użytkowni-
Pierwszy przypadek jest dość trudny do w punBB. Integracja jest skończona, chociaż ków z tabeli Wordpressu i w pętli dodać je
realizacji jeżeli system użytkowników i ma jedną wadę – wprowadziliśmy modyfi- do tabeli punBB według zapytania z funk-
uprawnień jest rozbudowany. JPortal ma od- kacje do kodu WordPressu, co utrudni jego cji wtyczki punbb_user_register. W tabe-
dzielonych zwykłych użytkowników od ad- aktualizację. Skrypt ten jednak posiada sys- li użytkowników punBB kolumna group_id
minów, przez co łatwo dla tego skryptu do- tem wtyczek – „plugins”. API tego systemu określa przynależność do grupy. Dla admi-
konać integracji typu pierwszego (istnieją- jest na tyle rozbudowane, że możemy prze- nistratora Wordpressu nadajemy wartość 1,

66 05/2007
WordPress i punBB

Listing 1. Integracja skryptów


// Poniżej wstaw:
// Otwórz plik wp-login.php i znajdź: /*
switch ($action) { punBB – zachowujemy czyste hasło, punbb używa sha
case 'logout' : */

// punbb wylogowanie $user_pass2 = $user_pass;

include '../punbb/config.php'; /* */
setcookie($cookie_name, NULL, time()-3600, '/', '', '0'); // Następnie znajdź:

// Następnie znajdź if ( $update ) {


$query = "UPDATE $wpdb->users SET user_pass=
do_action('password_reset'); '$user_pass', user_email='$user_email',
user_url='$user_url', user_nicename =
// Generate something random for a password... '$user_nicename', display_name = '$display_name'
// md5'ing current time with a rand salt WHERE ID = '$ID'";
$query = apply_filters('update_user_query', $query);
$new_pass = substr( md5( uniqid( microtime() ) ), 0, 7); $wpdb->query( $query );
$wpdb->query("UPDATE $wpdb->users SET user_pass = MD5(
'$new_pass'), user_activation_key = '' WHERE user_login = // Poniżej wstaw:
'$user->user_login'"); // punBB – aktualizacja emaila

// Poniżej wstaw $wpdb->query("UPDATE pun_users SET email='".$user_email."'


/* WHERE wp_id = ".$ID."");
punBB – resetowanie hasła
*/ /* */
// Następnie znajdź:
$wpdb->query("UPDATE pun_users SET password='".sha1(
$new_pass)."' WHERE username = ".$user->user_login.""); $query = apply_filters('create_user_query', $query);
$wpdb->query( $query );
/* */ $user_id = (int) $wpdb->insert_id;
// Następnie znajdź: Poniżej wstaw:

if ( wp_login($user_login, $user_pass, $using_cookie) ) { // punBB – rejestracja


if ( !$using_cookie )
wp_setcookie($user_login, $user_pass, false, '', '', $wpdb->query('INSERT INTO pun_users (username, group_id,
$rememberme); password, email, email_setting, save_pass, timezone,
language, style, registered, registration_ip, last_visit,
// Poniżej wstaw: wp_id) VALUES(\''.$user_login.'\', 4, \''.sha1(
/* $user_pass2).'\', \''.$user_email.'\', 1, 1, 1 ,
punBB – logowanie usera \'Polish\', \'Oxygen\', '.time().', \''.strip_tags(
*/ $_SERVER['REMOTE_ADDR']).'\', '.time().', '.
$user_id.')');
$user = $wpdb->get_row("SELECT id FROM pun_users WHERE username
= '".mysql_real_escape_string($user_login)."' LIMIT 1"); /* */
include '../punbb/config.php'; // Następnie znajdź:
setcookie($cookie_name, serialize(array($user->id, md5( // If password is changing, hash it now.
$cookie_seed.sha1($user_pass)))), time() + 31536000,
$cookie_path, $cookie_domain, $cookie_secure, true); if ( ! empty($userdata['user_pass']) ) {
$plaintext_pass = $userdata['user_pass'];
/* */ $userdata['user_pass'] = md5($userdata['user_pass']);
// Otwórz plik wp-includes/registration.php i znajdź:
// Are we updating or creating? // Poniżej wstaw:
// punBB – zmiana hasła
if ( !empty($ID) ) {
$ID = (int) $ID; $wpdb->query("UPDATE pun_users SET password='".sha1(
$update = true; $plaintext_pass)."' WHERE wp_id = ".$ID."");
} else {
$update = false; /* */

// Password is not hashed when creating new user.

www.phpsolmag.org 67
Narzędzia

czyli prawa admina forum. A co jeśli inte- le. Do kodu wtyczki wystarczy dodać kod Nie tylko punBB
grujemy forum zawierające już wielu użyt- z Listingu 3. Zaprezentowane rozwiązanie nie ogranicza się do
kowników i tematy/posty? Powyższy sposób Ostatni etap integracji to usunięcie odno- punBB. Taką samą metodę możemy zastosować
synchronizacji spowoduje sporo zamieszania śników w punBB do logowania i rejestracji do integrowania skryptów innego typu. W niektó-
w przynależności wiadomości do określonego lub ustawienie przekierowań na odpowied- rych przypadkach, gdy skrypt udostępnia API do
autora. Musimy zastosować nieco inne podej- nie strony w WordPressie: Na początku pliku integracji zadanie będziemy mieć ułatwione. Dla
ście. Nie usuwamy użytkowników. Ci, którzy register.php forum wstaw: header('Location: przykładu dla forum Invision Power Board istnie-
mieli odmienne loginy, stracą dotychczasowe http://www.url/do/wordpress/wp-login. je projekt IPB SDK umożliwiający szeroką integra-
konta na punBB. Dodajemy do tabeli punBB php?action=register'); Dla pliku login.php cję a także wykorzystywanie komponentów forum.
tych użytkowników WP, których nie ma na na początku wstaw: IPB SDK możemy pobrać ze strony http://www.ipb
forum. By pobrać takich użytkowników, wy- sdk.sourceforge.net. Dzięki tej klasie w prosty sposób
starczy zapytanie: header('Location: http://www.url/do/ otrzymamy dostęp do systemu logowania i użyt-
wordpress/wp-login.php'); kowników, właściwości forum takich jak BBco-
SELECT * FROM wp_users WHERE user_login de, ankiety, szukanie, posty, tematy i wiele wię-
NOT IN (SELECT username W profilu musimy wyłączyć zmianę hasła cej. By używać IPB SDK musimy mieć odpowied-
FROM pun_users) – edytuj profile.php i znajdź: nią wersję forum jak i klasy SDK – 1.6 dla IPB 2.1.*
lub 1.5 dla IPB 2.0.*. Gdy już wszystko gotowe nale-
Dodajemy według zapytania z funk- if ($action == 'change_pass') ży ustawić ścieżkę do katalogu z forum IPB w pliku
cji wtyczki punbb_user_register, a następ- { ipbsdk_conf.inc.php ( $root_path i $board_url).
nie nadajemy prawa admina na forum ad- Teraz możemy korzystać z IPB SDK. Zaczynamy
minowi z WP. Cały ten bałagan migracji Poniżej dodaj: od stworzenia obiektu klasy:
rozwiąże nasza wtyczka do Wordpressu.
Możemy dodać kod, który zostanie wykona- header('Location: http://www.url/do/ require_once 'ipbsdk_class.inc.php';
ny przy jej aktywacji i zsynchronizuje tabe- wordpress/wp-admin/profile.php'); $SDK =& new IPBSDK();

Listing 2. Prosta wtyczka zapewniająca integrację z punBB

<?php $wpuser = $wpdb->get_row("SELECT user_pass FROM ".


$wpdb->users." WHERE user_login =
/* '".mysql_real_escape_string($user_login)."' LIMIT 1");
Plugin Name: punBB integrator include '../punbb/config.php';
Plugin URI: http://www.rkblog.rk.edu.pl
Description: Allows Wordpress to manager punBB users IF($user->password == 'BRAK' and md5($user_pass) ==
- login/logout/register/password change etc. $wpuser->user_pass OR sha1($user_pass) !=
Version: 0.0.1 $user->password) {
Author: Riklaunim $wpdb->query("UPDATE pun_users SET password=
Author URI: http://www.rkblog.rk.edu.pl '".sha1($user_pass)."' WHERE username =
*/ '".mysql_real_escape_string($user_login)."'");
}
add_action ('profile_update', 'punbb_profile_update');
add_action ('wp_logout', 'punbb_wp_logout'); setcookie($cookie_name, serialize(array($user->id,
add_action ('wp_authenticate', 'punbb_wp_authenticate', 1, 2); md5($cookie_seed.sha1($user_pass)))), time() +
add_action ('user_register', 'punbb_user_register'); 31536000, $cookie_path, $cookie_domain,
$cookie_secure, true);
function punbb_profile_update($id) { }
global $wpdb;
$wpuser = $wpdb->get_row("SELECT user_login, user_email function punbb_user_register($id) {
FROM ".$wpdb->users." WHERE ID = ".$id." LIMIT 1"); global $wpdb;
$wpdb->query("UPDATE pun_users SET email='".$wpuser-> $wpuser = $wpdb->get_row("SELECT * FROM ".$wpdb->users."
user_email."' WHERE username = ".$wpuser-> WHERE ID = ".$id." LIMIT 1");
user_login.""); $wpdb->query('INSERT INTO pun_users (username,
} group_id, password, email, email_setting, save_pass,
timezone, language, style, registered,
function punbb_wp_logout() { registration_ip, last_visit) VALUES(\''.$wpuser->
include '../punbb/config.php'; user_login.'\', 4, \'BRAK\', \''.$wpuser->
setcookie($cookie_name, NULL, time()-3600, '/', '', '0'); user_email.'\', 1, 1, 1 , \'Polish\', \'Oxygen\
} ', '.time().', \''.strip_tags($_SERVER[
'REMOTE_ADDR']).'\', '.time().')');
function punbb_wp_authenticate($user_login, $user_pass) { }
global $wpdb;
$user = $wpdb->get_row("SELECT id, password FROM pun_users ?>
WHERE username = '".mysql_real_escape_string(
$user_login)."' LIMIT 1");

68 05/2007
WordPress i punBB

Po czym możemy dowolnie wykorzystywać


Listing 3. Kod wtyczki WP synchronizujący użytkowników API klasy w naszych skryptach. Oto prosty
przykład:
add_action('activate_jl-punbb/jl-punbb.php', 'punbb_sync_tables');
if ($SDK->is_loggedin()) {
function punbb_sync_tables() { echo 'jesteś zalogowany';
global $wpdb; } else {echo 'jesteś niezalogowany';}

// copy users from WP to punBB that doesn't have account on punBB By zalogować użytkownika wystarczy użyć
metody login:
$q = $wpdb->get_results("SELECT * FROM wp2_users WHERE user_login NOT IN (
SELECT username FROM pun_users)"); $sdk->login($LOGIN. $HASŁO);

foreach($q as $u) Wylogowanie to metoda logout:


{
IF($u->ID == 1) $sdk->logout();
{
$gid = 1; Za stworzenie nowego użytkownika na forum
} else { odpowiada metoda create_account:
$gid = 4;
} $sdk->create_account(LOGIN, HASŁO, EMAIL);

$wpdb->query('INSERT INTO pun_users (username, group_id, password, email, Podsumowanie


email_setting, save_pass, timezone, language, style, registered, Integrowanie skryptów wymaga dość dobrej
registration_ip, last_visit) VALUES(\''.$u->user_login.'\', '.$gid.', znajomości ich kodu oraz starannego rozplano-
\'BRAK\', \''.$u->user_email.'\', 1, 1, 1 , \'Polish\', \'Oxygen\', wania integracji. Niektóre skrypty, np. Word-
'.time().', \''.strip_tags($_SERVER['REMOTE_ADDR']).'\', '.time().')'); Press, łatwo rozszerzać, dodając kod integra-
} cji jako wtyczkę. Inne, np. forum IPB, posia-
dają odpowiednie narzędzia do integracji (IPB
// turn off emails for "dectivated" accounts on forum SDK). Mimo ułatwień integracja rozbudowa-
nych skryptów jest prawdziwym wyzwaniem
$wpdb->query('UPDATE pun_users SET email_setting = 2 WHERE username NOT IN ( dla programisty, testującym jego inteligencję i
SELECT user_login FROM wp_users) AND id > 1'); zdolność wyszukiwania informacji.

}
PIOTR MALIŃSKI
Autor jest studentem Politechniki Warszawskiej
na kierunku technologia chemiczna, a także pro-
gramistą PHP i Python.
Kontakt z autorem: riklaunim@gmail.com

www.phpsolmag.org
Testy konsumenckie

Testy konsumenckie
Statystyki zewnętrzne

W testach konsumenckich poruszamy tematykę statystyk zewnętrznych. Ponizej


prezentujemy wypowiedzi osób, które korzystają z usług firm świadczących
takie rozwiązanie.

cyjnym interfejsem. Funkcjonalność serwi- technologii DHTML i Flash. Większość wykre-


sów 7point i stat24 była bardzo podobna. sów posiada ładne animacje, co nie koliduje jed-
Podobne zestawienia, analizy, funkcje raportu- nak w żaden sposób z podstawowym przezna-
jące – wszystko to uporządkowane w intuicyj- czeniem, czyli prezentacją danych. Przydatnym
ne kategorie. Porównując wygląd obu syste- w praktyce rozwiązaniem jest możliwość ekspor-
mów, odnieśliśmy jednak wrażenie, że inter- tu oglądanych danych do formatu MS Excel. Jest
fejs stat24 – przez to, że został wykonany cał- też mechanizm generujący i wysyłający na poda-
kowicie w technologii Flash – jest mniej przej- ny e-maila zdalne, okresowe raporty tworzonych
rzysty i czytelny dla użytkownika. Z naszej według indywidualnych upodobań.
analizy cen usług wynikało również jedno- 7point oferuje również ciekawy program
znacznie, że znacznie tańsze rozwiązania ofe- partnerski, z którego aktywnie korzystamy.
ruje serwis 7point, jednak ze względu na do- Dzięki niemu mamy możliwość nadzorowania
syć dużą popularność stat24 postanowiliśmy przypisanych kont statystyk poprzez równie
wypróbować również i tę usługę. czytelny co w samych statystykach panel za-
Po rozpoczęciu prac implementujących wy- rządzający. Możemy rejestrować nowych klien-
magane skrypty do naszego CMS-a okazało się, tów i rejestrować dla nich usługi. Korzysta-
że system stat24 posiada pewne ograniczenie, jąc z tej formy zakupu, mamy możliwość uzy-
a mianowicie ilość skryptów zliczających, czyli skania atrakcyjnych rabatów lub prowizji na
ilość monitorowanych stron serwisu, jest ogra- usługi 7point. Bardzo dobrym rozwiązaniem
Artur Undro
niczona w tańszych wersjach statystyk. Ogra- z naszego punktu widzenia jest też możliwość
eCoder.pl niczenia tego nie miały statystyki oferowane wyboru, czy rozliczenie z klientem nastąpi
przez 7point nawet w wersji najtańszej. To osta- w formie odsprzedaży usługi przez naszą fir-
eCoder to młoda firma tworząca rozbudowa- tecznie wpłynęło na naszą decyzję. mę, czy też umowa sprzedaży zostanie za-
ne pod względem funkcjonalnym i techno- Dużym plusem dla 7point okazała się też warta bezpośrednio pomiędzy 7point a klien-
logicznym serwisy WWW. Stawiamy na mo- możliwość zdalnego przekazywania nazw po- tem końcowym. Niezależnie od wybranej for-
del usługi firma dla firmy z terenem działania szczególnych stron monitorowanego serwisu my nam, jako uczestnikowi programu part-
obejmującym cała Polskę. Nasze usługi opiera- do serwera kolekcjonującego dane o odwiedzi- nerskiego, przysługuje bardzo atrakcyjny rabat
my na autorskim systemie CMS, który cieszy nach. Osoba obsługująca dodawanie i edycję albo prowizja od sprzedaży.
się już od ponad 2 lat dużym uznaniem wśród kolejnych stron z poziomu CMS-a nie musi Od początku współpracy zarejestrowaliśmy
klientów. zmieniać i definiować tych nazw po raz drugi w już kilka kont, z których korzystają nasi klien-
Na początku tego roku podjęliśmy decyzję systemie statystyk. Wszystko odbywa się auto- ci. Jak do tej pory nie mieliśmy żadnych zgło-
o zaimplementowaniu do naszego CMS-a ze- matycznie w sposób niewidoczny zarówno dla szeń świadczących o nieprawidłowym działa-
wnętrznego systemu statystyk. Ustaliliśmy czte- redaktora strony, jak i dla internauty. Dzięki te- niu czy jakichkolwiek błędach w serwisie 7po-
ry główne kryteria, jakie system taki musi speł- mu przeglądając statystyki redaktor widzi zna- int. Otrzymujemy natomiast sygnały od klien-
nić: obsługa statystyk w języku polskim, łatwość jome nazwy, jakie występują na stronie, zamiast tów o tym, że wreszcie doczekali się systemu
implementacji skryptów usługodawcy do nasze- niewiele mówiących adresów internetowych. statystyk z prawdziwego zdarzenia.
go kodu, duża gama prezentowanych danych Minusem jest to, że funkcja ta nie jest dostępna Osobiście jestem zdania, że wybór 7point był
wynikowych statystyk oraz końcowa cena, ja- w najtańszej wersji statystyk. bardzo dobrą decyzją. Jestem zadowolony z do-
ką będzie musiał za usługę zapłacić nasz klient. Kilka słów od strony użytkownika statystyk. tychczasowej współpracy i polecam ich usłu-
Poszukiwania prowadziliśmy, przeczesując za- Możemy wybrać dowolny przedział czasowy, gi firmom, które zastanawiają się nad profesjo-
soby Internetu. Po kilku dniach pod uwagę w jakim dane będą nam prezentowane. Wszyst- nalnym systemem statystyk dla własnej strony
braliśmy trzy firmy: 7point (wówczas jeszcze ko jest posegregowane w jasno nazwane katego- WWW czy też jako moduł/dodatek do autor-
pod nazwą NeoStat), MyStat oraz stat24. rie i podkategorie, dzięki czemu łatwo odna- skich rozwiązań IT.
MyStat odrzuciliśmy jako system mało roz- leźć potrzebne informacje. Dane są przejrzyście
wijany, z licznymi ograniczeniami i nieatrak- prezentowane dzięki subtelnemu mieszaniu Ocena: «««««

70 05/2007
Statystyki zewnętrzne

w systemie funkcji oraz tabelaryczne zestawienie cji poszukiwali oraz skąd pochodzili. Zebrane
możliwych opcji dla każdego pakietu. w ten sposób dane pozwalają odpowiedzieć na
Z usługi 7point korzystamy od kilku miesię- pytanie, w jakim stopniu przygotowana przez
cy i stale utwierdzamy się w przekonaniu, że nas oferta jest dobrana do ich potrzeb.
był to najlepszy wybór. Opinię podzielają rów- Wiedzę tę poszerzają wykresy wyświetlają-
nież nasi partnerzy, którym polecamy produkt ce informacje o odbiciach, nowych i powraca-
i wdrażamy w realizowanych projektach. jących użytkownikach, stronach wejściowych
Statystyki stosujemy głównie do oceny po- i wyjściowych. Natomiast informacje technicz-
pularności serwisu oraz analizy skuteczności ne, takie jak stosowane przeglądarki, ustawie-
prowadzonych działań marketingowych. Wy- nia rozdzielczości ekranów, pomagają nam
soka precyzja prezentacji wyników pomaga w definiowaniu standardów, wykorzystywa-
w określeniu zachowań i preferencji użytkowni- nych przez internautów.
ków odwiedzających naszą stronę. Dzięki temu Dodatkowym atutem są tygodniowe rapor-
szybciej docieramy do ich potrzeb i pod tym ką- ty przesyłane na podany adres e-mailowy, na pod-
tem optymalizujemy witrynę, jej zawartość me- stawie których możemy analizować zmiany w za-
rytoryczną, ofertę, a także użyteczność. chowaniach internautów i tworzyć archiwum.
Emilia Adamczyk
Ze względu na to, że prowadzimy intensyw- Przyjemnym dla oka dodatkiem są wykresy
właściciel, specjalista ds. e–biznesu
ne kampanie promocyjne w wyszukiwarkach, 3D, a miły kontakt z pracownikami firmy, na
e–solution agencja interaktywna najbardziej istotnymi danymi prezentowanymi których pomoc zawsze można liczyć, jest waż-
w systemie są odsłony serwisu według słów klu- nym uzupełnieniem całej usługi. Firma 7point
czowych, które użytkownicy wpisują w wyszu- otwarta jest na nowe pomysły i propozycje part-
O statystykach 7point dowiedzieliśmy się z prze- kiwarki internetowe. Dają nam one pełen obraz nerów, które są rozważane i wdrażane do syste-
słanej do nas propozycji współpracy. Przeanalizo- w określeniu ich trafności. mu. Dzięki temu jest on stale rozwijany, oferu-
waliśmy wszystkie dostępne na rynku oferty i zde- Dużą zaletą jest również gromadzenie infor- jąc nowe funkcje przydatne użytkownikom.
cydowaliśmy się na korzystanie z tego właśnie pro- macji na temat kolejności wyświetlania pod- Statystyki 7point posiadają dużo więcej opcji
duktu. Przekonała nas przede wszystkim bogata stron serwisu, czyli ścieżek, po których wędro- przydatnych właścicielom witryn, specjalistom ds.
funkcjonalność oraz intuicyjna nawigacja, której wali internauci, a także szczegóły ich wizyt. Sta- marketingu, programistom, dlatego powinny być
brakowało konkurencyjnym rozwiązaniom. Po- tystyki 7point pokazują, w jaki sposób użyt- nieodłącznym elementem każdej strony WWW,
zytywne wrażenie zrobiła na nas również szybka kownicy wchodzą na stronę (bezpośrednio, która ma przynosić korzyści, a nie tylko być.
i profesjonalna pomoc administratora, a także czy- z wyszukiwarki, z katalogu czy innego adresu),
telny, szczegółowy opis wszystkich dostępnych ile czasu na niej spędzili, jakiego typu informa- Ocena: «««««

Problem zaczyna się w momencie, kiedy to Dzisiaj trzeba rozwijać swoją ofertę i pamiętać,
nie sam użytkownik, ale jego klient oczeku- że nie wszędzie Internet dostarczany jest z pręd-
je jasnych, rzetelnych i dobrze zaprezentowa- kością powyżej 1 Mbit/s. Nieatrakcyjny wygląd
nych informacji dotyczących jego strony inter- bądź ciągnące się w nieskończoność oczekiwanie
netowej (w przypadku sklepów temat jest jesz- na otwarcie strony na słabym EDGE-u (w Bielsku-
cze poważniejszy). Białej nie brakuje takich miejsc) odbiera chęć dal-
Nasza firma tworzy m.in. serwisy i sklepy in- szych poszukiwań.
ternetowe, a zatem to, co w końcowym efekcie Moja opinia przypomina tekst sponsorowany,
zobaczy nasz klient i jakie korzyści będzie czer- ale od momentu rozpoczęcia współpracy z 7point
pał ze współpracy z nami, staje się sprawą najważ- nasi klienci nie zgłosili praktycznie żadnych uwag
niejszą. Po wpisaniu w wyszukiwarce hasła „sta- co do jakości oraz rzetelności przedstawianych
Paweł Puzoń tystyki WWW” zwróciłem uwagę na kilka adre- danych. To komfort, który bardzo doceniam!
sów. Wśród nich był link sponsorowany statystyk Dodatkwe plusy? Oto kilka z nich: usługa
Statystyki internetowe. Niektórzy się zastana- 7point. Link sponsorowany w potocznej opinii umożliwia określenie dowolnego zakresu czasu
wiają: czy ktoś z tego jeszcze korzysta? Oczy- oznacza „są drodzy, bo ich stać”. Sam nie wiem, podczas odczytu, a prezentacja danych jest bar-
wiście tak! Łatwo sobie wyobrazić firmę, któ- dlaczego, ale tak jest. Początkowo poświęciłem im dzo przejrzysta. Ponadto 7point oferuje szeroki
ra wydaje miesięcznie kilkaset złotych (czasami trochę czasu tylko dlatego, że firma znajduje się w wybór miesięcznych limitów odsłon (łatwo do-
kilka tysięcy!) na pozycjonowanie swojej witry- moim mieście – Bielsku-Białej. Po analizie i omó- stosować do potrzeby konkretnej witryny). Wy-
ny w Google. Jak najprościej wykazać, że wyda- wieniu strony ze znajomymi doszedłem do wnio- kresy też są nieźle wykonane.
wane środki dają efekty, a zwiększająca się licz- sku, że warto. Kilka przykładów: 7point oferuje Ostatnia sprawa to promocje – ważne w na-
ba telefonów w firmie to nie efekt dobrej ko- dosyć duży wybór parametrów (ma też dobrze szym przypadku (firma proponuje upusty dla
niunktury, ale cierpliwej i skutecznej pracy in- wykonany podział na kategorie). W porównaniu z stałych klientów oraz program partnerski).
formatyka? konkurencją cena za ich usługi jest atrakcyjna. Co Żeby nie było zbyt słodko są i minusy. Przy-
Nasza firma często spotyka się z tego typu ważne, system nie ogranicza ilości zbieranych da- kład? Ostatnio firma zmieniła swoją nazwę
problemami i poniżej podzielę się swoimi spo- nych, a do jednego konta można „podpiąć” kilka i przebudowała główny układ witryny. Ponieważ
strzeżeniami. domen. Na początek wystarczy. istnieją od niedawna, takie zmiany wprowadza-
Na polskim rynku odnaleźć można jedynie Oczywiście na rynku są inne firmy i warto ją zamieszanie. Dla stałych bywalców to norma,
kilka ciekawych rozwiązać spośród firm ofe- o nich pamiętać. Wystarczy wspomnieć moc- ale ciekawscy dyrektorzy marketingu lub preze-
rujących profesjonalne statystyki WWW. Wy- no reklamowany stat24 oraz zapomniany już si firm nie przepadają za takimi zmianami.
bór jest więc mocno zawężony a co za tym idzie przeze mnie (kiedyś właściwie jedyny dostęp-
– łatwiejszy. ny, teraz wyglądający nieco archaicznie) MyStat. Ocena: «««««

www.phpsolmag.org 71
Testy konsumenckie

ną ilość subkont, aby móc udostępnić statystyki rolę odgrywają dane dotyczące ścieżek jakimi po-
swojej witryny innym osobom. Może także two- ruszają się po stronie użytkownicy – dzięki takim
rzyć określoną ilość niezależnych stref, by przeglą- informacjom właściciel witryny może prześle-
dać statystyki stron swojego serwisu w prostszy dzić zestawienie najbardziej typowych schema-
i wygodniejszy sposób. Dodatkowo system 7point tów poruszania się użytkowników po serwisie.
umożliwia monitorowanie większej liczby domen Ponadto istotną funkcjonalnością jest możliwość
Ustosunkowanie prezesa w ramach jednego konta. Warto również zwrócić wyboru dowolnego zakresu czasu prezentowa-
Rafał Wawak uwagę na fakt, że nasz system liczy również aliasy/ nych statystyk – dzięki tej funkcji użytkownik
mirrory domen bez konieczności ich rejestrowa- może przeglądać dane z dowolnego dnia, miesią-
W 2005 roku firma Lege Artis rozpoczęła prace nia (o ile zawartość ich stron nie różni się od za- ca lub roku. System 7point dostarcza również in-
nad nowym systemem statystyk internetowych. wartości stron zarejestrowanej domeny głównej). formacji technicznych o komputerze internau-
Zespół naszych programistów przez rok przygo- Kolejną, jedną z najistotniejszych dla nas ty (system, przeglądarka, rozdzielczość ekra-
towywał mechanizmy nowego systemu. W mar- kwestii, było stworzenie systemu, który dostar- nu itp.). Dodatkowo warto wspomnieć o jednej
cu 2006 roku uruchomiony został system staty- czałby kompleksowych informacji o serwisie je- z istotnych funkcji naszego serwisu jaką jest, cie-
styk internetowych pod domeną NeoStat.pl, któ- go użytkownikowi, tak aby w efekcie informa- sząca się dużym zainteresowaniem wśród na-
ry był zarazem projektem pilotażowym. Od maja cje te znajdowały swoje zastosowanie w prak- szych klientów, możliwość otrzymywania bez-
tego roku możemy już zaoferować naszym klien- tycznych działaniach naszych klientów. Sta- płatnych tygodniowych raportów, które zawie-
tom właściwą wersję statystyk – system 7point. raliśmy się, aby gama dostępnych danych by- rają zestawienie najistotniejszych statystyk wraz
Mamy nadzieję, że cały czas i praca, które zostały ła jak największa. Dzięki dostarczanym przez z tendencjami.
włożone w stworzenie systemu, przyniosą przede nas statystykom klient może więc zbadać lojal- Już od początku tworzenia systemu wiedzie-
wszystkim satysfakcję klientom, a nam pewność, ność użytkowników wobec swojego serwisu, je- liśmy, że nasi klienci powinni być w pełni usa-
że dostarczamy w pełni profesjonalnych usług. go jakość i atrakcyjność oraz sprawdzić jak jego tysfakcjonowani nie tylko oferowanym przez
Przy opracowywaniu systemu kierowaliśmy się serwis funkcjonuje. Ponadto klient ma dostęp nas produktem, lecz także całością świadczo-
kilkoma, istotnymi według nas względami – pro- do informacji na temat pozycji swojego serwisu nych przez nas usług. Dlatego mogą oni liczyć
stotą obsługi, z czym ściśle wiązała się również es- w wyszukiwarkach oraz lokalizacji i zachowania na pomoc techniczną zarówno na etapie insta-
tetyka i klarowność prezentowanych widoków. użytkowników. lacji statystyk, jak i w trakcie ich użytkowania.
Dołożyliśmy więc wszelkich starań, by interfejs Dane, jakie liczy 7point, dotyczą wielu istot- Poza tym staramy się być w stałym kontakcie
był przyjazny i łatwy w odbiorze, a co za tym nych dla właścicieli serwisów kwestii związa- z klientami , aby znać ich sugestie i opinie na te-
idzie, odczytanie danych nie stanowiło problemu nych ze statystykami. Są to między innymi szcze- mat użytkowania statystyk. Dzięki temu może-
nawet dla osób, które nie mają na co dzień do czy- gółowe informacje na temat użytkowników, wi- my na bieżąco rozwijać nasz produkt.
nienia ze statystykami internetowymi. zyt i odsłon. Mogą być one przeglądane w róż- Mimo że system 7point jest nową propozy-
Ponadto zależało nam, by klienci mogli korzy- nych kontekstach: słów kluczowych, ilości i czę- cją na rynku statystyk internetowych, do dziś
stać ze statystyk za rozsądną cenę oraz by płacili stotliwości powrotów, odbić czy też stron starto- miało okazję korzystać z niego aż kilkaset ser-
tylko za to, czego naprawdę potrzebują. Dlatego w wych i końcowych wizyt. Kolejne kategorie, pre- wisów. Już wersja pilotażowa odznaczała się
ramach naszego systemu klient może wybrać trzy zentowane przez system 7point, to niezbędne w szybkością oraz stabilnością działania. W efek-
rodzaje usług – Echo, Monitor lub Horizon. Każ- planowaniu kampanii w sieci zestawienia słów cie z naszych statystyk korzystają do dziś klien-
da z nich jest pomyślana w ten sposób, aby użyt- kluczowych i wyszukiwarek. Ogromną wagę ma- ci zarejestrowani wkrótce po uruchomieniu
kownik mógł wybrać optymalną wersję dla po- ją również informacje dotyczące genezy ruchu na wersji pilotażowej. Wśród monitorowanych
trzeb i charakteru swojej witryny. Usługi zróżni- stronie – dzięki statystykom 7point użytkownik przez nasz system serwisów można znaleźć za-
cowane są pod względem miesięcznego limitu od- ma dostęp do przeglądania zestawień na temat równo te, których miesięczną liczbę odsłon li-
słon, ilości prezentowanych parametrów oraz ce- wyszukiwarek, wejść ze stron, katalogów stron, czy się w milionach, jak i małe serwisy gene-
ny. W ramach poszczególnych wersji użytkow- poczty WWW, jak również zestawień dotyczą- rujące w miesiącu od kilku do kilkudziesięciu
nik może utworzyć w swoim serwisie określo- cych geolokalizacji użytkowników. Niebagatelną tysięcy odsłon.

wsparcie internautów w podejmowaniu decyzji Ze stat24 współpracujemy od ponad dwóch


zakupowych. Oprócz porównania cen użytkow- lat. Kiedy rozpoczynaliśmy współpracę, stat24
nicy Ceneo mają możliwość przeczytania opinii był liderem na rynku, co było głównym powo-
o produktach i sklepach pisanych przez samych dem, dla którego zdecydowaliśmy się na usługi
internautów oraz recenzji tworzonych przez eks- tej właśnie firmy. Poszukiwania serwisu zajmu-
pertów portali tematycznych. Najnowszą funk- jącego się monitoringiem stron WWW prowa-
cjonalnością są przewodniki zakupowe – specjalna dziły w prostej linii do stat24, który dość mocno
sekcja pomagająca dobrać produkt do potrzeb zaznaczał swoją obecność w Sieci.
przyszłego użytkownika. Jesteśmy bardzo zadowoleni z usługi stat24,
Ceneo działa na rynku od ponad dwóch badania oceniamy jako bardzo rzetelne. Tym
lat, współpracuje z niemal 500 sklepami han- niemniej pragnę podkreślić, że stat24 jest jed-
dlującymi w przestrzeni wirtualnej, na stro- nym z naszych strategicznych partnerów. Na-
nach serwisu prezentowanych jest około 400 sza współpraca ma więc troszkę inny charakter
Marcin Chwalik tysięcy unikalnych produktów, co daje w su- niż w przypadku przeciętnego posiadacza wi-
mie niemal 1,5 miliona porównywanych ofert tryny internetowej.
ceneo
sklepów. W przyszłości, zarówno bliższej jak i dalszej,
Ceneo to platforma gromadząca informacje o pro- Serwis jest odwiedzany przez 1,7 mln użyt- Ceneo nadal zamierza korzystać z usług stat24.
duktach i sklepach działających w polskim Inter- kowników miesięcznie, którzy generują kilka-
necie. Celem porównywarki jest kompleksowe naście milionów odsłon. Ocena: ««««

72 05/2007
Statystyki zewnętrzne

zaawansowanych serwisach WWW. Sygnały,


które otrzymujemy od naszych użytkowni-
ków, są niezwykle pozytywne i pozwalają oce-
nić nasza współpracę jako udane przedsięwzięcie.
Dlaczego zewnętrzne rozwiązanie statystyk?
Przede wszystkim należy podkreślić, że nie
zrezygnowaliśmy z oferowania własnego roz-
wiązania. Bazuje ono jednak na logach serwera
WWW, a zatem nie umożliwia np. określenia,
czy dany użytkownik po raz pierwszy odwiedza
nasz serwis, czy też powraca. Informacje udo-
stępniane za pomocaą naszych statystyk i roz-
wiązan stat24 są więc komplementarne. Z dru-
giej strony – świadomość, że rozwojem systemu
statystyk zewnętrznych zajmuje się wyspecja-
lizowana firma, pozwala zakładać, że produkt
ten będzie stale rozwijany, czyli uwzgledniający
aktualne trendy rynkowe czy aktualizujący in-
formacje geolokalizacyjne. Uzyskanie takiej spe-
cjalizacji po naszej stronie byłoby czaso– i kosz-
tochłonne.
Model współpracy opierający się o koope-
rację usługodawcy i wyspecjalizowanego pod-
miotu jest w naszym odczuciu niezwykle
efektywny, a doświadczenia innych rynków
pozwalają stwierdzić, że także efektywny.
W ten sposób dostawca usług internetowych
Krystian Sypuła
Współpraca była więc tak naprawdę pewnym jest w stanie zapewnić dodatkową funkcjo-
home.pl naturalnym rozwiązaniem – innowacyjne nalność swoim klientom, a firma – dostaw-
rozwiązanie lidera polskiego rynku statystyk ca rozwiązania – otrzymuje dodatkowy kanał
Obecnie naszym partnerem w dziedzinie ofe- (Gemius) oraz usług webhostingowych (ho- dystrybucji swoich produktów
rowania statystyk zewnętrzych jest stat24. me.pl). Usługę oferujemy w tej chwili w trzech Ocenę pozostawiamy naszym użytkownikom.
W zasadzie rozwiaząnie to wśród komercyj- wariantach: bezpłatnym dla wszystkich klien-
nych nie ma odpowiednika na polskim rynku. tow oraz dwóch płatnych, stosowanych przy

R E K L A M A

www.phpsolmag.org 73
Testy konsumenckie

comedycentral.pl. W przyszłości planujemy pod- my jej dynamiczny rozwój na polskim rynku.


piąć statystyki stat24 również pod nowe projek- stat24 dowodzą, że nie tylko monopoliści na
ty internetowe MTV. rynku są w stanie zaproponować usługi stoją-
Usługi stat24 nie odbiegają standardem od li- ce na najwyższym poziomie zarówno techno-
dera na rynku, jakim jest Gemius. Są wiarygodne, logicznym, jak i konsumenckim. Jak wspomi-
rzetelne i spełniają standardy badawcze. Dużą za- nałam, nowe produkty MTV Networks Pol-
letą jest także czytelny interfejs, pozwalający na ła- ska również korzystać będą z tych rozwiązań.
twe analizowanie wyników oglądalności również Zapewniana przez stat24 możliwość pełnego,
przez osoby nieposiadające dużego doświadcze- wiarygodnego monitoringu witryn MTV w cy-
nia w pracy z Internetem. Łatwo administrować klach dziennych pozwala nam oferować inter-
stat24, powielać skrypty. Dodatkowo stat24 ofe- nautom jeszcze lepszy produkt, który nie tylko
rują bardzo dogodny system rozliczeń dla klienta. jest uzupełnieniem oferty antenowej kanałów
Zdecydowanym minusem korzystania z us- z portfolio MTV Networks Polska , ale również
ługi jest ograniczona liczba skryptów monitoru- – dzięki specjalnym konkursom, odsłuchom
jących, których liczba uzależniona jest od opłat płyt, forom i sekcjom społecznościowym, po-
cennikowych. Nawiązując jeszcze do strony ad- zwala Internautom aktywnie uczestniczyć
ministracyjnej stat24, w ostatnim okresie zmie- w „życiu” MTV.
Z usługi monitoringu serwisów internetowych, niła się opcja geolokalizacji. Obecny sposób pre-
dostarczanej przez firmę stat24 Sp. z o.o., zentacji wyników jest mniej czytelny dla użyt- Beata Węgrzyn
korzystamy już drugi rok. stat24 zapewniają kownika. Również w sekcji Katalogi Odsyłające Menager ds. Internetu MTV Networks Polska
monitoring większości witryn internetowych przydałaby się łatwiejsza nawigacja. Sp. z o. o.
z portfolio MTV Networks Polska, tj. m.in. Jesteśmy ze stat24 prawie od początku
http://www.mtv.pl, http://www.vh1.pl i http://www. istnienia firmy i z przyjemnością obserwuje- Ocena: ««««

stat24 świadczy bezpłatne oraz płatne usłu- rencyjnej cenie lub całkowicie bezpłatnie. Po-
gi monitorowania serwisów WWW. Są to pro- nadto największymi zaletami stat24 są spraw-
dukty o unikalnej jakości i użyteczności, któ- dzona metodologia wykonywania badań oraz
rych wykorzystanie przyczynia się do rozwo- fakt, że dane udostępniane przez stat24 są
ju działalności biznesowej w Internecie i słu- w pełni akceptowalne przez agencje reklamo-
ży do zwiększania wartości posiadanych zaso- we oraz uznawane za wiarygodne źródło wie-
bów medialnych. Przestrzeń serwisów, które dzy o serwisie.
są monitorowane bezpłatnie, jest wykorzysty- W pełni zgadzam się z opinią home.pl. Ofer-
wana do wykonywania badań ankietowych In- ta stat24 uzupełnia ofertę produktową firmy
ternetu. Stat24 nigdy nie emitował reklam ko- gwarantując jej i jej klientom dostęp do profesjo-
mercyjnych. nalnego narzędzia badawczego.
Poza statystykami stat24 udostępnia każde- Współpraca z MTV układa się bardzo po-
mu klientowi bezpłatny licznik internetowy myślnie. Cieszy mnie, że MTV w przyszłości
oraz możliwość korzystania z rankingu. Dzię- planuje objąć monitoringiem inne swoje wi-
ki rankingowi stat24 każdy właściciel staty- tryny. Ustosunkowując się do punktu, w któ-
styk WWW może porównać oglądalność swo- rym jest mowa o małej ilości skryptów udo-
Ustosunkowanie jej witryny z innymi serwisami w danej katego- stępnianych za pośrednictwem stat24, chciała-
Dyrektor Zarządzający stat24 rii oraz klasyfikacji generalnej. Ponadto stat24 bym zaznaczyć, że stat24 jest produktem stwo-
Żaneta Tarnowska udostępnia raporty badawcze, które podsu- rzonym z myślą o małych i średnich przedsię-
mowują miesięczną oglądalność monitorowa- biorstwach – stąd ograniczona ilość skryptów.
stat24 tworzy grupa młodych ludzi, połączo- nych serwisów WWW. Raporty tworzone są Zgodnie z polityką firmy klientom, dla których
nych wspólną pasją, jaką jest Internet. Dziś, na podstawie danych zbieranych poprzez sta- udostępniana ilość skryptów zliczających jest
po 3 latach dynamicznego rozwoju, stat24 to tystyki stat24. niewystarczająca, proponujemy statystyki ge-
uznana marka w zakresie statystyk dla małych stat24 cały czas unowocześnia oferowaną miusTraffic.
i średnich serwisów internetowych. Monito- usługę. Na początku 2007 roku została wpro- Zgadzam się z wypowiedzią – jesteśmy part-
rujemy ponad 900 000 witryn, odnotowując wadzona nowa geolokalizacja, która graficznie nerem Ceneo, jednocześnie porównywarka ko-
miesięcznie ponad 3 mld odsłon, a dzięki in- obrazuje oglądalność w poszczególnych kra- rzysta z naszego narzędzia badawczego. Cie-
tegracji z platformą technologiczną firmy Ge- jach. Ponadto powstała szczegółowa mapa Pol- szy mnie, że Ceneo jest zadowolone z naszych
mius SA – lidera w dziedzinie badań Interne- ski, która dzięki mechanizmowi wyszukiwaw- usług.
tu – nasze statystyki są rzetelne, wiarygodne czemu pozwala na znalezienie użytkowników 4 gwiazdki to świetna ocena – mobilizu-
i spełniają wymogi europejskich standardów pochodzących nawet z niewielkich miejscowo- je nas do pracy, wprowadzania na rynek no-
badawczych. ści w kraju. wych rozwiązań oraz ulepszania dotychczaso-
Powstanie stat24 było decyzją Gemius SA Aktualnie stat24 prowadzi prace nad nowym wej usługi.
i wiązało się z umożliwieniem oferowania ma- produktem, który zostanie wprowadzony na ry-
łym i średnim przedsiębiorstwom profesjonal- nek w drugiej połowie 2007 roku.
nego narzędzia badawczego, jakim miał być stat24 jest w Polsce symbolem wiarygodno-
stat24. Główną misją stat24 jest poszerzanie za- ści, niezawodności i łatwego dostępu do przy-
sięgu badawczego, zarówno w Polsce, jak i w Eu- datnych informacji dotyczących witryny po-
ropie Środkowo-Wschodniej. przez czytelny interfejs po korzystnej i konku-

74 05/2007
Recenzja

Dreamweaver 8 z ASP, PHP i ColdFusion


Oficjalny podręcznik

Wydawnictwo: Helion
Autor: Jeffrey Bardzell
Stron: 472
Cena: 69 zł
ISBN: 83-246-0309-3

Dzisiaj, gdy zapotrzebowanie na strony WWW rośnie z dnia na Przechodząc przez kolejne lekcje, nauczymy się: tworzyć formu-
dzień, coraz więcej osób chce zająć się ich tworzeniem. Jednak larze służące do wysyłania maili, zbudujemy kalkulator cen wycie-
obecnie odchodzi się od prostych, statycznych witryn, standardem czek, pracować z bazą danych, budowania interfejsów wyszukiwa-
są strony dynamiczne. nia, tworzyć prosty CMS do aktualizacji stron, ponadto instalować
Materiał składa się z 15 lekcji, które nie są oddzielnymi zagadnie- środowiska ASP, PHP oraz ColdFusion. Każdy rozdział w sposób
niami, a składają się na jeden duży projekt. I tak na wstępie poznamy charakterystyczny dla serii Oficjalny Podręcznik, poprzedzony jest
charakterystykę fikcyjnej firmy turystycznej Newland Tours. Firma informacją co zrobimy podczas danej lekcji, przewidywany czas na
co prawda posiada już własną stronę WWW, jednak nie jest z niej jej ukończenie, oraz jakie pliki potrzebne będą do jej wykonania.
zadowolona. Wynika to z tego że informacje na stronie są nieaktu- Książka nie wyczerpuje całej problematyki a jest jedynie wstę-
alne, a wszelkie zmiany na witrynie wymagają skorzystania z usług pem do tematyki związanej z tworzeniem dynamicznych stron
webmastera, co nie jest ani tanie, ani praktyczne. Dlatego Newland WWW. Podsumowując ten podręcznik jest bardzo dobrą pozycją,
Tours zleca przebudowę strony na dynamiczną, z prostym CMSem który w przystępny sposób, opisuje przebudowę witryny. Małym
za pomocą którego jej pracownicy będą mogli w prosty sposób uak- niedopatrzeniem jest to, że na płycie CD pliki źródłowe są w wer-
tualniać ofertę. I to jest zadanie dla Ciebie drogi Czytelniku. Dzięki sji angielskiej, a książka opisuje polską wersję witryny. Jednak na
takiemu podejściu, mamy możliwość zapoznać się z potencjalnymi szczęście, polskie pliki źródłowe są do pobrania na strony wydaw-
problemami webmastera. nictwa Helion, pod adresem http://helion.pl/ksiazki/d8apof.htm.

Macromedia Flash 8 Professional


Oficjalny Podręcznik

Wydawnictwo: Helion
Autorzy: Tom Green, Jordan L. Chilcott
Stron: 480
Cena: 59 zł
ISBN: 83-246-0313-1

Książka Macromedia Flash 8 Professinal Oficialny Podręcznik to brany, że poziom trudności rośnie wraz z kolejnymi przerabianymi
obowiązkowa lektura, przeznaczona szczególnie dla osób które za- lekcjami. Przystępny język, wprowadzenia do lekcji, podsumowa-
wodowo zamierzają zajmować się projektowaniem i przygotowy- nia każdej lekcji, a także przybliżony czas potrzebny na wykonanie
waniem materiałów na potrzeby Internetu oraz coraz dynamicz- ćwiczenia sprawia że nauka jest przyjemna i wiemy czego się na-
niej rozwijającego się rynku aplikacji mobilnych. uczymy przerabiając kolejną lekcję.
Po podręcznik powinny sięgnąć osoby, posiadające przynajm- Zostały wyjaśnione tutaj takie tematy jak: praca z tekstem, grafi-
niej podstawową wiedzę na temat obsługi programu Flash 8 lub ze- ką, wideo, podstawy Action Script 2.0 oraz ich praktyczne zastoso-
tknęły się w swojej pracy z wcześniejszą wersją programu. Dzięki wanie np. do prezentacji slajdów, tworzenia animacji, przygotowy-
tej książce będą mogły rozwinąć swoje dotychczasowe umiejętno- waniem aplikacji na urządzenia przenośne itp.
ści, a tworzone projekty wzbogacić zarówno o elementy audio, wi- Na uwagę zasługuje to, że większość kolejnych lekcji, wyma-
deo lub dynamicznie uaktualniać zawartość stron przygotowywa- ga od czytelnika wykorzystania umiejętności zdobytych przy
nych we Flashu. Osobom rozpoczynającym swoją przygodę z Fla- wcześniejszych ćwiczeniach. Bardzo przydatne są również
shem polecam do lekturę książki „Macromedia Flash 8 Oficialny wskazówki oraz komentarze, dotyczące standardów oraz za-
Podręcznik”, który wprowadza i wyjaśnia podstawowe zagadnienia sad projektowych przyjętych przez osoby zajmujące się zawodo-
związane z programem Flash 8. wo Flashem. Sprawia to, że wykonując kolejne ćwiczenia szyb-
Materiał podręcznika został podzielony na 13 ciekawych lekcji ko i coraz lepiej opanowujemy obsługę praktycznego wykorzy-
(materiały źródłowe są dołączone na CD). Materiał zostały tak do- stania Flasha.

75
Recenzja

Delphi for PHP


W
marcu 2007 roku na rynku poja- cji tego typu, inaczej mówiąc, mieć już jakieś my przycisk Run i możemy już zaobserwować
wiło się pierwsze w swoim rodza- doświadczenie w pracy z innymi programami pewne rozbieżności. Strona wygląda tak jak
ju narzędzie RAD dla PHP. W Pol- Borlanda. Po zapoznaniu się z głównym oknem w naszym programie, ale niektóre z wykorzy-
sce było ono reklamowane na konferencji Code- możemy spróbować coś wykreować. Tworzymy stanych elementów, które działają pod IE, nie
Gear w Warszawie. Niestety na ten program po- nowy projekt i metodą „przeciągnij i upuść” prze- działają pod Firefoksem. Widać więc pewną
święecono zaledwie dwadzieścia minut i w tym rzucamy kilka elementów z okienka z kompo- stronniczość w przygotowywaniu tych kom-
czasie natrafiono na kilka niespodziewanych nentami na formę. Przykładowo umieszczone ponentów. Nie zadbano o właściwą współpra-
problemów.Jak to zwykle bywa, pierwsze pre- elementy widać na Rysunku 2. cę z obecnie wykorzystywanymi przeglądar-
zentacja pozostawiła duży niedosyt. Teraz nad- Po umieszczeniu elementów możemy kami. O ile elementy typu checkbox czy button
szedł czas na zaspokojenie tego głodu wiedzy. wreszcie spróbować coś wyświetlić. Wciska- działają poprawnie, o tyle np. pasek menu zo-

Instalacja
i pierwsze spostrzeżenia
Po włożeniu nośnika z oprogramowaniem ma-
my wybór składników instalacji – Delphi for PHP
oraz InterBase 2007. To dobre rozwiązanie, gdyż
nie każdy jest zainteresowany InterBase'em, a jed-
nocześnie ci, którzy chcą, mogą go sobie doinsta-
lować i mieć w pełni sprawną aplikację opartą o
profesjonalną bazę danych. Instalacja przebiega
bezproblemowo, ale jak zdążyłem zauważyć, w
razie próby reinstalacji programu pojawia się kło-
pot. Z rejestru systemu nie są usuwane informa-
cje o bibliotekach standardowych, przez co przy
ponownej instalacji są pomijane przez instalator i
program nie może działać właściwie. Warto więc
pamiętać o czyszczeniu rejestru przed ponow-
nym zainstalowaniem.
Po pomyślnej instalacji możemy uruchomić
nasze oprogramowanie. Jako pierwszy zobaczy-
my widok okienka na wzór borlandowskiego
Buildera, od którego przecież wywodzi się fir-
ma CodeGear (Rysunek 1.).
Rysunek 1. Główne okno Delphi for PHP
Po lewej stronie mamy okienka, w których
możemy obserwować wykorzystywane elemen-
ty umieszczone na formie oraz właściwości po-
szczególnych elementów. Po prawej stronie na-
tomiast znajduje się widok komponentów, baz
danych oraz elementów do wykorzystania w
aplikacji. W centralnym okienku natomiast
możemy zobaczyć kilka podstawowych infor-
macji o ostatnio edytowanych projektach, parę
nowinek na temat produktu oraz kilka najczę-
ściej wykorzystywanych funkcji programu, np.
otwarcie nowego pliku.

Pierwsze próby
Jak to zwykle bywa, zanim zaczniemy pracę z
nowym oprogramowaniem, należy zapoznać się
z dokumentacją. Tutaj natrafiamy na pierwszy
zawód. Dokumentacja jest całkiem spora, ma-
my opisane poszczególne klasy, metody i bi-
blioteki, ale liczbę przykładów można policzyć
na palcach obu rąk. A przecież, jak wiadomo,
człowiek najlepiej uczy się na przykładach. Aby
więc rozpocząć pracę z tym programem, warto
jest znać chociaż ogólne zasady działania aplika- Rysunek 2. Przykładowo dodane komponenty

76 05/2007
Delphi for PHP

baczymy tylko w IE. Jest to niedopuszczalne nie kodu dla metody podwójnego kliknięcia,
przy realizacji stron WWW, ponieważ straci- w której będziemy mogli oprogramować żąda-
libyśmy możliwość nawigacji. Po dodaniu ele- ne zadanie. Do poszczególnych elementów od-
mentów możemy je oprogramować, przecho- wołujemy się poprzez następujący schemat:
dząc do widoku kodu widocznego na Rysun-
ku 3. Pierwsze, co dostrzeżmy w tym wido- $this->nazwa_elementu;
ku, to liczba dołączanych plików. Jest ich cał-
kiem sporo jak na stronę, która tak naprawdę Gdy chcemy zmienić lub ustawić jakąś war-
nic jeszcze nie robi. Przechodząc dalej, mamy tość dla elementu, po prostu rozszerzamy po- Rysunek 5. Logowanie do panelu administratora
zadeklarowane zmienne reprezentujące doda- przedni kod do postaci:
ne elementy. Aby dodać jakąś akcję, musimy Przykładowy projekt
w trybie Design wybrać element i np. kliknąć $this->nazwa_elementu-> Firma CodeGear udostępniła przykładowy pro-
na niego dwa razy, co spowoduje wygenerowa- nazwa_parametru=wartość parametru; jekt e-commerce wykonany w Delphi for PHP
oparty o bazę InterBase. Może pobrać go każdy,
warunkiem jest tylko zarejestrowanie się na ich
stronie. Po utworzeniu bazy oraz umieszczeniu
plików strony na naszym serwerze Apache, któ-
ry został automatycznie zainstalowany z Delphi
for PHP, możemy zobaczyć pobraną aplikację.
Jedyne słowa, które się nasuwają po zobaczeniu
tej strony, to „żart” albo „porażka marketingo-
wa”. Stronę pokazano na Rysunkach 4. i 5.
Strony są nader proste i zrobienie takich wi-
tryn w PHP również nie powinno zająć du-
żo czasu, choć tę stronę zapewne wykonano
w znacznie krótszym czasie niż normalnie, ale
nie wygląda ona interesująco i na pewno nie po-
kazuje możliwości, którymi tak przecież chwa-
li się CodeGear. Strona ma funkcjonalność ni-
by–sklepu internetowego pozbawionego rze-
czywistych funkcji. Pliki tej aplikacji mają obję-
tość 1 MB (nie wliczając obrazków), co samo w
sobie może zaszokować każdego. O ile wygląd
łatwo zmienić, bo witryna wykorzystuje szablo-
ny, o tyle zdecydowanie nie można pochwalić
Rysunek 3. Widok kodu takiego rozmiaru strony WWW. Niestety tak
duży rozmiar jest odczuwalny podczas korzy-
stania z tej strony, jej wygenerowanie nawet na
localhoście trwa parę chwil. Edycja projektu nie
sprawia żadnych problemów, o ile znamy PHP,
natomiast całkowite przebudowanie takiej stro-
ny może okazać się dość skomplikowanym za-
daniem.

Podsumowanie
Możemy stwierdzić, że program Delphi for PHP
jest na pewno ciekawym rozwiązaniem, któ-
re w przyszłości może znaleźć swoje miejsce
na rynku. Niestety, w tym momencie sprawia
wrażenie oprogramowania robionego w pośpie-
chu, a jak wiadomo nawet przemyślany projekt
z braku czasu może wiele stracić na swojej funk-
cjonalności. Poza tym brakuje źródeł, z których
można czerpać wiedzę na temat tworzenia apli-
kacji w tym programie – dokumentacja pozba-
Rysunek 4. Strona dla zwykłego użytkownika wiona przykładów, dwa krótkie filmy oraz nie-
wielka strona to zdecydowanie za mało.

W sieci
• http://www.codegear.com/products/delphi/php – strona producenta, można z niej dowie-
ŁUKASZ SKOWROŃSKI
dzieć się paru rzeczy o produkcie oraz zobaczyć dwa filmy z przykładami wykorzystania pro- Autor jest studentem informatyki na Uniwersyte-
gramu. cie w Białymstoku oraz pracuje jako programista
• http://dn.codegear.com/article/36362 – strona producenta z przykładową stroną WWW. PHP w firmie E–Studio.
Kontak z autorem: lukas.skowronski@gmail.com

www.phpsolmag.org 77
Felieton

Witryny internetowe
– tylko dla zarobku?

W
szyscy zapewne wiemy, że na two- Ile straconych nerwów przy nauce programowa- a fakt, że informatyk to całkiem opłacalny za-
rzeniu witryn internetowych moż- nia? Sam zajmuje się tworzeniem witryn inter- wód, czerpiemy spore zarobki. Ale tak napraw-
na całkiem nieźle zarobić. Ale czy netowych i wiem, co to wszystko znaczy. dę, dlaczego? Może to chciwość... jedna z gor-
kiedykolwiek zastanawialiście się, czy istnieją oso- W „interesie” siedzę już około 5 lat i do tej szych z ludzkich cech. Zazwyczaj informatycy
by, które zrobią to dla nas „for free”? Surfując po pory pamiętam początki. Spotkałem tylko ma- nie mają czasu i chęci, aby zajmować się osoba-
Internecie, spotkałem wiele ogłoszeń „Zrobię stronę łą garstkę ludzi, którzy byli mi w stanie pomóc mi początkującymi. Idąc dalej, ludzi dziwi to, że
internetową za...”, „Profesjonalna firma stworzy stro- całkowicie za darmo. Reszta oferowała mi swo- zakładamy portale dla internautów, nie dla zarob-
nę internetową za...”. Dziwnie się poczułem... Oso- ją pomoc za 50, 100, 200, a nawet 1000 zł. ku. Mówię w tej chwili, o osobach które siedzą w
by, które chcą w jakiś sposób zaistnieć w cyber- Skąd nastolatek miał wziąć choćby 100 zł? tym wszystkim „po uszy”. Moi przyjaciele twier-
świecie, muszą za to płacić? Nie mogą sami stwo- Dzięki właśnie tej grupie osób prowadzę jed- dzą, że nie opłaca mi się prowadzić serwisu, bo
rzyć strony w jakimś darmowym oprogramowa- nak poważny portal internetowy... i za to im nie przynosi praktycznie żadnych zysków i pyta-
niu? Programów jest sporo, trzeba tylko umieć je dziękuję. Tak naprawdę powyższy wstęp nie ją, dlaczego to robią. Zgadnijcie, co odpowiadam...
znaleźć. Ale czy taka strona będzie chętnie ogląda- jest głównym tematem tego felietonu. Pragnę Dla satysfakcji, że ktoś może się dowiedzieć
na? Wydaje się, że nie bardzo... Zazwyczaj nie wy- pokazać, że czasami to 500 zł jest mniej warte tego, czego szukał. Daje mi to, można powiedzieć,
gląda ona najlepiej. A co z wyszukiwarkami? Sami od satysfakcji za podziękowania od osób, któ- ogromną satysfakcję, że ktoś, komu pomogłem,
wiecie, jak to wszystko jest zbudowane... rym pomogliśmy. Być może dzięki naszej po- przysyła mi zwyczajne „dziękuję za pomoc” lub
Wracając do tworzenia witryn, czy ktoś mocy ktoś zostanie sławnym programistą, bo inne miłe słowa. Myślę, że czasami warto...
będzie się uczył HTML-a, PHP, JS, obsługi go to wszystko zainteresuje. A może zostanie
Photoshopa czy Gimpa tylko po to, aby stwo- sławnym fotografem, bo stworzy prostą gale-
rzyć sobie własną stronę, gdzie np. umieści gale- rię zdjęć. WOJCIECH SAPIECHOWSKI
rię zdjęć? To byłoby zbyt pracochłonne. Ile cza- Przejdźmy dalej, dlaczego nie chcemy robić Od kilku lat zajmuje się tworzeniem witryn interneto-
su musimy poświęcićna to, aby w jakimś stop- stron „for free”? Zapewne, jak wiemy, pieniądze wych. Jest jednym z administratorów portalu WebHat.
niu pojąć możliwości programów graficznych? szybko „uciekają”, więc musimy jakoś zarabiać, Kontakt z autorem: sapcik@webhat.pl

78 05/2007
Prenumerata PRO
Więcej informacji: patrycja.wadolowska@software.com.pl tel.: 022 887-13-45

v
www.buyitpress.com

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!
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: pren@software.com.pl. 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)............................................................................................................................................................................


automatyczne przedłużenie prenumeraty

Tytuł
Ilość Od numeru Opłata
Ilość
zamawianych pisma lub w zł
numerów
prenumerat miesiąca z VAT
Software Developer’s Journal (1 płyta CD)
– dawniej Software 2.0 12 250/1801
Miesięcznik profesjonalnych programistów
SDJ Extra (od 1 do 4 płyt CD lub DVD)
– dawniej Software 2.0 Extra! 6 150/1352
Numery tematyczne dla programistów

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 (2 płyty CD)


Dwumiesięcznik użytkowników programu Adobe Photoshop
6 140

.psd numery specjalne


(.psd Extra + .psd Starter Kit)
6 140

Suma

Jeżeli chcesz zapłacić kartą kredytową, wejdź na


stronę naszego sklepu internetowego:
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
www.buyitpress.com
W następnym numerze PHP Solutions 6/2007 (23)

W sprzedaży od października

ROZWIĄZANIA UWAGA!! KOLEJNY TEST KONSUMENCKI


n Forum internetowe z wykorzystaniem Vanilla n UPS
alternatywa dla phpBB

NOWE ARTYKUŁY W DZIAŁACH


NARZĘDZIA
n dla początkujących
n PHP jako front-end dla systemu SAP
n dla zaawansowanych
n Module i theme czyli
stworzymy własny moduł do Drupala n kasa dla webmastera

I wiele innych artykułów, których nie możesz przeoczyć!

Redakcja zastrzega sobie możliwość zmiany zawartości pisma.

You might also like