Skip to content

Commit

Permalink
[11.x] Adds finally method to pipeline helper (#54110)
Browse files Browse the repository at this point in the history
* Adds finally method to the pipeline facade

* Apply fixes from StyleCI

* Update Pipeline.php

---------

Co-authored-by: StyleCI Bot <[email protected]>
Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
3 people authored Jan 7, 2025
1 parent 6755311 commit f80c63d
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 1 deletion.
28 changes: 27 additions & 1 deletion src/Illuminate/Pipeline/Pipeline.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ class Pipeline implements PipelineContract
*/
protected $method = 'handle';

/**
* The final callback to be executed after the pipeline ends regardless of the outcome.
*
* @var \Closure|null
*/
protected $finally;

/**
* Create a new class instance.
*
Expand Down Expand Up @@ -116,7 +123,13 @@ public function then(Closure $destination)
array_reverse($this->pipes()), $this->carry(), $this->prepareDestination($destination)
);

return $pipeline($this->passable);
try {
return $pipeline($this->passable);
} finally {
if ($this->finally) {
($this->finally)($this->passable);
}
}
}

/**
Expand All @@ -131,6 +144,19 @@ public function thenReturn()
});
}

/**
* Set a final callback to be executed after the pipeline ends regardless of the outcome.
*
* @param \Closure $callback
* @return $this
*/
public function finally(Closure $callback)
{
$this->finally = $callback;

return $this;
}

/**
* Get the final piece of the Closure onion.
*
Expand Down
118 changes: 118 additions & 0 deletions tests/Pipeline/PipelineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Illuminate\Tests\Pipeline;

use Exception;
use Illuminate\Container\Container;
use Illuminate\Pipeline\Pipeline;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -273,6 +274,123 @@ public function testPipelineConditionable()
$this->assertNull($_SERVER['__test.pipe.one']);
unset($_SERVER['__test.pipe.one']);
}

public function testPipelineFinally()
{
$pipeTwo = function ($piped, $next) {
$_SERVER['__test.pipe.two'] = $piped;

$next($piped);
};

$result = (new Pipeline(new Container))
->send('foo')
->through([PipelineTestPipeOne::class, $pipeTwo])
->finally(function ($piped) {
$_SERVER['__test.pipe.finally'] = $piped;
})
->then(function ($piped) {
return $piped;
});

$this->assertSame(null, $result);
$this->assertSame('foo', $_SERVER['__test.pipe.one']);
$this->assertSame('foo', $_SERVER['__test.pipe.two']);
$this->assertSame('foo', $_SERVER['__test.pipe.finally']);

unset($_SERVER['__test.pipe.one'], $_SERVER['__test.pipe.two'], $_SERVER['__test.pipe.finally']);
}

public function testPipelineFinallyMethodWhenChainIsStopped()
{
$pipeTwo = function ($piped) {
$_SERVER['__test.pipe.two'] = $piped;
};

$result = (new Pipeline(new Container))
->send('foo')
->through([PipelineTestPipeOne::class, $pipeTwo])
->finally(function ($piped) {
$_SERVER['__test.pipe.finally'] = $piped;
})
->then(function ($piped) {
return $piped;
});

$this->assertSame(null, $result);
$this->assertSame('foo', $_SERVER['__test.pipe.one']);
$this->assertSame('foo', $_SERVER['__test.pipe.two']);
$this->assertSame('foo', $_SERVER['__test.pipe.finally']);

unset($_SERVER['__test.pipe.one'], $_SERVER['__test.pipe.two'], $_SERVER['__test.pipe.finally']);
}

public function testPipelineFinallyOrder()
{
$std = new stdClass();

$result = (new Pipeline(new Container))
->send($std)
->through([
function ($std, $next) {
$std->value = 1;

return $next($std);
},
function ($std, $next) {
$std->value++;

return $next($std);
},
])->finally(function ($std) {
$this->assertSame(3, $std->value);

$std->value++;
})->then(function ($std) {
$std->value++;

return $std;
});

$this->assertSame(4, $std->value);
$this->assertSame(4, $result->value);
}

public function testPipelineFinallyWhenExceptionOccurs()
{
$std = new stdClass();

$this->expectException(Exception::class);
$this->expectExceptionMessage('My Exception: 1');

try {
(new Pipeline(new Container))
->send($std)
->through([
function ($std, $next) {
$std->value = 1;

return $next($std);
},
function ($std) {
throw new Exception('My Exception: '.$std->value);
},
])->finally(function ($std) {
$this->assertSame(1, $std->value);

$std->value++;
})->then(function ($std) {
$std->value = 0;

return $std;
});
} catch (Exception $e) {
$this->assertSame('My Exception: 1', $e->getMessage());
$this->assertSame(2, $std->value);

throw $e;
}
}
}

class PipelineTestPipeOne
Expand Down

0 comments on commit f80c63d

Please sign in to comment.