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. 【机器学习】【条件随机场CRF-3】条件随机场的参数化形式详解 + 画出对应的状态路径图 + 给出对应的矩阵表示

    1.条件随机场概念CRF,Conditional Random Field,是给定一组输入随机变量条件下另一组输出随机变量的条件概率分布模式,其特点是假设输出随机变量构成马尔可夫随机场. 条件随机场用 ...

  2. php页面最大执行时间 set_time_limit函数不起作用

      作者: default|标签:PHP set_time_limit 执行时间|2017-3-21 15:03   set_time_limit 不生效或者无效解决方法 <?php globa ...

  3. Springboot 2.x下多数据源配置

    本文同样适用于2.x版本下Mybatis的多数据源配置 项目中经常会遇到一个项目需要访问多个数据源的情况,多数情况下可以参考这个教程进行配置. 不过该教程适合springboot1.x版本,由于2.x ...

  4. 【codeforces 761D】Dasha and Very Difficult Problem

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  5. 2019-1-29-WPF-设置输入只能英文

    title author date CreateTime categories WPF 设置输入只能英文 lindexi 2019-1-29 15:8:4 +0800 2018-2-13 17:23: ...

  6. linux 分配和释放设备编号

    在建立一个字符驱动时你的驱动需要做的第一件事是获取一个或多个设备编号来使用. 为 此目的的必要的函数是 register_chrdev_region, 在 <linux/fs.h>中声明: ...

  7. 【HTML/CSS】置换元素

    置换元素: 一个内容不受CSS视觉格式化模型控制,CSS渲染模型并不考虑对此内容的渲染,且元素本身一般拥有固有尺寸(宽度,高度,宽高比)的元素,被称之为置换元素. 行内级置换和非置换元素的宽度定义 对 ...

  8. 2018-2-13-win10-uwp-判断文件存在

    title author date CreateTime categories win10 uwp 判断文件存在 lindexi 2018-2-13 17:23:3 +0800 2018-2-13 1 ...

  9. 30分钟全方位了解阿里云Elasticsearch(附公开课完整视频)

    摘要: 阿里云Elasticsearch提供100%兼容开源Elasticsearch的功能,以及Security.Machine Learning.Graph.APM等商业功能,致力于数据分析.数据 ...

  10. hibernate_检索策略

    一.概述 检索策略分三大块,类级别检索策略和关联级别检测策略. 类级别检索策略:get.load. 关联级别检索策略:order.getCustomer().getName() 上面这两种应该是看得懂 ...