Einen kleinen XMLRPC Client und Server.
Mit der nächsten Framework Version offiziell verfügbar, inkl der angesprochenen ACL:)
Zur Authentifizierung benutze ich den Authorization-Header. Damit können Webservices/APIs mit individuellen Berechtigungen realisiert werden zB. Über eine ACL. Anwendungen lassen sich dadurch auch besser in Backend/Frontend unterteilen und können auch auf externe Resourcen ausgelagert werden oder eingebunden werden(erhöhte Skalierbarkeit).
Die Objekte die dem Server via setObject() übergeben werden, müssen folgende Konventionen befolgen:
* alle indizierbaren Methoden müssen public sein.
* Methoden müssen folgenden parameter haben: function test($method_name, $params, $user_data) {}
Damit bietet diese Lösung eine Authorisierung und ein einfaches RPC Verfahren an. Das ganze sollte über eine SSL Verbindung laufen, da Passwörter übertragen werden(bzw der HASH). Durch Session-Passwörter könnte auch eine unverschlüsselte Verbindung genutzt werden.
Über diesen kleinen HTACCESS kommt man auch wenn PHP im FastCGI Modus ist an die authorization ran, dabei legt man den Auth-Header auf eine von PHP auslesbare Variable um:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}]
</IfModule>
jetzt muss man nur noch im PHP die Variablen auslesen und umschreiben:
//HTTP Auth Credentials
if ((!$_SERVER['PHP_AUTH_USER'] || !$_SERVER['PHP_AUTH_PW'])) {
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode(substr($_SERVER["REMOTE_USER"], 6)));
}
XMLRPC Core Class:
/** (c) 2010 Arne "w13531" Wenzel [ mailto: w13531 (at) terrorhippiecrew (dot) net ] **/
class LIB_Class_XMLRPC {
}
XMLRPC-Client:
/** (c) 2010 Arne "w13531" Wenzel [ mailto: w13531 (at) terrorhippiecrew (dot) net ] **/
class LIB_Class_XMLRPC_Client extends LIB_Class_XMLRPC {
private $host = null;
private $port = null;
private $user = null;
private $password = null;
private $path = null;
private $timeout = null;
private $connection = null;
private $errno = null;
private $errstr = null;
public function __construct($host = '127.0.0.1', $port = '80', $path = '/', $timeout = '5') {
$this->host = $host;
$this->port = $port;
$this->path = $path;
$this->timeout = $timeout;
}
public function connect() {
if($this->connection == null) {
if($this->port == '443') {
$this->connection = fsockopen('ssl://' . $this->host, $this->port, $this->errno, $this->errstr, $this->timeout);
} else {
$this->connection = fsockopen($this->host, $this->port, $this->errno, $this->errstr, $this->timeout);
}
}
}
public function disconnect() {
if($this->connection !== null) {
fclose($this->connection);
$this->connection = null;
}
}
public function setCredentials($user, $password) {
$this->user = $user;
$this->password = $password;
}
public function call($request, $params) {
$this->connect();
$xml_req = xmlrpc_encode_request($request, $params);
$query = "POST " . $this->path . " HTTP/1.0\n"
. "User-Agent: RIAD-F\n"
. "Host: " . $this->host . "\n"
. "Content-Type: text/xml\n";
if(!is_null($this->user) && !is_null($this->password)) {
$query .= "Authorization: Basic " . base64_encode($this->user.":".$this->password) . "\n";
}
$query .= "Content-Length: " . strlen($xml_req) . "\n\n"
. $xml_req . "\n";
if (!fputs($this->connection, $query, strlen($query))) {
return 0;
}
$contents = '';
while (!feof($this->connection)) {
$contents .= fgets($this->connection);
}
$xml = substr($contents, strpos($contents, "\r\n\r\n")+4);
$data = xmlrpc_decode($xml);
return $data;
}
}
XMLRPC-Server:
/** (c) 2010 Arne "w13531" Wenzel [ mailto: w13531 (at) terrorhippiecrew (dot) net ] **/
class LIB_Class_XMLRPC_Server extends LIB_Class_XMLRPC {
private $server = null;
public function __construct() {}
public function createServer() {
if($this->server === null) {
$this->server = xmlrpc_server_create();
}
}
public function destroyServer() {
if($this->server !== null) {
xmlrpc_server_destroy($this->server);
$this->server = null;
}
}
public function setObject(&$Object, $ServiceName = null) {
$this->createServer();
if(!is_object($Object)) {
return false;
}
if($ServiceName === null) {
$ServiceName = get_class($Object);
}
$methods = get_class_methods($Object);
foreach($methods as $value) {
if(is_callable(array(&$Object, $value))) {
xmlrpc_server_register_method($this->server, $ServiceName . '.' . $value, array(&$Object, $value));
}
}
return true;
}
public function setFunction($FunctionName, $ServiceName = null) {
$this->createServer();
if(!is_string($FunctionName)) {
return false;
}
if($ServiceName === null) {
$ServiceName = 'func';
}
if(is_callable($FunctionName)) {
xmlrpc_server_register_method($this->server, $ServiceName . '.' . $FunctionName, $FunctionName);
}
}
public function handle($xmlData, $UserData = null) {
$this->createServer();
return xmlrpc_server_call_method($this->server, $xmlData, $UserData);
}
}
?>
Der ganze Spass wird zB so genutzt(Server):
/** (c) 2010 Arne "w13531" Wenzel [ mailto: w13531 (at) terrorhippiecrew (dot) net ] **/
class APP_Controller_Index extends FW_Controller {
public function index_action(array $params = null) {
//connection to DB
$db = LIB_Model_PDODB::getInstance(FW_Config::getInstance()->get('DB'));
//setting up xmlrpc
$xmlrpc_server = new LIB_Class_XMLRPC_Server();
//loding working class and hand over db
$news = LIB_Class_News::getInstance($db);
//register working class xmlrpc
$xmlrpc_server->setObject($news, 'news');
//get request
$request = file_get_contents('php://input');
//answer xmlrp calls
$response = $xmlrpc_server->handle($request);
//output response
$this->response->addContent($response);
}
}
Client:
/** (c) 2010 Arne "w13531" Wenzel [ mailto: w13531 (at) terrorhippiecrew (dot) net ] **/
class APP_Controller_Index extends FW_Controller {
public function index_action(array $params = null) {
$xmlrpc_client = new LIB_Class_XMLRPC_Client('127.0.0.1', '443', '/xmlrpc/');
$xmlrpc_client->setCredentials('anonymous', 'anonymous');
$this->response->addContent(print_r($xmlrpc_client->call('news.test', '6'), true));
}
}