<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] # 說明 類自動加載已然是現代化框架必備的基礎設施,它讓我們只要設置好命名空間跟文件夾的對應關系,在使用到類的時候,就會自動去加載對應的類的文件。自動加載的核心是實現一個自動加載的方法,我們只要在該方法中添加命名空間到文件的映射規則,當到程序遇到「不認識」的類時,就會自動觸發該方法,自動去找到對應的類并加載之。 接下來,我們來分析框架的自動加載是如何實現的。 # 從入口文件出發 入口文件`public/index.php`開頭有: ``` require __DIR__ . '/../vendor/autoload.php'; ``` `autoload.php` 中的代碼: ``` require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInitxxx::getLoader(); ``` > 由于原類名較長,讓我們約定,類名后面有一長串 hash 字串的,都以‘xxx’代替,所以這里將類名標記為`ComposerAutoloaderInitxxx`。 第一行引入了 `autoload_real.php` 文件, 它里面定義了`ComposerAutoloaderInitxxx` 類,以及該類的若干靜態方法。我們從第二行語句展開分析。 # getLoader 方法代碼及分析 ``` public static function getLoader() { // 檢查$loaders是否有值,有則直接返回 // 相當于單例模式 if (null !== self::$loader) { return self::$loader; } /* |--------------------------------------------------------- | 將 `ComposerAutoloaderInitxxx` 類的`loadClassLoader`方法注冊為一個 | `__autoload`函數的實現,無法注冊成功則拋出錯誤,且添加到自動加載函數隊 | 列前面(即使用的類找不到時,自動調用`loadClassLoader`方法實現自動加載, | 具體實現見后面該方法分析) |--------------------------------------------------------- */ spl_autoload_register(array('ComposerAutoloaderInitxxx', 'loadClassLoader'), true, true); /* |--------------------------------------------------------- | 這里實例化一個ClassLoader類,并賦值到$loader成員。 | \Composer\Autoload\ClassLoader()按照字面的路徑是找不到該類的, | 所以會觸發`loadClassLoader`方法實現自動加載。 | `loadClassLoader`方法的代碼如下: | public static function loadClassLoader($class) | { | if ('Composer\Autoload\ClassLoader' === $class) { | require __DIR__ . '/ClassLoader.php'; | } | } | 所以這里成功將ClassLoader.php文件加載進來 |--------------------------------------------------------- */ self::$loader = $loader = new \Composer\Autoload\ClassLoader(); // 得到 $loader 之后去掉前面注冊的自動加載實現 spl_autoload_unregister(array('ComposerAutoloaderInitxxx', 'loadClassLoader')); // 靜態初始化只支持 PHP5.6 以上版本并且不支持 HHVM 虛擬機 $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); // 一般 $useStaticLoader == true if ($useStaticLoader) { // 加載 autoload\_static.php 文件 require_once __DIR__ . '/autoload_static.php'; // 調用上一步加載的文件中的類的 getInitializer 方法 // getInitializer 方法的分析見后面的(A)部分 call_user_func(\Composer\Autoload\ComposerStaticInitxxx::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); } } // register 方法將 classLoader 方法加入自動加載函數隊列 // 只要程序遇到不認識的類,就會使用該隊列中的函數去查找類對應的文件 // 最后將找到的文件 require 加載進來 // 查找不到會做一個標記,下次查找時就可以直接識別該類 // 的文件是找不到的,直接返回false。后面展開分析該函數,在(B)部分 $loader->register(true); // 加載全局函數(分靜態加載和非靜態加載,結果是一樣的) // 一般全局助手函數都在這里加載 // $files成員變量是一個數組,包含'文件標識(哈希值)=>文件路徑'的鍵值對 if ($useStaticLoader) { $includeFiles = Composer\Autoload\ComposerStaticInitxxx::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { // 注意到 composerRequirexxx 方法定義在本類的之外,封裝了require函數, // require進來的文件里面的變量,其作用域被包裹在`composerRequirexxx`中, // 防止require進來的文件含有$this或self而產生調用混淆或錯誤, // 而且該函數實現了require_once的效果,效率更高。分析見(C)部分。 composerRequirexxx($fileIdentifier, $file); } return $loader; } ``` ## (A)getInitializer 方法分析 ``` public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInitxxx::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInitxxx::$prefixDirsPsr4; $loader->fallbackDirsPsr0 = ComposerStaticInitxxx::$fallbackDirsPsr0; }, null, ClassLoader::class); } ``` 在PHP中,Closure類的摘要如下: ``` Closure { __construct ( void ) public static bind ( Closure $closure , object $newthis [, mixed $newscope = 'static' ] ) : Closure public bindTo ( object $newthis [, mixed $newscope = 'static' ] ) : Closure } ``` 其中,`bind`方法的做作用是:復制一個閉包,綁定指定的$this對象和類作用域。這里將一個閉包綁定到`ClassLoader`類,使得該類的私有成員變量可以被賦值,從而將`ComposerStaticInitxxx`類定義的有關空間命名映射的幾個變量(包括:prefixLengthsPsr4、prefixDirsPsr4、fallbackDirsPsr0)搬到`ClassLoader`類中。 該函數執行后得到的結果: ![](https://img.kancloud.cn/6a/11/6a115936a9e92e8e1cb2f831c7e1b9e9_522x279.png) `ClassLoader`的成員變量實現了初始化,即它們保存了各種形式的命名空間到文件夾路徑的映射。 ## (B) register 方法分析 ``` public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); } ``` 該方法將`loadClass`方法加入自動加載函數隊列,也就是當使用的類找不到時,觸發該方法去查找相應的類,注意到上面的第二個參數為`true`,說明是優先使用該方法作為自動加載的方法。那么,類的文件是如何被加載的,我們要到`loadClass`方法去尋找答案。`loadClass`方法代碼如下: ``` public function loadClass($class) { // 如果查找到文件 if ($file = $this->findFile($class)) { // 將文件加載進來 includeFile($file); return true; } } ``` 實際上,答案在 `findFile` 方法: ``` public function findFile($class) { // class map lookup // 如果classMap中有該類的文件映射,則直接返回對應的文件 if (isset($this->classMap[$class])) { return $this->classMap[$class]; } // 如果這個類已經被標為沒有授權或者找不到,則直接返回false if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { return false; } // 如果有APCU緩存文件 if (null !== $this->apcuPrefix) { $file = apcu_fetch($this->apcuPrefix.$class, $hit); if ($hit) { return $file; } } //使用psr4、psr0標準查找,**后面著重分析該方法** $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; } ``` ### findFileWithExtension 方法分析 ``` private function findFileWithExtension($class, $ext) { // PSR-4 lookup // 將‘\’轉為‘/’并加上后綴 // 以下分析,假設$class = app\Request // 即要查找app\Request類對應的文件 // 假設系統的DIRECTORY_SEPARATOR == ‘/’ // 則app\Request被轉為 app/Request.php $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; // 開頭為 a // prefixLengthsPsr4數組中,有'a' => [ 'app\' => 4] // 這時,該條件為true(php數組key不區分大小寫) // ( prefixLengthsPsr4將命名空間用首字母歸類,相當于建了一個索引, // 可以實現快速查找,如,這里如果沒有找到‘a’作為開頭的 // 就可以不用繼續查找,而是換別的查找方法。) if (isset($this->prefixLengthsPsr4[$first])) { $subPath = $class; // app\Request // 計算字符串中最后一個‘\’的位置,并賦值給$lastPos,并判斷是否存在‘\’ // 對于 app\Request,$lastPos = 3 while (false !== $lastPos = strrpos($subPath, '\\')) { // 從字符串開頭算起,取$lastPos個字符 // 這里得到$subPath=app' $subPath = substr($subPath, 0, $lastPos); // $search == 'app\' $search = $subPath . '\\'; // 查找prefixDirsPsr4數組對應key是否有值,其key-value值如下: /* 'app\' => [ [0] => your-project-dir\vendor\composer/../../app ] */ // 也就是說app\ 對應項目根目錄的app文件夾 if (isset($this->prefixDirsPsr4[$search])) { // $pathEnd == '\Request.php' $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); // 逐個檢查prefixDirsPsr4['app\']下的文件路徑是否包含需要的文件 foreach ($this->prefixDirsPsr4[$search] as $dir) { if (file_exists($file = $dir . $pathEnd)) { // \vendor\composer/../../app\Request.php // 也就是得到app目錄下的Request.php文件 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; } ``` 最后,如果能找到類對應的文件,則返回文件路徑,在`loadClass`方法中執行`includeFile($file)`將文件加載進來。 ## (C)composerRequirexxx 方法分析 在 `autoload_real.php` 文件中,有一個方法是定義在類的外部的,該方法代碼: ``` function composerRequirexxx($fileIdentifier, $file) { //文件標識為空才加載文件,實現了require_once的效果 if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { //`$file`里面的變量,其作用域被包裹在 `composerRequirexxx` // 避免$file里面的$this,self等變量穿透到外部 require $file; // 將文件標識為已加載過的 // 下次需要加載到該文件時,如果該文件已經加載過,就不用再加載 $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; } } ``` # 小結 自動加載所完成的工作有: * 實例化`ClassLoader`類,并初始化其成員變量 * 將`loadClass`方法加入自動加載函數隊列,且該方法實現了classMap,psr4,psr0等方式的文件路徑查找。當程序遇到不認識的類時,會調用該方法進行文件的加載 * 實現全局函數的加載 總的來說,自動加載一方面接管了我們手動寫一堆 require 或 include 的工作(想像一下,要require或include幾千個文件會是什么樣的情形),大大提高了開發效率和簡潔代碼;另一方面,自動加載是使用到了類的時候才去查找并加載類的文件,實現了按需加載,節約程序開銷,提高了程序的性能。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看