Dependency Injection
in 20min

Stephan Hochdörfer // 08.10.2017

Über mich

  • Stephan Hochdörfer
  • Head of Technology, bitExpert AG
    Mannheim
  • S.Hochdoerfer@bitExpert.de
  • @shochdoerfer

  • #PHP, #DevOps, #Automation
  • #phpugffm, #phpugmrn, #unKonf

Darum geht es heute nicht...

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation=
    "urn:magento:framework:ObjectManager/etc/config.xsd">

    <preference for="Magento\Catalog\Api\Data\ProductInterface"
        type="Magento\Catalog\Model\Product" />

    <type name="Magento\Customer\Model\ResourceModel\Visitor">
        <plugin name="catalogLog"
            type="Magento\Catalog\Model\Plugin\Log" />
    </type>
</config>

Inversion of Control





« [...] is a design principle in which custom-written portions
of a computer program receive the flow of control from a
generic framework. » - Wikipedia

Ohne Inversion of Control

<?php

class Mage_Catalog_Product_CompareController extends
    Mage_Core_Controller_Front_Action
{
    public function indexAction()
    {
        $items = $this->getRequest()->getParam('items');
        // [...]
        if ($items) {
            $items = explode(',', $items);
            $list = Mage::getSingleton('catalog/product_compare_list');
            $list->addProducts($items);
            $this->_redirect('*/*/*');
            return;
        }
        $this->loadLayout();
        $this->renderLayout();
    }
    // [...]
}

Keine loose Kopplung!

Nicht isoliert testbar!

Keine Wiederverwendung!

Dependencies nicht sichtbar!

Typ(un)sicherheit?

Mit Inversion of Control

<?php

class Mage_Catalog_Product_CompareController extends
    Mage_Core_Controller_Front_Action
{
    /**
     * @var Mage_Catalog_Model_Product_Compare_List
     */
    private $list;

    public function __construct(
        Zend_Controller_Request_Abstract $request,
        Zend_Controller_Response_Abstract $response,
        array $invokeArgs = array(),
        Mage_Catalog_Model_Product_Compare_List $list
    ) {
        parent::__construct($request, $response, $invokeArgs);
        $this->list = $list;
    }

    // [...]

Mit Inversion of Control

    // [...]

    public function indexAction()
    {
        $items = $this->getRequest()->getParam('items');
        // [...]
        if ($items) {
            $items = explode(',', $items);
            $this->list->addProducts($items);
            $this->_redirect('*/*/*');
            return;
        }
        $this->loadLayout();
        $this->renderLayout();
    }
}

Explizite Dependencies!

<?php

$request = new Zend_Controller_Request_Http();
$request->setParamSources(['items' => '1,2,3']);

$response = new Zend_Controller_Response_Http();

