Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@
"config": {
"sort-packages": true
},
"extra": {
"laravel": {
"providers": [
"PhpSwag\\Bridges\\Laravel\\PhpSwagServiceProvider"
]
}
},
"bin": ["bin/phpswag"],
"scripts": {
"lint": [
Expand Down
13 changes: 12 additions & 1 deletion src/Bridges/Laravel/Commands/GenerateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class GenerateCommand extends Command
*
* @var string
*/
protected $signature = 'phpswag:generate {--validate : Run validation on the generated spec}';
protected $signature = 'phpswag:generate'
. ' {--validate : Run validation on the generated spec}'
. ' {--filter-unused= : Filter unused schemas (true or false)}';

/**
* The console command description.
Expand Down Expand Up @@ -67,6 +69,15 @@ public function handle(): int
$core->enableCache($cacheFile);
}

// Apply filter-unused configuration
$filterUnusedOption = $this->option('filter-unused');
if ($filterUnusedOption === null || $filterUnusedOption === '') {
$filterUnused = (bool)($config['filter_unused'] ?? true);
} else {
$filterUnused = filter_var($filterUnusedOption, FILTER_VALIDATE_BOOLEAN);
}
$core->setFilterUnusedSchemas($filterUnused);

// Perform validation if flag is set
if ($this->option('validate')) {
$specArray = $core->generateSpecArray($paths);
Expand Down
11 changes: 11 additions & 0 deletions src/Bridges/Laravel/config/phpswag.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,15 @@
*/
'swagger_ui' => true,
'swagger_ui_path' => '/api/docs',

/*
|--------------------------------------------------------------------------
| Filter Unused Schemas
|--------------------------------------------------------------------------
|
| When set to true, schemas that are not referenced by any route/path
| will be filtered out from the generated specification.
|
*/
'filter_unused' => true,
];
5 changes: 4 additions & 1 deletion src/Core.php
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,10 @@ private function discoverStatement(Node $stmt, NameResolver $nameResolver): void
$tags = $this->docCollector->collectTags($docComment, $docStartLine, $this->currentlyAnalyzingFile);
foreach ($tags as $tag) {
if ($tag['name'] === '@template') {
$templates[] = $tag['value'];
$parts = preg_split('/\s+/', trim($tag['value']));
if (!empty($parts[0])) {
$templates[] = $parts[0];
}
}

if ($tag['name'] === '@extends' || $tag['name'] === '@implements') {
Expand Down
59 changes: 59 additions & 0 deletions tests/FrameworkBridgesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

class FrameworkBridgesTest extends TestCase
{
public static function setUpBeforeClass(): void
{
require_once __DIR__ . '/laravel-helpers.php';
}
public function testSymfonyConfigurationTree()
{
$configuration = new Configuration();
Expand Down Expand Up @@ -140,4 +144,59 @@ public function testLaravelServiceProviderInstantiation()

$this->assertInstanceOf(\Illuminate\Support\ServiceProvider::class, $provider);
}

public function testLaravelGenerateCommand()
{
global $laravelConfig;
$laravelConfig = [
'phpswag' => [
'paths' => ['examples/App'],
'output' => 'test-laravel-spec.yaml',
'format' => 'yaml',
'title' => 'Laravel Spec',
'version' => '2.0.0',
'description' => 'Generated in Laravel tests',
'host' => 'http://localhost:8000',
'cache' => false,
'filter_unused' => true,
]
];

$command = new \PhpSwag\Bridges\Laravel\Commands\GenerateCommand();
$laravelApp = $this->createMock(\Illuminate\Contracts\Foundation\Application::class);
$laravelApp->method('runningUnitTests')->willReturn(true);
$laravelApp->method('make')->willReturnCallback(function ($abstract, $parameters = []) {
if ($abstract === \Illuminate\Console\OutputStyle::class) {
return new \Illuminate\Console\OutputStyle($parameters['input'], $parameters['output']);
}
return null;
});
$laravelApp->method('call')->willReturnCallback(function ($callback, $parameters = []) {
if (is_array($callback) && $callback[0] instanceof \PhpSwag\Bridges\Laravel\Commands\GenerateCommand) {
return $callback[0]->handle();
}
return is_callable($callback) ? $callback(...$parameters) : null;
});
$command->setLaravel($laravelApp);

$application = new SymfonyApplication();
$application->add($command);

$tester = new CommandTester($application->find('phpswag:generate'));

$tester->execute([]);
$this->assertEquals(0, $tester->getStatusCode());
$this->assertStringContainsString('Documentation generated successfully to test-laravel-spec.yaml', $tester->getDisplay());

if (file_exists('test-laravel-spec.yaml')) {
unlink('test-laravel-spec.yaml');
}

// Test option override
$tester->execute(['--filter-unused' => 'false']);
$this->assertEquals(0, $tester->getStatusCode());
if (file_exists('test-laravel-spec.yaml')) {
unlink('test-laravel-spec.yaml');
}
}
}
31 changes: 31 additions & 0 deletions tests/GenericsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,35 @@ public function testGenericInheritance()
$this->assertStringContainsString("message:", $yaml);
$this->assertStringContainsString("App_User", $yaml);
}

public function testGenericsWithConstraints()
{
$dir = __DIR__ . '/fixtures/generics_constraints';
@mkdir($dir, 0777, true);

file_put_contents($dir . '/BaseModel.php', '<?php namespace App; class BaseModel { public string $id; }');
file_put_contents($dir . '/QueryFilter.php', '<?php namespace App; /** @template TModel of BaseModel */ class QueryFilter { }');
file_put_contents($dir . '/SortFilter.php', '<?php namespace App; use App\QueryFilter; use App\BaseModel;
/**
* @template TModel of BaseModel
*
* @extends QueryFilter<TModel>
*/
class SortFilter extends QueryFilter { }');
file_put_contents($dir . '/Controller.php', '<?php namespace App;
use App\SortFilter;
use App\BaseModel;
class Controller {
/**
* @route GET /sort
* @response 200 SortFilter<BaseModel>
*/
public function getSort() {}
}');

$core = new Core();
$yaml = $core->generate([$dir]);

$this->assertStringContainsString("App_SortFilter:", $yaml);
}
}
1 change: 1 addition & 0 deletions tests/fixtures/generics_constraints/BaseModel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?php namespace App; class BaseModel { public string $id; }
10 changes: 10 additions & 0 deletions tests/fixtures/generics_constraints/Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php namespace App;
use App\SortFilter;
use App\BaseModel;
class Controller {
/**
* @route GET /sort
* @response 200 SortFilter<BaseModel>
*/
public function getSort() {}
}
1 change: 1 addition & 0 deletions tests/fixtures/generics_constraints/QueryFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?php namespace App; /** @template TModel of BaseModel */ class QueryFilter { }
7 changes: 7 additions & 0 deletions tests/fixtures/generics_constraints/SortFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php namespace App; use App\QueryFilter; use App\BaseModel;
/**
* @template TModel of BaseModel
*
* @extends QueryFilter<TModel>
*/
class SortFilter extends QueryFilter { }
30 changes: 30 additions & 0 deletions tests/laravel-helpers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

if (!function_exists('app_path')) {
function app_path(string $path = ''): string
{
return $path;
}
}
if (!function_exists('public_path')) {
function public_path(string $path = ''): string
{
return $path;
}
}
if (!function_exists('storage_path')) {
function storage_path(string $path = ''): string
{
return $path;
}
}
if (!function_exists('config')) {
function config(string $key = null, mixed $default = null): mixed
{
global $laravelConfig;
if ($key === null) {
return $laravelConfig;
}
return $laravelConfig[$key] ?? $default;
}
}
Loading