Installation
composer require exsyst/swagger
Add to composer.json if using a monorepo or custom package structure.
Load an Existing Swagger/OpenAPI Spec
use EXSyst\Swagger\Swagger;
$swagger = Swagger::fromFile(base_path('api.json')); // Load from file
// OR
$swagger = new Swagger($specArray); // Load from array
First Use Case: Inspect Paths
$paths = $swagger->getPaths();
foreach ($paths as $path) {
echo $path->getPath(); // Output: /users, /products, etc.
}
Swagger class: Entry point for all operations.Paths and Definitions collections: Core for managing API endpoints and models.Path, Operation, Schema): Represent OpenAPI spec components.$swagger = new Swagger();
$paths = $swagger->getPaths();
$paths->add(new Path('/api/users', [
'get' => new Operation(['summary' => 'List users']),
]));
$swagger->toFile(base_path('generated-api.json'));
$swagger = Swagger::fromFile('external-api.json');
if ($swagger->isValid()) {
$swagger->normalize(); // Standardize naming/conventions
$swagger->toFile('normalized-api.json');
}
Route::get('/users', function () {
return User::all();
})->middleware('api');
// In a service provider or command:
$swagger = new Swagger();
$paths = $swagger->getPaths();
$paths->add(new Path('/users', [
'get' => new Operation([
'tags' => ['Users'],
'responses' => ['200' => new Response(['description' => 'List of users'])],
]),
]));
definitions (or components/schemas) dynamically.$definitions = $swagger->getDefinitions();
$definitions->add('User', new Schema([
'type' => 'object',
'properties' => [
'id' => ['type' => 'integer'],
'name' => ['type' => 'string'],
],
]));
Use with darkaonline/l5-swagger:
Combine this package with Laravel-specific Swagger generators (e.g., darkaonline/l5-swagger) for route-to-spec conversion, then use exsyst/swagger for post-processing.
// Example: Extend a generated spec
$spec = (new L5Swagger\Generator)->openapi();
$swagger = new Swagger($spec);
$swagger->getInfo()->setTitle('Enhanced API');
Leverage Collections:
Use Paths and Definitions collections to iterate, filter, or modify specs programmatically. Example:
$swagger->getPaths()->filter(function ($path) {
return str_starts_with($path->getPath(), '/admin');
});
Event-Driven Workflows: Trigger spec updates on model/route changes (e.g., using Laravel events or queue jobs).
Deprecated OpenAPI 2.0 Support:
swagger:) specs. Validate specs explicitly:
if (!$swagger->isValid()) {
throw new \RuntimeException('Invalid OpenAPI spec');
}
Immutable Models:
Path, Operation) are immutable after creation. Use setters or constructors to modify properties:
// Wrong (throws error):
$path->setPath('/new-path');
// Correct:
$path = new Path('/new-path', $path->getOperations());
File Handling:
Swagger::fromFile() and toFile() assume UTF-8 encoding. For non-UTF-8 files, decode manually:
$content = file_get_contents('api.json');
$swagger = new Swagger(json_decode($content, true));
Circular References:
json_encode() with JSON_UNESCAPED_SLASHES for debugging:
$json = json_encode($swagger->toArray(), JSON_UNESCAPED_SLASHES);
Validate Specs Early:
Use isValid() to catch malformed specs before processing:
if (!$swagger->isValid()) {
echo $swagger->getErrors(); // Dump validation errors
}
IDE Autocompletion: The package’s models mirror OpenAPI spec structure. Use your IDE’s PHPStan or Psalm integration to resolve types:
/** @var \EXSyst\Swagger\Path $path */
$path = $swagger->getPaths()->get('/users');
Diffing Specs:
Compare specs before/after changes using json_diff or spatie/array-to-xml:
$before = json_encode($swagger1->toArray());
$after = json_encode($swagger2->toArray());
echo json_diff($before, $after);
Custom Validators:
Extend EXSyst\Swagger\Validator to add domain-specific rules:
class CustomValidator extends \EXSyst\Swagger\Validator {
public function validate($spec) {
parent::validate($spec);
if (!isset($spec['servers'])) {
$this->errors[] = 'Missing servers definition';
}
}
}
Plugin System: Use anonymous functions or closures to modify specs during initialization:
$swagger = new Swagger($specArray);
$swagger->getInfo()->setVersion('1.0.0');
$swagger->getPaths()->each(function ($path) {
$path->getOperations()->each(function ($op) {
$op->setDeprecated(false);
});
});
Event Listeners:
Attach listeners to Paths/Definitions collections for reactive updates:
$paths = $swagger->getPaths();
$paths->on('add', function ($path) {
Log::info("Added path: {$path->getPath()}");
});
Hybrid Specs: Merge multiple specs (e.g., core API + modular extensions):
$base = Swagger::fromFile('base.json');
$extension = Swagger::fromFile('extension.json');
$merged = $base->merge($extension);
How can I help you explore Laravel packages today?