ez-php / asyncapi
AsyncAPI 3.0 document generation for broadcast channels, webhooks and WebSocket messages, attribute-driven
Requires
- php: ^8.5
- ez-php/contracts: ^2.7.0
- ez-php/http: ^2.7.0
- ez-php/json-schema: ^2.7.0
Requires (Dev)
- ez-php/docker: ^2.7.0
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
AsyncAPI 3.0 documentation for the event-driven side of an ez-php application: broadcast channels, outgoing webhooks, WebSocket messages. Attribute-driven like ez-php/openapi, with payload schemas from ez-php/json-schema.
Installation
composer require ez-php/asyncapi
Register the provider (provider/modules.php):
EzPhp\AsyncApi\AsyncApiServiceProvider::class,
Annotating messages
Put #[Channel] and #[Message] on the class that is sent or received. Its public properties are the payload:
use EzPhp\AsyncApi\Attributes\Channel; use EzPhp\AsyncApi\Attributes\Message; #[Channel('orders.{orderId}', description: 'Lifecycle events of one order')] #[Message('order.shipped', summary: 'An order left the warehouse')] final class OrderShipped { public function __construct( public string $orderId, public DateTimeImmutable $shippedAt, ) {} } #[Channel('chat')] #[Message('chat.message', action: Message::RECEIVE)] // incoming WebSocket message final class ChatMessage { public function __construct(public string $text) {} }
action:send(default; the application publishes it) orreceive(the application consumes it).{orderId}becomes a channel parameter.- Classes with the same address share one channel.
Configuration
config/asyncapi.php:
return [ 'endpoint' => getenv('ASYNCAPI_ENDPOINT') ?: '/asyncapi.json', 'classes' => [OrderShipped::class, ChatMessage::class], 'servers' => [ 'production' => ['host' => 'ws.example.com', 'protocol' => 'wss'], ], ];
There is no discovery: only listed classes are documented. A listed class that is missing, lacks an attribute, clashes on short name, or has an address that maps to another address's channel id (orders.{id} and orders/{id} both become orders_id) throws AsyncApiException. Title and version come from app.name / app.version.
Output
GET /asyncapi.json returns:
channels: one per address, with{param}parameters.operations:sendOrderShipped,receiveChatMessage, ...components.messages: one per class, keyed by its short name.components.schemas: the payload JSON Schemas.
Generate it without the endpoint:
$document = (new AsyncApiGenerator([OrderShipped::class], 'Shop', '1.0.0'))->generate()->toArray();
Not included
YAML output, an HTML/Studio viewer, code generation.
License
MIT