ThinkPHP控制器

时间:2009-09-22 14:57 作者:php开发框架 点击:
控制器 执行流程 项目编译 模块和操作 默认模块和操作 入口文件 URL模式 URL路由

  

 

执行流程

--------------------------------------------------------------------------------

我们对用户的第一次URL访问 http://<serverIp>/My/index.php/Index/show/ 所执行的流程进行详细的分析,用户的URL访问首先是定位到了My项目的index.php 入口文件(注意:如果使用了URL_REWRITE,可能index.php已经被隐藏了),项目的入口文件所做的其实是实例化一个App应用实例,并且执行这个应用。http://servername/appName/moduleName/actionName/params
来获取当前需要执行的项目(appName)、模块(moduleName)和操作(actionName),在某些情况下,appName可以不需要(通常是网站的首页,因为项目名称可以在入口文件中指定,这种情况下,appName就会被入口文件替代)
每个模块名称是一个Action文件,类似于我们平常所说的控制器,系统会自动寻找项目类库Action目录下面的相关类,如果没有找到,会尝试搜索应用目录下面的组件类中包含的模块类,如果依然没有,则抛出异常。
而actionName操作是首先判断是否存在Action类的公共方法,如果不存在则会继续寻找父类中的方法,如果依然不存在,则会检查是否存在空操作定义,如果还没有就会寻找是否存在自动匹配的模版文件。如果存在模版文件,那么就直接渲染模版输出。
因此应用开发中的一个重要过程就是给不同的模块定义具体的操作。一个应用如果不需要和数据库交互的时候可以不需要定义模型类,但是必须定义Action控制器。
Action控制器的定义非常简单,只要继承Action基础类就可以了,例如:http://servername/index.php/User/
http://servername/index.php/User/add
如果你需要增加或者重新定义自己的操作方法,增加一个方法就可以了,例如http://servername/index.php/User/select/了,系统会自动定位当前操作的模板文件。

默认模块和操作
------------------------------------------------------------------
 http://serverName/index.php,没有带任何模块和操作的参数,系统就会寻找默认模块和默认操作,通过DEFAULT_MODULE和DEFAULT_ACTION来定义,系统的默认模块设置是Index模块,默认操作设置是index操作。也就是说
http://serverName/index.php和
http://serverName/index.php/Index以及
http://serverName/index.php/Index/index 等效。

入口文件
--------------------------------------------------------------------
1、加载公共入口文件
在实例化App类之前,我们需要首先加载系统的公共入口文件ThinkPHP.php,这个文件是ThinkPHP的总入口,让我们来一探究竟。在加载ThinkPHP.php文件的过程中,其实完成了下面的操作:
 记录开始执行时间 $GLOBALS['_beginTime'];
 检测THINK_PATH定义,如果没有则创建;
 检测项目名称APP_NAME,如果没有则按照一定规则自动定义;
 检测项目编译缓存目录定义,没有则取项目的Temp目录;
 加载系统定义文件defines.php和公共函数文件functions.php;
 如果项目编译缓存目录不存在,则自动创建项目目录结构;
 加载系统核心类库(包括Base、App、Action、Model、View、ThinkException、Log);
 如果PHP版本低于5.2.0则加载兼容函数库compat.php;
 生成核心编译缓存~runtime.php;
 记录加载文件时间 $GLOBALS['_loadTime'];
2、 项目初始化init
在加载完成ThinkPHP的公共入口文件之后,我们就开始执行应用了,而首先应该是初始化App应用。
 设定错误和异常处理机制(set_error_handler和set_exception_handler);
 项目预编译并载入;
 设置时区支持;
 Session过滤器检查;
 session初始化;
 检查并加载插件;
 URL分析和调度;
 获取当前执行的模块和操作名;
 加载模块配置文件;
 页面防刷新机制检查;
 语言检查并读取对应的语言文件;
 模板检查并定义相关的模板变量;
 RBAC权限检测;
 如果开启静态写入则读取静态缓存文件;
 应用初始化过滤插件 app_init;
 记录应用初始化时间 $GLOBALS['_initTime']
