资讯专栏INFORMATION COLUMN

PHP命名空间和自动加载类

姘存按 / 483人阅读

摘要:在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。为了引用全局命名空间中的全局类,必须使用完全限定名称。实际上命名空间和自动加载类的结合就基本是通过路径形式将命名空间路径替换为实际路径。

背景

最近有个朋友问我 PHP 命名空间是咋样的,但是由于长期不做开发,笔者实际上也已经忘得差不多了,所以也回答不出来。只是记得和 Java 挺像的。事后重新查了一下 PHP 的官方文档,并且和 Java 做对比,Java 的命名空间实际上来自于 JVM 本身的机制,JVM 是基于 class 字节码文件加载类,由于类很容易出现重名的情况,换言之 class 字节码文件也会出现重名情况,所以就需要使用目录来管理不同的字节码文件,而为了保证加载正常,所以就需要命名空间这种机制。当然,也可以说是由于命名空间的存在才有了目录管理的方式。但是 PHP 和 Java 不一样,PHP 是一种动态脚本语言,它的代码分散在所有脚本中,当需要的时候才会使用 include 函数加载对应的文件,所以 PHP 的命名空间,实际上是基于 PHP 的自动加载类,自动加载类实现了才能保证 PHP 命名空间存在的意义。

命名空间概述

命名空间据笔者所知应该最早源于 C++ 语言,在 C++98 标准以后,为了保证各种命名不重合所推出的一种解决方案。现在的面向对象语言基本都有这种机制,当然除了命名空间以外,还有很多种方式,比如模块化,不过实际上这些机制都是用来解决封装问题的,所以笔者个人认为并无好坏之分。
先把 PHP 官方文档代码拉出来溜溜

非常容易理解的代码,从上面的代码中可以看到 PHP 定义的命名空间是怎么样的,不过笔者个人认为其定义非常反人类,居然使用反斜杠来分隔命名空间路径。不过有一点需要注意,名为 PHP 或 php 的命名空间,以及以这些名字开头的命名空间(例如PHPClasses)被保留用作语言内核使用,而不应该在用户空间的代码中使用。

定义命名空间

PHP 命名空间功能只能在 PHP5.3.0 以上版本使用,对于一个命名空间,只有类、接口、函数和常量会被包含在命名空间中。

当然,也可以使用花括号来包含所有需要的内容,就像这样。

不过这样很容易造成缩进上的问题,所以笔者不推荐使用,并且一般情况下,一个文件包含一个类,所以也不需要花括号来分割命名空间范围。

使用命名空间

对于命名空间路径来说,存在着三种形式

