2023-08-28

使用 Symfony 组件演示 PHP 中的依赖注入

使用 Symfony 组件演示 PHP 中的依赖注入

在本文中,我们将介绍一些使用 Symfony DependencyInjection 组件的示例。您将学习依赖注入的基础知识,它允许干净和模块化的代码,并且您将了解如何在带有 Symfony 组件的 PHP 应用程序中使用它。

什么是 Symfony DependencyInjection 组件?

Symfony DependencyInjection 组件提供了一种在 PHP 应用程序中实例化对象和处理依赖关系管理的标准方法。 DependencyInjection 组件的核心是一个容器,它保存应用程序中所有可用的服务。

在应用程序的引导阶段,您应该将应用程序中的所有服务注册到容器中。在稍后阶段,容器负责根据需要创建服务。更重要的是,容器还负责创建和注入服务的依赖项。

这种方法的好处是您不必对实例化对象的过程进行硬编码,因为将自动检测并注入依赖项。这会在应用程序的各个部分之间创建松散耦合。

在本文中,我们将探讨如何释放 DependencyInjection 组件的强大功能。像往常一样,我们将从安装和配置说明开始,然后实现一些实际示例来演示关键概念。

安装和配置

在本节中,我们将继续安装 DependencyInjection 组件。我假设您已经在系统中安装了 Composer,因为我们需要它来安装 Packagist 上提供的 DependencyInjection 组件。

因此,请继续使用以下命令安装 DependencyInjection 组件。

$composer require symfony/dependency-injection
登录后复制

这应该创建了 composer.json 文件,它应该如下所示:

{
    "require": {
        "symfony/dependency-injection": "^5.4"
    }
}
登录后复制

我们还将安装一些在我们的示例中有用的其他组件。

如果您想从 YAML 文件加载服务而不是在 PHP 代码中定义它,那么 Yaml 组件就可以派上用场,因为它可以帮助您将 YAML 字符串转换为 PHP 兼容的数据类型,反之亦然。< /p>

$composer require symfony/yaml
登录后复制

最后,我们将安装 Config 组件,它提供了几个实用程序类来初始化和处理在不同类型的文件(如 YAML、INI 和 XML)中定义的配置值。在我们的例子中,我们将使用它从 YAML 文件加载服务。

$composer require symfony/config
登录后复制

让我们将 composer.json 文件修改为如下所示。

{
    "require": {
        "symfony/dependency-injection": "^5.4",
        "symfony/yaml": "^5.4",
        "symfony/config": "^5.4"
    },
    "autoload": {
         "psr-4": {
             "Services//": "src"
         },
         "classmap": ["src"]
    }
}
登录后复制

由于我们添加了新的 classmap 条目,让我们继续通过运行以下命令来更新 Composer 自动加载器。

$composer dump -o
登录后复制

现在,您可以使用 Services 命名空间来自动加载 src 目录下的类。

这就是安装部分,但是你应该如何使用它呢?事实上,只需将 Composer 创建的 autoload.php 文件包含在您的应用程序中即可,如以下代码片段所示。

<?php
require_once './vendor/autoload.php';
 
// application code
?>
登录后复制

如何使用容器

在本节中,我们将通过一个示例来演示如何将服务注入到容器中。容器应该充当中央存储库,保存应用程序中的所有服务。稍后,我们可以使用容器来根据需要获取服务。

首先,让我们继续在 src/DemoService.php 中定义一个非常基本的服务,其中包含以下内容。

<?php
// src/DemoService.php
namespace Services;
 
class DemoService
{
  public function helloWorld()
  {
    return "Hello World!/n";
  }
}
登录后复制

这是一个非常简单的服务,暂时只实现了 helloWorld 方法。

接下来,继续在应用程序的根目录中创建包含以下内容的 basic_container.php 文件。

<?php
// basic_container.php
require_once './vendor/autoload.php';
use Symfony/Component/DependencyInjection/ContainerBuilder;
 
// init service container
$containerBuilder = new ContainerBuilder();
 
// add service into the service container
$containerBuilder->register('demo.service', '/Services/DemoService');
 
// fetch service from the service container
$demoService = $containerBuilder->get('demo.service');
echo $demoService->helloWorld();
登录后复制

首先,我们使用 new ContainerBuilder() 构造函数实例化了 ContainerBuilder 对象。接下来,我们使用 ContainerBuilder 对象的 register 方法将我们的自定义服务 /Services/DemoService 注入到容器中。 demo.service 充当我们服务的别名。

