什么是命名空间?从广义上来说,命名空间是一种封装事物的方法,在很多地方都可以见到这种抽象概念。例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,它就扮演了命名空间的角色。
举个简单的例子,文件foo.txt
可以同时在目录 /home/greg
和/home/other
中存在,但在同一个目录中不能存在两个 foo.txt
文件。另外,在目录 /home/greg
外访问foo.txt
文件时,我们必须将目录名以及目录分隔符放在文件名之前,例如 /home/greg/foo.txt
。这个原理应用到程序设计领域就是命名空间的概念。
PHP 命名空间可以解决以下两类问题:
定义命名空间使用namespace关键字
namespace命名空间名称解析规则
1.非限定名称:名称中不包含命名空间分割符,如:myself
2.限定名称:名称中含有命名空间分割符,如:admin\myself
3.完全限定名称:名称中包含分割符,并以命名空间分割符开始,如:\admin\myself (即绝对路径的概念)
虽然任意合法的 PHP 代码都可以包含在命名空间中,但只有类(包括抽象类和 traits)、接口、函数和常量等类型的代码受命名空间的影响。
<?php
/**
* 命名空间:namespace
* 全局成员: 常量,函数,类(接口),默认声明在全局空间中的
* 优点:全局调用
* 缺点:不能重复命名
*
*
* php5.3以后引入C# Java中应用成熟的命名空间
*/
// 在声明命名空间之前除了用于定义源文件编码方式的 declare 语句外,所有非 PHP 代码(包括空白符)都不能出现在命名空间声明之前
namespace MyProject;
const CONNECT_OK = 1;
interface Itest
{
public function getSite();
}
class className implements Itest {
public function getSite()
{
return __METHOD__." 命名空间<br>";
}
}
namespace OtherProject;
const CONNECT_OK = 1;
interface Itest
{
public function getSite();
}
class className implements Itest {
public function getSite()
{
return __METHOD__." 命名空间<br>";
}
}
// 也可以使用大括号进行包裹
namespace AnotherProject;
const CONNECT_OK = 1;
class className {
/* ... */
}
//在AnotherProject空间中访问MyProject空间里的类, 应该使用"\"表示root空间(全局空间)
echo (new \MyProject\className)->getSite();//绝对路径 类的完全限定名称\MyProject\className
echo (new \OtherProject\className)->getSite();
/*
MyProject\className::getSite 命名空间
OtherProject\className::getSite 命名空间
*/
?>
注意:在实际的编程实践中,并不提倡在同一个文件中定义多个命名空间。定义多个命名空间主要用于将多个 PHP 脚本合并在同一个文件中。在定义多个命名空间时建议使用大括号形式的语法。
将全局的非命名空间中的代码与命名空间中的代码组合在一起,只能使用大括号形式的语法,同时全局代码必须用一个不带名称的 namespace 语句加上大括号括起来
<?php
namespace MyProject{ // 命名空间中的代码
const CONNECT_OK = 1;
class className {
/* ... */
}
}
namespace AnotherProject{
const CONNECT_OK = 1;
class className {
/* ... */
}
}
namespace { // 命名空间外的全局代码 全局空间 匿名空间 公共空间
$obj = new MyProject\className;
}
?>
与目录和文件的关系很象,PHP 中的命名空间也允许指定层次化的命名空间名称。因此,命名空间的名字可以使用分层次的方式定义:
namespace App\Model;
namespace App\Controller\Home;
<?php
namespace Controller\Home\MyProject{
const CONNECT_OK = 1;
class className {
public function getInfo(){
echo __METHOD__." hello world<br>";
}
}
}
namespace Controller\Home{
const CONNECT_OK = 1;
class className {
public function getInfo(){
echo __METHOD__." hello world<br>";
}
}
// 查看类的完全限定名称
echo className::class."<br>";
// Controller\Home\className
(new MyProject\className)->getInfo();
// Controller\Home\MyProject\className::getInfo hello world
}
// 命名空间外的全局代码 全局空间 匿名空间 公共空间
namespace {
class className {
public function getInfo(){
echo __METHOD__." hello world<br>";
}
}
(new className)->getInfo();
(new Controller\Home\className)->getInfo();
$obj = new Controller\Home\MyProject\className();
$obj->getInfo();
/*
className::getInfo hello world
Controller\Home\className::getInfo hello world
Controller\Home\MyProject\className::getInfo hello world
*/
}
?>
在文件系统中访问一个文件有三种方式:
PHP 命名空间中的类名可以通过三种方式引用:
非限定名称,或不包含前缀的类名称
限定名称,或包含前缀的名称
完全限定名称,或包含了全局前缀操作符的名称
file1.php
<?php
function getInfo(){
return '我是全局空间下的函数';
}
function foo() {
echo __METHOD__."<br>";
}
?>
file2.php
<?php
namespace Foo\Bar\subnamespace;
const FOO = 1;
function foo() {
echo __METHOD__."<br>";
}
class foo
{
static function staticmethod() {
echo __METHOD__."<br>";
}
}
namespace Foo\Bar;
include 'file1.php';
const FOO = 2;
function foo() {
echo __METHOD__."<br>";
}
class foo
{
static function staticmethod() {
echo __METHOD__."<br>";
}
}
/* 非限定名称 */
foo(); // Foo\Bar\foo
foo::staticmethod(); // Foo\Bar\foo::staticmethod
echo FOO; // 解析为常量 Foo\Bar\FOO 2
echo getInfo(); // 我是全局空间下的函数
// 如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称
/* 限定名称 */
subnamespace\foo(); // Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // Foo\Bar\subnamespace\foo::staticmethod
echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO 1
/* 完全限定名称 */
\Foo\Bar\foo(); // Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // Foo\Bar\foo::staticmethod
echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO 2
?>
file3.php
<?php
namespace Foo;
function strlen($str) {
return "Foo空间的strlen";
}
const INI_ALL = 3;
class Exception {}
class Test {
const APP_NAME = '我是Foo空间中类常量 hello world';
}
echo strlen('hi'); // Foo空间的strlen
echo INI_ALL; // 当前空间下的常量 3
var_dump(new Exception); // object(Foo\Exception)#1 (0) { }
echo Test::APP_NAME; // 我是Foo空间中类常量 hello world
// 如果调用的函数与系统函数重名,或者与定义在全局空间中的自定义函数同名,优先调用的是当前空间中的函数, 找不到时,才会去全局空间查找
echo "<pre>";
echo \strlen('hi')."<br>" ; // 调用全局函数strlen
echo \INI_ALL."<br>"; // 访问全局常量 INI_ALL
var_dump(new \Exception('error')); // 实例化全局类 Exception
echo \Test::APP_NAME; // hello world
namespace {
class Test
{
const APP_NAME = ' hello world';
}
}
?>
注意:访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen() 或 \Exception 等。
PHP 允许通过别名引用或导入的方式来使用外部的命名空间,这是命名空间的一个重要特征。
使用 use 关键字可以实现命名空间的导入,从 PHP5.6 开始允许导入函数和常量,并为它们设置别名。语法格式如下:
use namespace;
在 PHP 中,别名是通过操作符 use 与 as 来实现的,语法格式如下:
use 命名空间 as 别名;
示例:使用 use 操作符导入和使用别名
<?php
/**
* use 在命名空间中的作用
* 1. 引入别的命名空间到当前空间 为命名空间起别名
* 2. 引入别的命名空间中的类到当前空间使用, 为别的命名空间中的类起别名
*/
namespace app\admin\controller;
//控制器类index
class Index
{
public function index()
{
return __METHOD__;
}
}
const CONSTANT = "zhang";
function getName($name){
return $name;
}
namespace extend\lib;
// 在当前空间下访问app\admin\controller中的Index类
echo ( new \app\admin\controller\Index )->index(),'<hr>';
//1. use 导入命名空间
use app\admin\controller;
// 成功导入命名空间以后 就可以不使用类的完全限定名称调用类元素
echo (new controller\Index)->index(),'<hr>';
//2.为空间起别名
use app\admin\controller as app;
echo (new app\Index)->index(),'<hr>';
// use 直接引入外部空间类-------------
//1. 使用use 引入一个外部类到当前空间
use app\admin\controller\Index;
//导入类以后就可以使用非限定名称度调用类元素
echo (new Index)->index(),'<hr>';
//2.为引入的外部空间该类起别名
use app\admin\controller\Index as in;
echo (new in)->index(),'<hr>';
// use 还可以导入一个常量和函数----------
// 导入一个函数
use function app\admin\controller\getName;
echo getName("zhang "); // zhang
// 导入一个函数并定义别名
use function app\admin\controller\getName as func;
echo func("shuai"); // shuai
// 导入一个常量
use const app\admin\controller\CONSTANT;
echo "<br>".CONSTANT;
在早期 PHP 开发中,开发者最烦的就是一堆 include 函数包含了一大堆文件,而且早期时候 PHP 面向对象的概念确实太差了,因为 PHP 作为一种脚本语言,不存在程序入口,所以脚本顺序化执行的诱惑力实在是很大,即使面向对象开发,但是缺少极佳的模块划分导入机制,代码可以说很难有美感。
/**
* 类的加载
*/
//应用入门文件
namespace app;
//传统引入加载方式
// require 'app/models/StaffsModel.php';
// require 'app/models/UserModel.php';
// require 'app/controller/User.php';
//类的自动加载
require 'app/autoload.php';
use app\models\StaffsModel;
use app\models\UserModel;
use app\controller\User;
use app\controller\Login;
$staffs = new StaffsModel;
$user = new UserModel;
$userController = new User;
$loginController = new Login;
/*
类名:app\models\StaffsModel
类名:app\models\UserModel
类名:app\controller\User
类名:app\controller\Login
*/
autoload() 是系统函数,名称是固定的,而且该函数没有方法体,需要我们自定义方法体。另外,该函数是自动调用的,当我们 new 一个类时,如果当前源文件中找不到这个类,PHP 则会自动调用 autoload() 函数,并将类名传递给 __autoload() 函数。
spl_autoload_register(function($className)
{
//先查看要加载的类
printf('类名:%s<br>',$className);
//将类命名空间与类文件所在的路径进行一一映射, linux / windows \
// app\controller\User <=> app/controller/User.php
//解决在linux系统中命名空间失效 class not found
$file = str_replace('\\',DIRECTORY_SEPARATOR,$className).'.php';
if( !(is_file($file) && file_exists($file)) )
throw new \Exception('文件名不合法或者文件不存在');
require $file;
});
【PHP 面向对象】面向对象(OOP)编程之魔术方法实现重载知识点归纳总结(三)
【PHP 面向对象】面向对象(OOP)编程知识点归纳总结(二)
【PHP 面向对象】面向对象(OOP)编程知识点归纳总结(一)