Although I’d normally write this article in Romanian I expect a wider audience for it, so I will post it in English.

Snippet

If you don’t want to read the entire article here are some shortcuts:

Dance-Tunes.com

Dance Tunes Logo

Dance-Tunes is a music online store, located in the Netherlands, where one can buy their favorite … dance tunes, of course. Organised in eight main genres (Downtempo, Dance, Techno/Minimal, Hardcore, Breaks, Electro/House, Hardstyle/Jump and Trance) Dance-Tunes has an extensive database of tunes, artists and labels.

The Public API

On the 22nd of February 2010, Dance-Tunes released their 1.0 version of the API. The main purpose of the API is to help create rich internet applications that are based on the information of tunes, artists, labels or charts. Developers are encouraged to use the public Dance-Tunes API to get creative and use the extensive library of labels, artists and tracks in their sites or applications.

Dance-Tunes has created a special page for developers where one can find all information about the public API. Each individual API call is explained and examples are given to show the correct structure of the calls and response. One can request an API key by logging in using the Dance-Tunes account details. The API key will be sent to the developer’s email address. One can download the API documentation in pdf format for printing and off-line viewing.

The PHP Client

The Dance-Tunes PHP API Client that I am releasing today, was written with intention of integrating into a Zend Framework application. As of this it follows the PHP File Formatting, Naming Conventions and Coding Style recommended by the Zend Framework Team.

It uses the Zend_Http and Zend_Exception packages from the Zend Framework classes. It can be used either with an entire Zend Framework application or as is. The dependencies can be load by themselves (just the 2 packages), or the Dancetunes class can be modified in order to be used in a simple PHP project. One can replace the use of Zend_Http package with the use of the cURL library and the Zend_Exception with the PHP base Exception.

Usage

Each of the documented function in the Dance-Tunes API has a corespondent wrapper method in the Dancetunes class that accepts the exact amount of parameters that the API requires, except the command and the session(params cmd and session) which are built in. For each session key, as the documentation specifies, a timestamp is updated on each request, and the session key is deleted if no request is performed for 6 hours. So, as a best practice, it is recommended to cache the session key locally for immediate access to the resources on other requests.

$dancetunes = new Dancetunes(DT_API_KEY);

if (!($sessionKey = $cache->load('sessionkey')))
{
    $dancetunes->setUserName(DT_USERNAME);
    $dancetunes->setPassword(DT_PASSWORD);
    
    $sessionKey = $dancetunes->authenticate();
    $cache->save($sessionKey, 'sessionkey');
}
$dancetunes->setSessionKey($sessionKey);

/* Get the first 10 releases */
$releases = $dancetunes->getNewReleases(11, 10, 1, 10);

/* Get a preview */
$audio = $dancetunes->getPreview( 123456 );
/* saving the audio content to disc */
file_put_contents('123456-tune-preview.mp3', $audio);

Responses

Dance-Tunes API will respond either with an XML string or with binary data (for artwork and preview). For the case of XML response I have created a Dancetunes_Vo class that takes a XML string and converts it into an array. Also for each response type I have created an individual class that extends the Dancetunes_Vo class and implements some particular methods for a specific result. For example the Dancetunes_NewReleases class will be the result of the Dancetunes::getNewReleases method and has a method for returning just the tunes info from the response, and another one to return the number of total possible tunes that can be returned on a specific request.

class Dancetunes_NewReleases extends Dancetunes_Vo {
    
    public function getTracks() {
        return $this->_array ['new_releases'] ['tune'];
    }
    
    public function getCount() {
        return $this->_array ['new_releases'] ['total_count'];
    }
}

These classes can be modified or extended in order to deal in a specific way with the XML response.


License

The package is released as an open source project under The New BSD License.

Copyright (c) 2010, George Enciu, ColorSoft System SRL, Romania

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of the ColorSoft System SRL nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Change Log

2010-04-06
  • Version 0.1 BETA released
2010-05-15
  • Added a version written in simple PHP
  • Still in 0.1 Beta

Download

All the code and download files can be found at the Google Code Project

Așa cum promiteam într-un articol precedent vom încerca realizarea unei aplicații pe baza API-ului oferit de rețeaua de micro-blogging Twitter și folosind protocolul OAuth.

Twitter

Pentru cei ce nu sunt familiarizaţi cu Twitter, acest serviciu oferă posibilitatea utilizatorilor de a trimite mesaje de maxim 140 caractere (gen SMS) şi în care, teoretic, s-ar transmite mesaje scurte, informative, care ar răspunde la întrebarea "What’s happening?". Prietenii acestuia, sau cei interesati de ce ar avea acea persoană de spus (cazul unei persoane publice) îi pot urmări twitt-urile fiind astfel la curent cu ce face sau cu ce are de zis. Astfel un utilizator poate urmări anumiţi utiliztatori şi poate fi urmarit de către alţii, creându-se astfel conexiunile pe care se bazează orice reţea socială.

Pe langă aplicaţia web de utilizare a sistemului pe care Twitter o ofera pe site-ul twitter.com, aceştia mai pun la dispoziţie dezvoltatorilor de software posibilitatea de a crea aplicaţii diverse folosindu-se de un API, un set de comenzi prin care cei autorizaţi să îl foloseasca pot citi şi scrie date direct în sistemul Twitter. Transmiterea de date se face prin formate standard, asigurându-se astfel că orice aplicaţie-client twitter s-ar folosi, datele transmise pot fi citite din orice altă aplicaţie-client.

Lista funcţiilor oferite de API-ul Twitter cuprinde toate funcţionalităţile oferite şi de site-ul twitter.com astfel că practic vorbind putem crea o aplicaţie identică cu site-ul actual, o clonă sau chiar o versiune mai bună, folosindu-se chiar de sistemul original Twitter, serviciu foarte popular cu un numar necunoscut de utilizatori nici până în ziua de azi.

Twitter, cei care ofera interfata de comunicare a aplicatiilor externe cu sistemul propriu-zis trebuie sa facă diferențierea între actiunile venite din partea site-ului și cele venite de la o aplicație, oricare ar fi aceasta. Mai mult ei trebuie să asigure suportul pentru o comunicatie sigură și să se știe tot timpul despre o acțiune de către ce aplicație este făcută și în numele cărui utilizator.

