hu's profile混吃等死滴高手PhotosBlogLists Tools Help

Blog


    November 30

    DIY Route for PHP

    大多基于MVC模型的framework都有叫做route的东西来实现Controller层。
    最近因为一个项目需要使用php4实现,Model层使用原有已经积累了的封装的数据处理层,View使用smarty模板,
    但没有Controller层或现有的controller层比较薄并且松散凌厉。虽然用apache的rewrite机制实现了类似http://www.domian.com/book/item/123/review 这样搜索引擎友好的url。
    好好的MVC少了一块多不爽,于是想实现个类似rails route的东西,能把用户的request转换成controller object的method call.
    建个模先,
    http://www.domian.com/book/      => $book->index()
    http://www.domian.com/book/30     => $book->index(20), page = 30
    http://www.domian.com/book/register    => $book->register()
    http://www.domian.com/book/category/1    => $book->category(1)
    http://www.domian.com/book/category/3/20   => $book->category(3,20), page = 20
    http://www.domian.com/book/item/123    => $book->item(123)
    http://www.domian.com/book/item/123/review  => $book->item(123)->review()
     
    前面的是设计好的url,后面是映射到的method call。url去掉域名既是参数,如/book/register。php中可以通过 $_SERVER['PATH_INFO']变量获取path_info。path_info中的参数可以按照:controller, :action, :param, :page, :nested_action进行分类。
    =>后面是映射到的method call。注:path_info在apache2上需要配置AcceptPathInfo on 才可以。
    现在两头的东西都有了,url参数可以通过path_info获取,被调用的method可以定义在一个类里,中间的映射怎么搞呢?
    于是定义了个映射阵列,

     var $_url_pattern = array(
      "c"   => array("/\//", "%c->index()"),
      "c/n"  => array("/\/(\d+)/", "%c->index('%n')"),        // controller/page
      "c/a"   => array("/\/[a-z](\w+)/", "%c->%a()"),        // controller/action
      "c/a/p"  => array("/\/[a-z](\w+)\/(\w+)/", "%c->%a('%p')"),      // controller/action/param
      "c/a/p/n"  => array("/\/[a-z](\w+)\/(\w+)\/(\d+)/", "%c->%a('%p', '%n')"),  // controller/action/param/page
      "c/a/p/o" => array("/\/[a-z](\w+)\/(\w+)\/[a-z](\w+)/", "\$r = %c->%a('%p'); \$r->%o()") // controller/action/param/other.action
     );
     
    url_pattern的key是个flag用来标识参数类型序列的,value分两部分,前面是定义的正则表达式用来匹配path_info,后面是method call的指令模板,确定这些实现这个route就不算什么问题了,对key按照字符长度进行逆序排序,然后匹配。把匹配了的参数带入指令模板然后eval就可以了。代码较长,不贴了.
     
     route这样用,定义book.php,只是个入口,apache解析url最先找的是book.php.
    <?
    require_once 'route.php';

    $controller = 'bookcontroller';
    $rt = new CRoute();
    $rt->dispatch($controller);
    ?>
     
    定义mycontroller.php
    <?

    class BookController
    {
     function index($param = null){
      print 'book.lindex and page='.$param;
     }
     
     function register(){
      //do register
      print 'book.register';
     }
     
     function category($param1 = null, $param2=null) {
      print 'category:'.$param1.' and '.$param2;
     }
     
     function item($param=null) {
     //do something
      print 'book item:'.$param.'</br>';
      return new Item($param);  // the object has a method named review()
     }
      
    }
    class Item {
     var $param = null;
     function Item($param){
      $this->param = $param;
     }
     
     function review(){
      print 'review:'.$this->param;
     }
    }

    ?>