• Strona główna
  • Curriculum Vitae
  • O mnie
  • Mapa strony
  • Kontakt
Niebieski Pomarańczowy Zielony Różowy Fioletowy

Usługi sieciowe w PHP: XML-RPC

Opublikowane 30 lipca 2010. Autor: Kamil Brenk. Wizyt: 43.

Kategorie: PHP
Tematyka: API, Flickr API, PHP, PHP PEAR, praktyczne skrypty, Protokół HTTP, Protokół XML-RPC, serwisy Mashup, URL scheme, Web Services w PHP

lip 30

W końcu przyszedł czas na zabranie się za dokończenie tematu dotyczącego Web Services w PHP. Tym razem zabierzemy się za kolejny protokół, jeden z popularniejszych, czyli XML-RPC (XML-Remote Procedure Call).

Uzupełnieniem tego artykułu, dla początkujących, może być wcześniejszy wpis: Usługi sieciowe w PHP: REST. Znajduje się tam słowniczek pojęć, wspomniana jest instalacja pakietów PEAR, omówiona jest z lekka idea usług sieciowych i aplikacji typu Mashup.

Czym jest XML-RPC?

XML-RPC jest protokołem XML służącym do przesyłu danych między różnymi punktami końcowymi (serwerami). Umożliwia to wymianę danych dla zdalnych procedur.

Daje to wspaniałą możliwość komunikowania się różnych aplikacji przy minimalnym wysiłku, ponieważ usługi z wykorzystaniem tego protokołu są bardzo proste do definiowania.

Komunikacja odbywa się z wykorzystaniem protokołu HTTP, więc bez żadnych problemów można używać protokołu XML-RPC w web-developerce.

Pełna specyfikacja XML-RPC

XML-RPC w praktyce

Gdy poznaliśmy już teoretyczne podstawy XML-RPC, możemy pokazać przykładowy kod przesyłany w żądaniu i odpowiedzi. Kod zaczerpnięty ze specyfikacji.

Request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /RPC2 HTTP/1.0
User-Agent: Frontier/5.1.2 (WinNT)
Host: betty.userland.com
Content-Type: text/xml
Content-length: 181

<?xml version="1.0"?>
<methodCall>
   <methodName>examples.getStateName</methodName>
   <params>
      <param>
         <value><i4>41</i4></value>
      </param>
   </params>
</methodCall>

Response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HTTP/1.1 200 OK
Connection: close
Content-Length: 158
Content-Type: text/xml
Date: Fri, 17 Jul 1998 19:55:08 GMT
Server: UserLand Frontier/5.1.2-WinNT

<?xml version="1.0"?>
<methodResponse>
   <params>
      <param>
         <value><string>South Dakota</string></value>
      </param>
   </params>
</methodResponse>

Jak więc widać powyżej, wysyłamy pewien pakiet danych o żądanej usłudze (nazwa metody, parametry) w formacie XML, w czego odpowiedzi dostajemy wynik – również jako XML.

Niestety, jest pewna dowolność w konstruowaniu odpowiedzi. Programista może napisać usługę w taki sposób, aby zwracała dane w formacie JSON czy PHP Array – nie jest to zgodne ze specyfikacją protokołu XML-RPC, jednak możliwe do wykonania, a co najgorsze – dość często stosowane.

XML-RPC w PHP

Protokół ten jest dość często wykorzystywany w PHP, w związku czym powstało multum bibliotek do jego obsługi.

Także i na potrzeby tego wpisu wykorzystamy jedną z takich bibliotek: XML_RPC2 z pakietu PEAR.

Aby biblioteka ta zadziałała konieczne jest także zainstalowane biblioteki cURL dla PHP. Ponadto na potrzeby poniższego przykładu wykorzystamy jeszcze jedną bibliotekę z pakietu PEAR odpowiedzialną za buforowanie wyników, czyli Cache_Lite, która jest zintegrowana z biblioteką XML_RPC2.

Warto korzystać z gotowych bibliotek tego typu, ponieważ bardzo skracają czas pracy. Zamiast samodzielnie tworzyć połączenie z wykorzystaniem cURL czy fsockopen, wystarczy napisać kilka linijek kodu z wykorzystaniem biblioteki.

W dodatku biblioteka potrafi buforować wyniki (zapisując na dysku wynikowe pliki), co bardzo skraca kolejne wywołania usługi. Jako, iż usługi sieciowe są najczęściej bardzo wolne, buforowanie jest wręcz wymagane!

To już wszystko, możemy zaczynać. Na początek napisałem prostą klasę XMLRPC, która jeszcze bardziej skraca odwoływanie się do zewnętrznych usług.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
require_once 'XML/RPC2/CachedClient.php';

