ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
**模型:** 所有的输入最终被推送到模型结束,所有的输出数据也都来自于模型(一般是操作数据库、文件或者web服务) **视图** 将数据从模型中取出并输出给用户,页面和表单在这里输出 **控制器** 控制器根据用户的请求确定用户可以做什么,接着我们用模型执行请求的操作并检索组装请求数据,最后调用视图将操作的结果显示给用户 控制器有一个最基本的用途就是接受GET参数以确定需要传送什么样的页面 ``` $page=basename($_GET['page']); if ($page=='user-account') { require_once 'user-account.php'; } require_once $page.'-view.php' ``` 需要.../test.php?page=user-accent&user-id=123&action=view这种url格式访问页面,不是我们想要的,我们怎么将url变成我们想要的/user-account/view/123这样的pathinfo访问模式呢?答:通过[url重写](https://www.kancloud.cn/a173512/thinkphp5/1806786) 我们可以通过$_SERVER['REQUEST_URI'];(这个变量只有 apache 才支持)来检索请求字符串 掌握了url重写和$_SERVER['REQUEST_URI']后我们就可以通过任何方式来解析url,要做到这一点我们需要创建一个路由router >[info]**创建路由的原因:** >* 允许指定精确的正则表达式 >* 支持指定键值对的语法 >* 创建一个带有限结构的完整解析器 > >在这个路由器中,我们可以指定:key或type:key中人一个作为url结构中的占位符,支持的类型如下: >* any >* integers >* alpha(包括破折号和下划线) >* alpha plus numeric >* 正则表达式(自定义模式) ``` abstract class RouterAbstract{ abstract public function addRoute($route,$options=array()); abstract public function getRoute($request); } class RouterRegex extends RouterAbstract{ const REGEX_ANY="([^/]+?)"; const REGEX_INT="([0-9]+?)"; const REGEX_ALPHA="([a-zA-Z_-]+?)"; const REGEX_ALPHANUMERIC="([0-9a-zA-Z_-]+?)"; const REGEX_STATIC="%s"; /** * [$routes 保存编译后的路由 * @var array */ protected $routes=array(); /** * $basUrl 保存基本url便于使用子文件夹(/store/app)中的路由器 * @var string */ protected $basUrl=''; /** * 指定基本的url * @param string $baseUrl 基本url */ public function setBaseUrl($baseUrl){ //url中充满了默认的分隔符,所以当需要转义时用@代替这些默认的/正则分隔符而是我们能够更加简单的创建正则表达式 $this->baseUrl=preg_quote($baseUrl,"@"); } /** * 增加路由,允许指定模式及一组将于键/值对结合的选项 * @param [type] $route [description] * @param array $options [description] */ public function addRoute($route,$options=array()){ $this->routes[]=array('pattern'=>$this->_pareRoute($route),'options'=>$options); } public function _pareRoute($route){ $baseUrl= $this->baseUrl; if ($route=='/') { return "@^$baseUrl/$@"; } //用/分割路由规则(如:/alpha:page/alpha:action/:id)得到每个部分 $parts=explode('/',$route);//array ( 0 => '', 1 => 'alpha:page', 2 => 'alpha:action', 3 => ':id', ) //初始url @^ $regex="@^$baseUrl"; //删除以/开头的路由规则分割时产生的空数组,并重组(索引也会变) if ($route[0]=="/") { array_shift($parts);//$parts=array ( 0 => 'alpha:page', 1 => 'alpha:action', 2 => ':id', ) } //便利路由规则分割后的数组已处理url foreach ($parts as $part) { $regex.="/";//$regex=@^/ $args=explode(':',$part);//array(2) { [0]=> string(5) "alpha" [1]=> string(6) "action" } if (count($args)==1) {// 没有:和alpha等这些只有值如page时的情况 $regex.=sprintf(self::REGEX_STATIC,preg_quote(array_shift($args),"@")); continue; }elseif ($args[0]=='') { //:id走这里 array_shift($args); $type=false; }else{ //alpha:page/alpha:action走这里,删除alpha,并返回删除的元素,数组为空返回null $type=array_shift($args); } //删除第一个元素并返回,这里返回的删除值分别就是page、action、id $key=array_shift($args); //如果类型不是alpha、int、等,而是regex(正则) if ($type=="regex") { $regex.=$key; continue; } //删除键名 $this->normalize($key); $regex.="(?P<$key>"; switch (strtolower($type)) { case 'int': case 'integer': $regex.=self::REGEX_INT; break; case 'alpha': $regex.=self::REGEX_ALPHA; break; case 'alphanumeric': case 'alphanum': case 'alnum': $regex.=self::REGEX_ALPHANUMERIC; break; default: $regex.=self::REGEX_ANY; break; } $regex.=")"; } $regex.='$@u'; //@^/(?p<page>([a-zA-Z_-]+?))/(?p<action>([a-zA-Z_-]+?))/(?p<id>([^/]+?))$@u //echo '<br>'.htmlentities($regex).'<br>'; return $regex; } /** * 清理alpha、int、alnum等键名 * @param [type] &$param [description] * @return [type] [description] */ public function normalize(&$param){ $param=preg_replace("/[^a-zA-Z0-9]/", "", $param); } /** * 解析指定的url,并将它解析到路由的键值对下 将url转化为mvc对应的数组 * @param string $request $_SERVER['REQUEST_URI']的值,如:user-account/view/123 * @return [type] [description] */ public function getRoute($request){ $matches=array(); foreach ($this->routes as $route) { if (preg_match($route['pattern'],$request ,$matches)) { foreach ($matches as $key => $value) { if (is_int($key)) { unset($matches[$key]); } } //合并数组 $result=$matches+$route['options']; return $result; } } return false; } } $router=new RouterRegex(); //$router->setBaseUrl('store'); $router->addRoute("/alpha:page/alpha:action/:id",array('controller'=>'default')); //$route=$router->getRoute('/user-account/view/123'); //var_export($route);//array ( 'page' => 'user-account', 'action' => 'view', 'id' => '123', 'controller' => 'default', ) //$router->addRoute("/photos/alnum:user/int:photoId/in/regex:(?P<groupType>([a-z]+?))-(?P<groupId>([0-9]+?))"); //$route=$router->getRoute('/photos/dishafik/5584919786/in/set-72157626290864145'); //var_export($route);//array ( 'user' => 'dishafik', 'photoId' => '5584919786', 'groupType' => 'set', 'groupId' => '72157626290864145', ) ``` 到这里我们实现了将 ``` class Photos_Controller{ protected $router=false; //dispatch:调度/分发/分派分配 public function dispatch($url,$default_data=array()){ //try{ if (!$this->router) { throw new Exception("没有设置路由", 1); } //将url转化为mvc对应的数组 $route=$this->router->getRoute($url); var_export($route); $controller=ucfirst($route['controller']); $action=ucfirst($route['action']); unset($route['controller']); unset($route['action']); //获取model实例 $model=$this->getModel($controller); $data=$model->{$action}($router); $data=$data+$default_data; //获取view实例 $view=$this->getView($controller,$action); //输出模板 echo $view->render($data); try{}catch(Exception $e){ try{ if ($url!='/error') { $data=array('message'=>$e->getMessage()); $this->dispatch('/error',$data); }else{ throw new Exception("Error Route Undefined", 1); } }catch(Exception $e){ echo "<h1>An Unkonwn error occurred.</h1>"; } } } public function setRouter(RouterAbstract $router){ $this->router=$router; } /** * 获取mode类实例 * @param [type] $name [description] * @return [type] [description] */ protected function getModel($name){ $name.='_Model'; $this->includeClass($name); return new $name; } /** * 获取view类实例 * @param [type] $name [description] * @param [type] $action [description] * @return [type] [description] */ protected function getView($name,$action){ $name.='_'.$action.'View'; $this->includeClass($name); return new $name; } protected function includeClass($name){ echo '<br>',$name;echo '<br>'; //str_replace(':','#', $str);//将:替换为# $file=str_replace(DIRECTORY_SEPARATOR, '_', $name).'.php'; echo $file; if (!file_exists($file)) { throw new Exception("类不存在", 1); } require_once $file; } } $router=new RouterRegex(); //$router->setBaseUrl('store'); $router->addRoute("/alpha:page/alpha:action/:id",array('controller'=>'photos')); $pc=new Photos_Controller(); $pc->setRouter($router); //$pc->dispatch('/photos/getPhoto/123'); //浏览器访问 http://localhost/test.php/photos/getPhoto/123 $pc->dispatch($_SERVER['REQUEST_URI']); ``` Photos_Model.php ``` <?php //模型 class Photos_Model{ public function getPhoto($options){ return array( 'title'=>'Booke in the Woods', 'width'=>427, 'height'=>640, 'url'=>'http://farm6.static.flickr.com/5142/55884010786_95a4c15e8a_z.jpg' ); } } ``` Photos_GetPhotoView.php ``` <?php //视图 //这里使用简单的sprintf调用HTML模板,后面用smarty、twig、savant等模板引擎 class Photos_GetPhotoView{ public function render($data){ $html='<h1>%s</h1>'.PHP_EOL; $html.='<img src="%s" width="%s" height="%s">'.PHP_EOL; $return=sprintf($html,$data['title'],$data['url'],$data['width'],$data['height']); return $return; } } ```