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對於兩種格式作了相應的調整,因此我在這裏想說,能用一個方法解決的絕對不用一個半,這樣才簡介。

固然了以後就引入了,這篇文章就是這樣,隨後可能還會更新,整體來講第一次寫技術博客,文章有不少地方都是我本身的猜測,按理說這不該該出如今這裏,可是我以爲這樣有利於我集中精神,好告終束,歡迎你們補充或支出錯誤

相關文章
相關標籤/搜索