php 命名空间 和 自动加载机制 的简单理解

79 篇文章 1 订阅


namespace 只是创建一个命名空间


use 只是导入一个命名空间


namespace 、use 都没有加载动作,和require、require_once、include、include_once没有关联


命名空间必须符合PSR4规范,即命名空间和文件路径要相对应


要说清楚这三个的关系,还得慢慢说。


首先说下php的命名空间:


php 命名空间中的类名可以通过三种方式引用:


1、非限定名称,或不包含前缀的类名称

例如:$a = new foo(); 或 foo::staticmethod();

如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo 

如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为 foo

警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称


2、限定名称,或包含前缀的类名称

例如:$a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();

如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\subnamespace\foo

如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为 subnamespace\foo


3、完全限定名称,或包含了全局前缀操作符的名称

例如:$a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();

在这总情况下,foo 总是被解析为代码中的文件名 (literal name)currentnamespace\foo


namespace A;//当前命名空间


new foo(); // A\foo (实际解析结果)

new \foo(); // foo (实际解析结果)

namespace A;//当前命名空间

/* 导入命名空间 */
use X\Y\foo;

new foo(); // X\Y\foo (实际解析结果)

new \foo(); // foo (实际解析结果)

new \Z\foo(); // Z\foo (实际解析结果)

其实use默认是省略了后面的as,例如 use X\Y\foo 其实是 use X\Y\foo as foo,默认别名是use指定的类名

namespace A;//当前命名空间

/* 导入命名空间并且使用别名 */
use X\Y\foo as Q;

new Q(); // X\Y\foo (实际解析结果)

new \foo(); // foo (实际解析结果)

new \Z\foo(); // Z\foo (实际解析结果)


命名空间的顺序

<?php
namespace A;
use B\D, C\E as F;

// 函数调用

foo();      // 首先尝试调用定义在命名空间"A"中的函数foo()
            // 再尝试调用全局函数 "foo"

\foo();     // 调用全局空间函数 "foo" 

my\foo();   // 调用定义在命名空间"A\my"中函数 "foo" 

F();        // 首先尝试调用定义在命名空间"A"中的函数 "F" 
            // 再尝试调用全局函数 "F"

// 类引用

new B();    // 创建命名空间 "A" 中定义的类 "B" 的一个对象
            // 如果未找到,则尝试自动装载类 "A\B"

new D();    // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象
            // 如果未找到,则尝试自动装载类 "B\D"

new F();    // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象
            // 如果未找到,则尝试自动装载类 "C\E"

new \B();   // 创建定义在全局空间中的类 "B" 的一个对象
            // 如果未发现,则尝试自动装载类 "B"

new \D();   // 创建定义在全局空间中的类 "D" 的一个对象
            // 如果未发现,则尝试自动装载类 "D"

new \F();   // 创建定义在全局空间中的类 "F" 的一个对象
            // 如果未发现,则尝试自动装载类 "F"

// 调用另一个命名空间中的静态方法或命名空间函数

B\foo();    // 调用命名空间 "A\B" 中函数 "foo"

B::foo();   // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
            // 如果未找到类 "A\B" ,则尝试自动装载类 "A\B"

D::foo();   // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法
            // 如果类 "B\D" 未找到,则尝试自动装载类 "B\D"

\B\foo();   // 调用命名空间 "B" 中的函数 "foo" 

\B::foo();  // 调用全局空间中的类 "B" 的 "foo" 方法
            // 如果类 "B" 未找到,则尝试自动装载类 "B"

// 当前命名空间中的静态方法或函数

A\B::foo();   // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法
              // 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B"

\A\B::foo();  // 调用命名空间 "A\B" 中定义的类 "B" 的 "foo" 方法
              // 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
?>

名称解析遵循下列规则:

  1. 对完全限定名称的函数,类和常量的调用在编译时解析。例如 new \A\B 解析为类 A\B
  2. 所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间 A\B\C 被导入为 C,那么对 C\D\e() 的调用就会被转换为 A\B\C\D\e()
  3. 在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间 A\B 内部调用 C\D\e(),则 C\D\e() 会被转换为 A\B\C\D\e() 。
  4. 非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间 A\B\C 导入为C,则 new C() 被转换为 new A\B\C() 
  5. 在命名空间内部(例如A\B),对非限定名称的函数调用是在运行时解析的。例如对函数 foo() 的调用是这样解析的:
    1. 在当前命名空间中查找名为 A\B\foo() 的函数
    2. 尝试查找并调用 全局(global) 空间中的函数 foo()
  6. 在命名空间(例如A\B)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用 new C() 及 new D\E() 的解析过程: new C()的解析:
    1. 在当前命名空间中查找A\B\C类。
    2. 尝试自动装载类A\B\C
    new D\E()的解析:
    1. 在类名称前面加上当前命名空间名称变成:A\B\D\E,然后查找该类。
    2. 尝试自动装载类 A\B\D\E
    为了引用全局命名空间中的全局类,必须使用完全限定名称 new \C()


接下来,说下php的自动加载机制:


在类的实例化过程中,系统做的工作大致是这样:


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

以上就是php的自动加载机制,在类实例化过程中,如果找不到类,会自动调用方法__autoload()


这里要特别说明下,__autoload()是魔术方法,在php源码中是个空方法!

(php 的 魔术方法基本上都是空方法,相当于一个拦截器,拦住后具体要干什么还是要自定义的,否则什么都不干)

换句话说,如果找不到类,会自动调用方法__autoload(),但仅此而已!php原本不会自动加载文件的!


要想实现真正的类自动加载,还是要重写这个方法,在里面写 require 或 include 加载语句的!



命名空间和自动加载的关系:


在类实例化过程中,如果找不到类,自动加载机制会根据命名空间组合$className并自动调用方法__autoload(),将$className作为参数,传入__autoload()



自动加载的历史发展:


直接在文件里写多条require/include加载语句  

->  __autoload() 自动加载 

->  spl_autoload_register() 注册自动加载 

->  composer自动加载




  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值