This paper mainly for the framework of internal container and facade realization as a reference point to achieve,

preface

Before this has been analyzed the automatic loading of the class, configuration file loading source analysis, this article is the third phase of the article, mainly for the realization of the container and facade class, parsing source code. And learn some knowledge points to achieve this function.

  • First article: ThinkPHP automatic loading Loader source analysis
  • Second article: ThinkPHP configuration file source analysis

Singleton mode

There are two design patterns that need to be understood before learning about containers and facades, singleton and register tree.

Start with a brief explanation of the singleton pattern.

  • Have a constructor and the property is private
  • Have a static member variable to hold an instance of the class
  • Have a static method to access the instance

Here is a simple singleton pattern for ka ka’s implementation, comparing the above three features to see if they are consistent.

The static variable is instance

Owns the construct and is private

And finally, there’s a getInstance static method

Here’s a simple test

Again, the index controller is tested and called four times to verify that its class has only been instantiated once

Visit this method to see

New-class is executed only once, which directly proves that the created class is instantiated only once.One of the questions I had before was why the constructor used private attributes.

Have you ever had that question before? Let’s take you through it

Private property constructors are defined in this class to prevent their classes from being instantiated externally.

The following error is reported when instantiating this class externally.

So why mention singleton patterns here! Because in the following learning container source code will be used

For example below thinkphp/library/think/Container. The PHP class is a Container for current instance.

At this point, the singleton pattern is simply understood, and the singleton pattern is also understood to better understand containers.

2. Register tree mode

Why do I mention the registry tree pattern here? Because the registry tree pattern is a dominant position in the framework, so you have to learn about it!

