prauga / flexdoc
Self-hosted FlexDoc OpenAPI renderer integration for PHP, Laravel, and Symfony
Requires
- php: >=8.2
Requires (Dev)
- illuminate/config: ^11.0 || ^12.0
- illuminate/container: ^11.0 || ^12.0
- illuminate/events: ^11.0 || ^12.0
- illuminate/http: ^11.0 || ^12.0
- illuminate/routing: ^11.0 || ^12.0
- illuminate/support: ^11.0 || ^12.0
- symfony/http-foundation: ^7.2
Suggests
- ext-openssl: Required for native HTTPS host execution
- illuminate/routing: Required for the Laravel integration
- illuminate/support: Required for Laravel service-provider auto-discovery
- symfony/http-foundation: Required for the Symfony controller integration
Provides
None
Conflicts
None
Replaces
None
README
prauga/flexdoc 0.4.5 provides a framework-neutral PHP 8.2+ host for the canonical FlexDoc renderer, plus thin Laravel and Symfony integrations. It is self-hosted and does not require a runtime CDN.
The canonical source remains in the FlexDoc monorepo. Prauga/flexdoc-php is the Composer/Packagist distribution repository and is synchronized automatically from this package directory.
Generic PHP
use Prauga\FlexDoc\FlexDocConfig; use Prauga\FlexDoc\FlexDocHost; $host = new FlexDocHost(new FlexDocConfig(path: '/docs', specUrl: '/openapi.json', title: 'My API'));
FlexDocConfig also accepts expand, tryItDefaultServer, tryItCredentials, and tryItApiClientPersistenceKey; expand may be a preset string or section array, and the persistence key may be a string or false.
Map responseForPath() or the explicit response methods through your HTTP framework.
Native API-host execution (3.3)
PHP can execute Try It requests from the API host without adding cURL or a third-party HTTP client. Native HTTPS execution requires PHP's OpenSSL extension; Composer lists ext-openssl as the dependency for that optional transport so renderer-only HTTP installations are not forced to enable it. Create a native executor with an explicit exact-origin allowlist and attach it to the renderer configuration:
use Prauga\FlexDoc\FlexDocConfig; use Prauga\FlexDoc\FlexDocHost; use Prauga\FlexDoc\HostExecution; $executor = new HostExecution([ 'https://api.example.internal', ]); $host = new FlexDocHost(new FlexDocConfig( path: '/docs', specUrl: '/openapi.json', tryItHostExecution: true, hostExecution: $executor, ));
When both tryItHostExecution: true and a real HostExecution are present, FlexDoc truthfully advertises hostExecution.available: true and the host owns POST /docs/__flexdoc/execute. Enabling the flag without an executor keeps available: false, and the execute path remains unregistered (404). The allowlist is server-only configuration and is never serialized into the docs HTML.
The PHP executor consumes the same canonical JSON or multipart envelope used by the other native hosts and FlexDoc Runner. It requires X-FlexDoc-Execute: 1, accepts only explicitly allowlisted HTTP(S) origins, strips unsafe transport headers, rejects cross-origin redirects, bounds incoming envelopes to 32 MiB and target response bodies to 10 MiB, bounds response header parsing, and applies a full-request deadline. Basic, Bearer, OAuth2 bearer-token, and header/query API-key request auth are supported as canonical request-draft features.
This first PHP slice intentionally advertises an empty host-only capability list. Session cookie jars, client certificates, Digest, Hawk, NTLM/Negotiate, OAuth 1.0, AWS Signature V4 remain unavailable until implemented natively. The 3.3 client host-routing change is responsible for sending ordinary Try It requests through an available native executor; capability entries remain reserved for additional host-only features.
The transport uses PHP's standard socket/TLS runtime rather than requiring ext-curl. Hostnames are resolved and validated first, then the connection is opened directly to one of the validated addresses while retaining the original hostname for the HTTP Host header and TLS SNI/certificate verification. Link-local/cloud-metadata destinations, including IPv4-mapped IPv6 forms, are rejected before connection. System HTTP proxy variables are not consulted by this socket transport.
The execute endpoint is a server-side network capability. Protect the docs subtree and __flexdoc/execute with the same application authentication/authorization policy you expect for the API documentation. X-FlexDoc-Execute: 1 is a protocol marker and cross-site friction, not a replacement for application authorization or the framework's CSRF model.
For framework-neutral integrations, route responseForRequest() so POST requests to the execute path can pass request headers/body and, for multipart requests, the parsed descriptor and uploaded formData[n] files.
Laravel
Laravel package auto-discovery loads FlexDocServiceProvider, which binds FlexDocHost and registers the docs and renderer routes. Configure flexdoc.path, flexdoc.spec_url, flexdoc.title, flexdoc.theme, and flexdoc.try_it_enabled in the application config. The adapter also accepts expand, try_it_default_server, try_it_credentials, and try_it_api_client_persistence_key; unset renderer settings are omitted. FLEXDOC_TRY_IT=false is parsed as a boolean and disables Try It. LaravelFlexDoc::register($router, $host) is also available for manual routing.
For native host execution, enable try_it_host_execution and provide host_execution_allowed_origins as an array or comma-separated string. The packaged config exposes FLEXDOC_HOST_EXECUTION and FLEXDOC_HOST_EXECUTION_ALLOWED_ORIGINS. Laravel registers the POST execute route only when a real executor can be created from a non-empty allowlist.
Protect the whole FlexDoc route set with Laravel middleware. With auto-discovery, set flexdoc.middleware to a non-empty array such as ['auth'], or set a comma-separated environment value such as:
FLEXDOC_MIDDLEWARE=auth,verified
For manual registration, pass the middleware directly:
LaravelFlexDoc::register($router, $host, ['auth', 'verified']);
Fail-closed rule: when try_it_host_execution / FLEXDOC_HOST_EXECUTION is enabled through the service provider and the normalized flexdoc.middleware list is empty, boot throws LogicException instead of registering an unauthenticated execute route. An origin allowlist restricts outbound destinations; it is not user authentication.
The configured middleware is attached to the docs page, renderer assets, and execute POST together. If your selected middleware stack includes Laravel's CSRF verifier, configure the application so POST /docs/__flexdoc/execute is exempted only when an equivalent API-style authentication boundary is used, or send the application's required CSRF credential from the surrounding integration. The browser execute protocol sends X-FlexDoc-Execute: 1, not a Laravel CSRF token. Do not expose the execute route without application authorization merely because the destination origin is allowlisted.
Laravel normalizes the request path used for route matching, so the single docs route serves both /docs and /docs/; the package integration tests dispatch both forms explicitly.
Symfony
Register FlexDocHost as a service and inject it into Prauga\FlexDoc\Symfony\FlexDocController. Route /docs, /docs/__flexdoc/renderer.js, and /docs/__flexdoc/renderer.css to the controller's corresponding methods. When native execution is enabled on the injected host, route POST /docs/__flexdoc/execute to FlexDocController::execute.
Apply the same authenticated firewall/access-control policy to the documentation and execute paths. For example, with a normal authenticated application firewall:
# config/packages/security.yaml security: firewalls: main: lazy: true provider: app_user_provider # configure the application's authenticator(s) here access_control: # Keep the explicit execute rule before the broader docs subtree rule. - { path: ^/docs/__flexdoc/execute$, roles: ROLE_API_DOCS, methods: [POST] } - { path: ^/docs(?:/|$), roles: ROLE_API_DOCS }
After that application protection is configured, construct the controller with the explicit acknowledgement:
$controller = new FlexDocController($host, hostExecutionProtected: true);
Fail-closed rule: if the injected host has native execution available and hostExecutionProtected is omitted or false, controller construction throws LogicException. The flag is only an assertion that the application firewall/access-control boundary above exists; it does not create authentication itself. An origin allowlist restricts outbound destinations and is not user authentication.
If the application uses cookie/session authentication, keep the execute POST inside the application's CSRF strategy or provide an equivalent API-authenticated boundary. X-FlexDoc-Execute: 1 is not a Symfony CSRF token.
Packaging
The package contains the version-matched assets/flexdoc.standalone.{js,css}. PHP CI byte-compares them with packages/client/dist/standalone.