background image

www.phpsolmag.org/pl

Artykuł w formie elektronicznej pochodzi z  magazynu PHP Solutions. Wszelkie prawa zastrzeżone. 

Rozpowszechnianie artykułu bez  zgody Software Wydawnictwo Sp. z o.o.  Zabronione.

 Software Wydawnictwo Sp. z o.o., ul. Piaskowa 3, 01-067 Warszawa, POLSKA.

 Kontakt: redakcja@phpsolmag.org 

background image

PHP Solutions Nr 1/2006

www.phpsolmag.org

48

Techniki

AJAX

PHP Solutions Nr 1/2006

www.phpsolmag.org

49

Techniki

A

JAX pozwala stworzyć dodatkowy 
kanał komunikacji między klientem 
a  serwerem  PHP,  a  tym  samym 

wysyłać  i  odbierać  dane  bez  przeładowy-
wania  strony.  Otwiera  to  zupełnie  nowe 
możliwości,  a  w  połączeniu  z  operacjami 
na  modelu  DOM  z  poziomu  JavaScriptu, 
oznacza  nadejście  ery  bogato  wyposażo-
nych,  interaktywnych  aplikacji  PHP,  wol-
nych od irytującego klikania i czekania.

W  tym  artykule  przedstawimy  prak-

tyczne  wprowadzenie  do  techniki  AJAX 
na przykładzie dwóch  bibliotek PHP i nie-
wielkiej aplikacji o działaniu podobnym do 
Google Suggest.

Czym jest AJAX?

AJAX (skrót od Asynchronous JavaScript 
And XML
) jest nazwą nowej metody pro-
gramowania,  łączącej  kilka  różnych  tech-
nik: (X)HTML i CSS do tworzenia interfej-
su  użytkownika,  DOM  (Document  Object 
Model
)  do  obsługi  elementów  dynamicz-
nych  i  interakcji  oraz 

XMLHttpRequest

  do 

Aplikacje tworzone w PHP pozwalają 

osiągnąć bardzo wiele przy ograniczonym 

oprogramowaniu klienckim, co oznacza łatwe 

wdrażanie i aktualizacje, a tym samym szybkie 

efekty pracy. Architektura ta ma też dotkliwe 

wady, jak opóźnienia między wyświetlaniem 

kolejnych stron lub brak możliwości pobierania 

nowych danych bez wysyłania formularza.

Na szczęście istnieje mechanizm AJAX.

asynchronicznej wymiany danych. Techni-
ki te są łączone w jedną całość za pomocą 
JavaScriptu,  odpowiedzialnego  za  logikę 
aplikacji i dynamiczną aktualizację interfej-
su użytkownika stosownie do potrzeb.

Pomimo  XML  w  nazwie,  AJAX  nie-

koniecznie  wymaga  używania  formatu 
XML  do  wymiany  danych.  Poza  XML-em 
obsługiwane  są  między  innymi  zwykły 
tekst,  sformatowany  HTML  (dodawany 
do  bieżącej  strony  poprzez  właściwość 

innerHTML

) oraz format JSON (JavaScript 

AJAX – wyjątkowo interaktywne 

i wydajne aplikacje WWW

Joshua Eichorn, Werner M. Krauß

W SIECI

1. http://sourceforge.net/

projects/jpspan/ – strona 

główna projektu JPSpan

2. http://pear.php.net/package/

HTML_AJAX/ – strona głów-

na pakietu HTML_AJAX

3. http://www.google.com/

webhp?complete=1&hl=en 

– Google Suggest

4. http://www.ajaxpatterns.org/ 

– serwis poświęcony wzor-

com aplikacji AJAX

5. http://www.ajaxpatterns.org/

Suggestion – opis wzorca 

w stylu Google Suggest

6. http://blog.joshuaeichorn.

com/archives/category/php/

ajax – blog Joshuy Eichorna 

poświęcony AJAX-owi

Powinieneś wiedzieć...

Powinieneś  się  dobrze  orientować 

w  zasadach  programowania  obiektowe-

go  w  PHP4  lub  PHP5.  Przyda  się  też 

pewna znajomość JavaScriptu.

Obiecujemy...

Po  przeczytaniu  artykułu  będziesz  znał 

zasadę  działania  i  stosowania  techniki 

AJAX  oraz  śledzenia  kodu.  Pokażemy 

też  możliwości  bibliotek  implementują-

cych tę technikę.

background image

PHP Solutions Nr 1/2006

www.phpsolmag.org

48

Techniki

AJAX

PHP Solutions Nr 1/2006

www.phpsolmag.org

49

Techniki

Object Notation), który można przepuścić 
przez 

eval()

  w  celu  uzyskania  typów 

JavaScript. Można też korzystać z dowol-
nego innego formatu danych dającego się 
obsłużyć w JavaScripcie i PHP.

AJAX-a  można  najprościej  zde-

finiować  jako  metodę  wykorzystania 
JavaScriptu  do  komunikacji  z  serwerem 
niezależnie od tradycyjnych żądań POST 
i  GET.  Strona  techniczna  jest  tu  jednak 
mniej istotna – najważniejsze są zupełnie 
nowe możliwości tworzenia aplikacji inter-
netowych.

Podstawą  pracy  AJAX-a  jest  obiekt 

XMLHttpRequest

,  stanowiący  standar-

dowy  element  wielu  przeglądarek.  Jeśli 
postanowisz  dodać  obsługę  AJAX-a  do 
swojej  aplikacji  za  pomocą  biblioteki, 
to  nie  musisz  wiele  wiedzieć  o  samym 

XMLHttpRequest

,  gdyż  wszystkim  zajmie 

się  biblioteka.  Fizyczna  implementacja 
obiektu 

XMLHttpRequest

  zależy  od  kon-

kretnej  przeglądarki  –  w  Internet  Explo-
rerze  jest  to  wbudowany  obiekt  ActiveX, 
natomiast w Firefoksie, Safari i większości 
innych przeglądarek jest on wewnętrznym 
obiektem JavaScriptu.

XMLHttpRequest

 udostępnia proste API, 

pozwalające wysyłać do serwera żądania 
HTTP metodami GET i POST. Dla serwe-
ra  są  to  zwyczajne  żądania  przeglądarki, 
zawierające  nawet  wszystkie  pliki  cookie 
dla  bieżącej  domeny  oraz  autoryzację 
HTTP (jeśli oczywiście jest włączona). Od 
strony  JavaScriptu, 

XMLHttpRequest

  daje 

dostęp  do  treści  i  nagłówków  podczas 
wysyłania  i  odbioru  żądań.  Możliwość  ta 
jest  często  używana  do  poinformowania 
serwera,  że  żądanie  nie  zostało  zgłoszo-

możliwość  uzyskania  znacznie  wyższego 
poziomu  interaktywności  w  aplikacjach 
internetowych. Reakcje programu na dzia-
łania  użytkownika  są  dużo  szybsze,  bez 
nużącego klikania i czekania, przez co ob-
sługa  całego  programu  znacznie  bardziej 
przypomina  pracę  z  tradycyjną  aplikacją 
stacjonarną.

Rysunek  1  przedstawia  przepływ  da-

nych w typowej aplikacji internetowej. Użyt-
kownik  wypełnia  formularz  i  wysyła  go  na 
serwer  WWW,  który  przetwarza  formularz 
i odsyła dane do czekającego użytkownika. 
Zwracanym  wynikiem  jest  pełna  strona 
HTML, którą przeglądarka klienta musi za-
ładować w całości (treść i strukturę). Mar-
nuje się w ten sposób czas i pasmo, gdyż 
kod  strony  wynikowej  najczęściej  niewiele 
się różni od kodu poprzedniej strony.

Aplikacja  AJAX  wysyła  do  serwera 

