[256] | 1 | PHP library/framework for building Web apps while respecting the 5 principles |
---|
| 2 | of RESTful design. |
---|
| 3 | |
---|
| 4 | * Give every "thing" an ID (aka URIs) |
---|
| 5 | * Link things together (HATEOAS) |
---|
| 6 | * Use standard methods (aka the standard interface) |
---|
| 7 | * Resources with multiple representations (aka standard document formats) |
---|
| 8 | * Communicate statelessly |
---|
| 9 | |
---|
| 10 | [See the Tonic site for more info](http://peej.github.com/tonic/). |
---|
| 11 | |
---|
| 12 | |
---|
| 13 | How it works |
---|
| 14 | ============ |
---|
| 15 | |
---|
| 16 | Everything is a resource, and a resource is defined as a PHP class. An annotation |
---|
| 17 | wires a URI (or a collection of URIs) to the resource, and methods that match |
---|
| 18 | the HTTP methods by name allow interaction with it. |
---|
| 19 | |
---|
| 20 | /** |
---|
| 21 | * This class defines an example resource that is wired into the URI /example |
---|
| 22 | * @uri /example |
---|
| 23 | */ |
---|
| 24 | class ExampleResource extends Resource { } |
---|
| 25 | |
---|
| 26 | The incoming HTTP request is turned into a list of negotiated URIs based on the |
---|
| 27 | accept request headers which can then be used to pick the best representation |
---|
| 28 | for the response. |
---|
| 29 | |
---|
| 30 | /** |
---|
| 31 | * This class defines an example resource that is wired into the URI /example |
---|
| 32 | * @uri /example |
---|
| 33 | */ |
---|
| 34 | class ExampleResource extends Resource { |
---|
| 35 | |
---|
| 36 | function get($request) { |
---|
| 37 | |
---|
| 38 | $response = new Response($request); |
---|
| 39 | |
---|
| 40 | $response->code = Response::OK; |
---|
| 41 | $response->body = 'Example response'; |
---|
| 42 | |
---|
| 43 | return $response; |
---|
| 44 | |
---|
| 45 | } |
---|
| 46 | |
---|
| 47 | } |
---|
| 48 | |
---|
| 49 | |
---|
| 50 | How to get started |
---|
| 51 | ================== |
---|
| 52 | |
---|
| 53 | The best place to get started is to get the hello world example running on your |
---|
| 54 | system, to do this you will need a web server running PHP5.1+. |
---|
| 55 | |
---|
| 56 | Place all of the Tonic files into your PHP include path so that other scripts can |
---|
| 57 | find it. By default on Windows this will probably be in "c:\php\includes\tonic" or |
---|
| 58 | on Linux/Unix it will be "/usr/share/php/tonic" |
---|
| 59 | |
---|
| 60 | Copy "docroot/dispatch.php" into your servers document root and edit it so that the |
---|
| 61 | require_once statement paths point to the Tonic library and the examples. |
---|
| 62 | |
---|
| 63 | Finally you need to route all incoming requests to dispatch.php. How you do this |
---|
| 64 | depends on your web server. If you are using Apache, the simplest way is to copy |
---|
| 65 | the .htaccess file from "docroot/.htaccess" into your Apache document root. |
---|
| 66 | |
---|
| 67 | |
---|
| 68 | Features |
---|
| 69 | ======== |
---|
| 70 | |
---|
| 71 | |
---|
| 72 | Request URI |
---|
| 73 | ----------- |
---|
| 74 | |
---|
| 75 | The URI that is processed for the request when you create the Tonic Request object |
---|
| 76 | is gather by default from the REQUEST_URI Apache variable. If you need to gather |
---|
| 77 | the URI from another $_SERVER variable or somewhere else then you can pass it into |
---|
| 78 | the Request objects constructor as a configuration option: |
---|
| 79 | |
---|
| 80 | $request = new Request(array( |
---|
| 81 | 'uri' => $_SERVER['PATH_INFO'] |
---|
| 82 | )); |
---|
| 83 | |
---|
| 84 | |
---|
| 85 | Base URI |
---|
| 86 | -------- |
---|
| 87 | |
---|
| 88 | If you want to put your Tonic dispatcher at a URL that isn't the root of a domain |
---|
| 89 | then you'll need to let the Request object know so that the @uri annotations ignore |
---|
| 90 | it: |
---|
| 91 | |
---|
| 92 | $request = new Request(array( |
---|
| 93 | 'baseUri' => '/some/base/uri' |
---|
| 94 | )); |
---|
| 95 | |
---|
| 96 | Don't put a trailing slash on the end. |
---|
| 97 | |
---|
| 98 | |
---|
| 99 | URI annotations |
---|
| 100 | --------------- |
---|
| 101 | |
---|
| 102 | Resources are attached to their URL by their @uri annotation: |
---|
| 103 | |
---|
| 104 | /** |
---|
| 105 | * @uri /example |
---|
| 106 | */ |
---|
| 107 | class ExampleResource extends Resource { } |
---|
| 108 | |
---|
| 109 | As well as a straight forward URI string, you can also use a regular expression |
---|
| 110 | so that a resource is tied to a range of URIs: |
---|
| 111 | |
---|
| 112 | /** |
---|
| 113 | * @uri /example/([a-z]+) |
---|
| 114 | */ |
---|
| 115 | class ExampleResource extends Resource { |
---|
| 116 | function get($request, $parameter) { |
---|
| 117 | ... |
---|
| 118 | } |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | URL template and Rails route style @uri annotations are also supported: |
---|
| 122 | |
---|
| 123 | /** |
---|
| 124 | * @uri /users/{username} |
---|
| 125 | */ |
---|
| 126 | class ExampleResource extends Resource { |
---|
| 127 | function get($request, $username) { |
---|
| 128 | ... |
---|
| 129 | } |
---|
| 130 | } |
---|
| 131 | |
---|
| 132 | /** |
---|
| 133 | * @uri /users/:username |
---|
| 134 | */ |
---|
| 135 | class ExampleResource extends Resource { |
---|
| 136 | function get($request, $username) { |
---|
| 137 | ... |
---|
| 138 | } |
---|
| 139 | } |
---|
| 140 | |
---|
| 141 | It is also possible for multiple resource to match the same URI, so you can |
---|
| 142 | prioritise which resource should be used by specifying a priority level as part |
---|
| 143 | of the annotation: |
---|
| 144 | |
---|
| 145 | /** |
---|
| 146 | * @uri /example/([a-z]+) |
---|
| 147 | */ |
---|
| 148 | class ExampleResource extends Resource { } |
---|
| 149 | |
---|
| 150 | /** |
---|
| 151 | * @uri /example/apple 2 |
---|
| 152 | */ |
---|
| 153 | class ExampleResource extends Resource { } |
---|
| 154 | |
---|
| 155 | By postfixing the @uri annotation with a number, of all the matching resources, |
---|
| 156 | the one with the highest postfixed number will be used. |
---|
| 157 | |
---|
| 158 | |
---|
| 159 | Mimetypes |
---|
| 160 | --------- |
---|
| 161 | |
---|
| 162 | To handle content negotiation via filename style extensions to URLs as well the |
---|
| 163 | HTTP Accept header, a mapping between extensions and mimetypes can be provided. |
---|
| 164 | By default this list contains a number of common mappings, if you need to add one |
---|
| 165 | or more of your own, pass them into the constructor as an array: |
---|
| 166 | |
---|
| 167 | $request = new Request(array( |
---|
| 168 | 'mimetypes' => array( |
---|
| 169 | 'ogv' => 'video/ogg' |
---|
| 170 | ) |
---|
| 171 | )); |
---|
| 172 | |
---|
| 173 | |
---|
| 174 | Mount points |
---|
| 175 | ------------ |
---|
| 176 | |
---|
| 177 | To make resources more portable, it is possible to "mount" them into your URL-space |
---|
| 178 | by providing a namespace name to URL-space mapping. Every resource within that |
---|
| 179 | namespace will in effect have the URL-space prefixed to their @uri annotation. |
---|
| 180 | |
---|
| 181 | $request = new Request(array( |
---|
| 182 | 'mount' => array( |
---|
| 183 | 'namespaceName' => '/some/mounted/uri' |
---|
| 184 | ) |
---|
| 185 | )); |
---|
| 186 | |
---|
| 187 | Again, don't put a trailing slash on the end, and if you aren't using PHP5.3 and |
---|
| 188 | namespaces, you can use the @namespace annotation. |
---|
| 189 | |
---|
| 190 | |
---|
| 191 | Response exceptions |
---|
| 192 | ------------------- |
---|
| 193 | |
---|
| 194 | The Request object and Resource objects can throw ResponseExceptions when a problem |
---|
| 195 | occurs that the object does not want to handle and so relinquishes control back |
---|
| 196 | to the dispatcher. |
---|
| 197 | |
---|
| 198 | The ResponseException has its code value set to the HTTP response code of the problem |
---|
| 199 | and its message set to a human readable reason for throwing the exception. The |
---|
| 200 | ResponseException::response() method can be used to produce a default Response object |
---|
| 201 | expressing the exception if required. |
---|
| 202 | |
---|
| 203 | The Request object throws a 404 ResponseException when the resource to be loaded |
---|
| 204 | does not exist. |
---|
| 205 | |
---|
| 206 | The Resource object throws a 405 ResponseException when the HTTP method from the |
---|
| 207 | request is not able to be handled by the resource. |
---|
| 208 | |
---|
| 209 | If you don't want to handle a problem within your Resource class, you can throw your |
---|
| 210 | own ResponseException and handle it in the dispatcher. Look at the auth example for |
---|
| 211 | an example of how. |
---|
| 212 | |
---|
| 213 | |
---|
| 214 | Autoloading classes |
---|
| 215 | ------------------- |
---|
| 216 | |
---|
| 217 | If you've got lots of resource classes and don't fancy including them all in your |
---|
| 218 | dispatcher, you can use the autoload function to load a resource class for a given |
---|
| 219 | URL-space. |
---|
| 220 | |
---|
| 221 | $request = new Request(array( |
---|
| 222 | 'autoload' => array( |
---|
| 223 | '/example/[a-z]+' => 'ClassName' |
---|
| 224 | ) |
---|
| 225 | )); |
---|
| 226 | |
---|
| 227 | In this example, the class ClassName will be autoloaded via [the standard PHP |
---|
| 228 | __autoload method](http://php.net/manual/en/language.oop5.autoload.php). |
---|
| 229 | |
---|
| 230 | |
---|
| 231 | |
---|
| 232 | For more information, read the code. Start with the dispatcher "docroot/dispatch.php" |
---|
| 233 | and then the examples in the "examples" directory. |
---|