composer 在laravel中引入文件的过程(文章中有本身的猜测,不是很精炼,想找答案的请左转)

在一开始固然是laravel的入口文件server。php 里面引入了public下的index。php
仍是这样写吧 server.php->public/index.php->bootstarp/autoload.php->vender/autoload.php->composer/autoload_real.php我曹,到这里在正式开始,而后他进去直接用了autoload_real下面的getleader方法,因此一切才刚刚开始。。。。。。。。。。php

那么咱们来看一下这个getloader方法作了什么
public static function getLoader()linux

{
    if (null !== self::$loader) {
        return self::$loader;
    }

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

    $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
    if ($useStaticLoader) {
        require_once __DIR__ . '/autoload_static.php';
        call_user_func(\Composer\Autoload\ComposerStaticInit5e29b2c2d84bb23a17bbe59e2ef81d8f::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\ComposerStaticInit5e29b2c2d84bb23a17bbe59e2ef81d8f::$files;
    } else {
        $includeFiles = require __DIR__ . '/autoload_files.php';
    }
    foreach ($includeFiles as $fileIdentifier => $file) {
        composerRequire5e29b2c2d84bb23a17bbe59e2ef81d8f($fileIdentifier, $file);
    }
    // var_dump($loader);
    // exit;
    return $loader;
}

首先他进去就先判断一下$loader这个静态变量是否是空的也就是说是一个小的单例,而后开始了注册loadclassloader这个方法,我曹我刚开始就不知道这个注册是什么鬼东西,后来才知道这是在php5.xx后使用的东西,以前就是__autoload()这个魔术方法的进一步使用,也就是在你实例化一个不存在的类是会调用这个方法,我很奇怪为何他们不直接调用这个方法而是选择用注册的方式来实现,就算是用直接调用这里也是会在每一次加载进来是运行这个方法,我能想到的做用就是在抛出异常的是时候能够控制,不对是我想错了,实际是这样的,这里这个方法只是在autoload.php中调用,可能在整个框架上的其余地方也有调用,也就是说在别的地方你new这个类的时候他会跳转到loadclassloader这个方法,这个方法就是帮你include进来这个文件,固然了注册自己就是帮你实例化这个类,以后他就马上取消了注册,也就是说在这里只是为了new一下这个类,也就是说如今是没有加载laravel容器和命名空间和compoer本身的自动加载,这里你要new一个类就得直接require进来以后再实例化,也就是说强行使用了注册来实例化一个类,若是按照我以前的作法必定是直接require进来文件,而后就实例化了,也就是说这里在我感受就是强行使代码优雅,毕竟他注册完就注销了,这个注册也就使用这一次,因此不存在外部调用的状况,好咱们继续日后看;laravel

接下来出现了php版本的问题,因此laravel针对不一样的版本进行处理,而后我就看见了defined(HHVM_VERSION)尼玛这是什么,在网上看了以后,我整我的都蒙了,我以前的理解是php代码会被翻译成C代码,而后C代码大家懂得变成机器吗,而后cpu开始执行,如今出现了一个新的叫作字符码,还有一种叫作即时编译器的东西叫jit,我大致说一下我在网上看见的,字节码是一种专门让编译器高效执行的代码,在hhvm首次执行php代码的时候会将全部代码转成字节码而后jit在有请求时会把要运行的字节码放在内存中,而后编译成机器码,也就是说这样有利于性能的提高,再深就等之后有时间再看了;编程

好,平复心情,面对编程这个深不见底的大海,心里有些恐慌,咱们接着往下看;数组

在判断当前的php版本大于5.6x而且没有hhvm以后开始引入autoload_static文件,wait这里我发现if中用的是requier——once而else里使用的是requier,好我要看看这是怎么回事,我曹,我傻逼了,autoload——static是一个完整的类,而else中是几个数组,咱们假设else中也用requier——once来引入,我曹我感受这里,若是说requier——once用在一个类中是为了节省性能,那一个数组是否有必要这样,仍是说laravel框架的开发者怕出现以前或者以后再来引入这个类致使资源浪费,那若是一个数组被屡次引入会怎么样,我曹我有傻逼了,在else中引入的各个文件中return的是一个匿名的数组,也就是说他没有赋给某个变量,全部不存在什么冲突,好咱们再回到以前的流程上;闭包

if中引入文件以后他使用了,函数回调,我以前不理解函数回调有什么意义,若是说他使用静态方法时为了提高性能节约空间,那为何要使用回调这种方式,彻底可使用直接调用的方式,哎,在网上找了一大圈,发现多数人说的回调都是为了高内聚,低耦合,这里暂且一笔带过,其实我以前理解的回调的用途在与其第二个参数能够以数组的形式依次做为函数的参数,这样的确很方便。 composer

好了咱们接着来看他调用的部分,Closure个人天这是什么啊,这是怎么引入的,没有响应的命名空间啊,好吧我查了一下,这是一个php自带的类,
意思是在调用一个匿名函数的闭包,固然了这里使用了、Closure::bind()有三个参数第一个天然是一个闭包函数,第二个是对于一个对象的绑定下面有例子,第三个参数是这个闭包函数的做用域,我在手册中看到一个例子,看得我有点蒙,由于我不是很理解第三个参数的意义,好吧咱们先把第二个参数的例子拿出来看看框架

<?php
class A {
    private static $sfoo = 1;
    private $ifoo = 2;
}
$cl1 = static function() {
    return A::$sfoo;
};
$cl2 = function() {
    return $this->ifoo;
};

$bcl1 = Closure::bind($cl1, null, 'A');
$bcl2 = Closure::bind($cl2, new A(), 'A');
echo $bcl1(), "\n";
echo $bcl2(), "\n";
?>

就想是js中的$this同样,表示的是如今正在活动的那个对象,如今咱们来看第三个参数的例子函数

MetaTrait.php
<?php
trait MetaTrait
{
   
    private $methods = array();
 
    public function addMethod($methodName, $methodCallable)
    {
        if (!is_callable($methodCallable)) {
            throw new InvalidArgumentException('Second param must be callable');
        }
        $this->methods[$methodName] = Closure::bind($methodCallable, $this, get_class());
    }
 
    public function __call($methodName, array $args)
    {
        if (isset($this->methods[$methodName])) {
            return call_user_func_array($this->methods[$methodName], $args);
        }
 
        throw RunTimeException('There is no method with the given name to call');
    }
 
}
?>
test.php
<?php
require 'MetaTrait.php';
 
class HackThursday {
    use MetaTrait;
 
    private $dayOfWeek = 'Thursday';
 
}
 
$test = new HackThursday();
$test->addMethod('when', function () {
    return $this->dayOfWeek;
});
 
echo $test->when();

说实话这个例子我看了好久,我历来没想到colsure::bind这个方法能够这样用,如同咱们看见的,这个类如同一个工厂一个,你能够建立一个闭包函数,加入到类中,以后就能够直接调用,而且没有出如今你自己的类中,要用时只须要use那个类就行,可是目前来讲我不知道这种方式有什么好处,回到咱们的问题上,在这里的回调就用的颇有意义,由于他的第一个参数就是一个变量,这意味着每次可能不同,而且加上第二个参数的做用,使得代码很是的简洁,配合__call使用味道更佳哦,好咱们回到正题,那么第三个参数在这里有什么作用呢,我看了一下closure::bind的返回值是一个新的对象或者出错时返回false,那么也就是说在$method中存的是一个对象,我把$method打印了出来是这个样子,我不知道咱们建立的对用的闭包函数是否是这个methods,因而我作了实验来肯定第三个参数是什么性能

clipboard.png

因而我就作了这样的实验

trait MetaTrait
    {
       
        public $methods = array();
        public function addMethod($methodName, $methodCallable)
        {
            if (!is_callable($methodCallable)) {
                throw new InvalidArgumentException('Second param must be callable');
            }
            $this->methods[$methodName] = Closure::bind($methodCallable, $this, Fuckyou::class);
            print_r($this->methods);
        }
        public function __call($methodName, array $args)
        {
            if (isset($this->methods[$methodName])) {
                return call_user_func_array($this->methods[$methodName], $args);
            }
            throw RunTimeException('There is no method with the given name to call');
        }
    }
    // require 'MetaTrait.php';
    class HackThursday {
        use MetaTrait;
        private $dayOfWeek = 'Thursday';
        public function fuck(){
            echo 'fuck';
        }
    }
    class Fuckyou {
        use MetaTrait;
        private $fuck = 'asd';
    }
    $fuck = new Fuckyou();
    $fuck->addMethod('whenfuck',function (){
        // return 1+1;
        return $this->fuck;
    });
    $test = new HackThursday();
    $test->addMethod('when', function () {
        return $this->dayOfWeek;
         // return 1+1;
    });
    // $m = new HackThursday ();
    // var_dump($m->methods);
    echo $fuck->whenfuck();
    echo $test->when();
    ?>

这个时候输出

clipboard.png

上面fuck类的方法是正常输出了,可是下边的显示没有权限访问,那什么状况下会没有权限,我以前是一直没有理解做用域的意思,我试着把$dayOfWeak的修饰改为public就能够正常显示。固然protected也不行,在切换着3个修饰符的时候我发现了一个问题,就是在when这个closure这个对象下面的对应变量会变,protected就没有对应的类,[dayOfWeek:protected]=>Thursday,而后我试着把第二个参数改为null,发现
那也就是说closure这个类,能够规定绑定那个对象,和这个方法的做用域,也就是说我能够绑定一个类,可是他做用域在另一个类中?我曹,i'm give up ,咱们回到static文件再来看,我曹终于回来了,这种方式能够再也不static文件中再引入Classloader就给变量赋值,固然也不用吧static文件放在loader_real 文件中,这是一个架空的方法, 那这样作的好处有什么呢,这样能够不用再任何一个文件中引入这4个变量,节省资源,也符合高聚低偶的原则

public static function getInitializer(ClassLoader $loader)
    {
        return \Closure::bind(function () use ($loader) {
            $loader->prefixLengthsPsr4 = ComposerStaticInit5e29b2c2d84bb23a17bbe59e2ef81d8f::$prefixLengthsPsr4;
            $loader->prefixDirsPsr4 = ComposerStaticInit5e29b2c2d84bb23a17bbe59e2ef81d8f::$prefixDirsPsr4;
            $loader->prefixesPsr0 = ComposerStaticInit5e29b2c2d84bb23a17bbe59e2ef81d8f::$prefixesPsr0;
            $loader->classMap = ComposerStaticInit5e29b2c2d84bb23a17bbe59e2ef81d8f::$classMap;
  
        }, null, ClassLoader::class);


    }

个人天,一直到这里咱们解释了autoload_real48行代码,迎来了这个类的核心功能$loader->register(true);

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

这行代码,注册了loadClass这个方法,后面两个参数第2个表示是否抛出异常,第三个表示是否在队列之首

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

这里传过来一个想要调用的类名,而后$this->findFile($class)去找对应的路径,若是存在就引入,注意这里return了true也就是说没有return的话就会抛出异常

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 || isset($this->missingClasses[$class])) {
            return false;
        }
        $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 (false === $file) {
            // Remember that this class does not exist.
            $this->missingClasses[$class] = true;
        }
        return $file;
    }

