[TOC]
* * * * *
## 1 文件加载的意义
大型项目工程中,通常将项目的功能组织**以文件进行组织**,并使用目录进行划分。
为了**使用不同文件中定义的函数,类等**,需要加载目标文件。
因此文件加载是**项目工程分文件组织**后,**加载功能目标文件**的方法。
## 2 文件的手动加载
>[info] 1 四个手动加载语言结构
* * * * *
**inculde**与**require**是php用来手动加载文件的函数,
**include_once**与**require_once**是限定同一个文件只被加载一次
1 首先按照参数给出的路径查找
2 没有给出目录,从include_path指定目录查找
3 include_path指定目录查找失败,在调用脚本文件所在目录和当前工作目录下查找
4 最后加载失败时,include发出一条警告,而require会发出致命错误
这四个是**语言构造器或者说是语言结构**,通常后面**接一个字符串**。
与普通函数不同,普通函数后面接带参数的大括号。
>[info] 2 示例
* * * * *
exampe1:基本的include例子。
~~~
vars.php
<?php
$color = 'green' ;
$fruit = 'apple' ;
?>
test.php
<?php
echo "A $color $fruit " ;
// A
include 'vars.php' ;
echo "A $color $fruit " ;
// A green apple
?>
~~~
>[danger] include等加载调用出现在一个函数里,则加载文件包含的代码中的普通变量的作用域范围就是该函数,其中的魔术变量是全局范围。
example2:函数中include文件
~~~
<?php
function foo ()
{
global $color ;
include 'vars.php' ;
echo "A $color $fruit " ;
}
/*vars.php中的$furit的作用域在foo()函数中,因此
因此foo()函数正常输出$fruit
echo语句中的$fruit为空
$color 声明为全局变量 ,因此$color的作用域在全局。
foo() echo都输出$color。
*/
foo ();
// A green apple
echo "A $color $fruit " ;
// A green
?>
~~~
>[info] 3 加载文件模式切换
加载文件时,php语法解析器会**从php模式切换到html模式**,**加载完文件后恢复到php模式**。因此也可以用来加载html文件,**其中的php代码必须包含在php的起始与结束标记中**
>[info] 4 url加载远程文件
php.ini的配置中URL fopen_wrappers激活时,可以使用url加载网络服务器文件进行解释。**与加载本地文件不同的是模板文件如果作为php代码解释,已经在服务器运行了,本地脚本只是包含其输出结果**。通常使用其他的文件打开函数如readfile()等更好
example #3 通过 HTTP 进行的 include
~~~
<?php
include 'http://www.example.com/file.txt?foo=1&bar=2' ;
//txt文件不会被运行
include 'file.php?foo=1&bar=2' ;
//查找本地file.php?foo=1&bar=2文件,而不是运行file.php
include 'http://www.example.com/file.php?foo=1&bar=2' ;
// 查找远程服务器的file.php,并以foo=1&bar=2参数运行。
$foo = 1 ;
$bar = 2 ;
include 'file.txt' ;
//加载本地file.txt文件
include 'file.php' ;
//加载本地file.php文件
?>
~~~
>[info] 5 加载返回值检测
include等加载失败时返回false,并发出警告,成功则返回1,除非包含文件使用return等给出了返回值。
需要注意的是include 不是函数,其参数不需要括号
example #4 比较 include 的返回值
~~~
<?php
//错误的检查返回值格式,
if (include( 'vars.php' ) == 'OK' ) {
echo 'OK' ;
}
//正确的检查其返回值格式
if ((include 'vars.php' ) == 'OK' ) {
echo 'OK' ;
}
?>
~~~
可以在目标文件中**使用return语句可以终止文件的加载,并返回调用的脚本**。thinkphp的默认**数组配置就是使用这个功能返回一个数组**。
example #5 include 和 return 语句
~~~
return.php
<?php
$var = 'PHP' ;
return $var ;
?>
noreturn.php
<?php
$var = 'PHP' ;
?>
testreturns.php
<?php
$foo = include 'return.php' ;
echo $foo ; // prints 'PHP'
$bar = include 'noreturn.php' ;
echo $bar ; // prints 1
?>
~~~
>[info] 6 加载包含函数的文件
包含文件中定义了函数,不管是return之前还是之后定义的,都可以独立在主文件中使用。
**php5会对重复加载文件时出现的函数重复定义发出致命错误报告**。php4不会对return之后定义的函数报错。**推荐使用include_once加载此类文件**
>[info] 7 tp5.0中手动加载的使用
~~~
index.php:
require __DIR__ . '/../thinkphp/start.php';
~~~
~~~
start.php:
require __DIR__ . '/base.php';
require CORE_PATH . 'Loader.php';
~~~
~~~
App.php
public static function run(){
...
if (!empty($config['extra_file_list'])) {
foreach ($config['extra_file_list'] as $file) {
$file = strpos($file, '.') ? $file : APP_PATH . $file . EXT;
if (is_file($file)) {
include_once $file;
}
}
}
...
}
private static function initModule($module, $config){
$module = (COMMON_MODULE == $module || !APP_MULTI_MODULE) ? '' : $module . DS;
if (is_file(APP_PATH . $module . 'init' . EXT)) {
include APP_PATH . $module . 'init' . EXT;
} else {
$path = APP_PATH . $module;
$config = Config::load(APP_PATH . $module . 'config' . EXT);
if ($config['app_status']) {
$config = Config::load(APP_PATH . $module . $config['app_status'] . EXT);
}
if ($config['extra_config_list']) {
foreach ($config['extra_config_list'] as $name => $file) {
$file = strpos($file, '.') ? $file : $path . $file . EXT;
Config::load($file, is_string($name) ? $name : pathinfo($file, PATHINFO_FILENAME));
}
}
if (is_file($path . 'alias' . EXT)) {
Loader::addMap(include $path . 'alias' . EXT);
}
if (APP_HOOK && is_file($path . 'tags' . EXT)) {
Hook::import(include $path . 'tags' . EXT);
}
if (is_file($path . 'common' . EXT)) {
include $path . 'common' . EXT;
}
if ($config['lang_switch_on'] && $module) {
Lang::load($path . 'lang' . DS . LANG_SET . EXT);
}
}
}
~~~
## 3 文件的自动加载
在php引入面向对象编程思想后,
开发者习惯将每个类的定义建立一个php文件。
使用这些类需要使用require加载需要的类文件。
为了简化类的加载,php中引入了类的自动加载
>[info] 1 __autoload
在php5中可以通过定义一个__auload()函数,
使用未被定义的类时自动调用。
通过调用此函数,脚本引擎可以加载目标类文件
~~~
<?php
function __autoload ( $class_name ) {
require_once $class_name . '.php' ;
}
$obj = new MyClass1 ();
$obj2 = new MyClass2 ();
//在上面创建对象时,会调用__autoload,使用require加载对应的Myclass1.php类文件和MyClass2.php类文件。
?>
~~~
加载的异常处理
~~~
<?php
function __autoload ( $name ) {
echo "Want to load $name .\n" ;
throw new Exception ( "Unable to load $name ." );
}
try {
$obj = new NonLoadableClass ();
} catch ( Exception $e ) {
echo $e -> getMessage (), "\n" ;
}
?>
~~~
输出:
Want to load NonLoadableClass.
Unable to load NonLoadableClass.
提示类加载失败
5.3+版本后,__autoload 函数抛出的异常,可以被 catch 语句块捕获。
抛出的是一个自定义异常,那么必须存在相应的自定义异常类
__autoload 函数可以递归的自动加载自定义异常类。
~~~
<?php
function __autoload ( $name ) {
echo "Want to load $name .\n" ;
throw new MissingException ( "Unable to load $name ." );
}
try {
$obj = new NonLoadableClass ();
} catch ( Exception $e ) {
echo $e -> getMessage (), "\n" ;
}
?>
~~~
输出:
Want to load NonLoadableClass.
Want to load MissingException.Fatal error: Class 'MissingException' not found in testMissingException.php on line 4
异常类查找失败
>[info] 2 spl_autoload
SPL提供了一个__autoload()函数默认实现。
如果不使用任何参数调用 autoload_register() 函数,则以后在进行 __autoload() 调用时会自动使用此函数
`void spl_autoload ( string $class_name [, string $file_extensions ] )`
> $class_name 加载的类名
> $file_extensions 文件后缀,默认为.inc或.php
* * * * *
>[info] 3 spl_autoload_register
注册给定的函数作为 __autoload 的实现,并将函数注册到SPL __autoload函数队列中
该函数会将__autoload()函数取代为lspl_autoload()或者spl_autoload_call()
因此程序中实现的__autoload()函数,需要使用该函数注册到SPL __autoload队列中
需要多条autoload函数,使用该函数。
实际上创建了autoload函数队列,按照定义逐个执行
而__autoload()只可定义一个
`bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )`
> $autoload_function 自动加载函数,省略则为spl_autoload
> $throw 注册失败时,是否抛出异常
> $prepend 添加到加载函数队列队首,还是加载函数队尾
spl_autoload_register()示例
~~~
<?php
// function __autoload($class) {
// include 'classes/' . $class . '.class.php';
// }
function my_autoloader ( $class ) {
include 'classes/' . $class . '.class.php' ;
}
spl_autoload_register ( 'my_autoloader' );
// 自 PHP 5.3.0 起可以使用一个匿名函数
spl_autoload_register (function ( $class ) {
include 'classes/' . $class . '.class.php' ;
});
?>
~~~
spl_autoload_register()加载失败示例
~~~
<?php
namespace Foobar ;
class Foo {
static public function test ( $name ) {
print '[[' . $name . ']]' ;
}
}
spl_autoload_register ( __NAMESPACE__ . '\Foo::test' );
new InexistentClass ;
?>
~~~
输出:
[[Foobar\InexistentClass]]
Fatal error: Class 'Foobar\InexistentClass' not found in ...
>[info] 4 其他相关函数
spl_autoload_unregister() 注销已注册的__autoload()函数
spl_autoload_extensions() 修改spl_autoload()函数默认文件扩展名
spl_autoload_functions() 返回注册的__autoload()函数
spl_autoload_call() 手动调用已注册的__autoload()函数
* * * * *
**在php的面向对象编程中,通常使用spl_autoload_register()来进行类的自动加载**
* * * * *
## 4 composer的自动加载实现
使用composer安装tp5后
thinkphp\vendor\composer\目录中包含了composer的自动加载实现,
其目录结构如下:
![](https://box.kancloud.cn/2016-03-11_56e272c8d3d04.jpg)
autoload_classmap.php ;类名映射文件
autoload_files.php ;文件名映射文件
autoload_namespace.php ;命名空间映射文件
autoload_psr4.php ;psr4规范修正文件
autoload_real.php ;composer加载器入口
ClassLoader.php ;composer加载器核心
>[info] 1 autoload_real.php
~~~
class ComposerAutoloaderInit900f8cefe6f24fb76e69c5c37058d680
{
// 全局唯一加载器实例,
private static $loader;
// 加载ClassLoader.php文件中的Composer\Autoload'ClassLoader类
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
// 生成加载器
public static function getLoader()
{
// 单例模式生成全局唯一加载器
if (null !== self::$loader) {
return self::$loader;
}
// 注册加载器 检测自动加载器的存在
spl_autoload_register(array('ComposerAutoloaderInit900f8cefe6f24fb76e69c5c37058d680', 'loadClassLoader'), true, true);
// 生成加载器实例
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
// 注销自动加载器 对应上面的检查过程,下面会再次注册
spl_autoload_unregister(array('ComposerAutoloaderInit900f8cefe6f24fb76e69c5c37058d680', 'loadClassLoader'));
// 加载 命名空间映射文件。分析见下
$map = require __DIR__ . '/autoload_namespaces.php';
// 调用$loader->set() 设置命名空间别名路径
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
// 加载 psr4规范修正文件
$map = require __DIR__ . '/autoload_psr4.php';
// 调用$laoder->setPsr4()设置命名空间别名路径
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
// 加载 类映射文件
$classMap = require __DIR__ . '/autoload_classmap.php';
// 调用$loader->addClassMap() 设置类的别名
if ($classMap) {
$loader->addClassMap($classMap);
}
// 注册加载器。
$loader->register(true);
// 加载 需要包含的文件
$includeFiles = require __DIR__ . '/autoload_files.php';
// 调用下面的函数依次加载需要包含的文件
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire900f8cefe6f24fb76e69c5c37058d680($fileIdentifier, $file);
}
return $loader;
}
}
// 加载目标文件,并保存加载信息到全局变量
$GLOBALS['_composer_autolod_files'],防止重复加载
function composerRequire900f8cefe6f24fb76e69c5c37058d680($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
~~~
>[info] 2 ClassLoader.php composer自动加载器核心
* * * * *
>[info] 成员变量
~~~
PSR-4规范
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
PSR-0规范
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
~~~
* * * * *
>[info] 成员方法
PSR-0
> getPrefixes() 获取前缀设置
`public function getPrefixes()`
> getFallbackDirs() 获取搜索目录
`public function getFallbackDirs()`
> add() 添加搜索目录
`public function add($prefix, $paths, $prepend = false)`
> setPsr4() 修改搜索目录前缀索引
`public function set($prefix, $paths)`
PSR-4
> getPrefixesPsr4() 获取前缀设置
` public function getPrefixesPsr4()`
> getFallbackDirsPsr4() 获取搜索目录
`public function getFallbackDirsPsr4()`
> addPsr4() 添加搜索目录
`public function addPsr4($prefix, $paths, $prepend = false)`
> setPsr4() 修改搜索目录前缀索引
`public function setPsr4($prefix, $paths)`
其他
> getClassMap() 获取类名映射信息
`public function getClassMap()`
> addClassMap() 添加类名映射信息
`public function addClassMap(array $classMap)`
> setUseIncludePath() 设置是否搜索include_path
`public function setUseIncludePath($useIncludePath)`
> getUseInlcudePath() 获取是否搜索include_path
public function getUseIncludePath()
> register() 注册加载函数
`public function register($prepend = false)`
> unregister() 注销加载函数
public function unregister()
> loadClsss() 加载目标类
`public function loadClass($class)`
> findFile() 搜索类目标文件
`public function findFile($class)`
> findFileWithExtension() 指定扩展名搜索文件
`private function findFileWithExtension($class, $ext)`
>[info] 3 其他文件
autoload_classmap.php
autoload_files.php
autoload_namespaces.php
autolaod_psr4.php
以**数组形式**返回需要操作的**映射信息**。
## 5 tp5的自动加载实现
thinkphp5的自动加载实现在Loader.php文件
分析见 [附:(Loader.php)自动加载](http://ihavenolimitations.xyz/zmwtp/tp5/119431)
- 更新记录
- 概述
- 文件索引
- 函数索引
- 章节格式
- 框架流程
- 前:章节说明
- 主:(index.php)入口
- 主:(start.php)框架引导
- 主:(App.php)应用启动
- 主:(App.php)应用调度
- C:(Controller.php)应用控制器
- M:(Model.php)数据模型
- V:(View.php)视图对象
- 附:(App.php)应用启动
- 附:(base.php)全局变量
- 附:(common.php)模式配置
- 附:(convention.php)全局配置
- 附:(Loader.php)自动加载器
- 附:(Build.php)自动生成
- 附:(Hook.php)监听回调
- 附:(Route.php)全局路由
- 附:(Response.php)数据输出
- 附:(Log.php)日志记录
- 附:(Exception.php)异常处理
- 框架工具
- 另:(helper.php)辅助函数
- 另:(Cache.php)数据缓存
- 另:(Cookie.php)cookie操作
- 另:(Console.php)控制台
- 另:(Debug.php)开发调试
- 另:(Error.php)错误处理
- 另:(Url.php)Url操作文件
- 另:(Loader.php)加载器实例化
- 另:(Input.php)数据输入
- 另:(Lang.php)语言包管理
- 另:(ORM.php)ORM基类
- 另:(Process.php)进程管理
- 另:(Session.php)session操作
- 另:(Template.php)模板解析
- 框架驱动
- D:(\config)配置解析
- D:(\controller)控制器扩展
- D:(\model)模型扩展
- D:(\db)数据库驱动
- D:(\view)模板解析
- D:(\template)模板标签库
- D:(\session)session驱动
- D:(\cache)缓存驱动
- D:(\console)控制台
- D:(\process)进程扩展
- T:(\traits)Trait目录
- D:(\exception)异常实现
- D:(\log)日志驱动
- 使用范例
- 服务器与框架的安装
- 控制器操作
- 数据模型操作
- 视图渲染控制
- MVC开发初探
- 模块开发
- 入口文件定义全局变量
- 运行模式开发
- 框架配置
- 自动生成应用
- 事件与插件注册
- 路由规则注册
- 输出控制
- 多种应用组织
- 综合应用
- tp框架整合后台auto架构快速开发
- 基础原理
- php默认全局变量
- php的魔术方法
- php命名空间
- php的自动加载
- php的composer
- php的反射
- php的trait机制
- php设计模式
- php的系统时区
- php的异常错误
- php的输出控制
- php的正则表达式
- php的闭包函数
- php的会话控制
- php的接口
- php的PDO
- php的字符串操作
- php的curl
- 框架心得
- 心:整体结构
- 心:配置详解
- 心:加载器详解
- 心:输入输出详解
- 心:url路由详解
- 心:模板详解
- 心:模型详解
- 心:日志详解
- 心:缓存详解
- 心:控制台详解
- 框架更新
- 4.20(验证类,助手函数)
- 4.27(新模型Model功能)
- 5.4(新数据库驱动)
- 7.28(自动加载)