spl_autoload的坑


莫名其妙的发现自动加载函数中抛出异常不会马上执行,就打算用简单的代码去重现下,还真的重现了。

文件有三个

– index.php

– a.class.php

– b.class.php

文件内容

<?php
class a extends b{
	public function __construct(){
		parent::__construct();
		echo 'in a<br>';
	}
}
<?php
namespace test;
echo 'file b.class.php';
class b{
	public function __construct(){
		echo 'in b<br>';
	}
}
<?php
function __autoload($classname){
	echo '__autoload 1<br>';
	var_dump($classname);
	$classname = substr($classname, strpos($classname, '') );
	if (!file_exists("{$classname}.class.php")){
		echo 'throw exception<br>';
		throw new Exception($classname . ' not found<br>');
	}
	require "{$classname}.class.php";
}
function __autoload2($classname){
	echo '__autoload 2<br>';
	var_dump($classname);
	$classname = substr($classname, strpos($classname, '') );
	if (!file_exists("{$classname}.class.php")){
		echo 'throw exception<br>';
		throw new Exception($classname . ' not found<br>');
	}
	require "{$classname}.class.php";
}
spl_autoload_register('__autoload');
spl_autoload_register('__autoload2');
$a = new a();

两个__autoload函数是一样的,在发现文件不存在的时候,都会抛出异常。执行结果如下:

__autoload 1
string ‘a’ (length=1)
__autoload 1
string ‘bb’ (length=2)
throw exception
__autoload 2
string ‘bb’ (length=2)
throw exception
__autoload 2
string ‘a’ (length=1)
__autoload 1
string ‘bb’ (length=2)
throw exception
__autoload 2
string ‘bb’ (length=2)
throw exception

Fatal error: Uncaught exception ‘Exception’ with message ‘bb not found<br>’ in D:wampwwwtestindex.php on line 18
Exception: bb not found<br> in D:wampwwwtestindex.php on line 18

仔细看看结果,各种理解不能。第5行的时候,抛出异常,被忽略了,没抛出。然后,为什么A2(__autoload2)又加载一遍a呢?然后,A1(__autoload)又加载了一遍bb,至此,A1已经加载两次bb了。然后,A1又加载了一遍bb。最后才抛出异常,看行号是A2函数抛出来的。

然后,上边有看到A2在A1已经加载过一次a的时候,又加载了一次a。而a文件中明显不能被require两次啊!否则会报class重复定义的错误的啊!!!可结果呢,没报。。

这个其实是说明了另外一个问题:如果PHP没完成定义类的所有步骤,就不算定义了一个类。比如,a的定义就需要把继承的父类找到,在找父类的时候失败了,a就算没定义成功。所以,来回定义a都是可以的,因为你没定义成功嘛。。。。

然后,继续猜测如上所有步骤:

A1接到加载a的通知,找到a.class.php文件并包含,发现bb找不到,A1又去找bb,发现找不到,抛出异常,因为存在两个自动加载函数,第一个找不到的时候,第二个上,所以这个时候A2去找bb,同样找不到,抛出异常。此时,你可能会感觉这不结束了么?两个自动加载函数都找不到的类,并且都抛出异常了,该结束了。可实际上,类a的自动加载并没有成功(我是这么理解的,可能不对,后边说),A2会再去尝试加载一次类a,然后,同样发现类bb,这个时候,针对bb的自动加载还是从A1开始,然后A2,最终,A2也没能找到bb。所以,加载类a的这个过程终结了,异常憋了半天终于抛出来来。

上边说,A2在找不到bb的时候是因为类a的加载并没有完成,所以才没抛出异常的。我就尝试修改了下测试代码验证这个问题。

<?php
class a extends b{
	public function __construct(){
		parent::__construct();
		echo 'in a<br>';
	}
}
<?php
<?php
function __autoload($classname){
	echo '__autoload 1<br>';
	var_dump($classname);
	$classname = substr($classname, strpos($classname, '') );
	if (!file_exists("{$classname}.class.php")){
		echo 'throw exception<br>';
		throw new Exception($classname . ' not found<br>');
	}
	echo 'include ', $classname, '<br>';
	require "{$classname}.class.php";
}
function __autoload2($classname){
	echo '__autoload 2<br>';
	var_dump($classname);
	$classname = substr($classname, strpos($classname, '') );
	if (!file_exists("{$classname}.class.php")){
		echo 'throw exception<br>';
		throw new Exception($classname . ' not found<br>');
	}
	echo 'include ', $classname, '<br>';
	require "{$classname}.class.php";
}
spl_autoload_register('__autoload');
spl_autoload_register('__autoload2');
$a = new a();

