Composer implements the principle of automatic loading

Introduction

The composer tool is generally used in the framework to manage dependencies. Among them, composer has an automatic class loading mechanism, which can load all class files in the library downloaded by composer. So how does composer's autoloading mechanism work?

The composer autoloading mechanism, taking the Laravel framework as an example:

  1. First introduced autoload.php in the entry file (/public/index.php)
require __DIR__.'/../vendor/autoload.php';
  1. Let's look at the content of autoload.php
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273::getLoader();
  1. Let's look at the content of autoload_real.php again
<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273
{
    private static $loader;

    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }
        spl_autoload_register(array('ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        spl_autoload_unregister(array('ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273', 'loadClassLoader'));

        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
        if ($useStaticLoader) {
            require_once __DIR__ . '/autoload_static.php';

            call_user_func(\Composer\Autoload\ComposerStaticInit1215780529014c2b50a6fca7ce889273::getInitializer($loader));
        } else {
            $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);

        if ($useStaticLoader) {
            $includeFiles = Composer\Autoload\ComposerStaticInit1215780529014c2b50a6fca7ce889273::$files;
        } else {
            $includeFiles = require __DIR__ . '/autoload_files.php';
        }
        foreach ($includeFiles as $fileIdentifier => $file) {
            composerRequire1215780529014c2b50a6fca7ce889273($fileIdentifier, $file);
        }

        return $loader;
    }
}

function composerRequire1215780529014c2b50a6fca7ce889273($fileIdentifier, $file)
{
    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
        require $file;

        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
    }
}

It can be seen that this section is the focus of composer's automatic loading. First, the ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273::getLoader() method is called in autoload.php, and getLoader() first determines whether the current \(loader is null, and if it is not null, it returns directly, otherwise it returns Initialize a ClassLoader class and assign it to the \) loader, then add the contents of the autoload_namespaces.php, autoload_psr4.php, autoload_classmap.php files to the corresponding array in $loader, and then register the loadClass function. All files shown in the path are included. When a new class is created, if the relevant class is not found, the loadClass function will be triggered. In loadClass(), findFile() is called to find the corresponding file. After the corresponding file is found The file will be returned, and then loadClass calls the includeFile() method to include the file, otherwise findFile returns false, which completes the automatic loading

  1. Let's take a look at findFile()
 public function findFile($class)
    {
        // class map lookup
        if (isset($this->classMap[$class])) {
            return $this->classMap[$class];
        }
        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
            return false;
        }
        if (null !== $this->apcuPrefix) {
            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
            if ($hit) {
                return $file;
            }
        }

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

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

        if (null !== $this->apcuPrefix) {
            apcu_add($this->apcuPrefix.$class, $file);
        }

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

        return $file;
    }

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

        $first = $class[0];
        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-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;
        }

        return false;
    }

The findFile() function first looks in the class Map. If it is not found, it will try to find it in the apcu cache. If it is still not found, it will call the findFileWithExtension() function to find it. If it is found, it will add the file to the apcu cache, if it can't find it, it will set a mark in the missingClasses array to indicate that the class can't be found

The findFileWithExtension() method finds the path information of the class file based on the information previously set by the \(loader->set(\) namespace, \(path) and \) loader->setPsr4($namespace, $path) methods

  1. The spl_autoload_register and spl_autoload_unregister functions are used in some places above
    1. spl_autoload_register function
      spl_autoload_register — register the given function as an implementation of __autoload,
      1. bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
      2. If prepend
        is true, spl_autoload_register() will add the function to the head of the queue, not the tail.
      3. If you have implemented the __autoload() function in your program, it must be explicitly registered in the __autoload() queue. Because the spl_autoload_register() function will replace the __autoload() function in Zend Engine with spl_autoload() or spl_autoload_call()
        Example:
      function __autoload($name)
      {
          require 'class/'.$name.'.php';
          echo '1';
      }
      function autoload_test($name)
      {
          echo '2';
      }
      spl_autoload_register('autoload_test');
      spl_autoload_register('__autoload');
      $ca=new Ca();
    2. spl_autoload_unregister function
      spl_autoload_unregister — unregisters a registered __autoload() function that will become invalid if the function queue is active and the queue becomes empty after the given function is unregistered. If the function is cancelled and the autoload function queue is invalid, it will not be automatically activated even if there is an __autoload function.
      1. bool spl_autoload_unregister ( mixed $autoload_function )

Due to my limited level, if you find something wrong in this article, please leave a message.
If you need to reprint, please indicate the source: http://www.cnblogs.com/zhuchenglin/p/8954364.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324976940&siteId=291194637