PHPでSwagger3(OpenAPI Specification 3)

# Swagger-PHP v3.xの使い方です

[ドキュメント](http://zircote.com/swagger-php/Getting-started.html)
[サンプル](https://github.com/zircote/swagger-php/blob/master/Examples/Examples.md)

## OpenAPI Specification 3

[OpenAPI Specification](https://swagger.io/resources/open-api/)の[OAS3](https://swagger.io/specification/)

* [Basic Structure](https://swagger.io/docs/specification/basic-structure/)
* [Describing Request Body](https://swagger.io/docs/specification/describing-request-body/)
* [Describing parameters](https://swagger.io/docs/specification/describing-parameters/)
* [Describing responses](https://swagger.io/docs/specification/describing-responses/)

# Swagger-PHP

```sh
composer require zircote/swagger-php
```

```sh
vendor/bin/openapi -h
```

```
Usage: openapi [--option value] [/path/to/project ...]

Options:
  --output (-o)     Path to store the generated documentation.
                    ex: --output openapi.yaml
  --exclude (-e)    Exclude path(s).
                    ex: --exclude vendor,library/Zend
  --bootstrap (-b)  Bootstrap a php file for defining constants, etc.
                    ex: --bootstrap config/constants.php
  --processor       Register an additional processor.
  --format          Force yaml or json.
  --debug           Show additional error information.
  --help (-h)       Display this help message.
```

# openapiファイルの作成（アノテーション記述後）

```sh
vendor/bin/openapi -o {出力先} --format {出力フォーマット} {スキャン対象ディレクトリ}
```

```sh:example
vendor/bin/openapi -o openapi.json --format json --debug app/Http/Controllers/Api/

vendor/bin/openapi -o openapi.json --format json app/Http/Controllers/Api/
```

# アノテーションの記述方法

## まとめて設定するもの

[infoObject](https://swagger.io/specification/#infoObject)
[ServerObject](https://swagger.io/specification/#serverObject)
[SecurityRequirementObject](https://swagger.io/specification/#securityRequirementObject)
[TagObject](https://swagger.io/specification/#tagObject)
[ExternalDocumentationObject](https://swagger.io/specification/#externalDocumentationObject)
の設定は下記のようにスキャン対象ディレクトリ内に作る（ファイル名は自由）

ServerObjectはアクセスするAPIになるのでほぼ必須

```php:app/Http/Controllers/Api/swagger.php
<?php

// 必須
/**
 * @OA\Info(
 *  title="API Example",
 *  description="description",
 *  version="1.0.0",
 *   @OA\Contact(
 *     email="support@example.com"
 *   )
 * )
 */

// オプション
/**
 * @OA\Server(
 *   description="OpenApi host",
 *   url="http://192.168.99.100"
 * ),
 * @OA\SecurityScheme(
 *   securityScheme="api_key",
 *   type="apiKey",
 *   in="header",
 *   name="api_key"
 * ),
 * @OA\ExternalDocumentation(
 *   description="Find out more about Swagger",
 *   url="http://192.168.99.100"
 * ),
 * @OA\Tag(
 *   name="example",
 *   description="example",
 *   @OA\ExternalDocumentation(
 *     description="Find out more",
 *     url="http://192.168.99.100"
 *   )
 * )
 */
```

## JSON形式のAPI設定例

```php:app/Http/Controllers/Api/ExampleController.php

/**
 * @OA\Post(
 *   path="/api/example/{id}",
 *   summary="具体例が無かったので寄せ集めてみた",
 *   @OA\RequestBody(
 *     required=true,
 *     @OA\JsonContent(
 *       type="object",
 *       required={"number", "text"},
 *       @OA\Property(
 *         property="number",
 *         type="integer",
 *         format="int32",
 *         example=1,
 *         description="リクエストボディのjsonのプロパティの例"
 *       ),
 *       @OA\Property(
 *         property="text",
 *         type="string",
 *         example="text",
 *         description="リクエストボディのjsonのプロパティの例"
 *       )
 *     )
 *   ),
 *   @OA\Parameter(
 *     name="id",
 *     in="path",
 *     required=true,
 *     description="パスからのパラメータ例",
 *     @OA\Schema(type="string")
 *   ),
 *   @OA\Parameter(
 *     name="queryString",
 *     in="query",
 *     required=true,
 *     description="クエリーストリングからのパラメータ例",
 *     @OA\Schema(type="string")
 *   ),
 *   @OA\Response(
 *     response=200,
 *     description="OK",
 *     @OA\JsonContent(
 *       type="object",
 *       @OA\Property(
 *         property="message",
 *         type="string",
 *         description="レスポンスボディjsonパラメータの例"
 *       )
 *     )
 *   ),
 *   @OA\Response(
 *     response=400,
 *     description="Bad Request",
 *     @OA\JsonContent(
 *       type="object",
 *       @OA\Property(
 *         property="message",
 *         type="string",
 *         description="レスポンスボディjsonパラメータの例"
 *       )
 *     )
 *   ),
 *   @OA\Response(
 *     response=401,
 *     description="Unauthorized",
 *     @OA\JsonContent(
 *       type="object",
 *       @OA\Property(
 *         property="message",
 *         type="string",
 *         description="レスポンスボディjsonパラメータの例"
 *       )
 *     )
 *   ),
 *   @OA\Response(
 *     response="default",
 *     description="Unexpected Error",
 *     @OA\JsonContent(
 *       type="object",
 *       @OA\Property(
 *         property="message",
 *         type="string",
 *         description="レスポンスボディjsonパラメータの例"
 *       )
 *     )
 *   )
 * )
 */
public function example($id)
{
    // example
}
```

## 設定の使いまわし

OpenAPI Specificationではグローバルな`components.schemas`セクションに登録して、`$ref`を使用して使いまわすことができるのでそれの設定を行えば良い

```php
/**
 * @OA\Schema(
 *   schema="product_id",
 *   type="integer",
 *   format="int32",
 *   description="参照用"
 * )
 */
```

/components/schemas/product_idで参照可能


### 参照を使用した例

```php:app/Http/Controllers/Api/ExampleController.php
/**
 * @OA\Schema(
 *   schema="message",
 *   type="object",
 *   description="参照用",
 *   @OA\Property(
 *     property="message",
 *     type="string",
 *     description="レスポンスボディjsonパラメータの例"
 *   )
 * )
 */

/**
 * @OA\Post(
 *   path="/api/example/{id}",
 *   summary="具体例が無かったので寄せ集めてみた",
 *   @OA\RequestBody(
 *     required=true,
 *     @OA\JsonContent(
 *       type="object",
 *       required={"number", "text"},
 *       @OA\Property(
 *         property="number",
 *         type="integer",
 *         format="int32",
 *         example=1,
 *         description="リクエストボディのjsonのプロパティの例"
 *       ),
 *       @OA\Property(
 *         property="text",
 *         type="string",
 *         example="text",
 *         description="リクエストボディのjsonのプロパティの例"
 *       )
 *     )
 *   ),
 *   @OA\Parameter(
 *     name="id",
 *     in="path",
 *     required=true,
 *     description="パスからのパラメータ例",
 *     @OA\Schema(type="string")
 *   ),
 *   @OA\Parameter(
 *     name="queryString",
 *     in="query",
 *     required=true,
 *     description="クエリーストリングからのパラメータ例",
 *     @OA\Schema(type="string")
 *   ),
 *   @OA\Response(
 *     response=200,
 *     description="OK",
 *     @OA\JsonContent(@OA\Property(ref="#/components/schemas/message"))
 *   ),
 *   @OA\Response(
 *     response=400,
 *     description="Bad Request",
 *     @OA\JsonContent(@OA\Property(ref="#/components/schemas/message"))
 *   ),
 *   @OA\Response(
 *     response=401,
 *     description="Unauthorized",
 *     @OA\JsonContent(@OA\Property(ref="#/components/schemas/message"))
 *   ),
 *   @OA\Response(
 *     response="default",
 *     description="Unexpected Error",
 *     @OA\JsonContent(@OA\Property(ref="#/components/schemas/message"))
 *   )
 * )
 */
public function example($id)
{
    // example
}
```

## プロパティをネストする例

```php
/**
 * @OA\Schema(
 *   schema="message",
 *   type="object",
 *   description="message",
 *   @OA\Property(
 *     property="message",
 *     type="string",
 *     description="メッセージ"
 *   )
 * ),
 * @OA\Schema(
 *   schema="user",
 *   type="object",
 *   description="user",
 *   required={"message", "user"},
 *   @OA\Property(property="message", ref="#/components/schemas/message"),
 *   @OA\Property(
 *     property="user",
 *     type="object",
 *     description="ユーザー",
 *     required={"id", "name"},
 *     @OA\Property(
 *       property="id",
 *       type="string",
 *       description="ID"
 *     ),
 *     @OA\Property(
 *       property="name",
 *       type="string",
 *       description="名前"
 *     )
 *   )
 * )
 */
```

# openapiファイルを作成し終わったら

これで作った`json`や`yaml`は`Swagger UI`に読み込ませて終わり

[Swagger UI](https://swagger.io/docs/open-source-tools/swagger-ui/usage/installation/)

htmlファイルのみで作るなら
https://github.com/swagger-api/swagger-ui/blob/v3.18.3/dist/index.html
こちらと同じようにhtmlを作成しリンクを以下に変更し、`https://petstore.swagger.io/v2/swagger.json`部分をローカルに作ったものに置き換える

```:差し替えるリンク
（現時点の最新）
https://unpkg.com/swagger-ui-dist@3.20.6/swagger-ui.css
https://unpkg.com/swagger-ui-dist@3.20.6/favicon-32x32.png
https://unpkg.com/swagger-ui-dist@3.20.6/favicon-16x16.png
https://unpkg.com/swagger-ui-dist@3.20.6/swagger-ui-bundle.js
https://unpkg.com/swagger-ui-dist@3.20.6/swagger-ui-standalone-preset.js
```

# 出力されたopenapiファイル

```json
{
    "openapi": "3.0.0",
    "info": {
        "title": "API Example",
        "description": "description",
        "contact": {
            "email": "support@example.com"
        },
        "version": "1.0.0"
    },
    "servers": [
        {
            "url": "http://192.168.99.100",
            "description": "OpenApi host"
        }
    ],
    "paths": {
        "/api/example/{id}": {
            "post": {
                "summary": "具体例が無かったので寄せ集めてみた",
                "operationId": "App\\Http\\Controllers\\Api\\ExampleController::example",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "description": "パスからのパラメータ例",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "queryString",
                        "in": "query",
                        "description": "クエリーストリングからのパラメータ例",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "required": [
                                    "number",
                                    "text"
                                ],
                                "properties": {
                                    "number": {
                                        "description": "リクエストボディのjsonのプロパティの例",
                                        "type": "integer",
                                        "format": "int32",
                                        "example": 1
                                    },
                                    "text": {
                                        "description": "リクエストボディのjsonのプロパティの例",
                                        "type": "string",
                                        "example": "text"
                                    }
                                },
                                "type": "object"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "properties": {
                                        "": {
                                            "$ref": "#/components/schemas/message"
                                        }
                                    },
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "properties": {
                                        "": {
                                            "$ref": "#/components/schemas/message"
                                        }
                                    },
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "properties": {
                                        "": {
                                            "$ref": "#/components/schemas/message"
                                        }
                                    },
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "default": {
                        "description": "Unexpected Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "properties": {
                                        "": {
                                            "$ref": "#/components/schemas/message"
                                        }
                                    },
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "message": {
                "description": "参照用",
                "properties": {
                    "message": {
                        "description": "レスポンスボディjsonパラメータの例",
                        "type": "string"
                    }
                },
                "type": "object"
            }
        },
        "securitySchemes": {
            "api_key": {
                "type": "apiKey",
                "name": "api_key",
                "in": "header"
            }
        }
    },
    "tags": [
        {
            "name": "example",
            "description": "example",
            "externalDocs": {
                "description": "Find out more",
                "url": "http://192.168.99.100"
            }
        }
    ],
    "externalDocs": {
        "description": "Find out more about Swagger",
        "url": "http://192.168.99.100"
    }
}
```