thinkPHP框架中的引导类(核心类)Think.class.php是一个非常重要的类。该类主要包含如下属性和方法:
该类主要在公告入口文件ThinkPHP.php中的最后一行被引用。
// 加载核心Think类 require CORE_PATH.'Think'.EXT; // 应用初始化 Think\Think::start();所以我们从strat方法开始分析。
static public function start() { // 注册AUTOLOAD方法 spl_autoload_register('Think\Think::autoload'); // 设定错误和异常处理 register_shutdown_function('Think\Think::fatalError'); set_error_handler('Think\Think::appError'); set_exception_handler('Think\Think::appException'); // 初始化文件存储方式 Storage::connect(STORAGE_TYPE); $runtimefile = RUNTIME_PATH.APP_MODE.'~runtime.php'; if(!APP_DEBUG && Storage::has($runtimefile)){ Storage::load($runtimefile); }else{ if(Storage::has($runtimefile)) Storage::unlink($runtimefile); $content = ''; // 读取应用模式 $mode = include is_file(CONF_PATH.'core.php')?CONF_PATH.'core.php':MODE_PATH.APP_MODE.'.php'; // 加载核心文件 foreach ($mode['core'] as $file){ if(is_file($file)) { include $file; if(!APP_DEBUG) $content .= compile($file); } } // 加载应用模式配置文件 foreach ($mode['config'] as $key=>$file){ is_numeric($key)?C(load_config($file)):C($key,load_config($file)); } // 读取当前应用模式对应的配置文件 if('common' != APP_MODE && is_file(CONF_PATH.'config_'.APP_MODE.CONF_EXT)) C(load_config(CONF_PATH.'config_'.APP_MODE.CONF_EXT)); // 加载模式别名定义 if(isset($mode['alias'])){ self::addMap(is_array($mode['alias'])?$mode['alias']:include $mode['alias']); } // 加载应用别名定义文件 if(is_file(CONF_PATH.'alias.php')) self::addMap(include CONF_PATH.'alias.php'); // 加载模式行为定义 if(isset($mode['tags'])) { Hook::import(is_array($mode['tags'])?$mode['tags']:include $mode['tags']); } // 加载应用行为定义 if(is_file(CONF_PATH.'tags.php')) // 允许应用增加开发模式配置定义 Hook::import(include CONF_PATH.'tags.php'); // 加载框架底层语言包 L(include THINK_PATH.'Lang/'.strtolower(C('DEFAULT_LANG')).'.php'); if(!APP_DEBUG){ $content .= "\nnamespace { Think\\Think::addMap(".var_export(self::$_map,true).");"; $content .= "\nL(".var_export(L(),true).");\nC(".var_export(C(),true).');Think\Hook::import('.var_export(Hook::get(),true).');}'; Storage::put($runtimefile,strip_whitespace('<?php '.$content)); }else{ // 调试模式加载系统默认的配置文件 C(include THINK_PATH.'Conf/debug.php'); // 读取应用调试配置文件 if(is_file(CONF_PATH.'debug'.CONF_EXT)) C(include CONF_PATH.'debug'.CONF_EXT); } } // 读取当前应用状态对应的配置文件 if(APP_STATUS && is_file(CONF_PATH.APP_STATUS.CONF_EXT)) C(include CONF_PATH.APP_STATUS.CONF_EXT); // 设置系统时区 date_default_timezone_set(C('DEFAULT_TIMEZONE')); // 检查应用目录结构 如果不存在则自动创建 if(C('CHECK_APP_DIR')) { $module = defined('BIND_MODULE') ? BIND_MODULE : C('DEFAULT_MODULE'); if(!is_dir(APP_PATH.$module) || !is_dir(LOG_PATH)){ // 检测应用目录结构 Build::checkDir($module); } } // 记录加载文件时间 G('loadTime'); // 运行应用 App::run(); }
spl_autoload_register
将函数注册到SPL __autoload函数队列中。如果该队列中的函数尚未激活,则激活它们。
如果在你的程序中已经实现了__autoload()函数,它必须显式注册到__autoload()队列中。因为 spl_autoload_register()函数会将Zend Engine中的__autoload()函数取代为spl_autoload()或spl_autoload_call()。
如果需要多条 autoload 函数,spl_autoload_register() 满足了此类需求。 它实际上创建了 autoload 函数的队列,按定义时的顺序逐个执行。相比之下, __autoload() 只可以定义一次。
所以我们只需要看autoload()函数。下面register_shutdown_function和set_error_handler、set_exception_handler 都只需要看里里面的函数即可。autoload函数实现了类库的自动加载,下面我详细的分析下autoload函数。
public static function autoload($class) { // 检查是否存在映射 //首先在加载未知的类时,根据类名检测在静态变量$_map中是否存在这样的类,存在的话,就不用再次加载了 if (isset(self::$_map[$class])) { include self::$_map[$class]; //判断在实例化类时是否存在反斜杠,不存在的话,直接。。。。。 } elseif (false !== strpos($class, '\\')) { //strstr()函数 详解(因为本人在这个函数上浪费过时间,故而提醒广大朋友) // $str="dsdfsd/mfg"; //strstr("/",$str);找到 / 在字符串第一次出现的位置,并返回剩余的部分。结果为 /mfg //strstr("/",$str,true);找到 / 在字符串第一次出现的位置,并返回之前的部分 dsdfsd。 //所以这个true还是很关键的。 $name = strstr($class, '\\', true); //如果返回的$name 在以下array('Think', 'Org', 'Behavior', 'Com', 'Vendor')中或者在ThinkPHP/Library 中会自动定位 // 再结合下面的代码 可以判断 // new Think\Page() (前提对应目录下存在这样的类库) // new Vendor\Page() // 像这样形式的实例化应该可以加载类库,但是在thinkphp中还是报错。 new \Think\Page() 这样才会正常 // 当时我看到这段代码感觉 new Think\Page() 符合要求啊,于是我百思不得其姐。最后将问题定位到命名空间请看 //下面的命名空间讲解。我看完就明白了。 //最后我得出结论:autoload($class) 中参数$class 是结合命名空间生成的 //new Think\Page() 这样实例化的话,生成的$class 是带上命名空间的 如 Home\Controller\Think\Page if (in_array($name, array('Think', 'Org', 'Behavior', 'Com', 'Vendor')) || is_dir(LIB_PATH . $name)) { // Library目录下面的命名空间自动定位 $path = LIB_PATH; } else { // 检测自定义命名空间 否则就以模块为命名空间 $namespace = C('AUTOLOAD_NAMESPACE'); $path = isset($namespace[$name]) ? dirname($namespace[$name]) . '/' : APP_PATH; } $filename = $path . str_replace('\\', '/', $class) . EXT; if (is_file($filename)) { // Win环境下面严格区分大小写 if (IS_WIN && false === strpos(str_replace('/', '\\', realpath($filename)), $class . EXT)) { return; } include $filename; } //关闭命名空间,可以自行探索 } elseif (!C('APP_USE_NAMESPACE')) { // 自动加载的类库层 foreach (explode(',', C('APP_AUTOLOAD_LAYER')) as $layer) { if (substr($class, -strlen($layer)) == $layer) { if (require_cache(MODULE_PATH . $layer . '/' . $class . EXT)) { return; } } } // 根据自动加载路径设置进行尝试搜索 foreach (explode(',', C('APP_AUTOLOAD_PATH')) as $path) { if (import($path . '.' . $class)) // 如果加载类成功则返回 return; } } }
这里使用了私有静态变量$_map,只能在类的内部访问,而不能在类的外部访问。静态成员只保留一个变量值,而这个变量值对所有的实例都是有效,也就是说,所有的实例共享这个成员。 这个变量中存放着已经加载过的类的名称。当然thinkphp初始化的时候,会自动加载一些类。至于如何自动加载,暂时留一个疑问?我们接着讲解autoload函数。
命名空间讲解:
时间有限,先写这些。