OAuth

Aici este locul în care intervine OAuth: avem un sistem, avem niste utilizatori, avem pentru fiecare utilizator resurse proprii (mesajele proprii, mesajele prietenilor si posibilitatea de a trimite noi mesaje) și nu în ultimul rând avem mai multe aplicații externe care pot comunica cu Twitter în numele unor utilizatori. Deci putem asocia conceptele OAuth după cum urmează:

  • Resursele: lista de mesaje transmise, mesajele prietenilor, lista de prieteni
  • Furnizorul de servicii: Twitter, cel care implementeaza protocolul OAuth și care ofera acces la serviciile sale.
  • Utilizatorul: orice persoană care și-a creat un cont folosind site-ul twitter.com
  • Consumatorul: aplicația care va utiliza twitter-ul in numele clientului.

Așadar OAuth este folosit pentru autentificarea aplicațiilor twitter, autentificarea utilizatorilor si autorizarea aplicației să efectueze operații asupra Twitter-ului în numele lor. O astfel de autorizare trebuie făcută explicit și pentru fiecare aplicație în parte. Fiind vorba despre o aplicație vom încerca și noi crearea uneia care va permite folosirea sistemului Twitter.

Inscrierea

Inregistrarea unei aplicatii Twitter

Inregistrarea unei aplicatii Twitter

Pentru ca o aplicație să poată funcționa corect și pentru a oferi siguranță și încredere utilizatorilor ea trebuie înregistrată cu furnizorul de servicii. Aceasta se face dintr-un cont existent de twitter la adresa http://twitter.com/apps unde se vor cere informații despre aplicație cum ar fi: nume, descriere, cine o dezvoltă, site-ul de unde se poate procura, tipul aplicatiei (desktop sau web) daca aplicația are nevoie doar de a citi date sau și de a scrie și nu în ultimul rând URL-ul de întoarcere, despre care vom vorbi mai târziu în detalierea aplicației.

După înregistrarea completă a aplicației, Twitter va genera un Consumer Key si un Consumer Secret, ambele reprezentând câte un sir de caractere din care primul este folosit pentru identificarea aplicației iar cel de-al doilea este folosit pentru a coda comunicarea dintre aplicatie si Twitter. Fiind un sir de caractere secret, el trebuie pastrat în siguranță și trebuie cunoscut doar de către dezvoltatorii aplicației.

Aplicația

Aplicația în sine poate fi de orice tip (web sau desktop) și poate fi scrisă în aproape orice limbaj de programare . Pentru demonstrare am ales limbajul PHP cu care vom crea o aplicație simplă de citire a datelor și de actualizare a statusului Twitter. Pentru aceasta am creat o clasă numită TwitterConnector și pentru care am definit câțiva membri privați și câteva constante:

class TwitterConnector
{
    private $_consumerKey = '';
    private $_consumerSecret = '';
    private $_tokenSecret = '';
    private $_token = '';

    private $_version = '1.0';

    const URL_REQUEST_TOKEN = 'https://twitter.com/oauth/request_token';
    const URL_ACCESS_TOKEN = 'https://twitter.com/oauth/access_token';
    const URL_AUTHORIZE = 'https://twitter.com/oauth/authorize?oauth_token=%s';
    const URL_AUTHENTICATE = 'https://twitter.com/oauth/authenticate?oauth_token=%s';
    const URL_DATA = 'http://api.twitter.com/1/';
}

$_consumerKey și $_consumerSecret sunt valorile primite de la Twitter cu identificatorul aplicației și cu cheia secretă de semnare a mesajelor. $_token și $_tokenSecret au aceeași însemnătate ca și consumer key și respectiv consumer secret, însă ele sunt atribuite pentru utilizator. Așadar cu un cod de identificare al aplicației și un token din partea clientului putem detemina ce utilizator a folosit ce aplicație, iar prin codul secret al consumatorului și cel al utilizatorului se poate comunica intr-un mod sigur cu sistemul Twitter. $_version reprezintă versiunea OAuth conform căror specificații realizăm aplicația.

URL_REQUEST_TOKEN, URL_ACCESS_TOKEN sunt URL-urile furnizate de către Twitter și sunt folosite pentru a obține un request token și un access token (pe care le vom detalia mai jos), URL_AUTHORIZE este folosit în cazul în care aplicația folosește sistemul de autentificare al lui Twitter, URL_AUTHENTICATE este folosit pentru a redirecționa utilizatorul pentru a autoriza aplicația iar URL_DATA este URL-ul de la care se vor obține date sau unde vor fi trimise comenzile de actualizare sau ștergere.

Vom crea în continuare constructorul clasei ce va primi ca parametrii consumer key si consumer secret pentru ușurință în dezvoltarea ulterioară. Deasemeni am definit și cate un setter si getter pentru a opera cu parametrii token si token secret.

 public function __construct($consumerKey, $consumerSecret)
    {
        $this->_consumerKey = $consumerKey;
        $this->_consumerSecret = $consumerSecret;
    }

    public function setToken($token)
    {
        $this->_token = $token;
    }

    public function getToken()
    {
        return $this->_token;
    }

    public function getTokenSecret()
    {
        return $this->_tokenSecret;
    }

    public function setTokenSecret($secret)
    {
        $this->_tokenSecret = $secret;
    }

Comunicarea cu sistemul Twitter

Pentru a comunica unitar cu Twitter am creat metoda doRequest, care primește drept parametrii adresa URL unde va face cererea, metoda HTTP prin care se va accesa sistemul Twitter (GET, POST, DELETE etc) precum și parametrii care vor fi trimiși. Pe lângă parametrii specifici fiecărei funcții descrise în documentația API-ului Twitter, sistemul are nevoie de a primi niște parametrii specifici OAuth pentru ca furnizorul de servicii să fie sigur că aplicația care acționeză în numele unui utilizator are acest drept și nu se încearcă accesarea neautorizată a sistemului. Acești parametrii sunt returnați de metoda getDefaultParameters și sunt adaugați la lista de parametrii specifici apelului pe care incercăm sa-l facem, folosind funcția array_merge.

