首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
185 阅读
2
php接口优化 使用curl_multi_init批量请求
144 阅读
3
《从菜鸟到大师之路 ElasticSearch 篇》
107 阅读
4
2024年备考系统架构设计师
104 阅读
5
PHP 文件I/O
92 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
786
篇文章
累计收到
33
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
560
篇与
的结果
2023-08-11
[PHP 技巧] 不要直接克隆对象,请使用深拷贝
[[PHP 技巧] 不要直接克隆对象,请使用深拷贝](https://blog.p2hp.com/archives/7499)声明:这篇文章的目的并不是让你停止使用 clone 关键字,而是让你作进一步理解它的用法,因为这部分的文档介绍不够清楚。众所周知,PHP 的 clone 关键字用于克隆对象的所有属性。其背后的原理在于默认创建一个与原有对象的属性值完全一样的新对象,此外,也可以通过类的 __clone() 方法来实现自定义行为。虽然该描述与我们的期望相符,但是,如果克隆的对象里面包含了对象类型的属性,可能会出现奇怪的结果。让我们看看具体的示例:final class Car { public $model; // 为了减少代码量,将其声明为公共方法 public function __construct(CarModel $model) { $this->model = $model; } }final class CarModel { public $name; public $year; public function __construct($name, $year) { $this->name = $name; $this->year = $year; }}这里有两个类: 汽车类 Car 以及它的模型类 CarModel。我们来看下创建并克隆 Car 类之后,改变其中的一个 CarModel 实例会发生什么:$bmwX1 = new Car(new CarModel('X1', 2015));$bmwX5 = clone $bmwX1;// 不同的对象,所以 id 不同 var_dump(spl_object_hash($bmwX1)); // "0000000037e353af0000558c268309ea" var_dump(spl_object_hash($bmwX5)); // "0000000037e353ac0000558c268309ea"// model 属性指向了同一个实例,这就意味着无论改变哪个另一个都会受到影响var_dump(spl_object_hash($bmwX1->model)); // "0000000037e353ad0000558c268309ea"var_dump(spl_object_hash($bmwX5->model)); // "0000000037e353ad0000558c268309ea"$bmwX5->model->name = 'X5';var_dump($bmwX1->model);// object(CarModel)#2 (2) { // ["name"]=> "X5"// ["year"]=> int(2015) // }var_dump($bmwX5->model);// object(CarModel)#2 (2) { // ["name"]=> "X5" // ["year"]=> int(2015) // }这应该不是你所期望的结果吧?我们来看看究竟发生了什么。当克隆对象的属性值为对象时,PHP 并不会去遍历并为其重新分配内存地址。幸运的是,我们可以通过 DeepCopy 扩展包来解决该问题。DeepCopy 扩展包通过递归的遍历并克隆对象属性来确保克隆的对象中所包含的全部实例都是新的。因此,每个实例都将拥有一个新的对象标识。工作原理如下:CloneDeepCopy让我们来看看改正原来的代码:use function DeepCopy\deep_copy;$bmwX1 = new Car(new CarModel('X1', 2015));$bmwX5 = deep_copy($bmwX1);var_dump(spl_object_hash($bmwX1->model)); // "000000006042b54c000000001a8ebc46"var_dump(spl_object_hash($bmwX5->model)); // "000000006042b543000000001a8ebc46"// 现在,bmwX1 和 bmwX5 对象的 model 属性将拥有不同的标识$bmwX5->model->name = 'X5';var_dump($bmwX1->model);// object(CarModel)#2 (2) {// ["name"]=> string(2) "X1"// ["year"]=> int(2015)// }var_dump($bmwX5->model);// object(CarModel)#13 (2) { // ["name"]=> string(2) "X5"// ["year"]=> int(2015) // }问题成功解决。希望通过这篇文章让你对 clone 关键字有更进一步的理解。
2023年08月11日
4 阅读
0 评论
0 点赞
2023-08-11
laravel万能路由( 自动路由、动态路由)实现方法分享
laravel万能路由( 自动路由、动态路由)实现方法分享有了万能路由就不用一条一条添加路由了,很方便。如果你要用资源控制器做Restful接口,那还是要写资源路由的,注意,资源路由一定要写在最上面。Route::resource('photos', 'PhotoController');//资源路由要写在上面。//万能路由Route::group(['middleware'=>['web']],function (){Route::any("/{module}/{controller}/{action}",function ($module,$class,$action){ $class = "App\\Http\\Controllers\$module\\".ucfirst(strtolower($class)).'Controller'; if(class_exists($class)) { $ctrl = \App::make($class); return \App::call([$ctrl, $action]); } return abort(404); })->where([ 'module'=>'[0-9a-zA-Z]+','class' => '[0-9a-zA-Z]+', 'action' => '[0-9a-zA-Z]+']);});在你的控制器方法中获取参数要用(Request $request)public function index(Request $request){ $name = $request->input('name'); echo $name; }友好url:如果你想拥有http://www.laravel65.com/Haha/photo/index/id/22/tag/php 这样的友好url请使用以下Route::resource('photos', 'PhotoController');//资源路由要写在上面。Route::get('/', function () {return view('welcome');});//万能路由Route::group(['middleware'=>['web']],function (){Route::any("/{search}",function ($search){ $urls=explode('/',$search); $module=$urls[0] ?? 'Index'; $className=$urls[1] ?? 'Index'; $action=$urls[2] ?? 'Index'; $class ="App\\Http\\Controllers\$module\\".ucfirst(strtolower($className)).'Controller'; $ctrl = \App::make($class); return \App::call([$ctrl, $action],[$search]); // } return abort(404); })->where('search', '.*');});同时在你控制器的方法中使用$search参数接收参数public function index(Request $request,$search){ var_dump($search); $name = $request->input('id'); echo 'index' .$name; }
2023年08月11日
10 阅读
0 评论
0 点赞
2023-08-11
一文详解 PHP 8 的新特性
一文详解 PHP 8 的新特性PHP 8 将于 2020 年 11 月 26 日发布。这是一个新的主要版本,这意味着它将引入一些突破性的更改,以及许多新功能和性能改进。现在 PHP 8 在功能冻结中,这意味着不能再添加任何新功能了。由于重大更改,您需要在代码中进行一些更改才能在 PHP 8 上运行。但是,如果您已经跟上最新版本,升级应该不会太难,因为大多数重大更改以前在 7.* 版本中已弃用。别担心,所有这些弃用都列在这篇文章中。除了突破性变化外,PHP 8 还带来了一系列不错的新功能,如 JIT 编译器、联合类型、attributes等。新特性联合类型rfc鉴于 PHP 的动态类型性质,在很多情况下,联合类型可能很有用。联合类型是两种或更多类型的集合,指示可以使用其中一种类型。public function foo(Foo|Bar $input): int|float;请注意,void永远不能是联合类型的一部分,因为它指示"没有任何返回值"。此外,可以使用|null编写nullable,也可以使用现有的?符号:public function foo(Foo|null $foo): void;public function bar(?Bar $bar): void;JIT rfcJIT (just in time:即时编译) 编译器承诺显著改进性能,尽管并不总是在 Web 请求的上下文中。我已经在真实的Web应用程序上做了我自己的基准测试,而且似乎JIT在这类PHP项目上并没有产生太大的影响。最重大的特性非 JIT 莫属。该特性的研发历时多年,熬过了 PHP 5 与 7 大版本系列(PHP 6 项目流产),直到去年 3 月份才最终通过投票确认将进入 PHP 8。JIT 是一种编译器策略,它将代码表述为一种中间状态,在运行时将其转换为依赖于体系结构的机器码,并即时执行。在 PHP 中,这意味着 JIT 将为 Zend VM 生成的指令视为中间表述,并以依赖于体系结构的机器码执行,也就是说托管代码的不再是 Zend VM,而是更为底层的 CPU。虽然自 PHP 7.0 以来,通过优化核心数据结构 HashTable、强化 Zend VM 中某些操作码与持续改进 OPCache 的 Optimizer 组件等具体措施,PHP 性能得到了显著提升,但是实际上这些优化似乎已经走到极限了。现在 JIT 从底层着手,被认为是目前提升 PHP 性能的最佳出路。关于引入 JIT 后的性能对比(以及 PHP 8 整体性能),可以参考一下 Phoronix 的基准测试(注:用的是 5 月底的源码构建版进行的测试)。下面是 PHP 开发团队提供的相关对比数据:如果您想详细了解 JIT 可以为 PHP 做什么,您可以阅读我在这里写的另一篇帖子。null安全操作符 rfc如果您熟悉null coalescing operator,您已经熟悉了它的缺点:它无法处理方法调用。相反,您需要中间检查,或者依赖于某些框架提供的可选帮助程序:$startDate = $booking->getStartDate();$dateAsString = $startDate ? $startDate->asDateTimeString() : null;通过添加 null安全运算符,我们现在可以在方法上具有空合并(null coalescing-like)一样的行为!$dateAsString = $booking->getStartDate()?->asDateTimeString();命名参数 rfc命名参数允许您通过指定值名称将值传递给函数,这样您就不必考虑它们的顺序,还可以跳过可选参数!function foo(string $a, string $b, ?string $c = null, ?string $d = null) { / … / }foo(b: 'value b', a: 'value a', d: 'value d',);您可以在此帖子中深入阅读它们。Attributes注解 rfcAttributes(在其他语言中通常称为注解:annotations)提供了一种向类添加元数据的方法,而无需分析文档块。关于快速查看,下面是 RFC 中attributes外观的示例:use App\Attributes\ExampleAttribute;@@ExampleAttributeclass Foo{@@ExampleAttribute public const FOO = 'foo'; @@ExampleAttribute public $x; @@ExampleAttribute public function foo(@@ExampleAttribute $bar) { }}@@Attributeclass ExampleAttribute{public $value; public function __construct($value) { $this->value = $value; }}请注意,此基本Attribute在原始 RFC 中称为PhpAttribute,但后来被另一个 RFC 更改。如果你想深入探讨attributes如何工作,以及如何建立自己的attributes;您可以在此博客上深入阅读attributes。另请注意,attribute语法仍然可以更改,它尚未决定。Match表达式 rfc你可以称它为switch表达式的大哥:match可以返回值,不需要break语句,可以组合条件,使用严格的类型比较,并且不执行任何类型的强制。如下所示:$result = match($input) {0 => "hello", '1', '2', '3' => "world",};您可以在此处详细阅读match表达式。Constructor property promotion(构造函数属性提升) rfc此 RFC 添加语法糖来创建值对象或数据传输对象。不用为类属性和构造函数指定它们,PHP 现在可以将它们合并为一个。代替如下代码:class Money {public Currency $currency; public int $amount; public function __construct( Currency $currency, int $amount, ) { $this->currency = $currency; $this->amount = $amount; }}你可以这样做:class Money {public function __construct( public Currency $currency, public int $amount, ) {}}关于property promotion,还有很多要讲的,你可以在这个帖子阅读。新的 static 返回类型 rfc虽然已经可以返回self,但static在 PHP 8 之前不是有效的返回类型。鉴于PHP的动态类型性质,这功能对许多开发人员都很有用。class Foo{public function test(): static { return new static(); }}新的 mixed 类型 rfc有些人可能称之为必要的邪恶:mixed类型导致许多人有复杂的感觉。不过, 有一个很好的论据可以提出: 一个缺少的类型在 Php 中可能意味着很多事情:函数不返回任何东西或返回null我们期待几种类型之一我们期待一个类型,不能在PHP中进行类型提示Because of the reasons above, it's a good thing the mixed type is added. mixed itself means one of these types:由于上述原因,添加mixed类型是件好事。mixed本身意味着以下类型之一:arrayboolcallableintfloatnullobjectresourcestring请注意,mixed也可以用作参数或属性类型,而不仅仅是作为返回类型。另请注意,由于mixed已包含null,因此不允许使其为空(nullable)。以下将触发错误:// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type.function bar(): ?mixed {}Throw表达式 rfc此 RFC 将throw 从语句更改为表达式,从而有可能在许多新位置引发异常:$triggerError = fn () => throw new MyError();$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');继承私有方法 rfc以前,PHP 用于对public、protected和private方法应用相同的继承检查。换句话说:private方法应遵循与protected方法和public方法相同的方法签名规则。这没有意义,因为子类无法访问私有方法。此 RFC 更改了该行为,以便不再对私有方法执行这些继承检查。此外,使用final private function也没有意义,这样做现在将触发警告:Warning: Private methods cannot be final as they are never overridden by other classesWeak maps rfc基于 PHP 7.4 中添加的weakrefs RFC,在 PHP 8 中添加了WeakMap实现。WeakMap保留对对象的引用,这些引用不会阻止这些对象被垃圾回收。以 ORM 为例,它们通常实现缓存,这些缓存保存对实体类的引用,以提高实体之间的关系性能。这些实体对象不能被垃圾回收,只要此缓存具有对它们的引用,即使缓存是唯一引用它们的对象。如果此缓存层使用弱引用和映射代替,PHP 将垃圾收集这些对象当再没有别的引用他们了。特别是在 ORM 的情况下,它可以管理请求中的数百个,如果不是数千个实体;weak maps可以提供更好、更资源友好的处理这些对象的方法。下面是weak maps的外观,来自 RFC 的示例:class Foo {private WeakMap $cache; public function getSomethingWithCaching(object $obj): object { return $this->cache[$obj] ??= $this->computeSomethingExpensive($obj); }}允许 ::class 在对象上 rfc一个小的,但有用的,新功能:现在可以使用::class在对象上,而不必使用get_class() 。它的工作方式与get_class()一样。$foo = new Foo();var_dump($foo::class);Non-capturing catches rfc每当你想捕获到一个异常在PHP 8之前,你必须将它存储到一个变量中,不管你是否使用该变量。用non-capturing catches,你可以省略变量,代替这样try {// Something goes wrong} catch (MySpecialException $exception) {Log::error("Something went wrong");}你可以这样做:try {// Something goes wrong} catch (MySpecialException) {Log::error("Something went wrong");}请注意,需要始终指定类型,不允许空的catch。如果要捕获所有异常和错误,可以使用"Throwable"作为捕获类型。参数列表中的尾随逗号 rfc在调用函数时,参数列表中仍然缺少尾随逗号支持。这现在允许在PHP 8中,这意味着您可以执行以下操作:public function(string $parameterA, int $parameterB, Foo $objectfoo,) {// …}旁注:在闭包的use列表中也支持尾随逗号,这是一个疏忽,现在通过单独的 RFC 添加。从interface创建 DateTime 对象您已经可以使用DateTime::createFromImmutable($immutableDateTime) 创建DateTime 对象从DateTimeImmutable对象,但另一方面却很棘手。通过添加DateTime::createFromInterface()和DatetimeImmutable::createFromInterface() 现在有一种通用的方式将DateTime 和DateTimeImmutable 互转换。DateTime::createFromInterface(DateTimeInterface $other);DateTimeImmutable::createFromInterface(DateTimeInterface $other);新的 Stringable interface rfcStringable接口可用于任何字符串的类型提示或实现__toString()。此外,每当类实现__toString(),它会自动实现幕后的接口,并且无需手动实现它。class Foo{public function __toString(): string { return 'foo'; }}function bar(Stringable $stringable) { / … / }bar(new Foo());bar('abc');新的 str_contains() 函数 rfc有些人可能会说它早就该有了,但我们终于不必再依赖strpos() 来知道字符串是否包含另一个字符串了。代替如下:if (strpos('string with lots of words', 'words') !== false) { / … / }你可以这样做if (str_contains('string with lots of words', 'words')) { / … / }新的 str_starts_with() 和 str_ends_with() 函数 rfc另外两个早就应该有了,这两个函数现在添加到核心中。str_starts_with('haystack', 'hay'); // truestr_ends_with('haystack', 'stack'); // true新的 fdiv() 函数 pr新的fdiv() 函数执行类似于fmod()和intdiv() 函数,允许除以0。而不是错误地得到INF, -INF 或 NAN,具体取决于情况。新的 get_debug_type() 函数 rfcget_debug_type()返回变量的类型。听起来像是gettype()会做的吗?get_debug_type()返回数组、字符串、匿名类和对象的更有用的输出。例如,在类\Foo\Bar上调用gettype()将返回object。使用get_debug_type() 将返回类名。可以在 RFC 中找到get_debug_type()和gettype()之间的差异的完整列表。新的 get_resource_id() 函数 pr资源是 PHP 中的特殊变量,指的是外部资源。一个示例是 MySQL 连接,另一个是文件句柄。这些资源中的每个资源都会被分配一个 ID,尽管以前知道 ID 的唯一方法就是将资源强制转换到int:$resourceId = (int) $resource;PHP 8 添加了get_resource_id() 函数,使此操作更加明显且类型安全:$resourceId = get_resource_id($resource);traits 中的抽象方法改进 rfcTraits 可以指定抽象方法,这些方法必须由使用它们的类实现。但有一个警告:在PHP8之前,这些方法实现的签名没有经过验证。以下有效:trait Test {abstract public function test(int $input): int;}class UsesTrait{use Test; public function test($input) { return $input; }}PHP 8 将在使用trait并实现其抽象方法时执行正确的方法签名验证。这意味着您需要如下写法:class UsesTrait{use Test; public function test(int $input): int { return $input; }}对象实现的 token_get_all() rfctoken_get_all()函数返回数组值。此 RFC 添加了一个俱有PhpToken::getAll()方法的PhpToken类。此实现适用于对象而不是纯值。它消耗的内存更少,更易于阅读。变量语法调整 rfc在统一变量语法RFC解决了一些在PHP的变量语法的不一致。该RFC旨在解决少数被忽视的情况。内部函数的类型注解 externals很多人投入,以添加适当的类型注释到所有内部函数。这是一个长期的问题,最终可以解决与以前版本中对PHP所做的所有更改。这意味着内部函数和方法在反射中将具有完整的类型信息。ext-json 扩展始终可用 rfc以前,在没有启用 JSON 扩展的情况下编译 PHP 是可能的,现在这不再可能了。由于 JSON 被广泛使用,因此最好开发人员始终可以依赖它的存在,而不必首先确保扩展存在。重大更改如前所述:这是一个主要版本更新,因此会有重大的变化。最好的做法是查看升级文档中的中断更改的完整列表。不过,这些重大更改中有许多在以前的 7.* 版本中已被弃用,因此,如果您多年来一直保持最新状态,升级到 PHP 8 应该不会那么困难。一致的类型错误 rfcPHP 中的用户定义的函数将引发TypeError,但内部函数没有,它们宁愿发出警告并返回null。自PHP 8起,内部函数的行为已经保持一致。重新分类的引擎警告 rfc以前只触发警告或通知的很多错误已转换为正确的错误。以下警告已更改。未定义的变量:Error异常而不是通知未定义的数组索引:警告而不是通知按零划分:除零错误而不是警告而划分尝试增加/递增非对象的"%s"属性:错误异常而不是警告尝试修改非对象的属性"%s":错误异常而不是警告尝试分配非对象的属性"%s":错误异常而不是警告从空值创建默认对象:错误异常而不是警告尝试获取非对象的属性"%s":警告而不是通知未定义属性: %s::$%s:警告而不是通知无法将元素添加到数组,因为下一个元素已被占用:错误异常而不是警告无法在非数组变量中取消设置偏移量:错误异常而不是警告不能将标量值用作数组:错误异常而不是警告只能解包数组和可遍历:类型错误异常而不是警告为每例提供无效参数():类型错误异常而不是警告非法偏移类型:类型错误异常而不是警告非法偏移类型设置或空:类型错误异常而不是警告未设置的非法偏移类型:类型错误异常而不是警告数组到字符串转换:警告而不是通知资源 ID#%d 用作偏移量,强制转换为整数 (%d):警告而不是通知出现字符串偏移转换:警告而不是通知未初始化字符串偏移量:%d:警告而不是通知无法将空字符串分配给字符串偏移量:错误异常而不是警告提供的资源不是有效的流资源:TypeError 异常而不是警告@ 操作符不再抑制致命错误此更改可能会揭示 PHP 8 之前再次隐藏的错误。请确保在生产环境设置display_errors=Off!默认的错误报告级别现在E_ALL代替一切, 除了E_NOTICE 和 E_DEPRECATED。这意味着许多错误可能会弹出,这些错误以前被静默忽略,尽管在PHP 8之前可能已经存在。默认PDO的错误模式 rfc从 RFC:PDO 的当前默认错误模式为silent。这意味着,当发生 SQL 错误时,除非开发人员实现自己的显式错误处理,否则不会发出任何错误或警告,也未引发异常。此 RFC 更改默认错误将更改为PDO::ERRMODE_EXCEPTION 在PHP 8 中。串联优先级 rfc虽然在 PHP 7.4 中已弃用,但此更改现已生效。如果您要写类似的东西:echo "sum: " . $a + $b;PHP 以前会这样解释:echo ("sum: " . $a) + $b;PHP 8 将使其被解释为:echo "sum: " . ($a + $b);对算术和位运算符进行更严格的类型检查 rfc在 PHP 8 之前,可以对数组、资源或对象应用算术或位运算符。这不再可能了,并且会引发TypeError:[] % [42];$object + 4;命名空间名称成为一个单个token rfcPHP 用于将命名空间的每个部分(用反斜杠\分隔 )解释为令牌序列。此 RFC 改变了该行为,这意味着保留名称现在可以在命名空间中使用更合理的数字字符串 rfcPHP 的类型系统尝试在遇到字符串中的数字时执行许多智能操作。这种 RFC 使这种行为更加一致和清晰。更理智的字符串与数字比较 rfc此 RFC 修复了 PHP 中非常奇怪的情况,其中0 == "foo"结果为true。还有其他一些边缘情况,此 RFC 修复它们。反射方法签名更改反射类的三个方法签名已更改:ReflectionClass::newInstance($args);ReflectionFunction::invoke($args);ReflectionMethod::invoke($object, $args);现在已成为:ReflectionClass::newInstance(...$args);ReflectionFunction::invoke(...$args);ReflectionMethod::invoke($object, ...$args);升级指南指定,如果扩展这些类,并且仍希望同时支持 PHP 7 和 PHP 8,则允许以下签名:ReflectionClass::newInstance($arg = null, ...$args);ReflectionFunction::invoke($arg = null, ...$args);ReflectionMethod::invoke($object, $arg = null, ...$args);稳定排序 rfc在PHP 8之前,排序算法是不稳定的。这意味着无法保证等值元素的顺序。PHP 8 将所有排序函数的行为更改为稳定的排序。不兼容的方法签名的致命错误 rfc从 RFC:由于不兼容的方法签名导致的继承错误当前引发致命错误或警告,具体取决于错误的原因和继承层次结构。其它弃用和更改在 PHP 7.* 开发期间,添加了几个弃用,这些弃用现在在 PHP 8 中完成。Deprecations in PHP 7.2Deprecations in PHP 7.3Deprecations in PHP 7.4Locale-independent float to string cast附件:The match operator in PHP 8Named arguments in PHP 8Property promotion in PHP 8The latest PHP version — 如何管理现代 PHP 版本Attributes in PHP 8 — 仔细查看attributes,也称为注释2020年的PHPPHP7.4新特性PHP 7.3新特性PHP中文站
2023年08月11日
10 阅读
0 评论
0 点赞
2023-08-11
Laravel 登录失败次数限制
Laravel 登录失败次数限制在用户身份验证的情况下,Laravel 具有内置的身份验证系统。我们可以根据要求轻松修改它。身份验证中包含的功能之一是Throttling.为什么我们需要throttling保护?基本上,throttling是用来保护暴力攻击的。它将在一定时间内检查登录尝试。在短登录中,throttling会计算用户或机器人尝试失败的登录尝试次数。使用自定义登录实现限制默认情况下,在内置身份验证控制器中实现限制。但是,如果我们需要实现它到自定义登录呢?实现自定义登录限制非常容易。首先,我们必须将ThrottlesLogins trait包含到您的控制器中。use Illuminate\Foundation\Auth\ThrottlesLogins;现在,将此ThrottlesLogins trait 加到控制器中。namespace App\Http\Controllers;use Illuminate\Http\Request;use Illuminate\Foundation\Auth\ThrottlesLogins;class AuthController extends Controller{use ThrottlesLogins; ......现在转到用于对用户进行身份验证的方法。在我的例子中,我使用了 login() POST 方法。并粘贴以下代码:public function login(Request $request){// Authenticate Inputs $request->validate([ 'username' => 'required', 'password' => 'required|min:6|max:18' ]); // If the class is using the ThrottlesLogins trait, we can automatically throttle // the login attempts for this application. We'll key this by the username and // the IP address of the client making these requests into this application. if (method_exists($this, 'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) { $this->fireLockoutEvent($request); return $this->sendLockoutResponse($request); } .......首先,我们验证了用户提交的输入,然后实现了hasTooManyLoginAttempts() 方法。此方法将检查用户在某个时间是否执行过一定数量的失败尝试,然后系统将通过sendLockoutResponse() 方法阻止该用户。现在,我们必须通过incrementLoginAttempts()方法指示对ThrottlesLogins trait的失败登录尝试。if( Auth::attempt(['username' => $username, 'password' => $password]) ){// Redirect to appropriate dashboard }else {// If the login attempt was unsuccessful we will increment the number of attempts // to login and redirect the user back to the login form. Of course, when this // user surpasses their maximum number of attempts they will get locked out. $this->incrementLoginAttempts($request); return redirect()->back() ->withInput($request->all()) ->withErrors(['error' => 'Please check your username / password.']);}您还可以通过$maxAttempts和$decayMinutes属性更改允许的最大尝试次数和限制的分钟数。在这里,您可以找到完整的代码。<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;use Illuminate\Foundation\Auth\ThrottlesLogins;class AuthController extends Controller{use ThrottlesLogins; /** * The maximum number of attempts to allow. * * @return int */ protected $maxAttempts = 5; /** * The number of minutes to throttle for. * * @return int */ protected $decayMinutes = 1; public function login(Request $request) { // Authenticate Inputs $request->validate([ 'username' => 'required', 'password' => 'required|min:6|max:18' ]); // If the class is using the ThrottlesLogins trait, we can automatically throttle // the login attempts for this application. We'll key this by the username and // the IP address of the client making these requests into this application. if (method_exists($this, 'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) { $this->fireLockoutEvent($request); return $this->sendLockoutResponse($request); } $username = $request->username; $password = $request->password; if( Auth::attempt(['username' => $username, 'password' => $password]) ){ // Redirect to appropriate dashboard } else { // If the login attempt was unsuccessful we will increment the number of attempts // to login and redirect the user back to the login form. Of course, when this // user surpasses their maximum number of attempts they will get locked out. $this->incrementLoginAttempts($request); return redirect()->back() ->withInput($request->all()) ->withErrors(['error' => 'Please check your username / password.']); } }}Related Posts:
2023年08月11日
15 阅读
0 评论
0 点赞
2023-08-11
一文详解PHP的内存泄露
一文详解PHP的内存泄露FPM 的黑魔法首先,传统的跑在 FPM 下的 PHP 代码是没有“内存泄漏”一说的,所谓的内存泄漏就是忘记释放内存,导致进程占用的物理内存(附1)持续增长,得益于 PHP 的短生命周期,PHP 内核有一个关键函数叫做php_request_shutdown此函数会在请求结束后,把请求期间申请的所有内存都释放掉,这从根本上杜绝了内存泄漏,极大的提高了 PHPer 的开发效率,同时也会导致性能的下降,例如单例对象,没必要每次请求都重新申请释放这个单例对象的内存。(这也是Swoole等cli方案的优势之一,因为 cli 请求结束不会清理内存)。Cli 下的内存泄漏相信 PHPer 都遇见过这个报错Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 12288 bytes),是由于向 PHP 申请的内存达到了上限导致的,在 FPM 下一定是因为这次 web 请求有大内存块申请,例如 Sql 查询返回一个超大结果集,但在 Cli 下报这个错大概率是因为你的 PHP 代码出现了内存泄漏。常见的泄漏姿势有:向类的静态属性中追加数据,例如://不停的调用foo() 内存就会一直涨function foo(){ ClassA::$pro[] = "the big string";}向 $GLOBAL 全局变量中追加数据,例如://不停的调用foo() 内存就会一直涨function foo(){ $GLOBAL['arr'][] = "the big string";}向函数的静态变量中追加数据,例如://不停的调用foo() 内存就会一直涨function foo(){ static $arr = []; $arr[] = "the big string";}我们需要检测工具有的同学可能会说很简单嘛,把追加的变量在请求结束后unset()掉就可以了。但真实场景远没有你想的那么简单:例一:function foo(){$obj = new ClassA(); //foo函数结束后将自动释放 $obj对象 $obj->pro[] = str_repeat("big string", 1024);}while (1) {foo(); sleep(1);}上述代码 Cli 运行起来会泄漏吗?肉眼来看肯定不会泄漏,因为foo()函数结束后$obj是栈上的对象自动释放掉了,但答案是可能泄漏也可能没泄漏,这取决于ClassA的定义:class classA{public $pro; public function __construct() { $this->pro = &$GLOBALS['arr']; //pro是其他变量的引用 }}如果ClassA的定义是上面的样子,那么这个例子就是泄漏的!!例二:class Test{public $pro = null; function run() { $var = "Im global var now";//此处 $var 是长生命周期。 $http = new \Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE); $http->on("request", function($req, $resp) { //此处没有给类的静态属性赋值,没有给全局变量赋值, //也没有给函数的静态变量赋值,但是这里是泄漏的,因为 $this 变成长生命周期了。 $this->pro[] = str_repeat("big string", 1024); $resp->end("hello world"); }); $http->start(); echo "run done\n"; //输出不了 //这个函数永远不会结束,局部变量也变成了"全局变量" } }(new Test())->run();new Test()的本意虽然是创建一个临时的对象,但是run()方法触发了server->start()方法,代码将不向下执行,run()函数结束不了,run()函数的局部变量$var和临时对象本身都可以视为全局变量了,给其追加数据都是泄漏的!!例三:由于php_request_shutdown的存在,很多 PHP 扩展其实是有内存泄漏的(emalloc 后没有 efree),但是在 FPM 下是可以正常运行的,而这些扩展放到 Cli 下就会有内存泄漏问题,如果没有工具,Cli 下遇到扩展的泄漏问题,那也只能 gg 了-.-!还有就是当我们调用第三方的类库的函数,要传一个参数,这个参数是全局变量,我不知道这个第三方库会不会给这个参数追加数据,一旦追加数据就会产生泄漏,同理别人给我的函数传的参数我也不敢赋值,第三方函数的返回值有没有全局变量我也不知道。综上我们需要一个检测工具,相对于其他语言 PHP 在这个领域是空白的,可以说没有这个工具整个 Cli 生态就无法真正的发展起来,因为复杂的项目都会遇到泄漏问题。Swoole Tracker可以检测泄漏问题,但它是一款商业产品,现在我们决定重构这个工具,把内存泄漏检测的功能(下文简称Leak工具)完全免费给 PHP 社区使用,完善 PHP 生态,回馈社区,下面我将概述它的具体用法和工作原理。Swoole Tracker 用法Leak工具的实现原理是直接拦截系统底层的 emalloc,erealloc,以及 efree 调用,记录一个巨大的指针表,emalloc/erealloc 的时候添加,efree 的时候删除表中的记录,如果请求结束,指针表中仍然有值就证明产生了内存泄漏,不仅能发现 PHP 代码的泄漏,扩展层甚至 PHP 语言层面的泄漏都能发现,从根本上杜绝泄漏问题。使用方式很简单:前往官网下载最新的 tracker(3.0+) 扩展。php.ini 加入以下配置:extension=swoole_tracker.so;总开关apm.enable=1;Leak检测开关apm.enable_malloc_hook=1在 Cli 模式下主业务逻辑一定是可以抽象成循环体函数的,例如Swoole的OnReceive函数,workerman 的OnMessage函数,以及上文例一中的foo()函数, 在循环体主函数(下文简称主函数)最开始加上trackerHookMalloc()调用即可:function foo(){trackerHookMalloc(); //标记主函数,开始hook malloc $obj = new ClassA(); $obj->pro[] = str_repeat("big string", 1024);}while (1) {foo(); sleep(1);}每次调用主函数结束后(第一次调用不会被记录),都会生成一个泄漏的信息到/tmp/trackerleak日志里面。查看泄漏结果在 Cli 命令行调用trackerAnalyzeLeak()函数即可分析泄漏日志,生成泄漏报告,可以直接php -r "trackerAnalyzeLeak();"即可。下面是泄漏报告的格式:没有内存泄漏的情况:[16916 (Loop 5)] ✅ Nice!! No Leak Were Detected In This Loop其中16916表示进程 id,Loop 5表示第 5 次调用主函数生成的泄漏信息有确定的内存泄漏:[24265 (Loop 8)] /Users/guoxinhua/tests/mem_leak/http_server.php:125 => [12928][24265 (Loop 8)] /Users/guoxinhua/tests/mem_leak/http_server.php:129 => [12928][24265 (Loop 8)] ❌ This Loop TotalLeak: [25216]表示第 8 次调用http_server.php的 125 行和 129 行,分别泄漏了 12928 字节内存,总共泄漏了 25216 字节内存。通过调用trackerCleanLeak()可以清除泄漏日志,重新开始。技术特性(技术难点)支持持续增长检测:想象一个场景,第一次请求运行主函数的时候申请 10 字节内存,然后请求结束前释放掉,然后第二次请求申请了 100 字节,请求结束再释放掉,虽然每次都能正确的释放内存但是每次又都申请更多的内存,最终导致内存爆掉,Leak工具支持这种检测,如果某一行代码有N次(默认 5 次)这种行为就会报"可疑的内存泄漏",格式如下:The Possible Leak As Malloc Size Keep Growth:/Users/guoxinhua/tests/mem_leak/hook_malloc_incri.php:39 => Growth Times : [8]; Growth Size : [2304]表示 39 行有 8 次 malloc size 的增长,总共增长了 2304 字节。支持跨 loop 分析://Swoole Http Server的OnRequest回调$http->on("request", function($request, $response) {trackerHookMalloc(); if(isset(classA::$leak['tmp'])){ unset(classA::$leak['tmp']);//每一次loop都释放上一次loop申请的内存}classA::$leak['tmp'] = str_repeat("big string", 1024);//申请内存 并在本次loop结束后不释放 $response->end("hello world");});按照正常的检测泄漏的理论,上述代码每次都会检测出泄漏,因为每次都给classA::$leak['tmp']赋值并在 Loop 结束也没有释放,但实际业务代码经常这样写,并且此代码也是不会产生泄漏的,因为本次 Loop 的泄漏会在下次释放掉,Leak工具会跨相邻 2 个Loop 进行分析,自动对冲上面这种情况的泄漏信息,如果是跨多个 Loop 的释放,会以如下格式输出:[28316 (Loop 2)] /Users/guoxinhua/tests/mem_leak/hook_efree_pre_loop.php:37 => [-12288]Free Pre (Loop 0) : /Users/guoxinhua/tests/mem_leak/hook_efree_pre_loop.php:42 => [12288][28316 (Loop 2)] /Users/guoxinhua/tests/mem_leak/hook_efree_pre_loop.php:42 => [12288][28316 (Loop 2)] ✅ Nice!! No Leak Were Detected In This Loop上述信息表示 Loop 2 释放了 Loop 0 的 12288 字节内存,然后 Loop 2 又申请了 12288 字节内存,总体来说本次 Loop 跑下来没有内存泄漏。支持循环引用情况:首先简单的介绍一下循环引用问题:function foo(){$o = new classA(); $o->pro[] = $o; //foo结束后 $o无法释放,因为自己引用了自己,即循环引用}while (1) {foo(); sleep(1);}因为循环引用,上面的代码每次运行foo()内存都会增长,但是这个代码确实没有内存泄漏的,因为增长到一定程度 PHP 会开启同步垃圾回收,把这种循环引用的内存都释放掉。但是这给Leak工具带来了麻烦,因为$o的变量是延迟释放的,foo()结束后会报泄漏,而这种写法又确实不是泄漏。Swoole Tracker的Leak工具会自动识别上面的情况,会马上释放循环引用的内存,不会造成误报。如果你发现你的进程内存一直涨,开启了 Tracker 的泄漏检测,通过memory_get_usage(false);打印发现内存不涨了,那么证明你的应用存在循环引用,并且本来就没有内存泄漏问题。支持子协程统计:function loop(){ trackerHookMalloc(); classA::$leak[] = str_repeat("big string", 1024);//申请内存go(function() { echo co::getcid() . "child\n"; go(function() { echo co::getcid()."child2\n"; classA::$leak = [];//释放内存 }); });}Co\run(function(){while (1) { loop(); sleep(1); }});上述代码申请的内存会在第二个子协程里面释放,Leak工具会自动识别协程环境,会在所有子协程都结束后才统计汇总,所以上述代码不会有误报情况。支持 defer,context:$http->on("request", function($request, $response) {trackerHookMalloc(); $context = Co::getContext(); $context['data'] = str_repeat("big string", 1024);//context会在协程结束自动释放 classA::$leak[] = str_repeat("big string1", 1024); defer(function() { classA::$leak = [];//注册defer释放内存 }); $response->end("hello world");});Leak工具会自动识别协程环境,如果存在 defer 和 context,会在 defer 执行结束和 context 释放之后再统计汇总,所以上述代码不会有误报情况,当然如果上面没有注册 defer 也会正确的报告泄漏信息。支持旁路函数干扰排除:例如一个进程由主函数响应请求(OnRequest 等),然后还有个定时器在运行(旁路函数),我们希望检测的是主循环函数的泄漏情况,而当主循环函数执行到一半的时候定时器函数执行了,并申请了内存,然后又切回到主循环函数,此时会误报,Leak工具会支持识别出旁路函数然后不收集旁路函数的 malloc 数据。除了上述这些,Leak工具还支持internd string抓取等等,在此不再展开。注意前几次 Loop 的泄漏信息不用管,因为大部分项目都有一些初始化的缓存是不释放的。检测期间尽量不要有并发。由于开启泄漏检测后性能会非常差,不要在 php.ini 中开启apm.enable_malloc_hook = 1压测。和 Swoole Tracker2.x 的检查泄漏原理不一样,不能一起用。一个进程只能有一个地方调用trackerHookMalloc()函数。Swoole4.5.3由于底层 api 有问题,Leak工具无法正常工作,请升级到最新版Swoole或者降级Swoole版本。附件:免费公开课--如何正确查看进程内存占用
2023年08月11日
13 阅读
0 评论
0 点赞
1
...
50
51
52
...
112