Per RFC-WS-6 §Q3 v1.3 addition 2. Centralises the Throwable -> failure_response_code mapping so the listener (ApplyBindingsOnFormSubmit::handle catch block) and the retry-service (FormFailureRetryService::recordFailure) produce identical classifications. Single behaviour-change point. Resolution order: FormBindingApplicatorException subclass dispatch via reasonCode(); fallback 'unknown_error' for anything outside the hierarchy. Wiring into the listener and the retry service lands in D2. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
40 lines
1.3 KiB
PHP
40 lines
1.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\FormBuilder\Bindings;
|
|
|
|
use App\Exceptions\FormBuilder\FormBindingApplicatorException;
|
|
use Throwable;
|
|
|
|
/**
|
|
* Maps a Throwable to a failure_response_code string.
|
|
*
|
|
* Used by both ApplyBindingsOnFormSubmit::handle's catch block (D2) and
|
|
* FormFailureRetryService::recordFailure (D2 update). Centralised so the
|
|
* listener and the retry-service produce identical classifications and a
|
|
* single behaviour change requires a single edit.
|
|
*
|
|
* Resolution order:
|
|
* 1. If the Throwable is a FormBindingApplicatorException, return its reasonCode().
|
|
* Subclass dispatch handles SchemaConfig / Infra / DataIntegrity / Timeout
|
|
* (Timeout extends Infra so it inherits 'temporary_error').
|
|
* 2. Otherwise, return 'unknown_error' — anything outside the hierarchy
|
|
* (database connection lost not surfaced as Infra, generic RuntimeException
|
|
* from a non-applicator code path, IdentityMatchInvariantViolation if it
|
|
* somehow leaks here) is unknown from the response-shape perspective.
|
|
*
|
|
* Per RFC-WS-6 §Q3 v1.3 addition 2.
|
|
*/
|
|
final class FormBindingExceptionClassifier
|
|
{
|
|
public static function classify(Throwable $exception): string
|
|
{
|
|
if ($exception instanceof FormBindingApplicatorException) {
|
|
return $exception->reasonCode();
|
|
}
|
|
|
|
return 'unknown_error';
|
|
}
|
|
}
|