Skip to content

Commit b794d78

Browse files
committed
Add deferred logic
1 parent ae907c3 commit b794d78

6 files changed

Lines changed: 244 additions & 0 deletions

File tree

Deferred.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2023 Jeremy Presutti <Jeremy@Presutti.us>
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
declare(strict_types=1);
20+
21+
namespace Feast;
22+
abstract class Deferred
23+
{
24+
protected bool $cancelled = false;
25+
abstract public function deferredAction(): void;
26+
27+
public function __destruct()
28+
{
29+
if ( $this->cancelled === false ) {
30+
$this->deferredAction();
31+
}
32+
}
33+
34+
public final function cancelDeferral(): void
35+
{
36+
$this->cancelled = true;
37+
}
38+
}

DeferredCall.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2023 Jeremy Presutti <Jeremy@Presutti.us>
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
declare(strict_types=1);
20+
21+
namespace Feast;
22+
class DeferredCall extends Deferred
23+
{
24+
protected bool $cancelled = false;
25+
26+
/** @var callable|null $callable */
27+
protected $callable = null;
28+
public function deferredAction(): void {
29+
/** @var callable $callable */
30+
$callable = $this->callable;
31+
$callable();
32+
}
33+
34+
public function __construct(callable $callable)
35+
{
36+
$this->callable = $callable;
37+
}
38+
}

FeastTests/DeferredTest.php

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2023 Jeremy Presutti <Jeremy@Presutti.us>
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
declare(strict_types=1);
20+
21+
use PHPUnit\Framework\TestCase;
22+
23+
class DeferredTest extends TestCase
24+
{
25+
public function testDeferredMethod(): void
26+
{
27+
$this->deferredTester();
28+
$output = $this->getActualOutputForAssertion();
29+
$this->assertEquals('This is firstThis is second', $output);
30+
}
31+
32+
public function testCancelledDeferredMethod(): void
33+
{
34+
$this->deferredTester(true);
35+
$output = $this->getActualOutputForAssertion();
36+
$this->assertEquals('This is first', $output);
37+
}
38+
39+
public function testDeferredCallable(): void
40+
{
41+
$this->deferredCallableTester();
42+
$output = $this->getActualOutputForAssertion();
43+
$this->assertEquals('This is firstThis is second', $output);
44+
}
45+
46+
public function testDeferredCallableEnsureOrder(): void
47+
{
48+
$this->deferredCallableTesterEnsureProcessingOrder();
49+
$output = $this->getActualOutputForAssertion();
50+
$this->assertEquals('This is firstThis is second', $output);
51+
}
52+
53+
public function testCancelledDeferredCallable(): void
54+
{
55+
$this->deferredCallableTester(true);
56+
$output = $this->getActualOutputForAssertion();
57+
$this->assertEquals('This is first', $output);
58+
}
59+
60+
protected function deferredTester(bool $cancel = false): void
61+
{
62+
$deferred = new \Mocks\DeferredMock('This is second');
63+
echo 'This is first';
64+
if ( $cancel ) {
65+
$deferred->cancelDeferral();
66+
}
67+
}
68+
69+
protected function deferredCallableTester(bool $cancel = false): void
70+
{
71+
$deferred = new \Feast\DeferredCall(function() { echo 'This is second'; });
72+
echo 'This is first';
73+
if ( $cancel ) {
74+
$deferred->cancelDeferral();
75+
}
76+
}
77+
78+
protected function deferredCallableTesterEnsureProcessingOrder(bool $cancel = false): void
79+
{
80+
$output = 'This is second';
81+
$deferred = new \Feast\DeferredCall(function() use ($output) { echo $output; });
82+
$output = 'This is not second';
83+
echo 'This is first';
84+
if ( $cancel ) {
85+
$deferred->cancelDeferral();
86+
}
87+
}
88+
89+
}

FeastTests/Mocks/DeferredMock.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2023 Jeremy Presutti <Jeremy@Presutti.us>
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
declare(strict_types=1);
20+
21+
namespace Mocks;
22+
23+
use Feast\Deferred;
24+
25+
class DeferredMock extends Deferred
26+
{
27+
28+
public function __construct(public string $echo)
29+
{
30+
31+
}
32+
33+
public function deferredAction(): void
34+
{
35+
echo $this->echo;
36+
}
37+
}

docs/deferred.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
[Back to Index](index.md)
2+
3+
# Deferring Actions
4+
5+
FEAST contains both a simple, callback based way and a more configurable extendable class to take deferred actions at
6+
the end of current scope (post function return if inside function, at end of script if in global scope) by making use of
7+
destructors.
8+
9+
## Deferred Callback
10+
11+
The `DeferredCall` class that allows you to call any "callable". The `DeferredCall` class constructor takes a callable
12+
as the only parameter. You may cancel the Deferred handler by calling the `cancel` method of the object returned.
13+
14+
The following code examples (greatly simplified) show usage.
15+
16+
```php
17+
protected function deferredCall(): void
18+
{
19+
$deferred = new \Feast\DeferredCall(function() { echo 'This is second'; });
20+
echo 'This is first.';
21+
}
22+
```
23+
24+
The above code would echo `This is first. This is second.`
25+
26+
```php
27+
protected function deferredCallCancelled(): void
28+
{
29+
$deferred = new \Feast\DeferredCall(function() { echo ' This is second'; });
30+
echo 'This is first.';
31+
$deferred->cancelDeferral();
32+
```
33+
34+
The above code would only echo `This is first.`
35+
36+
## Deferred Abstract class
37+
38+
The `Deferred` abstract class must be extended. This class has a required method `deferredAction` that must be
39+
implemented in the child. There are no mandatory constructor arguments for this class and can be used for more dynamic
40+
use cases.

docs/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ The release plan for FEAST can be found [here](release-schedule.md).
119119

120120
[Sessions](sessions.md)
121121

122+
[Deferred Actions](deferred.md)
123+
122124
## Scheduled and Queued Jobs
123125

124126
[Queueable Jobs](queues.md)

0 commit comments

Comments
 (0)