phpweb-servicesrestsymfonysymfony-http-foundation

Making my own argument resolver for my my own PHP framework


I decided to make my own mini - framework for PHP that I am going to use for a real life job, creation of a Web Service for a social app.

I got started with Fabien Potencier's guide to creating your own framework on top of the Symfony's components - http://symfony.com/doc/current/create_framework/index.html. I really liked his classLoader and http-foundation libraries and decided to integrate them.

I read the whole tutorial but I decided to stop integrating Symfony's components up to part 5 of the tutorial where he gets to the Symfony http kernel, route matcher and controller resolver (excluding those).

There are the front controller and route mapper file of my framework that qre in question.

front.php(the front controller)

<?php 

        require 'config/config.php';
        require 'config/routing.php';
        require 'src/autoloader.php';

        use Symfony\Component\HttpFoundation\Request;
        use Symfony\Component\HttpFoundation\Response;

        $request = Request::createFromGlobals();

        $response = new Response();


        $path = $request->getPathInfo();

        if(isset($siteMap[$path])) {
            call_user_func_array('src\Controllers' .'\\' . $siteMap[$path]['controller'] . '::' . $siteMap[$path]['action'], array($siteMap[$path]['arguments']));
        } else {    
            $response->setStatusCode('404');
            $response->setContent('Page not found');
        }

    $response->send();
?>  

My routing file :

/*  
    Map of routes
*/

$siteMap = array('/' => array('controller' => 'IndexController', 'action' => 'indexAction', 'arguments' => ''),
                    '/categories' => array('controller' => 'CategoriesController', 'action' => 'indexAction', '

What I want to know now is without further using Symfony's components, how can I do something like this:

In my routing file I wanna add an URL like '/hello' with Controller - Hello Controller and arguments name,age,gender that would correspond to a request in the browser GET www.base/hello/samuel/11/male.

And in the HelloController have an indexAction($name, $age, $gender) { ... }. I have tried looking at Symfony's source but it eludes me so far(I spent a lot of time going trough the source code of the libraries). I am going to modularize and separate the functions of the front controller and controller resolving even further but I want to get this down.

Ah and also any advice on building my framework further would be welcome(I am creating a framework for a REST - like web service that needs to be scalable and fast and handle tens of thousands requests per second maybe).


Solution

  • You must define regexes for your routes and match them against the requests, if you want to know how Symfony transforms its own route format e.g: '/path/to/{id}' to a regex, see RouteCompiler::compilePattern. That's out of the scope of this question and I will provide no code for it.

    The regex for your example route will be something like ^/hello/(\w*)/(\d*)/(\w*)/$, this will match '/hello/any string/any number/any string/' e.g: /hello/samuel/11/male (don't forget the ^ and the $, they match the beginning and end of the string).

    You must provide a regex for each route, and do a preg_match(rawurldecode($request->getPathInfo(), $regex /* as created earlier*/, $matches) against all of the routes (until one matches), instead of the isset($sitemap[$path]) if this returns false, the route does not match.otherwise you must get the arguments for your controller like so:

    preg_match(rawurldecode($request->getPathInfo(), $regex /* as created earlier*/, $matches)
    $args = array();
    // we omit the first element of $matches, since it's the full string for the match
    for($i = 1; $i < count($matches); $i++){
      $args[] = $matches[$i];
    }
    

    Now you can merge $args with the default arguments and call the controller.


    See UrlMatcher::matchCollection() on how Symfony matches each route against the regex, and constructs the arguments.