How many steps does it take from symfony framework to a complete project

Time:2020-5-27

preface

aboutphpThe framework of bothyiisymfonyOr maybelaravelWe all dabble in our work. For the resource package stored in the frameworkvendorFolders, entry files(index.phpperhapsapp.php)And we all meet with them every day. But are you really familiar with these files / folders? How does a complete project develop from a pure framework? What role does each part play in the framework of the building?

In the last chapter, we talked about dependency injection, and we don’t know if everyone understands it? No problem if you don’t understand. Today’s chapter has nothing to do with the previous one.

2、 Composer

Now we come to the next topic. Let’s talk about itcomposerThis tool. Everyone is not new to this tool. It is very convenient to install plug-ins with it. But do you know his principle? It’s a common class. How can it be loaded?composerSay, we’ve made it, and it’s up to youautoloadOperate.

2.1 __autoload

This is a particularly important knowledge point. We often see it in the framework’s entry file(__autoloadandspl_autoload_register。 Now, of course, you can only seespl_auto_register)。 But when asked about the functions and methods of these two methods, most people are still confused.

What are these two functions? What’s the convenience of autoloading?

includeandrequirePHP is the introduction of two basic methods of files. Direct use in small scale developmentincludeandrequireHowever, in large projects, there will be a large number of include and require stacks.(think about it, are you tired when I write hundreds of include in a file? )

Such code is neither elegant nor efficient, and it is also very difficult to maintain.

In order to solve this problem, some frameworks will provide a configuration list of imported files, which will be imported when the object is initialized. But this just makes the code more concise, and the effect of the introduction is still unsatisfactory. After PHP5, with the improvement of PHP object-oriented support,__autoloadFunction to make automatic loading possible.

Here I add two knowledge points that are not related to the current chapter:

  • The function of include is the same as that of require. The difference between them is that when an include error occurs, only a warning will be generated, and require will throw an error termination script.
  • include_ The only difference between once and include is include_ Once checks whether the file has been imported. If so, it will not be imported repeatedly.

The easiest way to implement automatic loading is to use__autoloadMagic method.When you reference a class that doesn’t exist,__autoloadWill be called, and your class name will be passed as a parameter. As for the specific logic of the function, it needs to be implemented by the user himself. Using this property, an automatic loading mechanism is created.
First create aautoload.phpTo do a simple test:

//When the class is not defined, the system automatically calls
function __autoload($class)
{
    /*Specific processing logic*/
    Echo $class; // simply output the undefined class name
}

new HelloWorld();

/**
 *Output HelloWorld and error message
 * Fatal error: Class 'HelloWorld' not found
 */


Through this simple example, we can find that in the process of class instantiation, the work done by the system is roughly as follows:

/*Simulation system instantiation process*/
function instance($class)
{
    //Returns an instance of a class if it exists
    if (class_exists($class, false)) {
        return new $class();
    }
    //Check whether autoload function is defined by user
    if (function_exists('__autoload')) {
        Wei Autoload ($class); // last introduced opportunity
    }
    //Check again if the class exists
    if (class_exists($class, false)) {
        return new $class();
    }Else {// system: I really have no idea
        throw new Exception('Class Not Found');
    }
}

got it__autoloadAfter the function works, let’s use it to implement automatic loading.

First, create a class file (it is recommended that the file name be the same as the class name). The code is as follows:

class [ClassName] 
{
    //Output the current class name when the object is instantiated
    function __construct()
    {
        echo '<h1>' . __CLASS__ . '</h1>';
    }
}

(I created aHelloWorldClass for demonstration) next we will define__autoloadTo enable automatic loading:

function __autoload($class)
{
    //Determine the file name according to the class name
    $file = $class . '.php';

    if (file_exists($file)) {
        Include $file; // import PHP file
    }
}

new HelloWorld();

/**
 *Output < H1 > HelloWorld</h1>
 */

It looks great, doesn’t it? Use this__autoloadYou can write a mechanism for automatically loading classes. But have you ever tried to write two in one file__autoloadYou don’t have to think about it. It’s wrong. In a large framework, you dare to guarantee that you have only one__autoload? Isn’t that too much trouble?

