PHP V5.3 中的新特性,第 3 部分: 名称空间

很多语言都提供了名称空间特性,包括 C++ 和 Java 编程语言。引入名称空间是为了帮助组织大型的代码库,因为在大型代码库中,应用程序经常会出现函数名或类名重叠问题,这会引起其他问题。使用名称空间可以帮助识别代码提供的函数或实用程序,甚至可以帮助指定其来源。一个例子就是 C# 中的 System 名称空间,它包含有 .NET 框架提供的所有函数和类。

在其他未提供正式名称空间的语言中(比如 PHP V5.2 以及更早版本),人们常常通过在类或函数名中使用特定的命名约定来发挥名称空间的作用。比如 Zend Framework,其中每个类名以 Zend 开头,并且每个子名称空间使用下划线分隔开。比如,类定义Zend_Db_Table 表示 Zend Framework 中的一个类并且提供数据库功能。这种方法的一个缺点就是产生的代码非常繁琐,尤其是那些包含好几层的类或函数(Zend Framework 中的 Zend_Cache_Backend_Apc 就是一个例子)。另一个问题就是所有代码必须遵循这种风格,因此如果在应用程序中集成了不遵循这种命名约定的第三方代码后,问题就复杂了。

PHP 名称空间的发展也并非一帆风顺。它们最初计划引入到 PHP V5 中,但是由于无法获得恰当的实现,因此在开发阶段被放弃。最后决定将它们并入到 PHP V6 中,在 2007 年决定将所有 nonunicode 增强移到另一个 PHP V5.x 发行版后,名称空间随后被移入到 PHP V5.3 中。尽管自最初的设计之后绝大部分名称空间行为没有发生变化,但是使用哪一种操作符却成了最大的问题,并且社区成员对这个问题有不同的看法。2008 年 10 月最终决定使用反斜杠作为操作符,从而解决了所有在语言设计和适用性方面使用各种其他操作符的问题。

PHP 名称空间

PHP 从其他语言中借鉴了很多名称空间的语法和设计 — 最突出的是 C++。然而,PHP 名称空间在某些方面具有自己的独特性,这对于希望像在其他语言中那样使用名称空间的用户来说是一个挑战。在本节中,我们将研究 PHP 名称空间的工作方式。

定义一个名称空间

定义一个新的名称空间非常简单。要定义新名称空间,在一个文件中添加清单 1 中的代码作为第一个命令或输出。

清单 1. 定义名称空间

<?php
namespace Foo;
class Example {}
?>

 

注意,以上 namespace 的声明必须是文件中的第一个命令或输出。在它的前面添加任何内容都会导致一个致命的错误。清单 2 展示了有关这方面的一些例子。

清单 2. 定义名称空间的错误方法

/* File1.php */
<?php
echo "hello world!";
namespace Bad;
class NotGood {}
?> 

/* File2.php */
 <?php
namespace Bad;
class NotGood {}
?>

 

在清单 2 的第 1 部分中,我们尝试在名称空间定义之前回传到控制台,这导致产生一个致命错误。在清单的第 2 部分中,我们在 <?php 打开标记的前面多加了一个空格,这样也导致一个致命错误。在编写自己的代码时一定要注意这种情况,因为这是 PHP 名称空间中很常见的一种错误。

但是,上面的两个例子都可以重新编写,将名称空间定义和将在名称空间声明中放入的代码放到独立的文件中,然后再将此文件包含到原始文件中。清单 3 演示了这一点。

清单 3. 修正定义名称空间的错误方法

/* Good.php */
<?php
namespace Good;
class IsGood() {}
?> 

/* File1.php */
<?php
echo "hello world!";
include './good.php';
?>

/* File2.php */
 <?php
include './good.php';
?>

 

现在我们已经了解了如何在一个文件中定义代码的名称空间,接下来让我们看看如何在应用程序中利用这个使用名称空间的代码。

使用带有名称空间的代码

定义了名称空间并在其中放入代码后,我们就可以在应用程序中方便地使用它。可以使用很多种方法调用带有名称空间的函数、类或常量。一种方式是显式地将名称空间引用为调用的前缀。另一种方法是为名称空间定义一个别名并使用该别名作为调用的前缀,这样做的目的是简化名称空间前缀。最后,我们可以只在代码中使用名称空间,这就使它成为默认名称空间,并且在默认情况下,使所有代码都引用默认名称空间。清单 4 演示了调用之间的不同之处。

清单 4. 在名称空间内调用函数

/* Foo.php */
<?php
namespace Foo;
function bar()
{
    echo "calling bar....";
}
?> 

/* File1.php */
<?php
include './Foo.php';
Foo/bar(); // outputs "calling bar....";
?> 

/* File2.php */
<?php
include './Foo.php';
use Foo as ns;
ns/bar(); // outputs "calling bar....";
?> 

/* File3.php */
<?php
include './Foo.php';
use Foo;
bar(); // outputs "calling bar....";
?>

 

清单 4 演示了在名称空间 Foo 内调用函数 bar() 的不同方法。在 File1.php 内,我们看到了如何进行显式调用,使用名称空间的名称作为调用前缀。File2.php 使用名称空间名称的别名,因此我们使用别名代替名称空间的名称。最后,File3.php 仅使用名称空间,这允许我们不需要使用任何前缀来调用 bar()

我们还可以在一个文件内定义多个名称空间,只需要在文件中添加更多 namespace 调用。清单 5 演示了这一点。

清单 5. 文件中的多个名称空间

<?php
namespace Foo;
class Test {} 

namespace Bar;
class Test {} 

$a = new Foo\Test;
$b = new Bar\Test; 

var_dump($a, $b); 

Output:
object(Foo\Test)#1 (0) {
}
object(Bar\Test)#2 (0) {
}

 

现在我们已经基本了解了如何在名称空间内进行调用,让我们了解一些更复杂的名称空间调用以及它们如何工作。

名称空间解析

要熟悉名称空间的使用,其中一个难点就是了解如何进行范围解析。尽管清单 4 所示的简单例子是合理的,但是当我们开始对名称空间进行彼此嵌套时,或者在一个名称空间中试图针对全局空间发出调用是,就会出现问题。PHP V5.3 提供了可以以合理的方式自动解决这些问题的规则。

让我们创建一些包含(include)文件,每个文件都定义了函数 hello()

清单 6. 在不同名称空间中定义的 hello() 函数

/* global.php */
<?php
function hello()
{
    echo 'hello from the global scope!';
}
?> 

/* Foo.php */
<?php
namespace Foo;
function hello()
{
    echo 'hello from the Foo namespace!';
}
?> 

/* Foo_Bar.php */
<?php
namespace Foo/Bar;
function hello()
{
    echo 'hello from the Foo/Bar namespace!';
}
?>

 

清单 6 在三个不同范围内对 hello() 函数定义了三次:在全局范围内,在 Foo 名称空间中,在 Foo/Bar 名称空间中。根据发出hello() 函数调用的范围,决定对哪个 hello() 函数执行调用。下面展示了这些调用的例子。在这里,我们将使用 Foo 名称空间查看如何在另一个名称空间中调用 hello() 函数。

清单 7. 从 Foo 名称空间调用所有 hello() 函数

<?php
include './global.php';
include './Foo.php';
include './Foo_Bar.php';

use Foo; 

hello();         // outputs 'hello from the Foo namespace!'
Bar\hello();   // outputs 'hello from the Foo/Bar namespace!'
\hello();       // outputs 'hello from the global scope!'
?>

 

可以看到,在当前名称空间内引用子名称空间时,可以缩短名称空间前缀(Foo/Bar/hello() 调用可被缩短为 Bar/hello())。并且我们看到如何指定以在全局空间内调用方法:只需使用名称空间操作符作为调用的前缀。

现在,我们已经了解了名称空间的工作机制,下面我们将查看如何在自己的代码中使用它们。

 

回页首

PHP 名称空间用例

名称空间的总体目标就是帮助我们更好地组织代码,减少全局空间内的定义数量。在本节中,我们将查看一些例子,看看名称空间如何帮助我们轻松地实现这些目标。

使用名称空间的第三方代码

许多 PHP 应用程序使用来自不同来源的代码,包括像 PEAR 库那样经过精心设计的代码,或者来自 CakePHP 或 Zend Framework 等各种框架的代码,或是来自 Internet 上不同位置的代码。在集成这些代码时,最主要的问题之一就是这些代码可能无法恰当地融合到已有代码中;函数或类名可能与应用程序中已经在使用的内容冲?。

其中一个例子就是 PEAR Date 包。它使用类名 Date,这是一个非常通用的类名,并且可以很好地切入到代码中的其他位置。因此,一个良好的解决方法就是在包内部的 Date.php 文件的顶部添加一个简单的名称空间命令。现在,当希望使用 PEAR Date 类而不是我们自己的 PEAR Date 类时,就不会感到迷惑。

清单 8. 按照名称空间的定义使用 PEAR Date 类

<?php 

require_once('PEAR/Date.php'); 

use PEAR\Date;    // the name of the namespace we've specified in PEAR/Date.php

// since the current namespace is PEAR\Date, we don't need to prefix the namespace name
$now = new Date();
echo $now->getDate();  // outputs the ISO formatted date 

// this example shows the full namespace specified as part of the class name
$now = new PEAR\Date\Date();
echo $now->getDate();  // outputs the ISO formatted date
?>

 

我们已经在 PEAR/Date.php 文件的 PEAR/Date 名称空间内定义了 PEAR Date 类,因此现在只需在我们的文件中包含代码并使用名称空间,或使用名称空间的名称作为类或函数名的前缀。通过这种方法,我们就可以安全地在应用程序中包含第三方代码。

名称冲突不仅仅是第三方代码才有的问题。如果大型代码库的各个部分永远不会互相靠近,那么也会出现此问题。在下一小节中,我们将了解名称空间如何应对这个情况。

 

回页首

避免实用函数名冲突

几乎所有 PHP 应用程序都具有大量实用方法。虽然并非应用程序的任意对象都包含实用方法,并且也不一定存在于应用程序的所有部分,但是总的来说它在应用程序中确实发挥着作用。但是,随着应用程序不断壮大,实用方法会引起维护问题。

其中一个产生问题的位置就是单元测试,我们编写代码来测试运行应用程序的代码。大多数单元测试套件被设计为运行整个测试套件中的所有测试。比如,我们有两个永远不会包含在一起的实用方法文件,但是在测试套件中,它们就会包含在一起,因为我们会一次性测试整个应用程序。尽管使用这种方式设计应用程序不利于长期维护,但是它确实存在于大型遗留代码库中。

清单 9 展示了如何避免这一问题。我们有两个文件 utils_left.php 和 utils_right.php,这是面向主要使用右手的用户和主要使用左手的用户的实用函数集合。对于每个文件,我们在其各自的名称空间内分别定义。

清单 9. utils_left.php 和 utils_right.php

/* utils_left.php */
<?php
namespace Utils\Left;

function whichHand()
{
    echo "I'm using my left hand!";
}
?> 

/* utils_right.php */
<?php
namespace Utils\Right; 

function whichHand()
{
    echo "I'm using my right hand!";
}
?>

 

我们定义了一个 whichHand() 函数,函数的输出表示我们使用哪一只手。在清单 10 中,我们看到可以方便地包含两个文件并在希望调用的名称空间之间进行切换。

清单 10. 同时使用 utils_left.php 和 utils_right.php 的示例

<?php
include('./utils_left.php');
include('./utils_right.php');

Utils\Left\whichHand();    // outputs "I'm using my left hand!"
Utils\Right\whichHand();  // outputs "I'm using my right hand!" 

use Utils\Left;
whichHand();                 // outputs "I'm using my left hand!" 

use Utils\Right;
whichHand();                 // outputs "I'm using my right hand!"

 

现在,两个文件可以安全地包含在一起,并且我们指定了处理函数调用所需使用的名称空间。而且,对现有代码的影响很小,因为重构功能只需要我们在文件的顶部添加 use 语句,表示要使用的名称空间。

可以对我们定义的 PHP 代码进一步扩展。在下一小节,我们将了解如何在名称空间内覆盖内部函数。

 

回页首

覆盖内部函数名称

虽然 PHP 的内部函数经常可以提供非常棒的实用方法,但有时它们不能按照我们期望的那样执行。我们需要增强它们的行为,以使函数符合我们的期望,但是我们也需要使用另一个名字重新定义函数,从而避免进一步混淆范围。

文件系统函数就需要我们执行这些操作。假设我们需要确保 file_put_contents() 创建的任何文件具有某些权限集。比如,假设我们希望这些文件的权限为只读;我们可以使用一个新的名称空间重新定义函数,如下所示。

清单 11. 在名称空间内定义 file_put_contents() 

<?php
namespace Foo; 

function file_put_contents( $filename, $data, $flags = 0, $context = null )
{
    $return = \file_put_contents( $filename, $data, $flags, $context );

    chmod($filename, 0444);

    return $return;
}
?>

 

我们在函数内调用内部 file_put_contents() 函数并使用一个反斜杠作为函数名的前缀,表示该函数应当在全局范围内处理,这表示将调用内部函数。调用了内部函数后,我们随后对文件执行 chmod() 命令来设置相应的权限。

还有许多例子可以演示如何使用名称空间增强代码。在任何情况下,我们应避免执行不恰当的修改,比如将函数名或类名作为前缀以生成独特的名称。我们现在还了解了如何使用名称空间在大型应用程序中更加安全地包含第三方代码,同时不需要担心名称冲突。

 

回页首

结束语

PHP V5.3 的名称空间是该语言中一个非常受欢迎的新增特性,可以帮助开发人员合理地组织应用程序的代码。该特性使您能够避免使用标准来处理名称空间,允许您编写更高效的代码。尽管名称空间的出现经历了很长时间,但对于受名称冲突困扰的大型 PHP 应用程序来说,它是一个非常受欢迎的特性。

时间: 2024-10-28 03:39:41

PHP V5.3 中的新特性,第 3 部分: 名称空间的相关文章

PHP V5.3 中的新特性,第 5 部分: 从 PHP V5.2 升级到 PHP V5.3

简介 本系列着重介绍 PHP V5.3 中的新特性,例如名称空间.闭包.对象管理.面向对象编程和 Phar.虽然这些动人的新特性作为该语言的增补广受欢迎,但 PHP V5.3 同时也是为进一步优化 PHP 而设计的.它构建在流行.稳定的 PHP V5.2 的基础上,并对该语言作了增强,使之更加强大.在本文中,了解 PHP V5.3 中的变化,以及从 PHP V5.2 升级到 PHP V5.3 时需要考虑的一些事情.   回页首 语法变化 该语言新增了名称空间和闭包(在 第 2 部分 和 第 3

PHP V5.3 中的新特性,第 1 部分: 对象接口的变化

PHP V5 和面向对象编程 与 PHP V4 提供的特性相比,2004 年发布的 PHP V5 在面向对象编程(OOP)和设计方面向前迈出了很大的一步.它提供了一些必要的改进,例如类可见性.合适的构造函数和解构函数.输入提示和类反射(class-reflection)API.它为在 PHP 中进行高级的面向对象编程敞开了大门,并允许实现更加简单的设计模式,以及更好的设计类和 API. PHP V5.3 在 OOP 方面提供了大量渐进式补充.这些改进一直集中在语法补充和性能改进方面.首先,我们将

PHP V5.3 中的新特性,第 4 部分: 创建并使用 Phar 归档

Phar 归档的概念来自 Java 技术的 JAR 归档,它允许使用单个文件打包应用程序,这个文件中包含运行应用程序所需的所有东西.该文件不同于单个可执行文件,后者通常由编程语言生成,比如 C,因为该文件实际上是一个归档文件而非编译过的应用程序.因此 JAR 文件实际上包含组成应用程序的文件,但是考虑到安全性,不对这些文件进行仔细区分.Phar 扩展正是基于类似的理念,但是在设计时主要针对 PHP 的 Web 环境.同样,与 JAR 归档不同的是,Phar 归档可由 PHP 本身处理,因此不需要

PHP V5.3 中的新特性,第 2 部分: 闭包及 lambda 函数

闭包函数和 lambda 函数绝对不是新出现的概念:它们均来自函数编程领域.函数编程 是一种编程风格,它将关注点从执行命令转移到表达式计算.这些表达式是使用函数构成的,结合这些函数可以得到我们要查找的结果.这种编程风格最常用于学术目的,但是也可以在人工智能与数学领域中见到,并且可以在用 Erlang.Haskell 及 Scheme 等语言编写的商业应用程序中找到. 闭包 最初是在 20 世纪 60 年代作为 Scheme 的一部分开发的,Scheme 是最著名的函数编程语言之一.Lambda

ASP 3.0中的新特性

    假如读者已经熟悉了ASP 2.0,并正在寻找3.0版本中的实际改变的列表,那么将在下面发现这些信息.假如读者是一个ASP的初学者,可以越过本章到下一章,那里循序渐进地介绍了ASP对象和它们的用法.    ASP 3.0新特性概要    在ASP 3.0中,有一些新的特性或经历较大的变化或改进的特性.    1. 无脚本的ASP    如早先提到的, ASP处理不包括任何脚本的.asp页的速度是很快的,假如你正在创建的站点或Web应用程序文件最终可能使用ASP,最好让这些文件使用.asp文

VBSctipt 5.0中的新特性

VBSctipt 5.0中的新特性 能够在ASP中应用的特性包括了那些由脚本引擎所提供的特性,这意味着VBScript的改进也可在ASP中应用.VBScript的改进如下所述: 1. 在脚本中使用类在VBScript中实现完整的VB类(class)模型,但明显的例外是在ASP服务器端的脚本事件.可以在脚本中创建类,使它们的属性和方法能够和用于页面的其余代码,例如:Class MyClass Private m_HalfValue 'Local variable to hold value of

iOS 各版本中的新特性(What&amp;#39;s New in iOS)- 目录翻译完成

iOS 各版本中的新特性(What's New in iOS) 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 介绍 Introduction文档组织结构 Organization of Thi

iOS7 中的新特性

iOS7 中的新特性 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 介绍 Introduction文档组织结构 Organization of This Document  iOS 7.0 用

WebSphere Application Server V7中的新特性

IBM WebSphere Application Server V7 中包括一些功能强大的新特性和显著的增强功能,以帮助您实现更高的工作效率.更强的安全性.更紧密的集成和简化的管理.了解这个新版本中的关键特性,这些特性使得该版本可以为您的面向服务的体系结构提供灵活而可靠的基础. 引言 IBM WebSphere Application Server 为面向服务的体系结构(Service Oriented Architecture,SOA)应用程序交付敏捷.可靠的基础,以使应用程序与业务和 IT