最后,我们使用 ContainerBuilder 对象的 get 方法从容器中获取我们的服务,并用它来调用 helloWorld 方法。

这是如何使用容器的基本演示。在下一节中,我们将扩展此示例以探索如何使用容器解决类依赖关系。

现实世界的示例

在本节中,我们将创建一个示例,演示如何使用 DependencyInjection 组件解析类依赖关系。

为了演示它,我们将创建一个新服务 DependentService ,它需要上一节中创建的 DemoService 服务作为依赖项。因此,我们将看到当实例化 DependentService 服务时,如何将 DemoService 服务作为依赖项自动注入。

继续创建包含以下内容的 src/DependentService.php 文件来定义 DependentService 服务。

<?php
// src/DependentService.php
namespace Services;
 
class DependentService
{
  private $demo_service;
 
  public function __construct(/Services/DemoService $demoService)
  {
    $this->demo_service = $demoService;
  }
 
  public function helloWorld()
  {
    return $this->demo_service->helloWorld();
  }
}
登录后复制

如您所见,为了实例化 DependentService 服务,需要 /Services/DemoService 服务。

接下来,继续创建包含以下内容的 di_container.php 文件。

<?php
// di_container.php
require_once './vendor/autoload.php';
use Symfony/Component/DependencyInjection/ContainerBuilder;
use Symfony/Component/DependencyInjection/Reference;
 
// init service container
$containerBuilder = new ContainerBuilder();
 
// add demo service into the service container
$containerBuilder->register('demo.service', '/Services/DemoService');
 
// add dependent service into the service container
$containerBuilder->register('dependent.service', '/Services/DependentService')
                 ->addArgument(new Reference('demo.service'));
 
// fetch service from the service container
$dependentService = $containerBuilder->get('dependent.service');
echo $dependentService->helloWorld();
登录后复制

我们使用相同的 register 方法将自定义服务 /Services/DependentService 注入到容器中。

除此之外,我们还使用了 addArgument 方法来通知容器有关 DependentService 服务的依赖关系。我们使用 Reference 类来通知容器在实例化 dependent.service 服务时需要注入 demo.service 服务。这样,就会根据需要自动注入依赖项!

最后,我们使用 ContainerBuilder 对象的 get 方法从 dependent.service 服务获取inline”>ContainerBuilder 对象并用它来调用 helloWorld 方法。

通过这种方式,DependencyInjection 组件提供了一种在应用程序中实例化对象和注入依赖项的标准方法。

如何使用 YAML 文件动态加载服务

在本节中,我们将探讨如何从 YAML 文件动态加载服务。基本上,我们将更新上一节中讨论的示例。

除了 DependencyInjection 组件之外,我们还需要另外两个 Symfony 组件来实现 YAML 示例:Config 和 Yaml。回想一下,我们已经在安装和配置部分中安装了这两个组件以及 DependencyInjection 组件本身。那么我们就可以出发了!

继续在应用程序的根目录中创建包含以下内容的 services.yaml 文件。

services:
    demo.service:
        class:     /Services/DemoService
    dependent.service:
        class:     /Services/DependentService
        arguments: ["@demo.service"]
登录后复制

如您所见,使用 YAML 语法定义服务非常简单。要定义服务的依赖项,您需要使用 arguments 键。

接下来,继续创建包含以下内容的 di_yaml_container.php 文件。

<?php
// di_yaml_container.php
require_once './vendor/autoload.php';
use Symfony/Component/DependencyInjection/ContainerBuilder;
use Symfony/Component/Config/FileLocator;
use Symfony/Component/DependencyInjection/Loader/YamlFileLoader;
 
// init service container
$containerBuilder = new ContainerBuilder();
 
// init yaml file loader
$loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__));
 
// load services from the yaml file
$loader->load('services.yaml');
 
// fetch service from the service container
$serviceOne = $containerBuilder->get('dependent.service');
echo $serviceOne->helloWorld();
登录后复制

一切都几乎相同,只是我们从 services.yaml 文件加载服务,而不是在 PHP 代码本身中定义它。这允许动态定义应用程序依赖项。

如何注入惰性服务

在某些情况下,您想要注入惰性服务。有时,您的服务实例化起来非常繁重。因此,您只希望在真正需要时注入这样的服务,而不是在此之前。这个问题的答案是惰性服务。