咱们来一行一行看$class[0]?,这是什么$class不是一个数组啊我测试以后显示是要引入类的第一个字母,还有这种操做?
那他就是删除了要引入类名的第一,像是这样
call_user_func(ComposerAutoloadComposerStaticInit5e29b2c2d84bb23a17bbe59e2ef81d8f::getInitializer($loader));
而后在classmap里面找,$this->classMapAuthoritative这个变量没有被赋值啊,我翻译了一下是权威性的意思,没事咱们先看一下他判断后作了什么。。。。。
return 了false这里先不谈,多是出于别的需求会阻止引入

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;
        }
        return false;
    }
}

DIRECTORY_SEPARATOR这个变量是一个php自带的变量不须要声明或者定义就可使用,就是在win环境或者liunx环境中的文件分割符会根据不一样的环境显示
在win环境中显示的是linux环境就是/,PSR4抬头这里就是将进来的 $class中的\变成/而后在$this->prefixLengthsPsr4中找对应的命名空间和对应的length
而后在$this->prefixDirsPsr4在下找对应的命名空间的路径而后return这个路径,以后的psr0也是根据psr0的规则来处理命名空间,我猜他的第一个路径必定不能有下划线,在psr0中用下划线来表明文件分割符,综上这个方法会返回一个路径。

接着咱们继续往下看,判断没有对应的路径而且支持HHVM就会把后缀改为.hh而后在调用$this->findFileWithExtension这个方法,这个方法中laravel对于两种格式作了相应的调整,因此我在这里想说,能用一个方法解决的绝对不用一个半,这样才简介。

固然了以后就引入了,这篇文章就是这样,随后可能还会更新,整体来讲第一次写技术博客,文章有不少地方都是我本身的猜测,按理说这不该该出如今这里,可是我以为这样有利于我集中精神,好告终束,欢迎你们补充或支出错误

相关文章
相关标签/搜索