di: Gabriele Farina 26 Ottobre 2006
La scorsa settimana mi sono occupato di introdurre il concetto di plugin in modo teorico e successivamente mostrare un'implementazione molto semplice per fornire alla propria applicazione la capacità di accettare plugin per estendere il framework. Scrivere applicazioni plugin-capable è ormai un'operazione che si può considerare di routine, quasi un vero e proprio obbligo se si desidera rendere pubblico e facilmente personalizzabile il proprio prodotto. In questo articolo cercherò invece di introdurvi alle metodologie utili per creare applicazioni in grado di esporre ad utenti esterni le proprie funzionalità.
Il concetto di API pubblica non è affatto una novità: il mondo dei Web service è nato e si è sviluppato al fine di promuovere e migliorare questo concetto; ormai è da considerarsi normale che un'applicazione di successo fornisca delle API per permettere ai propri utenti (o addirittura a chiunque) di sfruttare servizi più o meno complessi che una volta erano privati e fruibili solamente attraverso l'applicazione stessa. Basti pensare a Google o Flickr, che fanno delle API pubbliche uno degli strumenti più interessanti per chi desidera sfruttare questi immensi database di informazioni per i propri scopi, ludici o economici.
Con il tempo e con l'introduzione dei Web service sono andati ad affermarsi diversi protocolli di comunicazione che potremmo definire standard: tra questi possiamo ricordare SOAP, XML-RPC, XML e JSON; ognuno di questi standard ha i suoi pro ed i suoi contro, ed un servizio che punta ad essere il più cross platform possibile dovrebbe riuscire a comunicare sfruttando il maggior numero di protocolli implementabili. Spesso però non si hanno di queste esigenze, oppure si opta per soluzioni più semplici ma allo stesso tempo vincenti: JSON è un protocollo di comunicazione che è nato (in realtà si è affermato) successivamente alla prorompente entrata nel mondo dello sviluppo web della tecnologia AJAX, e pare essere una buona soluzione se si desidera far fruire le API ad applicazioni client; SOAP ed XML-RPC sono invece i due protocolli più utilizzati nell'implementazione dei webservice: a fronte di una maggior complessità, il primo dei due risulta molto più versatile e sta via via soppiantando il secondo; infine abbiamo tutta quella serie di soluzioni proprietarie che sfruttano chiamate XML semplici, header GET/POST o altre soluzioni simili.
Per l'esempio che andrò ad illustrare ho optato per l'utilizzo di XML semplice sia per le richieste sia per le risposte.
Come primo esempio illustrerò come implementare un semplice server capace di esporre delle API accessibili da chiunque voglia utilizzare le funzionalità esposte. Il protocollo di comunicazione utilizzato sarà XML sia per le richieste che per le risposte.
<?php
abstract class Service
{
abstract public function getServicesSignatures();
}
final class Type
{
const INT = 'int';
const STRING = 'string';
}
class ServiceServer
{
public $service_dir;
public function __construct($service_dir)
{
$this->service_dir = $service_dir;
}
public function run()
{
$data = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : file_get_contents("php://input");
if(!$data || strlen($data) == 0)
{
$this->dumpServices();
return false;
}
try
{
$dom = new DomDocument();
$dom->loadXML($data);
$method = $dom->firstChild->nodeName;
$arguments = array();
foreach($dom->firstChild->childNodes as $child)
{
$arguments[$child->nodeName] = $child->textContent;
}
list($service, $method) = explode('.', $method);
$service = $this->loadService($service);
$signatures = $service->getServicesSignatures();
if(!isset($signatures[$method]))
$this->reportError("Metodo ".$method." non implementato dal servizio ".$service);
$signature = $signatures[$method];
foreach($arguments as $key => $value)
{
switch($signature[$key])
{
case Type::INT:
$value = intval($value);
break;
}
$arguments[$key] = $value;
}
$result = call_user_func_array(array($service, $method), $arguments);
$this->reportResult("<Std.Result>".$result."</Std.Result>");
}catch(Exception $e)
{
$this->reportError($e->__toString());
}
return true;
}
private function dumpServices()
{
echo "<h2>Lista dei servizi disponibili</h2><ul>";
foreach($this->listServices() as $service => $signatures)
{
foreach($signatures as $method => $signature)
{
$params = array();
foreach($signature as $name => $type)
$params[] = $type." ".$name;
echo "<li>".$service.".".$method."(".implode(", ", $params).")</li>";
}
}
echo "</ul>";
}
private function reportError($e)
{
$xml = array("Std.Error", $e);
$this->reportResult($xml);
}
private function reportResult($result)
{
header('Content-Type', 'text/xml');
header('Content-Length', strlen($result));
echo $result;
}
public function listServices()
{
$services = array();
foreach(glob($this->service_dir."/*.php") as $service_file)
{
$service_name = basename($service_file, ".php");
$service = $this->loadService($service_name);
$services[$service_name] = $service->getServicesSignatures();
}
return $services;
}
public function loadService($name)
{
$service_file = $this->service_dir."/".$name.".php";
if(!file_exists($service_file))
throw Exception("Il servizio ".$name." non può essere caricato!");
require_once $service_file;
if(!class_exists($name))
throw Exception("Il servizio caricato (".$name.") non può essere instanziato!");
return new $name;
}
}
$server = new ServiceServer(dirname(__FILE__).'/services');
$server->run();
?>
Abbiamo definito tre classi che abbiamo evidenziato in rosso nel codice:
getServicesSignatures che si occuperà di restituire una array multidimensionale contenente la definizione dei metodi esposti dal servizio;Std.Error contenente la stringa di errore; nel caso di risultato corretto restituiremo Std.Result;Il sistema scelto è molto semplice ma flessibile; non sono stati implementati tutti i controlli possibili e le ottimizzazioni, ma il sistema è ben strutturato ed facilmente utilizzabile.
Guida Yii FrameworkCome creare applicazioni Web in modo semplice e veloce con il... |
Guida Zend FrameworkDiventate professionisti dello sviluppo Web. Zend Framework è lo... |
Guida Applicazioni Facebook con PHPCome realizzare un'applicazione per Facebook. Dalle basi della... |
Ogni lunedì, direttamente nella tua e-mail: script, articoli, guide e tutorial su PHP, MySQL e Apache.
Iscriviti alla newsletter
|
|
Corso PHP per Webmaster11 Giugno 2012 a Milano |
|
|
Corso Google AdWords Base25 Giugno 2012 a Milano |
|
|
Corso Google AdWords Base05 Giugno 2012 a Roma |