3、 项目预编译
 加载系统惯例配置文件convention.php;
 加载项目配置文件 config.php;
 加载项目公共文件 common.php;
 如果是调试模式加载系统调试配置文件 debug.php;
 如果定义了项目的调试配置文件则载入 debug.php;
 生成项目编译缓存文件~app.php;
4、 URL分析Dispatcher
 检查当前URL模式URL_MODEL;
 如果存在$_GET变量,则根据当前的URL模式和设置进行重定向;
 进行路由定义检测;
 分析PATH_INFO的URL信息到数组;
 把PATH_INFO得到的值和$_GET合并;
5、 获取模块和操作名
 检查VAR_MODULE变量(包括GET 和POST),如果未定义,则获取默认模块名;
 检查组件模块;
 检查模块伪装;
 检查VAR_ACTION变量(包括GET 和POST),如果未定义,则获取默认操作名;
 检查操作链;
 检查操作伪装;
6、 项目执行exec
 AUTO_LOAD_CLASS 检查 如果有则导入公共类;
 实例化当前模块的Action控制器类;
 如果Action控制器不存在则检查空模块 EmptyAction;
 检查操作链,如果有执行操作链;
 检查前置操作方法 _before_操作名;
 执行模块的操作方法,调度转移给Action控制器;
 执行后置操作方法 _after_操作名;
 执行应用结束过滤器 app_end;
 如果开启日志记录,写入错误日志;
7、 执行控制器的操作
 实例化视图类View;
 取得当前控制器名称;
 控制器初始化_initialize;
 如果操作方法不存在检查空操作 _empty;
 如果空操作没有定义则检查对应的模板文件;
 调用模型获取数据;
 渲染视图进行输出;
8、 调用模型获取数据find
 实例化模型类;
 模型初始化 _initialize;
 判断当前模型名称和对应数据表;
 实例化数据库操作对象;
 数据表字段检测并缓存;
 查询需要的数据;
 判断是否视图模型;
 如果是延时查询返回ResultIterator对象;
 取出数据对象的时候记录乐观锁;
 获取文本字段数据;
 获取关联数据;
 对数据对象自动编码转换;
 记录当前数据对象;
 返回定义的数据格式(数组或者stdClass对象)
9、 输出视图
 模板变量赋值;
 检测是否是布局输出;
 检测页面输出编码;
 缓存初始化过滤 ob_init;
 页面缓存开启ob_start;
 缓存开启后执行的过滤;
 模版文件名过滤 template_file;
 定位当前输出的模板文件;
 模版变量过滤 template_var;
 根据不同模版引擎进行处理;
 如果是PHP模板引擎,直接载入模板文件;
 使用内置模板引擎,检测缓存有效期;
 缓存无效则重新编译模板文件;
 载入模板缓存文件;
 获取并清空缓存;
 输出编码转换;
 输出过滤 ob_content;
 开启静态写入则写入静态文件;
 如果输出则获取视图运行时间;
 如果是display则渲染模板输出信息;
 开启页面Trace则显示页面Trace信息;
 如果是fetch则返回模板输出信息;

项目编译
--------------------------------------------------------------------------
 

ThinkPHP v1.0.0正式版本开始引入了新的项目编译机制,而在1.0.3版本中又进行了一些优化设置。
所谓的项目编译机制是指系统第一次运行的时候会自动生成核心缓存文件~runtime.php和项目编译缓存文件~app.php,第二次就会直接载入编译过的缓存文件,从而省去很多IO开销,加快执行速度,并且编译缓存文件默认是去掉空白和注释的,因此存在一个预编译的过程。项目编译机制对运行没有任何影响,预编译操作和其他的目录检测机制只会执行一次,因此无论在预编译过程中做了多少复杂的操作,对后面的执行没有任何效率的缺失。

