Restler 3 requereix PHP 5.3 o superior. Per versions anteriors de PHP utilitzeu Restler 2.
IMPORTANT: No utilitzeu git per fer la instal·lació. Si ho feu la instal·lació és més complexe. Vegeu Restler#Retorna_tot_el_rato_un_JSON_amb_error_404_not_found
Cal activar el mòdul d'Apache mod_rewrite:
$ sudo a2enmod rewrite $ sudo /etc/init.d/apache2 restart
A més es configuren els fitxer .htaccess i per tant cal estar atent al paràmetre:
AllowOverride
Per defecte sol esta a
AllowOverrride none
I cal que sigui:
AllowOverrride All
Per permetre que els fitxers .htaccess puguin sobrescriure qualsevol configuració d'Apache a nivell de directori.
Cal tenir en compte que els fitxers que ens proporciona Restler són:
$ cat .htaccess Options -MultiViews DirectoryIndex index.php <IfModule mod_rewrite.c> RewriteEngine On RewriteRule ^$ index.php [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </IfModule> <IfModule mod_php5.c> php_flag display_errors Off </IfModule>
i està pensat per a que l'API estigui a l'arrel. Si no és així (per exemple per tal d'executar els exemples) cal utilitzar RewriteBase:
$ cat .htaccess RewriteBase /restler/examples/_001_helloworld Options -MultiViews DirectoryIndex index.php <IfModule mod_rewrite.c> RewriteEngine On RewriteRule ^$ index.php [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </IfModule> <IfModule mod_php5.c> php_flag display_errors Off </IfModule>
Recursos:
Restler s'instal·la a:
/usr/share/restler
utilitzant:
https://codeload.github.com/Luracast/Restler/zip/v3rc3-Stable
La configuració d'Apache és:
$ cat /etc/apache2/conf.d/restler.conf # Include GOsa to your web service Alias /restler /usr/share/restler/public <Directory /usr/share/restler/public> AllowOverride All </Directory>
La web és accesible amb:
http://localhost/restler/
Per exemple, l'exemple 1, el Hello World:
http://localhost/restler/examples/_001_helloworld/readme.html http://localhost/restler/examples/_001_helloworld/say/hello
Els fitxers .htaccés es modifiquen per afegir RewriteBase:
$ cat .htaccess RewriteBase /restler/examples/_001_helloworld Options -MultiViews DirectoryIndex index.php <IfModule mod_rewrite.c> RewriteEngine On RewriteRule ^$ index.php [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </IfModule> <IfModule mod_php5.c> php_flag display_errors Off </IfModule>
Per defecte treballa amb Json, però es poden especificar altres formats com XML:
$r = new Restler(); $r->setSupportedFormats('JsonFormat', 'XmlFormat');
El primer format és el format per defecte quan el client no especifica res. Com pot el clients especificar el format desitjat?
Exemples extensions:
http://localhost/restler/examples/_003_multiformat/index.php/bmi.json -> JSON http://localhost/restler/examples/_003_multiformat/index.php/bmi.xml -> XML
Al utilitzar un navegador normalment es mostrarà el format XML per que és un dels formats demanats per defecte a les HTTP Accept Header per parts dels navegadors. En canvi en peticions AJAX o CURL retornarà JSON.
Fixeu-vos que només cal:
$ cat BMI.php
<?php class BMI { function index($height = 162.6, $weight = 84) { $result = new stdClass(); // 1 pound = 0.45359237 kilograms // 1 meter = 3.2808399 feet // 1 meter = 39.3700787 inches // 1 meter = 100 cms // assume height is given in centimeters $cm = $height; // assume weight is given in kilograms $kg = $weight; $meter = $cm / 100; $inches = $meter * 39.3700787; $feet = round($inches / 12); $inches = $inches % 12; $result->bmi = round($kg / ($meter * $meter), 2); $lb = round($kg / 0.45359237, 2); if ($result->bmi < 18.5) { $result->message = 'Underweight'; } elseif ($result->bmi <= 24.9) { $result->message = 'Normal weight'; } elseif ($result->bmi <= 29.9) { $result->message = 'Overweight'; } else { $result->message = 'Obesity'; } $result->metric = array( 'height' => "$cm centimeter", 'weight' => "$weight kilograms" ); $result->imperial = array( 'height' => "$feet feet $inches inches", 'weight' => "$lb pounds" ); return $result; } }
Observeu l'ús de classes PHP predefinides:
$result = new stdClass();
{[nota|el mètode és diu index per tal de que el rounting sigui automàtic. és a dir si no s'indica el mètode que es vol executar s'executa este}}
Recursos:
La clau està en el mètode:
$r->addAuthenticationClass('SimpleAuth');
On SimpleAuth és una classe que implementa l'autenticació. Un exemple molt bàsic:
$ cat SimpleAuth.php
<?php use Luracast\Restler\iAuthenticate; class SimpleAuth implements iAuthenticate { const KEY = 'rEsTlEr2'; function __isAllowed() { return isset($_GET['key']) && $_GET['key'] == SimpleAuth::KEY ? TRUE : FALSE; } function key() { return SimpleAuth::KEY; } }
Com podeu veure implementa la interfície iAuthenticate:
/usr/share/restler/vendor/Luracast/Restler$ cat iAuthenticate.php
<?php namespace Luracast\Restler; /** * Interface for creating authentication classes * * @category Framework * @package Restler * @subpackage auth * @author R.Arul Kumaran <[email protected]> * @copyright 2010 Luracast * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link http://luracast.com/products/restler/ * @version 3.0.0rc3 */ interface iAuthenticate extends iFilter { }
Que deriva de Ifilter:
<?php namespace Luracast\Restler;
/** * Interface for creating classes that perform authentication/access * verification * * @category Framework * @package Restler * @subpackage auth * @author R.Arul Kumaran <[email protected]> * @copyright 2010 Luracast * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link http://luracast.com/products/restler/ * @version 3.0.0rc3 */ interface iFilter { /** * Access verification method. * * API access will be denied when this method returns false * * @abstract * @return boolean true when api access is allowed false otherwise */ public function __isAllowed(); }
Els mètodes que estan protegits són els:
protected function restricted() { return 'protected method'; }
Amb un PHP Doc:
/** * @access protected */ function restricted2(){ return 'protected by comment'; }
O afegir:
Add @access protected comment to the class to protect all methods of that class
A totes les classes
Recursos:
Recursos:
Per fer funcionar l'exemple de fer un POST d'un objecte, cal posar
A la secció Target:
Al a secció Body:
{"name": "Anothersdadsadsa", "email": "[email protected]"}
i fer click al botó POST
Lo que en CURL seria:
$ curl -X POST http://localhost/restler/examples/_007_crud/index.php/authors -H "Content-Type: application/json" -d '{"name": "Anothersdadsadsa", "email": "[email protected]"}'
El explorador està molt bé no només com a documentació sinó que també permet provar les operacions GET, POST, PUT, etc.
Extret de: http://localhost/restler/examples/_007_crud/readme.html
Vegeu també PHP i Mysql
<?php /** * MySQL DB. All data is stored in data_pdo_mysql database * Create an empty MySQL database and set the dbname, username * and password below * * This class will create the table with sample data * automatically on first `get` or `get($id)` request */ use Luracast\Restler\RestException; class DB_PDO_MySQL { private $db; function __construct() { try { $options = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'); $this->db = new PDO( 'mysql:host=localhost;dbname=data_pdo_mysql', 'username', 'password', $options ); $this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); } catch (PDOException $e) { throw new RestException(501, 'MySQL: ' . $e->getMessage()); } } function get($id, $installTableOnFailure = FALSE) { $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); try { $sql = $this->db->prepare('SELECT * FROM authors WHERE id = :id'); $sql->execute(array(':id' => $id)); return $this->id2int($sql->fetch()); } catch (PDOException $e) { if (!$installTableOnFailure && $e->getCode() == '42S02') { $this->install(); return $this->get($id, TRUE); } throw new RestException(501, 'MySQL: ' . $e->getMessage()); } } function getAll($installTableOnFailure = FALSE) { $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); try { $stmt = $this->db->query('SELECT * FROM authors'); return $this->id2int($stmt->fetchAll()); } catch (PDOException $e) { if (!$installTableOnFailure && $e->getCode() == '42S02') { $this->install(); return $this->getAll(TRUE); } throw new RestException(501, 'MySQL: ' . $e->getMessage()); } } function insert($rec) { $sql = $this->db->prepare("INSERT INTO authors (name, email) VALUES (:name, :email)"); if (!$sql->execute(array(':name' => $rec['name'], ':email' => $rec['email']))) return FALSE; return $this->get($this->db->lastInsertId()); } function update($id, $rec) { $sql = $this->db->prepare("UPDATE authors SET name = :name, email = :email WHERE id = :id"); if (!$sql->execute(array(':id' => $id, ':name' => $rec['name'], ':email' => $rec['email']))) return FALSE; return $this->get($id); } function delete($id) { $r = $this->get($id); if (!$r || !$this->db->prepare('DELETE FROM authors WHERE id = ?')->execute(array($id))) return FALSE; return $r; } private function id2int($r) { if (is_array($r)) { if (isset($r['id'])) { $r['id'] = intval($r['id']); } else { foreach ($r as &$r0) { $r0['id'] = intval($r0['id']); } } } return $r; } private function install() { $this->db->exec( "CREATE TABLE authors ( id INT AUTO_INCREMENT PRIMARY KEY , name TEXT NOT NULL , email TEXT NOT NULL ) DEFAULT CHARSET=utf8;" ); $this->db->exec( "INSERT INTO authors (name, email) VALUES ('Jac Wright', '[email protected]'); INSERT INTO authors (name, email) VALUES ('Arul Kumaran', '[email protected]');" ); } }
Un exemple de com queda la documentació, basat en l'exemple 7 CRUD:
Un altre exemple és el exemple 8 que podeu trobar en local:
http://localhost/restler/examples/_008_documentation/explorer/index.html TODO????
Vegeu Swagger
Al crear l'objecte:
$r = new Restler();
Es pot crear amb les opcions:
$r = new Restler(true);
Per indicar que activem el cache o mode de producció. Per depurar pot ser útil:
$r = new Restler(true,true);
Per fer proves i que el fitxer routers.php es crei cada cop que es fa una petició
$ cat cache/routes.php
La carpeta cache ha de tenir permisos per ser escrita per l'usuari www-data:
/usr/share/restler1/public/examples/_001_helloworld$ ls -la total 60 drwxr-xr-x 3 www-data www-data 4096 mai 7 15:57 . drwxr-xr-x 15 root root 4096 mai 7 12:39 .. drwxr-xr-x 2 www-data www-data 4096 mai 7 15:46 cache -rwxr-xr-x 1 www-data www-data 292 mai 7 15:34 .htaccess -rwxr-xr-x 1 root root 354 mai 7 14:20 .htaccess~ -rwxr-xr-x 1 www-data www-data 308 mai 7 13:01 .htaccess.old -rwxr-xr-x 1 www-data www-data 845 mai 7 15:57 index.php -rwxr-xr-x 1 root root 854 mai 7 15:56 index.php~ -rw-r--r-- 1 www-data www-data 19208 mai 7 12:39 readme.html -rw-r--r-- 1 www-data www-data 3628 mai 7 12:39 readme.md -rwxr-xr-x 1 www-data www-data 120 mai 7 12:39 Say.php
NOTA: production mode writes human readable cache file for the routes in the cache directory by default. So make sure cache folder has necessary write permission.
Feu:
$ cd /usr/share/restler
i
$ sudo make composer-install
i
$ ls -la bin/ total 8 drwxr-xr-x 2 root root 4096 mai 7 16:43 . drwxr-xr-x 8 root root 4096 mai 7 16:43 .. lrwxrwxrwx 1 root root 31 mai 7 16:43 behat -> ../vendor/behat/behat/bin/behat
Permet fer tests de comportament de l'aplicació,primer:
$ sudo joe behat.yml
Canvieu la base_url. En el meu cas:
base_url: http://localhost/restler
i podeu executar un test exemple amb:
$ bin/behat features/examples/_001_helloworld.feature @example1 @helloworld Feature: Testing Helloworld Example Scenario: Saying Hello world # features/examples/_001_helloworld.feature:4 When I request "/examples/_001_helloworld/say/hello" # RestContext::iRequest() Then the response status code should be 200 # RestContext::theResponseStatusCodeShouldBe() And the response is JSON # RestContext::theResponseIsJson() And the type is "string" # RestContext::theTypeIs() And the value equals "Hello world!" # RestContext::theValueEquals() Scenario: Saying Hello Restler # features/examples/_001_helloworld.feature:11 Given that "to" is set to "Restler" # RestContext::thatItsStringPropertyIs() When I request "/examples/_001_helloworld/say/hello{?to}" # RestContext::iRequest() Then the response status code should be 200 # RestContext::theResponseStatusCodeShouldBe() And the response is JSON # RestContext::theResponseIsJson() And the type is "string" # RestContext::theTypeIs() And the value equals "Hello Restler!" # RestContext::theValueEquals() Scenario: Saying # features/examples/_001_helloworld.feature:19 When I request "/examples/_001_helloworld/say" # RestContext::iRequest() Then the response status code should be 404 # RestContext::theResponseStatusCodeShouldBe() And the response is JSON # RestContext::theResponseIsJson() And the type is "array" # RestContext::theTypeIs() Scenario: Saying Hi # features/examples/_001_helloworld.feature:25 When I request "/examples/_001_helloworld/say/hi" # RestContext::iRequest() Then the response status code should be 404 # RestContext::theResponseStatusCodeShouldBe() And the response is JSON # RestContext::theResponseIsJson() And the type is "array" # RestContext::theTypeIs() Scenario: Saying Hi Arul # features/examples/_001_helloworld.feature:31 Given that "to" is set to "Arul" # RestContext::thatItsStringPropertyIs() When I request "/examples/_001_helloworld/say/hi/{to}" # RestContext::iRequest() Then the response status code should be 200 # RestContext::theResponseStatusCodeShouldBe() And the response is JSON # RestContext::theResponseIsJson() And the type is "string" # RestContext::theTypeIs() And the value equals "Hi Arul!" # RestContext::theValueEquals() 5 escenarios (5 exitosos) 25 pasos (25 exitosos) 0m0.089s
I executeu els passos de l'apartat:
instal·lació
Vegeu Behat
En el meu cas el problema era utilitzar un git clone en comptes de baixar la versió estable de la rc 3.0. A l'arrel hi ha un gitignore i sembla que no s'ignoren certs fitxers com:
vendor/autoload.php
Cal descomentar la línia:
use Luracast\Restler\Restler;
Del fitxer index.php. Sinó dona errors:
$ sudo tail -f /var/log/apache2/error.log [Tue May 07 16:54:21 2013] [error] [client 127.0.0.1] PHP Warning: realpath() expects parameter 1 to be string, array given in /usr/share/restler/vendor/Luracast/Restler/AutoLoader.php on line 161, referer: http://localhost/restler/examples/_002_minimal/readme.html
Apareix l'error al mirar de consultar la documentació dels exemples en local:
http://localhost/restler/examples/_008_documentation/explorer/index.html
A la web de Restler el fitxer apareix ok:
http://restler3.luracast.com/examples/_008_documentation/resources.json { "apiVersion": "1", "swaggerVersion": "1.1", "basePath": "http://restler3.luracast.com/examples/_008_documentation", "apis": [ { "path": "/resources/authors-v1.{format}", "description": "" } ] }
Segons la documentació (https://github.com/Luracast/Restler-API-Explorer) l'error es degut a no afegir la línia:
$r->addAPIClass('Luracast\\Restler\\Resources'); //this creates resources.json at API Root
Quedant de la següent manera:
$ tail index.php
//$r = new Restler(); // comment the line above and uncomment the line below for production mode $r = new Restler(); $r->addAPIClass('Luracast\\Restler\\Resources'); //this creates resources.json at API Root $r->addAPIClass('improved\\Authors'); $r->addAPIClass('Resources');
Cal tenir en compte que no es tracta d'un fitxer que existeixi realment al sistema de fitxers. Aquest fitxer es crea automàticament per PHP (es pot veure pel navegador però no pel sistema de fitxers). Per que funcioni cal tenir correctament el .htaccess (és a dir amb el RewriteBase correcte). Això fa que la petició http://localhost/restler/examples/_008_documentation/resources.json s'envii al fitxer index.html qui dona la resposta resources.json de forma adequada:
/usr/share/restler/public/examples/_008_documentation$ cat .htaccess RewriteBase /restler/examples/_008_documentation DirectoryIndex index.php <IfModule mod_rewrite.c> RewriteEngine On RewriteRule ^$ index.php [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </IfModule> <IfModule mod_php5.c> php_flag display_errors Off </IfModule>
Recursos:
Apareix l'error al log d'Apache:
$ sudo tail -f /var/log/apache2/error.log [Wed May 08 11:24:56 2013] [error] [client 127.0.0.1] PHP Fatal error: Uncaught exception 'Exception' with message 'The cache directory `/usr/share/restler/public/examples/_008_documentation/cache` should exist with write permission.' in /usr/share/restler/vendor/Luracast/Restler/HumanReadableCache.php:128\nStack trace:\n#0 /usr/share/restler/vendor/Luracast/Restler/HumanReadableCache.php(75): Luracast\\Restler \\HumanReadableCache->throwException()\n#1 /usr/share/restler/vendor/Luracast/Restler/Restler.php(222): Luracast\\Restler\\HumanReadableCache->set('routes', Array)\n#2 [internal function]: Luracast\\Restler\\Restler->__destruct()\n#3 {main}\n thrown in /usr/share/restler/vendor/Luracast/Restler/HumanReadableCache.php on line 128
La solució es crear la carpeta amb permisos:
$ sudo mkdir cache $ sudo chown www-data:www-data cache
Això permet crear el fitxer routes.php:
$ head -n 30 cache/routes.php <?php $o = array(); // ** THIS IS AN AUTO GENERATED FILE. DO NOT EDIT MANUALLY ** //==================== GET ==================== $o['GET'] = array(); //==== GET v1/resources/{id}-v{version} ==== $o['GET']['v1/resources/{id}-v{version}'] = array ( 'className' => 'Resources', 'path' => 'v1/resources', 'methodName' => 'get', 'arguments' => array ( 'version' => 0, 'id' => 1, ), 'defaults' => array ( 0 => NULL, 1 => , ), 'metadata' => array ( 'description' => , 'longDescription' => , 'access' => 'hybrid', 'param' =>
; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's ; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok ; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting ; this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting ; of zero causes PHP to behave as before. Default is 1. You should fix your scripts ; to use SCRIPT_FILENAME rather than PATH_TRANSLATED. ; http://php.net/cgi.fix-pathinfo ;cgi.fix_pathinfo=1