博客列表 >【PHP 面向对象】面向对象(OOP)编程之解读命名空间使用知识点归纳总结(四)

【PHP 面向对象】面向对象(OOP)编程之解读命名空间使用知识点归纳总结(四)

 一纸荒凉* Armani
 一纸荒凉* Armani原创
2021年05月11日 14:30:09950浏览

PHP namespace:命名空间

什么是命名空间?从广义上来说,命名空间是一种封装事物的方法,在很多地方都可以见到这种抽象概念。例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,它就扮演了命名空间的角色。

举个简单的例子,文件foo.txt可以同时在目录 /home/greg/home/other 中存在,但在同一个目录中不能存在两个 foo.txt文件。另外,在目录 /home/greg外访问foo.txt文件时,我们必须将目录名以及目录分隔符放在文件名之前,例如 /home/greg/foo.txt。这个原理应用到程序设计领域就是命名空间的概念。

PHP 命名空间可以解决以下两类问题:

  • 用户编写的代码与 PHP 内部的类/函数/常量或第三方类/函数/常量之间的命名冲突;
  • 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,以提高源代码的可读性。
  • PSR-4规范 https://www.cnblogs.com/q1104460935/p/8084396.html

定义命名空间

定义命名空间使用namespace关键字

namespace命名空间名称解析规则

1.非限定名称:名称中不包含命名空间分割符,如:myself

2.限定名称:名称中含有命名空间分割符,如:admin\myself

3.完全限定名称:名称中包含分割符,并以命名空间分割符开始,如:\admin\myself (即绝对路径的概念)

虽然任意合法的 PHP 代码都可以包含在命名空间中,但只有类(包括抽象类和 traits)、接口、函数和常量等类型的代码受命名空间的影响。

  1. <?php
  2. /**
  3. * 命名空间:namespace
  4. * 全局成员: 常量,函数,类(接口),默认声明在全局空间中的
  5. * 优点:全局调用
  6. * 缺点:不能重复命名
  7. *
  8. *
  9. * php5.3以后引入C# Java中应用成熟的命名空间
  10. */
  11. // 在声明命名空间之前除了用于定义源文件编码方式的 declare 语句外,所有非 PHP 代码(包括空白符)都不能出现在命名空间声明之前
  12. namespace MyProject;
  13. const CONNECT_OK = 1;
  14. interface Itest
  15. {
  16. public function getSite();
  17. }
  18. class className implements Itest {
  19. public function getSite()
  20. {
  21. return __METHOD__." 命名空间<br>";
  22. }
  23. }
  24. namespace OtherProject;
  25. const CONNECT_OK = 1;
  26. interface Itest
  27. {
  28. public function getSite();
  29. }
  30. class className implements Itest {
  31. public function getSite()
  32. {
  33. return __METHOD__." 命名空间<br>";
  34. }
  35. }
  36. // 也可以使用大括号进行包裹
  37. namespace AnotherProject;
  38. const CONNECT_OK = 1;
  39. class className {
  40. /* ... */
  41. }
  42. //在AnotherProject空间中访问MyProject空间里的类, 应该使用"\"表示root空间(全局空间)
  43. echo (new \MyProject\className)->getSite();//绝对路径 类的完全限定名称\MyProject\className
  44. echo (new \OtherProject\className)->getSite();
  45. /*
  46. MyProject\className::getSite 命名空间
  47. OtherProject\className::getSite 命名空间
  48. */
  49. ?>

注意:在实际的编程实践中,并不提倡在同一个文件中定义多个命名空间。定义多个命名空间主要用于将多个 PHP 脚本合并在同一个文件中。在定义多个命名空间时建议使用大括号形式的语法。

将全局的非命名空间中的代码与命名空间中的代码组合在一起,只能使用大括号形式的语法,同时全局代码必须用一个不带名称的 namespace 语句加上大括号括起来

  1. <?php
  2. namespace MyProject{ // 命名空间中的代码
  3. const CONNECT_OK = 1;
  4. class className {
  5. /* ... */
  6. }
  7. }
  8. namespace AnotherProject{
  9. const CONNECT_OK = 1;
  10. class className {
  11. /* ... */
  12. }
  13. }
  14. namespace { // 命名空间外的全局代码 全局空间 匿名空间 公共空间
  15. $obj = new MyProject\className;
  16. }
  17. ?>

定义子命名空间

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

