* Add .gitattributes and .editorconfig
* Add basic fixer to ensure we have a @license docblock
* Add missing dev autoload config
This commit is contained in:
Martin Rademacher 2022-05-20 16:17:19 +12:00 committed by GitHub
parent 117caa0c0c
commit 46c3e4c493
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 289 additions and 157 deletions

13
.editorconfig Normal file
View File

@ -0,0 +1,13 @@
; top-most EditorConfig file
root = true
; Unix-style newlines
[*]
charset = utf-8
end_of_line = LF
insert_final_newline = true
trim_trailing_whitespace = true
[*.{php,json,yaml}]
indent_style = space
indent_size = 4

12
.gitattributes vendored Normal file
View File

@ -0,0 +1,12 @@
/.* export-ignore
/tools/ export-ignore
/phpstan*.neon export-ignore
/phpunit.xml.dist export-ignore
/psalm*.xml export-ignore
/tests/ export-ignore
* text=auto eol=lf
*.json text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4
*.md text whitespace=blank-at-eol,blank-at-eof
*.php text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4 diff=php
*.yml text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4

View File

@ -1,17 +1,23 @@
<?php
use OpenApi\Tools\CSFixer\LicenseFixer;
$finder = PhpCsFixer\Finder::create()
->path('src')->name('*.php')
->path('tests')->name('*.php')
->path('Examples')->name('*.php')
//->exclude('tests/Fixtures')
->path('tools')->name('*.php')
->in(__DIR__)
;
return (new PhpCsFixer\Config())
->registerCustomFixers([
new LicenseFixer(),
])
->setRules([
'@PSR2' => true,
'@DoctrineAnnotation' => true,
'OpenApi/license' => true,
'array_syntax' => ['syntax' => 'short'],
'no_unused_imports' => true,
'blank_line_before_statement' => ['statements' => ['return']],

View File

@ -1,112 +1,113 @@
{
"name": "zircote/swagger-php",
"type": "library",
"license": "Apache-2.0",
"bin": [
"bin/openapi"
],
"description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations",
"keywords": [
"json",
"rest",
"api",
"service discovery"
],
"homepage": "https://github.com/zircote/swagger-php/",
"authors": [
{
"name": "Robert Allen",
"email": "zircote@gmail.com"
"name": "zircote/swagger-php",
"type": "library",
"license": "Apache-2.0",
"bin": [
"bin/openapi"
],
"description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations",
"keywords": [
"json",
"rest",
"api",
"service discovery"
],
"homepage": "https://github.com/zircote/swagger-php/",
"authors": [
{
"name": "Robert Allen",
"email": "zircote@gmail.com"
},
{
"name": "Bob Fanger",
"email": "bfanger@gmail.com",
"homepage": "https://bfanger.nl"
},
{
"name": "Martin Rademacher",
"email": "mano@radebatz.net",
"homepage": "https://radebatz.net"
}
],
"config": {
"bin-dir": "bin",
"optimize-autoloader": true,
"sort-packages": true,
"allow-plugins": {
"composer/package-versions-deprecated": true
}
},
{
"name": "Bob Fanger",
"email": "bfanger@gmail.com",
"homepage": "https://bfanger.nl"
"minimum-stability": "stable",
"extra": {
"branch-alias": {
"dev-master": "4.x-dev"
}
},
{
"name": "Martin Rademacher",
"email": "mano@radebatz.net",
"homepage": "https://radebatz.net"
"require": {
"php": ">=7.2",
"ext-json": "*",
"doctrine/annotations": "^1.7",
"psr/log": "^1.1 || ^2.0 || 3.0",
"symfony/finder": ">=2.2",
"symfony/yaml": ">=3.3"
},
"autoload": {
"psr-4": {
"OpenApi\\": "src"
}
},
"require-dev": {
"composer/package-versions-deprecated": "^1.11",
"friendsofphp/php-cs-fixer": "^2.17 || ^3.0",
"phpstan/phpstan": "^1.6",
"phpunit/phpunit": ">=8",
"vimeo/psalm": "^4.23"
},
"autoload-dev": {
"exclude-from-classmap": [
"/tests/Fixtures"
],
"psr-4": {
"OpenApi\\Tools\\": "tools/src/",
"OpenApi\\Tests\\": "tests/",
"AnotherNamespace\\": "tests/Fixtures/AnotherNamespace",
"OpenApi\\Tests\\Fixtures\\Annotations\\": "tests/Fixtures/Annotations"
}
},
"scripts-descriptions": {
"cs": "Fix all codestyle issues",
"lint": "Test codestyle",
"test": "Run all non-legacy and codestyle tests",
"testlegacy": "Run tests using the legacy TokenAnalyser",
"testall": "Run all tests (test + testlegacy)",
"analyse": "Run static analysis (phpstan/psalm)",
"spectral": "Run spectral lint over all .yaml files in the Examples folder",
"docs:refgen": "Rebuild the annotations/attributes reference markup files",
"docs:dev": "Run dev server for local development of gh-pages",
"docs:build": "Re-build static gh-pages"
},
"scripts": {
"cs": "php-cs-fixer fix --allow-risky=yes",
"lint": "@cs --dry-run",
"test": [
"phpunit",
"@lint"
],
"testlegacy": "export PHPUNIT_ANALYSER=legacy && phpunit",
"testall": [
"@test",
"@testlegacy"
],
"analyse": [
"phpstan analyse --memory-limit=2G",
"psalm"
],
"spectral": "for ff in `find Examples -name '*.yaml'`; do spectral lint $ff; done",
"docs:refgen": "php tools/refgen.php",
"docs:dev": "cd docs && npm run dev",
"docs:build": [
"@docs:refgen",
"cd docs && npm run build"
]
}
],
"config": {
"bin-dir": "bin",
"optimize-autoloader": true,
"sort-packages": true,
"allow-plugins": {
"composer/package-versions-deprecated": true
}
},
"minimum-stability": "stable",
"extra": {
"branch-alias": {
"dev-master": "4.x-dev"
}
},
"require": {
"php": ">=7.2",
"ext-json": "*",
"doctrine/annotations": "^1.7",
"psr/log": "^1.1 || ^2.0 || 3.0",
"symfony/finder": ">=2.2",
"symfony/yaml": ">=3.3"
},
"autoload": {
"psr-4": {
"OpenApi\\": "src"
}
},
"require-dev": {
"composer/package-versions-deprecated": "^1.11",
"friendsofphp/php-cs-fixer": "^2.17 || ^3.0",
"phpstan/phpstan": "^1.6",
"phpunit/phpunit": ">=8",
"vimeo/psalm": "^4.23"
},
"autoload-dev": {
"exclude-from-classmap": [
"/tests/Fixtures"
],
"psr-4": {
"OpenApi\\Tests\\": "tests/",
"AnotherNamespace\\": "tests/Fixtures/AnotherNamespace",
"OpenApi\\Tests\\Fixtures\\Annotations\\": "tests/Fixtures/Annotations"
}
},
"scripts-descriptions": {
"cs": "Fix all codestyle issues",
"lint": "Test codestyle",
"test": "Run all non-legacy and codestyle tests",
"testlegacy": "Run tests using the legacy TokenAnalyser",
"testall": "Run all tests (test + testlegacy)",
"analyse": "Run static analysis (phpstan/psalm)",
"spectral": "Run spectral lint over all .yaml files in the Examples folder",
"docs:refgen": "Rebuild the annotations/attributes reference markup files",
"docs:dev": "Run dev server for local development of gh-pages",
"docs:build": "Re-build static gh-pages"
},
"scripts": {
"cs": "php-cs-fixer fix --allow-risky=yes",
"lint": "@cs --dry-run",
"test": [
"phpunit",
"@lint"
],
"testlegacy": "export PHPUNIT_ANALYSER=legacy && phpunit",
"testall": [
"@test",
"@testlegacy"
],
"analyse": [
"phpstan analyse --memory-limit=2G",
"psalm"
],
"spectral": "for ff in `find Examples -name '*.yaml'`; do spectral lint $ff; done",
"docs:refgen": "php docs/refgen.php",
"docs:dev": "cd docs && npm run dev",
"docs:build": [
"@docs:refgen",
"cd docs && npm run build"
]
}
}

View File

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
/**
* @license Apache 2.0
*/
namespace OpenApi\Processors;
use OpenApi\Analysis;

View File

@ -1,5 +1,9 @@
<?php
/**
* @license Apache 2.0
*/
namespace OpenApi\Processors;
use OpenApi\Annotations\AbstractAnnotation;

20
tools/refgen.php Normal file
View File

@ -0,0 +1,20 @@
<?php declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use OpenApi\Tools\Docs\RefGenerator;
$refgen = new RefGenerator(__DIR__ . '/../');
foreach ($refgen->types() as $type) {
ob_start();
echo $refgen->preamble($type);
foreach ($refgen->classesForType($type) as $name => $details) {
echo $refgen->formatHeader($name, $type);
$method = "format{$type}Details";
echo $refgen->$method($name, $details['fqdn'], $details['filename']);
}
file_put_contents($refgen->docPath('reference/' . strtolower($type) . '.md'), ob_get_clean());
}

View File

@ -0,0 +1,28 @@
<?php declare(strict_types=1);
/**
* @license Apache 2.0
*/
namespace OpenApi\Tools\CSFixer;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\Tokenizer\Tokens;
abstract class AbstractFixer implements FixerInterface
{
public function isCandidate(Tokens $tokens): bool
{
return true;
}
public function isRisky(): bool
{
return false;
}
public function supports(\SplFileInfo $file): bool
{
return $file->getExtension() == 'php';
}
}

View File

@ -0,0 +1,63 @@
<?php declare(strict_types=1);
/**
* @license Apache 2.0
*/
namespace OpenApi\Tools\CSFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
class LicenseFixer extends AbstractFixer
{
public function fix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if ($token->isComment()) {
if (false !== strpos($token->getContent(), '@license')) {
return;
}
}
}
$license = <<< EOC
/**
* @license Apache 2.0
*/
EOC;
if ($sequence = $tokens->findSequence([[T_NAMESPACE]])) {
$index = array_keys($sequence)[0];
$tokens->insertAt($index, new Token([
T_COMMENT,
$license,
]));
}
}
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'All .php files MUST have a @license docblock annotation before namespace / use statement(s)',
[]
);
}
public function getName(): string
{
return 'OpenApi/license';
}
public function getPriority(): int
{
return 5;
}
public function supports(\SplFileInfo $file): bool
{
return parent::supports($file) && false !== strpos($file->getPath(), '/src/');
}
}

View File

@ -1,10 +1,14 @@
<?php declare(strict_types=1);
/**
* @license Apache 2.0
*/
namespace OpenApi\Tools\Docs;
use OpenApi\Analysers\TokenScanner;
use OpenApi\Annotations\AbstractAnnotation;
require_once __DIR__ . '/../vendor/autoload.php';
class RefGenerator
{
const ATTRIBUTES = 'Attributes';
@ -12,15 +16,19 @@ class RefGenerator
const NO_DETAILS_AVAILABLE = 'No details available.';
protected $scanner;
protected $projectRoot;
public function __construct()
public function __construct($projectRoot)
{
$this->scanner = new TokenScanner();
$this->projectRoot = realpath($projectRoot);
}
public function docPath(string $relativeName): string
{
return $this->projectRoot . '/docs/' . $relativeName;
}
/**
*
*/
public function preamble(string $type): string
{
return <<< EOT
@ -37,13 +45,10 @@ In addition to this page, there are also a number of [examples](https://github.c
EOT;
}
/**
*
*/
public function classesForType(string $type): array
{
$classes = [];
$dir = new DirectoryIterator(__DIR__ . '/../src/' . $type);
$dir = new \DirectoryIterator($this->projectRoot . '/src/' . $type);
foreach ($dir as $entry) {
if (!$entry->isFile() || $entry->getExtension() != 'php') {
continue;
@ -63,17 +68,11 @@ EOT;
return $classes;
}
/**
*
*/
public function types(): array
{
return [self::ANNOTATIONS, self::ATTRIBUTES];
}
/**
*
*/
public function formatHeader(string $name, string $type): string
{
return <<< EOT
@ -83,16 +82,13 @@ EOT;
EOT;
}
/**
*
*/
public function formatAttributesDetails(string $name, string $fqdn, string $filename): string
{
$rctor = (new ReflectionClass($fqdn))->getMethod('__construct');
$rctor = (new \ReflectionClass($fqdn))->getMethod('__construct');
ob_start();
$rc = new ReflectionClass($fqdn);
$rc = new \ReflectionClass($fqdn);
$classDocumentation = $this->extractDocumentation($rc->getDocComment());
echo $classDocumentation['content'] . PHP_EOL;
@ -104,7 +100,7 @@ EOT;
$parameters = $rctor->getParameters();
if ($parameters) {
echo PHP_EOL . '#### Parameters' . PHP_EOL;
echo '---'.PHP_EOL;
echo '---' . PHP_EOL;
echo '<dl>' . PHP_EOL;
foreach ($parameters as $rp) {
@ -127,7 +123,7 @@ EOT;
if ($classDocumentation['see']) {
echo PHP_EOL . '#### Reference' . PHP_EOL;
echo '---'.PHP_EOL;
echo '---' . PHP_EOL;
foreach ($classDocumentation['see'] as $link) {
echo '- ' . $link . PHP_EOL;
@ -148,7 +144,7 @@ EOT;
ob_start();
$rc = new ReflectionClass($fqdn);
$rc = new \ReflectionClass($fqdn);
$classDocumentation = $this->extractDocumentation($rc->getDocComment());
echo $classDocumentation['content'] . PHP_EOL;
@ -161,11 +157,11 @@ EOT;
if ($properties) {
echo PHP_EOL . '#### Properties' . PHP_EOL;
echo '---'.PHP_EOL;
echo '---' . PHP_EOL;
echo '<dl>' . PHP_EOL;
foreach ($properties as $property) {
$rp = new ReflectionProperty($fqdn, $property);
$rp = new \ReflectionProperty($fqdn, $property);
$propertyDocumentation = $this->extractDocumentation($rp->getDocComment());
if ($var = $this->getReflectionType($fqdn, $rp, false, $propertyDocumentation['var'])) {
$var = ' : <span style="font-family: monospace;">' . $var . '</span>';
@ -193,7 +189,7 @@ EOT;
if ($classDocumentation['see']) {
echo PHP_EOL . '#### Reference' . PHP_EOL;
echo '---'.PHP_EOL;
echo '---' . PHP_EOL;
foreach ($classDocumentation['see'] as $link) {
echo '- ' . $link . PHP_EOL;
@ -212,7 +208,7 @@ EOT;
{
$props = [];
foreach ($fqdn::$_nested as $details) {
$props[] = ((array)$details)[0];
$props[] = ((array) $details)[0];
}
return $props;
@ -225,10 +221,11 @@ EOT;
{
if ($fqdn::$_parents) {
echo PHP_EOL . '#### Allowed in' . PHP_EOL;
echo '---'.PHP_EOL;
echo '---' . PHP_EOL;
$parents = array_map(function (string $parent) {
$shortName = $this->shortName($parent);
return '<a href="#' . strtolower($shortName) . '">' . $shortName . '</a>';
}, $fqdn::$_parents);
echo implode(', ', $parents) . PHP_EOL;
@ -236,10 +233,11 @@ EOT;
if ($fqdn::$_nested) {
echo PHP_EOL . '#### Nested elements' . PHP_EOL;
echo '---'.PHP_EOL;
echo '---' . PHP_EOL;
$nested = array_map(function (string $nested) {
$shortName = $this->shortName($nested);
return '<a href="#' . strtolower($shortName) . '">' . $shortName . '</a>';
}, array_keys($fqdn::$_nested));
echo implode(', ', $nested) . PHP_EOL;
@ -263,7 +261,7 @@ EOT;
$var = [];
if ($type = $rp->getType()) {
if ($type instanceof ReflectionUnionType) {
if ($type instanceof \ReflectionUnionType) {
foreach ($type->getTypes() as $type) {
$var[] = $type->getName();
}
@ -292,7 +290,7 @@ EOT;
return ['content' => '', 'see' => [], 'var' => '', 'params' => []];
}
$comment = preg_split('/(\n|\r\n)/', (string)$docblock);
$comment = preg_split('/(\n|\r\n)/', (string) $docblock);
$comment[0] = preg_replace('/[ \t]*\\/\*\*/', '', $comment[0]); // strip '/**'
$i = count($comment) - 1;
@ -334,20 +332,3 @@ EOT;
return ['content' => $content, 'see' => $see, 'var' => $var, 'params' => $params];
}
}
// ================================================================================
$refgen = new RefGenerator();
foreach ($refgen->types() as $type) {
ob_start();
echo $refgen->preamble($type);
foreach ($refgen->classesForType($type) as $name => $details) {
echo $refgen->formatHeader($name, $type);
$method = "format{$type}Details";
echo $refgen->$method($name, $details['fqdn'], $details['filename']);
}
file_put_contents(__DIR__ . '/reference/' . strtolower($type) . '.md', ob_get_clean());
}