What is a registered tree model?

  • Register tree mode is to register the object instance to a tree (this tree is not a real tree! Register to a global property.
  • The corresponding object instance can then be retrieved from the global tree using internal methods.

Such words certainly can not be better understood, next kaka take you to see a simple case to understand briefly.

A registry tree mode requires four things, a pool of the registry tree, to mount objects to the registry pool, to get objects from the registry pool, and to unload objects from the registry pool.

The figure below is a simple registration tree pattern for kaka writing.

If the code does not understand the need to repair the basis of ha!

Next, create a TestTree file in the same directory

Go to the controller to test whether there is a problem with the registration tree mode of the write

Be sure to pay attention to namespace issues when doing tests. The kaka directory here was previously configured in the auto-loading class. If not, check it out in the first article.

I’m just going to instantiate TestTree

Then use register tree mode to register the instance into the Object tree pool

Finally, get the class and call the TestTree method directly.

Finally, take a look at the final print, which is the return value of the getTreeContent method in the TestTree class.

Registration tree mode is the above kakaka explained these content, is not to learn for source code, these content is also we have to learn to use.

How to understand inversion of control and dependency injection

In fact, these two are referring to a thing, is a kind of programming ideas, do not want to be so difficult to understand and lofty.

So what is a container? A container is understood face to face as something that holds things. In programming, we often see variables and object properties as containers. What can be inside a container depends entirely on the definition of the container.

Now, however, we’re talking about a different kind of container that stores objects, classes, interfaces, rather than text and values. These containers are used to implement many advanced functions, most commonly decoupling between code and dependency injection.

So why are there two concepts, inversion of control and dependency injection? As mentioned above, they actually refer to the same thing, but they are described in different ways.

Just like you’re your father’s son, or you’re your grandfather’s grandson, either son or grandson means the same person. I’m just looking at it from a different Angle.

Inversion of control

Is to look at the problem from the perspective of the container, which controls the application and injects the external resources needed by the application in reverse.

Dependency injection

Is from the perspective of the application, which relies on the container to create and inject the external resources it needs.

role

Mainly used to reduce coupling between code.

Effective separation of objects from external resources required by the application.

The following two pictures can clearly illustrate the problem

I’ll give you a simple example

Define two classes as Person and Car, instantiate them in Person and call the pay method in Car.

I’m going to call it in my controller, and I’m going to print out 123 that Car returned, so I’m not going to print that.

Let’s change the code to pass Car directly to the Person class and call the corresponding method directly from the Person class using the passed object.

This is just a simple implementation to prepare you for reading framework container code, which will be explained in more detail later in this article.

Four, must be reflective mechanism

I don’t know if you know about GO’s reflection mechanism, kaka was a little confused after watching GO’s reflection mechanism at that time.

But after looking at REFLECTION in PHP, not only do I have a better understanding of reflection in GO, but I also have a better understanding of reflection in PHP.

The concept of reflection was first introduced in PHP5.0, and among the frameworks currently in use thinkPHP and Laravel are known to use reflection to implement dependency injection.

The understanding of reflection is to get something from the root. In programming, it means that as long as you know a class, you can know all the properties and methods of that class.

case

This is just a simple implementation case that gets all the methods and attributes of the class. You can see whether the print results in the figure below are consistent with TestReflection.

This also presents a problem from the side, that is, it will expose some information that should not be exposed.

There are many other interfaces provided by reflection. Here are a few commonly used ones, and the rest are parsed in the framework source code.

Execute a class method using reflection

The result of the print is kaka

Execute a class method with arguments using reflection

Use reflection to execute a method on a class that takes no arguments

Other methods you can try to try, because the reflection of the interface in the usual basic development is not how to use, which is introduced to everyone are behind reading the source code can be used to.

Now that we know about reflection, what can reflection do? One function point automatically generates documentation.

Reflection here is a simple understanding, as for more interface use can go to the official view corresponding interface information.

After we understand reflection, we’re going to start talking, and we’re going to start talking about containers. The following containers can be better understood only after the above foundation is laid.

5. Play with your container class

In this part, we will first realize a container of our own. We will explain the singleton mode, registration tree mode and reflection in a series, so as to deepen the impression and better understanding.

The dependency injection method dependency is used to decouple the code.

But this time! Containers are used to solve this problem.

First of all, the need to define a good class, this class is the use of singleton pattern and registration tree pattern, the previous article did not have a good look, must take a careful look, otherwise the text will be difficult to understand.

In order to facilitate future review, the case demonstration in each section is placed in the corresponding controller

Port the dependency injection code and configure the annotated route to access it to see if the final result is 123 as returned by Car

Test the print and everything is ok

This code was modified using the singleton pattern and the registry tree pattern

After modification, print the result, which is also the value returned by CAR, 123.

One thing to note here is that the set and get methods don’t coexist in the same method, but I’m just going to do it for you as a demonstration.

We will see how the set and GET methods are used later in the container source code. Here is just to let you experience the singleton mode and the registration tree mode.

Here’s a small change to modify the last two lines of code from above

Scenario 2

Now let’s modify the Person file

Add a constructor that assigns the parameters to the buy method. In the buy method, there is no need to pass the parameters, just use this->obj.

Now, if I were to run it directlydependencyThe route will report the following error because the constructor has an argument in Person that we didn’t pass.

In this case, we need to make a change, which is to pass in the instance of Car when instantiating Person so that there is no problem.

However, you will find that the above code, a simple few lines of code complicated like this, this time has already done more harm than good, regardless of the design pattern is good, blind use is also a burden on the project.

So this time reflection comes, reflection in the above article also carried on the simple introduction, must see ha! The article is a ring within a ring.

War of Reflection optimizes code

This is what the optimized code looks like, and the code is then simply parsed.

  • I’ve only changed it based on the previous codekaka/container/Container.phpThe get method in this class
  • Determine if the name person is in the container
  • Use the reflection interface, and then get the constructor passed in to the Person class
  • Just return the instance of Person if there is no constructor for Person
  • Gets the method of the Person constructor if person is stored in the constructor
  • Because the constructor in the Person class is not limited to one argument
  • So we need to loop to get the object for each parameter
  • Finally, the corresponding instance is created using the reflective newInstanceArgs interface

      
/** * Created by PhpStorm. * User: Date: 2020/9/21 * Time: 19:04 */

namespace container;


class Container
{
    /** * store container *@var array
     */
    public $instances = [];

    /** * the object instance of the container *@var array
     */
    protected static $instance;

    /** * Define a private constructor to prevent external classes from instantiating Container constructor
    private function __construct() {}/** * Gets an instance of the current container (singleton mode) *@return array|Container
     */
    public static function getInstance ()
    {
        if(is_null(self: :$instance)) {self: :$instance = new self(a); }return self: :$instance;
    }

    public function set ($key.$value)
    {
        return $this->instances[$key] = $value;
    }

    /** * User: click * Notes: Get the instance inside the container using reflection * Time :2020/9/21 22:04 *@param $key
     * @return mixed
     */
    public function get ($key)
    {
        if(!empty($this->instances[$key])){
            $key = $this->instances[$key];
        }

        $reflect = new \ReflectionClass($key);
        // Get the class constructor
        $c = $reflect->getConstructor();
        if(!$c) {return new $key;
        }

        // Get the constructor argument
        $params = $c->getParameters();
        foreach ($params as $param) {
    		 /** ReflectionClass Object ( [name] => container\dependency\Car ) */
            $class = $param->getClass();
            if(!$class){

            }else{
                // container\dependency\Car
                $args[] = $this->get($class->name); }}// Create a new class instance from the parameters given
        return $reflect->newInstanceArgs($args); }}Copy the code

fileapplication/index/controller/Container.phpThis is the change after modification

Problem a:kaka/container/dependency/Person.phpWhat does the parameter Car mean

The problem is simple, as you can see that the Car is the same directory as the car.php file. You can just think of it as a file in the same namespace.

Problem two: documentsapplication/index/controller/Container.phpWhy can I call the buy method directly

If we look at the value of obj, we can return an object that has already instantiated the Car class, so we don’t need to instantiate it. We can call buy directly because the arguments are passed through

The above is a simple container of ka ka implementation, if you do not understand or questions can be directly comment on the reply area.

The next step is to analyze the container in the framework, step by step back to the root.

Container class analysis Countable

As for Countable, I have not decided whether to write and show it to you in the form of an article, but in the later reading of the source code, a large number of Countable applications appeared.

In order for everyone to understand each technical point, kakaka or write out.

In file thinkphp/library/think/Container. PHP, you can directly see the use Countable interfaces, and implement it!

Came toCountableThe only method we see in this interface is count().

As you can see from the comment on the Count Elements of an object line in the code, this interface is an element of the computed object

Follow the PHP documentation for further information.

Abstract public Countable::count () : void () : int

Practical cases

Light said not to do, everything fails; Talk and work, quick success. Direct drive dry

New file kaka/container/countableTest. PHP, and add the following content

And then in the fileapplication/index/controller/Container.phpLearn how to use Countable.

Notice the usage here, which is to use count() directly;

How does count() differ from counting () in Countable

Take a look at the explanation in the PHP source code

As you can see, the first argument can be an array or countable

Countable simply rewrites the count method in the SPL in order to customize its own statistical rules.

int count ( mixed $array_or_countable [, int $mode = COUNT_NORMAL ] )
Copy the code

Count you don’t know how to use

While we’re at it, kaka gives you a general idea of a use of count that isn’t very common.

This is one of the most common and frequently seen use cases in development.

But if you are given a multidimensional array, such as the figure below, and asked to count this multidimensional array, how do you count it?

At this point, I think most of you are going to loop and define a counter to accumulate.

In fact, the count() function solves this requirement in this section.

The bottom print is “4—-6”

Using the count() function directly on an array gives you the length of the first array.

But the count() function takes a second argument, set to 1, to recursively count the number of elements in the array.

So if you look at the documentation at this point, the count() function itself takes two arguments

The first argument is must hungry, and the selection is array

The second argument defaults to 0 and does not count all elements in the multidimensional array

When the second argument is 1, it recursively evaluates all the elements in the multidimensional array.

Container Container class

The above implementation of a container to create their own, next look at the source of the container, after the above container in the technical points have been included.

In the next reading container source code will not be very difficult, if the previous article did not read, be sure to go through the ha!

A file that you’ve opened for the umpteenth time: public/index.php.

How many times have I opened this file to take a peek at the source code, only to give up looking at it.

After going through the registration tree pattern, you should be able to see what this line of code returns, rightContainer::get('app')

This line of code returns an instance of your app, and you can simply break it.

And you can see that the return is just a bunch of properties in the app class.

So register tree mode will not continue to go back to read before, otherwise more and more confusing.

So how do we define a container in a framework? How does it work?

Just focus on what the get() method does.

The code will trace back to the filethinkphp/library/think/Container.phpIn theget()methods

The getInstance() method is familiar. This is the singleton pattern described above.

Code tracing is possiblegetInstance()This method, you’ll see in the same file the singleton method that returns the Container instance.

The Container instance calls the make method

Static ::getInstance() returns an instance of Container and calls the make method of this class.

Before we start reading the source code for the make method, we need to briefly comb through a few properties.

It is important to remember these four attributes, and it is important to distinguish instance from instances.

The singleton returns an instance of the current class and all instances in the container.

Execution flow chart

Now that the code is clear, it’s time to look at the flow chart of execution to see more clearly.

The invokeClass method is resolved in detail

By reading the code flow above, or the flowchart above, you know that the code eventually leads to a method called invokeClass, which is this method.

All of this method is the use of reflective knowledge points, not in the above or before the article!

In the invokeClass method, the most important is the method bindParams binding parameters, this method is also all use reflection.

So I don’t need to say too much about how much reflection plays a role in the container.

Before this need to explain this, see this __make method, kakaka is especially deep memory ha!

This method in the previous study of the config source configuration of the article has been temporarily omitted, because at that time the knowledge of the knowledge and framework code execution process is not to explain the __make method stage.

The __make method is explained in detail here.

When you print the value of reflect, you return two objects of the reflection class, as shown below.

code$reflect->hasMethod('__make')Check whether there is a __make function in this reflection class

$method = new ReflectionMethod($class, ‘__make’); Is a method that executes the reflection class, in this case the __make method

When the breakpoint occurs, the method returns two existing __make reflection classes, because the breakpoint only shows two reflection classes.

This is mainly about think\Config.

The last line of code$method->isPublic() && $method->isStatic()It’s to determine whether the method is public or whether the method is static

$args = $this->bindParams($method, $vars); This line goes to the bindParams method, which is also dissected in detail below.

Parse the bindParams method

Let’s take a look at the bindParams method.

The parameter passed is a reflection class and the second parameter will not be explained, so far there is no response scenario.

The first parameter value is $reflect

Use reflection method$reflect->getNumberOfParameters()Gets the number of arguments in the corresponding method in the reflection class. The __make method is the same as above. The container code only gets the number of arguments for two methods: the __make method and the reflection class constructor.

Since there is no scenario for passing the VARS variable, this section will be skipped for now.

$params = $reflect->getParameters(); Is also a parameter that uses reflection to get the method.

The result is two sets of data.

So where did this data come from? Scroll up a little bit. Let’s see$reflectYou know what this parameter is.

The think\App reflection class does not have a __make method, so it takes arguments from the constructor.

Then the think\Log reflection class has a __make method, which returns the argument to __make, as shown below.

A class like Think \Log, which has both a __make method and a constructor, passes the bindParams method twice.

Next up are the parameters taken from the loop reflection class.

Gets the parameter name, and gets the corresponding reflection class

Finally, the obtained reflection class is passed to the getObjectParam method.

In thisgetObjectParamThere’s not much in the method.

Since $vars is an empty array from beginning to end, neither removing the first element of the array nor determining whether it is a closure is performed.

It will eventually return to execute the make method

The make method then returns the instance directly from the container

When a reflection class has a __make method, it is eventually executedreturn $method->invokeArgs(null, $args);Executes the reflection class method with an argument

Use containers to invoke configuration classes

Since you have read the container source code once, can you use the container to achieve it!

That’s fine, but you need to pay attention to the “Container” namespace. In this case, you need to rename the class to “Container”, so you don’t need to use it.

Up to here the source of the container on the explanation of almost, behind kaka will do a complete flow chart, to provide you to view.

Eight, container source code after reading summary

The registration model

This article starts with two design patterns, singleton and register tree.

The singleton pattern simply means that only one instance object is returned during the application declaration cycle, and no new objects are created.

Register tree mode understanding is to put the program used in the object will be stored in a tree, when using directly from the tree to obtain the object can be used directly.

Inversion of control dependency injection

Inversion of control and dependency injection must not be confused by their names. They are two different ways of looking at an event, one from a container perspective and the other from an application perspective.

From a container perspective, the container controls the application, injecting external resources into the application in reverse

From an application’s perspective, the application relies on the container to create and inject the external resources it needs.

reflection

There’s nothing to summarize about reflection, just open up the documentation and see, but it’s important to learn how to use it and know what it means and how to use it flexibly.

Container source code parsing

Container source code after you will find that the use of things is said above three knowledge points formed, the use of registration mode to the container object management.

Keep in mind for this diagram that these four attributes are used around in the source code.

One is the execution of the code

The most important methods in the container are invokeClass and bindParams, which have no problem following this clattering idea, executing bit by bit with this breakpoint flow.

It’s a little tricky to look at, but when you look at it closely you’ll see that you can learn a lot

Adhering to learning, blogging and sharing is the belief that Kakha has been upholding since she started her career. I hope the articles on the Internet can bring you a little help. I’m Kaka. See you next time.