diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ZendExpressivePathHandlerServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ZendExpressivePathHandlerServerCodegen.java index 91cf0a40ad0..89fb04d3f19 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ZendExpressivePathHandlerServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ZendExpressivePathHandlerServerCodegen.java @@ -1,8 +1,10 @@ package io.swagger.codegen.languages; import io.swagger.codegen.*; -import io.swagger.models.Operation; -import org.apache.commons.lang3.StringUtils; +import io.swagger.models.*; +import io.swagger.models.parameters.Parameter; +import io.swagger.models.parameters.QueryParameter; +import io.swagger.models.properties.*; import java.io.File; @@ -12,6 +14,12 @@ import java.util.List; import java.util.Map; public class ZendExpressivePathHandlerServerCodegen extends AbstractPhpCodegen { + + public static final String VEN_FROM_QUERY = "internal.ze-ph.fromQuery"; + public static final String VEN_COLLECTION_FORMAT = "internal.ze-ph.collectionFormat"; + public static final String VEN_QUERY_DATA_TYPE = "internal.ze-ph.queryDataType"; + public static final String VEN_HAS_QUERY_DATA = "internal.ze-ph.hasQueryData"; + @Override public CodegenType getTag() { return CodegenType.SERVER; @@ -29,6 +37,8 @@ public class ZendExpressivePathHandlerServerCodegen extends AbstractPhpCodegen { public ZendExpressivePathHandlerServerCodegen() { super(); + //no point to use double - http://php.net/manual/en/language.types.float.php , especially because of PHP 7+ float type declaration + typeMapping.put("double", "float"); embeddedTemplateDir = templateDir = "ze-ph"; invokerPackage = "App"; @@ -52,9 +62,14 @@ public class ZendExpressivePathHandlerServerCodegen extends AbstractPhpCodegen { supportingFiles.add(new SupportingFile("app.yml.mustache", packagePath + File.separator + "application" + File.separator + "config", "app.yml")); supportingFiles.add(new SupportingFile("path_handler.yml.mustache", packagePath + File.separator + "application" + File.separator + "config", "path_handler.yml")); supportingFiles.add(new SupportingFile("data_transfer.yml.mustache", packagePath + File.separator + "application" + File.separator + "config", "data_transfer.yml")); + supportingFiles.add(new SupportingFile("ErrorMiddleware.php.mustache", packagePath + File.separator + srcBasePath, "ErrorMiddleware.php")); supportingFiles.add(new SupportingFile("Date.php.mustache", packagePath + File.separator + srcBasePath + File.separator + "Strategy", "Date.php")); supportingFiles.add(new SupportingFile("DateTime.php.mustache", packagePath + File.separator + srcBasePath + File.separator + "Strategy", "DateTime.php")); + supportingFiles.add(new SupportingFile("QueryParameter.php.mustache", packagePath + File.separator + srcBasePath + File.separator + "Strategy", "QueryParameter.php")); + supportingFiles.add(new SupportingFile("QueryParameterArray.php.mustache", packagePath + File.separator + srcBasePath + File.separator + "Strategy", "QueryParameterArray.php")); supportingFiles.add(new SupportingFile("Type.php.mustache", packagePath + File.separator + srcBasePath + File.separator + "Validator", "Type.php")); + supportingFiles.add(new SupportingFile("QueryParameterType.php.mustache", packagePath + File.separator + srcBasePath + File.separator + "Validator", "QueryParameterType.php")); + supportingFiles.add(new SupportingFile("QueryParameterArrayType.php.mustache", packagePath + File.separator + srcBasePath + File.separator + "Validator", "QueryParameterArrayType.php")); additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, "1.0.0"); } @@ -115,6 +130,121 @@ public class ZendExpressivePathHandlerServerCodegen extends AbstractPhpCodegen { return super.toModelName(name); } + /** + * Generate additional model definitions from query parameters + * + * @param swagger + */ + @Override + public void preprocessSwagger(Swagger swagger) { + super.preprocessSwagger(swagger); + for (String pathKey : swagger.getPaths().keySet()) + { + Path path = swagger.getPath(pathKey); + Map operations = path.getOperationMap(); + for (HttpMethod method : operations.keySet()) + { + Operation operation = operations.get(method); + Map properties = new HashMap<>(); + for (Parameter parameter : operation.getParameters()) + { + Property property = convertParameterToProperty(parameter); + if (property != null) + { + properties.put(property.getName(), property); + } + } + if (!properties.isEmpty()) + { + Model model = new ModelImpl(); + String operationId = getOrGenerateOperationId(operation, pathKey, method.name()); + model.setDescription("Query parameters for " + operationId); + model.setProperties(properties); + model.getVendorExtensions().put(VEN_FROM_QUERY, Boolean.TRUE); + String definitionName = generateUniqueDefinitionName(operationId + "QueryData", swagger); + swagger.addDefinition(definitionName, model); + String definitionModel = "\\" + modelPackage + "\\" + toModelName(definitionName); + operation.getVendorExtensions().put(VEN_QUERY_DATA_TYPE, definitionModel); + operation.getVendorExtensions().put(VEN_HAS_QUERY_DATA, Boolean.TRUE); + } + } + } + } + + protected Property convertParameterToProperty(Parameter parameter) { + Property property = null; + if (parameter instanceof QueryParameter) + { + QueryParameter queryParameter = (QueryParameter) parameter; + switch (queryParameter.getType()) + { + case "string": + StringProperty stringProperty = new StringProperty(); + stringProperty.setMinLength(queryParameter.getMinLength()); + stringProperty.setMaxLength(queryParameter.getMaxLength()); + stringProperty.setPattern(queryParameter.getPattern()); + stringProperty.setEnum(queryParameter.getEnum()); + property = stringProperty; + break; + case "integer": + IntegerProperty integerProperty = new IntegerProperty(); + integerProperty.setMinimum(queryParameter.getMinimum()); + integerProperty.setMaximum(queryParameter.getMaximum()); + property = integerProperty; + break; + case "number": + FloatProperty floatProperty = new FloatProperty(); + floatProperty.setMinimum(queryParameter.getMinimum()); + floatProperty.setMaximum(queryParameter.getMaximum()); + property = floatProperty; + break; + case "boolean": + property = new BooleanProperty(); + break; + case "array": + ArrayProperty arrayProperty = new ArrayProperty(); + arrayProperty.setMinItems(queryParameter.getMinItems()); + arrayProperty.setMaxItems(queryParameter.getMaxItems()); + arrayProperty.setItems(queryParameter.getItems()); + String collectionFormat = queryParameter.getCollectionFormat(); + if (collectionFormat == null) { + collectionFormat = "csv"; + } + arrayProperty.getVendorExtensions().put(VEN_COLLECTION_FORMAT, collectionFormat); + property = arrayProperty; + break; + case "date": + property = new DateProperty(); + break; + case "date-time": + property = new DateTimeProperty(); + break; + } + if (property != null) + { + property.setName(queryParameter.getName()); + property.setDescription(queryParameter.getDescription()); + property.setRequired(queryParameter.getRequired()); + property.getVendorExtensions().put(VEN_FROM_QUERY, Boolean.TRUE); + } + } + return property; + } + + protected String generateUniqueDefinitionName(String name, Swagger swagger) + { + String result = name; + if (swagger.getDefinitions() != null) { + int count = 1; + while (swagger.getDefinitions().containsKey(result)) + { + result = name + "_" + count; + count += 1; + } + } + return result; + } + @Override public Map postProcessOperations(Map objs) { objs = super.postProcessOperations(objs); @@ -123,6 +253,7 @@ public class ZendExpressivePathHandlerServerCodegen extends AbstractPhpCodegen { String interfaceToImplement; StringBuilder interfacesToImplement = new StringBuilder(); String classMethod; + String pathPattern = null; for (CodegenOperation op : operationList) { switch (op.httpMethod) { case "GET": @@ -153,63 +284,41 @@ public class ZendExpressivePathHandlerServerCodegen extends AbstractPhpCodegen { } 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; } - @Override - public Map postProcessSupportingFileData(Map objs) { - objs = super.postProcessSupportingFileData(objs); - - Map apiInfo = (Map) objs.get("apiInfo"); - List> apis = (List>) apiInfo.get("apis"); - - List> routes = new ArrayList>(); - for (Map api : apis) { - String handler = (String) api.get("classname"); - String url = (String) api.get("baseName"); - if (url.charAt(0) == '/') { - url = url.substring(1); - } - insertRoute(routes, url.split("/"), 0, handler); - } - - objs.put("routes", routes); - return objs; - } - - private void insertRoute(List> routes, String[] urlParts, int currentUrlPartIndex, String handler) { - if (urlParts.length > currentUrlPartIndex) { - String urlPart = urlParts[currentUrlPartIndex]; - //List> subRoutes = null; - Map currentRoute = null; - for (Map route : routes) { - if (urlPart.equals(route.get("name"))) { - currentRoute = route; - break; + protected String generatePathPattern(CodegenOperation op) { + String result = op.path; + for (CodegenParameter pp : op.pathParams) { + StringBuilder replacement = new StringBuilder( "{" + pp.paramName); + if (pp.isEnum) { + StringBuilder enumRegExp = new StringBuilder(); + for (String enumValue : pp._enum) { + if (enumRegExp.length() > 0) { + enumRegExp.append("|"); + } + enumRegExp.append(enumValue.replaceAll("[\\Q<>()[]{}|^$-=!?*+.\\\\E]", "\\\\$0")); } + replacement.append(":"); + replacement.append(enumRegExp); + } else if (pp.isInteger) { + replacement.append(":0|(?:-?[1-9][0-9]*)"); + } else if (pp.isString && (pp.pattern != null) && (!pp.pattern.isEmpty())) { + replacement.append(":"); + replacement.append(pp.pattern); } - if (currentRoute == null) { - currentRoute = new HashMap(); - - String routePart = urlPart.replaceAll("^\\{(\\w+)\\}$", ":$1"); - boolean isLastUrlPart = currentUrlPartIndex == urlParts.length - 1; - - currentRoute.put("name", urlPart); - currentRoute.put("route", "/" + routePart); - currentRoute.put("type", (urlPart == routePart) ? "Literal" : "Segment"); - currentRoute.put("handler", isLastUrlPart ? handler : null); - currentRoute.put("hasChildren", false); - currentRoute.put("children", new ArrayList>()); - currentRoute.put("padding", StringUtils.repeat(' ', 4 * currentUrlPartIndex)); - - routes.add(currentRoute); - } - List> subRoutes = (List>) currentRoute.get("children"); - insertRoute(subRoutes, urlParts, currentUrlPartIndex + 1, handler); - currentRoute.put("hasChildren", !subRoutes.isEmpty()); + //TODO add regular expressions for other types if they are actually used for path parameters + replacement.append("}"); + result = result.replace("{" + pp.paramName + "}", replacement); } + return result; } } diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/ErrorMiddleware.php.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/ErrorMiddleware.php.mustache new file mode 100644 index 00000000000..f88ea0d0e8f --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/ze-ph/ErrorMiddleware.php.mustache @@ -0,0 +1,34 @@ +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; + } +} diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameter.php.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameter.php.mustache new file mode 100644 index 00000000000..1de1ca9e376 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameter.php.mustache @@ -0,0 +1,76 @@ + true, + self::TYPE_FLOAT => true, + self::TYPE_BOOL => true, + self::TYPE_STRING => true, + ]; + + /** + * @var string + */ + protected $type; + + /** + * QueryParameterArray constructor. + */ + public function __construct(array $options) + { + 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'])); + } + $this->type = $options['type']; + } + + + /** + * @inheritdoc + */ + public function extract($objectValue, $object = null) + { + $result = null; + if ($objectValue !== null) { + $result = (string)$objectValue; + } + return $result; + } + + /** + * @inheritdoc + */ + public function hydrate($arrayValue, $objectValue, array $array = null) + { + $result = null; + if ($arrayValue !== null) { + switch ($this->type) { + case self::TYPE_INT: + $result = (int)$arrayValue; + break; + case self::TYPE_FLOAT: + $result = (float)$arrayValue; + break; + case self::TYPE_BOOL: + $result = ($arrayValue === 'true')? true : false; + break; + case self::TYPE_STRING: + $result = (string)$arrayValue; + break; + } + } + return $result; + } +} diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameterArray.php.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameterArray.php.mustache new file mode 100644 index 00000000000..3504fb328ee --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameterArray.php.mustache @@ -0,0 +1,73 @@ + ',', + self::FORMAT_SSV => ' ', + self::FORMAT_TSV => "\t", + self::FORMAT_PIPES => '|', + self::FORMAT_MULTI => null, + ]; + + /** + * @var string|null + */ + protected $delimiter; + + public function __construct(array $options) + { + 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'])); + } + $this->delimiter = self::DELIMITER_MAP[$options['format']]; + } + + /** + * @inheritdoc + */ + public function extract($objectValue, $object = null) + { + $result = null; + if (is_array($objectValue)) { + if ($this->delimiter === null) { + $result = $objectValue; + } else { + $result = implode($this->delimiter, $objectValue); + } + } + return $result; + } + + /** + * @inheritdoc + */ + public function hydrate($arrayValue, $objectValue, array $array = null) + { + $result = null; + if ($arrayValue !== null) { + $list = null; + if ($this->delimiter === null) { + $list = (is_array($arrayValue))? $arrayValue : [$arrayValue]; + } else { + $list = explode($this->delimiter, $arrayValue); + } + $result = []; + foreach ($list as $item) { + $result[] = parent::hydrate($item, null); + } + } + return $result; + } +} diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameterArrayType.php.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameterArrayType.php.mustache new file mode 100644 index 00000000000..728e0d616e6 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameterArrayType.php.mustache @@ -0,0 +1,72 @@ +format; + } + + /** + * @param string $format + * @return self + */ + public function setFormat($format) + { + $this->format = $format; + return $this; + } + + protected function checkType($value) + { + $result = true; + 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)) { + foreach ($value as $item) { + $result = $result && parent::checkType($item); + } + } else { + $result = false; + } + } else { + switch ($this->type) { + case QueryParameterArray::TYPE_INT: + $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); + break; + case QueryParameterArray::TYPE_FLOAT: + $result = is_string($value) && preg_match(self::prepareRepeatingTypeRegExp(self::RE_FLOAT, $delimiter), $value); + break; + case QueryParameterArray::TYPE_STRING: + $result = is_string($value); + break; + default: + throw new \InvalidArgumentException(sprintf('Can not check for type %s.', $this->type)); + } + } + return $result; + } + + protected static function prepareRepeatingTypeRegExp($typeRegExp, $delimiter) + { + $escapedDelimiter = preg_quote($delimiter, '/'); + return '/^(' . $typeRegExp . ')(' . $escapedDelimiter . '('. $typeRegExp . '))*$/'; + } +} diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameterType.php.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameterType.php.mustache new file mode 100644 index 00000000000..1393018d42a --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/ze-ph/QueryParameterType.php.mustache @@ -0,0 +1,27 @@ +type) { + case QueryParameter::TYPE_INT: + return is_string($value) && preg_match('/^(' . self::RE_INT . ')$/', $value); + case QueryParameter::TYPE_BOOL: + return is_string($value) && preg_match('/^(' . self::RE_BOOL . ')$/', $value); + case QueryParameter::TYPE_FLOAT: + return is_string($value) && preg_match('/^(' . self::RE_FLOAT . ')$/', $value); + case QueryParameter::TYPE_STRING: + return is_string($value); + default: + throw new \InvalidArgumentException(sprintf('Can not check for type %s.', $this->type)); + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/README.md.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/README.md.mustache index 81eb543af3a..90660da8e6e 100644 --- a/modules/swagger-codegen/src/main/resources/ze-ph/README.md.mustache +++ b/modules/swagger-codegen/src/main/resources/ze-ph/README.md.mustache @@ -1,10 +1,34 @@ # Swagger generated server +Generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. + ## Overview -This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the -[OpenAPI-Spec](https://github.com/swagger-api/swagger-core/wiki) from a remote server, you can easily generate a server stub. This -is an example of building a PHP server. +This server stub aims to provide light, yet comprehensive structure for your API project using: -This example uses the [Zend Expressive](https://zendframework.github.io/zend-expressive) micro framework and [Path Handler](https://github.com/Articus/PathHandler) library. To see how to make this your own, please take a look at the template here: +- PHP: 5.6 or 7.* +- [Zend Expressive](https://zendframework.github.io/zend-expressive): 2.1 +- [Path Handler](https://github.com/Articus/PathHandler): 0.3 -[TEMPLATES](https://github.com/swagger-api/swagger-codegen/tree/master/modules/swagger-codegen/src/main/resources/ze-ph/) +## How to use +All you have to do to start development is: + +- install dependencies via [Composer](https://getcomposer.org/) +- create cache folder: `mkdir -p ./data/cache/ZendCache` (you will need it later for configuration and metadata caches - check comments in `./application/conig.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 `500 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 [Swagger Codegen repository](https://github.com/swagger-api/swagger-codegen/issues). diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/api.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/api.mustache index 2749cc42a83..5c8081932e1 100644 --- a/modules/swagger-codegen/src/main/resources/ze-ph/api.mustache +++ b/modules/swagger-codegen/src/main/resources/ze-ph/api.mustache @@ -11,11 +11,12 @@ use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; {{#operations}} -{{#description}} /** +{{#description}} * {{&description}} - */ {{/description}} + * @PHA\Route(pattern="{{pathPattern}}") + */ class {{classname}} implements {{interfacesToImplement}} { {{#operation}} @@ -26,13 +27,25 @@ class {{classname}} implements {{interfacesToImplement}} {{#description}} * {{description}} {{/description}} +{{#vendorExtensions}} +{{#internal.ze-ph.hasQueryData}} + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={ + * "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(name=PHConsumer\Json::class, mediaType="{{{mediaType}}}") {{/consumes}} {{^isPrimitiveType}} - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":{{dataType}}::class,"objectAttr":"{{paramName}}"}) +{{#isContainer}} + * TODO check if attribute is valid and can handle your container type +{{/isContainer}} + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":{{dataType}}::class,"objectAttr":"bodyData"}) {{/isPrimitiveType}} {{/bodyParam}} {{#produces}} @@ -50,10 +63,16 @@ class {{classname}} implements {{interfacesToImplement}} public function {{httpMethod}}(ServerRequestInterface $request) { //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}} ${{paramName}} */ - ${{paramName}} = $request->getAttribute("{{paramName}}"); + /** @var {{dataType}} $bodyData */ + $bodyData = $request->getAttribute("bodyData"); {{/isPrimitiveType}} {{/bodyParam}} throw new PHException\HttpCode(500, "Not implemented"); diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/app.yml.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/app.yml.mustache index 425019ca115..735c2ef1f68 100644 --- a/modules/swagger-codegen/src/main/resources/ze-ph/app.yml.mustache +++ b/modules/swagger-codegen/src/main/resources/ze-ph/app.yml.mustache @@ -1,26 +1,14 @@ dependencies: invokables: - #Has to add this line because currently router is strict requirement for Zend\Expressive\Application even if only middleware_pipeline is used - Zend\Expressive\Router\RouterInterface: Zend\Expressive\Router\ZendRouter Zend\Diactoros\Response\EmitterInterface: Zend\Diactoros\Response\SapiStreamEmitter + App\ErrorMiddleware: App\ErrorMiddleware factories: Zend\Expressive\Application: Zend\Expressive\Container\ApplicationFactory Articus\PathHandler\Middleware: Articus\PathHandler\MiddlewareFactory - Articus\DataTransfer\Service: Articus\DataTransfer\ServiceFactory - Zend\Stratigility\Middleware\ErrorHandler: Zend\Expressive\Container\ErrorHandlerFactory - Zend\Expressive\Middleware\ErrorResponseGenerator: Zend\Expressive\Container\WhoopsErrorResponseGeneratorFactory - Zend\Expressive\Whoops: Zend\Expressive\Container\WhoopsFactory - Zend\Expressive\WhoopsPageHandler: Zend\Expressive\Container\WhoopsPageHandlerFactory middleware_pipeline: error: - middleware: Zend\Stratigility\Middleware\ErrorHandler + middleware: App\ErrorMiddleware api: middleware: Articus\PathHandler\Middleware path: {{basePathWithoutHost}} - -whoops: - json_exceptions: - display: true - show_trace: true - ajax_only: true diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/composer.json.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/composer.json.mustache index aaf19cbddeb..5cab5ca6e2d 100644 --- a/modules/swagger-codegen/src/main/resources/ze-ph/composer.json.mustache +++ b/modules/swagger-codegen/src/main/resources/ze-ph/composer.json.mustache @@ -7,18 +7,17 @@ "require": { "php": "^5.6 || ^7.0", "ext-yaml" : "^1.2 || ^2.0", - "zendframework/zend-expressive": "^2.0", - "zendframework/zend-expressive-router": "^2.1", - "zendframework/zend-expressive-zendrouter": "^2.0", - "articus/path-handler": "0.2.*", - "articus/data-transfer": "*", - "zendframework/zend-serializer": "*", - "zendframework/zend-config": "*", - "filp/whoops": "^2.1.7" + "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" }, "autoload": { "psr-4": { "": "src/" } } -} +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/config.yml b/modules/swagger-codegen/src/main/resources/ze-ph/config.yml index 1a6111a88f5..f0f92e1589d 100644 --- a/modules/swagger-codegen/src/main/resources/ze-ph/config.yml +++ b/modules/swagger-codegen/src/main/resources/ze-ph/config.yml @@ -1,3 +1,41 @@ -#App -cache_configuration: false -debug: true +#Empty configuration placeholder, remove when you add any real configuration settings to this file +{} + +#Enable configuration cache +#cache_configuration: true + +#Enable routing cache for handlers +#Articus\PathHandler\Router\FastRouteAnnotation: +# metadata_cache: +# adapter: +# name: 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 +# options: +# cache_dir: data/cache/ZendCache +# namespace: path-handler +# plugins: +# serializer: +# serializer: phpserialize + +#Enable data transfer metadata cache for DTOs +#data_transfer: +# metadata_cache: +# adapter: +# name: filesystem +# options: +# cache_dir: data/cache/ZendCache +# namespace: data-transfer +# plugins: +# serializer: +# serializer: phpserialize diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/data_transfer.yml.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/data_transfer.yml.mustache index 8c9fb5bfceb..d5f734f5868 100644 --- a/modules/swagger-codegen/src/main/resources/ze-ph/data_transfer.yml.mustache +++ b/modules/swagger-codegen/src/main/resources/ze-ph/data_transfer.yml.mustache @@ -1,24 +1,28 @@ +dependencies: + factories: + Articus\DataTransfer\Service: Articus\DataTransfer\ServiceFactory + data_transfer: metadata_cache: adapter: name: blackhole -# adapter: -# name: filesystem -# options: -# cache_dir: data/cache/data_transfer -# namespace: dt - strategies: invokables: {{invokerPackage}}\Strategy\Date: {{invokerPackage}}\Strategy\Date {{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 + QueryParameter: {{invokerPackage}}\Strategy\QueryParameter + QueryParameterArray: {{invokerPackage}}\Strategy\QueryParameterArray validators: invokables: {{invokerPackage}}\Validator\Type: {{invokerPackage}}\Validator\Type + {{invokerPackage}}\Validator\QueryParameterType: {{invokerPackage}}\Validator\QueryParameterType + {{invokerPackage}}\Validator\QueryParameterArrayType: {{invokerPackage}}\Validator\QueryParameterArrayType factories: Articus\DataTransfer\Validator\Dictionary: Articus\DataTransfer\Validator\Factory Articus\DataTransfer\Validator\Collection: Articus\DataTransfer\Validator\Factory @@ -26,3 +30,5 @@ data_transfer: Dictionary: Articus\DataTransfer\Validator\Dictionary Collection: Articus\DataTransfer\Validator\Collection Type: {{invokerPackage}}\Validator\Type + QueryParameterType: {{invokerPackage}}\Validator\QueryParameterType + QueryParameterArrayType: {{invokerPackage}}\Validator\QueryParameterArrayType diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/model.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/model.mustache index a9106820843..6535afdff50 100644 --- a/modules/swagger-codegen/src/main/resources/ze-ph/model.mustache +++ b/modules/swagger-codegen/src/main/resources/ze-ph/model.mustache @@ -16,114 +16,10 @@ class {{classname}} {{#description}} * {{description}} {{/description}} - * @DTA\Data(field="{{baseName}}"{{^required}}, nullable=true{{/required}}) -{{^isPrimitiveType}} -{{^isListContainer}} -{{^isDate}} -{{^isDateTime}} - * @DTA\Strategy(name="Object", options={"type":{{datatype}}::class}) - * @DTA\Validator(name="Dictionary", options={"type":{{datatype}}::class}) -{{/isDateTime}} -{{/isDate}} -{{#isDate}} - * @DTA\Strategy(name="Date") - * @DTA\Validator(name="Date") -{{/isDate}} -{{#isDateTime}} - * @DTA\Strategy(name="DateTime") - * @DTA\Validator(name="Date", options={"format": \DateTime::RFC3339}) -{{/isDateTime}} -{{/isListContainer}} -{{#isListContainer}} - * @DTA\Strategy(name="ObjectArray", options={"type":{{#items}}{{datatype}}{{/items}}::class}) - * @DTA\Validator(name="Collection", options={"validators":{ - * {"name":"Dictionary", "options":{"type":{{#items}}{{datatype}}{{/items}}::class}} - * }}) -{{/isListContainer}} -{{/isPrimitiveType}} -{{#isPrimitiveType}} -{{#isListContainer}} -{{#items}} -{{#isString}} - * @DTA\Validator(name="Collection", options={"validators":{ - * {"name":"Type", "options":{"type":"string"}} - * }}) -{{/isString}} -{{#isInteger}} - * @DTA\Validator(name="Collection", options={"validators":{ - * {"name":"Type", "options":{"type":"int"}} - * }}) -{{/isInteger}} -{{#isLong}} - * @DTA\Validator(name="Collection", options={"validators":{ - * {"name":"Type", "options":{"type":"int"}} - * }}) -{{/isLong}} -{{#isBoolean}} - * @DTA\Validator(name="Collection", options={"validators":{ - * {"name":"Type", "options":{"type":"bool"}} - * }}) -{{/isBoolean}} -{{#isFloat}} - * @DTA\Validator(name="Collection", options={"validators":{ - * {"name":"Type", "options":{"type":"float"}} - * }}) -{{/isFloat}} -{{#isDouble}} - * @DTA\Validator(name="Collection", options={"validators":{ - * {"name":"Type", "options":{"type":"float"}} - * }}) -{{/isDouble}} -{{/items}} -{{/isListContainer}} -{{^isListContainer}} -{{#isString}} - * @DTA\Validator(name="Type", options={"type":"string"}) -{{/isString}} -{{#isInteger}} - * @DTA\Validator(name="Type", options={"type":"int"}) -{{/isInteger}} -{{#isLong}} - * @DTA\Validator(name="Type", options={"type":"int"}) -{{/isLong}} -{{#isBoolean}} - * @DTA\Validator(name="Type", options={"type":"bool"}) -{{/isBoolean}} -{{#isFloat}} - * @DTA\Validator(name="Type", options={"type":"float"}) -{{/isFloat}} -{{#isDouble}} - * @DTA\Validator(name="Type", options={"type":"float"}) -{{/isDouble}} -{{/isListContainer}} -{{/isPrimitiveType}} -{{#hasValidation}} -{{#minLength}} -{{#maxLength}} - * @DTA\Validator(name="StringLength", options={"min":{{minLength}}, "max":{{maxLength}}}) -{{/maxLength}} -{{/minLength}} -{{^minLength}} -{{#maxLength}} - * @DTA\Validator(name="StringLength", options={"max":{{maxLength}}}) -{{/maxLength}} -{{/minLength}} -{{#minLength}} -{{^maxLength}} - * @DTA\Validator(name="StringLength", options={"min":{{minLength}}}) -{{/maxLength}} -{{/minLength}} -{{#minimum}} - * @DTA\Validator(name="GreaterThan", options={"min":{{minimum}}{{^exclusiveMinimum}}, "inclusive":true{{/exclusiveMinimum}}}) -{{/minimum}} -{{#maximum}} - * @DTA\Validator(name="LessThan", options={"max":{{maximum}}{{^exclusiveMaximum}}, "inclusive":true{{/exclusiveMaximum}}}) -{{/maximum}} -{{#pattern}} - * @DTA\Validator(name="Regex", options={"pattern":"{{{pattern}}}"}) -{{/pattern}} -{{/hasValidation}} - * @var {{datatype}} + * @DTA\Data(field="{{baseName}}"{{^required}}, nullable=true{{/required}}){{#vendorExtensions}}{{#internal.ze-ph.fromQuery}} +{{>model_query_var}}{{/internal.ze-ph.fromQuery}}{{/vendorExtensions}}{{#vendorExtensions}}{{^internal.ze-ph.fromQuery}} +{{>model_normal_var}}{{/internal.ze-ph.fromQuery}}{{/vendorExtensions}}{{^vendorExtensions}} +{{>model_normal_var}}{{/vendorExtensions}} * @var {{datatype}} */ public ${{name}}; {{/vars}} diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/model_normal_var.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/model_normal_var.mustache new file mode 100644 index 00000000000..2cfae5001de --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/ze-ph/model_normal_var.mustache @@ -0,0 +1,64 @@ +{{^isPrimitiveType}} +{{^isContainer}} +{{^isDate}} +{{^isDateTime}} + * @DTA\Strategy(name="Object", options={"type":{{datatype}}::class}) + * @DTA\Validator(name="Dictionary", options={"type":{{datatype}}::class}) +{{/isDateTime}} +{{/isDate}} +{{#isDate}} + * @DTA\Strategy(name="Date") + * @DTA\Validator(name="Date") +{{/isDate}} +{{#isDateTime}} + * @DTA\Strategy(name="DateTime") + * @DTA\Validator(name="Date", options={"format": \DateTime::RFC3339}) +{{/isDateTime}} +{{/isContainer}} +{{#isContainer}} + * TODO check validator and strategy are correct and can handle container item type + * @DTA\Strategy(name="ObjectArray", options={"type":{{#items}}{{datatype}}{{/items}}::class}) + * @DTA\Validator(name="Collection", options={"validators":{ + * {"name":"Dictionary", "options":{"type":{{#items}}{{datatype}}{{/items}}::class}} + * }}) +{{/isContainer}} +{{/isPrimitiveType}} +{{#isPrimitiveType}} +{{#isContainer}} +{{#items}} + * TODO check validator and strategy are correct and can handle container item type + * @DTA\Validator(name="Collection", options={"validators":{ + * {"name":"Type", "options":{"type":"{{datatype}}"}} + * }}) +{{/items}} +{{/isContainer}} +{{^isContainer}} + * @DTA\Validator(name="Type", options={"type":"{{datatype}}"}) +{{/isContainer}} +{{/isPrimitiveType}} +{{#hasValidation}} +{{#minLength}} +{{#maxLength}} + * @DTA\Validator(name="StringLength", options={"min":{{minLength}}, "max":{{maxLength}}}) +{{/maxLength}} +{{/minLength}} +{{^minLength}} +{{#maxLength}} + * @DTA\Validator(name="StringLength", options={"max":{{maxLength}}}) +{{/maxLength}} +{{/minLength}} +{{#minLength}} +{{^maxLength}} + * @DTA\Validator(name="StringLength", options={"min":{{minLength}}}) +{{/maxLength}} +{{/minLength}} +{{#minimum}} + * @DTA\Validator(name="GreaterThan", options={"min":{{minimum}}{{^exclusiveMinimum}}, "inclusive":true{{/exclusiveMinimum}}}) +{{/minimum}} +{{#maximum}} + * @DTA\Validator(name="LessThan", options={"max":{{maximum}}{{^exclusiveMaximum}}, "inclusive":true{{/exclusiveMaximum}}}) +{{/maximum}} +{{#pattern}} + * @DTA\Validator(name="Regex", options={"pattern":"{{{pattern}}}"}) +{{/pattern}} +{{/hasValidation}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/model_query_var.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/model_query_var.mustache new file mode 100644 index 00000000000..54acf199052 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/ze-ph/model_query_var.mustache @@ -0,0 +1,54 @@ +{{^isPrimitiveType}} +{{^isContainer}} +{{#isDate}} + * @DTA\Strategy(name="Date") + * @DTA\Validator(name="Date") +{{/isDate}} +{{#isDateTime}} + * @DTA\Strategy(name="DateTime") + * @DTA\Validator(name="Date", options={"format": \DateTime::RFC3339}) +{{/isDateTime}} +{{/isContainer}} +{{#isContainer}} + * TODO add validator(s) and strategy for {{datatype}} and collection format {{internal.ze-ph.collectionFormat}} +{{/isContainer}} +{{/isPrimitiveType}} +{{#isPrimitiveType}} +{{#isContainer}} +{{#items}} + * TODO check validator and strategy are correct and can handle container item type + * @DTA\Strategy(name="QueryParameterArray", options={"type":"{{datatype}}", "format":"{{internal.ze-ph.collectionFormat}}"}) + * @DTA\Validator(name="QueryParameterArrayType", options={"type":"{{datatype}}", "format":"{{internal.ze-ph.collectionFormat}}"}) +{{/items}} +{{/isContainer}} +{{^isContainer}} + * @DTA\Strategy(name="QueryParameter", options={"type":"{{datatype}}"}) + * @DTA\Validator(name="QueryParameterType", options={"type":"{{datatype}}"}) +{{/isContainer}} +{{/isPrimitiveType}} +{{#hasValidation}} +{{#minLength}} +{{#maxLength}} + * @DTA\Validator(name="StringLength", options={"min":{{minLength}}, "max":{{maxLength}}}) +{{/maxLength}} +{{/minLength}} +{{^minLength}} +{{#maxLength}} + * @DTA\Validator(name="StringLength", options={"max":{{maxLength}}}) +{{/maxLength}} +{{/minLength}} +{{#minLength}} +{{^maxLength}} + * @DTA\Validator(name="StringLength", options={"min":{{minLength}}}) +{{/maxLength}} +{{/minLength}} +{{#minimum}} + * @DTA\Validator(name="GreaterThan", options={"min":{{minimum}}{{^exclusiveMinimum}}, "inclusive":true{{/exclusiveMinimum}}}) +{{/minimum}} +{{#maximum}} + * @DTA\Validator(name="LessThan", options={"max":{{maximum}}{{^exclusiveMaximum}}, "inclusive":true{{/exclusiveMaximum}}}) +{{/maximum}} +{{#pattern}} + * @DTA\Validator(name="Regex", options={"pattern":"{{{pattern}}}"}) +{{/pattern}} +{{/hasValidation}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/path_handler.yml.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/path_handler.yml.mustache index 583c701aa60..dafe2d0bd95 100644 --- a/modules/swagger-codegen/src/main/resources/ze-ph/path_handler.yml.mustache +++ b/modules/swagger-codegen/src/main/resources/ze-ph/path_handler.yml.mustache @@ -1,27 +1,42 @@ -path_handler: - routes: - routes: -{{#routes}}{{>route}}{{/routes}} - default_params: - middleware: '' +dependencies: + factories: + Zend\Expressive\Router\RouterInterface: Articus\PathHandler\Router\Factory\FastRouteAnnotation + +Articus\PathHandler\Middleware: + metadata_cache: + adapter: + name: blackhole + handlers: + abstract_factories: + - Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory +# consumers: +# factories: +# invokables: +# attributes: +# factories: +# invokables: +# producers: +# factories: +# invokables: + +Articus\PathHandler\Router\FastRouteAnnotation: metadata_cache: adapter: name: blackhole -# adapter: -# name: filesystem -# options: -# cache_dir: data/cache/path_handler -# namespace: ph - handlers: - invokables: {{#apiInfo}} {{#apis}} {{#operations}} - {{classname}}: {{package}}\{{classname}} + - {{package}}\{{classname}} +{{/operations}} +{{/apis}} +{{/apiInfo}} + +Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory: +{{#apiInfo}} +{{#apis}} +{{#operations}} + {{package}}\{{classname}}: [] {{/operations}} {{/apis}} {{/apiInfo}} -# consumers: -# attributes: -# producers: diff --git a/modules/swagger-codegen/src/main/resources/ze-ph/route.mustache b/modules/swagger-codegen/src/main/resources/ze-ph/route.mustache deleted file mode 100644 index cfb73260540..00000000000 --- a/modules/swagger-codegen/src/main/resources/ze-ph/route.mustache +++ /dev/null @@ -1,15 +0,0 @@ - {{padding}}'{{name}}': - {{padding}} type: {{type}} - {{padding}} options: - {{padding}} route: {{route}} -{{#handler}} - {{padding}} defaults: - {{padding}} handler: {{handler}} -{{/handler}} -{{#hasChildren}} -{{#handler}} - {{padding}} may_terminate: true -{{/handler}} - {{padding}} child_routes: -{{/hasChildren}} -{{#children}}{{>route}}{{/children}} \ No newline at end of file diff --git a/samples/server/petstore/ze-ph/.swagger-codegen/VERSION b/samples/server/petstore/ze-ph/.swagger-codegen/VERSION index f9f7450d135..855ff9501eb 100644 --- a/samples/server/petstore/ze-ph/.swagger-codegen/VERSION +++ b/samples/server/petstore/ze-ph/.swagger-codegen/VERSION @@ -1 +1 @@ -2.3.0-SNAPSHOT \ No newline at end of file +2.4.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/ze-ph/README.md b/samples/server/petstore/ze-ph/README.md index 81eb543af3a..90660da8e6e 100644 --- a/samples/server/petstore/ze-ph/README.md +++ b/samples/server/petstore/ze-ph/README.md @@ -1,10 +1,34 @@ # Swagger generated server +Generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. + ## Overview -This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the -[OpenAPI-Spec](https://github.com/swagger-api/swagger-core/wiki) from a remote server, you can easily generate a server stub. This -is an example of building a PHP server. +This server stub aims to provide light, yet comprehensive structure for your API project using: -This example uses the [Zend Expressive](https://zendframework.github.io/zend-expressive) micro framework and [Path Handler](https://github.com/Articus/PathHandler) library. To see how to make this your own, please take a look at the template here: +- PHP: 5.6 or 7.* +- [Zend Expressive](https://zendframework.github.io/zend-expressive): 2.1 +- [Path Handler](https://github.com/Articus/PathHandler): 0.3 -[TEMPLATES](https://github.com/swagger-api/swagger-codegen/tree/master/modules/swagger-codegen/src/main/resources/ze-ph/) +## How to use +All you have to do to start development is: + +- install dependencies via [Composer](https://getcomposer.org/) +- create cache folder: `mkdir -p ./data/cache/ZendCache` (you will need it later for configuration and metadata caches - check comments in `./application/conig.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 `500 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 [Swagger Codegen repository](https://github.com/swagger-api/swagger-codegen/issues). diff --git a/samples/server/petstore/ze-ph/application/config.yml b/samples/server/petstore/ze-ph/application/config.yml index 1a6111a88f5..f0f92e1589d 100644 --- a/samples/server/petstore/ze-ph/application/config.yml +++ b/samples/server/petstore/ze-ph/application/config.yml @@ -1,3 +1,41 @@ -#App -cache_configuration: false -debug: true +#Empty configuration placeholder, remove when you add any real configuration settings to this file +{} + +#Enable configuration cache +#cache_configuration: true + +#Enable routing cache for handlers +#Articus\PathHandler\Router\FastRouteAnnotation: +# metadata_cache: +# adapter: +# name: 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 +# options: +# cache_dir: data/cache/ZendCache +# namespace: path-handler +# plugins: +# serializer: +# serializer: phpserialize + +#Enable data transfer metadata cache for DTOs +#data_transfer: +# metadata_cache: +# adapter: +# name: filesystem +# options: +# cache_dir: data/cache/ZendCache +# namespace: data-transfer +# plugins: +# serializer: +# serializer: phpserialize diff --git a/samples/server/petstore/ze-ph/application/config/app.yml b/samples/server/petstore/ze-ph/application/config/app.yml index 414b59389f1..a47d04b9bc4 100644 --- a/samples/server/petstore/ze-ph/application/config/app.yml +++ b/samples/server/petstore/ze-ph/application/config/app.yml @@ -1,26 +1,14 @@ dependencies: invokables: - #Has to add this line because currently router is strict requirement for Zend\Expressive\Application even if only middleware_pipeline is used - Zend\Expressive\Router\RouterInterface: Zend\Expressive\Router\ZendRouter Zend\Diactoros\Response\EmitterInterface: Zend\Diactoros\Response\SapiStreamEmitter + App\ErrorMiddleware: App\ErrorMiddleware factories: Zend\Expressive\Application: Zend\Expressive\Container\ApplicationFactory Articus\PathHandler\Middleware: Articus\PathHandler\MiddlewareFactory - Articus\DataTransfer\Service: Articus\DataTransfer\ServiceFactory - Zend\Stratigility\Middleware\ErrorHandler: Zend\Expressive\Container\ErrorHandlerFactory - Zend\Expressive\Middleware\ErrorResponseGenerator: Zend\Expressive\Container\WhoopsErrorResponseGeneratorFactory - Zend\Expressive\Whoops: Zend\Expressive\Container\WhoopsFactory - Zend\Expressive\WhoopsPageHandler: Zend\Expressive\Container\WhoopsPageHandlerFactory middleware_pipeline: error: - middleware: Zend\Stratigility\Middleware\ErrorHandler + middleware: App\ErrorMiddleware api: middleware: Articus\PathHandler\Middleware path: /v2 - -whoops: - json_exceptions: - display: true - show_trace: true - ajax_only: true diff --git a/samples/server/petstore/ze-ph/application/config/data_transfer.yml b/samples/server/petstore/ze-ph/application/config/data_transfer.yml index 6040af29208..d6ee0e896a2 100644 --- a/samples/server/petstore/ze-ph/application/config/data_transfer.yml +++ b/samples/server/petstore/ze-ph/application/config/data_transfer.yml @@ -1,24 +1,28 @@ +dependencies: + factories: + Articus\DataTransfer\Service: Articus\DataTransfer\ServiceFactory + data_transfer: metadata_cache: adapter: name: blackhole -# adapter: -# name: filesystem -# options: -# cache_dir: data/cache/data_transfer -# namespace: dt - strategies: invokables: App\Strategy\Date: App\Strategy\Date App\Strategy\DateTime: App\Strategy\DateTime + App\Strategy\QueryParameter: App\Strategy\QueryParameter + App\Strategy\QueryParameterArray: App\Strategy\QueryParameterArray # factories: aliases: Date: App\Strategy\Date DateTime: App\Strategy\DateTime + QueryParameter: App\Strategy\QueryParameter + QueryParameterArray: App\Strategy\QueryParameterArray validators: invokables: App\Validator\Type: App\Validator\Type + App\Validator\QueryParameterType: App\Validator\QueryParameterType + App\Validator\QueryParameterArrayType: App\Validator\QueryParameterArrayType factories: Articus\DataTransfer\Validator\Dictionary: Articus\DataTransfer\Validator\Factory Articus\DataTransfer\Validator\Collection: Articus\DataTransfer\Validator\Factory @@ -26,3 +30,5 @@ data_transfer: Dictionary: Articus\DataTransfer\Validator\Dictionary Collection: Articus\DataTransfer\Validator\Collection Type: App\Validator\Type + QueryParameterType: App\Validator\QueryParameterType + QueryParameterArrayType: App\Validator\QueryParameterArrayType diff --git a/samples/server/petstore/ze-ph/application/config/path_handler.yml b/samples/server/petstore/ze-ph/application/config/path_handler.yml index 58ad234f634..e35f0537afc 100644 --- a/samples/server/petstore/ze-ph/application/config/path_handler.yml +++ b/samples/server/petstore/ze-ph/application/config/path_handler.yml @@ -1,187 +1,74 @@ -path_handler: - routes: - routes: - 'fake': - type: Literal - options: - route: /fake - defaults: - handler: Fake - may_terminate: true - child_routes: - 'jsonFormData': - type: Literal - options: - route: /jsonFormData - defaults: - handler: FakeJsonFormData - 'outer': - type: Literal - options: - route: /outer - child_routes: - 'boolean': - type: Literal - options: - route: /boolean - defaults: - handler: FakeOuterBoolean - 'composite': - type: Literal - options: - route: /composite - defaults: - handler: FakeOuterComposite - 'number': - type: Literal - options: - route: /number - defaults: - handler: FakeOuterNumber - 'string': - type: Literal - options: - route: /string - defaults: - handler: FakeOuterString - 'fake_classname_test': - type: Literal - options: - route: /fake_classname_test - defaults: - handler: FakeClassnameTest - 'pet': - type: Literal - options: - route: /pet - defaults: - handler: Pet - may_terminate: true - child_routes: - 'findByStatus': - type: Literal - options: - route: /findByStatus - defaults: - handler: PetFindByStatus - 'findByTags': - type: Literal - options: - route: /findByTags - defaults: - handler: PetFindByTags - '{petId}': - type: Segment - options: - route: /:petId - defaults: - handler: PetPetId - may_terminate: true - child_routes: - 'uploadImage': - type: Literal - options: - route: /uploadImage - defaults: - handler: PetPetIdUploadImage - 'store': - type: Literal - options: - route: /store - child_routes: - 'inventory': - type: Literal - options: - route: /inventory - defaults: - handler: StoreInventory - 'order': - type: Literal - options: - route: /order - defaults: - handler: StoreOrder - may_terminate: true - child_routes: - '{order_id}': - type: Segment - options: - route: /:order_id - defaults: - handler: StoreOrderOrderId - 'user': - type: Literal - options: - route: /user - defaults: - handler: User - may_terminate: true - child_routes: - 'createWithArray': - type: Literal - options: - route: /createWithArray - defaults: - handler: UserCreateWithArray - 'createWithList': - type: Literal - options: - route: /createWithList - defaults: - handler: UserCreateWithList - 'login': - type: Literal - options: - route: /login - defaults: - handler: UserLogin - 'logout': - type: Literal - options: - route: /logout - defaults: - handler: UserLogout - '{username}': - type: Segment - options: - route: /:username - defaults: - handler: UserUsername +dependencies: + factories: + Zend\Expressive\Router\RouterInterface: Articus\PathHandler\Router\Factory\FastRouteAnnotation - default_params: - middleware: '' +Articus\PathHandler\Middleware: metadata_cache: adapter: name: blackhole -# adapter: -# name: filesystem -# options: -# cache_dir: data/cache/path_handler -# namespace: ph - handlers: - invokables: - Fake: App\Handler\Fake - FakeJsonFormData: App\Handler\FakeJsonFormData - FakeOuterBoolean: App\Handler\FakeOuterBoolean - FakeOuterComposite: App\Handler\FakeOuterComposite - FakeOuterNumber: App\Handler\FakeOuterNumber - FakeOuterString: App\Handler\FakeOuterString - FakeClassnameTest: App\Handler\FakeClassnameTest - Pet: App\Handler\Pet - PetFindByStatus: App\Handler\PetFindByStatus - PetFindByTags: App\Handler\PetFindByTags - PetPetId: App\Handler\PetPetId - PetPetIdUploadImage: App\Handler\PetPetIdUploadImage - StoreInventory: App\Handler\StoreInventory - StoreOrder: App\Handler\StoreOrder - StoreOrderOrderId: App\Handler\StoreOrderOrderId - User: App\Handler\User - UserCreateWithArray: App\Handler\UserCreateWithArray - UserCreateWithList: App\Handler\UserCreateWithList - UserLogin: App\Handler\UserLogin - UserLogout: App\Handler\UserLogout - UserUsername: App\Handler\UserUsername + abstract_factories: + - Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory # consumers: +# factories: +# invokables: # attributes: +# factories: +# invokables: # producers: +# factories: +# invokables: + +Articus\PathHandler\Router\FastRouteAnnotation: + metadata_cache: + adapter: + name: blackhole + handlers: + - App\Handler\AnotherFakeDummy + - App\Handler\Fake + - App\Handler\FakeInlineAdditionalProperties + - App\Handler\FakeJsonFormData + - App\Handler\FakeOuterBoolean + - App\Handler\FakeOuterComposite + - App\Handler\FakeOuterNumber + - App\Handler\FakeOuterString + - App\Handler\FakeClassnameTest + - App\Handler\Pet + - App\Handler\PetFindByStatus + - App\Handler\PetFindByTags + - App\Handler\PetPetId + - App\Handler\PetPetIdUploadImage + - App\Handler\StoreInventory + - App\Handler\StoreOrder + - App\Handler\StoreOrderOrderId + - App\Handler\User + - App\Handler\UserCreateWithArray + - App\Handler\UserCreateWithList + - App\Handler\UserLogin + - App\Handler\UserLogout + - App\Handler\UserUsername + +Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory: + App\Handler\AnotherFakeDummy: [] + App\Handler\Fake: [] + App\Handler\FakeInlineAdditionalProperties: [] + App\Handler\FakeJsonFormData: [] + App\Handler\FakeOuterBoolean: [] + App\Handler\FakeOuterComposite: [] + App\Handler\FakeOuterNumber: [] + App\Handler\FakeOuterString: [] + App\Handler\FakeClassnameTest: [] + App\Handler\Pet: [] + App\Handler\PetFindByStatus: [] + App\Handler\PetFindByTags: [] + App\Handler\PetPetId: [] + App\Handler\PetPetIdUploadImage: [] + App\Handler\StoreInventory: [] + App\Handler\StoreOrder: [] + App\Handler\StoreOrderOrderId: [] + App\Handler\User: [] + App\Handler\UserCreateWithArray: [] + App\Handler\UserCreateWithList: [] + App\Handler\UserLogin: [] + App\Handler\UserLogout: [] + App\Handler\UserUsername: [] diff --git a/samples/server/petstore/ze-ph/composer.json b/samples/server/petstore/ze-ph/composer.json index 457b2209907..fd912caf6f4 100644 --- a/samples/server/petstore/ze-ph/composer.json +++ b/samples/server/petstore/ze-ph/composer.json @@ -7,18 +7,17 @@ "require": { "php": "^5.6 || ^7.0", "ext-yaml" : "^1.2 || ^2.0", - "zendframework/zend-expressive": "^2.0", - "zendframework/zend-expressive-router": "^2.1", - "zendframework/zend-expressive-zendrouter": "^2.0", - "articus/path-handler": "0.2.*", - "articus/data-transfer": "*", - "zendframework/zend-serializer": "*", - "zendframework/zend-config": "*", - "filp/whoops": "^2.1.7" + "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" }, "autoload": { "psr-4": { "": "src/" } } -} +} \ No newline at end of file diff --git a/samples/server/petstore/ze-ph/src/App/DTO/AdditionalPropertiesClass.php b/samples/server/petstore/ze-ph/src/App/DTO/AdditionalPropertiesClass.php index 1bd97b750ee..096b709e706 100644 --- a/samples/server/petstore/ze-ph/src/App/DTO/AdditionalPropertiesClass.php +++ b/samples/server/petstore/ze-ph/src/App/DTO/AdditionalPropertiesClass.php @@ -10,13 +10,20 @@ class AdditionalPropertiesClass { /** * @DTA\Data(field="map_property", nullable=true) + * TODO check validator and strategy are correct and can handle container item type + * @DTA\Validator(name="Collection", options={"validators":{ + * {"name":"Type", "options":{"type":"string"}} + * }}) * @var map[string,string] */ public $map_property; /** * @DTA\Data(field="map_of_map_property", nullable=true) - * @DTA\Strategy(name="Object", options={"type":map[string,map[string,string]]::class}) - * @DTA\Validator(name="Dictionary", options={"type":map[string,map[string,string]]::class}) + * TODO check validator and strategy are correct and can handle container item type + * @DTA\Strategy(name="ObjectArray", options={"type":map[string,string]::class}) + * @DTA\Validator(name="Collection", options={"validators":{ + * {"name":"Dictionary", "options":{"type":map[string,string]::class}} + * }}) * @var map[string,map[string,string]] */ public $map_of_map_property; diff --git a/samples/server/petstore/ze-ph/src/App/DTO/ArrayOfArrayOfNumberOnly.php b/samples/server/petstore/ze-ph/src/App/DTO/ArrayOfArrayOfNumberOnly.php index d3cdf2c34ae..3f2c5b42278 100644 --- a/samples/server/petstore/ze-ph/src/App/DTO/ArrayOfArrayOfNumberOnly.php +++ b/samples/server/petstore/ze-ph/src/App/DTO/ArrayOfArrayOfNumberOnly.php @@ -10,6 +10,7 @@ class ArrayOfArrayOfNumberOnly { /** * @DTA\Data(field="ArrayArrayNumber", nullable=true) + * TODO check validator and strategy are correct and can handle container item type * @DTA\Strategy(name="ObjectArray", options={"type":float[]::class}) * @DTA\Validator(name="Collection", options={"validators":{ * {"name":"Dictionary", "options":{"type":float[]::class}} diff --git a/samples/server/petstore/ze-ph/src/App/DTO/ArrayOfNumberOnly.php b/samples/server/petstore/ze-ph/src/App/DTO/ArrayOfNumberOnly.php index 6168949960e..d7b2724a2ea 100644 --- a/samples/server/petstore/ze-ph/src/App/DTO/ArrayOfNumberOnly.php +++ b/samples/server/petstore/ze-ph/src/App/DTO/ArrayOfNumberOnly.php @@ -10,6 +10,7 @@ class ArrayOfNumberOnly { /** * @DTA\Data(field="ArrayNumber", nullable=true) + * TODO check validator and strategy are correct and can handle container item type * @DTA\Validator(name="Collection", options={"validators":{ * {"name":"Type", "options":{"type":"float"}} * }}) diff --git a/samples/server/petstore/ze-ph/src/App/DTO/ArrayTest.php b/samples/server/petstore/ze-ph/src/App/DTO/ArrayTest.php index 64786f57404..3702ec0aec5 100644 --- a/samples/server/petstore/ze-ph/src/App/DTO/ArrayTest.php +++ b/samples/server/petstore/ze-ph/src/App/DTO/ArrayTest.php @@ -10,6 +10,7 @@ class ArrayTest { /** * @DTA\Data(field="array_of_string", nullable=true) + * TODO check validator and strategy are correct and can handle container item type * @DTA\Validator(name="Collection", options={"validators":{ * {"name":"Type", "options":{"type":"string"}} * }}) @@ -18,6 +19,7 @@ class ArrayTest public $array_of_string; /** * @DTA\Data(field="array_array_of_integer", nullable=true) + * TODO check validator and strategy are correct and can handle container item type * @DTA\Strategy(name="ObjectArray", options={"type":int[]::class}) * @DTA\Validator(name="Collection", options={"validators":{ * {"name":"Dictionary", "options":{"type":int[]::class}} @@ -27,6 +29,7 @@ class ArrayTest public $array_array_of_integer; /** * @DTA\Data(field="array_array_of_model", nullable=true) + * TODO check validator and strategy are correct and can handle container item type * @DTA\Strategy(name="ObjectArray", options={"type":\App\DTO\ReadOnlyFirst[]::class}) * @DTA\Validator(name="Collection", options={"validators":{ * {"name":"Dictionary", "options":{"type":\App\DTO\ReadOnlyFirst[]::class}} diff --git a/samples/server/petstore/ze-ph/src/App/DTO/EnumArrays.php b/samples/server/petstore/ze-ph/src/App/DTO/EnumArrays.php index f0ba9663716..3ea21512586 100644 --- a/samples/server/petstore/ze-ph/src/App/DTO/EnumArrays.php +++ b/samples/server/petstore/ze-ph/src/App/DTO/EnumArrays.php @@ -16,6 +16,7 @@ class EnumArrays public $just_symbol; /** * @DTA\Data(field="array_enum", nullable=true) + * TODO check validator and strategy are correct and can handle container item type * @DTA\Validator(name="Collection", options={"validators":{ * {"name":"Type", "options":{"type":"string"}} * }}) diff --git a/samples/server/petstore/ze-ph/src/App/DTO/EnumTest.php b/samples/server/petstore/ze-ph/src/App/DTO/EnumTest.php index e9a062b6844..c3c44c95347 100644 --- a/samples/server/petstore/ze-ph/src/App/DTO/EnumTest.php +++ b/samples/server/petstore/ze-ph/src/App/DTO/EnumTest.php @@ -23,7 +23,7 @@ class EnumTest /** * @DTA\Data(field="enum_number", nullable=true) * @DTA\Validator(name="Type", options={"type":"float"}) - * @var double + * @var float */ public $enum_number; /** diff --git a/samples/server/petstore/ze-ph/src/App/DTO/FindPetsByStatusQueryData.php b/samples/server/petstore/ze-ph/src/App/DTO/FindPetsByStatusQueryData.php new file mode 100644 index 00000000000..13006fb2b7a --- /dev/null +++ b/samples/server/petstore/ze-ph/src/App/DTO/FindPetsByStatusQueryData.php @@ -0,0 +1,21 @@ +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; + } +} diff --git a/samples/server/petstore/ze-ph/src/App/Handler/AnotherFakeDummy.php b/samples/server/petstore/ze-ph/src/App/Handler/AnotherFakeDummy.php new file mode 100644 index 00000000000..92d243b99f0 --- /dev/null +++ b/samples/server/petstore/ze-ph/src/App/Handler/AnotherFakeDummy.php @@ -0,0 +1,38 @@ +getAttribute("bodyData"); + throw new PHException\HttpCode(500, "Not implemented"); + } +} diff --git a/samples/server/petstore/ze-ph/src/App/Handler/Fake.php b/samples/server/petstore/ze-ph/src/App/Handler/Fake.php index 71ba8700f06..8e662549a55 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/Fake.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/Fake.php @@ -10,13 +10,16 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/fake") + */ class Fake implements Operation\PatchInterface, Operation\PostInterface, Operation\GetInterface { /** * To test \"client\" model * TODO check if consumer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Consumer(name=PHConsumer\Json::class, mediaType="application/json") - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\Client::class,"objectAttr":"body"}) + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\Client::class,"objectAttr":"bodyData"}) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/json") * @param ServerRequestInterface $request @@ -28,16 +31,16 @@ class Fake implements Operation\PatchInterface, Operation\PostInterface, Operati public function handlePatch(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\Client $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\Client $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } /** * Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트 * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation - * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml; charset=utf-8") + * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml; charset=utf-8") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation - * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/json; charset=utf-8") + * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/json; charset=utf-8") * @param ServerRequestInterface $request * * @throws PHException\HttpCode 500 if the method is not implemented @@ -49,6 +52,11 @@ class Fake implements Operation\PatchInterface, Operation\PostInterface, Operati } /** * To test enum parameters + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={ + * "type":\App\DTO\TestEnumParametersQueryData::class, + * "objectAttr":"queryData", + * "source": PHAttribute\Transfer::SOURCE_GET + * }) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="*/*") * @param ServerRequestInterface $request @@ -58,6 +66,8 @@ class Fake implements Operation\PatchInterface, Operation\PostInterface, Operati public function handleGet(ServerRequestInterface $request) { //TODO implement method + /** @var \App\DTO\TestEnumParametersQueryData $queryData */ + $queryData = $request->getAttribute("queryData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/FakeClassnameTest.php b/samples/server/petstore/ze-ph/src/App/Handler/FakeClassnameTest.php index afd81f25733..6c01e08daf9 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/FakeClassnameTest.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/FakeClassnameTest.php @@ -10,13 +10,16 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/fake_classname_test") + */ class FakeClassnameTest implements Operation\PatchInterface { /** * To test class name in snake case * TODO check if consumer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Consumer(name=PHConsumer\Json::class, mediaType="application/json") - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\Client::class,"objectAttr":"body"}) + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\Client::class,"objectAttr":"bodyData"}) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/json") * @param ServerRequestInterface $request @@ -28,8 +31,8 @@ class FakeClassnameTest implements Operation\PatchInterface public function handlePatch(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\Client $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\Client $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/FakeInlineAdditionalProperties.php b/samples/server/petstore/ze-ph/src/App/Handler/FakeInlineAdditionalProperties.php new file mode 100644 index 00000000000..7f3ed5efb38 --- /dev/null +++ b/samples/server/petstore/ze-ph/src/App/Handler/FakeInlineAdditionalProperties.php @@ -0,0 +1,31 @@ +getAttribute("body"); + /** @var \App\DTO\OuterBoolean $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterComposite.php b/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterComposite.php index d226397cfbc..2eac302e6be 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterComposite.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterComposite.php @@ -10,10 +10,13 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/fake/outer/composite") + */ class FakeOuterComposite implements Operation\PostInterface { /** - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\OuterComposite::class,"objectAttr":"body"}) + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\OuterComposite::class,"objectAttr":"bodyData"}) * @param ServerRequestInterface $request * * @throws PHException\HttpCode 500 if the method is not implemented @@ -23,8 +26,8 @@ class FakeOuterComposite implements Operation\PostInterface public function handlePost(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\OuterComposite $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\OuterComposite $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterNumber.php b/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterNumber.php index dce5e7d0538..707d63f0912 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterNumber.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterNumber.php @@ -10,10 +10,13 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/fake/outer/number") + */ class FakeOuterNumber implements Operation\PostInterface { /** - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\OuterNumber::class,"objectAttr":"body"}) + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\OuterNumber::class,"objectAttr":"bodyData"}) * @param ServerRequestInterface $request * * @throws PHException\HttpCode 500 if the method is not implemented @@ -23,8 +26,8 @@ class FakeOuterNumber implements Operation\PostInterface public function handlePost(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\OuterNumber $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\OuterNumber $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterString.php b/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterString.php index b3120698568..6ea0305d23e 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterString.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/FakeOuterString.php @@ -10,10 +10,13 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/fake/outer/string") + */ class FakeOuterString implements Operation\PostInterface { /** - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\OuterString::class,"objectAttr":"body"}) + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\OuterString::class,"objectAttr":"bodyData"}) * @param ServerRequestInterface $request * * @throws PHException\HttpCode 500 if the method is not implemented @@ -23,8 +26,8 @@ class FakeOuterString implements Operation\PostInterface public function handlePost(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\OuterString $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\OuterString $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/Pet.php b/samples/server/petstore/ze-ph/src/App/Handler/Pet.php index ba67f531078..000ebfbd5da 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/Pet.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/Pet.php @@ -10,6 +10,9 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/pet") + */ class Pet implements Operation\PostInterface, Operation\PutInterface { /** @@ -18,7 +21,7 @@ class Pet implements Operation\PostInterface, Operation\PutInterface * @PHA\Consumer(name=PHConsumer\Json::class, mediaType="application/json") * TODO check if consumer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Consumer(name=PHConsumer\Json::class, mediaType="application/xml") - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\Pet::class,"objectAttr":"body"}) + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\Pet::class,"objectAttr":"bodyData"}) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation @@ -30,8 +33,8 @@ class Pet implements Operation\PostInterface, Operation\PutInterface public function handlePost(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\Pet $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\Pet $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } /** @@ -40,7 +43,7 @@ class Pet implements Operation\PostInterface, Operation\PutInterface * @PHA\Consumer(name=PHConsumer\Json::class, mediaType="application/json") * TODO check if consumer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Consumer(name=PHConsumer\Json::class, mediaType="application/xml") - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\Pet::class,"objectAttr":"body"}) + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\Pet::class,"objectAttr":"bodyData"}) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation @@ -52,8 +55,8 @@ class Pet implements Operation\PostInterface, Operation\PutInterface public function handlePut(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\Pet $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\Pet $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/PetFindByStatus.php b/samples/server/petstore/ze-ph/src/App/Handler/PetFindByStatus.php index 15647ab7b1e..3594902ed92 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/PetFindByStatus.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/PetFindByStatus.php @@ -10,10 +10,18 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/pet/findByStatus") + */ class PetFindByStatus implements Operation\GetInterface { /** * Finds Pets by status + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={ + * "type":\App\DTO\FindPetsByStatusQueryData::class, + * "objectAttr":"queryData", + * "source": PHAttribute\Transfer::SOURCE_GET + * }) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation @@ -27,6 +35,8 @@ class PetFindByStatus implements Operation\GetInterface public function handleGet(ServerRequestInterface $request) { //TODO implement method + /** @var \App\DTO\FindPetsByStatusQueryData $queryData */ + $queryData = $request->getAttribute("queryData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/PetFindByTags.php b/samples/server/petstore/ze-ph/src/App/Handler/PetFindByTags.php index b39a3e5568c..7bfb3a8e7a2 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/PetFindByTags.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/PetFindByTags.php @@ -10,10 +10,18 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/pet/findByTags") + */ class PetFindByTags implements Operation\GetInterface { /** * Finds Pets by tags + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={ + * "type":\App\DTO\FindPetsByTagsQueryData::class, + * "objectAttr":"queryData", + * "source": PHAttribute\Transfer::SOURCE_GET + * }) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation @@ -27,6 +35,8 @@ class PetFindByTags implements Operation\GetInterface public function handleGet(ServerRequestInterface $request) { //TODO implement method + /** @var \App\DTO\FindPetsByTagsQueryData $queryData */ + $queryData = $request->getAttribute("queryData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/PetPetId.php b/samples/server/petstore/ze-ph/src/App/Handler/PetPetId.php index 2a9f470ee23..d6c0afcdd6f 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/PetPetId.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/PetPetId.php @@ -10,6 +10,9 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/pet/{petId}") + */ class PetPetId implements Operation\DeleteInterface, Operation\GetInterface, Operation\PostInterface { /** diff --git a/samples/server/petstore/ze-ph/src/App/Handler/PetPetIdUploadImage.php b/samples/server/petstore/ze-ph/src/App/Handler/PetPetIdUploadImage.php index c65c1ed019b..2ef9e98c82e 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/PetPetIdUploadImage.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/PetPetIdUploadImage.php @@ -10,6 +10,9 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/pet/{petId}/uploadImage") + */ class PetPetIdUploadImage implements Operation\PostInterface { /** diff --git a/samples/server/petstore/ze-ph/src/App/Handler/StoreInventory.php b/samples/server/petstore/ze-ph/src/App/Handler/StoreInventory.php index 3fdf28c052b..89466b0a3aa 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/StoreInventory.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/StoreInventory.php @@ -10,6 +10,9 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/store/inventory") + */ class StoreInventory implements Operation\GetInterface { /** diff --git a/samples/server/petstore/ze-ph/src/App/Handler/StoreOrder.php b/samples/server/petstore/ze-ph/src/App/Handler/StoreOrder.php index 0efb3ac62d9..04a89b4ce15 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/StoreOrder.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/StoreOrder.php @@ -10,11 +10,14 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/store/order") + */ class StoreOrder implements Operation\PostInterface { /** * Place an order for a pet - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\Order::class,"objectAttr":"body"}) + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\Order::class,"objectAttr":"bodyData"}) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation @@ -28,8 +31,8 @@ class StoreOrder implements Operation\PostInterface public function handlePost(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\Order $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\Order $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/StoreOrderOrderId.php b/samples/server/petstore/ze-ph/src/App/Handler/StoreOrderOrderId.php index e0bad2a5200..d552da86d59 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/StoreOrderOrderId.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/StoreOrderOrderId.php @@ -10,6 +10,9 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/store/order/{order_id}") + */ class StoreOrderOrderId implements Operation\DeleteInterface, Operation\GetInterface { /** diff --git a/samples/server/petstore/ze-ph/src/App/Handler/User.php b/samples/server/petstore/ze-ph/src/App/Handler/User.php index d4af4a33056..57b61bc41a1 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/User.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/User.php @@ -10,11 +10,14 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/user") + */ class User implements Operation\PostInterface { /** * Create user - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\User::class,"objectAttr":"body"}) + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\User::class,"objectAttr":"bodyData"}) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation @@ -26,8 +29,8 @@ class User implements Operation\PostInterface public function handlePost(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\User $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\User $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/UserCreateWithArray.php b/samples/server/petstore/ze-ph/src/App/Handler/UserCreateWithArray.php index 5481c525660..933ab5dc60d 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/UserCreateWithArray.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/UserCreateWithArray.php @@ -10,11 +10,15 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/user/createWithArray") + */ class UserCreateWithArray implements Operation\PostInterface { /** * Creates list of users with given input array - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\User[]::class,"objectAttr":"body"}) + * TODO check if attribute is valid and can handle your container type + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\User[]::class,"objectAttr":"bodyData"}) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation @@ -26,8 +30,8 @@ class UserCreateWithArray implements Operation\PostInterface public function handlePost(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\User[] $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\User[] $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/UserCreateWithList.php b/samples/server/petstore/ze-ph/src/App/Handler/UserCreateWithList.php index ee32d983639..ff34eb857d4 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/UserCreateWithList.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/UserCreateWithList.php @@ -10,11 +10,15 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/user/createWithList") + */ class UserCreateWithList implements Operation\PostInterface { /** * Creates list of users with given input array - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\User[]::class,"objectAttr":"body"}) + * TODO check if attribute is valid and can handle your container type + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\User[]::class,"objectAttr":"bodyData"}) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation @@ -26,8 +30,8 @@ class UserCreateWithList implements Operation\PostInterface public function handlePost(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\User[] $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\User[] $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/UserLogin.php b/samples/server/petstore/ze-ph/src/App/Handler/UserLogin.php index f31d8a49cc3..1a57443068d 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/UserLogin.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/UserLogin.php @@ -10,10 +10,18 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/user/login") + */ class UserLogin implements Operation\GetInterface { /** * Logs user into the system + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={ + * "type":\App\DTO\LoginUserQueryData::class, + * "objectAttr":"queryData", + * "source": PHAttribute\Transfer::SOURCE_GET + * }) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation @@ -27,6 +35,8 @@ class UserLogin implements Operation\GetInterface public function handleGet(ServerRequestInterface $request) { //TODO implement method + /** @var \App\DTO\LoginUserQueryData $queryData */ + $queryData = $request->getAttribute("queryData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Handler/UserLogout.php b/samples/server/petstore/ze-ph/src/App/Handler/UserLogout.php index 9b9546f37a3..96e8116e351 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/UserLogout.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/UserLogout.php @@ -10,6 +10,9 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/user/logout") + */ class UserLogout implements Operation\GetInterface { /** diff --git a/samples/server/petstore/ze-ph/src/App/Handler/UserUsername.php b/samples/server/petstore/ze-ph/src/App/Handler/UserUsername.php index 0d4478375b6..3e36ed9a38e 100644 --- a/samples/server/petstore/ze-ph/src/App/Handler/UserUsername.php +++ b/samples/server/petstore/ze-ph/src/App/Handler/UserUsername.php @@ -10,6 +10,9 @@ use Articus\PathHandler\Attribute as PHAttribute; use Articus\PathHandler\Exception as PHException; use Psr\Http\Message\ServerRequestInterface; +/** + * @PHA\Route(pattern="/user/{username}") + */ class UserUsername implements Operation\DeleteInterface, Operation\GetInterface, Operation\PutInterface { /** @@ -46,7 +49,7 @@ class UserUsername implements Operation\DeleteInterface, Operation\GetInterface, } /** * Updated user - * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\User::class,"objectAttr":"body"}) + * @PHA\Attribute(name=PHAttribute\Transfer::class, options={"type":\App\DTO\User::class,"objectAttr":"bodyData"}) * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation * @PHA\Producer(name=PHProducer\Transfer::class, mediaType="application/xml") * TODO check if producer is valid, if it has correct priority and if it can be moved to class annotation @@ -58,8 +61,8 @@ class UserUsername implements Operation\DeleteInterface, Operation\GetInterface, public function handlePut(ServerRequestInterface $request) { //TODO implement method - /** @var \App\DTO\User $body */ - $body = $request->getAttribute("body"); + /** @var \App\DTO\User $bodyData */ + $bodyData = $request->getAttribute("bodyData"); throw new PHException\HttpCode(500, "Not implemented"); } } diff --git a/samples/server/petstore/ze-ph/src/App/Strategy/QueryParameter.php b/samples/server/petstore/ze-ph/src/App/Strategy/QueryParameter.php new file mode 100644 index 00000000000..cf39ca0e638 --- /dev/null +++ b/samples/server/petstore/ze-ph/src/App/Strategy/QueryParameter.php @@ -0,0 +1,76 @@ + true, + self::TYPE_FLOAT => true, + self::TYPE_BOOL => true, + self::TYPE_STRING => true, + ]; + + /** + * @var string + */ + protected $type; + + /** + * QueryParameterArray constructor. + */ + public function __construct(array $options) + { + 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'])); + } + $this->type = $options['type']; + } + + + /** + * @inheritdoc + */ + public function extract($objectValue, $object = null) + { + $result = null; + if ($objectValue !== null) { + $result = (string)$objectValue; + } + return $result; + } + + /** + * @inheritdoc + */ + public function hydrate($arrayValue, $objectValue, array $array = null) + { + $result = null; + if ($arrayValue !== null) { + switch ($this->type) { + case self::TYPE_INT: + $result = (int)$arrayValue; + break; + case self::TYPE_FLOAT: + $result = (float)$arrayValue; + break; + case self::TYPE_BOOL: + $result = ($arrayValue === 'true')? true : false; + break; + case self::TYPE_STRING: + $result = (string)$arrayValue; + break; + } + } + return $result; + } +} diff --git a/samples/server/petstore/ze-ph/src/App/Strategy/QueryParameterArray.php b/samples/server/petstore/ze-ph/src/App/Strategy/QueryParameterArray.php new file mode 100644 index 00000000000..c506244c3f7 --- /dev/null +++ b/samples/server/petstore/ze-ph/src/App/Strategy/QueryParameterArray.php @@ -0,0 +1,73 @@ + ',', + self::FORMAT_SSV => ' ', + self::FORMAT_TSV => "\t", + self::FORMAT_PIPES => '|', + self::FORMAT_MULTI => null, + ]; + + /** + * @var string|null + */ + protected $delimiter; + + public function __construct(array $options) + { + 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'])); + } + $this->delimiter = self::DELIMITER_MAP[$options['format']]; + } + + /** + * @inheritdoc + */ + public function extract($objectValue, $object = null) + { + $result = null; + if (is_array($objectValue)) { + if ($this->delimiter === null) { + $result = $objectValue; + } else { + $result = implode($this->delimiter, $objectValue); + } + } + return $result; + } + + /** + * @inheritdoc + */ + public function hydrate($arrayValue, $objectValue, array $array = null) + { + $result = null; + if ($arrayValue !== null) { + $list = null; + if ($this->delimiter === null) { + $list = (is_array($arrayValue))? $arrayValue : [$arrayValue]; + } else { + $list = explode($this->delimiter, $arrayValue); + } + $result = []; + foreach ($list as $item) { + $result[] = parent::hydrate($item, null); + } + } + return $result; + } +} diff --git a/samples/server/petstore/ze-ph/src/App/Validator/QueryParameterArrayType.php b/samples/server/petstore/ze-ph/src/App/Validator/QueryParameterArrayType.php new file mode 100644 index 00000000000..590ca3685a4 --- /dev/null +++ b/samples/server/petstore/ze-ph/src/App/Validator/QueryParameterArrayType.php @@ -0,0 +1,72 @@ +format; + } + + /** + * @param string $format + * @return self + */ + public function setFormat($format) + { + $this->format = $format; + return $this; + } + + protected function checkType($value) + { + $result = true; + 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)) { + foreach ($value as $item) { + $result = $result && parent::checkType($item); + } + } else { + $result = false; + } + } else { + switch ($this->type) { + case QueryParameterArray::TYPE_INT: + $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); + break; + case QueryParameterArray::TYPE_FLOAT: + $result = is_string($value) && preg_match(self::prepareRepeatingTypeRegExp(self::RE_FLOAT, $delimiter), $value); + break; + case QueryParameterArray::TYPE_STRING: + $result = is_string($value); + break; + default: + throw new \InvalidArgumentException(sprintf('Can not check for type %s.', $this->type)); + } + } + return $result; + } + + protected static function prepareRepeatingTypeRegExp($typeRegExp, $delimiter) + { + $escapedDelimiter = preg_quote($delimiter, '/'); + return '/^(' . $typeRegExp . ')(' . $escapedDelimiter . '('. $typeRegExp . '))*$/'; + } +} diff --git a/samples/server/petstore/ze-ph/src/App/Validator/QueryParameterType.php b/samples/server/petstore/ze-ph/src/App/Validator/QueryParameterType.php new file mode 100644 index 00000000000..d2b4fcd16b8 --- /dev/null +++ b/samples/server/petstore/ze-ph/src/App/Validator/QueryParameterType.php @@ -0,0 +1,27 @@ +type) { + case QueryParameter::TYPE_INT: + return is_string($value) && preg_match('/^(' . self::RE_INT . ')$/', $value); + case QueryParameter::TYPE_BOOL: + return is_string($value) && preg_match('/^(' . self::RE_BOOL . ')$/', $value); + case QueryParameter::TYPE_FLOAT: + return is_string($value) && preg_match('/^(' . self::RE_FLOAT . ')$/', $value); + case QueryParameter::TYPE_STRING: + return is_string($value); + default: + throw new \InvalidArgumentException(sprintf('Can not check for type %s.', $this->type)); + } + } +}