PHP & understand the causes of autoload, PSR-0 and PSR-4 and analyze the differences between PS0-0 and PSR-4

1. include & require

We know that if an A.php file wants to introduce the classes in the B.php file, it needs to introduce B.php through include / require.
This method is not a problem for small projects, but for large projects, it usually contains many public files, such as foo / bar / dog PHP, according to the traditional way, we can import this file in every required place, but this will cause the following problems:

  1. Foo / bar / dog should be introduced in every place PHP, the operation is really cumbersome
  2. Increased number of codes
  3. Repeated pasting is prone to residue and leakage

Is there any way to solve this problem? Yes__ autoload is used to liberate include / require.

2. __autoload

__ autoload is a magic function newly added after php 5. This function is automatically triggered when using new xxx and passes a $class parameter. This parameter is the xxx part of new xxx. The following is its usage

function __autoload($class) {
	# $class = Foo\Bar\Dog
	require_once $class_name . '.php';
}
$dog = new Foo\Bar\Dog();
$dog->say();

This function helps us reduce a lot of include/require, but because__ Autoload can only be used once. Suppose we have not only the Foo/Bar directory, but also the Coo/Too, Aoo/Boo and other common directories. What can we do? Is there any way to load multiple times? have sql_autoload_register is to solve this problem.

3. sql_autoload_register

sql_autoload_register is specifically used to define multiple__ Autoload function. Its usage is as follows:

function my_autoload_1($class) {
	require_once $class_name . '.php';
}
function my_autoload_2($class)  {
	require_once $class_name . '.php';
}
function my_autoload_3($class) {
	require_once $class_name . '.php';
}
sql_autoload_register('my_autoload_1');
sql_autoload_register('my_autoload_2');
sql_autoload_register('my_autoload_3');
$dog1 = new Foo\Bar\Dog();
$dog1->say();
$dog2 = new Coo\Too\Dog();
$dog2->say();
$dog3 = new Foo\Bar\Dog();
$dog3->say();

Now the problem of multiple automatic loading has been solved due to sql_autoload_register can replace__ Autoload can also implement multiple functions__ Autoload, so__ Autoload will naturally be eliminated by PHP officials.
And that's it? No~

Everyone can use SQL_ autoload_ When we introduce the third-party plugin / add-on framework, we are familiar with the syntax of the third-party plugin / add-on framework. When we introduce the third-party plugin / add-on framework, we are familiar with the syntax of the third-party plugin / add-on framework, Later, a group of like-minded people came together to develop a specification for automatic loader, which is called PSR-0 and its full name is PHP standard recommended. We need to write our own automatic loader in accordance with this specification.

4. PSR-0

I will not elaborate on the specification of PSR-0 here. Interested parties can refer to the official documents PSR-0
Here we focus on how to write after realizing the PSR-0 automatic loader? In other words, which popular frameworks have helped us write an automatic loader of PSR-0 specification? How to use it? Here we take composer as an example and assume that our project structure is as follows:

src
	Foo
		Bar
			Dog.php
	Coo
		Too
			Dog.php
test.php

Then refer to the composer documentation, which needs to be in composer Mapping configuration in JSON

{
    "name": "cookcyq",
    "autoload": {
        "psr-0": {
            "Foo\\Bar": "src/",
            "Coo\\Too": "src/"
        }
    }
}

After configuration, it needs to execute: composer durmp auto - O, which will automatically generate autoload under vendor/composer / PHP file, we can use the autoloader happily by introducing this file.

// test.php
require_once "vendor/composer/autoload.php";
// Tips:
// Supported in PSR-0 specification_ Underline syntax, which will eventually be replaced by /, so the following is equivalent.
$dog1 = new Foo_Bar_Dog();
$dog2 = new Foo\Bar\Dog();

$dog3 = new Coo\Too_Dog();
$dog4 = new Coo\Too\Dog();

Some friends may wonder why they support underline_ What about this form? This is to play the role of independent scope and avoid repeated name conflicts, because namespace didn't appear at that time.
Well, is it over by PSR-0? However, the goose didn't, and the namespace appeared soon after.
Therefore, there is a new specification of PSR-4 later.

5. PSR-4

I think the new specification of PSR-4 is entirely due to the existence of namespace.
If you don't know what namespace is, you can refer to what I wrote earlier PHP & understanding namespaces

What is the difference between PSR-4 and PSR-0?

  1. PSR-4 does not support_ Underline this way, because there is already a namespace
  2. In composer The key in JSON must end with \ \, as follows
{
    "name": "cookcyq",
    "autoload": {
    	"psr-0": {
            "Foo\\Bar": "src/",
            "Coo\\Too": "src/"
        },
        "psr-4": {
            "Foo\\Bar\\": "src/",
            "Coo\\Too\\": "src/"
        }
    }
}

Seeing this, I believe you have understood the concept of autoload and how to use their autoload in composer. Students in a hurry don't have to look down. I think this is enough.

If you have enough time, you can read on.

6. Why is the composer compatible with PSR-0 without embracing PSR-4 directly?

The answer is obvious. At present, some ancient and useful plug-in authors still use the PSR-0 specification,
Some of the authors use_ Underline syntax, so composer can't cut across the board.

7. What are the differences between PSR-0 and PSR-4 implementations of composer?

