Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django-like URL Routing for PHP

I am looking for a way to provide URL routing similar to one in Django. I looked at a lot of resources online & I liked cobweb but the problem is I dont want to use the entire framework I just want to use the URL rerouting logic/code. Is there a good resource for just Django-like URL routing logic?

like image 553
Chantz Avatar asked Feb 09 '11 18:02

Chantz


People also ask

How does URL routing work in Django?

Django runs through each URL pattern, in order, and stops at the first one that matches the requested URL, matching against path_info . Once one of the URL patterns matches, Django imports and calls the given view, which is a Python function (or a class-based view).

What is URL mapping in Django?

It's where you define the mapping between URLs and views. A mapping is a tuple in URL patterns like − from django. conf. urls import patterns, include, url from django.


3 Answers

What you are looking for is a microframework. It's basically just the routing layer of a framework. There are several of these available. These looked interesting to me:

  • Limonade
  • Glue
  • Slim
  • Breeze

The one that really blew my mind though was Silex, which is based on Symfony2 and requires PHP 5.3.

like image 83
igorw Avatar answered Oct 21 '22 02:10

igorw


I had looked at Limonade micro PHP framework's URL routing posted a reply using limonade codebase. However I went back and looked at cobweb code and it is sort of pretty and nice. So I just took Cobweb's URL routing and refactored that in OOP PHP5.x to use in my own code.

for comparison see:- http://docs.djangoproject.com/en/dev/topics/http/urls/ vs. http://www.limonade-php.net/README.htm

The usual disclaimers are in place

1) I have not tested much!

2) It may not have all of Django's URL routing capabilities

<?php

/**
 *
 * @author rajeev jha (jha dot rajeev at gmail)
 * Django style URL routing. 
 * Pattern matching logic of this code is copied from cobweb framework
 * @see http://code.google.com/p/cobweb/source/browse/trunk/dispatch/url_resolver.class.php 
 *  
 */

class Gloo_Core_Router {

  private $table ;


    function __construct() {
    //initialize routing table 
    $this->table = new Gloo_Core_RoutingTable();
    }

  function getRoute($path,$domain=NULL){

    if(empty($path)) {
      $message = sprintf("Please supply a valid path to match :: got [%s] ", $path);
      trigger_error($message,E_USER_ERROR);
    }

    //all rules for this domain 
    $rules = $this->table->getRules($domain);    
    $route = NULL ;

    if($path == '/') 
      $route = $this->matchHome($rules);  
    else 
      $route = $this->match($rules,$path);

    return $route ;

  }

  private function matchHome($rules) {
    $route = NULL ;

    foreach($rules as $rule) {
      if($rule["pattern"] == '/') {
        $route = $this->createRoute($rule,array());
      }
    }

    return $route ;

  }

  private function match($rules,$path) {
    $path = ltrim($path, '/');
    $matches = array();
    $route = NULL ;

    foreach($rules as $rule) {
      if(preg_match($this->patternize($rule["pattern"]),$path,$matches) != 0 ) {
        //match happened 
        $matches = $this->sanitizeMatches($matches);
        $route = $this->createRoute($rule,$matches);
      }
    }

    return $route ;

  }

  private function createRoute($rule,$matches) {

    $route = $rule ;
    //add parameters
    $route["params"] = $matches ;
    return $route ;
  }

  private function sanitizeMatches($matches){
    //discard the first one 
    if (count($matches) >= 1)
      $matches = array_splice($matches,1);  

    $unset_next = false;

    //group name match will create a string key as well as int key 
    // like match["token"] = soemthing and match[1] = something 
    // remove int key when string key is present for same value

    foreach ($matches as $key => $value) {
      if (is_string($key)){
        $unset_next = true;
      } else if (is_int($key) && $unset_next) {
        unset($matches[$key]);
        $unset_next = false;
      }
    }

    return array_merge($matches);

  }

  private function patternize($pattern) {
    //http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
    //treat pattern as UTF-8
    return '{'.$pattern.'}u' ;
  }

}

?>


<?php

