Skip to content

Commit

Permalink
[php-mezzio-ph] PHP 8 support (OpenAPITools#9445)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Arthur Mogliev authored May 21, 2021
1 parent 4948ce7 commit ab6d661
Show file tree
Hide file tree
Showing 190 changed files with 2,967 additions and 1,221 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ samples/client/petstore/python-tornado/.venv/
# PHP
samples/client/petstore/php/OpenAPIClient-php/composer.lock
samples/openapi3/server/petstore/php-symfony/SymfonyBundle-php/composer.lock
samples/openapi3/server/petstore/php-mezzio-ph/composer.lock
samples/server/petstore/php-laravel/lib/composer.lock
samples/server/petstore/php-lumen/lib/composer.lock
samples/server/petstore/php-slim4/composer.lock
Expand Down
6 changes: 6 additions & 0 deletions bin/configs/php-mezzio-ph-modern.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
generatorName: php-mezzio-ph
outputDir: samples/server/petstore/php-mezzio-ph-modern
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/php-mezzio-ph-modern
additionalProperties:
modern: "true"
1 change: 1 addition & 0 deletions docs/generators/php-mezzio-ph.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|invokerPackage|The main namespace to use for all classes. e.g. Yay\Pets| |null|
|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|
|modelPackage|package for generated models| |null|
|modern|use modern language features (generated code will require PHP 8.0)| |false|
|packageName|The main package name for classes. e.g. GeneratedPetstore| |null|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);

namespace {{invokerPackage}};

use {{invokerPackage}}\Middleware;
use Interop\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Mezzio\Application;
use Mezzio\Handler\NotFoundHandler;
use Mezzio\MiddlewareFactory;
use Mezzio\Router\Middleware\DispatchMiddleware;
use Mezzio\Router\Middleware\MethodNotAllowedMiddleware;
use Mezzio\Router\Middleware\RouteMiddleware;
use Mezzio\Router\RouteCollector;
use Laminas\HttpHandlerRunner\Emitter\EmitterInterface;
use Laminas\HttpHandlerRunner\RequestHandlerRunner;
use Laminas\ServiceManager\Factory\FactoryInterface;
use Laminas\Stratigility\MiddlewarePipe;

class Factory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null): Application
{
$errorMiddleware = self::getErrorMiddleware($container);
$pipeline = new MiddlewarePipe();
$runner = new RequestHandlerRunner(
$pipeline,
$container->get(EmitterInterface::class),
$container->get(ServerRequestInterface::class),
static fn(\Throwable $error): ResponseInterface => $errorMiddleware->handleError($error)
);
$application = new Application(
$container->get(MiddlewareFactory::class),
$pipeline,
$container->get(RouteCollector::class),
$runner
);
$application->pipe($errorMiddleware);
$application->pipe(RouteMiddleware::class);
$application->pipe(MethodNotAllowedMiddleware::class);
$application->pipe(DispatchMiddleware::class);
$application->pipe(NotFoundHandler::class);
return $application;
}