$controller = new Mage_Catalog_Product_CompareController(
    $request,
    $response,
    []
);
[09-Oct-2017 13:05:13 Uncaught TypeError: Argument 4 passed to
Mage_Catalog_Product_CompareController::__construct() must be an
instance of Mage_Catalog_Model_Product_Compare_List, none given [...]

S.O.L.I.D.





« High level modules should not depend upon
low level modules. Both should depend upon
abstractions.» - Robert C. Martin

S.O.L.I.D.





« Abstractions should not depend upon details.
Details should depend upon abstractions. »
- Robert C. Martin

Dependency Injection?

Dependency Injection Arten




  • Property Injection
  • Setter Injection
  • Interface Injection
  • Constructor Injection

Property Injection

<?php

class Mage_Catalog_Product_CompareController extends
    Mage_Core_Controller_Front_Action
{
    /** 
     * @Inject 
     * @var Mage_Catalog_Model_Product_Compare_List
     */
    private $list;

    public function __construct(
        Zend_Controller_Request_Abstract $request,
        Zend_Controller_Response_Abstract $response,
        array $invokeArgs = array()
    ) {
        parent::__construct($request, $response, $invokeArgs);
    }

    // [...]
}

Property Injection





« Repeat after me: field injection
is a dumb idea... *sigh* » - @olivergierke

Setter Injection

<?php

class Mage_Catalog_Product_CompareController extends
    Mage_Core_Controller_Front_Action
{
    private $list;

    public function setProductCompareList(
        Mage_Catalog_Model_Product_Compare_List $list
    ) {
        $this->list = $list;
    }

    // [...]
}

Optionale Dependencies?





« The concept of optional dependencies is a lie! » - Me

Interface Injection

<?php

interface Mage_Catalog_Model_Product_Compare_ListAware
{
    public function setProductCompareList(
         Mage_Catalog_Model_Product_Compare_List $list
    );
}
class Mage_Catalog_Product_CompareController
    extends Mage_Core_Controller_Front_Action
    implements Mage_Catalog_Model_Product_Compare_ListAware
{
    private $list;

    public function setProductCompareList(
        Mage_Catalog_Model_Product_Compare_List $list
    ) {
        $this->list = $list;
    }
    // [...]
}

Constructor Injection

<?php

class Mage_Catalog_Product_CompareController extends
    Mage_Core_Controller_Front_Action
{
    private $list;

    public function __construct(
        Zend_Controller_Request_Abstract $request,
        Zend_Controller_Response_Abstract $response,
        array $invokeArgs = array()
        Mage_Catalog_Model_Product_Compare_List $list
    ) {
        parent::__construct($request, $response, $invokeArgs);
        $this->list = $list;
    }
}

DI Konfiguration



  • Config as code

  • Config as non-code

    • XML, YAML, ...

    • Annotations

Config as code - Disco

<?php

use bitExpert\Disco\Annotations\Bean;
use bitExpert\Disco\Annotations\Configuration;

/** @Configuration */
class BeanConfiguration
{
    /** @Bean */
    public function request(): Zend_Controller_Request_Http
    {
        return new Zend_Controller_Request_Http();
    }
    // [...]
    /** @Bean */
    public function cntrller(): Mage_Catalog_Product_CompareController
    {
        return new Mage_Catalog_Product_CompareController(
            $this->request(), $this->response(), []
        );
    }
}

Config as non-code - XML

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation=
    "urn:magento:framework:ObjectManager/etc/config.xsd">

    <preference for="Magento\Catalog\Api\Data\ProductInterface"
        type="Magento\Catalog\Model\Product" />

    <type name="Magento\Customer\Model\ResourceModel\Visitor">
        <plugin name="catalogLog"
            type="Magento\Catalog\Model\Plugin\Log" />
    </type>
</config>

 





« IoC is a good idea. Like all good ideas,
it should be used with moderation. »
- @unclebobmartin

Zu viele Abhängigkeiten...

<?php

namespace Magento\Framework\View\Element\Template;

class Context extends \Magento\Framework\View\Element\Context
{
    public function __construct(
        \Magento\Framework\App\RequestInterface $request,
        \Magento\Framework\View\LayoutInterface $layout,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Magento\Framework\UrlInterface $urlBuilder,
        \Magento\Framework\App\CacheInterface $cache,
        \Magento\Framework\View\DesignInterface $design,
        \Magento\Framework\Session\SessionManagerInterface $session,
        \Magento\Framework\Session\SidResolverInterface $sidResolver,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Framework\View\Asset\Repository $assetRepo,
        // [...]
    ) {
        // [...]
    }
}

ObjectManager is a lie!

<?php

namespace Magento\Framework\View\Element\Template;

class Context extends \Magento\Framework\View\Element\Context
{
    public function __construct(
        \Magento\Framework\ObjectManagerInterface $objectManager
    ) {
        // [...]
    }
}

ObjectManager is a lie!





« ContainerAware is the new Singleton. #php » - @tobySen

...aber "lazy loading"?

To new or not to new...

<?php

namespace Magento\Framework\Mail\Template;

class TransportBuilder
{
    // [...]

    protected function reset()
    {
        $this->message = $this->objectManager->create(
            \Magento\Framework\Mail\Message::class
        );
        $this->templateIdentifier = null;
        $this->templateVars = null;
        $this->templateOptions = null;
        return $this;
    }
}

To new or not to new...

<?php

namespace Magento\Framework\Mail;

class Message implements MailMessageInterface
{
    /**
     * Initialize dependencies.
     *
     * @param string $charset
     */
    public function __construct($charset = 'utf-8')
    {
        $this->zendMessage = new \Zend\Mail\Message();
        $this->zendMessage->setEncoding($charset);
    }

    // [...]
}

To new or not to new...

<?php

namespace Magento\Framework\Mail\Template;

class TransportBuilder
{
    // [...]

    protected function reset()
    {
        $this->message = new \Magento\Framework\Mail\Message();
        $this->templateIdentifier = null;
        $this->templateVars = null;
        $this->templateOptions = null;
        return $this;
    }
}

 





« Dependency Injection is a key element
of agile architecture » - Ward Cunningham

Vielen Dank! Fragen?


  • Stephan Hochdörfer
  • Head of Technology, bitExpert AG
    Mannheim
  • S.Hochdoerfer@bitExpert.de
  • @shochdoerfer

  • #PHP, #DevOps, #Automation
  • #phpugffm, #phpugmrn, #unKonf