php实例化类-PHP命名空间和类手动加载实现

2023-08-26 0 9,323 百度已收录

PHP命名空间和类手动加载实现

由 Academy Jun 3 年前创建,最后更新 3 年前 版本号 #38237 浏览次数 2 赞 2 收藏

谈谈从文件导入

PHP 5.3之前,要在PHP脚本中引入另一个PHP脚本中定义的代码(通常是函数或类),需要使用include、require、include_once、require_once等单词,include和require可以通过指定路径导入一个PHP脚本,不同的是include在没有找到对应路径脚本时会发出警告(E_WARNING),而require会抛出致命错误(E_COMPILE_ERROR),include_once/require_once也是用来引入指定路径的PHP脚本,与include/require的区别在于,如果指定的路径已经包含过,则不会再次包含。 换句话说,相同的路径脚本只会被包含一次。 include_once 和 require_once 之间的区别与 include/require 相同。

所以从性能角度来看,最好使用include_once/require_once。 至于使用include_once还是require_once,取决于你对指定路径不存在的PHP脚本的预期处理。

在接下来的工作中,我们多次使用它们来介绍其他PHP脚本文件。 例如,在博客项目入口文件index.php中,我们通过以下代码引入bootstrap.php来引入初始化函数bootApp来调用:

<?php
require_once 'bootstrap.php';
// 新增一个 IoC 容器,通过依赖注入获取对象实例
$container = Container::getInstance();
bootApp($container);
...

然后在bootstrap.php中,通过以下代码引入Container类定义:

<?php
require_once 'core/Container.php';
...

自动加载类文件

对于类文件的引入,如果你觉得重复编译require_once/include_once语句太麻烦,还可以使用spl_auto_register函数注册一个手动加载器,实现手动加载系统中未定义的类或socket。

例如我们将上述bootstrap.php中通过require_once导入Container类代码调整为通过spl_autoload_register函数手动注册:

spl_autoload_register(function ($className) {
    require_once 'core/' . $className. '.php';
});

这样我们只需要通过spl_autoload_register全局注册这个匿名函数即可。 当找不到Container类时,就会根据这个手动加载器来加载。

命名空间及其使用

结合require_once/include_once和spl_autoload_register,已经可以解决多个PHP脚本之间的引入和组合问题,从而构建复杂的系统,比如Web开发框架,或者第三方库等。其实之前PHP 5.3,这是第三方框架和库所做的。 不过细心的朋友可能已经注意到了,spl_autoload_register的自动类加载机制存在一个问题,就是不同库/组件类名的冲突。 因此,从 PHP 5.3 开始,引入了命名空间的概念,通过命名空间,可以很好的解决这个问题,而且与后者相比,代码的可读性更好。

在PHP中,当前脚本的命名空间是通过namespace关键字声明的。 通常,PHP 脚本文件属于命名空间。 我们在php_learning目录下新建一个ns子目录来存放教程代码,然后在ns目录下创建一个Test.php文件,编写简单的测试代码如下:

<?php
namespace App;
class Test
{
    public static function print ()
    {
        printf("这是一个测试类: %sn", __CLASS__);
    }
}

我们需要在PHP脚本的第一行代码中声明代码所属的命名空间(必须是第一行,否则会报错):

namespace App;

表示这个脚本中所有的PHP常量、变量、类、函数都属于这个命名空间,然后我们在这个命名空间中声明一个Test类,并声明一个静态方法print来复制类名。

接下来,我们在同一目录中创建一个 App.php 脚本来调用 Test::print() 方法:

App.php 和 Test.php 属于同一目录,因此声明了相同的命名空间。 在实际开发过程中,我们一般按照目录来组织和管理命名空间。 调用同一个命名空间下的类和函数,可以像前面的代码一样直接调用。 如果是不同命名空间的类和函数,则需要通过use关键字导入。 我们在ns目录下新建一个testing子目录,并在该子目录下新建Test.php。 在此 PHP 脚本中,我们定义了一个同名的泛型类型,继承自父目录中定义的 Test 父类:

这里,我们将基类的命名空间声明为AppTesting(同一命名空间中不允许有同名的类和函数),然后通过use关键字引入上层命名空间中的Test类,因为类名 与通用名称同名,因此通过as关键字为其设置一个别名BaseTest,然后,可以通过BaseTest引用Test的父类。

在Test子类中,我们重绘了父类BaseTest的打印方法。

最后,我们可以在 App.php 中像这样调用这个基类:

<?php
namespace App;
use AppTestingTest as SubTest;
Test::print();
SubTest::print();

如果不存在类名冲突,则不需要设置别名:

<?php
namespace App;
use AppTestingTest;
Test::print();

此外,您可以直接引用包含完整命名空间的类名,而无需使用 use 关键字:

<?php
namespace App;
Test::print();
AppTestingTest::print();

或者这样,使用部分名称空间:

<?php
namespace App;
use AppTesting;
Test::print();
TestingTest::print();

不过,我们的系列教程一致同意通过使用引入完整的命名空间,以防止代码繁琐,提高可读性。

注:大学君这里只是抛砖引玉,简单介绍一下PHP命名空间的基本使用。 更多详情请参考官方文档或者现代PHP系列新特性(一)——命名空间。

自动加载命名空间类

当然,现在调用php App.php会报错,无论是AppTest还是AppTestingTest类,都会提示找不到:

要解决这个问题,可以使用上面提到的 spl_autoload_register 函数,将类名的命名空间解析到对应的目录路径中(这就是为什么命名空间要按照目录来组织),然后通过 require_once/include_once 引入,我们在App.php中添加以下代码:

 $val)
    {
        if ($key == 0 || $key == count($path) - 1) {
            continue;
        }
        $filepath .= DIRECTORY_SEPARATOR . strtolower($val);
    }
    $filepath .= DIRECTORY_SEPARATOR . $filename;
    require_once $filepath;
});
Test::print();
SubTest::print();

这样我们就可以正常调用这段代码了:

通过 Composer 管理命名空间

在实际项目开发中,手动编译这段spl_autoload_register代码有点繁琐,尤其是当项目不仅编译自己的代码,还引入各种第三方库时。 我们可以使用PHP的包管理工具Composer来帮助我们管理这些命名空间和目录的路径映射。 在此之前,我们已经在PHP环境搭建章节中在本地系统中安装了Composer。 因此,我们只需要在ns目录下运行composer init来初始化Composer设置,然后按照向导一路往下。 ,最后会在项目根目录生成一个composer.json配置文件:

如果项目有第三方库依赖php实例化,可以在require中配置。 这是一个还没有任何依赖项的测试项目。 然后我们在其中配置autoload选项来设置类手动加载机制:

{
    "name": "php/test",
    "description": "A php namespace test project",
    "type": "project",
    "license": "Apache",
    "authors": [
        {
            "name": "xueyuanjun",
            "email": "yaojinbu@outlook.com"
        }
    ],
    "minimum-stability": "dev",
    "require": {},
    "autoload": {
        "classmap": [
            "."
        ]
    }
}

在这里,我们添加 . 到classmap数组中,表示当前根目录作为手动加载类的入口目录,Composer将从这里读取所有命名空间,完善目录映射关系。 接下来php实例化,执行composer install来初始化依赖库和类手动加载设置:

初始化过程中会在根目录下创建一个vendor,用于存放第三方依赖包和类,以便手动加载相关文件。 初始化完成后,可以看到vendor/composer/autoload_static.php已经包含了App及其子命名空间的目录映射:

该文件将由 autoload_real.php 引用,而 autoload_real.php 又将由vendor/autoload.php 引用:

<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit991075cd5c3b2a6d389bb443802f7669::getLoader();

autoload_php是手动加载所有Composer管理类的入口文件,所以我们只需要在代码中引入这个文件,就可以通过Composer管理所有类的手动加载。 在App.php中修改示例代码如下:

<?php
include_once 'vendor/autoload.php';
use AppTest;
use AppTestingTest as SubTest;
Test::print();
SubTest::print();

与之前通过spl_autoload_register自动编译来手动加载类相比,现在的代码更加简单、清晰。 执行phpApp.php,运行结果如下:

事实上,Composer底层也是通过spl_autoload_register函数实现类的手动加载,但在此之前,会构建命令空间和类脚本路径之间的映射。 更多细节可以参考Laravel框架如何基于Composer实现手动类和文件。 加载本教程,当然,作为 PHP 包管理工具,Composer 的功能远不止于此。 它更强大的功能是安装、维护和管理第三方扩展包和库。 由于篇幅限制,这里不再详细介绍。 现在有兴趣的朋友可以参考以下两个教程:

综上所述,在命令空间和Composer的支持下,我们可以轻松构建和维护基于PHP的复杂而现代的小型项目。 从下一个教程开始,我将向您展示如何从头开始构建 PHP Web 框架。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 php php实例化类-PHP命名空间和类手动加载实现 https://www.wkzy.net/game/162036.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务