Home >Backend Development >PHP Tutorial >One article to understand and implement IOC containers in modern PHP frameworks
This article brings you relevant knowledge about PHP, which mainly introduces the relevant content about IOC containers. The full name of IOC is: Inversion Of Control, inversion control. Let’s talk about it together. Take a look, hope it helps everyone.
I believe many people have heard of dependency injection. The basic conditions for dependency injection implementation are inseparable from containers. Containers are used to manage class dependencies and injection, and are responsible for service management and decoupling components. The simplest understanding is We can understand a container as a super large array dedicated to storing objects.
As shown in the figure, the caller obtains the object instance through the label of the container. As can be seen in the figure, it can be obtained through ::class or directly through the object. Indicates getting the instance object.
You may have heard of IOC containers. The full name of IOC is: (Inversion Of Control, inversion control).
Let’s understand what inversion of control is. In our traditional coding, our dependencies between classes are usually passed by new objects through coding, but using inversion of control we You can give control of the object to the container or framework for implementation. The purpose is to allow us to create objects without hard coding. As you can see from Figure 1, there are many objects stored in the container, and we can use them directly when we want to use them. The objects in the container do not require us to create them in code. When a certain class object is needed, the object will be obtained from the container. If the object does not exist, it will be automatically created. This means that we omit the process of creating objects in the code, and the container helps us realize this creation process. This is called inversion control. To sum up IOC in one sentence: transfer the control of creating objects to the instantiation of the container implementation class.
For example: If we want to create a class without using IOC
<?php class Sunny{ } $sunny = new Sunny();
We need to manually create a new class. In this case, it is hard-coded in the code.
The code using the IOC container can be written like this.
<?php class Sunny{ } $sunny = Container::getBean(Sunny::class);
Help us implement this class inside the container. Some students may have questions when they see this. If I use new Sunny, won’t the code be shorter and simpler? Let’s look at an example after looking at dependency injection.
Now that we know what IOC is, a new question arises. When we create a class, what should we do if the constructors of some classes require us to pass parameters? Through the study of IOC, we know that the IOC container will help us solve the problem of object instance creation. When creating an object in the container, it will perform a dependency search if it finds that the class has other dependencies. The process of the container finding the required objects is called DL ( Dependency Lookup, dependency lookup). Injecting required dependencies into code fragments is called DI (Dependency Injection, dependency injection).
For example, the new Sunny example mentioned in IOC. If there are multiple dependencies between classes.
<?php class Computer{ public function run(){ echo "编程中....\n"; } } class Sunny{ private $computer; public function __construct(Computer $computer){ $this->computer = $computer; } public function program(){ $this->computer->run(); } } $sunny = new Sunny(new Computer()); $sunny->program();
Here you can see that the Sunny class wants to depend on the Computer class for programming. If you use the IOC container to implement dependency injection, the code will be simple.
<?php class Computer{ public function run(){ echo "编程中....\n"; } } class Sunny{ private $computer; public function __construct(Computer $computer){ $this->computer = $computer; } public function program(){ $this->computer->run(); } } $sunny = Container::getBean(Sunny::class); $sunny->program();
One sentence summary: Solve the dependence on other classes when creating class instances, and dynamically provide an object with other objects it needs.
The problem solved by dependency inversion is to loosely couple the heavy dependencies between modules. The upper modules should not rely on the underlying modules, they should all rely on abstractions. Usually, a simple understanding of dependency inversion is programming towards interfaces or abstractions. Let's take a look at interface-oriented programming through the following example.
class Cache{ public function set($key,$value){ $redis = new CFile(); $redis->set($key,$value); } } class CFile{ public function set($key,$value){ echo "file:{$key}->{$value}\n"; } } $cache = new Cache(); $cache->set("name","sunny");
The above code seems to have no big problem, but what if one day the file cache is changed to Redis cache?
class Cache{ public function set($key,$value){ $redis = new CRedis(); $redis->set($key,$value); } } class CRedis{ public function set($key,$value){ echo "redis:{$key}->{$value}\n"; } } $cache = new Cache(); $cache->set("name","sunny");
It can be seen from this code that when the driver used by a cache changes, the Cache code must also make corresponding changes, because the code is written on the caller and the coupling degree becomes high. . It's the same as modifying the code, allowing programmers to program towards the interface, making the code more versatile and standardized.
interface ICache{ public function set($key,$value); } class CRedis implements ICache { public function set($key,$value) { echo "redis:{$key}->{$value}\n"; } } class CFile implements ICache{ public function set($key,$value) { echo "file:{$key}->{$value}\n"; } } class Cache{ private $drive; public function __construct(ICache $drive) { $this->drive = $drive; } public function set($key,$value){ $this->drive->set($key,$value); } } $cache = new Cache(new CFile()); $cache->set("name","sunny");
Many people think when they see this code, shouldn’t I just pass the desired object directly into the constructor? Why do we need to define an interface? In fact, the interface is defined to standardize the code. No matter which driver you use, as long as it implements my interface, you can use it. Without an interface, developers will not know what methods should be included in the driver when developing the driver. When we use interface, we only need to program for the interface. Cache does not care about how the class is implemented. Cache only operates according to the interface method.
Summary in one sentence: Dependency inversion to achieve loose coupling
<?php class Container { // 当前容器对象 private static $instance; // 存放在容器里面到实例 protected $instances = []; private function __construct() { } public static function getInstance() { if (!self::$instance) { self::$instance = new static(); } return self::$instance; } /** * 获取对象实例 * @param $key * @return mixed */ public function get($key) { if (isset($this->instances[$key])) { return $this->instances[$key]; } } /** * 绑定对象、闭包、类到容器 * @param $key * @param null $concrete * @return Container */ public function bind($key, $concrete = null) { if ($concrete instanceof Closure) { $this->instances[$key] = $concrete; } elseif (is_object($concrete)) { $this->instances[$key] = $concrete; } return $this; } } class Sunny { public function getName() { echo time() . "\n"; } } $app = Container::getInstance(); $sunny = $app->bind(Sunny::class,new Sunny()); $sunny = $app->get(Sunny::class); $sunny->getName();
Container.php <?php class Container { // 当前容器对象 private static $instance; // 存放在容器里面到实例 protected $instances = []; private function __construct() { } public static function getInstance() { if (!self::$instance) { self::$instance = new static(); } return self::$instance; } /** * 获取对象实例 * @param $key * @return mixed * @throws ReflectionException */ public function get($key) { if (isset($this->instances[$key])) { return $this->instances[$key]; } return $this->make($key); } /** * 绑定对象、闭包、类到容器 * @param $key * @param null $concrete * @return Container * @throws ReflectionException */ public function bind($key, $concrete = null) { if ($concrete instanceof Closure) { $this->instances[$key] = $concrete; } elseif (is_object($concrete)) { $this->instances[$key] = $concrete; } else { $this->make($key, $concrete); } return $this; } /** * 创建类绑定到类实例 * @param $abstract * @param null $atgs * @return mixed * @throws ReflectionException */ public function make($abstract, $atgs = null) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } $object = $this->invokeClass($abstract); $this->instances[$abstract] = $object; return $object; } /** * 反射解析类 * @param $abstract * @return object * @throws ReflectionException */ public function invokeClass($abstract) { $reflectionClass = new \ReflectionClass($abstract); // 获取构造方法 $construct = $reflectionClass->getConstructor(); // 获取参数得到实例 $params = $construct ? $this->parserParams($construct) : []; $object = $reflectionClass->newInstanceArgs($params); return $object; } /** * 解析构造方法参数 * @param $reflect * @return array * @throws ReflectionException */ public function parserParams(ReflectionMethod $reflect) { $args = []; $params = $reflect->getParameters(); if (!$params) { return $args; } if (count($params) > 0) { foreach ($params as $param) { $class = $param->getClass(); if ($class) { $args[] = $this->make($class->getName()); continue; } // 获取变量的名称 $name = $param->getName(); // 默认值 $def = null; // 如果有默认值,从默认值获取类型 if ($param->isOptional()) { $def = $param->getDefaultValue(); } $args[] = $_REQUEST[$name] ?? $def; } } return $args; } } Test.php <?php class Test { public $name; private $test1; public function __construct(Test1 $test1) { $this->test1 = $test1; $this->name = $this->test1->getName(); } } Test1.php <?php class Test1 { public function getName(){ return "test1返回的名字"; } } Sunny.php <?php require_once "./Container.php"; require_once "./Test.php"; require_once "./Test1.php"; class Sunny { private $test; public function __construct(Test $test) { $this->test = $test; } public function getName() { echo "获取test里面的name:{$this->test->name}\n"; } } $app = Container::getInstance(); $sunny = $app->get(Sunny::class); $sunny->getName();
Recommended learning: "PHP Video Tutorial"
The above is the detailed content of One article to understand and implement IOC containers in modern PHP frameworks. For more information, please follow other related articles on the PHP Chinese website!