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 相关文章推荐
增加反向链接的101个方法 站长推荐
Jan 31 PHP
PHPLog php 程序调试追踪工具
Sep 09 PHP
PHP得到mssql的存储过程的输出参数功能实现
Nov 23 PHP
解析php file_exists无效的解决办法
Jun 26 PHP
PHP实现自动登入google play下载app report的方法
Sep 23 PHP
php实现获取及设置用户访问页面语言类
Sep 24 PHP
PHP实现将textarea的值根据回车换行拆分至数组
Jun 10 PHP
ThinkPHP中Common/common.php文件常用函数功能分析
May 20 PHP
PHP的PDO预定义常量讲解
Jan 24 PHP
PHP PDOStatement::setFetchMode讲解
Feb 03 PHP
laravel批量生成假数据的方法
Oct 09 PHP
PHP实现爬虫爬取图片代码实例
Mar 03 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
各种咖啡的英文名子是什么
2021/03/03 新手入门
php4的session功能评述(一)
2006/10/09 PHP
android上传图片到PHP的过程详解
2015/08/03 PHP
PHP实现操作redis的封装类完整实例
2015/11/14 PHP
PHP实现bitmap位图排序与求交集的方法
2016/07/28 PHP
PHP将身份证正反面两张照片合成一张图片的代码
2017/04/08 PHP
菜鸟javascript基础资料整理2
2010/12/06 Javascript
JS事件Event元素(兼容IE,Firefox,Chorme)
2012/11/01 Javascript
jquery DIV撑大让滚动条滚到最底部代码
2013/06/06 Javascript
javaScript面向对象继承方法经典实现
2013/08/20 Javascript
iframe如何动态创建及释放其所占内存
2014/09/03 Javascript
简述JavaScript提交表单的方式 (Using JavaScript Submit Form)
2016/03/18 Javascript
JS中Json数据的处理和解析JSON数据的方法详解
2016/06/29 Javascript
Vuex和前端缓存的整合策略详解
2017/05/09 Javascript
angular ng-click防止重复提交实例
2017/06/16 Javascript
nodejs 图片预览和上传的示例代码
2017/09/30 NodeJs
vue项目中引入noVNC远程桌面的方法
2018/03/05 Javascript
vue的mixins属性详解
2018/03/14 Javascript
js实现右键弹出自定义菜单
2020/09/08 Javascript
Python实现模拟时钟代码推荐
2015/11/08 Python
基于python进行桶排序与基数排序的总结
2018/05/29 Python
python 集合 并集、交集 Series list set 转换的实例
2018/05/29 Python
Python常见排序操作示例【字典、列表、指定元素等】
2018/08/15 Python
Python解决线性代数问题之矩阵的初等变换方法
2018/12/12 Python
如何利用Python给自己的头像加一个小国旗(小月饼)
2020/10/02 Python
详解查看Python解释器路径的两种方式
2020/10/15 Python
CSS3实现10种Loading效果
2016/07/11 HTML / CSS
英国航空官网:British Airways
2016/09/11 全球购物
什么是组件架构
2016/05/15 面试题
实习心得体会
2014/01/02 职场文书
英语演讲稿范文
2014/01/03 职场文书
幼儿园春游活动方案
2014/01/19 职场文书
建材投资建议书
2014/05/16 职场文书
幼儿园门卫岗位职责范本
2014/07/02 职场文书
奉献爱心演讲稿
2014/09/04 职场文书
岳庙导游词
2015/02/04 职场文书