How many steps does it take to move from the symfony framework to a complete project? Dependency injection and reflection

Keywords: PHP Laravel SQL

Preface

For PHP frameworks, whether it's yii, symfony or laravel, everyone is involved in their work. For the resource bundles vendor folders, entry files (index.php or app.php) stored in the framework, people also meet with them every day. But are you really familiar with these files/folders? How does a complete project evolve from a pure framework? What role does each part play in framing this building?

I. Dependency Injection

Dependency injection is not sql injection o (o)

1.1 Talk about the relationship between dependency injection and reflection classes

When it comes to DI, 90% of articles refer to something called Reflection, which leads many viewers (including me) to think that two are one in itself, but in fact they are two different concepts. Reflection is a reflection class, but a dependency injection DI is a design pattern. How can the reflection class belong to the same thing as the design pattern? So their relationship is dependency -- dependency injection is a design pattern that depends on reflection classes.

1.2 What is Reflection

Write a very simple class A, in which there is only one object $a and one method a().

class A
{ 
    public $a;
    public function a()
    {
        echo __FUNCTION__;
    }
}

Then we start calling the reflection class Reflection.

$A       = new A();
$reflect = new ReflectionObject($A);
$props   = $reflect->getProperties(); // Get all objects of this class
...

We can know all the things we want to know about this class by reflection, and get all the details of a class like x-ray. But when I finished reflective classes, I was lost again. What is the purpose of designing this reflective class? If you want to know how many methods / objects in the original class look directly at the original class, can't you? Why do we need to do more? What does this have to do with dependency injection?
Don't worry, let's talk about something else first, and then look back.

1.3 All depend on what to do

Now there's a scenario where I want to instantiate B, but B depends on A. What should I do? It's as simple as the following.

Class A{
    // do sth.
}
Class B{
    public function __construct(A $a){
        // Class B depends on A
    }
}
$a = new A();
$b = new B($a); 

It's simple, isn't it? It can be solved in two strokes. It's not difficult. But now there are 26 classes in A-Z, class B depends on A, class C depends on B, and so on. So if I want to instantiate class Z, what do you need to do? Do you still use the method shown above? Wouldn't it take 26 lines to instantiate Z?

So is there a way to instantiate B directly and then automatically load all the other classes? Really. You need to use the reflection class just mentioned above.

<?php
/**
* Tool class, which is used to implement automatic dependency injection.
*/
class Ioc {
    // Get an object instance of a class
    public static function getInstance($className) {
        $paramArr = self::getMethodParams($className);
        return (new ReflectionClass($className))->newInstanceArgs($paramArr);
    }

    /**
     * The method parameters of the class are obtained, and only the type parameters are obtained.
     * By recursive method
     * @param  [type] $className   [Class name]
     * @param  [type] $methodsName [Method name]
     * @return [mixed]              
     */
    protected static function getMethodParams($className, $methodsName = '__construct') {

        // Obtain this class by reflection
        $class = new ReflectionClass($className);
        $paramArr = []; // Record parameters, and parameter types

        // Determine whether there is a constructor for this class
        if ($class->hasMethod($methodsName)) {
            // Get the constructor
            $construct = $class->getMethod($methodsName);

            // Judging whether the constructor has parameters
            $params = $construct->getParameters();

            if (count($params) > 0) {

                // Judging the Type of Parameters
                foreach ($params as $key => $param) {

                    if ($paramClass = $param->getClass()) {

                        // Get the parameter type name
                        $paramClassName = $paramClass->getName();

                        /**
                         * Get the parameter type
                         * Use recursion here
                         */
                        $args = self::getMethodParams($paramClassName);
                        $paramArr[] = (new ReflectionClass($paramClass->getName()))->newInstanceArgs($args);
                    }
                }
            }
        }
        return $paramArr;
    }

    /**
     * Method of executing class
     * @param  [type] $className  [Class name]
     * @param  [type] $methodName [Method name]
     * @param  [type] $params     [Additional parameters]
     * @return [type]             [description]
     */
    public static function make($className, $methodName, $params = []) {
        // Get instances of classes
        $instance = self::getInstance($className);
        // Obtaining the parameters needed for this method dependent on injection
        $paramArr = self::getMethodParams($className, $methodName);
        return $instance->{$methodName}(...array_merge($paramArr, $params));
    }
}

class A {
    // ...
}

class B {
    public function __construct(A $a) {
        // ...
    }
}

class C {
    public function __construct(B $b) {
        // ...
    }
}

$cObj = Ioc::getInstance('C');

Let's focus on the getMethodParams method of the Ioc class.

Let's start with the general situation: since this method uses recursion to traverse loaded classes to achieve the goal of loading all dependent classes.

This method first instantiates Reflection, then uses the getMethod method to get the _construct method of the loaded class, and then looks to see if there are other classes in this _construct as parameters. If so, it recursively repeats the above process until there is no dependency.

This is a simple example of dependency injection using Reflection. Although in the actual framework will be more complex than the above examples, but still much the same.

For example, in many frameworks, you will use a concept called container -- use $this - > getcontainer() - > get (your class? Name) to get classes. What high-tech thing is this container? Don't worry, he still depends on injection after all.

We'll get to the bottom of it and see how symfony container s are designed.

Posted by broheem on Tue, 24 Sep 2019 00:04:32 -0700