知识预备
写在前面
tp3.2实现的自动加载可以通过命名空间自动定位到类文件,实现这样的效果除了合理的处理手段外,比较有规律的项目结构也是必不可少的。- 源码(ThinkPHP/Library/Think/Think.class.php):
- 注册自动加载函数
- 源码(ThinkPHP/Library/Think/Think.class.php):
public static function start()
{
// 注册AUTOLOAD方法
spl_autoload_register('Think\Think::autoload');
.............................
- autoload方法:
public static function autoload($class)
{
// 检查是否存在映射
if (isset(self::$_map[$class])) {
include self::$_map[$class];
} elseif (false !== strpos($class, '\\')) {
$name = strstr($class, '\\', true);
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;
}
}
}
}
- 自动加载:
tp通过spl_aotuload_register()注册atuoload()方法,并且将当前引用类的类名传入:
- 类库映射:
tp支持给类文件别名,实现通过这个别名去映射这个类文件的路径,例如:
- 类库映射:
Think\Think::addMap('Think\Log',THINK_PATH.'Think\Log.php');
Think\Think::addMap('Org\Util\Array',THINK_PATH.'Org\Util\Array.php');
addMap():
// 注册classmap
public static function addMap($class, $map = '')
{
if (is_array($class)) {
self::$_map = array_merge(self::$_map, $class);
} else {
self::$_map[$class] = $map;
}
}
我们可以看到,执行addMap()就会给这个类的私有属性$_map[$class]添加成员,而且从autoload()方法代码执行顺序来看,是先加载类库映射的。
- 获取类文件所属目录
如果不存在类库映射,tp将自己去搜索这个类的路径,首先我们要知道这个常量 LIB_PATH,这个常理是在框架的入口文件中定义的(ThinkPHP/ThinkPHP.php) :
defined('LIB_PATH') or define('LIB_PATH', realpath(THINK_PATH . 'Library') . '/'); // 系统核心类库目录
tp的自动加载就是以这个常量所定义的路径作为参照点的,这个常量里面又包含另外一个THINK_PATH常量,这个常量是这样定义的
defined('THINK_PATH') or define('THINK_PATH', __DIR__ . '/');
这样LIB_PATH这个常量就是指的你的框架Library目录下面了。明白这个常量的含义后我们在回到tp的autoload()这个方法中:首先判断传入的类名有没有’\’(注意,第一个’\’是转义符),如果有,获取‘\’前面的字符,判断是不是Library下面目录,如果是,这个类文件的目录就是LIB_PATH这个常量所指的目录。现在有一个疑问?为什么传入的类名‘/’前面的字符可以实现路径的查找呢?其实这就和tp命名空间的规范很有关系了,因为在tp中,命名空间的名字和当前文件的所在相对目录是一致的,打个比方:
比如说现在的Think.class.php的目录是在 :“ThinkPHP/Library/Think/”下面,那么这个类所在的命名空间就是“Think”,假如我们尝试在Home/Index/index实例一个图像类,并且在自动加载函数里打印tp自动加载的类库,首先实例化图像类
我们知道,new Image()是Think命名空间下的,这个名称是非限定名称,编译是相当于是 new Think\Image() ,我们可以在加载方法中打印来看:
// 检查是否存在映射
if (isset(self::$_map[$class])) {
include self::$_map[$class];
} elseif (false !== strpos($class, '\\')) {
echo $class."<hr/>"; //打印加载类文件
$name = strstr($class, '\\', true);
if (in_array($name, array('Think', 'Org', 'Behavior', 'Com', 'Vendor')) || is_dir(LIB_PATH . $name)) {
........................
传过来的Think/Image这个类 ‘/’ 的前面的命名空间其实就是当前类所在的相对位置,所有tp通过这个命名空间来定位(命名空间根空间和目录一致)。
源码读到下面我就有点懵逼了……..
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;
}
这个
$namespace = C('AUTOLOAD_NAMESPACE');
我实在没找到是在哪里配置的,换句话讲,跑到Library目录以外怎么去自动加载就不知道了,先留个坑在这里以后来填,有知道的博友希望能够不吝赐教,感激不尽!
(残次文还是发表了吧………)