首页
留言
友情链接
壁纸
更多
关于
Search
1
dockerfile怎么执行shell脚本(利用tail -f /dev/null命令防止container启动后退出)
4,833 阅读
2
channel常见的异常总结
4,220 阅读
3
支付宝支付配置开发流程
1,373 阅读
4
HTTP 协议中的Content-Encoding
1,196 阅读
5
Laravel底层原理(二) —— 契约(Contracts)
918 阅读
PHP
composer
laravel
swoole
Docker
Linux
Go
随笔
mysql
nginx
Search
标签搜索
gopher
Docker
PHP
dockerfile
通道
go
defer
alipay
支付
git
phpstorm
IDEA
web安全
漏洞
socket
Royal
累计撰写
35
篇文章
累计收到
0
条评论
首页
栏目
PHP
composer
laravel
swoole
Docker
Linux
Go
随笔
mysql
nginx
页面
留言
友情链接
壁纸
关于
搜索到
4
篇与
laravel
的结果
2022-01-14
Laravel底层原理(四) —— 控制反转(IOC)
什么是 IoC 控制反转(Inversion of Control,缩写为 IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称 DI),还有一种方式叫 “依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。 — 维基百科 简单说来,就是一个类把自己的的控制权交给另外一个对象,类间的依赖由这个对象去解决。依赖注入属于依赖的显示申明,而依赖查找则是通过查找来解决依赖。Laravel 中的使用 注入一个类:App::bind('foo', function($app) { return new FooBar; });这个例子的意思是创建一个别名为 foo 的类,使用时实际实例化的是 FooBar。使用这个类的方法是:$value = App::make('foo');$value 实际上是 FooBar 对象。如果希望使用单例模式来实例化类,那么使用:App::singleton('foo', function() { return new FooBar; });这样的话每次实例化后的都是同一个对象。实现「控制反转」,有两种方式:Dependency Injection (DI) - 依赖注入Dependency Lookup - 依赖查找哪些方面的「控制」被「反转」了? 依赖对象的「获得」被反转了。Class A 中用到了 Class B 的对象 b,一般情况下,需要在 A 的代码中显式的 new 一个 B 的对象。采用依赖注入技术之后,A 的代码只需要定义一个私有的 B 对象,不需要直接 new 来获得这个对象,而是通过相关的 容器控制程序 来将 B 对象在外部 new 出来并注入到 A 类里的引用中。而具体获取的方法、对象被获取时的状态由 容器 来指定。假设我有一个 iPhone,我的 iPhone 依赖充电器才能充电。class iPhone { // 电量 private $power; // 充电 public function charge() { } }我还有个苹果原装充电器:class AppleCharger { public function charge() { return 100; } }在以前,iPhone 内部「控制」着只能用哪一款充电器:class iPhone { // 电量 private $power; // 充电,只能用原装的充电器 public function charge() { $charger = new AppleCharger; $this->power = $charger->charge(); } }// 充电 $iphone = new iPhone; $iphone->charge();使用依赖注入之后,我来决定给 iPhone 用哪一款充电器:class iPhone { private $power; private $charger; // 依赖注入充电器,╮(╯▽╰)╭哎算了只要是充电器就行 public function __construct(Charger $charger) { $this->charger = $charger; } // 充电 public function charge() { $this->power = $this->charger->charge(); } }interface Charger { public function charge(); }// Laravel 容器 use Illuminate\Container\Container; $container = Container::getInstance(); // 给它一个原装充电器: $container->bind(Charger::class, AppleCharger::class); // 或者给它其它充电器 $container->bind(Charger::class, OtherCharger::class); // 充电 $iphone = $container->make(iPhone::class); $iphone->charger();可见,使用依赖注入之后,控制权「反转」了,由外部来决定给它什么充电器 (依赖对象)。Laravel 管这个 容器控制程序 叫 Service Container (服务容器),它来控制着各种依赖的获取方法
2022年01月14日
474 阅读
0 评论
0 点赞
2022-01-13
Laravel底层原理(三) —— 依赖注入(DI)
概念依赖倒置原则 (Dependence Inversion Principle , 缩写为 DIP), 一种软件设计原则,是一个抽象的概念控制反转 (Inversion of Control , 缩写为 IoC),DIP 的一种具体实现方式.依赖注入 (Dependency injection , 缩写为 DI),IOC 的具体实现方式.依赖注入一词是由 Martin Fowler 提出的术语,它是将组件注入到应用程序中的一种行为。就像 Ward Cunningham 说的:依赖注入是敏捷架构中关键元素。什么是依赖 打个比方,就好比每个人都需要穿衣服才能进行正常的社会活动,这就是说是个人都需要依赖衣服才能正常社会活动。相应的,就是一个类需要另一个类才能完成工作,这就是依赖下面是person类依赖了衣服类class Person{ protected $clothes; public function __construct() { $this->clothes = new Clothes(); } }看上面的代码就知道 人依赖了衣服,什么是依赖注入呢,我们改造下上面的代码class Person { protected $clothes; public function __construct(Clothes $clothes) { $this->clothes = $clothes; } } $person = new Person(new Clothes()); 这里的Person 依赖注入了clothes类什么是控制反转(IOC)某人想开车,于是自己去造车,此时可以理解为控制正转,我想要车多大我就造多大,想要车是什么颜色就涂什么颜色.最后发现造车太难,于是选择去经销商买一辆车,可以理解为控制反转,经销商只提供蓝色的车,我就只能买蓝色的车,我被 经销商控制了,我依赖经销商给我注入车结论就是,造车难,买车容易,在写程序的时候使用依赖注入可以更好的解耦先看自己造车代码<?php class Car { //汽车可以跑,定义一个方法 run() public function run(){ return '滴滴滴,车开了'; } } class Person { private $car = null;//保存某人的车,暂时还没车 public function drive(){ $this->car = new Car();//要开车先造车,造了一辆车保存在某人的 $car 里 return $this->car->run();//调用车的 run() 方法 } } $xiaoming = new Person(); echo $xiaoming->drive();//输出 滴滴滴,车开了通过经销商依赖注入<?php class Car { //汽车可以跑,定义一个方法 run() public function run() { return '滴滴滴,车开了'; } } class Person { private $car = null;//保存某人的车,暂时还没车 //new 某人的时候,就给他注入一辆车,通过 $a 传入构造方法,并保存在 $car 里 public function __construct($a) { $this->car = $a; } public function drive() { return $this->car->run();//调用车的 run() 方法 } } $car = new Car();//买一辆车 $xiaoming = new Person($car);//new 小明的时候,把刚才买的车注入 echo $xiaoming->drive();//输出 滴滴滴,车开了万一 new 小明的时候,给他买了一架飞机,并注入,小明不会开飞机怎么办,通过类型限定解决这个问题class Person { . . . public function __construct(Car $a)// <----这里做类型限定,保证不会注入飞机,也不会注入火箭 { $this->car = $a; } . . .IOC容器 在上例中,我们是手动的去 new 一辆车并注入给某人,现在用一个容器统一的去管理这些需要注入的类。<?php class Car { //汽车可以跑,定义一个方法 run() public function run() { return '滴滴滴,车开了'; } } class Person { private $car = null;//保存某人的车,暂时还没车 //new 某人的时候,就给他注入一辆车,通过 $a 传入构造方法 public function __construct(Car $a)// <----这里做了类型限定 { $this->car = $a; } public function drive() { return $this->car->run();//调用车的 run() 方法 } } //写一个简单的 IoC 容器类 class Container{ private static $objArr = [];//定义一个静态的空数组 public static function set($flag, Callable $func){ self::$objArr[$flag] = $func;//存入键值对,键是一个字符串,作为标识符,值是一个匿名函数 } public static function get($flag){ $tmp = self::$objArr[$flag];//取出标识符对应的匿名函数,用$tmp临时保存一下 return $tmp();//在$tmp后名加上括号,表示执行这个函数,并返回 } } //下面这条语句执行完毕后,会在 $objArr 里存入一个键值对,键是 car ,值是这个匿名函数,该匿名函数返回的是创建 Car 对象的语句 Container::set('Car', function(){ return new Car(); }); //下面这条语句执行完毕后,会在 $objArr 里存入一个键值对,键是 person ,值是这个匿名函数,该匿名函数返回的是创建 Person 对象的语句 Container::set('Person', function(){ return new Person(Container::get('Car'));//直接去容器中取一辆车出来,并作为参数传给 Person 类的构造函数 }); $xiaomin = Container::get('Person');//直接去容器中取一个人出来,取名叫小明 echo $xiaomin->drive();//输出 滴滴滴,车开了IOC 是什么呢,看明白了依赖注入 (DI) 后就很容易理解了通过 DI 我们可以看到,一个类所需要的依赖类是由我们主动实例化后传入类中的。控制反转的意思就是说将依赖类的控制权交出去,由主动变被动。依赖注入的方式上面的例子都是通过构造方式的形式依赖注入进去,那还能通过其他方式么?Setter 方法注入#class UserProvider{ protected $connection; public function __construct(){ ... } public function setConnection( Connection $con ){ $this->connection = $con; } ...接口注入#interface ConnectionInjector{ public function injectConnection( Connection $con ); } class UserProvider implements ConnectionInjector{ protected $connection; public function __construct(){ ... } public function injectConnection( Connection $con ){ $this->connection = $con; } }所以,简单讲只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI) 。是不是豁然开朗?事实上,就是这么简单。
2022年01月13日
441 阅读
0 评论
0 点赞
2021-12-29
Laravel底层原理(二) —— 契约(Contracts)
概述 Contract,翻译过来叫契约、协议等。在 Laravel-china 的翻译中,是一个不翻词,这里也使用 Contract 来代替。Contract 就是接口 Interface,用来规范某些服务的功能结构的,在 Laravel 中称之为契约。以缓存操作为例,我们直接使用 Cache::get() 和 Cache::put() 即可完成缓存的获取和设置,语法很简单。此时问题来了:缓存的实现有很多种,例如文件缓存,Memcache 缓存,Redis 缓存等,要保证任何一种缓存的操作都具备 get 和 put 方法,如何保证?就是需要每种缓存服务的实现,都实现 Cache 契约,契约就是接口,就可以保证每种缓存的实现都具有一致的结构了。缓存服务实现与 Cache 契约结构说明 我们以 Cache 契约和一系列 Cache 服务实现为例,说一下这个结构:缓存的相关实现,都位于:Illuminate\Cache\ 下:Laravel8.77.1 预设了:Apc, Array, File, Database, Memcached, Null, Redis, Taggable 多种缓存的具体实现方案,以常用的 Redis 为例,Illuminate\Cache\RedisStore 就是 redis 缓存服务实现,查看其源代码:<?php namespace Illuminate\Cache; use Illuminate\Contracts\Cache\LockProvider; use Illuminate\Contracts\Redis\Factory as Redis; use Illuminate\Redis\Connections\PhpRedisConnection; class RedisStore extends TaggableStore implements LockProvider { }可见,该 Redis 缓存功能类,需要实现一个 Store 的接口,这个接口就是 Illuminate\Contracts\Cache\LockProvider ,在 Laravel 中称之为契约,看该 Cache 契约的实现:<?php namespace Illuminate\Contracts\Cache; interface LockProvider { /** * Get a lock instance. * * @param string $name * @param int $seconds * @param string|null $owner * @return \Illuminate\Contracts\Cache\Lock */ public function lock($name, $seconds = 0, $owner = null); /** * Restore a lock instance using the owner identifier. * * @param string $name * @param string $owner * @return \Illuminate\Contracts\Cache\Lock */ public function restoreLock($name, $owner); } 接口契约中定义了关于缓存应该具备的方法,这样在缓存操作时,无论文件,Redis 或者 Apc 缓存,都具有了统一的接口,不用担心使用上的语法差异了。说了这么多,其实就是操作上的抽象层,将需要的操作提取,保证所有驱动实现具有统一的结构。这就是接口的常规目的,Laravel 中叫成了契约而已,没有特殊功能。官方文档概要 在契约这篇官方文档中,主要说明契约的优势。内容如下:使用契约原因,可以使得代码低耦合,保证代码的简洁性。内置契约参考列表文档中,有一个章节是比较 Facade 和 Contract。放在一起比较的原因,应该是在 Laraval 中,通常每个 Contract 都有对应的 Facade。就像我们 Cache,就有 Cache 的 Facace 和 Cache 的 Contract。当使用 Cache 时,会导致我们确定不了当前时契约 Contract 还是门面 Facade。这就是放在一起比较的原因吧,语法类似。但除此之外,Contract 与 Facade 功能完全不同,作用也不同,其实没有什么可比性。Facade,简化服务的调用语法的功能。Contract,定义一组服务的通用操作接口。一组相关的服务,既需要通用的接口,也需要简化调用的操作。就是需要 Contract 也需要 Facade,两者作用完全不同,其实不用混淆,不用放在一起比较的!
2021年12月29日
918 阅读
0 评论
1 点赞
2021-12-25
Laravel底层原理(一) —— 门面(Facades)
什么是门面门面为应用服务容器中的绑定类提供了一个「静态」接口。Laravel 内置了很多门面,你可能在不知道的情况下正在使用它们。Laravel 的门面作为服务容器中底层类的「静态代理」,相比于传统静态方法,在维护时能够提供更加易于测试、更加灵活、简明优雅的语法。 Laravel 的所有门面都定义在 Illuminate\Support\Facades 命名空间下,所以我们可以轻松访问到门面。 在config/app.php文件中找到别名这一栏可以看到很多门面: 所有的 Laravel Facades 都定义在 vendor/laravel/framework/src/Illuminate/Support/Facades 目录下何时使用门面门面有诸多优点,其提供了简单、易记的语法,让我们无需记住长长的类名即可使用 Laravel 提供的功能特性,此外,由于他们对 PHP 动态方法的独到用法,使得它们很容易测试。但是,使用门面也有需要注意的地方,一个最主要的危险就是类范围蠕变。由于门面如此好用并且不需要注入,在单个类中使用过多门面,会让类很容易变得越来越大。使用依赖注入则会让此类问题缓解,因为一个巨大的构造函数会让我们很容易判断出类在变大。因此,使用门面的时候要尤其注意类的大小,以便控制其有限职责。注:构建与 Laravel 交互的第三方扩展包时,最好注入 Laravel契约(这个后面单独讲)而不是使用门面,因为扩展包在 Laravel 之外构建,你将不能访问 Laravel 的门面测试辅助函数。如何创建门面以下是在Laravel中创建Facade的步骤。步骤1 - 创建自定义PHP类文件。步骤2 - 创建服务提供者并将该类绑定到服务提供商。步骤3 - 将ServiceProvider注册到Config app.php作为提供者。步骤4 - 创建类,这个类扩展到luminate Support Facades Facade。步骤5 - 将点4配置为Config app.php作为别名。创建门面实例步骤1 - 创建文件及目录(\App\Units\Test\Test)步骤2 - 通过执行以下命令创建一个名为TestProvider的服务提供者。php artisan make:provider TestProvider 成功执行后,您将收到以下输出 -以及在app/Providers目录下生成TestProvider.php服务文件步骤3 - 注册服务提供者 config/app.php中的 providers。步骤4 - 创建一个门面类 在 App\Facade下步骤5 - 在config/app.php中的aliases步骤6 - 开始调用源码分析在 Laravel 应用中,门面就是一个为容器中对象提供访问方式的类。该机制原理由 Facade 类实现。Laravel 自带的门面,以及我们创建的自定义门面,都会继承自 Illuminate\Support\Facades\Facade 基类。门面类只需要实现一个方法: getFacadeAccessor 。正是 getFacadeAccessor 方法定义了从容器中解析什么,然后 Facade 基类使用魔术方法 __callStatic() 从你的门面中调用解析对象。\App\Facade\Test::test(); 实际上是调用了父类 Illuminate\Support\Facades\Facade 中的 __callStatic方法;//就是这个魔术方法 public static function __callStatic($method, $args) { //最后是根据子类的 getFacadeAccessor 方法中返回的字符串,从服务容器中解析出对应的服务组件类 $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } return $instance->$method(...$args); }getFacadeRoot()方法源码public static function getFacadeRoot() { //根据getFacadeAccessor方法返回的字符串,解析出服务容器中对应的类 return static::resolveFacadeInstance(static::getFacadeAccessor()); } protected static function resolveFacadeInstance($name) { //如果getFacadeAccessor方法中的是服务组件类,直接返回,不用解析了 if (is_object($name)) { return $name; } //判断是否解析过,要是解析过,就直接返回,否则从下面的服务容器中取 if (isset(static::$resolvedInstance[$name])) { return static::$resolvedInstance[$name]; } //从服务容器中取, //static::$app就是vendor/laravel/framework/src/Illuminate/Foundation/Application.php //为什么可以像从数组中取东西一样?因为Application类继承的vendor/laravel/framework/src/Illuminate/Container/Container.php 容器基类,实现了ArrayAccess接口,这个接口可以去百度查 if (static::$app) { return static::$resolvedInstance[$name] = static::$app[$name]; } } 这个$app在setFacadeApplication被赋值public static function setFacadeApplication($app) { static::$app = $app; }setFacadeApplication方法在 vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterFacades.php被调用public function bootstrap(Application $app) { Facade::clearResolvedInstances(); //在这里被调用 Facade::setFacadeApplication($app); AliasLoader::getInstance(array_merge( $app->make('config')->get('app.aliases', []), $app->make(PackageManifest::class)->aliases() ))->register(); }bootstrap方法又在 vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php 中调用//为HTTP请求引导应用程序。 public function bootstrap() { if (! $this->app->hasBeenBootstrapped()) { /** * $this->bootstrappers() 返回的是这个数组,此数组就在此类中 * * protected $bootstrappers = [ * \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, //env文件的加载引导类 * \Illuminate\Foundation\Bootstrap\LoadConfiguration::class, //配置文件的加载引导类 * \Illuminate\Foundation\Bootstrap\HandleExceptions::class, //异常处理类引导类 * \Illuminate\Foundation\Bootstrap\RegisterFacades::class, //注册门面引导类 * \Illuminate\Foundation\Bootstrap\RegisterProviders::class, //注册服务提供者引导类,路由服务就是在这里加载的 * \Illuminate\Foundation\Bootstrap\BootProviders::class, //启动服务提供者引导类 * ]; * */ //最终完成配置文件加载,环境配置,门面,服务提供者的注册及启动 $this->app->bootstrapWith($this->bootstrappers()); } }这个Kernel.php,被 app/Http/Kernel.php 类继承最终app/Http/Kernel.php类,在bootstrap/app.php中注册,在public/index.php中被调用======================================================================在 config/app.php 文件的 aliases 数组中,有门面类的别名配置在 vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterFacades.php 文件中解析到容器的
2021年12月25日
843 阅读
0 评论
0 点赞