目录
什么是Pimple,为什么在PHP中使用它?
Pimple是如何工作的?
如何安装Pimple?
如何在Pimple中定义服务?
如何在Pimple中访问服务?
如何在Pimple中共享服务?
我可以在Pimple中扩展服务吗?
如何保护Pimple中的参数?
如何在一个项目中使用Pimple?
使用Pimple的好处是什么?
首页 后端开发 php教程 PHP主|用丘疹注入依赖

PHP主|用丘疹注入依赖

Feb 24, 2025 am 08:57 AM

PHP Master | Dependency Injection with Pimple

核心要点

  • 依赖注入是应用开发中的一个关键概念,通过将依赖项注入模块而不是硬编码它们,可以编写更高效、更易维护的代码。
  • Pimple是一个简单的依赖注入容器,它使用PHP的闭包以可管理的方式定义依赖项,有助于保持代码的可维护性。
  • 注入依赖项的两种主要技术是基于构造函数的依赖注入和基于设置器的注入,每种技术都有其自身的优缺点。
  • Pimple通过充当定义依赖项的容器来支持软件开发的DRY(不要重复自己)原则,避免重复,从而更轻松地管理和集中应用程序中的服务。
  • Pimple还提供高级功能,例如使用共享对象返回相同实例的能力,以及无需影响原始实现即可动态修改现有闭包的能力。

在应用程序开发中,我们尝试创建独立的模块,以便在未来的项目中重用代码。但是,创建提供有用功能的完全独立的模块很困难;除非正确管理它们的依赖关系,否则它们可能会导致维护噩梦。这就是依赖注入派上用场的地方,因为它使我们能够注入代码正常运行所需的依赖项,而无需将它们硬编码到模块中。Pimple是一个简单的依赖注入容器,它利用PHP的闭包以可管理的方式定义依赖项。在本文中,我们将探讨硬编码依赖项的问题,依赖注入如何解决这些问题,以及如何使用Pimple来使利用依赖注入的代码更易于维护。

具体依赖项的问题

在编写应用程序时,我们会使用许多PHP类。一个类可能需要调用一个或多个其他类的方法来提供预期的功能,因此我们说第一个类依赖于其他类。例如:

<?php
class A
{
    public function a1() {
        $b = new B();
        $b->b1();
    }
}
登录后复制
登录后复制
登录后复制
登录后复制

类A依赖于类B。如果类B不可用,则上述代码将无法工作。此外,每次我们在类中硬编码对象的创建时,我们都会对该类产生具体的依赖关系。具体依赖关系是编写可测试代码的障碍。更好的方法是向类A提供类B的对象。这些对象可以通过A的构造函数或setter方法提供。在我们进一步讨论之前,让我们来看一个更现实的场景。

如今,在社交网络网站上共享内容非常普遍,大多数网站都在其网站上直接显示其社交资料提要。假设我们有一个名为SocialFeeds的类,它从Twitter、Facebook、Google 等社交网站生成提要。创建单独的类来处理这些服务中的每一个。在这里,我们将研究与Twitter交互的类TwitterService。SocialFeeds类使用TwitterService请求Twitter提要。TwitterService与数据库交互以检索访问API的特定用户令牌。令牌传递给OAuth类,该类使用提供的令牌检索提要并将其返回给SocialFeeds类。

<?php
class A
{
    public function a1() {
        $b = new B();
        $b->b1();
    }
}
登录后复制
登录后复制
登录后复制
登录后复制
<?php
class SocialFeeds
{
    public function getSocialFeeds() {
        $twService = new TwitterService();
        echo $twService->getTweets();
    }
}
登录后复制
登录后复制
登录后复制
<?php
class TwitterService
{
    public function getTweets() {
        $db = new DB();
        $query = "Query to get user token from database";
        $token = $db->getQueryResults($query);

        $oauth = new OAuth();
        return $oauth->requestTwitterFeed($token);
    }
}
登录后复制
<?php
class OAuth
{
    public function requestTwitterFeed($token) {
        // Retrieve and return twitter feed using the token         
    }
}
登录后复制

很明显,SocialFeeds依赖于TwitterService。但是TwitterService依赖于DB和OAuth,因此SocialFeeds间接依赖于DB和OAuth。那么问题是什么呢?SocialFeeds依赖于三个类的具体实现,因此不可能在没有其他类的真实实现的情况下单独测试SocialFeeds。或者,假设我们想使用不同的数据库或不同的OAuth提供程序。在这种情况下,我们必须在整个代码中用新类替换现有类。

修复具体依赖项

解决这些依赖项问题的方案很简单,即在必要时动态提供对象,而无需使用具体实现。有两种类型的技术可以注入依赖项:基于构造函数的依赖注入和基于设置器的注入。

基于构造函数的注入

