Learn with our tutorials and training

developerWorks provides tutorials, articles and other
technical resources to help you grow your development skills
on a wide variety of topics and products. Learn about a specific
product or take a course and get certified. So, what do you want to learn
about?

Featured products

Featured destinations

Find a community and connect

Learn from the experts and share with other developers in one of our
dev centers. Ask questions and get answers with dW answers. Search for local events
in your area. All in developerWorks communities.

Create REST applications with the Slim micro-framework

Until a few years ago, using a framework to develop PHP applications was
the exception rather than the rule. Today, frameworks like CakePHP, Symfony, CodeIgniter, and Zend Framework are widely used
for PHP application development, each one offers unique features to
streamline and simplify application development.

Often a full-fledged framework is overkill. Think about prototyping a web
application, creating a quick and dirty CRUD front end, or deploying a
basic REST API. You can do all these tasks with a traditional framework,
but the time and effort involved in learning and using it often outweigh
the benefits. Now consider micro-frameworks which enable rapid web
application development and prototyping without the performance overhead
and learning curve of full-fledged frameworks.

In this article, I introduce you to Slim, a PHP micro-framework that's
designed for rapid development of web applications and APIs. Don't be
fooled by the name: Slim comes with a sophisticated URL router and support
for page templates, flash messages, encrypted cookies, and middleware.
It's also extremely easy to understand and use, and it comes with great
documentation and an enthusiastic developer community.

This article walks you through the process of building a simple REST API
with Slim. In addition to explaining how to implement the four basic REST
methods with Slim's URL router, it demonstrates how Slim's unique features
make it easy to add advanced features such as API authentication and
multi-format support.

Understanding REST

First, refresh your understanding of REST, otherwise known as
Representational State Transfer. REST differs from SOAP in that it is
based on resources and actions, rather than on methods and data types. A
resource is simply a URL referencing the object or entity on which you
want to perform the actions—for example, /users or
/photos—and an action is one of the four HTTP verbs:

GET (retrieve)

POST (create)

PUT (update)

DELETE (remove)

To better understand this, consider a simple example. Suppose that you have
a file-sharing application and you need API methods for developers to
remotely add new files to, or retrieve existing files from, the
application data store. Under the REST approach, you expose a URL
endpoint, such as /files, and examine the HTTP method used to access this
URL to understand the action required. For example, you POST an HTTP
packet to /files to create a new file, or send a request to GET /files to
retrieve a list of available files.

This approach is much easier to understand, as it maps existing HTTP verbs
to CRUD operations. It also consumes fewer resources, because no formal
definition of data types of request/response headers is needed.

The typical REST conventions for URL requests, and what they mean, are:

GET /items: Retrieve a list of items

GET /items/123: Retrieve item 123

POST /items: Create a new item

PUT /items/123: Update item 123

DELETE /items/123: Remove item 123

Slim's URL router helps developers "map resource URIs to callback functions
for specific HTTP request methods," such as GET, POST, PUT, and DELETE.
Defining a new REST API is simple as defining these callback methods and
filling them with appropriate code. And that's exactly what you will do in
the rest of this article.

Creating the application
stub

Before you start to implement a REST API, let's review a few notes.
Throughout this article, I assume that you have a working development
environment (Apache, PHP, and MySQL) and that you're familiar with the
basics of SQL and XML. I also assume that your Apache web server is
configured to support virtual hosting, URL rewriting, and PUT and DELETE
requests.

A REST API is designed to work with resources. The resources in this case
are articles, each of which has a title, a URL, a date, and a unique
identifier. These resources are stored in a MySQL database, and the
example REST API developed in this article allows developers to retrieve,
add, delete, and update these articles using normal REST conventions. The
majority of this article assumes JSON request and response bodies. Fore
more information on how to handle XML requests and responses, see Supporting multiple-response formats later
in the article.

Step 1: Create the application
directory structure

Change to the web server's document root directory (typically
/usr/local/apache/htdocs on Linux® or c:\Program Files\Apache\htdocs
on Windows®) and create a new subdirectory for the application. Name
this directory slim/.

shell> cd /usr/local/apache/htdocs
shell> mkdir slim

This directory is referenced throughout this article as $SLIM_ROOT.

Step 2: Download the Slim
framework

Next, you need to add Slim. If you're using Composer, the PHP dependency
manager, simply create a file at $SLIM_ROOT/composer.json with the
following content:

{
"require": {
"slim/slim": "2.*"
}
}

Install Slim using Composer with the command:

shell> php composer.phar install

To load Slim, add this line to your application's 'index.php' file:

<?php
require 'vendor/autoload.php';

If you do not use Composer, you can manually download the Slim framework
and
extract the Slim directory within the download archive to a directory in
your PHP include path, or to $SLIM_ROOT/Slim.

Step 3: Initialize the example
database

The next step is to initialize the application database. Create a new MySQL
table named "articles" to hold article records:

To interact with this MySQL database, you should also download RedBeanPHP,
a low-footprint ORM library. This library is available as a single file
that you can easily include in a PHP script. Remember to copy the file to
a directory in your PHP include path or to $SLIM_ROOT/RedBean.

Step 4: Define a virtual
host

To make it easier to access the application, define a new virtual host and
set it to the working directory. This optional step is recommended
especially when you work on a development machine that holds multiple
in-progress applications as it creates a closer replica of the target
deployment environment. Slim also comes with a ".htaccess" file that lets
you use pretty URLs that remove the "index.php" prefix.

To set up a named virtual host for the application, open the Apache
configuration file (httpd.conf or httpd-vhosts.conf) and add these lines
to it:

These lines define a new virtual host, http://example.localhost/, whose
document root corresponds to the $SLIM_ROOT. Restart the web server to
activate these new settings. Note that you might need to update your
network's local DNS server to tell it know about the new host.

After you complete this task, point your browser to
http://example.localhost/, and you should see something like Figure 1.

Figure 1. The default Slim application
index page

Handling GET requests

Slim works by defining router callbacks for HTTP methods and endpoints
simply by calling the corresponding method—get() for GET requests,
post() for POST requests, and so on—and passing the URL route to be
matched as the first argument of the method. The final argument to the
method is a function, which specifies the actions to take when the route
is matched to an incoming request.

The typical REST API supports two types of GET requests, the first for a
list of resources (GET /articles) and the second for a specific resource
(GET /articles/123). Let's begin by writing code to handle the first
scenario: responding to a GET /articles request with a list of all
available article records.

Listing 1 loads the Slim and RedBean classes. It also
registers the Slim auto-loader to ensure that other Slim classes are
loaded as needed (remember that you don't need to require Slim or to
register its auto-loader if you use Composer). Next, RedBeanPHP's
R::setup() method opens a connection to the MySQL database by
passing it the appropriate credentials, and its R::freeze()
method locks the database schema against any RedBean-propagated changes.
Finally, a new Slim application object is initialized; this object serves
as the primary control point for defining router callbacks.

The next step is to define router callbacks for HTTP methods and endpoints.
Listing 1 calls the application object's
get() method and passes it the URL route '/articles as its
first argument. The anonymous function passed as the final argument
usesRedBeanPHP's R::find() method to retrieve all the records
from the 'articles' database table, turn them into a PHP array with the
R::exportAll() method, and return the array to the caller as
a JSON-encoded response body. Slim's response object also exposes a
header() method that you can use to set any response header;
in Listing 1, the header() method sets the
'Content-Type' header so the client knows to interpret it as a JSON
response.

Figure 2. A GET request and response in
JSON format

Note that you can use the $app->contentType() helper method
instead of the header() method to directly access the
request's content type, or treat Slim's response object as an array (it
implements the ArrayAccess interface) and set headers.

Similarly, you can handle GET requests for specific resources, by adding a
callback for the URL route "/articles/:id". Listing 2
illustrates.

Slim can automatically extract route parameters from URLs. Listing 2 illustrates this as it extracts the :id parameter from
the GET request and passes it to the callback function. Within the
function, the R::findOne() method retrieves the corresponding
resource record. This record is returned to the client as a JSON-encoded
response body. In the event that the provided identifier is invalid and no
matching resource is found, the callback function throws a custom
ResourceNotFoundException. The try- catch block then converts this into a
404 Not Found server response.

Figure 3. A GET request and response
(successful) in JSON format

Figure 4. A GET request and response
(unsuccessful) in JSON format

Slim also supports route conditions, allowing developers to specify regular
expressions for route parameters. If these parameters are not met, the
route callback will not execute. You can specify conditions on a per-route
basis, as shown in thus code:

A Slim application also exposes a notFound() method, which you
can use to define custom code for scenarios where no route match is
possible. This method is an alternative to throwing a custom exception and
manually setting the 404 server response code.

Handling POST requests

Handling POST requests is a little more involved. The usual REST convention
is that a POST request creates a new resource, with the request body
containing all the necessary inputs (in this case, the author and title)
for the resource. The callback function needs to decode the request body,
convert it into a RedBean object, and save it to the database, then return
a JSON-encoded representation of the newly-created resource.

In Listing 3, the callback function retrieves the body of
the request (which is assumed to be a JSON-encoded packet) using the
request object's getBody() and converts it to a PHP object using the
json_decode() function. Next, a new RedBeanPHP article object is
initialized with the properties from the PHP object and stored in the
database using the R::store() method. The new object is then
exported to an array and returned to the client as a JSON packet.

Figure 5. A POST request and response in
JSON format

Handling PUT and DELETE
requests

PUT requests are used to indicate a modification to an existing resource
and, as such, include a resource identifier in the request string. A
successful PUT implies that the existing resource has been replaced with
the resource specified in the PUT request body. The response to a
successful PUT can be either status code 200 (OK), with the response body
containing a representation of the updated resource, or status code 204
(No Content) with an empty response body.