namespace App\Model;
namespace App\Controller\Home;

  1. <?php
  2. namespace Controller\Home\MyProject{
  3. const CONNECT_OK = 1;
  4. class className {
  5. public function getInfo(){
  6. echo __METHOD__." hello world<br>";
  7. }
  8. }
  9. }
  10. namespace Controller\Home{
  11. const CONNECT_OK = 1;
  12. class className {
  13. public function getInfo(){
  14. echo __METHOD__." hello world<br>";
  15. }
  16. }
  17. // 查看类的完全限定名称
  18. echo className::class."<br>";
  19. // Controller\Home\className
  20. (new MyProject\className)->getInfo();
  21. // Controller\Home\MyProject\className::getInfo hello world
  22. }
  23. // 命名空间外的全局代码 全局空间 匿名空间 公共空间
  24. namespace {
  25. class className {
  26. public function getInfo(){
  27. echo __METHOD__." hello world<br>";
  28. }
  29. }
  30. (new className)->getInfo();
  31. (new Controller\Home\className)->getInfo();
  32. $obj = new Controller\Home\MyProject\className();
  33. $obj->getInfo();
  34. /*
  35. className::getInfo hello world
  36. Controller\Home\className::getInfo hello world
  37. Controller\Home\MyProject\className::getInfo hello world
  38. */
  39. }
  40. ?>

使用命名空间

在文件系统中访问一个文件有三种方式:

  • 相对文件名形式如 foo.txt。它会被解析为 currentdirectory/foo.txt,其中 currentdirectory 表示当前目录。因此如果当前目录是 /home/foo,则该文件名被解析为 /home/foo/foo.txt;
  • 相对路径名形式如 subdirectory/foo.txt。它会被解析为 currentdirectory/subdirectory/foo.txt;
  • 绝对路径名形式如 /main/foo.txt。它会被解析为 /main/foo.txt。

PHP 命名空间中的类名可以通过三种方式引用:

  1. 非限定名称,或不包含前缀的类名称

  2. 限定名称,或包含前缀的名称

  3. 完全限定名称,或包含了全局前缀操作符的名称

file1.php

  1. <?php
  2. function getInfo(){
  3. return '我是全局空间下的函数';
  4. }
  5. function foo() {
  6. echo __METHOD__."<br>";
  7. }
  8. ?>

file2.php

  1. <?php
  2. namespace Foo\Bar\subnamespace;
  3. const FOO = 1;
  4. function foo() {
  5. echo __METHOD__."<br>";
  6. }
  7. class foo
  8. {
  9. static function staticmethod() {
  10. echo __METHOD__."<br>";
  11. }
  12. }
  13. namespace Foo\Bar;
  14. include 'file1.php';
  15. const FOO = 2;
  16. function foo() {
  17. echo __METHOD__."<br>";
  18. }
  19. class foo
  20. {
  21. static function staticmethod() {
  22. echo __METHOD__."<br>";
  23. }
  24. }
  25. /* 非限定名称 */
  26. foo(); // Foo\Bar\foo
  27. foo::staticmethod(); // Foo\Bar\foo::staticmethod
  28. echo FOO; // 解析为常量 Foo\Bar\FOO 2
  29. echo getInfo(); // 我是全局空间下的函数
  30. // 如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称
  31. /* 限定名称 */
  32. subnamespace\foo(); // Foo\Bar\subnamespace\foo
  33. subnamespace\foo::staticmethod(); // Foo\Bar\subnamespace\foo::staticmethod
  34. echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO 1
  35. /* 完全限定名称 */
  36. \Foo\Bar\foo(); // Foo\Bar\foo
  37. \Foo\Bar\foo::staticmethod(); // Foo\Bar\foo::staticmethod
  38. echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO 2
  39. ?>

file3.php

  1. <?php
  2. namespace Foo;
  3. function strlen($str) {
  4. return "Foo空间的strlen";
  5. }
  6. const INI_ALL = 3;
  7. class Exception {}
  8. class Test {
  9. const APP_NAME = '我是Foo空间中类常量 hello world';
  10. }
  11. echo strlen('hi'); // Foo空间的strlen
  12. echo INI_ALL; // 当前空间下的常量 3
  13. var_dump(new Exception); // object(Foo\Exception)#1 (0) { }
  14. echo Test::APP_NAME; // 我是Foo空间中类常量 hello world
  15. // 如果调用的函数与系统函数重名,或者与定义在全局空间中的自定义函数同名,优先调用的是当前空间中的函数, 找不到时,才会去全局空间查找
  16. echo "<pre>";
  17. echo \strlen('hi')."<br>" ; // 调用全局函数strlen
  18. echo \INI_ALL."<br>"; // 访问全局常量 INI_ALL
  19. var_dump(new \Exception('error')); // 实例化全局类 Exception
  20. echo \Test::APP_NAME; // hello world
  21. namespace {
  22. class Test
  23. {
  24. const APP_NAME = ' hello world';
  25. }
  26. }
  27. ?>

注意:访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen() 或 \Exception 等。

命名空间导入

