[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 authDirName = "Auth";
protected String authPackage = "";
protected String appDirName = "App";
protected String appPackage = "";
protected String psr7Implementation = "slim-psr7";
protected String interfacesDirName = "Interfaces";
protected String interfacesPackage = "";
@ -92,6 +94,7 @@ public class PhpSlim4ServerCodegen extends AbstractPhpCodegen {
modelPackage = invokerPackage + "\\" + modelDirName;
authPackage = invokerPackage + "\\" + authDirName;
interfacesPackage = invokerPackage + "\\" + interfacesDirName;
appPackage = invokerPackage + "\\" + appDirName;
outputFolder = "generated-code" + File.separator + "slim4";
modelTestTemplateFiles.put("model_test.mustache", ".php");
@ -167,6 +170,8 @@ public class PhpSlim4ServerCodegen extends AbstractPhpCodegen {
authPackage = invokerPackage + "\\" + authDirName;
// Update interfacesPackage
interfacesPackage = invokerPackage + "\\" + interfacesDirName;
// update appPackage
appPackage = invokerPackage + "\\" + appDirName;
}
// 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("interfacesTestPath", "./" + toSrcPath(interfacesPackage, testBasePath));
// same for app classes
additionalProperties.put("appPackage", appPackage);
additionalProperties.put("appSrcPath", "./" + toSrcPath(appPackage, srcBasePath));
if (additionalProperties.containsKey(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("index.mustache", "public", "index.php"));
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
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("htaccess_deny_all", "config", ".htaccess"));
supportingFiles.add(new SupportingFile("config_example.mustache", "config" + File.separator + "dev", "example.inc.php"));
supportingFiles.add(new SupportingFile("config_example.mustache", "config" + File.separator + "prod", "example.inc.php"));
supportingFiles.add(new SupportingFile("config_dev_default.mustache", "config" + File.separator + "dev", "default.inc.php"));
supportingFiles.add(new SupportingFile("config_prod_default.mustache", "config" + File.separator + "prod", "default.inc.php"));
if (Boolean.TRUE.equals(generateModels)) {
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>
RewriteEngine On
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}}
This server has been generated with [Laminas (Zend) PSR-7 implementation](https://github.com/laminas/laminas-diactoros).
{{/isZendDiactoros}}
[PHP-DI](https://php-di.org/doc/frameworks/slim.html) package used as dependency container.
## Requirements
@ -34,7 +35,10 @@ $ composer install
## 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
@ -103,25 +107,14 @@ $ composer phplint
## Show errors
Switch on option in your application config file like:
```diff
return [
'slimSettings' => [
- 'displayErrorDetails' => false,
+ 'displayErrorDetails' => true,
'logErrors' => true,
'logErrorDetails' => true,
],
Switch your app environment to development in `public/.htaccess` file:
```ini
## .htaccess
<IfModule mod_env.c>
SetEnv APP_ENV 'development'
</IfModule>
```
## 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}}
## API Endpoints
@ -135,17 +128,22 @@ All URIs are relative to *{{{basePath}}}*
namespace {{apiPackage}};
use {{apiPackage}}\AbstractPetApi;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
class PetApi extends AbstractPetApi
{
public function addPet($request, $response, $args)
{
public function addPet(
ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
// 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.
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}}
namespace {{authPackage}};
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Dyorg\TokenAuthentication;
use Dyorg\TokenAuthentication\TokenSearch;
@ -24,12 +23,6 @@ use Dyorg\TokenAuthentication\Exceptions\UnauthorizedExceptionInterface;
*/
abstract class {{abstractNamePrefix}}Authenticator{{abstractNameSuffix}}
{
/**
* @var ContainerInterface|null Slim app container instance
*/
protected $container;
/**
* @var string[]|null List of required scopes
*/
@ -49,12 +42,10 @@ abstract class {{abstractNamePrefix}}Authenticator{{abstractNameSuffix}}
/**
* Authenticator constructor
*
* @param ContainerInterface|null $container Slim app container instance
* @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;
}

View File

@ -6,10 +6,11 @@
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* 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}};
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Slim\Exception\HttpNotImplementedException;
@ -23,25 +24,8 @@ use Slim\Exception\HttpNotImplementedException;
*/
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}}
{{#operation}}
/**
* {{httpMethod}} {{operationId}}
{{#summary}}
@ -56,7 +40,11 @@ abstract class {{classname}}
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
{{#hasPathParams}}
{{#pathParams}}
* @param {{{dataType}}} ${{paramName}}{{#description}} {{.}}{{/description}}{{^description}} {{paramName}}{{/description}}
{{/pathParams}}
{{/hasPathParams}}
*
* @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method
@ -64,19 +52,21 @@ abstract class {{classname}}
* @deprecated
{{/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}}
$headers = $request->getHeaders();
{{#headerParams}}
${{paramName}} = $request->hasHeader('{{baseName}}') ? $headers['{{baseName}}'] : null;
{{/headerParams}}
{{/hasHeaderParams}}
{{#hasPathParams}}
{{#pathParams}}
${{paramName}} = $args['{{baseName}}'];
{{/pathParams}}
{{/hasPathParams}}
{{#hasQueryParams}}
$queryParams = $request->getQueryParams();
{{#queryParams}}
@ -105,6 +95,9 @@ abstract class {{classname}}
$message = "How about implementing {{nickname}} as a {{httpMethod}} method in {{apiPackage}}\{{userClassname}} class?";
throw new HttpNotImplementedException($request, $message);
}
{{^-last}}
{{/-last}}
{{/operation}}
{{/operations}}
}

View File

@ -9,30 +9,30 @@
],
"require": {
"php": "^7.4 || ^8.0",
"slim/slim": "^4.5.0",
"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}}
"guzzlehttp/psr7": "^1.6.1",
"http-interop/http-factory-guzzle": "^1.0.0"
"http-interop/http-factory-guzzle": "^1.0.0",
{{/isGuzzlePsr7}}
{{#isZendDiactoros}}
"laminas/laminas-diactoros": "^2.3.0"
"laminas/laminas-diactoros": "^2.3.0",
{{/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": {
"overtrue/phplint": "^2.0.2",
{{#generateTests}}
"phpunit/phpunit": "^8.0 || ^9.0",
{{/generateTests}}
"overtrue/phplint": "^2.0.2",
"squizlabs/php_codesniffer": "^3.5"
},
"autoload": {
@ -58,5 +58,8 @@
{{/generateTests}}
"phpcs": "phpcs",
"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
/config/**/*.*
!/config/.htaccess
!/config/dev/example.inc.php
!/config/prod/example.inc.php
!/config/dev/default.inc.php
!/config/prod/default.inc.php

View File

@ -9,49 +9,64 @@
require_once __DIR__ . '/../vendor/autoload.php';
use {{invokerPackage}}\SlimRouter;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use OpenAPIServer\Mock\OpenApiDataMocker;
use DI\Bridge\Slim\Bridge;
use DI\ContainerBuilder;
use {{appPackage}}\RegisterDependencies;
use {{appPackage}}\RegisterRoutes;
use {{appPackage}}\RegisterMiddlewares;
use Slim\Factory\ServerRequestCreatorFactory;
use Slim\ResponseEmitter;
{{/apiInfo}}
// load config file
$config = [];
if (is_array($prodConfig = @include(__DIR__ . '/../config/prod/config.inc.php'))) {
$config = $prodConfig;
} elseif (is_array($devConfig = @include(__DIR__ . '/../config/dev/config.inc.php'))) {
$config = $devConfig;
} else {
throw new InvalidArgumentException('Config file missed or broken.');
// Instantiate PHP-DI ContainerBuilder
$builder = new ContainerBuilder();
// consider prod by default
$env;
switch (strtolower($_SERVER['APP_ENV'] ?? 'prod')) {
case 'development':
case 'dev':
$env = 'dev';
break;
case 'production':
case 'prod':
default:
$env = 'prod';
}
$router = new SlimRouter($config);
$app = $router->getSlimApp();
// Main configuration
$builder->addDefinitions(__DIR__ . "/../config/{$env}/default.inc.php");
// Parse json, form data and xml
$app->addBodyParsingMiddleware();
// Config file for the environment if exists
$userConfig = __DIR__ . "/../config/{$env}/config.inc.php";
if (file_exists($userConfig)) {
$builder->addDefinitions($userConfig);
}
/**
* The routing middleware should be added before the ErrorMiddleware
* Otherwise exceptions thrown from it will not be handled
*/
$app->addRoutingMiddleware();
// Set up dependencies
$dependencies = new RegisterDependencies();
$dependencies($builder);
/**
* Add Error Handling Middleware
*
* @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.
// Build PHP-DI Container instance
$container = $builder->build();
* Note: This middleware should be added last. It will not handle any exceptions/errors
* for middleware added after it.
*/
$app->addErrorMiddleware(
$config['slimSettings']['displayErrorDetails'] ?? false,
$config['slimSettings']['logErrors'] ?? true,
$config['slimSettings']['logErrorDetails'] ?? true
);
// Instantiate the app
$app = Bridge::create($container);
$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
/config/**/*.*
!/config/.htaccess
!/config/dev/example.inc.php
!/config/prod/example.inc.php
!/config/dev/default.inc.php
!/config/prod/default.inc.php

View File

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

View File

@ -4,6 +4,7 @@
* [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.
[PHP-DI](https://php-di.org/doc/frameworks/slim.html) package used as dependency container.
## Requirements
@ -23,7 +24,10 @@ $ composer install
## 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
@ -86,25 +90,14 @@ $ composer phplint
## Show errors
Switch on option in your application config file like:
```diff
return [
'slimSettings' => [
- 'displayErrorDetails' => false,
+ 'displayErrorDetails' => true,
'logErrors' => true,
'logErrorDetails' => true,
],
Switch your app environment to development in `public/.htaccess` file:
```ini
## .htaccess
<IfModule mod_env.c>
SetEnv APP_ENV 'development'
</IfModule>
```
## 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
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;
use OpenAPIServer\Api\AbstractPetApi;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
class PetApi extends AbstractPetApi
{
public function addPet($request, $response, $args)
{
public function addPet(
ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
// 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.
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": {
"php": "^7.4 || ^8.0",
"slim/slim": "^4.5.0",
"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-server-middleware": "^1.0",
"slim/psr7": "^1.1.0"
"ybelenko/openapi-data-mocker-server-middleware": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^8.0 || ^9.0",
"overtrue/phplint": "^2.0.2",
"phpunit/phpunit": "^8.0 || ^9.0",
"squizlabs/php_codesniffer": "^3.5"
},
"autoload": {
@ -37,5 +37,8 @@
"test-models": "phpunit --testsuite Models",
"phpcs": "phpcs",
"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.
* https://github.com/openapitools/openapi-generator
* 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;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Slim\Exception\HttpNotImplementedException;
@ -36,23 +37,6 @@ use Slim\Exception\HttpNotImplementedException;
*/
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
* Summary: Add a new pet to the store
@ -60,13 +44,14 @@ abstract class AbstractPetApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @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();
$message = "How about implementing addPet as a POST method in OpenAPIServer\Api\PetApi class?";
throw new HttpNotImplementedException($request, $message);
@ -78,16 +63,18 @@ abstract class AbstractPetApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
* @param int $petId Pet id to delete
*
* @return ResponseInterface
* @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();
$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?";
throw new HttpNotImplementedException($request, $message);
}
@ -100,13 +87,14 @@ abstract class AbstractPetApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @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();
$status = (key_exists('status', $queryParams)) ? $queryParams['status'] : null;
$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 ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method
* @deprecated
*/
public function findPetsByTags(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
public function findPetsByTags(
ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$queryParams = $request->getQueryParams();
$tags = (key_exists('tags', $queryParams)) ? $queryParams['tags'] : null;
$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 ResponseInterface $response Response
* @param array|null $args Path arguments
* @param int $petId ID of pet to return
*
* @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method
*/
public function getPetById(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$petId = $args['petId'];
public function getPetById(
ServerRequestInterface $request,
ResponseInterface $response,
int $petId
): ResponseInterface {
$message = "How about implementing getPetById as a GET method in OpenAPIServer\Api\PetApi class?";
throw new HttpNotImplementedException($request, $message);
}
@ -162,13 +153,14 @@ abstract class AbstractPetApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @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();
$message = "How about implementing updatePet as a PUT method in OpenAPIServer\Api\PetApi class?";
throw new HttpNotImplementedException($request, $message);
@ -180,14 +172,16 @@ abstract class AbstractPetApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
* @param int $petId ID of pet that needs to be updated
*
* @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method
*/
public function updatePetWithForm(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$petId = $args['petId'];
public function updatePetWithForm(
ServerRequestInterface $request,
ResponseInterface $response,
int $petId
): ResponseInterface {
$body = $request->getParsedBody();
$name = (isset($body['name'])) ? $body['name'] : null;
$status = (isset($body['status'])) ? $body['status'] : null;
@ -202,14 +196,16 @@ abstract class AbstractPetApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
* @param int $petId ID of pet to update
*
* @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method
*/
public function uploadFile(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$petId = $args['petId'];
public function uploadFile(
ServerRequestInterface $request,
ResponseInterface $response,
int $petId
): ResponseInterface {
$body = $request->getParsedBody();
$additionalMetadata = (isset($body['additionalMetadata'])) ? $body['additionalMetadata'] : 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.
* https://github.com/openapitools/openapi-generator
* 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;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Slim\Exception\HttpNotImplementedException;
@ -36,23 +37,6 @@ use Slim\Exception\HttpNotImplementedException;
*/
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
* Summary: Delete purchase order by ID
@ -60,14 +44,16 @@ abstract class AbstractStoreApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
* @param string $orderId ID of the order that needs to be deleted
*
* @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method
*/
public function deleteOrder(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$orderId = $args['orderId'];
public function deleteOrder(
ServerRequestInterface $request,
ResponseInterface $response,
string $orderId
): ResponseInterface {
$message = "How about implementing deleteOrder as a DELETE method in OpenAPIServer\Api\StoreApi class?";
throw new HttpNotImplementedException($request, $message);
}
@ -80,13 +66,14 @@ abstract class AbstractStoreApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @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?";
throw new HttpNotImplementedException($request, $message);
}
@ -99,14 +86,16 @@ abstract class AbstractStoreApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
* @param int $orderId ID of pet that needs to be fetched
*
* @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method
*/
public function getOrderById(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$orderId = $args['orderId'];
public function getOrderById(
ServerRequestInterface $request,
ResponseInterface $response,
int $orderId
): ResponseInterface {
$message = "How about implementing getOrderById as a GET method in OpenAPIServer\Api\StoreApi class?";
throw new HttpNotImplementedException($request, $message);
}
@ -118,13 +107,14 @@ abstract class AbstractStoreApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @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();
$message = "How about implementing placeOrder as a POST method in OpenAPIServer\Api\StoreApi class?";
throw new HttpNotImplementedException($request, $message);

View File

@ -19,10 +19,11 @@
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* 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;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Slim\Exception\HttpNotImplementedException;
@ -36,23 +37,6 @@ use Slim\Exception\HttpNotImplementedException;
*/
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
* Summary: Create user
@ -60,13 +44,14 @@ abstract class AbstractUserApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @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();
$message = "How about implementing createUser as a POST method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message);
@ -78,13 +63,14 @@ abstract class AbstractUserApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @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();
$message = "How about implementing createUsersWithArrayInput as a POST method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message);
@ -96,13 +82,14 @@ abstract class AbstractUserApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @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();
$message = "How about implementing createUsersWithListInput as a POST method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message);
@ -115,14 +102,16 @@ abstract class AbstractUserApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
* @param string $username The name that needs to be deleted
*
* @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method
*/
public function deleteUser(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$username = $args['username'];
public function deleteUser(
ServerRequestInterface $request,
ResponseInterface $response,
string $username
): ResponseInterface {
$message = "How about implementing deleteUser as a DELETE method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message);
}
@ -134,14 +123,16 @@ abstract class AbstractUserApi
*
* @param ServerRequestInterface $request Request
* @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
* @throws HttpNotImplementedException to force implementation class to override this method
*/
public function getUserByName(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$username = $args['username'];
public function getUserByName(
ServerRequestInterface $request,
ResponseInterface $response,
string $username
): ResponseInterface {
$message = "How about implementing getUserByName as a GET method in OpenAPIServer\Api\UserApi class?";
throw new HttpNotImplementedException($request, $message);
}
@ -153,13 +144,14 @@ abstract class AbstractUserApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @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();
$username = (key_exists('username', $queryParams)) ? $queryParams['username'] : null;
$password = (key_exists('password', $queryParams)) ? $queryParams['password'] : null;
@ -173,13 +165,14 @@ abstract class AbstractUserApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
* @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?";
throw new HttpNotImplementedException($request, $message);
}
@ -191,14 +184,16 @@ abstract class AbstractUserApi
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
* @param string $username name that need to be deleted
*
* @return ResponseInterface
* @throws HttpNotImplementedException to force implementation class to override this method
*/
public function updateUser(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$username = $args['username'];
public function updateUser(
ServerRequestInterface $request,
ResponseInterface $response,
string $username
): ResponseInterface {
$body = $request->getParsedBody();
$message = "How about implementing updateUser as a PUT method in OpenAPIServer\Api\UserApi class?";
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
*/
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;
namespace OpenAPIServer\App;
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;
use Slim\Psr7\Factory\ResponseFactory;
use Exception;
/**
* SlimRouter Class Doc Comment
* RegisterRoutes Class Doc Comment
*
* @package OpenAPIServer
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
class SlimRouter
class RegisterRoutes
{
/** @var App instance */
private $slimApp;
/** @var array[] list of all api 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 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) {
$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?";
throw new HttpNotImplementedException($request, $message);
};
$middlewares = [];
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) {
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(
$route = $app->map(
[$operation['httpMethod']],
"{$operation['basePathWithoutHost']}{$operation['path']}",
$callback,
$middlewares
$callback
)->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;
}
}
}

View File

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

View File

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

View File

@ -22,48 +22,63 @@
require_once __DIR__ . '/../vendor/autoload.php';
use OpenAPIServer\SlimRouter;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use OpenAPIServer\Mock\OpenApiDataMocker;
use DI\Bridge\Slim\Bridge;
use DI\ContainerBuilder;
use OpenAPIServer\App\RegisterDependencies;
use OpenAPIServer\App\RegisterRoutes;
use OpenAPIServer\App\RegisterMiddlewares;
use Slim\Factory\ServerRequestCreatorFactory;
use Slim\ResponseEmitter;
// load config file
$config = [];
if (is_array($prodConfig = @include(__DIR__ . '/../config/prod/config.inc.php'))) {
$config = $prodConfig;
} elseif (is_array($devConfig = @include(__DIR__ . '/../config/dev/config.inc.php'))) {
$config = $devConfig;
} else {
throw new InvalidArgumentException('Config file missed or broken.');
// Instantiate PHP-DI ContainerBuilder
$builder = new ContainerBuilder();
// consider prod by default
$env;
switch (strtolower($_SERVER['APP_ENV'] ?? 'prod')) {
case 'development':
case 'dev':
$env = 'dev';
break;
case 'production':
case 'prod':
default:
$env = 'prod';
}
$router = new SlimRouter($config);
$app = $router->getSlimApp();
// Main configuration
$builder->addDefinitions(__DIR__ . "/../config/{$env}/default.inc.php");
// Parse json, form data and xml
$app->addBodyParsingMiddleware();
// Config file for the environment if exists
$userConfig = __DIR__ . "/../config/{$env}/config.inc.php";
if (file_exists($userConfig)) {
$builder->addDefinitions($userConfig);
}
/**
* The routing middleware should be added before the ErrorMiddleware
* Otherwise exceptions thrown from it will not be handled
*/
$app->addRoutingMiddleware();
// Set up dependencies
$dependencies = new RegisterDependencies();
$dependencies($builder);
/**
* Add Error Handling Middleware
*
* @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.
// Build PHP-DI Container instance
$container = $builder->build();
* Note: This middleware should be added last. It will not handle any exceptions/errors
* for middleware added after it.
*/
$app->addErrorMiddleware(
$config['slimSettings']['displayErrorDetails'] ?? false,
$config['slimSettings']['logErrors'] ?? true,
$config['slimSettings']['logErrorDetails'] ?? true
);
// Instantiate the app
$app = Bridge::create($container);
$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);