Skip to content

Commit

Permalink
Replace current processor array with a pipeline class (#1585)
Browse files Browse the repository at this point in the history
Deprecates all processor related methods on `Generator` and provides simpler, more powerful alternatives in the new `Pipeline` class that now internally wraps processors.

Also deprecates the unused `ProcessorInterface`.
  • Loading branch information
DerManoMann authored Jun 5, 2024
1 parent 067c1d6 commit 489305a
Show file tree
Hide file tree
Showing 16 changed files with 352 additions and 105 deletions.
24 changes: 12 additions & 12 deletions Examples/processors/schema-query-parameter-attributes/scan.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use OpenApi\Generator;
use OpenApi\Pipeline;
use OpenApi\Processors\BuildPaths;
use SchemaQueryParameterProcessor\SchemaQueryParameter;

Expand All @@ -11,19 +12,18 @@
// and our custom processor
$classLoader->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();
24 changes: 10 additions & 14 deletions Examples/processors/schema-query-parameter/scan.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use OpenApi\Generator;
use OpenApi\Pipeline;
use OpenApi\Processors\BuildPaths;
use SchemaQueryParameterProcessor\SchemaQueryParameter;

Expand All @@ -11,23 +12,18 @@
// and our custom processor
$classLoader->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();
2 changes: 1 addition & 1 deletion bin/openapi
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ foreach ($options["processor"] as $processor) {
} elseif (class_exists($processor)) {
$processor = new $processor();
}
$generator->addProcessor($processor);
$generator->getProcessorPipeline()->add($processor);
}

$analyser = $options['legacy']
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand All @@ -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)
->setProcessorPipeline(new \OpenApi\Pipeline($processors))
->setAliases(['MY' => 'My\Annotations'])
->setNamespaces(['My\\Annotations\\'])
->setAnalyser(new \OpenApi\Analysers\TokenAnalyser())
Expand Down
108 changes: 70 additions & 38 deletions src/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ class Generator
protected $namespaces;

/** @var AnalyserInterface|null The configured analyzer. */
protected $analyser;
protected $analyser = null;

/** @var array<string,mixed> */
protected $config = [];

/** @var array<ProcessorInterface|callable>|null List of configured processors. */
protected $processors = null;
/** @var Pipeline|null */
protected $processorPipeline = null;

/** @var LoggerInterface|null PSR logger. */
protected $logger = null;
Expand Down Expand Up @@ -243,13 +243,10 @@ public function setConfig(array $config): Generator
return $this;
}

/**
* @return array<ProcessorInterface|callable>
*/
public function getProcessors(): array
public function getProcessorPipeline(): Pipeline
{
if (null === $this->processors) {
$this->processors = [
if (null === $this->processorPipeline) {
$this->processorPipeline = new Pipeline([
new Processors\DocBlockDescriptions(),
new Processors\MergeIntoOpenApi(),
new Processors\MergeIntoComponents(),
Expand All @@ -267,77 +264,108 @@ 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->processorPipeline->walk($walker);
}

public function setProcessorPipeline(Pipeline $processor): Generator
{
$this->processorPipeline = $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->getProcessorPipeline());

return $this->processors;
return $this;
}

/**
* @return array<ProcessorInterface|callable>
*
* @deprecated
*/
public function getProcessors(): array
{
return $this->getProcessorPipeline()->pipes();
}

/**
* @param array<ProcessorInterface|callable>|null $processors
*
* @deprecated
*/
public function setProcessors(?array $processors): Generator
{
$this->processors = $processors;
$this->processorPipeline = null !== $processors ? new Pipeline($processors) : null;

return $this;
}

/**
* @param callable|ProcessorInterface $processor
* @param class-string|null $before
*
* @deprecated
*/
public function addProcessor($processor, ?string $before = null): Generator
{
$processors = $this->getProcessors();
$processors = $this->processorPipeline ?: $this->getProcessorPipeline();
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->processorPipeline = $processors;

return $this;
}

/**
* @param callable|ProcessorInterface $processor
*
* @deprecated
*/
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->processorPipeline ?: $this->getProcessorPipeline();
$processors->remove($processor);
$this->processorPipeline = $processors;

return $this;
}
Expand All @@ -348,6 +376,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
{
Expand Down Expand Up @@ -390,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,
Expand All @@ -401,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'])
->setProcessorPipeline($config['processor'])
->setProcessorPipeline(new Pipeline($config['processors']))
->generate($sources, $config['analysis'], $config['validate']);
}

Expand Down Expand Up @@ -455,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->getProcessorPipeline()->process($analysis);

if ($analysis->openapi) {
// overwrite default/annotated version
Expand Down
Loading

0 comments on commit 489305a

Please sign in to comment.