Take your time,spl_autoload_register()It’s time to play. But before we explain it, let’s talk about another important concept – namespace.

2.3 namespace

In fact, namespace is not a new thing. Many languages (such as C + +) already support this feature. But PHP started late, until PHP 5.3Later. Namespace is a kind of identity in short. Its main purpose is to solve the problem of naming conflict.
Just like in daily life, there are many people with the same name, how to distinguish these people? Then we need to add some extra signs. It seems good to regard the work unit as a sign, so you don’t have to worry about the embarrassment of “name bump”.

Here we have a small task to introduce Baidu’s CEO Robin Li:

Namespace Baidu;

Class Robin Li
{
    function __construct()
    {
        Echo 'founder of Baidu';
    }
}

This is Robin Li’s basic information.namespaceIt’s his unit logo,classIt’s his name. Namespace via keywordnamespaceTo declare. If a file contains a namespace, it must declare the namespace before all other code.

New Baidu Robin Li (); // restricted class name
New \ Baidu / Robin Li (); // fully qualified class name

In general, no matter to introduce to others"Baidu Robin Li"still"Baidu Inc Robin Li"They can understand. When the current namespace is not declared, the qualified class name and the fully qualified class name are equivalent. Because if you do not specify a space, the default is global ().

Namespace Google;

New Baidu / Robin Li (); // Google \ "Baidu \" Robin Li (actual results)
New \ "Baidu \" Robin Li (); // Baidu \ "Robin Li \" (actual results)

If you introduce Robin Li to their employees in the Google Corporation, you must point out that it is “Baidu Inc’s Robin Li”. Otherwise, he would think that Baidu is a department of Google, and Robin Li is just one of them. This example shows the difference between using a qualified class name and a fully qualified class name in a namespace.(fully qualified class name = current namespace + qualified class name)

/*Import namespace*/
Use Baidu Robin Li;
New Robin Li (); // Baidu Robin Li (actual results)

/*Set alias*/
Use Baidu Robin Li AS CEO;
New CEO (); // Baidu Robin Li (actual results)

/*In any case*/
New \ "Baidu \" Robin Li (); // Baidu \ "Robin Li \" (actual results)

The first situation is that others have already known Robin Li. You only need to directly say the name, and he will know who you are referring to. The second thing is that Robin Li is their CEO. You can say CEO directly, he can react immediately. Using namespace only makes the class name prefix, which is not easy to conflict, and the system will not automatically import.
If the file is not imported, the system will trigger before throwing the “class not found” error__autoloadFunction, passing in the qualified class name as an argument.
So the above examples are based on the fact that you have manually introduced related files, otherwise the system will throw “Class’ Baidu Robin Li ‘not found”.

2.4 spl_autoload_register

Next, let’s implement automatic loading with namespaces. Here we usespl_autoload_register() Function, which requires your PHP version number greater than 5.12.
spl_autoload_registerThe function is to register the incoming function (parameters can be in the form of callback function or function name) with theSPL __autoloadFunction queue and remove the system default__autoload()Function. Once calledspl_autoload_register()Function, when calling undefined classes, the system will call and register tospl_autoload_register()All functions of a function, not an automatic call __autoload()Function.

Now, let’s create aLinuxClass, which uses osAs its namespace (it is recommended that the file name be consistent with the class name):

Namespace OS; // namespace

Class Linux // class name
{
    function __construct()
    {
        echo '<h1>' . __CLASS__ . '</h1>';
    }
}

Next, create a PHP file in the same directory, and use thespl_autoload_registerAutomatic loading by function callback:

spl_autoload_register(function ($class) { // class = os\Linux

    /*Qualified class name path mapping*/
    $class_map = array(
        //Qualified class name = > file path
        'os\Linux' => './Linux.php',
    );

    /*Determine the file name according to the class name*/
    $file = $class_map[$class];

    /*Import related documents*/
    if (file_exists($file)) {
        include $file;
    }
});

new \os\Linux();

Here we use an array to save the relationship between the class name and the file path, so that when the class name is passed in, the autoloader will know which file to import to load the class.