class XMLRPC {

    // client xml-rpc
    private $client;

    /**
     * Magic method: constructor
     *
     * @param string    web service url
     * @param string    prefix
     * @param int       cache time in seconds
    **/

    public function __construct($url, $prefix='', $cache_time=3600) {
   
        $options = array(
            'prefix' => $prefix,
            'cacheDebug' => false,
            'cacheOptions' => array(
                'cacheDir' => './tmp/',
                'lifetime' => $cache_time,
                'cacheByDefault' => true
            )
        );
   
        // We make the XML_RPC2_CachedClient object
        // same syntax than XML_RPC2_Client
        $this->client = XML_RPC2_CachedClient::create($url, $options);
       
    }
   
   
    /**
     * Magic method: call
     *
     * @param string    method name
     * @param mixed     params
     * @return object   response
    **/

    public function __call($name, $arguments) {
   
        try {

            // run method from web service
            $result = call_user_func_array(
                array($this->client, $name),
                $arguments
            );

            // response is array - return object
            if (is_array($result)) {
                return new ArrayObject($result);
            }
           
            // response is xml - return object
            $result_xml = simplexml_load_string(
                $result,
                'SimpleXMLElement',
                LIBXML_NOERROR
            );
            if ($result_xml !== false) {
                return $result_xml;
            }
           
            // response is string or other type
            return $result;

        } catch (XML_RPC2_FaultException $e) {

            // The XMLRPC server returns a XMLRPC error
            die('Exception #' . $e->getFaultCode() . ' : '
                . $e->getFaultString());
           

        } catch (Exception $e) {

            // Other errors (HTTP or networking problems...)
            die('Exception : ' . $e->getMessage());

        }
       
    }


}

Krótkie omówienie klasy:

  1. Tworzymy folder “tmp”, w którym będzie przechowywany cache.
  2. Wywołujemy instancję klasy XMLRPC – w tym momencie jest utworzony obiekt klasy XML_RPC2_CachedClient z metodą create, który tworzy połączenie z usługą.

    Jako parametr podajemy adres usługi i ustawienia, m. in. prefix dla metod, folder dla cache, czas życia buforu.

  3. Na utworzonym wcześniej obiekcie wywołujemy metody właściwej już usługi sieciowej. W wyniku otrzymujemy obiekt SimpleXMLElement ze zwróconej odpowiedzi w postaci XML.

    W przypadku, gdyby usługa nie zwróciła dokumentu XML, a tablicę array to dokonywana jest konwersja na obiekt ArrayObject – jakoś przyjemniej mi się pracuje z wykorzystaniem tej klasy.

    W przypadku zwrócenia przez usługę innego typu danych – dane te zostają zwrócone bezpośrednio do wywołanej metody.

W prawdziwej aplikacji warto by obsłużyć błędy w jakiś lepszy sposób, a nie zabijać od razu aplikację i pokazywać błędy userowi :-)

Przykładowe zastosowanie: UPC Database

UPC to takie amerykańskie kody kreskowe dla produktów :D powiedzmy więc, że mamy kilka takich numerów UPC i chcielibyśmy wiedzieć co to za produkty. W tym celu posłużymy się usługą UPC Database.

Oto przykładowe zastosowanie stworzonej klasy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$client = new XMLRPC('http://www.upcdatabase.com/rpc');

$results = $client->lookupUPC('016600190027');
if (is_object($results)) {

    echo "Numer UPC: " . $results->offsetGet('upc');
    echo "Produkt: " . $results->offsetGet('description');

}

$results = $client->lookupUPC('011926112401');
if (is_object($results)) {

    echo "Numer UPC: " . $results->offsetGet('upc');
    echo "Produkt: " . $results->offsetGet('description');

}

W wyniku czego dostajemy:

1
2
3
4
5
Numer UPC:      016600190027
Produkt:        Schweppes Raspberry Gingerale

Numer UPC:      011926112401
Produkt:        Honey Nut Cheerios Cereal

Prawda, że proste?

Przykładowe zastosowanie: Flickr

Drugim i ostatnim przykładem będzie również zagraniczny serwis, Flickr. Jest to chyba największy tego rodzaju internetowy manager zdjęć, w którym każdy użytkownik może publikować swoje zdjęcia, opisywać je, tworzyć albumy, itp itd.

Na potrzeby przykładu pobierzemy kilka adresów opatrzonych wybranym przez nas tagiem:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// new XML RPC Client
$client = new XMLRPC(
    'http://api.flickr.com/services/xmlrpc/',
    'flickr.photos.'
);

// search tags
$results = $client->search(array(
    'api_key'   => '[API_KEY_FROM_FLICKR]',
    'tags'      => 'money',
    'per_page'  => 5
));

if (is_object($results) && isset($results->photo)) {

    foreach ($results->photo as $id => $result) {
        echo 'http://farm' . $result['farm'] . '.static.flickr.com/' . $result['server'] . '/' . $result['id'] . '_' . $result['secret'] . ".jpg";
    }

}

Na wyjściu otrzymujemy:

1
2
3
4
5
http://farm5.static.flickr.com/4146/4842869683_984e815ecc.jpg
http://farm5.static.flickr.com/4108/4843415934_901ba5db50.jpg
http://farm5.static.flickr.com/4111/4843400822_542f815732.jpg
http://farm5.static.flickr.com/4128/4842735689_14e7090041.jpg
http://farm5.static.flickr.com/4109/4843254586_2c5d2030c1.jpg

Wszystko więc działa jak należy i nie sprawia większych problemów. Podsumowując, protokół XML-RPC jest przyjaznym użytkownikowi protokołem i znacząco ułatwia wykorzystanie usług sieciowych w tworzonych aplikacjach.

Przydatne linki
  • Katalog usług opartych o protokół XML-RPC

Komentarze (2)

  1. sokzzuka 4 sierpnia 2010

    W Zend Frameworku są już od jakiegoś czasu dostępne web services w postaci XML-RPC, SOAP i Json RPC, wszystkie trzy dostępne pod jednym interfejsem i dość nienajgorzej zaimplementowane…

  2. Kamil Brenk 4 sierpnia 2010

    W Zendzie i Symfony jest chyba wszystko, o czym tylko programista może pomyśleć :D niemniej jednak chciałem sprawdzić samemu jak to wszystko wygląda “od środka” – wpis ten popełniłem głównie w ramach nauki :-) bez korzystania z frameworków.



Dodaj komentarz

XHTML: Możesz użyć następujących tagów
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code lang="" escaped=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Kamil Brenk Blog

PHP, JavaScript, SQL, HTML

  • Informacje o blogu

    Kamil Brenk

    Blog o tworzeniu aplikacji na potrzeby sieci Web.

    Praktyczne przykłady, porady i sztuczki. PHP, SQL, AJAX, JavaScript, HTML i pochodne.

    Kanał RSS

    • Najnowsze
    • Komentarze
    • Popularne
    • Konwersja JS i CSS do PNG
    • Optymalizacja wyrażeń regularnych
    • Cross-Domain JavaScript
    • Kompendium programisty #1
    • Jak pobierać zewnętrzne zasoby?
    • 960 Grid System
    • Kamil Brenk: @eN: Nie sposób się nie zgodzić z tym co piszesz, masz...
    • Michal Wachowski: To jest na prawdę szalone :D
    • The one: Zarąbista gierka :D
    • eN.: Problem w tym że nie zwracają tego samego, a dokładniej1...
    • Michal Wachowski: DAS - deterministyczne automaty skończone, AS - to samo ale bez...
    • Kamil Brenk: DAS / AS? Pierwsze słyszę :-) Raczej nie będę już miał...
    • Michal Wachowski: Jak na studiach będziesz mieć AS i DAS to wiele się wyjaśni...
    • Gramatyka w PHP, część 1
    • Projekt aplikacji po stronie klienta
    • Optymalizacja wyrażeń regularnych
    • Yii PHP Framework vs Symphony
    • Minimalizacja zapytań HTTP
    • Jak pobierać zewnętrzne zasoby?
    • Usługi sieciowe w PHP: REST
  • Szukajka
    Wpisz co chcesz wyszukać na stronie…
  • Kategorie
    • Apache
    • Front-end Development
    • HTML5 & CSS3
    • Inne
    • JavaScript
    • PHP
    • Po godzinach
    • Protokół HTTP
    • SQL
    • Wyrażenia regularne
  • Moje serwisy
    • Testy zawodowe
    • Miłość, uczucia i seks
  • Czytane blogi
    • Wojciech Sznapka
    • Wojciech Soczyński
    • Dzienniki zyxowe
    • Przemysław "eRIZ" Pawliczuk
  • Archiwum
    • sierpień 2010
    • lipiec 2010
    • czerwiec 2010
    • maj 2010
    • kwiecień 2010
    • marzec 2010
    • luty 2010
    • styczeń 2010
  • Strona główna
  • Curriculum Vitae
  • O mnie
  • Mapa strony
  • Kontakt

Kamil Brenk © 2010. All rights reserved.

Designed by FTL Wordpress Themes brought to you by Smashing Magazine.

Do góry ∧