Skip to content

Commit

Permalink
Remove deprecated DocBlockParser::$defaultImports and update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
DerManoMann committed Oct 1, 2021
1 parent 1cfde0c commit 108e7d6
Show file tree
Hide file tree
Showing 14 changed files with 80 additions and 111 deletions.
40 changes: 18 additions & 22 deletions docs/Generator-migration.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
# Using the `Generator`

## Motivation

Code to perform a fully customized scan using `swagger-php` so far required to use 3 separate static elements:
1. `\OpenApi\scan()`

The function to scan for OpenApi annotations.
1. `DocBlockParser::$whitelist`

List of namespaces that should be detected by the doctrine annotation parser.
1. `DocBlockParser::$defaultImports`

Imports to be set on the used doctrine `DocParser`.
Allows to pre-define annotation namespaces. The `@OA` namespace, for example, is configured
as `['oa' => 'OpenApi\\Annotations']`.

The new `Generator` class provides an object-oriented way to use `swagger-php` and all its aspects in a single place.
The `Generator` class provides an object-oriented way to use `swagger-php` and all its aspects in a single place.

## The `\OpenApi\scan()` function

Expand Down Expand Up @@ -45,7 +31,7 @@ require("vendor/autoload.php");
$openapi = \OpenApi\scan(__DIR__, ['exclude' => ['tests'], 'pattern' => '*.php']);
```

The two configuration options for the underlying Doctrine doc-block parser `DocBlockParser::$whitelist` and `DocBlockParser::$defaultImports`
The two configuration options for the underlying Doctrine doc-block parser `aliases` and `namespaces`
are not part of this function and need to be set separately.

Being static this means setting them back is the callers responsibility and there is also the fact that
Expand All @@ -55,7 +41,7 @@ Therefore, having a single side-effect free way of using swwagger-php seemed lik

## The `\OpenApi\Generator` class

The new `Generator` class can be used in object-oriented (and fluent) style which allows for easy customization
The `Generator` class can be used in object-oriented (and fluent) style which allows for easy customization
if needed.

In that case to actually process the given input files the **non-static** method `generate()` is to be used.
Expand All @@ -73,13 +59,23 @@ $finder = \Symfony\Component\Finder\Finder::create()->files()->name('*.php')->in
$openapi = (new \OpenApi\Generator($logger))
->setProcessors($processors)
->setAliases(['MY' => 'My\Annotations'])
->setAnalyser(new \OpenApi\Analysers\TokenAnalyser())
->setNamespaces(['My\\Annotations\\'])
->setAnalyser(new \OpenApi\Analysers\TokenAnalyser())
->generate(['/path1/to/project', $finder], new \OpenApi\Analysis(), $validate);
```

The `aliases` property corresponds to the now also deprecated static `Analyser::$defaultImports`,
`namespaces` to `Analysis::$whitelist`.
`Aliases` and `namespaces` are additional options that allow to customize the parsing of docblocks.

Defaults:
* **aliases**: `['oa' => 'OpenApi\\Annotations']`

Aliases help the underlying `doctrine annotations library` to parse annotations. Effectively they avoid having
to write `use OpenApi\Annotations as OA;` in your code and make `@OA\property(..)` annotations still work.

* **namespaces**: `['OpenApi\\Annotations\\']`

Namespaces control which annotation namespaces can be autoloaded automatically. Under the hood this
is handled by registering a custom loader with the `doctrine annotation library`.

Advantages:
* The `Generator` code will handle configuring things as before in a single place
Expand Down Expand Up @@ -137,8 +133,8 @@ echo $openapi->toYaml();
* * \SplFileInfo
* * \Symfony\Component\Finder\Finder
* @param array $options
* aliases: null|array Defaults to `DocBlockParser::$defaultImports`.
* namespaces: null|array Defaults to `DocBlockParser::$whitelist`.
* aliases: null|array Defaults to `['oa' => 'OpenApi\\Annotations']`.
* namespaces: null|array Defaults to `['oa' => 'OpenApi\\Annotations\\']`.
* analyser: null|StaticAnalyser Defaults to a new `StaticAnalyser`.
* analysis: null|Analysis Defaults to a new `Analysis`.
* processors: null|array Defaults to `Analysis::processors()`.
Expand Down
2 changes: 1 addition & 1 deletion src/Analysers/DocBlockAnnotationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function setGenerator(Generator $generator): void
{
$this->generator = $generator;

$this->docBlockParser->docParser->setImports($generator->getAliases());
$this->docBlockParser->setAliases($generator->getAliases());
}

