3

If I add a 3rd party bundle, say from Knp bundles for example, should I wrap it first or should I use it directly in my code?

If I decide to wrap it, where do I put the wrapping code? In a separate new bundle? In my application bundle?

To clarify:

I'm not asking about how to add a thirds party bundle to my project. I'm not asking what a bundle is.

This question is aimed at encapsulating 3rd party code behind wrapper classes. Since the bundle was developed by a 3rd party developer it is a subject for unexpected changes that could break my code.

How do you wrap a 3rd party bundle after adding it to your project?

4

2 回答 2

4

这是对 Symfony2 中包含的第 3 方捆绑包的回答composer,它不是指特殊捆绑包。

首先

只要您将请求的捆绑包的版本修复为您的稳定版本(1.*composer.json不需要包装。

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

如果你包装

如果您想使用dev-master给定 3rd 方捆绑包的版本,可能会发生重大变化。但是,当有稳定版本时,不应该出现真正想要包含的情况。dev-master

dev-master无论如何,我看到了两种方法,如果您想要包含或想要包装它以显示错误、记录错误、捕获异常等,这可能是有意义的:

构建使用 3rd 方捆绑服务的所有实例的单个服务类

此服务类可以在您使用 3rd 方捆绑包的捆绑包中,在这种方法中不需要额外的捆绑包。

这样,您就有了一个类似的服务acme.thirdparty.client,它包装了其他服务的单个方法调用。您需要注入您需要的所有第 3 方服务(或创建所需子类的实例)并包装所有所需的方法调用。

# 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();
    }
}

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

或者你可以:

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

整个捆绑包的优点是对您确切想要包装的内容更加灵活。您可以包装整个服务或仅包装单个存储库,并用您自己的类替换包装的类。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而不是替换它并不是很清楚。这也忽略了依赖注入的整个想法。

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

也许已经有你想要的 3rd 方包的包装包(比如SensioBuzzBundlefor the Buzz Browser),在这种情况下,你可以使用这些包而不是自己编写所有东西。

结论

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

于 2013-12-03T12:50:16.423 回答
-1

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

阅读这个很容易使用。

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

或在 app/config/config.yml 中配置包

于 2013-11-06T11:03:49.747 回答