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. |
---|