/**
 *
 * @author rajeev jha (jha dot rajeev at gmail)
 * Django style URL routing. 
 * URL routing table 
 *  
 */


class Gloo_Core_RoutingTable {

  private $rules ;
  private $splrules ;
  const ANY_DOMAIN = '__ANY__' ;


  function __construct() {
    $this->rules = array();
    $this->splrules = array();

    //@todo inject from outside    
    $this->createRule(self::ANY_DOMAIN, '/', 'Gloo_Controller_Home');
    //match alphanumeric + dashes
    //a pcre word (\w) does not contain dashes
    $this->createRule(self::ANY_DOMAIN, '^(?P<token>[-\w]+)$','Gloo_Controller_Post');
    $this->createRule(self::ANY_DOMAIN,  '^page/(?P<pagenum>\d+)$','Gloo_Controller_Home');
    $this->createRule(self::ANY_DOMAIN,  '^(?P<token>\w+)/page/(?P<pagenum>\d+)$','Gloo_Controller_Post');
    $this->createRule(self::ANY_DOMAIN, '^category/(?P<name>\w+)$','Gloo_Controller_Category');
    $this->createRule(self::ANY_DOMAIN,  '^category/(?P<name>\w+)/page/(?P<pagenum>\d+)$','Gloo_Controller_Category');

    //special rules 
    $this->createRule('www.test1.com','^(?P<token>\w+)$','Gloo_Controller_File', array("template" => "post.php"));

  }

  function createRule($domain,$pattern,$action,$options=NULL) {

    if(empty($domain)) {
      trigger_error("No domain supplied for rule" ,E_USER_ERROR);
    }


        $rule = array();

    $rule["pattern"] = $pattern;
    $rule["action"] = $action ;

        //Add options

    if(is_null($options))
      $rule["options"] = array();
    else
      $rule["options"] = $options ;

    $rule["domain"] = $domain ;

    //add to generic or domain specific rules
    if($domain == self::ANY_DOMAIN)
      $this->rules[] = $rule ;
    else
      $this->splrules[$domain][] = $rule ;

  }

  function getRules($domain) {
    if(empty($domain))
      return $this->rules ;

    //valid domain - rules as well
    // add to existing rules

    if(array_key_exists($domain,$this->splrules)) {
      $splrules = $this->splrules[$domain];
      $rules = array_merge($this->rules,$splrules);
      return $rules ;

    } else {
      return $this->rules ;
    }

  }

}


?>

Now, You can add any rules to Routing table initialization. depending on what domain + "path" you supply to router you will get back the "controller" string together with the "named group" parameters and other matched parameters. I am not including class instantiation logic for "controller" strings because 1) that would be complicated 2) If your intention is to just get a Django like URL routing then this much is good enough.

References

  1. Limonade code is here:- https://github.com/sofadesign/limonade/blob/master/lib/limonade.php

  2. Cobweb code is here:- http://code.google.com/p/cobweb/source/browse/trunk/dispatch/url_resolver.class.php

  3. My Code is here:- https://code.google.com/p/webgloo/source/browse/trunk/php/lib/Gloo/Core/Router.php
  4. Old version of my code :- what I edited here - was based on limonade and it was quite brain damaged. Feel free to look at Limonade link above. I am removing it.

Usage

$router = new Gloo_Core_Router();

$path = 'category/news/page/3' ;
printf("Now matching path [%s]" , $path);
$route = $router->getRoute($path);
print_r($route);

$path = '/category/Health/page' ;
printf("Now matching path [%s]" , $path);
$route = $router->getRoute($path);
print_r($route);

You have to change the class names and include dependencies but hey, you are a programmer ;D

like image 3
rjha94 Avatar answered Oct 21 '22 04:10

rjha94


I don't think is exactly the same, but the Zend Framework has pretty descent routing functionality that you might want to look at. The Zend Framework is kind of a component-based framework that doesn't force you into using the whole thing. But I think if you use the routing functionality you might need to also use their controller mechanism as well since the router is built on top.

like image 1
shaune Avatar answered Oct 21 '22 04:10

shaune