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 相关文章推荐
最省空间的计数器
Oct 09 PHP
一个可以删除字符串中HTML标记的PHP函数
Oct 09 PHP
php 学习资料零碎东西
Dec 04 PHP
通过JavaScript或PHP检测Android设备的代码
Mar 09 PHP
php用正则表达式匹配中文实例详解
Nov 06 PHP
PHP自动重命名文件实现方法
Nov 04 PHP
php从memcache读取数据再批量写入mysql的方法
Dec 29 PHP
PHP callback函数使用方法和注意事项
Jan 23 PHP
UPUPW 更新 64 位 Apache 系列 PHP 7.0 正式版
Dec 08 PHP
PHP+Apache+Mysql环境搭建教程
Aug 01 PHP
thinkPHP5 tablib标签库自定义方法详解
May 10 PHP
PHP xpath()函数讲解
Feb 11 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
PHP5新特性: 更加面向对象化的PHP
2006/11/18 PHP
PHP下用rmdir实现删除目录的三种方法小结
2008/04/20 PHP
火车采集器 免费版使出收费版本功能实现原理
2009/09/17 PHP
PHP中通过加号合并数组的一个简单方法分享
2011/01/27 PHP
单点登录 Ucenter示例分析
2013/10/29 PHP
Yii2中关联查询简单用法示例
2016/08/10 PHP
基于javascript 闭包基础分享
2013/07/10 Javascript
JS 对输入框进行限制(常用的都有)
2013/07/30 Javascript
js实现拖拽效果
2015/02/12 Javascript
jQuery实现的多屏图像图层切换效果实例
2015/05/07 Javascript
黑帽seo劫持程序,js劫持搜索引擎代码
2015/09/15 Javascript
js实现tab切换效果实例
2015/09/16 Javascript
js判断输入字符串是否为空、空格、null的方法总结
2016/06/14 Javascript
thinkphp标签实现bootsrtap轮播carousel实例代码
2017/02/19 Javascript
基于JavaScript实现的希尔排序算法分析
2017/04/14 Javascript
Angular2.0实现modal对话框的方法示例
2018/02/18 Javascript
webpack打包并将文件加载到指定的位置方法
2018/02/22 Javascript
Vue项目使用CDN优化首屏加载问题
2018/04/01 Javascript
vue如何判断dom的class
2018/04/26 Javascript
Vuex 使用及简单实例(计数器)
2018/08/29 Javascript
OpenLayers3实现图层控件功能
2020/09/25 Javascript
解决VantUI popup 弹窗不弹出或无蒙层的问题
2020/11/03 Javascript
python中的五种异常处理机制介绍
2014/09/02 Python
小议Python中自定义函数的可变参数的使用及注意点
2016/06/21 Python
python 读文件,然后转化为矩阵的实例
2018/04/23 Python
python数据批量写入ScrolledText的优化方法
2018/10/11 Python
TensorFlow MNIST手写数据集的实现方法
2020/02/05 Python
Python找出列表中出现次数最多的元素三种方式
2020/02/24 Python
CentOS 7如何实现定时执行python脚本
2020/06/24 Python
用python制作个音乐下载器
2021/01/30 Python
Html5与App的通讯方式详解
2019/10/24 HTML / CSS
PHP如何删除一个Cookie值
2012/11/15 面试题
C语言基础笔试题
2013/04/27 面试题
中秋节晚会开场白
2015/05/29 职场文书
SQLServer2008提示评估期已过解决方案
2021/04/12 SQL Server
解决golang 关于全局变量的坑
2021/05/06 Golang