Având acești parametrii putem semna cererea, prin metoda generateSignature (detaliată mai jos), iar apoi folosind funcțiile cURL vom contacta Twitter-ul pentru a transmite sau pentru a obține date. Parametrii pe care ii primim sunt in format array deci vor trebui transformați într-un format ce poate fi trimis atat prin metoda GET (deci adaugat la URL) cât și prin metoda POST. cURL suporta pentru ambele variante transmiterea parametrilor folosind o structură name-value-pair (NVP), adica perechi de tipul atribut1=valoare1&atribut2=valoare2

 protected function doRequest($url, $method, $params)
{
    $params = array_merge($this->getDefaultParameters(), $params);
    $params = $this->encodeParams($params);

    $params['oauth_signature'] = $this->generateSignature($url, $method, $params);

    $ch = curl_init();
    if (strtolower($method) == 'post')
    {
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $this->toNVP($params, true));
        curl_setopt($ch, CURLOPT_URL, $url);
    }
    else
    {
        curl_setopt($ch, CURLOPT_URL, $url . '?' . $this->toNVP($params, true));
    }

    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    return curl_exec($ch);
}

Observați că am tratat separat cazul în care parametrii se trimit prin POST fată de cel în care se trimit prin GET, cazul în care se doresc apelarea altor metode fiind ușor de adaugat, putând constitui o temă pentru cei care doresc continuarea exemplului pe care il dezvoltăm.

Semnarea cererilor

Orice cerere către furnizorul de servicii, fie pentru a obtine un token fie pentru a obtine accesul la resursele protejate trebuie să fie însoțită de o semnătură, pentru a dovedi autenticitatea celui care o cere. Semnătura este compusă din parametrii cu care se face cererea și din cheia secretă și (dacă există) tokenul secret. Modul de semnare a cererii este definită în secțiunea 9 a documentației OAuth și este la rândul ei realizată în mai multi pași.

Desi specificațiile OAuth permit folosirea mai multor metode de criptarea a semnaturii, de fapt recomandă folosirea câtorva metode, dar nu ingrădește definirea și documentarea altora, Twitter nu sportă criptarea semnăturii decât prin metoda HMAC-SHA1. Avem nevoie așadar de un sir de caractere de bază și de o cheie de criptare. Sirul de caractere este format din concatenarea metodei HTTP folosite, a URL-ului si a listei de parametrii normalizați conform NVP. Acest sir de caractere de bază trebuie codat folosind o cheie, formată din concatenarea consumer secret si (dacă există) token secret.

 protected function generateSignature($url, $method , $params)
{
    $nvp = $this->toNVP($params, true);
    $nvp = $this->encode($nvp);
    $url = $this->encode($url);

    $string = "{$method}&{$url}&{$nvp}";
    $key = $this->encode($this->_consumerSecret) . '&' . $this->encode($this->_tokenSecret);
    return  base64_encode(hash_hmac('sha1', $string, $key, true));
}

Rezultatul funcției hash_hamc fiind unul binar, el trebuie transformat într-unul ASCII folosind funcția base64_encode. Metodele toNVP și fromNVP sunt folosite pentru a obține o codare NVP dintr-un array și un array dintr-un string codat NVP. Metoda toNVP are totuși particularitatea ca va ordona valorile din vector în ordine alfabetică a cheilor fiecărui element (aceasta fiind o cerintă din specificațile OAuth). O altă cerință OAuth este ca orice parametru să fie codat UTF-8 și trebuie să fie codat URL, lucru care se asigură prin metoda encode.

 protected function toNVP(array $array)
{
    ksort($array);

    $nvp = array();
    foreach($array as $name => $value)
    {
      $value = $this->encode($value);
      $nvp[] = "{$name}={$value}";
    }

    return implode('&', $nvp);
}

protected function fromNVP($string)
{
    $list = explode('&', $string);
    $output = array();
    foreach($list as $element)
    {
        $tmp = explode('=', $element);
        $output[$tmp[0]] = $tmp[1];
    }
    return $output;
}

protected function encode($value)
{
    return rawurlencode(utf8_encode($value));
}

Parametrii obligatorii, ceruți la fiecare cerere către Twitter sunt:

  • oauth_consumer_key – parametru consumer key cu care am instanțiat clasa
  • oauth_signature_method – metoda folosită de a semna cererea, in cazul nostru va fi tot timpul HMAC-SHA1
  • oauth_timestamp – reprezintă UNIX TIMESTAMP, sau numărul de secunde trecute din 01.01.1970 și este util pentru a verifica daca o nouă cerere este făcută după precedenta și de a preveni posibile atacuri
  • oauth_nonce – este un string unic, generat aleator și pentru care nu se impune nici o restricție de lungime. Pentru generarea lui am folosit funcția generateNonce care folosește o combinație de funcții de generarea de coduri unice (uniqid) si de codare (md5)
  • oauth_version – versiunea OAuth care se utilizează (poate fi opțional)
  • oauth_token – daca există definit, tokenul va fi inclus.

Parametrul oauth_signature este cerut la fiecare cerere, dar el trebuie omis în construirea semnăturii.

 protected function getDefaultParameters()
{
    $params['oauth_consumer_key'] = $this->_consumerKey;
    $params['oauth_signature_method'] = 'HMAC-SHA1';
    $params['oauth_timestamp'] = time();
    $params['oauth_nonce'] = $this->generateNonce();
    $params['oauth_version'] = $this->_version;
    if ($this->_token != '')
    {
        $params['oauth_token'] = $this->_token;
    }

    return $params;
}

protected function generateNonce()
{
    return md5(uniqid(rand(), true));
}

Autentificarea și autorizarea

În secțiunea 6 a specificațiilor OAuth se definește procesul de autentificare și autorizare ca fiind unul în 3 pași:

  1. Consumatorul obține de la Twitter un token neautorizat (numit request token)
  2. Utilizatorul autorizează tokenul neautorizat spre folosire de către consumator (de regulă utilizatorul este direcționat la Twitter pentru autorizare și posibil autentificare)
  3. Consumatorul schimbă token-ul neautorizat cu unul autorizat (numit access token)

