Inversion of control (IOC) and dependency injection (DI)

concept

I understand that IOC and DI are JAVA concepts in the early stage, which are not accepted by the PHP industry for a long time, because the concept of PHP is simple and efficient, but the long-term development makes PHP have to go this way for engineering specifications and development decoupling. Laraval framework brings the concept of PHP development, and gradually each major framework has started to take the standardized development step. This includes ThinkPHP, easysoole, Swoft, and so on.

Inversion of control

Traditional development mode if we want to go to an object, we have to use new. In this way, the class control permission is in the hands of people, which means that when the program needs to create the dependent object actively (see Figure 1-1 below).

// Figure 1-1
// Common dependency injection pattern
class Course
{
	protected $user;
	public function __construct(User $user) {
		$this->user = $user;
	}
}

Dependency injection

The former says that in inversion of control, the system will store all request singleton beans or global singleton beans in a special IoC container, selectively inject the corresponding demand classes according to the needs of the code, and the injected classes are managed by the IoC container, reducing the coupling of the programs, so that the developers only need to pay attention to the corresponding business logic.

The relationship between the two

It can be said that the two complement each other. Dependency injection relies on IOC inversion of control, which is the ultimate goal of IOC, and IOC for dependency injection is the premise of its implementation. Here we take a chestnut. In fact, the composer of PHP can be understood as an IOC container. We dynamically load and delete the corresponding Bao Wenjian according to the needs of the program. The loading process can be understood as the corresponding dependency injection process.

Simple implementation of IOC container

Simple container interface

<?php
namespace library\Container;
interface ContainerInterface {
	public function get(string $bean);
	public function has(string $bean);
	public function set(string $bean, $value)
}

Define container base class

<?php
namespace library\Container;
class ContainerAccess implements \ArrayAccess {
	private $keys = [];
	public function __construct(array $value = []) {
		
	}
	public function offsetExists($offset) {
		return isset($this->keys[$offset]);
	}
	public function offsetGet($offset) {
		return $this->keys[$offset];
	}
	public function offsetSet($offset, $value) {
		$this->keys[$offset] = $value; 
	}
	public function offsetUnset($offset) {
		if(isset($this->keys[$offset])) {
			unset($this->keys[$offset])
		}
	}
}

Container class implementation

<?php

namespace library\Container;

class Container extends ContainerAccess implements ContainerInterface
{
    /**
     * Store bean method
     *
     * @var array
     */
    protected $inject = [];
    /**
     * Store bean object
     *
     * @var array
     */
    protected $instance = [];

    /**
     * Get bean object
     *
     * @param string $bean
     * @return mixed
     */
    public function get($bean)
    {
        return $this->offsetGet($bean);
    }

    /**
     * Whether bean exists
     *
     * @param [type] $bean
     * @return boolean
     */
    public function has($bean)
    {
        return $this->offsetExists($bean);
    }

    /**
     * Set bean object
     *
     * @param string $bean
     * @param string $value
     * @return void
     */
    public function set($bean, $value)
    {
        $this->offset($bean, $value);
    }

    /**
     * Get bean
     *
     * @param string $bean
     * @return void
     */
    public function __get($bean)
    {
        return $this->offset($bean);
    }

    /**
     * Get bean
     *
     * @param string $bean
     * @return void
     */
    public function __set($bean, $value)
    {
        $this->offset($bean, $value);
    }

    /**
     * Set injection method
     *
     * @param string $bean
     * @param string $methodName
     * @param Closure $methodBody
     * @return void
     */
    public function setInjectMethod($bean, $methodName, $methodBody)
    {
        if (!isset($this->inject[$bean][$methodName])) {
            $this->inject[$bean][$methodName] = $methodBody;
        }
    }

    /**
     * Get injection method
     *
     * @param string $bean
     * @param string $methodName
     * @return mixed
     */
    public function getInjectMethod($bean, $methodName)
    {
        if (isset($this->inject[$bean][$methodName])) {
            return $this->inject[$bean][$methodName];
        }
    }

    /**
     * Call bean method
     *
     * @param string $bean
     * @param string $methodName
     * @param array $parameters
     * @return void
     */
    public function callback($bean, $methodName, $parameters = [])
    {
        $method = get_class_methods($this->get($bean));
        if (in_array($methodName, $method, TRUE)) {
            call_user_func_array([$this->get($bean), $methodName], $parameters);
        } else {
            $injectMethod = $this->getInjectMethod($bean, $methodName);
            if (isset($injectMethod)) {
                call_user_func($injectMethod, $parameters);
            } else {
                throw new \RuntimeException("$bean Does not exist on $methodName method");
            }
        }
    }
}

Injection instance

<?php

$user = new model\User;
$user->age = 25;
$user->name = 'xxxx';
$container = new \library\Container\Container();
// Store in container
$container->set('user', $user);
// Get userBean
$userBean = $container->user;
// Call the user method
$userBean->callback('user', 'save');
// Methods that do not exist in injection classes
$container->setInjectMethod('user', 'methodTest', function () use ($userBean) {
    for ($i = 0; $i < 6; $i++) {
        echo $i;
    }
});
// Calling a method whose class does not exist
$container->callback('user', 'methodTest');

Tags: Programming PHP Java

Posted on Wed, 17 Jun 2020 00:46:27 -0400 by david212