Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Openapi Laravel Package

cebe/php-openapi

Read and write OpenAPI 3.0.x YAML and JSON files as PHP objects, with a CLI tool for validating and converting descriptions. A low-level library used by higher-level API tooling.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package alongside your Laravel app:

composer require cebe/php-openapi

This gives you two things: the cebe\openapi\Reader/Writer API for programmatic access, and a vendor/bin/php-openapi CLI binary for validation and conversion. For a Laravel project you'll typically want both.

Where to look first. Open Reader, Writer, and the cebe\openapi\spec\* classes. Every OpenAPI object (OpenApi, Info, PathItem, Operation, Schema, etc.) is a typed PHP class with public properties matching the spec field names — so $spec->info->title, $spec->paths['/users']->get->responses['200'], and $spec->components->schemas['User'] map 1:1 to the YAML you already know. Skim the "Completeness" table in the README to confirm the object you need is implemented (it's essentially complete for 3.0.x).

Your first use case — turn a spec you already have into a validated, introspectable object inside an artisan command:

use cebe\openapi\Reader;

$path = base_path('openapi.yaml');
$spec = Reader::readFromYamlFile(realpath($path)); // realpath matters, see Gotchas

if (!$spec->validate()) {
    foreach ($spec->getErrors() as $error) {
        $this->error($error);
    }
    return self::FAILURE;
}

$this->info($spec->info->title . ' ' . $spec->info->version);
$this->info('Paths: ' . count(iterator_to_array($spec->paths)));

Run php artisan tinker and inspect the parsed object — this is the fastest way to learn the shape of the API before wiring it into anything.

Implementation Patterns

1. Spec-driven request validation. The most common Laravel use is reading the spec once and validating incoming requests against it. cebe/php-openapi gives you the parsed object; pair it with league/openapi-psr7-validator (listed as a dependent) for actual request validation. Wrap the read in a singleton bound in a service provider so you don't parse the YAML on every request:

// AppServiceProvider::register()
$this->app->singleton(\cebe\openapi\spec\OpenApi::class, function () {
    return \cebe\openapi\Reader::readFromYamlFile(
        realpath(config('openapi.path'))
    );
});

Then a middleware or FormRequest-style validator resolves the OpenApi instance and validates the PSR-7 request.

2. Artisan commands as the day-to-day surface. Put spec operations behind commands so they're discoverable and CI-friendly:

// php artisan openapi:validate
$spec = Reader::readFromYamlFile(realpath($this->argument('file')));
exit($spec->validate() ? 0 : 2);

openapi:convert (YAML ↔ JSON), openapi:inline (bundle external $refs into one file for consumers), and openapi:diff (read two specs and report added/removed paths) are all short commands built directly on Reader/Writer.

3. Generating specs from Laravel models/routes. When the spec is the source of truth in code, build it with the spec\* classes and serialize on deploy:

use cebe\openapi\spec\{OpenApi, Info, Paths, PathItem, Operation, Responses, Response};

$openapi = new OpenApi([
    'openapi' => '3.0.3',
    'info'    => new Info(['title' => config('app.name'), 'version' => '1.0.0']),
    'paths'   => new Paths(),
]);
$openapi->paths->getPaths()['/health'] = new PathItem([
    'get' => new Operation([
        'responses' => new Responses(['200' => new Response(['description' => 'OK'])]),
    ]),
]);
file_put_contents(storage_path('app/openapi.json'), Writer::writeToJson($openapi));

Commit the generated file (or upload it as a build artifact) so downstream consumers — spectator for contract tests, API gateways, client SDK generators — have a stable contract.

4. Contract testing with spectator. hotmeteor/spectator (a dependent) validates your Laravel test requests against the spec. The flow is: export the spec in a test setup step using Writer, then assert each feature test response conforms. This gives you regression protection on the API surface without hand-maintaining JSON schemas.

5. Reference resolution for bundled distribution. If your spec splits $refs across components/*.yaml, run:

vendor/bin/php-openapi inline openapi.yaml openapi-bundled.yaml

in CI and ship the bundled artifact. Programmatically:

use cebe\openapi\ReferenceContext;
$spec = Reader::readFromYamlFile(realpath($path), OpenApi::class, ReferenceContext::RESOLVE_MODE_INLINE);
\cebe\openapi\Writer::writeToYamlFile($spec, $out);

RESOLVE_MODE_INLINE keeps internal #/components/... refs but pulls in external files.

6. Docs and Postman generation. Iterate $spec->paths to emit a markdown reference page or a Postman collection. Since parameters, request bodies, and responses are all typed objects, you get description, schema->type, and required fields without regex-parsing YAML.

Gotchas and Tips

  • realpath() is not optional. Relative $refs (./components/User.yaml) resolve against the file's directory, computed from the path you pass in. Pass a relative path and external refs break silently or throw. Always realpath($path) for local files.
  • Validation is shallow. $spec->validate() only catches structural errors (wrong types, missing required fields, misplaced keys). Full JSON-Schema validation against the OpenAPI 3.0 schema lives only in the CLI (vendor/bin/php-openapi validate), which pulls in jsonrainbow/json-schema. Use the CLI in CI and validate() as a fast pre-check.
  • The spec object is not an array. Don't json_encode($spec) — it won't produce what you expect. Always go through Writer::writeToJson() / writeToYaml(). Likewise, count($spec->paths) fails because Paths implements IteratorAggregate but not Countable; use iterator_to_array() or getPaths().
  • Reference objects are distinct. When you use RESOLVE_MODE_INLINE, unresolved internal refs stay as cebe\openapi\spec\Reference instances, not the target object. Check $node instanceof Reference before dereferencing. With RESOLVE_MODE_ALL, recursive schemas intentionally stop at a depth limit and remain as Reference — don't assume full flattening.
  • CLI exit codes. 2 = validation failure, 1 = other error (unreadable file, unsupported refs), 0 = success. Wire php-openapi validate into a CI step and treat 2 differently from 1 in your pipeline.
  • getRawSpecData() (added 1.8.0) returns the original un-parsed array — useful when you need fields the typed classes drop (e.g., vendor extensions you don't want mapped) or when the spec has non-standard keys.
  • Extension properties (x-*) are accessible via $node->getExtensions() or dynamic property access on the object. This is how you attach Laravel-specific metadata (e.g., x-middleware, x-policy) and read it back in route generation.
  • Only OpenAPI 3.0.x. No 3.1 support. If you're on tools emitting 3.1 (examples array vs map, nullable changes), you'll need to downconvert first — the CLI convert command won't do it for you.
  • Path parameters must be declared. The library won't validate that /users/{id} has a matching parameters entry on the PathItem or Operation. Missing path params silently pass validate() but break spectator and league/openapi-psr7-validator at request time.
  • Schema object validation is unimplemented in the library itself (see the Completeness table). You can read Schema objects, but validating an instance against a Schema is out of scope — rely on league/openapi-psr7-validator or opis/json-schema for that.
  • Writing order/normalisation. Writer preserves insertion order of keys, so build specs in a deterministic order (alphabetise paths, group components) to keep diffs clean. Numeric response codes (200, 404) become string keys in JSON — this is correct per spec but surprises people.
  • SecurityRequirement quirk: before 1.8.0, referenced Security objects and SecurityRequirement arrays compiled to the wrong shape. Pin cebe/php-openapi: ^1.8 if your spec uses security schemes with references.
  • Don't parse on every request. Reading + resolving refs is I/O heavy. Cache the parsed OpenApi object (serialize once to PHP via var_export + OPcache, or bind as singleton) rather than re-reading YAML per HTTP request — especially in queue workers and Octane, where per-request bootstrapping burns CPU.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource