xhb8413 发表于 2011-12-22 08:54

PHP 5.3中的命名空间

<p><strong>为什么我们需要命名空间?</strong></p>
<p>随着你的PHP代码库的增长,对之前定义的函数和类名进行修改时风险也更高了,当你试图增加第三方组件或插件时问题更严重,如果存在两个或两个以上的代码集实现了一个“Database”和“User”类会怎么样?</p>
<p>直到目前,唯一的解决办法是使用长的类/函数名,例如Wordpress在每个类和函数名前都使用了前缀“WP_”, Zend Framework使用了极具描述性的命名约定,导致类名非常冗长,如:</p>
<p>Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive</p>
<p>命名冲突问题可以使用命名空间来解决,PHP常量、类和函数可以被组合到命名空间库中。</p>
<p><strong>如何定义命名空间?</strong></p>
<p>默认情况下,所有常量、类和函数名都放在全局空间下,就和PHP支持命名空间之前一样。</p>
<p>在PHP文件的顶部使用一个关键字namespace就可以定义命名空间,它必须是第一个命令(declare除外),在它前面不能出现非PHP代码、HTML或空格。如:</p><ol class="dp-c"><li class="alt"><span><span>&lt;&nbsp;?php &nbsp;</span></span></li><li class=""><span></span><span class="comment"><font color="#008200">//&nbsp;define&nbsp;this&nbsp;code&nbsp;in&nbsp;the&nbsp;'MyProject'&nbsp;namespace </font></span><span>&nbsp;</span></li><li class="alt"><span>namespace&nbsp;MyProject; &nbsp;</span></li><li class=""><span>&nbsp;</span></li><li class="alt"><span></span><span class="comment"><font color="#008200">//&nbsp;...&nbsp;code&nbsp;... </font></span><span>&nbsp;</span></li></ol>
<p>这一行下面的代码都是指定给MyProject命名空间的,为相同代码块嵌套命名空间或定义多个命名空间是不可能的,如果你真这样干,只有最后一个命名空间才能识别,但你可以在同一个文件中定义不同的命名空间代码,如:</p><ol class="dp-c"><li class="alt"><span><span>&lt;&nbsp;?php &nbsp;</span></span></li><li class=""><span>namespace&nbsp;MyProject1; &nbsp;</span></li><li class="alt"><span></span><span class="comment"><font color="#008200">//&nbsp;PHP&nbsp;code&nbsp;for&nbsp;the&nbsp;MyProject1&nbsp;namespace </font></span><span>&nbsp;</span></li><li class=""><span>&nbsp;</span></li><li class="alt"><span>namespace&nbsp;MyProject2; &nbsp;</span></li><li class=""><span></span><span class="comment"><font color="#008200">//&nbsp;PHP&nbsp;code&nbsp;for&nbsp;the&nbsp;MyProject2&nbsp;namespace </font></span><span>&nbsp;</span></li><li class="alt"><span>&nbsp;</span></li><li class=""><span></span><span class="comment"><font color="#008200">//&nbsp;Alternative&nbsp;syntax </font></span><span>&nbsp;</span></li><li class="alt"><span>namespace&nbsp;MyProject3&nbsp;{ &nbsp;</span></li><li class=""><span>&nbsp;</span><span class="comment"><font color="#008200">//&nbsp;PHP&nbsp;code&nbsp;for&nbsp;the&nbsp;MyProject3&nbsp;namespace </font></span><span>&nbsp;</span></li><li class="alt"><span>} &nbsp;</span></li><li class=""><span>?&gt; &nbsp;</span></li></ol>
<p>尽管这么干是可以的,但我建议你不要这么做,最好还是每个文件中只定义一个命名空间,免得把你弄糊涂了。</p>
<p><strong>子命名空间</strong></p>
<p>PHP允许定义具有层次的命名空间以便库能够细分,子命名空间使用一个反斜线字符(\)分隔,如:</p>
<p>◆MyProject\SubName</p>
<p>◆MyProject\Database\MySQL</p>
<p>◆CompanyName\MyProject\Library\Common\Widget1</p>
<p><strong>调用命名空间代码</strong></p>
<p>在lib1.php文件中我们使用App\Lib1 namespace命名空间定义了一个常量、一个函数和一个类,如:</p>
<p>lib1.php</p><ol class="dp-xml"><li class="alt"><span><span class="tag"><strong><font color="#006699">&lt;</font></strong></span><span>&nbsp;?php &nbsp;</span></span></li><li class=""><span>//&nbsp;application&nbsp;library&nbsp;1 &nbsp;</span></li><li class="alt"><span>namespace&nbsp;App\Lib1; &nbsp;</span></li><li class=""><span>&nbsp;</span></li><li class="alt"><span>const&nbsp;</span><span class="attribute"><font color="#ff0000">MYCONST</font></span><span>&nbsp;=&nbsp;</span><span class="attribute-value"><font color="#0000ff">'App\Lib1\MYCONST'</font></span><span>; &nbsp;</span></li><li class=""><span>&nbsp;</span></li><li class="alt"><span>function&nbsp;MyFunction()&nbsp;{ &nbsp;</span></li><li class=""><span>&nbsp;return&nbsp;__FUNCTION__; &nbsp;</span></li><li class="alt"><span>} &nbsp;</span></li><li class=""><span>&nbsp;</span></li><li class="alt"><span>class&nbsp;MyClass&nbsp;{ &nbsp;</span></li><li class=""><span>&nbsp;static&nbsp;function&nbsp;WhoAmI()&nbsp;{ &nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;return&nbsp;__METHOD__; &nbsp;</span></li><li class=""><span>&nbsp;} &nbsp;</span></li><li class="alt"><span>} &nbsp;</span></li><li class=""><span></span><span class="tag"><strong><font color="#006699">?&gt;</font></strong></span><span>&nbsp;</span></li></ol>
<p>现在我们可以在另一个PHP文件包括这段代码,如:</p>
<p>myapp.php</p><ol class="dp-xml"><li class="alt"><span><span class="tag"><strong><font color="#006699">&lt;</font></strong></span><span>&nbsp;?php &nbsp;</span></span></li><li class=""><span>header('Content-type:&nbsp;text/plain'); &nbsp;</span></li><li class="alt"><span>require_once('lib1.php'); &nbsp;</span></li><li class=""><span>&nbsp;</span></li><li class="alt"><span>echo&nbsp;\App\Lib1\MYCONST&nbsp;.&nbsp;"\n"; &nbsp;</span></li><li class=""><span>echo&nbsp;\App\Lib1\MyFunction()&nbsp;.&nbsp;"\n"; &nbsp;</span></li><li class="alt"><span>echo&nbsp;\App\Lib1\MyClass::WhoAmI()&nbsp;.&nbsp;"\n"; &nbsp;</span></li><li class=""><span></span><span class="tag"><strong><font color="#006699">?&gt;</font></strong></span><span>&nbsp;</span></li></ol>
<p>在myapp.php中并没有定义命名空间,因此这段代码存在全局空间中,任何对MYCONST、MyFunction和MyClass的直接引用
都会失败,因为它们存在于App\Lib1命名空间中,为了调用lib1.php中的代码,我们可以在\App\Lib1命名空间前添加前缀定义一个完全
合格的名称,下面是我载入myapp.php时的输出结果:</p><ol class="dp-xml"><li class="alt"><span><span>App\Lib1\MYCONST&nbsp;&nbsp; &nbsp;</span></span></li><li class=""><span>App\Lib1\MyFunction&nbsp;&nbsp; &nbsp;</span></li><li class="alt"><span>App\Lib1\MyClass::WhoAmI&nbsp;</span></li></ol>
<p>完全合格名称可以变得很长,定义长名称,如App-Lib1-MyClass,有一些明显的好处。 <br></p><p><br></p><p><br></p><p><strong>清单 1. 定义<font color="#000000"><b style="color:black;background-color:#A0FFFF">名称空间</b></font></strong><br>
</p><table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
namespace Foo; <br>
class Example {} <br>
?&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
注意,以上 namespace 的声明必须是文件中的第一个命令或输出。在它的前面添加任何内容都会导致一个致命的错误。清单 2 展示了有关这方面的一些例子。<br>
<br>
<strong>清单 2. 定义<b style="color:black;background-color:#A0FFFF">名称空间</b>的错误方法</strong><br>
<table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
/* File1.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
echo "hello world!"; <br>
namespace Bad; <br>
class NotGood {}<br>
?&gt; <br>
<br>
/* File2.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
namespace Bad; <br>
class NotGood {} <br>
?&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
在清单 2 的第 1 部分中,我们尝试在<b style="color:black;background-color:#A0FFFF">名称空间</b>定义之前回传到控制台,这导致产生一个致命错误。在清单的第 2 部分中,我们在 &lt;?<b style="color:black;background-color:#ffff66">php</b> 打开标记的前面多加了一个空格,这样也导致一个致命错误。在编写自己的代码时一定要注意这种情况,因为这是 <b style="color:black;background-color:#ffff66">PHP</b> <b style="color:black;background-color:#A0FFFF">名称空间</b>中很常见的一种错误。<br>
但是,上面的两个例子都可以重新编写,将<b style="color:black;background-color:#A0FFFF">名称空间</b>定义和将在<b style="color:black;background-color:#A0FFFF">名称空间</b>声明中放入的代码放到独立的文件中,然后再将此文件包含到原始文件中。清单 3 演示了这一点。<br>
<br>
<strong>清单 3. 修正定义<b style="color:black;background-color:#A0FFFF">名称空间</b>的错误方法</strong><br>
<table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
/* Good.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
namespace Good; <br>
class IsGood() {} <br>
?&gt; <br>
<br>
/* File1.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
echo "hello world!"; <br>
include './good.<b style="color:black;background-color:#ffff66">php</b>'; <br>
?&gt;<br>
<br>
/* File2.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
include './good.<b style="color:black;background-color:#ffff66">php</b>'; <br>
?&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
现在我们已经了解了如何在一个文件中定义代码的<b style="color:black;background-color:#A0FFFF">名称空间</b>,接下来让我们看看如何在应用程序中利用这个使用<b style="color:black;background-color:#A0FFFF">名称空间</b>的代码。<br>
使用带有<b style="color:black;background-color:#A0FFFF">名称空间</b>的代码定义了<b style="color:black;background-color:#A0FFFF">名称空间</b>并在其中放入代码后,我们就可以在应用程序中方便地使用它。可以使用很多种方法调用带有<b style="color:black;background-color:#A0FFFF">名称空间</b>的函数、类或常量。一种方式是显式地将<b style="color:black;background-color:#A0FFFF">名称空间</b>引用为调用的前缀。另一种方法是为<b style="color:black;background-color:#A0FFFF">名称空间</b>定义一个别名并使用该别名作为调用的前缀,这样做的目的是简化<b style="color:black;background-color:#A0FFFF">名称空间</b>前缀。最后,我们可以只在代码中使用<b style="color:black;background-color:#A0FFFF">名称空间</b>,这就使它成为默认<b style="color:black;background-color:#A0FFFF">名称空间</b>,并且在默认情况下,使所有代码都引用默认<b style="color:black;background-color:#A0FFFF">名称空间</b>。清单 4 演示了调用之间的不同之处。<br>
<br>
<strong>清单 4. 在<b style="color:black;background-color:#A0FFFF">名称空间</b>内调用函数</strong><br>
<table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
/* Foo.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
namespace Foo; <br>
function bar() <br>
{ <br>
&nbsp; &nbsp; echo "calling bar....";<br>
} <br>
?&gt; <br>
<br>
/* File1.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
include './Foo.<b style="color:black;background-color:#ffff66">php</b>'; <br>
Foo/bar(); // outputs "calling bar...."; <br>
?&gt; <br>
<br>
/* File2.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
include './Foo.<b style="color:black;background-color:#ffff66">php</b>';<br>
use Foo as ns; <br>
ns/bar(); // outputs "calling bar...."; <br>
?&gt; <br>
<br>
/* File3.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
include './Foo.<b style="color:black;background-color:#ffff66">php</b>'; <br>
use Foo; <br>
bar(); // outputs "calling bar...."; <br>
?&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
清单 4 演示了在<b style="color:black;background-color:#A0FFFF">名称空间</b> Foo 内调用函数 bar() 的不同方法。在 File1.<b style="color:black;background-color:#ffff66">php</b>内,我们看到了如何进行显式调用,使用<b style="color:black;background-color:#A0FFFF">名称空间</b>的名称作为调用前缀。File2.<b style="color:black;background-color:#ffff66">php</b>使用<b style="color:black;background-color:#A0FFFF">名称空间</b>名称的别名,因此我们使用别名代替<b style="color:black;background-color:#A0FFFF">名称空间</b>的名称。最后,File3.<b style="color:black;background-color:#ffff66">php</b> 仅使用<b style="color:black;background-color:#A0FFFF">名称空间</b>,这允许我们不需要使用任何前缀来调用 bar()。<br>
我们还可以在一个文件内定义多个<b style="color:black;background-color:#A0FFFF">名称空间</b>,只需要在文件中添加更多 namespace 调用。清单 5 演示了这一点。<br>
<br>
<strong>清单 5. 文件中的多个<b style="color:black;background-color:#A0FFFF">名称空间</b></strong><br>
<table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
namespace Foo; <br>
class Test {} <br>
<br>
namespace Bar; <br>
class Test {} <br>
<br>
$a = new Foo\Test; <br>
$b = new Bar\Test; <br>
<br>
var_dump($a, $b); <br>
<br>
Output: <br>
object(Foo\Test)#1 (0) {&nbsp;&nbsp;<br>
}&nbsp;&nbsp;<br>
object(Bar\Test)#2 (0) {&nbsp;&nbsp;<br>
}&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
现在我们已经基本了解了如何在<b style="color:black;background-color:#A0FFFF">名称空间</b>内进行调用,让我们了解一些更复杂的<b style="color:black;background-color:#A0FFFF">名称空间</b>调用以及它们如何工作。<br>
<b style="color:black;background-color:#A0FFFF">名称空间</b>解析要 熟悉<b style="color:black;background-color:#A0FFFF">名称空间</b>的使用,其中一个难点就是了解如何进行范围解析。尽管清单 4所示的简单例子是合理的,但是当我们开始对<b style="color:black;background-color:#A0FFFF">名称空间</b>进行彼此嵌套时,或者在一个<b style="color:black;background-color:#A0FFFF">名称空间</b>中试图针对全局空间发出调用是,就会出现问题。<b style="color:black;background-color:#ffff66">PHP</b> V5.3提供了可以以合理的方式自动解决这些问题的规则。<br>
让我们创建一些包含(include)文件,每个文件都定义了函数 hello()。<br>
<br>
<strong>清单 6. 在不同<b style="color:black;background-color:#A0FFFF">名称空间</b>中定义的 hello() 函数</strong><br>
<table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
/* global.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
function hello() <br>
{ <br>
&nbsp; &nbsp; echo 'hello from the global scope!';<br>
} <br>
?&gt; <br>
<br>
/* Foo.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
namespace Foo; <br>
function hello() <br>
{ <br>
&nbsp; &nbsp; echo 'hello from the Foo namespace!';<br>
} <br>
?&gt; <br>
<br>
/* Foo_Bar.<b style="color:black;background-color:#ffff66">php</b> */ <br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
namespace Foo/Bar; <br>
function hello() <br>
{ <br>
&nbsp; &nbsp; echo 'hello from the Foo/Bar namespace!';<br>
} <br>
?&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
清单 6 在三个不同范围内对 hello() 函数定义了三次:在全局范围内,在 Foo <b style="color:black;background-color:#A0FFFF">名称空间</b>中,在 Foo/Bar <b style="color:black;background-color:#A0FFFF">名称空间</b>中。根据发出 hello() 函数调用的范围,决定对哪个 hello() 函数执行调用。下面展示了这些调用的例子。在这里,我们将使用 Foo <b style="color:black;background-color:#A0FFFF">名称空间</b>查看如何在另一个<b style="color:black;background-color:#A0FFFF">名称空间</b>中调用 hello() 函数。<br>
<br>
<strong>清单 7. 从 Foo <b style="color:black;background-color:#A0FFFF">名称空间</b>调用所有 hello() 函数</strong><br>
<table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
include './global.<b style="color:black;background-color:#ffff66">php</b>'; <br>
include './Foo.<b style="color:black;background-color:#ffff66">php</b>';<br>
include './Foo_Bar.<b style="color:black;background-color:#ffff66">php</b>';<br>
<br>
use Foo; <br>
<br>
hello();&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;// outputs 'hello from the Foo namespace!' <br>
Bar\hello();&nbsp; &nbsp;// outputs 'hello from the Foo/Bar namespace!' <br>
\hello();&nbsp; &nbsp;&nbsp; &nbsp; // outputs 'hello from the global scope!' <br>
?&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
可以看到,在当前<b style="color:black;background-color:#A0FFFF">名称空间</b>内引用子<b style="color:black;background-color:#A0FFFF">名称空间</b>时,可以缩短<b style="color:black;background-color:#A0FFFF">名称空间</b>前缀(Foo/Bar/hello() 调用可被缩短为 Bar/hello())。并且我们看到如何指定以在全局空间内调用方法:只需使用<b style="color:black;background-color:#A0FFFF">名称空间</b>操作符作为调用的前缀。<br>
现在,我们已经了解了<b style="color:black;background-color:#A0FFFF">名称空间</b>的工作机制,下面我们将查看如何在自己的代码中使用它们。<br>
<br>
<br>
<b style="color:black;background-color:#ffff66">PHP</b> <b style="color:black;background-color:#A0FFFF">名称空间</b>用例<b style="color:black;background-color:#A0FFFF">名称空间</b>的总体目标就是帮助我们更好地组织代码,减少全局空间内的定义数量。在本节中,我们将查看一些例子,看看<b style="color:black;background-color:#A0FFFF">名称空间</b>如何帮助我们轻松地实现这些目标。<br>
使用<b style="color:black;background-color:#A0FFFF">名称空间</b>的第三方代码许 多 <b style="color:black;background-color:#ffff66">PHP</b>
应用程序使用来自不同来源的代码,包括像 PEAR 库那样经过精心设计的代码,或者来自 CakePHP 或 ZendFramework
等各种框架的代码,或是来自
Internet上不同位置的代码。在集成这些代码时,最主要的问题之一就是这些代码可能无法恰当地融合到已有代码中;函数或类名可能与应用程序中已经在
使用的内容冲?。<br>
其中一个例子就是 PEAR Date 包。它使用类名 Date,这是一个非常通用的类名,并且可以很好地切入到代码中的其他位置。因此,一个良好的解决方法就是在包内部的 Date.<b style="color:black;background-color:#ffff66">php</b>文件的顶部添加一个简单的<b style="color:black;background-color:#A0FFFF">名称空间</b>命令。现在,当希望使用 PEAR Date 类而不是我们自己的 PEAR Date 类时,就不会感到迷惑。<br>
<br>
<strong>清单 8. 按照<b style="color:black;background-color:#A0FFFF">名称空间</b>的定义使用 PEAR Date 类</strong><br>
<table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
<br>
require_once('PEAR/Date.<b style="color:black;background-color:#ffff66">php</b>'); <br>
<br>
use PEAR\Date;&nbsp; &nbsp; // the name of the namespace we've specified in PEAR/Date.<b style="color:black;background-color:#ffff66">php</b><br>
<br>
// since the current namespace is PEAR\Date, we don't need to prefix the namespace name<br>
$now = new Date(); <br>
echo $now-&gt;getDate();&nbsp;&nbsp;// outputs the ISO formatted date <br>
<br>
// this example shows the full namespace specified as part of the class name <br>
$now = new PEAR\Date\Date(); <br>
echo $now-&gt;getDate();&nbsp;&nbsp;// outputs the ISO formatted date&nbsp;&nbsp;<br>
?&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
我们已经在 PEAR/Date.<b style="color:black;background-color:#ffff66">php</b> 文件的 PEAR/Date <b style="color:black;background-color:#A0FFFF">名称空间</b>内定义了 PEAR Date 类,因此现在只需在我们的文件中包含代码并使用<b style="color:black;background-color:#A0FFFF">名称空间</b>,或使用<b style="color:black;background-color:#A0FFFF">名称空间</b>的名称作为类或函数名的前缀。通过这种方法,我们就可以安全地在应用程序中包含第三方代码。<br>
名称冲突不仅仅是第三方代码才有的问题。如果大型代码库的各个部分永远不会互相靠近,那么也会出现此问题。在下一小节中,我们将了解<b style="color:black;background-color:#A0FFFF">名称空间</b>如何应对这个情况。<br>
<br>
<br>
避免实用函数名冲突几乎所有 <b style="color:black;background-color:#ffff66">PHP</b> 应用程序都具有大量实用方法。虽然并非应用程序的任意对象都包含实用方法,并且也不一定存在于应用程序的所有部分,但是总的来说它在应用程序中确实发挥着作用。但是,随着应用程序不断壮大,实用方法会引起维护问题。<br>
其中一个产生问题的位置就是单元测试,我们编写代码来测试运行应用程序的代码。大多数单元测试套件被设计为运行整个测试套件中的所有测试。比如,我们有两
个永远不会包含在一起的实用方法文件,但是在测试套件中,它们就会包含在一起,因为我们会一次性测试整个应用程序。尽管使用这种方式设计应用程序不利于长
期维护,但是它确实存在于大型遗留代码库中。<br>
清单 9 展示了如何避免这一问题。我们有两个文件 utils_left.<b style="color:black;background-color:#ffff66">php</b> 和 utils_right.<b style="color:black;background-color:#ffff66">php</b>,这是面向主要使用右手的用户和主要使用左手的用户的实用函数集合。对于每个文件,我们在其各自的<b style="color:black;background-color:#A0FFFF">名称空间</b>内分别定义。<br>
<br>
<strong>清单 9. utils_left.<b style="color:black;background-color:#ffff66">php</b> 和 utils_right.<b style="color:black;background-color:#ffff66">php</b></strong><br>
<table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
/* utils_left.<b style="color:black;background-color:#ffff66">php</b> */<br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
namespace Utils\Left;<br>
<br>
function whichHand() <br>
{ <br>
&nbsp; &nbsp; echo "I'm using my left hand!";<br>
} <br>
?&gt; <br>
<br>
/* utils_right.<b style="color:black;background-color:#ffff66">php</b> */<br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
namespace Utils\Right; <br>
<br>
function whichHand() <br>
{ <br>
&nbsp; &nbsp; echo "I'm using my right hand!";<br>
} <br>
?&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
我们定义了一个 whichHand() 函数,函数的输出表示我们使用哪一只手。在清单 10 中,我们看到可以方便地包含两个文件并在希望调用的<b style="color:black;background-color:#A0FFFF">名称空间</b>之间进行切换。<br>
<br>
<strong>清单 10. 同时使用 utils_left.<b style="color:black;background-color:#ffff66">php</b> 和 utils_right.<b style="color:black;background-color:#ffff66">php</b> 的示例</strong><br>
<table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
include('./utils_left.<b style="color:black;background-color:#ffff66">php</b>'); <br>
include('./utils_right.<b style="color:black;background-color:#ffff66">php</b>');<br>
<br>
Utils\Left\whichHand();&nbsp; &nbsp; // outputs "I'm using my left hand!"<br>
Utils\Right\whichHand();&nbsp;&nbsp;// outputs "I'm using my right hand!" <br>
<br>
use Utils\Left; <br>
whichHand();&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;// outputs "I'm using my left hand!" <br>
<br>
use Utils\Right; <br>
whichHand();&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;// outputs "I'm using my right hand!"&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
现在,两个文件可以安全地包含在一起,并且我们指定了处理函数调用所需使用的<b style="color:black;background-color:#A0FFFF">名称空间</b>。而且,对现有代码的影响很小,因为重构功能只需要我们在文件的顶部添加 use 语句,表示要使用的<b style="color:black;background-color:#A0FFFF">名称空间</b>。<br>
可以对我们定义的 <b style="color:black;background-color:#ffff66">PHP</b> 代码进一步扩展。在下一小节,我们将了解如何在<b style="color:black;background-color:#A0FFFF">名称空间</b>内覆盖内部函数。<br>
<br>
<br>
覆盖内部函数名称虽然 <b style="color:black;background-color:#ffff66">PHP</b> 的内部函数经常可以提供非常棒的实用方法,但有时它们不能按照我们期望的那样执行。我们需要增强它们的行为,以使函数符合我们的期望,但是我们也需要使用另一个名字重新定义函数,从而避免进一步混淆范围。<br>
文件系统函数就需要我们执行这些操作。假设我们需要确保 file_put_contents() 创建的任何文件具有某些权限集。比如,假设我们希望这些文件的权限为只读;我们可以使用一个新的<b style="color:black;background-color:#A0FFFF">名称空间</b>重新定义函数,如下所示。<br>
<br>
<strong>清单 11. 在<b style="color:black;background-color:#A0FFFF">名称空间</b>内定义 file_put_contents() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong><br>
<table class="t_table" style="width:98%" cellspacing="0"><tbody><tr><td>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>
&lt;?<b style="color:black;background-color:#ffff66">php</b> <br>
namespace Foo; <br>
<br>
function file_put_contents( $filename, $data, $flags = 0, $context = null ) <br>
{ <br>
&nbsp; &nbsp; $return = \file_put_contents( $filename, $data, $flags, $context );<br>
&nbsp; &nbsp; <br>
&nbsp; &nbsp; chmod($filename, 0444);<br>
<br>
&nbsp; &nbsp; return $return;<br>
} <br>
?&gt;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</td></tr></tbody></table><br>
我们在函数内调用内部 file_put_contents() 函数并使用一个反斜杠作为函数名的前缀,表示该函数应当在全局范围内处理,这表示将调用内部函数。调用了内部函数后,我们随后对文件执行 chmod() 命令来设置相应的权限。<br>
还有许多例子可以演示如何使用<b style="color:black;background-color:#A0FFFF">名称空间</b>增强代码。在任何情况下,我们应避免执行不恰当的修改,比如将函数名或类名作为前缀以生成独特的名称。我们现在还了解了如何使用<b style="color:black;background-color:#A0FFFF">名称空间</b>在大型应用程序中更加安全地包含第三方代码,同时不需要担心名称冲突。<br>
<br>
结束语<b style="color:black;background-color:#ffff66">PHP</b> V5.3的<b style="color:black;background-color:#A0FFFF">名称空间</b>是该语言中一个非常受欢迎的新增特性,可以帮助开发人员合理地组织应用程序的代码。该特性使您能够避免使用标准来处理<b style="color:black;background-color:#A0FFFF">名称空间</b>,允许您编写更高效的代码。尽管<b style="color:black;background-color:#A0FFFF">名称空间</b>的出现经历了很长时间,但对于受名称冲突困扰的大型 <b style="color:black;background-color:#ffff66">PHP</b> 应用程序来说,它是一个非常受欢迎的特性
页: [1]
查看完整版本: PHP 5.3中的命名空间