PHP包和命名空间
PHP和包
包由一组相关的类以某种方式组合而成,可以用于隔离系统中的各个部分。有些编程语言可以识别出包,并为它们提供不同的命名空间。虽然PHP没有原生的包概念,但它自PHP5.3起引入了命名空间。
命名空间
在PHP5.3之前,类名是全局有效的。也就是说,如果我们给一个类起名为ShoppingBasket,那么这个类在整个系统中都是可用的。这就造成了两大问题:首先,这可能导致命名冲突。
<?php
require_once __DIR__ . "/Outputter.php";
class Outputter
{
// 输出数据
}现在假设被包含的Outputter.php中有如下定义
<?php
class Outputter
{
// 这是一个测试文件 没有任何实际功能
}结果
Cannot declare class Outputter, because the name is already in use in Outputter.php在PHP引入命名空间之前,有一种方法可以绕过这个问题,那就是在类名前加上包名,这样能够确保类名是唯一的。
// Outputter.php
class useful_Outputter
{
// 这是一个测试文件 没有任何实际功能
}这种方法的问题是,随着项目越来越庞大,类名也会变得越来越长。
命名空间来救场
PHP5.3引入了命名空间。从本质上看,命名空间就是一个桶,我们可以向其中放入类、函数和变量。在命名空间中,我们可以无限制地访问这些元素。但在外部,必须先导入命名空间或引用它才能访问其内部的东西。
<?php
namespace my;
require_once __DIR__ . "/Outputter.php";
class Outputter
{
// 输出数据
}<?php
namespace useful;
class Outputter
{
// 这是一个测试文件 没有任何实际功能
}namespace关键字会创建一个命名空间,且必须在代码文件的第一条有效语句(注释不算)中声明命名空间。这里创建了两个命名空间my和useful。通常需要创建更深层次的命名空间。我们可以使命名空间以组织或项目的标识符开头,接着通过包名进一步限定。PHP支持嵌套的命名空间。为此,简单地用反斜杠分隔各层命名空间即可。
<?php
namespace popp\ch05\batch04\util;
class Debug
{
public static function helloWorld()
{
print "hello from Debug";
}
}通常会用与产品或组织有关的名称来定义库。那么现在应该如何调用方法呢?事实上,这取决于从哪里调用方法。如果在命名空间内调用方法,那么可以直接这样做:
Debug::helloWorld();这称为“非限定名”。因为我们已经在popp\ch05\batch04\util这个命名空间中,所以不用在类名前添加任何路径。如果要从命名空间的外部访问类,那么可以按照以下方式:
\popp\ch05\batch04\util\Debug::helloWorld();现在请看下面的代码:
namespace main;
popp\ch05\batch04\util\Debug::helloWorld();会报错:
Fatal error: Uncaught Error: Class 'main\popp\ch05\batch04\util\Debug' not found in ...这是因为这里使用的是相对命名空间,PHP会在当前命名空间main下查找popp\ch05\batch04\util,但是没找到,于是程序就报错了。就像在使用绝对URL和绝对文件路径时以分隔符开头一样,我们也可以使用绝对命名空间。但是,如果我按照《深入PHP》这本书所述使用绝对命名空间修改如下:
namespace main;
\popp\ch05\batch04\util\Debug::helloWorld();还是会报错:
Fatal error: Uncaught Error: Class 'popp\ch05\batch04\util\Debug' not found in ...这是因为没有将util文件引入进来,命名空间其实是逻辑上的,并不会真正加载那个 php 文件进来。文件是文件,语法解析是语法解析。所以需要:
<?php
namespace main;
// 引入文件
require_once __DIR__ . "/util.php";
\popp\ch05\batch04\util\Debug::helloWorld();结果:
hello from Debug对于这个的相关讨论可以看下这个链接https://www.v2ex.com/t/657493
也可以使用use关键字
<?php
namespace main;
require_once __DIR__ . "/util.php";
use popp\ch05\batch04\util;
util\Debug::helloWorld();这里use后面的命名空间并没有使用以反斜杠开头,这是因为use关键字会从全局空间开始查找参数,而不是当前命名空间。你也可以写成下面这种形式:
<?php
namespace main;
require_once __DIR__ . "/util.php";
use popp\ch05\batch04\util\Debug;
Debug::helloWorld();现在有一个问题,如果调用Debug类的命名空间中已经存在一个Debug类,那么会发生什么呢?
// util 文件
<?php
namespace popp\ch05\batch04\util;
class Debug
{
public static function helloWorld()
{
print "hello from popp\\ch05\\batch04\\util\\Debug";
}
}// main 文件
<?php
namespace main;
require_once __DIR__ . "/util.php";
use popp\ch05\batch04\util\Debug;
class Debug
{
public static function helloWorld()
{
print "hello from main";
}
}
Debug::helloWorld();这会发生一个错误:
Cannot declare class main\Debug because the name is already in use这种情况就可以给命名空间的类起一个别名
<?php
namespace main;
require_once __DIR__ . "/util.php";
use popp\ch05\batch04\util\Debug as coreDebug;
class Debug
{
public static function helloWorld()
{
print "hello from main";
}
}
Debug::helloWorld();
coreDebug::helloWorld();在use语句中使用as语句可以为Debug类起一个别名coreDebug。
如果你正在命名空间内编写代码,而且想要访问位于全局空间(非命名空间)中的类,那么可以在类名前加上一个反斜杠来访问它。
以下是一个定义在全局空间下的类:
<?php
class Lister
{
public static function helloWorld()
{
print "hello from global";
}
}以下是命名空间的代码:
<?php
namespace popp\ch05\batch04\util;
require_once __DIR__ . "/util.php";
class Lister
{
public static function helloWorld()
{
print "hello from " . __NAMESPACE__ . "<br/>";
}
}
Lister::helloWorld(); // 局部访问
\Lister::helloWorld(); //全局访问结果:
hello from popp\ch05\batch04\util
hello from global注意本例中的__NAMESPACE__常量,可以通过它来输出当前的命名空间,有助于调试程序。
另外,可以在同一个文件中声明多个命名空间。以下是通过namespace关键字和大括号声明命名空间的一种语法。
<?php
namespace com\getinstance\util {
class Lister
{
public static function helloWorld()
{
print "hello from global";
}
}
}
namespace other {
\com\getinstance\util\Lister::helloWorld();
}如果必须在同一个文件中定义多个命名空间,那么推荐以上这种声明语法。但通常而言,在每个文件中定义一个命名空间是一种最佳实践。
注意:不能在同一个文件中同时使用大括号和行命名空间语法,只能选择其一并贯彻整个文件
评论已关闭