wyłącznie żądania pobierające nowe, po-
trzebne  dane,  a  odpowiedź  serwera  jest 
przetwarzana przez JavaScript po stronie 
klienta. Dzięki wprowadzeniu tej dodatko-
wej  warstwy  JavaScriptu,  przetwarzanie 
danych  nie  spowalnia  działania  interfejsu 
użytkownika. Cała aplikacja działa znacz-
nie  szybciej,  gdyż  między  serwerem, 
a  klientem  przesyłanych  jest  nieporów-
nanie  mniej  danych,  a  spora  część  prze-
twarzania  odbywa  się  po  stronie  klienta 
(Rysunek 2).

Praktycznie  rzecz  ujmując,  stworze-

nie aplikacji AJAX wymaga zatem dwóch 

����

����������������

������

�����������������������

������������������

���������������������

������������������
���������������������

���

����

�����������������

��������������������
�������������������

Rysunek 1. 

Przepływ danych w tradycyjnej aplikacji internetowej

����

������

���

����

����

����������������

������������������

������

������������������

������

������������������������

�������������������

���������������

�������������������

���������������

��������������

������������������������

������������

������������������

��������������

������������������������

�������������

Rysunek 2. 

Przepływ danych w aplikacji AJAX

ne  bezpośrednio  przez  użytkownika,  lecz 
poprzez 

XMLHttpRequest

.  Odebraną  treść 

można  traktować  jako  zwykły  tekst,  ale 
w  przypadku  treści  typu 

text/xml

  można 

też  stworzyć  obiekt  XML  DOM.  Obsługa 
modelu  DOM  przyczyniła  się  do  popular-
ności  XML-a  jako  formatu  wymiany  da-
nych  między  klientem  a  serwerem,  nato-
miast utrzymanie obsługi zwykłego tekstu 
pozwala  korzystać  z  dowolnego  formatu 
dającego  się  przetworzyć  na  poziomie 
JavaScriptu.

Dlaczego AJAX?

Najważniejszym  argumentem  przema-
wiającym  za  korzystaniem  z AJAX-a  jest 

background image

PHP Solutions Nr 1/2006

www.phpsolmag.org

50

Techniki

elementów:  odpowiednich  skryptów  po 
stronie klienta i specjalnego kanału komu-
nikacji z serwerem.

Zalety techniki AJAX

AJAX ma wiele zalet, z których najbardziej 
zauważalną  jest  znaczące  rozszerzenie 
zakresu  możliwości  interfejsu  użytkowni-
ka.  Jednak  samo  w  sobie  to  nie  wystar-
czy  –  w  końcu  istnieje  też  wiele  innych 
technologii  o  zbliżonych  możliwościach. 
O  wyjątkowości  AJAX-a  stanowi  przede 
wszystkim  to,  że  bazuje  on  na  uznanych 
standardach,  więc  w  przeciwieństwie  do 
innych  narzędzi  do  tworzenia  interaktyw-
nych  aplikacji  internetowych  (na  przykład 
Flasha) można go z łatwością wpasować 
w istniejące procesy deweloperskie. Moż-
na  więc  dalej  korzystać  ze  swojego  ulu-
bionego edytora czy środowiska programi-
stycznego,  bez  konieczności  poznawania 
nowych narzędzi.

Istnieje  też  wiele  darmowych  zesta-

wów narzędzi Open Source ułatwiających 
tworzenie  i  rozwijanie  aplikacji  AJAX, 
a przy okazji redukujących objętość kodu 
JavaScriptu, jaki trzeba wpisywać ręcznie. 
W dalszej części artykułu zobaczymy, jak 
dołączać  obsługę  AJAX-a  do  własnych 
aplikacji z pomocą popularnych bibliotek.

Wady AJAX-a

Opisywana  metoda  interakcji  z  klientem 
ma  też  swoje  wady.  Nie  można  przewi-
dzieć, z jakiej przeglądarki korzysta użyt-
kownik,  więc  aplikacja  może  nie  działać 
na  niekompatybilnych  przeglądarkach 
lub przy wyłączonej obsłudze JavaScrip-
tu.  Oznacza  to,  że  dobrą  praktyką  jest 
uwzględnienie awaryjnej metody obsługi, 
na przykład poprzez stworzenie bazowej 
aplikacji  z  wykorzystaniem  tradycyjnych 
technik,  a  następnie  rozbudowanie  jej 
o  opcjonalne  usprawnienia  używające 
AJAX-a.

Trzeba  też  pamiętać,  że  aplikacje 

z AJAX-em  nie  będą  działać  w  Internet 

Listing 1. 

Serwer JPSpan

<?

php

session_start

()

;

// Klasa powitania

class

 HelloWorld 

{

   

function

 HelloWorld

()

 

{

      

if

 

(

!

isset

(

$_SESSION

[

'strings'

]))

 

{

         

$_SESSION

[

'strings'

]

 = 

array

(

'Hello'

,

'World'

,

'Hello World'

)

;

      

}

   

}

   

function

 addString

(

$string

)

 

{

      

$_SESSION

[

'strings'

][]

 = 

$string

;

      

return

 

$this

-

>

stringCount

()

;

   

}

   

function

 randomString

()

 

{

      

$index

 = 

rand

(

0,

count

(

$_SESSION

[

'strings'

])

-1

)

;

      

return

 

$_SESSION

[

'strings'

][

$index

]

;

   

}

   

function

 stringCount

()

 

{

      

return

 

count

(

$_SESSION

[

'strings'

])

;

   

}

}

// Ustawienie stałej JPSPAN

require_once

 

'jpspan-0.4.3/JPSpan.php'

;

// Załadowanie serwera PostOffice

require_once JPSPAN . 

'Server/PostOffice.php'

;

// Utworzenie serwera PostOffice

$S

 = & 

new

 JPSpan_Server_PostOffice

()

;

// Rejestracja klasy w serwerze

$S

-

>

addHandler

(

new

 HelloWorld

())

;

// Obsługa wyświetlania JavaScriptu po dodaniu
// ciągu ?client do URL-a serwera

if

 

(

isset

(

$_SERVER

[

'QUERY_STRING'

])

 &&

   strcasecmp

(

$_SERVER

[

'QUERY_STRING'

]

'client'

)

==0

)

 

{

  

 // Wyłączenie kompresji wynikowego JavaScriptu

  

 // (m.in. usuwania białych znaków) z powodu 

  

 // problemów wydajnościowych

   define

(

'JPSPAN_INCLUDE_COMPRESS'

,false

)

;

  

 // Wyświetlenie klienckiego JavaScriptu

   

$S

-

>

displayClient

()

;

}

else

 

{

  

 // Początek faktycznej obsługi żądań

  

 // Dołączenie obsługi błędów

  

 // błędy, ostrzeżenia i komunikaty serializowane do JS

  

 // Obsługiwanie żądań

   

$S

-

>

serve

()

;

}
?>

JPSpan i PEAR

Wersja  biblioteki  JPSpan  dla  repozy-

torium  PEAR  jest  dostępna  w  serwisie 

http://www.pearified.com.  Do  instalacji 

będzie  potrzebny  PEAR  w  nowej  wersji 

1.4,  obsługujący  inne  kanały  niż  tylko 

http://pear.php.net.  Poleceniem 

pear 

channel-discover  pearified.com

  na-

leży  dodać  kanał  do  repozytorium,  po 

czym  można  już  zainstalować  JPSpan 

poleceniem 

pear  install  pearified/

JavaScript_JPSpan

.

Explorerze z wyłączoną obsługą ActiveX, 
co  często  dotyczy  na  przykład  kafejek 
internetowych.  Może  się  także  zdarzyć, 
że aplikacja będzie działać nieco inaczej 
w  różnych  przeglądarkach  i  na  różnych 
platformach,  choć  to  samo  dotyczy 
tworzenia  tradycyjnych  aplikacji  interne-
towych.