使用基于构造函数的依赖注入,依赖对象是在外部创建的,并作为参数传递给类的构造函数。我们可以将这些对象分配给类变量,并在类内任何地方使用。SocialFeeds类的基于构造函数的注入如下所示:

<?php
class DB
{
    public function getQueryResults($query) {
        // Get results from database and return token
    }
}
登录后复制

TwitterService的实例作为对象传递给构造函数。SocialFeeds仍然依赖于TwitterService,但现在我们可以自由地提供不同版本的Twitter服务提供程序,甚至可以提供用于测试目的的模拟对象。关于TwitterService,DB和OAuth类也以类似的方式定义。

<?php
class SocialFeeds
{
    public $twService;

    public function __construct($twService) {
        $this->twService = $twService;
    }

    public function getSocialFeeds() {
        echo $this->twService->getTweets();
    }
}
登录后复制

基于设置器的注入

使用基于设置器的注入,对象是通过setter方法而不是构造函数提供的。以下是SocialFeeds类的基于设置器的依赖注入实现:

<?php
$db = new DB();
$oauth = new OAuth();
$twService = new TwitterService($db, $oauth);
$socialFeeds = new SocialFeeds($twService);
$socialFeeds->getSocialFeeds();
登录后复制

现在包括DB和OAuth的初始化代码如下所示:

<?php
class SocialFeeds
{
    public $twService;

    public function getSocialFeeds() {
        echo $this->twService->getTweets();
    }

    public function setTwitterService($twService) {
        $this->twService = $twService;
    }
}
登录后复制

构造函数与设置器注入

选择基于构造函数的注入还是基于设置器的注入取决于您。当需要所有依赖项才能实例化类时,基于构造函数的注入是合适的。当并非每次都需要依赖项时,基于设置器的注入是合适的。

优点

  • 构造函数 – 只需查看类的构造函数即可识别类的所有依赖项
  • 设置器 – 添加新的依赖项就像添加新的setter方法一样简单,不会破坏现有代码

缺点

  • 构造函数 – 添加新的依赖项会增加构造函数的参数;需要更新整个应用程序中的现有代码以提供新的依赖项
  • 设置器 – 我们必须手动搜索必要的依赖项,因为它们没有在任何地方指定

了解了依赖注入和各种注入技术后,是时候看看Pimple以及它如何融入其中了。

Pimple在DI中的作用

当我们已经可以使用前面提到的技术注入依赖项时,您可能想知道为什么需要Pimple。要回答这个问题,我们需要看看DRY原则。

不要重复自己(DRY)是软件开发的一个原则,旨在减少各种信息的重复,这在多层架构中特别有用。DRY原则的陈述是“每个知识片段都必须在一个系统中具有单个、明确、权威的表示”——维基百科

考虑基于构造函数的注入示例。每次我们想要SocialFeed类的对象时,我们都必须重复实例化和传递其依赖项的整个设置过程。根据DRY,应避免此类代码以防止维护出现问题。Pimple充当定义此类依赖项以避免重复的容器。让我们来看一个简单的例子,看看Pimple是如何工作的。

<?php
class A
{
    public function a1() {
        $b = new B();
        $b->b1();
    }
}
登录后复制
登录后复制
登录后复制
登录后复制

创建Pimple的实例充当存储依赖项的容器。它实现SPL ArrayAccess接口,因此使用它与使用数组非常相似。首先,我们定义了一个键,该键保存我们想要的某个任意类的名称。然后,我们定义了一个闭包来返回指定类的实例,该实例充当服务。请注意,将向$c传递容器的实例,因此我们可以根据需要引用其他已定义的键;每个已定义的参数或对象都可通过$c变量在闭包中使用。现在,每当我们想要类的实例时,我们都可以引用键来检索对象。让我们将SocialFeeds示例转换为Pimple。Pimple官方网站上的示例显示了基于构造函数的注入,因此在这里我们将说明基于设置器的注入。请记住,为了使用Pimple,我们不需要修改前面定义的任何setter方法或代码——我们只是封装了逻辑。

<?php
class SocialFeeds
{
    public function getSocialFeeds() {
        $twService = new TwitterService();
        echo $twService->getTweets();
    }
}
登录后复制
登录后复制
登录后复制

DB和OAuth类都是独立的模块,因此我们直接在闭包内返回它们的新的实例。然后,我们使用基于设置器的注入向TwitterService类添加依赖项。我们已经将DB和OAuth类添加到容器中,因此我们可以使用$c['db']和$c['oauth']直接在函数内访问它们。现在,依赖项作为服务封装在容器内。每当我们想要使用不同的DB类或不同的OAuth服务时,我们只需替换容器语句中的类,一切都会完美运行。使用Pimple,您只需要在一个地方添加新的依赖项。

高级Pimple用法

