php - Symfony2 中应该如何包装 3rd 方包?

标签 php symfony architecture bundle

如果我添加第 3 方 bundle ,请说来自 Knp bundles例如,我应该先包装它还是应该直接在我的代码中使用它?

如果我决定包装它,我将包装代码放在哪里?在单独的新 bundle 中?在我的应用程序包中?

澄清:

我不是在问如何将第三方 bundle 添加到我的项目中。
我不是在问 bundle 是什么。

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

将第 3 方 bundle 添加到项目后如何打包?

最佳答案

这是通过 composer 包含的第 3 方 bundle 的答案在 Symfony2 中,一般来说,它并不指一个特殊的包。

首先

只要您在 1.* 中将请求的包的版本修复为稳定版本(如 composer.json ) (并且只要开发人员遵循他自己的指导方针),您应该不会遇到接口(interface)兼容性中断的任何问题,因此不需要包装。

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

如果你想包裹

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

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

构建一个使用第 3 方包的所有服务实例的服务类

此服务类可能位于使用第 3 方 bundle 的 bundle 之一中,在这种方法中不需要额外的 bundle 。

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

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

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

或者你可以:

创建一个包含多个服务的 bundle ,每个服务包都包装了 3rd 方 bundle 的一个服务

整个包的优点是可以更灵活地处理您真正想要包装的内容。您可以包装整个服务或仅单个存储库,并用您自己的类替换包装的类。 DI 容器允许覆盖注入(inject)的类,如下所示:
# 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
     }
}

请注意,为覆盖另一个类的类实现接口(interface)可能是强制性的。同样,第二个可能不是最好的主意,因为那时不太清楚您实际上是在包装 ConcreteService而不仅仅是更换它。这也忽略了依赖注入(inject)的整个想法。

这种方法需要更多的工作,意味着需要进行更多的测试,但如果您想要更多的灵 active ,这就是要走的路。

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

结论

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

关于php - Symfony2 中应该如何包装 3rd 方包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19810282/

相关文章:

php - bigcommerce API更新产品问题 "The field ' calculated_price'无法写入。请在重试之前将其从您的请求中删除”

Symfony2 :传递给 Doctrine\ORM\EntityRepository::__construct() 的参数 2 必须是 Doctrine\ORM\Mapping\ClassMetadata 的实例,没有给出

java - 如何在 Android 的 MVVM 中处理回调和状态

PHP首先将字符串初始化为 bool 值

php - 用户 '' @'localhost' 的访问被拒绝(使用密码 : NO),,但数据已交付

php - Symfony2 @template() 误解

php - Symfony2 Elastica bundle (elasticsearch)-可能仅限制 'active'项目?

design-patterns - 您将如何设计您的应用程序?通过继承还是通过组合?

api - 当从具有超媒体约束的 REST API 提供响应时,如何向客户端指示要使用的 HTTP 方法(动词)?

php - mysql_query 不返回资源