1.0.2版本之前默认的生成路径是项目的根目录,1.0.2版本开始,默认的编译缓存目录为TEMP_PATH (项目的临时文件目录)。
还可以在入口文件里面设置RUNTIME_PATH,例如:

 

  1. defined('RUNTIME_PATH','./MyApp/runtime/');    

系统会自动把编译缓存文件放到该目录下面生成,注意这样的话在Linux环境下面需要对RUNTIME_PATH目录设置可写权限。

注意在调试模式下面不会生成项目编译缓存,但是依然会生成核心缓存。
如果不希望生成核心缓存文件的话,可以在项目入口文件里面设置 CACHE_RUNTIME
例如:

 

如果在非调试模式下面,修改了配置文件或者项目公共文件,需要删除项目编译文件。1.0.4以上版本修改配置文件会自动重新生成编译缓存文件。

1.0.3版本以上,还可以设置对编译缓存的内容是否进行去空白和注释,例如:

 
  1. define('STRIP_RUNTIME_SPACE',false);    

则生成的编译缓存文件是没有经过去注释和空白的,仅仅是把文件合并到一起,这样的好处是便于调试的错误定位,建议部署模式的时候把上面的设置为True或者删除该定义。

注意事项
下面的一些问题需要引起注意,便于在开发过程中更快的解决问题:
1、编译缓存目录必须设置为可写
2、如果有修改核心文件,可能需要删除核心编译缓存~runtime.php

模块和操作
------------------------------------------------------------------
 

ThinkPHP里面会根据当前的URL来分析要执行的模块和操作。这个分析工作由URL调度器来实现,官方内置了Dispatcher来完成该调度。在Dispatcher调度器中,会根据
 

 
  1. Class UserAction extends Action{}   

你甚至不需要定义任何操作方法,就可以完成很多默认的操作,因为Action基础类已经为你定义了很多常用的操作方法。例如,我们可以执行(如果已经存在对应模板文件的情况)
 

 
  1. Class UserAction extends Action{      
  2.   // 定义一个select操作方法,注意操作方法不需要任何参数      
  3.  Public function select(){      
  4.   // select操作方法的逻辑实现      
  5.    // ……      
  6.    $this->display(); // 输出模板页面      
  7. }      
  8. }    

我们就可以执行

如果使用

 

 

ThinkPHP框架采用单一入口模式部署项目,所以,每个项目至少需要一个入口文件来执行项目。入口文件并不一定是指index.php文件,虽然通常都这么设置。一个项目的入口文件可以放置在WEB目录下面任何需要的位置,区别仅仅在于读取的ThinkPHP系统目录的路径就不同而已。
对于一个项目来说,入口文件其实没有做任何与项目本身相关的逻辑或者处理,而仅仅是实例化一个ThinkPHP项目实例而已,所以,建议不要在入口文件里面做过多的处理工作,以导致额外的维护工作量。一个入口文件的标准写法是(假设该入口文件是位于项目目录下面的index.php文件):

  1. // 常量定义      
  2. define('THINK_PATH''../ThinkPHP');       
  3. define('APP_NAME''Hello');      
  4. define('APP_PATH''.');      
  5. // 加载框架公共入口文件       
  6. require(THINK_PATH."/ThinkPHP.php");      
  7. // 实例化一个网站应用实例      
  8. $App = new App();       
  9. // 执行应用程序      
  10. $App->run();    

如果你把ThinkPHP所在路径添加到了PHP的include_path中,那么THINK_PATH可以无需定义。APP_NAME如果不定义,则默认为入口文件名。
无论你的项目有多么复杂或者多名简单,入口文件都是不变的,事实上,入口文件仅仅是实例化一个WEB应用而已,但是却必不可少。你可以为你的项目在多个位置定义多个入口文件,这并不违反框架的规则,而且也不影响项目的运行。
  再次强调,不要在项目入口文件里面添加任何逻辑处理代码。
