From 6c5390ce608e641565657febce234e2c65dbb74b Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Fri, 19 Apr 2024 16:08:41 +1200 Subject: [PATCH 01/10] Initial pipeline --- src/Pipeline.php | 97 ++++++++++++++++++++++++++++++++++++++++++ tests/PipelineTest.php | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/Pipeline.php create mode 100644 tests/PipelineTest.php diff --git a/src/Pipeline.php b/src/Pipeline.php new file mode 100644 index 00000000..fe1da50a --- /dev/null +++ b/src/Pipeline.php @@ -0,0 +1,97 @@ + + */ + protected $pipes = []; + + public function __construct(array $pipes = []) + { + $this->pipes = $pipes; + } + + /** + * @deprecated This will be removed in 5.0 + */ + public function pipes(): array + { + return $this->pipes; + } + + public function add(callable $pipe): Pipeline + { + $this->pipes[] = $pipe; + + return $this; + } + + public function remove(?callable $pipe, ?callable $matcher = null): Pipeline + { + if (!$pipe && !$matcher) { + throw new \InvalidArgumentException('pipe or callable must not be empty'); + } + + if ($matcher) { + $tmp = []; + foreach ($this->pipes as $pipe) { + if ($matcher($pipe)) { + $tmp[] = $pipe; + } + } + + $this->pipes = $tmp; + } else { + if (false === ($key = array_search($pipe, $this->pipes, true))) { + return $this; + } + + unset($this->pipes[$key]); + + $this->pipes = array_values($this->pipes); + } + + return $this; + } + + /** + * @param callable $matcher Callable to determine the position to insert (returned as `int`) + */ + public function insert(callable $pipe, callable $matcher): Pipeline + { + $index = $matcher($this->pipes); + if ($index < 0 || $index > count($this->pipes)) { + throw new \InvalidArgumentException('Matcher result out of range'); + } + + array_splice($this->pipes, $index, 0, [$pipe]); + + return $this; + } + + public function walk(callable $walker): Pipeline + { + foreach ($this->pipes as $pipe) { + $walker($pipe); + } + + return $this; + } + + public function process($payload) + { + foreach ($this->pipes as $pipe) { + /** @deprecated null payload returned from pipe */ + $payload = $pipe($payload) ?: $payload; + } + + return $payload; + } +} diff --git a/tests/PipelineTest.php b/tests/PipelineTest.php new file mode 100644 index 00000000..8f8581b1 --- /dev/null +++ b/tests/PipelineTest.php @@ -0,0 +1,74 @@ +add = $add; + } + + // ------------------------------------------------------------------------ + + public function __invoke($payload) + { + return $payload . $this->add; + } + }; + } + + public function testProcess() + { + $pipeline = new Pipeline([$this->pipe('x')]); + $result = $pipeline->process(''); + + $this->assertEquals('x', $result); + } + + public function testAdd() + { + $pipeline = new Pipeline(); + + $pipeline->add($this->pipe('a')); + $this->assertEquals('a', $pipeline->process('')); + + $pipeline->add($this->pipe('b')); + $this->assertEquals('ab', $pipeline->process('')); + } + + public function testRemoved() + { + $pipeline = new Pipeline(); + + $pipeline->add($pipec = $this->pipe('c')); + $pipeline->add($piped = $this->pipe('d')); + $this->assertEquals('cd', $pipeline->process('')); + + $pipeline->remove($pipec); + $this->assertEquals('d', $pipeline->process('')); + } + + public function testInsert() + { + $pipeline = new Pipeline(); + + $pipeline->add($this->pipe('x')); + $pipeline->add($this->pipe('z')); + $this->assertEquals('xz', $pipeline->process('')); + + $pipeline->insert($this->pipe('y'), function ($pipes) { return 1; }); + $this->assertEquals('xyz', $pipeline->process('')); + } +} From 8ed34a8ee2798e942429b2ab7cba6a3fe7face74 Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Fri, 19 Apr 2024 16:22:58 +1200 Subject: [PATCH 02/10] Wrap processors internally in pipeline --- src/Generator.php | 58 ++++++++++++++++++++--------------------- src/Pipeline.php | 2 +- tests/GeneratorTest.php | 8 ------ 3 files changed, 29 insertions(+), 39 deletions(-) diff --git a/src/Generator.php b/src/Generator.php index fb6ae362..82a217e8 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -48,12 +48,12 @@ class Generator protected $namespaces; /** @var AnalyserInterface|null The configured analyzer. */ - protected $analyser; + protected $analyser = null; /** @var array */ protected $config = []; - /** @var array|null List of configured processors. */ + /** @var Pipeline|null List of configured processors. */ protected $processors = null; /** @var LoggerInterface|null PSR logger. */ @@ -249,7 +249,7 @@ public function setConfig(array $config): Generator public function getProcessors(): array { if (null === $this->processors) { - $this->processors = [ + $this->processors = new Pipeline([ new Processors\DocBlockDescriptions(), new Processors\MergeIntoOpenApi(), new Processors\MergeIntoComponents(), @@ -267,26 +267,26 @@ public function getProcessors(): array new Processors\MergeXmlContent(), new Processors\OperationId(), new Processors\CleanUnmerged(), - ]; + ]); } $config = $this->getConfig(); - foreach ($this->processors as $processor) { - $rc = new \ReflectionClass($processor); + $walker = function (callable $pipe) use ($config) { + $rc = new \ReflectionClass($pipe); // apply config $processorKey = lcfirst($rc->getShortName()); if (array_key_exists($processorKey, $config)) { foreach ($config[$processorKey] as $name => $value) { $setter = 'set' . ucfirst($name); - if (method_exists($processor, $setter)) { - $processor->{$setter}($value); + if (method_exists($pipe, $setter)) { + $pipe->{$setter}($value); } } } - } + }; - return $this->processors; + return $this->processors->walk($walker)->pipes(); } /** @@ -294,7 +294,7 @@ public function getProcessors(): array */ public function setProcessors(?array $processors): Generator { - $this->processors = $processors; + $this->processors = null !== $processors ? new Pipeline($processors) : null; return $this; } @@ -305,21 +305,23 @@ public function setProcessors(?array $processors): Generator */ public function addProcessor($processor, ?string $before = null): Generator { - $processors = $this->getProcessors(); + $processors = $this->processors ?: new Pipeline($this->getProcessors()); if (!$before) { - $processors[] = $processor; + $processors->add($processor); } else { - $tmp = []; - foreach ($processors as $current) { - if ($current instanceof $before) { - $tmp[] = $processor; + $matcher = function (array $pipes) use ($before) { + foreach ($pipes as $ii => $current) { + if ($current instanceof $before) { + return $ii; + } } - $tmp[] = $current; - } - $processors = $tmp; + + return null; + }; + $processors->insert($processor, $matcher); } - $this->setProcessors($processors); + $this->processors = $processors; return $this; } @@ -329,15 +331,9 @@ public function addProcessor($processor, ?string $before = null): Generator */ public function removeProcessor($processor, bool $silent = false): Generator { - $processors = $this->getProcessors(); - if (false === ($key = array_search($processor, $processors, true))) { - if ($silent) { - return $this; - } - throw new \InvalidArgumentException('Processor not found'); - } - unset($processors[$key]); - $this->setProcessors($processors); + $processors = $this->processors ?: new Pipeline($this->getProcessors()); + $processors->remove($processor); + $this->processors = $processors; return $this; } @@ -348,6 +344,8 @@ public function removeProcessor($processor, bool $silent = false): Generator * @param ProcessorInterface|callable $processor the new processor * @param null|callable $matcher Optional matcher callable to identify the processor to replace. * If none given, matching is based on the processors class. + * + * @deprecated */ public function updateProcessor($processor, ?callable $matcher = null): Generator { diff --git a/src/Pipeline.php b/src/Pipeline.php index fe1da50a..34956a72 100644 --- a/src/Pipeline.php +++ b/src/Pipeline.php @@ -67,7 +67,7 @@ public function remove(?callable $pipe, ?callable $matcher = null): Pipeline public function insert(callable $pipe, callable $matcher): Pipeline { $index = $matcher($this->pipes); - if ($index < 0 || $index > count($this->pipes)) { + if (null === $index || $index < 0 || $index > count($this->pipes)) { throw new \InvalidArgumentException('Matcher result out of range'); } diff --git a/tests/GeneratorTest.php b/tests/GeneratorTest.php index 4683b549..74943fcb 100644 --- a/tests/GeneratorTest.php +++ b/tests/GeneratorTest.php @@ -107,14 +107,6 @@ public function testRemoveProcessor(): void $this->assertEquals($processors, $generator->getProcessors()); } - public function testRemoveProcessorNotFound(): void - { - $this->expectException(\InvalidArgumentException::class); - - (new Generator())->removeProcessor(function () { - }); - } - protected function assertOperationIdHash(Generator $generator, bool $expected): void { foreach ($generator->getProcessors() as $processor) { From 6c1406a2e5f815eed0272c228b11db213eb3211a Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Fri, 19 Apr 2024 16:26:34 +1200 Subject: [PATCH 03/10] Expose processor pipeline --- src/Generator.php | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Generator.php b/src/Generator.php index 82a217e8..d3ac81c4 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -54,7 +54,7 @@ class Generator protected $config = []; /** @var Pipeline|null List of configured processors. */ - protected $processors = null; + protected $processor = null; /** @var LoggerInterface|null PSR logger. */ protected $logger = null; @@ -248,8 +248,13 @@ public function setConfig(array $config): Generator */ public function getProcessors(): array { - if (null === $this->processors) { - $this->processors = new Pipeline([ + return $this->getProcessor()->pipes(); + } + + public function getProcessor(): Pipeline + { + if (null === $this->processor) { + $this->processor = new Pipeline([ new Processors\DocBlockDescriptions(), new Processors\MergeIntoOpenApi(), new Processors\MergeIntoComponents(), @@ -286,7 +291,7 @@ public function getProcessors(): array } }; - return $this->processors->walk($walker)->pipes(); + return $this->processor->walk($walker); } /** @@ -294,7 +299,7 @@ public function getProcessors(): array */ public function setProcessors(?array $processors): Generator { - $this->processors = null !== $processors ? new Pipeline($processors) : null; + $this->processor = null !== $processors ? new Pipeline($processors) : null; return $this; } @@ -305,7 +310,7 @@ public function setProcessors(?array $processors): Generator */ public function addProcessor($processor, ?string $before = null): Generator { - $processors = $this->processors ?: new Pipeline($this->getProcessors()); + $processors = $this->processor ?: new Pipeline($this->getProcessors()); if (!$before) { $processors->add($processor); } else { @@ -321,7 +326,7 @@ public function addProcessor($processor, ?string $before = null): Generator $processors->insert($processor, $matcher); } - $this->processors = $processors; + $this->processor = $processors; return $this; } @@ -331,9 +336,9 @@ public function addProcessor($processor, ?string $before = null): Generator */ public function removeProcessor($processor, bool $silent = false): Generator { - $processors = $this->processors ?: new Pipeline($this->getProcessors()); + $processors = $this->processor ?: new Pipeline($this->getProcessors()); $processors->remove($processor); - $this->processors = $processors; + $this->processor = $processors; return $this; } From 3866524ffe51eba2a9f4f90aba2eada1dd809e1b Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Fri, 19 Apr 2024 17:08:00 +1200 Subject: [PATCH 04/10] WIP update references to deprecated methods --- .../scan.php | 24 ++++----- .../schema-query-parameter/scan.php | 24 ++++----- bin/openapi | 2 +- docs/reference/generator.md | 6 +-- src/Analysis.php | 2 + src/Generator.php | 53 ++++++++++++++----- tests/Analysers/ReflectionAnalyserTest.php | 6 +-- tests/Analysers/TokenAnalyserTest.php | 4 +- tests/Annotations/AbstractAnnotationTest.php | 4 +- tests/Annotations/AttachableTest.php | 3 +- tests/Annotations/NestedPropertyTest.php | 1 + tests/OpenApiTestCase.php | 3 +- tests/Processors/AugmentPropertiesTest.php | 1 + tests/RefTest.php | 2 +- 14 files changed, 83 insertions(+), 52 deletions(-) diff --git a/Examples/processors/schema-query-parameter-attributes/scan.php b/Examples/processors/schema-query-parameter-attributes/scan.php index 081396be..83575d45 100644 --- a/Examples/processors/schema-query-parameter-attributes/scan.php +++ b/Examples/processors/schema-query-parameter-attributes/scan.php @@ -1,6 +1,7 @@ addPsr4('SchemaQueryParameterProcessor\\', __DIR__); -$openapiGenerator = new Generator(); -$processors = []; -foreach ($openapiGenerator->getProcessors() as $processor) { - $processors[] = $processor; - if ($processor instanceof BuildPaths) { - $processors[] = new SchemaQueryParameter(); +$insertMatch = function (array $pipes) { + foreach ($pipes as $ii => $pipe) { + if ($pipe instanceof BuildPaths) { + return $ii; + } } -} -$openapi = $openapiGenerator - ->setProcessors($processors) - ->generate([__DIR__ . '/app']); - -file_put_contents(__DIR__ . '/schema-query-parameter.yaml', $openapi->toYaml()); + return null; +}; +$openapi = (new Generator()) + ->withProcessor(function (Pipeline $pipeline) use ($insertMatch) { $pipeline->insert(new SchemaQueryParameter(), $insertMatch); }) + ->generate([__DIR__ . '/app']); +// file_put_contents(__DIR__ . '/schema-query-parameter.yaml', $openapi->toYaml()); echo $openapi->toYaml(); diff --git a/Examples/processors/schema-query-parameter/scan.php b/Examples/processors/schema-query-parameter/scan.php index 7311e6ce..83575d45 100644 --- a/Examples/processors/schema-query-parameter/scan.php +++ b/Examples/processors/schema-query-parameter/scan.php @@ -1,6 +1,7 @@ addPsr4('SchemaQueryParameterProcessor\\', __DIR__); -$generator = new Generator(); - -// merge our custom processor -$processors = []; -foreach ($generator->getProcessors() as $processor) { - $processors[] = $processor; - if ($processor instanceof BuildPaths) { - $processors[] = new SchemaQueryParameter(); +$insertMatch = function (array $pipes) { + foreach ($pipes as $ii => $pipe) { + if ($pipe instanceof BuildPaths) { + return $ii; + } } -} -$options = [ - 'processors' => $processors, -]; + return null; +}; -$openapi = $generator - ->setProcessors($processors) +$openapi = (new Generator()) + ->withProcessor(function (Pipeline $pipeline) use ($insertMatch) { $pipeline->insert(new SchemaQueryParameter(), $insertMatch); }) ->generate([__DIR__ . '/app']); // file_put_contents(__DIR__ . '/schema-query-parameter.yaml', $openapi->toYaml()); echo $openapi->toYaml(); diff --git a/bin/openapi b/bin/openapi index b37d4535..498f3de0 100755 --- a/bin/openapi +++ b/bin/openapi @@ -219,7 +219,7 @@ foreach ($options["processor"] as $processor) { } elseif (class_exists($processor)) { $processor = new $processor(); } - $generator->addProcessor($processor); + $generator->getProcessor()->add($processor); } $analyser = $options['legacy'] diff --git a/docs/reference/generator.md b/docs/reference/generator.md index f70e6f05..5ac86544 100644 --- a/docs/reference/generator.md +++ b/docs/reference/generator.md @@ -32,9 +32,9 @@ $openapi = \OpenApi\scan(__DIR__, ['exclude' => ['tests'], 'pattern' => '*.php'] ``` 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. +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 +Being static this means setting them back is the callers responsibility and there is also the fact that some Doctrine configuration currently can not be reverted easily. Therefore, having a single side effect free way of using swagger-php seemed like a good idea... @@ -57,7 +57,7 @@ $processors = [/* my processors */]; $finder = \Symfony\Component\Finder\Finder::create()->files()->name('*.php')->in(__DIR__); $openapi = (new \OpenApi\Generator($logger)) - ->setProcessors($processors) + ->setProcessor(new \OpenApi\Pipeline($processors)) ->setAliases(['MY' => 'My\Annotations']) ->setNamespaces(['My\\Annotations\\']) ->setAnalyser(new \OpenApi\Analysers\TokenAnalyser()) diff --git a/src/Analysis.php b/src/Analysis.php index 6ba3a081..8a73b5f2 100644 --- a/src/Analysis.php +++ b/src/Analysis.php @@ -422,6 +422,8 @@ public function split() * Apply the processor(s). * * @param callable|ProcessorInterface|array $processors One or more processors + * + * @deprecated */ public function process($processors = null): void { diff --git a/src/Generator.php b/src/Generator.php index d3ac81c4..8b4c46d1 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -243,14 +243,6 @@ public function setConfig(array $config): Generator return $this; } - /** - * @return array - */ - public function getProcessors(): array - { - return $this->getProcessor()->pipes(); - } - public function getProcessor(): Pipeline { if (null === $this->processor) { @@ -294,8 +286,39 @@ public function getProcessor(): Pipeline return $this->processor->walk($walker); } + public function setProcessor(Pipeline $processor): Generator + { + $this->processor = $processor; + + return $this; + } + + /** + * Chainable method that allows to modify the processor pipeline. + * + * @param callable $with callable with the current processor pipeline passed in + */ + public function withProcessor(callable $with): Generator + { + $with($this->getProcessor()); + + return $this; + } + + /** + * @return array + * + * @deprecated + */ + public function getProcessors(): array + { + return $this->getProcessor()->pipes(); + } + /** * @param array|null $processors + * + * @deprecated */ public function setProcessors(?array $processors): Generator { @@ -307,10 +330,12 @@ public function setProcessors(?array $processors): Generator /** * @param callable|ProcessorInterface $processor * @param class-string|null $before + * + * @deprecated */ public function addProcessor($processor, ?string $before = null): Generator { - $processors = $this->processor ?: new Pipeline($this->getProcessors()); + $processors = $this->processor ?: $this->getProcessor(); if (!$before) { $processors->add($processor); } else { @@ -333,10 +358,12 @@ public function addProcessor($processor, ?string $before = null): Generator /** * @param callable|ProcessorInterface $processor + * + * @deprecated */ public function removeProcessor($processor, bool $silent = false): Generator { - $processors = $this->processor ?: new Pipeline($this->getProcessors()); + $processors = $this->processor ?: $this->getProcessor(); $processors->remove($processor); $this->processor = $processors; @@ -393,6 +420,7 @@ public static function scan(iterable $sources, array $options = []): ?OA\OpenApi 'namespaces' => self::DEFAULT_NAMESPACES, 'analyser' => null, 'analysis' => null, + 'processor' => null, 'processors' => null, 'logger' => null, 'validate' => true, @@ -404,7 +432,8 @@ public static function scan(iterable $sources, array $options = []): ?OA\OpenApi ->setAliases($config['aliases']) ->setNamespaces($config['namespaces']) ->setAnalyser($config['analyser']) - ->setProcessors($config['processors']) + ->setProcessor($config['processor']) + ->setProcessor(new Pipeline($config['processors'])) ->generate($sources, $config['analysis'], $config['validate']); } @@ -458,7 +487,7 @@ public function generate(iterable $sources, ?Analysis $analysis = null, bool $va $this->scanSources($sources, $analysis, $rootContext); // post-processing - $analysis->process($this->getProcessors()); + $this->getProcessor()->process($analysis); if ($analysis->openapi) { // overwrite default/annotated version diff --git a/tests/Analysers/ReflectionAnalyserTest.php b/tests/Analysers/ReflectionAnalyserTest.php index bb5b3b2b..07d3309b 100644 --- a/tests/Analysers/ReflectionAnalyserTest.php +++ b/tests/Analysers/ReflectionAnalyserTest.php @@ -94,7 +94,7 @@ public function testApiDocBlockBasic(AnalyserInterface $analyser): void ->withContext(function (Generator $generator) use ($analyser) { $analyser->setGenerator($generator); $analysis = $analyser->fromFile($this->fixture('Apis/DocBlocks/basic.php'), $this->getContext([], $generator->getVersion())); - $analysis->process($generator->getProcessors()); + $generator->getProcessor()->process($analysis); return $analysis; }); @@ -125,7 +125,7 @@ public function testApiAttributesBasic(AnalyserInterface $analyser): void ->withContext(function (Generator $generator) use ($analyser) { $analyser->setGenerator($generator); $analysis = $analyser->fromFile($this->fixture('Apis/Attributes/basic.php'), $this->getContext([], $generator->getVersion())); - $analysis->process(($generator)->getProcessors()); + $generator->getProcessor()->process($analysis); return $analysis; }); @@ -166,7 +166,7 @@ public function testApiMixedBasic(AnalyserInterface $analyser): void ->withContext(function (Generator $generator) use ($analyser) { $analyser->setGenerator($generator); $analysis = $analyser->fromFile($this->fixture('Apis/Mixed/basic.php'), $this->getContext([], $generator->getVersion())); - $analysis->process(($generator)->getProcessors()); + $generator->getProcessor()->process($analysis); return $analysis; }); diff --git a/tests/Analysers/TokenAnalyserTest.php b/tests/Analysers/TokenAnalyserTest.php index 00cf3951..b333bec2 100644 --- a/tests/Analysers/TokenAnalyserTest.php +++ b/tests/Analysers/TokenAnalyserTest.php @@ -280,7 +280,7 @@ public function testPhp8PromotedProperties(): void public function testAnonymousFunctions(): void { $analysis = $this->analysisFromFixtures(['PHP/AnonymousFunctions.php'], [], new TokenAnalyser()); - $analysis->process((new Generator())->getProcessors()); + (new Generator())->getProcessor()->process($analysis); $infos = $analysis->getAnnotationsOfType(OA\Info::class, true); $this->assertCount(1, $infos); @@ -295,6 +295,6 @@ public function testPhp8NamedArguments(): void $schemas = $analysis->getAnnotationsOfType(OA\Schema::class, true); $this->assertCount(1, $schemas); - $analysis->process((new Generator())->getProcessors()); + (new Generator())->getProcessor()->process($analysis); } } diff --git a/tests/Annotations/AbstractAnnotationTest.php b/tests/Annotations/AbstractAnnotationTest.php index 7a052713..12d5b9f6 100644 --- a/tests/Annotations/AbstractAnnotationTest.php +++ b/tests/Annotations/AbstractAnnotationTest.php @@ -130,7 +130,7 @@ public function testMatchNested(string $class, $expected): void public function testDuplicateOperationIdValidation(): void { $analysis = $this->analysisFromFixtures(['DuplicateOperationId.php']); - $analysis->process((new Generator())->getProcessors()); + (new Generator())->getProcessor()->process($analysis); $this->assertOpenApiLogEntryContains('operationId must be unique. Duplicate value found: "getItem"'); $this->assertFalse($analysis->validate()); @@ -151,7 +151,7 @@ public function testValidateExamples(): void $this->skipLegacy(); $analysis = $this->analysisFromFixtures(['BadExampleParameter.php']); - $analysis->process((new Generator())->getProcessors()); + (new Generator())->getProcessor()->process($analysis); $this->assertOpenApiLogEntryContains('Required @OA\PathItem() not found'); $this->assertOpenApiLogEntryContains('Required @OA\Info() not found'); diff --git a/tests/Annotations/AttachableTest.php b/tests/Annotations/AttachableTest.php index 70e6f30c..b3b6d549 100644 --- a/tests/Annotations/AttachableTest.php +++ b/tests/Annotations/AttachableTest.php @@ -9,6 +9,7 @@ use OpenApi\Analysis; use OpenApi\Annotations as OA; use OpenApi\Generator; +use OpenApi\Pipeline; use OpenApi\Processors\CleanUnusedComponents; use OpenApi\Tests\Fixtures\Annotations\CustomAttachable; use OpenApi\Tests\OpenApiTestCase; @@ -31,7 +32,7 @@ public function testCustomAttachableImplementationsAreAttached(): void (new Generator()) ->addAlias('oaf', 'OpenApi\Tests\Fixtures\Annotations') ->addNamespace('OpenApi\Tests\Fixtures\Annotations\\') - ->setProcessors($this->processors([CleanUnusedComponents::class])) + ->withProcessor(function (Pipeline $processor) { $processor->remove(null, function ($pipe) { return !$pipe instanceof CleanUnusedComponents; }); }) ->generate($this->fixtures(['UsingCustomAttachables.php']), $analysis); $schemas = $analysis->getAnnotationsOfType(OA\Schema::class, true); diff --git a/tests/Annotations/NestedPropertyTest.php b/tests/Annotations/NestedPropertyTest.php index 8bc99f59..ae452787 100644 --- a/tests/Annotations/NestedPropertyTest.php +++ b/tests/Annotations/NestedPropertyTest.php @@ -7,6 +7,7 @@ namespace OpenApi\Tests\Annotations; use OpenApi\Generator; +use OpenApi\Pipeline; use OpenApi\Processors\AugmentProperties; use OpenApi\Processors\AugmentSchemas; use OpenApi\Processors\MergeIntoComponents; diff --git a/tests/OpenApiTestCase.php b/tests/OpenApiTestCase.php index cfcc0d50..6fecc379 100644 --- a/tests/OpenApiTestCase.php +++ b/tests/OpenApiTestCase.php @@ -16,6 +16,7 @@ use OpenApi\Context; use OpenApi\Analysers\TokenAnalyser; use OpenApi\Generator; +use OpenApi\Pipeline; use PHPUnit\Framework\TestCase; use Psr\Log\AbstractLogger; use Psr\Log\LoggerInterface; @@ -242,7 +243,7 @@ public function analysisFromFixtures(array $files, array $processors = [], ?Anal (new Generator($this->getTrackingLogger())) ->setAnalyser($analyzer ?: $this->getAnalyzer()) - ->setProcessors($processors) + ->setProcessor(new Pipeline($processors)) ->generate($this->fixtures($files), $analysis, false); return $analysis; diff --git a/tests/Processors/AugmentPropertiesTest.php b/tests/Processors/AugmentPropertiesTest.php index 112589c2..afd0d477 100644 --- a/tests/Processors/AugmentPropertiesTest.php +++ b/tests/Processors/AugmentPropertiesTest.php @@ -9,6 +9,7 @@ use OpenApi\Analysers\ReflectionAnalyser; use OpenApi\Annotations as OA; use OpenApi\Generator; +use OpenApi\Pipeline; use OpenApi\Processors\AugmentProperties; use OpenApi\Processors\AugmentSchemas; use OpenApi\Processors\MergeIntoComponents; diff --git a/tests/RefTest.php b/tests/RefTest.php index d860cfdb..a14bd1c8 100644 --- a/tests/RefTest.php +++ b/tests/RefTest.php @@ -27,7 +27,7 @@ public function testRef(): void $openapi->merge($this->annotationsFromDocBlockParser($comment)); $analysis = new Analysis([], $this->getContext()); $analysis->addAnnotation($openapi, $this->getContext()); - $analysis->process((new Generator())->getProcessors()); + (new Generator())->getProcessor()->process($analysis); $analysis->validate(); // escape / as ~1 From bf2bcd0af3b9d30feecab1d5ade0493a52b57000 Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Fri, 19 Apr 2024 17:17:19 +1200 Subject: [PATCH 05/10] Make Pipeline::remove more flexible --- src/Pipeline.php | 13 ++++++++++++- tests/PipelineTest.php | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/Pipeline.php b/src/Pipeline.php index 34956a72..2ab27123 100644 --- a/src/Pipeline.php +++ b/src/Pipeline.php @@ -33,12 +33,23 @@ public function add(callable $pipe): Pipeline return $this; } - public function remove(?callable $pipe, ?callable $matcher = null): Pipeline + /** + * @param callable|class-string|null $pipe + */ + public function remove($pipe = null, ?callable $matcher = null): Pipeline { if (!$pipe && !$matcher) { throw new \InvalidArgumentException('pipe or callable must not be empty'); } + // allow matching on class name in $pipe in a string + if (is_string($pipe) && !$matcher) { + $pipeClass = $pipe; + $matcher = function ($pipe) use ($pipeClass) { + return !$pipe instanceof $pipeClass; + }; + } + if ($matcher) { $tmp = []; foreach ($this->pipes as $pipe) { diff --git a/tests/PipelineTest.php b/tests/PipelineTest.php index 8f8581b1..0e14cbc1 100644 --- a/tests/PipelineTest.php +++ b/tests/PipelineTest.php @@ -10,6 +10,11 @@ class PipelineTest extends OpenApiTestCase { + public function __invoke($payload) + { + return $payload . 'x'; + } + protected function pipe(string $add) { return new class($add) { @@ -48,18 +53,42 @@ public function testAdd() $this->assertEquals('ab', $pipeline->process('')); } - public function testRemoved() + public function testRemoveStrict() { $pipeline = new Pipeline(); $pipeline->add($pipec = $this->pipe('c')); - $pipeline->add($piped = $this->pipe('d')); + $pipeline->add($this->pipe('d')); $this->assertEquals('cd', $pipeline->process('')); $pipeline->remove($pipec); $this->assertEquals('d', $pipeline->process('')); } + public function testRemoveMatcher() + { + $pipeline = new Pipeline(); + + $pipeline->add($pipec = $this->pipe('c')); + $pipeline->add($this->pipe('d')); + $this->assertEquals('cd', $pipeline->process('')); + + $pipeline->remove(null, function ($pipe) use ($pipec) { return $pipe !== $pipec; }); + $this->assertEquals('d', $pipeline->process('')); + } + + public function testRemoveClassString() + { + $pipeline = new Pipeline(); + + $pipeline->add($this->pipe('c')); + $pipeline->add($this); + $this->assertEquals('cx', $pipeline->process('')); + + $pipeline->remove(__CLASS__); + $this->assertEquals('c', $pipeline->process('')); + } + public function testInsert() { $pipeline = new Pipeline(); From 2b5e49eb0e1cbdc9bd476d22cc18f5459751c208 Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Fri, 19 Apr 2024 17:21:08 +1200 Subject: [PATCH 06/10] CS --- tests/Annotations/NestedPropertyTest.php | 1 - tests/Processors/AugmentPropertiesTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/Annotations/NestedPropertyTest.php b/tests/Annotations/NestedPropertyTest.php index ae452787..8bc99f59 100644 --- a/tests/Annotations/NestedPropertyTest.php +++ b/tests/Annotations/NestedPropertyTest.php @@ -7,7 +7,6 @@ namespace OpenApi\Tests\Annotations; use OpenApi\Generator; -use OpenApi\Pipeline; use OpenApi\Processors\AugmentProperties; use OpenApi\Processors\AugmentSchemas; use OpenApi\Processors\MergeIntoComponents; diff --git a/tests/Processors/AugmentPropertiesTest.php b/tests/Processors/AugmentPropertiesTest.php index afd0d477..112589c2 100644 --- a/tests/Processors/AugmentPropertiesTest.php +++ b/tests/Processors/AugmentPropertiesTest.php @@ -9,7 +9,6 @@ use OpenApi\Analysers\ReflectionAnalyser; use OpenApi\Annotations as OA; use OpenApi\Generator; -use OpenApi\Pipeline; use OpenApi\Processors\AugmentProperties; use OpenApi\Processors\AugmentSchemas; use OpenApi\Processors\MergeIntoComponents; From 665a911d29c177f973da4d2021516a04aa24872f Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Sun, 21 Apr 2024 13:16:40 +1200 Subject: [PATCH 07/10] . --- src/Analysis.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Analysis.php b/src/Analysis.php index 8a73b5f2..6ba3a081 100644 --- a/src/Analysis.php +++ b/src/Analysis.php @@ -422,8 +422,6 @@ public function split() * Apply the processor(s). * * @param callable|ProcessorInterface|array $processors One or more processors - * - * @deprecated */ public function process($processors = null): void { From 5e7738dc7f8b7e25c119ed651ddf073a97b588a8 Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Wed, 1 May 2024 14:36:51 +1200 Subject: [PATCH 08/10] Remove use of deprecated processor methods --- tests/GeneratorTest.php | 4 ++-- tests/OpenApiTestCase.php | 14 +++++++++----- tools/src/Docs/ProcGenerator.php | 26 ++++++++++++++------------ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/tests/GeneratorTest.php b/tests/GeneratorTest.php index 74943fcb..6f412bbf 100644 --- a/tests/GeneratorTest.php +++ b/tests/GeneratorTest.php @@ -109,11 +109,11 @@ public function testRemoveProcessor(): void protected function assertOperationIdHash(Generator $generator, bool $expected): void { - foreach ($generator->getProcessors() as $processor) { + $generator->getProcessor()->walk(function ($processor) use ($expected) { if ($processor instanceof OperationId) { $this->assertEquals($expected, $processor->isHash()); } - } + }); } public static function configCases(): iterable diff --git a/tests/OpenApiTestCase.php b/tests/OpenApiTestCase.php index 6fecc379..1950e8e2 100644 --- a/tests/OpenApiTestCase.php +++ b/tests/OpenApiTestCase.php @@ -226,13 +226,17 @@ public static function fixtures(array $files): array }, $files); } - public static function processors(array $strip = [], array $add = []): array + public static function processors(array $strip = []): array { - $processors = (new Generator())->getProcessors(); + $processors = []; - $processors = array_filter($processors, function ($processor) use ($strip) { - return !is_object($processor) || !in_array(get_class($processor), $strip); - }); + (new Generator()) + ->getProcessor() + ->walk(function ($processor) use (&$processors, $strip) { + if (!is_object($processor) || !in_array(get_class($processor), $strip)) { + $processors[] = $processor; + } + }); return $processors; } diff --git a/tools/src/Docs/ProcGenerator.php b/tools/src/Docs/ProcGenerator.php index 702ccac9..8e71cb15 100644 --- a/tools/src/Docs/ProcGenerator.php +++ b/tools/src/Docs/ProcGenerator.php @@ -51,19 +51,21 @@ public function getProcessorsDetails(): array $processors = []; $defaultProcessors = []; - foreach ((new Generator())->getProcessors() as $processor) { - $rc = new \ReflectionClass($processor); - $class = $rc->getName(); + (new Generator()) + ->getProcessor() + ->walk(function ($processor) use (&$processors, &$defaultProcessors) { + $rc = new \ReflectionClass($processor); + $class = $rc->getName(); - $defaultProcessors[] = $class; - $processors[] = [ - 'class' => $class, - 'name' => $rc->getShortName(), - 'default' => true, - 'options' => $this->getOptionsDetails($rc), - 'phpdoc' => $this->extractDocumentation($rc->getDocComment()), - ]; - } + $defaultProcessors[] = $class; + $processors[] = [ + 'class' => $class, + 'name' => $rc->getShortName(), + 'default' => true, + 'options' => $this->getOptionsDetails($rc), + 'phpdoc' => $this->extractDocumentation($rc->getDocComment()), + ]; + }); $proccesorsDir = dirname((new \ReflectionClass(MergeIntoOpenApi::class))->getFileName()); foreach (glob("$proccesorsDir/*.php") as $processor) { From f8b62a1279d84b8306093468d47b1efb338e836c Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Wed, 1 May 2024 14:37:59 +1200 Subject: [PATCH 09/10] Deprecate `ProcessorInterface` --- src/Processors/ProcessorInterface.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Processors/ProcessorInterface.php b/src/Processors/ProcessorInterface.php index e8e96ee8..67e8e820 100644 --- a/src/Processors/ProcessorInterface.php +++ b/src/Processors/ProcessorInterface.php @@ -6,6 +6,9 @@ namespace OpenApi\Processors; +/** + * @deprecated + */ interface ProcessorInterface { } From 6b355b175db94227a79c90cc0a69cc1c7fffad27 Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Wed, 1 May 2024 15:09:59 +1200 Subject: [PATCH 10/10] Clarify processor pipeline naming. --- bin/openapi | 2 +- docs/reference/generator.md | 2 +- src/Generator.php | 36 ++++++++++---------- src/Pipeline.php | 5 +++ tests/Analysers/ReflectionAnalyserTest.php | 6 ++-- tests/Analysers/TokenAnalyserTest.php | 4 +-- tests/Annotations/AbstractAnnotationTest.php | 4 +-- tests/GeneratorTest.php | 2 +- tests/OpenApiTestCase.php | 4 +-- tests/RefTest.php | 2 +- tools/src/Docs/ProcGenerator.php | 2 +- 11 files changed, 37 insertions(+), 32 deletions(-) diff --git a/bin/openapi b/bin/openapi index 498f3de0..a41fa0d7 100755 --- a/bin/openapi +++ b/bin/openapi @@ -219,7 +219,7 @@ foreach ($options["processor"] as $processor) { } elseif (class_exists($processor)) { $processor = new $processor(); } - $generator->getProcessor()->add($processor); + $generator->getProcessorPipeline()->add($processor); } $analyser = $options['legacy'] diff --git a/docs/reference/generator.md b/docs/reference/generator.md index 5ac86544..48c997df 100644 --- a/docs/reference/generator.md +++ b/docs/reference/generator.md @@ -57,7 +57,7 @@ $processors = [/* my processors */]; $finder = \Symfony\Component\Finder\Finder::create()->files()->name('*.php')->in(__DIR__); $openapi = (new \OpenApi\Generator($logger)) - ->setProcessor(new \OpenApi\Pipeline($processors)) + ->setProcessorPipeline(new \OpenApi\Pipeline($processors)) ->setAliases(['MY' => 'My\Annotations']) ->setNamespaces(['My\\Annotations\\']) ->setAnalyser(new \OpenApi\Analysers\TokenAnalyser()) diff --git a/src/Generator.php b/src/Generator.php index 8b4c46d1..ff9c1f75 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -53,8 +53,8 @@ class Generator /** @var array */ protected $config = []; - /** @var Pipeline|null List of configured processors. */ - protected $processor = null; + /** @var Pipeline|null */ + protected $processorPipeline = null; /** @var LoggerInterface|null PSR logger. */ protected $logger = null; @@ -243,10 +243,10 @@ public function setConfig(array $config): Generator return $this; } - public function getProcessor(): Pipeline + public function getProcessorPipeline(): Pipeline { - if (null === $this->processor) { - $this->processor = new Pipeline([ + if (null === $this->processorPipeline) { + $this->processorPipeline = new Pipeline([ new Processors\DocBlockDescriptions(), new Processors\MergeIntoOpenApi(), new Processors\MergeIntoComponents(), @@ -283,12 +283,12 @@ public function getProcessor(): Pipeline } }; - return $this->processor->walk($walker); + return $this->processorPipeline->walk($walker); } - public function setProcessor(Pipeline $processor): Generator + public function setProcessorPipeline(Pipeline $processor): Generator { - $this->processor = $processor; + $this->processorPipeline = $processor; return $this; } @@ -300,7 +300,7 @@ public function setProcessor(Pipeline $processor): Generator */ public function withProcessor(callable $with): Generator { - $with($this->getProcessor()); + $with($this->getProcessorPipeline()); return $this; } @@ -312,7 +312,7 @@ public function withProcessor(callable $with): Generator */ public function getProcessors(): array { - return $this->getProcessor()->pipes(); + return $this->getProcessorPipeline()->pipes(); } /** @@ -322,7 +322,7 @@ public function getProcessors(): array */ public function setProcessors(?array $processors): Generator { - $this->processor = null !== $processors ? new Pipeline($processors) : null; + $this->processorPipeline = null !== $processors ? new Pipeline($processors) : null; return $this; } @@ -335,7 +335,7 @@ public function setProcessors(?array $processors): Generator */ public function addProcessor($processor, ?string $before = null): Generator { - $processors = $this->processor ?: $this->getProcessor(); + $processors = $this->processorPipeline ?: $this->getProcessorPipeline(); if (!$before) { $processors->add($processor); } else { @@ -351,7 +351,7 @@ public function addProcessor($processor, ?string $before = null): Generator $processors->insert($processor, $matcher); } - $this->processor = $processors; + $this->processorPipeline = $processors; return $this; } @@ -363,9 +363,9 @@ public function addProcessor($processor, ?string $before = null): Generator */ public function removeProcessor($processor, bool $silent = false): Generator { - $processors = $this->processor ?: $this->getProcessor(); + $processors = $this->processorPipeline ?: $this->getProcessorPipeline(); $processors->remove($processor); - $this->processor = $processors; + $this->processorPipeline = $processors; return $this; } @@ -432,8 +432,8 @@ public static function scan(iterable $sources, array $options = []): ?OA\OpenApi ->setAliases($config['aliases']) ->setNamespaces($config['namespaces']) ->setAnalyser($config['analyser']) - ->setProcessor($config['processor']) - ->setProcessor(new Pipeline($config['processors'])) + ->setProcessorPipeline($config['processor']) + ->setProcessorPipeline(new Pipeline($config['processors'])) ->generate($sources, $config['analysis'], $config['validate']); } @@ -487,7 +487,7 @@ public function generate(iterable $sources, ?Analysis $analysis = null, bool $va $this->scanSources($sources, $analysis, $rootContext); // post-processing - $this->getProcessor()->process($analysis); + $this->getProcessorPipeline()->process($analysis); if ($analysis->openapi) { // overwrite default/annotated version diff --git a/src/Pipeline.php b/src/Pipeline.php index 2ab27123..d54ab271 100644 --- a/src/Pipeline.php +++ b/src/Pipeline.php @@ -96,6 +96,11 @@ public function walk(callable $walker): Pipeline return $this; } + /** + * @param mixed $payload + * + * @return mixed + */ public function process($payload) { foreach ($this->pipes as $pipe) { diff --git a/tests/Analysers/ReflectionAnalyserTest.php b/tests/Analysers/ReflectionAnalyserTest.php index 07d3309b..b8fd6d3d 100644 --- a/tests/Analysers/ReflectionAnalyserTest.php +++ b/tests/Analysers/ReflectionAnalyserTest.php @@ -94,7 +94,7 @@ public function testApiDocBlockBasic(AnalyserInterface $analyser): void ->withContext(function (Generator $generator) use ($analyser) { $analyser->setGenerator($generator); $analysis = $analyser->fromFile($this->fixture('Apis/DocBlocks/basic.php'), $this->getContext([], $generator->getVersion())); - $generator->getProcessor()->process($analysis); + $generator->getProcessorPipeline()->process($analysis); return $analysis; }); @@ -125,7 +125,7 @@ public function testApiAttributesBasic(AnalyserInterface $analyser): void ->withContext(function (Generator $generator) use ($analyser) { $analyser->setGenerator($generator); $analysis = $analyser->fromFile($this->fixture('Apis/Attributes/basic.php'), $this->getContext([], $generator->getVersion())); - $generator->getProcessor()->process($analysis); + $generator->getProcessorPipeline()->process($analysis); return $analysis; }); @@ -166,7 +166,7 @@ public function testApiMixedBasic(AnalyserInterface $analyser): void ->withContext(function (Generator $generator) use ($analyser) { $analyser->setGenerator($generator); $analysis = $analyser->fromFile($this->fixture('Apis/Mixed/basic.php'), $this->getContext([], $generator->getVersion())); - $generator->getProcessor()->process($analysis); + $generator->getProcessorPipeline()->process($analysis); return $analysis; }); diff --git a/tests/Analysers/TokenAnalyserTest.php b/tests/Analysers/TokenAnalyserTest.php index b333bec2..dac65837 100644 --- a/tests/Analysers/TokenAnalyserTest.php +++ b/tests/Analysers/TokenAnalyserTest.php @@ -280,7 +280,7 @@ public function testPhp8PromotedProperties(): void public function testAnonymousFunctions(): void { $analysis = $this->analysisFromFixtures(['PHP/AnonymousFunctions.php'], [], new TokenAnalyser()); - (new Generator())->getProcessor()->process($analysis); + (new Generator())->getProcessorPipeline()->process($analysis); $infos = $analysis->getAnnotationsOfType(OA\Info::class, true); $this->assertCount(1, $infos); @@ -295,6 +295,6 @@ public function testPhp8NamedArguments(): void $schemas = $analysis->getAnnotationsOfType(OA\Schema::class, true); $this->assertCount(1, $schemas); - (new Generator())->getProcessor()->process($analysis); + (new Generator())->getProcessorPipeline()->process($analysis); } } diff --git a/tests/Annotations/AbstractAnnotationTest.php b/tests/Annotations/AbstractAnnotationTest.php index 12d5b9f6..7dbc837f 100644 --- a/tests/Annotations/AbstractAnnotationTest.php +++ b/tests/Annotations/AbstractAnnotationTest.php @@ -130,7 +130,7 @@ public function testMatchNested(string $class, $expected): void public function testDuplicateOperationIdValidation(): void { $analysis = $this->analysisFromFixtures(['DuplicateOperationId.php']); - (new Generator())->getProcessor()->process($analysis); + (new Generator())->getProcessorPipeline()->process($analysis); $this->assertOpenApiLogEntryContains('operationId must be unique. Duplicate value found: "getItem"'); $this->assertFalse($analysis->validate()); @@ -151,7 +151,7 @@ public function testValidateExamples(): void $this->skipLegacy(); $analysis = $this->analysisFromFixtures(['BadExampleParameter.php']); - (new Generator())->getProcessor()->process($analysis); + (new Generator())->getProcessorPipeline()->process($analysis); $this->assertOpenApiLogEntryContains('Required @OA\PathItem() not found'); $this->assertOpenApiLogEntryContains('Required @OA\Info() not found'); diff --git a/tests/GeneratorTest.php b/tests/GeneratorTest.php index 6f412bbf..09d1b97f 100644 --- a/tests/GeneratorTest.php +++ b/tests/GeneratorTest.php @@ -109,7 +109,7 @@ public function testRemoveProcessor(): void protected function assertOperationIdHash(Generator $generator, bool $expected): void { - $generator->getProcessor()->walk(function ($processor) use ($expected) { + $generator->getProcessorPipeline()->walk(function ($processor) use ($expected) { if ($processor instanceof OperationId) { $this->assertEquals($expected, $processor->isHash()); } diff --git a/tests/OpenApiTestCase.php b/tests/OpenApiTestCase.php index 1950e8e2..7b75819d 100644 --- a/tests/OpenApiTestCase.php +++ b/tests/OpenApiTestCase.php @@ -231,7 +231,7 @@ public static function processors(array $strip = []): array $processors = []; (new Generator()) - ->getProcessor() + ->getProcessorPipeline() ->walk(function ($processor) use (&$processors, $strip) { if (!is_object($processor) || !in_array(get_class($processor), $strip)) { $processors[] = $processor; @@ -247,7 +247,7 @@ public function analysisFromFixtures(array $files, array $processors = [], ?Anal (new Generator($this->getTrackingLogger())) ->setAnalyser($analyzer ?: $this->getAnalyzer()) - ->setProcessor(new Pipeline($processors)) + ->setProcessorPipeline(new Pipeline($processors)) ->generate($this->fixtures($files), $analysis, false); return $analysis; diff --git a/tests/RefTest.php b/tests/RefTest.php index a14bd1c8..b852341c 100644 --- a/tests/RefTest.php +++ b/tests/RefTest.php @@ -27,7 +27,7 @@ public function testRef(): void $openapi->merge($this->annotationsFromDocBlockParser($comment)); $analysis = new Analysis([], $this->getContext()); $analysis->addAnnotation($openapi, $this->getContext()); - (new Generator())->getProcessor()->process($analysis); + (new Generator())->getProcessorPipeline()->process($analysis); $analysis->validate(); // escape / as ~1 diff --git a/tools/src/Docs/ProcGenerator.php b/tools/src/Docs/ProcGenerator.php index 8e71cb15..b35e3849 100644 --- a/tools/src/Docs/ProcGenerator.php +++ b/tools/src/Docs/ProcGenerator.php @@ -52,7 +52,7 @@ public function getProcessorsDetails(): array $defaultProcessors = []; (new Generator()) - ->getProcessor() + ->getProcessorPipeline() ->walk(function ($processor) use (&$processors, &$defaultProcessors) { $rc = new \ReflectionClass($processor); $class = $rc->getName();