From adb3be8562ce002cf37da1a57d3b83c00e27a56e Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Mon, 15 Dec 2025 18:02:54 +0330 Subject: [PATCH 01/19] Add error-result proposal. Signed-off-by: Yousha Aleayoub --- proposed/error-result-meta.md | 65 +++++++++ proposed/error-result.md | 240 ++++++++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 proposed/error-result-meta.md create mode 100644 proposed/error-result.md diff --git a/proposed/error-result-meta.md b/proposed/error-result-meta.md new file mode 100644 index 000000000..be771aa7b --- /dev/null +++ b/proposed/error-result-meta.md @@ -0,0 +1,65 @@ +# Error & Result Handling Meta Document + +## 1. Summary + +This PSR proposes standard interfaces for representing operation results and errors in a type-safe, composable manner. It defines: + +- A `ResultInterface` representing the outcome of an operation (success or failure) +- An `ErrorInterface` representing detailed error information +- Standard patterns for chaining, transforming, and inspecting results + +This actually enables libraries to return predictabe, structured outcomes without exceptions while maintaining interoperability. + +## 2. Why Bother? + +### Current Problems + +1. Libraries use mixed approaches (exceptions, error codes, null returns) +2. PHP's type system cant distinguish between valid returns and errors +3. Simple `false` or `null` returns don't carry error details +4. Each framework implements its own result/error objects +5. Not all failures are exceptional; some are expected business cases + +### Benefits + +- Chain operations without try-catch nesting +- Preserve error context across bondaries +- Libraries can share error semantics +- Avoids exception overhead for expected failures +- Distinguishes between technical errors and business rule violations +- _PHPStan/PHPCS can verify error handling_ + +## 3. Scope + +### 3.1 Goals + +- Define standard interfaces for operation results +- Provide base implementation for common use cases +- Enable interoperability between error-aware libraries +- Support both synchronous and asynchronous patterns +- Integrate with existing PSRs (PSR-3 logging, PSR-14 events) +- Be compatible with PHP 7.4+ type systems + +### 3.2 Non-Goals + +- **Not** replacing exceptions for truly exceptional conditions +- **Not** prescribing logging or monitoring implementation +- **Not** definng transport/serialization formats +- **Not** handling global error/exception handlers +- **Not** replacing HTTP status codes in PSR-7/15/18 + +## 4. Approaches + +### 4.1 Chosen Approach: Tagged Union with Monadic Methods + +**Why this approach:** + +- PHP's type system supports it via `isSuccess()`/`isFailure()` discrimination +- Provides both imperative `if` and functional `map()` access patterns +- Familiar to developers from modern languges (Rust, Kotlin, Swift...) +- Maintains PHP's pragmatic balance between OOP and functional patterns +- Can be extended for async/await patterns when Fibers mature + +## 5. People + +- **Proposer**: Yousha Aleayoub - [blog](https://yousha.blog.ir) diff --git a/proposed/error-result.md b/proposed/error-result.md new file mode 100644 index 000000000..c3cd91449 --- /dev/null +++ b/proposed/error-result.md @@ -0,0 +1,240 @@ +# DRAFT: Error and Result Handling + +## 1. Overview + +This document describes standard interfaces for representing operation results in PHP applications. + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119). + +## 2. Definitions + +- **Result**: The outcome of an operation that may succeed or fail. +- **Success Result**: A result containing a succesful value. +- **Failure Result**: A result containing error information. +- **Error**: Structured information abot why an operation failed. + +## 3. Interfaces + +### 3.1 `ErrorInterface` + +```php + + */ + public function getContext(): array; + + /** + * Returns the underlying/previous error. + */ + public function getPrevious(): ?ErrorInterface; + + /** + * Creates a new instance with additional context. + * + * @param array $context + */ + public function withContext(array $context): self; +} +``` + +### 3.2 `ResultInterface` + +```php + + */ + public function map(callable $transform): ResultInterface; + + /** + * Transforms the error. + * + * @template TNewError of ErrorInterface + * @param callable(TError): TNewError $transform + * @return ResultInterface + */ + public function mapError(callable $transform): ResultInterface; + + /** + * Chains operations. + * + * @template TNew + * @param callable(TValue): ResultInterface $operation + * @return ResultInterface + */ + public function then(callable $operation): ResultInterface; + + /** + * Handle both success and failure cases. + * + * @template TReturn + * @param callable(TValue): TReturn $onSuccess + * @param callable(TError): TReturn $onFailure + * @return TReturn + */ + public function fold(callable $onSuccess, callable $onFailure): mixed; + + /** + * Returns the value if successful, otherwise default. + * + * @param TValue $default + * @return TValue + */ + public function getValueOr(mixed $default): mixed; +} +``` + +### 3.3 `ResultFactoryInterface` + +```php + + */ + public function success(mixed $value): ResultInterface; + + /** + * Create a failed result. + * + * @template E of ErrorInterface + * @param E $error + * @return ResultInterface + */ + public function failure(ErrorInterface $error): ResultInterface; + + /** + * Create result from a callable that may throw. + * + * @template T + * @param callable(): T $operation + * @param callable(\Throwable): ErrorInterface $errorMapper + * @return ResultInterface + */ + public function try(callable $operation, callable $errorMapper): ResultInterface; +} +``` + +## 4. Usage Examples + +### 4.1 Basic Usage + +```php +$result = $userRepository->findById($id); + +if ($result->isSuccess()) { + $user = $result->getValue(); + echo "Found: " . $user->getName(); +} else { + $error = $result->getError(); + logError($error->getCode(), $error->getContext()); +} +``` + +### 4.2 Functional paradigm + +```php +$email = $userRepository->findById($id) + ->map(fn($user) => $user->getEmail()) + ->getValueOr('default@example.com'); +``` + +### 4.3 Chaining Operations + +```php +$result = $validator->validate($input) + ->then(fn($validated) => $repository->save($validated)) + ->then(fn($entity) => $notifier->notifyCreated($entity)) + ->mapError(fn($error) => new PublicError($error->getMessage())); +``` + +## 5. Error Classification + +Implementations MAY extend error types: + +- `ValidationError` (business rule violations) +- `NotFoundError` (missing resources) +- `ConflictError` (state conflicts) +- `AuthenticationError` (auth failures) +- `AuthorizationError` (permission issues) +- `InfrastructureError` (system failures) + +## 6. Backward Compatibility + +For gradual adoption, libraries MAY: + +1. Add new methods returning `ResultInterface` alongside old methods +2. Provide adapters from exceptions to results From 82f9440f3d72afe0ab3f616f38a4e450a321a85b Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 10:42:03 +0330 Subject: [PATCH 02/19] Update proposed/error-result-meta.md Co-authored-by: Alexander Makarov --- proposed/error-result-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposed/error-result-meta.md b/proposed/error-result-meta.md index be771aa7b..e60d248d6 100644 --- a/proposed/error-result-meta.md +++ b/proposed/error-result-meta.md @@ -4,7 +4,7 @@ This PSR proposes standard interfaces for representing operation results and errors in a type-safe, composable manner. It defines: -- A `ResultInterface` representing the outcome of an operation (success or failure) +- A `ResultInterface` representing the outcome of an operation (success or failure and the actual result value) - An `ErrorInterface` representing detailed error information - Standard patterns for chaining, transforming, and inspecting results From 7b332be5b4331e60a846b837d851caf322f34a4d Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 10:43:26 +0330 Subject: [PATCH 03/19] Update error-result-meta.md --- proposed/error-result-meta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposed/error-result-meta.md b/proposed/error-result-meta.md index e60d248d6..faed38d8d 100644 --- a/proposed/error-result-meta.md +++ b/proposed/error-result-meta.md @@ -27,7 +27,7 @@ This actually enables libraries to return predictabe, structured outcomes withou - Libraries can share error semantics - Avoids exception overhead for expected failures - Distinguishes between technical errors and business rule violations -- _PHPStan/PHPCS can verify error handling_ +- _PHPStan/PHPCS/Psalm can verify error handling_ ## 3. Scope From 9a019aaacf7cac5de691cb4df9daee3144136b50 Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 10:43:43 +0330 Subject: [PATCH 04/19] Update proposed/error-result.md Co-authored-by: Alexander Makarov --- proposed/error-result.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposed/error-result.md b/proposed/error-result.md index c3cd91449..3c1ef68e5 100644 --- a/proposed/error-result.md +++ b/proposed/error-result.md @@ -11,7 +11,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S - **Result**: The outcome of an operation that may succeed or fail. - **Success Result**: A result containing a succesful value. - **Failure Result**: A result containing error information. -- **Error**: Structured information abot why an operation failed. +- **Error**: Structured information about why an operation failed. ## 3. Interfaces From 9793d5e40c253042ea8ff2a827c5312371ffc59a Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 11:11:11 +0330 Subject: [PATCH 05/19] Update error-result.md Add Execution rules comment at interface level. Improve methods doc comments. --- proposed/error-result.md | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/proposed/error-result.md b/proposed/error-result.md index 3c1ef68e5..459048774 100644 --- a/proposed/error-result.md +++ b/proposed/error-result.md @@ -70,6 +70,11 @@ use Psr\Error\ErrorInterface; /** * Represents the result of an operation. * + * Execution rules: + * - If the result is a failure, `map()` and `then()` MUST NOT call their callbacks. + * - If the result is a success, `mapError()` MUST NOT call its callback. + * - `then()` MUST NOT wrap results; it must flatten them. + * * @template TValue * @template TError of ErrorInterface */ @@ -101,47 +106,41 @@ interface ResultInterface public function getError(): ?ErrorInterface; /** - * Transforms the success value. + * Applies a transformation to the success value. * - * @template TNew - * @param callable(TValue): TNew $transform - * @return ResultInterface + * Called only if the result is successful. + * Failures are propagated unchanged. */ public function map(callable $transform): ResultInterface; /** - * Transforms the error. + * Applies a transformation to the error. * - * @template TNewError of ErrorInterface - * @param callable(TError): TNewError $transform - * @return ResultInterface + * Called only if the result is a failure. + * Success values are propagated unchanged. */ public function mapError(callable $transform): ResultInterface; /** - * Chains operations. + * Chains another operation that returns a Result. * - * @template TNew - * @param callable(TValue): ResultInterface $operation - * @return ResultInterface + * Called only if the result is successful. + * The returned Result is flattened (no nesting). */ public function then(callable $operation): ResultInterface; /** - * Handle both success and failure cases. + * Resolves the result into a single value. * - * @template TReturn - * @param callable(TValue): TReturn $onSuccess - * @param callable(TError): TReturn $onFailure - * @return TReturn + * Exactly one callback is called. + * This terminates the Result pipeline. */ public function fold(callable $onSuccess, callable $onFailure): mixed; /** - * Returns the value if successful, otherwise default. + * Returns the success value or a default if failed. * - * @param TValue $default - * @return TValue + * Does not expose the error. */ public function getValueOr(mixed $default): mixed; } From 263d367bd53f922d6876bf2c305dd0d5a396ee82 Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 11:14:42 +0330 Subject: [PATCH 06/19] Update error-result.md Move Backward Compatibility section to meta document. --- proposed/error-result.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/proposed/error-result.md b/proposed/error-result.md index 459048774..a23afa207 100644 --- a/proposed/error-result.md +++ b/proposed/error-result.md @@ -230,10 +230,3 @@ Implementations MAY extend error types: - `AuthenticationError` (auth failures) - `AuthorizationError` (permission issues) - `InfrastructureError` (system failures) - -## 6. Backward Compatibility - -For gradual adoption, libraries MAY: - -1. Add new methods returning `ResultInterface` alongside old methods -2. Provide adapters from exceptions to results From 9a56db9a0e7e338ae707551dac67869b1f23f32d Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 11:15:50 +0330 Subject: [PATCH 07/19] Update error-result-meta.md Add Backward Compatibility section. --- proposed/error-result-meta.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/proposed/error-result-meta.md b/proposed/error-result-meta.md index faed38d8d..a3293159a 100644 --- a/proposed/error-result-meta.md +++ b/proposed/error-result-meta.md @@ -60,6 +60,13 @@ This actually enables libraries to return predictabe, structured outcomes withou - Maintains PHP's pragmatic balance between OOP and functional patterns - Can be extended for async/await patterns when Fibers mature -## 5. People +## 5. Backward Compatibility + +For gradual adoption, libraries MAY: + +1. Add new methods returning `ResultInterface` alongside old methods +2. Provide adapters from exceptions to results + +## 6. People - **Proposer**: Yousha Aleayoub - [blog](https://yousha.blog.ir) From 2ce8ecf1c1891219896396c986e22f8dcce04827 Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 11:18:45 +0330 Subject: [PATCH 08/19] Update error-result.md Move section to meta document. --- proposed/error-result.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/proposed/error-result.md b/proposed/error-result.md index a23afa207..d8ce4db25 100644 --- a/proposed/error-result.md +++ b/proposed/error-result.md @@ -219,14 +219,3 @@ $result = $validator->validate($input) ->then(fn($entity) => $notifier->notifyCreated($entity)) ->mapError(fn($error) => new PublicError($error->getMessage())); ``` - -## 5. Error Classification - -Implementations MAY extend error types: - -- `ValidationError` (business rule violations) -- `NotFoundError` (missing resources) -- `ConflictError` (state conflicts) -- `AuthenticationError` (auth failures) -- `AuthorizationError` (permission issues) -- `InfrastructureError` (system failures) From 6c15f173eb940552b90f3e021aa2b2916c1f89b5 Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 11:19:34 +0330 Subject: [PATCH 09/19] Update error-result-meta.md Add Error Classification section. --- proposed/error-result-meta.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/proposed/error-result-meta.md b/proposed/error-result-meta.md index a3293159a..614831f4b 100644 --- a/proposed/error-result-meta.md +++ b/proposed/error-result-meta.md @@ -67,6 +67,17 @@ For gradual adoption, libraries MAY: 1. Add new methods returning `ResultInterface` alongside old methods 2. Provide adapters from exceptions to results -## 6. People +## 6. Error Classification + +Implementations MAY extend error types: + +- `ValidationError` (business rule violations) +- `NotFoundError` (missing resources) +- `ConflictError` (state conflicts) +- `AuthenticationError` (auth failures) +- `AuthorizationError` (permission issues) +- `InfrastructureError` (system failures) + +## 7. People - **Proposer**: Yousha Aleayoub - [blog](https://yousha.blog.ir) From a49cf19336a18ddd2b3926fb1e1a3bad7912cdbc Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 11:26:08 +0330 Subject: [PATCH 10/19] Update error-result.md Improve example. --- proposed/error-result.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/proposed/error-result.md b/proposed/error-result.md index d8ce4db25..5fbf1b8dd 100644 --- a/proposed/error-result.md +++ b/proposed/error-result.md @@ -215,7 +215,14 @@ $email = $userRepository->findById($id) ```php $result = $validator->validate($input) - ->then(fn($validated) => $repository->save($validated)) - ->then(fn($entity) => $notifier->notifyCreated($entity)) - ->mapError(fn($error) => new PublicError($error->getMessage())); + ->then(fn($v) => $repository->save($v)) + ->then(fn($e) => $notifier->notifyCreated($e)) + ->mapError(fn($err) => new PublicError($err->getMessage())); + +// At the end... +if ($result->isSuccess()) { + $finalEntity = $result->getValue(); // From notifier. +} else { + $publicError = $result->getError(); // Already transformed to PublicError. +} ``` From 6839a96e788e7f04ac10382c95570200e2b5ca51 Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 11:39:29 +0330 Subject: [PATCH 11/19] Update error-result-meta.md Add Typing and Generics section. Add group address. --- proposed/error-result-meta.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/proposed/error-result-meta.md b/proposed/error-result-meta.md index 614831f4b..23ae844f1 100644 --- a/proposed/error-result-meta.md +++ b/proposed/error-result-meta.md @@ -78,6 +78,16 @@ Implementations MAY extend error types: - `AuthorizationError` (permission issues) - `InfrastructureError` (system failures) -## 7. People +- ## 7. Typing and Generics -- **Proposer**: Yousha Aleayoub - [blog](https://yousha.blog.ir) +Since PHP does not currently support generics at the language level: + +- This PSR uses phpdoc-based generics (@template) to express the relationship between success values and error values, following established practice in the PHP ecosystem. +- Implementations are expected to enforce the invariant that a Result contains either a success value or an error, but never both. +- Consumers SHOULD rely on `isSuccess()` / `isFailure()` (or equivalent terminal operations such as `fold()`) before accessing the contained value. +- This PSR does not require nor encourage implementors to create separate result classes for each possible value or error type. + +## 8. People + +- **Proposer:** Yousha Aleayoub - [blog](https://yousha.blog.ir) +- **Group:** https://groups.google.com/g/php-fig/c/OpEuvGERM5A From 06881d7aa0f3309630a9a40b8d02508c824bec9c Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 11:51:27 +0330 Subject: [PATCH 12/19] Update error-result-meta.md Add PSR-3 notes. --- proposed/error-result-meta.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposed/error-result-meta.md b/proposed/error-result-meta.md index 23ae844f1..d8f3157a5 100644 --- a/proposed/error-result-meta.md +++ b/proposed/error-result-meta.md @@ -10,6 +10,8 @@ This PSR proposes standard interfaces for representing operation results and err This actually enables libraries to return predictabe, structured outcomes without exceptions while maintaining interoperability. +Note: Error messages follow PSR-3 placeholder semantics. This enables deferred formatting, localization, and structured validation output without imposing a formatter or translator. + ## 2. Why Bother? ### Current Problems From 95da5f8957f14c4275a276004582494ce5b322f0 Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 11:53:34 +0330 Subject: [PATCH 13/19] Update error-result.md Improve docblocks for PSR-3. --- proposed/error-result.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/proposed/error-result.md b/proposed/error-result.md index 5fbf1b8dd..404a8c5da 100644 --- a/proposed/error-result.md +++ b/proposed/error-result.md @@ -33,12 +33,18 @@ interface ErrorInterface extends \Throwable public function getCode(): string; /** - * Returns human-readable error message. + * Returns a human-readable error message. + * + * The message MAY contain placeholders in the form `{placeholder}`. + * Context values MAY be provided to replace placeholders for display, + * logging, or translation purposes. + * + * Message formatting and translation are the responsibility of the consumer. */ public function getMessage(): string; /** - * Returns additional error context. + * Returns structured context data for the error. * * @return array */ From f5bf499e9a46bbd2b079b39f4c7b5fbde7c3820a Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 17:33:13 +0330 Subject: [PATCH 14/19] Update error-result.md --- proposed/error-result.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposed/error-result.md b/proposed/error-result.md index 404a8c5da..1729ce263 100644 --- a/proposed/error-result.md +++ b/proposed/error-result.md @@ -29,6 +29,10 @@ interface ErrorInterface extends \Throwable { /** * Returns a machine-readable error code. + * + * This code is a stable identifier intended for programmatic use + * (like UUIDs, HTTP status codes, enum-like values, localization, PDO/SQLSTATE), and is not related + * to Throwable::getCode(). */ public function getCode(): string; From 2e0180dfd70e8e9817f663511ca17fbb2d794903 Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 17:52:46 +0330 Subject: [PATCH 15/19] Update error-result.md Improved doc comments. --- proposed/error-result.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/proposed/error-result.md b/proposed/error-result.md index 1729ce263..d56fb47be 100644 --- a/proposed/error-result.md +++ b/proposed/error-result.md @@ -120,6 +120,11 @@ interface ResultInterface * * Called only if the result is successful. * Failures are propagated unchanged. + * + * @template TNew + * @param callable(TValue): TNew $transform + * A callable that receives the success value and returns a new value. + * @return ResultInterface */ public function map(callable $transform): ResultInterface; @@ -128,6 +133,11 @@ interface ResultInterface * * Called only if the result is a failure. * Success values are propagated unchanged. + * + * @template TNewError of ErrorInterface + * @param callable(TError): TNewError $transform + * A callable that receives the error and returns a new error. + * @return ResultInterface */ public function mapError(callable $transform): ResultInterface; @@ -136,6 +146,11 @@ interface ResultInterface * * Called only if the result is successful. * The returned Result is flattened (no nesting). + * + * @template TNew + * @param callable(TValue): ResultInterface $operation + * A callable that receives the success value and returns a Result. + * @return ResultInterface */ public function then(callable $operation): ResultInterface; @@ -144,6 +159,11 @@ interface ResultInterface * * Exactly one callback is called. * This terminates the Result pipeline. + * + * @template TReturn + * @param callable(TValue): TReturn $onSuccess + * @param callable(TError): TReturn $onFailure + * @return TReturn */ public function fold(callable $onSuccess, callable $onFailure): mixed; @@ -151,6 +171,10 @@ interface ResultInterface * Returns the success value or a default if failed. * * Does not expose the error. + * + * @param TValue $default + * The value to return if the result is a failure. + * @return TValue */ public function getValueOr(mixed $default): mixed; } From dd2ec696d406cf04d37fe63ec20016f5d22834ad Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 18:11:51 +0330 Subject: [PATCH 16/19] Update error-result-meta.md Add Namespaces section. --- proposed/error-result-meta.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/proposed/error-result-meta.md b/proposed/error-result-meta.md index d8f3157a5..46f98598f 100644 --- a/proposed/error-result-meta.md +++ b/proposed/error-result-meta.md @@ -80,7 +80,7 @@ Implementations MAY extend error types: - `AuthorizationError` (permission issues) - `InfrastructureError` (system failures) -- ## 7. Typing and Generics +## 7. Typing and Generics Since PHP does not currently support generics at the language level: @@ -89,7 +89,14 @@ Since PHP does not currently support generics at the language level: - Consumers SHOULD rely on `isSuccess()` / `isFailure()` (or equivalent terminal operations such as `fold()`) before accessing the contained value. - This PSR does not require nor encourage implementors to create separate result classes for each possible value or error type. -## 8. People +## 8. Namespaces +- `ErrorInterface` is defined in the `Psr\Error` namespace to allow error objects to be reused independently of `ResultInterface`. +- Errors in this PSR are plain value objects that may be created, transformed, transported, logged, or rendered without necessarily being wrapped in a `Result`. +- `ResultInterface` composes an error but does not own the error abstraction. Keeping these concerns in separate namespaces avoids coupling error representation to a single control-flow mechanism and preserves flexibility for future use-cases. + +## 9. People + +- **PHP-FIG team** - **Proposer:** Yousha Aleayoub - [blog](https://yousha.blog.ir) -- **Group:** https://groups.google.com/g/php-fig/c/OpEuvGERM5A +- **Discussion Group:** https://groups.google.com/g/php-fig/c/OpEuvGERM5A From 2b705457bef0e2bdf03b4d68993c2392e3dcb194 Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 18:28:35 +0330 Subject: [PATCH 17/19] Update error-result.md --- proposed/error-result.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposed/error-result.md b/proposed/error-result.md index d56fb47be..b33f8f94c 100644 --- a/proposed/error-result.md +++ b/proposed/error-result.md @@ -104,7 +104,7 @@ interface ResultInterface * Returns the success value * * @return TValue - * @throws \RuntimeException If result is a failure. + * @throws \BadMethodCallException If result is a failure. */ public function getValue(): mixed; From 9965b150574d7d3ba33d4aa2f2b202e6fa434fcd Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 18:47:41 +0330 Subject: [PATCH 18/19] Update error-result-meta.md Add Note #2 for optional factory interface. --- proposed/error-result-meta.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposed/error-result-meta.md b/proposed/error-result-meta.md index 46f98598f..a85dc15d4 100644 --- a/proposed/error-result-meta.md +++ b/proposed/error-result-meta.md @@ -12,6 +12,8 @@ This actually enables libraries to return predictabe, structured outcomes withou Note: Error messages follow PSR-3 placeholder semantics. This enables deferred formatting, localization, and structured validation output without imposing a formatter or translator. +Note #2: `ResultFactoryInterface` is OPTIONAL. Libraries and apps MAY use direct construction or static named constructors (like Result::success()), if they control the implementation. The factory interface exists only to enable interoperable creation when implementations are abstracted. + ## 2. Why Bother? ### Current Problems From e1c191e02705602fd34541cea0fd3d9f21caebe9 Mon Sep 17 00:00:00 2001 From: Yousha Aleayoub Date: Wed, 17 Dec 2025 19:01:22 +0330 Subject: [PATCH 19/19] Update error-result-meta.md Add Note #3 Either Monad pettern. --- proposed/error-result-meta.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposed/error-result-meta.md b/proposed/error-result-meta.md index a85dc15d4..78af93bbf 100644 --- a/proposed/error-result-meta.md +++ b/proposed/error-result-meta.md @@ -14,6 +14,8 @@ Note: Error messages follow PSR-3 placeholder semantics. This enables deferred f Note #2: `ResultFactoryInterface` is OPTIONAL. Libraries and apps MAY use direct construction or static named constructors (like Result::success()), if they control the implementation. The factory interface exists only to enable interoperable creation when implementations are abstracted. +Note #3: This PSR implements the well-established Either monad (from functional programming), commonly used in Haskell, Scala, Rust, and Kotlin. The interface provides map(fmap), mapError(leftMap), then(flatMap/bind), and fold(either)—all standard Either operations. + ## 2. Why Bother? ### Current Problems