public function build(\Reflector $reflector, Context $context): array
Expand Down
20 changes: 9 additions & 11 deletions src/Analysers/DocBlockParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,24 @@
*/
class DocBlockParser
{
/**
* Use @OA\* for OpenAPI annotations (unless overwritten by a use statement).
*
* @deprecated use \OpenApi\Generator::setAliases() instead
*/
public static $defaultImports = ['oa' => 'OpenApi\\Annotations'];

/**
* @var DocParser
*/
public $docParser;
protected $docParser;

public function __construct(?DocParser $docParser = null)
public function __construct(array $aliases = [])
{
$docParser = $docParser ?: new DocParser();
$docParser = new DocParser();
$docParser->setIgnoreNotImportedAnnotations(true);
$docParser->setImports(static::$defaultImports);
$docParser->setImports($aliases);
$this->docParser = $docParser;
}

public function setAliases(array $aliases)
{
$this->docParser->setImports($aliases);
}

/**
* Use doctrine to parse the comment block and return the detected annotations.
*
Expand Down
16 changes: 8 additions & 8 deletions src/Analysers/TokenAnalyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ public function fromCode(string $code, Context $context): Analysis
*/
protected function fromTokens(array $tokens, Context $parseContext): Analysis
{
$docBlockParser = new DocBlockParser();
$analysis = new Analysis([], $parseContext);
$generator = $this->generator ?: new Generator();
$analysis = new Analysis([], $parseContext);
$docBlockParser = new DocBlockParser($generator->getAliases());

reset($tokens);
$token = '';

$imports = $generator->getAliases();
$aliases = $generator->getAliases();

$parseContext->uses = [];
// default to parse context to start with
Expand Down Expand Up @@ -351,8 +351,8 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis

if ($token[0] === T_NAMESPACE) {
$parseContext->namespace = $this->parseNamespace($tokens, $token, $parseContext);
$imports['__NAMESPACE__'] = $parseContext->namespace;
$docBlockParser->docParser->setImports($imports);
$aliases['__NAMESPACE__'] = $parseContext->namespace;
$docBlockParser->setAliases($aliases);
continue;
}

Expand All @@ -371,16 +371,16 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis

$namespaces = $generator->getNamespaces();
if (null === $namespaces) {
$imports[strtolower($alias)] = $target;
$aliases[strtolower($alias)] = $target;
} else {
foreach ($namespaces as $namespace) {
if (strcasecmp(substr($target . '\\', 0, strlen($namespace)), $namespace) === 0) {
$imports[strtolower($alias)] = $target;
$aliases[strtolower($alias)] = $target;
break;
}
}
}
$docBlockParser->docParser->setImports($imports);
$docBlockParser->setAliases($aliases);
}
}
}
Expand Down
35 changes: 14 additions & 21 deletions src/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use OpenApi\Analysers\AnalyserInterface;
use OpenApi\Analysers\AttributeAnnotationFactory;
use OpenApi\Analysers\DocBlockAnnotationFactory;
use OpenApi\Analysers\DocBlockParser;
use OpenApi\Analysers\ReflectionAnalyser;
use OpenApi\Annotations\OpenApi;
use OpenApi\Loggers\DefaultLogger;
Expand Down Expand Up @@ -50,11 +49,16 @@ class Generator
/** @var string Magic value to differentiate between null and undefined. */
public const UNDEFINED = '@OA\Generator::UNDEFINED🙈';

/** @var string[] */
public const DEFAULT_ALIASES = ['oa' => 'OpenApi\\Annotations'];
/** @var string[] */
public const DEFAULT_NAMESPACES = ['OpenApi\\Annotations\\'];

/** @var array Map of namespace aliases to be supported by doctrine. */
protected $aliases = null;
protected $aliases;

/** @var array|null List of annotation namespaces to be autoloaded by doctrine. */
protected $namespaces = ['OpenApi\\Annotations\\'];
protected $namespaces;

