m3m0r7 / php-deferrable
延迟执行代码
Requires
- php: >=7.2
Requires (Dev)
- phpunit/phpunit: ^9.0
README
PHP-Deferrable 是一个简单而强大的延迟执行代码库。这个库类似于 Golang。这个库非常简单,因为它不依赖于其他库。
文档
- 英语(当前语言)
- 日本語
安装
使用 composer
composer require m3m0r7/php-deferrable
截至今天的所有问题
Go 有 defer,你可以在返回之前执行 defer 的内容。然而,尽管 PHP 没有defer,但可以使用 try-finally
或析构函数来达到延迟执行。
try { // ... do something } finally { // post-processing }
这有一些问题:后处理代码可能会很繁琐,如果 try
语法太长,你不知道该怎么办。而且你将遭受不必要的缩进。
php-deferrable
通过提供非常简单的函数和类来解决问题。
快速入门
use function PHPDeferrable\defer; use function PHPDeferrable\deferrable; class MyClass { public function doSomething1() { defer(function () { echo "Three!\n"; }); defer(function () { echo "Two!\n"; }); echo "One!\n"; } public function doSomething2() { defer(function () { echo "NyanNyan!\n"; }); echo "Wanwan!\n"; } } /** * @var MyClass $myClass */ $myClass = deferrable(MyClass::class, ...$somethingArguments); $myClass->doSomething1(); $myClass->doSomething2();
如下所示
One!
Two!
There!
Wanwan!
NyanNyan!
延迟函数
你可以将一个函数传递给延迟。
use function PHPDeferrable\defer; use function PHPDeferrable\deferrable; deferrable(function () { defer(function () { echo "0: deferred call\n"; }); echo "0: first call\n"; })(); deferrable(function () { defer(function () { echo "1: deferred call\n"; }); echo "1: first call\n"; })();
如下所示
0: first call
0: deferred call
1: first call
1: deferred call
延迟函数可以返回一个值。
use function PHPDeferrable\defer; use function PHPDeferrable\deferrable; $result = deferrable(function () { defer(function () { // do something. }); return "Return value\n"; })(); echo $result;
如下所示
Return value
延迟可以操作资源上下文。
use function PHPDeferrable\defer; use function PHPDeferrable\deferrable; deferrable(function () { $handle = fopen('php://memory', 'r') defer(function () use ($handle) { fclose($handle) }); // ... do something })();
defer
可以传递任何参数,并且会根据上下文复制。
use function PHPDeferrable\defer; use function PHPDeferrable\deferrable; deferrable(function () { $message = 'Hello World'; defer(function ($message) { echo $message; }, $message); // ... do something })();
如下所示
Hello World
并且可以在 defer
函数中通过引用更改参数值。
use function PHPDeferrable\defer; use function PHPDeferrable\deferrable; deferrable(function () { $message = 'Hello World'; defer(function (&$message) { echo $message; }, $message); defer(function (&$message) { $message = 'The cat has big power.'; }, $message); // ... do something })();
如下所示
The cat has big power.
defer 的异常
通常,php-deferrable 被设计成即使 defer 中抛出异常,延迟栈的处理也会继续。这是为了解决 Go 没有异常,但 PHP 有异常的不一致问题。
deferrable(function() { defer(function () { throw new Exception('exception 1'); }); defer(function () { throw new Exception('exception 2'); }); defer(function () { throw new Exception('exception 3'); }); })()
在上面的例子中,所有异常都组合在一起,并以 MergedDeferringException
返回。
但是,你可能希望在发生异常时停止。当然,这样的手段也是可用的。如果发生异常,有两种方式可以暂停 defer 处理。
第一种使用 DeferBailableScope :: of
在发生异常时返回当前的延迟作用域本身。
deferrable( DeferBailableScope::of(function() { defer(function () { throw new ThirdException('exception 1'); }); defer(function () { throw new SecondException('exception 2'); }); defer(function () { throw new FirstException('exception 3'); }); ) })()
或者使用类
class MyClassTest { public function doSomething() { defer(function () { throw new ThirdException('exception 1'); }); defer(function () { throw new SecondException('exception 2'); }); defer(function () { throw new FirstException('exception 3'); }); } } $myClass = deferrable( DeferBailableScope::of( MyClassTest::class ) ); $myClass->doSomething();
在这种情况下,FirstException
被抛出为异常到外部作用域。抛出 FirstException
的原因是 defer 过程弹出堆栈。换句话说,过程从最后一个注册的 defer 开始。与 DeferBailableScope
相比,如果你想显式指定可以继续的异常,请使用 DeferContinuableScope
。
第二种是抛出一个继承自 DeferBailableExceptionInterface
的异常。如果你继承这个接口,停止在该点合并异常并只返回继承的异常。
class SecondException extends \Exception implements DeferBailableExceptionInterface { } deferrable(function() { defer(function () { throw new ThirdException('exception 1'); }); defer(function () { throw new SecondException('exception 2'); }); defer(function () { throw new FirstException('exception 3'); }); })()
在上面的情况下,抛出 SecondException
。在 Defer :: createContext
的情况下,可以通过传递作用域类型作为第一个参数来控制。
class Example { public function doSomething() { $context = Defer::createContext(DeferrableScopeType::BAILABLE); $context->defer(function () { throw new ThirdException('exception 1'); }); $context->defer(function () { throw new SecondException('exception 2'); }); $context->defer(function () { throw new FirstException('exception 3'); }); } } (new Example())->doSomething();
在上面的情况下,抛出 FirstException
异常。
上下文操作器
上下文操作器是一个非常简单的延迟函数操作器。你可以通过使用它来降低内存使用。对于想要延迟类的人来说,不需要用 deferrable
函数包装。
class MyClass { public function doSomething() { $context = Defer::createContext(); $context->defer(function () { echo "Two!"; }); echo "One!"; } } $myClass = new MyClass(); $myClass->doSomething();
许可证
MIT