Routing in RESTful API in PHP - php

I am a bit familiar with PHP MVC. Say, I have a controller like so:
class Customers{
public function method1( param1, param2, param3, ..., param_n ){
}
}
In my bootstraping page, I can grab a browser URL like so:
$url = explode('/', filter_var(rtrim( $_GET['url'], '/' ), FILTER_SANITIZE_URL));
I do $controller = $url[0] and $method = $url[1]. Any other elements in $url after the second index are parameters and can be collected into an array variable, say $params. Then I route to the relevant controller method and parameters like so:
call_user_func_array([$controller, $method], $params);
PLEASE NOTE: Above code is for illustration purposes. I always do checks in real-life situations. Those checks are not shown here, so do not use the above examples for serious projects.
Now, I want to implement a RESTful API using MVC pattern. What I already know:
No browser is involved, so $_GET['url'] is out of it.
The endpoint is obtained from $_SERVER['REQUEST_URI']
The method is obtained from $_SERVER['REQUEST_METHOD']
How do I route to an endpoint, for example, customers/{12345}/orders to get the orders of a particular customer with id 12345?
How can I do this?

The quickest way to achieve what you want would be to just use FastRoute. But where is fun in that :)
That said, I am a but confused about the premise of yours. Is it a REST API, which will be consumed by some other code (like in a phone or a 3rd party web app), or is it a proper website, where you just want to get pretty URLs?
Because if it's the former case, then making fancy URL parsing is completely pointless. Instead of messing round with URLs, you should be reading this article. Real REST API does not need a fancy URL parsing.
I will assume that what you are actually makingis a proper website, but with pretty URLs.
First you would have to implement a routing mechanism, which takes a list of regexp patterns, matches them agains your provided URL (which you could be extraction from $_GET['url'] or maybe $_SERVER[REQUEST_URI] (your code actually wouldn't care from where the URL was gathered ... you shouldn't be accessing superglobals inside functions/classes).
A simple version of this is explained in this answer. I am to lazy to rewrite it all =P
The second (and highly optional) part is creating code, that would take a human-readable route notation (like: /users/{id|[0-9]+}as an example) and turning it into the regular expression, which can be consumed by your routing mechanism.
If you decide to have the human-readable notations, then there are two major directions:
inline notations (see the example above or FastRoute)
config file (probably JSON or YAML) with notations
As for "how the end result might look like", you can probably look at the code sample here. That would illustrate one of the available option for the router's public interface.
TL;DR
Your question is vague and it is hard to understand what exactly would be helpful for you.

Related

Is it good to use ( $request->get('sth') ) instead of ( setting some parameters ) in controller function in Laravel

Is it OK to use
$id = $request->get('some_id');
instead of setting some parameters in Routes AND Controller like:
Route::get('some_page/{parameters}', 'controllerName#functionName');
function functionName($parameters)
{
$id = $parameters;
}
Appreciation
Of course it's good. When you're using GET, both ways are similar and if you like to use $request->get() for some reason, it's totally ok.
If you're using Form, it's the only right way. Plus, you can create custom Request class to use it for validation and other operations:
https://laravel.com/docs/master/validation#form-request-validation
They have two fundamentally different goals.
Using $request->get() is a way to retrieve a value from inside the php's REQUEST object regardless of its association with routing pattern you use.
Following HTTP's standards, you probably use $_GET to read some value without it changing the database [significantly] and you use $_POST to write data to you server.
While {pattern} in routing ONLY and ONLY should be used as a way for your application to locate something, some resource(s); in other words, its only goal is to help you route something in your server.
Nevertheless, in certain cases, such as /user/{id} the value of {id} might encounter some overlapping as to whether be treated as a route parameter or as a key of $_REQUEST.
Things such as tokens, filters criteria, sorting rules, referrers (when not significantly) etc. can be read right from $_REQUEST without interfering them into routing pattern of you application.

How to set up a single entry point architecture similar to Laravel's?

Despite my efforts to find a tutorial on how to set up a secure, single entry point architecture for a web application built with PHP, I've not been able to find a good one. Who knows, maybe my search queries were bad...
Looking at Laravel's code seemed like a good idea, but it's a good way to get your head spinning. There's just too much going on there for me to understand.
That being said, how would I go about for creating such an architecture that is both simple to apply to an app and secure (e.g. protect against local file inclusions) at the same time?
First of all, you need to redirect all your requests to a single PHP file. That part you do in .htaccess on Apache or it's counterparts on other servers.
Then you need to explore what data you can see in $_SERVER. It's quite common to use $_SERVER['PATH_INFO'], but the choice will depend on how exactly you rewrite the request.
Then you need to create a router, that has a list of regular expression and tries to match then against the URL fragment that you have acquired.
Here are few example that might give you some ideas:
'#^/(?P<page>[^/\\\\.,;?\n]+)$#'
'#^/user/(?P<id>[0-9]+)/(?P<nickname>[^/\.,;?\n]+)$#'
'#^(?:/test/(?P<parameter>[^/\\\\.,;?\n]+))?/mandatory$#'
It is common practice tho have these regular expressions generated from much simpler notations, but for the first iteration you should not focus on it too much.
Also, if you use expressions, that have optional fragments, you should also provide "fallback" values. These values would be used as defaults, if fragment is not provided, but pattern is matched.
The way I do it all looks like this in PHP:
/*
* Routing mechanism
*/
$uri = isset( $_SERVER[ 'PATH_INFO' ] )
? $_SERVER[ 'PATH_INFO' ]
: '/';
$builder = new RequestBuilder;
$request = $builder->create();
$request->setUri( $uri );
$router = new Router( new RouteBuilder );
$router->import(
$reader->getAsArray( __DIR__ . '/config/routes.json' )
);
$router->route( $request );
After this the $request variable contains an object, which then you can query for specific parameter using commands like $id = $request->getParameter('id') or $controller = $request->getParameter('controller').
If you do not mess up with patterns themselves, then the values, that you extract will be safe against unauthorized file inclusions.

RESTful PHP Frameworks that support Resources, Methods, and Formats

There seem to be a variety of PHP frameworks that claim RESTful design patterns.
I'm looking for a framework that does a very good job providing a solid solution to these three items (or their equivalence).
1. Resource Requests
The first thing to do is be able to handle URL to resource resolution.
/path/to/resource = controller.action()
2. Request Methods
The second thing is to handle different types of request methods.
GET /path/to/resource = controller.get()
POST /path/to/resource = controller.post()
Perhaps with a fallback to a universal handler if no request method matches.
GET /path/to/resource = controller.action()
PUT /path/to/resource = controller.action()
POST /path/to/resource = controller.action()
3. Response Formats
Last, I have seen people attach formats to the end of URL's to help the framework know what type of response is expected.
/path/to/resource.html
/path/to/resource.json
Other ways people pass the response format in the header or as a URI param (?format=json).
These are three items that have to be covered. However, they don't have to be handled the same way I just showed - those are just examples.
I have been researching for a similar framework, but there doesn't seem to be much going on in the PHP world. Here is a related question about PHP REST frameworks.
Recess looks interesting, and I found the new REST controllers and routers of the Zend Framework pretty useful. I also started implementing an easier approach on top of the Zend components. Basically you register a bunch of view renderers (HTML, JSON and a simple form of XML are supported out of the box, via accept header parsing or overwriting it with a format=? parameter) and body parsers (Web Foms, and JSON are enabled) and based on this interface:
interface Feathry_Rest_Resource
{
public function index($params = null);
public function get($id, $params = null);
public function post($data, $params = null);
public function put($data, $id = null, $params = null);
public function delete($id, $params = null);
}
Where each method returns a plain array or object (with a toArray method) you can create a RESTful resource.
The advantage is, that your resources are entirely decoupled from any Representation.
They don't even have to know that they are being used via HTTP as long as they follow
the interface.
It is still very alpha, and there didn't seem to be much interest in it, but it does work so maybe you want to give it a try.
Recess has an interesting approach using annotations to define the routing.
If you're using PHP 5.3, then Tonic is a very lightweight framework with a focus on REST. It also uses a similar style to Recess using docblocks for the routing.
Personally I use Zend Framework with a custom version of their Zend_Rest_Route.
Each of these also go some way to providing some handling of the response formats, allowing you to provide content negotiation based not only on the method you describe, but by using the Accept header field to determine which formats the client will understand. My personal approach in ZF was to work with a data structure (basically an ArrayObject) with custom serializers for JSON, XML, YAML, etc. and have the controller determine the best output format to use.

PHP REST API Routing

I have been looking at APIs and developing a REST API for a project that we are working on.
The API only accepts connections from one source in JSON format, I understand that bit fine.
If understand the majority of what is being said, however I don't understand the 3rd code example down and where the routing information would go.
The example they have provided is:
$data = RestUtils::processRequest();
switch($data->getMethod)
{
case 'get':
// retrieve a list of users
break;
case 'post':
$user = new User();
$user->setFirstName($data->getData()->first_name); // just for example, this should be done cleaner
// and so on...
$user->save();
break;
// etc, etc, etc...
}
The part I am unsure on is how to accept the original request i.e. /get/user/1 - how do you route that to the correct part of the script.
If there has been another SO question (I have searched for quite some time) or any further educational examples please do point me in the right direction.
Update
I have found a few routing PHP classes out there, but nothing thats just small and does what it says on the tin, everything seems to do routing + 2000 other things on top.
I now have all the classes I need for this project named as I wish to access them from the URI i.e.:
/data/users
/data/users/1
/hash/users
/hash/users/1
/put/users/1?json={data}
So all of these should use the users class, then one of the data, hash or put methods passing anything additional after that into the method as arguments.
If anyone could just explain how that bit works that would be a huge help!
Thanks :)
From the outset it looks like the website you've pointed out does not include a router or a dispatcher. There are plenty of PHP5 frameworks around which include a route and/or a dispatch or some description. (http://en.wikipedia.org/wiki/Comparison_of_Web_application_frameworks#PHP)
A router would be a class which have a list of predefined routes these could be really basic or quite complex, all depends on want you want to do. A good REST router IMO would look something like this:
:module/:controller/:params
And then the router would then router to the correct action based on the HTTP request (GET, POST, PUT, DELETE, OPTIONS)
public function getAction($id) {
// Load item $id
}
In your case, you will need a redirect rule that will send the request to something like this
index.php?user=id. Then you can process the get request.
The best solution I found for php REST architecture (including routing) is:
http://peej.github.com/tonic/