AJAX  oferuje  spore  możliwości  inte-

rakcji,  ale  do  wielu  zadań  po  prostu  się 
nie nadaje, na przykład do dynamicznego 
rysowania  elementów  czy  obsługi  anima-
cji  –  w  takich  sytuacjach  lepiej  korzystać 

z Flasha. Warto w tym miejscu zaznaczyć, 
że  pomimo  teoretycznej  możliwości  połą-
czenia  zalet  AJAX-a  i  Flasha  w  ramach 
jednej aplikacji, złożoność takiego rozwią-
zania  jest  na  tyle  duża,  że  lepiej  używać 
tych technik osobno.

Wykorzystanie bibliotek

Istnieje  wiele  bibliotek  narzędziowych 
mających na celu ułatwienie integracji Ja-
vaScriptu  i  PHP.  Wszystkie  uwzględniają 
jakąś  metodę  przesyłania  danych,  ale 
większość oferuje dodatkowe możliwości, 

background image

AJAX

PHP Solutions Nr 1/2006

www.phpsolmag.org

51

Techniki

od  bezpośredniego  odwzorowania  metod 
klas  PHP  na  pośrednika  JavaScriptu,  po 
środowisko  tworzenia  elementów  inter-
fejsu  użytkownika.  Przyjrzyjmy  się  bliżej 
dwóm  popularnym  pakietom:  bibliotekom 
JPSpan i HTML_AJAX.

JPSpan

Najpierw zajmiemy się pakietem JPSpan 
–  jedną  z  bardziej  dojrzałych  bibliotek 
AJAX  dla  PHP,  dostępną  od  listopada 
2004  r.  Podstawową  funkcją  biblioteki 
jest  niezależna  od  przeglądarki  obsłu-
ga  mechanizmu  AJAX  bazująca  na 

XMLHttpRequest

,  z  możliwością  wyboru 

pracy  synchronicznej  lub  asynchronicz-
nej.  Dostępne  jest  wspólne,  obiektowe 
API dla JavaScriptu i PHP. JPSpan obsłu-
guje też wiele innych funkcji, na przykład 
przezroczyste  odwzorowania  obiektów 
z  bardzo  dobrą  serializacją  danych,  po-

zwalającą odwzorowywać tablice PHP na 
obiekty JavaScriptu. Strona z serwera jest 
dołączana do wynikowych stron HTML ze 
znacznikiem 

client

, generując pośrednie 

klasy JavaScript o takim samym API, jak 
klasy  PHP.  Ze  względu  na  ograniczenia 
PHP4,  wszystkie  nazwy  klas  i  metod  są 
zapisywane  małymi  literami.  Domyślny 
serwer  JPSpan  nosi  nazwę 

JPSpan_

Server_PostOffice

  i  może  służyć  do 

odwzorowywania  na  JavaScript  zarówno 
całych  klas,  jak  i  ich  części.  Korzystając 
z  serwera  w  dużym  serwisie  można 
rozważyć  dodanie  znacznika 

class

,  co 

pozwoli  ograniczyć  liczbę  klas  dołącza-
nych i rejestrowanych na serwerze, a tym 
samym  zmniejszy  koszt  przetwarzania. 
Osobiście  nie  doświadczyłem  jednak 
żadnych  problemów  wydajnościowych 
nawet przy pięciu stale zarejestrowanych 
klasach integracyjnych.

Wywołania  polegają  na  utworzeniu 

instancji  odpowiedniej  klasy  JavaScrip-
tu,  a  następnie  wywoływaniu  jej  metod. 
W  chwili  utworzenia  instancji  w  trybie 
asynchronicznym,  określana  jest  klasa 
zwrotna,  po  czym  wyniki  są  przesyłane 
jej  metodom  o  takich  samych  nazwach, 
jak  metody  pierwotnie  wywoływane. 
JPSpan dodatkowo obsługuje złożone ty-
py danych, w tym wielowymiarowe tablice 
i  obiekty,  jak  również  serializację  i  prze-
kazywanie  błędów  PHP  do  JavaScriptu 
z możliwością konfiguracji obsługi błędów 
po stronie klienta.

Strona serwera

Przyjrzyjmy  się  działaniu  JPSpan  nieco 
bliżej  na  prostym  przykładzie  typu  Hello 
World
,  wyświetlającym  losowy  napis 
w  reakcji  na  kliknięcie  i  pozwalającym 
dodawać  nowe  napisy.  Zaczynamy  od 
utworzenia klasy 

helloworld

, zawierającej 

proste metody PHP do obsługi dodawania 
napisów  do  tablicy  sesyjnej,  zwracania 
długości  tablicy  i  wyświetlania  losowe-
go  napisu  z  tablicy.  Są  to  odpowiednio 
metody 

addString()

stringCount()

 

randomString()

,  a  ich  kod  przedstawia 

Listing 1.

Praca z klasami JPSpan nie różni się 

niczym  od  obsługi  zwykłych  klas.  Trzeba 
tylko  pamiętać,  że  klasa  ta  jest  odtwa-
rzana  przy  każdym  wywołaniu  ze  strony 
JavaScriptu,  więc  utrzymywanie  danych 
składowych między wywołaniami wymaga 
pamiętania instancji klasy w ramach sesji.

Musimy  jeszcze  dołączyć  JPSpan 

i  odpowiedni  plik  serwera  PostOffice,  po 
czym  możemy  stworzyć  nową  instancję 
serwera  i  zarejestrować  w  niej  naszą 
klasę  wywołując 

$S->addHandler(new 

Listing 2. 

Klient JPSpan

<

html

>

<

head

>

  

<

title

>

Hello World w JPSpan

<

/title

>

  

<

script type=

'text/javascript'

 

src=

'jpspan_server.php?client'

><

/script

>

  

<

script

>

  // Klasa JavaScript zawierająca metody zwrotne

   

var

 

hwCallback

 = 

{

      randomstring: 

function

(

result

) {

         

document

.getElementById

(

'canvas'

)

.

innerHTML

 += '

<

p

>

'+result+'

<

/p

>

';

      },
      stringcount: 

function

(

result

) {

         

document

.getElementById

(

'count'

)

.

innerHTML

 = 

result

;

      },
      addstring: 

function

(

result

) {

         

document

.getElementById

(

'count'

)

.

innerHTML

 = 

result

;

      

}

   

}

   // Utworzenie obiektu zdalnego. Jego nazwa jest odwzorowana małymi literami, 
   // gdyż w nazwach klas i funkcji PHP4 wielkość liter nie jest rozpoznawana. 
   // Rejestrując każdą klasę na serwerze można przywrócić rozróżnianie
   // wielkości liter.

   

var

 

remoteHW

 = 

new

 helloworld

(

hwCallback

)

;

   

function

 do_addString

() {

      

remoteHW

.addstring

(

document

.getElementById

(

'string'

)

.

value

)

;

        

document

.getElementById

(

'string'

)

.

value

 = 

''

;

   

}

  

<

/script

>

<

/head

>

<

body onLoad=

"remoteHW.stringcount()"

>

  

<

input type=

"button"

 

name=

"check"

 

value=

"Pokaż losowy napis"

 

onclick=

 

   

"remoteHW.randomstring(); return false;"

>

  

<

div

>

Liczba losowych napisów: 

<

span id=

"count"

><

/span

><

/div

>

  

<

div id=

"canvas"

 

style=

"border: solid 1px black; margin: 1em; padding: 1em;"

><

/div

>

  

<

div

>

    Podaj nowy napis:
    

<

input type=

"text"

 

name=

"string"

 

id=

"string"

 

size=

"20"

>

    

<

input type=

"button"

 

name=

"check"

 

value=

"Dodaj nowy napis"

 

onclick=

 

     

"do_addString(); return false;"

>

  

<

/div

>

<

/body

>

<

/html

>

HTML_AJAX

a nazwy klas

Nazwy  klas  zwracane  przez  PHP4  są 

zawsze zapisane małymi literami. Jeśli 

koniecznie  chcesz  zachować  rozróż-

nienie  wielkości  liter  lub  potrzebujesz 

zgodności  między  PHP4  i  PHP5, 

musisz  skorzystać  z  dodatkowych  pa-

rametrów  metody 

registerClass()

określających  nazwę  klasy  rejestrowa-

nej w JavaScripcie oraz tablicę ekspor-

towanych metod:

$server = new HTML_AJAX_Server();
$hello = new HelloWorld();
$methods = array('foo','bar');
$server->registerClass($hello, 

§

   'Example', $methods);

background image

PHP Solutions Nr 1/2006

www.phpsolmag.org

52

Techniki

HelloWorld())

