diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Middleware/Dispatcher.php | 110 | ||||
-rw-r--r-- | src/Middleware/ExceptionHandler.php | 48 | ||||
-rw-r--r-- | src/Middleware/LegacyMiddleware.php | 284 | ||||
-rw-r--r-- | src/Middleware/NotFoundResponse.php | 47 | ||||
-rw-r--r-- | src/Middleware/SendResponseHandler.php | 45 | ||||
-rw-r--r-- | src/helpers.php | 30 |
6 files changed, 563 insertions, 1 deletions
diff --git a/src/Middleware/Dispatcher.php b/src/Middleware/Dispatcher.php new file mode 100644 index 00000000..774040fb --- /dev/null +++ b/src/Middleware/Dispatcher.php @@ -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; + } +} diff --git a/src/Middleware/ExceptionHandler.php b/src/Middleware/ExceptionHandler.php new file mode 100644 index 00000000..a5db0337 --- /dev/null +++ b/src/Middleware/ExceptionHandler.php @@ -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); + } + } +} diff --git a/src/Middleware/LegacyMiddleware.php b/src/Middleware/LegacyMiddleware.php new file mode 100644 index 00000000..41b2e471 --- /dev/null +++ b/src/Middleware/LegacyMiddleware.php @@ -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 />' + ])); + } +} diff --git a/src/Middleware/NotFoundResponse.php b/src/Middleware/NotFoundResponse.php new file mode 100644 index 00000000..c5d51d2d --- /dev/null +++ b/src/Middleware/NotFoundResponse.php @@ -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); + } +} diff --git a/src/Middleware/SendResponseHandler.php b/src/Middleware/SendResponseHandler.php new file mode 100644 index 00000000..06406fe0 --- /dev/null +++ b/src/Middleware/SendResponseHandler.php @@ -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; + } +} diff --git a/src/helpers.php b/src/helpers.php index c3c727ec..2a90dcde 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -6,13 +6,15 @@ use Engelsystem\Config\Config; use Engelsystem\Http\Request; use Engelsystem\Renderer\Renderer; use Engelsystem\Routing\UrlGenerator; +use Psr\Http\Message\ResponseInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Zend\Diactoros\Stream; /** * Get the global app instance * * @param string $id - * @return mixed + * @return mixed|Application */ function app($id = null) { @@ -81,6 +83,32 @@ function request($key = null, $default = null) } /** + * @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 mixed $default * @return SessionInterface|mixed |