项目入口文件的位置是可以随意的,原则上可以放置在web目录下面的任何位置。

URL模式
----------------------------------------------
 

ThinkPHP支持四种URL模式,可以通过设置URL_MODEL参数来定义,包括普通模式、PATHINFO、REWRITE和兼容模式。

普通模式 设置URL_MODEL 为0
采用传统的URL参数模式
http://serverName/appName/?m=module&a=action&id=1

PATHINFO模式 设置URL_MODEL 为1
默认情况使用URL_PATHINFO模式,ThinkPHP内置强大的PATHINFO支持,提供灵活和友好URL支持。PATHINFO模式还包括普通模式和智能模式两种:
普通模式 设置PATH_MODEL 参数为1
该模式下面URL参数没有顺序,例如
http://serverName/appName/m/module/a/action/id/1
http://serverName/appName/a/action/id/1/m/module
以上URL等效

智能模式 设置PATH_MODEL 参数为2 (系统默认的模式)
自动识别模块和操作,例如
http://serverName/appName/module/action/id/1/  或者
http://serverName/appName/module,action,id,1/
在智能模式下面,第一个参数会被解析成模块名称(或者路由名称,下面会有描述),第二个参数会被解析成操作(在第一个参数不是路由名称的前提下),后面的参数是显式传递的,而且必须成对出现,例如:
http://serverName/appName/module/action/year/2008/month/09/day/21/
其中参数之间的分割符号由PATH_DEPR参数设置,默认为"/",例如我们设置PATH_DEPR为"^"的话,就可以使用下面的URL访问:
http://serverName/appName/module^action^id^1/
注意不要使用"@" 和"&"符号进行分割,该符号有特殊用途,可能会导致其他的冲突。
略加修改,就可以展示出富有诗意的URL,呵呵~
如果想要简化URL的形式可以通过路由功能(后面会有描述),在PATH_INFO模式下面,会把相关参数转换成GET变量,以及并入REQUEST变量,因此不妨碍应用里面的以上变量获取。

REWRITE模式 设置URL_MODEL 为2
该URL模式和PATHINFO模式功能一样,除了可以不需要在URL里面写入口文件,和可以定义.htaccess 文件外。
例如,我们可以增加如下的.htaccess内容把所有操作都指向index.php文件。

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

兼容模式 设置URL_MODEL 为3
兼容模式是普通模式和PATHINFO模式的结合,并且可以让应用在需要的时候直接切换到PATHINFO模式而不需要更改模板和程序,基本上可以支持任何的运行环境。
只需要传入PATHINFO 兼容模式获取变量VAR_PATHINFO,默认值为s,例如
http://serverName/appName/?s=/module/action/id/1/ 
会执行和上面的URL等效的操作,并且也可以支持参数分割符号的定义,例如在PATH_DEPR设置为"~"的情况下,下面的URL有效:
http://serverName/appName/?s=module~action~id~1
兼容模式的情况下面模板文件不用做任何更改,保持和PATHINFO模式的写法一样,只需要在切换URL模式的时候清空下模板缓存目录。

URL路由
-------------------------------------------------------------------
 

ThinkPHP支持URL路由功能,要启用路由功能,需要设置ROUTER_ON 参数为true。开启路由功能后,系统会自动进行路由检测,如果在路由定义里面找到和当前URL匹配的路由名称,就会进行路由解析和重定向。路由功能需要定义路由定义文件,位于项目的配置目录下面,文件名为routes.php,定义格式:

  1. Return Array(      
  2. // 第一种方式 常规路由      
  3.  'RouteName'=>array('模块名称','操作名称','参数定义','额外参数'),      
  4. // 第二种方式 泛路由      
  5. ‘RouteName@’=>array(      
  6. array('路由匹配正则','模块名称','操作名称','参数定义','额外参数'),      
  7. ),      
  8. …更多的路由名称定义      
  9. )    

