php是使用require(require_once)include(include_once)关键字加载类文件。但是在实际的开发工程中我们基本上不会去使用这些关键字去加载类。 因为这样做会使得代码的维护相当的困难。实际的开发中我们会在文件的开始位置用use关键字使用类,然后直接new这个类就可以了. 至于类是怎么加载的,一般都是框架或者composer去实现的。

<?php

use Illuminate\Container\Container;

$container = new Container();

自动加载

我们可以通过一段伪代码来模拟一下在类的实例化工程中类是如何工作的

function instance($class)
{
// 如果类已加载则返回其实例
if (class_exists($class, false)) {
return new $class();
}
// 查看 autoload 函数是否被用户定义
if (function_exists('__autoload')) {
__autoload($class); // 最后一次加载类的机会
}
// 再次检查类是否存在
if (class_exists($class, false)) {
return new $class();
} else { // 系统:我实在没辙了
throw new Exception('Class Not Found');
}
}

php在语言层面提供了__autoload 魔术方法给用户来实现自己的自动加载逻辑。当用户去new一个类的时候,如果该类没有被加载,php会在抛出错误前调用__autoload方法去加载类。下面的例子中的__autoload方法只是简单的输出要加载类的名称, 并没有去实际的加载对应的类, 所以会抛出错误。

<?php

use Illuminate\Container\Container;

$container = new Container();

function __autoload($class)
{
/* 具体处理逻辑 */
echo $class;// 简单的输出要加载类的名称
} /**
*
运行结果
Illuminate\Container\Container
Fatal error: Uncaught Error: Class 'Illuminate\Container\Container' not found in D:\project\php\laravel_for_ci_cd\test\ClassLoader.php:5
Stack trace:
#0 {main}
thrown in D:\project\php\laravel_for_ci_cd\test\ClassLoader.php on line 5
*/

明白了 __autoload 函数的工作原理之后,我们来用它去实现一个最简单自动加载。我们会有index.phpPerson.php两个文件在同一个目录下。

//index.php
<?php
function __autoload($class)
{
// 根据类名确定文件名
$file = './'.$class . '.php';
if (file_exists($file)) {
include $file; // 引入PHP文件
}
}
new Person(); /*---------------------分割线-------------------------------------*/ //Person.php
class Person
{
// 对象实例化时输出当前类名
function __construct()
{
echo '<h1>' . __CLASS__ . '</h1>';
}
} /**运行结果
* 输出 <h1>Person</h1>
*/

命名空间

命名空间并不是什么新鲜的事务,很多语言都早就支持了这个特性(只是叫法不相同),它主要解决的一个问题就是命名冲突! 就好像日常生活中很多人都会重名,我们必须要通过一些标识来区分他们的不同。比如说现在我们要用php介绍一个叫张三的人 ,他在财务部门工作。我们可以这样描述。

namespace 财务部门;

class 张三
{
function __construct()
{
echo '财务部门的张三';
}
}

这就是张三的基本资料 , namespace是他的部门标识,class是他的名称. 这样大家就可以知道他是财务部门张三而不是工程部门张三

非限定名称,限定名称和完全限定名称

1.非限定名称,或不包含前缀的类名称,例如 $comment = new Comment(); 如果当前命名空间是Blog\ArticleComment将被解析为、\Blog\Article\Comment。如果使用Comment的代码不包含在任何命名空间中的代码(全局空间中),则Comment会被解析为\Comment

注意: 如果文件的开头有使用use关键字 use one\two\Comment;Comment会被解析为 *one\two\Comment*。

2.限定名称,包含前缀的名称,例如 $comment = new Article\Comment(); 如果当前的命名空间是Blog,则Comment会被解析为\Blog\Article\Comment。如果使用Comment的代码不包含在任何命名空间中的代码(全局空间中),则Comment会被解析为\Article\Comment

3.完全限定名称,或包含了全局前缀操作符的名称,例如 $comment = new \Article\Comment(); 在这种情况下,Comment总是被解析为\Article\Comment

spl_autoload

接下来让我们要在含有命名空间的情况下去实现类的自动加载。我们使用 spl_autoload_register() 函数来实现,这需要你的 PHP 版本号大于 5.12。spl_autoload_register函数的功能就是把传入的函数(参数可以为回调函数函数名称形式)注册到 SPL __autoload 函数队列中,并移除系统默认的 __autoload() 函数。一旦调用 spl_autoload_register() 函数,当调用未定义类时,系统就会按顺序调用注册到 spl_autoload_register() 函数的所有函数,而不是自动调用 __autoload() 函数。

现在, 我们来创建一个 Linux 类,它使用 os 作为它的命名空间(建议文件名与类名保持一致):

<?php
namespace os; // 命名空间 class Linux // 类名
{
function __construct()
{
echo '<h1>' . __CLASS__ . '</h1>';
}
}

接着,在同一个目录下新建一个 index.php文件,使用 spl_autoload_register 以函数回调的方式实现自动加载:

<?php

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 是关于由文件路径自动载入对应类的相关规范,规范规定了一个完全限定类名需要具有以下结构:

<顶级命名空间>(<子命名空间>)*<类名>

PSR-4 规范中必须要有一个顶级命名空间,它的意义在于表示某一个特殊的目录文件基目录)。子命名空间代表的是类文件相对于文件基目录的这一段路径(相对路径),类名则与文件名保持一致(注意大小写的区别)。

举个例子:在全限定类名 \app\view\news\Index 中,如果 app 代表 C:\Baidu,那么这个类的路径则是 C:\Baidu\view\news\Index.php.我们就以解析 \app\view\news\Index 为例,编写一个简单的 Demo:

