【问题标题】:How to inject interfaces (not class) in symfony3?如何在 symfony3 中注入接口(不是类)?
【发布时间】:2017-04-19 15:57:52
【问题描述】:

我对 Symfony DependencyInjection 组件有疑问。我想将接口注入控制器,所以我只能使用接口方法。但是,我注意到我可以使用实现接口的类中的任何公共方法,这是错误的。我关注了伟大的文章:http://php-and-symfony.matthiasnoback.nl/2014/05/inject-a-repository-instead-of-an-entity-manager/

编写测试服务类和接口

interface ITestService
{
    public function interfaceFunction();
}

class TestService implements ITestService
{
    public function interfaceFunction() {/* do somenthing */}

    public function classFunction() {/*do somenthing*/}
}

将我的应用程序服务类配置为服务 (test_service)

# file: app/config/services.yml
test_service:
    class: MyApp\Application\Services\TestService

将我的控制器配置为服务:

# file: app/config/services.yml
test_controller:
    class: MyApp\AppBundle\Controller\TestController
    arguments:
        - '@test_service'

在控制器中使用服务

class TestController extends Controller
{
    private testService;

    function _construct(ITestService $testService)
    {
        $this->testService = $testService;
    }

    public function indexAction()
    {
        // This should be inaccesible but it works :(
        $this->testService->classFunction();

        // This is the only function I should use.
        $this->testService->interfaceFunction();
    }

【问题讨论】:

  • 我认为OOP中不存在这样的概念。您可以尝试使用 traits 但 afaik 那些不是类型提示...
  • 为什么不能访问? $testService 仍然是 TestService 类的对象,无论该类是否实现了某些接口。
  • 谢谢,@JovanPerovic 和 yoshi。好吧,在我的构造函数中,我正在等待一个带有 ITestService 合同的对象。我应该放弃本合同中不存在的任何方法。我使用 c# 和城堡 Windsor IoC 框架,我只能使用接口中定义的方法。这对我来说很有意义,但是 symfony 组件让我感到困惑。
  • 这是一个 PHP 问题。就像实现接口和对象的方式一样。您的 IDE 至少应该阻止您使用非接口方法。
  • PHP 解析方法以在运行时调用。您不需要明确的downcasting 对象类,例如Java 或C++。事实上,您根本不需要指定类型提示:方法会很好地工作。类型提示是运行时检查、方法签名的契约。

标签: dependency-injection interface symfony


【解决方案1】:

正如@Timurib says,这是因为尽管有类型提示,PHP 直到运行时才评估要调用的方法。这可能被视为不受欢迎的事情,但它允许使用一些技术,例如Duck Typing

这里有一个基于您提供的示例的简化示例(它没有将 Symfony 容器混入其中,因为这纯粹与 PHP 相关)。你可以run it on 3v4l.org:

interface IService
{
    public function interfaceFunction();
}

final class ServiceWithOtherFunction implements IService
{
    public function interfaceFunction() { echo "ServiceWithOtherFunction interfaceFunction\n"; }

    public function otherFunction() { echo "ServiceWithOtherFunction otherFunction\n"; }
}

final class Controller
{
    private $service;

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

    public function indexAction()
    {
        $this->service->interfaceFunction();

        $this->service->otherFunction();
    }
}

$controllerWithOtherFunction = new Controller(new ServiceWithOtherFunction);

$controllerWithOtherFunction->indexAction();

输出:

ServiceWithOtherFunction interfaceFunction
ServiceWithOtherFunction otherFunction

但是当我们注入另一个不包含otherFunction的实现时,代码会在运行时抛出一个Error

final class ServiceWithoutOtherFunction implements IService
{
    public function interfaceFunction() { echo "ServiceWithoutOtherFunction interfaceFunction\n"; }
}

$controllerWithoutOtherFunction = new Controller(new ServiceWithoutOtherFunction);

$controllerWithoutOtherFunction->indexAction();

输出:

ServiceWithoutOtherFunction interfaceFunction

Fatal error: Uncaught Error: Call to undefined method ServiceWithoutOtherFunction::otherFunction() in /in/mZcRq:28
Stack trace:
#0 /in/mZcRq(43): Controller->indexAction()
#1 {main}
  thrown in /in/mZcRq on line 28

Process exited with code 255.

如果您打算使用接口、DI 和 DIC,则不应调用任何公共方法而不是接口公开的方法。这是真正利用接口优势的唯一方法:从实现细节中解耦,并且能够在不更改 Controller 内部的任何内容的情况下更改要注入的类。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-09
    • 2017-02-02
    • 1970-01-01
    • 1970-01-01
    • 2016-08-03
    • 1970-01-01
    相关资源
    最近更新 更多