PHP 命名空间与spl_autoload_register() 自动加载机制
欢迎使用Markdown编辑器
include 和 require 是PHP中引入文件的两个基本方法。在小规模开发中直接使用 include 和 require 但在大型项目中会造成大量的 include 和 require 堆积。这样的代码既不优雅,执行效率也很低,而且维护起来也相当困难。
为了解决这个问题,部分框架会给出一个引入文件的配置清单,在对象初始化的时候把需要的文件引入。但这只是让代码变得更简洁了一些,引入的效果仍然是差强人意。PHP5 之后,随着 PHP 面向对象支持的完善,__autoload 函数才真正使得自动加载成为可能。
-
include 和 require 功能是一样的,它们的不同在于 include 出错时只会产生警告,而 require 会抛出错误终止脚本。
-
include_once 和 include 唯一的区别在于 include_once 会检查文件是否已经引入,如果是则不会重复引入。
=自动加载==
实现自动加载最简单的方式就是使用 __autoload 魔术方法。当需要使用的类没有被引入时,这个函数会在PHP报错前被触发,未定义的类名会被当作参数传入。至于函数具体的逻辑,这需要用户自己去实现。
首先创建一个 autoload.php 来做一个简单的测试:
复制代码
// 类未定义时,系统自动调用
function __autoload($class)
{
/* 具体处理逻辑 */
echo $class;// 简单的输出未定义的类名
}
new HelloWorld();
/**
- 输出 HelloWorld 与报错信息
- Fatal error: Class ‘HelloWorld’ not found
*/
复制代码
通过这个简单的例子可以发现,在类的实例化过程中,系统所做的工作大致是这样的:
复制代码
/* 模拟系统实例化过程 */
function instance(KaTeX parse error: Expected '}', got 'EOF' at end of input: … (class_exists(class, false)) {
return new KaTeX parse error: Expected 'EOF', got '}' at position 14: class(); }̲ // 查看 auto…class); // 最后一次引入的机会
}
// 再次检查类是否存在
if (class_exists($class, false)) {
return new $class();
} else { // 系统:我实在没辙了
throw new Exception(‘Class Not Found’);
}
}
复制代码
明白了 __autoload 函数的工作原理之后,那就让我们来用它去实现自动加载。
首先创建一个类文件(建议文件名与类名一致),代码如下:
复制代码
class [ClassName]
{
// 对象实例化时输出当前类名
function __construct()
{
echo ‘
’ . CLASS . ‘
’;}
}
复制代码
(我这里创建了一个 HelloWorld 类用作演示)接下来我们就要定义 __autoload 的具体逻辑,使它能够实现自动加载:
复制代码
function __autoload($class)
{
// 根据类名确定文件名
$file = $class . ‘.php’;
if (file_exists($file)) {
include $file; // 引入PHP文件
}
}
new HelloWorld();
/**
- 输出
HelloWorld
*/
复制代码
=命名空间==
其实命名空间并不是什么新生事物,很多语言(例如C++)早都支持这个特性了。只不过 PHP 起步比较晚,直到 PHP 5.3 之后才支持。
命名空间简而言之就是一种标识,它的主要目的是解决命名冲突的问题。
就像在日常生活中,有很多姓名相同的人,如何区分这些人呢?那就需要加上一些额外的标识。
把工作单位当成标识似乎不错,这样就不用担心 “撞名” 的尴尬了。
这里我们来做一个小任务,去介绍百度的CEO李彦宏:
复制代码
namespace 百度;
class 李彦宏
{
function __construct()
{
echo ‘百度创始人’;
}
}
复制代码
↑ 这就是李彦宏的基本资料了,namespace 是他的单位标识,class 是他的姓名。
命名空间通过关键字 namespace 来声明。如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间。
new 百度\李彦宏(); // 限定类名
new \百度\李彦宏(); // 完全限定类名
↑ 在一般情况下,无论是向别人介绍 “百度 李彦宏” 还是 “百度公司 李彦宏”,他们都能够明白。
在当前命名空间没有声明的情况下,限定类名和完全限定类名是等价的。因为如果不指定空间,则默认为全局(\)。
namespace 谷歌;
new 百度\李彦宏(); // 谷歌\百度\李彦宏(实际结果)
new \百度\李彦宏(); // 百度\李彦宏(实际结果)
↑ 如果你在谷歌公司向他们的员工介绍李彦宏,一定要指明是 “百度公司的李彦宏”。否则他会认为百度是谷歌的一个部门,而李彦宏只是其中的一位员工而已。
这个例子展示了在命名空间下,使用限定类名和完全限定类名的区别。(完全限定类名 = 当前命名空间 + 限定类名)
复制代码
/* 导入命名空间 */
use 百度\李彦宏;
new 李彦宏(); // 百度\李彦宏(实际结果)
/* 设置别名 */
use 百度\李彦宏 AS CEO;
new CEO(); // 百度\李彦宏(实际结果)
/* 任何情况 */
new \百度\李彦宏();// 百度\李彦宏(实际结果)
复制代码
↑ 第一种情况是别人已经认识李彦宏了,你只需要直接说名字,他就能知道你指的是谁。第二种情况是李彦宏就是他们的CEO,你直接说CEO,他可以立刻反应过来。
使用命名空间只是让类名有了前缀,不容易发生冲突,系统仍然不会进行自动导入。
如果不引入文件,系统会在抛出 “Class Not Found” 错误之前触发 __autoload 函数,并将限定类名传入作为参数。
所以上面的例子都是基于你已经将相关文件手动引入的情况下实现的,否则系统会抛出 " Class ‘百度\李彦宏’ not found"。
=spl_autoload==
接下来让我们要在含有命名空间的情况下去实现自动加载。这里我们使用 spl_autoload_register() 函数来实现,这需要你的 PHP 版本号大于 5.12。
spl_autoload_register 函数的功能就是把传入的函数(参数可以为回调函数或函数名称形式)注册到 SPL __autoload 函数队列中,并移除系统默认的 __autoload() 函数。
一旦调用 spl_autoload_register() 函数,当调用未定义类时,系统就会按顺序调用注册到 spl_autoload_register() 函数的所有函数,而不是自动调用 __autoload() 函数。
现在,我们来创建一个 Linux 类,它使用 os 作为它的命名空间(建议文件名与类名保持一致):
复制代码
namespace os; // 命名空间
class Linux // 类名
{
function __construct()
{
echo ‘
’ . CLASS . ‘
’;}
}
复制代码
接着,在同一个目录下新建一个 PHP 文件,使用 spl_autoload_register 以函数回调的方式实现自动加载:
复制代码
spl_autoload_register(function ($class) { // class = os\Linux
/* 限定类名路径映射 */
$class_map = array(
// 限定类名 => 文件路径
'os\\Linux' => './Linux.php',
);
/* 根据类名确定文件名 */
$file = $class_map[$class];
/* 引入相关文件 */
if (file_exists($file)) {
include $file;
}
});
new \os\Linux();
复制代码
这里我们使用了一个数组去保存类名与文件路径的关系,这样当类名传入时,自动加载器就知道该引入哪个文件去加载这个类了。
但是一旦文件多起来的话,映射数组会变得很长,这样的话维护起来会相当麻烦。如果命名能遵守统一的约定,就可以让自动加载器自动解析判断类文件所在的路径。接下来要介绍的PSR-4 就是一种被广泛采用的约定方式。
=PSR-4规范==
PSR-4 是关于由文件路径自动载入对应类的相关规范,规范规定了一个完全限定类名需要具有以下结构:
<顶级命名空间>(<子命名空间>)*<类名>
如果继续拿上面的例子打比方的话,顶级命名空间相当于公司,子命名空间相当于职位,类名相当于人名。那么李彦宏标准的称呼为 “百度公司 CEO 李彦宏”。
PSR-4 规范中必须要有一个顶级命名空间,它的意义在于表示某一个特殊的目录(文件基目录)。子命名空间代表的是类文件相对于文件基目录的这一段路径(相对路径),类名则与文件名保持一致(注意大小写的区别)。
举个例子:在全限定类名 \app\view\news\Index 中,如果 app 代表 C:\Baidu,那么这个类的路径则是 C:\Baidu\view\news\Index.php
我们就以解析 \app\view\news\Index 为例,编写一个简单的 Demo:
复制代码
$class = ‘app\view\news\Index’;
/* 顶级命名空间路径映射 */
$vendor_map = array(
‘app’ => ‘C:\Baidu’,
);
/* 解析类名为文件路径 */
v
e
n
d
o
r
=
s
u
b
s
t
r
(
vendor = substr(
vendor=substr(class, 0, strpos($class, ‘\’)); // 取出顶级命名空间[app]
$vendor_dir =
v
e
n
d
o
r
m
a
p
[
vendor_map[
vendormap[vendor]; // 文件基目录[C:\Baidu]
r
e
l
p
a
t
h
=
d
i
r
n
a
m
e
(
s
u
b
s
t
r
(
rel_path = dirname(substr(
relpath=dirname(substr(class, strlen($vendor))); // 相对路径[/view/news]
f
i
l
e
n
a
m
e
=
b
a
s
e
n
a
m
e
(
file_name = basename(
filename=basename(class) . ‘.php’; // 文件名[Index.php]
/* 输出文件所在路径 */
echo $vendor_dir . $rel_path . DIRECTORY_SEPARATOR . $file_name;
复制代码
通过这个 Demo 可以看出限定类名转换为路径的过程。那么现在就让我们用规范的面向对象方式去实现自动加载器吧。
首先我们创建一个文件 Index.php,它处于 \app\mvc\view\home 目录中:
复制代码
namespace app\mvc\view\home;
class Index
{
function __construct()
{
echo ’
Welcome To Home
';}
}
复制代码
接着我们在创建一个加载类(不需要命名空间),它处于 \ 目录中:
复制代码
class Loader
{
/* 路径映射 */
public static $vendorMap = array(
‘app’ => DIR . DIRECTORY_SEPARATOR . ‘app’,
);
/**
* 自动加载器
*/
public static function autoload($class)
{
$file = self::findFile($class);
if (file_exists($file)) {
self::includeFile($file);
}
}
/**
* 解析文件路径
*/
private static function findFile($class)
{
$vendor = substr($class, 0, strpos($class, '\\')); // 顶级命名空间
$vendorDir = self::$vendorMap[$vendor]; // 文件基目录
$filePath = substr($class, strlen($vendor)) . '.php'; // 文件相对路径
return strtr($vendorDir . $filePath, '\\', DIRECTORY_SEPARATOR); // 文件标准路径
}
/**
* 引入文件
*/
private static function includeFile($file)
{
if (is_file($file)) {
include $file;
}
}
}
复制代码
最后,将 Loader 类中的 autoload 注册到 spl_autoload_register 函数中:
复制代码
include ‘Loader.php’; // 引入加载器
spl_autoload_register(‘Loader::autoload’); // 注册自动加载
new \app\mvc\view\home\Index(); // 实例化未引用的类
/**
- 输出:
Welcome To Home
*/
复制代码
示例中的代码其实就是 ThinkPHP 自动加载器源码的精简版,它是 ThinkPHP 5 能实现惰性加载的关键。
新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
- 全新的界面设计 ,将会带来全新的写作体验;
- 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
- 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
- 全新的 KaTeX数学公式 语法;
- 增加了支持甘特图的mermaid语法1 功能;
- 增加了 多屏幕编辑 Markdown文章功能;
- 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
- 增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
设定内容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' | ‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" | “Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash | – is en-dash, — is em-dash |
创建一个自定义列表
-
Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