RESTfooly PHP REST RESTfooly Peter Andreas Möller 14.09.2011 2011 Peter Andreas Möller 0.1 Download latest RESTfooly is a simple HTTP handler written in PHP. It writes, read, modify or delete custom objects using REST bindings. Use RESTfooly to prototype or implement Web services. By default custom objects are stored in a SQLite database. Customize RESTfooly to store objects in any manner, even in noSQL databases. RESTfooly serves several types of access management, including a clever session based access mechanism. If user based authentication is needed - it can be be done as well. RESTfooly is published under the GNU Public License V3. Installation RESTfooly needs a HTTP Server and PHP. To install RESTfooly do: $ wget https://restfooly.googlecode.com/files/restfooly-0.1.tar.gz $ tar xzvf restfooly-0.1.tar.gz If using Apache HTTP Server mod_rewrite must be enabled. To configure the Apache HTTP Server, when PHP is used as module, place the following to the end of your httpd.conf or inside a virtual host configuration anywhere. Alias /resources/ "C:/Users/pa/Documents/www/restfooly/" <Directory "C:/Users/pa/Documents/www/restfooly"> AllowOverride All AddType application/x-httpd-php .php </Directory> Keep in mind the rewrite rule in .htaccess-file within RESTfoolys base dir. The rule maps all requests to the handler. RewriteEngine on RewriteRule ^.*$ handler.php [L] Test RESTfooly includes unit tests. To execute the tests simpletest is needed. To download simpletest and execute the tests do: $ cd test $ wget http://sourceforge.net/projects/simpletest/files/simpletest/simpletest_1.1/simpletest_1.1alpha3.tar.gz/download $ tar xzvf simpletest_1.1alpha3.tar.gz $ php test.php Note: The unit tests try to reach RESTfooly at http://127.0.0.1:80/resources. Configuration RESTfooly is configured by inc/RESTfooly.inc. The documentation of the configuration paramters is given in this file. The following shows the default settings (server defaults). <?php /** * connect to database (path to sqlite database) * @var string */ $cfg['DB_CONNECT_STRING'] = 'include/RESTfooly.sqlite'; /** * remove this absolute(!) path from requested path within HTTP header * @var string */ $cfg['HTTP_BASE_URI'] = '/resources'; /** * inlcude error object into response body or not * @var boolean */ $cfg['ERROR_VERBOSE'] = true; /** * type of access control for any client * * * 0 create, read, update or delete any object (None) * * 1 read any object (Read Only) * * 2 read or create any object (Read Write) * * 3 read or create any object update or delete those created in this session (Session Based) * * 4 custom based access control (Custom) * * NOTE: The Session Based access control is more appropriate than the Read Write setting. * It allows clients to correct their own input until session end. * * @var integer */ $cfg['ACCESS_CONTROL'] = 0; /** * maximum number of rows within a result set * @var integer */ $cfg['DB_MAX_ROWS'] = 500; /** * maximum length of http param name and value * @var integer */ $cfg['DB_MAX_LENGTH'] = 2056; ?> Dataschema RESTfooly stores custom objects within a custom table within a SQLite database. Therefore a data schema must be created. The following example shows the given bindings for SQL data schemas. -- table name is plural!! (objects) CREATE TABLE annotations ( -- primary key is singular, suffix "_id" and integer!! (object_id) annotation_id integer primary key, classid integer not null, author varchar(64) not null, url varchar(512) not null, annotation text not null, -- optional automatically set on creation! created text, -- optional automatically set on update! updated text ); To load the data schema into a sqlite database do: $ cd bin $ php dbLoad.php -s schema.sqlite -d ../include/RESTfooly.sqlite To create a RESTfooly-conform SQL data schema for pumkin anyway do: $ php dbLoad.php -p annotation CREATE TABLE annotations ( annotation_id integer primary key, created text, updated text ); Interface RESTfooly covers some aspects of RESTful web services. There is a configurable base URI, a given Internet Media Type, JSON, and a set of operations using HTTP-Methods. RESTfoolys protocol may not hypermedia driven, as suspected, but it is useful in my opinion. The following interface description is given by HTTP-requests and responses. The JSON strings are formatted to increase the readability. Use HTTP GET-method to query members (objects) of a collection (with a given url) GET /resources/annotations?url=http://www.example.org/article2 HTTP/1.1 Host: 127.0.0.1 Connection: Close HTTP/1.1 200 OK Content-Length: 215 Connection: close Content-Type: application/json; charset="utf8" { "status":"ok", "data": [ { "annotation_id":"2", "classid":"1", "author":"user", "url":"http:\/\/www.example.org\/article2", "annotation":"this is an odanary comment to this article", "created":"2011-09-08", "updated":null } ] } Use HTTP GET-method to query a member of a collection with annotation_id = 1 GET /resources/annotations/1 HTTP/1.1 Host: 127.0.0.1 Connection: Close HTTP/1.1 200 OK Content-Length: 221 Connection: close Content-Type: application/json; charset="utf8" { "status":"ok", "data": [ { "annotation_id":"1", "classid":"2", "author":"you", "url":"http:\/\/www.example.com\/article", "annotation":"this is no odanary comment to this article", "created":"2011-09-08", "updated":"2011-09-08" } ] } Use HTTP POST-method to create a new member POST /resources/annotations HTTP/1.1 Host: 127.0.0.1 Content-Type: application/x-www-form-urlencoded Content-Length: 110 Connection: Close classid=1&author=user&url=http://www.example.org/article&annotation=this is an odanary comment to this article HTTP/1.1 201 Created Content-Length: 42 Connection: close Content-Type: application/json; charset="utf8" { "status":"ok", "data": { "annotation_id":3 } } Use HTTP PUT-method to update the member with annotation_id = 1 PUT /resources/annotations/1 HTTP/1.1 Host: 127.0.0.1 Content-Type: application/x-www-form-urlencoded Content-Length: 109 Connection: Close classid=2&author=you&url=http://www.example.com/article&annotation=this is no odanary comment to this article HTTP/1.1 200 OK Content-Length: 46 Connection: close Content-Type: application/json; charset="utf8" { "status":"ok", "data": { "annotation_id":"'1'" } } Use HTTP DELETE-method to delete member with annotation_id = 2 DELETE /resources/annotations/2 HTTP/1.1 Host: 127.0.0.1 Connection: Close HTTP/1.1 200 OK Content-Length: 44s Connection: close Content-Type: application/json; charset="utf8" { "status":"ok", "data": { "annotation_id":"2" } } Tutorial Setting ACCESS_CONTROL = 4 enables Custom Access Control. With this setting a User Authentication can be implemented. <?php require_once("RESTfooly.php"); class rfAuthen extends RESTfooly { /** * overwrite custom access control method */ protected function httpAccessControlCustom() { // start session session_start(); // user is loged in if (isset($_SESSION["login"])) { return; } // user tries login else if (isset($_SERVER["PHP_AUTH_USER"]) && isset($_SERVER["PHP_AUTH_PW"]) && $this->dbLoginValid(["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])) { $_SESSION["login"] = true; return; } // authentication required header('WWW-Authenticate: Basic realm="RESTfooly"'); header("HTTP/1.0 401 Unauthorized"); exit(1); } /** * check for login * @param string $login * @param string $passwd */ protected function dbLoginValid($login, $passwd) { return isset($this->dbGetOne("SELECT login FROM accounts WHERE login=%s AND passwd=%s", $this->dbEscape($login), $this->dbEscape(md5($passwd)))))?true:false; } }?> Roadmap RESTfooly shall include a pageing mechanism. The media type of responses shall be extended to more than JSON. HTML-representation shall be generated on the fly by XSLT. RESTfooly shall become a RESTful webservice. The default database shall become sqlite3. RESTfooly may extensible for JSON-Requests.