在上述场景中,Pimple会在每次请求时从闭包返回每个类的新的实例。在某些情况下,我们需要使用相同的对象而无需每次都初始化新的实例,例如连接到数据库就是一个完美的例子。Pimple提供了使用共享对象返回相同实例的能力,这样做需要我们通过share()方法指定闭包,如下所示:

<?php
class A
{
    public function a1() {
        $b = new B();
        $b->b1();
    }
}
登录后复制
登录后复制
登录后复制
登录后复制

此外,到目前为止,我们已经在Pimple容器中的单个位置定义了所有依赖项。但是,考虑一下我们需要具有其依赖项的服务,但配置方式与原始服务略有不同的情况。例如,假设我们需要访问ORM来实现TwitterService类的某些功能。我们不能更改现有的闭包,因为它会强制所有现有功能使用ORM。Pimple提供extend()方法来动态修改现有闭包,而不会影响原始实现。考虑以下代码:

<?php
class SocialFeeds
{
    public function getSocialFeeds() {
        $twService = new TwitterService();
        echo $twService->getTweets();
    }
}
登录后复制
登录后复制
登录后复制

现在,我们能够在特殊情况下使用tweet_service的不同扩展版本。第一个参数是服务的名称,第二个参数是一个函数,该函数可以访问对象实例和容器。实际上,extend()是动态添加依赖项以适应不同情况的强大方法,但请确保将服务的扩展版本限制在最低限度,因为它会增加重复代码的数量。

总结

管理依赖项是Web应用程序开发中最重要和最困难的任务之一。我们可以使用构造函数和setter方法的依赖注入来有效地管理它们。但是,依赖注入本身也有一些麻烦,Pimple通过提供一个轻量级容器来以DRY的方式创建和存储对象依赖项来解决这些问题。请随时在下面的评论中分享您在项目中管理依赖项的经验,以及您对Pimple作为依赖注入容器的看法。

关于使用Pimple进行依赖注入的常见问题解答 (FAQ)

什么是Pimple,为什么在PHP中使用它?

Pimple是一个简单的PHP依赖注入容器,允许您管理和集中应用程序中的服务。它用于PHP,使代码更灵活、更可重用和更易于测试。通过使用Pimple,您可以在一个地方实例化对象,然后将它们注入到应用程序的不同部分,从而减少对全局状态的需求,并使您的代码更易于维护和测试。

Pimple是如何工作的?

Pimple通过在容器中存储服务定义来工作。这些定义是可以调用(函数或方法)的,它们返回服务的实例。当您从容器访问服务时,Pimple会执行服务定义以创建服务对象。这允许您以集中方式管理服务,并在整个应用程序中共享服务。

如何安装Pimple?

可以使用Composer(PHP的依赖项管理工具)安装Pimple。您可以在系统上全局安装Composer,然后通过运行命令composer require pimple/pimple在项目中引入Pimple。

如何在Pimple中定义服务?

在Pimple中,您可以通过将可调用对象分配给容器中的键来定义服务。可调用对象应返回服务的实例。例如,您可以像这样为邮件发送器类定义服务:

$container['mailer'] = function ($c) { return new Mailer($c['smtp']); };

在此示例中,邮件发送器服务定义为Mailer类的新的实例,其中smtp服务作为依赖项注入。

如何在Pimple中访问服务?

您可以使用带有服务键的数组表示法来访问Pimple中的服务。例如,您可以像这样访问邮件发送器服务:$mailer = $container['mailer'];。当您访问服务时,Pimple会执行服务定义并返回服务对象。

如何在Pimple中共享服务?

默认情况下,Pimple每次访问服务时都会返回服务的新的实例。如果您想共享服务并每次返回相同的实例,可以使用share()方法。例如,您可以像这样共享邮件发送器服务:$container['mailer'] = $container->share(function ($c) { return new Mailer($c['smtp']); });

我可以在Pimple中扩展服务吗?

是的,您可以使用extend()方法扩展Pimple中的服务。这允许您在定义服务后修改它。例如,您可以像这样扩展邮件发送器服务以添加其他配置:

$container['mailer'] = $container->extend('mailer', function ($mailer, $c) { $mailer->setFrom($c['email.from']); return $mailer; });

在此示例中,setFrom()方法使用email.from服务作为参数在邮件发送器服务上调用。

如何保护Pimple中的参数?

在Pimple中,您可以使用protect()方法保护参数(不应被视为服务的参数)。这允许您在容器中存储值,而不会将它们视为服务定义。例如,您可以像这样保护配置值:$container['config.value'] = $container->protect(function () { return 'value'; });

如何在一个项目中使用Pimple?

您可以通过创建PimpleContainer类的新的实例并在其中定义服务来在一个项目中使用Pimple。然后,您可以在应用程序中需要的地方从容器访问服务。这允许您以集中方式管理服务并将它们注入到应用程序的不同部分。

