diff --git a/composer.json b/composer.json
index 85ca832..2dd2b85 100644
--- a/composer.json
+++ b/composer.json
@@ -21,7 +21,8 @@
}
],
"require": {
- "php": "^8.1"
+ "php": "^8.1",
+ "texthtml/object-reaper": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0|^11.0|^12.0",
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 3095b52..49996b3 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -7,6 +7,8 @@ parameters:
check:
missingCheckedExceptionInThrows: true
tooWideThrowType: true
+ uncheckedExceptionClasses:
+ - LogicException
ignoreErrors:
-
message: '#Unreachable statement - code above always terminates.#'
diff --git a/psalm.xml b/psalm.xml
index f3a177a..7928432 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -26,9 +26,15 @@
+
+
+
+
+
+
-
+
@@ -73,4 +79,7 @@
+
+
+
diff --git a/src/Option/Some.php b/src/Option/Some.php
index b5bcf5d..36431a2 100644
--- a/src/Option/Some.php
+++ b/src/Option/Some.php
@@ -198,7 +198,7 @@ public function zip(Option $option): Option
* @param Option $option
* @param callable(T, U):V $callback
* @return (U is never ? Option\None : Option)
- * @return Option
+ * @phpstan-return Option
*/
public function zipWith(Option $option, callable $callback): Option
{
diff --git a/src/Result/MustBeUsed.php b/src/Result/MustBeUsed.php
index 27ddefb..6fd968b 100644
--- a/src/Result/MustBeUsed.php
+++ b/src/Result/MustBeUsed.php
@@ -2,64 +2,46 @@
namespace TH\Maybe\Result;
-use TH\Maybe\Result;
+use TH\ObjectReaper\Reaper;
/** @internal */
trait MustBeUsed
{
- /** @var \ArrayAccess,ResultCreationTrace>|null */
- private static ?\ArrayAccess $toBeUsed = null;
-
- /**
- * @return \ArrayAccess,ResultCreationTrace>
- */
- private static function toBeUsedMap(): \ArrayAccess
+ /** @return \WeakMap */
+ private static function reapers(): \WeakMap
{
- return self::$toBeUsed ??= self::emptyToBeUsedMap();
- }
+ static $reapers;
- /**
- * @return \WeakMap,ResultCreationTrace>
- */
- private static function emptyToBeUsedMap(): \WeakMap
- {
- /** @var \WeakMap,ResultCreationTrace> */
- return new \WeakMap();
+ return $reapers ??= new \WeakMap();
}
/**
- * Mark a result as needed to be used
+ * Mark a result as needed to be used. Must be called at most once on an object.
*/
private function mustBeUsed(): void
{
- self::toBeUsedMap()[$this] = new ResultCreationTrace();
+ $reapers = self::reapers();
+
+ if (isset($reapers[$this])) {
+ throw new \LogicException('Object already register to be used');
+ }
+
+ $creationTrace = new ResultCreationTrace();
+
+ $reapers[$this] = Reaper::watch($this, static fn () => throw new UnusedResultException($creationTrace));
}
/**
- * Mark a result as used
+ * Mark a result as used.
*/
private function used(): void
{
- unset(self::toBeUsedMap()[$this]);
+ $reaper = self::reapers()[$this] ?? throw new \LogicException('Object not registered to be used');
+ $reaper->forget();
}
public function __clone()
{
$this->mustBeUsed();
}
-
- /**
- * @throws UnusedResultException
- */
- public function __destruct()
- {
- $map = self::toBeUsedMap();
-
- if (isset($map[$this])) {
- $creationTrace = $map[$this];
- unset($map[$this]);
-
- throw new UnusedResultException($creationTrace);
- }
- }
}
diff --git a/src/Result/ResultCreationTrace.php b/src/Result/ResultCreationTrace.php
index 07c4b78..bea3e66 100644
--- a/src/Result/ResultCreationTrace.php
+++ b/src/Result/ResultCreationTrace.php
@@ -3,7 +3,7 @@
namespace TH\Maybe\Result;
/** @internal */
-final class ResultCreationTrace extends \RuntimeException
+final class ResultCreationTrace extends \LogicException
{
public function __construct()
{
diff --git a/src/functions/internal.php b/src/functions/internal.php
new file mode 100644
index 0000000..a42ed9e
--- /dev/null
+++ b/src/functions/internal.php
@@ -0,0 +1,31 @@
+ $exceptionClasses
+ * @throws \Throwable
+ * @return T
+ * @internal
+ * @nodoc
+ */
+function trap(
+ \Throwable $error,
+ callable $callback,
+ string ...$exceptionClasses,
+): mixed {
+ foreach ($exceptionClasses as $exceptionClass) {
+ if (\is_a($error, $exceptionClass)) {
+ return $callback($error);
+ }
+ }
+
+ throw $error;
+}
diff --git a/src/functions/option.php b/src/functions/option.php
index 509e2bb..bb1dc48 100644
--- a/src/functions/option.php
+++ b/src/functions/option.php
@@ -209,8 +209,7 @@ function unzip(Option $option): array
function transpose(Option $option): Result
{
/** @var Result