Implemented PSR-15 middleware handler

This commit is contained in:
Igor Scheller 2018-08-07 03:18:22 +02:00
parent 92c26718fd
commit 20c03a155d
10 changed files with 591 additions and 252 deletions

View File

@ -15,11 +15,13 @@
], ],
"require": { "require": {
"php": ">=7.0.0", "php": ">=7.0.0",
"ext-gettext": "*",
"erusev/parsedown": "^1.6", "erusev/parsedown": "^1.6",
"illuminate/container": "5.5.*", "illuminate/container": "5.5.*",
"illuminate/database": "5.5.*", "illuminate/database": "5.5.*",
"illuminate/support": "^5.5", "illuminate/support": "^5.5",
"psr/container": "^1.0", "psr/container": "^1.0",
"psr/http-server-middleware": "^1.0",
"psr/log": "^1.0", "psr/log": "^1.0",
"symfony/http-foundation": "^3.3", "symfony/http-foundation": "^3.3",
"symfony/psr-http-message-bridge": "^1.0", "symfony/psr-http-message-bridge": "^1.0",

View File

@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Engelsystem 2.0\n" "Project-Id-Version: Engelsystem 2.0\n"
"POT-Creation-Date: 2017-12-29 19:01+0100\n" "POT-Creation-Date: 2017-12-29 19:01+0100\n"
"PO-Revision-Date: 2017-12-29 19:03+0100\n" "PO-Revision-Date: 2018-08-07 02:00+0200\n"
"Last-Translator: msquare <msquare@notrademark.de>\n" "Last-Translator: msquare <msquare@notrademark.de>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: de_DE\n" "Language: de_DE\n"
@ -2874,13 +2874,13 @@ msgstr ""
"keine Nummer hast, bitte einfach \"-\" angeben." "keine Nummer hast, bitte einfach \"-\" angeben."
#: /Users/msquare/workspace/projects/engelsystem/public/index.php:218 #: /Users/msquare/workspace/projects/engelsystem/public/index.php:218
msgid "No Access" msgid "Page not found"
msgstr "Kein Zugriff" msgstr "Seite nicht gefunden"
#: /Users/msquare/workspace/projects/engelsystem/public/index.php:219 #: /Users/msquare/workspace/projects/engelsystem/public/index.php:219
msgid "" msgid ""
"You don't have permission to view this page . You probably have to sign in " "This page could not be found or you don't have permission to view it. You "
"or register in order to gain access!" "probably have to sign in or register in order to gain access!"
msgstr "" msgstr ""
"Du hast keinen Zugriff auf diese Seite. Registriere Dich und logge Dich " "Diese Seite existiert nicht oder Du hast keinen Zugriff. Melde Dich an um Zugriff "
"bitte ein, um Zugriff zu erhalten!" "zu erhalten!"

View File

@ -1,252 +1,27 @@
<?php <?php
use Engelsystem\Http\Request; use Engelsystem\Application;
use Engelsystem\Middleware\Dispatcher;
use Engelsystem\Middleware\ExceptionHandler;
use Engelsystem\Middleware\LegacyMiddleware;
use Engelsystem\Middleware\NotFoundResponse;
use Engelsystem\Middleware\SendResponseHandler;
use Psr\Http\Message\ServerRequestInterface;
require_once realpath(__DIR__ . '/../includes/engelsystem.php'); require_once realpath(__DIR__ . '/../includes/engelsystem.php');
$free_pages = [ /** @var Application $app */
'admin_event_config', $app = app();
'angeltypes',
'api',
'atom',
'credits',
'ical',
'login',
'public_dashboard',
'rooms',
'shift_entries',
'shifts',
'shifts_json_export',
'shifts_json_export_all',
'stats',
'users',
'user_driver_licenses',
'user_password_recovery',
'user_worklog'
];
// Gewünschte Seite/Funktion /** @var ServerRequestInterface $request */
$page = ''; $request = $app->get('psr7.request');
$title = '';
$content = '';
/** @var Request $request */ $dispatcher = new Dispatcher([
$request = $app->get('request'); SendResponseHandler::class,
$page = $request->query->get('p'); ExceptionHandler::class,
if (empty($page)) { LegacyMiddleware::class,
$page = $request->path(); NotFoundResponse::class,
$page = str_replace('-', '_', $page);
}
if ($page == '/') {
$page = isset($user) ? 'news' : 'login';
}
if (
preg_match('/^\w*$/i', $page)
&& (
in_array($page, $free_pages)
|| (isset($privileges) && in_array($page, $privileges))
)
) {
$title = $page;
switch ($page) {
case 'api':
error('Api disabled temporarily.');
redirect(page_link_to());
break;
case 'ical':
require_once realpath(__DIR__ . '/../includes/pages/user_ical.php');
user_ical();
break;
case 'atom':
require_once realpath(__DIR__ . '/../includes/pages/user_atom.php');
user_atom();
break;
case 'shifts_json_export':
require_once realpath(__DIR__ . '/../includes/controller/shifts_controller.php');
shifts_json_export_controller();
break;
case 'shifts_json_export_all':
require_once realpath(__DIR__ . '/../includes/controller/shifts_controller.php');
shifts_json_export_all_controller();
break;
case 'stats':
require_once realpath(__DIR__ . '/../includes/pages/guest_stats.php');
guest_stats();
break;
case 'user_password_recovery':
require_once realpath(__DIR__ . '/../includes/controller/users_controller.php');
$title = user_password_recovery_title();
$content = user_password_recovery_controller();
break;
case 'public_dashboard':
list($title, $content) = public_dashboard_controller();
break;
case 'angeltypes':
list($title, $content) = angeltypes_controller();
break;
case 'shift_entries':
list($title, $content) = shift_entries_controller();
break;
case 'shifts':
list($title, $content) = shifts_controller();
break;
case 'users':
list($title, $content) = users_controller();
break;
case 'user_angeltypes':
list($title, $content) = user_angeltypes_controller();
break;
case 'user_driver_licenses':
list($title, $content) = user_driver_licenses_controller();
break;
case 'shifttypes':
list($title, $content) = shifttypes_controller();
break;
case 'admin_event_config':
list($title, $content) = event_config_edit_controller();
break;
case 'rooms':
list($title, $content) = rooms_controller();
break;
case 'news':
$title = news_title();
$content = user_news();
break;
case 'news_comments':
require_once realpath(__DIR__ . '/../includes/pages/user_news.php');
$title = user_news_comments_title();
$content = user_news_comments();
break;
case 'user_meetings':
$title = meetings_title();
$content = user_meetings();
break;
case 'user_myshifts':
$title = myshifts_title();
$content = user_myshifts();
break;
case 'user_shifts':
$title = shifts_title();
$content = user_shifts();
break;
case 'user_worklog':
list($title, $content) = user_worklog_controller();
break;
case 'user_messages':
$title = messages_title();
$content = user_messages();
break;
case 'user_questions':
$title = questions_title();
$content = user_questions();
break;
case 'user_settings':
$title = settings_title();
$content = user_settings();
break;
case 'login':
$title = login_title();
$content = guest_login();
break;
case 'register':
$title = register_title();
$content = guest_register();
break;
case 'logout':
$title = logout_title();
$content = guest_logout();
break;
case 'admin_questions':
$title = admin_questions_title();
$content = admin_questions();
break;
case 'admin_user':
$title = admin_user_title();
$content = admin_user();
break;
case 'admin_arrive':
$title = admin_arrive_title();
$content = admin_arrive();
break;
case 'admin_active':
$title = admin_active_title();
$content = admin_active();
break;
case 'admin_free':
$title = admin_free_title();
$content = admin_free();
break;
case 'admin_news':
require_once realpath(__DIR__ . '/../includes/pages/admin_news.php');
$content = admin_news();
break;
case 'admin_rooms':
$title = admin_rooms_title();
$content = admin_rooms();
break;
case 'admin_groups':
$title = admin_groups_title();
$content = admin_groups();
break;
case 'admin_import':
$title = admin_import_title();
$content = admin_import();
break;
case 'admin_shifts':
$title = admin_shifts_title();
$content = admin_shifts();
break;
case 'admin_log':
$title = admin_log_title();
$content = admin_log();
break;
case 'credits':
require_once realpath(__DIR__ . '/../includes/pages/guest_credits.php');
$title = credits_title();
$content = guest_credits();
break;
default:
require_once realpath(__DIR__ . '/../includes/pages/guest_start.php');
$content = guest_start();
break;
}
} else {
// Wenn schon eingeloggt, keine-Berechtigung-Seite anzeigen
if (isset($user)) {
$title = _('No Access');
$content = _('You don\'t have permission to view this page . You probably have to sign in or register in order to gain access!');
} else {
// Sonst zur Loginseite leiten
redirect(page_link_to('login'));
}
}
$event_config = EventConfig();
$parameters = [
'key' => (isset($user) ? $user['api_key'] : ''),
];
if ($page == 'user_meetings') {
$parameters['meetings'] = 1;
}
echo view(__DIR__ . '/../templates/layout.html', [
'theme' => isset($user) ? $user['color'] : config('theme'),
'title' => $title,
'atom_link' => ($page == 'news' || $page == 'user_meetings')
? ' <link href="'
. page_link_to('atom', $parameters)
. '" type = "application/atom+xml" rel = "alternate" title = "Atom Feed">'
: '',
'start_page_url' => page_link_to('/'),
'credits_url' => page_link_to('credits'),
'menu' => make_menu(),
'content' => msg() . $content,
'header_toolbar' => header_toolbar(),
'faq_url' => config('faq_url'),
'contact_email' => config('contact_email'),
'locale' => locale(),
'event_info' => EventConfig_info($event_config) . ' <br />'
]); ]);
$dispatcher->setContainer($app);
$dispatcher->handle($request);

View File

@ -0,0 +1,110 @@
<?php
namespace Engelsystem\Middleware;
use Engelsystem\Application;
use InvalidArgumentException;
use LogicException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class Dispatcher implements MiddlewareInterface, RequestHandlerInterface
{
/** @var MiddlewareInterface[] */
protected $stack;
/** @var Application */
protected $container;
/** @var RequestHandlerInterface */
protected $next;
/**
* @param MiddlewareInterface[] $stack
* @param Application|null $container
*/
public function __construct($stack = [], Application $container = null)
{
$this->stack = $stack;
$this->container = $container;
}
/**
* Process an incoming server request and return a response, optionally delegating
* response creation to a handler.
*
* Could be used to group middleware
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$this->next = $handler;
return $this->handle($request);
}
/**
* Handle the request and return a response.
*
* It calls all configured middleware and handles their response
*
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$middleware = array_shift($this->stack);
if (!$middleware) {
if ($this->next) {
return $this->next->handle($request);
}
throw new LogicException('Middleware queue is empty');
}
if (is_string($middleware)) {
$middleware = $this->resolveMiddleware($middleware);
}
if (!$middleware instanceof MiddlewareInterface) {
throw new InvalidArgumentException('Middleware is no instance of ' . MiddlewareInterface::class);
}
return $middleware->process($request, $this);
}
/**
* Resolve the middleware with the container
*
* @param string $middleware
* @return MiddlewareInterface
*/
protected function resolveMiddleware($middleware)
{
if (!$this->container instanceof Application) {
throw new InvalidArgumentException('Unable to resolve middleware ' . $middleware);
}
if ($this->container->has($middleware)) {
return $this->container->get($middleware);
}
return $this->container->make($middleware);
}
/**
* @param Application $container
*/
public function setContainer(Application $container)
{
$this->container = $container;
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace Engelsystem\Middleware;
use Engelsystem\Exceptions\Handler as ExceptionsHandler;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class ExceptionHandler implements MiddlewareInterface
{
/** @var ContainerInterface */
protected $container;
/**
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Handles any exceptions that occurred inside other middleware while returning it to the default response handler
*
* Should be added at the beginning
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
try {
return $handler->handle($request);
} catch (\Throwable $e) {
/** @var ExceptionsHandler $handler */
$handler = $this->container->get('error.handler');
$content = $handler->exceptionHandler($e, true);
return response($content, 500);
}
}
}

View File

@ -0,0 +1,284 @@
<?php
namespace Engelsystem\Middleware;
use Engelsystem\Http\Request;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class LegacyMiddleware implements MiddlewareInterface
{
protected $free_pages = [
'admin_event_config',
'angeltypes',
'api',
'atom',
'credits',
'ical',
'login',
'public_dashboard',
'rooms',
'shift_entries',
'shifts',
'shifts_json_export',
'shifts_json_export_all',
'stats',
'users',
'user_driver_licenses',
'user_password_recovery',
'user_worklog'
];
/** @var ContainerInterface */
protected $container;
/**
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Handle the request the old way
*
* Should be used before a 404 is send
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
global $user;
global $privileges;
/** @var Request $appRequest */
$appRequest = $this->container->get('request');
// Default page content
$content = '';
$page = $appRequest->query->get('p');
if (empty($page)) {
$page = $appRequest->path();
$page = str_replace('-', '_', $page);
}
if ($page == '/') {
$page = isset($user) ? 'news' : 'login';
}
if (
preg_match('/^\w+$/i', $page)
&& (
in_array($page, $this->free_pages)
|| (isset($privileges) && in_array($page, $privileges))
)
) {
$title = $page;
switch ($page) {
case 'api':
error('Api disabled temporarily.');
redirect(page_link_to());
break;
case 'ical':
require_once realpath(__DIR__ . '/../includes/pages/user_ical.php');
user_ical();
break;
case 'atom':
require_once realpath(__DIR__ . '/../includes/pages/user_atom.php');
user_atom();
break;
case 'shifts_json_export':
require_once realpath(__DIR__ . '/../includes/controller/shifts_controller.php');
shifts_json_export_controller();
break;
case 'shifts_json_export_all':
require_once realpath(__DIR__ . '/../includes/controller/shifts_controller.php');
shifts_json_export_all_controller();
break;
case 'stats':
require_once realpath(__DIR__ . '/../includes/pages/guest_stats.php');
guest_stats();
break;
case 'user_password_recovery':
require_once realpath(__DIR__ . '/../includes/controller/users_controller.php');
$title = user_password_recovery_title();
$content = user_password_recovery_controller();
break;
case 'public_dashboard':
list($title, $content) = public_dashboard_controller();
break;
case 'angeltypes':
list($title, $content) = angeltypes_controller();
break;
case 'shift_entries':
list($title, $content) = shift_entries_controller();
break;
case 'shifts':
list($title, $content) = shifts_controller();
break;
case 'users':
list($title, $content) = users_controller();
break;
case 'user_angeltypes':
list($title, $content) = user_angeltypes_controller();
break;
case 'user_driver_licenses':
list($title, $content) = user_driver_licenses_controller();
break;
case 'shifttypes':
list($title, $content) = shifttypes_controller();
break;
case 'admin_event_config':
list($title, $content) = event_config_edit_controller();
break;
case 'rooms':
list($title, $content) = rooms_controller();
break;
case 'news':
$title = news_title();
$content = user_news();
break;
case 'news_comments':
require_once realpath(__DIR__ . '/../includes/pages/user_news.php');
$title = user_news_comments_title();
$content = user_news_comments();
break;
case 'user_meetings':
$title = meetings_title();
$content = user_meetings();
break;
case 'user_myshifts':
$title = myshifts_title();
$content = user_myshifts();
break;
case 'user_shifts':
$title = shifts_title();
$content = user_shifts();
break;
case 'user_worklog':
list($title, $content) = user_worklog_controller();
break;
case 'user_messages':
$title = messages_title();
$content = user_messages();
break;
case 'user_questions':
$title = questions_title();
$content = user_questions();
break;
case 'user_settings':
$title = settings_title();
$content = user_settings();
break;
case 'login':
$title = login_title();
$content = guest_login();
break;
case 'register':
$title = register_title();
$content = guest_register();
break;
case 'logout':
$title = logout_title();
$content = guest_logout();
break;
case 'admin_questions':
$title = admin_questions_title();
$content = admin_questions();
break;
case 'admin_user':
$title = admin_user_title();
$content = admin_user();
break;
case 'admin_arrive':
$title = admin_arrive_title();
$content = admin_arrive();
break;
case 'admin_active':
$title = admin_active_title();
$content = admin_active();
break;
case 'admin_free':
$title = admin_free_title();
$content = admin_free();
break;
case 'admin_news':
require_once realpath(__DIR__ . '/../includes/pages/admin_news.php');
$content = admin_news();
break;
case 'admin_rooms':
$title = admin_rooms_title();
$content = admin_rooms();
break;
case 'admin_groups':
$title = admin_groups_title();
$content = admin_groups();
break;
case 'admin_import':
$title = admin_import_title();
$content = admin_import();
break;
case 'admin_shifts':
$title = admin_shifts_title();
$content = admin_shifts();
break;
case 'admin_log':
$title = admin_log_title();
$content = admin_log();
break;
case 'credits':
require_once realpath(__DIR__ . '/../includes/pages/guest_credits.php');
$title = credits_title();
$content = guest_credits();
break;
default:
require_once realpath(__DIR__ . '/../includes/pages/guest_start.php');
$content = guest_start();
break;
}
} else {
return $handler->handle($request);
}
if (empty($title) and empty($content)) {
return $handler->handle($request);
}
$event_config = EventConfig();
$parameters = [
'key' => (isset($user) ? $user['api_key'] : ''),
];
if ($page == 'user_meetings') {
$parameters['meetings'] = 1;
}
return response(view(__DIR__ . '/../../templates/layout.html', [
'theme' => isset($user) ? $user['color'] : config('theme'),
'title' => $title,
'atom_link' => ($page == 'news' || $page == 'user_meetings')
? ' <link href="'
. page_link_to('atom', $parameters)
. '" type = "application/atom+xml" rel = "alternate" title = "Atom Feed">'
: '',
'start_page_url' => page_link_to('/'),
'credits_url' => page_link_to('credits'),
'menu' => make_menu(),
'content' => msg() . $content,
'header_toolbar' => header_toolbar(),
'faq_url' => config('faq_url'),
'contact_email' => config('contact_email'),
'locale' => locale(),
'event_info' => EventConfig_info($event_config) . ' <br />'
]));
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace Engelsystem\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class NotFoundResponse implements MiddlewareInterface
{
/**
* Returns a 404: Page not found response
*
* Should be the last middleware
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
global $user;
$event_config = EventConfig();
$content = info(
_('This page could not be found or you don\'t have permission to view it. You probably have to sign in or register in order to gain access!'),
true
);
return response(view(__DIR__ . '/../../templates/layout.html', [
'theme' => isset($user) ? $user['color'] : config('theme'),
'title' => _('Page not found'),
'atom_link' => '',
'start_page_url' => page_link_to('/'),
'credits_url' => page_link_to('credits'),
'menu' => make_menu(),
'content' => msg() . $content,
'header_toolbar' => header_toolbar(),
'faq_url' => config('faq_url'),
'contact_email' => config('contact_email'),
'locale' => locale(),
'event_info' => EventConfig_info($event_config) . ' <br />'
]), 404);
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace Engelsystem\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class SendResponseHandler implements MiddlewareInterface
{
/**
* Send the server response to the client
*
* This should be the first middleware
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$response = $handler->handle($request);
if (!headers_sent()) {
header(sprintf(
'HTTP/%s %s %s',
$response->getProtocolVersion(),
$response->getStatusCode(),
$response->getReasonPhrase()
), true, $response->getStatusCode());
foreach ($response->getHeaders() as $name => $values) {
foreach ($values as $value) {
header($name . ': ' . $value, false);
}
}
}
echo $response->getBody();
return $response;
}
}

View File

@ -6,13 +6,15 @@ use Engelsystem\Config\Config;
use Engelsystem\Http\Request; use Engelsystem\Http\Request;
use Engelsystem\Renderer\Renderer; use Engelsystem\Renderer\Renderer;
use Engelsystem\Routing\UrlGenerator; use Engelsystem\Routing\UrlGenerator;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Zend\Diactoros\Stream;
/** /**
* Get the global app instance * Get the global app instance
* *
* @param string $id * @param string $id
* @return mixed * @return mixed|Application
*/ */
function app($id = null) function app($id = null)
{ {
@ -80,6 +82,32 @@ function request($key = null, $default = null)
return $request->input($key, $default); return $request->input($key, $default);
} }
/**
* @param string $content
* @param int $status
* @param array $headers
* @return ResponseInterface
*/
function response($content = '', $status = 200, $headers = [])
{
/** @var ResponseInterface $response */
$response = app('psr7.response');
/** @var Stream $stream */
$stream = app()->make(Stream::class, ['stream' => 'php://memory', 'mode' => 'wb+']);
$stream->write($content);
$stream->rewind();
$response = $response
->withBody($stream)
->withStatus($status);
foreach ($headers as $key => $value) {
$response = $response->withAddedHeader($key, $value);
}
return $response;
}
/** /**
* @param string $key * @param string $key
* @param mixed $default * @param mixed $default