In Listing 4, the identifier provided as part of the URL
route is used to retrieve the corresponding resource from the database as
a RedBeanPHP object. The body of the request (which is assumed to be a
JSON-encoded packet) is converted to a PHP object with the json_decode()
function, and its properties are used to overwrite the properties of the
RedBeanPHP object. The modified object is then saved back to the database,
and a JSON representation returned to the caller with a 200 server
response code. In the event that the identifier provided does not match an
existing resource, a custom exception is thrown and a 404 server error
response sent back to the client.

As with Listing 4, Listing 5 uses the
resource identifier from the URL to retrieve the corresponding resource
from the database as an object and then uses the R::trash()
method to permanently delete it. The response to a successful DELETE
request can be a 200 (OK) with the status in the response body or a 204
(No Content) with an empty response body; Listing 5 does
the latter.

Figure 7. A DELETE request and response in
JSON format

Adding authentication with route
middleware

In addition to flexible routing, Slim also supports so-called "route
middleware." In its simplest terms, middleware is one or more custom
functions that are invoked before the callback function for the
corresponding route. Middleware makes it possible to perform custom
processing on a request; as an Ephemera blog post on "Rack Middleware Use
Case Examples" explains, middleware "...can manipulate the request, the
response, it can halt the processing altogether, or do completely
unrelated stuff, like logging."

To illustrate how Slim's middleware architecture works, consider a common
API requirement: authentication. With middleware, it's possible to ensure
that API requests are properly authenticated, by passing the request
through a custom authentication method before allowing them to be
processed.

Listing 6 illustrates a simple authentication function
and adds this function as middleware to the GET request handler:

The example authenticate() function in Listing 6 is
referenced from within the GET route handler as middleware, and it
automatically receives the route object as an argument. It can also access
the Slim application object using the Slim::getInstance()
method.

Now, when the user requests the URL "/articles", the authenticate()
function will be invoked before the request is processed. This function
uses a custom validation routine to authenticate the request and allows
further processing only if the validation routine returns true. In case
validation fails, the authenticate() function halts processing and sends
the client a 401 Authorization Required response.

Listing 7 provides a more concrete example of how you
might implement the authenticate() method, by checking for
cookies with the user identifier "demo" and API key "demo." These cookies
are set by calling a new API method, /demo, that allows a
client temporary access to the API for a five-minute period.

In this case, the client first sends a request to the API endpoint '/demo",
which sets encrypted cookies with the "demo" credentials valid for five
minutes. These cookies will automatically accompany any subsequent request
to the same host. The authenticate() middleware function, which is
attached to the GET handler for the "/articles" URL endpoint, checks each
incoming GET request for these cookies. It then uses the validateUserKey()
function to verify the user's credentials (in this example, simply by
checking for the value "demo"). After the cookies expire, the
validateUserKey() function returns false, and the authenticate()
middleware terminates the request and disallows access to the "/article"
URL endpoint with a 401 server error.

Figure 8. An unauthenticated GET request
and response in JSON format

The accompanying code archive uses this same middleware to authenticate
POST, PUT, and DELETE requests. For obvious reasons, this system isn't
particularly secure and should never be used in a production environment;
it's included in this article purely for illustrative purposes.

As this example illustrates, route middleware comes in handy to perform
request pre-processing; it can also be used for other tasks, such as
request filtering or logging.

Supporting
multiple-response formats

The previous sections illustrated how to set up a simple JSON-based REST
API. XML is also a popular data-exchange format, and it's often necessary
to support XML request and response bodies in your REST API.

Slim provides full access to request headers. One of the easiest ways to
meet this requirement is to use the "Content-Type" request header to
determine the data format used in the transaction. Consider
Listing 8, which revises the callback function for GET requests to
return either XML or JSON response bodies, based on the "Content-Type"
header:

Listing 8. The handler for GET requests,
with XML and JSON format
support

In Listing 9, depending on whether the content type is
"application/json" or "application/xml," the request body is converted to
a PHP object using json_decode() or simplexml_load_string(). The object is
then saved to the database and a JSON or XML representation of the
resource is returned to the client. Figure 10
illustrates the XML POST request and response:

Figure 10. A POST request and response in
XML format

Conclusion

Slim provides a powerful, extensible framework for building a REST API,
making it easy for application developers to allow third-party access to
application functions using an intuitive architectural pattern. Slim's URL
matching and routing capabilities, coupled with its lightweight API and
support for various HTTP methods, make it ideal for rapid API prototyping
and implementation.

See Downloads for all the code implemented in this
article, together with a simple jQuery-based test script that you can use
to perform GET, POST, PUT and DELETE requests on the example API. I
recommend that you get the code, start playing with it, and maybe try to
add new things to it. I guarantee you won't break anything, and it will
definitely add to your learning.