.  Pozostaje  jeszcze  tylko 

określić,  czy  chcemy  wysłać  kliencki  kod 
JavaScript,  czy  też  obsługiwać  żądania. 
Jak  widać  na  Listingu  1,  obiektowe  API 
JPSpan bardzo ułatwia przygotowania po 
stronie serwera.

Strona klienta

Teraz zajmiemy się stroną kliencką naszej 
prostej  aplikacji  –  Listing  2  przedstawia 
kod klienta. Od razu zwraca uwagę wyraź-
ne rozdzielenie kodu HTML i PHP podczas 
pracy z JPSpan. Wystarczy tylko umieścić 
w ramach strony HTML poniższy, automa-
tycznie  generowany  kod,  odpowiedzialny 
za faktyczne połączenie HTML i PHP:

<script type='text/javascript'
  src='jpspan_server.php?client'>
</script>

Przy  odrobinie  pracy  nagłówkami  po-
zwala  to  wygodnie  obsłużyć  składowanie 
znanych  informacji  po  stronie  klienta, 
co  bardzo  przydaje  się  na  przykład  przy 
dodawaniu  do  istniejącego  serwisu  pola 
autouzupełniania.

Następnie tworzymy klasę JavaScriptu 

o nazwie 

hwCallback

, zawierającą metody 

zwrotne  zastępujące  treść  odpowiednich 
elementów 

<div>

  wartościami  podanymi 

przez  serwer  z  wykorzystaniem  właści-
wości 

innerHTML

. Pozostaje już tylko utwo-

rzyć zdalny obiekt:

var remoteHW=new helloworld(hwCallback);

Klasa 

helloworld

  jest  wyeksportowaną 

klasą  PHP,  którą  wcześniej  utworzyliśmy 
po  stronie  serwera.  Nazwa  klasy  zawie-
ra  wyłącznie  małe  litery,  gdyż  PHP4  nie 
rozróżnia  wielkości  liter  w  nazwach  klas 
i funkcji. Reszta kodu z Listingu 2. to już 
tylko  dodanie  formularza  HTML  z  odpo-
wiednimi metodami obsługi – i już może-
my się pobawić naszą pierwszą aplikacją 
stworzoną w technologii AJAX.

HTML_AJAX

Biblioteka  HTML_AJAX  daje  znacznie 
większe  możliwości  niż  JPSpan,  ale  dla 
uproszczenia przykładu skorzystamy z po-
dobnej  konfiguracji,  co  w  przypadku  po-
przedniego  przykładu:  zewnętrzna  stro-
na  serwera  generuje  kod  pośredni  Java-
Scriptu,  który  jest  dołączany  i  faktycznie 
wykonywany  na  stronie  HTML.  HTML_
AJAX potrafi też generować cały kod po-
średnika i serwera w jednym skrypcie, ale 

Listing 3. 

HTML_AJAX może posłużyć do pobierania treści z innej strony na tym 

samym serwerze

<

html

>

<

head

>

  

<

script type=

'text/javascript'

 

src=

"server.php?client=main"

><

/script

>

  

<

script type=

'text/javascript'

 

src=

"server.php?client=dispatcher"

><

/script

>

  

<

script type=

'text/javascript'

 

src=

"server.php?client=HttpClient"

><

/script

>

  

<

script type=

'text/javascript'

 

src=

"server.php?client=Request"

><

/script

>

  

<

script type=

'text/javascript'

 

src=

"server.php?client=json"

><

/script

>

<

/head

>

<

body

>

<

script type=

"text/javascript"

>

  

function

 clearTarget() {

    

document

.getElementById

(

'target'

)

.

innerHTML

 = 

'clear'

;

  

}

  // Operacja 'grab' jest najprostszym zastosowaniem HTML_AJAX, polegającym na
  // wysłaniu żądania do strony i pobraniu wyników. Można jej używać w trybie 
  // synchronicznym lub asynchronicznym (z wywołaniem zwrotnym).

  

var

 

url

 = 

'README'

;

  

function

 grabSync

() {

    

document

.getElementById

(

'target'

)

.

innerHTML

 = HTML_AJAX.grab

(

url

)

;

  

}

  

function

 grabAsync

() {

    HTML_AJAX.grab

(

url

,

grabCallback

)

;

  

}

  

function

 grabCallback

(

result

) {

    

document

.getElementById

(

'target'

)

.

innerHTML

 = 

result

;

  

}

  // Operacja 'replace' może działać albo na adresie (tak jak grab), albo na 
  // zdalnej metodzie. W tym drugim przypadku trzeba ustawić defaultServerUrl na adres
  // eksportujący wywoływaną metodę. Obecnie replace używa wyłącznie synchronicznych
  // wywołań AJAX - wywołania asynchroniczne mogą się pojawić w przyszłości.

  

function

 replaceUrl

() {

    HTML_AJAX.replace

(

'target'

,

url

)

;

  

}

<

/script

>

<

ul

>

  

<

li

><

a href=

"javascript:clearTarget()"

>

Czyszczenie pola docelowego

<

/a

><

/li

>

  

<

li

><

a href=

"javascript:grabSync()"

>

Przykład pobrania synchronicznego

<

/a

><

/li

>

  

<

li

><

a href=

"javascript:grabAsync()"

>

Przykład pobrania asynchronicznego 

<

/a

><

/li

>

  

<

li

><

a href=

"javascript:replaceUrl()"

>

Zastąpienie zawartości treścią pobraną 

spod wskazanego adresu

<

/a

><

/li

>

<

/ul

>

<

div style=

"white-space: pre; padding: 1em; margin: 1em; width: 600px; height:

  300px; border: solid 2px black; overflow: auto;"

 

id=

"target"

>

Pole docelowe

<

/div

>

<

/body

>

<

/html

>

Listing 4. 

Klasa implementująca serwer podpowiedzi (plik suggest.class.php)

class

 suggest 

{

   

function

 suggest

()

 

{

      

require_once

 

'pear_array.php'

;

      

$this

-

>

strings = 

$aPear

;

   

}

   

function

 getString

(

$input

=

''

)

 

{

      

if

 

(

$input

 == 

''

)

 

return

 

''

;

      

$input

 = 

strtolower

(

$input

)

;

      

$suggestStrings

=

array

()

;

      

foreach

 

(

$this

-

>

strings as 

$string

)

 

{

         

if

 

(

strpos

(

strtolower

(

$string

)

,

$input

)

 === 0

)

 

{

            

$suggestStrings

[]

 = 

$string

;

         

}

      

}

      

return

 

$suggestStrings

;

   

}

}

background image

AJAX

PHP Solutions Nr 1/2006

www.phpsolmag.org

53

Techniki

nie  polecałbym  tej  metody,  gdyż  tracimy 
wtedy  możliwość  lokalnego  składowania 
wcześniej  wygenerowanego  kodu  Java-
Scriptu.

Instalacja  pakietu  HTML_AJAX  jest 

bardzo  prosta  –  wystarczy  wykonać  po-
lecenie 

pear  install  HTML_AJAX-alpha