However, once there are more files, the mapping array will become very long, so maintenance will be quite troublesome. If the naming can comply with the uniform convention, the autoloader can automatically parse and determine the path of the class file. What’s nextPSR-4It is a widely used way of agreement.

2.4 psr-4 specification

PSR-4This is a specification for automatically loading corresponding classes by file path. The specification specifies that a fully qualified class name needs to have the following structure:

\< top level namespace > (\ < child namespace >) * \ < class name >

If you continue to use the example above, the top-level namespace is equivalent to the company, the sub namespace is equivalent to the position, and the class name is equivalent to the person name. Then the Robin Li standard is called “Baidu Inc CEO Robin Li”.

There must be a top-level namespace in the psr-4 specification, which means to represent a special directory (file base directory). The sub namespace represents the path (relative path) of the class file relative to the file base directory, and the class name is consistent with the file name (note the case difference).

For example: in the fully qualified class name \app\view\news\IndexMedium, ifapprepresentativeC:\Baidu, the path of this class isC:\Baidu\view\news\Index.php

Let’s analyze\app\view\news\IndexFor example, write a simple Demo

$class = 'app\view\news\Index';

/*Top level namespace path mapping*/
$vendor_map = array(
    'app' => 'C:\Baidu',
);

/*Resolve class name as file path*/
$vendor = substr ($class, 0, strpos ($class, '\ \'); // fetch the top-level namespace [app]
$vendor_ dir = $vendor_ Map [$vendor]; // file base directory [C: \ Baidu]
$rel_ Path = dirname (substr ($class, strlen ($vendor)); // relative path [/ view / news]
$file_ Name = basename ($class). '. PHP'; // filename[ Index.php ]

/*Path of output file*/
echo $vendor_dir . $rel_path . DIRECTORY_SEPARATOR . $file_name;

Through thisDemoYou can see the process of converting a qualified class name to a path. Now let’s implement the autoloader in a standard object-oriented way.

First we create a file Index.php, it is in \app\mvc\view\homeIn the directory:

namespace app\mvc\view\home;

class Index
{
    function __construct()
    {
        echo '<h1> Welcome To Home </h1>';
    }
}

Next, we are creating a load class (no namespace is required), which is in the directory:

class Loader
{
    /*Path mapping*/
    public static $vendorMap = array(
        'app' => __DIR__ . DIRECTORY_SEPARATOR . 'app',
    );

    /**
     *Autoloader
     */
    public static function autoload($class)
    {
        $file = self::findFile($class);
        if (file_exists($file)) {
            self::includeFile($file);
        }
    }

    /**
     *Resolve file path
     */
    private static function findFile($class)
    {
        $vendor = substr ($class, 0, strpos ($class, '\ \'); // top level namespace
        $vendordir = self:: $vendormap [$vendor]; // file base directory
        $filepath = substr ($class, strlen ($vendor)). '. PHP'; // file relative path
        return strtr($vendorDir . $filePath, '\', DIRECTORY_ Separator); // file standard path
    }

    /**
     *Import file
     */
    private static function includeFile($file)
    {
        if (is_file($file)) {
            include $file;
        }
    }
}

Finally, theLoaderIn classautoloadRegister tospl_autoload_registerIn the function:

include ' Loader.php '; // introduce loader
spl_ autoload_ Register ('loader:: autoload '); // register to autoload

New \ app \ MVC \ view \ home \ index(); // instantiate unreferenced classes

/**
 *Output: < H1 > welcome to home</h1>
 */

2.4 composer

Having said so much, it’s time tocomposerIt’s on the stage. I’m not going to go into details about installation. Let’s take a lookvendor/composerDetails of

vendor
----autoload_classmap.php
----autoload_files.php
----autoload_namespace.php
----autoload_psr4.php
----autoload_real.php
----autoload_static.php
----ClassLoader.php
----install.json 
autoload.php

Let me see firstvendor/autoload.php

<?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer' . '/autoload_real.php';

return ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326::getLoader();


It implements the getloader method in an automatically generated class composerautoloader initff1d77c9141523097b07ee2acc23326.
We follow up to autoload_ real.php Up.

    public static function getLoader()
    {
        if (null !== self::$loader) {
            return autoload_real.phpself::$loader;
        }

        spl_autoload_register(array('ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        spl_autoload_unregister(array('ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326', 'loadClassLoader'));

        $map = require __DIR__ . '/autoload_namespaces.php';
        foreach ($map as $namespace => $path) {
            $loader->set($namespace, $path);
        }

        $map = require __DIR__ . '/autoload_psr4.php';
        foreach ($map as $namespace => $path) {
            $loader->setPsr4($namespace, $path);
        }

        $classMap = require __DIR__ . '/autoload_classmap.php';
        if ($classMap) {
            $loader->addClassMap($classMap);
        }

        $loader->register(true);

        $includeFiles = require __DIR__ . '/autoload_files.php';
        foreach ($includeFiles as $file) {
            composerRequireff1d77c91141523097b07ee2acc23326($file);
        }

        return $loader;
    }

It is clear that he willautoload_namespaces.phpautoload_psr4.phpautoload_classmap.phpautoload_files.phpSeveral configuration files are included and related processing is carried out(setPsr4), last registered(register)。
So let’s follow upregistermethod:

    public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
    }

This function is one line, but it’s simple and straightforward. It directly calls PHP’s ownspl_autoload_registerFunctions, registration processing__autoloadThat is to sayloadClassmethod. Follow uploadClassmethod:

    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            includeFile($file);

            return true;
        }
    }

From the function name, you can roughly know the flow: if there is$classThe corresponding one$file, then include.
Then go to findfile method:

    public function findFile($class)
    {
        // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
        if ('\' == $class[0]) {
            $class = substr($class, 1);
        }

        // class map lookup
        if (isset($this->classMap[$class])) {
            return $this->classMap[$class];
        }
        if ($this->classMapAuthoritative) {
            return false;
        }

        $file = $this->findFileWithExtension($class, '.php');

        // Search for Hack files if we are running on HHVM
        if ($file === null && defined('HHVM_VERSION')) {
            $file = $this->findFileWithExtension($class, '.hh');
        }

        if ($file === null) {
            // Remember that this class does not exist.
            return $this->classMap[$class] = false;
        }

        return $file;
    }

Find the file through the class name, and finally lock it in thefindFileWithExtensionMethod.
Follow upfindFileWithExtensionmethod:

    private function findFileWithExtension($class, $ext)
    {
        // PSR-4 lookup
        $logicalPathPsr4 = strtr($class, '\', DIRECTORY_SEPARATOR) . $ext;

        $first = $class[0];
        if (isset($this->prefixLengthsPsr4[$first])) {
            foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
                if (0 === strpos($class, $prefix)) {
                    foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-4 fallback dirs
        foreach ($this->fallbackDirsPsr4 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
                return $file;
            }
        }

        // PSR-0 lookup
        if (false !== $pos = strrpos($class, '\')) {
            // namespaced class name
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
        } else {
            // PEAR-like class name
            $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;
                        }
                    }
                }
            }
        }

        // PSR-0 fallback dirs
        foreach ($this->fallbackDirsPsr0 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                return $file;
            }
        }

        // PSR-0 include paths.
        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
            return $file;
        }
    }

The final implementation willNamespace \ classSuch a class name is converted toDirectory name / class name.phpSuch a path and return the full path.

I findcomposerOfautoloadAndphpSelf containedspl_autoload, there is a small difference when including files. That is,spl_autoloadWill find.incType, butcomposercan’t.

It can also be found that, although the name of the configuration file isautoload_psr4.php, but in factpsr0Autoloading of formats is also supported. The biggest difference between them ispsr0Use “? In “Instead of” “between directories.”.

So much for the above, it’s time to summarize. from__autoloadreachspl_autoload_registerAgaincomposerandpsr4method. What is the purpose of PHP’s official and community design? They’re there to solve itincludeInconvenient documents. One by oneincludeIt’s not convenient. I use it nowspl_autoload_registerDirect AutoincludeYes. But we can’t just write, we have rules, so we have thempsr4