Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
freekmurze committed Apr 26, 2021
1 parent da61188 commit d49ef42
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 11 deletions.
40 changes: 38 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ The closures to run shouldn't return objects, only primitives and arrays are all

### Running code before and after each closure

If you need to execute code some before each callable passed to `run`, you can pass a callable to `before`. This callable passed to `before` will be executed in the child process right before the callable passed to `run` will execute.
If you need to execute code some before or after each callable passed to `run`, you can pass a callable to `before` or `after. This callable passed will be executed in the child process right before or fater the callable passed to `run` will execute.

### Using `before` and `after` in the child process

Here's an example where we are going to get a value from the database using a Laravel Eloquent model. In order to let the child process use the DB, it is necessary to reconnect to the DB. The closuse passed to `before` will run in both child processes that are created for the closures passed to `run`.

Expand All @@ -92,12 +94,46 @@ use Spatie\Fork\Fork;
->before(fn () => DB::connection('mysql')->reconnect())
->run(
fn () => User::find(1)->someLongRunningFunction(),
fn () => User::find(2)->someLongRunningFunction()
fn () => User::find(2)->someLongRunningFunction(),
);
```

If you need to perform some cleanup in the child process after the callable has run, you can use the `after` method on a `Spatie\Fork\Fork` instance.

### Using `before` and `after` in the parent process.

If you need to let the callable passed to `before` or `after` run in the parent process, then you need to pass the callable to the `parent` argument.

```php
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Spatie\Fork\Fork;

Fork::new()
->before(parent: fn() => echo 'this runs in the parent process')
->run(
fn () => User::find(1)->someLongRunningFunction(),
fn () => User::find(2)->someLongRunningFunction(),
);
```

You can also pass different closures, to be run in the child and the parent process

```php
use Spatie\Fork\Fork;

Fork::new()
->before(
child: fn() => echo 'this runs in the child process',
parent: fn() => echo 'this runs in the parent process',
)
->run(
fn () => User::find(1)->someLongRunningFunction(),
fn () => User::find(2)->someLongRunningFunction(),
);
```


## Testing

```bash
Expand Down
32 changes: 23 additions & 9 deletions src/Fork.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

class Fork
{
protected ?Closure $before = null;
protected ?Closure $toExecuteBeforeInChildProcess = null;
protected ?Closure $toExecuteBeforeInParentProcess = null;

protected ?Closure $toExecuteAfterInChildProcess = null;
protected ?Closure $toExecuteAfterInParentProcess = null;

protected ?Closure $after = null;

Expand All @@ -16,16 +20,18 @@ public static function new(): self
return new self();
}

public function before(callable $before): self
public function before(callable $child = null, callable $parent = null): self
{
$this->before = $before;
$this->toExecuteBeforeInChildProcess = $child;
$this->toExecuteBeforeInParentProcess = $parent;

return $this;
}

public function after(callable $after): self
public function after(callable $child = null, callable $parent = null): self
{
$this->after = $after;
$this->toExecuteAfterInChildProcess = $child;
$this->toExecuteAfterInParentProcess = $parent;

return $this;
}
Expand All @@ -35,6 +41,10 @@ public function run(callable ...$callables): array
$processes = [];

foreach ($callables as $order => $callable) {
if ($this->toExecuteBeforeInParentProcess) {
($this->toExecuteBeforeInParentProcess)();
}

$process = Task::fromCallable($callable, $order);

$processes[] = $this->forkForProcess($process);
Expand Down Expand Up @@ -77,6 +87,10 @@ protected function waitFor(Task ...$runningProcesses): array
$output[$process->order()] = $process->output();

unset($runningProcesses[$key]);

if ($this->toExecuteAfterInParentProcess) {
($this->toExecuteAfterInParentProcess)();
}
}
}

Expand All @@ -99,8 +113,8 @@ protected function executingInChildProcess(
Task $process,
Socket $socketToParent,
): void {
if ($this->before) {
($this->before)();
if ($this->toExecuteBeforeInChildProcess) {
($this->toExecuteBeforeInChildProcess)();
}

$output = $process->execute();
Expand All @@ -111,8 +125,8 @@ protected function executingInChildProcess(

socket_write($socketToParent, $output);

if ($this->after) {
($this->after)();
if ($this->toExecuteAfterInChildProcess) {
($this->toExecuteAfterInChildProcess)();
}

socket_close($socketToParent);
Expand Down
28 changes: 28 additions & 0 deletions tests/ForkTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,34 @@ function () {
$this->assertEquals([1], $results);
}

/** @test */
public function the_callable_given_to_before_can_be_run_in_the_parent_process()
{
$value = 0;

Fork::new()
->before(parent: function () use (&$value) {
$value++;
})
->run(fn () => 1, fn () => 2);

$this->assertEquals(2, $value);
}

/** @test */
public function the_callable_given_to_after_can_be_run_in_the_parent_process()
{
$value = 0;

Fork::new()
->before(parent: function () use (&$value) {
$value++;
})
->run(fn () => 1, fn () => 2);

$this->assertEquals(2, $value);
}

/** @test */
public function it_will_not_hang_by_truncating_the_result_when_large_output_is_returned()
{
Expand Down

0 comments on commit d49ef42

Please sign in to comment.