Datei wird geladen, bitte warten...
Zitiervorschau
Tytuł oryginału: PHP Web 2.0 Mashup Projects: Create practical mushups in PHP, grabbing and mixing data from Google Maps, Flickr, Amazon, YouTube, MSN Search, Yahoo!, Last.fm, and 411Sync.com Tłumaczenie: Maciej Jezierski Projekt okładki: Shantanu Zagade Recenzent: Stoyan Stefanow ISBN: 978-83-246-6937-0 Copyright © Packt Publishing 2007. First published in the English language under the title PHP Web 2.0 Mashup Projects: Create practical mushups in PHP, grabbing and mixing data from Google Maps, Flickr, Amazon, YouTube, MSN Search, Yahoo!, Last.fm, and 411Sync.com Polish edition copyright © 2009 by Wydawnictwo Helion S.A. All Rights Reserved. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 032 231 22 19, 032 230 98 63 e-mail: [email protected] WWW: http://helion.pl (księgarnia internetowa, katalog książek) Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/phpw2m.zip Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie?phpw2m_ebook Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland.
• Poleć książkę na Facebook.com
• Księgarnia internetowa
• Kup w wersji papierowej
• Lubię to! » Nasza społeczność
• Oceń książkę
Kiedyś wróżka powiedziała mi, że w lutym spotkam anioła na ziemi. Tę książkę dedykuję lutowemu aniołowi i miłości mojego życia, Anneliese Strunk. Dałaś mi więcej szczęścia, inspiracji i radości w życiu, niż kiedykolwiek mógłbym sobie wyobrazić.
Spis treści
4
Spis treści
Spis treści O autorze
9
O recenzencie
11
Wstęp
13
Rozdział 1. Wprowadzenie do aplikacji typu mashup
17
Web 2.0 i mashup Znaczenie danych Społeczności użytkowników Jak będziemy tworzyć aplikacje typu mashup Więcej aplikacji typu mashup
19 19 20 21 22
Rozdział 2. Zrób zakupy w Amazon
23
Podsumowanie projektu XML-RPC Struktura XML-RPC Odpowiedź XML-RPC Obsługa XML-RPC w PHP Wykonywanie żądania XML-RPC Przetwarzanie odpowiedzi XML-RPC Tworzenie klasy parsującej XML-RPC Wykorzystanie PEAR do obsługi XML-RPC REST Praca z REST w PHP Wykonywanie żądania REST Przetwarzanie odpowiedzi REST Interfejs API internetowej bazy danych UPC Interfejs API Amazon Przegląd ECS Budowa żądania REST ECS Tworzenie aplikacji typu mashup Wyszukiwanie produktów Podsumowanie
23 24 24 29 31 31 40 41 44 46 48 48 55 65 67 68 69 71 71 84
5
Spis treści
Rozdział 3. Stwórz własną wyszukiwarkę Podsumowanie projektu SOAP Web Services Descriptor Language (WSDL) i XML Schema Data (XSD) Podstawowa struktura WSDL Komunikat SOAP Klasa SoapClient w PHP Tworzenie parametrów Tworzenie instancji SoapClient Wykonywanie wywołania za pomocą metod SoapClient Obsługa odpowiedzi SOAP Usługa sieciowa Microsoft Live Search Korzystanie z wyszukiwania Usługa Yahoo! Search Korzystanie z wyszukiwania stron internetowych Tworzenie strony agregującej Podsumowanie
Rozdział 4. Własna szafa grająca z teledyskami
85 85 86 87 87 99 103 104 105 107 110 113 113 117 117 119 124
125
Podsumowanie projektu XSPF RSS Przegląd YouTube Interfejs programistyczny YouTube Przegląd Last.fm Usługi internetowe Audioscrobbler Parsowanie za pomocą PEAR Instalacja i korzystanie z pakietów File_XSPF Services_YouTube XML_RSS Tworzenie aplikacji typu mashup Architektura aplikacji typu mashup Strona główna Strona nawigacyjna Strona z zawartością Korzystanie z aplikacji typu mashup Podsumowanie
125 126 129 136 137 139 140 141 142 143 144 147 150 150 151 152 153 155 158
Rozdział 5. Zdjęcia londyńskiego metra
159
Podsumowanie projektu Wstępne planowanie Znalezienie informacji o stacjach metra Integracja serwisów Google Maps i Flickr Kolejność operacji w aplikacji RDF (Resource Description Framework)
159 160 161 162 163 164
6
Spis treści
SPARQL Analiza przedmiotu zapytania Anatomia zapytania SPARQL Klauzule WHERE w języku SPARQL Dodatkowe własności języka SPARQL RDF API for PHP (RAP) Obiekt XMLHttpRequest Przegląd informacji na temat obiektu XMLHttpRequest Korzystanie z obiektu Notacja JSON (JavaScript Object Notation) Przegląd obiektów JavaScript Struktura JSON Korzystanie z właściwości JSON Serializacja odpowiedzi JSON Interfejs API Google Maps Tworzenie mapy Geokodowanie Znaczniki Zdarzenia Obiekty InfoWindow Interfejs API Flickr Services Wykonywanie operacji wyszukiwania Interpretacja wyników zwróconych przez usługę Pobieranie fotografii lub strony z fotografiami Tworzenie aplikacji typu mashup Tworzenie bazy danych i wypełnianie jej danymi Klasa interfejsu z bazą danych — TubeSource Główny interfejs użytkownika Wykorzystanie usług Flickr i technologii AJAX Podsumowanie
Skorowidz
166 167 168 169 177 177 180 181 182 186 187 187 188 189 190 191 192 194 195 195 198 199 200 202 203 204 214 216 220 230
231
7
Spis treści
8
O autorze Shu-Wai Chow zajmuje się programowaniem komputerów i informatyką od ośmiu lat. Karierę rozpoczął w Sacramento w Kalifornii, gdzie spędził cztery lata, pracując jako webmaster w firmie Educaid należącej do korporacji First Union, a następne cztery w firmie Vision Service Plan jako programista aplikacji. W ciągu tych lat pracy biegle poznał technologie Java, JSP, PHP, ColdFusion, ASP, LDAP, XSLT i XSL-FO. Shu doradza również w sprawie adopcji kotów kilku organizacjom zajmującym się opieką nad zwierzętami w Sacramento. Obecnie pracuje jako inżynier oprogramowania w firmie Antenna Software w Jersey City w stanie New Jersey i kończy studia z ekonomii na uniwersytecie stanowym w Rutgers. Shu urodził się w brytyjskim Hongkongu, ale dorastał głównie w Palo Alto w stanie Kalifornia. Mieszka w regionie Jersey Shore z siedmioma bardzo wymagającymi kotami, bardzo inteligentnymi ptakami, jaszczurką bezogoniastą, rybą z gatunku bojownik wspaniały, której akwarium wymaga stałej pielęgnacji, kolonią żuków dermestidae, ulubioną gitarą Fender Stratocaster i ukochaną narzeczoną. Podczas pracy nad tą książką wiele osób i firm udzieliło mi pomocy. Przede wszystkim dziękuję personelowi wydawnictwa Packt Publishing, zwłaszcza Dougowi Patersonowi, Nikhilowi Bangerze, Adilowi Rizwanowi i Abhijeetowi Deobhakcie za profesjonalizm, zlecenie mi tej pracy oraz za wiarę we mnie (której do tej pory nie rozumiem do końca). Dziękuję Stoyanowi Stefanovowi za doskonałe komentarze podczas recenzowania książki. Twoje uwagi i sugestie przyczyniły się do poprawy jej jakości i mojego osobistego rozwoju.
PHP Web 2.0. Tworzenie aplikacji typu mashup
Każdy rozdział wymaga specjalnej uwagi. Rozdział 2: Dziękuję personelowi serwisów UPC Internet Database i Amazon, Inc. za udzielenie zgody na wykorzystanie materiałów. Szczególnie dziękuję wszystkim pracownikom serwisu UPC Internet Database za udzielanie odpowiedzi na moje pytania. Rozdział 3: Dziękuję firmom Yahoo! i Microsoft za udzielenie zgody na wykorzystanie materiałów, a także za dobrą współpracę wydziałów prawnych wymienionych firm. Specjalne podziękowania należą się firmom Data Access Corporation i Vincent Oorsprong za udostępnienie pomocnych i pouczających usług SOAP. Firma Data Access Worldwide (www.dataaccess.com) jest dostawcą zaawansowanych produktów i usług, które pomagają klientom tworzyć świetne aplikacje oraz lepiej wykorzystać swoje dane. Rozdział 4: Dziękuję pracownikom serwisów YouTube i Last.fm za udzielenie zgody na wykorzystanie materiałów. Specjalne podziękowania kieruję do firmy Last.fm za zainteresowanie moim projektem. Rozdział 5: Dziękuję kierownictwu serwisów Google, Flickr oraz Jo Walschowi za zezwolenie na skorzystanie z materiałów. Specjalne podziękowania należą się pracownikom serwisu Google, których entuzjazm przekonał mnie, że pomysł z tą książką nie był taki zły. W ostatniej książce zapomniałem podziękować Edowi Hansenowi i Keithowi Easterbrookowi. Osoby te zmusiły nas do wykorzystywania interfejsu WebSphere Application Developer. Bez zapoznania się ze środowiskiem WSAD nigdy nie używałbym Eclipse i nigdy nie skorzystałbym z PHPEclipse. Oznacza to, że nigdy nie napisałbym mojej pierwszej książki, a bez niej nie byłoby tej książki. Zatem dziękuję Wam, Panowie, i przepraszam za przeoczenie. Dziękuję również restauracji Kaya’s Kitchen of Belmar z New Jersey za najlepsze dania wegetariańskie na całym świecie. Jeśli ktoś z czytelników tej książki kiedykolwiek znajdzie się w regionie Jersey Shore, koniecznie powinien odwiedzić tę restaurację. Na koniec dziękuję producentom wina Palo Alto Duck Pond, pracownikom restauracji Hobee’s w El Camino i Arastradero, stadionowi Dodger Stadium oraz Davisowi Greenbeltowi.
10
Rozdział XX. • ???
O recenzencie Stoyan Stefanov jest programistą aplikacji internetowych w serwisie Yahoo!. Posiada certyfikat Zend Certified Engineer, jest autorem książek oraz aktywnym członkiem międzynarodowej społeczności użytkowników PHP. Jego osobisty blog jest dostępny pod adresem http://www. ´phpied.com.
11
PHP Web 2.0. Tworzenie aplikacji typu mashup
12
Wstęp Aplikacja typu mashup, zwana inaczej stroną agregującą, to strona lub aplikacja internetowa, która łączy dane z dwóch lub więcej internetowych źródeł w jedną całość. Niniejsza książka wprowadzi Cię w to zagadnienie. Nauczysz się tworzyć projekty w języku PHP pobierające dane z jednego miejsca w sieci, łączące je z istotnymi informacjami z innego miejsca w sieci i prezentujące je jako całość. Wszystkie rozwiązania zastosowane w tej książce zostały opracowane przy użyciu darmowych narzędzi i dokładnie opisane. Kod umożliwiający stworzenie stron agregujących można pobrać z naszej witryny. Książka jest praktycznym podręcznikiem z pięcioma szczegółowo opisanymi scenariuszami. Posłuży Ci do nauki tworzenia aplikacji typu mashup.
O czym jest ta książka Dowiesz się, w jaki sposób napisać kod PHP korzystający zdalnie z usług takich jak Google Maps, Flickr, Amazon, YouTube, MSN Search, Yahoo!, Last.fm oraz bazy danych Internet UPC Database. Poznasz także technologie, formaty danych i protokoły potrzebne do korzystania z tych usług i interfejsów oraz kilka darmowych narzędzi PHP. Dowiesz się, w jaki sposób technologie te współpracują z sobą. Zobaczysz, jak wykorzystując wyobraźnię, stworzyć własne rozbudowane witryny internetowe. Rozdział 1 zawiera przegląd witryn agregujących: czym one są i dlaczego będą dla Ciebie przydatne. W rozdziale 2 stworzymy podstawową stronę agregującą i wybierzemy się na zakupy. Będziemy po prostu wyszukiwać produkty na witrynie Amazon.com, posługując się uniwersalnym kodem produktu (UPC). W tym celu omówimy dwie podstawowe usługi sieciowe — XML-RPC i REST. Baza danych Internet UPC Database jest usługą opartą na XML-RPC, natomiast Amazon posługuje się usługą REST.
PHP Web 2.0. Tworzenie aplikacji typu mashup
Stworzymy kod wywołujący usługi XML-RPC i REST. Korzystając z funkcji SAX PHP, zbudujemy rozszerzalny, zorientowany obiektowo parser XML. Aplikacja typu mashup opisana w tym rozdziale łączy informacje z usługi E-Commerce Service (ECS) firmy Amazon z informacjami z bazy danych Internet UPC Database. W rozdziale 3 stworzymy własną wyszukiwarkę korzystającą z technologii MSN i Yahoo!. Rozpoczniemy od wprowadzenia do SOAP, najbardziej złożonego z protokołów sieciowych. SOAP w dużym stopniu oparty jest na standardach WSDL i XSD, które również zostaną omówione. Przyjrzymy się dokumentowi WSDL i dowiemy się, w jaki sposób odczytać z niego dostępne usługi i przekazywane typy danych. Korzystając z rozszerzenia PHP5, SoapClient, odwołamy się do serwera SOAP w celu pobrania danych. W końcu stworzymy stronę agregującą, łączącą wyniki wyszukiwania z Microsoft Live i Yahoo!. Do zbudowania strony agregującej w rozdziale 4 wykorzystamy interfejs API z witryny YouTube, a także kanały informacyjne z muzycznej strony społecznościowej Last.fm. Przyjrzymy się trzem różnym formatom plików opartym na XML pochodzącym z tych dwóch witryn: XSPF dla list piosenek, RSS dla często aktualizowanych informacji oraz formatowi stworzonemu przez YouTube. Stworzymy stronę agregującą, która pobiera piosenki z kanałów RSS Last.fm i odpytuje YouTube o teledyski tych piosenek. Zamiast tworzyć własne parsery XML do obsługi tych formatów, wykorzystamy pakiety z repozytorium PEAR, po jednym dla każdego formatu. Korzystając z tych pakietów, stworzymy zorientowaną obiektowo warstwę abstrakcji dla wspomnianych formatów, którą wykorzystamy w aplikacji typu mashup. W rozdziale 5 wykorzystamy niemalże wszystko, a więc RDF, SPARQL, RAP, Google Maps, Flickr, technologię AJAX oraz format JSON. Stworzymy stronę do prezentowania obrazów z witryny Flickr na mapach Google Maps. Zobaczymy, w jaki sposób odczytywać dokumenty RDF i jak pobierać z nich dane za pomocą SPARQL oraz RAP dla RDF. Pobierzemy szerokość i długość geograficzną londyńskich stacji metra. Następnie wyświetlimy je na mapie Google Maps, a z serwisu Flickr pobierzemy zdjęcia, które są z nimi związane. Aplikacja musi komunikować się z serwerami API, zatem wykorzystamy technologię AJAX i format JSON, który ostatnio staje się głównym formatem danych. Największym problemem w aplikacjach ajaksowych jest sytuacja, w której kilka czynności jest wykonywanych jednocześnie. Poznamy sposoby rozwiązania tego problemu.
Czego będziesz potrzebował do tej książki Żeby pracować z przykładami z książki i skorzystać z zaprezentowanego kodu, będziesz potrzebował serwera internetowego z oprogramowaniem PHP 5.0 lub wyższym oraz Apache 1.3. We wszystkich przykładach założyłem, że serwer internetowy działa na lokalnym komputerze, na którym również piszesz skrypty. Ponadto w rozdziale 5 będziesz potrzebować serwera MySQL. Podobnie jak w poprzednich przypadkach, zakładamy, że serwer MySQL działa lokalnie i jest poprawnie skonfigurowany.
14
Wstęp
Żeby szybko zainstalować PHP, Apache i MySQL, skorzystaj z XAMPP (http://www.apache ´friends.org/en/xampp.html). Jest to łatwy w użyciu program, który zainstaluje za jednym zamachem m.in. PHP, Apache i MySQL. XAMPP jest dostępny dla systemów operacyjnych Windows, Linux i Mac OS X. Wiele standardowych dystrybucji Linuksa ma już zainstalowane oprogramowanie PHP, Apache i MySQL. Sprawdź w dokumentacji dystrybucji, jak je uruchomić. Mac OS X domyślnie ma zainstalowane oprogramowanie PHP i Apache. Możesz je włączyć, wybierając opcję Web Sharing w okienku Sharing Preferences. MySQL można zainstalować za pomocą programu instalacyjnego pobranego z MySQL.com (http://dev.mysql.com/downloads/mysql/4.1.html).
Konwencje W książce wykorzystane zostały różne style tekstu. Poniżej znajdują się przykłady stylów wraz z objaśnieniem ich znaczenia. Dla kodu używane są trzy style. Kod w tekście oznaczany jest następująco: „Możemy dołączyć inne pliki za pomocą dyrektywy include”. Blok kodu będzie przedstawiony w ten sposób:
examples.getStateName
42
Pierwsza linia nagłówka, POST, oraz linia Host informują nas, że wywołanie XML-RPC odnosi się do usługi internetowej znajdującej się pod adresem betty.userland/RCP2. Pierwszą użyteczną informacją w ciele żądania jest nazwa funkcji, examples.getStateName. Przekazujemy do niej jako parametr liczbę całkowitą 42. Głównym elementem wywołania XML-RPC jest methodCall. Ma on jeden wymagany element potomny, methodName, określający nazwę metody, która ma zostać wywołana. Jeśli do wywołania przekazywane są jakieś parametry, znajdują się one w elemencie params. Wywołanie procedury może wymagać nieograniczonej liczby parametrów. Wywołania XML-RPC nie mają nazwanych parametrów. Inaczej mówiąc, nie nadajesz parametrom nazw przed przypisaniem im wartości, na przykład: // To jest nieprawidłowe. Parametry nie mają nazw.
42
Zamiast tego, jeśli zdalna funkcja wymaga więcej niż jednego parametru, musi ona określać ich prawidłową kolejność. Żeby dowiedzieć się, jaka jest prawidłowa kolejność parametrów, musisz przeczytać dokumentację danego interfejsu API.
25
PHP Web 2.0. Tworzenie aplikacji typu mashup
// Prawidłowym sposobem rozróżniania parametrów jest ich kolejność // określona przez interfejs API.
42 13 32
W żądaniu każdy parametr znajduje się w elemencie param. W każdym elemencie param właściwy parametr znajduje się w elemencie value. W elemencie value znajdują się wartości parametrów i ich typy danych.
Typy danych XML-RPC Parametry XML-RPC mogą należeć do jednego z trzech typów. Każdy z nich reprezentowany jest w inny sposób wewnątrz elementu value: Q skalary: podstawowe typy danych, Q tablice: podobne do numerycznie indeksowanych tablic PHP, Q struktury: odpowiedniki tablic asocjacyjnych z PHP.
Teraz wykorzystamy typy danych i ich strukturalne definicje jako parametry żądania. Jednak takie same typy używane są w całej komunikacji XML-RPC. Dane w odpowiedzi serwera będą sformatowane według tego samego schematu.
Wartości skalarne Wartości skalarne są powszechnie używanymi prymitywnymi typami danych dostępnymi w większości języków. Niemalże każda wartość skalarna musi znajdować się w elemencie, który określa jej typ danych.
Łańcuch Jest to podstawowy łańcuch tekstowy. Odpowiada on typowi string z PHP.
Witaj, świecie!
Ponieważ jest to najczęściej stosowany typ danych, każda wartość w elemencie value bez znacznika typu danych domyślnie traktowana będzie jako łańcuch. Na przykład poniższy kod jest całkowicie prawidłowy w XML-RPC i zawarta w nim wartość będzie domyślnie traktowana jako łańcuch:
Żegnaj, okrutny świecie!
26
Rozdział 2. • Zrób zakupy w Amazon
Liczba całkowita Jest to czterobajtowa liczba całkowita ze znakiem. Typ ten jest odpowiednikiem typu integer w PHP. Jest określany przez znaczniki int lub i4 dla czterobajtowej liczby całkowitej.
42
// To oznacza to samo:
42
Liczba zmiennoprzecinkowa Typ double jest liczbą zmiennoprzecinkową o podwójnej precyzji. Jest on odpowiednikiem typu float w PHP. Warto zauważyć, że w PHP 4 występował również typ double, ale zrezygnowano z niego na rzecz typu float.
-44.352301
Typ boolowski Jest to podstawowy typ do określania prawdy lub fałszu. Odpowiada on typowi boolean w PHP. Różnica polega na tym, że w PHP i wielu innych językach prawda i fałsz mogą być reprezentowane za pomocą słów kluczowych TRUE i FALSE lub wartości 1 dla prawdy i 0 dla fałszu. W XML-RPC typ boolowski reprezentowany jest tylko za pomocą 1 lub 0.
0
Data i czas Typ daty i czasu określa datę i czas z dokładnością do jednej sekundy. Ma on format RRRRMM ´DDTHH:MM:SS. Powinny się w nim znajdować rok, miesiąc, dzień, godzina, minuta, sekunda. „T” jest literą. W PHP nie ma typu daty i czasu, więc trzeba go prezentować w postaci łańcucha.
20060710T11:30:32
W PHP 5 funkcja date przyjmuje parametr „c”, który powoduje zwrócenie czasu w formacie ISO 8601. Ponieważ czas nie jest zwracany dokładnie w takim formacie, jaki wymagany jest przez XML-RPC, do tego celu będziemy posługiwać się funkcją, która automatycznie zakoduje format daty i czasu.
27
PHP Web 2.0. Tworzenie aplikacji typu mashup
Dane binarne zakodowane w base64 Żeby przesłać dane binarne poprzez XML-RPC, należy zakodować je do postaci base64 i umieścić w znacznikach base64.
Pj4UijBdhLr6IdvCc0Ad3NVP4OidTd8E1kRY5Edh
W PHP nie ma binarnego typu danych, ale na potrzeby przesyłu przez XML-RPC możesz zakodować w base64 pliki pobrane za pomocą funkcji file_get_contents.
Tablice Tablice indeksowane numerycznie są przekazywane jako pojedyncza struktura w elemencie value. Do ich definiowania służy specyficzna struktura elementów potomnych.
Jeden Małpa 4.307400
Przed danymi występują dwa poziomy elementów potomnych: array, który określa, że wartość parametru jest tablicą, oraz data, który oznacza początek danych. Dane są rozmieszczone tak samo jak wartości skalarne. Każdemu elementowi w znaczniku array odpowiada element value, którego elementy potomne określają typ danych. Tablice mogą być zagnieżdżone. W każdym elemencie value może znajdować się kolejny element array, o ile są one ułożone w kolejności array/data/value.
Struktura Struktury są podobne do tablic i stanowią w XML-RPC reprezentację tablic asocjacyjnych PHP. Każdy element posiada parę składającą się z nazwanego klucza i wartości. Podobnie jak w przypadku tablic, struktura jest definiowana za pomocą pojedynczego elementu struct. Każda pozycja struktury ma jeden element member, a każdy element member ma jeden wymagany element
28
Rozdział 2. • Zrób zakupy w Amazon
name, który określa nazwę pozycji, i jeden element value reprezentujący wartość. Również podobnie jak w przypadku tablic, element value jest zgodny z zasadami określonymi dla wartości skalarnych w XML-RPC.
Jeden To jest łańcuch
Dwa 1
To jest nazwa -98.330000
Żądania XML-RPC są zasadniczo żądaniami HTTP POST określającymi, jaka metoda ma zostać wywołana, i zawierającymi odpowiednio sformatowane parametry. Przyjrzyjmy się teraz odpowiedzi z serwera.
Odpowiedź XML-RPC Po wykonaniu żądania możemy od usługi oczekiwać jednego z dwóch typów odpowiedzi. Jeśli żądanie XML-RPC zostało wykonane prawidłowo, otrzymamy dane w postaci określonej przez specyfikację XML-RPC. Jeśli wystąpił błąd, zwrócony zostanie specjalny komunikat błędu XML-RPC. Podobnie jak w przypadku zwykłego wywołania strony, wraz z odpowiedzią zostanie zwrócony nagłówek: HTTP/1.x 200 OK Date: Fri, 11 Aug 2007 23:34:43 GMT Server: Apache/1.3.33 (Darwin) PHP/5.1.4 DAV/1.0.3 X-Powered-By: PHP/5.1.4 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: text/xml
Piękna przemowa
29
PHP Web 2.0. Tworzenie aplikacji typu mashup
Powyższa odpowiedź jest prawidłową odpowiedzią XML-RPC. Powinna wyglądać znajomo. Główny element to methodResponse, który wskazuje, że jest to odpowiedź. Po nim następuje element potomny params. Każda ze zwracanych wartości umieszczona jest w elemencie param. Wewnątrz niego każda wartość jest zgodna z zasadami określania wartości skalarnych, które widzieliśmy wcześniej. W tym przykładzie widać pojedynczy łańcuch zwracany przez usługę. Jednak podobnie jak w przypadku żądań, w znaczniku params może znajdować się wiele wartości, tablica lub struktura. Na przykład tablica zwrócona przez usługę wyglądałaby tak jak poniżej:
system.multicall system.listMethods system.getCapabilities
Jeśli usługa nie może wykonać żądania, zwróci błąd XML-RPC. Zamiast elementu params, w elemencie methodResponse będzie znajdował się element fault. Element methodResponse zawsze zawiera potomka params lub fault, ale nigdy ich obydwu naraz. Błąd zwracany przez XML-RPC jest zasadniczo strukturą. Znajdują się w niej dwie nazwane składowe. faultString jest zrozumiałym dla człowieka komunikatem błędu, a faultCode liczbą całkowitą przypisaną przez usługę. Ani faultString, ani faultCode nie są zdefiniowane ani ustandaryzowane w specyfikacjach XML-RPC. Całkowicie zależą one od implementacji serwera. HTTP/1.x 200 OK Date: Fri, 11 Aug 2007 23:41:18 GMT Server: Apache/1.3.33 (Darwin) PHP/5.1.4 DAV/1.0.3 X-Powered-By: PHP/5.1.4 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: text/xml
faultCode 4
30
Rozdział 2. • Zrób zakupy w Amazon
zdalneWywolanie
Witaj!
Funkcja xmlrpc_encode_request stworzyła za ciebie całe wywołanie XML-RPC. Jeśli zmodyfikujesz typ przekazywanej zmiennej, funkcja xmlrpc_encode_request będzie potrafiła określić odpowiednik jej typu danych w XML-RPC. Możemy przetestować tę funkcjonalność za pomocą poniższego skryptu:
32
Rozdział 2. • Zrób zakupy w Amazon
W powyższym kodzie do zmiennej $singleVar przypisujemy łańcuch tekstowy, którego wartością jest "88". Przed przekazaniem jej do funkcji xmlrpc_encode_request rzutujemy ją na typ integer. Funkcja xmlrpc_encode_request odzwierciedli ten typ danych, umieszczając wartość XML-RPC w znacznikach int. Jeśli ponownie otworzysz stronę w przeglądarce i podejrzysz jej źródło, zobaczysz poniższą strukturę:
zdalneWywolanie
88
Istnieją dwa specjalne przypadki typów danych, przy których musimy zachować ostrożność.
Typ danych double Pierwszym przypadkiem jest typ danych double. Funkcja xmlrpc_encode_request zawsze wyrówna liczby typu double do sześciu miejsc po przecinku. Na przykład przesłanie liczby 1,6: $singleVar = 1.6; $requestMessage = xmlrpc_encode_request('zdalneWywolanie', $singleVar);
spowoduje wygenerowanie następującego kodu XML-RPC:
1.600000
Oprócz tego wszystkie wartości mające więcej niż sześć cyfr po przecinku zostaną zaokrąglone. Możemy zobaczyć, jak to działa, przesyłając długą liczbę, która powinna być zaokrąglona w górę, i długą liczbę, która powinna być zaokrąglona w dół. $roundedUp = 1.123456789; $roundedOff = 1.87654321; $requestMessage1 = xmlrpc_encode_request('zdalneWywolanie', $roundedUp); $requestMessage2 = xmlrpc_encode_request('zdalneWywolanie, $roundedOff);
33
PHP Web 2.0. Tworzenie aplikacji typu mashup
Dwa powyższe wywołania dadzą w wyniku następujące elementy reprezentujące wartości:
1.123457
1.876543
Typy danych daty i czasu oraz base64 Drugim przypadkiem, w którym powinniśmy zachować ostrożność, jest data i czas oraz base64. Żaden z tych typów danych nie ma swojego odpowiednika w PHP. Jeśli przypiszesz je do zmiennej, PHP uzna ich typ za łańcuch tekstowy. Przesłanie ich do funkcji xmlrpc_encode_request również spowoduje, że zostaną potraktowane jako łańcuchy. Jeśli na przykład będziemy mieli specjalny łańcuch z danymi binarnymi, PHP może go potraktować tylko jak łańcuch znakowy: $singleVar = '8923hfjsd89783hiuyi9yhw938ihfasid9yh32iyr9wy'; $ requestMessage = xmlrpc_encode_request('zdalneWywolanie', $singleVar);
Powyższy kod spowoduje wygenerowanie żądania XML-RPC. Jeśli zdalne wywołanie będzie oczekiwać danych typu base64, wystąpi błąd.
8923hfjsd8938ihfasid9yh32iyr9wy
Żeby wymusić na PHP traktowanie tych typów danych jako typów XML-RPC, przed wywołaniem xmlrpc_encode_request musimy wywołać funkcję xmlrpc_set_type. Funkcja ta przyjmuje dwa parametry: pierwszym jest zmienna, której typ chcesz zmodyfikować, a drugim żądany typ danych XML-RPC. Jeśli weźmiemy ten sam łańcuch danych binarnych i wywołamy xmlrpc_set_type, żeby zmienić jego typ na base64: $singleVar = '8923hfjsd89783hiuyi9yhw938ihfasid9yh32iyr9wy'; xmlrpc_set_type($singleVar, "base64"); $requestMessage = xmlrpc_encode_request('zdalneWywolanie', $singleVar);
wynikowe żądanie XML-RPC będzie miało postać wymaganą przez usługę:
ODkyM2hmanNkODkzOGloZmFzaWQ5eWgzMml5cjl3eQ==
To samo dotyczy łańcucha w postaci prawidłowego formatu daty i czasu: $singleVar = '20060814T09:08:23'; xmlrpc_set_type($singleVar, "datetime"); $requestMessage = xmlrpc_encode_request('zdalneWywolanie', $singleVar);
34
Rozdział 2. • Zrób zakupy w Amazon
Po zakodowaniu wartość zostanie umieszczona w prawidłowym elemencie daty i czasu żądania XML-RPC:
20060814T09:08:23
Funkcja xmlrpc_set_type została stworzona specjalnie w celu rozwiązania problemu braku typów base64 oraz daty i czasu w PHP i ich obecności w XML-RPC. Drugi parametr, określający rzutowanie na typ danych XML-RPC, które chcesz wykonać, może przyjmować tylko wartości base64 i date/time. Nie jest to w zamierzeniu ogólna funkcja do rzutowania typów, taka jak settype w PHP. Pozostałe struktury tworzone przez funkcję xmlrpc_encode_request wykorzystują podstawową obsługę danych skalarnych. W podobny sposób obsługiwane są struktury i tablice. W ich przypadku na poziomie elementu value stosowane są te same schematy prezentowania wartości skalarnych. Głównym ograniczeniem funkcji xmlrpc_encode_request jest to, że przyjmuje ona tylko jeden parametr zmiennej. Żeby to obejść, funkcja przyjmuje jako parametr zmiennej różne struktury tablicowe i w zależności od nich tworzy różne żądania XML-RPC.
Tworzenie żądania XML-RPC z wieloma parametrami Czasami może zdarzyć się, że do wywołania zdalnego musisz przekazać więcej niż jeden parametr. Żeby to zrobić, stwórz tablicę zmiennych i przekaż ją jako drugi argument do funkcji xmlrpc_encode_request, tak jak poniżej:
Wynikiem powyższego kodu będzie żądanie XML-RPC z elementem param dla każdej zmiennej. Jak widać, mieszanie typów danych jest dozwolone.
Jeden
2.090000
20060814T09:08:23
35
PHP Web 2.0. Tworzenie aplikacji typu mashup
Przekazywanie tablic w żądaniach XML-RPC Pamiętaj, że tablica XML-RPC jest zserializowaną, numerycznie indeksowaną tablicą PHP. Musimy przekazać ją jako drugi parametr do funkcji xmlrpc_encode_request. Jednak jeśli po prostu przekażemy tablicę, funkcja xmlrpc_encode_request stworzy wieloparametrowe żądanie, tak jak widzieliśmy wcześniej. Rozwiązanie tego problemu polega na umieszczeniu numerycznie indeksowanej tablicy w innej tablicy i przekazaniu takiej paczki w postaci drugiego parametru xmlrpc_encode_request:
Powyższy kod stworzy wymaganą tablicę XML-RPC, którą widzieliśmy w przykładzie we wcześniejszej części rozdziału. Takie rozwiązanie działa, ponieważ każdy parametr w żądaniu XML-RPC umieszczony jest we własnym elemencie value. Z technicznego punktu widzenia przekazaliśmy do funkcji xmlrpc_encode_request tylko jeden parametr. Funkcja rozpoznała go jako tablicę indeksowaną numerycznie i stworzyła wymaganą strukturę wewnątrz elementu value.
Przekazywanie struktur w żądaniach XML-RPC Tablica XML-RPC ma się do struktury mniej więcej tak, jak numerycznie indeksowana tablica PHP do tablicy asocjacyjnej PHP. Wiedząc o tym, możemy logicznie wywnioskować, że w celu stworzenia struktury musimy tylko zmienić drugi przekazywany parametr z tablicy indeksowanej numerycznie na tablicę asocjacyjną. $listArray = array("Jeden" => "To jest łańcuch", "Dwa" => "true, "To jest ´nazwa" => -98.33); $requestMessage = xmlrpc_encode_request('zdalneWywolanie', array($listArray));
W powyższym kodzie do zmiennej $listArray przed przekazaniem jej do xmlrpc_encode_re ´quest przypisujemy tablicę, w której wartości przypisane są do kluczy. Wynikiem będzie przykładowa struktura, którą analizowaliśmy we wcześniejszej części rozdziału.
Tworzenie nagłówka wywołania żądania Kiedy mamy już zawartość żądania, możemy stworzyć wymagany nagłówek HTTP. Nagłówek umieszczany jest przed zawartością. Zaczęliśmy od końca, ponieważ w jednej z części nagłówka trzeba podać długość zawartości.
36
Rozdział 2. • Zrób zakupy w Amazon
Przyjrzyjmy się całemu żądaniu XML-RPC: POST /rpc HTTP/1.0 User-Agent: XML-RPC Client Host: www.upcdatabase.com Content-Type: text/xml Content-Length: 185
lookupUPC
079400560704
Widać tu, że kilka pozycji nagłówka może być stałych. Inne pozycje (wyróżnione w kodzie) mogą zostać zastąpione zmiennymi: 1. nazwa skryptu serwera, do którego się odwołujemy, znajdująca się po POST, 2. nazwa serwera, 3. długość zawartości, 4. sama zawartość. Z pewnością warto stworzyć funkcję, która zwracałaby pełne żądanie. Parametrami będą wyżej wymienione zmienne. function createPostRequest($remoteServer, $remotePath, $requestBody) { $theRequest = "POST " . $remotePath . " HTTP/1.0\n" . "User-Agent: XML-RPC Client\n" . "Host: " . $remoteServer . "\n" . "Content-Type: text/xml\n" . "Content-Length: " .strlen($requestBody) . "\n" . "\n" . $requestBody . "\n"; return $theRequest; } createPostRequest jest prostą funkcją. Przyjmuje trzy parametry i łączy je ze standardowymi
nagłówkami żądania HTTP, tworząc pełne żądanie XML-RPC. Parametrami tymi są: Q $remoteSerwer — nazwa i domena serwera (np. "www.amazon.com"). Nie dodawaj protokołu, na przykład http://, ponieważ jest to nie tylko zbędne, ale również
nieprawidłowe. Q $remotePath — ścieżka do usługi XML-RPC zaczynająca się w punkcie początkowym
serwera. Wartość ta zaczyna się od ukośnika (/) i pełni tę samą funkcję, co ścieżka absolutna w strukturze katalogów. Przykładem mogłoby być "/services/xmlrpc".
37
PHP Web 2.0. Tworzenie aplikacji typu mashup
Q $requestBody — część żądania zwracana przez xmlrpc_encode_request. Jest to część
żądania XML-RPC zawierająca dokument XML. Omawiana funkcja wykorzystuje parametry $remoteSerwer i $remotePath do pierwszej linii nagłówka oraz linii zawierającej nazwę serwera. Zawartość, $requestBody, nie tylko musi być dodana na końcu żądania, ale także należy umieścić w nagłówku jej długość obliczoną za pomocą funkcji strlen. Zauważ, w jaki sposób łączone są z sobą nagłówki w funkcji. Chociaż są podzielone i przełamane dla większej czytelności, w samym łańcuchu znaków z nagłówkami, pomiędzy znakami końca linii ("\n") a początkiem następnej linii nie ma żadnych spacji ani tabulacji. Jest to ścisłe wymaganie formatowania protokołu HTTP. Jeśli na początku linii znajdowałyby się jakieś spacje, żądanie nie powiodłoby się.
Wywoływanie XML-RPC za pomocą gniazd Mamy już w pełni sformatowane wywołanie XML-RPC. Wszystkie zmienne zostały zserializowane, stworzone zostały również nagłówki. Jedyną rzeczą, która pozostała do zrobienia, jest właściwe wywołanie usługi i przesłanie do niej żądania. Gniazda stanowią przejrzysty sposób wywoływania XML-RPC. Gniazdo jest zasadniczo bezpośrednim połączeniem z innym komputerem. Możemy stworzyć gniazdo do serwera XML-RPC i bezpośrednio przesłać do niego żądanie. Można to zrobić za pomocą kolejnej funkcji korzystającej z wyniku funkcji createPostRequest. Będzie ona nie tylko wysyłać żądanie do serwera, ale również pobierać odpowiedź, którą później przetworzymy. function send($remoteServer, $remotePort, $fullXMLRPCRequest) { $headers = ''; $data = ''; $socket = fsockopen($remoteServer, $remotePort); fwrite($socket, $fullXMLRPCRequest); while ($str = trim(fgets($socket))) { $headers .= $str . "\n"; } while (!feof($socket)) { $data .= fgets($socket); } fclose($socket); return $data; }
Funkcja ta również przyjmuje trzy parametry: Q $remoteServer — jest to dokładnie ta sama zmienna, która była wykorzystywana w createPostRequest. Podobnie jak w przypadku nagłówka HTTP, nie należy dodawać do niej protokołu http://.
38
Rozdział 2. • Zrób zakupy w Amazon
Q $remotePort — port sieciowy, przez który chcesz się łączyć. Dla standardowych
połączeń HTTP będzie to 80. Q $fullXMLRPCRequest — pełne żądanie XML-RPC z nagłówkami i zawartością. Jest to wynik funkcji createPostRequest. Funkcja ta wykonuje kilka czynności. Po inicjalizacji kilku zmiennych wykorzystujemy fsockopen do otwarcia połączenia do zdalnego serwera: $socket = fsockopen($remoteServer, $request);
Wywołanie fwrite() wyśle żądanie XML-RPC bezpośrednio do serwera poprzez gniazdo: fwrite($socket, $request);
Gniazdo jest zasadniczo tunelem. Nie tylko wykonujemy poprzez nie zapis, ale również odczytujemy odpowiedź serwera. Pierwszą rzeczą zwracaną po zakończeniu zapisu jest nagłówek HTTP odpowiedzi. Nie potrzebujemy tej informacji, więc możemy po prostu odczytać odpowiedź i umieścić ją w zmiennej lokalnej, która zostanie usunięta po zakończeniu wykonywania funkcji. while ($str = trim(fgets($socket))) { $headers .= $str . '\n'; }
Po nagłówkach odczytamy to, co nas naprawdę interesuje — zawartość odpowiedzi XML-RPC: while (!feof($socket)) { $data .= fgets($socket); }
Dopóki przez gniazdo przesyłane są dane, odczytujemy je wszystkie i zapisujemy w zmiennej lokalnej. Po odczytaniu całej odpowiedzi usuwamy gniazdo i zwracamy dane do kodu wywołującego funkcję: fclose($socket); return $data;
Jak dotąd przekazaliśmy zmienne do stworzonej funkcji createPostRequest(), która zwróciła pełne żądanie XML-RPC. Następnie przekazaliśmy żądanie do kolejnej stworzonej przez nas funkcji, czyli send(). Funkcja ta otwiera połączenie do zdalnego serwera, zapisuje żądanie i odczytuje odpowiedź. Zwraca ona odpowiedź XML-RPC. Teraz musimy przetworzyć tę odpowiedź na dane PHP, które można wykorzystać.
39
PHP Web 2.0. Tworzenie aplikacji typu mashup
Przetwarzanie odpowiedzi XML-RPC Do przetworzenia danych z odpowiedzi XML-RPC i przekształcenia ich na zmienne PHP wykorzystamy funkcję odwrotną do xmlrpc_encode_request() — xmlrpc_decode(). Funkcja xmlrpc_encode_request() potrafiła rozpoznać, czy zmienne są zmiennymi prymitywnymi, tablicami, czy strukturami, i przetworzyć je na żądanie XML-RPC. Funkcja xmlrpc_decode() pobierze odpowiedź XML-RPC, rozpozna typy zwróconych danych, na przykład czy są one prostymi typami, strukturami, czy tablicami, i zwróci albo prostą wartość PHP (w przypadku typów prymitywnych), albo tablicę wartości (w przypadku tablic i struktur). Funkcja ta przetworzy także błędy odpowiedzi XML-RPC. Za pomocą innej funkcji, xmlrpc_ ´is_fault(), możemy określić, czy w odpowiedzi znajduje się błąd, i odpowiednio go obsłużyć. Stwórzmy funkcję, która pokaże, w jaki sposób korzystać zarówno z xmlrpc_decode(), jak i z xmlrpc_is_fault(). function processXMLRPCResponse($xmlrpcResponse) { $data = xmlrpc_decode($xmlrpcResponse); if (xmlrpc_is_fault($data)) { echo 'Kod błędu: ' . $data['faultCode']. "\n"; echo 'Komunikat błędu: ' . $data['faultString']; } else { if (is_array($data)) { var_dump($data); } else { echo $data; } } }
Funkcja jako parametr przyjmuje odpowiedź XML-RPC. Pierwszą czynnością wykonywaną w funkcji jest przetworzenie odpowiedzi XML-RPC przez funkcję xmlrpc_decode(). Jeśli przyjrzysz się zwracanej wartości, zobaczysz, że jest to wartość prosta lub tablica. Jeśli usługa zwróci pojedynczą wartość, zmienna $data po prostu będzie ją zawierać. Jeśli zwrócona zostałaby tablica, w zmiennej $data znajdowałaby się numerycznie indeksowana tablica PHP. Jeśli zwrócona zostałaby struktura lub błąd XML-RPC, $data będzie zawierać tablicę asocjacyjną PHP. Przekształcenie to odbywa się wewnątrz konstrukcji if-else i zagnieżdżonej w niej drugiej konstrukcji if-else. Pierwsza instrukcja if wywołuje funkcję xmlrpc_is_fault(). Funkcja ta określa, czy struktura danych zawiera błąd XML-RPC. Jeśli tak jest, zwróconą wartością będzie tablica asocjacyjna z dwiema pozycjami o indeksach faultCode i faultString. Ich wartościami będą elementy odpowiedzi faultCode i faultString.
40
Rozdział 2. • Zrób zakupy w Amazon
Jeśli nie wystąpił błąd, funkcja sprawdza, co znajduje się w odpowiedzi: tablica czy wartość. Tutaj po prostu wypisujemy wartość na ekranie. W praktyce będziesz chciał zrobić coś innego, na przykład przekazać tablicę do innej funkcji, żeby ją przejrzeć lub przeszukać.
Tworzenie klasy parsującej XML-RPC Trzy funkcje createPostRequest, send i processXMLResponse to podstawowe narzędzia umożliwiające stworzenie ogólnej klasy parsowania XML-RPC. Żeby łatwiej było korzystać z tych funkcji, możemy dodać publiczną funkcję osłaniającą, która będzie je wywoływać. class XMLRPCParser { public function callService($remoteMethod, $parameters, $remoteServer, $remotePath, $port=80) { $requestXML = xmlrpc_encode_request($remoteMethod, $parameters); $fullRequest = $this->createPostRequest($remoteServer, $remotePath, ´$requestXML); $response = $this->send($remoteServer, $port, $fullRequest); return xmlrpc_decode($response); } private function createPostRequest($remoteServer, $remotePath, ´$requestBody) { $theRequest = "POST " . $remotePath . "HTTP/1.0\n" . "Host: " . $remoteServer . "\n" . "User-Agent: XML-RPC Client\n" . "Content-Type: text/xml\n" . "Content-Length: " .strlen($requestBody) . "\n" . "\n" . $requestBody . "\n"; return $theRequest; } private function send($remoteServer, $remotePort, $fullXMLRPCRequest) { $headers = ''; $data = ''; $socket = fsockopen($remoteServer, $remotePort); fwrite($socket, $fullXMLRPCRequest); while ($str = trim(fgets($socket))) { $headers .= $str . '\n'; } $data = ''; while (!feof($socket)) { $data .= fgets($socket); } fclose($socket); return $data; } public function processXMLRPCResponse($data) { if (xmlrpc_is_fault($data)) { echo 'Kod błędu: ' . $data['faultCode']. "\n"; echo 'Komunikat błędu: ' . $data['faultString'];
41
PHP Web 2.0. Tworzenie aplikacji typu mashup
} else { if (is_array($data)) { var_dump($data); } else { echo $data; } } } }
Trzy ostatnie funkcje już widzieliśmy. Pierwsza z nich, callService(), jest funkcją osłaniającą dla funkcji xmlrpc_encode_request(), createPostRequest i send(). Każda aplikacja, która będzie chciała wykonać wywołanie XML-RPC, może po prostu skorzystać z funkcji callService() obsługującej całość wywołania. Przekazujemy do niej nazwę zdalnej funkcji, którą chcemy wywołać, jej parametry w postaci tablicy, nazwę zdalnego serwera, ścieżkę, ścieżkę do usługi, a jeśli zajdzie taka potrzeba, także numer portu. W pierwszej linii tej funkcji do xmlrpc_enco ´de_request() przekazywana jest nazwa metody i jej parametry w celu stworzenia żądania XML-RPC. Następnie żądanie oraz nazwa serwera i ścieżka do usługi przekazywane są do funkcji createPostRequest(). Zwraca ona żądanie XML-RPC wraz z poprawnym nagłówkiem HTTP POST. W końcu przekazujemy te dane do funkcji send(), w której poprzez gniazdo przesyłane jest żądanie XML-RPC. Wartość zwracana przez send() jest odpowiedzią XML-RPC.
Testowanie klasy parsera XML-RPC Żeby zobaczyć, jak ta klasa działa, możemy użyć jej do wywołania kilku ogólnodostępnych usług XML-RPC. W przykładzie, w pliku example_RPC.php znajduje się klasa parsująca oraz parę linii kodu wywołujących kilka usług: $parser = new XMLRPCParser(); echo "Pierwszy przykład"; $returnedData = $parser->callService('latestDownloadURL', null, 'www.upcdatabase.com', '/rpc', 80); $parser->processXMLRPCResponse($returnedData); echo "Drugi przykład"; $returnedData = $parser->callService('geocode', '1000 Elysian Park Ave., Los Angeles, CA', 'geocoder.us', '/service/xmlrpc', 80); $parser->processXMLRPCResponse($returnedData); class XMLRPCParser { /* Reszta klasy XMLRPCParser */
W pierwszej linii tworzymy obiekt XMLRPCParser, który wykorzystamy dwukrotnie. Za pierwszym razem usługa XML-RPC Internet UPC Database posłuży do wywołania metody latestDown
42
Rozdział 2. • Zrób zakupy w Amazon
´loadURL. Zwraca ona bieżący adres URL do spakowanej pełnej bazy danych Internet UPC Database. Usługa znajduje się pod adresem http://ww.upcdatabase.com/rpc. Wykorzystamy ją później w stronie agregującej do wyszukania informacji o produkcie przed wysłaniem ich do Amazon.
Usługa ta nie wymaga żadnych parametrów; wiele metod XML-RPC nie wymaga parametrów. Jednak funkcja xmlrpc_encode_request() wymaga przekazania parametru jako drugiego argumentu. W takim przypadku po prostu przekazujesz do niej null, tak jak w omawianym przykładzie, w którym w pierwszej linii funkcji callService() do funkcji xmlrpc_encode_request() przekazujemy null. Za drugim razem korzystamy z usługi geocoder znajdującej się pod adresem http://geocoder.us/ ´service/xmlrpc. Usługa ta pobiera adres i zwraca jego położenie geograficzne. Zdalna metoda nazywa się geocode i jako parametr przyjmuje łańcuch znakowy z adresem. W obydwu przypadkach wynik funkcji callService() przekazujemy do funkcji processXMLResponse(), która po prostu wyświetla wynik wywołania usługi. Teraz jesteśmy gotowi do przetestowania przykładu. Zapisz plik na serwerze internetowym i wczytaj stronę do przeglądarki. Powinieneś zobaczyć wynik podobny do przedstawionego na poniższym rysunku.
W pierwszym przykładzie zwracany jest po prostu łańcuch znaków, wyświetlany w przeglądarce przez funkcję processXMLRPCResponse(). W przykładzie z usługą geocoder w odpowiedzi zwracana jest tablica. Funkcja processXMLRPCResponse() przekazuje tablicę do funkcji var_dump(), która wyświetla klucz, wartość i typ danych wartości dla każdego elementu danej tablicy.
43
PHP Web 2.0. Tworzenie aplikacji typu mashup
Po przetestowaniu klasy możemy ją wykorzystać w stronie agregującej. Pamiętaj, żeby przed właściwym wykorzystaniem klasy usnąć linie, które tworzą przykładowy obiekt i go testują.
Wykorzystanie PEAR do obsługi XML-RPC PHP obsługuje XML-RPC, nie korzystając z zewnętrznych bibliotek. Jednak rozwiązanie, które tu zastosowaliśmy, ma kilka wad. Przede wszystkim wymaga, żeby interpreter PHP został skompilowany z opcją -with-xml-rpc. Jeśli tak nie jest, będziesz musiał go przekompilować. Jeśli pracujesz w środowisku korporacyjnym, na współużytkowanym serwerze internetowym lub korzystasz z gotowych plików binarnych, ponowna kompilacja może być niemożliwa. Z programistycznego punktu widzenia podejście to jest nieco kłopotliwe. Funkcje xmlrpc_en ´code_request() i xmlrpc_decode_request() wykonują za nas większość przekształceń, ale ciągle musimy tworzyć nagłówki HTTP i operować na poziomie gniazd, żeby stworzyć tunel do serwera. Wydaje się, że obydwa zadania mogłyby zostać przeniesione do osobnej funkcji. Na szczęście PEAR wyposażono w rozszerzenie XML-RPC, które wykonuje obydwie te czynności. PEAR, o ile jeszcze nie jesteś z nim zaznajomiony, to oficjalny zestaw zewnętrznych bibliotek PHP. Chociaż jest to osobny projekt o otwartym kodzie, program do instalacji i zarządzania pakietów jest dołączany do oficjalnej dystrybucji PHP. Korzystając z programu instalacyjnego PEAR, możesz łatwo zainstalować, zaktualizować i odinstalować biblioteki PEAR. Służą do tego proste polecenia. Żeby zobaczyć, co jest zainstalowane, przejdź do linii poleceń serwera PHP i wpisz polecenie wyświetlające rozszerzenia PEAR: Buttercup:~ root# pear list
W zależności od tego, w jaki sposób został zainstalowany PHP, być może będziesz musiał być zalogowany jako użytkownik root, żeby zarządzać bibliotekami PEAR na komputerze. Inny problem pojawia się wtedy, gdy dysponujesz niestandardową instalacją PHP, w której nie ma PEAR. W takim przypadku możesz pobrać go ze strony http://pear.php.net/package/PEAR/ ´download. Przy założeniu, że masz odpowiednie uprawnienia i zainstalowany jest PEAR, możesz zobaczyć poniższą listę: Buttercup:~ root# pear list Installed packages: =================== Package Version Archive_Tar 1.1 Console_Getopt 1.2 … PEAR 1.3.6 XML_Parser 1.0.1 XML_RPC 1.4.0
44
State stable stable stable stable stable
Rozdział 2. • Zrób zakupy w Amazon
Są to aktualnie zainstalowane rozszerzenia PEAR. Jeśli w ostatniej linii zobaczysz XML_RPC, wszystko jest w porządku. Oznacza to, że rozszerzenie XML-RPC jest już zainstalowane i możesz z niego skorzystać. Jeśli na liście nie ma XML_RPC, możesz łatwo zainstalować to rozszerzenie, wpisując: Buttercup:~ root# pear install XML-RPC
Przyjrzyjmy się szybko, w jaki sposób wykorzystać rozszerzenie XML-RPC do współpracy z usługami XML-RPC. Stwórzmy prosty skrypt, który wywołuje usługę z bazy danych UPC Internet Database:
W pierwszej linii za pomocą instrukcji require_once() dodajemy do skryptu wymaganą bibliotekę PEAR. Dzięki temu dostępne stają się wszystkie funkcje XML_RPC z PEAR. Instrukcja ta musi znajdować się w pierwszej linii skryptu. W następnej — $params = array (new XML_RPC_VALUE('079400560704', 'string'));
— tworzymy tablicę parametrów. Dzieje się tu kilka rzeczy. Najpierw tworzymy nowy obiekt XML_RPC_Value. W PEAR każdy parametr znajduje się we własnym obiekcie XML_RPC_Value. Argumenty są następnie umieszczane w tablicy. Rozszerzenie XML_RPC przyjmuje tę tablicę i tworzy wymaganą strukturę parametrów w żądaniu. Obiekt XML_RPC_Value przyjmuje dwa argumenty. Pierwszym z nich jest wartość parametru, drugim typ danych XML-RPC. Chociaż podejście to wymaga określenia typu danych XML-RPC, eliminuje potrzebę wywoływania funkcji xmlrpc_set_type() dla typów daty i czasu oraz base64, dzięki czemu konwersja jest bardziej spójna, a kod bardziej czytelny: $msg = new XML_RPC_Message('lookupUPC', $params);
W kolejnej linii dla nowego obiektu, XML_RPC_Message, podajemy nazwę funkcji, którą chcemy wywołać, i stworzoną przed chwilą tablicę parametrów. Zwracaną wartością jest obiektowa reprezentacja wszystkich elementów wymaganych do stworzenia komunikatu żądania XML-RPC. W końcu, wykorzystując obiekt XML_RPC_Message i informacje o usłudze, tworzymy obiekt XML_RPC_ ´Client. Obiekt klienta ma wszystkie informacje, które potrzebne są do stworzenia żądania XML-RPC. Zawiera on nazwę serwera, ścieżkę do usługi i nazwę zdalnego wywołania. Metoda send() obiektu wykorzystuje te informacje i wykonuje żądanie XML-RPC do usługi. Wywołujemy metodę send(), przekazując do niej obiekt komunikatu, który zawiera parametry wymagane do wykonania zdalnego wywołania:
45
PHP Web 2.0. Tworzenie aplikacji typu mashup
$retVal = $client->send($msg);
Metoda ta automatycznie pobiera odpowiedź usługi i umieszcza ją w obiekcie klasy XML_RPC_ ´Response. XML_RPC_Response jest klasą PHP reprezentującą odpowiedź XML-RPC. Jej metody umożliwiają pobranie danych lub stwierdzenie, czy odpowiedź zawiera błąd i jaki to jest błąd. Do pobrania danych z odpowiedzi wykorzystujemy metodę value(): $valueObj = $retVal->value();
W końcu dotarliśmy do właściwych danych. Metoda value() zwraca kolejny obiekt XML_RPC_value, który zawiera tylko dane zwrócone przez zdalne wywołanie. Żeby pobrać właściwe dane, które są w nim przechowywane, wywołujemy metodę scalarVal(). Bez względu na to, czy odpowiedź zawiera pojedynczą wartość skalarną, strukturę, czy tablicę, zostanie ona zwrócona przez metodę scalarVal(). To do nas należy wykrycie i odpowiednie obsłużenie zwróconych złożonych wartości. W taki sposób wykorzystuje się PEAR do stworzenia klienta XML-RPC. To tylko bardzo skrótowe i pobieżne wprowadzenie w zagadnienie. Głębsze spojrzenie na obiekty, właściwości i metody XML-RPC pozwoli lepiej poznać sposoby wykonywania ważniejszych zadań, takich jak wykrywanie błędów i zmiana portu wywołania. Więcej informacji na temat rozszerzenia XML_RPC w PEAR możesz znaleźć w książce PEAR. Programowanie w PHP Stoyana Stefanova i in., opublikowanej przez wydawnictwo Helion.
REST Już po wprowadzeniu XML-RPC pojawiły się inne standardy usług internetowych wykonujące zadania, do których on się nie nadawał. Niektóre z nich, jak SOAP i jego pochodne, dają programistom większe możliwości i są bardziej elastyczne. Inne, jak RSS są wykorzystywane w specyficznych zastosowaniach. Sytuacja ta nie różni się specjalnie od innych trendów rozwoju technologii, w których standardy są na początku proste, a potem z lepszym czy gorszym skutkiem stają się coraz bardziej skomplikowane. Całkiem niedawno popularny stał się jednak nowy standard usług internetowych, który rozwija się wbrew temu trendowi. REST, akronim od Representational State Transfer (przesyłanie stanu reprezentacji), nie jest sformalizowanym standardem, ale rodzajem architektury. W teorii REST jest rozbudowany i nowoczesny. Jego zastosowanie nie ogranicza się tylko do sieci Web, jednak przeglądarki i serwery internetowe bardzo dobrze wpisują się w jego podstawy teoretyczne. Omówiony w 2000 roku w dysertacji doktorskiej Roya Fieldinga, jest próbą opisu architektury oprogramowania sieciowego. W praktyce REST jest prosty i elastyczny. Podobnie jak w przypadku XML-RPC, klient wykonuje żądanie do serwera, ale na tym kończą się podobieństwa. Żądania REST są po prostu
46
Rozdział 2. • Zrób zakupy w Amazon
żądaniami HTTP. Mogą mieć postać jednej z pięciu standardowych metod HTTP: GET, POST, PUT, DELETE lub HEAD. Trzy ostatnie są rzadko używane podczas tworzenia aplikacji internetowych i podobnie jest w przypadku REST. Widać wyraźną przewagę POST i GET. Jednak wiele interfejsów API wykorzystuje GET, nawet jeśli do danej operacji lepiej nadaje się POST. Tak czy siak, oprogramowanie wykonujące żądania REST działa na podobnej zasadzie jak przeglądarki odwołujące się do serwera internetowego. Sposób przekazywania parametrów żądania określony jest w interfejsie API. W przypadku REST opartego na metodzie GET, parametry będą znajdować się w łańcuchu adresu URL. Dokładnie tak samo przekazywane są parametry przy normalnym wykorzystaniu metody GET. Nie musimy umieszczać żądania w strukturze XML. Również sposób zwracania wartości całkowicie zależy od interfejsu API. Niektóre z nich zwracają jakiś rodzaj struktury XML, inne po prostu łańcuch znaków bez żadnej struktury. Ostatni sposób jest szczególnie często stosowany w odpowiedziach zwracających pojedynczą wartość. Zwracane błędy nie mają ustandaryzowanej struktury. Sposób ich zwracania określany jest przez interfejs API, a sposób obsługi całkowicie zależy od programisty. Programiści wykorzystujący REST wskazują, że nawet prosty XML-RPC może być w niektórych przypadkach przesadą. REST bardzo dobrze sprawdza się w usługach internetowych, w których między klientem a serwerem nie jest przesyłana zbyt duża ilość danych, chociaż jego elastyczność umożliwia obsługę złożonych odpowiedzi. REST uzmysławia także, że w kontekście HTTP typy danych są całkowicie umowne. W HTTP wszystko, co jest przesyłane, stanowi tak naprawdę łańcuch znaków. Liczba zmiennoprzecinkowa jest nią tylko dlatego, że znajduje się w znaczniku double XML-RPC. Także wartości boolowskie mogą być z łatwością reprezentowane za pomocą słów „prawda” i „fałsz”, „T” i „N” albo nawet „stół” i „krzesło”. To od usługi zależy, czy 1,974234 będzie traktowane jako liczba zmiennoprzecinkowa, a „T” jako prawda, dzięki czemu po stronie klienta nie trzeba rzutować typów. REST i AJAX Istotnym powodem ostatniego wzrostu popularności REST jest to, że bardzo dobrze współpracuje on z inną dość znaną architekturą. Asynchroniczny JavaScript i XML, znany również jako AJAX, oparty jest na przeglądarce wysyłającej informacje do serwera poprzez obiekt JavaScript XMLHttpRequest, parsowaniu odpowiedzi XML za pomocą funkcji DOM i dynamicznej aktualizacji strony bez jej przeładowywania. Obiekt XMLHttpRequest wymaga tylko adresu URL usługi i łańcucha parametrów w postaci par nazwa-wartość. Połączenie łańcucha w taki sposób, aby powstało kompletne żądanie XML-RPC w JavaScript, byłoby trudne i męczące. Musiałbyś stworzyć prawidłowo sformatowany dokument XML w całości w JavaScript, mieszając obydwa języki. Połączenie łańcucha znaków z parami nazwa-wartość i utworzenie z niego adresu URL, a tego dokładnie wymaga REST, jest o wiele prostsze. Chociaż skupiamy się tu na wykorzystaniu REST w PHP, pamiętaj o tym, kiedy będziesz pisał klienta w JavaScript i miał możliwość wyboru protokołu w API. Więcej informacji na temat pisania aplikacji ajaksowych znajdziesz w książce Christiana Darie i in. AJAX i PHP. Tworzenie interaktywnych aplikacji internetowych (Helion 2008).
47
PHP Web 2.0. Tworzenie aplikacji typu mashup
Praca z REST w PHP Podobnie jak w przypadku XML-RPC, musimy za pomocą PHP stworzyć żądanie REST oraz przetworzyć odpowiedź. Nie istnieją specjalne funkcje do obsługi REST, ale nie są one potrzebne. Ponieważ w REST wykorzystywane jest proste żądanie HTTP, do wysłania żądania i przechwycenia odpowiedzi możemy użyć istniejących funkcji. Odpowiedź będzie miała postać dokumentu XML. Przyjrzymy się dwóm sposobom jej przetwarzania i przekształcania na dane PHP.
Wykonywanie żądania REST Jak wspominaliśmy wcześniej, REST jest zasadniczo podobny do żądania strony internetowej wysyłanego do serwera internetowego i nie ma w nim niczego niezwykłego. Przyjrzymy się kilku sposobom wykonywania tego żądania.
Metody GET i POST Tworzyliśmy już wcześniej żądania POST podczas wykonywania wywołań XML-RPC. Przyjrzyjmy się bliżej różnicom między nagłówkiem GET i POST. Poniżej znajduje się żądanie GET: GET /aService.php?Jeden=1&Dwa=2+plus+2%25&Trzy=3 HTTP/1.0 Host: localhost User-Agent: A PHP Client
Należy zwrócić uwagę na kilka spraw: 1. Pierwszym poleceniem w pierwszej linii jest GET. 2. Następnie znajduje się ścieżka do usługi, /aService.php. 3. Łańcuch zapytania Jeden=1&Dwa=2+plus+2%25&Trzy=3 jest zestawem parametrów znajdującym się za ścieżką, oddzielonym od niej znakiem zapytania. Każdy parametr oddzielony jest znakiem &. 4. Wartości parametrów muszą być zakodowane do postaci URL. W tym przykładzie wartością parametru Dwa jest 2 plus 2%25. 5. Poza nagłówkami nie ma żadnych danych. Używając tych samych parametrów, zamieńmy żądanie GET na żądanie POST i porównajmy je. POST /aService.php HTTP/1.0 User-Agent: XML-RPC Client Host: localhost Content-Type: application/x-www-form-urlencoded Content-Length: 28 Jeden=1&Dwa=2+i+2%25&Trzy=3
48
Rozdział 2. • Zrób zakupy w Amazon
1. Pierwszym poleceniem w pierwszej linii jest POST. 2. Po nim, podobnie jak poprzednio, znajduje się ścieżka do usługi. 3. Parametry Jeden=1&Dwa=2+plus+2%25&Trzy=3 nie znajdują się za ścieżką, ale tworzą zawartość żądania. 4. Wartości zakodowane są w postaci URL. 5. Żądania POST z przeglądarek internetowych muszą mieć również nagłówek Content-Type o wartości application/x-www-from-urlencoded. W żądaniu XML-RPC, które miało postać dokumentu XML, wartością tego nagłówka było text/xml. 6. Nagłówek Content-Length musi wskazywać rozmiar zawartości żądania. Żądania HTTP mogą mieć więcej nagłówków i być bardziej skomplikowane. Jednak w przypadku żądań REST są to jedyne wymagane nagłówki. We wszystkich przykładach wykorzystujemy HTTP 1.0. To może wydawać się archaizmem, ponieważ HTTP 1.1 jest już dostępny od kilku lat. Jednak używamy starszego protokołu, ponieważ serwery HTTP 1.1 często odpowiadają blokowymi odpowiedziami, kiedy wykorzystywany jest protokół 1.1. Oznacza to, że w celu skrócenia odpowiedzi serwer zaczyna ją wysyłać, nie znając jeszcze jej rozmiaru i nie przesyłając nagłówka Content-Length. O ile większość nowoczesnych klientów obsługuje taką sytuację bez problemu, PHP tego nie potrafi. Funkcjonalność gniazd powoduje, że musielibyśmy sami obsłużyć przesyłanie bloków odpowiedzi. Chociaż nie jest to niemożliwe, jednak jest dość żmudne. Dlatego też w tej książce będziemy używać HTTP 1.0.
Wykorzystanie gniazd do inicjowania żądań REST Znając różnicę między GET i POST, możemy zmodyfikować poprzednie funkcje żądań XML RPC wykorzystujące POST, tak żeby pracowały z REST. Poprzednia funkcja służąca do wykonywania połączeń przez gniazda, czyli send(), pobiera pełne żądanie HTTP, otwiera gniazdo i zwraca odpowiedź serwera. Możemy jej użyć do wykonania dokładnie tych samych czynności. Wszystko, czego potrzebujemy, to pełne żądanie HTTP. Aby je stworzyć, możemy zmodyfikować i wykorzystać kolejną wcześniej używaną funkcję, createPostRequest(). Musimy zmienić jednak w niej kilka kluczowych elementów: 1. Aby zastąpić zmienne PHP sformatowaną zawartością, nie możemy korzystać z xmlrpc_encode_request(). 2. Zmienne muszą być przekonwertowane na łańcuch zapytania URL. W przypadku GET należy je dołączyć do ścieżki do usługi, a w przypadku POST — do zawartości żądania. 3. O ile żądania XML-RPC są przesyłane za pomocą POST, większość żądań REST wykonywanych jest za pomocą GET. Jednak niektóre interfejsy API mogą wymagać użycia POST. Musimy być przygotowani na obie możliwości.
49
PHP Web 2.0. Tworzenie aplikacji typu mashup
Żeby poradzić sobie z problemami z pierwszego punktu i niektórymi problemami z drugiego punktu, stworzymy funkcję, która będzie przyjmować tablicę asocjacyjną, przeglądać ją, kodować wartości do postaci URL i tworzyć pojedynczy łańcuch zapytania z kluczy i wartości. function makeParameterString($anArray) { $returnMe = ''; foreach($anArray as $key => $value) { $returnMe .= "&" . $key . "=" . urlencode($value); } return substr($returnMe, 1); }
Powyższa funkcja zakłada, że klucze tablicy są nazwami parametrów, a wartości w tablicy są wartościami zmiennych, które chcesz przekazać. Pętla foreach przechodzi tablicę i formatuje pary nazwa-wartość dla każdego elementu tablicy w łańcuchu zapytania. Każda para nazwa-wartość jest oddzielona znakiem &, dlatego umieszczamy go na początku. Łączymy nazwę parametru, $key, ze znakiem równości wskazującym przypisanie wartości, a następnie samą wartością reprezentowaną przez $value. Przed dołączeniem wartość jest przetwarzana za pomocą funkcji urlencode(). Na zewnątrz pętli za pomocą substr() usuwamy pierwszy & z pierwszej pary nazwa-wartość, a następnie zwracamy łańcuch zapytania.
Tworzenie funkcji żądań GET i POST Teraz, kiedy mamy już łańcuch parametrów, możemy stworzyć nagłówki i przygotować żądanie. Zaczniemy od żądania POST, ponieważ jest ono uderzająco podobne do odpowiednika dla XML-RPC. function createPostRequest($remoteServer, $remotePath, $paramString) { $theRequest = "POST " . $remotePath . " HTTP/1.0\n" . "User-Agent: XML-RPC Client\n" . "Host: " . $remoteServer . "\n" . "Content-Type: application/x-www-form-urlencoded\n" . "Content-Length: " .strlen($paramString) . "\n" . "\n" . $paramString . "\n"; return $theRequest; }
Powyższa funkcja pobiera adres serwera, ścieżkę do usługi oraz łańcuch parametrów i tworzy z nich pełne żądanie POST. Jedyną różnicą jest zmiana nagłówka Content-Type z wymaganego przez XML-RPC text/xml na application/x-www-form-urlencoded, który wskazuje na bardziej ogólny typ żądania POST. Teraz możemy stworzyć funkcję przygotowującą żądanie GET. Jest ono podobne do żądania POST, ale parametry zostaną umieszczone w pierwszej linii, metoda zostanie zmieniona na GET, a nagłówek Content-Length otrzyma wartość 0, ponieważ nie będzie żadnej zawartości.
50
Rozdział 2. • Zrób zakupy w Amazon
function createGetRequest($remoteServer, $remotePath, $paramString) { $theRequest = "GET " . $remotePath . "?" . $paramString . " HTTP/1.0\n" . "Host: " . $remoteServer . "\n" . "User-Agent: XML-RPC Client\n\n"; return $theRequest; }
W pierwszej linii zamiast metody POST pojawiła się metoda GET. Ścieżka umieszczona jest tak jak zazwyczaj, ale teraz dodaliśmy do niej znak zapytania wskazujący, że zamierzamy wysłać do serwera jakieś parametry. Wartości tych parametrów znajdują się za znakiem zapytania. Nagłówki Content-Type i Content-Length i zawartość zostały usunięte, ponieważ w tym przypadku nie są wymagane. Przyjrzyjmy się teraz funkcjom pobierającym odpowiedź usługi REST.
Tworzenie klasy parsującej REST Podobnie jak w przypadku klasy XMLRPCParser, powinniśmy stworzyć funkcję osłaniającą, która ułatwi wykonywanie wywołań REST. Żeby zwiększyć czytelność kodu, umieścimy wszystkie funkcje w klasie. class RESTParser { public function callService($parameters, $remoteServer, $remotePath, $httpMethod, $port=80) { $paramString = $this->makeParameterString($parameters); switch(strtoupper($httpMethod)) { case "POST": $fullRequest = $this->createPostRequest($remoteServer, $remotePath, $paramString); break; case "GET": $fullRequest = $this->createGetRequest($remoteServer, $remotePath, $paramString); break; default: $fullRequest = $this->createGetRequest($remoteServer, $remotePath, $paramString); } return $this->send($remoteServer, $port, $fullRequest); } private function makeParameterString($anArray) { $returnMe = ''; foreach($anArray as $key => $value) { $returnMe .= "&" . $key . "=" . urlencode($value); } return substr($returnMe, 1); } private function createPostRequest($remoteServer, $remotePath, $paramString) {
51
PHP Web 2.0. Tworzenie aplikacji typu mashup
$theRequest = "POST " . $remotePath . " HTTP/1.0\n" . "Host: " . $remoteServer . "\n" . "User-Agent: XML-RPC Client\n" . "Content-Type: application/x-www-form-urlencoded\n" . "Content-Length: " .strlen($paramString) . "\n" . "\n" . $paramString . "\n"; return $theRequest; } function createGetRequest($remoteServer, $remotePath, $paramString) { $theRequest = "GET " . $remotePath . "?" . $paramString . " HTTP/1.0\n" . "Host: " . $remoteServer . "\n" . "User-Agent: XML-RPC Client\n\n"; return $theRequest; } private function send($remoteServer, $remotePort, $fullXMLRPCRequest) { $headers = ''; $data = ''; $socket = fsockopen($remoteServer, $remotePort); fwrite($socket, $fullXMLRPCRequest); while ($str = trim(fgets($socket))) { $headers .= $str . '\n'; } $data = ''; while (!feof($socket)) { $data .= fgets($socket); } fclose($socket); return $data; } }
Powyższa klasa używa tych samych funkcji, które opisywaliśmy. Znajduje się w niej również nowa funkcja osłonowa callService(), podobna do funkcji o tej samej nazwie w klasie XMLRPCParser, ale z dwoma wyjątkami. Po pierwsze, nie ma parametru określającego nazwę zdalnej metody, ponieważ w REST nie występuje takie pojęcie. Po drugie, oczekuje słowa kluczowego GET lub POST określającego, której metody HTTP chcemy użyć do wywołania REST. Podsumujmy sekwencję czynności w tej klasie: 1. Do metody makeParameterString() przekazywana jest tablica w celu pobrania łańcucha zapytania zakodowanego do postaci URL. 2. Następnie wynik tej funkcji przekazywany jest do metody createGetRequest() lub createPostRequest() w zależności od typu żądania, które chcemy wykonać. Dzięki temu otrzymujemy pełne żądanie z odpowiednimi nagłówkami. 3. Żądanie to przekazujemy do przedstawionej wcześniej metody send(), która zwróci odpowiedź REST z usługi.
52
Rozdział 2. • Zrób zakupy w Amazon
Testowanie klasy parsującej REST Do przetestowania funkcji REST możemy stworzyć podstawową usługę REST na serwerze internetowym. Dzięki temu zobaczymy, jak działają wszystkie funkcje. Usługa będzie pobierała przychodzące żądanie, pokazywała, jaka metoda została użyta do jego wykonania, i wypisywała wszystkie parametry POST lub GET, które zostały przesłane w żądaniu. Stwórz plik z następującym kodem: Metoda
Zmienne POST:
Klucz: | Wartość:
Zmienne GET:
Klucz: | Wartość:
Nazwij ten plik RESTService.php i umieść go na serwerze w miejscu, które jest dostępne z poziomu przeglądarki internetowej. Skrypt, wykorzystując globalną tablicę $_SERVER, wyświetla metodę, jaka została użyta do uzyskania dostępu do niego. Następnie sprawdza globalne tablice $_GET i $_POST i wypisuje ich elementy. Z usługą tą będziemy komunikować się za pomocą przykładowej strony klienta REST, exampleREST.php. Podobnie jak w przypadku XML-RPC, na początku pliku z klasą RESTParser umieścimy kod testowy. Kod stworzy obiekt parsera i dwie tablice. Pierwszej tablicy użyjemy do wywołania usługi za pomocą POST, drugiej do wywołania usługi za pomocą GET.
Shu Chow's Playlist 2006-11-24T12:01:21Z
126
Rozdział 4. • Własna szafa grająca z teledyskami
Pure Lightning Seeds
file:///Users/schow/Music/Pure.mp3
Roadrunner The Modern Lovers
file:///Users/schow/Music/Roadrunner.mp3
The Bells April Smith
file:///Users/schow/Music/The_Bells.mp3
playlist jest głównym elementem całego dokumentu. Wymaga co najmniej jednego elementu potomnego, trackList, choć może mieć tych elementów więcej. W tym przykładzie tytuł listy odtwarzania jest określony w elemencie title, a data utworzenia w elemencie date. Wewnątrz elementu trackList znajdują się ścieżki tworzące listę odtwarzania. Każdy utwór znajduje się w elemencie track. Informacje o utworze, włączając w to położenie pliku, przechowywane są w elementach wewnątrz elementu track. W tym przykładzie dla każdego utworu określono tytuł, nazwę wykonawcy i położenie pliku. Oficjalne specyfikacje pozwalają na podanie większej liczby elementów z informacjami o utworze, takich jak długość utworu czy informacje o albumie.
Poniżej znajduje się podsumowanie elementów potomnych elementu playlist: Element potomny listy odtwarzania
Wymagany?
Opis
trackList
Tak
Rodzic poszczególnych elementów utworu. Jest to jedyny wymagany element listy odtwarzania. Może być pusty, jeśli lista nie zawiera żadnych utworów.
title
Nie
Tytuł listy odtwarzania XSPF czytelny dla człowieka.
creator
Nie
Nazwa twórcy listy odtwarzania.
annotation
Nie
Komentarz do listy odtwarzania.
info
Nie
Adres URL strony zawierającej więcej informacji na temat listy.
location
Nie
Adres URL samej listy.
127
PHP Web 2.0. Tworzenie aplikacji typu mashup
Element potomny listy odtwarzania
Wymagany?
Opis
identifier
Nie
Unikalny identyfikator listy odtwarzania. Musi być prawidłowym identyfikatorem URN.
image
Nie
Adres URL obrazka reprezentującego listę odtwarzania.
date
Nie
Data stworzenia (nie ostatniej modyfikacji!) listy odtwarzania. Musi mieć format typu danych dateTime schematu XML, na przykład „2004-02-27T03:30:00”.
license
Nie
Jeśli lista odtwarzania podlega jakiejś licencji, jest ona określona w tym elemencie.
attribution
Nie
Jeśli lista odtwarzania jest modyfikacją innej listy, ten element odsyła do oryginalnej listy.
link
Nie
Umożliwia umieszczenie w liście odtwarzania zasobów niezwiązanych z XSPF.
meta
Nie
Umożliwia umieszczenie w liście odtwarzania metadanych niezwiązanych XSPF.
extension
Nie
Umożliwia umieszczenie w liście odtwarzania rozszerzeń XML niezwiązanych z XSPF.
Element trackList może mieć nieograniczoną liczbę elementów reprezentujących poszczególne ścieżki. track jest jedynym dozwolonym elementem potomnym trackList. Elementy potomne elementu track zawierają informacje o każdym utworze. W poniższej tabeli znajdują się elementy potomne elementu track. Element potomny utworu
Wymagany?
Opis
location
Nie
Adres URL pliku z utworem.
identifier
Nie
Identyfikator listy odtwarzania. Musi być prawidłowym identyfikatorem URN.
title
Nie
Tytuł utworu zrozumiały do człowieka.
creator
Nie
Nazwa twórcy utworu (zazwyczaj wykonawca piosenki).
annotation
Nie
Komentarze do utworu.
info
Nie
Adres URL do strony zawierającej więcej informacji o utworze.
image
Nie
Adres URL do obrazka reprezentującego ścieżkę.
album
Nie
Nazwa albumu, z którego pochodzi utwór.
trackNum
Nie
Numer porządkowy pozycji utworu w albumie.
duration
Nie
Czas odtwarzania utworu w milisekundach.
link
Nie
Umożliwia umieszczenie w liście odtwarzania zasobów niezwiązanych z XSPF.
128
Rozdział 4. • Własna szafa grająca z teledyskami
Element potomny utworu
Wymagany?
Opis
meta
Nie
Umożliwia umieszczenie w liście odtwarzania metadanych niezwiązanych z XSPF.
extension
Nie
Umożliwia umieszczenie w liście odtwarzania rozszerzeń XML niezwiązanych z XSPF.
Zauważ, że format XSPF jest bardzo prosty i przystosowany do opisu listy utworów muzycznych. Nie został zaprojektowany jako repozytorium ani baza danych piosenek. Nie ma tu zbyt wielu możliwości przetwarzania listy. XSPF jest jedynie formatem współużytkowanej listy odtwarzania i niczym więcej.
RSS Najprostsza odpowiedź na pytanie, czym jest RSS, brzmi: jest to plik XML wykorzystywany do publikowania często odczytywanych informacji, takich jak wiadomości, wpisy w blogu czy łącza do wiadomości audio i wideo. Witryny z wiadomościami, takie jak Slashdot.org czy New York Times, udostępniają swoje newsy w kanałach informacyjnych. Kolejne publikowane wiadomości dodawane są do kanałów informacyjnych. Dzięki temu, że RSS jest oparty na XML, może być z łatwością odczytywany przez oprogramowanie agregujące zewnętrznych firm. Za pomocą jednego programu można pobrać dane z różnych źródeł i przeczytać wiadomości w jednym miejscu. Pliki RSS mogą być również odczytywane i parsowane przez aplikacje internetowe. Jeśli udostępnię kanał z wiadomościami z mojego bloga, umożliwię pobranie go innej witrynie i śledzenie mojego codziennego życia. Jest to jeden ze sposobów, dzięki któremu niewielka witryna może udostępnić podstawową usługę internetową minimalnym kosztem. Pełniejsza odpowiedź brzmiałaby, że jest to grupa standardów XML (wykorzystywanych do publikowania często aktualizowanych informacji takich jak wiadomości lub blogi), które mogą być mało kompatybilne z sobą. Każda wersja ma także za sobą historię konfliktów i sporów. Nie będziemy jednak się zagłębiać w kłótnie wokół RSS. Przyjrzymy się po prostu, co z tego wynikło. RSS jest dostępny obecnie w dwóch głównych odmianach: Q Gałąź RSS 1.0 zawiera wersje 0.90, 1.0 oraz 1.1. Jej celem jest rozszerzalność
i elastyczność. Minusem jest złożoność standardu. Q Gałąź RSS 2.0 zawiera wersje 0.91, 0.92 oraz 2.0.x. Jej celem jest prostota i łatwość użycia. Minusem jest to, że ma zbyt małe możliwości jak na wymagania skomplikowanych witryn i kanałów informacyjnych. Obydwa formaty mają podobne konstrukcje. Po głównym elemencie XML występują metadane na temat samego kanału. Po nich znajduje się jeden lub więcej elementów, którymi mogą być wiadomości, wpisy w blogu lub wiadomości audio i wideo. Elementy te są istotą kanału RSS.
129
PHP Web 2.0. Tworzenie aplikacji typu mashup
Poniżej znajduje się przykładowy plik RSS 1.1 z witryny XML.com:
XML.com http://xml.com/pub
XML.com features a rich mix of information and services for the XML community.
XML.com http://xml.com/universal/images/xml_tiny.gif
The Restful Web: Amazon's Simple Queue Service
http://www.xml.com/pub/a/2005/01/05/restful.html
In Joe Gregorio's latest Restful Web column, he explains that Amazon's Simple Queue Service, a web service offering a queue for reliable storage of transient messages, isn't as RESTful as it claims.
Transforming XML: Extending XSLT with EXSLT
http://www.xml.com/pub/a/2005/01/05/tr-xml.html
In this month's Transforming XML column, Bob DuCharme reports happily that the promise of XSLT extensibility via EXSLT has become a reality.
Głównym elementem pliku RSS jest Channel. Zaraz po nim znajdują się elementy opisujące wydawcę kanału. Elementy title, link, description oraz image zawierają dodatkowe informacje o kanale.
130
Rozdział 4. • Własna szafa grająca z teledyskami
Właściwa zawartość znajduje się w elemencie items. Nawet jeśli kanał nie zawiera wiadomości, element items jest wymagany, ale będzie pusty. Poniższa tabela zawiera charakterystykę poszczególnych elementów. Element potomny elementu channel
Wymagany?
Opis
title
Tak
Zrozumiały dla człowieka tytuł kanału.
link
Tak
Adres URL kanału.
description
Tak
Zrozumiały dla człowieka opis kanału.
items
Tak
Element rodzic dla elementów item.
image
Nie
Miejsce, w którym przechowywane są informacje o oficjalnym logo kanału.
others
Nie
W tym miejscu mogą znajdować się wszystkie pozostałe elementy, które nie znajdują się w przestrzeni nazw RSS. Przestrzeń nazw musi zostać zadeklarowana wcześniej, a elementy potomne muszą zawierać przedrostek.
Jeśli zostanie użyty element image, informacje o logo kanału będą znajdować się w jego elementach potomnych. Wymagany jest w nim element title, a element link zawierający właściwy adres URL obrazka, chociaż opcjonalny, jest niezwykle użyteczny.
Każdy wpis w blogu lub wiadomość audio czy wideo jest reprezentowana przez element item. W omawianym pliku RSS każdy element ma tytuł, łącze i opis. Plik zawiera dwa elementy item przed znacznikami zamykającymi elementów items i Channel. Zauważ, że używana jest przestrzeń nazw rdf. Odmiana RSS 1.0 w dużym zakresie korzysta z RDF (ang. Rich Description Framework — rozbudowany schemat opisu). RDF jest schematem XML służącym do tworzenia dokumentów, które są nie tylko przyjazne dla komputera, ale również dla człowieka. Tematy poszczególnych informacji są umieszczone w jednym miejscu. Żeby skorzystać z RSS 1.0, nie musimy zbyt wiele wiedzieć na temat RDF. Jednak przyjrzymy się mu bliżej w przyszłym projekcie. Na razie musisz zapamiętać, że w odmianie RSS 1.0 każda wiadomość reprezentowana jest przez pojedynczy element item. W tabeli na następnej stronie znajdują się elementy potomne elementu item. Kiedy przyjrzymy się kanałowi RSS 2.0 z witryny IBM DeveloperWorks, zauważymy mnóstwo podobieństw:
developerWorks : Linux : Technical library http://www.ibm.com/developerworks/index.html
131
PHP Web 2.0. Tworzenie aplikacji typu mashup
Element potomny elementu item
Wymagany?
Opis
title
Tak
Zrozumiały dla człowieka tytuł elementu.
link
Tak
Adres URL elementu.
description
Tak
Zrozumiały dla człowieka opis elementu.
others
Nie
W tym miejscu może zostać umieszczony dowolny element nieznajdujący się w przestrzeni nazw RSS. Przestrzeń nazw musi zostać zadeklarowana wcześniej, a elementy potomne muszą mieć przedrostki.
The latest content from IBM developerWorks
Wed, 10 Jan 2007 01:03:11 EST en-us Copyright 2004 IBM Corporation.
IBM developerWorks
http://www-106.ibm.com/developerworks/i/dwlogo-small.gif
http://www.ibm.com/developerworks/index.html
thumbnail_url ?>' alt='' />
146
Rozdział 4. • Własna szafa grająca z teledyskami
Uruchomienie tego zapytania w przeglądarce spowoduje wyświetlenie wynikowej strony z filmami, które pasują do wyszukiwanego przez nas opisu. Pamiętaj, że przed uruchomieniem pliku YouTubePearTest.php musisz zainstalować CURL.
XML_RSS Podobnie jak w przypadku innych rozszerzeń PEAR, XML_RSS zmienia coś bardzo złożonego, czyli RSS, w coś bardzo prostego i łatwego do wykorzystania, czyli obiekty PHP. Pełna dokumentacja tego pakietu znajduje się pod adresem http://pear.php.net/package/XML_RSS/docs/ ´XML_RSS. W porównaniu z Services_YouTube i File_XSPF, XML_RSS różni się nieco filozofią. Dwa pierwsze pakiety pobierają informacje z miejsca, które Cię interesuje, i umieszczają je we właściwościach obiektów PHP. Na przykład File_XSPF pobiera nazwy ścieżek do obiektu Track, a Ty z kolei używasz metody getTitle() do pobrania tytułu ścieżki. W przypadku Services_YouTube wszystko działa na tej samej zasadzie, z tym że właściwości są publiczne i nie ma metod dostępowych. Odwołujesz się do właściwości filmu bezpośrednio w obiekcie video.
147
PHP Web 2.0. Tworzenie aplikacji typu mashup
W przypadku XML_RSS wartości, które nas interesują, znajdują się w tablicach asocjacyjnych. Metody pakietu zwracają tablice asocjacyjne, do których bezpośrednio się odwołujesz. Jest to niewielka różnica, ale powinieneś o niej wiedzieć na wypadek, gdybyś chciał spojrzeć do kodu źródłowego. Oznacza to także, że musisz zajrzeć do dokumentacji, żeby sprawdzić, jakie klucze tablicy są dostępne. Przyjrzyjmy się na przykładzie, jak to działa. Plik z przykładowym kodem nazywa się RSSPEARTest.php. Jeden z kanałów informacyjnych witryny Audioscrobbler zwraca plik RSS z piosenkami, które ostatnio odtwarzał użytkownik. Kanał nie zawsze zawiera dane, ponieważ po kilku godzinach odtwarzane piosenki są usuwane z listy i kanału informacyjnego. Dlatego też ten kanał najbardziej przyda się użytkownikowi, który często korzysta z Last.fm. Użytkownik RJ będzie tu dobrym przykładem. Wygląda na to, że zawsze czegoś słucha. Pobierzemy jego kanał informacyjny z serwisu Audioscrobbler.
Metoda getChannelInfo() zwraca tablicę, w której znajdują się metadane o kanale przechowywanym w pliku. Tablica zawiera elementy title, description oraz link z pliku RSS. Każdy z nich jest przechowywany w elemencie tablicy, którego klucz ma taką samą nazwę, jak nazwa tego elementu.
Dane w odpowiedzi będą zakodowane w formacie UTF-8. Dlatego też musimy wyświetlić stronę z kodowaniem UTF-8. Powyższa linia umieści deklarację XML na początku strony, zapewniając jej prawidłowe wyświetlenie. Umieszczenie zwykłej deklaracji
Teraz rozpoczniemy właściwe tworzenie strony. Zaczniemy od wykorzystania tablicy zwróconej przez getChannelInfo() do wyświetlenia elementów title i description kanału.
Wybór
Zawartość
Wynik zapytania do YouTube dla
W powyższym kodzie HTML napotykamy instrukcję if. Sprawdzamy w niej, czy w tablicy $firstVideo w ogóle coś się znajduje. Musimy sprawdzić, czy obiekt istnieje. Title:
Wprowadzono następujące dane:
Kluczowe dla tej aplikacji jest używanie na stronie parametru z zapytaniem o nazwie field.
Przegląd informacji na temat obiektu XMLHttpRequest Obiekt XMLHttpRequest jest sercem technologii AJAX. Jest to obiekt wbudowany we wszystkie nowoczesne przeglądarki internetowe (w wersji 5.0 i nowszych) służący do zarządzania żądaniami HTTP. Jest podobny do innych tego typu obiektów — na przykład obiektu form zarządzającego wszystkimi elementami formularzy lub obiektu window zarządzającego oknem przeglądarki. Cała technologia AJAX w istocie jest techniką wykorzystania obiektu XMLHttpRequest w celu wysyłania do serwera żądań HTTP wyzwalanych przez zdarzenie JavaScript po załadowaniu strony. Serwer zwraca dane, a obiekt XMLHttpRequest przekazuje odpowiedź serwera do jakiejś funkcji JavaScript na stronie. Dzięki wykorzystaniu JavaScript informacje o arkuszu stylów oraz dane obiektowego modelu dokumentu w przeglądarce (Document Object Model — DOM) zmieniają się dynamicznie. Spróbujmy przeanalizować cykl życia prostego obiektu XMLHttp ´Request.
181
PHP Web 2.0. Tworzenie aplikacji typu mashup
Korzystanie z obiektu Cykl życia obiektu rozpoczyna się od zdarzenia JavaScript. Może to być dowolne zdarzenie w aplikacji — naprowadzenie wskaźnika myszy na określoną część strony, załadowanie strony, kliknięcie przycisku itp. Po wyzwoleniu zdarzenia wykonywane są następujące działania: 1. Utworzenie obiektu XMLHttpRequest. 2. Zdefiniowanie docelowych informacji serwerowych (adres URL, ścieżka, port itp.) żądania HTTP, które mamy zamiar wykonać. 3. Podobnie jak w przypadku usług sieciowych, z których korzystaliśmy wcześniej, należy zdefiniować treść, która zostanie przesłana w żądaniu HTTP. Może to być pusty ciąg znaków. 4. Określenie funkcji wywoływanej zwrotnie i jej zdefiniowanie. 5. Wykorzystanie metody send() obiektu w celu wysłania żądania. 6. Przechwycenie odpowiedzi serwera w funkcji wywoływanej zwrotnie i wykorzystanie jej do modyfikacji strony.
Tworzenie obiektu Istnieją dwa sposoby tworzenia obiektu XMLHttpRequest, w zależności od tego, z jakiej przeglądarki korzystają użytkownicy. Jeśli użytkownik używa przeglądarki Mozilla (Firefox, Camino), Safari lub Opera, w celu stworzenia obiektu powinien wywołać konstruktor XMLHttpRequest(). Jeśli używa przeglądarki Internet Explorer 6, powinien skorzystać z ActiveX do utworzenia obiektu Microsoft.XMLHTTP, który jest klonem obiektu XMLHttpRequest. Należy skorzystać z tych metod w celu umieszczenia zwróconych obiektów w globalnej zmiennej JavaScript. Do wykrycia obecności obiektu XMLHttpRequest lub ActiveX można posłużyć się JavaScript. g_xmlHttp = null; function createXMLHttpRequest() { if (window.XMLHttpRequest){ g_xmlHttp = new XMLHttpRequest() } else if (window.ActiveXObject) { g_xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } }
Funkcję tę należy wywołać na początku żądania.
Tworzenie żądania HTTP Początek żądania HTTP powinien nastąpić po wykonaniu przez użytkownika jakiejś czynności. Żądanie zainicjujemy w momencie, kiedy użytkownik wyzwoli zdarzenie zwolnienia klawisza w polu tekstowym. Oznacza to, że w momencie, kiedy użytkownik wciśnie klawisz, aplikacja wywoła funkcję JavaScript, która nawiąże połączenie z serwerem.
182
Rozdział 5. • Zdjęcia londyńskiego metra
W naszym przykładzie funkcja ma nazwę sendRequest(). Trzeba teraz ją napisać. Funkcja utworzy obiekt XMLHttpRequest, zdefiniuje parametry serwera, zdefiniuje funkcję wywoływaną zwrotnie po przechwyceniu odpowiedzi serwera i na koniec wyśle żądanie. function sendRequest() { createXMLHttpRequest(); var url = "/mashups/ch6/examples/ajaxResponse.php?field=" + document.theForm.inputField.value; g_xmlHttp.onreadystatechange = parseResponse; g_xmlHttp.open("GET", url, true); g_xmlHttp.send(null); }
Pierwsza instrukcja tej funkcji wywołuje funkcję createXMLHttpRequest(), która tworzy obiekt XMLHttpRequest i umieszcza go w globalnej zmiennej g_xmlHttp. Instrukcja w drugim wierszu umieszcza adres URL usługi w zmiennej. Jest to wirtualny adres URL usługi. Można również utworzyć bezwzględny adres URL do usługi, ale nie jest to konieczne (powiemy o tym później). Ostatnia część instrukcji umieszcza wartość pola tekstowego w parametrze zapytania o nazwie field — na tę wartość oczekuje nasza usługa. W następnych trzech instrukcjach wykorzystano metody i właściwości obiektu XMLHttpRequest. onreadystatechange jest właściwością, która zawiera funkcję JavaScript wywoływaną zwrotnie dla tego obiektu. Należy ustawić ją na wartość nazwy funkcji, bez nawiasu otwierającego i zamykającego. Funkcja ta będzie wywołana w momencie, kiedy serwer odpowie. Można wybrać tylko jedną funkcję wywołania zwrotnego. Aby uruchomić ich więcej, należy utworzyć funkcję-opakowanie typu fasada i ustawić ją jako funkcję wywoływaną zwrotnie. Metoda open pobiera obiekt gotowy do wysłania żądania. Pierwsze dwa parametry są obowiązkowe. Pierwszy parametr określa metodę HTTP, która zostanie wykorzystana. Drugi parametr określa adres URL. Trzeci parametr decyduje o tym, czy obiekt powinien używać trybu asynchronicznego. Parametr jest opcjonalny, ale warto ustawić go na wartość true, ponieważ domyślną wartością jest false, a my chcemy działać w trybie asynchronicznym. Jeśli nie ustawimy tego parametru na true, pozostaniemy w trybie synchronicznym, co oznacza, że pozostała część skryptu nie uruchomi się, dopóki obiekt XMLHttpRequest nie odbierze odpowiedzi od serwera. Metoda send wysyła żądanie, pobierając jeden obowiązkowy parametr — treść żądania. W tym przykładzie wysyłamy wartość null, ponieważ wykonujemy żądanie GET. Samo żądanie nie ma żadnej treści. Gdybyśmy wykonywali żądanie POST, umieścilibyśmy parametry w oddzielnym ciągu znaków i przekazali je w formie parametru metody send. Po wywołaniu metody send następuje wykonanie żądania HTTP i uruchamia się funkcja wywoływana zwrotnie.
183
PHP Web 2.0. Tworzenie aplikacji typu mashup
Tworzenie i korzystanie z funkcji wywoływanej zwrotnie Funkcja wywoływana zwrotnie realizuje dwa zasadnicze zadania. Po pierwsze, przechwytuje odpowiedź serwera. Po drugie, przetwarza tę odpowiedź. Funkcję rozpoczniemy od kilku testów, dzięki którym uzyskamy pewność, że dane z serwera rzeczywiście dotarły. Gdybyśmy tego nie zrobili, pozostała część kodu uruchomiłaby się przedwcześnie, bez wszystkich niezbędnych fragmentów odpowiedzi serwera. Pierwsza instrukcja if sprawdza właściwość readyState obiektu XMLHttpRequest. W czasie obsługi żądania ta wartość zmienia się. Właściwość ta może przyjąć pięć dopuszczalnych wartości: Wartość readyState
Znaczenie
0
Niezainicjowane
1
Ładowanie
2
Załadowane
3
Interaktywne
4
Wykonane
Dane są gotowe do parsowania i wykorzystania przez aplikację internetową tylko wtedy, gdy właściwość ma wartość 4. Druga instrukcja if sprawdza właściwość status obiektu XMLHttpRequest. Jej wartość jest tym samym kodem, który zwraca 404 w przypadku brakującego pliku, 500 jeśli wystąpi wewnętrzny błąd serwera itp. Kod 200 wskazuje, że transakcja zakończyła się sukcesem. Zawsze trzeba pamiętać o sprawdzeniu, czy obsługa żądania zakończyła się pomyślnie. Jeśli tego nie zrobimy, dane mogą okazac się bezużyteczne. function parseResponse() { if (g_xmlHttp.readyState == 4) { if (g_xmlHttp.status == 200) { var response = g_xmlHttp.responseXML; var outputArea = document.getElementById("ServerResponse"). firstChild; var responseElements = response.getElementsByTagName("textField"); outputArea.nodeValue = responseElements[0].firstChild.nodeValue; } } }
Pierwszy wiersz za zagnieżdżoną instrukcją if pobiera wartość we właściwości responseXML obiektu XMLHttpRequest i umieszcza ją w zmiennej. Właśnie w tej właściwości przeglądarka przechowuje odpowiedź serwera. Inspekcja zmiennej umożliwia przeglądanie odpowiedzi XML z serwera. 184
Rozdział 5. • Zdjęcia londyńskiego metra
Druga instrukcja przechwytuje węzeł strony HTML, na której zostanie wyświetlona odpowiedź. Do przechodzenia w głąb modelu DOM wykorzystaliśmy funkcję JavaScript getElementById(). Te same funkcje DOM można wykorzystać w kodzie JavaScript w celu wydobycia informacji z odpowiedzi serwera. Dokładnie taką operację wykonujemy w trzeciej instrukcji. Wiemy, że informacje, które nas interesują, są umieszczone w elemencie textField odpowiedzi. Koncentrujemy się na tym i pobieramy węzeł. Każdy element DOM przechowuje wyświetlany tekst we właściwości o nazwie nodeValue. W czwartej instrukcji ustawiamy wartość właściwości nodeValue obszaru wyjściowego na wartość nodeValue odpowiedzi. Powoduje to modyfikację strony WWW przy każdym uruchomieniu funkcji. Aby zobaczyć działanie tego kodu w praktyce, należy wpisać jakiś tekst w polu tekstowym formularza wyświetlonego przez skrypt ajaxTest.php.
W kodzie sprawdziliśmy, czy status HTTP ma wartość 200. Jest to dobra praktyka, ale żeby można było z niej skorzystać, musi działać protokół sieciowy HTTP. Oznacza to, że strona musi zostać załadowana w przeglądarce WWW za pośrednictwem HTTP. Jeśli załadujemy stronę za pośrednictwem systemu plików (np. file:// ´ajaxTest.php, zamiast http://localhost/.../ajaxTest.php), test statusu nie powiedzie się i kod nie będzie mógł działać prawidłowo.
Jest to standardowy sposób wyzwalania aplikacji AJAX, który działa bardzo dobrze. Jednak parsowanie modelu DOM może sprawiać kłopoty. Trzeba przetwarzać dwie struktury DOM — lokalną stronę WWW i odpowiedź serwera. Na szczęście można uniknąć parsowania odpowiedzi serwera. Przede wszystkim istnieje siostrzana właściwość responseText, która spełnia tę samą funkcję. W niej zostanie zapisana odpowiedź serwera, jeśli będzie to tekstowy ciąg znaków, a nie XML. Zamiast przeszukiwania modelu DOM w poszukiwaniu informacji, które nas interesują, można więc bezpośrednio wykorzystać tekstową odpowiedź. Jeśli jesteś jedynie programistą warstwy
185
PHP Web 2.0. Tworzenie aplikacji typu mashup
frontend znacznie większej aplikacji internetowej, a ambicją firmy jest przesyłanie wszystkich danych w formacie XML, opcja skorzystania z odpowiedzi tekstowej może być niedostępna. Format XML lepiej zastosować również wtedy, gdy usługa internetowa będzie wykorzystywana na zewnątrz. Jeśli jednak piszemy bardzo prostą usługę na potrzeby jednej aplikacji, powinniśmy pamiętać, że nie wszystkie dane muszą mieć format XML. Z powodzeniem można przekazać prosty ciąg znaków i skorzystać po stronie klienta z właściwości responseText. Jeśli odpowiedź usługi sieciowej jest zbyt skomplikowana, aby można ją było wyrazić w postaci tekstowej, można ją sformatować w notacji JSON (JavaScript Object Notation) i w ten sposób przesłać odpowiedź do strony. W dalszym ciągu będzie to odpowiedź tekstowa, zatem można skorzystać z właściwości responseText i pominąć parsowanie. Notacja JSON oferuje strukturę języka XML, a jednocześnie prostotę zwykłego ciągu tekstowego. Wprowadzenie w tematykę JSON zamieszczono w następnym podrozdziale. Debugowanie aplikacji AJAX Debugowanie żądań i odpowiedzi serwera może być złożone. Do tego celu nie można skorzystać ze standardowego środowiska IDE. Potrzebne jest narzędzie do obserwacji strumieni HTTP. Na szczęście użytkownicy przeglądarki Firefox mogą skorzystać ze skryptu Greasemonkey, który realizuje właśnie tę funkcję. Greasemonkey jest rozszerzeniem Firefox, które pozwala użytkownikom na pisanie własnego kodu JavaScript i stosowanie go do witryny internetowej, którą odwiedzają. Skrypt jest dostępny pod adresem https://addons. ´mozilla.org/firefox/748/. Po zainstalowaniu go należy pobrać wskazówkę dotyczącą debugowania obiektu XMLHttpRequest (http://blog.monstuff.com/archives/000250.html). Narzędzie to pozwala na obserwację wszystkiego, co jest wysyłane z przeglądarki, oraz wszystkiego, co do niej przychodzi. Inne przydatne rozszerzenia przeglądarki Firefox to LiveHTTPHeaders, które wyświetla nagłówki żądania i odpowiedzi HTTP, oraz Firebug — debuger JavaScript i CSS. W przypadku przeglądarki Internet Explorer obserwację żądań HTTP umożliwia narzędzie komercyjne o nazwie HTTPWatch.
Notacja JSON (JavaScript Object Notation) Notacja JSON (JavaScript Object Notation) to po prostu format transferu danych, podobny do SOAP lub XML-RPC. W odróżnieniu od tych dwóch formatów, JSON nie bazuje na XML. Jest to kod JavaScript, który odwołuje się do definicji i formatów w stylu języka C. Chociaż z nazwy wynika, że jest to notacja obiektowa języka JavaScript, w wielu językach działających po stronie serwera są wbudowane parsery do interpretacji formatu JSON. Z tego względu i z uwagi na swoją prostotę, format ten stał się popularną alternatywą dla XML w sytuacji, gdy trzeba utrzymać komunikację między przeglądarką WWW a serwerem. Strona macierzysta pojektu JSON znajduje się pod adresem http://www.json.org.
186
Rozdział 5. • Zdjęcia londyńskiego metra
Przegląd obiektów JavaScript Spróbujmy najpierw dokonać szybkiego przeglądu obiektów JavaScript. W języku JavaScript klasy definiuje się tak, jakby były funkcjami. Aby zdefiniować właściwości klasy, należy skorzystać ze słowa kluczowego this, za którym następuje kropka i nazwa właściwości. W celu zdefiniowania metod klasy również należy skorzystać ze słowa kluczowego this, a następnie wpisać kropkę, nazwę funkcji, znak równości, słowo kluczowe function i definicję funkcji. Na przykład kod zamieszczony poniżej może być w języku JavaScript definicją obiektu Kot: function Kot (imie) { this.imie = imie; this.plec; this.wiek; this.je = function() { alert("Mniam"); } this.spi = function() { alert("zzzz…"); } }
Pokazana definicja klasy wymaga przekazania do konstruktora imienia, ponieważ jest to jedyny obowiązkowy parametr w definicji klasy. Egzemplarze klasy Kot można tworzyć w następujący sposób: tenKot = new Kot("Sabina"); innyKot = new Kot("Filemon");
Obiekty w JavaScript są dość proste. W modelu obiektowym nie ma słów kluczowych, które definiują metodę dostępu. Wszystkie składowe obiektów są publiczne. Aby uzyskać dostęp do właściwości obiektu lub nadać im wartości, wystarczy skorzystać z notacji z kropką. tenKot.plec = "F"; //Sabina jest teraz samicą. innyKot.imie = "Bonifacy"; //Filemon zmienił imię.
Zwróćmy uwagę na notację z kropką, z której skorzystaliśmy w celu uzyskania dostępu do właściwości obiektu. Takiej samej notacji można użyć w celu uzyskania dostępu do właściwości JSON.
Struktura JSON Do oddzielenia definicji obiektu od wartości służy znak równości umieszczany za nazwą obiektu. Właściwości obiektu są otoczone nawiasami klamrowymi. Mają one postać par nazwa-wartość rozdzielonych dwukropkiem.
187
PHP Web 2.0. Tworzenie aplikacji typu mashup
Dla właściwości JSON można wykorzystać następujące typy danych: Typ
Format
Przykłady
Number
Integer, float lub real. Wartość liczbowa.
1, 2.8217
String
Wartość ujęta w cudzysłów.
"Wartość", "Inna wartość"
Boolean
True/false, bez cudzysłowów (apostrofów).
true, false
Array
Lista ujęta w nawiasy kwadratowe.
[34, 498, 12]
Object
Nawiasy klamrowe.
{ wlasciwość nr jeden: wartosc nr jeden }
Null
Null.
Null
Strukturę obiektu Kot w języku JavaScript zaprezentowano powyżej. W formacie JSON można go przedstawić i rozszerzyć w następujący sposób: var kot = { imie: "Sabina", plec: "F", wiek: 4, wysterylizowany: true, kolnierzyk: { wisiorek: "dzwonek", kolor: "zielony" } }
Gdybyśmy chcieli stworzyć reprezentację takiego kota w XML, byłoby to nieco bardziej kłopotliwe i z pewnością wymagałoby więcej bajtów.
Sabina F 4 true
dzwonek zielony
Korzystanie z właściwości JSON W przykładzie pokazanym powyżej można uzyskać łatwy dostęp do właściwości obiektu kot za pośrednictwem notacji z kropką, przy czym kot jest obiektem nadrzędnym. Aby odczytać imię kota, należy skorzystać ze zmiennej kot.imie, wiek można odczytać ze zmiennej kot.wiek itp. Przykład użycia notacji z kropką w celu skorzystania z obiektu JSON przesłanego w odpowiedzi
188
Rozdział 5. • Zdjęcia londyńskiego metra
zaprezentowano w przykładowym pliku jsonExample.html. W tym celu wystarczy skorzystać z notacji z kropką w celu przejścia do właściwego poziomu hierarchii. Poniższy kod wyświetla kolor kołnierzyka Sabiny z wykorzystaniem zmiennej kot.kolnierzyk.kolor. function getColor() { alert("Kolor kołnierzyka Sabiny: " + kot.kolnierzyk.kolor); }
JavaScript jest językiem bez typów (co oznacza, że nie trzeba określać typu danych dla zmiennej), zatem można korzystać z właściwości bezpośrednio za pomocą notacji z kropką. Jedynymi elementami wymagającymi konwersji lub modyfikacji są tablice JSON. Spróbujmy na przykład dołączyć do powyższego przykładu tablicę kolorów futra. wiek: 4, kolorfutra: ["biały", "rudy"], wysterylizowany: true,
Dostęp do właściwości kolorfutra można uzyskać za pomocą notacji z kropką, choć są z tym pewne komplikacje. Jeśli spróbujemy uzyskać bezpośredni dostęp do tablicy, otrzymamy jej elementy oddzielone przecinkami. Zmienna kot.kolorfutra będzie miała wartość "biały, rudy". Aby uzyskać dostęp do poszczególnych elementów, należy podać numer elementu w nawiasach za nazwą tablicy tak, jak dla standardowej tablicy JavaScript. Wówczas zmienna kot.kolorfu ´tra[0] uzyska wartość "biały", natomiast zmienna kot.kolorfutra[1] będzie miała wartość "rudy". Notacja z kropką umożliwia również sprawdzenie długości tablicy. W tym celu wystarczy skorzystać z odwołania do .length za nazwą tablicy. Tak więc zmienna kot.kolorfutra.length będzie miała wartość 2.
Serializacja odpowiedzi JSON W przykładzie kot jest obiektem JavaScipt poddanym serializacji. Wskazują na to nawiasy klamrowe, które bezpośrednio otaczają właściwości. Oznacza to, że z danymi możemy pracować bezpośrednio za pośrednictwem notacji z kropką. Jednak bardzo często można spotkać tekstowe reprezentacje obiektów JSON. Jednym z takich przypadków jest obiekt JSON zapisany we właściwości responseText obiektu XMLHttpRequest. Oczywiście pod względem struktury obiekt jest zapisany w formacie JSON. Dane są jednak zrzutowane do postaci tekstowej. Aby przekształcić ciąg znaków JSON w obiekt JavaScript, należy przetworzyć go za pomocą metody eval(): var kot = '{"imie": "Sabina", "plec": "F", "wiek": 4, "wysterylizowany": true, "kolor": ["biały", "rudy"], "kolnierzyk": { "wisiorek": "dzwonek", "kolor": "zielony" }}'; var sabinaObj = eval('(' + kot + ')');
189
PHP Web 2.0. Tworzenie aplikacji typu mashup
function getColor() { alert("Kolor kołnierzyka Sabiny: " + sabinaObj.kolnierzyk.kolor); }
Metoda eval() uruchamia kod, który został do niej przekazany. Ponieważ przekazujemy kod sformatowany jako obiekt, funkcja także zwróci obiekt. Przykład przekształcenia z postaci nieserializowanej na serializowaną zamieszczono w pliku jsonTest.html. Należy zwrócić uwagę, że w wywołaniu funkcji eval() trzeba otoczyć ciąg znaków nawiasami oznaczającymi literał znakowy. Wynika to stąd, że chociaż kod wygląda jak obiekt JavaScript, to funkcja eval() interpretuje otwierający nawias klamrowy w ciągu znaków jako znak otwierający standardowy blok, a nie jako początek obiektu. Umieszczenie go w nawiasach spowoduje przełączenie parsera w tryb parsowania wyrażeń, który powoduje prawidłowe parsowanie kodu jako obiektu JavaScript. Uwaga na eval() Podczas korzystania z funkcji eval() należy zachować ostrożność. Funkcja ślepo uruchamia dowolny kod, który zostanie do niej przesłany, dlatego trzeba mieć pełne zaufanie do źródła danych wejściowych.
Na koniec powrócimy do interfejsów API. Istnieją tylko dwa interfejsy API, którym powinniśmy się przyjrzeć w tym rozdziale — Google Maps oraz usługi sieciowe Flickr.
Interfejs API Google Maps Interfejs API Google Maps umożliwia zewnętrznym programistom używanie zestawu funkcji serwisu Google Maps w ich własnych witrynach. Wszystko, co można zrobić w serwisie Google Maps, jest możliwe do wykonania za pośrednictwem interfejsu Google Maps API. Strona główna dokumentacji interfejsu API Google Maps znajduje się pod adresem http://www.google.com/ ´apis/maps/documentation/. Dokumentacja jest dosyć obszerna. W tym rozdziale przeanalizujemy podstawy działania API i skoncentrtujemy się na własnościach, które zostaną wykorzystane w aplikacji. Znajomość organizacji API ma kluczowe znaczenie dla wyszukiwania informacji i korzystania z interfejsu API Google Maps w przyszłych projektach. Interfejs API Google Maps wymaga klucza API. Aby go otrzymać, należy dokonać darmowej rejestracji pod adresem http://www.google.com/apis/maps/signup.html. Klucz jest wymagany w przypadku, gdy chcemy zastosować interfejs API na stronie. Przed wykonaniem jakichkolwiek operacji z tym interfejsem należy umieścić poniższy znacznik na początku znacznika head strony:
190
Rozdział 5. • Zdjęcia londyńskiego metra
Interfejs API bazuje na obiektach języka JavaScript. Centralnym obiektem jest mapa Google Maps wyświetlająca się na stronie. Wszystko, co wyświetla się na mapie Google Maps, włącznie z kontrolkami mapy, ikonami, liniami i białą ramką okna informacyjnego, to po prostu obiekty JavaScript dodane do mapy. Podczas analizy przykładów zamieszczonych w tym rozdziale stworzymy tę samą stronę, która została dołączona do archiwum z przykładami i znajduje się w pliku googleMapTest.php.
Tworzenie mapy Aby utworzyć mapę, potrzebny jest egzemplarz klasy GMap2. Jedynym obowiązkowym parametrem konstruktora klasy GMap2 jest kontener HTML umieszczany na mapie. Zazwyczaj jest to pusty znacznik div. Mapa Google Map wyświetla się w przestrzeni zajmowanej przez ten znacznik, dlatego wspomniany kontener ma bardzo duże znaczenie. Do pozycjonowania mapy na stronie można wykorzystać CSS. Rozmiar kontenera determinuje rozmiar mapy. Przeanalizujmy prosty przykład:
Interfejs Google Maps - podstawy
Ten prosty kod utworzy mapę Google Map. Do przechowywania mapy zadeklarowaliśmy zmienną globalną g_map. Po wyzwoleniu zdarzenia onload uruchamia się funkcja load. Wewnątrz funkcji load następuje uruchomienie funkcji JavaScript GBrowserIsCompatible, która sprawdza kompatybilność przeglądarki. Jeśli test zakończy się pomyślnie, można utworzyć mapę przy użyciu egzamplarza klasy GMap2. Kontener przekazujemy do konstruktora klasy GMap2 za pomocą funkcji JavaScript obsługi modelu DOM — getElementById. Ponieważ rozmiar elementu div wynosi 800×600 pikseli, mapa również będzie miała taki rozmiar.
191
PHP Web 2.0. Tworzenie aplikacji typu mashup
Gdybyśmy rzeczywiście uruchomili ten kod, zauważylibyśmy, że nie jest on zbyt użyteczny. Uzyskalibyśmy pustą, szarą mapę. Problem polega na tym, że mapa nie dysponuje informacjami, na czym ma się skoncentrować. Informację tę należy podać za pomocą metody setCenter() obiektu mapy. Metoda setCenter() może zostać wywołana w dowolnym czasie przez dowolne zdarzenie. Parametrem metody jest obiekt GLatLng.
Geokodowanie Niemal wszystkie operacje wykonywane na mapie Google bazują na współrzędnych określających szerokość i długość geograficzną. Problem polega na tym, że w codziennej komunikacji częściej używamy adresów niż współrzędnych geograficznych. Proces translacji adresu na współrzędne szerokości i długości geograficznej jest znany jako geokodowanie. Służy do tego obiekt GClientGeocoder. W celu utworzenia geokodera najpierw należy utworzyć egzemplarz obiektu GClientGeocoder. Obiekt ten udostępnia metodę o nazwie getLatLng(), która pobiera dwa parametry. Pierwszy jest ciągiem znaków zawierającym adres, który chcemy wyszukać. Drugi jest funkcją wywoływaną zwrotnie po tym, jak serwer zwróci wyniki. Serwery Google przekazują obiekt GLatLng do funkcji wywoływanej zwrotnie. Obiekt GLatLng zawiera informacje o współrzędnych geograficznych. Aby utworzyć obiekt GLatLng, trzeba przekazać dwa parametry — szerokość i długość geograficzną. Dostęp do tych właściwości można uzyskać za pomocą metod lat() i long() tego obiektu. Pewna niedogodność w posługiwaniu się metodą getLatLng() polega na tym, że nie zwraca ona obiektu GLatLng do kodu wywołującego. Ponieważ jednak taki obiekt jest przekazywany do funkcji wywoływanej zwrotnie, w celu wykorzystania wyników geokodowania trzeba utworzyć taką funkcję. Powróćmy do naszego kodu JavaScript — aby mógł on skoncentrować się na adresie, trzeba w nim wprowadzić niewielką modyfikację:
192
Rozdział 5. • Zdjęcia londyńskiego metra
W zmodyfikowanym skrypcie tworzymy obiekt GClientGeocoder w funkcji load. Mapę tworzymy tak, jak poprzednio. Następnie wywołujemy metodę getLatLng(), przekazując adres i funkcję wywołania zwrotnego — centerMapCallback. W metodzie centerMapCallback() przechwytujemy obiekt GLatLng przekazany w formie parametru i przekazujemy go do metody setCenter() obiektu mapy w celu właściwego centrowania. Drugi parametr, którego wartość to 14, oznacza poziom powiększenia. Kiedy interfejs API żąda poziomu powiększenia, można podać liczbę całkowitą o wartości od zera do siedemnastu. Im większa liczba, tym większe przybliżenie.
W naszej aplikacji nie będziemy korzystali z geokodowania, ale pomimo to warto zapoznać się z obiektem GClientGeocoder. W przykładzie będziemy dość często posługiwać się obiektem GLatLng. Obydwa obiekty pełnią bardzo ważną funkcję w interfejsie API Google Maps. Jak można się przekonać, w aplikacjach często trzeba używać obu tych obiektów.
193
PHP Web 2.0. Tworzenie aplikacji typu mashup
Znaczniki Obiekty GLatLng często są wykorzystywane jako parametry znaczników. Znaczniki są wskaźnikami Google służącymi do identyfikacji specyficznego miejsca na mapie. Każdy znacznik jest egzemplarzem klasy GMarker. W celu utworzenia prostego znacznika trzeba jedynie wykonać dwie czynności: utworzyć obiekt GMarker i dodać go do mapy. W naszym przykładzie możemy umieścić znacznik w adresie poprzez dodanie trzech linijek w funkcji wywoływanej zwrotnie: function centerMapCallback(returnedPoint){ var marker = new GMarker(returnedPoint); g_map.setCenter(returnedPoint, 14); g_map.addOverlay(marker); }
Pierwsza linijka tworzy obiekt GMarker i umieszcza go w zmiennej lokalnej o nazwie marker. Druga linijka ustawia powiększenie fragmentu mapy tak, jak poprzednio. Trzecia linijka dodaje znacznik do mapy Google.
Obiekt GMarker może pobrać drugi parametr — obiekt GMarkerOptions. Jedynym celem tego obiektu jest sprecyzowanie znacznika. Za jego pomocą można zdefiniować własne ikony znaczników lub zezwolić na ich przeciąganie. W tym celu wystarczy jedynie ustawić właściwości obiektu GMarkerOptions. Informacje o operacjach, jakie można wykonywać na znacznikach, znajdują się w dokumentacji obiektu GMarkerOptions dostępnej pod adresem http://www.google.com/apis/maps/documentation/reference. ´html#GMarkerOptions.
194
Rozdział 5. • Zdjęcia londyńskiego metra
Zdarzenia Jak można przeczytać w dokumencie Google Maps API Class References, z niektórymi obiektami są powiązane zdarzenia. Obiekty to elementy, które użytkownik widzi i z którymi może wykonywać operacje — na przykład sama mapa, linie i znaczniki. Dzięki nim można wywoływać funkcje JavaScript w odpowiedzi na działania użytkowników. Zdarzeniami zarządza przestrzeń nazw GEvent. Aby zarejestrować zdarzenie, należy je dodać do obiektu GEvent za pomocą metody addListener(). Metoda addListener() pobiera trzy parametry. Pierwszym argumentem jest obiekt, dla którego ma być aktywne zdarzenie. Drugim argumentem jest rodzaj zdarzenia (kliknięcie, przeciągnięcie itp.). Ostatni argument określa funkcję obsługi, która uruchamia się w odpowiedzi na wyzwolenie zdarzenia. Spróbujmy dodać zdarzenie do znacznika. Wystarczy dopisać kilka linii kodu do funkcji wywołania zwrotnego, aby po kliknięciu znacznika wyświetlało się okno informacyjne: function centerMapCallback(returnedPoint){ var marker = new GMarker(returnedPoint); g_map.setCenter(returnedPoint, 14); g_map.addOverlay(marker); GEvent.addListener(marker, "click", function() { alert("Kliknięto znacznik!"); } ); }
Ponieważ GEvent nie jest obiektem użytkownika, nie trzeba tworzyć jego egzemplarza. Egzemplarz tego obiektu tworzy się automatycznie po załadowaniu interfejsu API Google Maps. W odpowiedzi na wyzwolenie zdarzenia click znacznika następuje uruchomienie funkcji obsługi.
Obiekty InfoWindow Okna informacyjne JavaScript nie wyglądają zbyt okazale. Bardziej efektowne są białe okna podobne do tych, które wyświetlają się podczas korzystania z map Google. Przypominają one chmurki z komiksów. Wskazują określoną lokalizację na mapie i zawierają przydatne informacje na jej temat. W terminologii interfejsu API Google Maps są to obiekty InfoWindow. W interfejsie API są one reprezentowane przez klasę GInfoWindow. Dla każdej mapy Google istnieje jedno i tylko jedno okno InfoWindow. Wynikają z tego dwa fakty. Po pierwsze, kiedy wyświetla się i znika z ekranu okno InfoWindow, następuje jedynie przełączanie jego widoczności. Operacja ta jest realizowana za pośrdnictwem wbudowanych zdarzeń API (np. poprzez kliknięcie przycisku zamknięcia okna InfoWindow) lub programowo — poprzez wywołanie metod show() lub hide(). Po drugie, zdarzenia współdzielą i aktualizują to samo okno InfoWindow. Kiedy widzimy, że w oknie InfoWindow wyświetla się nowa zawartość, na przykład w przypadku przełączenia
195
PHP Web 2.0. Tworzenie aplikacji typu mashup
z jednego znacznika do innego, metody DOM języka JavaScript zmieniają zawartość okna Info ´Window. Do wykonania tych samych działań będziemy zmuszeni podczas wykorzystywania okien InfoWindow w naszej aplikacji. Spróbujmy wprowadzić dalsze modyfikacje w przykładowym skrypcie. Kiedy użytkownik kliknie znacznik, zamiast okna informacyjnego JavaScript pojawi się okno InfoWindow. Należy pamiętać, że już w momencie utworzenia egzemplarza mapy następuje jej powiązanie z oknem InfoWindow. Z tego względu nie ma potrzeby tworzenia obiektu GInfoWindow. Należy jedynie wydać polecenie, by okno wyświetliło się w miejscu, w którym sobie tego życzymy. Okno InfoWindow można ustawić w dowolnym punkcie poprzez przekazanie obiektu GLatLng do metody reset() obiektu GInfoWindow. Następnie wystarczy wyświetlić je za pomocą metody show() obiektu. Istnieje jednak szybszy sposób wykonania tej operacji. Wyświetlanie okien Info ´Window objaśniających znaczniki to jedna z najpopularniejszych operacji wykonywanych na mapach Google. Jest tak częsta, że zespół programistów interfejsu API Google Maps stworzył metody obiektu GMarker, które ja wykonują. Zaleta tego sposobu polega na tym, że metody dotyczą znacznika, zatem okno InfoWindow automatycznie wyświetla się nad znacznikiem. W związku z tym nie ma potrzeby śledzenia współrzędnych geograficznych znacznika. Aby wyświetlić okno InfoWindow zamiast okna informacyjnego JavaScript, trzeba zmodyfikować procedurę obsługi zdarzenia: GEvent.addListener(marker, "click", function() { marker.openInfoWindowHtml("Mój znacznik!"); } );
Rozmiar okna InfoWindow jest określony przez szerokość i wysokość największego kontenera HTML znajdującego się wewnątrz. W związku z tym istnieje możliwość zarządzania rozmiarem poprzez dodanie właściwości CSS height i width do zawierającego je kontenera. Na przykład możemy stworzyć okno InfoWindow o rozmiarach w przybliżeniu 320×250 pikseli poprzez umieszczenie znacznika div o rozmiarach 320×250 pikseli:
196
Rozdział 5. • Zdjęcia londyńskiego metra
.openInfoWindowHtml(" Jakiś HTML");
W wersji 2.5 i nowszych interfejsu API wprowadzono obsługę zakładek w oknach InfoWindow. Aby okno InfoWindow wyposażyć w zakładki, należy utworzyć obiekt GInfoWindowTab dla każdej zakładki. Konstruktor tej klasy pobiera dwa parametry. Pierwszym z nich jest etykieta zakładki, drugi to jej zawartość. Wszystkie obiekty GInfoWindowTab należy umieścić w tablicy JavaScript. Klasa GMarker obsługuje również metodę o nazwie openInfoWindowTabs(). Metoda ta pobiera tablicę obiektów GInfoWindowTab. Jej wywołanie spowoduje otwarcie okna InfoWindow, ale będzie to okno z zakładkami, których zawartość stanowią obiekty GInfoWindowTab. Aby wykorzystać zakładki w oknie InfoWindow, należy nieco zodyfikować funkcję wywoływaną zwrotnie: function centerMapCallback(returnedPoint){ var tabsArray = new Array(); tabsArray[0] = new GInfoWindowTab("Jeden", "
Zawartość zakładki nr 1
"); tabsArray[1] = new GInfoWindowTab("Dwa", "
Zawartość zakładki nr 2
"); var marker = new GMarker(returnedPoint); g_map.setCenter(returnedPoint, 14); g_map.addOverlay(marker); GEvent.addListener(marker, "click", function() { marker.openInfoWindowTabs(tabsArray); } ); }
Na tym zakończymy omawianie podstawowych własności interfejsu API Google Maps. Dostępnych jest sporo dodatkowych własności. Do najbardziej interesujących należą:
197
PHP Web 2.0. Tworzenie aplikacji typu mashup
Q możliwość rysowania linii na mapie, podobnie jak w przypadku funkcji pokazywania
drogi w serwisie Google Maps; Q interfejs REST dla usługi zwracającej XML, dzięki któremu można skorzystać z bazy danych Google Maps w aplikacjach działających po stronie serwera; Q menedżer znaczników pozwalający na obsługę dużej liczby znaczników o różnych
poziomach powiększenia; Q przesłanianie kart map za pomocą obiektu GMapTiles.
Osoby, które intensywnie wykorzystują interfejs API Google Maps w aplikacjach, powinny również pamiętać o obiektach opcji. Obiekty te oferują dodatkowe możliwości wykorzystania interfejsu API w aplikacjach. Na przykład obiekt GMarkerOptions pozwala na stworzenie niestandardowych znaczników. Nawet bez zaawansowanych własności interfejs API Google Maps daje olbrzymie możliwości. Z pewnością poznaliśmy więcej własności, niż trzeba do utworzenia naszej aplikacji.
Interfejs API Flickr Services Serwis Flickr, który koncentruje się na współdzieleniu zdjęć, jest jedną ze starszych witryn społecznościowych. Serwer ten jest również jednym z pierwszych, który udostępnił interfejs API zewnętrznym programistom. Dzięki temu zdobył dużą bazę użytkowników, a jego interfejs API jest bardzo bogaty. Flickr Services to prawdopodobnie najbardziej uniwersalny internetowy interfejs API, z jakim mieliśmy styczność. Jego strona macierzysta znajduje się pod adesem http://www.flickr.com/services/api/. Do skorzystania z interfejsu potrzebny jest darmowy klucz programisty. Ponieważ firma Flickr jest satelitą firmy Yahoo!, potrzebne będzie również darmowe konto Yahoo!. Na stronie http://www.flickr.com/services/api/keys/ wyświetlają się pytania o obydwa konta. Na tej stronie można również dokonać rejestracji w celu uzyskania obu kont. Podobnie jak w przypadku innych interfejsów API obsługujących serwisy społecznościowe, interfejs API serwisu Flickr Services koncentruje się nie tylko na głównym temacie, ale także udostępnia metody pozwalające na tworzenie zapytań dotyczących społeczności. Między innymi można pobrać wpisy na osobistym blogu oraz ulubione zdjęcia określonego użytkownika, jeśli udzielił na to zgody w ustawieniach prywatności. Istnieje również interfejs API pozwalający na wykonywanie operacji z grupami serwisu Flickr, dzięki któremu można wyszukiwać zdjęcia i informacje od osób o podobnych zainteresowaniach. Oczywiście dwie największe grupy metod dotyczą zdjęć i ich zestawów. Użytkownik może uporządkować swoje fotografie w zestawy. Serwis Flickr, podobnie jak Last.fm i YouTube, w dużym stopniu bazuje na znacznikach użytkowników. Operacje wyszukiwania zdjęć zależą od znaczników wprowadzonych przez użytkowników.
198
Rozdział 5. • Zdjęcia londyńskiego metra
Największe wrażenie w witrynie Flickr Services robi liczba formatów żądań i odpowiedzi. Dla żądań można wybrać dowolny spośród najbardziej popularnych formatów — REST, SOAP i XML-RPC. Jeśli chodzi o odpowiedzi, można wybrać własny schemat XML serwisu Flickr, a także formaty SOAP, XML-RPC, JSON lub nawet serializowane obiekty PHP. Niezależnie od wybranego formatu, w usłudze Flickr operacje są wykonywane w sposób spójny. Wszystkie żądania pobierają te same parametry i zwracają te same dane. Dla każdego z nich trzeba jedynie zastosować inny sposób parsowania.
Wykonywanie operacji wyszukiwania Ponieważ usługi Flickr są tak spójne, najlepszym sposobem zrozumienia ich działania jest przeanalizowanie przykładu. W aplikacji skoncentrujemy się na grupie metod dotyczących fotografii. W szczególności potrzebna jest metoda wyszukiwania zdjęć na podstawie znaczników wprowadzanych przez użytkowników. Spróbujmy uruchomić wyszukiwanie w postaci podobnej do tej, którą wykorzystamy w aplikacji. Postaramy się, aby przykład był maksymalnie prosty. Żądanie usługi wyślemy z wykorzystaniem formatu REST. Nasza aplikacja internetowa jest sterowana za pomocą PHP, zatem interesujące byłoby zastosowanie serializowanej odpowiedzi PHP. Ponieważ jednak wiele operacji będzie wykonywanych za pomocą JavaScript, skorzystamy z formatu JSON. Prosta odpowiedź XML z wywołania REST również jest do zaakceptowania, ale byłoby lepiej, gdyby udało się uniknąć parsowania modelu DOM. Nazwy metod są opisowe i dają wskazówki na temat ich przeznaczenia. Jeśli zajrzymy do dokumentacji metody flickr.photos.search pod adresem http://www.flickr.com/services/api/flickr ´photos.search.html, przekonamy się, że metoda jest dokładnie tym, czego potrzebujemy do wyszukiwania fotografii. Adres URL dla wszystkich żądań REST serwisu Flickr to http://api.flickr.com/services/rest/. Po adresie URL należy podać parametry metody w formacie żądania GET. Dwa parametry są obowiązkowe dla wszystkich żądań REST, a mianowicie method oraz api_key. Wartość parametru method jest nazwą metody, którą chcemy wywołać. Wartość parametru api_key to klucz API serwisu Flickr. Kompletny adres URL do wywołania metody flickr.photos.search będzie miał następującą postać: http://api.flickr.com/services/rest/?method=flickr.photos. search&api_key=TWÓJ_KLUCZ_API_SERWISU_FLICKR
Na stronie z dokumentacją metod znajdują się wszystkie parametry, które może pobrać metoda. Lista dostępnych parametrów metody flickr.photos.search jest dość obszerna. Dzięki temu zyskujemy duże możliwości dostrajania operacji wyszukiwania. Zgodnie z dokumentacją jedynym parametrem obowiązkowym jest api_key. Jest to jednak trochę mylące, ponieważ trzeba również określić kryteria wyszukiwania. Aby przeszukiwać znaczniki, należy skorzystać z parametru tag i wpisać kryteria rozdzielone przecinkami. Dowolne wyszukiwanie tekstowe umożliwia parametr text, w którym trzeba podać dowolny ciąg tekstowy. Chociaż obydwa są parametrami 199
PHP Web 2.0. Tworzenie aplikacji typu mashup
opcjonalnymi, trzeba uwzględnić jeden lub drugi. W przeciwnym razie serwis Flickr zwróci komunikat z informacją, że puste wyszukiwanie nie jest dozwolone. Tak czy inaczej, w przypadku zastosowania formatu REST należy wykorzystać adres URL do kodowania kryteriów. http://api.flickr.com/services/rest/?method=flickr.photos. search&api_key=TWÓJ_KLUCZ_API_SERWISU_FLICKR&text=fender%20stratocaster
W przypadku zastosowania formatu XML-RPC lub SOAP należy wykorzystać takie same parametry, jak te wymienione w dokumentacji i sformatować parametry oraz wartości zgodnie z wymogami formatu. Dla formatu SOAP punkt docelowy usługi (ang. service endpoint) znajduje się pod adresem http://api.flickr.com/services/soap/, a dla formatu XML-RPC — pod adresem http://api. ´flickr.com/services/xmlrpc/.
Interpretacja wyników zwróconych przez usługę Jeśli po dodaniu właściwego klucza API spróbujemy wywołać powyższy adres URL w przeglądarce, operacja wyszukiwania uruchomi się i otrzymamy dynamiczną odpowiedź z serwera.
...
Dane są w standardowym formacie zwracanym przez serwis Flickr zawsze wtedy, gdy żądanie dotyczy fotografii. Domyślne wywołanie zwraca 100 wyników na stronę. Element photos grupuje poszczególne elementy photo na stronie. Każdy element photo reprezentuje fotografię zwróconą w wynikach wyszukiwania. Aby zmienić aktywną stronę, należy przekazać do wywołania parametr page. Można również zmodyfikować liczbę fotografii zwracanych na stronie; służy do tego parametr per_page. Każdy element photo w gruncie rzeczy jest kolekcją atrybutów dotyczących fotografii. Te atrybuty są bardzo ważne. Trzeba je znać, aby móc załadować zdjęcie. Ostatnie trzy dane typu Boolean przyjmują wartość 1 lub 0. Wymagają również przeprowadzenia uwierzytelniania użytkownika wywołującego usługę z wykorzystaniem metod uwierzytelniania interfejsu API.
200
Rozdział 5. • Zdjęcia londyńskiego metra
Atrybut
Opis
Id
Unikatowy identyfikator fotografii.
Owner
Identyfikator osoby będącej właścicielem fotografii.
Secret
Pomocniczy identyfikator wykorzystywany do identyfikacji fotografii.
Server
Serwer, na którym jest zapisana fotografia.
Farm
Farma serwerów, na której jest zapisana fotografia.
Title
Tytuł fotografii.
isPublic
Wartość typu Boolean wskazująca, czy właściciel publicznie udostępnia fotografię.
isFriend
Wartość typu Boolean wskazująca, czy właściciel znajduje się na liście przyjaciół.
isFamily
Wartość typu Boolean wskazująca, czy właściciel znajduje się na liście członków rodziny.
Zwrócone dane są tym, czego potrzebujemy, ale mają nieprawidłowy format. Chcemy, aby wyniki zostały zwrócone w formacie JSON. W tym celu trzeba przekazać parametr format do wywołania usługi. W naszym przypadku parametr ten ma wartość json. http://api.flickr.com/services/rest/?method=flickr.photos.search&api_ key=TWÓJ_KLUCZ_API_SERWISU_FLICKR&text=fender%20stratocaster&format=json
Po dodaniu tego parametru uzyskamy z serwera następującą odpowiedź: jsonFlickrApi({ "photos": { "page":1, "pages":20, "perpage":100, "total":"1904", "photo":[ {"id":"412962278", "owner":"43203076@N00", "secret":"63e7e2e1f0", "server":"183", "farm":1, "title":"Doin\u2019 Studio Time", "ispublic":1, "isfriend":0, "isfamily":0}, {"id":"412463850", "owner":"63895350@N00", "secret":"26b97edbb5", "server":"172", "farm":1, "title": "Norby with his Fender Stratocaster", "ispublic":1, "isfriend":0, "isfamily":0}, {"id":"411598583", "owner":"75859527@N00", "secret":"657eb806c8", "server":"172", "farm":1, "title":"Hocus Pocus", "ispublic":1, "isfriend":0, "isfamily":0}, ... ] } })
201
PHP Web 2.0. Tworzenie aplikacji typu mashup
Strona z dokumentacją każdej z metod zawiera opis zwróconego formatu XML wywołania. Na tej podstawie można dokonać świadomego wyboru odpowiednika formatu JSON. Ogólnie rzecz biorąc, atrybuty elementów w dokumencie XML są właściwościami obiektu w dokumencie JSON. Zagnieżdżone elementy są poddawane translacji na obiekty zagnieżdżone. Przedmiot wyszukiwania, niezależnie od tego, czy są to wpisy na blogu, użytkownicy w grupie, czy — tak jak w naszym przypadku — zdjęcia, jest zwracany w postaci tablic JSON. W przypadku problemów z wyznaczeniem dokładnej translacji metody, zawsze można ręcznie skierować żądanie do przeglądarki, tak jak zrobiliśmy to w naszym przykładzie. Wyniki w formacie JSON są umieszczone w wywołaniu do interfejsu jsonFlickrApi. Domyślnie interfejs API zakłada, że chcemy przekazać wyniki JSON do wywoływanej zwrotnie funkcji JavaScript. Jeśli w aplikacji jest funkcja jsonFlickrApi, interpreter JavaScript przekazuje do niej obiekt JSON w momencie odebrania odpowiedzi. Następnie automatycznie uruchamia funkcję. Może to być kontroler w kodzie JavaScript, który sprawdza wartość zwracaną przez usługę. Trzeba jednak utworzyć w kodzie funkcję o nazwie jsonFlickrApi i skonfigurować ją tak, by wykonywała właściwe działania na zwróconym kodzie JSON. Aby zrezygnować z tej możliwości, należy wyłączyć automatyczne wywołanie zwrotne poprzez przesłanie wartości true (1) jako parametru nojsoncallback w wywołaniu. Dzięki temu uzyskamy ten sam ciąg tekstowy bez funkcji jsonFlickrApi(). http://api.flickr.com/services/rest/?method=flickr.photos.search&api_ key=TWÓJ_KLUCZ_API_SERWISU_FLICKR&text=fender%20stratocaster&format=json&nojson callback=1
Pobieranie fotografii lub strony z fotografiami Kiedy otrzymaliśmy już wyniki, możemy skorzystać z danych w celu pobrania fotografii z serwisu Flickr. Adresy URL zdjęć w serwisie Flickr mają następujący format: http://farm{ID-FARMY}.static.flickr.com/{ID-SERWERA}/{ID}_{HASŁO}{ROZMIAR}.jpg
Z wyjątkiem rozmiaru wszystkie inne zmienne można pobrać bezpośrednio z odpowiedzi do wywołania usługi sieciowej flickr.photos.search. ID-FARMY oznacza wartość atrybutu farm. ID-SERWERA oznacza wartość atrybutu server. ID oznacza wartość atrybutu id. HASŁO odpowiada atrybutowi secret w odpowiedzi XML. ROZMIAR określa
rozmiar pobieranej fotografii. Atrybut ma postać znaku podkreślenia, za którym znajduje się jeden znak. Może to być jedna z poniższych liter: Przyrostek
Znaczenie
Maksymalna liczba pikseli na jeden bok
_o
Pierwotny rozmiar
*
_b
Duży
1024
None
Średni
500
202
Rozdział 5. • Zdjęcia londyńskiego metra
Przyrostek
Znaczenie
Maksymalna liczba pikseli na jeden bok
_m
Mały
240
_t
Miniaturka
100
_s
Mały kwadrat
75 px×75 px
Jedna z pierwszych fotografii została zwrócona w formacie XML w następującej postaci:
Możemy skorzystać z tych informacji, aby utworzyć adres URL zmniejszonej wersji fotografii: http://farm1.static.flickr.com/172/411598583_657eb806c8_m.jpg
Zdjęcia w oryginalnym rozmiarze pobiera się inaczej. Trzeba dla nich podać osobny kod hasła za pomocą atrybutu o nazwie originalsecret. Trzeba również uwzględnić rozszerzenie typu pliku. Można je odczytać z innego atrybutu i nazwy original_format. Aby uzyskać te atrybuty, należy o nie wystąpić w pierwotnym żądaniu — za pomocą parametru extras. Wartością tego parametru jest rozdzielona przecinkami lista atrybutów, które nie zostały uwzględnione w domyślnej odpowiedzi. http://api.flickr.com/services/rest/?method=flickr.photos. search&api_key=TWÓJ_KLUCZ_API_SERWISU_FLICKR&text=fender%20stratocaster&form at=json&nojsoncallback=1&extras=originalsecret,original_format
Informacje o dostępnych parametrach dodatkowych można znaleźć w dokumentacji metody. Adres URL do strony WWW z fotografiami konstruuje się w podobny sposób. Adres URL ma następujący format: http://www.flickr.com/photos/{UID_UŻYTKOWNIKA}/{ID-FOTOGRAFII}
W dokumentacji opisano szereg innych elementów, do których można konstruować łącza. Na przykład można tworzyć adresy URL do zbioru zdjęć lub profilu użytkownika.
Tworzenie aplikacji typu mashup Przygotowując się do stworzenia aplikacji typu mashup, przeanalizowaliśmy wiele technologii. Niektóre z nich to absolutne nowości. Trzeba jednak z nich skorzystać, aby spełnić wymagania stosunkowo nowych specyfikacji. Nie jest wielkim zaskoczeniem, że źródłami danych nie zawsze są internetowe interfejsy API. Zachowanie elastyczności i poszukiwanie nowych technologii ma duże znaczenie. W tym momencie dysponujemy wiedzą potrzebną do rozpoczęcia tworzenia aplikacji.
203
PHP Web 2.0. Tworzenie aplikacji typu mashup
Dobrym punktem startowym jest baza danych. Przypomnijmy sobie z diagramu operacji, że odwiedzający przez cały czas bezpośrednio lub niebezpośrednio komunikuje się z kilkoma różnymi komponentami aplikacji. Wiele spośród tych komponentów bazuje na mapie Google Maps, którą trzeba stworzyć wcześniej. Mapa wykorzystuje bazę danych jako źródło informacji o lokalizacji znaczników.
Tworzenie bazy danych i wypełnianie jej danymi Do stworzenia strony agregującej potrzebne są trzy rodzaje informacji: o stacjach metra, o liniach metra i o przyporządkowaniu stacji do poszczególnych linii. Trzeba również pamiętać, że określona stacja może jednocześnie należeć do więcej niż jednej linii. Ponieważ źródłem danych aplikacji jest dokument RDF z informacjami o stacjach metra, przeanalizujmy jego zawartość i sprawdźmy, jakimi danymi dysponujemy.
Analiza pliku Pierwsza część pliku zawiera dane o stacjach. Typowy zapis dotyczący stacji ma następującą postać:
179613 Tube Acton Town Station Acton Town Station
-0.280009
519478
Serwis zwraca po jednym elemencie photo dla każdego znalezionego zdjęcia. W formacie JSON są one interpretowane jako tablica. Z tego względu element photo należy interpretować jako tablicę obiektów photo. Ustawiliśmy zmienną totalPhotos, której zadaniem jest śledzenie całkowitej liczby zwracanych zdjęć. Ostatnia zmienna lokalna, którą ustawiamy — l_flickrString, służy do zapamiętania lokalnych wyników z serwisu Flickr. Zostanie ona dołączona do zmiennej globalnej g_flickrString: g_flickrString = "" + g_stationName + "
"
Kod HTML, który będzie tworzył zawartość okna InfoWindow, jest zapisany w zmiennej g_flickr ´String. W tym przypadku rozpoczynamy ciąg od powtórzenia nazwy stacji, która wcześniej, kiedy użytkownik po raz pierwszy kliknął znacznik, była zapisana w zmiennej globalnej. if (totalPhotos > 0) { for (x = 0; x < totalPhotos; x++) { l_flickrString = " " + "" + ""; l_flickrString = l_flickrString.replace(/PHOTO_OWNER/g, photo[x].owner); l_flickrString = l_flickrString.replace(/PHOTO_ID/g, photo[x].id); l_flickrString = l_flickrString.replace(/PHOTO_FARM/g, photo[x].farm); l_flickrString = l_flickrString.replace(/PHOTO_SERVER/g, photo[x].server); l_flickrString = l_flickrString.replace(/PHOTO_SECRET/g,
226
Rozdział 5. • Zdjęcia londyńskiego metra
photo[x].secret); g_flickrString = g_flickrString + l_flickrString; }
Powyższy fragment kodu umożliwia wykorzystanie danych z serwisu Flickr. Klauzula if sprawdza, czy serwis zwrócił jakieś wyniki. Jeśli tak, przetwarzamy obiekty photo w pętli for. Liczba iteracji jest ograniczona wartością zmiennej totalPhotos. W każdej iteracji pętli tworzy się ciąg znaków zawierający adres URL odwołujący się do zwróconej fotografii oraz znacznik kotwicy odwołujący się do strony ze zdjęciami. Ten ciąg znaków jest zapisany w zmiennej l_flickrString. W celu poprawy czytelności w ciągu wykorzystaliśmy kilka znaków-wypełniaczy przeznaczonych dla wartości z serwisu Flickr. Następnie skorzystamy z metody replace() w celu zastąpienia tych symboli właściwymi wartościami z tablicy photo. Na koniec dołączamy zmienną l_flickrString do zmiennej globalnej g_flickrString. } else { g_flickrString = g_flickrString + "
Nie znaleziono zdjęć dla tej stacji.
"; } } }
Następnie zamykamy blok if-else. Instrukcja else informuje o braku wyników. Należy zaktualizować zmienną g_flickrString, wpisując komunikat informujący użytkownika, że operacja wyszukiwania nie zwróciła żadnych wyników. Jedynym zadaniem tej funkcji jest utworzenie ciągu HTML, który wyświetli się w oknie InfoWindow. Przyjrzyjmy się operacji aktualizowania okna InfoWindow za pomocą tego ciągu. Główna operacja wypełniania danymi zachodzi w funkcji updateInfoBox(): function updateInfoBox() { if (g_flickrString == undefined) { var timeout = window.setTimeout("updateInfoBox()", 3000); } else { g_map.getInfoWindow().getContentContainers()[0].innerHTML = "" + g_flickrString + ""; // Porządkowanie g_flickrString = null; g_stationName = null; } }
Jest to ostatnia funkcja wywoływana przez procedurę nasłuchiwania zdarzeń. GEvent.addListener(marker, "click", function() { createXMLHttpRequest(); g_stationName = stationName; retrieveFlickrPhotos(stationName); marker.openInfoWindowHtml("" + stationName + "
"); updateInfoBox(); });
Należy jednak pamiętać, że wywołanie usługi następuje w innym miejscu. W czasie, kiedy są pobierane te informacje, okno już tam się znajduje. Jest to kolejna sytuacja wyścigu. Jeśli wywołamy zmienną g_flickrString, zanim zostanie ustawiona, przekonamy się, że jest niezdefiniowana. Jeśli zmienna g_flickrString jest pusta, należy skorzystać z funkcji JavaScript setTimeout(), aby uruchomiła się po upływie trzech sekund. Opóźnienie w uruchomieniu jest częstą taktyką wykorzystywaną w implementacjach AJAX. Jeśli znaleziono wyniki, uzyskujemy węzeł DOM okna InfoWindow. Operację tę wykonano za pomocą narzędzia DOM Inspector w przeglądarce Firefox. Po tej czynności możemy dołączyć zmienną g_flickrString do węzła. Na koniec czyścimy zmienne globalne poprzez ustawienie ich na wartość null. W końcu aplikacja agregująca jest kompletna. Możemy teraz ją przetestować. Ładujemy stronę w przeglądarce i zaznaczamy linię za pomocą rozwijanego menu. Wyświetlą się znaczniki odpowiadające stacjom linii. Klikamy jeden ze znaczników. Zobacz na rysunek na następnej stronie.
228
Rozdział 5. • Zdjęcia londyńskiego metra
Wyświetli się okno InfoWindow. Dzięki wykorzystaniu technologii AJAX aplikacja wyszukuje zdjęcia stacji w serwisie Flickr. Kiedy znajdzie fotografie, dodaje pierwsze cztery zdjęcia do okna InfoWindow.
229
PHP Web 2.0. Tworzenie aplikacji typu mashup
Podsumowanie W tym rozdziale przeanalizowaliśmy wiele technologii. Dowiedzieliśmy się, w jaki sposób czyta się dokumenty RDF i jak można wydobywać z nich dane z wykorzystaniem języka SPARQL oraz biblioteki RAP. Te standardy są stosunkowo nowe. Biorąc jednak pod uwagę dążenie do udostępniania jak największej ilości informacji przez RSS, są one skazane na rozwój. Podczas tworzenia warstwy frontend aplikacji korzystaliśmy z nowych technologii umożliwiających komunikację serwera z urządzeniem, w tym z technologii AJAX. Największym problemem w aplikacjach AJAX są sytuacje wyścigu. W tym rozdziale zaprezentowaliśmy kilka technik radzenia sobie z nimi.
230
Skorowidz A adres URL, 69 agregacja danych, 21 AJAX, 47, 161, 180, 220 debugowanie aplikacji, 186 odpowiedzi, 225 Alkemis, 22 Amazon, 23 Amazon E-Commerce Service, 67 adres usługi, 69 informacje o kliencie, 69 informacje o produktach, 68 informacje o restauracjach, 69 informacje o sprzedawcach, 69 koszyk na zakupy, 68, 79 obsługa odpowiedzi XML, 75 obsługa XSLT, 68 odpowiedzi wyszukiwania, 75 operacje, 68 wyszukiwanie produktów, 68, 71 żądania REST, 69 Amazon Web Services, 67 ECS, 68 interfejs API, 67 Amazon XML, 72 Apache, 21 API, 17, 21 API Audioscrobbler Web Services, 140 API Flickr Services, 198 flickr.photos.search, 199 interpretacja wyników, 200
JSON, 202 jsonFlickrApi, 202 operacja wyszukiwania, 199 pobieranie fotografii lub strony z fotografiami, 202 REST, 199 żądania REST, 199 API Google Maps, 161, 190 geokodowanie, 192 GEvent, 195 GInfoWindow, 195 GLatLng, 192, 194 InfoWindow, 195, 196 okna informacyjne, 195 tworzenie mapy, 191 zakładki, 197 zdarzenia, 195 znaczniki, 194 API YouTube, 137 aplikacje AJAX, 185 aplikacje mashup, 17, 150, 203 architektura, 150 korzystanie z aplikacji, 155 strona główna, 151 strona nawigacyjna, 152 strona z zawartością, 153 tworzenie, 71 wyszukiwanie produktów, 71 architektura aplikacji typu mashup, 150 array, 28, 36 arrayType, 94 ASC, 175
PHP Web 2.0. Tworzenie aplikacji typu mashup
ASIN, 74 Asynchroniczny JavaScript i XML, 47 Asynchronous JavaScript and XML, 161, 180 ataki cross-site scrpting, 163 Atom, 135 Audioscrobbler, 140
B base64, 28, 34 baza danych, 204 binding, 95, 97 body, 98 boolean, 27
C centerMapCallback(), 193 complexContent, 94 complexType, 92, 93 copy(), 224 createGetRequest, 51 createPostRequest, 50 createXMLHttpRequest(), 183 cross-site scrpting, 163 czas, 27, 34
D dane, 19 dane binarne zakodowane w base64, 28 data, 27, 34 dateTime.iso8601, 27 DbModel, 177 debugowanie aplikacji AJAX, 186 definiowanie funkcji zwrotnych, 58 definitions, 88 DELETE, 47 DESC, 175 DISTINCT, 175 dokumenty WSDL, 87 binding, 88, 95, 97 complexContent, 94 complexType, 92, 93 definicje przestrzeni nazw, 92 definitions, 88 document, 95 Google SOAP Search, 97 jednokierunkowa odpowiedź, 96
232
message, 88, 94, 98 metadane transakcji, 98 MSN Search, 91 operacje, 96 operation, 96 portType, 88, 96 powiadomienie, 97 powiązanie document, 95 powiązanie RPC, 95 prośba-odpowiedź, 97 sekcje, 88 service, 99 struktura, 87 tablice, 93 types, 88, 89 typy danych, 89 typy proste, 89, 92 typy złożone, 92 unie, 91 use, 98 żądanie-odpowiedź, 96 dokumenty XML, 75 DOM, 47, 55, 181 double, 27, 33
E EAN, 66 ECS, 67, 68 enumeration, 90 Envelope, 100 eval(), 190 EXAMINE, 167
F faultCode, 30 faultString, 30 File_XSPF, 141, 142, 143, 152 instalacja, 142 lista piosenek Last.fm, 143 pobieranie informacji z listy, 143 XSPF, 143 Flickr, 19, 160, 161, 162, 198, 220 flickr.photos.search, 199 float, 27 fractionDigits, 90 FROM, 178 fsockopen, 39
Skorowidz
function, 187 funkcje zwrotne, 55, 58 fwrite, 39
numery EAN, 66 processXMLRPCResponse, 66 wyszukiwanie produktów, 71 XML-RPC, 65
G Gametripping.com, 22 geokodowanie, 192 GET, 47, 48 getDefaultModel(), 178 getElementById(), 185 GEvent, 195, 218 GInfoWindow, 195 GLatLng, 161, 192, 194, 217 GMarker, 161 gniazda, 38 inicjowanie żądań REST, 49 Google, 19 Google Maps, 161, 162 GLatLng, 161 GMarker, 161 XMLHttpRequest, 180 Google WSDL, 95 grafiki „ładowania” strony, 225 GUnload(), 218
H HEAD, 47 Header, 100 Housingmaps.com, 22 HTTPWatch, 186 hybrydowe aplikacje internetowe, 17
I informacje o stacjach metra, 161 InfoWindow, 195, 196, 228 integer, 27 integracja serwisów Google Maps i Flickr, 162 interfejs API, 21 Amazon, 67 internetowa baza danych UPC, 65 Internet UPC Database, 43, 65 interfejs API, 65 lookupEAN, 65 lookupUPC, 65
J JavaScript, 163 obiekty, 187 JavaScript Object Notation, 162, 186 jednoparametrowe żądania XML-RPC, 32 język SPARQL, 167 UML, 163 WSDL, 86 XML, 24 JSON, 162, 186 Array, 188 Boolean, 188 Null, 188 Number, 188 Object, 188 serializacja odpowiedzi, 189 String, 188 struktura, 187 typy danych, 188 właściwości, 188 jsonFlickrApi, 202
K Keggy, 22 kierowanie żądania XMLHttpRequest, 222 klasa parsera SAX, 60 klasa parsująca REST, 51 kolejność operacji w aplikacji, 163 komunikacja przez sieć, 24 komunikaty SOAP, 99 Body, 101 Envelope, 100 Fault, 102 Header, 100 nagłówek, 100 powiązanie dokumentu, 101 powiązanie RPC, 101 zawartość, 101 koszyk na zakupy Amazon, 79
233
PHP Web 2.0. Tworzenie aplikacji typu mashup
L Last.fm, 126, 139 Audioscrobbler, 140 dane albumu, 140 dane forum, 140 dane grup, 140 dane oznaczenia, 140 dane profilu użytkownika, 140 dane wykonawcy, 140 informacje o ścieżce, 140 kanały informacyjne, 140 LiveJournal, 140 length, 90 liczby, 27 LIMIT, 174 LiveJournal, 140 lookupEAN, 65, 73 lookupUPC, 65, 73
Ł łańcuchy tekstowe, 26
M mapy, 191 mashup, 17, 19 maxExclusive, 90 maxInclusive, 90 maxLength, 90 MemModel, 177 message, 94 metadane transakcji SOAP, 98 methodCall, 25 methodName, 25 methodResponse, 30 Microsoft Live Search, 86, 113 Results, 115 SearchRequest, 114 wyszukiwanie, 113 minExclusive, 90 minInclusive, 90 minLength, 90 MySQL, 205
234
N nagłówek wywołania żądania, 36 nagłówki SOAP, 98 nodeValue, 185 notacja JSON, 162, 186 numery EAN, 66
O obiekt, 164, 169 obiektowy model dokumentu, 55 obiekty JavaScript, 187 obsługa błędów SOAP, 110 obsługa odpowiedzi SOAP, 110 obsługa XML-RPC, 44 PHP, 31 odpowiedzi AJAX, 225 REST, 55 SOAP, 110 wyszukiwanie ECS, 75 XML-RPC, 29, 40 OFFSET, 174 okna informacyjne, 195 onreadystatechange, 183 Open Source Geospatial Foundation, 162 open(), 183 openInfoWindowHtml(), 218 operation, 96 ORDER BY, 174
P params, 30 parseFlickrSearch(), 223 parser SAX, 56 XML, 55 XML-RPC, 42 parsowanie odpowiedzi AJAX, 225 odpowiedzi XML, 47 RSS, 148 part, 98 pattern, 90 PEAR, 44, 142
Skorowidz
PHP, 21 obsługa XML-RPC, 31 REST, 48 SOAP, 103 SoapClient, 103 planowanie, 160 pliki RSS, 129 podmiot, 164, 169 Popurls, 22 portType, 96 POST, 25, 47, 48 predykat, 164, 169 PREFIX, 168, 171, 207 protokół SOAP, 86 przekazywanie tablic w żądaniach XML-RPC, 36 przetwarzanie odpowiedzi REST, 55 odpowiedzi XML-RPC, 40 SAX, 60 PUT, 47
R race conditions, 223 RAP, 177, 209 DbModel, 177 MemModel, 177 modele, 178 zapytania SPARQL, 178 RDF, 131, 160, 162, 164 obiekt, 164 obiekty programowania, 165 podmiot, 164 predykat, 164 przestrzeń nazw, 165 resource, 166 trójki, 164 type, 165 URI, 165 RDF API for PHP, 177 RDFAPI_INCLUDE_DIR, 178 Remote Procedure Call, 24 Representational State Transfer, 46 Resource Description Format, 160 Resource Description Framework, 164 responseText, 185, 189, 226 REST, 21, 23, 46, 163 funkcje żądań GET i POST, 50 GET, 48
gniazda, 49 inicjowanie żądań, 49 klasa parsująca, 51 odpowiedzi, 55 PHP, 48 POST, 48 przetwarzanie odpowiedzi, 55 testowanie klasy parsującej, 53 wykonywanie żądania, 48 żądania, 46, 48 RESTParser, 51, 118 Results, 115 RPC, 24, 95 RSS, 126, 129, 152 author, 135 category, 134, 135 channel, 131, 134, 135 cloud, 134 comments, 135 copyright, 134 description, 131, 132, 134, 135 docs, 134 enclosure, 135 generator, 134 guid, 135 image, 131, 134 item, 132 items, 131 kanały, 130 language, 134 lastBuildDate, 134 link, 131, 132, 134, 135 logo kanału, 131 managingEditor, 134 others, 131, 132 pubDate, 134, 135 rating, 134 rdf, 131 skipDays, 134 skipHours, 134 source, 135 textInput, 134 title, 131, 132, 134, 135 ttl, 135 webMaster, 135 RSS 1.1, 130, 162 RSS 2.0, 131
235
PHP Web 2.0. Tworzenie aplikacji typu mashup
S SAX, 55, 75, 118 funkcje PHP, 56 funkcje zwrotne, 58 klasa parsera, 60 kodowanie, 57 opcje parsera, 57 pobieranie danych z dokumentu XML, 63 testowanie funkcji zwrotnych, 59 zmiana wielkości liter, 57 SAXParser, 61, 62 schemat bazy danych, 205 screen-scraping, 160 SearchRequest, 114 SELECT, 168 semantyczna sieć stron internetowych, 21 send(), 183 sendRequest(), 183 serializacja, 32 odpowiedzi JSON, 189 service, 99 Services_YouTube, 141, 144 filmy, 146 informacje o użytkownikach, 145 instalacja, 142 korzystanie, 145 określanie protokołu, 146 REST, 146 serwer proxy żądań XMLHttpRequest, 220 sesje, 81 setCenter(), 192, 193 setTimeout(), 228 SHOW TABLES, 167 skalary, 26 SOAP, 21, 46, 86 Envelope, 100 kody błędów, 102 komunikaty, 99 Microsoft Live Search, 86 odpowiedzi, 110 raportowanie błędów, 102 struktura dokumentu WSDL, 87 WSDL, 86, 87 XSD, 87 soap:body, 98 SoapClient, 103 __getFunctions, 107 __getTypes, 107
236
__soapCall, 109 obsługa błędów SOAP, 110 obsługa odpowiedzi SOAP, 110 obsługa poprawnej odpowiedzi, 111 tryb nie-WSDL, 105, 106 tryb WSDL, 105, 106 tworzenie instancji, 105 tworzenie instancji w trybie nie-WSDL, 106 tworzenie instancji w trybie WSDL, 106 tworzenie parametrów, 104 wykonywanie wywołania, 107 wywoływanie operacji SOAP w trybie nie-WSDL, 109 wywoływanie operacji SOAP w trybie WSDL, 109 żądania SOAP, 103 soap-env, 101 SoapFault, 110 SoapServer, 103 SPARQL, 160, 166 analiza przedmiotu zapytania, 167 ASC, 175 DESC, 175 DISTINCT, 175 identyfikatory URI, 168 LIMIT, 174 OFFSET, 174 ograniczenia, 174 ORDER BY, 174 porządkowanie, 174 PREFIX, 168 przesunięcia, 174 SELECT, 168 UNION, 175 WHERE, 168, 169, 173 zapytania, 168, 170 zapytania o typy, 173 SPARQL Protocol and RDF Query Language, 160 SPARQLer, 166, 170 społeczności użytkowników, 20 SQL, 167 start_session, 81 status HTTP, 185 string, 26 strony agregujące, 17, 119 XSPF, 141 struct, 29 struktura dokumentu WSDL, 87
Skorowidz
struktury, 26, 28 sytuacja wyścigu, 223 szafa grająca z teledyskami, 125
T tablice, 26, 28, 93 tablice asocjacyjne, 148 this, 187 totalDigits, 90 trójki, 164 try-catch, 110 TubeSource, 214 tworzenie aplikacje mashup, 71, 203 baza danych, 204 jednoparametrowe żądania XML-RPC, 32 nagłówek wywołania żądania, 36 obiekt XMLHttpRequest, 182 serwer proxy żądań XMLHttpRequest, 220 strony agregujące, 119 żądania XML-RPC z wieloma parametrami, 35 typ boolowski, 27 types, 89 typy danych XML-RPC, 26 XSD, 89
U UML, 163 unie, 91 Unified Modeling Language, 163 UNION, 175 Universal Product Code, 23, 65 UPC, 23, 65 URI, 165 URL, 69 usługi internetowe, 46 UTF-8, 148 użytkownicy, 20
W Web 2.0, 19 Web Services Descriptor Language, 86, 87 WHERE, 168, 169, 173 whiteSpace, 90
Wii Seeker, 17 witryny mashup, 18 właściwości JSON, 188 WSDL, 86, 87 operacje, 96 opis typów danych, 86 parametry, 104 wstępne planowanie, 160 wykonywanie żądania REST, 48 żądania XML-RPC, 31 wysyłanie żądań XMLHttpRequest, 163 wyszukiwanie produktów, 71 wyszukiwarka, 85 wywołania XML-RPC, 25 gniazda, 38
X XML, 24, 47, 87 XML Schema Data, 86, 87 XML Shareable Playlist Format, 126 xml_create_parser, 57 xml_parse, 58, 62 xml_parser_create, 56, 62 xml_parser_set_option, 57 XML_RPC, 45 XML_RPC_Client, 45 XML_RPC_Message, 45 XML_RPC_Response, 46 XML_RPC_Value, 45, 46 XML_RSS, 141, 147, 152 getChannelInfo(), 148, 149 getItems(), 149 parse(), 148 parsowanie RSS, 148 tablice asocjacyjne, 148 tworzenie obiektu, 148 xml_set_character_data_handler, 58 xml_set_element_handler, 57, 58 xml_set_object, 62 XML_Unserializer, 141 XMLHttpRequest, 47, 163, 180, 186 cykl życia, 182 funkcja wywoływana zwrotnie, 184 JSON, 186 korzystanie z obiektu, 182 odpowiedź, 185 onreadystatechange, 183
237
PHP Web 2.0. Tworzenie aplikacji typu mashup
XMLHttpRequest open(), 183 responseText, 185 send(), 182, 183 sendRequest(), 183 status, 184 tworzenie obiektu, 182 tworzenie żądania HTTP, 182 wysyłanie żądania, 182 XMLHttpRequest, 180 żądania HTTP, 182 XML-RPC, 21, 24 base64, 34 czas, 34 dane binarne, 28 data, 34 gniazda, 38 Internet UPC Database, 65 klasa parsująca, 41 komunikacja, 24 liczby, 27 łańcuchy tekstowe, 26 nagłówek wywołania żądania, 36 obsługa w PHP, 31 odpowiedzi, 29, 40 PEAR, 44 serializacja danych, 32 skalary, 26 specyfikacja, 25 struktury, 28 tablice, 28 typ boolowski, 27 typy danych, 26 wykonywanie żądania, 31 wywołania, 25 wywołanie procedury, 24 żądania, 25 XML-RPC Encode Request, 32 xmlrpc_decode, 31, 40, 66 xmlrpc_encode_request, 31, 32, 33, 34, 35, 36, 40, 49 xmlrpc_is_fault, 31, 40 xmlrpc_set_type, 34, 35, 45 XMLRPCParser, 41, 42, 65 XSD, 86, 87, 89 XSDI, 101 XSLT, 68 XSPF, 126, 141 album, 128 annotation, 127, 128
238
attribution, 128 creator, 127, 128 date, 127, 128 duration, 128 extension, 128, 129 identifier, 128 image, 128 info, 127, 128 license, 128 link, 128 location, 127, 128 meta, 128, 129 playlist, 127 title, 127, 128 track, 127, 128 trackList, 127 trackNum, 128 XSPF_Track, 143 XSPF_Tracks, 144
Y Yahoo! Search Web Service, 117 wyszukiwanie stron internetowych, 117 YouTube, 19, 126, 136 get_profile, 137, 138 interfejs programistyczny, 137 kategoryzowanie filmów, 136 methodResponse, 138 oznaczanie, 136 społeczność, 136 umieszczanie w innych witrynach, 137 wiadomości o nowych filmach użytkownika, 136 wyszukiwanie filmów, 136 XML-RPC, 137 youtube.users.get_profile, 137 youtube.videos.list_featured, 137 żądania REST, 137
Z zakładki, 197 zapytania SPARQL, 166, 168, 170, 178, 206 zdjęcia londyńskiego metra, 159 AJAX, 220 aplikacja mashup, 203 baza danych, 204 dane o stacjach, 204
Skorowidz
Flickr, 220, 222 getAllLines(), 216 getStationsByLine(), 217 GEvent, 218 GLatLng, 217 Google Maps, 204 informacje o stacjach metra, 161 interfejs użytkownika, 216 kierowanie żądania XMLHttpRequest, 222 klucz Google API, 216 parseFlickrSearch(), 223 parsowanie odpowiedzi AJAX, 225 RAP, 209 retrieveFlickrPhotos(), 223 schemat bazy danych, 205 serwer proxy żądań XMLHttpRequest, 220 skrypt wypełniający bazę danych, 209 sytuacja wyścigu, 223 TubeSource, 214 wstępne planowanie, 160 XMLHttpRequest, 222 zapytania o linie, 207 zapytania o przyporządkowanie linii do stacji, 208 zapytania o stacje, 206 zapytania SPARQL, 206 żądania HTTP, 220 znaczenie danych, 19
Ź źródła danych, 21
Ż żądania GET, 48 HTTP, 47, 49, 182 POST, 48 REST, 46, 48 REST ECS, 69 SOAP, 103 XMLHttpRequest, 163 żądania XML-RPC, 25, 31 przekazywanie struktur, 36 przekazywanie tablic, 36 tworzenie, 32 wiele parametrów, 35
239