[php-slim4] Add dependency injection container 2 (#11159)

* Change packages order alphabetically

* Add PHP-DI package to Composer template

* Remove ContainerInterface from APIs

User shouldn't access container directly, it's an anti-pattern.

Ref: https://php-di.org/doc/best-practices.html#rules-for-using-a-container-and-dependency-injection

* Change app templates to use PHP-DI

Application looks more like a default Slim skeleton now.

Ref: https://github.com/slimphp/Slim-Skeleton

* Rename SlimRouter to RegisterRoutes

Since it's callable class new name fits better.

* Add short documentation

* Refresh samples
This commit is contained in:
Yuriy Belenko 2021-12-29 05:57:19 +03:00 committed by GitHub
parent ac55ac9d55
commit aa61220db2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 862 additions and 1106 deletions

View File

@ -50,6 +50,8 @@ public class PhpSlim4ServerCodegen extends AbstractPhpCodegen {
protected String artifactId = "openapi-server"; protected String artifactId = "openapi-server";
protected String authDirName = "Auth"; protected String authDirName = "Auth";
protected String authPackage = ""; protected String authPackage = "";
protected String appDirName = "App";
protected String appPackage = "";
protected String psr7Implementation = "slim-psr7"; protected String psr7Implementation = "slim-psr7";
protected String interfacesDirName = "Interfaces"; protected String interfacesDirName = "Interfaces";
protected String interfacesPackage = ""; protected String interfacesPackage = "";
@ -92,6 +94,7 @@ public class PhpSlim4ServerCodegen extends AbstractPhpCodegen {
modelPackage = invokerPackage + "\\" + modelDirName; modelPackage = invokerPackage + "\\" + modelDirName;
authPackage = invokerPackage + "\\" + authDirName; authPackage = invokerPackage + "\\" + authDirName;
interfacesPackage = invokerPackage + "\\" + interfacesDirName; interfacesPackage = invokerPackage + "\\" + interfacesDirName;
appPackage = invokerPackage + "\\" + appDirName;
outputFolder = "generated-code" + File.separator + "slim4"; outputFolder = "generated-code" + File.separator + "slim4";
modelTestTemplateFiles.put("model_test.mustache", ".php"); modelTestTemplateFiles.put("model_test.mustache", ".php");
@ -167,6 +170,8 @@ public class PhpSlim4ServerCodegen extends AbstractPhpCodegen {
authPackage = invokerPackage + "\\" + authDirName; authPackage = invokerPackage + "\\" + authDirName;
// Update interfacesPackage // Update interfacesPackage
interfacesPackage = invokerPackage + "\\" + interfacesDirName; interfacesPackage = invokerPackage + "\\" + interfacesDirName;
// update appPackage
appPackage = invokerPackage + "\\" + appDirName;
} }
// make auth src path available in mustache template // make auth src path available in mustache template
@ -178,6 +183,10 @@ public class PhpSlim4ServerCodegen extends AbstractPhpCodegen {
additionalProperties.put("interfacesSrcPath", "./" + toSrcPath(interfacesPackage, srcBasePath)); additionalProperties.put("interfacesSrcPath", "./" + toSrcPath(interfacesPackage, srcBasePath));
additionalProperties.put("interfacesTestPath", "./" + toSrcPath(interfacesPackage, testBasePath)); additionalProperties.put("interfacesTestPath", "./" + toSrcPath(interfacesPackage, testBasePath));
// same for app classes
additionalProperties.put("appPackage", appPackage);
additionalProperties.put("appSrcPath", "./" + toSrcPath(appPackage, srcBasePath));
if (additionalProperties.containsKey(PSR7_IMPLEMENTATION)) { if (additionalProperties.containsKey(PSR7_IMPLEMENTATION)) {
this.setPsr7Implementation((String) additionalProperties.get(PSR7_IMPLEMENTATION)); this.setPsr7Implementation((String) additionalProperties.get(PSR7_IMPLEMENTATION));
} }
@ -213,7 +222,9 @@ public class PhpSlim4ServerCodegen extends AbstractPhpCodegen {
supportingFiles.add(new SupportingFile("composer.mustache", "", "composer.json")); supportingFiles.add(new SupportingFile("composer.mustache", "", "composer.json"));
supportingFiles.add(new SupportingFile("index.mustache", "public", "index.php")); supportingFiles.add(new SupportingFile("index.mustache", "public", "index.php"));
supportingFiles.add(new SupportingFile(".htaccess", "public", ".htaccess")); supportingFiles.add(new SupportingFile(".htaccess", "public", ".htaccess"));
supportingFiles.add(new SupportingFile("SlimRouter.mustache", toSrcPath(invokerPackage, srcBasePath), "SlimRouter.php")); supportingFiles.add(new SupportingFile("register_dependencies.mustache", toSrcPath(appPackage, srcBasePath), "RegisterDependencies.php"));
supportingFiles.add(new SupportingFile("register_middlewares.mustache", toSrcPath(appPackage, srcBasePath), "RegisterMiddlewares.php"));
supportingFiles.add(new SupportingFile("register_routes.mustache", toSrcPath(appPackage, srcBasePath), "RegisterRoutes.php"));
// don't generate phpunit config when tests generation disabled // don't generate phpunit config when tests generation disabled
if (Boolean.TRUE.equals(generateApiTests) || Boolean.TRUE.equals(generateModelTests)) { if (Boolean.TRUE.equals(generateApiTests) || Boolean.TRUE.equals(generateModelTests)) {
@ -224,8 +235,8 @@ public class PhpSlim4ServerCodegen extends AbstractPhpCodegen {
supportingFiles.add(new SupportingFile("phpcs.xml.mustache", "", "phpcs.xml.dist")); supportingFiles.add(new SupportingFile("phpcs.xml.mustache", "", "phpcs.xml.dist"));
supportingFiles.add(new SupportingFile("htaccess_deny_all", "config", ".htaccess")); supportingFiles.add(new SupportingFile("htaccess_deny_all", "config", ".htaccess"));
supportingFiles.add(new SupportingFile("config_example.mustache", "config" + File.separator + "dev", "example.inc.php")); supportingFiles.add(new SupportingFile("config_dev_default.mustache", "config" + File.separator + "dev", "default.inc.php"));
supportingFiles.add(new SupportingFile("config_example.mustache", "config" + File.separator + "prod", "example.inc.php")); supportingFiles.add(new SupportingFile("config_prod_default.mustache", "config" + File.separator + "prod", "default.inc.php"));
if (Boolean.TRUE.equals(generateModels)) { if (Boolean.TRUE.equals(generateModels)) {
supportingFiles.add(new SupportingFile("base_model.mustache", toSrcPath(invokerPackage, srcBasePath), "BaseModel.php")); supportingFiles.add(new SupportingFile("base_model.mustache", toSrcPath(invokerPackage, srcBasePath), "BaseModel.php"));

View File

@ -1,3 +1,7 @@
<IfModule mod_env.c>
SetEnv APP_ENV 'production'
</IfModule>
<IfModule mod_rewrite.c> <IfModule mod_rewrite.c>
RewriteEngine On RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f

View File

@ -15,6 +15,7 @@ This server has been generated with [Guzzle PSR-7](https://github.com/guzzle/psr
{{#isZendDiactoros}} {{#isZendDiactoros}}
This server has been generated with [Laminas (Zend) PSR-7 implementation](https://github.com/laminas/laminas-diactoros). This server has been generated with [Laminas (Zend) PSR-7 implementation](https://github.com/laminas/laminas-diactoros).
{{/isZendDiactoros}} {{/isZendDiactoros}}
[PHP-DI](https://php-di.org/doc/frameworks/slim.html) package used as dependency container.
## Requirements ## Requirements
@ -34,7 +35,10 @@ $ composer install
## Add configs ## Add configs
Application requires at least one config file(`config/dev/config.inc.php` or `config/prod/config.inc.php`). You can use [config/dev/example.inc.php](config/dev/example.inc.php) as starting point. [PHP-DI package](https://php-di.org/doc/getting-started.html) helps to decouple configuration from implementation. App loads configuration files in straight order(`$env` can be `prod` or `dev`):
1. `config/$env/default.inc.php` (contains safe values, can be committed to vcs)
2. `config/$env/config.inc.php` (user config, excluded from vcs, can contain sensitive values, passwords etc.)
3. `lib/App/RegisterDependencies.php`
## Start devserver ## Start devserver
@ -103,25 +107,14 @@ $ composer phplint
## Show errors ## Show errors
Switch on option in your application config file like: Switch your app environment to development in `public/.htaccess` file:
```diff ```ini
return [ ## .htaccess
'slimSettings' => [ <IfModule mod_env.c>
- 'displayErrorDetails' => false, SetEnv APP_ENV 'development'
+ 'displayErrorDetails' => true, </IfModule>
'logErrors' => true,
'logErrorDetails' => true,
],
``` ```
## Mock Server
For a quick start uncomment [mocker middleware options](config/dev/example.inc.php#L67-L94) in your application config file.
Used packages:
* [Openapi Data Mocker](https://github.com/ybelenko/openapi-data-mocker) - first implementation of OAS3 fake data generator.
* [Openapi Data Mocker Server Middleware](https://github.com/ybelenko/openapi-data-mocker-server-middleware) - PSR-15 HTTP server middleware.
* [Openapi Data Mocker Interfaces](https://github.com/ybelenko/openapi-data-mocker-interfaces) - package with mocking interfaces.
{{#generateApiDocs}} {{#generateApiDocs}}
## API Endpoints ## API Endpoints
@ -135,17 +128,22 @@ All URIs are relative to *{{{basePath}}}*
namespace {{apiPackage}}; namespace {{apiPackage}};
use {{apiPackage}}\AbstractPetApi; use {{apiPackage}}\AbstractPetApi;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
class PetApi extends AbstractPetApi class PetApi extends AbstractPetApi
{ {
public function addPet(
public function addPet($request, $response, $args) ServerRequestInterface $request,
{ ResponseInterface $response
): ResponseInterface {
// your implementation of addPet method here // your implementation of addPet method here
} }
} }
``` ```
When you need to inject dependencies into API controller check [PHP-DI - Controllers as services](https://github.com/PHP-DI/Slim-Bridge#controllers-as-services) guide.
Place all your implementation classes in `./src` folder accordingly. Place all your implementation classes in `./src` folder accordingly.
For instance, when abstract class located at `./lib/Api/AbstractPetApi.php` you need to create implementation class at `./src/Api/PetApi.php`. For instance, when abstract class located at `./lib/Api/AbstractPetApi.php` you need to create implementation class at `./src/Api/PetApi.php`.

View File

@ -1,331 +0,0 @@
<?php
{{>licenseInfo}}
/**
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* Do not edit the class manually.
*/{{#apiInfo}}
namespace {{invokerPackage}};
use Slim\Factory\AppFactory;
use Slim\Interfaces\RouteInterface;
use Slim\Exception\HttpNotImplementedException;
use Psr\Container\ContainerInterface;
use InvalidArgumentException;
use Dyorg\TokenAuthentication;
use Dyorg\TokenAuthentication\TokenSearch;
use Psr\Http\Message\ServerRequestInterface;
use OpenAPIServer\Mock\OpenApiDataMocker;
use OpenAPIServer\Mock\OpenApiDataMockerRouteMiddleware;
{{#isSlimPsr7}}
use Slim\Psr7\Factory\ResponseFactory;
{{/isSlimPsr7}}
{{#isNyholmPsr7}}
use Nyholm\Psr7\Factory\Psr17Factory
{{/isNyholmPsr7}}
{{#isGuzzlePsr7}}
use GuzzleHttp\Psr7\HttpFactory;
{{/isGuzzlePsr7}}
{{#isZendDiactoros}}
use Zend\Diactoros\ResponseFactory;
{{/isZendDiactoros}}
use Exception;
/**
* SlimRouter Class Doc Comment
*
* @package {{invokerPackage}}
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
class SlimRouter
{
/** @var App instance */
private $slimApp;
/** @var array[] list of all api operations */
private $operations = [
{{#apis}}
{{#operations}}
{{#operation}}
[
'httpMethod' => '{{httpMethod}}',
'basePathWithoutHost' => '{{{basePathWithoutHost}}}',
'path' => '{{{path}}}',
'apiPackage' => '{{apiPackage}}',
'classname' => '{{classname}}',
'userClassname' => '{{userClassname}}',
'operationId' => '{{operationId}}',
'responses' => [
{{#responses}}
'{{#isDefault}}default{{/isDefault}}{{^isDefault}}{{code}}{{/isDefault}}' => [
'jsonSchema' => '{{{jsonSchema}}}',
],
{{/responses}}
],
'authMethods' => [
{{#hasAuthMethods}}
{{#authMethods}}
// {{type}} security schema named '{{name}}'
{{#isBasicBasic}}
[
'type' => '{{type}}',
'isBasic' => true,
'isBearer' => false,
'isApiKey' => false,
'isOAuth' => false,
],
{{/isBasicBasic}}
{{#isBasicBearer}}
[
'type' => '{{type}}',
'isBasic' => true,
'isBearer' => true,
'isApiKey' => false,
'isOAuth' => false,
],
{{/isBasicBearer}}
{{#isApiKey}}
[
'type' => '{{type}}',
'isBasic' => false,
'isBearer' => false,
'isApiKey' => true,
'isOAuth' => false,
'keyParamName' => '{{keyParamName}}',
'isKeyInHeader' => {{#isKeyInHeader}}true{{/isKeyInHeader}}{{^isKeyInHeader}}false{{/isKeyInHeader}},
'isKeyInQuery' => {{#isKeyInQuery}}true{{/isKeyInQuery}}{{^isKeyInQuery}}false{{/isKeyInQuery}},
'isKeyInCookie' => {{#isKeyInCookie}}true{{/isKeyInCookie}}{{^isKeyInCookie}}false{{/isKeyInCookie}},
],
{{/isApiKey}}
{{#isOAuth}}
[
'type' => '{{type}}',
'isBasic' => false,
'isBearer' => false,
'isApiKey' => false,
'isOAuth' => true,
'scopes' => [
{{#scopes}}
'{{scope}}',{{#description}} // {{.}}{{/description}}
{{/scopes}}
],
],
{{/isOAuth}}
{{/authMethods}}
{{/hasAuthMethods}}
],
],
{{/operation}}
{{/operations}}
{{/apis}}
];
/**
* Class constructor
*
* @param ContainerInterface|array $settings Either a ContainerInterface or an associative array of app settings
*
* @throws HttpNotImplementedException When implementation class doesn't exists
* @throws Exception when not supported authorization schema type provided
*/
public function __construct($settings = [])
{
if ($settings instanceof ContainerInterface) {
// Set container to create App with on AppFactory
AppFactory::setContainer($settings);
}
$this->slimApp = AppFactory::create();
// middlewares requires Psr\Container\ContainerInterface
$container = $this->slimApp->getContainer();
{{#hasAuthMethods}}
$authPackage = '{{authPackage}}';
$basicAuthenticator = function (ServerRequestInterface &$request, TokenSearch $tokenSearch) use ($authPackage) {
$message = "How about extending {{abstractNamePrefix}}Authenticator{{abstractNameSuffix}} class by {$authPackage}\BasicAuthenticator?";
throw new HttpNotImplementedException($request, $message);
};
$apiKeyAuthenticator = function (ServerRequestInterface &$request, TokenSearch $tokenSearch) use ($authPackage) {
$message = "How about extending {{abstractNamePrefix}}Authenticator{{abstractNameSuffix}} class by {$authPackage}\ApiKeyAuthenticator?";
throw new HttpNotImplementedException($request, $message);
};
$oAuthAuthenticator = function (ServerRequestInterface &$request, TokenSearch $tokenSearch) use ($authPackage) {
$message = "How about extending {{abstractNamePrefix}}Authenticator{{abstractNameSuffix}} class by {$authPackage}\OAuthAuthenticator?";
throw new HttpNotImplementedException($request, $message);
};
{{/hasAuthMethods}}
$userOptions = $this->getSetting($settings, 'tokenAuthenticationOptions', null);
// mocker options
$mockerOptions = $this->getSetting($settings, 'mockerOptions', null);
$dataMocker = $mockerOptions['dataMocker'] ?? new OpenApiDataMocker();
{{#isSlimPsr7}}
$responseFactory = new ResponseFactory();
{{/isSlimPsr7}}
{{#isNyholmPsr7}}
$responseFactory = new Psr17Factory();
{{/isNyholmPsr7}}
{{#isGuzzlePsr7}}
$responseFactory = new HttpFactory();
{{/isGuzzlePsr7}}
{{#isZendDiactoros}}
$responseFactory = new ResponseFactory();
{{/isZendDiactoros}}
$getMockStatusCodeCallback = $mockerOptions['getMockStatusCodeCallback'] ?? null;
$mockAfterCallback = $mockerOptions['afterCallback'] ?? null;
foreach ($this->operations as $operation) {
$callback = function ($request, $response, $arguments) use ($operation) {
$message = "How about extending {$operation['classname']} by {$operation['apiPackage']}\\{$operation['userClassname']} class implementing {$operation['operationId']} as a {$operation['httpMethod']} method?";
throw new HttpNotImplementedException($request, $message);
};
$middlewares = [];
if (class_exists("\\{$operation['apiPackage']}\\{$operation['userClassname']}")) {
$callback = "\\{$operation['apiPackage']}\\{$operation['userClassname']}:{$operation['operationId']}";
}
{{#hasAuthMethods}}
foreach ($operation['authMethods'] as $authMethod) {
switch ($authMethod['type']) {
case 'http':
$authenticatorClassname = "\\{$authPackage}\\BasicAuthenticator";
if (class_exists($authenticatorClassname)) {
$basicAuthenticator = new $authenticatorClassname($container);
}
$middlewares[] = new TokenAuthentication($this->getTokenAuthenticationOptions([
'authenticator' => $basicAuthenticator,
'regex' => $authMethod['isBearer'] ? '/Bearer\s+(.*)$/i' : '/Basic\s+(.*)$/i',
'header' => 'Authorization',
'parameter' => null,
'cookie' => null,
'argument' => null,
], $userOptions));
break;
case 'apiKey':
$authenticatorClassname = "\\{$authPackage}\\ApiKeyAuthenticator";
if (class_exists($authenticatorClassname)) {
$apiKeyAuthenticator = new $authenticatorClassname($container);
}
$middlewares[] = new TokenAuthentication($this->getTokenAuthenticationOptions([
'authenticator' => $apiKeyAuthenticator,
'regex' => '/^(.*)$/i',
'header' => $authMethod['isKeyInHeader'] ? $authMethod['keyParamName'] : null,
'parameter' => $authMethod['isKeyInQuery'] ? $authMethod['keyParamName'] : null,
'cookie' => $authMethod['isKeyInCookie'] ? $authMethod['keyParamName'] : null,
'argument' => null,
], $userOptions));
break;
case 'oauth2':
$authenticatorClassname = "\\{$authPackage}\\OAuthAuthenticator";
if (class_exists($authenticatorClassname)) {
$oAuthAuthenticator = new $authenticatorClassname($container, $authMethod['scopes']);
}
$middlewares[] = new TokenAuthentication($this->getTokenAuthenticationOptions([
'authenticator' => $oAuthAuthenticator,
'regex' => '/Bearer\s+(.*)$/i',
'header' => 'Authorization',
'parameter' => null,
'cookie' => null,
'argument' => null,
], $userOptions));
break;
default:
throw new Exception('Unknown authorization schema type');
}
}
{{/hasAuthMethods}}
if (is_callable($getMockStatusCodeCallback)) {
$mockSchemaResponses = array_map(function ($item) {
return json_decode($item['jsonSchema'], true);
}, $operation['responses']);
$middlewares[] = new OpenApiDataMockerRouteMiddleware($dataMocker, $mockSchemaResponses, $responseFactory, $getMockStatusCodeCallback, $mockAfterCallback);
}
$this->addRoute(
[$operation['httpMethod']],
"{$operation['basePathWithoutHost']}{$operation['path']}",
$callback,
$middlewares
)->setName($operation['operationId']);
}
}
/**
* Merges user defined options with dynamic params
*
* @param array $staticOptions Required static options
* @param array $userOptions User options
*
* @return array Merged array
*/
private function getTokenAuthenticationOptions(array $staticOptions, array $userOptions = null)
{
if (is_array($userOptions) === false) {
return $staticOptions;
}
return array_merge($userOptions, $staticOptions);
}
/**
* Returns app setting by name.
*
* @param ContainerInterface|array $settings Either a ContainerInterface or an associative array of app settings
* @param string $settingName Setting name
* @param mixed $default Default setting value.
*
* @return mixed
*/
private function getSetting($settings, $settingName, $default = null)
{
if ($settings instanceof ContainerInterface && $settings->has($settingName)) {
return $settings->get($settingName);
} elseif (is_array($settings) && array_key_exists($settingName, $settings)) {
return $settings[$settingName];
}
return $default;
}
/**
* Add route with multiple methods
*
* @param string[] $methods Numeric array of HTTP method names
* @param string $pattern The route URI pattern
* @param callable|string $callable The route callback routine
* @param array|null $middlewares List of middlewares
*
* @return RouteInterface
*
* @throws InvalidArgumentException If the route pattern isn't a string
*/
public function addRoute(array $methods, string $pattern, $callable, $middlewares = [])
{
$route = $this->slimApp->map($methods, $pattern, $callable);
foreach ($middlewares as $middleware) {
$route->add($middleware);
}
return $route;
}
/**
* Returns Slim Framework instance
*
* @return App
*/
public function getSlimApp()
{
return $this->slimApp;
}
}
{{/apiInfo}}

View File

@ -9,7 +9,6 @@
*/{{#apiInfo}} */{{#apiInfo}}
namespace {{authPackage}}; namespace {{authPackage}};
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Dyorg\TokenAuthentication; use Dyorg\TokenAuthentication;
use Dyorg\TokenAuthentication\TokenSearch; use Dyorg\TokenAuthentication\TokenSearch;
@ -24,12 +23,6 @@ use Dyorg\TokenAuthentication\Exceptions\UnauthorizedExceptionInterface;
*/ */
abstract class {{abstractNamePrefix}}Authenticator{{abstractNameSuffix}} abstract class {{abstractNamePrefix}}Authenticator{{abstractNameSuffix}}
{ {
/**
* @var ContainerInterface|null Slim app container instance
*/
protected $container;
/** /**
* @var string[]|null List of required scopes * @var string[]|null List of required scopes
*/ */
@ -49,12 +42,10 @@ abstract class {{abstractNamePrefix}}Authenticator{{abstractNameSuffix}}
/** /**
* Authenticator constructor * Authenticator constructor
* *
* @param ContainerInterface|null $container Slim app container instance
* @param string[]|null $requiredScope List of required scopes * @param string[]|null $requiredScope List of required scopes
*/ */
public function __construct(ContainerInterface $container = null, $requiredScope = null) public function __construct($requiredScope = null)
{ {
$this->container = $container;
$this->requiredScope = $requiredScope; $this->requiredScope = $requiredScope;
} }

View File

@ -6,10 +6,11 @@
* NOTE: This class is auto generated by the openapi generator program. * NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator * https://github.com/openapitools/openapi-generator
* Do not edit the class manually. * Do not edit the class manually.
* Extend this class with your controller. You can inject dependencies via class constructor,
* @see https://github.com/PHP-DI/Slim-Bridge basic example.
*/ */
namespace {{apiPackage}}; namespace {{apiPackage}};
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Slim\Exception\HttpNotImplementedException; use Slim\Exception\HttpNotImplementedException;
@ -23,25 +24,8 @@ use Slim\Exception\HttpNotImplementedException;
*/ */
abstract class {{classname}} abstract class {{classname}}
{ {
/**
* @var ContainerInterface|null Slim app container instance
*/
protected $container;
/**
* Route Controller constructor receives container
*
* @param ContainerInterface|null $container Slim app container instance
*/
public function __construct(ContainerInterface $container = null)
{
$this->container = $container;
}
{{#operations}} {{#operations}}
{{#operation}} {{#operation}}
/** /**
* {{httpMethod}} {{operationId}} * {{httpMethod}} {{operationId}}
{{#summary}} {{#summary}}
@ -56,7 +40,11 @@ abstract class {{classname}}
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments {{#hasPathParams}}
{{#pathParams}}
* @param {{{dataType}}} ${{paramName}}{{#description}} {{.}}{{/description}}{{^description}} {{paramName}}{{/description}}
{{/pathParams}}
{{/hasPathParams}}
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
@ -64,19 +52,21 @@ abstract class {{classname}}
* @deprecated * @deprecated
{{/isDeprecated}} {{/isDeprecated}}
*/ */
public function {{operationId}}(ServerRequestInterface $request, ResponseInterface $response, array $args) public function {{operationId}}(
{ ServerRequestInterface $request,
ResponseInterface $response{{#hasPathParams}},{{/hasPathParams}}
{{#hasPathParams}}
{{#pathParams}}
{{{dataType}}} ${{paramName}}{{^-last}},{{/-last}}
{{/pathParams}}
{{/hasPathParams}}
): ResponseInterface {
{{#hasHeaderParams}} {{#hasHeaderParams}}
$headers = $request->getHeaders(); $headers = $request->getHeaders();
{{#headerParams}} {{#headerParams}}
${{paramName}} = $request->hasHeader('{{baseName}}') ? $headers['{{baseName}}'] : null; ${{paramName}} = $request->hasHeader('{{baseName}}') ? $headers['{{baseName}}'] : null;
{{/headerParams}} {{/headerParams}}
{{/hasHeaderParams}} {{/hasHeaderParams}}
{{#hasPathParams}}
{{#pathParams}}
${{paramName}} = $args['{{baseName}}'];
{{/pathParams}}
{{/hasPathParams}}
{{#hasQueryParams}} {{#hasQueryParams}}
$queryParams = $request->getQueryParams(); $queryParams = $request->getQueryParams();
{{#queryParams}} {{#queryParams}}
@ -105,6 +95,9 @@ abstract class {{classname}}
$message = "How about implementing {{nickname}} as a {{httpMethod}} method in {{apiPackage}}\{{userClassname}} class?"; $message = "How about implementing {{nickname}} as a {{httpMethod}} method in {{apiPackage}}\{{userClassname}} class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
} }
{{^-last}}
{{/-last}}
{{/operation}} {{/operation}}
{{/operations}} {{/operations}}
} }

View File

@ -9,30 +9,30 @@
], ],
"require": { "require": {
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
"slim/slim": "^4.5.0",
"dyorg/slim-token-authentication": "dev-slim4", "dyorg/slim-token-authentication": "dev-slim4",
"ybelenko/openapi-data-mocker": "^1.0",
"ybelenko/openapi-data-mocker-server-middleware": "^1.0",
{{#isSlimPsr7}}
"slim/psr7": "^1.1.0"
{{/isSlimPsr7}}
{{#isNyholmPsr7}}
"nyholm/psr7": "^1.3.0",
"nyholm/psr7-server": "^0.4.1"
{{/isNyholmPsr7}}
{{#isGuzzlePsr7}} {{#isGuzzlePsr7}}
"guzzlehttp/psr7": "^1.6.1", "guzzlehttp/psr7": "^1.6.1",
"http-interop/http-factory-guzzle": "^1.0.0" "http-interop/http-factory-guzzle": "^1.0.0",
{{/isGuzzlePsr7}} {{/isGuzzlePsr7}}
{{#isZendDiactoros}} {{#isZendDiactoros}}
"laminas/laminas-diactoros": "^2.3.0" "laminas/laminas-diactoros": "^2.3.0",
{{/isZendDiactoros}} {{/isZendDiactoros}}
{{#isNyholmPsr7}}
"nyholm/psr7": "^1.3.0",
"nyholm/psr7-server": "^0.4.1",
{{/isNyholmPsr7}}
"php-di/slim-bridge": "^3.2",
{{#isSlimPsr7}}
"slim/psr7": "^1.1.0",
{{/isSlimPsr7}}
"ybelenko/openapi-data-mocker": "^1.0",
"ybelenko/openapi-data-mocker-server-middleware": "^1.0"
}, },
"require-dev": { "require-dev": {
"overtrue/phplint": "^2.0.2",
{{#generateTests}} {{#generateTests}}
"phpunit/phpunit": "^8.0 || ^9.0", "phpunit/phpunit": "^8.0 || ^9.0",
{{/generateTests}} {{/generateTests}}
"overtrue/phplint": "^2.0.2",
"squizlabs/php_codesniffer": "^3.5" "squizlabs/php_codesniffer": "^3.5"
}, },
"autoload": { "autoload": {
@ -58,5 +58,8 @@
{{/generateTests}} {{/generateTests}}
"phpcs": "phpcs", "phpcs": "phpcs",
"phplint": "phplint ./ --exclude=vendor" "phplint": "phplint ./ --exclude=vendor"
},
"config": {
"sort-packages": true
} }
} }

View File

@ -0,0 +1,35 @@
<?php
/**
* App configuration defaults for development.
* This file used when 'APP_ENV' variable set to 'dev' or 'development'. Check public/.htaccess file
*/
// Enable error reporting
error_reporting(E_ALL);
ini_set("display_errors", 1);
/**
* Each environment(dev, prod) should contain two files default.inc.php and config.inc.php.
* This is the first file with development defaults. It contains all data which can be safely committed
* to VCS(version control system). For sensitive values(passwords, api keys, emails) use config.inc.php
* and make sure it's excluded from VCS by .gitignore.
* do not add dependencies here, use {{appPackage}}\RegisterDependencies class
* @see https://php-di.org/doc/php-definitions.html#values
*/
return [
'mode' => 'development',
// slim framework settings
'slim.displayErrorDetails' => true,
'slim.logErrors' => false,
'slim.logErrorDetails' => false,
// PDO
'pdo.dsn' => 'mysql:host=localhost;charset=utf8mb4',
'pdo.username' => 'root',
'pdo.password' => 'root',
'pdo.options' => [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
],
];

View File

@ -1,83 +0,0 @@
<?php
{{>licenseInfo}}
/**
* App configuration file example.
*
* Copy file to config/dev/config.inc.php and config/prod/config.inc.php
* App loads dev config only when prod doesn't exist
* in other words if both configs presented - prod config applies
*/
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use OpenAPIServer\Mock\OpenApiDataMocker;
$mocker = new OpenApiDataMocker();
$mocker->setModelsNamespace('{{modelPackage}}\\');
return [
'slimSettings' => [
'displayErrorDetails' => false,
'logErrors' => true,
'logErrorDetails' => true,
],
'tokenAuthenticationOptions' => [
/**
* Tokens are essentially passwords. You should treat them as such and you should always
* use HTTPS. If the middleware detects insecure usage over HTTP it will return unauthorized
* with a message Required HTTPS for token authentication. This rule is relaxed for requests
* on localhost. To allow insecure usage you must enable it manually by setting secure to
* false.
* Default: true
*/
// 'secure' => true,
/**
* Alternatively you can list your development host to have relaxed security.
* Default: ['localhost', '127.0.0.1']
*/
// 'relaxed' => ['localhost', '127.0.0.1'],
/**
* By default on occurred a fail on authentication, is sent a response on json format with a
* message (`Invalid Token` or `Not found Token`) and with the token (if found), with status
* `401 Unauthorized`. You can customize it by setting a callable function on error option.
* Default: null
*/
// 'error' => null,
],
'mockerOptions' => [
// 'dataMocker' => $mocker,
// 'getMockStatusCodeCallback' => function (ServerRequestInterface $request, array $responses) {
// // check if client clearly asks for mocked response
// $pingHeader = 'X-{{invokerPackage}}-Mock';
// $pingHeaderCode = 'X-{{invokerPackage}}-Mock-Code';
// if (
// $request->hasHeader($pingHeader)
// && $request->getHeader($pingHeader)[0] === 'ping'
// ) {
// $responses = (array) $responses;
// $requestedResponseCode = ($request->hasHeader($pingHeaderCode)) ? $request->getHeader($pingHeaderCode)[0] : 'default';
// if (array_key_exists($requestedResponseCode, $responses)) {
// return $requestedResponseCode;
// }
// // return first response key
// reset($responses);
// return key($responses);
// }
// return false;
// },
// 'afterCallback' => function (ServerRequestInterface $request, ResponseInterface $response) {
// // mark mocked response to distinguish real and fake responses
// return $response->withHeader('X-{{invokerPackage}}-Mock', 'pong');
// },
],
];

View File

@ -0,0 +1,35 @@
<?php
/**
* App configuration defaults for production.
* This file used when 'APP_ENV' variable set to 'prod' or 'production'. Check public/.htaccess file
*/
// Disable error reporting
error_reporting(0);
ini_set('display_errors', '0');
/**
* Each environment(dev, prod) should contain two files default.inc.php and config.inc.php.
* This is the first file with production defaults. It contains all data which can be safely committed
* to VCS(version control system). For sensitive values(passwords, api keys, emails) use config.inc.php
* and make sure it's excluded from VCS by .gitignore.
* do not add dependencies here, use {{appPackage}}\RegisterDependencies class
* @see https://php-di.org/doc/php-definitions.html#values
*/
return [
'mode' => 'production',
// slim framework settings
'slim.displayErrorDetails' => false,
'slim.logErrors' => true,
'slim.logErrorDetails' => true,
// PDO
'pdo.dsn' => 'mysql:host=localhost;charset=utf8mb4',
'pdo.username' => 'root',
'pdo.password' => 'root',
'pdo.options' => [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
],
];

View File

@ -20,5 +20,5 @@ composer.phar
# Application config may contain sensitive data # Application config may contain sensitive data
/config/**/*.* /config/**/*.*
!/config/.htaccess !/config/.htaccess
!/config/dev/example.inc.php !/config/dev/default.inc.php
!/config/prod/example.inc.php !/config/prod/default.inc.php

View File

@ -9,49 +9,64 @@
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
use {{invokerPackage}}\SlimRouter; use DI\Bridge\Slim\Bridge;
use Psr\Http\Message\ServerRequestInterface; use DI\ContainerBuilder;
use Psr\Http\Message\ResponseInterface; use {{appPackage}}\RegisterDependencies;
use OpenAPIServer\Mock\OpenApiDataMocker; use {{appPackage}}\RegisterRoutes;
use {{appPackage}}\RegisterMiddlewares;
use Slim\Factory\ServerRequestCreatorFactory;
use Slim\ResponseEmitter;
{{/apiInfo}} {{/apiInfo}}
// load config file // Instantiate PHP-DI ContainerBuilder
$config = []; $builder = new ContainerBuilder();
if (is_array($prodConfig = @include(__DIR__ . '/../config/prod/config.inc.php'))) {
$config = $prodConfig; // consider prod by default
} elseif (is_array($devConfig = @include(__DIR__ . '/../config/dev/config.inc.php'))) { $env;
$config = $devConfig; switch (strtolower($_SERVER['APP_ENV'] ?? 'prod')) {
} else { case 'development':
throw new InvalidArgumentException('Config file missed or broken.'); case 'dev':
$env = 'dev';
break;
case 'production':
case 'prod':
default:
$env = 'prod';
} }
$router = new SlimRouter($config); // Main configuration
$app = $router->getSlimApp(); $builder->addDefinitions(__DIR__ . "/../config/{$env}/default.inc.php");
// Parse json, form data and xml // Config file for the environment if exists
$app->addBodyParsingMiddleware(); $userConfig = __DIR__ . "/../config/{$env}/config.inc.php";
if (file_exists($userConfig)) {
$builder->addDefinitions($userConfig);
}
/** // Set up dependencies
* The routing middleware should be added before the ErrorMiddleware $dependencies = new RegisterDependencies();
* Otherwise exceptions thrown from it will not be handled $dependencies($builder);
*/
$app->addRoutingMiddleware();
/** // Build PHP-DI Container instance
* Add Error Handling Middleware $container = $builder->build();
*
* @param bool $displayErrorDetails -> Should be set to false in production
* @param bool $logErrors -> Parameter is passed to the default ErrorHandler
* @param bool $logErrorDetails -> Display error details in error log
* which can be replaced by a callable of your choice.
* Note: This middleware should be added last. It will not handle any exceptions/errors // Instantiate the app
* for middleware added after it. $app = Bridge::create($container);
*/
$app->addErrorMiddleware(
$config['slimSettings']['displayErrorDetails'] ?? false,
$config['slimSettings']['logErrors'] ?? true,
$config['slimSettings']['logErrorDetails'] ?? true
);
$app->run(); // Register middleware
$middleware = new RegisterMiddlewares();
$middleware($app);
// Register routes
// yes, it's anti-pattern you shouldn't get deps from container directly
$routes = $container->get(RegisterRoutes::class);
$routes($app);
// Create Request object from globals
$serverRequestCreator = ServerRequestCreatorFactory::create();
$request = $serverRequestCreator->createServerRequestFromGlobals();
// Run App & Emit Response
$response = $app->handle($request);
$responseEmitter = new ResponseEmitter();
$responseEmitter->emit($response);

View File

@ -0,0 +1,58 @@
<?php
{{>licenseInfo}}
declare(strict_types=1);
/**
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* Do not edit the class manually.
*/
namespace {{appPackage}};
/**
* RegisterDependencies
*
* Recommendations from template creator:
*
* I don't use imports(eg. use Slim\Middleware\ErrorMiddleware) here because each package unlikely
* be used in code twice. It helps to keep that file short and make Git history cleaner.
*
* This class declared as final because two classes with dependency injections can cause confusion. Edit
* template of this class or use your own implementation instead(overwrite index.php to import your
* custom class).
*/
final class RegisterDependencies
{
/**
* Adds dependency definitions.
*
* @param \DI\ContainerBuilder $containerBuilder Container builder.
*
* @see https://php-di.org/doc/php-definitions.html
*/
public function __invoke(\DI\ContainerBuilder $containerBuilder): void
{
$containerBuilder->addDefinitions([
// Response factory required as typed argument in next ErrorMiddleware injection
\Psr\Http\Message\ResponseFactoryInterface::class => \DI\factory([\Slim\Factory\AppFactory::class, 'determineResponseFactory']),
// Slim error middleware
// @see https://www.slimframework.com/docs/v4/middleware/error-handling.html
\Slim\Middleware\ErrorMiddleware::class => \DI\autowire()
->constructorParameter('displayErrorDetails', \DI\get('slim.displayErrorDetails', false))
->constructorParameter('logErrors', \DI\get('slim.logErrors', true))
->constructorParameter('logErrorDetails', \DI\get('slim.logErrorDetails', true)),
// PDO class for database managing
\PDO::class => \DI\create()
->constructor(
\DI\get('pdo.dsn'),
\DI\get('pdo.username'),
\DI\get('pdo.password'),
\DI\get('pdo.options', null)
),
]);
}
}

View File

@ -0,0 +1,51 @@
<?php
{{>licenseInfo}}
declare(strict_types=1);
/**
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* Do not edit the class manually.
*/
namespace {{appPackage}};
/**
* RegisterMiddlewares
*
* Recommendations from template creator:
*
* There is no way to add route related middlewares here, add global ones. Route related middlewares
* can be applied in \{{appPackage}}\RegisterRoutes class.
*
* I add middlewares by full class names(\Slim\Middleware\ErrorMiddleware::class) because that way
* Slim initiates them with options from Container. They already configured, don't need to pass any
* options manually.
*
* I don't use imports(eg. use Slim\Middleware\ErrorMiddleware) here because each package unlikely
* be used in code twice. It helps to keep that file short and make Git history cleaner.
*
* This class declared as final because two classes with middlewares can cause confusion. Edit
* template of this class or use your own implementation instead(overwrite index.php to import your
* custom class).
*/
final class RegisterMiddlewares
{
/**
* Adds middlewares to Slim app instance.
*
* @param \Slim\App $app App instance.
*/
public function __invoke(\Slim\App $app): void
{
// Parse json, form data and xml
$app->addBodyParsingMiddleware();
// Add Routing Middleware
$app->addRoutingMiddleware();
// Add Error Middleware
$app->add(\Slim\Middleware\ErrorMiddleware::class);
}
}

View File

@ -0,0 +1,137 @@
<?php
{{>licenseInfo}}
declare(strict_types=1);
/**
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* Do not edit the class manually.
*/{{#apiInfo}}
namespace {{appPackage}};
use Slim\Exception\HttpNotImplementedException;
/**
* RegisterRoutes Class Doc Comment
*
* @package {{invokerPackage}}
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
class RegisterRoutes
{
/** @var array[] list of all api operations */
private $operations = [
{{#apis}}
{{#operations}}
{{#operation}}
[
'httpMethod' => '{{httpMethod}}',
'basePathWithoutHost' => '{{{basePathWithoutHost}}}',
'path' => '{{{path}}}',
'apiPackage' => '{{apiPackage}}',
'classname' => '{{classname}}',
'userClassname' => '{{userClassname}}',
'operationId' => '{{operationId}}',
'responses' => [
{{#responses}}
'{{#isDefault}}default{{/isDefault}}{{^isDefault}}{{code}}{{/isDefault}}' => [
'jsonSchema' => '{{{jsonSchema}}}',
],
{{/responses}}
],
'authMethods' => [
{{#hasAuthMethods}}
{{#authMethods}}
// {{type}} security schema named '{{name}}'
{{#isBasicBasic}}
[
'type' => '{{type}}',
'isBasic' => true,
'isBearer' => false,
'isApiKey' => false,
'isOAuth' => false,
],
{{/isBasicBasic}}
{{#isBasicBearer}}
[
'type' => '{{type}}',
'isBasic' => true,
'isBearer' => true,
'isApiKey' => false,
'isOAuth' => false,
],
{{/isBasicBearer}}
{{#isApiKey}}
[
'type' => '{{type}}',
'isBasic' => false,
'isBearer' => false,
'isApiKey' => true,
'isOAuth' => false,
'keyParamName' => '{{keyParamName}}',
'isKeyInHeader' => {{#isKeyInHeader}}true{{/isKeyInHeader}}{{^isKeyInHeader}}false{{/isKeyInHeader}},
'isKeyInQuery' => {{#isKeyInQuery}}true{{/isKeyInQuery}}{{^isKeyInQuery}}false{{/isKeyInQuery}},
'isKeyInCookie' => {{#isKeyInCookie}}true{{/isKeyInCookie}}{{^isKeyInCookie}}false{{/isKeyInCookie}},
],
{{/isApiKey}}
{{#isOAuth}}
[
'type' => '{{type}}',
'isBasic' => false,
'isBearer' => false,
'isApiKey' => false,
'isOAuth' => true,
'scopes' => [
{{#scopes}}
'{{scope}}',{{#description}} // {{.}}{{/description}}
{{/scopes}}
],
],
{{/isOAuth}}
{{/authMethods}}
{{/hasAuthMethods}}
],
],
{{/operation}}
{{/operations}}
{{/apis}}
];
/**
* Add routes to Slim app.
*
* @param \Slim\App $app Pre-configured Slim application instance
*
* @throws HttpNotImplementedException When implementation class doesn't exists
*/
public function __invoke(\Slim\App $app): void
{
foreach ($this->operations as $operation) {
$callback = function ($request) use ($operation) {
$message = "How about extending {$operation['classname']} by {$operation['apiPackage']}\\{$operation['userClassname']} class implementing {$operation['operationId']} as a {$operation['httpMethod']} method?";
throw new HttpNotImplementedException($request, $message);
};
$middlewares = [];
if (class_exists("\\{$operation['apiPackage']}\\{$operation['userClassname']}")) {
// Notice how we register the controller using the class name?
// PHP-DI will instantiate the class for us only when it's actually necessary
$callback = ["\\{$operation['apiPackage']}\\{$operation['userClassname']}", $operation['operationId']];
}
$route = $app->map(
[$operation['httpMethod']],
"{$operation['basePathWithoutHost']}{$operation['path']}",
$callback
)->setName($operation['operationId']);
foreach ($middlewares as $middleware) {
$route->add($middleware);
}
}
}
}
{{/apiInfo}}

View File

@ -20,5 +20,5 @@ composer.phar
# Application config may contain sensitive data # Application config may contain sensitive data
/config/**/*.* /config/**/*.*
!/config/.htaccess !/config/.htaccess
!/config/dev/example.inc.php !/config/dev/default.inc.php
!/config/prod/example.inc.php !/config/prod/default.inc.php

View File

@ -2,11 +2,14 @@
README.md README.md
composer.json composer.json
config/.htaccess config/.htaccess
config/dev/example.inc.php config/dev/default.inc.php
config/prod/example.inc.php config/prod/default.inc.php
lib/Api/AbstractPetApi.php lib/Api/AbstractPetApi.php
lib/Api/AbstractStoreApi.php lib/Api/AbstractStoreApi.php
lib/Api/AbstractUserApi.php lib/Api/AbstractUserApi.php
lib/App/RegisterDependencies.php
lib/App/RegisterMiddlewares.php
lib/App/RegisterRoutes.php
lib/Auth/AbstractAuthenticator.php lib/Auth/AbstractAuthenticator.php
lib/BaseModel.php lib/BaseModel.php
lib/Model/ApiResponse.php lib/Model/ApiResponse.php
@ -15,7 +18,6 @@ lib/Model/Order.php
lib/Model/Pet.php lib/Model/Pet.php
lib/Model/Tag.php lib/Model/Tag.php
lib/Model/User.php lib/Model/User.php
lib/SlimRouter.php
phpcs.xml.dist phpcs.xml.dist
phpunit.xml.dist phpunit.xml.dist
public/.htaccess public/.htaccess

View File

@ -4,6 +4,7 @@
* [Slim 4 Documentation](https://www.slimframework.com/docs/v4/) * [Slim 4 Documentation](https://www.slimframework.com/docs/v4/)
This server has been generated with [Slim PSR-7](https://github.com/slimphp/Slim-Psr7) implementation. This server has been generated with [Slim PSR-7](https://github.com/slimphp/Slim-Psr7) implementation.
[PHP-DI](https://php-di.org/doc/frameworks/slim.html) package used as dependency container.
## Requirements ## Requirements
@ -23,7 +24,10 @@ $ composer install
## Add configs ## Add configs
Application requires at least one config file(`config/dev/config.inc.php` or `config/prod/config.inc.php`). You can use [config/dev/example.inc.php](config/dev/example.inc.php) as starting point. [PHP-DI package](https://php-di.org/doc/getting-started.html) helps to decouple configuration from implementation. App loads configuration files in straight order(`$env` can be `prod` or `dev`):
1. `config/$env/default.inc.php` (contains safe values, can be committed to vcs)
2. `config/$env/config.inc.php` (user config, excluded from vcs, can contain sensitive values, passwords etc.)
3. `lib/App/RegisterDependencies.php`
## Start devserver ## Start devserver
@ -86,25 +90,14 @@ $ composer phplint
## Show errors ## Show errors
Switch on option in your application config file like: Switch your app environment to development in `public/.htaccess` file:
```diff ```ini
return [ ## .htaccess
'slimSettings' => [ <IfModule mod_env.c>
- 'displayErrorDetails' => false, SetEnv APP_ENV 'development'
+ 'displayErrorDetails' => true, </IfModule>
'logErrors' => true,
'logErrorDetails' => true,
],
``` ```
## Mock Server
For a quick start uncomment [mocker middleware options](config/dev/example.inc.php#L67-L94) in your application config file.
Used packages:
* [Openapi Data Mocker](https://github.com/ybelenko/openapi-data-mocker) - first implementation of OAS3 fake data generator.
* [Openapi Data Mocker Server Middleware](https://github.com/ybelenko/openapi-data-mocker-server-middleware) - PSR-15 HTTP server middleware.
* [Openapi Data Mocker Interfaces](https://github.com/ybelenko/openapi-data-mocker-interfaces) - package with mocking interfaces.
## API Endpoints ## API Endpoints
All URIs are relative to *http://petstore.swagger.io/v2* All URIs are relative to *http://petstore.swagger.io/v2*
@ -117,17 +110,22 @@ All URIs are relative to *http://petstore.swagger.io/v2*
namespace OpenAPIServer\Api; namespace OpenAPIServer\Api;
use OpenAPIServer\Api\AbstractPetApi; use OpenAPIServer\Api\AbstractPetApi;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
class PetApi extends AbstractPetApi class PetApi extends AbstractPetApi
{ {
public function addPet(
public function addPet($request, $response, $args) ServerRequestInterface $request,
{ ResponseInterface $response
): ResponseInterface {
// your implementation of addPet method here // your implementation of addPet method here
} }
} }
``` ```
When you need to inject dependencies into API controller check [PHP-DI - Controllers as services](https://github.com/PHP-DI/Slim-Bridge#controllers-as-services) guide.
Place all your implementation classes in `./src` folder accordingly. Place all your implementation classes in `./src` folder accordingly.
For instance, when abstract class located at `./lib/Api/AbstractPetApi.php` you need to create implementation class at `./src/Api/PetApi.php`. For instance, when abstract class located at `./lib/Api/AbstractPetApi.php` you need to create implementation class at `./src/Api/PetApi.php`.

View File

@ -9,15 +9,15 @@
], ],
"require": { "require": {
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
"slim/slim": "^4.5.0",
"dyorg/slim-token-authentication": "dev-slim4", "dyorg/slim-token-authentication": "dev-slim4",
"php-di/slim-bridge": "^3.2",
"slim/psr7": "^1.1.0",
"ybelenko/openapi-data-mocker": "^1.0", "ybelenko/openapi-data-mocker": "^1.0",
"ybelenko/openapi-data-mocker-server-middleware": "^1.0", "ybelenko/openapi-data-mocker-server-middleware": "^1.0"
"slim/psr7": "^1.1.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^8.0 || ^9.0",
"overtrue/phplint": "^2.0.2", "overtrue/phplint": "^2.0.2",
"phpunit/phpunit": "^8.0 || ^9.0",
"squizlabs/php_codesniffer": "^3.5" "squizlabs/php_codesniffer": "^3.5"
}, },
"autoload": { "autoload": {
@ -37,5 +37,8 @@
"test-models": "phpunit --testsuite Models", "test-models": "phpunit --testsuite Models",
"phpcs": "phpcs", "phpcs": "phpcs",
"phplint": "phplint ./ --exclude=vendor" "phplint": "phplint ./ --exclude=vendor"
},
"config": {
"sort-packages": true
} }
} }

View File

@ -0,0 +1,35 @@
<?php
/**
* App configuration defaults for development.
* This file used when 'APP_ENV' variable set to 'dev' or 'development'. Check public/.htaccess file
*/
// Enable error reporting
error_reporting(E_ALL);
ini_set("display_errors", 1);
/**
* Each environment(dev, prod) should contain two files default.inc.php and config.inc.php.
* This is the first file with development defaults. It contains all data which can be safely committed
* to VCS(version control system). For sensitive values(passwords, api keys, emails) use config.inc.php
* and make sure it's excluded from VCS by .gitignore.
* do not add dependencies here, use OpenAPIServer\App\RegisterDependencies class
* @see https://php-di.org/doc/php-definitions.html#values
*/
return [
'mode' => 'development',
// slim framework settings
'slim.displayErrorDetails' => true,
'slim.logErrors' => false,
'slim.logErrorDetails' => false,
// PDO
'pdo.dsn' => 'mysql:host=localhost;charset=utf8mb4',
'pdo.username' => 'root',
'pdo.password' => 'root',
'pdo.options' => [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
],
];

View File

@ -1,96 +0,0 @@
<?php
/**
* OpenAPI Petstore
* PHP version 7.4
*
* @package OpenAPIServer
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
/**
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
* The version of the OpenAPI document: 1.0.0
* Generated by: https://github.com/openapitools/openapi-generator.git
*/
/**
* App configuration file example.
*
* Copy file to config/dev/config.inc.php and config/prod/config.inc.php
* App loads dev config only when prod doesn't exist
* in other words if both configs presented - prod config applies
*/
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use OpenAPIServer\Mock\OpenApiDataMocker;
$mocker = new OpenApiDataMocker();
$mocker->setModelsNamespace('OpenAPIServer\Model\\');
return [
'slimSettings' => [
'displayErrorDetails' => false,
'logErrors' => true,
'logErrorDetails' => true,
],
'tokenAuthenticationOptions' => [
/**
* Tokens are essentially passwords. You should treat them as such and you should always
* use HTTPS. If the middleware detects insecure usage over HTTP it will return unauthorized
* with a message Required HTTPS for token authentication. This rule is relaxed for requests
* on localhost. To allow insecure usage you must enable it manually by setting secure to
* false.
* Default: true
*/
// 'secure' => true,
/**
* Alternatively you can list your development host to have relaxed security.
* Default: ['localhost', '127.0.0.1']
*/
// 'relaxed' => ['localhost', '127.0.0.1'],
/**
* By default on occurred a fail on authentication, is sent a response on json format with a
* message (`Invalid Token` or `Not found Token`) and with the token (if found), with status
* `401 Unauthorized`. You can customize it by setting a callable function on error option.
* Default: null
*/
// 'error' => null,
],
'mockerOptions' => [
// 'dataMocker' => $mocker,
// 'getMockStatusCodeCallback' => function (ServerRequestInterface $request, array $responses) {
// // check if client clearly asks for mocked response
// $pingHeader = 'X-OpenAPIServer-Mock';
// $pingHeaderCode = 'X-OpenAPIServer-Mock-Code';
// if (
// $request->hasHeader($pingHeader)
// && $request->getHeader($pingHeader)[0] === 'ping'
// ) {
// $responses = (array) $responses;
// $requestedResponseCode = ($request->hasHeader($pingHeaderCode)) ? $request->getHeader($pingHeaderCode)[0] : 'default';
// if (array_key_exists($requestedResponseCode, $responses)) {
// return $requestedResponseCode;
// }
// // return first response key
// reset($responses);
// return key($responses);
// }
// return false;
// },
// 'afterCallback' => function (ServerRequestInterface $request, ResponseInterface $response) {
// // mark mocked response to distinguish real and fake responses
// return $response->withHeader('X-OpenAPIServer-Mock', 'pong');
// },
],
];

View File

@ -0,0 +1,35 @@
<?php
/**
* App configuration defaults for production.
* This file used when 'APP_ENV' variable set to 'prod' or 'production'. Check public/.htaccess file
*/
// Disable error reporting
error_reporting(0);
ini_set('display_errors', '0');
/**
* Each environment(dev, prod) should contain two files default.inc.php and config.inc.php.
* This is the first file with production defaults. It contains all data which can be safely committed
* to VCS(version control system). For sensitive values(passwords, api keys, emails) use config.inc.php
* and make sure it's excluded from VCS by .gitignore.
* do not add dependencies here, use OpenAPIServer\App\RegisterDependencies class
* @see https://php-di.org/doc/php-definitions.html#values
*/
return [
'mode' => 'production',
// slim framework settings
'slim.displayErrorDetails' => false,
'slim.logErrors' => true,
'slim.logErrorDetails' => true,
// PDO
'pdo.dsn' => 'mysql:host=localhost;charset=utf8mb4',
'pdo.username' => 'root',
'pdo.password' => 'root',
'pdo.options' => [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
],
];

View File

@ -1,96 +0,0 @@
<?php
/**
* OpenAPI Petstore
* PHP version 7.4
*
* @package OpenAPIServer
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
/**
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
* The version of the OpenAPI document: 1.0.0
* Generated by: https://github.com/openapitools/openapi-generator.git
*/
/**
* App configuration file example.
*
* Copy file to config/dev/config.inc.php and config/prod/config.inc.php
* App loads dev config only when prod doesn't exist
* in other words if both configs presented - prod config applies
*/
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use OpenAPIServer\Mock\OpenApiDataMocker;
$mocker = new OpenApiDataMocker();
$mocker->setModelsNamespace('OpenAPIServer\Model\\');
return [
'slimSettings' => [
'displayErrorDetails' => false,
'logErrors' => true,
'logErrorDetails' => true,
],
'tokenAuthenticationOptions' => [
/**
* Tokens are essentially passwords. You should treat them as such and you should always
* use HTTPS. If the middleware detects insecure usage over HTTP it will return unauthorized
* with a message Required HTTPS for token authentication. This rule is relaxed for requests
* on localhost. To allow insecure usage you must enable it manually by setting secure to
* false.
* Default: true
*/
// 'secure' => true,
/**
* Alternatively you can list your development host to have relaxed security.
* Default: ['localhost', '127.0.0.1']
*/
// 'relaxed' => ['localhost', '127.0.0.1'],
/**
* By default on occurred a fail on authentication, is sent a response on json format with a
* message (`Invalid Token` or `Not found Token`) and with the token (if found), with status
* `401 Unauthorized`. You can customize it by setting a callable function on error option.
* Default: null
*/
// 'error' => null,
],
'mockerOptions' => [
// 'dataMocker' => $mocker,
// 'getMockStatusCodeCallback' => function (ServerRequestInterface $request, array $responses) {
// // check if client clearly asks for mocked response
// $pingHeader = 'X-OpenAPIServer-Mock';
// $pingHeaderCode = 'X-OpenAPIServer-Mock-Code';
// if (
// $request->hasHeader($pingHeader)
// && $request->getHeader($pingHeader)[0] === 'ping'
// ) {
// $responses = (array) $responses;
// $requestedResponseCode = ($request->hasHeader($pingHeaderCode)) ? $request->getHeader($pingHeaderCode)[0] : 'default';
// if (array_key_exists($requestedResponseCode, $responses)) {
// return $requestedResponseCode;
// }
// // return first response key
// reset($responses);
// return key($responses);
// }
// return false;
// },
// 'afterCallback' => function (ServerRequestInterface $request, ResponseInterface $response) {
// // mark mocked response to distinguish real and fake responses
// return $response->withHeader('X-OpenAPIServer-Mock', 'pong');
// },
],
];

View File

@ -19,10 +19,11 @@
* NOTE: This class is auto generated by the openapi generator program. * NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator * https://github.com/openapitools/openapi-generator
* Do not edit the class manually. * Do not edit the class manually.
* Extend this class with your controller. You can inject dependencies via class constructor,
* @see https://github.com/PHP-DI/Slim-Bridge basic example.
*/ */
namespace OpenAPIServer\Api; namespace OpenAPIServer\Api;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Slim\Exception\HttpNotImplementedException; use Slim\Exception\HttpNotImplementedException;
@ -36,23 +37,6 @@ use Slim\Exception\HttpNotImplementedException;
*/ */
abstract class AbstractPetApi abstract class AbstractPetApi
{ {
/**
* @var ContainerInterface|null Slim app container instance
*/
protected $container;
/**
* Route Controller constructor receives container
*
* @param ContainerInterface|null $container Slim app container instance
*/
public function __construct(ContainerInterface $container = null)
{
$this->container = $container;
}
/** /**
* POST addPet * POST addPet
* Summary: Add a new pet to the store * Summary: Add a new pet to the store
@ -60,13 +44,14 @@ abstract class AbstractPetApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function addPet(ServerRequestInterface $request, ResponseInterface $response, array $args) public function addPet(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$body = $request->getParsedBody(); $body = $request->getParsedBody();
$message = "How about implementing addPet as a POST method in OpenAPIServer\Api\PetApi class?"; $message = "How about implementing addPet as a POST method in OpenAPIServer\Api\PetApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
@ -78,16 +63,18 @@ abstract class AbstractPetApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments * @param int $petId Pet id to delete
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function deletePet(ServerRequestInterface $request, ResponseInterface $response, array $args) public function deletePet(
{ ServerRequestInterface $request,
ResponseInterface $response,
int $petId
): ResponseInterface {
$headers = $request->getHeaders(); $headers = $request->getHeaders();
$apiKey = $request->hasHeader('api_key') ? $headers['api_key'] : null; $apiKey = $request->hasHeader('api_key') ? $headers['api_key'] : null;
$petId = $args['petId'];
$message = "How about implementing deletePet as a DELETE method in OpenAPIServer\Api\PetApi class?"; $message = "How about implementing deletePet as a DELETE method in OpenAPIServer\Api\PetApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
} }
@ -100,13 +87,14 @@ abstract class AbstractPetApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function findPetsByStatus(ServerRequestInterface $request, ResponseInterface $response, array $args) public function findPetsByStatus(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$queryParams = $request->getQueryParams(); $queryParams = $request->getQueryParams();
$status = (key_exists('status', $queryParams)) ? $queryParams['status'] : null; $status = (key_exists('status', $queryParams)) ? $queryParams['status'] : null;
$message = "How about implementing findPetsByStatus as a GET method in OpenAPIServer\Api\PetApi class?"; $message = "How about implementing findPetsByStatus as a GET method in OpenAPIServer\Api\PetApi class?";
@ -121,14 +109,15 @@ abstract class AbstractPetApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
* @deprecated * @deprecated
*/ */
public function findPetsByTags(ServerRequestInterface $request, ResponseInterface $response, array $args) public function findPetsByTags(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$queryParams = $request->getQueryParams(); $queryParams = $request->getQueryParams();
$tags = (key_exists('tags', $queryParams)) ? $queryParams['tags'] : null; $tags = (key_exists('tags', $queryParams)) ? $queryParams['tags'] : null;
$message = "How about implementing findPetsByTags as a GET method in OpenAPIServer\Api\PetApi class?"; $message = "How about implementing findPetsByTags as a GET method in OpenAPIServer\Api\PetApi class?";
@ -143,14 +132,16 @@ abstract class AbstractPetApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments * @param int $petId ID of pet to return
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function getPetById(ServerRequestInterface $request, ResponseInterface $response, array $args) public function getPetById(
{ ServerRequestInterface $request,
$petId = $args['petId']; ResponseInterface $response,
int $petId
): ResponseInterface {
$message = "How about implementing getPetById as a GET method in OpenAPIServer\Api\PetApi class?"; $message = "How about implementing getPetById as a GET method in OpenAPIServer\Api\PetApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
} }
@ -162,13 +153,14 @@ abstract class AbstractPetApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function updatePet(ServerRequestInterface $request, ResponseInterface $response, array $args) public function updatePet(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$body = $request->getParsedBody(); $body = $request->getParsedBody();
$message = "How about implementing updatePet as a PUT method in OpenAPIServer\Api\PetApi class?"; $message = "How about implementing updatePet as a PUT method in OpenAPIServer\Api\PetApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
@ -180,14 +172,16 @@ abstract class AbstractPetApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments * @param int $petId ID of pet that needs to be updated
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function updatePetWithForm(ServerRequestInterface $request, ResponseInterface $response, array $args) public function updatePetWithForm(
{ ServerRequestInterface $request,
$petId = $args['petId']; ResponseInterface $response,
int $petId
): ResponseInterface {
$body = $request->getParsedBody(); $body = $request->getParsedBody();
$name = (isset($body['name'])) ? $body['name'] : null; $name = (isset($body['name'])) ? $body['name'] : null;
$status = (isset($body['status'])) ? $body['status'] : null; $status = (isset($body['status'])) ? $body['status'] : null;
@ -202,14 +196,16 @@ abstract class AbstractPetApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments * @param int $petId ID of pet to update
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function uploadFile(ServerRequestInterface $request, ResponseInterface $response, array $args) public function uploadFile(
{ ServerRequestInterface $request,
$petId = $args['petId']; ResponseInterface $response,
int $petId
): ResponseInterface {
$body = $request->getParsedBody(); $body = $request->getParsedBody();
$additionalMetadata = (isset($body['additionalMetadata'])) ? $body['additionalMetadata'] : null; $additionalMetadata = (isset($body['additionalMetadata'])) ? $body['additionalMetadata'] : null;
$file = (key_exists('file', $request->getUploadedFiles())) ? $request->getUploadedFiles()['file'] : null; $file = (key_exists('file', $request->getUploadedFiles())) ? $request->getUploadedFiles()['file'] : null;

View File

@ -19,10 +19,11 @@
* NOTE: This class is auto generated by the openapi generator program. * NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator * https://github.com/openapitools/openapi-generator
* Do not edit the class manually. * Do not edit the class manually.
* Extend this class with your controller. You can inject dependencies via class constructor,
* @see https://github.com/PHP-DI/Slim-Bridge basic example.
*/ */
namespace OpenAPIServer\Api; namespace OpenAPIServer\Api;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Slim\Exception\HttpNotImplementedException; use Slim\Exception\HttpNotImplementedException;
@ -36,23 +37,6 @@ use Slim\Exception\HttpNotImplementedException;
*/ */
abstract class AbstractStoreApi abstract class AbstractStoreApi
{ {
/**
* @var ContainerInterface|null Slim app container instance
*/
protected $container;
/**
* Route Controller constructor receives container
*
* @param ContainerInterface|null $container Slim app container instance
*/
public function __construct(ContainerInterface $container = null)
{
$this->container = $container;
}
/** /**
* DELETE deleteOrder * DELETE deleteOrder
* Summary: Delete purchase order by ID * Summary: Delete purchase order by ID
@ -60,14 +44,16 @@ abstract class AbstractStoreApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments * @param string $orderId ID of the order that needs to be deleted
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function deleteOrder(ServerRequestInterface $request, ResponseInterface $response, array $args) public function deleteOrder(
{ ServerRequestInterface $request,
$orderId = $args['orderId']; ResponseInterface $response,
string $orderId
): ResponseInterface {
$message = "How about implementing deleteOrder as a DELETE method in OpenAPIServer\Api\StoreApi class?"; $message = "How about implementing deleteOrder as a DELETE method in OpenAPIServer\Api\StoreApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
} }
@ -80,13 +66,14 @@ abstract class AbstractStoreApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function getInventory(ServerRequestInterface $request, ResponseInterface $response, array $args) public function getInventory(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$message = "How about implementing getInventory as a GET method in OpenAPIServer\Api\StoreApi class?"; $message = "How about implementing getInventory as a GET method in OpenAPIServer\Api\StoreApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
} }
@ -99,14 +86,16 @@ abstract class AbstractStoreApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments * @param int $orderId ID of pet that needs to be fetched
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function getOrderById(ServerRequestInterface $request, ResponseInterface $response, array $args) public function getOrderById(
{ ServerRequestInterface $request,
$orderId = $args['orderId']; ResponseInterface $response,
int $orderId
): ResponseInterface {
$message = "How about implementing getOrderById as a GET method in OpenAPIServer\Api\StoreApi class?"; $message = "How about implementing getOrderById as a GET method in OpenAPIServer\Api\StoreApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
} }
@ -118,13 +107,14 @@ abstract class AbstractStoreApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function placeOrder(ServerRequestInterface $request, ResponseInterface $response, array $args) public function placeOrder(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$body = $request->getParsedBody(); $body = $request->getParsedBody();
$message = "How about implementing placeOrder as a POST method in OpenAPIServer\Api\StoreApi class?"; $message = "How about implementing placeOrder as a POST method in OpenAPIServer\Api\StoreApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);

View File

@ -19,10 +19,11 @@
* NOTE: This class is auto generated by the openapi generator program. * NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator * https://github.com/openapitools/openapi-generator
* Do not edit the class manually. * Do not edit the class manually.
* Extend this class with your controller. You can inject dependencies via class constructor,
* @see https://github.com/PHP-DI/Slim-Bridge basic example.
*/ */
namespace OpenAPIServer\Api; namespace OpenAPIServer\Api;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Slim\Exception\HttpNotImplementedException; use Slim\Exception\HttpNotImplementedException;
@ -36,23 +37,6 @@ use Slim\Exception\HttpNotImplementedException;
*/ */
abstract class AbstractUserApi abstract class AbstractUserApi
{ {
/**
* @var ContainerInterface|null Slim app container instance
*/
protected $container;
/**
* Route Controller constructor receives container
*
* @param ContainerInterface|null $container Slim app container instance
*/
public function __construct(ContainerInterface $container = null)
{
$this->container = $container;
}
/** /**
* POST createUser * POST createUser
* Summary: Create user * Summary: Create user
@ -60,13 +44,14 @@ abstract class AbstractUserApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function createUser(ServerRequestInterface $request, ResponseInterface $response, array $args) public function createUser(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$body = $request->getParsedBody(); $body = $request->getParsedBody();
$message = "How about implementing createUser as a POST method in OpenAPIServer\Api\UserApi class?"; $message = "How about implementing createUser as a POST method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
@ -78,13 +63,14 @@ abstract class AbstractUserApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function createUsersWithArrayInput(ServerRequestInterface $request, ResponseInterface $response, array $args) public function createUsersWithArrayInput(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$body = $request->getParsedBody(); $body = $request->getParsedBody();
$message = "How about implementing createUsersWithArrayInput as a POST method in OpenAPIServer\Api\UserApi class?"; $message = "How about implementing createUsersWithArrayInput as a POST method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
@ -96,13 +82,14 @@ abstract class AbstractUserApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function createUsersWithListInput(ServerRequestInterface $request, ResponseInterface $response, array $args) public function createUsersWithListInput(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$body = $request->getParsedBody(); $body = $request->getParsedBody();
$message = "How about implementing createUsersWithListInput as a POST method in OpenAPIServer\Api\UserApi class?"; $message = "How about implementing createUsersWithListInput as a POST method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
@ -115,14 +102,16 @@ abstract class AbstractUserApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments * @param string $username The name that needs to be deleted
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function deleteUser(ServerRequestInterface $request, ResponseInterface $response, array $args) public function deleteUser(
{ ServerRequestInterface $request,
$username = $args['username']; ResponseInterface $response,
string $username
): ResponseInterface {
$message = "How about implementing deleteUser as a DELETE method in OpenAPIServer\Api\UserApi class?"; $message = "How about implementing deleteUser as a DELETE method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
} }
@ -134,14 +123,16 @@ abstract class AbstractUserApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments * @param string $username The name that needs to be fetched. Use user1 for testing.
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function getUserByName(ServerRequestInterface $request, ResponseInterface $response, array $args) public function getUserByName(
{ ServerRequestInterface $request,
$username = $args['username']; ResponseInterface $response,
string $username
): ResponseInterface {
$message = "How about implementing getUserByName as a GET method in OpenAPIServer\Api\UserApi class?"; $message = "How about implementing getUserByName as a GET method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
} }
@ -153,13 +144,14 @@ abstract class AbstractUserApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function loginUser(ServerRequestInterface $request, ResponseInterface $response, array $args) public function loginUser(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$queryParams = $request->getQueryParams(); $queryParams = $request->getQueryParams();
$username = (key_exists('username', $queryParams)) ? $queryParams['username'] : null; $username = (key_exists('username', $queryParams)) ? $queryParams['username'] : null;
$password = (key_exists('password', $queryParams)) ? $queryParams['password'] : null; $password = (key_exists('password', $queryParams)) ? $queryParams['password'] : null;
@ -173,13 +165,14 @@ abstract class AbstractUserApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function logoutUser(ServerRequestInterface $request, ResponseInterface $response, array $args) public function logoutUser(
{ ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$message = "How about implementing logoutUser as a GET method in OpenAPIServer\Api\UserApi class?"; $message = "How about implementing logoutUser as a GET method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
} }
@ -191,14 +184,16 @@ abstract class AbstractUserApi
* *
* @param ServerRequestInterface $request Request * @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response * @param ResponseInterface $response Response
* @param array|null $args Path arguments * @param string $username name that need to be deleted
* *
* @return ResponseInterface * @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method * @throws HttpNotImplementedException to force implementation class to override this method
*/ */
public function updateUser(ServerRequestInterface $request, ResponseInterface $response, array $args) public function updateUser(
{ ServerRequestInterface $request,
$username = $args['username']; ResponseInterface $response,
string $username
): ResponseInterface {
$body = $request->getParsedBody(); $body = $request->getParsedBody();
$message = "How about implementing updateUser as a PUT method in OpenAPIServer\Api\UserApi class?"; $message = "How about implementing updateUser as a PUT method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);

View File

@ -0,0 +1,71 @@
<?php
/**
* OpenAPI Petstore
* PHP version 7.4
*
* @package OpenAPIServer
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
/**
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
* The version of the OpenAPI document: 1.0.0
* Generated by: https://github.com/openapitools/openapi-generator.git
*/
declare(strict_types=1);
/**
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* Do not edit the class manually.
*/
namespace OpenAPIServer\App;
/**
* RegisterDependencies
*
* Recommendations from template creator:
*
* I don't use imports(eg. use Slim\Middleware\ErrorMiddleware) here because each package unlikely
* be used in code twice. It helps to keep that file short and make Git history cleaner.
*
* This class declared as final because two classes with dependency injections can cause confusion. Edit
* template of this class or use your own implementation instead(overwrite index.php to import your
* custom class).
*/
final class RegisterDependencies
{
/**
* Adds dependency definitions.
*
* @param \DI\ContainerBuilder $containerBuilder Container builder.
*
* @see https://php-di.org/doc/php-definitions.html
*/
public function __invoke(\DI\ContainerBuilder $containerBuilder): void
{
$containerBuilder->addDefinitions([
// Response factory required as typed argument in next ErrorMiddleware injection
\Psr\Http\Message\ResponseFactoryInterface::class => \DI\factory([\Slim\Factory\AppFactory::class, 'determineResponseFactory']),
// Slim error middleware
// @see https://www.slimframework.com/docs/v4/middleware/error-handling.html
\Slim\Middleware\ErrorMiddleware::class => \DI\autowire()
->constructorParameter('displayErrorDetails', \DI\get('slim.displayErrorDetails', false))
->constructorParameter('logErrors', \DI\get('slim.logErrors', true))
->constructorParameter('logErrorDetails', \DI\get('slim.logErrorDetails', true)),
// PDO class for database managing
\PDO::class => \DI\create()
->constructor(
\DI\get('pdo.dsn'),
\DI\get('pdo.username'),
\DI\get('pdo.password'),
\DI\get('pdo.options', null)
),
]);
}
}

View File

@ -0,0 +1,64 @@
<?php
/**
* OpenAPI Petstore
* PHP version 7.4
*
* @package OpenAPIServer
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
/**
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
* The version of the OpenAPI document: 1.0.0
* Generated by: https://github.com/openapitools/openapi-generator.git
*/
declare(strict_types=1);
/**
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* Do not edit the class manually.
*/
namespace OpenAPIServer\App;
/**
* RegisterMiddlewares
*
* Recommendations from template creator:
*
* There is no way to add route related middlewares here, add global ones. Route related middlewares
* can be applied in \OpenAPIServer\App\RegisterRoutes class.
*
* I add middlewares by full class names(\Slim\Middleware\ErrorMiddleware::class) because that way
* Slim initiates them with options from Container. They already configured, don't need to pass any
* options manually.
*
* I don't use imports(eg. use Slim\Middleware\ErrorMiddleware) here because each package unlikely
* be used in code twice. It helps to keep that file short and make Git history cleaner.
*
* This class declared as final because two classes with middlewares can cause confusion. Edit
* template of this class or use your own implementation instead(overwrite index.php to import your
* custom class).
*/
final class RegisterMiddlewares
{
/**
* Adds middlewares to Slim app instance.
*
* @param \Slim\App $app App instance.
*/
public function __invoke(\Slim\App $app): void
{
// Parse json, form data and xml
$app->addBodyParsingMiddleware();
// Add Routing Middleware
$app->addRoutingMiddleware();
// Add Error Middleware
$app->add(\Slim\Middleware\ErrorMiddleware::class);
}
}

View File

@ -15,39 +15,26 @@
* Generated by: https://github.com/openapitools/openapi-generator.git * Generated by: https://github.com/openapitools/openapi-generator.git
*/ */
declare(strict_types=1);
/** /**
* NOTE: This class is auto generated by the openapi generator program. * NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator * https://github.com/openapitools/openapi-generator
* Do not edit the class manually. * Do not edit the class manually.
*/ */
namespace OpenAPIServer; namespace OpenAPIServer\App;
use Slim\Factory\AppFactory;
use Slim\Interfaces\RouteInterface;
use Slim\Exception\HttpNotImplementedException; use Slim\Exception\HttpNotImplementedException;
use Psr\Container\ContainerInterface;
use InvalidArgumentException;
use Dyorg\TokenAuthentication;
use Dyorg\TokenAuthentication\TokenSearch;
use Psr\Http\Message\ServerRequestInterface;
use OpenAPIServer\Mock\OpenApiDataMocker;
use OpenAPIServer\Mock\OpenApiDataMockerRouteMiddleware;
use Slim\Psr7\Factory\ResponseFactory;
use Exception;
/** /**
* SlimRouter Class Doc Comment * RegisterRoutes Class Doc Comment
* *
* @package OpenAPIServer * @package OpenAPIServer
* @author OpenAPI Generator team * @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator * @link https://github.com/openapitools/openapi-generator
*/ */
class SlimRouter class RegisterRoutes
{ {
/** @var App instance */
private $slimApp;
/** @var array[] list of all api operations */ /** @var array[] list of all api operations */
private $operations = [ private $operations = [
[ [
@ -843,191 +830,36 @@ class SlimRouter
]; ];
/** /**
* Class constructor * Add routes to Slim app.
* *
* @param ContainerInterface|array $settings Either a ContainerInterface or an associative array of app settings * @param \Slim\App $app Pre-configured Slim application instance
* *
* @throws HttpNotImplementedException When implementation class doesn't exists * @throws HttpNotImplementedException When implementation class doesn't exists
* @throws Exception when not supported authorization schema type provided
*/ */
public function __construct($settings = []) public function __invoke(\Slim\App $app): void
{ {
if ($settings instanceof ContainerInterface) {
// Set container to create App with on AppFactory
AppFactory::setContainer($settings);
}
$this->slimApp = AppFactory::create();
// middlewares requires Psr\Container\ContainerInterface
$container = $this->slimApp->getContainer();
$authPackage = 'OpenAPIServer\Auth';
$basicAuthenticator = function (ServerRequestInterface &$request, TokenSearch $tokenSearch) use ($authPackage) {
$message = "How about extending AbstractAuthenticator class by {$authPackage}\BasicAuthenticator?";
throw new HttpNotImplementedException($request, $message);
};
$apiKeyAuthenticator = function (ServerRequestInterface &$request, TokenSearch $tokenSearch) use ($authPackage) {
$message = "How about extending AbstractAuthenticator class by {$authPackage}\ApiKeyAuthenticator?";
throw new HttpNotImplementedException($request, $message);
};
$oAuthAuthenticator = function (ServerRequestInterface &$request, TokenSearch $tokenSearch) use ($authPackage) {
$message = "How about extending AbstractAuthenticator class by {$authPackage}\OAuthAuthenticator?";
throw new HttpNotImplementedException($request, $message);
};
$userOptions = $this->getSetting($settings, 'tokenAuthenticationOptions', null);
// mocker options
$mockerOptions = $this->getSetting($settings, 'mockerOptions', null);
$dataMocker = $mockerOptions['dataMocker'] ?? new OpenApiDataMocker();
$responseFactory = new ResponseFactory();
$getMockStatusCodeCallback = $mockerOptions['getMockStatusCodeCallback'] ?? null;
$mockAfterCallback = $mockerOptions['afterCallback'] ?? null;
foreach ($this->operations as $operation) { foreach ($this->operations as $operation) {
$callback = function ($request, $response, $arguments) use ($operation) { $callback = function ($request) use ($operation) {
$message = "How about extending {$operation['classname']} by {$operation['apiPackage']}\\{$operation['userClassname']} class implementing {$operation['operationId']} as a {$operation['httpMethod']} method?"; $message = "How about extending {$operation['classname']} by {$operation['apiPackage']}\\{$operation['userClassname']} class implementing {$operation['operationId']} as a {$operation['httpMethod']} method?";
throw new HttpNotImplementedException($request, $message); throw new HttpNotImplementedException($request, $message);
}; };
$middlewares = []; $middlewares = [];
if (class_exists("\\{$operation['apiPackage']}\\{$operation['userClassname']}")) { if (class_exists("\\{$operation['apiPackage']}\\{$operation['userClassname']}")) {
$callback = "\\{$operation['apiPackage']}\\{$operation['userClassname']}:{$operation['operationId']}"; // Notice how we register the controller using the class name?
// PHP-DI will instantiate the class for us only when it's actually necessary
$callback = ["\\{$operation['apiPackage']}\\{$operation['userClassname']}", $operation['operationId']];
} }
foreach ($operation['authMethods'] as $authMethod) { $route = $app->map(
switch ($authMethod['type']) {
case 'http':
$authenticatorClassname = "\\{$authPackage}\\BasicAuthenticator";
if (class_exists($authenticatorClassname)) {
$basicAuthenticator = new $authenticatorClassname($container);
}
$middlewares[] = new TokenAuthentication($this->getTokenAuthenticationOptions([
'authenticator' => $basicAuthenticator,
'regex' => $authMethod['isBearer'] ? '/Bearer\s+(.*)$/i' : '/Basic\s+(.*)$/i',
'header' => 'Authorization',
'parameter' => null,
'cookie' => null,
'argument' => null,
], $userOptions));
break;
case 'apiKey':
$authenticatorClassname = "\\{$authPackage}\\ApiKeyAuthenticator";
if (class_exists($authenticatorClassname)) {
$apiKeyAuthenticator = new $authenticatorClassname($container);
}
$middlewares[] = new TokenAuthentication($this->getTokenAuthenticationOptions([
'authenticator' => $apiKeyAuthenticator,
'regex' => '/^(.*)$/i',
'header' => $authMethod['isKeyInHeader'] ? $authMethod['keyParamName'] : null,
'parameter' => $authMethod['isKeyInQuery'] ? $authMethod['keyParamName'] : null,
'cookie' => $authMethod['isKeyInCookie'] ? $authMethod['keyParamName'] : null,
'argument' => null,
], $userOptions));
break;
case 'oauth2':
$authenticatorClassname = "\\{$authPackage}\\OAuthAuthenticator";
if (class_exists($authenticatorClassname)) {
$oAuthAuthenticator = new $authenticatorClassname($container, $authMethod['scopes']);
}
$middlewares[] = new TokenAuthentication($this->getTokenAuthenticationOptions([
'authenticator' => $oAuthAuthenticator,
'regex' => '/Bearer\s+(.*)$/i',
'header' => 'Authorization',
'parameter' => null,
'cookie' => null,
'argument' => null,
], $userOptions));
break;
default:
throw new Exception('Unknown authorization schema type');
}
}
if (is_callable($getMockStatusCodeCallback)) {
$mockSchemaResponses = array_map(function ($item) {
return json_decode($item['jsonSchema'], true);
}, $operation['responses']);
$middlewares[] = new OpenApiDataMockerRouteMiddleware($dataMocker, $mockSchemaResponses, $responseFactory, $getMockStatusCodeCallback, $mockAfterCallback);
}
$this->addRoute(
[$operation['httpMethod']], [$operation['httpMethod']],
"{$operation['basePathWithoutHost']}{$operation['path']}", "{$operation['basePathWithoutHost']}{$operation['path']}",
$callback, $callback
$middlewares
)->setName($operation['operationId']); )->setName($operation['operationId']);
}
}
/**
* Merges user defined options with dynamic params
*
* @param array $staticOptions Required static options
* @param array $userOptions User options
*
* @return array Merged array
*/
private function getTokenAuthenticationOptions(array $staticOptions, array $userOptions = null)
{
if (is_array($userOptions) === false) {
return $staticOptions;
}
return array_merge($userOptions, $staticOptions);
}
/**
* Returns app setting by name.
*
* @param ContainerInterface|array $settings Either a ContainerInterface or an associative array of app settings
* @param string $settingName Setting name
* @param mixed $default Default setting value.
*
* @return mixed
*/
private function getSetting($settings, $settingName, $default = null)
{
if ($settings instanceof ContainerInterface && $settings->has($settingName)) {
return $settings->get($settingName);
} elseif (is_array($settings) && array_key_exists($settingName, $settings)) {
return $settings[$settingName];
}
return $default;
}
/**
* Add route with multiple methods
*
* @param string[] $methods Numeric array of HTTP method names
* @param string $pattern The route URI pattern
* @param callable|string $callable The route callback routine
* @param array|null $middlewares List of middlewares
*
* @return RouteInterface
*
* @throws InvalidArgumentException If the route pattern isn't a string
*/
public function addRoute(array $methods, string $pattern, $callable, $middlewares = [])
{
$route = $this->slimApp->map($methods, $pattern, $callable);
foreach ($middlewares as $middleware) { foreach ($middlewares as $middleware) {
$route->add($middleware); $route->add($middleware);
} }
return $route; }
}
/**
* Returns Slim Framework instance
*
* @return App
*/
public function getSlimApp()
{
return $this->slimApp;
} }
} }

View File

@ -22,7 +22,6 @@
*/ */
namespace OpenAPIServer\Auth; namespace OpenAPIServer\Auth;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Dyorg\TokenAuthentication; use Dyorg\TokenAuthentication;
use Dyorg\TokenAuthentication\TokenSearch; use Dyorg\TokenAuthentication\TokenSearch;
@ -37,12 +36,6 @@ use Dyorg\TokenAuthentication\Exceptions\UnauthorizedExceptionInterface;
*/ */
abstract class AbstractAuthenticator abstract class AbstractAuthenticator
{ {
/**
* @var ContainerInterface|null Slim app container instance
*/
protected $container;
/** /**
* @var string[]|null List of required scopes * @var string[]|null List of required scopes
*/ */
@ -62,12 +55,10 @@ abstract class AbstractAuthenticator
/** /**
* Authenticator constructor * Authenticator constructor
* *
* @param ContainerInterface|null $container Slim app container instance
* @param string[]|null $requiredScope List of required scopes * @param string[]|null $requiredScope List of required scopes
*/ */
public function __construct(ContainerInterface $container = null, $requiredScope = null) public function __construct($requiredScope = null)
{ {
$this->container = $container;
$this->requiredScope = $requiredScope; $this->requiredScope = $requiredScope;
} }

View File

@ -1,3 +1,7 @@
<IfModule mod_env.c>
SetEnv APP_ENV 'production'
</IfModule>
<IfModule mod_rewrite.c> <IfModule mod_rewrite.c>
RewriteEngine On RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f

View File

@ -22,48 +22,63 @@
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
use OpenAPIServer\SlimRouter; use DI\Bridge\Slim\Bridge;
use Psr\Http\Message\ServerRequestInterface; use DI\ContainerBuilder;
use Psr\Http\Message\ResponseInterface; use OpenAPIServer\App\RegisterDependencies;
use OpenAPIServer\Mock\OpenApiDataMocker; use OpenAPIServer\App\RegisterRoutes;
use OpenAPIServer\App\RegisterMiddlewares;
use Slim\Factory\ServerRequestCreatorFactory;
use Slim\ResponseEmitter;
// load config file // Instantiate PHP-DI ContainerBuilder
$config = []; $builder = new ContainerBuilder();
if (is_array($prodConfig = @include(__DIR__ . '/../config/prod/config.inc.php'))) {
$config = $prodConfig; // consider prod by default
} elseif (is_array($devConfig = @include(__DIR__ . '/../config/dev/config.inc.php'))) { $env;
$config = $devConfig; switch (strtolower($_SERVER['APP_ENV'] ?? 'prod')) {
} else { case 'development':
throw new InvalidArgumentException('Config file missed or broken.'); case 'dev':
$env = 'dev';
break;
case 'production':
case 'prod':
default:
$env = 'prod';
} }
$router = new SlimRouter($config); // Main configuration
$app = $router->getSlimApp(); $builder->addDefinitions(__DIR__ . "/../config/{$env}/default.inc.php");
// Parse json, form data and xml // Config file for the environment if exists
$app->addBodyParsingMiddleware(); $userConfig = __DIR__ . "/../config/{$env}/config.inc.php";
if (file_exists($userConfig)) {
$builder->addDefinitions($userConfig);
}
/** // Set up dependencies
* The routing middleware should be added before the ErrorMiddleware $dependencies = new RegisterDependencies();
* Otherwise exceptions thrown from it will not be handled $dependencies($builder);
*/
$app->addRoutingMiddleware();
/** // Build PHP-DI Container instance
* Add Error Handling Middleware $container = $builder->build();
*
* @param bool $displayErrorDetails -> Should be set to false in production
* @param bool $logErrors -> Parameter is passed to the default ErrorHandler
* @param bool $logErrorDetails -> Display error details in error log
* which can be replaced by a callable of your choice.
* Note: This middleware should be added last. It will not handle any exceptions/errors // Instantiate the app
* for middleware added after it. $app = Bridge::create($container);
*/
$app->addErrorMiddleware(
$config['slimSettings']['displayErrorDetails'] ?? false,
$config['slimSettings']['logErrors'] ?? true,
$config['slimSettings']['logErrorDetails'] ?? true
);
$app->run(); // Register middleware
$middleware = new RegisterMiddlewares();
$middleware($app);
// Register routes
// yes, it's anti-pattern you shouldn't get deps from container directly
$routes = $container->get(RegisterRoutes::class);
$routes($app);
// Create Request object from globals
$serverRequestCreator = ServerRequestCreatorFactory::create();
$request = $serverRequestCreator->createServerRequestFromGlobals();
// Run App & Emit Response
$response = $app->handle($request);
$responseEmitter = new ResponseEmitter();
$responseEmitter->emit($response);