php框架

piaoling  2011-12-23 10:53:27
大东哥

分享个自己弄的php框架 v0.0001版

大东哥 发表于 11-18 20:36 1个月前, 11回/522阅, 最后回答: 8天前 (6人收藏 ,收藏 |举报)
3

最近整理做过的小东西,发现为学php曾短暂搞过个php框架。后面忙别的就没下文了,估计接下来也没什么精力搞,想想还是放出来晒晒吧,主要是思路的交流。

项目目录结构采用playframework的类似结构。

既然宣称它是个MVC框架,基本的东西要有:
路由,参数绑定,参数验证,重定向,模版机制,自定义标签,ORM,插件结构。实现了这几个的基本东西,一个MVC框架就成形,接下来做的就是完善各部份功能与性能的改善。

一个个说这版提供了什么功能:

路由:

   路由的配置其目的是url到action的映射。我这里直接用pcre来做这个映射,示例:

  

001 <?php
002  
003         Router::root('GET','application.home');
004  
005         Router::route('GET','/docs', "docs.index");
006         Router::route('GET','/downloads', "downloads.index");
007  
008 //将形如/admin/user/list/1这样的url映射到controllers/admin/user.php的list方法,1为参数       
009         Router::route('*','/admin/(?P<ct>.+)/(?P<ac>.+)/(?P<id>d+)','admin/{ct}.{ac}');
010  
011         Router::route('*','/admin/(?P<ct>.+)/(?P<ac>.+)','admin/{ct}.{ac}');
012  
013     Router::route('*','/(?P<ct>.+)/(?P<ac>.+)/(?P<id>d+)','{ct}.{ac}');
014 //映射所有的url
015     Router::route('*','/(?P<ct>.+)/(?P<ac>.+)','{ct}.{ac}');
016         
017 这里面有几个说明的地方
018 第一个参数,声明接受的http方法。
019 第二个参数声明url的正则表达式:
020      分组必须用()包含,因为匹配是分组捕获的。ct是表示控制器,ac表示action方法
021      这两的字面量是写死的。
022      会有需要带参数的url声明,如/blog/2011/11/list这样的url需要映射到控制器方法,可这么声明:
023      Router::route('GET','/blog/(?P<year>d+)/(?P<month>d+)','blog.list');
024 第三个参数声明控制器规则
025 匹配是按照声明的顺序,逐个查找来匹配的。
026  
027 ?>
028  
029 控制器:
030 PM的控制器不需要继承任何类,唯一的要求是放到controllers里面。示例:
031 controllers/news.php
032  
033 <?php
034  
035 PM::_include("core", "model/Db");
036  
037 class news {
038  
039     public static function all() {
040  
041         $db = Db::get();
042         $results = $db->find("select * from news order by id desc", array())->fetchAsMap(10);
043         MVC::putTArg("objects", $results);
044         MVC::render();
045         
046     }
047  
048     public static function show() {
049  
050         $db = Db::get();
051         $object = $db->find("select * from news where id=" . $_GET['id'])->fetchSigAsObject();
052         MVC::putTArg("object", $object);
053         MVC::render();
054         
055     }
056  
057 }
058  
059 ?>
060  
061  
062 几点说明:
063 action方法都是静态方法,由于php的运行机制,不需要关心同步问题。
064 支持flash范围的变量。MVC::flash('success','成功');
065 简单的参数绑定:MVC::bind($object, $_POST);
066 参数验证:MVC::validate(array('required' => $_POST['username']));
067           会去找pm/mvc/validate/下面的required.php文件:
068         class PM_MVC_Validator_required {
069  
070            public $msg = '不能为空';
071            public function validate($value) {
072                 if ($value === '' || is_null($value || !isset($value)))
073                     return false;
074                 else
075                     return true;
076             }
077             function __get($name) {
078                 return $this->$name;
079             }
080             function __set($name, $value) {
081                 $this->$name = $value;
082             }
083         }
084       自定义验证增照这个模版方法改就好了。
085  
086 渲染模版:
087           MVC::render();默认会去找views目录下与action方法同名的模版文件。
088           也支持渲染不同名的文件。MVC::render('list.html');
089 重定向:
090         有两种,如果是跨域重定向,直接写上url,如:MVC::redirect('http://www.oschina.net');
091         其它形式会根据action表达式来解析:
092         MVC::redirect('show'),会根据Router里声明的路由,反向解析出最匹配的url后重定向。
093  
094 控制器还实现了简单的拦截机制:
095 如果在控制方法中声明了 __before方法,则该控制器方法调用前都会被__before拦截到,如:
096 <?php
097  
098 public class admin {
099     public static function __before() {
100         //assert logged
101         $username = $_SESSION['username'];
102         if (!$username)
103             MVC::redirect("errors.forbidden");
104         MVC::$layout = '/layouts/admin.html';//指定布局文件为admin.html。
105  }
106 }
107 ?>
108  
109 拦截机制还支持继承结构:
110 public class user extends admin{},这个控制器下的方法将会首先被父类admin的__before
111 拦截。

 