Așa vom proceda și noi: inițial vom instanția clasa și vom cere un request token, cu care vom redirecționa utilizatorul către site-ul twitter.com pentru a-l autoriza. După ce acesta se autentifică și autorizează aplicația noastră să opereze sistemul twitter în numele lui, twitter.com va redirecționa utilizatorul către URL-ul de întoarcere pe care l-am definit în momentul în care am declarat intenția de a crea o aplicație pe site-ul twitter.com. În acest fel, chiar daca s-ar petrece o scurgere de informații și consumer secret ar ajunge la persoane cu intenții necurate, sistemul twitter va redirecționa clientul înapoi către URL-ul definit la inregistrarea aplicției, eliminând astfel posibila fraudă.

$tw = new TwitterConnector(CONSUMER_KEY, CONSUMER_SECRET);

if (isset($_SESSION['oauth_token']) && isset($_SESSION['oauth_token_secret']))
{
    /**
      * in cazul in care avem stocate pe sesiune informatiile 
      * despre token putem instantia clasa
      * si folosi toate functionalitatile twitter
      **/
    $tw->setToken($_SESSION['oauth_token']);
    $tw->setTokenSecret($_SESSION['oauth_token_secret']);
    
    if (count($_POST) > 0)
    {
        if (isset($_POST['msg']) && strlen($_POST['msg']) <= 140 )
        {
            $tw->updateStatus('json', $_POST['msg'], null);
            header('Location: index.php');
        }
    }
}
else if (isset($_GET['oauth_token']))
{
    /**
      * in cazul in care primim pe GET informatiile despre un token 
      * inseamna ca el este unul neautorizat
      * si vom incerca sa-l autorizam dupa care vom scrie pe sesiune 
      * informatiile despre access token
      **/
    $tw->setToken($_GET['oauth_token']);
    $tw->setTokenSecret('');

    $tw->getAccessToken();
    
    $_SESSION['oauth_token'] = $tw->getToken();
    $_SESSION['oauth_token_secret'] = $tw->getTokenSecret();
}
else
{
    /**
      * in cazul in care nu suntem in nici un caz din cele de mai sus
      * inseamna ca utilizatorul a accesat pentru prima oara aplicatia noastra
      * deci vom obtine un reuest token si apoi vom redirectiona utilizatorul 
      * catre twitter.com pentru autorizare
      **/
    $tw->requestToken();
    $tw->redirect();
}

Comunicarea

Odata autorizată, aplicația poate accesa resursele protejate ale utilizatorului și poate acționa în numele lui pentru a urmări alți utilizatori, a trimite mesaje noi sau orice alte acțiuni permise prin API-ul Twitter. Evident ca într-o situație reală aceste acțiuni vor fi dictate de către însuși utilizator și aplicația doar le va intermedia și nu să trimită mesaje în neștirea utilizatorului. Deși token-ul de acces este emis pentru fiecare aplicație pentru un termen nelimitat utilizatorul are posibilitatea sa revoce acest acces prin intermediul twitter.com.

Doar pentru scop demostrativ am implementat funcționalitatea pentru a prelua "time-line"-ul utilizatorului (adică ultimele mesaje trimise de către cei pe care utilizatorul îi urmărește) și funcția de actualizare a status-ului. Pentru oricare din funcționalitățile API-ului Twitter oferă răspunsul în 2 variante: JSON și XML. Pentru aceasta am creat funcția returnFormatedReply care va întoarce rezultatul formatat în funcție de cererea făcută

public function getHomeTimeline($format)
{
    $reply = $this->doRequest(
                self::URL_DATA . 'statuses/home_timeline.' . $format, 
                'GET', 
                array()
    );
    return $this->returnFormatedReply($format, $reply);
}

public function updateStatus($format, $status, $inreply)
{
    $reply = $this->doRequest(
                self::URL_DATA . 'statuses/update.' . $format, 
                'POST', 
                array('status' => $status, 'in_reply_to_status_id' => $inreply)
    );
    return $this->returnFormatedReply($format, $reply);
}

protected function returnFormatedReply($format, $reply)
{
    switch ($format)
    {
        case 'json':
            return json_decode($reply);
            break;
        case 'xml':
            return new SimpleXMLElement($reply);
            break;
        default:
            return null;
    }
}

Considerente finale

Exemplu folosit este pe departe de a fi un exemplu complet funcțional pentru a fi folosit într-o aplicație Twitter gata de a fi lansată în producție; de exemplu nu tratează în nici un fel erorile posibile de la Twitter. Am creat acest exemplu pentru a demonstra posibilitatea de implementare a specificațiilor OAuth pe partea de consumator în contextul PHP. Totusi reprezintă un bun început pentru a se ajunge la o aplicație twitter de sine stătătoare si care sa funcționeze corect în toate cazurile. O modalitate de a face un cache al mesajelor și adăugarea unor funcționalități JavaScript pentru o experientă a utilizatorului mai placută, ar putea fi un început pentru o posibilă aplicație funcționala 100%. Provocăm pe oricine are timpul și este dispus la o completare și o continuare a codului început aici pentru a realiza o aplicație corectă si completă, și supunem la dezbateore orice idee în acest sens ați avea.

Puteți testa aplicația la adresa http://sandbox.inphpwetrust.com/twitter/, puteți obține codul sursă sau ne puteți urmări pe Twitter

OAuth (sau Open Authentication) este un protocol liber prin care se poate permite accesul la resurse aflate pe un alt site fără a dezvălui datele de conectare (nume utilizator și parolă) către site-ul în care se dorește accesul la acele resurse partajate (imagini, video, adrese de email, conturi bancare etc.)

Este practic o ”cheie pentru valet” sau un ”ecuson pentru vizitator” cu care se acordă un acces mai restrâns la resursele personale aflate pe un site, decât dacă le-am accesat direct din acel site. La fel cum o cheie pentru valet (cel care se ocupa de parcarea mașinii) ar permite accesul doar la anumite funcții ale mașinii și pentru o perioadă restrânsă sau cum un ecuson de vizitator nu ar permite accesul la orice încăpere dintr-o clădire.