/** @var AnalyserInterface The configured analyzer. */
protected $analyser;
Expand All @@ -71,20 +75,14 @@ public function __construct(?LoggerInterface $logger = null)
{
$this->logger = $logger;

$this->setAliases(self::DEFAULT_ALIASES);
$this->setNamespaces(self::DEFAULT_NAMESPACES);

// kinda config stack to stay BC...
$this->configStack = new class() {
private $defaultImports;

public function push(Generator $generator): void
{
$this->generator = $generator;

// save current state
$this->defaultImports = DocBlockParser::$defaultImports;

// update state with generator config
DocBlockParser::$defaultImports = $generator->getAliases();

if (class_exists(AnnotationRegistry::class, true)) {
// keeping track of &this->generator allows to 'disable' the loader after we are done;
// no unload, unfortunately :/
Expand Down Expand Up @@ -116,21 +114,16 @@ function (string $class) use (&$gref): bool {
public function pop(): void
{
$this->generator = null;

DocBlockParser::$defaultImports = $this->defaultImports;
}
};
}

public function getAliases(): array
{
$aliases = null !== $this->aliases ? $this->aliases : DocBlockParser::$defaultImports;
$aliases['oa'] = 'OpenApi\\Annotations';

return $aliases;
return $this->aliases;
}

public function setAliases(?array $aliases): Generator
public function setAliases(array $aliases): Generator
{
$this->aliases = $aliases;

Expand Down Expand Up @@ -282,8 +275,8 @@ public static function scan(iterable $sources, array $options = []): ?OpenApi
{
// merge with defaults
$config = $options + [
'aliases' => null,
'namespaces' => null,
'aliases' => self::DEFAULT_ALIASES,
'namespaces' => self::DEFAULT_NAMESPACES,
'analyser' => null,
'analysis' => null,
'processors' => null,
Expand Down
27 changes: 13 additions & 14 deletions tests/Analysers/DocBlockParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,15 @@

namespace OpenApi\Tests\Analysers;

use OpenApi\Analysers\DocBlockParser;
use OpenApi\Tests\OpenApiTestCase;

class DocBlockParserTest extends OpenApiTestCase
{
protected function setUp(): void
{
parent::setUp();
DocBlockParser::$defaultImports['swg'] = 'OpenApi\Annotations';
}

protected function tearDown(): void
{
unset(DocBlockParser::$defaultImports['swg']);
parent::tearDown();
}
const SWG_ALIAS = ['swg' => 'OpenApi\Annotations'];

public function testParseContents()
{
$annotations = $this->annotationsFromDocBlockParser('@OA\Parameter(description="This is my parameter")');
$annotations = $this->annotationsFromDocBlockParser('@OA\Parameter(description="This is my parameter")', self::SWG_ALIAS);
$this->assertIsArray($annotations);
$parameter = $annotations[0];
$this->assertInstanceOf('OpenApi\Annotations\Parameter', $parameter);
Expand All @@ -35,6 +24,16 @@ public function testParseContents()
public function testDeprecatedAnnotationWarning()
{
$this->assertOpenApiLogEntryContains('The annotation @SWG\Definition() is deprecated.');
$this->annotationsFromDocBlockParser('@SWG\Definition()');
$this->annotationsFromDocBlockParser('@SWG\Definition()', self::SWG_ALIAS);
}

public function testExtraAliases()
{
$extraAliases = [
'contact' => 'OpenApi\Annotations\Contact', // use OpenApi\Annotations\Contact;
'ctest' => 'OpenApi\Tests\ConstantsTesT', // use OpenApi\Tests\ConstantsTesT as CTest;
];
$annotations = $this->annotationsFromDocBlockParser('@Contact(url=CTest::URL)', $extraAliases);
$this->assertSame('http://example.com', $annotations[0]->url);
}
}
6 changes: 3 additions & 3 deletions tests/Analysers/TokenAnalyserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public function descriptions()
*/
public function testDescription($type, $name, $fixture, $fqdn, $extends, $methods, $interfaces, $traits)
{
$analysis = $this->analysisFromFixtures($fixture);
$analysis = $this->analysisFromFixtures([$fixture]);

list($pType, $sType) = $type;
$description = $analysis->$pType[$fqdn];
Expand All @@ -228,7 +228,7 @@ public function testDescription($type, $name, $fixture, $fqdn, $extends, $method

public function testNamespacedConstAccess()
{
$analysis = $this->analysisFromFixtures('Parser/User.php');
$analysis = $this->analysisFromFixtures(['Parser/User.php']);
$schemas = $analysis->getAnnotationsOfType(Schema::class, true);

$this->assertCount(1, $schemas);
Expand Down Expand Up @@ -257,7 +257,7 @@ public function testPhp8AttributeMix()
*/
public function testPhp8NamedProperty()
{
$analysis = $this->analysisFromFixtures('PHP/Php8NamedProperty.php');
$analysis = $this->analysisFromFixtures(['PHP/Php8NamedProperty.php']);
$schemas = $analysis->getAnnotationsOfType(Schema::class, true);

$this->assertCount(1, $schemas);
Expand Down
2 changes: 1 addition & 1 deletion tests/Annotations/NestedPropertyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class NestedPropertyTest extends OpenApiTestCase
{
public function testNestedProperties()
{
$analysis = $this->analysisFromFixtures('NestedProperty.php');
$analysis = $this->analysisFromFixtures(['NestedProperty.php']);
$analysis->process([
new MergeIntoOpenApi(),
new MergeIntoComponents(),
Expand Down
13 changes: 0 additions & 13 deletions tests/ConstantsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

namespace OpenApi\Tests;

use OpenApi\Analysers\DocBlockParser;
use OpenApi\Analysers\TokenAnalyser;
use OpenApi\Generator;

Expand Down Expand Up @@ -60,16 +59,4 @@ public function testDynamicImports()
$analyser->fromFile($this->fixture('Customer.php'), $this->getContext());
$analyser->fromFile($this->fixture('ThirdPartyAnnotations.php'), $this->getContext());
}

public function testDefaultImports()
{
$backup = DocBlockParser::$defaultImports;
DocBlockParser::$defaultImports = [
'contact' => 'OpenApi\Annotations\Contact', // use OpenApi\Annotations\Contact;
'ctest' => 'OpenApi\Tests\ConstantsTesT', // use OpenApi\Tests\ConstantsTesT as CTest;
];
$annotations = $this->annotationsFromDocBlockParser('@Contact(url=CTest::URL)');
$this->assertSame('http://example.com', $annotations[0]->url);
DocBlockParser::$defaultImports = $backup;
}
}
Loading

0 comments on commit 108e7d6

Please sign in to comment.