Search by

phil / api-contract

phil

Both sides of an unversioned HTTP API contract in plain PHP: RFC 9457 problem details, Deprecation and Sunset signals, tolerant reading of enumerations, and the verification of an OpenAPI document against levels of rules

0.1.0-alpha 2026-11-12 23:21 UTC

This package is auto-updated.

Last update: 2026-11-12 23:52:37 UTC


README

Problem details, deprecation signals, tolerant reading and OpenAPI verification for an HTTP API without version numbers.

phil/api-contract holds both sides of an unversioned HTTP API contract: the server that keeps it and the client that reads it. It writes RFC 9457 problem details strictly and reads them tolerantly. It carries the Deprecation (RFC 9745), Sunset (RFC 8594) and Link: rel="deprecation" signals. It reads enumerations tolerantly, and refuses an unknown value in a field of authority. It verifies an OpenAPI document against three named levels of rules, from a document any tool reads to a contract a client reads without guessing, and reports each finding with its rule, its severity and its place in the document. It does not listen to requests, decorate an HTTP client or store anything. Idempotency keys live in phil/idempotency.

The contract is REST and JSON, with no /v1 in the path; GraphQL is outside it. Every change is additive, and a change that cannot be additive becomes a new resource next to the old one, which announces its own retirement in every response. The package is for developers who publish or consume such an API and want those rules in types and in CI rather than in conventions.

It is a plain PHP library, usable in any PHP code, without dependencies. The record of observed deprecations is a port (ObservedDeprecations) that the caller implements, and a rule of verification an interface (Contract\OpenApiRule) that an application implements for its own rules. In a Symfony application, reach for phil/api-contract-bundle, which wires listeners, #[Idempotent] controllers, an observing HTTP client and a verification command on top of it.

Status

Area Types State
Problem details Problem\ProblemDetails Implemented and tested
Deprecation signals Deprecation\DeprecationSignal, Deprecation\LinkHeader, Http\HttpDate Implemented and tested
Record of observed deprecations Deprecation\ObservedDeprecations Port
Tolerant reading TolerantReading\TolerantEnum, Exception\UnknownAuthorityValue Implemented and tested
OpenAPI verification OpenApi\ContractVerifier, OpenApiDocument, RuleLevel, Severity, Finding, Report, the 17 rules of OpenApi\Rule Implemented and tested
Rule of verification Contract\OpenApiRule Interface
Exceptions Exception\ApiContractException, InvalidContractValue, UnreadableDocument Implemented

Documentation

Document What it covers
Installation Requirements.
Problem details RFC 9457, create() for the server and fromArray() for the client, every check and its reason.
Deprecation and Sunset Writing and reading the three headers, HTTP-dates, Link parsing, the record of observed deprecations.
Tolerant reading Unknown enumeration values: a default, or a refusal in a field of authority.
OpenAPI verification The three levels, the seventeen rules, findings and reports, the bounds of reading.
Extension points ObservedDeprecations, with an example adapter; OpenApiRule, with an example rule.
Testing What the suite pins, and how to test your adapters.
Reference Every public type.

Quick start

use Phil\ApiContract\Deprecation\DeprecationSignal;
use Phil\ApiContract\Problem\ProblemDetails;

$problem = ProblemDetails::create(422, 'https://example.com/problems/out-of-stock', 'Out of stock');
// json_encode($problem->toArray()), Content-Type: application/problem+json

$signal = DeprecationSignal::fromHeaders($headers['deprecation'] ?? null, $headers['sunset'] ?? null, $headers['link'] ?? []);
use Phil\ApiContract\OpenApi\{ContractVerifier, OpenApiDocument, RuleLevel};

$report = ContractVerifier::withBuiltInRules()->verify(OpenApiDocument::fromJson($json), RuleLevel::Strict);
$report->fails(); // true when one finding is an error

Requirements

PHP 8.5 or later.

Contributing

Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.

License

MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Parler Haut Interagir Librement project. See LICENSE.