Skip to content

Commit

Permalink
✨ Add do notation
Browse files Browse the repository at this point in the history
  • Loading branch information
matyo91 committed Sep 25, 2023
1 parent d1ae85a commit db38950
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 16 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

## v1.1.x


- Add Flow\FlowInterface::do notation from https://github.com/fp4php/functional
- Update Flow\FlowInterface::fn to accept as first argument
- Closure : it's the job itself
- array : configuration for FlowInterface instanciation
- FlowInterface : the FlowInterface instance itself

## v1.1.4

Expand Down
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Flow concept aims to solve

## Installation

PHP 8.2 is the minimal version to use Flow
PHP 8.2 is the minimal version to use Flow
The recommended way to install it through [Composer](http://getcomposer.org) and execute

```bash
Expand All @@ -29,19 +29,29 @@ composer require darkwood/flow
use Flow\Flow\Flow;
use Flow\Ip;

$flow = (new Flow(fn (object $data) => $data['number'] += 1))
->fn(new Flow(fn (object $data) => $data['number'] *= 2));
class D1 {
public function __construct(public int $n1) {}
}

$ip = new Ip(new ArrayObject(['number' => 4]));
$flow($ip, fn ($ip) => printf("my number %d\n", $ip->data['number'])); // display 'my number 10'
class D2 {
public function __construct(public int $n2) {}
}

$flow = Flow::do(static function() {
yield fn (D1 $data1) => new D2($data1->n1 += 1);
yield fn (D2 $data2) => $data2->n2 * 2;
});

$ip = new Ip(new D1(4));
$flow($ip, fn ($ip) => printf("my number %d\n", $ip->data->n2)); // display 'my number 10'
```

## Examples

A working script is available in the bundled `examples` directory

- Run Flow : `php examples/flow.php`
- Start Server : `php examples/server.php`
- Start Server : `php examples/server.php`
Start Client(s) : `php examples/client.php`

## Documentation
Expand Down
9 changes: 7 additions & 2 deletions examples/flow.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,14 @@
$ip->data->number = null;
};

$flow = (new Flow($job1, $errorJob1, new MaxIpStrategy(2), $driver))
$flow = Flow::do(static function() use($job1, $job2, $errorJob1, $errorJob2) {
yield [$job1, $errorJob1, new MaxIpStrategy(2)];
yield [$job2, $errorJob2, new MaxIpStrategy(2)];
}, ['driver' => $driver]);

/*$flow = (new Flow($job1, $errorJob1, new MaxIpStrategy(2), $driver))
->fn(new Flow($job2, $errorJob2, new MaxIpStrategy(2), $driver))
;
;*/

$ipPool = new SplObjectStorage();

Expand Down
12 changes: 12 additions & 0 deletions src/Exception/LogicException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Flow\Exception;

use Flow\ExceptionInterface;
use LogicException as NativeLogicException;

class LogicException extends NativeLogicException implements ExceptionInterface
{
}
67 changes: 61 additions & 6 deletions src/Flow/Flow.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
use Closure;
use Flow\Driver\FiberDriver;
use Flow\DriverInterface;
use Flow\Exception\LogicException;
use Flow\ExceptionInterface;
use Flow\FlowInterface;
use Flow\Ip;
use Flow\IpStrategy\LinearIpStrategy;
use Flow\IpStrategyInterface;
use Generator;
use SplObjectStorage;

use function count;
Expand Down Expand Up @@ -74,20 +76,42 @@ public function __construct(
$this->callbacks = new SplObjectStorage();
}

public static function do(callable $callable, ?array $config = null): FlowInterface {
$generator = $callable();

if($generator instanceof Generator) {
$flows = [];

while ($generator->valid()) {
$flow = self::flowUnwrap($generator->current(), $config);

$generator->send($flow);

$flows[] = $flow;
}

$return = $generator->getReturn();
if(!empty($return)) {
$flows[] = self::flowUnwrap($return, $config);
}

return $flow;
}

return self::flowUnwrap($generator, $config);
}

public function __invoke(Ip $ip, Closure $callback = null): void
{
$this->callbacks->offsetSet($ip, $callback);
$this->ipStrategy->push($ip);
$this->nextIpJob();
}

/**
* @param FlowInterface<T2> $flow
*
* @return FlowInterface<T1>
*/
public function fn(FlowInterface $flow): FlowInterface
public function fn(callable|FlowInterface $flow): FlowInterface
{
$flow = self::flowUnwrap($flow);

if ($this->fnFlow) {
$this->fnFlow->fn($flow);
} else {
Expand Down Expand Up @@ -133,4 +157,35 @@ private function nextIpJob(): void
})($ip->data);
}
}

/**
* @param Closure|array<mixed>|FlowInterface $flow
* @param ?array{
* "jobs"?: Closure|array,
* "errorJobs"?: Closure|array,
* "ipStrategy"?: IpStrategyInterface
* "driver"?: DriverInterface
* } $config
* @return FlowInterface
*/
private static function flowUnwrap($flow, ?array $config = null): FlowInterface
{
if($flow instanceof Closure) {
return new Flow(...["jobs" => $flow], ...($config ?? []));
} else if(is_array($flow)) {
if(array_key_exists("jobs", $flow)) {
return new Flow(...$flow, ...($config ?? []));
}

if(count($flow) === 0) {
throw new LogicException(sprintf('Flow is empty'));
}

foreach($flow as $flowIt) {

}
}

return $flow;
}
}
32 changes: 31 additions & 1 deletion src/FlowInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,39 @@ public function __invoke(Ip $ip, Closure $callback = null): void;
/**
* @template T2
*
* @param FlowInterface<T2> $flow
* @param Closure|array|FlowInterface<T2> $flow can be Closure as Job, array configuration for Flow instanciation or FlowInterface instance
*
* @return FlowInterface<T1>
*/
public function fn(self $flow): self;

/**
* Do-notation a.k.a. for-comprehension.
*
* Syntax sugar for sequential {@see FlowInterface::fn()} calls
*
* Syntax "$unwrappedValue = yield $box" mean:
* 1) unwrap the $box
* 2) if there is nothing in the box then short-circuit (stop) the callable
* 3) place contained in $box value into $unwrappedValue variable
*
* ```php
* >>> Option::do(function() {
* $a = 1;
* $b = yield new Flow(fn() => );
* $c = yield Option::some(3);
* $d = yield Option::none(); // short circuit here
* $e = 5; // not executed
* return [$a, $b, $c, $d, $e]; // not executed
* });
* => None
* ```
*
* @template TS
* @template TO
*
* @param callable(): Generator<int, Option<TS>, TS, TO> $callable
* @return Option<TO>
*/
public static function do(callable $callable, ?array $config = null): FlowInterface;
}
18 changes: 18 additions & 0 deletions tests/Flow/FlowTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ public function testJobs(DriverInterface $driver): void
$driver->start();
}

/**
* @dataProvider jobProvider
*
* @param DriverInterface<T1,T2|void> $driver
*/
public function testDo(): void
{

}

/**
* @return array<array<mixed>>
*/
Expand Down Expand Up @@ -114,4 +124,12 @@ public static function jobProvider(): iterable
}], 0],
]);
}

/**
* @return array<array<mixed>>
*/
public static function doProvider(): iterable
{

}
}

0 comments on commit db38950

Please sign in to comment.