Înțelegerea protocolului OAuth se bazează pe cunoașterea următorilor termeni:

  • Resursele – sunt obiectele private ale unui utilizator la care se dorește accesul selectiv de către alte site-uri sau aplicații
  • Furnizorul de Servicii – este cel care suporta toate aspectele ale implementarii protocolului, cel care urmează sa ofere acces la resursele care le are în administrare către alți clienți. Poate fi un site de stocare de poze care oferă acces altor site-uri pentru preluarea imaginilor stocate de către un anume utilizator. Poate fi un site care stochează toate contactele (nume, email, telefon, adresa) personale ale unor utilizatori. Poate fi, în general, orice serviciu care oferă stocarea de informații private și accesibile selectiv. De regulă, acest furnizor de servicii, își oferă serviciile în baza unei înscrieri și în baza unei autentificări folosind un nume utilizator și o parolă.
  • Utilizatorul – este cel pentu care s-a inventat acest protocol. Utilizatorul are resurse în cadrul unui furnizor de servicii (imagini, video, contacte, mesaje) care nu vrea sa le facă publice, dar care vrea sa le folosească și pe alte site-uri. El este cel care va decide cărui site va acorda acces la resursele proprii
  • Consumatorul – este site-ul de pe care se dorește accesarea resurselor aflate la furnizorul de servicii.
  • Jetonul – sau celebrul token, este identificatorul prin care furnizorul de servicii va comunica cu consumatorul. În baza lui se va ști ce resurse și ale cărui utilizator trebuiesc partajate

Astfel, presupunem că deținem un cont pe site-ul Flickr în care avem stocate câteva imagini din vacanța de iarna. Aceste imagini le dorim printate folosind un serviciu online. În situația de față furnizorul de servicii este flickr.com deoarece deține niște resurse pe care dorim sa le partajăm folosind protocolul OAuth către un site ce oferă servicii de printare. Pentru a obține imaginile stocate pe flickr.com putem manual să le descărcăm și să le încarcăm pe site-ul de printat fotografii. Dar ar dura prea mult. Site-ul de printare fotografii se oferă să le preia automat în numele nostru. Pentru aceasta noi va trebui să furnizăm adresa de email şi parola pentru ca site-ul să ceară în numele nostru lista de fotografii pe care o avem pe Flickr. Intervine problema securităţii şi a increderii faţă de site-ul care ne va printa pozele: furnizând parola, aceasta poate fi stocată pe serverele lor, iar cineva rău intenţionat poate crea daune.

Aici intervine OAuth. Procesul de obţinere de informaţii de la Flickr fără a dezvălui parola pe alte site-uri decât Flickr.com este următorul

  1. Accesăm site-ul comsumatorului, în cazul nostru cel care ne va oferi servicii de printat fotografii.
  2. În cadrul acestuia vom găsi informatii cum că este dispus să preia imagini de pe site-ul flick.com
  3. Pentru a prelua aceste informatii vom fi redirectionati pe flickr.com
  4. Fiind pe flickr.com, deci într-un loc sigur, putem să ne autentificăm cu emailul şi cu parola persoanlă
  5. După autentificare flickr.com ne va redirecţiona înapoi pe site-ul de la care am pornit (cel de printare imagini) şi îi va furniza acestuia şi un jeton (token)
  6. Folosind acest jeton, site-ul de printare fotografii va face cereri in numele nostru catre flickr.com pentru a accesa fotografiile ce urmeaya a fi printate.

Jetonul este un cod unic, securizat, cu ajutorul acelui caruia se asigură faptul că se vor accesa doar imaginile din contul nostru şi pentru o perioadă finită de timp.

Vom arăta în următorul articol o implementare parctică a protocolului OAuth folosind un serviciu de microblogging. Vom crea o aplicaţie care interacţionează prin OAuth cu o platfomă externă pentru autentificare şi trimitere de mesaje.

 

În timp ce programatorii PHP încep să descarce si să testeze cea mai mare modifcare adusă limbajului de programare din ultimii 7 ani, mulţi se întreabă nu despre ce le-a adus Moş Crăciun în sacul lui plin de jucării pentru a le folosi în dezvoltarea aplicaţiilor de mâine ci despre modificările aduse în limbajul de programare care vor afecta aplicaţiile de ieri.

Vestea cea buna este că dacă v-aţi ţinut aplicaţia în ton cu modificările aduse limbajului sunt puţine modificări care vă vor afecta. Vestea cea rea este că pe măsura ce vă îndepărataţi de versiunea curentă portarea aplicatiilor devine din ce în ce mai grea.

Ceea ce urmează nu este o listă de noi funcţionalităţi din PHP 5.3, puteţi găsi destule referinţe bune in spaţiul web pentru informare. Acest articol este o sinteză a manualului de migrare la PHP 5.3. Vom acoperi acele subiecte care afectează continuitatea ramurii 5.x

Functii de procesare a vectorilor

Inainte de PHP 5.3, multe din funcţiile folosite la procesarea vectorilor puteau primi ca parametrii un obiect sau un vector. Din PHP 5.3 multe din aceste funcţii vor putea primi doar vectori. Daca doriţi accesarea unei proprietati din obiecte cu una din următoarele funcţii, va trebui să le convertiţi la array în prealabil.

Modificari în funcţiile magice

Înainte de 5.3 metodele magince puteau fi declarate cu orice modificator de vizibilitate.

  • __get()
  • __set()
  • __isset()
  • __unset()
  • __call()

Din PHP 5.3 aceste metode trebuiesc declarate publice, si nu pot fi statice.

Considerate depăşite

PHP are o lista de funcţii care au fost marcate pentru a fi eliminate din limbaj. Majoritatea acestor funcţii nu au o folosire comună, dar ar fi utilă o verificare a lor. Funcţiile marcate pentru eliminare sunt:

În plus, căteva din directivele php.ini au fost marcate pentru eliminare. Daca sunt activate ele vor emite un avertisment de nivel E_DEPRECATED:

  • define_syslog_variables
  • register_globals
  • register_long_arrays
  • safe_mode
  • magic_quotes_gpc
  • magic_quotes_runtime
  • magic_quotes_sybase