001 视图模版:
002  
003   模版是基于宏哥推荐的vem那个于2007年就停止更新的模版引擎。
004   除了支持基本功能外,还做了一些扩展:模版布局,自定义标签等。
005  
006 先看看布局文件:
007 views/layouts/main.html
008 <!DOCTYPE>
009 <html lang='zh-CN' xml:lang='zh-CN' xmlns='http://www.w3.org/1999/xhtml'>
010  
011 <head>
012     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
013     <title>PM</title>
014     <link rel="shortcut icon" href="{site}/favicon.ico" />
015     <link rel="stylesheet" type="text/css" href="{site}/static/css/screen.css" media="screen"/>
016     <link rel="stylesheet" type="text/css" href="{site}/static/css/pm.css" media="screen"/>
017 </head>
018 <body>
019  
020         <div class="container">
021                 <!-- top -->
022                 <div class="span-24 top">
023                         {include:/widgets/top.html}
024                 </div>
025                 <!-- center -->
026                 <div class="span-24 center">
027                         <!-- left -->
028                         <div style="width:270px;float:left;">
029                                {include:/widgets/left.html}
030                         </div>
031                         <!-- right -->
032                         <div style="float:left;margin-left:5px;width:675px;">
033                                 {__doLayout()}
034                         </div>
035                 </div>
036                 
037                 <hr />
038                 <div class="span-24"
039                         style="text-align: center; font-size: 12px; padding-bottom: 10px;">
040                       {include:/widgets/footer.html}       
041                 </div>
042  
043         </div>
044     
045 </body>
046 </html>
047 可以看到有个{__doLayout()},普通模版内容会嵌入到该声明中,这样就实现了模版布局。
048  
049 普通视图文件:
050  
051 views/news/list.html
052  
053 //循环
054 {foreach objects,object}
055     <li>{object.title}</li>
056 {end}
057  
058 //@ 后面跟action表达式,编译的时候会根据Router里的声明解析出url
059 <a href="{@:news.list}">新闻列表</a> //  /news/list
060 <form action="{@:save}"></form>   // /news/save
061  
062 自定义标签:
063 只要将文件放到views/tags下,就成为一个标签了,如:
064 views/tags/select.html //自定义html <select>标签
065  
066 <select value="{_param['value']}" name="{_param['name']}">
067  
068     {foreach:$this->data->_param['items'],item}
069         {if:item.id == _param['value']}
070             <option value="{item.id}" selected="selected">
071                 {item.name}
072             </option>
073         {else}
074             <option value="{item.id}">
075                 {item.name}
076             </option>
077         {end}
078     {end}
079     
080 </select>
081 引用标签:
082 {tag:select<-('items'=>@@items)}{/tag}
083 可以向标签内传参数
084 '<-'后面根的就是需要传入的参数,其形式就是数组的形式,不过省略了array声明。
085 传入参数有个引用的范围问题:
086 @@表示引用模版全局变量,也就是由MVC::putTArg()设置的变量
087 @表示模版局部变量,如循环内的变量
088 $表示引用php范围变量。
089 (好像很复杂啊??)
090 标签内要引用传入的参数的话,是:{_params['varName']}
091 支持嵌套标签。
092     ---- 还实现了什么功能,我要看自己写的代码,因为蛮久没弄了,:)。
093  
094 MVC相关的东西,基本完事。
095  
096 ORM
097    orm做得比较的粗糙,因为好像搞个复杂的orm也没有什么必要,还是看看我的思路吧。
098    将所有的Model放到app/models目录下。
099  
100 配置db相关参数
101 文件位置:
102 conf/app.php
103 $AppConf = array(
104     'tmpDir' => 'tmp',
105     'viewsDir' => 'app/views/',
106     'controllersDir' => 'app/controllers/',
107     'frontController' => '/index.php',
108     'web.root' => '/pmorg',
109     'db.host' => 'localhost',
110     'db.username' => 'root',
111     'db.password' => '123456',
112     'db.name' => 'test'
113 );
114  
115 看到,以db开头的就是数据源相关的配置。
116 看看一个模型的示例:
117  
118 PM::_include('core', 'model/PM_Model');
119  
120 class news_model extends PM_Model{
121  
122     public $id;
123     public $title = '';
124     public $text = '';
125     public $dateCreated;
126     public $dateUpdated;
127     
128     //参数验证
129     static $constraints = array(
130         
131         'id' => array('required' => true)
132     
133     );
134  
135    //数据库映射相关 (只是思路而已)
136     static $mapping = array(
137        'table' => 'news',
138        'id' => array('column' => 'id','type' => 'number','sqlType' => '','vendor' => '','lazy' => 'false')
139     );
140  
141 }
142  
143 只是这么个思路,参数验证是实现了,数据库映射相关只实现部分哦。
144  
145 控制器代码:
146  
147 public static function save(){
148     var $model = new news_model();
149     MVC::bind($model,$_POST);
150     $model.save();
151     if(MVC::hasValidErrors()){
152        MVC::flash('errors',MVC::validErrors());
153        redirect();
154     }
155     else
156        echo '保存成功'';
157 }
158  
159 ?>

插件结构只有几个思路,暂时没精力去实现了,以上介绍的可能不包含全部功能,提到的功能也可能没实现(98%还是实现了的)。

这个框架是为熟悉php目的,边看php文档,边写的,没有看其他开源实现,错漏应该不少了。

目的就是晒晒自己的小东西,与分享一下思路,你硬要说,没做好就别放出来,那我也懒得理了。

下载:http://www.oschina.net/code/snippet_1579_7193

结构:http://www.oschina.net/question/1579_29616

类别 :  PHP(78)  |  浏览(4390)  |  评论(0)
发表评论(评论将通过邮件发给作者):

Email: