PHP命名空间namespace学习

PHP命名空间namespace学习

说明

命名空间是php5.3.0版本引入的.主要是解决名称冲突的问题.C++和C#也称为namespace,Java称为package,虽然名字不一样,但是意义是一样的.

这块在查看php手册时,感觉解释的比较清楚.

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

解决问题

  • 通过使用命名空间,可以解决class,function,const名字冲突

  • 为很长的名称创建简短的别名,提高代码可读性

定义命名空间

namespace必须定义在所有代码之前,除declare关键字,namespace之前可以出现注释和空格但是不能出现其他的代码.

namespace命名规则建议最顶层的命名规则以vendor命名,公司名称或组织名称,必须是全球唯一的名称,以免namespace名称冲突.

另手册提示,名为PHP或php的命名空间,以及以这些名字开头的命名空间(例如PHP\Classes)被保留用作语言内核使用,而不应该在用户空间的代码中使用。这和上面的差不多是一个意思.

命名空间名称不区分大小写.

示例:

//定义顶级命名空间
namespace myNamespace;

定义子命名空间

与目录和文件的关系很像,PHP命名空间也允许指定层次化的命名空间名称,命名空间的名字可以使用分层次的方式定义.

namespace MyProject\Sub\Level;

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

上面的例子创建了常量MyProject\Sub\Level\CONNECT_OK,类 MyProject\Sub\Level\Connection和函数 MyProject\Sub\Level\connect。

这里说明子命名空间的文件并不一定要在对应的目录下存在,也可以和上级命名空间的文件在同目录,但是参考PSR-4 autoloader标准,子命名空间的代码需要在对应的目录下.这个可以参考示例二.

规则

尽量不要在同一个文件中定义多个命名空间,虽然语法上是可以这么操作的,但是不推荐.非常不提倡在同一个文件中定义多个命名空间

使用命名空间

在讨论如何使用命名空间之前,必须了解 PHP 是如何知道要使用哪一个命名空间中的元素的。可以将 PHP 命名空间与文件系统作一个简单的类比。在文件系统中访问一个文件有三种方式:

  • 相对文件名形式如foo.txt。它会被解析为 currentdirectory/foo.txt,其中 currentdirectory 表示当前目录。因此如果当前目录是 /home/foo,则该文件名被解析为/home/foo/foo.txt。

  • 相对路径名形式如subdirectory/foo.txt。它会被解析为 currentdirectory/subdirectory/foo.txt。

  • 绝对路径名形式如/main/foo.txt。它会被解析为/main/foo.txt。

PHP 命名空间中的元素使用同样的原理。例如,类名可以通过三种方式引用:

  • 非限定名称,或不包含前缀的类名称,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。 警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。详情参见 使用命名空间:后备全局函数名称/常量名称。

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

  • 完全限定名称,或包含了全局前缀操作符的名称,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo。 下面是一个使用这三种方式的实例:

//file1.php

<?php
namespace Foo\Bar\subnamespace;

const FOO = 1;
function foo() {}
class foo
{
    static function staticmethod() {}
}
?>
//file2.php

<?php
namespace Foo\Bar;
include 'file1.php';

const FOO = 2;
function foo() {}
class foo
{
    static function staticmethod() {}
}

/* 非限定名称 */
foo(); // 解析为 Foo\Bar\foo resolves to function Foo\Bar\foo
foo::staticmethod(); // 解析为类 Foo\Bar\foo的静态方法staticmethod。resolves to class Foo\Bar\foo, method staticmethod
echo FOO; // resolves to constant Foo\Bar\FOO

/* 限定名称 */
subnamespace\foo(); // 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // 解析为类 Foo\Bar\subnamespace\foo,
                                  // 以及类的方法 staticmethod
echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO

/* 完全限定名称 */
\Foo\Bar\foo(); // 解析为函数 Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // 解析为类 Foo\Bar\foo, 以及类的方法 staticmethod
echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO
?>

use关键字

上面的例子,可以看到,在调用命名空间下的具体代码时,需要写全部名称,如果这么编码的话,肯定会疯掉的.

我们可以使用use关键字来为命名空间设置别名.

<?php
namespace foo;
use My\Full\Classname as Another;

// 下面的例子与 use My\Full\NSname as NSname 相同
use My\Full\NSname;

// 导入一个全局类
use ArrayObject;

// importing a function (PHP 5.6+)
use function My\Full\functionName;

// aliasing a function (PHP 5.6+)
use function My\Full\functionName as func;

// importing a constant (PHP 5.6+)
use const My\Full\CONSTANT;

$obj = new namespace\Another; // 实例化 foo\Another 对象
$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象
// 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象
func(); // calls function My\Full\functionName
echo CONSTANT; // echoes the value of My\Full\CONSTANT

实践

上面基本都说明了命名空间的定义和使用. 在实际使用中,可能还会有一些疑问.

Q:使用了命名空间的代码文件,如何在其他代码文件中使用

A: 使用了命名空间的代码文件,在其他代码文件中使用是需要include/require进去的,也可以使用自动加载的方式来引入.

/*
---
  |- a.php
  |- test.php
*/
<?php
/**
 * a.php
 */
namespace Test\A;

class a{
    public function __construct(){
        print_r("this is class a \n");
    }
}

<?php
/**
 * test.php
 */
//include('a.php');
//调用代码文件a.class.php下的命名空间
$a = new Test\A\a();

//以下是输出
a. 注释掉test.php里的include代码,运行的结果为:
$ php test.php
PHP Fatal error:  Class 'Test\A\a' not found in test.php on line 4

Fatal error: Class 'Test\A\a' not found in tes
t.php on line 4

b. 去掉include的注释,运行结果如下:
$ php test.php
this is class a

调用方式示例:

/*
---
  |- a.php
  |- test.php
*/
<?php
/**
 * a.php
 */
namespace Test\A;

class a{
    public function __construct(){
        print_r("this is class a \n");
    }
}

<?php
/**
 * test.php
 */
include('a.php');
use Test\A\a;
use Test\A\a as Aa;
//调用代码文件a.class.php下的命名空间
$a = new Test\A\a(); //this is class a
$aa = new a(); //this is class a
$aa = new Aa(); //this is class a

示例二:

/**
--
  |-login.class.php
  |-regist.class.php
  |-test.php
*/

/**
 * login.class.php
 */
namespace userCenter; 

function check_username(){ 
    echo "login OK<br>"; 
} 

class login{ 
    public function save(){ 
        echo "login had saved<br>"; 
    } 
}
/**
 * regist.class.php
 */
namespace userCenter\regist\re 
{ 
    function check_username() { 
        echo "regist OK<br>"; 
    }   

    class regist{ 
        public function save(){ 
            echo "regist had saved<br>"; 
        } 
    } 
}
/**
 * test.php
 */
require "login.class.php"; 
require "regist.class.php"; 

use userCenter\regist\re;    //使用use调用空间 
use userCenter\regist\re as reg;  //as定义别名 

echo \userCenter\check_username();  //绝对调用 

$login = new \userCenter\login(); 
echo $login->save(); 

echo re\check_username();    //相对调用 
echo reg\check_username();       //别名调用   

$regist = new reg\regist(); 
echo $regist->save();

Ifelsend.com

相关文章

发表评论

返回顶部