在PHP5.3之前,我们经常将类命名为Product_info_price_list之类,以避免类还有函数的命名冲突和污染。
另外还一个目标,比如我们想在一个项目中使用两个框架,需要我们从这些框架中抽取一些有用的函数或类,因为这些框架有着这样那样的优势,经过挑选后的整合是个大坑,那就是很多类的命名是一样的,重名冲突会相当严重。
从PHP 5.3之后,它顺应潮流,推出了命名空间(name space)这样一个概念。如果你写过Java或.NET,那么就能明白,这个概念就是用来解决上面这些命名混乱等问题的。命名空间可以让我们不用起那么长的名字,可以继续使用较短的名字来命名,也可以省却一些学英语的小烦恼。
PHP命名空间均声明在文件顶部,适用于所有在该文件中声明的类、方法和常数。
我会重点介绍命名空间对类的影响,这些原则也适合于其他项目。我们举例来说,下面这段代码都包含在名为Shipping的命名空间里,如代码清单2-32所示:
代码清单2-32 shipping的命名空间代码
namespace shipping; class courier { public $name; public $home_country; public static function getCourier($courier_list){ return $courier_list; } }
正常情况下,如果想实例化一个Courier对象,PHP会在全局命名空间中寻找这个类,由于它已经被定义在shipping命名空间里了,结果肯定是找不到的。
正确引用的方法是使用它的全名:shipping\Courier(注意是反斜杠)。
当在全局命名空间里将所有的类整齐放入小命名空间时,这个工作当然是非常棒的,但当要在另一个命名空间的代码中包含类时我们该怎么办?遇到这种情况,只需要使用一个引导命名空间的标识:反斜杠放在类名的前面,表示PHP将从命名空间中的顶部开始查找。
因此,在任意命名空间中使用命名空间中的类,可以这样做:
namespace Fred; $courier = new \shipping\Courier();
要引用Courier这个类,我们需要知道自己在哪一个命名空间中,比如:
·在shipping命名空间中,称为Courier。
·在全局命名空间中,称为shipping\Courier。
·在其他命名空间中,需要从顶部开始,这样来指代它:\shipping\Courier。
为了突出命名空间的价值,我们可以在一个名为Fred的命名空间下声明另一个Courier类,而且在代码中两个对象可以使用相同的类名而不报错,只需在顶层命名空间中声明这两个类即可。
当我们想使用两个或更多框架下的类库而不出毛病时,如Laravel或Zend,这些框架中都可能有一个类叫Log。可以在命名空间内部再创建命名空间,注意命名空间之间用分隔符就可以。
举个例子,一个网站可能同时具有博客和电子商务的功能,它有类似这样一个命名空间的类结构:
Shop Products products productCategory shipping Courier admin user User
由于Courier类位于第3层,用命名空间声明时将shop\shipping放进一个文件顶部即可。
加上适当的前缀,我们使用通过命名空间操作符替代多个下划线的方法,解决了长类名的问题。PHP还是允许我们像操作数据表一样把名字进行简写,以指代命名空间,包括在一个文件中使用多个命名空间。
代码清单2-33描述了我们刚才所描述的一系列类,如下:
代码清单2-33 使用命名空间
use shop\shipping; //使用哪个命名空间 use admin\user as u; //命名空间别名 //我们用哪个命名空间的Courier类 $couriers = shipping\Courier::getCouriersByCountry('China'); //浏览用户账号并显示名字 $user = new u\User(); Echo $user->getDisplayName();
我们可以看到,因为声明了shipping命名空间,可以将嵌套命名空间中的最低一层作为缩写。第二行的命名空间使用,我们将user重命名成u,可以用u这个空间名来引用这个类。
以上这些对于我们逐步解决多数特定元素的同名问题大有裨益,你完全可以为这些名字另取一个更有特色的简称来加以区分。
事实上,命名空间被更多地应用于自动加载(Auto Load)功能上,它长得也非常像目录分隔符。相对于PHP而言,命名空间是一个新增加的内容,我们需要在类库和框架两层概念中深入理解它们。