Cum orice emite un mesaj E_DEPRECATED va fi eliminat la urmatoarea versiune majora a limbajului, acestea sunt niste indicii pentru programatori la ce sa se uite pentru trecerea la PHP 6.

Reconsiderare

În PHP 5.0, funcţia is_a() a fost marcată pentru eliminare in favoarea operatorului instanceof, şi totuşi nu a fost eliminată din limbaj. În PHP 5.3, această decizie a fost reconsiderată, iar apelul către funcţia is_a() nu mai emite un mesaj E_DEPRECATED.

Cuvinte rezervate

Au fost adăugate două noi cuvinte rezervate

Având în vedere natura acestor cuvinte este puţin probabil ca ele să existe în codul provenit din versiunile vechi ale limbajului. Totuşi este o idee buna să va scanaţi codul pentru aceste cuvinte rezervate. Dacă există, ele vor genera erori de parsare. Nu pot fi folosite ca nume de funcţii, clase etc.

Concluzie

Ghidul migrarii către PHP 5.3 poate fi găsit în documentaţia de pe php.net. Nu sunt multe elemente din PHP 5.3 care să împiedice un cod bine scris in PHP 5.x sa funcţioneze în versiunea 5.3. Aceasta nouă versiune vine mai mult cu adaugiri la limbaj.

Multumiri

De cele mai multe ori se trece peste aprecierea si multumirea celor care au contribuit la dezvoltarea PHP-ului. Nu cred ca vom trece mai departe fără a face si noi acest lucru. Multumim întregii echipe care si-a dedicat timpul pentru crearea şi îmbunătăţirea PHP-uluiâ

Acest articol este o traducere a articolului Migrating to PHP 5.3.0 aparut in techPortal.

Cache-ul este o zonă temporară de stocare de informatii, care duplică niste date considerate originale, creat pentru acces direct si rapid asupra acestor date. Aceasta mapare a datelor faţă de locul lor de stocare iniţial, se face pentru  acele date care sunt greu de accesat în mod direct, care se afla în zone partajate, care ar avea un timp de prelucrare ridicat si care ar fi solicitate în mod frecvent, iar rezultatul ar fi de fiecare dată acelaşi.

Sisteme de cache se întalnesc implementate în microprocesoare(exemplul fiind cache-ul de nivel 2), în hard disk-uri(pentru a micşora timpul de citire al datelor), în sisteme de management al bazei de date(MySQL va ţine în cache rezultatul unei interogari, iar la primirea aceleiaşi interogări va returna rezultatul din cache, făra a interoga tabelele în sine), in browser-ele pe car ele folosim zi de zi(Firefox de exemplu îşi va face cache la imagini, ca la un refresh sa nu fie nevoit sa le preia de pe server din nou), chiar şi Google deţine propriul cache din care poate furniza conţinutul paginilor web.

Aşadar, scopul declarat al cache-ului este de a economisi timp.

Teoria Cache-ului

Există cateva concepte cheie în teoria cache-ului care trebuiesc respectate în implementarea unui astfel de sistem

  1. identificatorul unic – care va fi folosit la identificarea elementului în cache
  2. durata de viaţă – defineşte cât timp un element din cache va fi considerat valid
  3. preluarea condiţionată – astfel încat părţile din cod care ar accesa informaţiile originale să fie evitate, dar să si permită reîmprospătarea
  4. resetarea la cerere – pentru păstrarea consistenţei informaţiilor din cache cu cele din locaţia originală, este necesară posibilitatea ca la o modificare a datelor din această locaţie, cache-ul sa fie marcat ca invalid şi reconstruit

Astfel, un algoritm general de folosire a cache-urilor ar fi:

  • dacă elementul din cache cerut de aplicaţie există atunci el va fi returnat intocmai
  • dacă elementul din cache cerut nu există atunci datele acelui cache se vor aduce din locaţia originală, se va crea elementul corespunzător în cache, iar datele vor fi returnate aplicaţiei

Aplicare

Aşadar, vom presupune o apicaţie web, pentru care avem un număr mare de accesări atât din partea vizitatorilor dar şi din partea celor care administrează respectivul website. Pentru o şi mai buna exemplificare, vom considera cazul standard al unui magazin online, în care avem listări de categorii, listări de produse din fiecare categorie şi afisări detaliate de produse(preţ, descriere, detalii tehnice etc). În spatele site-ului, respectiv în aplicaţia de administrare a acestuia, avem un număr de operatori care lucrează necontenit la imbunătăţirea informaţiilor prezentate pe acel site. Mai mult decât atât, să mai luam în calcul existenţa unor aplicaţii care periodic sincronizează preţurile şi stocurile produselor cu cele existente la furnizorii direcţi. Pentru a îmbunătăţi imaginea de ansamblu să considerăm ca magazinul are câteva zeci de mii de produse. În cuvinte mult mai simple şi mai tehnice: o mulţime de interogări sql de tip insert, update dar mai ales select.

Dezavantajul unui astfel de scenariu este evident cel al supraîncărcării bazei de date cu interogări care de cele mai multe ori se vor repeta şi vor furniza acelaşi set de date. Cu toate că, de exemplu, MySQL deţine un cache propriu din care returnează un set de date al unei interogari la o repetare a acesteia, aplicaţia PHP care interogheză baza de date va trebui sa realizeze tot protocolul de comunicare, să furnizeze interogarea şi să primească datele, deci nişte timpi deşi mici, deloc de neglijat în contextul unui volum de trafic ridicat.

Continuând scenariul nostru de “groază” mai trebuie luat în considerare faptul ca la o interogare de tip insert sau update, cele cauzate de aplicaţia de administrare, pot apărea lock-uri pe câmpurile, înregistrările sau chiar tabelele din baza de date, deci până la terminarea execuţiei şi scrierea ori modificarea cu succes a datelor în bază, o instrucţiune select, nu va putea citi baza de date pentru preluarea informaţiilor şi va fi pusă în aşteptare până la terminarea tranzacţiei. Rezultă un timp mort şi mai mare. Cache-ul ar trebui sa intervină în astfel de momente, când putem spune ca majoritatea interogarilor vor furniza acelaşi set de date pentru perioade definite de timp,  iar rularea lor nu ne-ar aduce decât dezavantaje.

