Follow-up from #364 / #367.
Context
After #367 lands, JsonSchemaException carries structured errors but does not distinguish source: it is thrown from both JsonSchemaInterceptor::validateRequest() (client-bad-input → 4xx) and validateResponse() (server-produced-bad-output → 5xx) as the same type.
Routing today happens only via the handler interface that fires:
JsonSchemaRequestExceptionHandlerInterface ← request validation
JsonSchemaExceptionHandlerInterface ← response validation
If the same exception is caught outside that handler chain (a generic logger, a unified ErrorObject, a try/catch around a resource call) the consumer cannot tell whether to surface a 4xx or a 5xx — even though the HTTP-level semantic is the opposite.
Proposal
Introduce two concrete subclasses of JsonSchemaException so that instanceof discriminates source:
class JsonSchemaException extends LogicException implements ExceptionInterface { /* unchanged */ }
final class JsonSchemaRequestException extends JsonSchemaException {} // client input failed validation
final class JsonSchemaResponseException extends JsonSchemaException {} // own response failed validation
JsonSchemaException loses final so subclassing is permitted.
- Interceptor throws the appropriate concrete subclass.
- Existing
catch (JsonSchemaException $e) keeps working — BC safe.
- New code can narrow:
catch (JsonSchemaRequestException $e) { return new ErrorObject(422, ...); }.
Alternatives considered
- Discriminator field (
public readonly JsonSchemaSource $source enum). Single class, smaller API surface. But narrow-by-instanceof is more idiomatic in PHP and reads better at catch sites.
- Leave as-is — handler context already routes. But that forces every direct-catch consumer to re-implement the dispatch logic.
Out of scope
- The actual HTTP status assignment lives in the resource / framework layer, not in this exception change.
- Handler interface signatures stay as-is for now (they already discriminate source by which one is called).
cc @koriym
Follow-up from #364 / #367.
Context
After #367 lands,
JsonSchemaExceptioncarries structured errors but does not distinguish source: it is thrown from bothJsonSchemaInterceptor::validateRequest()(client-bad-input → 4xx) andvalidateResponse()(server-produced-bad-output → 5xx) as the same type.Routing today happens only via the handler interface that fires:
JsonSchemaRequestExceptionHandlerInterface← request validationJsonSchemaExceptionHandlerInterface← response validationIf the same exception is caught outside that handler chain (a generic logger, a unified ErrorObject, a try/catch around a resource call) the consumer cannot tell whether to surface a 4xx or a 5xx — even though the HTTP-level semantic is the opposite.
Proposal
Introduce two concrete subclasses of
JsonSchemaExceptionso thatinstanceofdiscriminates source:JsonSchemaExceptionlosesfinalso subclassing is permitted.catch (JsonSchemaException $e)keeps working — BC safe.catch (JsonSchemaRequestException $e) { return new ErrorObject(422, ...); }.Alternatives considered
public readonly JsonSchemaSource $sourceenum). Single class, smaller API surface. But narrow-by-instanceofis more idiomatic in PHP and reads better at catch sites.Out of scope
cc @koriym