Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
fail-fast: true
matrix:
operating-system: [ ubuntu-latest ]
php: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5' ]
php: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
dependencies: [ 'lowest', 'highest' ]

name: PHP ${{ matrix.php }} on ${{ matrix.operating-system }} with ${{ matrix.dependencies }} dependencies
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/security-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
fail-fast: true
matrix:
operating-system: [ ubuntu-latest ]
php: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5' ]
php: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
dependencies: [ 'highest' ]

name: PHP ${{ matrix.php }} on ${{ matrix.operating-system }} with ${{ matrix.dependencies }} dependencies
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Programmatically, the method `Generator::setVersion()` can be used to change the

## Requirements

`swagger-php` requires at least PHP 7.4 for annotations and PHP 8.1 for using attributes.
`swagger-php` requires at least PHP 8.1.

## Installation (with [Composer](https://getcomposer.org))

Expand Down Expand Up @@ -96,7 +96,7 @@ Generate always-up-to-date documentation.
```php
<?php
require("vendor/autoload.php");
$openapi = \OpenApi\Generator::scan(['/path/to/project']);
$openapi = (new \OpenApi\Generator->generate(['/path/to/project']);
header('Content-Type: application/x-yaml');
echo $openapi->toYaml();
```
Expand Down
4 changes: 2 additions & 2 deletions bin/openapi
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use OpenApi\Analysers\DocBlockAnnotationFactory;
use OpenApi\Analysers\ReflectionAnalyser;
use OpenApi\Annotations\OpenApi;
use OpenApi\Generator;
use OpenApi\Util;
use OpenApi\SourceFinder;
use OpenApi\Loggers\ConsoleLogger;