Jeśli  na  serwerze  nie  masz  narzędzia 
PEAR,  możesz  po  prostu  pobrać  pakiet 
http://pear.php.net/package/HTML_AJAX
rozpakować  go  i  ręcznie  umieścić  w  wy-
branym  katalogu,  wymienionym  oczywi-
ście  w  ramach  parametru 

include_path

 

php.ini.

Rysunek 3. 

Serwer podpowiedzi w akcji

R

E

K

L

A

M

A

Serwer

Strona  HTML_AJAX  działająca  na  serwe-
rze jest bardzo prosta – jej działanie polega 
na utworzeniu instancji serwera 

HTML_AJAX_

Server

, zarejestrowaniu wszystkich ekspor-

towanych klas (zwanych tu stubs, czyli na-
miastkami)  i  obsługiwaniu  nadchodzących 
żądań. Istnieją trzy możliwe rodzaje żądań:

Ÿ

   Żądanie 

klienckie, 

zawierające 

?client=all

  w  ciągu  zapytania. 

Zamiast 

all

  można  też  podać  jedną 

z części składowych klienta. Obecnie 
obsługiwanych  jest  pięć  części  (

Ma-

in

Dispatcher

HttpClient

Request

 

JSON

),  a  w  przyszłości  zostanie  do-

dana obsługa dodatkowych, opcjonal-
nych części.

Ÿ

   Żądanie  generowanej  namiastki,  za-

wierające 

?stub=nazwaklasy

  w  ciągu 

zapytania (można też podać wartość 

all

).

Ÿ

   Żądanie  AJAX,  zawierające  w  ciągu 

zapytania 

?c=nazwaklasy&m=nazwame

tody

.

Pierwsze  dwa  rodzaje  żądań  można 
łączyć  w  jednym  żądaniu,  lecz  trzeba 
pamiętać,  że  wiąże  się  to  z  pewnym 
kompromisem: mniej żądań to mniej po-
łączeń  z  serwerem,  ale  z  drugiej  strony 
generowane  namiastki  zmieniają  się 
częściej  od  danych  klienckich,  co  może 
negatywnie  wpłynąć  na  efektywność 
lokalnego  składowania  kodu.  Ostrożnie 
należy  też  korzystać  z  żądań 

stub=all

gdyż  namiastka  dla,  na  przykład,  dzie-
sięciu  klas  może  już  być  spora.  W  ko-
lejnej  wersji  biblioteki  HTML_AJAX  po-
jawi się możliwość podawania wielu klas 
w  ramach  jednego  żądania  namiastki 
w  postaci  listy  rozdzielanej  przecinkami, 
a więc 

stub=test,test2

.

background image

PHP Solutions Nr 1/2006

www.phpsolmag.org

54

Techniki

Łatwa aktualizacja

treści bez AJAX-a

HTML_AJAX pozwala też korzystać z pod-
stawowych  możliwości  AJAX-a  wyłącznie 
po  stronie  klienckiego  JavaScriptu,  dzięki 
czemu  można  bardzo  szybko  dodać  do 
strony proste elementy używające AJAX-a 
lub  włączyć  HTML_AJAX  do  istniejącego 
środowiska. Typowym zastosowaniem jest 
aktualizacja zawartości elementu HTML za 
pomocą  treści  wygenerowanej  przez  inną 
stronę  PHP,  co  daje  elastyczność  ramek 

<iframe>

 bez ich wad (patrz Listing 3).

Po dołączeniu do strony niezbędnego 

kodu  JavaScriptu  można  pobierać  treść 
ze  wskazanego  adresu  na  serwerze 
w trybie synchronicznym za pomocą 

HTML_

AJAX.grab(url)

 lub w trybie asynchronicz-

nym za pomocą 

HTML_AJAX.grab(url,grab

Callback)

,  gdzie  argument 

grabCallback

 

wskazuje  funkcję  zwrotną  automatycznie 
wywoływaną  przez  HTML_AJAX  po  po-
braniu  treści.  Można  też  wywołać 

HTML_A

JAX.replace('target',url)

,  by  zastąpić 

zawartość  elementu  o  identyfikatorze 
podanym  jako 

target

  treścią  pobraną 

z  adresu 

url

.  Ze  względów  bezpieczeń-

stwa można w ten sposób pobierać treści 
wyłącznie  z  adresów  na  tym  samym 
serwerze.  Nie  jest  to  jednak  ograniczenie 
biblioteki  HTML_AJAX,  lecz  ograniczenie 
przeglądarki, mające na celu zapobieganie 
atakom cross site scripting (XSS).

Przykład w stylu Google 

Suggest z HTML_AJAX

Pora  na  nieco  bardziej  zaawansowany 
przykład:  pole  podpowiedzi  podobne  do 
mechanizmu  Google  Suggest  (http://
www.google.com/webhp?complete=1&hl=
en
),  ale  służące  do  wyszukiwania  pakie-
tów  PEAR  (patrz  też  Ramka  Wzorzec 
AJAX Suggest
).

HTML_AJAX pozwala zarządzać inte-

rakcją klienta z serwerem w kilku prostych 
wierszach kodu. Jak widać na Listingu 4., 
klasa  obsługująca  stronę  PHP  jest  dość 
prosta. Dla potrzeb przykładu korzystamy 

z  tablicy  zawierającej  możliwe  hasła  wy-
szukiwania, choć w rzeczywistej aplikacji 
byłyby  one  prawdopodobnie  pobierane 
z  bazy  danych.  Lista  wyszukiwania  wy-
maga  tylko  jednej  metody 

getString()

która porównuje przekazany ciąg znaków 
z  kolejnymi  pozycjami  tablicy.  Pasujące 
elementy  są  następnie  kopiowane  do 
tablicy  wyników,  która  jest  ostatecznie 
zwracana.

Teraz uruchamiamy serwer usługi (Li-

sting  5).  W  tym  przykładzie  skorzystamy 
z klasy 

AutoServer

, która rozszerza pod-

stawową  klasę  serwera  i  dodaje  metodę 
inicjalizacyjną  dla  każdej  klasy.  Pozwala 
to  zarządzać  eksportem  kilku  klas  PHP 
za pomocą jednego serwera – wystarczy 
ustawić  wartość  zmiennej 

$initMethods

 

na 

true

 i nadać metodom inicjalizacyjnym 

nazwy  w  postaci 

initNazwaKlasy

,  co  dla 

naszej  klasy  oznacza  utworzenie  meto-
dy 

initSuggest()

.  Wykorzystanie  klasy 

AutoServer

  w  tak  prostym  przykładzie  to 

oczywiście strzelanie z armaty do muchy, 
ale  pokazuje  ciekawe  możliwości  biblio-
teki HTML_AJAX, które mogą się bardzo 
przydać w większych projektach.

I  to  by  było  na  tyle  po  stronie  PHP. 

Jeśli  nasza  metoda  działa  poprawnie 
i  nie  generuje  żadnych  błędów,  to  nie 
musimy jej więcej zmieniać i możemy się 
zająć  implementacją  po  stronie  klienta. 
Rzut  oka  na  Listing  6  pokazuje,  że  za-
czynamy  od  dołączenia  JavaScriptu  dla 
serwera:

<script type='text/javascript'
  src='auto_server.php?client=
  all&stub=suggest'></script>

Następnie  tworzymy  kod  obsługi  żądania 
w postaci metody 

do_suggest()

 oraz funk-

cję zwrotną do wyświetlania wyników, a na 
koniec  tworzymy  nową  instancję  zdalnej 
wyszukiwarki  AJAX.  Reszta  kodu  to  po 
prostu  formularz  z  jednym  polem  teksto-
wym  i  elementem 

<div>

  do  wyświetlania 

wyników. Dodanie do pola tekstowego ob-
sługi  zdarzenia 

onkeyup="do_suggest(); 

