命名空间 namespace
这是PHP5.3后添加的命名空间,
命名空间说白了就是给一个文件里的类,方法,属性都加了一个前缀,防止在一个文件中引入两个不同的文件时候,里面的类,方法,属性出现重名的情况,因为PHP中不允许两个函数或者类出现相同的名字(如果类名不一样,里面的方法名可以一样的),否则会产生一个致命的错误。当然,命名空间也不能出现两个相同的名字。
比如
文件1:
a.php 声明一个命名空间,在其命名空间下定义一个类
注意命名空间用\右斜杠区分
<?php
namespace my\name1;//命名空间的声明
class a{
public $name ='张三';
const age= '18';
public function Money(){
echo '1元';
}
static function staticMoney(){
echo '2元';
}
}
文件2:
b.php 声明其命名空间 并引入文件1的命名空间
<?php
namespace my\name2;//命名空间的声明,在第一行申明
require_once("a.php");
//使用命名空间,具体到类名(如果不具体到类名也可以,如果这里是use my\name1;下面使用的时候就得写:name1\a-> )。使用use的好处就是如果一个命名空间的名字很长的话,下面的代码可以直接只用类名调用方法等。如果命名空间里的类名有重复的话使用as起个别名即可,下面的代码使用别名调用方法。
use my\name1\a;
// 访问成员属性:
$a=new a();//如果没有使用关键字use 来引入命名空间,那这个类会提示不存在
print_r($a->name);
echo a::age;//访问常量不需要实例化
// 访问普通方法:
$a=new a();
$a->Money();
// 访问静态方法
a::staticMoney();
// 如果不使用use引入a.php的命名空间,在b.php中访问a.php的内容,需要包含a.php,以及new a.php的命名空间。
require_once("a.php");
$a=new \my\name1\a();//如果b.php中声明了命名空间,这里my前面必须加\,否则PHP解析器会认为我想调用当前空间下的元素。因为调用的这个命名空间在公共空间里,告诉程序不要在当前文件里找,
print_r($a->name);
//如果a.php中没有写namespace my\name1;则在b.php中访问a.php的内容,需要包含a.php,以及new a.php的类名。
require_once("a.php");
$a=new \a();
print_r($a->name);
总体来说,就是先用namespace声明,用到的时候就用use来引用就可以了。如果一个类有了命名空间,那这个命名空间就是这个文件的第一路径
公共空间
我有一个common_inc.php脚本文件,里面有一些好用的函数和类:
<?php
function getIP() { }
class FilterXSS { }
在一个命名空间里引入这个脚本,脚本里的元素不会归属到这个命名空间。如果这个脚本里没有定义其它命名空间,它的元素就始终处于公共空间中:
<?php
namespace Blog\Article;
include './common_inc.php';
$filter_XSS = new FilterXSS(); //出现致命错误:找不到Blog\Article\FilterXSS类
$filter_XSS = new \FilterXSS(); //正确
调用公共空间的方式是直接在元素名称前加 \ 就可以了,否则PHP解析器会认为我想调用当前空间下的元素。除了自定义的元素,还包括PHP自带的元素,都属于公共空间。
要提一下,其实公共空间的函数和常量不用加 \ 也可以正常调用(不明白PHP为什么要这样做),但是为了正确区分元素,还是建议调用函数的时候加上 \
自动载入:
<?php
// 最原始的方法:require,
// require,需要必须的意思。在一开始就加载,放在脚本程序的最前面,文件出错了,主程序也停了。require是无条件包含也就是如果一个流程里加入require,无论条件成立与否都会先执行require。
// incluce包含的意思在用到时加载,可放在流程控制的处理区段中,文件中出错了,主程序继续往下执行
// _once后缀表示已加载的不加载,先检查要导入的档案是不是已经在该程序中的其它地方被导入过了,如果有的话就不会再次重复导入
// require 'test1.php';
// require 'test2.php';
test1::test();
echo '<br />';
test2::test();
// 之后的方法:__autoload自动载入类文件,后来不使用了,
// 如何可以不用require文件,当调用函数的时候自动载入需要文件名?
//test1::test();会将test1当作文件名传入,所以写类文件的时候需要注意,文件名和类名要相同!
function __autoload($class){
require __DIR__.'/'.$class.'.php';
}
spl_autoload_register方法
<?php
//因为有时候一个项目里面会有多个框架的参与,每一个框架里如果都有一个__autoload魔术方法的时候就会出现函数重复,所以php5.3引入了spl_autoload_register()函数,只需要将框架里的自动载入的__autoload换个名字,将这个名字作为参数传入spl_autoload_register函数即可,这个函数告诉PHP碰到调用没有定义(找不到)的类就执行这个函数的参数这个方法。
class test {
public static function loadprint( $class ) {
$file = $class . '.class.php';
if (is_file($file)) {
require_once($file);
}
}
}
//当调用没有的文件的时候加载loadprint函数,如果项目中运用了两个框架,每一个框架里都有一个__autoload魔术方法,只需要将其改名,改为下面的方法即可。
spl_autoload_register( "test::loadprint" );
//另外写法:
spl_autoload_register( array('test','loadprint') );
如果框架中就是使用了spl_autoload_register函数,可以将其当做一个方法使用,不需要再写个类。
注册类自动装载函数 PSR-4规范 简单版本
<?php
/**
* 注册类自动装载函数 PSR-4规范 简单版本
* @author 邹义良
*/
spl_autoload_register(function ($class) {
// 兼容 PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
// 将类名转换为路径
// DIRECTORY_SEPARATOR php内置常量,为系统目录分隔符,Windows值\,linux值/
//strstr 将$class中的\替换为系统目录分割符,'\\'第一个\是转义的意思,strtr比str_replace块,但是strtr要求替换的字符和被替换的字符长度相等才准确,这里符合要求
$path = strtr($class, '\\', DIRECTORY_SEPARATOR);
// 拼接完整文件名
$file = __DIR__ . DIRECTORY_SEPARATOR . $path . '.php';
// 检测文件是否存在
if (file_exists($file)) {
include $file;
}
});
不同类文件中的命名空间之间是相互分离的
test.php
<?php
include("./NameSpaceClass.php");
include("./UseNameSpace.php");
use Util\NameSpaceClass;//虽然在这里使用了NameSpaceClass.php的命名空间,但是UseNameSpace.php中不能共用
UseNameSpace::use_out_put();
NameSpaceClass.php
<?php
namespace Util;
class NameSpaceClass
{
public static function out_put()
{
echo "This is a Utils namespace \n";
}
}
UseNameSpace.php:
<?php
use Util\NameSpaceClass;//必须写,不同类文件中的命名空间之间是相互分离的
class UseNameSpace
{
public static function use_out_put()
{
echo NameSpaceClass::out_put();
}
}
按照惯性思维,这种方式test从上到下,一次定义命名空间别名,加载类文件,输出结果,虽然最终的UseNameSpace不在NameSpaceClass的命名空间内,但最开始已经定义命名空间别名(use Util\NameSpaceClass;)了, 应该会没问题的了。
结果却出现了错误
PHP Fatal error: Class ‘NameSpaceClass’ not found in /tmp/UseNameSpace.php on line 7
UseNameSpace.php 中暂时未定义命名空间,但他的类方法直接调用了NameSpaceClass的方法,我们知道直接这么做是错误的,因为两个根本不在一个命名空间内
分析
惯性思维里,php的include操作类似于把要include的文件内容直接插入到该include的位置, 被include的文件应该直接能使用原有文件的已经定义的任何内容。
但实际上在命名空间这里却不一样,不同类文件中的命名空间之间是相互分离的,即某个文件中定义的use的命名空间,在其他的类文件中不能使用。
以下来自一个大牛对我的指导(涨姿势)
在包括其他的许多语言中,在定义类库文件的时候,会在类库中定义好本身的依赖关系,并做好不同情况下的兼容处理,而这也正式底层类库的设计难点
PHP的命名空间正是这种依赖关系的一个体现,在定义好了一个类库后,我们应该也要定义好他依赖的本类库甚至是第三方的命名空间(用use),我们不能指望用户在拿到这个类库后还要自己去尝试找到类的依赖命名空间,最起码,我们应该给他一个提示,使用了哪些命名空间,怎么加载等等。
解决
在UseNameSpace.php中添加依赖类库的命名空间别名
use Util\NameSpaceClass;