但是这到底是如何工作的呢?事实上,当您配置惰性服务时,不是注入实际的服务,而是注入该服务的代理。从表面上看,代理服务的行为就像实际服务一样,但是一旦您开始与代理服务交互,实际服务就会被实例化。

要使用惰性服务,我们需要安装 symfony/proxy-manager-bridge 软件包。让我们首先这样做。

$composer require symfony/proxy-manager-bridge
登录后复制

接下来,我们将修改 di_container.php 示例,以了解如何使用代理管理器创建和注入代理服务。

继续将 di_container.php 文件的代码替换为以下内容。

<?php
require_once './vendor/autoload.php';

use ProxyManager/Factory/LazyLoadingValueHolderFactory;
use ProxyManager/Proxy/LazyLoadingInterface;
use Symfony/Component/DependencyInjection/ContainerBuilder;

// code for creating proxy starts
$factory     = new LazyLoadingValueHolderFactory();
$initializer = function (& $wrappedObject, LazyLoadingInterface $proxy, $method, array $parameters, & $initializer) {
    global $containerBuilder;
    $initializer   = null; // disable initialization
    $wrappedObject = $containerBuilder->get('demo.service'); // fill your object with values here

    return true; // confirm that initialization occurred correctly
};
$proxy = $factory->createProxy('/Services/DemoService', $initializer);
// code for creating proxy ends

global $containerBuilder;
$containerBuilder = new ContainerBuilder();
$containerBuilder->register('demo.service', '/Services/DemoService');
$containerBuilder->register('dependent.service', '/Services/DependentService')->addArgument($proxy);
$dependentService = $containerBuilder->get('dependent.service');

echo $dependentService->helloWorld();
登录后复制

首先,我们创建了 LazyLoadingValueHolderFactory 类的实例。接下来,我们使用此类的 createProxy 方法来定义如何创建 /Services/DemoService 类的实例。 createProxy 方法的第二个参数是一个匿名函数,当需要实例化类的实际实例而不是代理对象时,将调用该函数。因此,匿名函数处理创建实际实例的逻辑。

除此之外,几乎是一样的。创建代理实例后,我们将在 addArgument 方法中传递它,而不是使用 Reference 类创建实例。

这种方法的好处是,每当我们创建 /Services/DependentService 类的实例时,它都不会创建 /Services/DemoService 类的实际对象,而是创建一个代理对象。 /Services/DemoService 类的实际对象只有在调用它的任何方法时才会被创建。

要确认这一点,您可以转储 $dependentService 对象,您应该会看到类似这样的内容。

Services/DependentService Object
(
    [demo_service:Services/DependentService:private] => ProxyManagerGeneratedProxy/__PM__/Services/DemoService/Generatedccdf8241e892da36c12500ed72d82829 Object
        (
            [valueHoldera1fd8:ProxyManagerGeneratedProxy/__PM__/Services/DemoService/Generatedccdf8241e892da36c12500ed72d82829:private] => 
            [initializere3996:ProxyManagerGeneratedProxy/__PM__/Services/DemoService/Generatedccdf8241e892da36c12500ed72d82829:private] => Closure Object
                (
                    [parameter] => Array
                        (
                            [&$wrappedObject] => <required>
                            [$proxy] => <required>
                            [$method] => <required>
                            [$parameters] => <required>
                            [&$initializer] => <required>
                        )

                )

        )

)
登录后复制

如您所见,它创建了代理对象而不是实际的 /Services/DemoService 对象。

如果没有使用代理方法,输出会是这样的。

Services/DependentService Object
(
    [demo_service:Services/DependentService:private] => Services/DemoService Object
        (
        )

)
登录后复制

如您所见,它创建了 /Services/DemoService 类的实例。这就是服务延迟加载的工作原理!

结论

Symfony DependencyInjection 组件在本教程中占据了中心位置。我们了解了如何安装和配置 DependencyInjection,以及一些如何使用它的实际示例。

我对 Symfony 框架的解耦组件非常着迷和兴奋,您可以为您的应用程序挑选这些组件。将它们插入您的代码中,它们就可以工作了!总而言之,我只能看到这种框架方法对我们 PHP 社区的好处。

以上就是使用 Symfony 组件演示 PHP 中的依赖注入的详细内容,更多请关注php中文网其它相关文章!

https://www.php.cn/faq/599032.html

发表回复

Your email address will not be published. Required fields are marked *