protected static function getErrorMiddleware(ContainerInterface $container): Middleware\InternalServerError
{
return $container->get(Middleware\InternalServerError::class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);

namespace {{invokerPackage}}\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Laminas\Stdlib\ErrorHandler;

class InternalServerError implements MiddlewareInterface
{
public function __construct(protected \Closure $responseGenerator)
{
}

/**
* @inheritdoc
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$result = null;
try {
ErrorHandler::start();
$result = $handler->handle($request);
ErrorHandler::stop(true);
}
catch (\Throwable $error) {
$result = $this->handleError($error);
}
return $result;
}

public function handleError(\Throwable $error): ResponseInterface
{
\error_log((string)$error);
return $this->generateEmptyResponse()->withStatus(500, 'Internal server error');
}

protected function generateEmptyResponse(): ResponseInterface
{
return ($this->responseGenerator)();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# OpenAPI generated server

Generated by the [OpenAPI Generator](https://openapi-generator.tech) project.

## Overview
This server stub aims to provide light, yet comprehensive structure for your API project using:

- PHP: >=8.0
- [Laminas Mezzio](https://docs.mezzio.dev/mezzio/): >=3.3
- [Path Handler](https://github.com/Articus/PathHandler): >=0.7

## How to use
All you have to do to start development is:

- 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`)
- create cache folder: `mkdir -p ./data/cache` (you will need it later for configuration and metadata caches - check comments in `./application/config.yml`)
- 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`)

After that you should be able to call all methods from your API spec. Most of the negative scenarios should be handled:

- `404 Not found` for unknown routes
- `406 Not acceptable` for invalid `Accept` header
- `415 Unsupported media type` for invalid `Content-Type` header
- `400 Malformed JSON` for unparsable JSON body
- `422 Unprocessable entity` for parsable JSON body that fails validation

But for obvious reason you will not get any `200 OK`, only `501 Not implemented`. So your next steps are:

- check all TODOs left in the stub code where generator was not smart enough and could not guarantee correct implementation
- implement your API security mechanism (either special attribute or separate middleware) - generator does not do anything about it yet
- implement your handlers - the most tricky part :)

## Enjoy!
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).
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);

namespace {{package}};

use Articus\PathHandler\PhpAttribute as PHA;
use Articus\PathHandler\Consumer as PHConsumer;
use Articus\PathHandler\Producer as PHProducer;
use Articus\PathHandler\Attribute as PHAttribute;
use Articus\PathHandler\Exception as PHException;
use Psr\Http\Message\ServerRequestInterface;

{{#operations}}
{{#description}}
/**
* {{&description}}
*/
{{/description}}
#[PHA\Route("{{pathPattern}}")]
class {{classname}}
{
{{#operation}}
/**
{{#summary}}
* {{summary}}
{{/summary}}
{{#description}}
* {{description}}
{{/description}}
*/
#[PHA\{{httpMethod}}]
{{#vendorExtensions}}
{{#internal.ze-ph.hasQueryData}}
#[PHA\Attribute(PHAttribute\Transfer::class, [
"type" => {{internal.ze-ph.queryDataType}}::class,
"objectAttr" => "queryData",
"source" => PHAttribute\Transfer::SOURCE_GET
])]
{{/internal.ze-ph.hasQueryData}}
{{/vendorExtensions}}
{{#bodyParam}}
{{#consumes}}
// TODO check if consumer is valid, if it has correct priority and if it can be moved to class annotation
#[PHA\Consumer("{{{mediaType}}}", PHConsumer\Json::class)]
{{/consumes}}
{{^isPrimitiveType}}
#[PHA\Attribute(PHAttribute\Transfer::class, ["type" => {{dataType}}::class, "objectAttr" => "bodyData"])]
{{/isPrimitiveType}}
{{/bodyParam}}
{{#produces}}
// TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation
#[PHA\Producer("{{{mediaType}}}", PHProducer\Transfer::class)]
{{/produces}}
public function {{operationId}}(ServerRequestInterface $request){{#returnType}}: {{returnType}}{{/returnType}}
{
//TODO implement method
{{#vendorExtensions}}
{{#internal.ze-ph.hasQueryData}}
/** @var {{internal.ze-ph.queryDataType}} $queryData */
$queryData = $request->getAttribute("queryData");
{{/internal.ze-ph.hasQueryData}}
{{/vendorExtensions}}
{{#bodyParam}}
{{^isPrimitiveType}}
/** @var {{dataType}} $bodyData */
$bodyData = $request->getAttribute("bodyData");
{{/isPrimitiveType}}
{{/bodyParam}}
throw new PHException\HttpCode(501, "Not implemented");
}
{{/operation}}
}
{{/operations}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "{{#lambda.lowercase}}{{gitUserId}}/{{gitRepoId}}{{/lambda.lowercase}}",
"description": "{{description}}",
"license": "unlicense",
"version": "{{artifactVersion}}",
"type": "project",
"require": {
"php": "^8.0",
"ext-yaml": "^2.2",
"mezzio/mezzio": "^3.3",
"laminas/laminas-diactoros": "^2.5",
"articus/path-handler": "^0.7",
"articus/data-transfer": "^0.5",
"articus/openapi-generator-common": "^0.2",
"psr/simple-cache": "^1.0",
"laminas/laminas-config": "^3.4",
"laminas/laminas-stdlib": "^3.3",
"laminas/laminas-validator": "^2.14",
"nikic/fast-route": "^1.3"
},
"autoload": {
"psr-4": {
"": "src/"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
# router:
# cache:
# directory: ./data/cache/PathHandler
# #Enable handler metadata cache
# metadata:
# cache:
# directory: ./data/cache/PathHandler

#Enable handler metadata cache
#Articus\PathHandler\MetadataProvider\PhpAttribute:
# cache:
# directory: ./data/cache/PathHandler

#Enable data transfer metadata cache for DTOs
#Articus\DataTransfer\MetadataProvider\Annotation:
#Articus\DataTransfer\MetadataProvider\PhpAttribute:
# cache:
# directory: ./data/cache/DataTransfer
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
//Use Composer autoload that includes code both from ../src and ../vendor
require __DIR__ . '/../vendor/autoload.php';

//Register Doctrine annotation autoload
\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists');

//Path to file for caching full configuration
const CONFIG_CACHE_PATH = __DIR__ . '/../data/cache/config.php';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
dependencies:
factories:
Articus\DataTransfer\Service: Articus\DataTransfer\Factory
Articus\DataTransfer\MetadataProvider\PhpAttribute: Articus\DataTransfer\MetadataProvider\Factory\PhpAttribute
Articus\DataTransfer\Strategy\PluginManager: Articus\DataTransfer\Strategy\Factory\PluginManager
Articus\DataTransfer\Validator\PluginManager: Articus\DataTransfer\Validator\Factory\PluginManager
Laminas\Validator\ValidatorPluginManager: Laminas\Validator\ValidatorPluginManagerFactory
aliases:
Articus\DataTransfer\ClassMetadataProviderInterface: Articus\DataTransfer\MetadataProvider\PhpAttribute
Articus\DataTransfer\FieldMetadataProviderInterface: Articus\DataTransfer\MetadataProvider\PhpAttribute

Articus\DataTransfer\Strategy\PluginManager:
invokables:
QueryStringScalar: OpenAPIGenerator\Common\Strategy\QueryStringScalar
QueryStringScalarArray: OpenAPIGenerator\Common\Strategy\QueryStringScalarArray
factories:
Date: OpenAPIGenerator\Common\Strategy\Factory\MutableDate
DateTime: OpenAPIGenerator\Common\Strategy\Factory\MutableDateTime
ObjectList: OpenAPIGenerator\Common\Strategy\Factory\NoArgObjectList
ObjectMap: OpenAPIGenerator\Common\Strategy\Factory\NoArgObjectMap
ScalarList: OpenAPIGenerator\Common\Strategy\Factory\ScalarList
ScalarMap: OpenAPIGenerator\Common\Strategy\Factory\ScalarMap

Articus\DataTransfer\Validator\PluginManager:
invokables:
Scalar: OpenAPIGenerator\Common\Validator\Scalar
QueryStringScalar: OpenAPIGenerator\Common\Validator\QueryStringScalar
QueryStringScalarArray: OpenAPIGenerator\Common\Validator\QueryStringScalarArray
abstract_factories:
- Articus\DataTransfer\Validator\Factory\Laminas

validators:
invokables:
Count: Laminas\Validator\IsCountable
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{{#items
}}{{^isContainer
}}{{#isPrimitiveType}}"{{dataType}}"{{/isPrimitiveType
}}{{^isPrimitiveType}}{{dataType}}::class{{/isPrimitiveType
}}{{/isContainer
}}{{#isContainer
}}{{dataType}}::class{{/isContainer
}}{{/items}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{{#additionalProperties
}}{{^isContainer
}}{{#isPrimitiveType}}"{{dataType}}"{{/isPrimitiveType
}}{{^isPrimitiveType}}{{dataType}}::class{{/isPrimitiveType
}}{{/isContainer
}}{{#isContainer
}}{{dataType}}::class{{/isContainer
}}{{/additionalProperties}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
{{#models}}{{#model}}
namespace {{package}};

use Articus\DataTransfer\PhpAttribute as DTA;

{{#vendorExtensions
}}{{#internal.ze-ph.fromContainer}}{{>model_container}}{{/internal.ze-ph.fromContainer
}}{{^internal.ze-ph.fromContainer}}{{>model_object}}{{/internal.ze-ph.fromContainer
}}{{/vendorExtensions
}}{{^vendorExtensions}}{{>model_object}}{{/vendorExtensions
}}{{/model}}{{/models}}
Loading

0 comments on commit ab6d661

Please sign in to comment.