如果有人读这篇文章并跟着做的话,希望你能使用支持函数跳转的编辑器,还要善用var_dump和exit,对着源码去调试着看。跟着入口文件读,执行到哪里你看到哪里,对于那些不能一眼看出来的配置,则要记录下来,可能一个比较简单的功能会写出很长的代码,这个时候难免会看到后面忘了前面。
那么进入正题,从index.php文件可以看到入口文件只定义了几项常量作为配置,紧接着就引入了require './ThinkPHP/ThinkPHP.php';
1 // 检测PHP环境 2 if(version_compare(PHP_VERSION,'5.3.0','<')) die('require PHP > 5.3.0 !'); 3 4 // 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false 5 define('APP_DEBUG',True); 6 7 // 定义应用目录 8 define('APP_PATH','./Application/'); 9 10 // 引入ThinkPHP入口文件 11 require './ThinkPHP/ThinkPHP.php';
在ThinkpPHP文件依然比较简单,tp定义了一些常量配置项(defined函数的写法让之前在index入口文件里定义的配置项不会被重置)记录了运行时间和内存使用信息,进行了php版本的判断,以及cli命令行模式的判断。并在文件末尾再次引入了Think核心类,并进行了初始化。
require CORE_PATH.'Think'.EXT;路径为ThinkPHP\Library\Think\Think.class.php
这个think类就比较长了,一开始就定义了$_map , $_instance两个数组,其中$_map作为映射数组使用,think类在会把核心模块的路劲存在这个数组里。$_instance则存储了系统运行时所实例化的对象
1 // 类映射 2 private static $_map = array(); 3 4 // 实例化对象 5 private static $_instance = array();
刚刚的入口文件执行了start方法来运行系统。这个start方法则一开始就通过spl_autoload_register方法注册了自动加载函数。(php本身有一些魔术方法,在执行某些方法,或变量时,如果它找不到这个方法或变量,就会执行相应的魔术方法,tp便是用自己的自动引入方法替换了该方法,达到不需要引入,直接new对象,系统便会自动引入该类文件的目的)
1 static public function start() { 2 // 注册AUTOLOAD方法 3 spl_autoload_register('Think\Think::autoload'); 4 // 设定错误和异常处理 5 register_shutdown_function('Think\Think::fatalError'); 6 set_error_handler('Think\Think::appError'); 7 set_exception_handler('Think\Think::appException'); 8 9 // 初始化文件存储方式 10 Storage::connect(STORAGE_TYPE); 11 ················
我们往下翻看,autoload方法传入了一个$class类名,然后便在类的$_map里检测是否存在该类的映射,如果有,则表明是系统核心模块直接通过存储的地址include引入;反之则会判断$class是否为带有命名空间的路径字符串,然后通过strstr函数分割字符串获得命名空间前缀,判断是否为tp系统定义的命名空间,然后便通过之前定义的常量来确定文件路径。同时判断是否为win环境,进行大小写的匹配,然后include引入;
1 public static function autoload($class) { 2 // 检查是否存在映射 3 if(isset(self::$_map[$class])) { 4 include self::$_map[$class]; 5 }elseif(false !== strpos($class,'\\')){ 6 $name = strstr($class, '\\', true); 7 if(in_array($name,array('Think','Org','Behavior','Com','Vendor')) || is_dir(LIB_PATH.$name)){ 8 // Library目录下面的命名空间自动定位 9 $path = LIB_PATH; 10 }else{ 11 // 检测自定义命名空间 否则就以模块为命名空间 12 $namespace = C('AUTOLOAD_NAMESPACE'); 13 $path = isset($namespace[$name])? dirname($namespace[$name]).'/' : APP_PATH; 14 } 15 $filename = $path . str_replace('\\', '/', $class) . EXT; 16 if(is_file($filename)) { 17 // Win环境下面严格区分大小写 18 if (IS_WIN && false === strpos(str_replace('/', '\\', realpath($filename)), $class . EXT)){ 19 return ; 20 } 21 include $filename; 22 } 23 }elseif (!C('APP_USE_NAMESPACE')) { 24 // 自动加载的类库层 25 foreach(explode(',',C('APP_AUTOLOAD_LAYER')) as $layer){ 26 if(substr($class,-strlen($layer))==$layer){ 27 if(require_cache(MODULE_PATH.$layer.'/'.$class.EXT)) { 28 return ; 29 } 30 } 31 } 32 // 根据自动加载路径设置进行尝试搜索 33 foreach (explode(',',C('APP_AUTOLOAD_PATH')) as $path){ 34 if(import($path.'.'.$class)) 35 // 如果加载类成功则返回 36 return ; 37 } 38 } 39 }