tito10047 / php-defer
延迟函数会将函数的执行推迟到周围函数返回时执行。
2.0.2
2024-08-29 07:18 UTC
Requires
- php: >=8.1
Requires (Dev)
- phpunit/phpunit: ^10.5
README
php-defer
PHP 对 Go 语言中的 defer 语句的实现
PHP 延迟函数将函数调用(延迟函数)安排在执行延迟函数的函数返回之前立即执行。这是一种处理资源必须无论函数通过哪个路径返回都必须释放的情况的非常规但有效的方法。典型的例子是解锁互斥锁或关闭文件。
// Contents returns the file's contents as a string. function contents($filename) { $f = fopen($filename, "r"); if ($f === false) { throw new Exception("Error opening the file"); } $defer = defer(fclose(...),$f); // fclose will run when we're finished. $result = ""; while (($buffer = fread($f, 100)) !== false) { $result .= $buffer; } if (feof($f) === false) { // $f will be closed if we return here. throw new Exception("Error reading the file"); } // $f will be closed if we return here. return $result; }
延迟对 Close 等函数的调用有两个优点。首先,它可以保证您永远不会忘记关闭文件,如果以后编辑函数添加新的返回路径,很容易犯这个错误。其次,它意味着关闭操作紧邻打开操作,这比将其放在函数末尾更清晰。
安装
composer require tito10047/defer
快速示例
function foo($a){ echo "in defer {$a}".PHP_EOL; } function a() { echo "before defer".PHP_EOL; $defer = defer(foo(...),1); $defer(foo(...),2); $defer(foo(...),3); echo "after defer".PHP_EOL; }; echo "start".PHP_EOL; a(); echo "end".PHP_EOL;
将打印
start
before defer
after defer
in defer 3
in defer 2
in defer 1
end
3 条规则
延迟语句的行为简单且可预测。有三条简单的规则
1.
延迟函数的参数在延迟语句评估时评估。
在这个例子中,表达式 "i" 在 printf 调用被延迟时评估。延迟调用将在函数返回后打印 0
。
function a(){ $i=0; $_ = defer(printf(...),$i); $i++; }
将打印 000
2.
延迟函数调用在周围函数返回后按 Last In First Out 顺序执行。
此函数将打印 3210
function b(){ $defer = defer(); for($i=0;$i<4;$i++){ $defer(printf(...),$i); } }
3.
延迟函数在类型为 is 时不能修改返回值,但可以修改数组或对象的引用内容。
在这个例子中,延迟函数在周围函数返回后递增 $o->i
,但不能修改返回的 $i
。此示例将打印 2-3
function c() { $i=1; $o=new \stdClass(); $o->i=2; $defer = defer(function () use (&$i, $o) { $o->i++; $i++; }); $i++; return [$i,$o]; } list($i,$o) = c(); echo "{$i}-{$o->i}".PHP_EOL;
PHP 限制
- 在 php defer 实现中,您不能修改返回值。只能修改返回引用的内容。
- 您需要在使用之前实例化 defer 对象,使用
$defer = new Defer()
或$defer = defer()
用法
namespace test; require_once __DIR__.'/../vendor/autoload.php'; function myFunc(){} class Foo{ public function myMethod(){} } function a(){ // defer custom function without parameter // function name must be with his namespace $defer = defer('test\myFunc'); // defer function with one parameter $defer(printf(...),"test"); // defer function with more parameters $defer('printf',"%s-%s",10,12); // defer with anonymous function $defer(function (){}); $func = function (){}; $defer($func); //defer method $foo = new Foo(); $defer([$foo,'myMethod']); } a();