zircote/swagger-php
swagger-php generates OpenAPI 3.0/3.1/3.2 documentation from your PHP 8.2+ code using attributes (preferred) or optional Doctrine annotations. Use it via CLI or programmatically, with helpful error reporting and a full documentation site.
x-tagGroupsOpenApi has the concept of grouping endpoints using tags. On top of that, some tools
(redocly, for example)
support further grouping via the vendor extension x-tagGroups.
<<< @/snippets/guide/cookbook/x_tag_groups_at.php
<<< @/snippets/guide/cookbook/x_tag_groups_an.php
[@OA](https://github.com/OA)\Response<<< @/snippets/guide/cookbook/response_examples_at.php
<<< @/snippets/guide/cookbook/response_examples_an.php
OpenApi allows a single reference to external documentation. This is a part of the top level [@OA](https://github.com/OA)\OpenApi.
<<< @/snippets/guide/cookbook/external_documentation_at.php
<<< @/snippets/guide/cookbook/external_documentation_an.php
::: tip
If no [@OA](https://github.com/OA)\OpenApi is configured, swagger-php will create one automatically.
That means the above example would also work with just the OA\ExternalDocumentation annotation.
/**
* [@OA](https://github.com/OA)\ExternalDocumentation(
* description="More documentation here...",
* url="https://example.com/externaldoc1/"
* )
*/
:::
Sometimes properties or even lists (arrays) may contain data of different types. This can be expressed using oneOf.
<<< @/snippets/guide/cookbook/properties_with_union_types_at.php
<<< @/snippets/guide/cookbook/properties_with_union_types_an.php
This will resolve into this YAML
openapi: 3.0.0
components:
schemas:
StringList:
properties:
value:
type: array
items:
anyOf:
-
type: string
type: object
String:
properties:
value:
type: string
type: object
Object:
properties:
value:
type: object
type: object
mixedList:
properties:
fields:
type: array
items:
oneOf:
-
$ref: '#/components/schemas/StringList'
-
$ref: '#/components/schemas/String'
-
$ref: '#/components/schemas/Object'
type: object
An API might have zero or more security schemes. These are defined at the top level and vary from simple to complex:
<<< @/snippets/guide/cookbook/security_schemas_at.php
<<< @/snippets/guide/cookbook/security_schemas_an.php
To declare an endpoint as secure and define what security schemes are available to authenticate a client, it needs to be added to the operation, for example:
<<< @/snippets/guide/cookbook/secure_endpoint_at.php
<<< @/snippets/guide/cookbook/secure_endpoint_an.php
::: tip Endpoints can support multiple security schemes and have custom options too:
<<< @/snippets/guide/cookbook/security_schema_tips_at.php
<<< @/snippets/guide/cookbook/security_schema_tips_an.php
<<< @/snippets/guide/cookbook/file_upload_with_headers_at.php
<<< @/snippets/guide/cookbook/file_upload_with_headers_an.php
The OA\Xml annotation may be used to set the XML root element for a given [@OA](https://github.com/OA)\XmlContent response body
<<< @/snippets/guide/cookbook/set_xml_root_name_at.php
<<< @/snippets/guide/cookbook/set_xml_root_name_an.php
Form posts are [@OA](https://github.com/OA)\Post requests with a multipart/form-data [@OA](https://github.com/OA)\RequestBody. The relevant bit looks something like this
<<< @/snippets/guide/cookbook/uploading_multipart_formdata_at.php
<<< @/snippets/guide/cookbook/uploading_multipart_formdata_an.php
Unless specified, each endpoint needs to declare what security schemes it supports. However, there is a way to also configure security schemes globally for the whole API.
This is done on the [@OA](https://github.com/OA)\OpenApi annotation:
<<< @/snippets/guide/cookbook/default_security_at.php
<<< @/snippets/guide/cookbook/default_security_an.php
Complex, nested data structures are defined by nesting [@OA](https://github.com/OA)\Property annotations inside others (with type="object").
<<< @/snippets/guide/cookbook/nested_objects_at.php
<<< @/snippets/guide/cookbook/nested_objects_an.php
oneOfA response with either a single or a list of QualificationHolder's.
<<< @/snippets/guide/cookbook/oneof_example_at.php
<<< @/snippets/guide/cookbook/oneof_example_an.php
Global responses are found under /components/responses and can be referenced/shared just like schema definitions (models)
<<< @/snippets/guide/cookbook/reusing_response_at.php
<<< @/snippets/guide/cookbook/reusing_response_an.php
::: tip response parameter is always required
Even if referencing a shared response definition, the response parameter is still required.
:::
Using */* as mediaType is not possible using annotations.
Example:
/**
* [@OA](https://github.com/OA)\MediaType(
* mediaType="*/*",
* [@OA](https://github.com/OA)\Schema(type="string",format="binary")
* )
*/
The doctrine annotations library used for parsing annotations does not handle this and will interpret the */ bit as the end of the comment.
Using just * or application/octet-stream might be usable workarounds.
Multiple response with same response="200"There are two scenarios where this can happen
response value.response value.The API does include basic support for callbacks. However, this needs to be set up mostly manually.
Example
#[OA\Get(
// ...
callbacks: [
'onChange' => [
'{$request.query.callbackUrl}' => [
'post' => [
'requestBody' => new OA\RequestBody(
description: 'subscription payload',
content: [
new OA\MediaType(
mediaType: 'application/json',
schema: new OA\Schema(
properties: [
new OA\Property(
property: 'timestamp',
type: 'string',
format: 'date-time',
description: 'time of change'
),
],
),
),
],
),
],
],
],
],
// ...
)]
/**
* [@OA](https://github.com/OA)\Get(
* ...
* callbacks={
* "onChange"={
* "{$request.query.callbackUrl}"={
* "post": {
* "requestBody": [@OA](https://github.com/OA)\RequestBody(
* description="subscription payload",
* [@OA](https://github.com/OA)\MediaType(mediaType="application/json", [@OA](https://github.com/OA)\Schema(
* [@OA](https://github.com/OA)\Property(property="timestamp", type="string", format="date-time", description="time of change")
* ))
* )
* },
* "responses": {
* "202": {
* "description": "Your server implementation should return this HTTP status code if the data was received successfully"
* }
* }
* }
* }
* }
* ...
* )
*/
Typically, a model is annotated by adding a [@OA](https://github.com/OA)\Schema annotation to the class and then individual [@OA](https://github.com/OA)\Property annotations
to the individually declared class properties.
It is possible, however, to nest @\Property annotations inside a schema even without properties. In fact, all that is needed
is a code anchor - e.g. an empty class.
<<< @/snippets/guide/cookbook/virtual_model_at.php
<<< @/snippets/guide/cookbook/virtual_model_an.php
Typically, when referencing schemas this is done using $ref's
#[OAT\Schema(schema: 'user')]
class User
{
}
#[OAT\Schema()]
class Book
{
/**
* [@var](https://github.com/var) User
*/
#[OAT\Property(ref: '#/components/schemas/user')]
public $author;
}
This works but is not very convenient.
First, when using custom schema names (schema: 'user'), this needs to be taken into account everywhere.
Secondly, having to write ref: '#/components/schemas/user' is tedious and error-prone.
Using attributes, all this changes as we can take advantage of PHP itself by referring to a schema by its (fully qualified) class name.
With the same User schema as before, the Book::author property could be written in a few different ways
#[OAT\Property()]
public User author;
or
/**
* [@var](https://github.com/var) User
*/
#[OAT\Property()]
public author;
or
#[OA\Property(type: User::class)]
public author;
/** [@OA](https://github.com/OA)\Property() */
public User author;
or
/**
* [@var](https://github.com/var) User
* [@OA](https://github.com/OA)\Property()
*/
public author;
As of PHP 8.1 there is native support for enum's.
swagger-php supports enums in much the same way as class names can be used to reference schemas.
Example
<<< @/snippets/guide/cookbook/enums_at.php
<<< @/snippets/guide/cookbook/enums_an.php
However, in this case the schema generated for State will be an enum:
components:
schemas:
PullRequest:
properties:
state:
$ref: '#/components/schemas/State'
type: object
State:
type: string
enum:
- OPEN
- MERGED
- DECLINED
&q[]=1&q[]=1PHP allows having query parameters multiple times in the url and will combine the values to an array if the parameter
name uses trailing []. In fact, it is possible to create nested arrays too by using more than one pair of [].
In terms of OpenAPI, the parameters can be considered a single parameter with a list of values.
<<< @/snippets/guide/cookbook/multi_value_query_parameter_at.php
<<< @/snippets/guide/cookbook/multi_value_query_parameter_an.php
The corresponding bit of the spec will look like this:
parameters:
-
name: 'things[]'
in: query
description: 'A list of things.'
required: false
schema:
type: array
items:
type: integer
swagger-ui will show a form that allows to add/remove items (integer values in this case) to/from a list
and post those values as something like ?things[]=1&things[]=2&things[]=0
Even with using refs, there is a bit of overhead in sharing responses. One way around that is to write
your own response classes.
The beauty is that in your custom __construct() method you can prefill as much as you need.
Best of all, this works for both annotations and attributes.
Example:
use OpenApi\Attributes as OA;
/**
* [@Annotation](https://github.com/Annotation)
*/
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class BadRequest extends OA\Response
{
public function __construct()
{
parent::__construct(response: 400, description: 'Bad request');
}
}
class Controller
{
#[OA\Get(path: '/foo', responses: [new BadRequest()])]
public function get()
{
}
#[OA\Post(path: '/foo')]
#[BadRequest]
public function post()
{
}
/**
* [@OA](https://github.com/OA)\Delete(
* path="/foo",
* [@BadRequest](https://github.com/BadRequest)()
* )
*/
public function delete()
{
}
}
::: tip Annotations only?
If you are only interested in annotations, you can leave out the attribute setup line (#[\Attribute...) for BadRequest.
Furthermore, your custom annotations should extend from the OpenApi\Annotations namespace.
:::
<<< @/snippets/guide/cookbook/class_constants_at.php
<<< @/snippets/guide/cookbook/class_constants_an.php
The const property is supported in OpenApi 3.1.0.
components:
schemas:
Airport:
properties:
kind:
type: string
const: Airport
For 3.0.0 this is serialized into a single value enum.
components:
schemas:
Airport:
properties:
kind:
type: string
enum:
- Airport
How can I help you explore Laravel packages today?