這篇文章主要介紹了關於Laravel服務容器(IocContainer)的解讀,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下
Laravel的核心是IocContainer, 文檔中稱其為“服務容器”,服務容器是一個用於管理類別依賴和執行依賴注入的強大工具,Laravel中的功能模組例如Route、Eloquent ORM、Request、Response等等等等,實際上都是與核心無關的類別模組提供的,這些類別從註冊到實例化,最終被我們所使用,其實都是laravel 的服務容器負責的。
如果對服務容器是什麼沒有清晰概念的話推薦一篇博文來了解一下服務容器的來龍去脈:laravel神奇的服務容器
服務容器中有兩個概念控制反轉( IOC)和依賴注入(DI):
依賴注入和控制反轉是對同一件事的不同描述,它們描述的角度不同。依賴注入是從應用程式的角度在描述,應用程式依賴容器來創建並注入它所需的外部資源。而控制反轉是從容器的角度在描述,容器控制應用程序,由容器反向的向應用程式註入應用程式所需的外部資源。
在Laravel中框架把自帶的各種服務綁定到服務容器,我們也可以綁定自訂服務到容器。當應用程式需要使用某一個服務時,服務容器會講服務解析出來同時自動解決服務之間的依賴然後交給應用程式使用。
本篇就來探討Laravel中的服務綁定和解析是如何實現的
#服務綁定
#常用的綁定服務到容器的方法有instance , bind, singleton, alias。下面我們分別來看一下。
instance
將一個已存在的物件綁定到服務容器裡,然後透過名稱解析該服務時,容器將總回這個綁定的實例。
$api = new HelpSpot\API(new HttpClient); $this->app->instance('HelpSpot\Api', $api);
會把物件註冊到服務容器的$instnces屬性裡
[ 'HelpSpot\Api' => $api//$api是API类的对象,这里简写了 ]
bind
綁定服務到服務容器
有三種綁定方式:
1.绑定自身 $this->app->bind('HelpSpot\API', null); 2.绑定闭包 $this->app->bind('HelpSpot\API', function () { return new HelpSpot\API(); });//闭包直接提供类实现方式 $this->app->bind('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });//闭包返回需要依赖注入的类 3. 绑定接口和实现 $this->app->bind('Illuminate\Tests\Container\IContainerContractStub', 'Illuminate\Tests\Container\ContainerImplementationStub');
針對第一種情況,其實在bind方法內部會在綁定服務之前透過getClosure()
為服務產生閉包,我們來看bind方法原始碼。
public function bind($abstract, $concrete = null, $shared = false) { $abstract = $this->normalize($abstract); $concrete = $this->normalize($concrete); //如果$abstract为数组类似['Illuminate/ServiceName' => 'service_alias'] //抽取别名"service_alias"并且注册到$aliases[]中 //注意:数组绑定别名的方式在5.4中被移除,别名绑定请使用下面的alias方法 if (is_array($abstract)) { list($abstract, $alias) = $this->extractAlias($abstract); $this->alias($abstract, $alias); } $this->dropStaleInstances($abstract); if (is_null($concrete)) { $concrete = $abstract; } //如果只提供$abstract,则在这里为其生成concrete闭包 if (! $concrete instanceof Closure) { $concrete = $this->getClosure($abstract, $concrete); } $this->bindings[$abstract] = compact('concrete', 'shared'); if ($this->resolved($abstract)) { $this->rebound($abstract); } } protected function getClosure($abstract, $concrete) { // $c 就是$container,即服务容器,会在回调时传递给这个变量 return function ($c, $parameters = []) use ($abstract, $concrete) { $method = ($abstract == $concrete) ? 'build' : 'make'; return $c->$method($concrete, $parameters); }; }
bind把服務註冊到服務容器的$bindings屬性裡類似這樣:
$bindings = [ 'HelpSpot\API' => [//闭包绑定 'concrete' => function ($app, $paramters = []) { return $app->build('HelpSpot\API'); }, 'shared' => false//如果是singleton绑定,这个值为true ] 'Illuminate\Tests\Container\IContainerContractStub' => [//接口实现绑定 'concrete' => 'Illuminate\Tests\Container\ContainerImplementationStub', 'shared' => false ] ]
singleton
public function singleton($abstract, $concrete = null) { $this->bind($abstract, $concrete, true); }
singleton 方法是bind方法的變種,綁定一個只需要解析一次的類別或介面到容器,然後接下來對於容器的呼叫該服務將會傳回同一個實例
alias
把服務和服務別名註冊到容器:
public function alias($abstract, $alias) { $this->aliases[$alias] = $this->normalize($abstract); }
alias 方法在上面講bind方法裡面有用到過,它會把把服務別名和服務類別的對應關係註冊到服務容器的$aliases屬性裡。
例如:
$this->app->alias('\Illuminate\ServiceName', 'service_alias');
綁定完服務後在使用時就可以透過
$this->app->make('service_alias');
將服務物件解析出來,這樣make的時候就不用寫那些比較長的類別名稱了,對make方法的使用體驗上有很大提升。
服務解析
make: 從服務容器中解析出服務對象,該方法接收你想要解析的類別名稱或介面名稱作為參數
/** * Resolve the given type from the container. * * @param string $abstract * @param array $parameters * @return mixed */ public function make($abstract, array $parameters = []) { //getAlias方法会假定$abstract是绑定的别名,从$aliases找到映射的真实类型名 //如果没有映射则$abstract即为真实类型名,将$abstract原样返回 $abstract = $this->getAlias($this->normalize($abstract)); // 如果服务是通过instance()方式绑定的,就直接解析返回绑定的service if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } // 获取$abstract接口对应的$concrete(接口的实现) $concrete = $this->getConcrete($abstract); if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete, $parameters); } else { //如果时接口实现这种绑定方式,通过接口拿到实现后需要再make一次才能 //满足isBuildable的条件 ($abstract === $concrete) $object = $this->make($concrete, $parameters); } foreach ($this->getExtenders($abstract) as $extender) { $object = $extender($object, $this); } //如果服务是以singleton方式注册进来的则,把构建好的服务对象放到$instances里, //避免下次使用时重新构建 if ($this->isShared($abstract)) { $this->instances[$abstract] = $object; } $this->fireResolvingCallbacks($abstract, $object); $this->resolved[$abstract] = true; return $object; } protected function getConcrete($abstract) { if (! is_null($concrete = $this->getContextualConcrete($abstract))) { return $concrete; } // 如果是$abstract之前没有注册类实现到服务容器里,则服务容器会认为$abstract本身就是接口的类实现 if (! isset($this->bindings[$abstract])) { return $abstract; } return $this->bindings[$abstract]['concrete']; } protected function isBuildable($concrete, $abstract) { return $concrete === $abstract || $concrete instanceof Closure; }
透過對make方法的梳理我們發現,build方法的功能是建構解析出來的服務的物件的,下面看一下建構物件的具體流程。 (建構過程中用到了PHP類別的反射來實現服務的依賴注入)
public function build($concrete, array $parameters = []) { // 如果是闭包直接执行闭包并返回(对应闭包绑定) if ($concrete instanceof Closure) { return $concrete($this, $parameters); } // 使用反射ReflectionClass来对实现类进行反向工程 $reflector = new ReflectionClass($concrete); // 如果不能实例化,这应该是接口或抽象类,再或者就是构造函数是private的 if (! $reflector->isInstantiable()) { if (! empty($this->buildStack)) { $previous = implode(', ', $this->buildStack); $message = "Target [$concrete] is not instantiable while building [$previous]."; } else { $message = "Target [$concrete] is not instantiable."; } throw new BindingResolutionException($message); } $this->buildStack[] = $concrete; // 获取构造函数 $constructor = $reflector->getConstructor(); // 如果构造函数是空,说明没有任何依赖,直接new返回 if (is_null($constructor)) { array_pop($this->buildStack); return new $concrete; } // 获取构造函数的依赖(形参),返回一组ReflectionParameter对象组成的数组表示每一个参数 $dependencies = $constructor->getParameters(); $parameters = $this->keyParametersByArgument( $dependencies, $parameters ); // 构建构造函数需要的依赖 $instances = $this->getDependencies( $dependencies, $parameters ); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); } //获取依赖 protected function getDependencies(array $parameters, array $primitives = []) { $dependencies = []; foreach ($parameters as $parameter) { $dependency = $parameter->getClass(); // 某一依赖值在$primitives中(即build方法的$parameters参数)已提供 // $parameter->name返回参数名 if (array_key_exists($parameter->name, $primitives)) { $dependencies[] = $primitives[$parameter->name]; } elseif (is_null($dependency)) { // 参数的ReflectionClass为null,说明是基本类型,如'int','string' $dependencies[] = $this->resolveNonClass($parameter); } else { // 参数是一个类的对象, 则用resolveClass去把对象解析出来 $dependencies[] = $this->resolveClass($parameter); } } return $dependencies; } //解析出依赖类的对象 protected function resolveClass(ReflectionParameter $parameter) { try { // $parameter->getClass()->name返回的是类名(参数在typehint里声明的类型) // 然后递归继续make(在make时发现依赖类还有其他依赖,那么会继续make依赖的依赖 // 直到所有依赖都被解决了build才结束) return $this->make($parameter->getClass()->name); } catch (BindingResolutionException $e) { if ($parameter->isOptional()) { return $parameter->getDefaultValue(); } throw $e; } }
服務容器就是laravel的核心, 它透過依賴注入很好的替我們解決物件之間的相互依賴關係,而又透過控制反轉讓外部來定義具體的行為(Route, Eloquent這些都是外部模組,它們自己定義了行為規範,這些類別從註冊到實例化給你使用才是服務容器負責的)。
一個類別要被容器所能夠提取,必須先註冊至這個容器。既然 laravel 稱這個容器叫做服務容器,那麼我們需要某個服務,就得先註冊、綁定這個服務到容器,那麼提供服務並綁定服務至容器的東西,就是服務提供者(ServiceProvider)。服務提供者主要分為兩個部分,register(註冊) 和 boot(引導、初始化)由於篇幅問題關於Laravel服務提供器的內容請看另一篇Laravel核心解讀--服務提供器(ServiceProvider)。
以上就是本文的全部內容,希望對大家的學習有所幫助,更多相關內容請關注PHP中文網!
相關推薦:
#以上是Laravel服務容器(IocContainer)的解讀的詳細內容。更多資訊請關注PHP中文網其他相關文章!

PHP用於構建動態網站,其核心功能包括:1.生成動態內容,通過與數據庫對接實時生成網頁;2.處理用戶交互和表單提交,驗證輸入並響應操作;3.管理會話和用戶認證,提供個性化體驗;4.優化性能和遵循最佳實踐,提升網站效率和安全性。

PHP在數據庫操作和服務器端邏輯處理中使用MySQLi和PDO擴展進行數據庫交互,並通過會話管理等功能處理服務器端邏輯。 1)使用MySQLi或PDO連接數據庫,執行SQL查詢。 2)通過會話管理等功能處理HTTP請求和用戶狀態。 3)使用事務確保數據庫操作的原子性。 4)防止SQL注入,使用異常處理和關閉連接來調試。 5)通過索引和緩存優化性能,編寫可讀性高的代碼並進行錯誤處理。

在PHP中使用預處理語句和PDO可以有效防範SQL注入攻擊。 1)使用PDO連接數據庫並設置錯誤模式。 2)通過prepare方法創建預處理語句,使用佔位符和execute方法傳遞數據。 3)處理查詢結果並確保代碼的安全性和性能。