结果如下:

__autoload 1
string ‘a’ (length=1)
include a
__autoload 1
string ‘b’ (length=1)
include b
__autoload 2
string ‘b’ (length=1)
include b

Fatal error: Class ‘b’ not found in D:wampwwwtesta.class.php on line 2

可以看到,跟我最开始想的结果一样,当两个自动加载函数都找不到一个类的时侯,那么就报错。然后,我想了想又改了下index.php。接着上代码:

<?php
function __autoload($classname){
	echo '__autoload 1<br>';
	var_dump($classname);
	$classname = substr($classname, strpos($classname, '') );
	if (!file_exists("{$classname}.class.php")){
		echo 'throw exception<br>';
		throw new Exception($classname . ' not found<br>');
	}
	echo 'include ', $classname, '<br>';
	require "{$classname}.class.php";
	if (!class_exists($classname, false)){
		echo 'throw exception2<br>';
		throw new Exception($classname . ' not found2<br>');
	}
}
function __autoload2($classname){
	echo '__autoload 2<br>';
	var_dump($classname);
	$classname = substr($classname, strpos($classname, '') );
	if (!file_exists("{$classname}.class.php")){
		echo 'throw exception<br>';
		throw new Exception($classname . ' not found<br>');
	}
	echo 'include ', $classname, '<br>';
	require "{$classname}.class.php";
	if (!class_exists($classname, false)){
		echo 'throw exception2<br>';
		throw new Exception($classname . ' not found2<br>');
	}
}
spl_autoload_register('__autoload');
spl_autoload_register('__autoload2');
$a = new a();

我在自动加载函数后边判断了下类是否存在,不存在还抛出异常。

结果如下:

__autoload 1
string ‘a’ (length=1)
include a
__autoload 1
string ‘b’ (length=1)
include b
throw exception2
__autoload 2
string ‘b’ (length=1)
include b
throw exception2
__autoload 2
string ‘a’ (length=1)
include a
__autoload 1
string ‘b’ (length=1)
include b
throw exception2
__autoload 2
string ‘b’ (length=1)
include b
throw exception2

Fatal error: Uncaught exception ‘Exception’ with message ‘b not found2<br>’ in D:wampwwwtestindex.php on line 29
Exception: b not found2<br> in D:wampwwwtestindex.php on line 29

结果又跟版本1一样了。。。我就又改了下代码:

<?php
function __autoload($classname){
	echo '__autoload 1<br>';
	var_dump($classname);
	$classname = substr($classname, strpos($classname, '') );
	if (!file_exists("{$classname}.class.php")){
		echo 'throw exception<br>';
		throw new Exception($classname . ' not found<br>');
	}
	echo 'include ', $classname, '<br>';
	require "{$classname}.class.php";
	if (!class_exists($classname, false)){
		echo 'throw exception2<br>';
// 		throw new Exception($classname . ' not found2<br>');
	}
}
function __autoload2($classname){
	echo '__autoload 2<br>';
	var_dump($classname);
	$classname = substr($classname, strpos($classname, '') );
	if (!file_exists("{$classname}.class.php")){
		echo 'throw exception<br>';
		throw new Exception($classname . ' not found<br>');
	}
	echo 'include ', $classname, '<br>';
	require "{$classname}.class.php";
	if (!class_exists($classname, false)){
		echo 'throw exception2<br>';
// 		throw new Exception($classname . ' not found2<br>');
	}
}
spl_autoload_register('__autoload');
spl_autoload_register('__autoload2');
$a = new a();

就是把抛出异常的代码注释掉了。结果如下:

__autoload 1
string ‘a’ (length=1)
include a
__autoload 1
string ‘b’ (length=1)
include b
throw exception2
__autoload 2
string ‘b’ (length=1)
include b
throw exception2

Fatal error: Class ‘b’ not found in D:wampwwwtesta.class.php on line 2

可以看出autoload在处理函数中是否有抛出异常上跟其他函数是不一样的。而官网也没看到相关说明啊。。。

最终,我只能猜测:

存在多个autuload函数的时候,每加载一个类,只要前边的函数没执行完(包括最后抛出异常),那么就算此类没加载成功,就会有后边的autoload函数继续加载。

 


发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注