Is it a good idea to allow the router to look up controllers from a database?

In most of the tutorials for PHP MVC design structures, a router class is used to take user input and look up the right controller in order to process that input accurately. The input normally takes the form of a url. For example http://example.com/foo/bar/ would ask the router to find the controller named foo and fire the method bar.
PHP also has an auto-include function which requires a consistent naming system to used for your classes. For example, if I wanted to get an instance of the class Foo, I would need the file to be called Foo.php and inside would be a class called Foo.
Unfortunately, in tandem, these mechanisms place competing demands on the naming system. The url is very 'front facing' and as such, clients will often need it to reflect the content of a particular page. For example, if I have a page that gives directions to a venue, the client may need this page to be at http://example.com/venues/directions/. Later the content may change and the number of venues is reduced to 1 and now the client wishes the url to read http://example.com/venue/directions/. Obviously, this is a very trivial example but I think the need to change urls occasionally is clear.
Since the urls are very tightly connected to the controller classes, a change to a url means the class file name, the class name itself and any instances of that class will all require changing. For more complex systems this seems very time consuming, to me.
What sort of solutions are there to this problem? It seems to me that a directory of sorts will be necessary in which relations between urls and controllers are stored such that any url could be used to call any controller regardless of what either is named. Thus we would have a venues controller and a venues url and when the client requests a change in the url, we just make a change in the directory translating the new "venue" into "venues".
I think the best way to do this would be in a database table but this then requires an extra query for every user request. Is a database the right way to implement a directory? Is a directory even the best way to solve the above problem? Is this problem even a problem in the first place???
Some solutions I have seen to this is to explicitly specify a routing "connection" to a controller/action.
For example, in NiceDog, you can specify routes like
R('venues?/directions')
->controller('VenueController')
->action('directionsAction')
->on('GET');
Allowing a regular expression to match the URL. (The expression above will match venue/directions or venues/directions) I like this method, because it decouples the controller naming scheme from the URL naming scheme.
Another more configuration-based approach would be to have some kind of explicit mapping:
$routes = array(
'venues?' =>
array('controller'=>'VenueController','action'=>'indexAction'),
'venues?/directions' =>
array('controller'=>'VenueController','action'=>'directionsAction')
);
And then a simple dispatch function:
function dispatch($url,$routes) {
foreach ($routes as $pattern=>$map) {
if (preg_match($pattern,$url,$matches)) {
$controller = new $map['controller']();
$action = $map['action'];
call_user_func_array(
array($controller,$action),
array_slice($matches,1)
);
return true;
}
}
throw new Exception('Route not found.');
}
A good way to solve this problem is for the MVC framework to allow you to specify your own routing rules. For example, see URI Routing in the CodeIgniter framework's documentation.
To go along with your example, this would allow you to remap requests for /venues/ to /venue.

Categories