使用Pimple的好处是什么?

Pimple为PHP开发提供了许多好处。它使您的代码更灵活,因为它允许您以集中方式管理服务。它使您的代码更易于重用,因为它允许您在整个应用程序中共享服务。它使您的代码更易于测试,因为它允许您注入模拟服务进行测试。通过使用Pimple,您可以提高代码质量,并使其更易于维护和测试。

以上是PHP主|用丘疹注入依赖的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

在PHP API中说明JSON Web令牌(JWT)及其用例。 在PHP API中说明JSON Web令牌(JWT)及其用例。 Apr 05, 2025 am 12:04 AM

JWT是一种基于JSON的开放标准,用于在各方之间安全地传输信息,主要用于身份验证和信息交换。1.JWT由Header、Payload和Signature三部分组成。2.JWT的工作原理包括生成JWT、验证JWT和解析Payload三个步骤。3.在PHP中使用JWT进行身份验证时,可以生成和验证JWT,并在高级用法中包含用户角色和权限信息。4.常见错误包括签名验证失败、令牌过期和Payload过大,调试技巧包括使用调试工具和日志记录。5.性能优化和最佳实践包括使用合适的签名算法、合理设置有效期、

PHP 8.1中的枚举(枚举)是什么? PHP 8.1中的枚举(枚举)是什么? Apr 03, 2025 am 12:05 AM

PHP8.1中的枚举功能通过定义命名常量增强了代码的清晰度和类型安全性。1)枚举可以是整数、字符串或对象,提高了代码可读性和类型安全性。2)枚举基于类,支持面向对象特性,如遍历和反射。3)枚举可用于比较和赋值,确保类型安全。4)枚举支持添加方法,实现复杂逻辑。5)严格类型检查和错误处理可避免常见错误。6)枚举减少魔法值,提升可维护性,但需注意性能优化。

会话如何劫持工作,如何在PHP中减轻它? 会话如何劫持工作,如何在PHP中减轻它? Apr 06, 2025 am 12:02 AM

会话劫持可以通过以下步骤实现:1.获取会话ID,2.使用会话ID,3.保持会话活跃。在PHP中防范会话劫持的方法包括:1.使用session_regenerate_id()函数重新生成会话ID,2.通过数据库存储会话数据,3.确保所有会话数据通过HTTPS传输。

描述扎实的原则及其如何应用于PHP的开发。 描述扎实的原则及其如何应用于PHP的开发。 Apr 03, 2025 am 12:04 AM

SOLID原则在PHP开发中的应用包括:1.单一职责原则(SRP):每个类只负责一个功能。2.开闭原则(OCP):通过扩展而非修改实现变化。3.里氏替换原则(LSP):子类可替换基类而不影响程序正确性。4.接口隔离原则(ISP):使用细粒度接口避免依赖不使用的方法。5.依赖倒置原则(DIP):高低层次模块都依赖于抽象,通过依赖注入实现。

解释PHP中的晚期静态绑定(静态::)。 解释PHP中的晚期静态绑定(静态::)。 Apr 03, 2025 am 12:04 AM

静态绑定(static::)在PHP中实现晚期静态绑定(LSB),允许在静态上下文中引用调用类而非定义类。1)解析过程在运行时进行,2)在继承关系中向上查找调用类,3)可能带来性能开销。

什么是REST API设计原理? 什么是REST API设计原理? Apr 04, 2025 am 12:01 AM

RESTAPI设计原则包括资源定义、URI设计、HTTP方法使用、状态码使用、版本控制和HATEOAS。1.资源应使用名词表示并保持层次结构。2.HTTP方法应符合其语义,如GET用于获取资源。3.状态码应正确使用,如404表示资源不存在。4.版本控制可通过URI或头部实现。5.HATEOAS通过响应中的链接引导客户端操作。

您如何在PHP中有效处理异常(尝试,捕捉,最后,投掷)? 您如何在PHP中有效处理异常(尝试,捕捉,最后,投掷)? Apr 05, 2025 am 12:03 AM

在PHP中,异常处理通过try,catch,finally,和throw关键字实现。1)try块包围可能抛出异常的代码;2)catch块处理异常;3)finally块确保代码始终执行;4)throw用于手动抛出异常。这些机制帮助提升代码的健壮性和可维护性。

PHP中的匿名类是什么?您何时可以使用它们? PHP中的匿名类是什么?您何时可以使用它们? Apr 04, 2025 am 12:02 AM

匿名类在PHP中的主要作用是创建一次性使用的对象。1.匿名类允许在代码中直接定义没有名字的类,适用于临时需求。2.它们可以继承类或实现接口,增加灵活性。3.使用时需注意性能和代码可读性,避免重复定义相同的匿名类。

See all articles