From 77d7215033a521a88659919a224c285517bb65e1 Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Mon, 2 Dec 2024 10:08:30 +1300 Subject: [PATCH] Remove legacy `TokenAnalyser` --- .github/workflows/build.yml | 5 - README.md | 3 - bin/openapi | 8 +- composer.json | 9 +- docs/guide/generating-openapi-documents.md | 1 - docs/guide/under-the-hood.md | 1 + rector.php | 3 - src/Analysers/TokenAnalyser.php | 639 ------------------- tests/Analysers/ReflectionAnalyserTest.php | 2 - tests/Analysers/TokenAnalyserTest.php | 300 --------- tests/Annotations/AbstractAnnotationTest.php | 2 - tests/ConstantsTest.php | 11 - tests/ContextTest.php | 3 +- tests/ExamplesTest.php | 28 +- tests/OpenApiTestCase.php | 14 +- tests/Processors/AugmentTagsTest.php | 8 - tests/Processors/ExpandEnumsTest.php | 7 - tests/UtilTest.php | 28 - 18 files changed, 18 insertions(+), 1054 deletions(-) delete mode 100644 src/Analysers/TokenAnalyser.php delete mode 100644 tests/Analysers/TokenAnalyserTest.php diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9b29c2fe2..bc6d95b31 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,8 +38,3 @@ jobs: - name: PHPUnit Tests run: bin/phpunit --configuration phpunit.xml.dist --coverage-text - - - name: PHPUnit Legacy Tests - run: bin/phpunit --configuration phpunit.xml.dist --coverage-text - env: - PHPUNIT_ANALYSER: 'legacy' diff --git a/README.md b/README.md index 3794db60c..7f1ac3b2b 100644 --- a/README.md +++ b/README.md @@ -98,9 +98,6 @@ The `openapi` command line interface can be used to generate the documentation t ```shell ./vendor/bin/openapi --help ``` -Starting with version 4 the default analyser used on the command line is the new `ReflectionAnalyser`. - -Using the `--legacy` flag (`-l`) the legacy `TokenAnalyser` can still be used. ### Usage from the Deserializer diff --git a/bin/openapi b/bin/openapi index a41fa0d75..dae40fbbb 100755 --- a/bin/openapi +++ b/bin/openapi @@ -4,7 +4,6 @@ use OpenApi\Analysers\AttributeAnnotationFactory; use OpenApi\Analysers\DocBlockAnnotationFactory; use OpenApi\Analysers\ReflectionAnalyser; -use OpenApi\Analysers\TokenAnalyser; use OpenApi\Annotations\OpenApi; use OpenApi\Generator; use OpenApi\Util; @@ -23,7 +22,6 @@ error_reporting(E_ALL); // Possible options and their default values. $options = [ 'config' => [], - 'legacy' => false, 'output' => false, 'format' => 'auto', 'exclude' => [], @@ -36,7 +34,6 @@ $options = [ ]; $aliases = [ 'c' => 'config', - 'l' => 'legacy', 'o' => 'output', 'e' => 'exclude', 'n' => 'pattern', @@ -132,7 +129,6 @@ Usage: openapi [--option value] [/path/to/project ...] Options: --config (-c) Generator config ex: -c operationId.hash=false - --legacy (-l) Use legacy TokenAnalyser; default is the new ReflectionAnalyser --output (-o) Path to store the generated documentation. ex: --output openapi.yaml --exclude (-e) Exclude path(s). @@ -222,9 +218,7 @@ foreach ($options["processor"] as $processor) { $generator->getProcessorPipeline()->add($processor); } -$analyser = $options['legacy'] - ? new TokenAnalyser() - : new ReflectionAnalyser([new DocBlockAnnotationFactory(), new AttributeAnnotationFactory()]); +$analyser = new ReflectionAnalyser([new DocBlockAnnotationFactory(), new AttributeAnnotationFactory()]); $openapi = $generator ->setVersion($options['version']) diff --git a/composer.json b/composer.json index 51c62f36a..1dce1a1fa 100644 --- a/composer.json +++ b/composer.json @@ -86,9 +86,7 @@ "cs": "Fix all codestyle issues", "rector": "Automatic refactoring", "lint": "Test codestyle", - "test": "Run all non-legacy and codestyle tests", - "testlegacy": "Run tests using the legacy TokenAnalyser", - "testall": "Run all tests (test + testlegacy)", + "test": "Run all PHP, codestyle and rector tests", "analyse": "Run static analysis (phpstan/psalm)", "spectral-examples": "Run spectral lint over all .yaml files in the Examples folder", "spectral-scratch": "Run spectral lint over all .yaml files in the tests/Fixtures/Scratch folder", @@ -108,11 +106,6 @@ "export XDEBUG_MODE=off && phpunit", "@lint" ], - "testlegacy": "export XDEBUG_MODE=off && export PHPUNIT_ANALYSER=legacy && phpunit", - "testall": [ - "@test", - "@testlegacy" - ], "analyse": [ "export XDEBUG_MODE=off && phpstan analyse --memory-limit=2G", "export XDEBUG_MODE=off && psalm" diff --git a/docs/guide/generating-openapi-documents.md b/docs/guide/generating-openapi-documents.md index 92fa204b2..e19ba23e2 100644 --- a/docs/guide/generating-openapi-documents.md +++ b/docs/guide/generating-openapi-documents.md @@ -36,7 +36,6 @@ Usage: openapi [--option value] [/path/to/project ...] Options: --config (-c) Generator config ex: -c operationId.hash=false - --legacy (-l) Use legacy TokenAnalyser; default is the new ReflectionAnalyser --output (-o) Path to store the generated documentation. ex: --output openapi.yaml --exclude (-e) Exclude path(s). diff --git a/docs/guide/under-the-hood.md b/docs/guide/under-the-hood.md index 6be39cd00..489fa6e68 100644 --- a/docs/guide/under-the-hood.md +++ b/docs/guide/under-the-hood.md @@ -5,6 +5,7 @@ - The `Generator` iterates over the given sources (Symfony `Finder`, file/directory list, etc) - The configured analyser (`AnalyserInterface`) reads the files and builds an `Analysis` object. Default (as of v4) is the `ReflectionAnalyser`. Alternatively, there is the `TokenAnalyser` which was the default in v3. +- The legacy `TokenAnalyser` was removed in v5. - The `Analysis` object and its annotations are then processed by the configured processors. - If enabled, the analysis/annotations are validated. - The root `OpenApi` annotation then contains all annotations and is serialized into YAML/JSON. diff --git a/rector.php b/rector.php index e0a7c581b..9bf87fe2a 100644 --- a/rector.php +++ b/rector.php @@ -18,9 +18,6 @@ ->withSkip([ CombineIfRector::class, ExplicitBoolCompareRector::class, - ExplicitReturnNullRector::class => [ - __DIR__ . '/src/Analysers/TokenAnalyser.php', - ], ForRepeatedCountToOwnVariableRector::class, RemoveAlwaysTrueIfConditionRector::class => [ __DIR__ . '/src/Processors/ExpandEnums.php', diff --git a/src/Analysers/TokenAnalyser.php b/src/Analysers/TokenAnalyser.php deleted file mode 100644 index ad2dc0210..000000000 --- a/src/Analysers/TokenAnalyser.php +++ /dev/null @@ -1,639 +0,0 @@ -logger->error("php.ini \"opcache.save_comments = 0\" interferes with extracting annotations.\n[LINK] https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.save-comments"); - } - } - } - $tokens = token_get_all(file_get_contents($filename)); - - return $this->fromTokens($tokens, new Context(['filename' => $filename], $context)); - } - - /** - * Extract and process all doc-comments from the contents. - * - * @param string $code PHP code. (including fromTokens($tokens, $context); - } - - /** - * Shared implementation for parseFile() & parseContents(). - * - * @param array $tokens The result of a token_get_all() - */ - protected function fromTokens(array $tokens, Context $parseContext): Analysis - { - $generator = $this->generator ?: new Generator(); - $analysis = new Analysis([], $parseContext); - $docBlockParser = new DocBlockParser($generator->getAliases()); - - reset($tokens); - $token = ''; - - $aliases = $generator->getAliases(); - - $parseContext->uses = []; - // default to parse context to start with - $schemaContext = $parseContext; - - $classDefinition = false; - $interfaceDefinition = false; - $traitDefinition = false; - $enumDefinition = false; - $comment = false; - - $line = 0; - $lineOffset = $parseContext->line ?: 0; - - while ($token !== false) { - $previousToken = $token; - $token = $this->nextToken($tokens, $parseContext); - - if (is_array($token) === false) { - // Ignore tokens like "{", "}", etc - continue; - } - - if (defined('T_ATTRIBUTE') && $token[0] === T_ATTRIBUTE) { - // consume - $this->parseAttribute($tokens, $token, $parseContext); - continue; - } - - if ($token[0] === T_DOC_COMMENT) { - if ($comment) { - // 2 Doc-comments in succession? - $this->analyseComment($analysis, $docBlockParser, $comment, new Context(['line' => $line], $schemaContext)); - } - $comment = $token[1]; - $line = $token[2] + $lineOffset; - continue; - } - - if (in_array($token[0], [T_ABSTRACT, T_FINAL])) { - // skip - $token = $this->nextToken($tokens, $parseContext); - } - - if ($token[0] === T_CLASS) { - // Doc-comment before a class? - if (is_array($previousToken) && $previousToken[0] === T_DOUBLE_COLON) { - // php 5.5 class name resolution (i.e. ClassName::class) - continue; - } - - $token = $this->nextToken($tokens, $parseContext); - - if (is_string($token) && ($token === '(' || $token === '{')) { - // php7 anonymous classes (i.e. new class() { public function foo() {} };) - continue; - } - - if (is_array($token) && ($token[1] === 'extends' || $token[1] === 'implements')) { - // php7 anonymous classes with extends (i.e. new class() extends { public function foo() {} };) - continue; - } - - if (!is_array($token)) { - // PHP 8 named argument - continue; - } - - $interfaceDefinition = false; - $traitDefinition = false; - $enumDefinition = false; - - $schemaContext = new Context(['class' => $token[1], 'line' => $token[2]], $parseContext); - if ($classDefinition) { - $analysis->addClassDefinition($classDefinition); - } - $classDefinition = [ - 'class' => $token[1], - 'extends' => null, - 'properties' => [], - 'methods' => [], - 'context' => $schemaContext, - ]; - - $token = $this->nextToken($tokens, $parseContext); - - if ($token[0] === T_EXTENDS) { - $schemaContext->extends = $this->parseNamespace($tokens, $token, $parseContext); - $classDefinition['extends'] = $schemaContext->fullyQualifiedName($schemaContext->extends); - } - - if ($token[0] === T_IMPLEMENTS) { - $schemaContext->implements = $this->parseNamespaceList($tokens, $token, $parseContext); - $classDefinition['implements'] = array_map(function (?string $source) use ($schemaContext): string { - return $schemaContext->fullyQualifiedName($source); - }, $schemaContext->implements); - } - - if ($comment) { - $schemaContext->line = $line; - $this->analyseComment($analysis, $docBlockParser, $comment, $schemaContext); - $comment = false; - continue; - } - - // @todo detect end-of-class and reset $schemaContext - } - - if ($token[0] === T_INTERFACE) { // Doc-comment before an interface? - $classDefinition = false; - $traitDefinition = false; - $enumDefinition = false; - - $token = $this->nextToken($tokens, $parseContext); - - if (!is_array($token)) { - // PHP 8 named argument - continue; - } - - $schemaContext = new Context(['interface' => $token[1], 'line' => $token[2]], $parseContext); - if ($interfaceDefinition) { - $analysis->addInterfaceDefinition($interfaceDefinition); - } - $interfaceDefinition = [ - 'interface' => $token[1], - 'extends' => null, - 'properties' => [], - 'methods' => [], - 'context' => $schemaContext, - ]; - - $token = $this->nextToken($tokens, $parseContext); - - if ($token[0] === T_EXTENDS) { - $schemaContext->extends = $this->parseNamespaceList($tokens, $token, $parseContext); - $interfaceDefinition['extends'] = array_map(function (?string $source) use ($schemaContext): string { - return $schemaContext->fullyQualifiedName($source); - }, $schemaContext->extends); - } - - if ($comment) { - $schemaContext->line = $line; - $this->analyseComment($analysis, $docBlockParser, $comment, $schemaContext); - $comment = false; - continue; - } - - // @todo detect end-of-interface and reset $schemaContext - } - - if ($token[0] === T_TRAIT) { - $classDefinition = false; - $interfaceDefinition = false; - $enumDefinition = false; - - $token = $this->nextToken($tokens, $parseContext); - - if (!is_array($token)) { - // PHP 8 named argument - continue; - } - - $schemaContext = new Context(['trait' => $token[1], 'line' => $token[2]], $parseContext); - if ($traitDefinition) { - $analysis->addTraitDefinition($traitDefinition); - } - $traitDefinition = [ - 'trait' => $token[1], - 'properties' => [], - 'methods' => [], - 'context' => $schemaContext, - ]; - - if ($comment) { - $schemaContext->line = $line; - $this->analyseComment($analysis, $docBlockParser, $comment, $schemaContext); - $comment = false; - continue; - } - - // @todo detect end-of-trait and reset $schemaContext - } - - if (defined('T_ENUM') && $token[0] === T_ENUM) { - $classDefinition = false; - $interfaceDefinition = false; - $traitDefinition = false; - - $token = $this->nextToken($tokens, $parseContext); - - if (!is_array($token)) { - // PHP 8 named argument - continue; - } - - $schemaContext = new Context(['enum' => $token[1], 'line' => $token[2]], $parseContext); - if ($enumDefinition) { - $analysis->addEnumDefinition($enumDefinition); - } - $enumDefinition = [ - 'enum' => $token[1], - 'properties' => [], - 'methods' => [], - 'context' => $schemaContext, - ]; - - if ($comment) { - $schemaContext->line = $line; - $this->analyseComment($analysis, $docBlockParser, $comment, $schemaContext); - $comment = false; - continue; - } - - // @todo detect end-of-trait and reset $schemaContext - } - - if ($token[0] === T_STATIC) { - $token = $this->nextToken($tokens, $parseContext); - if ($token[0] === T_VARIABLE) { - // static property - $propertyContext = new Context( - [ - 'property' => substr($token[1], 1), - 'static' => true, - 'line' => $line, - ], - $schemaContext - ); - - if ($classDefinition) { - $classDefinition['properties'][$propertyContext->property] = $propertyContext; - } - if ($traitDefinition) { - $traitDefinition['properties'][$propertyContext->property] = $propertyContext; - } - if ($comment) { - $this->analyseComment($analysis, $docBlockParser, $comment, $propertyContext); - $comment = false; - } - continue; - } - } - - if (in_array($token[0], [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_VAR])) { // Scope - [$type, $nullable, $token] = $this->parseTypeAndNextToken($tokens, $parseContext); - if ($token[0] === T_VARIABLE) { - // instance property - $propertyContext = new Context( - [ - 'property' => substr($token[1], 1), - 'type' => $type, - 'nullable' => $nullable, - 'line' => $line, - ], - $schemaContext - ); - - if ($classDefinition) { - $classDefinition['properties'][$propertyContext->property] = $propertyContext; - } - if ($interfaceDefinition) { - $interfaceDefinition['properties'][$propertyContext->property] = $propertyContext; - } - if ($traitDefinition) { - $traitDefinition['properties'][$propertyContext->property] = $propertyContext; - } - if ($comment) { - $this->analyseComment($analysis, $docBlockParser, $comment, $propertyContext); - $comment = false; - } - } elseif ($token[0] === T_FUNCTION) { - $token = $this->nextToken($tokens, $parseContext); - if ($token[0] === T_STRING) { - $methodContext = new Context( - [ - 'method' => $token[1], - 'line' => $line, - ], - $schemaContext - ); - - if ($classDefinition) { - $classDefinition['methods'][$token[1]] = $methodContext; - } - if ($interfaceDefinition) { - $interfaceDefinition['methods'][$token[1]] = $methodContext; - } - if ($traitDefinition) { - $traitDefinition['methods'][$token[1]] = $methodContext; - } - if ($comment) { - $this->analyseComment($analysis, $docBlockParser, $comment, $methodContext); - $comment = false; - } - } - } - continue; - } elseif ($token[0] === T_FUNCTION) { - $token = $this->nextToken($tokens, $parseContext); - if ($token[0] === T_STRING) { - $methodContext = new Context( - [ - 'method' => $token[1], - 'line' => $line, - ], - $schemaContext - ); - - if ($classDefinition) { - $classDefinition['methods'][$token[1]] = $methodContext; - } - if ($interfaceDefinition) { - $interfaceDefinition['methods'][$token[1]] = $methodContext; - } - if ($traitDefinition) { - $traitDefinition['methods'][$token[1]] = $methodContext; - } - if ($comment) { - $this->analyseComment($analysis, $docBlockParser, $comment, $methodContext); - $comment = false; - } - } - } - - if (in_array($token[0], [T_NAMESPACE, T_USE]) === false) { - // Skip "use" & "namespace" to prevent "never imported" warnings) - if ($comment) { - // Not a doc-comment for a class, property or method? - $this->analyseComment($analysis, $docBlockParser, $comment, new Context(['line' => $line], $schemaContext)); - $comment = false; - } - } - - if ($token[0] === T_NAMESPACE) { - $parseContext->namespace = $this->parseNamespace($tokens, $token, $parseContext); - $aliases['__NAMESPACE__'] = $parseContext->namespace; - $docBlockParser->setAliases($aliases); - continue; - } - - if ($token[0] === T_USE) { - $statements = $this->parseUseStatement($tokens, $token, $parseContext); - foreach ($statements as $alias => $target) { - if ($classDefinition) { - // class traits - $classDefinition['traits'][] = $schemaContext->fullyQualifiedName($target); - } elseif ($traitDefinition) { - // trait traits - $traitDefinition['traits'][] = $schemaContext->fullyQualifiedName($target); - } else { - // not a trait use - $parseContext->uses[$alias] = $target; - - $namespaces = $generator->getNamespaces(); - if (null === $namespaces) { - $aliases[strtolower($alias)] = $target; - } else { - foreach ($namespaces as $namespace) { - if (strcasecmp(substr($target . '\\', 0, strlen($namespace)), $namespace) === 0) { - $aliases[strtolower($alias)] = $target; - break; - } - } - } - $docBlockParser->setAliases($aliases); - } - } - } - } - - // cleanup final comment and definition - if ($comment) { - $this->analyseComment($analysis, $docBlockParser, $comment, new Context(['line' => $line], $schemaContext)); - } - if ($classDefinition) { - $analysis->addClassDefinition($classDefinition); - } - if ($interfaceDefinition) { - $analysis->addInterfaceDefinition($interfaceDefinition); - } - if ($traitDefinition) { - $analysis->addTraitDefinition($traitDefinition); - } - if ($enumDefinition) { - $analysis->addEnumDefinition($enumDefinition); - } - - return $analysis; - } - - /** - * Parse comment and add annotations to analysis. - */ - private function analyseComment(Analysis $analysis, DocBlockParser $docBlockParser, string $comment, Context $context): void - { - $analysis->addAnnotations($docBlockParser->fromComment($comment, $context), $context); - } - - /** - * The next non-whitespace, non-comment token. - * - * - * @return array|string The next token (or false) - */ - private function nextToken(array &$tokens, Context $context) - { - while (true) { - $token = next($tokens); - if (is_array($token)) { - if ($token[0] === T_WHITESPACE) { - continue; - } - if ($token[0] === T_COMMENT) { - $pos = strpos($token[1], '@OA\\'); - if ($pos) { - $line = $context->line ? $context->line + $token[2] : $token[2]; - $commentContext = new Context(['line' => $line], $context); - $context->logger->warning('Annotations are only parsed inside `/**` DocBlocks, skipping ' . $commentContext); - } - continue; - } - } - - return $token; - } - } - - private function parseAttribute(array &$tokens, &$token, Context $parseContext): void - { - $nesting = 1; - while ($token !== false) { - $token = $this->nextToken($tokens, $parseContext); - if (!is_array($token) && '[' === $token) { - ++$nesting; - continue; - } - - if (!is_array($token) && ']' === $token) { - --$nesting; - if (!$nesting) { - break; - } - } - } - } - - /** - * @return int[] - */ - private function php8NamespaceToken(): array - { - return defined('T_NAME_QUALIFIED') ? [T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED] : []; - } - - /** - * Parse namespaced string. - * - * @param array|string $token - */ - private function parseNamespace(array &$tokens, &$token, Context $parseContext): string - { - $namespace = ''; - $nsToken = array_merge([T_STRING, T_NS_SEPARATOR], $this->php8NamespaceToken()); - while ($token !== false) { - $token = $this->nextToken($tokens, $parseContext); - if (!in_array($token[0], $nsToken)) { - break; - } - $namespace .= $token[1]; - } - - return $namespace; - } - - /** - * Parse comma separated list of namespaced strings. - * - * @param array|string $token - */ - private function parseNamespaceList(array &$tokens, &$token, Context $parseContext): array - { - $namespaces = []; - while ($namespace = $this->parseNamespace($tokens, $token, $parseContext)) { - $namespaces[] = $namespace; - if ($token != ',') { - break; - } - } - - return $namespaces; - } - - /** - * Parse a use statement. - * - * @param (int|mixed)[]|string $token - */ - private function parseUseStatement(array &$tokens, &$token, Context $parseContext): array - { - $normalizeAlias = function ($alias): string { - $alias = ltrim($alias, '\\'); - $elements = explode('\\', $alias); - - return array_pop($elements); - }; - - $class = ''; - $alias = ''; - $statements = []; - $explicitAlias = false; - $nsToken = array_merge([T_STRING, T_NS_SEPARATOR], $this->php8NamespaceToken()); - while ($token !== false) { - $token = $this->nextToken($tokens, $parseContext); - $isNameToken = in_array($token[0], $nsToken); - if (!$explicitAlias && $isNameToken) { - $class .= $token[1]; - $alias = $token[1]; - } elseif ($explicitAlias && $isNameToken) { - $alias .= $token[1]; - } elseif ($token[0] === T_AS) { - $explicitAlias = true; - $alias = ''; - } elseif ($token === ',') { - $statements[$normalizeAlias($alias)] = $class; - $class = ''; - $alias = ''; - $explicitAlias = false; - } elseif ($token === ';') { - $statements[$normalizeAlias($alias)] = $class; - break; - } else { - break; - } - } - - return $statements; - } - - /** - * Parse type of variable (if it exists). - */ - private function parseTypeAndNextToken(array &$tokens, Context $parseContext): array - { - $type = Generator::UNDEFINED; - $nullable = false; - $token = $this->nextToken($tokens, $parseContext); - - if ($token[0] === T_STATIC) { - $token = $this->nextToken($tokens, $parseContext); - } - - if ($token === '?') { // nullable type - $nullable = true; - $token = $this->nextToken($tokens, $parseContext); - } - - $qualifiedToken = array_merge([T_NS_SEPARATOR, T_STRING, T_ARRAY], $this->php8NamespaceToken()); - $typeToken = array_merge([T_STRING], $this->php8NamespaceToken()); - // drill down namespace segments to basename property type declaration - while (in_array($token[0], $qualifiedToken)) { - if (in_array($token[0], $typeToken)) { - $type = $token[1]; - } - $token = $this->nextToken($tokens, $parseContext); - } - - return [$type, $nullable, $token]; - } -} diff --git a/tests/Analysers/ReflectionAnalyserTest.php b/tests/Analysers/ReflectionAnalyserTest.php index 64cb85248..fa731788d 100644 --- a/tests/Analysers/ReflectionAnalyserTest.php +++ b/tests/Analysers/ReflectionAnalyserTest.php @@ -182,8 +182,6 @@ public function testApiMixedBasic(AnalyserInterface $analyser): void */ public function testPhp8PromotedProperties(): void { - $this->skipLegacy(); - $analysis = $this->analysisFromFixtures(['PHP/Php8PromotedProperties.php']); $schemas = $analysis->getAnnotationsOfType(OA\Schema::class, true); diff --git a/tests/Analysers/TokenAnalyserTest.php b/tests/Analysers/TokenAnalyserTest.php deleted file mode 100644 index dac65837a..000000000 --- a/tests/Analysers/TokenAnalyserTest.php +++ /dev/null @@ -1,300 +0,0 @@ -setGenerator(new Generator()); - - return $analyser->fromCode('getContext()); - } - - public static function singleDefinitionCases(): iterable - { - return [ - 'global-class' => ['class AClass {}', '\AClass', 'AClass', 'classes', 'class'], - 'global-interface' => ['interface AInterface {}', '\AInterface', 'AInterface', 'interfaces', 'interface'], - 'global-trait' => ['trait ATrait {}', '\ATrait', 'ATrait', 'traits', 'trait'], - - 'namespaced-class' => ['namespace SNS\Foo; class AClass {}', '\SNS\Foo\AClass', 'AClass', 'classes', 'class'], - 'namespaced-interface' => ['namespace SNS\Foo; interface AInterface {}', '\SNS\Foo\AInterface', 'AInterface', 'interfaces', 'interface'], - 'namespaced-trait' => ['namespace SNS\Foo; trait ATrait {}', '\SNS\Foo\ATrait', 'ATrait', 'traits', 'trait'], - ]; - } - - /** - * @dataProvider singleDefinitionCases - */ - public function testSingleDefinition(string $code, string $fqdn, string $name, string $type, string $typeKey): void - { - $analysis = $this->analysisFromCode($code); - - $this->assertSame([$fqdn], array_keys($analysis->$type)); - $definition = $analysis->{$type}[$fqdn]; - $this->assertSame($name, $definition[$typeKey]); - $this->assertTrue(!array_key_exists('extends', $definition) || !$definition['extends']); - $this->assertSame([], $definition['properties']); - $this->assertSame([], $definition['methods']); - } - - public static function extendsDefinitionCases(): iterable - { - return [ - 'global-class' => ['class BClass extends Other {}', '\BClass', 'BClass', '\Other', 'classes', 'class'], - 'namespaced-class' => ['namespace NC\Foo; class BClass extends \Other {}', '\NC\Foo\BClass', 'BClass', '\Other', 'classes', 'class'], - 'global-class-explicit' => ['class EClass extends \Bar\Other {}', '\EClass', 'EClass', '\Bar\Other', 'classes', 'class'], - 'namespaced-class-explicit' => ['namespace NCE\Foo; class AClass extends \Bar\Other {}', '\NCE\Foo\AClass', 'AClass', '\Bar\Other', 'classes', 'class'], - 'global-class-use' => ['use XBar\Other; class XClass extends Other {}', '\XClass', 'XClass', '\XBar\Other', 'classes', 'class'], - 'namespaced-class-use' => ['namespace NCU\Foo; use YBar\Other; class AClass extends Other {}', '\NCU\Foo\AClass', 'AClass', '\YBar\Other', 'classes', 'class'], - 'namespaced-class-as' => ['namespace NCA\Foo; use Bar\Some as Other; class AClass extends Other {}', '\NCA\Foo\AClass', 'AClass', '\Bar\Some', 'classes', 'class'], - 'namespaced-class-same' => ['namespace NCS\Foo; class AClass extends Other {}', '\NCS\Foo\AClass', 'AClass', '\NCS\Foo\Other', 'classes', 'class'], - - 'global-interface' => ['interface BInterface extends Other {}', '\BInterface', 'BInterface', ['\Other'], 'interfaces', 'interface'], - 'namespaced-interface' => ['namespace NI\Foo; interface AInterface extends \Other {}', '\NI\Foo\AInterface', 'AInterface', ['\Other'], 'interfaces', 'interface'], - 'global-interface-explicit' => ['interface XInterface extends \ZBar\Other {}', '\XInterface', 'XInterface', ['\ZBar\Other'], 'interfaces', 'interface'], - 'namespaced-interface-explicit' => ['namespace NIE\Foo; interface AInterface extends \ABar\Other {}', '\NIE\Foo\AInterface', 'AInterface', ['\ABar\Other'], 'interfaces', 'interface'], - 'global-interface-use' => ['use BBar\Other; interface YInterface extends Other {}', '\YInterface', 'YInterface', ['\BBar\Other'], 'interfaces', 'interface'], - 'namespaced-interface-use' => ['namespace NIU\Foo; use EBar\Other; interface AInterface extends Other {}', '\NIU\Foo\AInterface', 'AInterface', ['\EBar\Other'], 'interfaces', 'interface'], - 'namespaced-interface-use-multi' => ['namespace NIUM\Foo; use FBar\Other; interface AInterface extends Other, \More {}', '\NIUM\Foo\AInterface', 'AInterface', ['\FBar\Other', '\More'], 'interfaces', 'interface'], - 'namespaced-interface-as' => ['namespace NIA\Foo; use Bar\Some as Other; interface AInterface extends Other {}', '\NIA\Foo\AInterface', 'AInterface', ['\Bar\Some'], 'interfaces', 'interface'], - ]; - } - - /** - * @dataProvider extendsDefinitionCases - * - * @param string|array $extends - */ - public function testExtendsDefinition(string $code, string $fqdn, string $name, $extends, string $type, string $typeKey): void - { - $analysis = $this->analysisFromCode($code); - - $this->assertSame([$fqdn], array_keys($analysis->$type)); - $definition = $analysis->{$type}[$fqdn]; - $this->assertSame($name, $definition[$typeKey]); - $this->assertSame($extends, $definition['extends']); - } - - public static function usesDefinitionCases(): iterable - { - return [ - 'global-class-use' => ['class YClass { use Other; }', '\YClass', 'YClass', ['\Other'], 'classes', 'class'], - 'namespaced-class-use' => ['namespace UNCU\Foo; class AClass { use \Other; }', '\UNCU\Foo\AClass', 'AClass', ['\Other'], 'classes', 'class'], - 'namespaced-class-use-namespaced' => ['namespace UNCUN\Foo; use GBar\Other; class AClass { use Other; }', '\UNCUN\Foo\AClass', 'AClass', ['\GBar\Other'], 'classes', 'class'], - 'namespaced-class-use-namespaced-as' => ['namespace UNCUNA\Foo; use HBar\Other as Some; class AClass { use Some; }', '\UNCUNA\Foo\AClass', 'AClass', ['\HBar\Other'], 'classes', 'class'], - - 'global-trait-use' => ['trait ATrait { use Other; }', '\ATrait', 'ATrait', ['\Other'], 'traits', 'trait'], - 'namespaced-trait-use' => ['namespace UNTU\Foo; trait ATrait { use \Other; }', '\UNTU\Foo\ATrait', 'ATrait', ['\Other'], 'traits', 'trait'], - 'namespaced-trait-use-explicit' => ['namespace UNTUE\Foo; trait ATrait { use \DBar\Other; }', '\UNTUE\Foo\ATrait', 'ATrait', ['\DBar\Other'], 'traits', 'trait'], - 'namespaced-trait-use-multi' => ['namespace UNTUEM\Foo; trait ATrait { use \Other; use \More; }', '\UNTUEM\Foo\ATrait', 'ATrait', ['\Other', '\More'], 'traits', 'trait'], - 'namespaced-trait-use-mixed' => ['namespace UNTUEX\Foo; use TBar\Other; trait ATrait { use Other, \More; }', '\UNTUEX\Foo\ATrait', 'ATrait', ['\TBar\Other', '\More'], 'traits', 'trait'], - 'namespaced-trait-use-as' => ['namespace UNTUEA\Foo; use MBar\Other as Some; trait ATrait { use Some; }', '\UNTUEA\Foo\ATrait', 'ATrait', ['\MBar\Other'], 'traits', 'trait'], - ]; - } - - /** - * @dataProvider usesDefinitionCases - */ - public function testUsesDefinition(string $code, string $fqdn, string $name, array $traits, string $type, string $typeKey): void - { - $analysis = $this->analysisFromCode($code); - - $this->assertSame([$fqdn], array_keys($analysis->$type)); - $definition = $analysis->{$type}[$fqdn]; - $this->assertSame($name, $definition[$typeKey]); - $this->assertSame($traits, $definition['traits']); - } - - public function testWrongCommentType(): void - { - $analyser = new TokenAnalyser(); - $this->assertOpenApiLogEntryContains('Annotations are only parsed inside `/**` DocBlocks'); - $analyser->fromCode("getContext()); - } - - public function testThirdPartyAnnotations(): void - { - $generator = (new Generator()) - ->setAnalyser(new TokenAnalyser()); - $generator - ->withContext(function (Generator $generator, Analysis $analysis, Context $context) { - $defaultAnalysis = $generator->getAnalyser()->fromFile($this->fixture('ThirdPartyAnnotations.php'), $this->getContext()); - $this->assertCount(3, $defaultAnalysis->annotations, 'Only read the @OA annotations, skip the others.'); - }); - - // Allow the analyser to parse 3rd party annotations, which might - // contain useful info that could be extracted with a custom processor - $generator->addNamespace('AnotherNamespace\\Annotations\\'); - $openapi = $generator - ->generate([$this->fixture('ThirdPartyAnnotations.php')]); - $this->assertSame('api/3rd-party', $openapi->paths[0]->path); - $this->assertCount(4, $openapi->_unmerged); - - $analysis = $openapi->_analysis; - $annotations = $analysis->getAnnotationsOfType('AnotherNamespace\Annotations\Unrelated'); - $this->assertCount(4, $annotations); - $context = $analysis->getContext($annotations[0]); - $this->assertInstanceOf('OpenApi\Context', $context); - $this->assertSame('ThirdPartyAnnotations', $context->class); - $this->assertSame('\OpenApi\Tests\Fixtures\ThirdPartyAnnotations', $context->fullyQualifiedName($context->class)); - $this->assertCount(1, $context->annotations); - } - - public function testAnonymousClassProducesNoError(): void - { - try { - $analyser = new TokenAnalyser(); - $analysis = $analyser->fromFile($this->fixture('PHP/php7.php'), $this->getContext()); - $this->assertNotNull($analysis); - } catch (\Throwable $t) { - $this->fail("Analyser produced an error: {$t->getMessage()}"); - } - } - - public static function descriptions(): iterable - { - return [ - 'class' => [ - ['classes', 'class'], - 'User', - 'Parser/User.php', - '\OpenApi\Tests\Fixtures\Parser\User', - '\OpenApi\Tests\Fixtures\Parser\Sub\SubClass', - ['getFirstName'], - null, - ['\OpenApi\Tests\Fixtures\Parser\HelloTrait'], // use ... as ... - ], - 'interface' => [ - ['interfaces', 'interface'], - 'UserInterface', - 'Parser/UserInterface.php', - '\OpenApi\Tests\Fixtures\Parser\UserInterface', - ['\OpenApi\Tests\Fixtures\Parser\OtherInterface'], - null, - null, - null, - ], - 'trait' => [ - ['traits', 'trait'], - 'HelloTrait', - 'Parser/HelloTrait.php', - '\OpenApi\Tests\Fixtures\Parser\HelloTrait', - null, - null, - null, - ['\OpenApi\Tests\Fixtures\Parser\OtherTrait', '\OpenApi\Tests\Fixtures\Parser\AsTrait'], - ], - ]; - } - - /** - * @dataProvider descriptions - * - * @param string|array $extends - */ - public function testDescription(array $type, string $name, string $fixture, string $fqdn, $extends, ?array $methods, ?array $interfaces, ?array $traits): void - { - $analysis = $this->analysisFromFixtures([$fixture]); - - list($pType, $sType) = $type; - $description = $analysis->$pType[$fqdn]; - - $this->assertSame($name, $description[$sType]); - if (null !== $extends) { - $this->assertSame($extends, $description['extends']); - } - if (null !== $methods) { - $this->assertSame($methods, array_keys($description['methods'])); - } - if (null !== $interfaces) { - $this->assertSame($interfaces, $description['interfaces']); - } - if (null !== $traits) { - $this->assertSame($traits, $description['traits']); - } - } - - public function testNamespacedConstAccess(): void - { - $analysis = $this->analysisFromFixtures(['Parser/User.php']); - /** @var OA\Schema[] $schemas */ - $schemas = $analysis->getAnnotationsOfType(OA\Schema::class, true); - - $this->assertCount(1, $schemas); - $this->assertEquals(User::CONSTANT, $schemas[0]->example); - } - - /** - * @requires PHP 8 - */ - public function testPhp8AttributeMix(): void - { - $analysis = $this->analysisFromFixtures(['PHP/Label.php', 'PHP/Php8AttrMix.php']); - /** @var OA\Schema[] $schemas */ - $schemas = $analysis->getAnnotationsOfType(OA\Schema::class, true); - - $this->assertCount(1, $schemas); - $analysis->process($this->processors([CleanUnusedComponents::class])); - - /** @var OA\Property[] $properties */ - $properties = $analysis->getAnnotationsOfType(OA\Property::class, true); - $this->assertCount(2, $properties); - $this->assertEquals('id', $properties[0]->property); - $this->assertEquals('otherId', $properties[1]->property); - } - - /** - * @requires PHP 8 - */ - public function testPhp8PromotedProperties(): void - { - $analysis = $this->analysisFromFixtures(['PHP/Php8PromotedProperties.php'], [], new TokenAnalyser()); - $schemas = $analysis->getAnnotationsOfType(OA\Schema::class, true); - - $this->assertCount(1, $schemas); - $analysis->process($this->processors([CleanUnusedComponents::class])); - - /** @var OA\Property[] $properties */ - $properties = $analysis->getAnnotationsOfType(OA\Property::class); - // ignores the attribute on $id - $this->assertCount(1, $properties); - $this->assertEquals('labels', $properties[0]->property); - } - - public function testAnonymousFunctions(): void - { - $analysis = $this->analysisFromFixtures(['PHP/AnonymousFunctions.php'], [], new TokenAnalyser()); - (new Generator())->getProcessorPipeline()->process($analysis); - - $infos = $analysis->getAnnotationsOfType(OA\Info::class, true); - $this->assertCount(1, $infos); - } - - /** - * @requires PHP 8 - */ - public function testPhp8NamedArguments(): void - { - $analysis = $this->analysisFromFixtures(['PHP/Php8NamedArguments.php'], [], new TokenAnalyser()); - $schemas = $analysis->getAnnotationsOfType(OA\Schema::class, true); - - $this->assertCount(1, $schemas); - (new Generator())->getProcessorPipeline()->process($analysis); - } -} diff --git a/tests/Annotations/AbstractAnnotationTest.php b/tests/Annotations/AbstractAnnotationTest.php index 7dbc837f6..b516becb5 100644 --- a/tests/Annotations/AbstractAnnotationTest.php +++ b/tests/Annotations/AbstractAnnotationTest.php @@ -148,8 +148,6 @@ public function testIsRoot(): void */ public function testValidateExamples(): void { - $this->skipLegacy(); - $analysis = $this->analysisFromFixtures(['BadExampleParameter.php']); (new Generator())->getProcessorPipeline()->process($analysis); diff --git a/tests/ConstantsTest.php b/tests/ConstantsTest.php index a6acad28d..67a0486bd 100644 --- a/tests/ConstantsTest.php +++ b/tests/ConstantsTest.php @@ -6,9 +6,6 @@ namespace OpenApi\Tests; -use OpenApi\Analysers\TokenAnalyser; -use OpenApi\Generator; - class ConstantsTest extends OpenApiTestCase { public const URL = 'http://example.com'; @@ -51,12 +48,4 @@ public function testAutoloadConstant(): void $annotations = $this->annotationsFromDocBlockParser('@OA\Contact(name=AnotherNamespace\Annotations\Constants::INVALID_TIMEZONE_LOCATION)'); $this->assertSame('invalidTimezoneLocation', $annotations[0]->name); } - - public function testDynamicImports(): void - { - $analyser = new TokenAnalyser(); - $analyser->setGenerator((new Generator())->setNamespaces(null)); - $analyser->fromFile($this->fixture('Customer.php'), $this->getContext()); - $analyser->fromFile($this->fixture('ThirdPartyAnnotations.php'), $this->getContext()); - } } diff --git a/tests/ContextTest.php b/tests/ContextTest.php index 9e2648ad7..adfcac6b3 100644 --- a/tests/ContextTest.php +++ b/tests/ContextTest.php @@ -7,7 +7,6 @@ namespace OpenApi\Tests; use OpenApi\Annotations as OA; -use OpenApi\Analysers\TokenAnalyser; use OpenApi\Context; use OpenApi\Generator; use OpenApi\Tests\Fixtures\Customer; @@ -19,7 +18,7 @@ public function testFullyQualifiedName(): void { $this->assertOpenApiLogEntryContains('Required @OA\PathItem() not found'); $openapi = (new Generator($this->getTrackingLogger())) - ->setAnalyser(new TokenAnalyser()) + ->setAnalyser($this->getAnalyzer()) ->generate([$this->fixture('Customer.php')]); $context = $openapi->components->schemas[0]->_context; // resolve with namespace diff --git a/tests/ExamplesTest.php b/tests/ExamplesTest.php index a7fea5edf..5899a195e 100644 --- a/tests/ExamplesTest.php +++ b/tests/ExamplesTest.php @@ -11,7 +11,6 @@ use OpenApi\Analysers\AttributeAnnotationFactory; use OpenApi\Analysers\DocBlockAnnotationFactory; use OpenApi\Analysers\ReflectionAnalyser; -use OpenApi\Analysers\TokenAnalyser; use OpenApi\Annotations as OA; use OpenApi\Generator; use OpenApi\Serializer; @@ -26,7 +25,7 @@ public static function exampleDetails(): iterable 'example-object.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'misc' => [ @@ -35,7 +34,7 @@ public static function exampleDetails(): iterable 'misc.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'nesting' => [ @@ -44,7 +43,7 @@ public static function exampleDetails(): iterable 'nesting.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'petstore-3.0' => [ @@ -53,7 +52,7 @@ public static function exampleDetails(): iterable 'petstore-3.0.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'petstore.swagger.io' => [ @@ -62,7 +61,7 @@ public static function exampleDetails(): iterable 'petstore.swagger.io.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'swagger-spec/petstore' => [ @@ -71,7 +70,7 @@ public static function exampleDetails(): iterable 'petstore.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'swagger-spec/petstore-simple' => [ @@ -80,7 +79,7 @@ public static function exampleDetails(): iterable 'petstore-simple.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'swagger-spec/petstore-simple-3.1.0' => [ @@ -89,7 +88,7 @@ public static function exampleDetails(): iterable 'petstore-simple-3.1.0.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'swagger-spec/petstore-with-external-docs' => [ @@ -98,7 +97,7 @@ public static function exampleDetails(): iterable 'petstore-with-external-docs.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'polymorphism' => [ @@ -125,7 +124,7 @@ public static function exampleDetails(): iterable 'using-interfaces.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'using-traits' => [ @@ -134,7 +133,7 @@ public static function exampleDetails(): iterable 'using-traits.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; yield 'using-links' => [ @@ -143,7 +142,7 @@ public static function exampleDetails(): iterable 'using-links.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token', 'reflection'], + 'analysers' => ['reflection'], ]; if (\PHP_VERSION_ID >= 80100) { @@ -162,7 +161,7 @@ public static function exampleDetails(): iterable 'webhooks.yaml', 'debug' => false, 'expectedLog' => [], - 'analysers' => ['token','reflection'], + 'analysers' => ['reflection'], ]; yield 'webhooks81' => [ @@ -188,7 +187,6 @@ public static function exampleDetails(): iterable public static function exampleMappings(): iterable { $analysers = [ - 'token' => new TokenAnalyser(), 'reflection' => new ReflectionAnalyser([new DocBlockAnnotationFactory(), new AttributeAnnotationFactory()]), ]; diff --git a/tests/OpenApiTestCase.php b/tests/OpenApiTestCase.php index 985f5ad5d..30f08b1cd 100644 --- a/tests/OpenApiTestCase.php +++ b/tests/OpenApiTestCase.php @@ -14,7 +14,6 @@ use OpenApi\Analysis; use OpenApi\Annotations as OA; use OpenApi\Context; -use OpenApi\Analysers\TokenAnalyser; use OpenApi\Generator; use OpenApi\Pipeline; use PHPUnit\Framework\TestCase; @@ -85,13 +84,6 @@ public function log($level, $message, array $context = []): void }; } - public function skipLegacy(): void - { - if ($this->getAnalyzer() instanceof TokenAnalyser) { - $this->markTestSkipped(); - } - } - public function getContext(array $properties = [], ?string $version = OA\OpenApi::DEFAULT_VERSION): Context { return new Context( @@ -104,11 +96,7 @@ public function getContext(array $properties = [], ?string $version = OA\OpenApi public function getAnalyzer(): AnalyserInterface { - $legacyAnalyser = getenv('PHPUNIT_ANALYSER') === 'legacy'; - - return $legacyAnalyser - ? new TokenAnalyser() - : new ReflectionAnalyser([new DocBlockAnnotationFactory(), new AttributeAnnotationFactory()]); + return new ReflectionAnalyser([new DocBlockAnnotationFactory(), new AttributeAnnotationFactory()]); } public function assertOpenApiLogEntryContains(string $needle, string $message = ''): void diff --git a/tests/Processors/AugmentTagsTest.php b/tests/Processors/AugmentTagsTest.php index 2d049efe7..43c90bfb3 100644 --- a/tests/Processors/AugmentTagsTest.php +++ b/tests/Processors/AugmentTagsTest.php @@ -15,8 +15,6 @@ class AugmentTagsTest extends OpenApiTestCase */ public function testFilteredAugmentTags(): void { - $this->skipLegacy(); - $config = [ 'pathFilter' => ['paths' => ['#^/hello/#']], 'cleanUnusedComponents' => ['enabled' => true], @@ -31,8 +29,6 @@ public function testFilteredAugmentTags(): void */ public function testDedupedAugmentTags(): void { - $this->skipLegacy(); - $analysis = $this->analysisFromFixtures(['SurplusTag.php'], static::processors()); $this->assertCount(3, $analysis->openapi->tags, 'Expecting 3 unique tags'); @@ -43,8 +39,6 @@ public function testDedupedAugmentTags(): void */ public function testAllowUnusedTags(): void { - $this->skipLegacy(); - $analysis = $this->analysisFromFixtures( ['UnusedTags.php'], static::processors(), @@ -62,8 +56,6 @@ public function testAllowUnusedTags(): void */ public function testAllowUnusedTagsWildcard(): void { - $this->skipLegacy(); - $analysis = $this->analysisFromFixtures( ['UnusedTags.php'], static::processors(), diff --git a/tests/Processors/ExpandEnumsTest.php b/tests/Processors/ExpandEnumsTest.php index da946caca..0af7ccae3 100644 --- a/tests/Processors/ExpandEnumsTest.php +++ b/tests/Processors/ExpandEnumsTest.php @@ -21,13 +21,6 @@ */ class ExpandEnumsTest extends OpenApiTestCase { - public function setUp(): void - { - parent::setUp(); - - $this->skipLegacy(); - } - public function testExpandUnitEnum(): void { $analysis = $this->analysisFromFixtures(['PHP/Enums/StatusEnum.php']); diff --git a/tests/UtilTest.php b/tests/UtilTest.php index bf4c95297..f739db055 100644 --- a/tests/UtilTest.php +++ b/tests/UtilTest.php @@ -6,40 +6,12 @@ namespace OpenApi\Tests; -use OpenApi\Analysers\TokenAnalyser; use OpenApi\Annotations as OA; -use OpenApi\Generator; use OpenApi\Util; use Symfony\Component\Finder\Finder; class UtilTest extends OpenApiTestCase { - public function testExclude(): void - { - $exclude = [ - 'Customer.php', - 'CustomerInterface.php', - 'GrandAncestor.php', - 'InheritProperties', - 'Apis', - 'PHP', - 'Parser', - 'Analysers', - 'Processors', - 'Scratch', - 'TypedProperties.php', - 'Unreferenced.php', - 'UsingRefs.php', - 'UsingPhpDoc.php', - 'UsingCustomAttachables', - 'DuplicateOperationId', - ]; - $openapi = (new Generator()) - ->setAnalyser(new TokenAnalyser()) - ->generate(Util::finder($this->fixture(''), $exclude)); - $this->assertSame('Fixture for ParserTest', $openapi->info->title, 'No errors about duplicate @OA\Info() annotations'); - } - public function testRefEncode(): void { $this->assertSame('#/paths/~1blogs~1{blog_id}~1new~0posts', '#/paths/' . Util::refEncode('/blogs/{blog_id}/new~posts'));