m3m0r7/php-deferrable

延迟执行代码

0.5.1 2020-03-18 01:25 UTC

This package is auto-updated.

Last update: 2024-09-29 05:22:23 UTC


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