Interogând baza de date pentru a obţine informaţiile despre produsele din categoria “Monitoare LCD” vom obţine unset de date reprezentat printr-un vector cu produsele din acea categorie. Datele din acest vector pot fi introduse în cache. Conform algoritmului general descris mai sus putem scrie urmatorul cod PHP

if (!($data = loadFromCache('cache_for_category_id_' . $categoryId)))
{
    $data = loadDataFromDatabase($categoryId);
    saveToCache('cache_for_category_id_' . $categoryId, $data, 3600);
}

Plecăm astfel de la premisa ca informaţia căutată se afla în cache şi chiar încercăm să o preluăm. Dacă functia loadFromCache() va întoarce o valoare nulă atunci înseamnă ca datele nu se află în cache şi ele vor trebui aduse din baza de date, lucru ce se va face prin functia loadDataFromDatabase() iar apoi salvate în cache cu ajutorul funcţiei saveToCache(), cache valabil o ora. Chiar dacă presupunerea noastră iniţială referitoare la existenţa datelor în cache este adevărată sau nu, după executarea acestei porţiuni de cod vom avea în variabila $data informaţiile necesare.

Trebuie avut în vedere faptul că în tot acest timp datele considerate valide sunt cele din baza de date, cache-ul fiind doar o copie locala a acesteia. Deşi sistemul ne va reseta automat cache-ul după expirarea perioadei de viaţă, vor exista situaţii când cache-ul va deveni inconsistent, adică nu va mai reflecta realitatea din baza de date. Deci, la adăugarea unui produs nou în baza de date în categoria “Monitoare LCD”, cache-ul construit mai devreme nu mai este consistent(nu conţine si acest nou produs). Cum varianta în care aşteptăm trecerea celor 3600 de secunde pentru a se recrea cache-ul nu ne multumeşte(perioada putând fi mult mai mare), aplicaţia de administrare va trebui sa intervină asupra cache-ului şî să invalideze înregistrarea ce conţine datele din această categorie. În acest mod vom forţa recreerea cache-ului cu noile informaţii la următoarea accesare a categoriei respective.

resetCache('cache_for_category_id_' . $categoryId);

În tot scenariul de mai sus am considerat crearea de cache-uri pe categorii şi nu unul global care să conţină toate categoriile existente pe site, din considerente de acces si de resetare. Este mai simplu sa alegem direct cache-ul categoriei pe care dorim să o afişăm decât sa încărcăm toate categoriile prin care să o căutăm pe cea dorită, precum este mai normal ca la introducerea produsului nou în categorie să resetăm doar cache-ul categoriei respective şi nu cel al tuturor produselor.

Unelte

PHP nu deţine nativ funcţii de lucru cu cache-ul, însă există extensii PECL care pot fi instalate şi cu care se pot lucra, printre care enumerăm Memcache şi APC.

Folosirea extensiei Memcache:

$cache = new Memcache();
$cache->addServer('localhost');