<?php

$class = 'app\view\news\Index';

/* 顶级命名空间路径映射 */
$vendor_map = array(
'app' => 'C:\Baidu',
); /* 解析类名为文件路径 */
$vendor = substr($class, 0, strpos($class, '\\')); // 取出顶级命名空间[app]
$vendor_dir = $vendor_map[$vendor]; // 文件基目录[C:\Baidu]
$rel_path = dirname(substr($class, strlen($vendor))); // 相对路径[/view/news]
$file_name = basename($class) . '.php'; // 文件名[Index.php] /* 输出文件所在路径 */
echo $vendor_dir . $rel_path . DIRECTORY_SEPARATOR . $file_name;

浅析PHP类的自动加载和命名空间的更多相关文章

  1. final关键字,类的自动加载,命名空间

    final关键字 1.final可以修饰方法和类,但是不能修饰属性: 2.Final修饰的类不能被继承: 3.Fina修饰的方法不能被重写,子类可以对已被final修饰的父类进行访问,但是不能对父类的 ...

  2. Yaf零基础学习总结5-Yaf类的自动加载

    Yaf零基础学习总结5-Yaf类的自动加载 框架的一个重要功能就是类的自动加载了,在第一个demo的时候我们就约定自己的项目的目录结构,框架就基于这个目录结构来自动加载需要的类文件. Yaf在自启动的 ...

  3. thinkphp系列:类的自动加载是如何设计的

    在使用框架开发时,可以发现框架有很多核心类,却很少看到显示的引入某个文件的代码,这是因为框架都采用了类的自动加载机制,即使用到类时,框架会自动找到该类所在文件的位置并引入该文件.为了更容易看出代码思路 ...

  4. PHP面向对象学习-属性 类常量 类的自动加载 构造函数和析构函数 访问控制(可见性)

    在类的成员方法里面,可以用 ->(对象运算符):$this->property(其中 property 是该属性名)这种方式来访问非静态属性.静态属性则是用 ::(双冒号):self::$ ...

  5. PHP面向对象----- 类的自动加载

    1.类的自动加载 spl_autoload_register函数 test.php <?php spl_autoload_register('autoload'); // require_onc ...

  6. Drupal如何实现类的自动加载?

    Drupal通过spl_autoload_register()注册类加载器实现自动加载: function _drupal_bootstrap_database() { // ... .... spl ...

  7. 再谈 tp的 实例化 类 的自动加载

    表示一个域名下的所有/任何主机 使用 的格式是: [*.] example.com 其中 , example.com叫着 裸域名. (这个example.com/net/org不能被注册, 被保留) ...

  8. php composer 实现类的自动加载

    我们在开发项目中会经常用到第三方的类库插件,但是如果每次需要使用的时候都会在代码的某一处去引入,然后在实例化,这样做感觉很不方便,那么怎么实现自动加载呢,下面简单介绍使用composer实现自动加载: ...

  9. tp5底层源码分析之------tp5.1类的自动加载机制

    tp框架作为国内主流框架,目前已经发布了6.0版本,相当于3.*版本是进行了重构,今天我们从源码的角度来研究下tp5.1自动加载的实现 作为单入口框架,从入口文件看起,入口文件在public/下,那么 ...

随机推荐

  1. Vue的第一个实例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. vue 使用webpack打包后路径报错以及 alias 的使用

    一.vue 使用webpack打包后路径报错(两步解决) 1. config文件夹 ==> index.js ==> 把assetsPublicPath的 '/ '改为 './' 2. b ...

  3. Eclipse修改控制台字体

    步骤:Window-->Preference-->General-->Appearance-->Colors and Fonts-->Basic-->Text Fo ...

  4. C++ 驱动开发 error LNK2019

    最近在写一个机器人的时候,发现驱动无法编译通过.本文告诉大家如何解决这个问题. 在 VisualStudio 2017 15.8 的版本提供新的功能C++ Just My Code Stepping ...

  5. dotnet 使用 lz4net 压缩 Stream 或文件

    在 dotnet 可以使用 LZ4 这个无损的压缩算法,这个压缩算法的压缩率不高但是速度很快.这个库支持在 .NET Standard 1.6 .NET Core .NET Framework Mon ...

  6. tensorflow学习笔记——ResNet

    自2012年AlexNet提出以来,图像分类.目标检测等一系列领域都被卷积神经网络CNN统治着.接下来的时间里,人们不断设计新的深度学习网络模型来获得更好的训练效果.一般而言,许多网络结构的改进(例如 ...

  7. Spring MVC 模拟

    在Spring MVC中,将一个普通的java类标注上Controller注解之后,再将类中的方法使用RequestMapping注解标注,那么这个普通的java类就够处理Web请求,示例代码如下: ...

  8. Hibernate各种查询方式及查询策略(转)

    转自:https://www.cnblogs.com/xujingyang/p/6734203.html 在了解Hibernate的基本知识后,重点让我们看下相关查询方式和查询优化策略吧! 话不多说, ...

  9. unsupported jsonb version number 123

    PostgreSQL  jsonb 入库时遇到   unsupported jsonb version number 123 变通方法 insert into  htclanedata (laneda ...

  10. TDengine 时序数据库的 ADO.Net Core 提供程序 Maikebing.EntityFrameworkCore.Taos

    简介 Entity, Framework, EF, Core, Data, O/RM, entity-framework-core,TDengine Maikebing.Data.Taos 是一个基于 ...