摘要:中是如何實現(xiàn)代碼的自動加載的入口腳本的以下兩行代碼其中的作用注冊為自動加載函數(shù)。這個負責引入了一個類中的,隨后立即解除注冊。注冊中的為自動加載函數(shù),并利用配置文件即目錄下的文件對這個自動加載函數(shù)進行了初始化。
1.基本知識
Include與require 的作用:
當一個文件被包含時,其中所包含的代碼繼承了 include 所在行的變量范圍。從該處開始,調用文件在該行處可用的任何變量在被調用的文件中也都可用。不過所有在包含文件中定義的函數(shù)和類都具有全局作用域。
Include與require的區(qū)別:
未找到文件則 include 結構會發(fā)出一條警告;require 會發(fā)出一個致命錯誤。
如何實現(xiàn)類的自動加載:
bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
(__autoload() 已被棄用)
可以注冊任意數(shù)量的自動加載器
注意:自動加載不可用于 PHP 的 CLI 交互模式
自動加載規(guī)范
1.PSR-0:https://github.com/PizzaLiu/P...
2.PSR-4:https://github.com/PizzaLiu/P...
3.PEAR:全是以"_"作為分隔
在Yii2.0的運行過程中主要由以下兩個方法來實現(xiàn)代碼的自動加載:
1.path_to_your_project/vendor/composer/ClassLoader.php中的ClassLoader::loadClass()方法,這個方法是由composer提供的。
2.類yiiBaseYii的autoload()方法,這個方法是由Yii2框架提供的。
Yii2中是如何實現(xiàn)代碼的自動加載的?
入口腳本的以下兩行代碼:
require(__DIR__ . "/../vendor/autoload.php"); require(__DIR__ . "/../vendor/yiisoft/yii2/Yii.php");
1.注冊ComposerAutoloaderInit06ca19902d5e5679bb4a73b919aadb2a::loadClassLoader($class)為自動加載函數(shù)。這個loader負責引入了一個類:ClassLoader.php中的ComposerAutoloadClassLoader(),隨后立即解除注冊。
2.注冊vendor/composer/ClassLoader.php中的ClassLoader::loadClass($class)為自動加載函數(shù),并利用配置文件(即vendor/composer目錄下的autoload_*.php文件)對這個自動加載函數(shù)進行了初始化。這個函數(shù)實現(xiàn)了PSR-0,PSR-4,classmap等方式來自動加載。
3.Require “vendor/composer/autoload_static.php”中的$files(作為全局函數(shù)使用)
4.將2中的loader返回到入口腳本
注意:
1.正如前面所提到的ClassLoader::loadClass($class)這個方法是由composer提供的,而配置文件(即vendor/composer目錄下的autoload_*.php文件)則是在執(zhí)行composer命令update/install的時候生成的。更多關于composer自動加載的內容參考composer自動加載,深入學習composer自動加載機制
對vendor/composer/ClassLoader.php中的ClassLoader::loadClass($class)詳細分析:
1.該loader有4個配置文件 : autoload_namespaces.php,autoload_psr4.php,autoload_classmap.php,autoload_files.php(這4個文件都是由composer生成的),還有一個比較特殊的文件autoload_static(也是由composer生成的,主要是為了提高效率,相當于緩存)
2.autoload_namespaces.php:(對應的是一些符合PSR-0的目錄或文件)
return array( "HTMLPurifier" => array($vendorDir . "/ezyang/htmlpurifier/library"), "Diff" => array($vendorDir . "/phpspec/php-diff/lib"), );
如何處理上面的配置數(shù)組? 答:將數(shù)據(jù)配置到數(shù)組prefixesPsr0中
$map = require __DIR__ . "/autoload_namespaces.php"; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } ClassLoader::set() public function set($prefix, $paths) { if (!$prefix) { //若為空串,則設置一個目錄作為任何命名空間的備用目錄(相當于默認目錄) $this->fallbackDirsPsr0 = (array) $paths; } else { //prefixesPsr0數(shù)組,參考autoload_static.php文件 $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; } }
3.autoload_psr4.php:
a)包含的命名空間目錄:vendor/yiisoft下滿足psr-4的目錄(包括yii命名空間,即yii api 中包含的類)
如何處理上面的配置數(shù)組? 答 : 將數(shù)據(jù)配置到數(shù)組prefixLengthsPsr4,prefixDirsPsr4中
$map = require __DIR__ . "/autoload_namespaces.php"; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } public function setPsr4($prefix, $paths) { if (!$prefix) {//若為空串,則設置一個目錄作為任何命名空間的備用目錄(相當于默認目錄) $this->fallbackDirsPsr4 = (array) $paths; } else { $length = strlen($prefix); if ("" !== $prefix[$length - 1]) { throw new InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = (array) $paths; } }
4.autoload_classmap.php
a)命令"composer dump-autoload -o"會生成這么一個文件,通過classmap方式,可以提高自動加載的效率
(相比使用PSR-0或PSR-4自動加載,可以減少計算量和IO,后面會詳細分析)
如何處理上面的配置數(shù)組? 答:將數(shù)據(jù)配置到數(shù)組classMap中
$classMap = require __DIR__ . "/autoload_classmap.php"; if ($classMap) { $loader->addClassMap($classMap); } public function addClassMap(array $classMap) { if ($this->classMap) { $this->classMap = array_merge($this->classMap, $classMap); } else { $this->classMap = $classMap; } }
5.autoload_files.php
a)主要包括了需要立即require的文件( 通常是庫文件,也可以是自定義的,引入作為全局函數(shù)使用)
如何處理:直接require
6.autoload_static.php
當滿足以下條件:
1.PHP_VERSION_ID >= 50600
2.&& !defined("HHVM_VERSION")
3.&& (!function_exists("zend_loader_file_encoded") || !zend_loader_file_encoded());
則直接使用autoload_static文件而不采用上面的4個文件。
require_once __DIR__ . "/autoload_static.php"; call_user_func(ComposerAutoloadComposerStaticInit06ca19902d5e5679bb4a73b919aadb2a::getInitializer($loader)); //上面的getInitializer()具體做什么? //其實就是直接把已經生成好的prefixLengthsPsr4,prefixDirsPsr4,prefixesPsr0,classMap一一賦值給loader,而不是像上面提到的那樣一個一個配置 public static function getInitializer(ClassLoader $loader) { return Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInit06ca19902d5e5679bb4a73b919aadb2a::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit06ca19902d5e5679bb4a73b919aadb2a::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInit06ca19902d5e5679bb4a73b919aadb2a::$prefixesPsr0; $loader->classMap = ComposerStaticInit06ca19902d5e5679bb4a73b919aadb2a::$classMap; }, null, ClassLoader::class); }
上面關于Closure::bind()的使用參考http://www.cnblogs.com/iforev...
7.總結:(對應關系)
prefixLengthsPsr4 <=> autoload_psr4.php prefixDirsPsr4 <=> autoload_psr4.php prefixesPsr0 <=> autoload_namespaces.php(lib或src目錄,使用psr0) classMap <=> autoload_classmap
使用ComposerAutoloadClassLoader::loadClass()加載文件的順序:
1.先從classMap中找(時間復雜度O(1))
2.查看是否文件之前已經查找過,證實不存在($missingClasses)
3.如果有使用apc緩存的話從緩存中取
4.查找文件名后綴為”.php”的文件
- a)PSR-4 lookup:格式化類名,通過prefixLengthsPsr4,prefixDirsPsr4找到文件的絕對路徑 - b)PSR-4 fallback:根據(jù)$fallbackDirsPsr4查找根命名空間下的目錄 - c)PSR-0 lookup:分純pear格式,pear+命名空間格式,根據(jù)prefixesPsr0找到文件的絕對路徑 - d)PSR-0 lookup:根據(jù)fallbackDirsPsr0查找根命名空間下的目錄 - e)PSR-0 include:如果允許使用include path方式的話,使用stream_resolve_include_path()返回絕對路徑 - f)找不到,返回false
5.如果有使用HHVM的話,找后綴為”.hh”的文件,回到4下的具體查找(即a,b,c..)
6.如果有使用apc緩存的話,將找到的文件的絕對路徑存儲到apc緩存中
注意:
1.在ClassLoader::findFileWithExtension($class, $ext)中實現(xiàn)了PSR-0和PSR-4的自動加載,其時間復雜度均為O(n2),相比于classmap的方式而言(時間復雜度為O(1))是低效的,因此在生產環(huán)境中可以采用composer命令"composer dump-autoload -o"進行優(yōu)化。
1.定義類Yii(需要手動引入其父類的文件,而不是靠自動加載)
2.注冊Yii::autoload()為自動加載函數(shù)
3.賦值Yii::$classMap (其值即yii2 api 中介紹的所有類,對應文件vendoryiisoftyii2classes.php)
4.生成依賴注入容器:Yii::$container = new yiidiContainer();
相對于ComposerAutoloadClassLoader::loadClass(),Yii.php所做的就簡單明了許多了,如果所需加載的類在Yii::$classMap中有定義則直接通過它加載,沒有的話就解析別名,然后加載。如果解析別名后依然找不到相應的文件路徑,則使用composer提供的自動加載函數(shù)來加載(即ClassLoader::loadClass($class))
Yii::autoload()主要能引入什么類?
1.Yii::$classMap中定義的yii2核心類
2.引入我們的應用中自己創(chuàng)建的類(依靠命名空間和別名,遵循PSR-4)
最終注冊了的自動加載方法以及順序:
1.Yii::autoload()
2.vendorcomposerClassLoader.php中的ClassLoader::loadClass($class)
注意 :
1.通過上面的兩個方法都可以引入Yii2.0框架自身的所有類,在順序上會優(yōu)先使用Yii::autoload()(主要是利用了Yii::classMap)
2.Yii::autoload()是由Yii框架提供的,而ClassLoader->loadClass($class)是由composer提供的。
準備:通過composer require安裝yii上的第三方擴展。
例如
$ php composer.phar require kartik-v/yii2-markdown "dev-master"
1.安裝完成之后可以發(fā)現(xiàn)vendor/yiisoft/extensions.php文件中多了以下內容:
"kartik-v/yii2-markdown" => array ( "name" => "kartik-v/yii2-markdown", "version" => "9999999-dev", "alias" => array ( "@kartik/markdown" => $vendorDir . "/kartik-v/yii2-markdown", ),
2.在應用的啟動過程中,會執(zhí)行方法yiiaseApplication::bootstrap()
/** * Initializes extensions and executes bootstrap components. * This method is called by [[init()]] after the application has been fully configured. * If you override this method, make sure you also call the parent implementation. */ protected function bootstrap() { if ($this->extensions === null) { $file = Yii::getAlias("@vendor/yiisoft/extensions.php"); $this->extensions = is_file($file) ? include($file) : []; } foreach ($this->extensions as $extension) { if (!empty($extension["alias"])) { foreach ($extension["alias"] as $name => $path) { Yii::setAlias($name, $path); } } if (isset($extension["bootstrap"])) { $component = Yii::createObject($extension["bootstrap"]); if ($component instanceof BootstrapInterface) { Yii::trace("Bootstrap with " . get_class($component) . "::bootstrap()", __METHOD__); $component->bootstrap($this); } else { Yii::trace("Bootstrap with " . get_class($component), __METHOD__); } } } foreach ($this->bootstrap as $class) { $component = null; if (is_string($class)) { if ($this->has($class)) { $component = $this->get($class); } elseif ($this->hasModule($class)) { $component = $this->getModule($class); } elseif (strpos($class, "") === false) { throw new InvalidConfigException("Unknown bootstrapping component ID: $class"); } } if (!isset($component)) { $component = Yii::createObject($class); } if ($component instanceof BootstrapInterface) { Yii::trace("Bootstrap with " . get_class($component) . "::bootstrap()", __METHOD__); $component->bootstrap($this); } else { Yii::trace("Bootstrap with " . get_class($component), __METHOD__); } } }
根據(jù)上面的代碼可以知道:
yii2.0框架會根據(jù)"@vendor/yiisoft/extensions.php"為所有第三方擴展設置別名,同時這個別名與擴展代碼所在的目錄相對應。
3.在需要的時候使用Yii::autoload()加載
/** * Class autoload loader. * This method is invoked automatically when PHP sees an unknown class. * The method will attempt to include the class file according to the following procedure: * * 1. Search in [[classMap]]; * 2. If the class is namespaced (e.g. `yiiaseComponent`), it will attempt * to include the file associated with the corresponding path alias * (e.g. `@yii/base/Component.php`); * * This autoloader allows loading classes that follow the [PSR-4 standard](http://www.php-fig.org/psr/psr-4/) * and have its top-level namespace or sub-namespaces defined as path aliases. * * Example: When aliases `@yii` and `@yii/bootstrap` are defined, classes in the `yiiootstrap` namespace * will be loaded using the `@yii/bootstrap` alias which points to the directory where bootstrap extension * files are installed and all classes from other `yii` namespaces will be loaded from the yii framework directory. * * Also the [guide section on autoloading](guide:concept-autoloading). * * @param string $className the fully qualified class name without a leading backslash "" * @throws UnknownClassException if the class does not exist in the class file */ public static function autoload($className) { if (isset(static::$classMap[$className])) { $classFile = static::$classMap[$className]; if ($classFile[0] === "@") { $classFile = static::getAlias($classFile); } } elseif (strpos($className, "") !== false) { $classFile = static::getAlias("@" . str_replace("", "/", $className) . ".php", false); if ($classFile === false || !is_file($classFile)) { return; } } else { return; } include($classFile); if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) { throw new UnknownClassException("Unable to find "$className" in file: $classFile. Namespace missing?"); } }
4.總結:可以認為說第三方擴展的自動加載其實就是使用了別名解析和PSR-4
1.在index.php中加載 yii.php
2.Yii.php實際上是引入了文件 YiiBase.php
3.在YiiBase.php文件中有如下代碼:
spl_autoload_register(array("YiiBase","autoload"));
4.自動加載器YiiBase::autoload()
a)先從靜態(tài)變量$classMap中找(從代碼中看,這個貌似貌似沒有用到)
b)再從靜態(tài)變量$_coreClasses中找 (這個寫死在YiiBase文件中了,包含了所有api)
c)如果允許 include_path,則直接include
d)不允許 include_path,根據(jù)self::$_includePaths 逐一查找文件,找到的話就include
e)按照類psr-4的方式查找( 與 .)(注意:這里這里需要用到一些常見的別名)
/** * Class autoload loader. * This method is provided to be invoked within an __autoload() magic method. * @param string $className class name * @return boolean whether the class has been loaded successfully */ public static function autoload($className) { // use include so that the error PHP file may appear if(isset(self::$classMap[$className])) include(self::$classMap[$className]); elseif(isset(self::$_coreClasses[$className]))//這個數(shù)組是寫死在YiiBase中的,包含所有yii api include(YII_PATH.self::$_coreClasses[$className]); else { // include class file relying on include_path if(strpos($className,"")===false) // class without namespace { if(self::$enableIncludePath===false) { foreach(self::$_includePaths as $path) { $classFile=$path.DIRECTORY_SEPARATOR.$className.".php"; if(is_file($classFile)) { include($classFile); if(YII_DEBUG && basename(realpath($classFile))!==$className.".php") throw new CException(Yii::t("yii","Class name "{class}" does not match class file "{file}".", array( "{class}"=>$className, "{file}"=>$classFile, ))); break; } } } else include($className.".php"); } else // class name with namespace in PHP 5.3 { $namespace=str_replace("",".",ltrim($className,"")); if(($path=self::getPathOfAlias($namespace))!==false) include($path.".php"); else return false; } return class_exists($className,false) || interface_exists($className,false); } return true; }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/25572.html
摘要:框架的版本已經發(fā)布,版本最低要求是,將會在今年早些時候推出正式版。閱讀的代碼,能學到很多東西,代碼寫的很優(yōu)雅,用到了很多版本的新特性。 yii框架的v-2.0-alpha版本已經發(fā)布,PHP版本最低要求是PHP-5.4,將會在今年早些時候推出正式版。閱讀yii2的代碼,能學到很多東西,代碼寫的很優(yōu)雅,用到了很多php-5.4版本的新特性。 BaseYii這個類,在yii2框架中被稱作...
摘要:運行來安裝指定的擴展。這更便于用戶辨別是否是的擴展。當用戶運行安裝一個擴展時,文件會被自動更新使之包含新擴展的信息。上述代碼表明該擴展依賴于包。例如,上述的條目聲明將對應于別名。為達到這個目的,你應當在公開發(fā)布前做測試。 簡述 擴展是專門設計的在 Yii 應用中隨時可拿來使用的, 并可重發(fā)布的軟件包。 基礎 例如, yiisoft/yii2-debug 擴展在你的應用的每個頁面底部添加...
摘要:行為是如何注冊到組件的呢通過注冊行為之后,實際上是添加到了的屬性中那么行為中的屬性,就添加到了,中進行直接調用行為里面的方法的時候,實際上觸發(fā)了里面的魔術方法繼承鏈圖解 Yii2 框架Trace 準備 了解composer的autoload psr0 psr4 加載機制 了解spl_autoload_register 了解依賴注入的實現(xiàn)原理反射 了解常用魔術方法__set,__get...
摘要:在分析源碼的過程中主要借助了工具。運行應用分析在上面的構造函數(shù)執(zhí)行完后,開始運行應用。發(fā)送響應到終端用戶入口腳本接收應用主體傳來的退出狀態(tài)并完成請求的處理。 前言 本文主要分析Yii2應用的啟動、運行的過程,主要包括以下三部分:入口腳本、啟動應用、運行應用。在分析源碼的過程中主要借助了Xdebug工具。 入口腳本 文件位置:webindex.php //定義全局變量 defined(...
摘要:我們都知道,的緩存是支持依賴的,就是我們設置的緩存是否失效除了過期時間還決定于它所依賴的東東是否變化。用好依賴將大大提高我們使用緩存的效果,本節(jié)講解緩存的種依賴方式。現(xiàn)在我們仍然通過一個例子說明,假設我們的一個緩存同時依賴于兩個依賴。 我們都知道,yii2的緩存是支持依賴的,就是我們設置的緩存是否失效除了過期時間還決定于它所依賴的東東是否變化。 用好依賴將大大提高我們使用緩存的效果,本...
閱讀 1133·2023-04-26 00:12
閱讀 3271·2021-11-17 09:33
閱讀 1068·2021-09-04 16:45
閱讀 1194·2021-09-02 15:40
閱讀 2173·2019-08-30 15:56
閱讀 2963·2019-08-30 15:53
閱讀 3557·2019-08-30 11:23
閱讀 1935·2019-08-29 13:54