您的位置:首页 > 编程语言 > PHP开发

PHP中的命名空间

2017-10-12 17:46 113 查看

命名空间

PHP中的命名空间,简单来说就像是文件夹。在同一个PHP脚本文件中不能引用相同名字的类或者函数,但是由于开发过程中会使用第三方的SDK或者是团队合作开发,难以避免会有命名重复的可能。

而命名空间的引入就是为了解决这一问题,命名空间就相当于文件夹,同一个文件夹中不能创建相同名字的文件或者文件夹,但是如果相同名字的文件夹在不同文件中就不存在这一问题。

引用一段来自PHP官方文档的话。

什么是命名空间?从广义上来说,命名空间是一种封装事物的方法。在很多地方都可以见到这种抽象概念。例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,它就扮演了命名空间的角色。具体举个例子,文件 foo.txt 可以同时在目录/home/greg 和 /home/other 中存在,但在同一个目录中不能存在两个 foo.txt 文件。另外,在目录 /home/greg 外访问 foo.txt 文件时,我们必须将目录名以及目录分隔符放在文件名之前得到 /home/greg/foo.txt。这个原理应用到程序设计领域就是命名空间的概念。

在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:

1. 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。

2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。

命名空间定义有几点需要注意的地方

命名空间必须在其它所有代码之前声明

命名空间必须在所有代码之前除了declare关键字

允许将同一个命名空间的内容分割存放在不同的文件中

也就说命名空间可以存放在多个文件中

可以在同一个文件中定义多个命名空间

可以在一个PHP脚本中定义多个命名空间,但是这样不方便管理。同时有两种定义的方法,推荐使用第二种。

第一种 简单组合语法

<?php
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

namespace AnotherProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
?>


第二种 大括号语法

<?php
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace AnotherProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}
?>


命名空间的三种使用方式

非限定名称 直接引用类

限定名称 引用相对路径

完全限定名称 引用绝对路劲

命名空间的优点

命名空间一个很重的作用,就是组件化开发提供了便利和可能,也就是通过命名空间来组织文件,使得某个组件的文件的路径和命名空间具有一定的关系,最终可以直接通过命名空间找到相应的文件。

Laravel框架中的自动加载

include和require关键字是通过手动的方式对相应文件进行包含,实际上PHP提供了更加方便的文件包含方法,即类的自动加载方法。类的自动加载可以通过魔术变量__autoload(string $class)实现,也可以通过函数spl_autoload_register注册一个自动加载相应实例如下:

<?php
function __atuoload($class){
require_once($class.".php");
}
?>


当使用一个类名时,如果该类没有被当前文件包含,则会调用__autoload(class)魔术方法,而其中的class 为使用类的名称。但在实际应用中,通常spl_autoload_register注册自定义函数作为自动加载类的实现,因为__autoload()魔术函数只可以定义一次,而spl_atutoload_register可以将多个类自动加载方法注册到队列中,即创建了autoload函数队列,在调用时按照定义时的顺序逐个执行。

其函数定义如下:

bool spl_autoload_register([callable autoloadfunction[,boolthrow=true[,bool $prepend=false]]])

通过spl_autoload_register()函数加载的自动加载函数可以是全局函数,也可以是某个类实例对象的函数,即通过array(“对象名”,“函数名”)注册。

laravle中的实现方式

在laravel框架中,通过函数
spl_autoload_register()
实现类的自动加载函数的注册,其中类的自动加载函数队列中包含了两个类的自动加载函数,一个是composer生成的基于PSR规范的自动加载函数,另一个是Laravel框架核心别名的自动加载函数。

其中composer生成的自动加载函数注册过程如下:

文件 laravel\public\index.php

<?php
require __DIR__.'/../bootstrap/autoload.php';
?>


文件 laravel\bootstrap\autoload.php

<?php
define('LARAVEL_STATR',micotime(true));
requier __DIR__.'/../vendor/autoload.php';
?>


在Laravel框架中,public\index.php文件作为文件的入口,其中第一句就保护鸟了自动加载文件
autoload.php
,这个文件中继续包含位于vendor目录下的composer的自动加载文件。

文件 laravel\vendor\autoload.php

<?php
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit9f6ff2d01ba6cb93f0cabaf91359a37c::getLoader();
?>


通过composer工具创建依赖管理时,会在vendor目录下创建autoload.php文件和一个composer文件夹,其中composer 文件夹中包含了类自动加载函数注册的相关实现,而autoload.php文件时对外提供接口,通过包含该文件就可以完成类自动加载函数的注册。通过上述代码可以看到,autoload.php文件包含了composer目录下的autoload_real.php文件,而autoload_real.php文件中定义了一个类,这个类末尾有一串数字的方式定义,并且定义了getLoader()函数,该函数首先实例化Composer\Autoload\ClassLoader类,然后通过该实例添加相关的文件路径配置,包括命名空间(autoload_namespaces.php文件定义)配置,PSR-4规范(autoload_psr4.php文件定义)配置,类映射(autoload_classmap.php文件定义)配置,接着调用
$loader->register(true)
注册类自动加载函数,最后加载全局文件,即在
autoload_files.php
文件中配置的内容。

下面介绍如何注册类自动加载函数,以及类自动加载函数时如何实现类自动加载的。

文件:laravel\vender\composer\ClassLoader.php

<?php
namespace Composer\Autoload;
class ClassLoader{
//省略加载文件路径函数的相关代码
public function register($prepend = false){
spl_autoload_register(array($this,'loadClass'),true,$prepend);
}

public function loadClass($class){
if($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
}
//省略了根据加载文件路劲查找文件具体位置的相关代码
?>


通过前面的介绍,已经了解了类的自动加载时在Composer\Autoload\ClassLoader类中实现的,实例化该类并将该类的命名空间和文件路径的对应关系注册到对应的属性中,然后通过实例方法register($prepend = false)注册一个类自动加载函数,即为该类实例的loadClass方法,并且将其注册在类自动加载函数队列的末尾,当使用一个未包含的类名时,会自动调用loadClass方法并通过参数获取包含命名空间的类名信息,接着根据类的命名空间与文件路径的对应关系查找文件路径,最后通过includeFile()函数包含该文件,实现类的自动加载。

默认Laravel框架包含两个类的自动加载函数,其中一个时在外观注册(Illuminate\Foundation\Bootstrap\RegisterFacades类实现的)过程中实现的,这里只需要了解有这样一个类自动加载函数被注册到堆栈中就可以,后面可以在“请求到相应的生命周期”内容,了解调用过程,这里只介绍类的自动加载函数的注册过程。在注册过程中也有先实例化后调用的register()函数,进而调用prependToLoaderStack()函数,将
load($alias)
函数注册未类的自动加载函数,该函数的作用主要时将外观别名与外观名(Facades)对应起来,从而实现对应外观类的静态方法调用。对应类的自动加载函数注册过程实现代码如下:

文件 Illuminate\Foundation\AliasLoader.php

<?php
namespace Illuminate\Foundation;
class AliasLoader {
//加载一个类别名,实际上是给外观类起了一个别名,使两个对应一个类
public function load($alias){
if(isset($this->alliases($alias))){
return class_alias($this->aliases[$alias],$alias);
}
}
//添加别名到自动加载函数中
public fucntion alias($class, $alias) {
$this->aliases[$class] = $alias;
}

//注册自动加载函数到自动加载堆栈中
public function register (){
if(!$this->registered) {
$this->prependToLoaderStack();
$this->registered = true;
}
}

//将类的自动加载函数添加在自动加载堆栈首部
protected function prependToLoaderStack(){
spl_autoload_register([$this,'load'],true,true);
}

}

?>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: