首页
留言
友情链接
壁纸
更多
关于
Search
1
dockerfile怎么执行shell脚本(利用tail -f /dev/null命令防止container启动后退出)
4,942 阅读
2
channel常见的异常总结
4,259 阅读
3
支付宝支付配置开发流程
1,403 阅读
4
HTTP 协议中的Content-Encoding
1,251 阅读
5
Laravel底层原理(二) —— 契约(Contracts)
939 阅读
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
页面
留言
友情链接
壁纸
关于
搜索到
14
篇与
PHP
的结果
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日
457 阅读
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日
939 阅读
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日
882 阅读
0 评论
0 点赞
2021-12-06
PhpStorm设置注释模板
1. 设置函数注释模板 *设置位置:"File"->"Settings"->"Editor"->"File and Code Templates";选择PHP Function Doc Comment 粘贴如下模板代码:/** * Notes: * User: ${USER} * DateTime: ${DATE} ${TIME} ${PARAM_DOC} #if (${TYPE_HINT} != "void") * @return ${TYPE_HINT} #end ${THROWS_DOC} */生成注释,在函数上方输入“/**“,按”enter“键即可出现对应注释内容,如图2. 设置文档头部注释 选择PHP File Header Comment3. 设置类注释 选择PHP Class Doc
2021年12月06日
352 阅读
0 评论
0 点赞
2021-07-30
使用EasySwoole3.3.7搭建基础应用框架实现部分功能
1. 环境说明运行环境:LINUX,系统版本centos7PHP运行版本:7.4.21Swoole扩展版本:4.5.7Easyswoole版本:3.3.7mongodb版本:4.4.62. 实现功能说明实现入参过滤,入参限制等功能EasySwoole的控制器并没有提供类似中间件的说法,EasySwoole 3.4.x 版本之前:可在项目根目录的EasySwooleEvent.php 中看到onRequestonRequest ,所以在onRequestonRequest事件进行验证。第一步:在APP目录下生成Libraries自定义类文件夹,建立测试拦截类,防止sql注入,对一些关键词进行屏蔽;第二步:在Event中的OnRequest事件进行全局的请求参数过滤或者拦截;签名校验通过RSA加密方式生成一对公私钥,采用私钥加签公钥验签的方式对请求数据进行验签,签名原文生成机制,采用按字段首字母字典升序首尾拼接生成。第一步:创建自定义类RsaClient,包含生成公私钥,签名原文,验签等方法;第二步:一对公私钥配置在项目配置文件/config/rsa中第三步:在Event中的OnRequest事件对请求数据进行验签,验签通过后继续下一步请求;实现Redis和MySql连接池实现MYSQL和REDIS连接池可有效解决减少创建连接的时间,控制资源的使用,达到连接复用的目的。第一步:加载依赖包通过composer安装easyswoole/pool通用连接池组件,easyswoole/redis-pool 协程连接池组件,easyswoole/orm 安装orm组件;第二步:在配置文件dev中添加配置项包含MySQL连接信息,连接池最小和最大连接数,连接池对象最大闲置时间以及REDIS的连接信息,最大最小连接数等;第三步:在EasySwooleEvent.php文件的initialize方法中初始化连接池连接配置信息,日志记录异常;实现日志处理自定义类用ioc重写系统日志类,实现LoggerInterface接口。第一步:在自定义类中增加Log文件实现系统日志类接口;第二步:项目根目录创建文件 bootstrap.php,IOC覆盖系统日志类,注册自定义日志处理器;use App\Log\LogHandler; use EasySwoole\Component\Di; use EasySwoole\EasySwoole\SysConst; //IOC覆盖系统日志类 Di::getInstance()->set(SysConst::LOGGER_HANDLER, LogHandler::class); 实现elasticsearch协程客户端通过composer 安装easyswoole/elasticsearch组件实现mongodb客户端通过composer安装easyswoole/sync-invoker组件和mongodb/mongodb组件,定义驱动,客户端调用类定义,在 EasySwoole 全局事件 的 mainServerCreate 事件中进行服务注册。实现IP限流和IP白名单机制使用 Swoole\Table,存储用户访问情况,使用定时器,将前一周期的访问情况清空,统计下一周期。第一步:IP 访问统计类,实现了 初始化 Table、统计 IP访问次数、获取一个周期内次数超过一定值的记录;class IpList { use Singleton; /** @var Table */ protected $table; public function __construct() { TableManager::getInstance()->add('ipList', [ 'ip' => [ 'type' => Table::TYPE_STRING, 'size' => 16 ], 'count' => [ 'type' => Table::TYPE_INT, 'size' => 8 ], 'lastAccessTime' => [ 'type' => Table::TYPE_INT, 'size' => 8 ] ], 1024 * 128); $this->table = TableManager::getInstance()->get('ipList'); } public function access(string $ip): int { $key = substr(md5($ip), 8, 16); $info = $this->table->get($key); if ($info) { $this->table->set($key, [ 'lastAccessTime' => time(), 'count' => $info['count'] + 1, ]); return $info['count'] + 1; } else { $this->table->set($key, [ 'ip' => $ip, 'lastAccessTime' => time(), 'count' => 1, ]); return 1; } } public function clear() { foreach ($this->table as $key => $item) { $this->table->del($key); } } public function accessList($count = 10): array { $ret = []; foreach ($this->table as $key => $item) { if ($item['count'] >= $count) { $ret[] = $item; } } return $ret; } }第二步:封装完IP统计 的操作之后,我们就可以在 EasySwooleEvent.php 中的 mainServerCreate 回调事件中初始化 IpList 类和定时器,注册 IP 统计自定义进程第三步:在 EasySwooleEvent.php 中的 onRequestonRequest 事件中 接着我们在判断和统计IP的访问 3. 项目地址 https://gitee.com/sangshuaidong2020/easyswoole
2021年07月30日
693 阅读
0 评论
1 点赞
1
2
3