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

简介: 很多语言都提供了名称空间特性,包括 C++ 和 Java™ 编程语言。引入名称空间是为了帮助组织大型的代码库,因为在大型代码库中,应用程序经常会出现函数名或类名重叠问题,这会引起其他问题。使用名称空间可以帮助识别代码提供的函数或实用程序,甚至可以帮助指定其来源。

很多语言都提供了名称空间特性,包括 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 应用程序来说,它是一个非常受欢迎的特性。

目录
相关文章
|
5月前
|
安全 IDE 编译器
深入理解PHP 7的新特性及其对现代Web开发的影响
【7月更文挑战第30天】本文将深入探索PHP 7版本中引入的关键新特性,并分析这些改进如何优化现代Web开发实践。通过对比PHP 5和PHP 7的性能差异,我们将揭示PHP 7如何提升应用响应速度和资源利用效率。此外,本文还将讨论PHP 7对开发者工作流程的影响,包括新的语言特性、错误处理机制以及内置函数的增强,旨在为读者提供全面了解PHP 7所带来的变革性影响。
|
5月前
|
存储 SQL 编译器
PHP 8新特性深度解析与实战应用
本文将深入探讨PHP 8的新增特性,并结合实际案例演示如何有效利用这些特性优化现有项目。通过本文,您将了解到PHP 8带来的性能提升、安全性增强以及代码简化等方面的改进,以及如何将这些新特性融入日常开发工作之中。 【7月更文挑战第29天】
54 8
|
5月前
|
缓存 安全 测试技术
深入PHP 7:新特性与性能提升解析
在PHP 7的发布中,我们见证了一系列令人兴奋的性能改进和新特性的加入。本文将深入探讨这些变化如何影响开发者的日常编程实践,并展示通过实际例子如何最大化利用PHP 7的优势。准备好迎接代码效率和开发体验的全新升级!
|
5月前
|
安全 API PHP
深入理解PHP 7的新特性及其对现代Web开发的影响
【7月更文挑战第29天】本文将探索PHP 7版本引入的一系列新特性,并分析它们如何革新了现代Web开发。我们将从性能提升、语言特性增强、以及面向对象编程的改进等方面进行详细讨论,旨在为开发者提供一份全面的PHP 7新特性指南,帮助他们更好地利用这些新工具优化和加速Web应用的开发。
|
5月前
|
大数据 编译器 API
PHP 7新特性深度解析与应用实践
【7月更文挑战第28天】本文深入探讨PHP 7带来的革新特性,从性能优化到语法改进,逐一剖析其背后的技术原理和实际影响。通过实例展示如何有效利用这些新特性来提升开发效率和代码质量,为PHP开发者提供切实可行的升级和优化策略。
|
5月前
|
存储 设计模式 安全
PHP 7新特性及其对现代Web开发的影响
随着PHP 7的发布,这一广泛使用的服务器端脚本语言带来了显著的性能提升和新的编程特性。本文将深入探讨PHP 7中的关键更新,包括性能优化、类型声明、异常处理改进以及匿名类等,并分析这些变化如何影响现代Web开发的实践和模式。通过具体案例,我们将了解开发者如何利用这些新特性来编写更快、更安全、更易维护的代码。 【7月更文挑战第28天】
38 3
|
5月前
|
安全 编译器 API
深入理解PHP 8的新特性及其对现代Web开发的影响
【7月更文挑战第29天】随着PHP 8的发布,这个广受欢迎的服务器端脚本语言迎来了重大更新。本文将深入探讨PHP 8引入的关键新特性,包括JIT编译器、联合类型、命名参数、匹配表达式以及错误处理改进等,并分析这些变化如何影响现代Web开发实践。我们将通过具体示例和代码片段,展示这些新特性如何提升性能、增强类型安全和支持更简洁的代码编写,同时讨论它们对现有PHP项目的迁移和维护的潜在影响。
43 2
|
5月前
|
设计模式 缓存 PHP
深入理解PHP 7中的新特性与性能优化
【7月更文挑战第28天】本文将探索PHP 7版本中引入的新特性及其对性能的影响。不同于传统的功能介绍,我们将通过实际代码示例和性能分析,揭示这些新特性如何提升开发效率和应用程序的运行速度,同时指出在实际应用中可能遇到的陷阱与挑战。
|
5月前
|
存储 监控 大数据
深入理解PHP 7的新特性及其性能优化技巧
【7月更文挑战第26天】随着PHP 7的发布,这一广泛使用的编程语言迎来了重大的性能提升和新特性。本文将深入探讨PHP 7的核心改进点,包括性能提升的原因、新引入的语言特性以及如何利用这些变化来优化现有和未来的PHP项目。我们将通过实际代码示例和性能分析工具来展示如何有效地应用这些新特性以达到最佳的性能表现。
|
5月前
|
编译器 PHP 开发者
探索PHP 8的新特性及其对现代Web开发的影响
随着PHP 8的发布,这个广受欢迎的服务器端脚本语言迎来了一系列令人兴奋的新特性和性能改进。本文将深入探讨PHP 8中的一些关键更新,包括JIT编译器、联合类型、命名参数、匹配表达式等,并讨论这些变化如何影响现代Web开发的实践。我们将通过具体示例来揭示新特性的实际应用场景,同时分析它们可能给开发者带来的挑战与机遇。文章旨在为读者提供一份关于PHP 8新特性的实用指南,帮助他们更好地适应这一新版本的语言环境。