mirror of
https://github.com/OpenAPITools/openapi-generator.git
synced 2025-12-18 21:47:04 +00:00
[PHP][php-ze-ph] Support for PHP 7.1+, Zend Expressive 3.2 and PathHander 0.4 (#1902)
* - support for PHP 7.1, Zend Expressive 3.2 and PathHander 0.4 for php-ze-ph generator * - fixed mess with petstore samples (added new files, removed obsolete files) * php-ze-ph: - overwriting "*/*" media type for producers with "n/a" (PathHandler does not support that cause it makes no sense to return response with "Content-Type: */*") - "array" return type declaration for handler methods with ambiguous "container" return type - better way to generate attribute annotation stub for request body data with ambiguous "container" type - fixed missing dependency in composer.json - minor optimization for container.php - samples for OAS3 petstore spec * php-ze-ph: - note about ext-yaml in stub README - updated .gitignore * php-ze-ph: - logging '*/*' replacement as warning
This commit is contained in:
committed by
William Cheng
parent
c871e3bc81
commit
77d2de4e3d
@@ -26,11 +26,14 @@ import io.swagger.v3.oas.models.parameters.Parameter;
|
||||
import io.swagger.v3.oas.models.parameters.QueryParameter;
|
||||
import org.openapitools.codegen.*;
|
||||
import org.openapitools.codegen.utils.ModelUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class PhpZendExpressivePathHandlerServerCodegen extends AbstractPhpCodegen {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PhpZendExpressivePathHandlerServerCodegen.class);
|
||||
|
||||
public static final String VEN_FROM_QUERY = "internal.ze-ph.fromQuery";
|
||||
public static final String VEN_COLLECTION_FORMAT = "internal.ze-ph.collectionFormat";
|
||||
@@ -78,7 +81,8 @@ public class PhpZendExpressivePathHandlerServerCodegen extends AbstractPhpCodege
|
||||
supportingFiles.add(new SupportingFile("app.yml.mustache", "application" + File.separator + "config", "app.yml"));
|
||||
supportingFiles.add(new SupportingFile("path_handler.yml.mustache", "application" + File.separator + "config", "path_handler.yml"));
|
||||
supportingFiles.add(new SupportingFile("data_transfer.yml.mustache", "application" + File.separator + "config", "data_transfer.yml"));
|
||||
supportingFiles.add(new SupportingFile("ErrorMiddleware.php.mustache", srcBasePath, "ErrorMiddleware.php"));
|
||||
supportingFiles.add(new SupportingFile("Factory.php.mustache", srcBasePath, "Factory.php"));
|
||||
supportingFiles.add(new SupportingFile("InternalServerError.php.mustache", srcBasePath + File.separator + "Middleware", "InternalServerError.php"));
|
||||
supportingFiles.add(new SupportingFile("Date.php.mustache", srcBasePath + File.separator + "Strategy", "Date.php"));
|
||||
supportingFiles.add(new SupportingFile("DateTime.php.mustache", srcBasePath + File.separator + "Strategy", "DateTime.php"));
|
||||
supportingFiles.add(new SupportingFile("QueryParameter.php.mustache", srcBasePath + File.separator + "Strategy", "QueryParameter.php"));
|
||||
@@ -291,46 +295,42 @@ public class PhpZendExpressivePathHandlerServerCodegen extends AbstractPhpCodege
|
||||
objs = super.postProcessOperationsWithModels(objs, allModels);
|
||||
Map<String, Object> operations = (Map<String, Object>) objs.get("operations");
|
||||
List<CodegenOperation> operationList = (List<CodegenOperation>) operations.get("operation");
|
||||
String interfaceToImplement;
|
||||
StringBuilder interfacesToImplement = new StringBuilder();
|
||||
String classMethod;
|
||||
String httpMethodDeclaration;
|
||||
String pathPattern = null;
|
||||
for (CodegenOperation op : operationList) {
|
||||
switch (op.httpMethod) {
|
||||
case "GET":
|
||||
interfaceToImplement = "Operation\\GetInterface";
|
||||
classMethod = "handleGet";
|
||||
httpMethodDeclaration = "Get()";
|
||||
break;
|
||||
case "POST":
|
||||
interfaceToImplement = "Operation\\PostInterface";
|
||||
classMethod = "handlePost";
|
||||
httpMethodDeclaration = "Post()";
|
||||
break;
|
||||
case "PATCH":
|
||||
interfaceToImplement = "Operation\\PatchInterface";
|
||||
classMethod = "handlePatch";
|
||||
httpMethodDeclaration = "Patch()";
|
||||
break;
|
||||
case "PUT":
|
||||
interfaceToImplement = "Operation\\PutInterface";
|
||||
classMethod = "handlePut";
|
||||
httpMethodDeclaration = "Put()";
|
||||
break;
|
||||
case "DELETE":
|
||||
interfaceToImplement = "Operation\\DeleteInterface";
|
||||
classMethod = "handleDelete";
|
||||
httpMethodDeclaration = "Delete()";
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unknown HTTP Method " + op.httpMethod + " not allowed");
|
||||
httpMethodDeclaration = "HttpMethod(\"" + op.httpMethod + "\")";
|
||||
}
|
||||
if (interfacesToImplement.length() > 0) {
|
||||
interfacesToImplement.append(", ");
|
||||
op.httpMethod = httpMethodDeclaration;
|
||||
//Producing content with media type "*/*" is not supported
|
||||
if (op.produces != null) {
|
||||
for (Map<String, String> p: op.produces) {
|
||||
if (p.replace("mediaType", "*/*", "n/a")) {
|
||||
LOGGER.warn("Media type range '*/*' is not supported, using 'n/a' for code generation instead");
|
||||
}
|
||||
}
|
||||
}
|
||||
interfacesToImplement.append(interfaceToImplement);
|
||||
op.httpMethod = classMethod;
|
||||
//All operations have same path because of custom operation grouping, so path pattern can be calculated only once
|
||||
if (pathPattern == null) {
|
||||
pathPattern = generatePathPattern(op);
|
||||
}
|
||||
}
|
||||
operations.put("interfacesToImplement", interfacesToImplement.toString());
|
||||
operations.put("pathPattern", pathPattern);
|
||||
|
||||
return objs;
|
||||
|
||||
@@ -1,26 +1,9 @@
|
||||
# ref: https://github.com/github/gitignore/blob/master/ZendFramework.gitignore
|
||||
#based on .gitignore generated by https://github.com/zendframework/zend-expressive-skeleton
|
||||
.idea
|
||||
|
||||
# Composer files
|
||||
composer.phar
|
||||
vendor/
|
||||
|
||||
# Local configs
|
||||
config/autoload/*.local.php
|
||||
|
||||
# Binary gettext files
|
||||
*.mo
|
||||
|
||||
# Data
|
||||
data/logs/
|
||||
# Default cache folder
|
||||
data/cache/
|
||||
data/sessions/
|
||||
data/tmp/
|
||||
temp/
|
||||
|
||||
#Doctrine 2
|
||||
data/DoctrineORMModule/Proxy/
|
||||
data/DoctrineORMModule/cache/
|
||||
|
||||
# Legacy ZF1
|
||||
demos/
|
||||
extras/documentation
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{invokerPackage}}\Strategy;
|
||||
|
||||
@@ -7,8 +8,7 @@ class Date extends DateTime
|
||||
const DATE_TIME_FORMAT = 'Y-m-d';
|
||||
|
||||
/**
|
||||
* @param $arrayValue
|
||||
* @return \DateTime
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function parseDateString($arrayValue)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{invokerPackage}}\Strategy;
|
||||
|
||||
@@ -9,7 +10,7 @@ class DateTime implements StrategyInterface
|
||||
const DATE_TIME_FORMAT = \DateTime::RFC3339;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function extract($objectValue, $object = null)
|
||||
{
|
||||
@@ -21,7 +22,7 @@ class DateTime implements StrategyInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function hydrate($arrayValue, $objectValue, array $array = null)
|
||||
{
|
||||
@@ -37,7 +38,7 @@ class DateTime implements StrategyInterface
|
||||
|
||||
/**
|
||||
* @param $arrayValue
|
||||
* @return \DateTime
|
||||
* @return bool|\DateTime
|
||||
*/
|
||||
protected function parseDateString($arrayValue)
|
||||
{
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace {{invokerPackage}};
|
||||
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Zend\Stdlib\ErrorHandler;
|
||||
|
||||
class ErrorMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function process(Request $request, DelegateInterface $delegate)
|
||||
{
|
||||
$result = null;
|
||||
try {
|
||||
ErrorHandler::start();
|
||||
$result = $delegate->process($request);
|
||||
ErrorHandler::stop(true);
|
||||
if (!($result instanceof Response)) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Invalid response: expecting %s, got %s',
|
||||
Response::class,
|
||||
is_object($result)? get_class($result) : gettype($result)
|
||||
));
|
||||
}
|
||||
}
|
||||
catch (\Exception $error) {
|
||||
$result = (new \Zend\Diactoros\Response())->withStatus(500, 'Internal server error');
|
||||
error_log((string)$error);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?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 Zend\Expressive\Application;
|
||||
use Zend\Expressive\Handler\NotFoundHandler;
|
||||
use Zend\Expressive\MiddlewareFactory;
|
||||
use Zend\Expressive\Router\Middleware\DispatchMiddleware;
|
||||
use Zend\Expressive\Router\Middleware\MethodNotAllowedMiddleware;
|
||||
use Zend\Expressive\Router\Middleware\RouteMiddleware;
|
||||
use Zend\Expressive\Router\RouteCollector;
|
||||
use Zend\HttpHandlerRunner\Emitter\EmitterInterface;
|
||||
use Zend\HttpHandlerRunner\RequestHandlerRunner;
|
||||
use Zend\ServiceManager\Factory\FactoryInterface;
|
||||
use Zend\Stratigility\MiddlewarePipe;
|
||||
|
||||
class Factory implements FactoryInterface
|
||||
{
|
||||
public function __invoke(ContainerInterface $container, $requestedName, array $options = null): Application
|
||||
{
|
||||
$errorMiddleware = $container->get(Middleware\InternalServerError::class);
|
||||
if (!($errorMiddleware instanceof Middleware\InternalServerError)) {
|
||||
throw new \LogicException(\sprintf(
|
||||
'Invalid error middleware: expecting %s, not %s.',
|
||||
Middleware\InternalServerError::class,
|
||||
\is_object($errorMiddleware) ? \get_class($errorMiddleware) : \gettype($errorMiddleware)
|
||||
));
|
||||
}
|
||||
$pipeline = new MiddlewarePipe();
|
||||
$runner = new RequestHandlerRunner(
|
||||
$pipeline,
|
||||
$container->get(EmitterInterface::class),
|
||||
$container->get(ServerRequestInterface::class),
|
||||
function(\Throwable $error) use ($errorMiddleware) : ResponseInterface
|
||||
{
|
||||
return $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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?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 Zend\Stdlib\ErrorHandler;
|
||||
|
||||
class InternalServerError implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
protected $responseGenerator;
|
||||
|
||||
/**
|
||||
* @param callable $responseGenerator
|
||||
*/
|
||||
public function __construct(callable $responseGenerator)
|
||||
{
|
||||
$this->responseGenerator = $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)();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{invokerPackage}}\Strategy;
|
||||
|
||||
@@ -31,7 +32,7 @@ class QueryParameter implements StrategyInterface
|
||||
if (empty($options['type'])) {
|
||||
throw new \InvalidArgumentException('Option "type" is required.');
|
||||
} elseif (!isset(self::TYPE_MAP[$options['type']])) {
|
||||
throw new \InvalidArgumentException(sprintf('Unknown type "%s".', $options['type']));
|
||||
throw new \InvalidArgumentException(\sprintf('Unknown type "%s".', $options['type']));
|
||||
}
|
||||
$this->type = $options['type'];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{invokerPackage}}\Strategy;
|
||||
|
||||
@@ -28,8 +29,8 @@ class QueryParameterArray extends QueryParameter
|
||||
parent::__construct($options);
|
||||
if (empty($options['format'])) {
|
||||
throw new \InvalidArgumentException('Option "format" is required.');
|
||||
} elseif (!array_key_exists($options['format'], self::DELIMITER_MAP)) {
|
||||
throw new \InvalidArgumentException(sprintf('Unknown format "%s".', $options['format']));
|
||||
} elseif (!\array_key_exists($options['format'], self::DELIMITER_MAP)) {
|
||||
throw new \InvalidArgumentException(\sprintf('Unknown format "%s".', $options['format']));
|
||||
}
|
||||
$this->delimiter = self::DELIMITER_MAP[$options['format']];
|
||||
}
|
||||
@@ -40,11 +41,11 @@ class QueryParameterArray extends QueryParameter
|
||||
public function extract($objectValue, $object = null)
|
||||
{
|
||||
$result = null;
|
||||
if (is_array($objectValue)) {
|
||||
if (\is_array($objectValue)) {
|
||||
if ($this->delimiter === null) {
|
||||
$result = $objectValue;
|
||||
} else {
|
||||
$result = implode($this->delimiter, $objectValue);
|
||||
$result = \implode($this->delimiter, $objectValue);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
@@ -59,9 +60,9 @@ class QueryParameterArray extends QueryParameter
|
||||
if ($arrayValue !== null) {
|
||||
$list = null;
|
||||
if ($this->delimiter === null) {
|
||||
$list = (is_array($arrayValue))? $arrayValue : [$arrayValue];
|
||||
$list = (\is_array($arrayValue))? $arrayValue : [$arrayValue];
|
||||
} else {
|
||||
$list = explode($this->delimiter, $arrayValue);
|
||||
$list = \explode($this->delimiter, $arrayValue);
|
||||
}
|
||||
$result = [];
|
||||
foreach ($list as $item) {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{invokerPackage}}\Validator;
|
||||
|
||||
use App\Strategy\QueryParameterArray;
|
||||
@@ -6,14 +8,14 @@ use App\Strategy\QueryParameterArray;
|
||||
class QueryParameterArrayType extends QueryParameterType
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @var null|string
|
||||
*/
|
||||
protected $format;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFormat()
|
||||
public function getFormat(): ?string
|
||||
{
|
||||
return $this->format;
|
||||
}
|
||||
@@ -22,21 +24,21 @@ class QueryParameterArrayType extends QueryParameterType
|
||||
* @param string $format
|
||||
* @return self
|
||||
*/
|
||||
public function setFormat($format)
|
||||
public function setFormat(string $format): self
|
||||
{
|
||||
$this->format = $format;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function checkType($value)
|
||||
protected function checkType($value): bool
|
||||
{
|
||||
$result = true;
|
||||
if (!array_key_exists($this->format, QueryParameterArray::DELIMITER_MAP)) {
|
||||
throw new \InvalidArgumentException(sprintf('Can not check for format %s.', $this->format));
|
||||
if (!\array_key_exists($this->format, QueryParameterArray::DELIMITER_MAP)) {
|
||||
throw new \InvalidArgumentException(\sprintf('Can not check for format %s.', $this->format));
|
||||
}
|
||||
$delimiter = QueryParameterArray::DELIMITER_MAP[$this->format];
|
||||
if ($delimiter === null) {
|
||||
if (is_array($value)) {
|
||||
if (\is_array($value)) {
|
||||
foreach ($value as $item) {
|
||||
$result = $result && parent::checkType($item);
|
||||
}
|
||||
@@ -46,27 +48,27 @@ class QueryParameterArrayType extends QueryParameterType
|
||||
} else {
|
||||
switch ($this->type) {
|
||||
case QueryParameterArray::TYPE_INT:
|
||||
$result = is_string($value) && preg_match(self::prepareRepeatingTypeRegExp(self::RE_INT, $delimiter), $value);
|
||||
$result = \is_string($value) && \preg_match(self::prepareRepeatingTypeRegExp(self::RE_INT, $delimiter), $value);
|
||||
break;
|
||||
case QueryParameterArray::TYPE_BOOL:
|
||||
$result = is_string($value) && preg_match(self::prepareRepeatingTypeRegExp(self::RE_BOOL, $delimiter), $value);
|
||||
$result = \is_string($value) && \preg_match(self::prepareRepeatingTypeRegExp(self::RE_BOOL, $delimiter), $value);
|
||||
break;
|
||||
case QueryParameterArray::TYPE_FLOAT:
|
||||
$result = is_string($value) && preg_match(self::prepareRepeatingTypeRegExp(self::RE_FLOAT, $delimiter), $value);
|
||||
$result = \is_string($value) && \preg_match(self::prepareRepeatingTypeRegExp(self::RE_FLOAT, $delimiter), $value);
|
||||
break;
|
||||
case QueryParameterArray::TYPE_STRING:
|
||||
$result = is_string($value);
|
||||
$result = \is_string($value);
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException(sprintf('Can not check for type %s.', $this->type));
|
||||
throw new \InvalidArgumentException(\sprintf('Can not check for type %s.', $this->type));
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected static function prepareRepeatingTypeRegExp($typeRegExp, $delimiter)
|
||||
protected static function prepareRepeatingTypeRegExp(string $typeRegExp, string $delimiter): string
|
||||
{
|
||||
$escapedDelimiter = preg_quote($delimiter, '/');
|
||||
$escapedDelimiter = \preg_quote($delimiter, '/');
|
||||
return '/^(' . $typeRegExp . ')(' . $escapedDelimiter . '('. $typeRegExp . '))*$/';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{invokerPackage}}\Validator;
|
||||
|
||||
use App\Strategy\QueryParameter;
|
||||
@@ -9,19 +11,19 @@ class QueryParameterType extends Type
|
||||
const RE_BOOL = 'true|false';
|
||||
const RE_FLOAT = '0(\.\d+)?|-?[1-9]\d*(\.\d+)?|-0\.\d+';
|
||||
|
||||
protected function checkType($value)
|
||||
protected function checkType($value): bool
|
||||
{
|
||||
switch ($this->type) {
|
||||
case QueryParameter::TYPE_INT:
|
||||
return is_string($value) && preg_match('/^(' . self::RE_INT . ')$/', $value);
|
||||
return \is_string($value) && \preg_match('/^(' . self::RE_INT . ')$/', $value);
|
||||
case QueryParameter::TYPE_BOOL:
|
||||
return is_string($value) && preg_match('/^(' . self::RE_BOOL . ')$/', $value);
|
||||
return \is_string($value) && \preg_match('/^(' . self::RE_BOOL . ')$/', $value);
|
||||
case QueryParameter::TYPE_FLOAT:
|
||||
return is_string($value) && preg_match('/^(' . self::RE_FLOAT . ')$/', $value);
|
||||
return \is_string($value) && \preg_match('/^(' . self::RE_FLOAT . ')$/', $value);
|
||||
case QueryParameter::TYPE_STRING:
|
||||
return is_string($value);
|
||||
return \is_string($value);
|
||||
default:
|
||||
throw new \InvalidArgumentException(sprintf('Can not check for type %s.', $this->type));
|
||||
throw new \InvalidArgumentException(\sprintf('Can not check for type %s.', $this->type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ 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: 5.6 or 7.*
|
||||
- [Zend Expressive](https://zendframework.github.io/zend-expressive): 2.1
|
||||
- [Path Handler](https://github.com/Articus/PathHandler): 0.3
|
||||
- PHP: >=7.1
|
||||
- [Zend Expressive](https://zendframework.github.io/zend-expressive): >=3.2
|
||||
- [Path Handler](https://github.com/Articus/PathHandler): >=0.4
|
||||
|
||||
## How to use
|
||||
All you have to do to start development is:
|
||||
|
||||
- install dependencies via [Composer](https://getcomposer.org/)
|
||||
- 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/ZendCache` (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`)
|
||||
|
||||
@@ -24,11 +24,11 @@ After that you should be able to call all methods from your API spec. Most of th
|
||||
- `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 `500 Not implemented`. So your next steps are:
|
||||
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 `ze-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).
|
||||
Hopefully this stub will reduce the amount of boilerplate code you have to write manually. If you have any suggestions or questions about `php-ze-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).
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{invokerPackage}}\Validator;
|
||||
|
||||
use Zend\Validator\AbstractValidator;
|
||||
@@ -18,14 +20,14 @@ class Type extends AbstractValidator
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var null|string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
public function getType(): ?string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
@@ -34,14 +36,14 @@ class Type extends AbstractValidator
|
||||
* @param string $type
|
||||
* @return self
|
||||
*/
|
||||
public function setType($type)
|
||||
public function setType(string $type): self
|
||||
{
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function isValid($value)
|
||||
{
|
||||
@@ -53,19 +55,19 @@ class Type extends AbstractValidator
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function checkType($value)
|
||||
protected function checkType($value): bool
|
||||
{
|
||||
switch ($this->type) {
|
||||
case 'int':
|
||||
return is_int($value);
|
||||
return \is_int($value);
|
||||
case 'bool':
|
||||
return is_bool($value);
|
||||
return \is_bool($value);
|
||||
case 'float':
|
||||
return is_float($value) || is_int($value);
|
||||
return \is_float($value) || \is_int($value);
|
||||
case 'string':
|
||||
return is_string($value);
|
||||
return \is_string($value);
|
||||
default:
|
||||
throw new \InvalidArgumentException(sprintf('Can not check for type %s.', $this->type));
|
||||
throw new \InvalidArgumentException(\sprintf('Can not check for type %s.', $this->type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{package}};
|
||||
|
||||
use Articus\PathHandler\Operation;
|
||||
use Articus\PathHandler\Annotation as PHA;
|
||||
use Articus\PathHandler\Consumer as PHConsumer;
|
||||
use Articus\PathHandler\Producer as PHProducer;
|
||||
@@ -17,7 +17,7 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
{{/description}}
|
||||
* @PHA\Route(pattern="{{pathPattern}}")
|
||||
*/
|
||||
class {{classname}} implements {{interfacesToImplement}}
|
||||
class {{classname}}
|
||||
{
|
||||
{{#operation}}
|
||||
/**
|
||||
@@ -27,6 +27,7 @@ class {{classname}} implements {{interfacesToImplement}}
|
||||
{{#description}}
|
||||
* {{description}}
|
||||
{{/description}}
|
||||
* @PHA\{{httpMethod}}
|
||||
{{#vendorExtensions}}
|
||||
{{#internal.ze-ph.hasQueryData}}
|
||||
* @PHA\Attribute(name=PHAttribute\Transfer::class, options={
|
||||
@@ -44,8 +45,11 @@ class {{classname}} implements {{interfacesToImplement}}
|
||||
{{^isPrimitiveType}}
|
||||
{{#isContainer}}
|
||||
* TODO check if attribute is valid and can handle your container type
|
||||
* @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":"{{dataType}}","objectAttr":"bodyData"})
|
||||
{{/isContainer}}
|
||||
{{^isContainer}}
|
||||
* @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":{{dataType}}::class,"objectAttr":"bodyData"})
|
||||
{{/isContainer}}
|
||||
{{/isPrimitiveType}}
|
||||
{{/bodyParam}}
|
||||
{{#produces}}
|
||||
@@ -54,13 +58,16 @@ class {{classname}} implements {{interfacesToImplement}}
|
||||
{{/produces}}
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @throws PHException\HttpCode 500 if the method is not implemented
|
||||
* @throws PHException\HttpCode 501 if the method is not implemented
|
||||
{{#returnType}}
|
||||
*
|
||||
{{#returnContainer}}
|
||||
* TODO check if generated return container type is valid
|
||||
{{/returnContainer}}
|
||||
* @return {{returnType}}
|
||||
{{/returnType}}
|
||||
*/
|
||||
public function {{httpMethod}}(ServerRequestInterface $request)
|
||||
public function {{operationId}}(ServerRequestInterface $request){{#returnType}}: {{#returnContainer}}array{{/returnContainer}}{{^returnContainer}}{{returnType}}{{/returnContainer}}{{/returnType}}
|
||||
{
|
||||
//TODO implement method
|
||||
{{#vendorExtensions}}
|
||||
@@ -75,7 +82,7 @@ class {{classname}} implements {{interfacesToImplement}}
|
||||
$bodyData = $request->getAttribute("bodyData");
|
||||
{{/isPrimitiveType}}
|
||||
{{/bodyParam}}
|
||||
throw new PHException\HttpCode(500, "Not implemented");
|
||||
throw new PHException\HttpCode(501, "Not implemented");
|
||||
}
|
||||
{{/operation}}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
dependencies:
|
||||
invokables:
|
||||
Zend\Diactoros\Response\EmitterInterface: Zend\Diactoros\Response\SapiStreamEmitter
|
||||
App\ErrorMiddleware: App\ErrorMiddleware
|
||||
Zend\HttpHandlerRunner\Emitter\EmitterInterface: Zend\HttpHandlerRunner\Emitter\SapiStreamEmitter
|
||||
factories:
|
||||
Zend\Expressive\Application: Zend\Expressive\Container\ApplicationFactory
|
||||
Articus\PathHandler\Middleware: Articus\PathHandler\MiddlewareFactory
|
||||
Zend\Expressive\Application: App\Factory
|
||||
Zend\Expressive\MiddlewareContainer: Zend\Expressive\Container\MiddlewareContainerFactory
|
||||
Zend\Expressive\MiddlewareFactory: Zend\Expressive\Container\MiddlewareFactoryFactory
|
||||
Zend\Expressive\Router\RouteCollector: Zend\Expressive\Router\RouteCollectorFactory
|
||||
Zend\Expressive\Router\RouterInterface: Articus\PathHandler\RouteInjection\Factory
|
||||
|
||||
middleware_pipeline:
|
||||
error:
|
||||
middleware: App\ErrorMiddleware
|
||||
api:
|
||||
middleware: Articus\PathHandler\Middleware
|
||||
path: {{basePathWithoutHost}}
|
||||
Psr\Http\Message\ServerRequestInterface: Zend\Expressive\Container\ServerRequestFactoryFactory
|
||||
Psr\Http\Message\StreamInterface: Zend\Expressive\Container\StreamFactoryFactory
|
||||
Psr\Http\Message\ResponseInterface: Zend\Expressive\Container\ResponseFactoryFactory
|
||||
|
||||
Zend\Expressive\Router\Middleware\RouteMiddleware: Zend\Expressive\Router\Middleware\RouteMiddlewareFactory
|
||||
Zend\Expressive\Router\Middleware\DispatchMiddleware: Zend\Expressive\Router\Middleware\DispatchMiddlewareFactory
|
||||
Zend\Expressive\Router\Middleware\MethodNotAllowedMiddleware: Zend\Expressive\Router\Middleware\MethodNotAllowedMiddlewareFactory
|
||||
Zend\Expressive\Handler\NotFoundHandler: Zend\Expressive\Container\NotFoundHandlerFactory
|
||||
App\Middleware\InternalServerError: Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory
|
||||
|
||||
Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory:
|
||||
App\Middleware\InternalServerError:
|
||||
- Psr\Http\Message\ResponseInterface
|
||||
|
||||
@@ -5,15 +5,17 @@
|
||||
"version": "{{artifactVersion}}",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": "^5.6 || ^7.0",
|
||||
"ext-yaml" : "^1.2 || ^2.0",
|
||||
"zendframework/zend-expressive": "^2.1",
|
||||
"articus/path-handler": "^0.3",
|
||||
"articus/data-transfer": "^0.1",
|
||||
"zendframework/zend-serializer": "^2.8",
|
||||
"zendframework/zend-config": "^3.1",
|
||||
"nikic/fast-route": "^1.2",
|
||||
"http-interop/http-middleware": "^0.4.1"
|
||||
"php": "^7.1",
|
||||
"ext-yaml": "^2.0",
|
||||
"zendframework/zend-expressive": "^3.2",
|
||||
"zendframework/zend-diactoros": "^2.1",
|
||||
"articus/path-handler": "^0.4",
|
||||
"articus/data-transfer": "^0.2",
|
||||
"doctrine/annotations": "^1.6",
|
||||
"zendframework/zend-cache": "^2.8",
|
||||
"zendframework/zend-serializer": "^2.9",
|
||||
"zendframework/zend-config": "^3.2",
|
||||
"nikic/fast-route": "^1.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
@@ -4,38 +4,36 @@
|
||||
#Enable configuration cache
|
||||
#cache_configuration: true
|
||||
|
||||
#Enable routing cache for handlers
|
||||
#Articus\PathHandler\Router\FastRouteAnnotation:
|
||||
# metadata_cache:
|
||||
# adapter:
|
||||
# name: filesystem
|
||||
#Articus\PathHandler\RouteInjection\Factory:
|
||||
# #Enable routing table cache
|
||||
# router:
|
||||
# cache:
|
||||
# adapter: filesystem
|
||||
# options:
|
||||
# cache_dir: data/cache/ZendCache
|
||||
# namespace: fast-route
|
||||
# plugins:
|
||||
# serializer:
|
||||
# serializer: phpserialize
|
||||
|
||||
#Enable consumer, attribute and producer cache for handlers
|
||||
#Articus\PathHandler\Middleware:
|
||||
# metadata_cache:
|
||||
# adapter:
|
||||
# name: filesystem
|
||||
# namespace: ph-router
|
||||
# plugins:
|
||||
# serializer:
|
||||
# serializer: phpserialize
|
||||
# #Enable handler metadata cache
|
||||
# metadata:
|
||||
# cache:
|
||||
# adapter: filesystem
|
||||
# options:
|
||||
# cache_dir: data/cache/ZendCache
|
||||
# namespace: path-handler
|
||||
# plugins:
|
||||
# serializer:
|
||||
# serializer: phpserialize
|
||||
# namespace: ph-metadata
|
||||
# plugins:
|
||||
# serializer:
|
||||
# serializer: phpserialize
|
||||
|
||||
#Enable data transfer metadata cache for DTOs
|
||||
#data_transfer:
|
||||
#Articus\DataTransfer\Service:
|
||||
# metadata_cache:
|
||||
# adapter:
|
||||
# name: filesystem
|
||||
# options:
|
||||
# cache_dir: data/cache/ZendCache
|
||||
# namespace: data-transfer
|
||||
# namespace: dt
|
||||
# plugins:
|
||||
# serializer:
|
||||
# serializer: phpserialize
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Zend\Config\Factory as ConfigFactory;
|
||||
|
||||
//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';
|
||||
|
||||
@@ -24,20 +29,15 @@ if (is_readable(CONFIG_CACHE_PATH)) {
|
||||
]);
|
||||
|
||||
//Cache full configuration
|
||||
if (isset($config['cache_configuration']) && $config['cache_configuration']) {
|
||||
if ($config['cache_configuration'] ?? false) {
|
||||
if (!ConfigFactory::toFile(CONFIG_CACHE_PATH, $config)) {
|
||||
throw new \RuntimeException('Failed to cache configuration');
|
||||
}
|
||||
}
|
||||
}
|
||||
//Get configuration for container
|
||||
$dependencies = [];
|
||||
if (isset($config['dependencies'])) {
|
||||
$dependencies = $config['dependencies'];
|
||||
}
|
||||
|
||||
//Create container
|
||||
$container = new \Zend\ServiceManager\ServiceManager($dependencies);
|
||||
$container = new \Zend\ServiceManager\ServiceManager($config['dependencies'] ?? []);
|
||||
|
||||
//Register full configuration as a service
|
||||
$container->setService('config', $config);
|
||||
|
||||
@@ -2,7 +2,7 @@ dependencies:
|
||||
factories:
|
||||
Articus\DataTransfer\Service: Articus\DataTransfer\ServiceFactory
|
||||
|
||||
data_transfer:
|
||||
Articus\DataTransfer\Service:
|
||||
metadata_cache:
|
||||
adapter:
|
||||
name: blackhole
|
||||
@@ -12,7 +12,6 @@ data_transfer:
|
||||
{{invokerPackage}}\Strategy\DateTime: {{invokerPackage}}\Strategy\DateTime
|
||||
{{invokerPackage}}\Strategy\QueryParameter: {{invokerPackage}}\Strategy\QueryParameter
|
||||
{{invokerPackage}}\Strategy\QueryParameterArray: {{invokerPackage}}\Strategy\QueryParameterArray
|
||||
# factories:
|
||||
aliases:
|
||||
Date: {{invokerPackage}}\Strategy\Date
|
||||
DateTime: {{invokerPackage}}\Strategy\DateTime
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
{{#models}}{{#model}}
|
||||
namespace {{package}};
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
dependencies:
|
||||
factories:
|
||||
Zend\Expressive\Router\RouterInterface: Articus\PathHandler\Router\Factory\FastRouteAnnotation
|
||||
|
||||
Articus\PathHandler\Middleware:
|
||||
metadata_cache:
|
||||
adapter:
|
||||
name: blackhole
|
||||
Articus\PathHandler\RouteInjection\Factory:
|
||||
paths:
|
||||
'{{basePathWithoutHost}}':
|
||||
{{#apiInfo}}
|
||||
{{#apis}}
|
||||
{{#operations}}
|
||||
- {{package}}\{{classname}}
|
||||
{{/operations}}
|
||||
{{/apis}}
|
||||
{{/apiInfo}}
|
||||
handlers:
|
||||
abstract_factories:
|
||||
- Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory
|
||||
- Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory
|
||||
# consumers:
|
||||
# factories:
|
||||
# invokables:
|
||||
@@ -19,19 +21,6 @@ Articus\PathHandler\Middleware:
|
||||
# factories:
|
||||
# invokables:
|
||||
|
||||
Articus\PathHandler\Router\FastRouteAnnotation:
|
||||
metadata_cache:
|
||||
adapter:
|
||||
name: blackhole
|
||||
handlers:
|
||||
{{#apiInfo}}
|
||||
{{#apis}}
|
||||
{{#operations}}
|
||||
- {{package}}\{{classname}}
|
||||
{{/operations}}
|
||||
{{/apis}}
|
||||
{{/apiInfo}}
|
||||
|
||||
Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory:
|
||||
{{#apiInfo}}
|
||||
{{#apis}}
|
||||
|
||||
Reference in New Issue
Block a user