非限定名称,或者说不包含前缀的类名称。例如 $a=new foo();foo::staticmethod();。如果当前命名空间是 currentnamespacefoo 将被解析为 currentnamespacefoo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo`。

限定名称,或包含前缀的名称,例如 $a = new subnamespacefoo();subnamespacefoo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespacesubnamespacefoo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为 subnamespacefoo

完全限定名称,或包含了全局前缀操作符的名称,例如, $a = new currentnamespacefoo();currentnamespacefoo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名 (literal name)currentnamespacefoo

由于 PHP 本身动态语言的特性,所以完全可以使用字符串动态访问命名空间内的元素。

不过有一点需要注意,就是单双引号之间的区别,单引号可以不需要处理 的转译处理,而双引号就必须使用 等转译符号。
Java 语言使用 import 机制引入命名空间,由于 Java 可以指定到类名,所以 Java 最多只能导入到具体类,而 PHP 则可以指定到一个命名空间内的类、常量、方法等,并且支持命名空间别名。

名称解析规则

首先就是前面讲过的三种名称类型,名称解析遵循以下规则:

对完全限定名称的函数,类和常量的调用在编译时解析。例如 new AB 解析为类 AB。

所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间 ABC 被导入为 C,那么对 CDe() 的调用就会被转换为 ABCDe()。

在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间 AB 内部调用 CDe(),则 CDe() 会被转换为 ABCDe() 。

非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间 ABC 导入为C,则 new C() 被转换为 new ABC() 。

在命名空间内部(例如AB),对非限定名称的函数调用是在运行时解析的。例如对函数 foo() 的调用是这样解析的:

在当前命名空间中查找名为 ABfoo() 的函数

尝试查找并调用 全局(global) 空间中的函数 foo()。

在命名空间(例如AB)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用 new C() 及 new DE() 的解析过程:

new C()的解析:

在当前命名空间中查找ABC类。

尝试自动装载类ABC。

new DE()的解析:

在类名称前面加上当前命名空间名称变成:ABDE,然后查找该类。

尝试自动装载类 ABDE。

为了引用全局命名空间中的全局类,必须使用完全限定名称 new C()。

从上面的规则来看,实际上 PHP 的导入规则和 Java 有点类似,但是却有不一样,主要是因为 Java 是完全面向对象的,而 PHP 本质上还只是一种基于对象的语言。

自动加载类

在早期 PHP 开发中,开发者最烦的就是一堆 include 函数包含了一大堆文件,而且早期时候 PHP 面向对象的概念确实太差了,因为 PHP 作为一种脚本语言,不存在程序入口,所以脚本顺序化执行的诱惑力实在是很大,即使面向对象开发,但是缺少极佳的模块划分导入机制,代码可以说很难有美感,最大的代表就是 Wordpress。如果有朋友看过这个典型项目,可以觉得非常痛苦,因为各种初始化、业务流程都分散在各个不同的文件中,使用 include 函数进行衔接,然后每次页面渲染都是同样的要走一趟流程。当然,这是 Wordpress 的历史包袱,而在支持老版本 PHP 的情况下 Wordpress 代码已经写得足够优化了。
在 PHP5 中就不需要这么麻烦了,因为可以定义一个 __autoload() 函数,当调用一个未定义的类的时候就会启动此函数,从而在抛出错误之前做最后的补救,不过这个函数的本意已经被完全曲解使用了,现在都用来做自动加载。
注意,这个函数实际上已经不被推荐使用了,相反,现在应当使用 spl_autoload_register() 来注册类的自动加载函数。

bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )

autoload_function 是需要注册的自动装载函数,如果此项为空,则会注册 spl_autoload 函数,
throw 此参数设置了 autoload_function 无法成功注册时, spl_autoload_register() 是否抛出异常。
prepend 如果是 true,spl_autoload_register() 会添加函数到队列之首,而不是队列尾部。
上面提到了 spl_autoload 函数,实际上注册函数的规范就应当遵循此函数,函数声明如下:

void spl_autoload ( string $class_name [, string $file_extensions ] )

由于这个函数默认实现是通过 C 语言,所以这里给出一个 PHP 语言的实现规范。

大致上就和这个是类似的。实际上命名空间和自动加载类的结合就基本是通过路径形式

function __autoload(){
    $dir = "./libralies";
    set_include_path(get_include_path(). PATH_SEPARATOR. $dir);
    $class = str_replace("", "/", $class) . ".php"; 
    require_once($class);
}

将命名空间路径替换为实际路径。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/21491.html

相关文章

  • PHP自动加载功能原理解析

    摘要:前言在开始之前,欢迎关注我自己的博客这篇文章是对自动加载功能的一个总结,内容涉及的自动加载功能的命名空间的与标准等内容。要实现第一步,第二步的功能,必须在开发时约定类名与磁盘文件的映射方法,只有这样我们才能根据类名找到它对应的磁盘文件。 前言 在开始之前,欢迎关注我自己的博客:www.leoyang90.cn 这篇文章是对PHP自动加载功能的一个总结,内容涉及PHP的自动加载功能、P...

    Imfan 评论0 收藏0
  • Composer的Autoload源码实现——启动与初始化

    摘要:任务是加载类的初始化顶级命名空间与文件路径映射初始化和注册。在实际情况下可能会出现这样的情况。值得注意的是这个函数返回的是一个匿名函数,为什么呢原因就是类中的等等都是的。。。关于匿名函数的绑定功能。 前言 在开始之前,欢迎关注我自己的博客:www.leoyang90.cn 上一篇文章,我们讨论了 PHP 的自动加载原理、PHP 的命名空间、PHP 的 PSR0 与 PSR4 标准,有...

    MarvinZhang 评论0 收藏0
  • ThinkPHP5.1 源码浅析(二)自动加载机制

    摘要:如果遍历后没有找到,则加载失败。在之后碰到了之后直接拿来用,提高系统自动加载的性能。这里我们就讲完了注册自动加载。使用自动加载我们在中定义了我们自动加载函数式方法。 继 生命周期的第二篇,大家尽可放心,不会随便鸽文章的 第一篇中,我们提到了入口脚本,也说了,里面注册了自动加载的功能 本文默认你有自动加载和命名空间的基础。如果没有请 看此篇文章 php 类的自动加载与命名空间 自动加载...

    mudiyouyou 评论0 收藏0
  • Composer的Autoload源码实现——注册与运行

    前言 在开始之前,欢迎关注我自己的博客:www.leoyang90.cn上一篇 文章我们讲到了 Composer 自动加载功能的启动与初始化,经过启动与初始化,自动加载核心类对象已经获得了顶级命名空间与相应目录的映射,换句话说,如果有命名空间 AppConsoleKernel,我们已经知道了 App 对应的目录,接下来我们就要解决下面的就是 ConsoleKernel这一段。 注册 我们先回顾...

    wanghui 评论0 收藏0
  • 自动加载命名空间

    摘要:不传参数,直接调用,会默认调用来加载类,如果后面再调用有传参数的,也会失效的如果使用了命名空间,那么会把路径和类名一同带过来的。 自动加载 两种实现方式 1、__autoload(); 2、spl_autoload_register(); (主要使用) __autoload()现在很少使用,因为使用这种方式,在一个系统的实现中,假如需要使用很多其它的类库,这些类库可能是由...

    songjz 评论0 收藏0

发表评论

0条评论

姘存按

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<