if(!($data = $cache->get('cache_id'))
{
    $data = getData();
    $cache->add('cache_id', $data);
}
$cache->delete('cache_id');

Folosirea extensiei APC:

if(!($data = apc_fetch('cache_id'))
{
    $data = getData();
    apc_add('cache_id', $data);
}
apc_delete('cache_id');

Diferenţa dintre cele 2 extensii este aceea că Memcache va stoca informaţiile în memoria RAM a serverului, pe când APC le va stoca in fişiere pe hard disc.

UPDATE:

A Practical Guide to Data Caching with Zend Server scrisă de Shahar Evron, Product Manager la Zend Technologies, Inc., este o lucrare apărută recent şi pe care o recomandăm celor interesati de acest subiect.

Până la un subiect mai serios, care se află deja în scriere, vin cu o problemă, ce se adresează celor care se considera începători. Mi s-a întamplat sa dau această “problemă” unora de nivelul celui menţionat mai devreme, iar răspunsurile să  mă surprindă de fiecare dată.

Aşadar, dându-se un array (vector) cu 2 milioane de elemente, indexate corespunzător de la 0 la 1.999.999 cu valori de tip string, să zicem sub 1 KB ca dimensiune. 

Scrieţi o funcţie care să determine dacă vectorul în cauză este sau nu mulţime. Evident, se cere un timp de execuţie al funcţiei minim

Un vector este considerat mulţime dacă are toate valorile distincte

Citeam zilele tecute pe The PHP Benchmark un articol  care compară diversele metode prin care se poate parcurge un vector asociativ. În mod normal, un foreach simplu care ne-ar funriza cheia şi valoarea aflată la cel index ar fi de ajuns la fel şi soluţia unui for cu indice de la zero la numarul maxim de elemente, în cazul în care cheile vectorului ar fi numere întregi. Cu toate acestea însă, în cazul unui volum mare de date utilizarea unui foreach, uşoară şi comodă, poate duce la un timp de execuţie foarte mare

 
reset($aHash);
foreach($aHash as $key => $val)
{
    $aHash[$key] .= "a";
}

Exemplul de mai sus, are timp de executie 409 µs pe un vector de 100 de elemente.

 
$key = array_keys($aHash);
$size = sizeOf($key);
for ($i=0; $i<$size; $i++)
{
    $aHash[$key[$i]] .= "a";
}

Deşi cel de-al 2-lea exemplu presupune mai multe opreaţii decât primul, timpul de execuţie în conditiile în care furnizăm acelaşi vector de 100 de elemente este de 38 µs ... de 10 ori mai putin.

Adobe FLEX

In acest articol vom incerca sa va dam cateva idei despre ce reprezinta si mai ales despre ce poate face Adobe Flex.

Ce este Flash stie toata lumea. Il intalnim pe aproape orice pagina de web. Cat de bun sau folositor este nu face obiectul acestui articol insa avand in vedere ca Adobe raporteaza ca numarul de download-uri pentru aceasta aplicatie depaseste 10 milioane vom pleca de la premiza ca este folosit pe scara larga.

Ce este RIA sau mai bine zis ce intelegem prin RIA?. Rich Internet Application pleaca de la premiza ca orice activitate se executa fara refresh-ul paginii in browser. Utilizand tehnologia transferurilor asincrone de date catre si dinspre server sau mai bine zis AJAX aplicatii de acest fel au inceput a fi dezvoltate pe platforme precum Adobe Flash, Java FX sau Microsoft Silverlight simuland pana la un anumit nivel o aplicatie tip desktop.

FLEX SDK reprezinta un framework pentru dezvoltarea aplicatiilor Flash. Acest pachet este distribuit gratis si poate genera doua tipuri de aplicatii. Prima dintre ele se poate executa intr-o pagina html si este livrata sub forma unui fisier swf. Cea de-a doua genereaza un fisier air care in esenta lui este un installer a unei aplicatii ce ruleaza local pe orice fel de platforma cu ajutorul lui Adobe AIR. Tutoriale pentru acest produs se gasesc cam peste tot pe internet insa echipa InPHPweTrust le recomanda pe cele de pe site-ul producatorului.

De ce Adobe FLEX pe un site intitulat “In PHP we trust” ? Vom incerca o deductie scurta. Orice aplicatie medie fie desktop fie web comunica cu o baza de date sau cu un sistem de acumulare de date unificat. Acest sitem de cunostinte cu siguranta va avea nevoie de o aplicatie de management care sa ruleze pe server si care sa gestioneze datele rapid si eficient. In articolele urmatoare va vom prezenta solutii sau idei despre cum se pot construi aplicatiile PHP si FLEX pentru un mai bun “user experience”.

Vă doresc un an nou mai bun, sau cel puţin la fel de bun pe cât a fost 2008, plin de realizări şi, evident, cu mult cod.

La multi ani!

Una din primele întrebări pe care le adresăm unui programator începător dornic de cunoaştere aprofundată a programării web este: “Ce este web-ul?”. Deşi o întrebare cu un răspuns aparent simplu, rar ni se întamplă să primim un acelaşi răspuns. Pentru fiecare dintre noi web-ul înseamnă altceva, şi se referă întotdeauna la motivele pentru care îl utilizăm. Poate fi un mediu de socializat, un bun suport de promovare, de comunicare, de făcut marketing şi implicit bani. Pentru unii însă, web-ul este o reţea interconectată de calculatoare, de maşini care interacţionează între ele pe principiul client-server.

Arhitectura client-server este o arhitectură de bază în funcţionare internetului, şi neînţeleasă de la început, poate face ca aprofundarea programării web şi a conceptelor din aceasta sa fie foarte greu de înţeles şi poate duce la confuzii care pot avea efecte devastatoare asupra unor aplicaţii lansate publice.

Client

Clientul este partea din această arhitectură care va iniţia comunicarea. Este cel care va trimite o cerere către un server pentru a primi un răspuns, nişte informaţii peronalizate pentru cererea care tocmai a făcut-o, date in general. Clientul poate fi un browser de web care se conectează la un server web, poate fi un client de e-mail(gen Thunderbird sau Microsoft Outlook) care se conectează la un server de email, trimite datele de autentificare pentru un cont de e-mail si cererea de primire a mesajelor noi, poate fi un client de FTP(gen Smart FTP sau Cute FTP) care va trimite unui server de FTP cererea de stocare pe acel server a unui fişier, urmat de fişierul în sine şi va primi ca răspuns confirmarea primirii acestuia sau un mesaj de eroare corespunzător. Aşadar clientul este cel care va acţiona şi va determina un întreg lanţ de acţiuni din partea server-ului: iniţiază cererea către server, aşteaptă raspunsul de la server, primeşte răspunsul de la server şi în final îl returnează utilizatorului posibil într-un mod formatat.

Server

Un server, în general, este probabil cea mai pasivă, indolentă şi comodă invenţie de pe internet. Serverul nu are niciodată iniţiativă, principala lui activitate este de a sta şi de a aştepta. Serverul nu va acţiona niciodată din cont propriu, nu va transmite date decât dacă este întrebat şi decât dacă sunt urmate anumite reguli de comunicare. Cu toate acestea, odată “deranjat” un server va face tot posibilul să mulţumească cererea clientului. Când este pornit, un server va lua poziţia de aşteptare de conexiuni (numită mai tehnic: listening state), de regulă acesta ascultă pe un anume port primirea conexiunilor. La primirea unei astfel de conexiuni, deci implicit a unei cereri, el va face toate demersurile necesare pentru a returna rezultatul aşteptat. Dacă este un server web, va întoarce clientului (browserul web) codul html al paginii care a fost cerută, dacă este un server de e-mail va returna clientului o listă cu toate email-urile pe care le-a primit de la ultima cerere, daca este un server de MySQL va prelua interogarea SQL primită o va executa şi va returna setul de date rezultat. Aşadar un server stă şi aşteaptă conexiuni pe care le va servi cererile de îndată ce au fost primite.

Diferenţe

Nu trebuie înţeles că toate calculatoarele personale, laptopurile sau orice maşină folosită în mod frecvent de o fiinţă umană este un client şi restul sunt servere. Noţiunea de client-server este întâlnită între oricare două maşini între care exista o cale de acces, o conexiune directă sau indirectă. De exemplu, să presupunem ca avem o maşină pe care ţinem o bază de date MySQL, o maşină pe care o folosim ca web server (deci unde stocăm fişierele unui web site) şi o maşină care va fi folosită drept client. Astfel, iniţiem o cerere de la client către serverul web pentru a primi o anumită pagina în care vom vizualiza, de exemplu, produsele de pe un magazin online. Serverul web primeşte cererea şi va acţiona întocmai, însă datele necesare generării acestei pagini HTML care va conţine informaţiile despre produse, se află în baza de date, deci pe un alt server. Serverul web, prin intermediul librariilor din PHP va iniţia acum o cerere către serverul de MySQL pentru a întoarce informaţiile despre produse, pe care cel din urmă le va furniza. În această ultimă situaţie serverul web este client pentru serverul de MySQL pentru că el este cel care iniţiază o cerere de date iar baza de date va furniza raspunsul la această cerere. În final, cu datele primite se va furniza pagina HTML complet formatată.