return  false;"

  powoduje  wywołanie 

funkcji 

do_suggest()

  po  każdym  zwolnie-

niu klawisza (zdarzenie 

onkeypress

 byłoby 

obsługiwane zbyt wcześnie).

Jak to działa?

Każda  zmiana  wartości  pola  tekstowego 
powoduje wywołanie funkcji 

do_suggest()

która  z  kolei  wywołuje  metodę 

remoteSu

ggest.getstring()

  javascriptowej  klasy 

Wzorzec AJAX Suggest

Podpowiadanie użytkownikom możliwych sposobów uzupełnienia tekstu wprowadzanego 

w polu tekstowym uatrakcyjnia stronę i ułatwia korzystanie z niej – wystarczy zapropono-

wać kilka słów lub wyrażeń, które mogą pasować do danych wprowadzanych przez użyt-

kownika.  Do  implementacji  mechanizmu  uzupełniania  służy  najczęściej  połączenie  pola 

tekstowego z listą rozwijaną wraz z synchronizacją tych elementów.

Użytkownik może podać dowolny tekst, a bieżąca pozycja na liście będzie odpowia-

dać dotychczas wprowadzonemu ciągowi. Możliwe jest również wybranie elementu z listy, 

co spowoduje zastąpienie zawartości pola tekstowego wybranym hasłem.

Implementacja tego wzorca wiąże się z reguły z wykorzystaniem zwykłego pola tek-

stowego i stworzeniem niewidocznej z początku warstwy (elementu 

<div>

), w której będą 

umieszczane  kolejne  podpowiedzi.  Do  pola  tekstowego  trzeba  dołączyć  funkcję  obsługi 

zdarzenia,  kontrolującą  zawartość  pola  w  celu  zapewnienia  poprawnego  wyświetlania 

pasujących podpowiedzi na liście.

Nie będziemy oczywiście wysyłać żądania po każdym naciśnięciu klawisza – lepiej sko-

rzystać z techniki dławienia zgłoszeń. W tym przypadku przeglądarka sprawdza co (na przy-

kład) 350 milisekund, czy zawartość pola uległa zmianie – jeśli tak, to do serwera wysyłane 

jest odpowiednie żądanie. Pozwala to ograniczyć liczbę żądań (i tym samym zaoszczędzić 

nieco pasma), a przy okazji nie będzie przeszkadzać szybko piszącemu użytkownikowi.

Serwer odpowiada na żądanie wysyłając uporządkowaną listę pasujących podpowie-

dzi, którą po stronie klienta odbiera funkcja zwrotna. Funkcja ta wprowadza w modelu doku-

mentu odpowiednie zmiany, by użytkownik mógł przejrzeć nowe podpowiedzi i ewentualnie 

wybrać jedną z nich. Z każdą pozycją listy skojarzona jest funkcja obsługi kliknięcia, odpo-

wiadająca za aktualizację pola tekstowego treścią wybranej przez użytkownika pozycji.

Tryb synchroniczny i asynchroniczny

Biblioteki  JPSpan  i  HTML_AJAX  obsługują  pracę  zarówno  w  trybie  asynchronicznym

(z wywołaniami zwrotnymi), jak i synchronicznym (z bezpośrednim zwracaniem wartości). 

Ogólnie lepiej jest stosować operacje asynchroniczne, gdyż wywołania synchroniczne 

mogą  wstrzymywać  pracę  interfejsu  użytkownika  w  oczekiwaniu  na  odpowiedź  drugiej 

strony. Oczywiście niekiedy jest to zachowanie pożądane, ale przesyłając większe ilości 

danych trzeba wtedy zawsze pamiętać o wyświetleniu komunikatu typu proszę czekać

Wywołania  synchroniczne  są  znacznie  łatwiejsze  do  oprogramowania,  ale  mimo  to 

lepiej oprzeć się pokusie bezkrytycznego ich stosowania. Żądania AJAX w sieci lokalnej 

najczęściej trwają nie dłużej niż 50 ms, ale w przypadku przesyłania danych przez Internet 

czas  ten  najczęściej  wzrasta  do  ponad  250  ms.  Oznacza  to,  że  użytkownik  nie  będzie 

w stanie skorzystać z żadnego elementu strony czy nawet przełączyć się na inną zakładkę 

przeglądarki przez ćwierć, a często i pół sekundy.

background image

AJAX

PHP Solutions Nr 1/2006

www.phpsolmag.org

55

Techniki

HTML_AJAX.  Ta  komunikuje  się  z  ser-
werem,  który  odsyła  tablicę  pasujących 
podpowiedzi,  przekazywaną  następnie 
funkcji zwrotnej, która kończy cały proces 
dokonując  niezbędnych  zmian  w  struktu-
rze dokumentu i wyświetlając podpowiedzi 
w ramach elementu 

<div>

.

W  ten  sposób  mamy  już  działający, 

choć  nie  najpiękniejszy  przykład.  Po 
pierwsze,  funkcja  autouzupełniania  prze-
glądarki w tym przypadku tylko przeszka-
dza.  Możemy  ją  jednak  łatwo  wyłączyć 
dodając  do  pola  tekstowego  atrybut 

autocomplete="off"

.  Po  drugie,  sposób 

wyświetlania  podpowiedzi  pozostawia 
bardzo  wiele  do  życzenia.  Spróbujmy 
więc ulepszyć funkcję zwrotną – Listing 7. 
przedstawia poprawiony kod.

Po  usunięciu  poprzedniej  zawartości 

elementu 

resultDiv,

 wyświetlającego wy-

niki, opakowujemy każdy wynik w osobny 
znacznik 

<span>

 dla uzyskania lepszej kon-

troli  nad  formatowaniem,  po  czym  w  pętli 

for

  dodajemy  kolejne  wyniki  do  warstwy 

resultDiv

.  Etap  opakowania  wykonujemy 

wywołując  metody  JavaScriptu 

document.

createElement("span")

  i 

appendChild()

Dla zwiększenia czytelności można popra-
cować  nad  stylem  (Listing 8).  Najważniej-
szy  jest  tu  wpis  powodujący  wyświetlanie 
podpowiedzi  jedna  pod  drugą  zamiast 
w jednym wierszu:

#suggestions span {
  display: block;
}

Listing 8. 

Style CSS dla klienta 

podpowiedzi

{

  

padding

: 0;

  

margin

: 0;

  

font-family

 : Arial, sans-serif;

}

#suggestions 

{

  

max-height

: 200px;

  

width

 : 306px;

  

border

: 1px solid #000;

  

overflow

 : auto;

  

margin-top

 : -1px;

  

float

 : left;

}

#string 

{

  

width

 : 300px;

  

font-size

 : 13px;

  

padding-left

 : 4px;

}

#suggestions span 

{

  

display

: block;

}

Listing 5. 

Serwer podpowiedzi w HTML_AJAX (plik auto_server.php)

session_start

()

;

require_once

 

'HTML/AJAX/Server.php'

;

class

 AutoServer 

extends

 HTML_AJAX_Server 

{

   // Ustawienie tego znacznika jest konieczne, by korzystać z metod inicjalizacyjnych

   

var

 

$initMethods

 = 

true

;

  

 // Metody inicjalizacyjne dla klasy podpowiedzi

   

function

 initSuggest

()

 

{

      

require_once

 

'suggest.class.php'

;

         

$suggest

 = 

new

 suggest

()

;

         

$this

-

>

registerClass

(

$suggest

)

;

   

}

}

$server

 = 

new

 AutoServer

()

;

$server

-

>

handleRequest

()

;

Listing 6. 

Prosty klient podpowiedzi

<

html

>

<

head

>

  

<

title

>

HTML_AJAX Suggest

<

/title

>

  

<script type='text/javascript' src='auto_server.php?client=all&stub=suggest'>

  </script>

  

<

script

>

   

function

 do_suggest

