Skip to content

Commit ab6d661

Browse files
author
Arthur Mogliev
authored
[php-mezzio-ph] PHP 8 support (#9445)
* Huge update for php-mezzio-ph generator: - proper container support for data types - support for query parameter references and query parameter schema references - dependency update (PHP 7.3+, PathHandler 0.7+, DataTransfer 0.5+) * Sample regeneration after rebasing for php-mezzio-ph * - added custom CLI option for php-mezzio-ph to generate code using modern PHP syntax - removed obsolete php-mezzio-ph samples in samples/openapi3/server/petstore/php-mezzio-ph * - fixes for JavaDoc declarations that seems to break CI * - fix for outdated sample file
1 parent 4948ce7 commit ab6d661

File tree

190 files changed

+2967
-1221
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

190 files changed

+2967
-1221
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ samples/client/petstore/python-tornado/.venv/
177177
# PHP
178178
samples/client/petstore/php/OpenAPIClient-php/composer.lock
179179
samples/openapi3/server/petstore/php-symfony/SymfonyBundle-php/composer.lock
180-
samples/openapi3/server/petstore/php-mezzio-ph/composer.lock
181180
samples/server/petstore/php-laravel/lib/composer.lock
182181
samples/server/petstore/php-lumen/lib/composer.lock
183182
samples/server/petstore/php-slim4/composer.lock

bin/configs/php-mezzio-ph-modern.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
generatorName: php-mezzio-ph
2+
outputDir: samples/server/petstore/php-mezzio-ph-modern
3+
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/php-mezzio-ph-modern
5+
additionalProperties:
6+
modern: "true"

docs/generators/php-mezzio-ph.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
1515
|invokerPackage|The main namespace to use for all classes. e.g. Yay\Pets| |null|
1616
|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C#have this enabled by default).|<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|true|
1717
|modelPackage|package for generated models| |null|
18+
|modern|use modern language features (generated code will require PHP 8.0)| |false|
1819
|packageName|The main package name for classes. e.g. GeneratedPetstore| |null|
1920
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
2021
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpMezzioPathHandlerServerCodegen.java

Lines changed: 165 additions & 58 deletions
Large diffs are not rendered by default.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace {{invokerPackage}};
5+
6+
use {{invokerPackage}}\Middleware;
7+
use Interop\Container\ContainerInterface;
8+
use Psr\Http\Message\ResponseInterface;
9+
use Psr\Http\Message\ServerRequestInterface;
10+
use Mezzio\Application;
11+
use Mezzio\Handler\NotFoundHandler;
12+
use Mezzio\MiddlewareFactory;
13+
use Mezzio\Router\Middleware\DispatchMiddleware;
14+
use Mezzio\Router\Middleware\MethodNotAllowedMiddleware;
15+
use Mezzio\Router\Middleware\RouteMiddleware;
16+
use Mezzio\Router\RouteCollector;
17+
use Laminas\HttpHandlerRunner\Emitter\EmitterInterface;
18+
use Laminas\HttpHandlerRunner\RequestHandlerRunner;
19+
use Laminas\ServiceManager\Factory\FactoryInterface;
20+
use Laminas\Stratigility\MiddlewarePipe;
21+
22+
class Factory implements FactoryInterface
23+
{
24+
public function __invoke(ContainerInterface $container, $requestedName, array $options = null): Application
25+
{
26+
$errorMiddleware = self::getErrorMiddleware($container);
27+
$pipeline = new MiddlewarePipe();
28+
$runner = new RequestHandlerRunner(
29+
$pipeline,
30+
$container->get(EmitterInterface::class),
31+
$container->get(ServerRequestInterface::class),
32+
static fn(\Throwable $error): ResponseInterface => $errorMiddleware->handleError($error)
33+
);
34+
$application = new Application(
35+
$container->get(MiddlewareFactory::class),
36+
$pipeline,
37+
$container->get(RouteCollector::class),
38+
$runner
39+
);
40+
$application->pipe($errorMiddleware);
41+
$application->pipe(RouteMiddleware::class);
42+
$application->pipe(MethodNotAllowedMiddleware::class);
43+
$application->pipe(DispatchMiddleware::class);
44+
$application->pipe(NotFoundHandler::class);
45+
46+
return $application;
47+
}
48+
49+
protected static function getErrorMiddleware(ContainerInterface $container): Middleware\InternalServerError
50+
{
51+
return $container->get(Middleware\InternalServerError::class);
52+
}
53+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace {{invokerPackage}}\Middleware;
5+
6+
use Psr\Http\Message\ResponseInterface;
7+
use Psr\Http\Message\ServerRequestInterface;
8+
use Psr\Http\Server\MiddlewareInterface;
9+
use Psr\Http\Server\RequestHandlerInterface;
10+
use Laminas\Stdlib\ErrorHandler;
11+
12+
class InternalServerError implements MiddlewareInterface
13+
{
14+
public function __construct(protected \Closure $responseGenerator)
15+
{
16+
}
17+
18+
/**
19+
* @inheritdoc
20+
*/
21+
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
22+
{
23+
$result = null;
24+
try {
25+
ErrorHandler::start();
26+
$result = $handler->handle($request);
27+
ErrorHandler::stop(true);
28+
}
29+
catch (\Throwable $error) {
30+
$result = $this->handleError($error);
31+
}
32+
return $result;
33+
}
34+
35+
public function handleError(\Throwable $error): ResponseInterface
36+
{
37+
\error_log((string)$error);
38+
return $this->generateEmptyResponse()->withStatus(500, 'Internal server error');
39+
}
40+
41+
protected function generateEmptyResponse(): ResponseInterface
42+
{
43+
return ($this->responseGenerator)();
44+
}
45+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# OpenAPI generated server
2+
3+
Generated by the [OpenAPI Generator](https://openapi-generator.tech) project.
4+
5+
## Overview
6+
This server stub aims to provide light, yet comprehensive structure for your API project using:
7+
8+
- PHP: >=8.0
9+
- [Laminas Mezzio](https://docs.mezzio.dev/mezzio/): >=3.3
10+
- [Path Handler](https://github.com/Articus/PathHandler): >=0.7
11+
12+
## How to use
13+
All you have to do to start development is:
14+
15+
- install dependencies via [Composer](https://getcomposer.org/) (small note: [ext-yaml](https://pecl.php.net/package/yaml) is used only for configuration parsing, so if you want to drop this dependency, simply adjust `./application/container.php`)
16+
- create cache folder: `mkdir -p ./data/cache` (you will need it later for configuration and metadata caches - check comments in `./application/config.yml`)
17+
- start PHP development server: `php -S 0.0.0.0:8080 -t ./public` (or any other SAPI you prefer, just make sure that you configure webroot to `./public` and rewrites to `./public/index.php`)
18+
19+
After that you should be able to call all methods from your API spec. Most of the negative scenarios should be handled:
20+
21+
- `404 Not found` for unknown routes
22+
- `406 Not acceptable` for invalid `Accept` header
23+
- `415 Unsupported media type` for invalid `Content-Type` header
24+
- `400 Malformed JSON` for unparsable JSON body
25+
- `422 Unprocessable entity` for parsable JSON body that fails validation
26+
27+
But for obvious reason you will not get any `200 OK`, only `501 Not implemented`. So your next steps are:
28+
29+
- check all TODOs left in the stub code where generator was not smart enough and could not guarantee correct implementation
30+
- implement your API security mechanism (either special attribute or separate middleware) - generator does not do anything about it yet
31+
- implement your handlers - the most tricky part :)
32+
33+
## Enjoy!
34+
Hopefully this stub will reduce the amount of boilerplate code you have to write manually. If you have any suggestions or questions about `php-mezzio-ph` generator, feel free to create issue either in [Path Handler repository](https://github.com/Articus/PathHandler/issues) or in [OpenAPI Generator repository](https://openapi-generator.tech/issues).
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace {{package}};
5+
6+
use Articus\PathHandler\PhpAttribute as PHA;
7+
use Articus\PathHandler\Consumer as PHConsumer;
8+
use Articus\PathHandler\Producer as PHProducer;
9+
use Articus\PathHandler\Attribute as PHAttribute;
10+
use Articus\PathHandler\Exception as PHException;
11+
use Psr\Http\Message\ServerRequestInterface;
12+
13+
{{#operations}}
14+
{{#description}}
15+
/**
16+
* {{&description}}
17+
*/
18+
{{/description}}
19+
#[PHA\Route("{{pathPattern}}")]
20+
class {{classname}}
21+
{
22+
{{#operation}}
23+
/**
24+
{{#summary}}
25+
* {{summary}}
26+
{{/summary}}
27+
{{#description}}
28+
* {{description}}
29+
{{/description}}
30+
*/
31+
#[PHA\{{httpMethod}}]
32+
{{#vendorExtensions}}
33+
{{#internal.ze-ph.hasQueryData}}
34+
#[PHA\Attribute(PHAttribute\Transfer::class, [
35+
"type" => {{internal.ze-ph.queryDataType}}::class,
36+
"objectAttr" => "queryData",
37+
"source" => PHAttribute\Transfer::SOURCE_GET
38+
])]
39+
{{/internal.ze-ph.hasQueryData}}
40+
{{/vendorExtensions}}
41+
{{#bodyParam}}
42+
{{#consumes}}
43+
// TODO check if consumer is valid, if it has correct priority and if it can be moved to class annotation
44+
#[PHA\Consumer("{{{mediaType}}}", PHConsumer\Json::class)]
45+
{{/consumes}}
46+
{{^isPrimitiveType}}
47+
#[PHA\Attribute(PHAttribute\Transfer::class, ["type" => {{dataType}}::class, "objectAttr" => "bodyData"])]
48+
{{/isPrimitiveType}}
49+
{{/bodyParam}}
50+
{{#produces}}
51+
// TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation
52+
#[PHA\Producer("{{{mediaType}}}", PHProducer\Transfer::class)]
53+
{{/produces}}
54+
public function {{operationId}}(ServerRequestInterface $request){{#returnType}}: {{returnType}}{{/returnType}}
55+
{
56+
//TODO implement method
57+
{{#vendorExtensions}}
58+
{{#internal.ze-ph.hasQueryData}}
59+
/** @var {{internal.ze-ph.queryDataType}} $queryData */
60+
$queryData = $request->getAttribute("queryData");
61+
{{/internal.ze-ph.hasQueryData}}
62+
{{/vendorExtensions}}
63+
{{#bodyParam}}
64+
{{^isPrimitiveType}}
65+
/** @var {{dataType}} $bodyData */
66+
$bodyData = $request->getAttribute("bodyData");
67+
{{/isPrimitiveType}}
68+
{{/bodyParam}}
69+
throw new PHException\HttpCode(501, "Not implemented");
70+
}
71+
{{/operation}}
72+
}
73+
{{/operations}}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "{{#lambda.lowercase}}{{gitUserId}}/{{gitRepoId}}{{/lambda.lowercase}}",
3+
"description": "{{description}}",
4+
"license": "unlicense",
5+
"version": "{{artifactVersion}}",
6+
"type": "project",
7+
"require": {
8+
"php": "^8.0",
9+
"ext-yaml": "^2.2",
10+
"mezzio/mezzio": "^3.3",
11+
"laminas/laminas-diactoros": "^2.5",
12+
"articus/path-handler": "^0.7",
13+
"articus/data-transfer": "^0.5",
14+
"articus/openapi-generator-common": "^0.2",
15+
"psr/simple-cache": "^1.0",
16+
"laminas/laminas-config": "^3.4",
17+
"laminas/laminas-stdlib": "^3.3",
18+
"laminas/laminas-validator": "^2.14",
19+
"nikic/fast-route": "^1.3"
20+
},
21+
"autoload": {
22+
"psr-4": {
23+
"": "src/"
24+
}
25+
}
26+
}

samples/openapi3/server/petstore/php-mezzio-ph/application/config.yml renamed to modules/openapi-generator/src/main/resources/php-mezzio-ph-modern/config.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
# router:
1010
# cache:
1111
# directory: ./data/cache/PathHandler
12-
# #Enable handler metadata cache
13-
# metadata:
14-
# cache:
15-
# directory: ./data/cache/PathHandler
12+
13+
#Enable handler metadata cache
14+
#Articus\PathHandler\MetadataProvider\PhpAttribute:
15+
# cache:
16+
# directory: ./data/cache/PathHandler
1617

1718
#Enable data transfer metadata cache for DTOs
18-
#Articus\DataTransfer\MetadataProvider\Annotation:
19+
#Articus\DataTransfer\MetadataProvider\PhpAttribute:
1920
# cache:
2021
# directory: ./data/cache/DataTransfer

samples/openapi3/server/petstore/php-mezzio-ph/application/container.php renamed to modules/openapi-generator/src/main/resources/php-mezzio-ph-modern/container.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
//Use Composer autoload that includes code both from ../src and ../vendor
77
require __DIR__ . '/../vendor/autoload.php';
88

9-
//Register Doctrine annotation autoload
10-
\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists');
11-
129
//Path to file for caching full configuration
1310
const CONFIG_CACHE_PATH = __DIR__ . '/../data/cache/config.php';
1411

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
dependencies:
2+
factories:
3+
Articus\DataTransfer\Service: Articus\DataTransfer\Factory
4+
Articus\DataTransfer\MetadataProvider\PhpAttribute: Articus\DataTransfer\MetadataProvider\Factory\PhpAttribute
5+
Articus\DataTransfer\Strategy\PluginManager: Articus\DataTransfer\Strategy\Factory\PluginManager
6+
Articus\DataTransfer\Validator\PluginManager: Articus\DataTransfer\Validator\Factory\PluginManager
7+
Laminas\Validator\ValidatorPluginManager: Laminas\Validator\ValidatorPluginManagerFactory
8+
aliases:
9+
Articus\DataTransfer\ClassMetadataProviderInterface: Articus\DataTransfer\MetadataProvider\PhpAttribute
10+
Articus\DataTransfer\FieldMetadataProviderInterface: Articus\DataTransfer\MetadataProvider\PhpAttribute
11+
12+
Articus\DataTransfer\Strategy\PluginManager:
13+
invokables:
14+
QueryStringScalar: OpenAPIGenerator\Common\Strategy\QueryStringScalar
15+
QueryStringScalarArray: OpenAPIGenerator\Common\Strategy\QueryStringScalarArray
16+
factories:
17+
Date: OpenAPIGenerator\Common\Strategy\Factory\MutableDate
18+
DateTime: OpenAPIGenerator\Common\Strategy\Factory\MutableDateTime
19+
ObjectList: OpenAPIGenerator\Common\Strategy\Factory\NoArgObjectList
20+
ObjectMap: OpenAPIGenerator\Common\Strategy\Factory\NoArgObjectMap
21+
ScalarList: OpenAPIGenerator\Common\Strategy\Factory\ScalarList
22+
ScalarMap: OpenAPIGenerator\Common\Strategy\Factory\ScalarMap
23+
24+
Articus\DataTransfer\Validator\PluginManager:
25+
invokables:
26+
Scalar: OpenAPIGenerator\Common\Validator\Scalar
27+
QueryStringScalar: OpenAPIGenerator\Common\Validator\QueryStringScalar
28+
QueryStringScalarArray: OpenAPIGenerator\Common\Validator\QueryStringScalarArray
29+
abstract_factories:
30+
- Articus\DataTransfer\Validator\Factory\Laminas
31+
32+
validators:
33+
invokables:
34+
Count: Laminas\Validator\IsCountable
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{{#items
2+
}}{{^isContainer
3+
}}{{#isPrimitiveType}}"{{dataType}}"{{/isPrimitiveType
4+
}}{{^isPrimitiveType}}{{dataType}}::class{{/isPrimitiveType
5+
}}{{/isContainer
6+
}}{{#isContainer
7+
}}{{dataType}}::class{{/isContainer
8+
}}{{/items}}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{{#additionalProperties
2+
}}{{^isContainer
3+
}}{{#isPrimitiveType}}"{{dataType}}"{{/isPrimitiveType
4+
}}{{^isPrimitiveType}}{{dataType}}::class{{/isPrimitiveType
5+
}}{{/isContainer
6+
}}{{#isContainer
7+
}}{{dataType}}::class{{/isContainer
8+
}}{{/additionalProperties}}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
declare(strict_types=1);
3+
{{#models}}{{#model}}
4+
namespace {{package}};
5+
6+
use Articus\DataTransfer\PhpAttribute as DTA;
7+
8+
{{#vendorExtensions
9+
}}{{#internal.ze-ph.fromContainer}}{{>model_container}}{{/internal.ze-ph.fromContainer
10+
}}{{^internal.ze-ph.fromContainer}}{{>model_object}}{{/internal.ze-ph.fromContainer
11+
}}{{/vendorExtensions
12+
}}{{^vendorExtensions}}{{>model_object}}{{/vendorExtensions
13+
}}{{/model}}{{/models}}

0 commit comments

Comments
 (0)