PHP命名空间(namespace)原理与用法详解


Posted in PHP onDecember 11, 2019

本文实例讲述了PHP命名空间(namespace)原理与用法。分享给大家供大家参考,具体如下:

PHP 命名空间(namespace)是在PHP 5.3中加入的,它可以解决以下两类问题:

  1. 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
  2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。

我们在默认情况下,所有常量、类和函数名都放在全局空间下,就和PHP支持命名空间之前一样,命名空间通过关键字namespace 来声明,如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间。我们来看下语法:

<?php 
// 定义代码在 'MyProject' 命名空间中 
namespace MyProject; 
// ... 代码 ...

我们也可以在同一个文件中定义不同的命名空间代码,如下:

<?php 
namespace MyProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
namespace AnotherProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
?>

不过我不建议使用这种语法在单个文件中定义多个命名空间,有需要的话,可以使用大括号形式的语法,如下:

<?php
namespace MyProject {
  const CONNECT_OK = 1;
  class Connection { /* ... */ }
  function connect() { /* ... */ }
}
namespace AnotherProject {
  const CONNECT_OK = 1;
  class Connection { /* ... */ }
  function connect() { /* ... */ }
}
?>

我们如果要将全局的非命名空间中的代码与命名空间中的代码组合在一起,只能使用大括号形式的语法,并且全局代码必须用一个不带名称的 namespace 语句加上大括号括起来,如下:

<?php
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>

我们在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的 declare 语句,要记住,除了这个之外的所有非 PHP 代码包括空白符都不能出现在命名空间的声明之前,如下:

<?php
declare(encoding='UTF-8'); //定义多个命名空间和不包含在命名空间中的代码
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>

看个错误的写法:

<html>
<?php
namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句
?>

与目录和文件的关系很像,PHP 命名空间也允许指定层次化的命名空间的名称,因此,命名空间的名字可以使用分层次的方式定义,模式如下:

<?php
namespace MyProject\Sub\Level; //声明分层次的单个命名空间
const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */ }
?>

上述代码中,创建了常量 MyProject\Sub\Level\CONNECT_OK,类 MyProject\Sub\Level\Connection 和函数 MyProject\Sub\Level\Connect。

咱们再来看下PHP 命名空间中的类名的引用方式:

  1. 非限定名称,或不包含前缀的类名称,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。 警告:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。
  2. 限定名称,或包含前缀的名称,例如 $a = new subnamespace\foo(); 或 subnamespace\foo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespace\subnamespace\foo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespace\foo。
  3. 完全限定名称,或包含了全局前缀操作符的名称,例如, $a = new \currentnamespace\foo(); 或 \currentnamespace\foo::staticmethod();。在这种情况下,foo 总是被解析为代码中的文字名(literal name)currentnamespace\foo。

咱们用来个文件来演示下,首先来看f1.php的代码:

<?php
namespace Foo\Bar\subnamespace; 
const FOO = 1;
function foo() {}
class foo
{
  static function staticmethod() {}
}
?>

之后就是f2.php的代码:

<?php
namespace Foo\Bar;
include 'f1.php';
const FOO = 2;
function foo() {}
class foo
{
  static function staticmethod() {}
}
/* 非限定名称 */
foo(); // 解析为函数 Foo\Bar\foo
foo::staticmethod(); // 解析为类 Foo\Bar\foo ,方法为 staticmethod
echo FOO; // 解析为常量 Foo\Bar\FOO
/* 限定名称 */
subnamespace\foo(); // 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // 解析为类 Foo\Bar\subnamespace\foo,
                 // 以及类的方法 staticmethod
echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO
/* 完全限定名称 */
\Foo\Bar\foo(); // 解析为函数 Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // 解析为类 Foo\Bar\foo, 以及类的方法 staticmethod
echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO
?>

我们访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen() 或 \Exception 或 \INI_ALL。之后,咱们再来看下在命名空间内部访问全局类、函数和常量的实例:

<?php
namespace Foo;
function strlen() {}
const INI_ALL = 3;
class Exception {}
$a = \strlen('hi'); // 调用全局函数strlen
$b = \INI_ALL; // 访问全局常量 INI_ALL
$c = new \Exception('error'); // 实例化全局类 Exception
?>

PHP 命名空间的实现受到其语言自身的动态特征的影响,我们先来看一段代码:

<?php
class classname
{
  function __construct()
  {
    echo __METHOD__,"\n";
  }
}
function funcname()
{
  echo __FUNCTION__,"\n";
}
const constname = "global";
$a = 'classname';
$obj = new $a; // prints classname::__construct
$b = 'funcname';
$b(); // prints funcname
echo constant('constname'), "\n"; // prints global
?>

我们如果要将上述的代码转换到命名空间中,并且动态访问元素,就必须使用完全限定名称(包括命名空间前缀的类名称)。注意因为在动态的类名称、函数名称或常量名称中,限定名称和完全限定名称没有区别,因此其前导的反斜杠是不必要的。如下:

<?php
namespace namespacename;
class classname
{
  function __construct()
  {
    echo __METHOD__,"\n";
  }
}
function funcname()
{
  echo __FUNCTION__,"\n";
}
const constname = "namespaced";
include 'example1.php';
$a = 'classname';
$obj = new $a; // 输出 classname::__construct
$b = 'funcname';
$b(); // 输出函数名
echo constant('constname'), "\n"; // 输出 global
/* 如果使用双引号,使用方法为 "\\namespacename\\classname"*/
$a = '\namespacename\classname';
$obj = new $a; // 输出 namespacename\classname::__construct
$a = 'namespacename\classname';
$obj = new $a; // 输出 namespacename\classname::__construct
$b = 'namespacename\funcname';
$b(); // 输出 namespacename\funcname
$b = '\namespacename\funcname';
$b(); // 输出 namespacename\funcname
echo constant('\namespacename\constname'), "\n"; // 输出 namespaced
echo constant('namespacename\constname'), "\n"; // 输出 namespaced
?>

好啦,本次记录就到这里了,后续的记录会在之后的文章中有体现。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
十天学会php之第四天
Oct 09 PHP
php&amp;java(一)
Oct 09 PHP
关于在php.ini中添加extension=php_mysqli.dll指令的说明
Jun 14 PHP
PHP6 mysql连接方式说明
Feb 09 PHP
php array_push()数组函数:将一个或多个单元压入数组的末尾(入栈)
Jul 12 PHP
php中echo()和print()、require()和include()等易混淆函数的区别
Feb 22 PHP
php在项目中寻找代码的坏味道(综艺命名)
Jul 19 PHP
深入array multisort排序原理的详解
Jun 18 PHP
php实现读取手机客户端浏览器的类
Jan 09 PHP
PHP pear安装配置教程
May 14 PHP
php的api数据接口书写实例(推荐)
Sep 22 PHP
详解PHP 二维数组排序保持键名不变
Mar 06 PHP
在 Laravel 6 中缓存数据库查询结果的方法
Dec 11 #PHP
PHP超级全局变量【$GLOBALS,$_SERVER,$_REQUEST等】用法实例分析
Dec 11 #PHP
关于PHP5.6+版本“No input file specified”问题的解决
Dec 11 #PHP
Laravel5.1 框架数据库操作DB运行原生SQL的方法分析
Jan 07 #PHP
Laravel5.1 框架表单验证操作实例详解
Jan 07 #PHP
PHP字符串与数组处理函数用法小结
Jan 07 #PHP
Laravel5.1 框架响应基本用法实例分析
Jan 04 #PHP
You might like
实现树状结构的两种方法
2006/10/09 PHP
redis查看连接数及php模拟并发创建redis连接的方法
2016/12/15 PHP
java script编程起步(第三课)
2007/01/10 Javascript
Jquery 模板数据绑定插件的使用方法详解
2013/07/08 Javascript
鼠标划过实现延迟加载并隐藏层的js代码
2013/10/11 Javascript
JavaScript中的数组特性介绍
2014/12/30 Javascript
JavaScript在浏览器标题栏上显示当前日期和时间的方法
2015/03/19 Javascript
微信WeixinJSBridge API使用实例
2015/05/25 Javascript
详解JavaScript函数
2015/12/01 Javascript
利用imgareaselect辅助后台实现图片上传裁剪
2017/03/02 Javascript
webpack配置文件和常用配置项介绍
2017/04/28 Javascript
JavaScript实现的仿新浪微博原生态输入字数即时检查功能【兼容IE6】
2017/09/26 Javascript
Node.js学习教程之HTTP/2服务器推送【译】
2017/10/31 Javascript
微信小程序拼接图片链接无底洞深入探究
2019/09/03 Javascript
JS实现单张或多张图片持续无缝滚动的示例代码
2020/05/10 Javascript
[58:25]VP vs RNG 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
[40:31]Secret vs Alliacne 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
Python中的对象,方法,类,实例,函数用法分析
2015/01/15 Python
在DigitalOcean的服务器上部署flaskblog应用
2015/12/19 Python
python如何实现远程控制电脑(结合微信)
2015/12/21 Python
centos6.4下python3.6.1安装教程
2017/07/21 Python
基于Django contrib Comments 评论模块(详解)
2017/12/08 Python
Python使用爬虫抓取美女图片并保存到本地的方法【测试可用】
2018/08/30 Python
Python后台管理员管理前台会员信息的讲解
2019/01/28 Python
python numpy 常用随机数的产生方法的实现
2019/08/21 Python
如何基于python测量代码运行时间
2019/12/25 Python
CSS3动画:5种预载动画效果实例
2017/04/05 HTML / CSS
玩具反斗城葡萄牙官方商城:Toys"R"Us葡萄牙
2016/10/21 全球购物
Dillard’s百货官网:Dillards.com
2018/05/26 全球购物
西式婚礼证婚词
2014/01/12 职场文书
银行委托书范本
2014/04/04 职场文书
教育实践活动对照检查材料
2014/09/23 职场文书
大学感恩节活动策划方案
2014/10/11 职场文书
学习十八届四中全会依法治国心得体会
2014/11/03 职场文书
Python合并pdf文件的工具
2021/07/01 Python
MySQL存储过程及语法详解
2022/08/05 MySQL