或者
 
  1. $_routes = Array(      
  2.  'RouteName'=>array('模块名称','操作名称','参数定义','额外参数'),      
  3. 'RouteName@'=>array(      
  4. array('路由匹配正则','模块名称','操作名称','参数定义','额外参数'),      
  5. ),      
  6. …更多的路由名称定义      
  7. )    

系统在执行Dispatch解析的时候,会判断当前URL是否存在定义的路由名称,如果有就会按照定义的路由规则来进行URL解析。例如,我们启用了路由功能,并且定义了下面的一个路由规则: 

 
  1. 'blog'=>array('Blog','index','year,month,day','userId=1&status=1')    

那么我们在执行
http://serverName/appName/blog/2007/9/15/的时候就会实际执行Blog模块的index操作,后面的参数/2007/9/15/ 就会依次按照 year/month/day 来解析,并且会隐含传入userId=1和status=1 两个参数。
另外一种路由参数的传入是
http://serverName/appName/?r=blog&year=2007&month=9&day=15,会执行上面相同的路由解析,该方式主要是提供不支持PATHINFO方式下面的兼容实现。其中r由VAR_ROUTER参数定义,默认是r。

泛路由支持
新版引入了泛路由支持,提供了对同一个路由名称的多个规则的支持,使得URL的设置更加灵活,例如,我们对Blog路由名称需要有多个规则的路由:

 
  1. 'Blog@'=>array(      
  2.  array('/^\/(\d+)(\/p\/\d)?$/','Blog','read','id'),      
  3.  array('/^\/(\d+)\/(\d+)/','Blog','archive','year,month'),      
  4.  ), 

第一个路由规则表示解析 Blog/123 这样的URL到Blog模块的read操作
第二个路由规则表示解析 Blog/2007/08 这样的URL到Blog模块的archive操作
泛路由的定义难度就在路由正则的定义上面,其它参数和常规路由的使用一致。

举个简单路由的例子,如果我们有一个City模块,而我们希望能够通过类似下面这样的URL地址来访问具体某个城市的操作:
http://serverName/index.php/City/shanghai/
shanghai这个操作方法是不存在的,我们给相关的城市操作定义了一个city方法,如下:

 
  1. Class CityAction extends Action{       
  2. public function city(){       
  3. // 读取城市名称       
  4. $cityName = $_GET['name'];       
  5. echo ('当前城市:'.$cityName);       
  6. }       
  7. }   

接下来我们来定义路由文件,实现类似于
http://serverName/index.php/City/shanghai/
这样的解析,路由文件名称是

 
  1. Return array(       
  2. 'City'=>array('City','city','name');       
  3. );   

这样,URL里面所有的City模块都会被路由到City模块的city操作,而后面的第二个参数会被解析成 $_GET[‘name’]
接下来,我们就可以在浏览器里面输入
http://serverName/index.php/City/beijing/
http://serverName/index.php/City/shanghai/
http://serverName/index.php/City/shenzhen/
会看到依次输出的结果是:
当前城市:beijing
当前城市:shanghai
当前城市:shenzhen


标签(Tag):控制器 执行流程 项目编译 模块和操作 默认模块和操作 入口文件 URL模式 URL路由
------分隔线----------------------------
推荐内容
  • thinkphp 自劢完成

    在Model类定义 $_auto 属性,可以完成数据自劢处理功能array(填充字...

  • ThinkPHP控制器

    控制器 执行流程 项目编译 模块和操作 默认模块和操作 入口文...

  • ThinkPHP类库

    ThinkPHP类库,ThinkPHP子目录列表,ThinkPHP命名约定,ThinkPHP系统基类库...

  • ThinkPHP调试配置

    ThinkPHP支持调试配置文件,如果启用了调试模式的话,那么会导...

  • ThinkPHP动态配置

    ThinkPHP的配置是动态的,在具体的Action方法里面,我们仍然可以...

  • ThinkPHP模块配置

    ThinkPHP还特别引入了模块配置文件的支持,支持对某些模块进行...

热点内容