if (class_exists(Generator::class) === false) {
Expand Down Expand Up @@ -224,7 +224,7 @@ $openapi = $generator
->setVersion($options['version'])
->setConfig($options['config'])
->setAnalyser($analyser)
->generate(Util::finder($paths, $exclude, $pattern));
->generate(new SourceFinder($paths, $exclude, $pattern));

if ($options['output'] === false) {
if (strtolower($options['format']) === 'json') {
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@
"minimum-stability": "stable",
"extra": {
"branch-alias": {
"dev-master": "5.x-dev"
"dev-master": "6.x-dev"
}
},
"require": {
"php": ">=7.4",
"php": ">=8.1",
"ext-json": "*",
"nikic/php-parser": "^4.19 || ^5.0",
"phpstan/phpdoc-parser": "^2.0",
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ function getGuideSidebar() {
{
text: 'Upgrading',
items: [
{ text: 'Migration from 5.x to 6.x', link: '/guide/migrating-to-v6' },
{ text: 'Migration from 4.x to 5.x', link: '/guide/migrating-to-v5' },
{ text: 'Migration from 3.x to 4.x', link: '/guide/migrating-to-v4' },
{ text: 'Migration from 2.x to 3.x', link: '/guide/migrating-to-v3' },
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/generating-openapi-documents.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ In its simplest form this may look something like

```php
<?php
require("vendor/autoload.php");
require('vendor/autoload.php');

$openapi = \OpenApi\Generator::scan(['/path/to/project']);
$openapi = (new \OpenApi\Generator)->generate(['/path/to/project']);

header('Content-Type: application/x-yaml');
echo $openapi->toYaml();
Expand Down
3 changes: 1 addition & 2 deletions docs/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,5 @@ either adding [`Annotations`](using-annotations.md) or [`Attributes`](using-attr
:::

::: warning Requirements
Using `swagger-php` requires a minimum of **PHP&nbsp;7.4** for using annotations and
at least **PHP&nbsp;8.1** to use attributes.
Using `swagger-php` requires a minimum of **PHP&nbsp;8.1**.
:::
24 changes: 24 additions & 0 deletions docs/guide/migrating-to-v6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Migrating to v6

## Overview

v6 is mostly a cleanup release with updated dependencies. The main changes are:

* Minimum required PHP version is now 8.1
* ...

For most installations upgrading should not require any changes.

## Removed deprecated elements
### Methods `\Openapi\Generator::getProcessors()` and `\Openapi\Generator::setProcessors()`
Use `getProcessorPipeline()` and `setProcessorPipeline(new Pipeline(...))` methods instead

### Static method `\Openapi\Generator::scan()`
Main entry point into the `Generator` is now the **non-static** `generate()` method:
```php
(new Generator())->generate(/* ... */);
```

### `Utils` helper
Most methods in the class were internal to start with and the `Util::finder()` factory methods is now replaced with
the new `SourceFinder` class.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ features:

Add `swagger-php` attributes (or legacy annotations) to your source code.

⚠️ `doctrine/annotations` is going to be deprecated in the future, so wherever
⚠️ The `doctrine/annotations` library used to parse annotation is going to be deprecated, so wherever
possible attributes should be used.

<codeblock id="minimal">
Expand Down
131 changes: 14 additions & 117 deletions docs/reference/generator.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,17 @@
# Using the `Generator`

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

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

For a long time the `\OpenApi\scan()` function was the main entry point into using swagger-php from PHP code.

```php
/**
* Scan the filesystem for OpenAPI annotations and build openapi-documentation.
*
* @param array|Finder|string $directory The directory(s) or filename(s)
* @param array $options
* exclude: string|array $exclude The directory(s) or filename(s) to exclude (as absolute or relative paths)
* pattern: string $pattern File pattern(s) to scan (default: *.php)
* analyser: defaults to StaticAnalyser
* analysis: defaults to a new Analysis
* processors: defaults to the registered processors in Analysis
*
* @return OpenApi
*/
function scan($directory, $options = []) { /* ... */ }
```

Using it looked typically something like this:
```php
require("vendor/autoload.php");

$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.

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...
The `Generator` class is the main entry point into `swagger-php`.

## The `\OpenApi\Generator` class

The `Generator` class can be used in object-oriented (and fluent) style which allows for easy customization
if needed.
The `Generator` class provides an object-oriented (and fluent) API to generate OpenApi specs
and allows for easy customization where needed.

In that case to actually process the given input files the **non-static** method `generate()` is to be used.

Full example of using the `Generator` class to generate OpenApi specs.
## Full example of using the `Generator` class to generate OpenApi specs

```php
require("vendor/autoload.php");
require('vendor/autoload.php');

$validate = true;
$logger = new \Psr\Log\NullLogger();
Expand All @@ -67,99 +29,34 @@ $openapi = (new \OpenApi\Generator($logger))
->generate(['/path1/to/project', $finder], new \OpenApi\Analysis([], $context), $validate);
```

`Aliases` and `namespaces` are additional options that allow to customize the parsing of docblocks.
`Aliases` and `namespaces` setting are optional and only required when using annotations.
They allow to customize the parsing of docblocks and are passed through to the underlying `doctrine/annotations` library.

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

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

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

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

Advantages:
* The `Generator` code will handle configuring things as before in a single place
* Static settings will be reverted to the default once finished
* The get/set methods allow for using type hints
* Static configuration is deprecated and can be removed at some point without code changes
* Build in support for PSR logger
* Support for [Symfony Finder](https://symfony.com/doc/current/components/finder.html), `\SplInfo` and file/directory names (`string) as source.

The minimum code required, using the `generate()` method, looks quite similar to the old `scan()` code:

```php
/**
* Generate OpenAPI spec by scanning the given source files.
*
* @param iterable $sources PHP source files to scan.
* Supported sources:
* * string - file / directory name
* * \SplFileInfo
* * \Symfony\Component\Finder\Finder
* @param null|Analysis $analysis custom analysis instance
* @param bool $validate flag to enable/disable validation of the returned spec
*/
public function generate(iterable $sources, ?Analysis $analysis = null, bool $validate = true): \OpenApi\OpenApi { /* ... */ }
```

## Basic example
```php
require("vendor/autoload.php");
require('"vendor/autoload.php');

$openapi = (new \OpenApi\Generator())->generate(['/path1/to/project']);
```

For those that want to type even less and keep using a plain array to configure `swagger-php` there is also a static version:

```php
<?php
require("vendor/autoload.php");

$openapi = \OpenApi\Generator::scan(['/path/to/project']);

header('Content-Type: application/x-yaml');
echo $openapi->toYaml();
```

**Note:** While using the same name as the old `scan()` function, the `Generator::scan` method is not
100% backwards compatible.

```php
/**
* Static wrapper around `Generator::generate()`.
*
* @param iterable $sources PHP source files to scan.
* Supported sources:
* * string
* * \SplFileInfo
* * \Symfony\Component\Finder\Finder
* @param array $options
* aliases: null|array Defaults to `['oa' => 'OpenApi\\Annotations']`.
* namespaces: null|array Defaults to `['OpenApi\\Annotations\\']`.
* analyser: null|AnalyserInterface Defaults to a new `ReflectionAnalyser` supporting both docblocks and attributes.
* analysis: null|Analysis Defaults to a new `Analysis`.
* processors: null|array Defaults to `Analysis::processors()`.
* logger: null|\Psr\Log\LoggerInterface If not set logging will use \OpenApi\Logger as before.
* validate: bool Defaults to `true`.
* version: string Defaults to `\OpenApi\Annotations\OpenApi::VERSION_3_0_0`. Alternatives are: `\OpenApi\Annotations\OpenApi::VERSION_3_1_0`.
*/
public static function scan(iterable $sources, array $options = []): OpenApi { /* ... */ }
```

Most notably the `exclude` and `pattern` keys are no longer supported. Instead, a Symfony `Finder` instance can be passed in
as source directly (same as with `Generator::generate()`).

If needed, the `\OpenApi\Util` class provides a builder method that allows to keep the status-quo
The `generate()` method does not support the CLI `exclude` and `pattern` options directly.
Instead, a custom Symfony `Finder` class (`SourceFinder`) can be used that understands these options.

```php
$exclude = ['tests'];
$pattern = '*.php';

$openapi = \OpenApi\Generator::scan(\OpenApi\Util::finder(__DIR__, $exclude, $pattern));

// same as

$openapi = \OpenApi\scan(__DIR__, ['exclude' => $exclude, 'pattern' => $pattern]);
$openapi = (new \OpenApi\Generator())->generate(new \OpenApi\SourceFinder(__DIR__, $exclude, $pattern));
```
15 changes: 13 additions & 2 deletions rector.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector;
use Rector\CodeQuality\Rector\For_\ForRepeatedCountToOwnVariableRector;
use Rector\CodeQuality\Rector\If_\CombineIfRector;
use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector;
Expand All @@ -12,6 +13,7 @@
use Rector\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector;
use Rector\DeadCode\Rector\If_\RemoveDeadInstanceOfRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector;
use Rector\ValueObject\PhpVersion;

Expand Down Expand Up @@ -40,7 +42,16 @@
ParamTypeByMethodCallTypeRector::class => [
__DIR__ . '/src/Serializer.php',
],
ClassPropertyAssignToConstructorPromotionRector::class,
CompleteDynamicPropertiesRector::class => [
__DIR__ . '/src/Annotations/AbstractAnnotation.php',
],
])
->withPreparedSets(true, true, true, true)
->withPhpVersion(PhpVersion::PHP_74)
->withPreparedSets(
deadCode: true,
codeQuality: true,
codingStyle: true,
typeDeclarations: true,
)
->withPhpVersion(PhpVersion::PHP_81)
->withPhpSets();
2 changes: 1 addition & 1 deletion src/Analysers/AttributeAnnotationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function __construct(bool $ignoreOtherAttributes = false)

public function isSupported(): bool
{
return \PHP_VERSION_ID >= 80100;
return true;
}

public function build(\Reflector $reflector, Context $context): array
Expand Down
2 changes: 1 addition & 1 deletion src/Analysers/ComposerAutoloaderScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function scan(array $namespaces): array
if ($autoloader = static::getComposerAutoloader()) {
foreach (array_keys($autoloader->getClassMap()) as $unit) {
foreach ($namespaces as $namespace) {
if (0 === strpos($unit, $namespace)) {
if (str_starts_with($unit, $namespace)) {
$units[] = $unit;
break;
}
Expand Down
6 changes: 3 additions & 3 deletions src/Analysers/DocBlockAnnotationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ public function build(\Reflector $reflector, Context $context): array
$aliases = $this->generator ? $this->generator->getAliases() : [];

if (method_exists($reflector, 'getShortName') && method_exists($reflector, 'getName')) {
$aliases[strtolower($reflector->getShortName())] = $reflector->getName();
$aliases[strtolower((string) $reflector->getShortName())] = $reflector->getName();
}

if ($context->with('scanned')) {
$details = $context->scanned;
foreach ($details['uses'] as $alias => $name) {
$aliasKey = strtolower($alias);
$aliasKey = strtolower((string) $alias);
if ($name != $alias && !array_key_exists($aliasKey, $aliases)) {
// real aliases only
$aliases[strtolower($alias)] = $name;
$aliases[strtolower((string) $alias)] = $name;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Analysers/TokenScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ protected function collect_stmts(array $stmts, string $namespace): array
};
$result = [];
foreach ($stmts as $stmt) {
switch (get_class($stmt)) {
switch ($stmt::class) {
case Use_::class:
$uses += $this->collect_uses($stmt);
break;
Expand Down
Loading