Skip to content

Commit 3c67ff6

Browse files
committed
update
1 parent 343fd56 commit 3c67ff6

File tree

2 files changed

+42
-8
lines changed

2 files changed

+42
-8
lines changed

3/zend_autoload.md

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
### 3.4.7 类的自动加载
22
在实际使用中,通常会把一个类定义在一个文件中,然后使用时include加载进来,这样就带来一个问题:在每个文件的头部都需要包含一个长长的include列表,而且当文件名称修改时也需要把每个引用的地方都改一遍,另外前面我们也介绍过,原则上父类需要在子类定义之前定义,当存在大量类时很难得到保证,因此PHP提供了一种类的自动加载机制,当使用未被定义的类时自动调用类加载器将类加载进来,方便类的同一管理。
33

4-
PHP中提供了两种方式实现自动加载:`__autoload()``spl_autoload_register()`,两种方式差别不大,我们先介绍下他们的用法再分析PHP中的实现原理
4+
在内核实现上类的自动加载实际就是定义了一个钩子函数,实例化类时如果在EG(class_table)中没有找到对应的类则会调用这个钩子函数,调用完以后再重新查找一次。这个钩子函数保存在EG(autoload_func)中
55

6-
__(1)__autoload():__
6+
PHP中提供了两种方式实现自动加载:`__autoload()``spl_autoload_register()`
77

8-
用户自定义一个`__autoload()`函数即可,参数是类名,当实例化一个类是如果没有找到这个类则会调用此函数,所以我们就可以在这个函数中进行类的加载,比如:
8+
***(1)__autoload():***
9+
10+
这种方式比较简单,用户自定义一个`__autoload()`函数即可,参数是类名,当实例化一个类是如果没有找到这个类则会查找用户是否定义了`__autoload()`函数,如果定义了则调用此函数,比如:
911
```php
1012
//文件1:my_class.php
1113
<?php
@@ -26,7 +28,7 @@ var_dump($obj);
2628

2729
__(2)spl_autoload_register():__
2830

29-
`spl_autoload_register()`提供了更加灵活的注册方式,可以支持任意数量的加载器,比如第三方库加载规则不可能保持一致,这样就可以通过此函数注册自己的加载器了。
31+
相比`__autoload()`只能定义一个加载器,`spl_autoload_register()`提供了更加灵活的注册方式,可以支持任意数量的加载器,比如第三方库加载规则不可能保持一致,这样就可以通过此函数注册自己的加载器了,在实现上spl创建了一个队列来保存用户注册的加载器,然后定义了一个spl_autoload函数到EG(autoload_func),当找不到类时内核回调spl_autoload,这个函数再依次调用用户注册的加载器,没调用一个重新检查下查找的类是否在EG(class_table)中已经注册,仍找不到的话继续调用下一个加载器,直到类成功注册为止
3032

3133
```c
3234
bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
@@ -50,6 +52,38 @@ var_dump($obj);
5052
```
5153
这个例子执行时就会将autoload_one()、autoload_two()都调一遍,假如第一个函数就成功注册了my_class类则不会再调后面的加载器。
5254

53-
___
54-
55-
55+
内核查找类通过`zend_lookup_class_ex()`完成,我们简单看下其处理过程。
56+
```c
57+
//file: zend_execute_API.c
58+
ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zval *key, int use_autoload)
59+
{
60+
...
61+
//从EG(class_table)符号表找类的zend_class_entry,如果找到说明类已经编译,直接返回
62+
ce = zend_hash_find_ptr(EG(class_table), lc_name);
63+
if (ce) {
64+
if (!key) {
65+
zend_string_release(lc_name);
66+
}
67+
return ce;
68+
}
69+
...
70+
//如果没有通过spl注册则看下是否定义了__autoload()
71+
if (!EG(autoload_func)) {
72+
zend_function *func = zend_hash_str_find_ptr(EG(function_table), "__autoload", sizeof("__autoload") - 1);
73+
if (func) {
74+
EG(autoload_func) = func;
75+
} else {
76+
return NULL;
77+
}
78+
}
79+
...
80+
fcall_cache.function_handler = EG(autoload_func);
81+
...
82+
//调用EG(autoload_func)函数,然后再查一次EG(class_table)
83+
if ((zend_call_function(&fcall_info, &fcall_cache) == SUCCESS) && !EG(exception)) {
84+
ce = zend_hash_find_ptr(EG(class_table), lc_name);
85+
}
86+
...
87+
}
88+
```
89+
SPL的具体实现比较简单,这里不再介绍。

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
* [3.4.4 动态属性](3/zend_prop.md)
3333
* [3.4.5 魔术方法](3/zend_magic_method.md)
3434
* 3.4.6 抽象类和接口
35-
* 3.4.7 类的自动加载
35+
* [3.4.7 类的自动加载](3/zend_autoload.md)
3636
* [3.5 运行时缓存](3/zend_runtime_cache.md)
3737
* [第4章 PHP基础语法实现](4/php_language.md)
3838
* 4.1 变量

0 commit comments

Comments
 (0)