For novices (including me), the explanations of PSR-0 and PSR-4 often found are very confusing.

I can understand the PSR-0 mapping relationship in the figure, but I am confused about PSR-4. Suppose bar The PHP file is placed in Src / acme / foo / bar PHP, but acme \ foo = > / SRC / bar PHP's / SRC / bar This mapping relationship of PHP can't find bar PHP file? Therefore, various searches were conducted out of curiosity, and the results were still disappointing. Most of them were either copied from official examples or copied from others, and then did not explain why such a relationship occurred. At least for me, this explanation did not work. At present, the way I comfort myself is: the bottom layer will automatically help us find a complete path to import, and then this mapping relationship does not refer to the above import file relationship, so I feel more comfortable. So I thought, instead of looking for it, I'd better see how the source code helps US import the final file, so I have the next source code analysis, Don't worry, I just pick the most critical part here, because I can't understand the rest.

8. Analyze the implementation principle of composer psr-0 & psr-4

This is the complete directory structure of this case

Suppose composer JSON adopts PSR-4, which is as follows

{
    "name": "cookcyq",
    "license": "n",
    "require": {},
    "autoload": {
        "psr-4": {
            "Foo\\Bar\\": "vendor/foo/bar/src"
        }
    }
}

Use comopser durmp auto - O to generate the following files:

We focus on the classloader PHP, which contains several core properties and methods of PSR-0 and PSR-4 to realize automatic loading

Key attributes:

class ClassLoader {
	// PSR-4 key attributes
    private $prefixLengthsPsr4 = array();
    private $prefixDirsPsr4 = array();
	// PSR-0 key attributes
	private $prefixesPsr0 = array();
    private $fallbackDirsPsr0 = array();

Key method 1 add / addPsr4(): add composer "psr-0" in JSON: {...} And psr-4 ": {...} Add key attributes to the content, and the source code is as follows

public function add($prefix, $paths, $prepend = false) { ...Code and addPsr4 almost }
public function addPsr4($prefix, $paths, $prepend = false)
    {
        if (!$prefix) {
            // ...
        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
            $length = strlen($prefix);
            // End must be added\ 
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
        } elseif ($prepend) {
            // Prepend directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                (array) $paths,
                $this->prefixDirsPsr4[$prefix]
            );
        } else {
            // Append directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                $this->prefixDirsPsr4[$prefix],
                (array) $paths
            );
        }
    }
   

You don't need to look at the above code, just care about the final storage structure, which is similar to:

// PSR-4==========================
$prefixLengthsPsr4 = [
    "F" => [
        "Foo\\Bar\\" => 6,
    ]
]
$prefixDirsPsr4 = [
    "Foo\\Bar\\" => [
        "vendor/foo/bar/src"
    ]
]

// PSR-0==========================
$prefixesPsr0 = [
    "F" => [
        "Foo\\Bar\\" => [
            "vendor/foo/bar/src"
        ]
    ]
]

Key method 2 findFileWithExtension(): finding the complete file path of PSR-0 and PSR-4 is completed here. When it is found, it can be introduced into includeFile. The basic process of autoload is like this. The key source code is as follows:

private function findFileWithExtension($class, $ext)
    {
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
        $first = $class[0];
        // ============================PSR-4 query=============================================
        if (isset($this->prefixLengthsPsr4[$first])) {
            $subPath = $class;
            while (false !== $lastPos = strrpos($subPath, '\\')) {
                $subPath = substr($subPath, 0, $lastPos);
                $search = $subPath . '\\';
                if (isset($this->prefixDirsPsr4[$search])) {
                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
                        if (file_exists($file = $dir . $pathEnd)) {
                            return $file;
                        }
                    }
                }
            }
        }
		// ============================PSR-0 query=============================================
		// Conditional statements that support underscores
		if (false !== $pos = strrpos($class, '\\')) {
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
        } else {
            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
        }
        
        if (isset($this->prefixesPsr0[$first])) {
            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
                if (0 === strpos($class, $prefix)) {
                    foreach ($dirs as $dir) {
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                            return $file;
                        }
                    }
                }
            }
        }
        return false;
    }

In the above code, we only need to find the most critical two-layer loop:
PSR-4 adopts: while + foreach
PSR-0 adopts: foreach + foeach
Because PSR-4 doesn't need_ The underscore and the end must be marked with \ \, which is bound to be realized in another way to find the file path structure. Only with these two attributes: $prefixLengthsPsr4 / $prefixDirsPsr4, and then combined with whre +foreach to find the complete path, while PSR-0 only defines the $prefixesPsr0 attribute, so foreach + foeach is used to find the complete path, Finally, both methods succeeded in finding the complete path.

9. Well, let's make a summary

  1. PSR-0 support_ Underline, PSR-4 does not support
  2. composer.json PSR-0 does not need to be followed by \ \, PSR-4 must be followed by \ \\

The rest is in composer JSON configuration usage is exactly the same. Only the way to find the full file path adopts different circular strategies.

reference:
https://stackoverflow.com/questions/24868586/what-are-the-differences-between-psr-0-and-psr-4#:~:text=The%20summary%20is%20that%20PSR,part%20following%20the%20anchor%20point.
https://my.oschina.net/sallency/blog/893518

Keywords: PHP Back-end

Added by jibster on Sun, 30 Jan 2022 17:55:37 +0200