Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/m3m0r7/php-deferrable
👽 Deferrable run your code with PHP
https://github.com/m3m0r7/php-deferrable
Last synced: 5 days ago
JSON representation
👽 Deferrable run your code with PHP
- Host: GitHub
- URL: https://github.com/m3m0r7/php-deferrable
- Owner: m3m0r7
- License: mit
- Created: 2020-03-15T07:57:20.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2020-03-29T00:08:50.000Z (almost 5 years ago)
- Last Synced: 2024-04-19T22:21:11.303Z (9 months ago)
- Language: PHP
- Homepage:
- Size: 96.7 KB
- Stars: 32
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme-ja.md
- License: license
Awesome Lists containing this project
README
# PHP-Deferrable - Simple and Powerful deferrable run code library
`PHP-Deferrable` はシンプルでかつ強力にあなたのコードを遅延処理することができるライブラリです。
このライブラリは Go の defer とよく似ています。
テスト以外に置いて他のライブラリに依存しないため、このライブラリはとてもシンプルなライブラリです。## ドキュメント
- [English](./readme.md)
- 日本語 (現在)## インストール
Composer を使用する:
```
composer require m3m0r7/php-deferrable
```## 今までの課題
Go には defer があり、return が返る前に defer の中身を実行することが出来ます。
しかし PHP は defer がないものの、 `try-finally` やデストラクタの破棄タイミングを用いて defer を実現することが可能です。```php
try {
// ... do something
} finally {
// 後処理
}
```これにはいくつか問題があり、後処理をするコードが煩雑になる可能性と、 `try` 構文が長くなりすぎてしまうと、何を処理するのかわからなくなってしまいます。
そして、不要なインデントに苛まれることでしょう。`php-deferrable` はその課題を解決するため、非常にシンプルな関数とクラスを提供することにより、それらすべての問題を解決します。
## クイックスタート
```php
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!
```## Deferrable 関数
`deferrable` 関数に callable なパラメータを渡すことも可能です。```php
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
````deferrable` 関数は関数の実行結果を返り値として返すことも可能です。
```php
use function PHPDeferrable\defer;
use function PHPDeferrable\deferrable;$result = deferrable(function () {
defer(function () {
// do something.
});
return "Return value\n";
})();echo $result;
```上記は以下のようになります。
```
Return value
````deferrable` は resource の後処理などにも有用です。
```php
use function PHPDeferrable\defer;
use function PHPDeferrable\deferrable;deferrable(function () {
$handle = fopen('php://memory', 'r')
defer(function () use ($handle) {
fclose($handle)
});
// ... do something
})();```
`defer` は可変長引数を用いてパラメータを渡すことも可能です。パラメータはコンテキストに準じてコピーされます。
```php
use function PHPDeferrable\defer;
use function PHPDeferrable\deferrable;deferrable(function () {
$message = 'Hello World';
defer(function ($message) {
echo $message;
}, $message);
// ... do something
})();```
上記は下記のようになります。
```
Hello World
```また、リファレンスとすることにより、 `defer` 内でパラメータの値を変更させることも可能です。
```php
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 内で例外が投げられてもスタックされた defer の処理は継続して行うように設計されています。
これは Go には例外はなく、しかし PHP に例外があるという矛盾を解決するためです。```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 の処理を中断する方法は二通りあります。1 つ目は現在の deferrable スコープそのものを例外が発生した場合返すように `DeferBailableScope::of` を使用します。
```php
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');
});
)
})()
```クラスの場合は下記になります。
```php
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` を使用します。2 つ目は `DeferBailableExceptionInterface` を継承した例外を投げる方法です。
このインタフェースを継承している場合、その時点で例外のマージを止めて、その継承された例外のみを返します。```php
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` の場合、第一引数にスコープの種類を渡すことにより制御可能です。
```php
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` が例外として投げられます。
## コンテキストマニピュレータ
コンテキストマニピュレータは非常にシンプルな方法で遅延処理を実現しています。
この方法を使うことにより defer と deferrable との実行と比較して PHP における無駄なスタックやメモリ使用量を控えることが出来ます。
これを使うことによりクラス名を `deferrable` でラップする必要がなくなります。```php
class MyClass
{
public function doSomething()
{
$context = Defer::createContext();
$context->defer(function () {
echo "Two!";
});
echo "One!";
}
}$myClass = new MyClass();
$myClass->doSomething();
```## ライセンス
MIT