如何将第三方包捆绑在Symfony2中?

时间:2013-11-06 10:59:10

标签: php symfony architecture bundle

如果我添加第三方软件包,例如来自Knp bundles,我应该先将它包装起来还是直接在我的代码中使用它?

如果我决定把它包起来,我在哪里放包装代码?在一个单独的新捆绑?在我的应用程序包中?

澄清:

我不是在询问如何将三分派对添加到我的项目中。 我不是在询问捆绑是什么。

这个问题旨在将第三方代码封装在包装类之后。由于该软件包是由第三方开发人员开发的,因此它可能会导致意外更改,这可能会破坏我的代码。

如何在将第三方捆绑包添加到项目后将其包装好?

2 个答案:

答案 0 :(得分:4)

这是Symfony2中通过composer包含的第三方捆绑包的答案,一般不会引用特殊捆绑包。

首先

只要您将所请求的捆绑包的版本修复为1.*中的稳定版本(例如composer.json)(并且只要开发人员遵循他自己的指南行),您就不应该对接口的兼容性中断没有任何问题,因此不需要包装。

但我假设您希望通过在包装器代码中抛出异常和/或实现回退来防止任何代码中断,以便使用包装器代码的所有内容仍然可以工作或至少显示适当的错误。

如果您想要换行

如果要使用给定第三方软件包的dev-master版本,可能会发生重大更改。但是,当存在稳定版本时,不应该存在确实希望包含dev-master的情况。

无论如何,我看到有两种方式,在你想要包含dev-master或想要包装它以显示错误,记录它们,捕获异常等的情况下,这可能是有意义的:

构建一个使用第三方软件包

的所有服务实例的服务类

此服务类可能位于使用第三方软件包的一个软件包中,此方法不需要额外的软件包。

通过这种方式,您可以使用acme.thirdparty.client这样的单个服务来包装其他服务的单个方法调用。您需要注入所需的所有第三方服务(或创建所需子类的实例)并包装所有所需的方法调用。

# src/Acme/MyBundle/Resources/config/services.yml
parameters:
    acme.thirdparty.wrapper.class: Acme\MyBundle\Service\WrapperClass

services:
    acme.thirdparty.wrapper:
        class: %acme.thirdparty.wrapper.class%
        arguments:
            someService: @somevendor.somebundle.someservice
            someOtherService: @somevendor.somebundle.someotherservice

服务类:

<?php
namespace Acme\MyBundle\Service;

use SomeVendor\SomeBundle\SomeService\ConcreteService;
use SomeVendor\SomeBundle\SomeService\OtherConcreteService;

class WrapperClass
{
    private $someService;

    private $someOtherService;

    public function __construct(ConcreteService $someService, OtherConcreteService $someOtherService)
    {
        $this->someService = $someService;
        $this->someOtherService = $someOtherService;
    }

    /**
     * @see SomeVendor\SomeBundle\SomeService\ConcreteService::someMethod
     */
    public function someMethod($foo, $bar = null)
    {
        // Do stuff
        return $this->someService->someMethod();
    }

    /**
     * @see SomeVendor\SomeBundle\SomeService\ConcreteOtherService::someOtherMethod
     */
    public function someOtherMethod($baz)
    {
        // Do stuff
        return $this->someOtherService->someOtherMethod();
    }
}

然后,您可以为这些方法调用添加一些错误处理(例如捕获所有异常并记录它们等),从而防止服务类之外的任何代码中断。但不用说,这并不能阻止第三方软件包的任何意外行为。

或者你可以:

创建一个包含多个服务的捆绑包,每个捆绑服务包含第三方捆绑包的单个服务

整个捆绑包的优点是可以更灵活地包装您想要包装的内容。您可以包装整个服务或仅包含单个存储库,并使用您自己的类替换包装的类。 DI容器允许覆盖注入的类,如下所示:

# src/Acme/WrapperBundle/Resources/config/services.yml
parameters:
    somevendor.somebundle.someservice.class: Acme\WrapperBundle\Service\WrapperClass

通过覆盖类参数somevendor.somebundle.someservice.class,所有使用此类的服务现在都是Acme\WrapperBundle\Service\WrapperClass的实例。这个包装类可以扩展基类:

<?php
namespace Acme\WrapperBundle\Service;

use SomeVendor\SomeBundle\SomeService\ConcreteService;

class WrapperClass extends ConcreteService
{
     /**
      * @see ConcreteService::someMethod
      */
     public function someMethod($foo, $bar = null)
     {
         // Do stuff here
         parent::someMethod($foo, $bar);
         // And some more stuff here
     }
}

...或者可以使用原始类的实例来包装它:

<?php
namespace Acme\WrapperBundle\Service;

use SomeVendor\SomeBundle\SomeService\ConcreteServiceInterface;
use SomeVendor\SomeBundle\SomeService\ConcreteService;

class WrapperClass implements ConcreteServiceInterface
{
    private $someService;

    /**
     * Note that this class should have the same constructor as the service. 
     * This could be achieved by implementing an interface
     */
    public function __construct($foo, $bar)
    {
        $this->someService = new ConcreteService($foo, $bar);
    }

     /**
      * @see ConcreteService::someMethod
      */
     public function someMethod($foo, $bar = null)
     {
         // Do stuff here
         $this->someService->someMethod($foo, $bar);
         // And some more stuff here
     }
}

请注意,为覆盖另一个类的类实现接口可能是必需的。另外第二个可能不是最好的主意,因为那时你不是很清楚你实际上是在包裹ConcreteService而不仅仅是替换它。这也忽略了依赖注入的整个想法。

这种方法需要做更多的工作,并且需要更多的测试,但如果你想要更多的灵活性,这就是你要走的路。

也许已经存在了所需的第三方捆绑包的封装包(如SensioBuzzBundle的{​​{1}}),在这种情况下,您可以使用它们而不是自己编写所有内容。

结论

信任开发人员并包含稳定版本(如Buzz Browser用于错误修正和新功能,或1.*仅用于错误修正)是可行的方法。如果没有稳定版本或者您想要包含1.0.*,则可以选择包装。如果你想包装你的代码,建立一个额外的包是更灵活的方式,但如果没有太多的代码可以包装,单个服务类就足够了。

答案 1 :(得分:-1)

此捆绑包的文档可在Resources / doc中找到 捆绑目录:

阅读本文它很容易使用。

将您的捆绑包添加到/ vendor / bundles 将以下命名空间条目添加到自动装带器中的registerNamespaces调用: // app / autoload.php

或配置捆绑包  应用程序/配置/ config.yml

我希望这对你有所帮助。