() {

      remoteSuggest.getstring

(

document

.getElementById

(

'string'

)

.

value

)

;

   

}

  
 // Stworzenie tablicy asocjacyjnej do składowania metod zwrotnych

   

var

 

suggestCallback

 = 

{

      getstring: 

function

(

result

) {

         

document

.getElementById

(

'suggestions'

)

.

innerHTML

 = 

result

;

      

}

   }

  // Utworzenie obiektu zdalnego. Jego nazwa jest odwzorowana małymi literami,
  // gdyż w nazwach klas i funkcji PHP4 wielkość liter nie jest rozpoznawana. 
  // Rejestrując każdą klasę na serwerze można przywrócić rozróżnianie
  // wielkości liter.

   

var

 

remoteSuggest

 = 

new

 suggest

(

suggestCallback

)

;

  

<

/script

>

<

/head

>

<

body

>

  

<

div

>

    Podaj nazwę pakietu PEAR:
    

<

input type=

"text"

 

name=

"string"

 

id=

"string"

 

size=

"20"

 

onkeyup=

"

      do_suggest(); return false;"

>

    

<

input type=

"button"

 

name=

"check"

 

value=

"Podpowiedz..."

 

onclick=

"do_suggest();

      return false;"

>

  

<

/div

>

  

<

div id=

"suggestions"

>

&nbsp;

<

/div

>

<

/body

>

<

/html

>

Listing 7. 

Ulepszona metoda zwrotna

var

 

suggestCallback

 = 

{

   getstring: 

function

(

resultSet

) {

      

var

 

resultDiv

 = 

document

.getElementById

(

'suggestions'

)

;

      resultDiv.innerHTML = '';
      

for

(

var

 

f

=0; 

f

<

resultSet.length

; ++

f

){

         

var

 

result

=

document

.createElement

(

"span"

)

;

         

result

.

innerHTML

 = 

resultSet

[

f

]

;

         

resultDiv

.appendChild

(

result

)

;

      

}

   }
}

background image

PHP Solutions Nr 1/2006

www.phpsolmag.org

56

Techniki

Warstwa  wyników  powinna  początkowo 
być  ukryta,  więc  w  pliku  CSS  podajemy 
dla  niej  atrybut 

display:  none

,  który  po 

otrzymaniu wyników przełączamy na war-
tość 

block

 w ramach metody zwrotnej:

resultDiv.style.display='block';
if (!resultSet)
  resultDiv.style.display='none';

Dodatkowe  sprawdzenie  zapobiega  wy-
świetleniu warstwy, gdy serwer nie zwróci 
żadnych podpowiedzi.

Pora dodać do wyników nieco interak-

cji  –  na  razie  tylko  widzimy  podpowiedzi, 
ale  nie  możemy  wybierać  pozycji  z  listy. 
Efekt  wybierania  osiągniemy  dodając 
obsługę zdarzeń do elementu 

<span>

 każ-

dego wyniku:

result.onmouseover = highlight;
result.onmouseout = unHighlight;
result.onmousedown = selectEntry;

Spowoduje  to  dodanie  funkcji  Java-
Script  do  każdego  zdefiniowanego 
zdarzenia.  Działanie  funkcji 

highlight()

 

unHighlight()

  polega  po  prostu  na 

zmianie klasy CSS elementu 

<span>

:

function highlight (){
  this.className='highlight';
}

Klasa CSS 

highlight

 wygląda tak:

.highlight {
  background-color: 0000ff;
  color: fff;
}

Minimalna  wersja  naszej  wyszukiwarki 
powinna  obsługiwać  zastąpienie  zawar-
tości  pola  tekstowego  podpowiedzią 
klikniętą  przez  użytkownika.  Pole  ma 
identyfikator 

string

, więc podmiana jego 

treści jest prosta:

function selectEntry () {
  document.getElementById('string')
    .value = this.innerHTML;
}

Wartość pola tekstowego jest zastępowa-
na zawartością danego elementu 

<span>

czyli jednym z wyników zwróconych przez 
serwer AJAX.

Całość  wygląda  już  znacznie  lepiej 

(Rysunek  3).  Przykład  działa  poprawnie, 

Listing 9. 

Ostateczna wersja klienta podpowiedzi

<

html

>

<

head

>

  

<

title

>

Podpowiedzi HTML_AJAX

<

/title

>

  

<

link rel=

"StyleSheet"

 

type=

"text/css"

 

href=

"suggest3.css"

 /

>

  

<script type='text/javascript' src='auto_server.php?client=all&stub=suggest'>

  

<

/script

><

script

>

   

var

 

string

 = '';

   

var

 

oldstring

 = '';

   

var

 

timeout

1000

/* czas w ms między sprawdzeniami - dobrą wartością jest 250*/

   

function

 do_suggest

() {

      

string

 = 

document

.getElementById

(

'string'

)

.

value

;

      

if

 (

string

 != oldstring

)

 

{

         /* Przy pustym polu nie wysyłaj żądania...  */

         

if

 (

string

) {

            remoteSuggest.getstring(

string

)

;

         

}

         /* ... tylko ukryj warstwę */

         

else

 

{

            

document

.getElementById

(

'suggestions'

)

.style.display = 

'none'

;

         

}

         

oldstring

 = 

string

;

      

}

      

window

.setTimeout

(

'do_suggest()'

timeout

)

;

   

}

   // Stworzenie tablicy asocjacyjnej do składowania metod zwrotnych

   

var

 

suggestCallback

 = 

{

      getstring: 

function

(

resultSet

) {

         

var

 

resultDiv

 = 

document

.getElementById

(

'suggestions'

)

;

         

resultDiv

.innerHTML = 

''

;

         

resultDiv

.style.display = 

'block'

;

         

if

 

(

!

resultSet

)

 

resultDiv

.style.display = 

'none'

;

         

else

{

            

for

(

var

 

f

=0; 

f

<

resultSet.length; ++f

){

 

              

var

 result=

document

.

createElement

(

"span"

)

;

 

              

result.innerHTML = resultSet

[

f

]

;

 

              

result.onmouseover = highlight;

 

              

result.onmouseout = unHighlight;

 

              

result.onmousedown = selectEntry;

 

              

resultDiv.appendChild

(

result

)

;

 

           

}

         }
      }
   }

   // Utworzenie obiektu zdalnego

 

  

var

 remoteSuggest = 

new

 suggest

(

suggestCallback

)

;

   // Funkcje obsługi interakcji

   

function

 

highlight

   

() {

 

this

.

className

 =

 'highlight'; 

}

   

function

 

unHighlight

 

()

 

{

 

this

.

className

 

=

 ''; 

}

   

function

 

selectEntry

 

()

 

{

 

document

.getElementById(

'string'

)

.

value

 =

this

.innerHTML;

 

  

}

 

  

<

/script

>

<

/head

>

<

body onload=

"do_suggest()"

>

  

<

h1

>

HTML_AJAX Example: Suggest

<

/h1

>

  

<

p

>

Uwaga: czas między sprawdzeniami jest ustawiony na 1000ms dla celów

  demonstracyjnych. W praktyce lepiej korzystać z wartości rzędu 350ms.

<

/p

>

  

<

div id=

"error"

><

/div

>

  Podaj nazwę pakietu PEAR:
  

<

form method=

"get"

 

id=

"suggest"

>

    

<

input type=

"text"

 

name=

"string"

 

id=

"string"

 

size=

"20"

 

autocomplete=

"off"

>

    

<

input type=

"button"

 

name=

"check"

 

value=

"Podpowiedz..."

 

onkeyup=

"do_suggest();

      return false;"

>

    

<

div id=

"suggestions"

>

&nbsp;

<

/div

>

  

<

/form

>

<

/body

>

<

/html

>

background image

AJAX

PHP Solutions Nr 1/2006

www.phpsolmag.org

57