PHP 允许通过别名引用或导入的方式来使用外部的命名空间,这是命名空间的一个重要特征。

使用 use 关键字可以实现命名空间的导入,从 PHP5.6 开始允许导入函数和常量,并为它们设置别名。语法格式如下:

use namespace;

在 PHP 中,别名是通过操作符 use 与 as 来实现的,语法格式如下:

use 命名空间 as 别名;

示例:使用 use 操作符导入和使用别名

  1. <?php
  2. /**
  3. * use 在命名空间中的作用
  4. * 1. 引入别的命名空间到当前空间 为命名空间起别名
  5. * 2. 引入别的命名空间中的类到当前空间使用, 为别的命名空间中的类起别名
  6. */
  7. namespace app\admin\controller;
  8. //控制器类index
  9. class Index
  10. {
  11. public function index()
  12. {
  13. return __METHOD__;
  14. }
  15. }
  16. const CONSTANT = "zhang";
  17. function getName($name){
  18. return $name;
  19. }
  20. namespace extend\lib;
  21. // 在当前空间下访问app\admin\controller中的Index类
  22. echo ( new \app\admin\controller\Index )->index(),'<hr>';
  23. //1. use 导入命名空间
  24. use app\admin\controller;
  25. // 成功导入命名空间以后 就可以不使用类的完全限定名称调用类元素
  26. echo (new controller\Index)->index(),'<hr>';
  27. //2.为空间起别名
  28. use app\admin\controller as app;
  29. echo (new app\Index)->index(),'<hr>';
  30. // use 直接引入外部空间类-------------
  31. //1. 使用use 引入一个外部类到当前空间
  32. use app\admin\controller\Index;
  33. //导入类以后就可以使用非限定名称度调用类元素
  34. echo (new Index)->index(),'<hr>';
  35. //2.为引入的外部空间该类起别名
  36. use app\admin\controller\Index as in;
  37. echo (new in)->index(),'<hr>';
  38. // use 还可以导入一个常量和函数----------
  39. // 导入一个函数
  40. use function app\admin\controller\getName;
  41. echo getName("zhang "); // zhang
  42. // 导入一个函数并定义别名
  43. use function app\admin\controller\getName as func;
  44. echo func("shuai"); // shuai
  45. // 导入一个常量
  46. use const app\admin\controller\CONSTANT;
  47. echo "<br>".CONSTANT;

命名空间类的自动加载

在早期 PHP 开发中,开发者最烦的就是一堆 include 函数包含了一大堆文件,而且早期时候 PHP 面向对象的概念确实太差了,因为 PHP 作为一种脚本语言,不存在程序入口,所以脚本顺序化执行的诱惑力实在是很大,即使面向对象开发,但是缺少极佳的模块划分导入机制,代码可以说很难有美感。

  1. /**
  2. * 类的加载
  3. */
  4. //应用入门文件
  5. namespace app;
  6. //传统引入加载方式
  7. // require 'app/models/StaffsModel.php';
  8. // require 'app/models/UserModel.php';
  9. // require 'app/controller/User.php';
  10. //类的自动加载
  11. require 'app/autoload.php';
  12. use app\models\StaffsModel;
  13. use app\models\UserModel;
  14. use app\controller\User;
  15. use app\controller\Login;
  16. $staffs = new StaffsModel;
  17. $user = new UserModel;
  18. $userController = new User;
  19. $loginController = new Login;
  20. /*
  21. 类名:app\models\StaffsModel
  22. 类名:app\models\UserModel
  23. 类名:app\controller\User
  24. 类名:app\controller\Login
  25. */

autoload() 是系统函数,名称是固定的,而且该函数没有方法体,需要我们自定义方法体。另外,该函数是自动调用的,当我们 new 一个类时,如果当前源文件中找不到这个类,PHP 则会自动调用 autoload() 函数,并将类名传递给 __autoload() 函数。

  1. spl_autoload_register(function($className)
  2. {
  3. //先查看要加载的类
  4. printf('类名:%s<br>',$className);
  5. //将类命名空间与类文件所在的路径进行一一映射, linux / windows \
  6. // app\controller\User <=> app/controller/User.php
  7. //解决在linux系统中命名空间失效 class not found
  8. $file = str_replace('\\',DIRECTORY_SEPARATOR,$className).'.php';
  9. if( !(is_file($file) && file_exists($file)) )
  10. throw new \Exception('文件名不合法或者文件不存在');
  11. require $file;
  12. });

【PHP 面向对象】面向对象(OOP)编程之魔术方法实现重载知识点归纳总结(三)
【PHP 面向对象】面向对象(OOP)编程知识点归纳总结(二)
【PHP 面向对象】面向对象(OOP)编程知识点归纳总结(一)

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议