浅析PHP类的自动加载和命名空间

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;

原文地址:https://www.cnblogs.com/php-learning-road/p/12196077.html

时间: 2024-10-12 01:03:40

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

final,类的自动加载,命名空间

final是干什么的一般是为了防止父类的一个方法被重写如果父类中的方法被声明为 final,则子类无法覆盖该方法.如果一个类被声明为 final,则不能被继承. Note: 属性不能被定义为 final,只有类和方法才能被定义为 final. 类的自动加载类的自动加载是指,在外面的页面中,并不需要去"引入"类文件,但是程序会在需要的时候动态加载需要的类文件. spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(in

面向对象之final,类的自动加载,命名空间

Final关键字:最终的属性不能被定义为 final,只有类和方法才能被定义为 final.特点:Final类不能被继承: 如果我们不希望一个类被继承,我们使用final来修饰这个类.这个类将无法被继承.比如我们设定的Math类,涉及了我们要做的数学计算方法,这些算法也没有必要修改,也没有必要被继承,我们把它设置成final类型.Final 方法不能被重写:如果不希望类中的某个方法被子类重写,我们可以设置这个方法为final方法,只需要在这个方法前加上final修饰符类的自动加载简化开发者书写文

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

final关键字 1.final可以修饰方法和类,但是不能修饰属性: 2.Final修饰的类不能被继承: 3.Fina修饰的方法不能被重写,子类可以对已被final修饰的父类进行访问,但是不能对父类的方法进行重写. 扩展内容: 类的自动加载: 什么是类的自动加载? Spl_autoload_register():注册给定的函数作为__autoload()函数的实现 类的自动加载 在最开始使用的是__autoload()进行自动加载,但是在PHP5.3之前,__autoload函数抛出的异常不能被

类的自动加载

很多开发者写面向对象的应用程序时对每个类的定义建立一个 PHP 源文件.一个很大的烦恼是不得不在每个脚本开头写一个长长的包含文件列表(每个类一个文件). 在 PHP 5 中,不再需要这样了.可以定义一个 __autoload() 函数,它会在试图使用尚未被定义的类时自动调用.通过调用此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类. spl_autoload_register() 提供了一种更加灵活的方式来实现类的自动加载.因此,不再建议使用 __autoload() 函数,在

PHP的类文件自动加载机制

搜集于网络,学习php的类的自动加载机制,在实际大型项目中其重要性尤为突出. PHP的类自动加载机制 在PHP开发过程中,如果希望从外部引入一个class,通常会使用include和require方法,去把定义这个class的文件包含进来.这个在小规模开发的时候,没什么大问题.但在大型的开发项目中,这么做会产生大量的require或者include方法调用,这样不因降低效率,而且使得代码难以维护,况且require_once的代价很大. 在PHP5之前,各个PHP框架如果要实现类的自动加载,一般

Final关键字和类的自动加载

Final关键字: 父类中的方法被声明为 final,则子类无法覆盖该方法.如果一个类被声明为 final,则不能被继承. 一般是为了防止父类的一个方法被重写.只能用来定义类和定义方法, 不能使用final这个关键字来定义成员属性,因为final是常量的意思,我们在PHP里定义常量使用的是define()函数,所以不能使用final来定义成员属性.1.被final修饰的类不可以被继承 2.被final修饰的方法不可以被重写 3.被final修饰的变量不可以被改变被final修饰不可变的是变量的引

PHP面向对象——Final关键字 类的自动加载

1.Final关键字 含义:最终的,最后的 作用: 1.如果父类中的方法被声明为 final,则子类无法覆盖该方法. 如果一个类被声明为 final,则不能被继承. 2.属性不能被定义为 final,只有类和方法才能被定义为 final. 用法:直接在类或者方法前加final关键字. --------------------------------------------------------------------------- 2.类的自动加载 含义: 1.解决代码重复,把重复的代码写在一

php 类的自动加载

在编写面向对象(OOP) 程序时,很多开发者为每个类新建一个 PHP 文件. 这会带来一个烦恼:每个脚本的开头,都需要包含(include)一个长长的列表(每个类都有个文件). 在 PHP 5 中,已经不再需要这样了. spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载.通过注册自动加载器,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类. Tip 尽管 __autoload()

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

Yaf零基础学习总结5-Yaf类的自动加载 框架的一个重要功能就是类的自动加载了,在第一个demo的时候我们就约定自己的项目的目录结构,框架就基于这个目录结构来自动加载需要的类文件. Yaf在自启动的时候, 会通过SPL注册一个自己的Autoloader, 出于性能的考虑, 对于框架相关的MVC类, Yaf Autoloader只以目录映射的方式尝试一次. 具体的目录映射规则如下: 对于控制器 Controller 默认模块下为{项目路径}/controllers/, 否则为{项目路径}/mod