Techniki

ale do serwera wysyłanych jest zbyt wie-
le  żądań  –  jeśli  użytkownik  wpisze  coś 
bardzo  szybko,  a  korzysta  z  powolnego 
łącza,  to  może  dochodzić  do  wysyłania 
kolejnego  żądania  podpowiedzi  przed 
otrzymaniem  odpowiedzi  na  żądanie 
poprzednie.  Spróbujmy  jakoś  temu  za-
pobiec.

Skorzystajmy  z  techniki  zwanej 

dławieniem  zgłoszeń  (ang.  submission 
throttling
).  W  tym  przypadku  kliencki 
JavaScript będzie co jakiś czas (na przy-
kład  co  350  milisekund)  sprawdzał,  czy 
wartość  pola  tekstowego  uległa  zmianie 
–  jeśli  tak,  to  zostanie  wysłane  żądanie 
do serwera (patrz Listing 9). Dodatkowo 
sprawdzimy  też,  czy  pole  nie  jest  przy-
padkiem  puste  –  w  takiej  sytuacji  nie 
wysyłamy  żądania  i  ukrywamy  warstwę 
wyników.

Jak  widać,  dodanie  imponującej  in-

terakcji  do  formularzy  i  aplikacji  nie  jest 
wcale trudne. Nasz prosty przykład moż-
na  oczywiście  rozbudować,  dodając  na 
przykład możliwość przechodzenia po li-
ście wyników klawiszami kursora czy też 
obsługę  lokalnego  składowania  danych, 
pozwalającego oszczędzić sporo pasma.

Śledzenie kodu AJAX

Podczas  eksperymentów  z  AJAX-em 
zauważysz  zapewne,  że  technika  ta 
wymaga  nowego  podejścia  do  śledzenia 
kodu. Nie wystarczy już śledzić kodu PHP 
–  trzeba  jeszcze  pilnować  JavaScriptu 
i  obsługiwanej  przez AJAX-a  komunikacji 
między klientem i serwerem. Na szczęście 
nie jest to trudne.

Przede  wszystkim,  każdy  moduł 

kodu  należy  testować  osobno.  Pracując 
w JavaScripcie dobrze jest stworzyć funk-
cję  pomocniczą,  na  przykład  prościutki 
odpowiednik 

print_r()

 z PHP:

function print_r(input) {
  var ret;
..for(var i in input) {
....ret += "["+i+"] = "+input[i]+"\n";
..}
..alert(ret);
}

Możliwości  obserwacji  w  bibliotece 
JPSpan pozwalają też rejestrować między 
innymi  błędy  i  udane  wywołania  funkcji 
AJAX.  W  domyślnej  konfiguracji  serwera 
błędy PHP są przekazywane jako ostrze-
żenia JavaScript. Niekiedy można też na-
trafić na ostrzeżenia wynikające z błędów 

JavaScriptu, co wynika z tego, że JPSpan 
przechwytuje również te błędy i zgłasza je 
jako ostrzeżenia.

Podczas pracy z HTML_AJAX można 

dodać własną funkcję obsługi błędu, która 
będzie  podmieniać  zawartość  elementu 

<div>

 o identyfikatorze 

error

:

HTML_AJAX.onError = function(e) {
  msg = "\nn";
  for(var i in e) {
    msg += i + ':' + e[i] +"\n";
  }
  document.getElementById('error').
    innerHTML += msg;
}

Pozwoli  to  przechwytywać  wszystkie  błę-
dy AJAX – najczęściej będą to zwyczajne 
błędy PHP, ale mogą się też pojawiać błę-
dy 404, błędy wygaśnięcia i inne.

Na  koniec  zalecałbym  tworzenie  apli-

kacji  dla  przeglądarki  Firefox,  a  dopiero 
potem  testowanie  ich  w  Internet  Explo-
rerze.  Firefox  ma  nieporównanie  lep-
sze  narzędzia  programistyczne  od  IE, 
a w dodatku oferuje bardzo wiele przydat-
nych rozszerzeń.

Podsumowanie

Wiedząc już czym jest AJAX i jak z niego 
korzystać,  możesz  rozważyć  zastosowa-
nie tej techniki w swoich stronach. Prawi-
dłowe  używanie  AJAX-a  pozwala  często 
osiągnąć imponujące wyniki, ale nie zna-
czy to, że należy bezkrytycznie stosować 
tę  technikę  we  wszystkich  witrynach.  Za-
wsze trzeba mieć na uwadze podstawowe 
przeznaczenie danego serwisu – być mo-
że w konkretnym przypadku lepiej byłoby 
dopracować  nawigację  lub  prezentację 

treści, niż dodawać interakcję korzystającą 
z AJAX-a. Koniecznie trzeba też uwzględ-
niać  docelowych  odbiorców  –  jeśli  więk-
szość użytkowników z różnych względów 
ma  wyłączoną  obsługę  JavaScriptu,  to 
wprowadzenie  AJAX-a  raczej  nie  będzie 
dobrym pomysłem. Podobną rolę odgrywa 
skala  –  aplikacja  wyposażona  w AJAX-a 
znacznie lepiej sprawdzi się w przypadku 
niewielkich serwisów intranetowych (gdzie 
łatwo  rozwiązać  problemy  konfiguracyjne 
i ujednolicić przeglądarki) niż w przypadku 
rozbudowanych,  publicznie  dostępnych 
witryn.  Krótko  mówiąc,  wprowadzenie 
AJAX-a  może  dać  dobre  wyniki  pod 
warunkiem,  że  nadrzędnym  celem  pozo-
stanie uzyskanie jak najlepszych walorów 
użytkowych. 

n

Joshua  Eichorn  tworzy  serwisy  PHP 

od  siedmiu  lat.  Jest  autorem  phpDocu-

mentor  –  wielokrotnie  nagradzanego 

i  szeroko  używanego  narzędzia  doku-

mentującego  kod  PHP.  Jest  też  szefem 

projektu  HTML_AJAX,  dostarczającego 

implementację techniki AJAX dla repozy-

torium PEAR. Obecnie pracuje jako star-

szy  architekt  oprogramowania  w  firmie 

Uversa  Inc.,  gdzie  tworzy  dla  klientów 

unikatowe  rozwiązania.  Technikę  AJAX 

stosował  jeszcze  przed  jej  oficjalnym 

opracowaniem  i  popularyzacją.  Mieszka 

w Phoenix w stanie Arizona (USA).

Kontakt: josh@bluga.net

Werner M. Krauß programuje w PHP od 

1999  r.  Gdy  nie  gra  na  gitarze,  zajmuje 

się  tworzeniem  dokumentacji  dla  frame-

worka Seagull.

Kontakt: werner.krauss@hallstatt.net

O autorze

Komunikat ładowania

Uruchamiając przykłady z HTML_AJAX zauważysz, że przy każdym wywołaniu AJAX po-

jawia się czerwone okienko z komunikatem loading. Powiadomienie to jest automatycznie 

wyświetlane  przez  HTML_AJAX  i  jest  po  prostu  warstwą  o  określonym  identyfikatorze, 

tworzoną jeśli wcześniej nie istniała. Jeśli więc chcesz zmienić ten komunikat, wystarczy 

gdzieś w kodzie HTML umieścić na przykład taki fragment:

<div id="HTML_AJAX_LOADING" style=
   "background-color : blue; color : white; display : none; position : absolute;
   right : 50px; top : 50px;">
 Ładowanie nowego napisu...</div>

Wyświetlania  komunikatu  nie  da  się  w  obecnej  wersji  wyłączyć  po  stronie  serwera,  ale 

w przyszłych wersjach HTML_AJAX taka możliwość już będzie. Aby zapobiec wyświetla-

niu komunikatu trzeba nadpisać generującą go funkcję JavaScriptu:

HTML_AJAX.onOpen = function(){
  // nic
}