Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F24894519
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
24 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/DataTypes/Option/None.php b/src/DataTypes/Option/None.php
index 4813b12..dc29e34 100644
--- a/src/DataTypes/Option/None.php
+++ b/src/DataTypes/Option/None.php
@@ -1,32 +1,45 @@
<?php
namespace Keruald\OmniTools\DataTypes\Option;
use InvalidArgumentException;
class None extends Option {
public function isSome () : bool {
return false;
}
public function isNone () : bool {
return true;
}
public function getValue () : mixed {
throw new InvalidArgumentException(<<<'EOD'
This option is a none, so it doesn't have a value.
You can check first with isSome() if this is a value.
EOD
);
}
public function map (callable $callable) : Option {
return $this;
}
- public function orElse (mixed $default) : mixed {
+ public function or (Option $default) : Option {
return $default;
}
+
+ public function orElse (callable $callable) : Option {
+ return $callable();
+ }
+
+ public function getValueOr (mixed $default) : mixed {
+ return $default;
+ }
+
+ public function getValueOrElse (callable $callable) : mixed {
+ return $callable();
+ }
+
}
diff --git a/src/DataTypes/Option/Option.php b/src/DataTypes/Option/Option.php
index ab97d81..6579395 100644
--- a/src/DataTypes/Option/Option.php
+++ b/src/DataTypes/Option/Option.php
@@ -1,33 +1,66 @@
<?php
namespace Keruald\OmniTools\DataTypes\Option;
abstract class Option {
public abstract function isSome () : bool;
public abstract function isNone () : bool;
public abstract function getValue() : mixed;
public abstract function map(callable $callable) : self;
- public abstract function orElse(mixed $default) : mixed;
+ /**
+ * Returns the option if it contains a value,
+ * otherwise returns the default option.
+ */
+ public abstract function or(Option $default) : self;
+
+ /**
+ * Returns the option if it contains a value,
+ * otherwise calls the callable and returns the result.
+ *
+ * The callable is called only on None, allowing lazy evaluation.
+ * The callable must return an Option.
+ */
+ public abstract function orElse(callable $callable) : self;
+
+ /**
+ * Returns the value of the option if it is Some,
+ * or the default value if the option is None.
+ *
+ * @param mixed $default The default value
+ * @return mixed
+ */
+ public abstract function getValueOr(mixed $default) : mixed;
+
+ /**
+ * Returns the value of the option if it is Some,
+ * or the result of the callable if the option is None.
+ *
+ * The callable is called only on None, allowing lazy evaluation.
+ *
+ * @param callable $callable A function that returns a default value.
+ * @return mixed
+ */
+ public abstract function getValueOrElse(callable $callable) : mixed;
///
/// Helper to build options
///
/**
* Converts a nullable value to an Option.
*
* @param mixed $value
*
* @return Option An instance of None if the value is null; otherwise, an instance of Some.
*/
public static function from (mixed $value) : self {
return match ($value) {
null => new None,
default => new Some($value),
};
}
}
diff --git a/src/DataTypes/Option/Some.php b/src/DataTypes/Option/Some.php
index ab4fbb8..d449d94 100644
--- a/src/DataTypes/Option/Some.php
+++ b/src/DataTypes/Option/Some.php
@@ -1,59 +1,71 @@
<?php
namespace Keruald\OmniTools\DataTypes\Option;
use InvalidArgumentException;
use Keruald\OmniTools\Reflection\Type;
class Some extends Option {
private mixed $value = null;
private string $type = "NULL";
public function __construct ($value = null) {
if ($value !== null) {
$this->setValue($value);
}
}
public function isSome () : bool {
return true;
}
public function isNone () : bool {
return false;
}
public function getValue () : mixed {
return $this->value;
}
public function setValue (mixed $value) : void {
$type = Type::getTypeOf($value);
if (!$this->isAcceptableValueType($type)) {
throw new InvalidArgumentException(<<<'EOD'
When you mutate the value of an Some object, you can't mutate the object type.
Please consider return a new Some instead.
EOD
);
}
$this->value = $value;
$this->type = $type;
}
private function isAcceptableValueType (string $type) : bool {
return $this->value === null || $type === $this->type;
}
public function map (callable $callable) : Option {
$value = $callable($this->value);
return new self($value);
}
- public function orElse (mixed $default) : mixed {
+ public function or (Option $default) : Option {
+ return $this;
+ }
+
+ public function orElse (callable $callable) : Option {
+ return $this;
+ }
+
+ public function getValueOr (mixed $default) : mixed {
+ return $this->value;
+ }
+
+ public function getValueOrElse (callable $callable) : mixed {
return $this->value;
}
}
diff --git a/src/DataTypes/Result/Err.php b/src/DataTypes/Result/Err.php
index 1661c28..0f62c77 100644
--- a/src/DataTypes/Result/Err.php
+++ b/src/DataTypes/Result/Err.php
@@ -1,54 +1,87 @@
<?php
namespace Keruald\OmniTools\DataTypes\Result;
-use Exception;
+use Keruald\OmniTools\Reflection\CallableElement;
+
use InvalidArgumentException;
use Throwable;
class Err extends Result {
private ?Throwable $error;
+ const CB_TOO_MANY_ARGS = "The callback must take 0 or 1 argument.";
+
public function __construct (Throwable $error = null) {
$this->error = $error;
}
public function isOK () : false {
return false;
}
public function isError () : true {
return true;
}
public function getValue () : mixed {
throw new InvalidArgumentException(<<<'EOD'
This result is an error, so it doesn't have a value.
You can check first with isOK() if this is a value.
Or if you want the error, use getError().
EOD
);
}
public function getError () : Throwable {
return $this->error;
}
public function setError (Throwable $error) : void {
$this->error = $error;
}
public function map (callable $callable) : self {
return $this;
}
public function mapErr (callable $callable) : self {
$error = $callable($this->error);
return new self($error);
}
- public function orElse (mixed $default) : mixed {
+ public function or (Result $default) : Result {
return $default;
}
+
+ public function orElse (callable $callable) : Result {
+ $argc = (new CallableElement($callable))->countArguments();
+
+ return match($argc) {
+ 0 => $callable(),
+ 1 => $callable($this->error),
+
+ default => throw new InvalidArgumentException(
+ self::CB_TOO_MANY_ARGS, 0, $this->error
+ ),
+ };
+ }
+
+ public function getValueOr (mixed $default) : mixed {
+ return $default;
+ }
+
+ public function getValueOrElse (callable $callable) : mixed {
+ $argc = (new CallableElement($callable))->countArguments();
+
+ return match($argc) {
+ 0 => $callable(),
+ 1 => $callable($this->error),
+
+ default => throw new InvalidArgumentException(
+ self::CB_TOO_MANY_ARGS, 0, $this->error
+ ),
+ };
+ }
}
diff --git a/src/DataTypes/Result/Ok.php b/src/DataTypes/Result/Ok.php
index 86b54f9..ec624a8 100644
--- a/src/DataTypes/Result/Ok.php
+++ b/src/DataTypes/Result/Ok.php
@@ -1,63 +1,75 @@
<?php
namespace Keruald\OmniTools\DataTypes\Result;
use InvalidArgumentException;
use Keruald\OmniTools\Reflection\Type;
class Ok extends Result {
private mixed $value = null;
private string $type = "NULL";
public function __construct ($value = null) {
if ($value !== null) {
$this->setValue($value);
}
}
public function isOK () : true {
return true;
}
public function isError () : false {
return false;
}
public function getValue () : mixed {
return $this->value;
}
public function setValue (mixed $value) : void {
$type = Type::getTypeOf($value);
if (!$this->isAcceptableValueType($type)) {
throw new InvalidArgumentException(<<<'EOD'
When you mutate the value of an Ok object, you can't mutate the object type.
Please consider return a new Ok instead.
EOD
);
}
$this->value = $value;
$this->type = $type;
}
private function isAcceptableValueType (string $type) : bool {
return $this->value === null || $type === $this->type;
}
public function map (callable $callable) : self {
$value = $callable($this->value);
return new self($value);
}
public function mapErr (callable $callable) : self {
return $this;
}
- public function orElse (mixed $default) : mixed {
+ public function or (Result $default) : Result {
+ return $this;
+ }
+
+ public function orElse (callable $callable) : Result {
+ return $this;
+ }
+
+ public function getValueOr (mixed $default) : mixed {
+ return $this->value;
+ }
+
+ public function getValueOrElse (callable $callable) : mixed {
return $this->value;
}
}
diff --git a/src/DataTypes/Result/Result.php b/src/DataTypes/Result/Result.php
index 90d93a5..fca8fad 100644
--- a/src/DataTypes/Result/Result.php
+++ b/src/DataTypes/Result/Result.php
@@ -1,15 +1,51 @@
<?php
namespace Keruald\OmniTools\DataTypes\Result;
abstract class Result {
public abstract function isOK () : bool;
public abstract function isError () : bool;
public abstract function getValue () : mixed;
public abstract function map(callable $callable) : self;
public abstract function mapErr(callable $callable): self;
- public abstract function orElse(mixed $default) : mixed;
+ /**
+ * Returns the result if it is Ok,
+ * or the default result if an Err.
+ *
+ * @param mixed $default The default value
+ * @return mixed
+ */
+ public abstract function or(Result $default) : self;
+
+ /**
+ * Returns the result if it is Ok,
+ * otherwise calls the callable and returns the new result.
+ *
+ * The callable is called only on Err, allowing lazy evaluation.
+ * The callable must return a Result.
+ */
+ public abstract function orElse(callable $callable) : self;
+
+ /**
+ * Returns the value of the result if it is Ok,
+ * or the default value if the option is Err.
+ *
+ * @param mixed $default The default value
+ * @return mixed
+ */
+ public abstract function getValueOr(mixed $default) : mixed;
+
+ /**
+ * Returns the value of the result if it is Ok,
+ * or the result of the callable if the option is Err.
+ *
+ * The callable is called only on Err, allowing lazy evaluation.
+ *
+ * @param callable $callable A function that returns a default value.
+ * @return mixed
+ */
+ public abstract function getValueOrElse(callable $callable) : mixed;
}
diff --git a/src/Events/Propagation.php b/src/Events/Propagation.php
index cb0690c..6513a6e 100644
--- a/src/Events/Propagation.php
+++ b/src/Events/Propagation.php
@@ -1,74 +1,74 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Events;
use BadFunctionCallException;
use Keruald\OmniTools\DataTypes\Option\None;
use Keruald\OmniTools\DataTypes\Option\Option;
use Keruald\OmniTools\DataTypes\Option\Some;
use RuntimeException;
use Throwable;
/**
* Static class providing helper methods to propagate events.
*/
class Propagation {
/**
* Calls a set of functions with the specified parameters.
* This is intended for callback purpose.
*
* @param iterable $callables The functions to call, each item a callable
* @param array $parameters The parameters to pass to the functions [optional]
*/
public static function call (iterable $callables, array $parameters = []) : void {
foreach ($callables as $callable) {
if (!is_callable($callable)) {
$previous = self::grabException($parameters);
- throw new BadFunctionCallException("Callback for this method.", 0, $previous->orElse(null));
+ throw new BadFunctionCallException("Callback for this method.", 0, $previous->getValueOr(null));
}
call_user_func_array($callable, $parameters);
}
}
/**
* Calls a set of functions with the specified parameters.
* If no function is present, throws an exception.
*
* @param iterable $callables The functions to call, each item a callable
* @param array $parameters The parameters to pass to the functions
* [optional]
* @param Throwable|null $exception The exception to throw if no callback is
* provided [optional]
*
* @throws Throwable
*/
public static function callOrThrow (iterable $callables, array $parameters = [], Throwable $exception = null) : void {
if (!count($callables)) {
throw $exception ?? self::grabException($parameters)
- ->orElse(new RuntimeException);
+ ->getValueOrElse(fn () => new RuntimeException);
}
static::call($callables, $parameters);
}
/**
* Grabs the first exception among specified items.
*
* @param iterable $items The items to check
* @return Option<Throwable>
*/
private static function grabException (iterable $items) : Option {
foreach ($items as $item) {
if ($item instanceof Throwable) {
return new Some($item);
}
}
return new None;
}
}
diff --git a/tests/DataTypes/Option/NoneTest.php b/tests/DataTypes/Option/NoneTest.php
index 7809e35..e11b107 100644
--- a/tests/DataTypes/Option/NoneTest.php
+++ b/tests/DataTypes/Option/NoneTest.php
@@ -1,47 +1,68 @@
<?php
namespace Keruald\OmniTools\Tests\DataTypes\Option;
use InvalidArgumentException;
use Keruald\OmniTools\DataTypes\Option\None;
use Keruald\OmniTools\DataTypes\Option\Option;
+use Keruald\OmniTools\DataTypes\Option\Some;
use PHPUnit\Framework\TestCase;
class NoneTest extends TestCase {
private Option $v;
public function setUp () : void {
$this->v = new None;
}
public function testIsSome () : void {
$this->assertFalse($this->v->isSome());
}
public function testIsNone () : void {
$this->assertTrue($this->v->isNone());
}
public function testGetValue () : void {
$this->expectException(InvalidArgumentException::class);
$this->v->getValue();
}
public function testMap () : void {
$callback = function ($n) {
return $n * 2;
};
$mapped_v = $this->v->map($callback);
$this->assertEquals($mapped_v, $this->v);
}
+ public function testOr () : void {
+ $actual = $this->v->or(new Some(666));
+
+ $this->assertTrue($actual->isSome());
+ $this->assertEquals(666, $actual->getValue());
+ }
+
public function testOrElse () : void {
- $value = $this->v->orElse(666);
+ $actual = $this->v->orElse(fn () => new Some(666));
+
+ $this->assertTrue($actual->isSome());
+ $this->assertEquals(666, $actual->getValue());
+ }
+
+ public function testGetValueOr () : void {
+ $value = $this->v->getValueOr(666);
+
+ $this->assertEquals(666, $value);
+ }
+
+ public function testGetValueOrElse () : void {
+ $value = $this->v->getValueOrElse(fn () => 666);
$this->assertEquals(666, $value);
}
}
diff --git a/tests/DataTypes/Option/SomeTest.php b/tests/DataTypes/Option/SomeTest.php
index d541fe0..daa6227 100644
--- a/tests/DataTypes/Option/SomeTest.php
+++ b/tests/DataTypes/Option/SomeTest.php
@@ -1,57 +1,77 @@
<?php
namespace Keruald\OmniTools\Tests\DataTypes\Option;
use InvalidArgumentException;
use Keruald\OmniTools\DataTypes\Option\Some;
use Keruald\OmniTools\DataTypes\Option\Option;
use PHPUnit\Framework\TestCase;
class SomeTest extends TestCase {
private Option $v;
public function setUp () : void {
$this->v = new Some;
$this->v->setValue(42);
}
public function testIsSome () : void {
$this->assertTrue($this->v->isSome());
}
public function testIsNone () : void {
$this->assertFalse($this->v->isNone());
}
public function testGetValue () : void {
$this->assertEquals(42, $this->v->getValue());
}
public function testSetValue () : void {
$this->v->setValue(666);
$this->assertEquals(666, $this->v->getValue());
}
public function testSetValueWhenTypeIsMutated () : void {
$this->expectException(InvalidArgumentException::class);
$this->v->setValue("Another type");
}
public function testMap () : void {
$callback = function ($n) {
return $n * 2;
};
$mapped_v = $this->v->map($callback);
$this->assertEquals(84, $mapped_v->getValue());
}
+ public function testOr () : void {
+ $actual = $this->v->or(new Some(666));
+
+ $this->assertTrue($actual->isSome());
+ $this->assertEquals(42, $actual->getValue());
+ }
+
public function testOrElse () : void {
- $value = $this->v->orElse(666);
+ $actual = $this->v->orElse(fn () => new Some(666));
+
+ $this->assertTrue($actual->isSome());
+ $this->assertEquals(42, $actual->getValue());
+ }
+
+ public function testGetValueOr () : void {
+ $value = $this->v->getValueOr(666);
+
+ $this->assertEquals(42, $value);
+ }
+
+ public function testGetValueOrElse () : void {
+ $value = $this->v->getValueOrElse(fn () => 666);
$this->assertEquals(42, $value);
}
}
diff --git a/tests/DataTypes/Result/ErrTest.php b/tests/DataTypes/Result/ErrTest.php
index a9f35c3..f73c186 100644
--- a/tests/DataTypes/Result/ErrTest.php
+++ b/tests/DataTypes/Result/ErrTest.php
@@ -1,56 +1,106 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Tests\DataTypes\Result;
use DivisionByZeroError;
use Exception;
+use InvalidArgumentException;
+use RuntimeException;
use Throwable;
use Keruald\OmniTools\DataTypes\Result\Err;
+use Keruald\OmniTools\DataTypes\Result\Ok;
+
use PHPUnit\Framework\TestCase;
class ErrTest extends TestCase {
public function setUp () : void {
$this->v = new Err;
$this->v->setError(new DivisionByZeroError());
}
public function testIsOk () : void {
$this->AssertFalse($this->v->isOk());
}
public function testIsError () : void {
$this->assertTrue($this->v->isError());
}
public function testGetValue () : void {
$this->expectException("InvalidArgumentException");
$this->v->getValue();
}
public function testMap () : void {
$callback = function ($n) {
return $n * 2;
};
$mapped_v = $this->v->map($callback);
$this->assertEquals($mapped_v, $this->v);
}
public function testMapErr () : void {
$callback = function (Throwable $ex) {
return new Exception();
};
$mapped_v = $this->v->mapErr($callback);
$this->assertInstanceOf(Exception::class, $mapped_v->getError());
}
+ public function testOr () : void {
+ $actual = $this->v->or(new Ok(666));
+
+ $this->assertTrue($actual->isOk());
+ $this->assertEquals(666, $actual->getValue());
+ }
+
public function testOrElse () : void {
- $value = $this->v->orElse(666);
+ $actual = $this->v->orElse(fn () => new Ok(666));
+
+ $this->assertTrue($actual->isOk());
+ $this->assertEquals(666, $actual->getValue());
+ }
+
+ public function testOrElseWithCallbackArgument () : void {
+ $actual = $this->v->orElse(
+ fn ($error) => new Err(new RuntimeException(get_class($error)))
+ );
+
+ $this->assertTrue($actual->isError());
+ $this->assertInstanceOf(RuntimeException::class, $actual->getError());
+ $this->assertEquals("DivisionByZeroError", $actual->getError()->getMessage());
+ }
+
+ public function testOrElseWithTooManyCallbackArgument () : void {
+ $this->expectException(InvalidArgumentException::class);
+ $this->v->orElse(fn ($error, $extraneous) => get_class($error));
+ }
+
+ public function testGetValueOr () : void {
+ $value = $this->v->getValueOr(666);
+
+ $this->assertEquals(666, $value);
+ }
+
+ public function testGetValueOrElse () : void {
+ $value = $this->v->getValueOrElse(fn () => 666);
$this->assertEquals(666, $value);
}
+
+ public function testGetValueOrElseWithCallbackArgument () : void {
+ $actual = $this->v->getValueOrElse(fn ($error) => get_class($error));
+
+ $this->assertEquals("DivisionByZeroError", $actual);
+ }
+
+ public function testGetValueOrElseWithTooManyCallbackArgument () : void {
+ $this->expectException(InvalidArgumentException::class);
+ $this->v->getValueOrElse(fn ($error, $extraneous) => get_class($error));
+ }
}
diff --git a/tests/DataTypes/Result/OkTest.php b/tests/DataTypes/Result/OkTest.php
index c73238d..a880702 100644
--- a/tests/DataTypes/Result/OkTest.php
+++ b/tests/DataTypes/Result/OkTest.php
@@ -1,65 +1,85 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Tests\DataTypes\Result;
use Exception;
use Keruald\OmniTools\DataTypes\Result\Ok;
use PHPUnit\Framework\TestCase;
class OkTest extends TestCase {
public function setUp () : void {
$this->v = new Ok;
$this->v->setValue(42);
}
public function testIsOk () : void {
$this->AssertTrue($this->v->isOk());
}
public function testIsError () : void {
$this->assertFalse($this->v->isError());
}
public function testGetValue () : void {
$this->assertEquals(42, $this->v->getValue());
}
public function testSetValue () : void {
$this->v->setValue(666);
$this->assertEquals(666, $this->v->getValue());
}
public function testSetValueWhenTypeIsMutated () : void {
$this->expectException("InvalidArgumentException");
$this->v->setValue("Another type");
}
public function testMap () : void {
$callback = function ($n) {
return $n * 2;
};
$mapped_v = $this->v->map($callback);
$this->assertEquals(84, $mapped_v->getValue());
}
public function testMapErr () : void {
$callback = function (Exception $ex) {
return new Exception();
};
$mapped_v = $this->v->mapErr($callback);
$this->assertEquals($mapped_v, $this->v);
}
+ public function testOr () : void {
+ $actual = $this->v->or(new Ok(666));
+
+ $this->assertTrue($actual->isOk());
+ $this->assertEquals(42, $actual->getValue());
+ }
+
public function testOrElse () : void {
- $value = $this->v->orElse(666);
+ $actual = $this->v->orElse(fn () => new Ok(666));
+
+ $this->assertTrue($actual->isOk());
+ $this->assertEquals(42, $actual->getValue());
+ }
+
+ public function testGetValueOr () : void {
+ $value = $this->v->getValueOr(666);
+
+ $this->assertEquals(42, $value);
+ }
+
+ public function testGetValueOrElse () : void {
+ $value = $this->v->getValueOrElse(fn () => 666);
$this->assertEquals(42, $value);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Mar 18, 12:55 (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3539873
Default Alt Text
(24 KB)
Attached To
Mode
rKOT Keruald OmniTools
Attached
Detach File
Event Timeline
Log In to Comment