PHP和Python各有優劣,選擇取決於項目需求和個人偏好。 1.PHP適合快速開發和維護大型Web應用。 2.Python在數據科學和機器學習領域佔據主導地位。

PHP在電子商務、內容管理系統和API開發中廣泛應用。 1)電子商務:用於購物車功能和支付處理。 2)內容管理系統:用於動態內容生成和用戶管理。 3)API開發:用於RESTfulAPI開發和API安全性。通過性能優化和最佳實踐,PHP應用的效率和可維護性得以提升。

PHP可以輕鬆創建互動網頁內容。 1)通過嵌入HTML動態生成內容,根據用戶輸入或數據庫數據實時展示。 2)處理表單提交並生成動態輸出,確保使用htmlspecialchars防XSS。 3)結合MySQL創建用戶註冊系統,使用password_hash和預處理語句增強安全性。掌握這些技巧將提升Web開發效率。

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

PHP仍然具有活力,其在現代編程領域中依然佔據重要地位。 1)PHP的簡單易學和強大社區支持使其在Web開發中廣泛應用;2)其靈活性和穩定性使其在處理Web表單、數據庫操作和文件處理等方面表現出色;3)PHP不斷進化和優化,適用於初學者和經驗豐富的開發者。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SublimeText3 Linux新版
SublimeText3 Linux最新版

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中