├── .gitignore ├── .php_cs.dist ├── LICENSE ├── composer.json ├── phpunit.xml.dist └── src ├── Facade.php └── Middleware ├── AuthorizerMiddleware.php ├── DispatcherMiddleware.php ├── ErrorHandler ├── AbstractErrorHandler.php ├── ErrorHandler.php ├── NotAuthenticatedHandler.php ├── NotAuthorizedHandler.php ├── NotFoundHandler.php ├── WhoopsHandler.php └── templates │ ├── 401.html.php │ ├── 401.json.php │ ├── 403.html.php │ ├── 403.json.php │ ├── 404.html.php │ ├── 404.json.php │ ├── 500.html.php │ └── 500.json.php ├── FilterMiddleware.php ├── HtmlRendererMiddleware.php ├── JsonRendererMiddleware.php ├── MiddlewareDispatcher.php └── RequestMatcherMiddleware.php /.gitignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | vendor/ 3 | tests/resources/ 4 | apigen.neon 5 | composer.lock 6 | phpunit.xml 7 | .php_cs 8 | .php_cs.cache -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | in(__DIR__) 5 | ; 6 | 7 | return Symfony\CS\Config\Config::create() 8 | ->setRules(array( 9 | '@Symfony' => true, 10 | 'empty_return' => false, 11 | )) 12 | ->finder($finder) 13 | ; 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Mathieu Decaffmeyer 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gobline/application", 3 | "description": "Gobline Application", 4 | "license": "BSD-3-Clause", 5 | "authors": [ 6 | { 7 | "name": "Mathieu Decaffmeyer", 8 | "email": "mdecaffmeyer@gmail.com" 9 | } 10 | ], 11 | "keywords": [ 12 | "gobline", 13 | "mvc", "mvc framework", 14 | "hmvc", "hmvc framework", 15 | "mvvm", "mvvm framework" 16 | ], 17 | "homepage": "https://github.com/gobline", 18 | "autoload": { 19 | "psr-4": { 20 | "Gobline\\Application\\": "src/" 21 | } 22 | }, 23 | "require": { 24 | "php": ">=7.0.0", 25 | "gobline/container": "^2.0", 26 | "gobline/registrar": "^1.0", 27 | "gobline/environment": "^1.0", 28 | "gobline/router": "^3.0", 29 | "gobline/filter": "^2.0", 30 | "gobline/flash": "^2.0", 31 | "gobline/auth": "^2.0", 32 | "gobline/acl": "^2.0", 33 | "gobline/view": "^4.0", 34 | "filp/whoops": "^2.1", 35 | "zendframework/zend-diactoros": "^1.2", 36 | "relay/relay": "^1.0" 37 | }, 38 | "extra": { 39 | "branch-alias": { 40 | "dev-master": "4.0.x-dev" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | ./tests 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Facade.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application; 13 | 14 | use Zend\Diactoros\Response; 15 | use Zend\Diactoros\ServerRequestFactory; 16 | use Zend\Diactoros\Response\SapiEmitter; 17 | use Gobline\Container\Container; 18 | use Gobline\Environment\Environment; 19 | use Gobline\Registrar\Registrar; 20 | use Gobline\View\Helper\ViewHelperRegistry; 21 | use Gobline\Router\RouteCollection; 22 | use Gobline\Flash\Flash; 23 | use Gobline\Auth\CurrentUser; 24 | use Gobline\Auth\CurrentUserInterface; 25 | use Gobline\Auth\Persistence\CurrentUser as SessionDecorator; 26 | use Gobline\Application\Middleware\MiddlewareDispatcher; 27 | use Gobline\Application\Middleware\RequestMatcherMiddleware; 28 | use Gobline\Application\Middleware\FilterMiddleware; 29 | use Gobline\Application\Middleware\AuthorizerMiddleware; 30 | use Gobline\Application\Middleware\DispatcherMiddleware; 31 | use Gobline\Application\Middleware\JsonRendererMiddleware; 32 | use Gobline\Application\Middleware\HtmlRendererMiddleware; 33 | use Gobline\Application\Middleware\ErrorHandler\NotFoundHandler; 34 | use Gobline\Application\Middleware\ErrorHandler\NotAuthenticatedHandler; 35 | use Gobline\Application\Middleware\ErrorHandler\NotAuthorizedHandler; 36 | use Gobline\Application\Middleware\ErrorHandler\ErrorHandler; 37 | use Gobline\Router\Exception\NoMatchingRouteException; 38 | use Gobline\Auth\Exception\NotAuthenticatedException; 39 | use Gobline\Acl\Exception\NotAuthorizedException; 40 | 41 | /** 42 | * @author Mathieu Decaffmeyer 43 | */ 44 | class Facade 45 | { 46 | private $container; 47 | private $request; 48 | private $response; 49 | 50 | public function __construct() 51 | { 52 | $this->container = new Container(); 53 | $this->container->registerSelf(); 54 | 55 | $this->request = ServerRequestFactory::fromGlobals(); 56 | $this->response = new Response(); 57 | 58 | $this->registerEnvironment(); 59 | $this->registerRouteCollection(); 60 | $this->registerFlash(); 61 | $this->registerAuth(); 62 | $this->registerViewHelperRegistry(); 63 | $this->registerDispatcher(); 64 | } 65 | 66 | public function run() 67 | { 68 | $dispatcher = $this->getDispatcher(); 69 | 70 | if (!$dispatcher->hasMiddlewares()) { 71 | $this->addDefaultMiddlewares(); 72 | } 73 | 74 | if (!$dispatcher->hasErrorHandlers()) { 75 | $this->addDefaultErrorHandlers(); 76 | } 77 | 78 | $response = $dispatcher->dispatch($this->request, $this->response); 79 | 80 | if (headers_sent()) { 81 | echo $response->getBody(); 82 | } else { 83 | (new SapiEmitter())->emit($response); 84 | } 85 | } 86 | 87 | public function getRequest() 88 | { 89 | return $this->request; 90 | } 91 | 92 | public function getResponse() 93 | { 94 | return $this->response; 95 | } 96 | 97 | public function getContainer() 98 | { 99 | return $this->container; 100 | } 101 | 102 | public function getRegistrar() 103 | { 104 | return new Registrar($this->container); 105 | } 106 | 107 | public function getViewHelperRegistry() 108 | { 109 | return $this->container->get(ViewHelperRegistry::class); 110 | } 111 | 112 | public function getDispatcher() 113 | { 114 | return $this->container->get(MiddlewareDispatcher::class); 115 | } 116 | 117 | public function getRouteCollection() 118 | { 119 | return $this->container->get(RouteCollection::class); 120 | } 121 | 122 | public function __call($name, array $arguments) 123 | { 124 | return $this->container->get(RouteCollection::class)->$name(...$arguments); 125 | } 126 | 127 | public function setDebugMode($debugMode) 128 | { 129 | $this->container->get(Environment::class)->setDebugMode($debugMode); 130 | } 131 | 132 | private function registerEnvironment() 133 | { 134 | $environment = new Environment(); 135 | $environment 136 | ->setRequest($this->request) 137 | ->setResponse($this->response) 138 | ->setBasePathResolver('auto'); 139 | 140 | $this->container->share($environment); 141 | } 142 | 143 | private function registerRouteCollection() 144 | { 145 | $this->container->share(new RouteCollection()); 146 | } 147 | 148 | private function registerFlash() 149 | { 150 | $flash = new Flash(); 151 | $flash->initialize(); 152 | 153 | $this->container->share($flash); 154 | } 155 | 156 | private function registerAuth() 157 | { 158 | $auth = new SessionDecorator(new CurrentUser()); 159 | 160 | $this->container 161 | ->share($auth) 162 | ->alias(CurrentUserInterface::class, $auth); 163 | } 164 | 165 | private function registerViewHelperRegistry() 166 | { 167 | $this->container->share(new ViewHelperRegistry($this->container)); 168 | } 169 | 170 | private function registerDispatcher() 171 | { 172 | $this->container->share(MiddlewareDispatcher::class); 173 | } 174 | 175 | private function addDefaultMiddlewares() 176 | { 177 | $this->getDispatcher() 178 | ->addMiddleware(RequestMatcherMiddleware::class) 179 | ->addMiddleware(AuthorizerMiddleware::class) 180 | ->addMiddleware(FilterMiddleware::class) 181 | ->addMiddleware(DispatcherMiddleware::class) 182 | ->addMiddleware(JsonRendererMiddleware::class) 183 | ->addMiddleware(HtmlRendererMiddleware::class); 184 | } 185 | 186 | private function addDefaultErrorHandlers() 187 | { 188 | $this->getDispatcher() 189 | ->addErrorHandler(NoMatchingRouteException::class, NotFoundHandler::class) 190 | ->addErrorHandler(NotAuthenticatedException::class, NotAuthenticatedHandler::class) 191 | ->addErrorHandler(NotAuthorizedException::class, NotAuthorizedHandler::class) 192 | ->addErrorHandler(\Exception::class, ErrorHandler::class); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/Middleware/AuthorizerMiddleware.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware; 13 | 14 | use Psr\Http\Message\ServerRequestInterface; 15 | use Psr\Http\Message\ResponseInterface; 16 | use Gobline\Auth\CurrentUserInterface; 17 | use Gobline\Auth\Exception\NotAuthenticatedException; 18 | use Gobline\Acl\Acl; 19 | use Gobline\Acl\Exception\NotAuthorizedException; 20 | 21 | /** 22 | * @author Mathieu Decaffmeyer 23 | */ 24 | class AuthorizerMiddleware 25 | { 26 | private $currentUser; 27 | private $acl; 28 | 29 | public function __construct(CurrentUserInterface $currentUser, Acl $acl) 30 | { 31 | $this->currentUser = $currentUser; 32 | $this->acl = $acl; 33 | } 34 | 35 | public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) 36 | { 37 | $requiredRole = $request->getAttribute('_auth'); 38 | 39 | if (!$requiredRole) { 40 | return $next($request, $response); 41 | } 42 | 43 | $userRole = $this->currentUser->getRole(); 44 | 45 | if (!$userRole) { 46 | throw new NotAuthenticatedException(); 47 | } 48 | 49 | if ( 50 | !$this->acl->hasRole($requiredRole) || 51 | !$this->acl->hasRole($userRole) || 52 | ( 53 | !$this->acl->getRole($userRole)->equals($requiredRole) && 54 | !$this->acl->getRole($userRole)->inherits($requiredRole) 55 | ) 56 | ) { 57 | if (!$this->currentUser->isAuthenticated()) { 58 | throw new NotAuthenticatedException(); 59 | } 60 | 61 | throw new NotAuthorizedException(); 62 | } 63 | 64 | return $next($request, $response); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Middleware/DispatcherMiddleware.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware; 13 | 14 | use Psr\Http\Message\ServerRequestInterface; 15 | use Psr\Http\Message\ResponseInterface; 16 | use Gobline\Container\ContainerInterface; 17 | 18 | /** 19 | * @author Mathieu Decaffmeyer 20 | */ 21 | class DispatcherMiddleware 22 | { 23 | private $container; 24 | 25 | public function __construct(ContainerInterface $container) 26 | { 27 | $this->container = $container; 28 | } 29 | 30 | public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) 31 | { 32 | $handler = $request->getAttribute('_handler'); 33 | 34 | if ($handler) { 35 | $response = $handler($request, $response, ...$this->getAttributes($request)); 36 | } 37 | 38 | $actionModel = $request->getAttribute('_action'); 39 | 40 | if (!$actionModel) { 41 | return $next($request, $response); 42 | } 43 | 44 | if (is_string($actionModel)) { 45 | $actionModel = $this->container->get($actionModel); 46 | } 47 | 48 | if (is_callable($actionModel)) { 49 | $actionModel($request, ...$this->getAttributes($request)); 50 | } 51 | 52 | $request = $request->withAttribute('_model', $actionModel); 53 | 54 | return $next($request, $response); 55 | } 56 | 57 | private function getAttributes(ServerRequestInterface $request) 58 | { 59 | return array_values(array_filter($request->getAttributes(), 60 | function ($parameter) { 61 | return $parameter[0] !== '_'; 62 | }, ARRAY_FILTER_USE_KEY)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/AbstractErrorHandler.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware\ErrorHandler; 13 | 14 | use Psr\Http\Message\ServerRequestInterface; 15 | use Psr\Http\Message\ResponseInterface; 16 | use Psr\Log\LoggerInterface; 17 | use Zend\Diactoros\Response\JsonResponse; 18 | use Zend\Diactoros\Response\HtmlResponse; 19 | 20 | /** 21 | * @author Mathieu Decaffmeyer 22 | */ 23 | abstract class AbstractErrorHandler 24 | { 25 | private $logger; 26 | private $logLevel; 27 | 28 | public function setTemplate($template, $accept = 'text/html') 29 | { 30 | $this->templates[$accept] = $template; 31 | } 32 | 33 | public function setLogger(LoggerInterface $logger, $logLevel = 'error') 34 | { 35 | $this->logger = $logger; 36 | $this->logLevel = $logLevel; 37 | 38 | return $this; 39 | } 40 | 41 | abstract protected function getCode(); 42 | 43 | protected function getData() 44 | { 45 | return []; 46 | } 47 | 48 | protected function getTemplates() 49 | { 50 | return [ 51 | 'application/json' => __DIR__.'/templates/'.$this->getCode().'.json.php', 52 | 'text/html' => __DIR__.'/templates/'.$this->getCode().'.html.php', 53 | ]; 54 | } 55 | 56 | public function __invoke(ServerRequestInterface $request, ResponseInterface $response, \Throwable $e) 57 | { 58 | if ($this->logger) { 59 | $this->logger->log($this->logLevel, $e->getMessage(), ['exception' => $e]); 60 | } 61 | 62 | $accept = $request->getHeaderLine('Accept'); 63 | if ($accept && preg_match('#^application/([^+\s]+\+)?json#', $accept)) { 64 | $content = include $this->templates['application/json']; 65 | 66 | return new JsonResponse($content); 67 | } 68 | 69 | $data = $this->getData(); 70 | 71 | $render = function () use ($data) { 72 | extract($data); 73 | include $this->getTemplates()['text/html']; 74 | }; 75 | 76 | ob_start(); 77 | try { 78 | $render(); 79 | } finally { 80 | $content = ob_get_clean(); 81 | } 82 | 83 | return new HtmlResponse($content, $this->getCode()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/ErrorHandler.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware\ErrorHandler; 13 | 14 | use Gobline\Environment\Environment; 15 | 16 | /** 17 | * @author Mathieu Decaffmeyer 18 | */ 19 | class ErrorHandler extends AbstractErrorHandler 20 | { 21 | private $environment; 22 | 23 | public function __construct(Environment $environment) 24 | { 25 | $this->environment = $environment; 26 | } 27 | 28 | protected function getCode() 29 | { 30 | return 500; 31 | } 32 | 33 | protected function getData() 34 | { 35 | return [ 36 | 'homeUri' => $this->environment->buildUri('/'), 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/NotAuthenticatedHandler.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware\ErrorHandler; 13 | 14 | use Gobline\Environment\Environment; 15 | use Gobline\Auth\CurrentUserInterface; 16 | 17 | /** 18 | * @author Mathieu Decaffmeyer 19 | */ 20 | class NotAuthenticatedHandler extends AbstractErrorHandler 21 | { 22 | private $currentUser; 23 | private $environment; 24 | 25 | public function __construct(Environment $environment, CurrentUserInterface $currentUser) 26 | { 27 | $this->environment = $environment; 28 | $this->currentUser = $currentUser; 29 | } 30 | 31 | protected function getCode() 32 | { 33 | return 401; 34 | } 35 | 36 | protected function getData() 37 | { 38 | $sessionExpired = false; 39 | 40 | if ( 41 | $this->currentUser instanceof \Gobline\Auth\Persistence\Session && 42 | $this->currentUser->isSessionExpired() 43 | ) { 44 | $sessionExpired = true; 45 | } 46 | 47 | return [ 48 | 'sessionExpired' => $sessionExpired, 49 | 'homeUri' => $this->environment->buildUri('/'), 50 | ]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/NotAuthorizedHandler.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware\ErrorHandler; 13 | 14 | use Gobline\Environment\Environment; 15 | 16 | /** 17 | * @author Mathieu Decaffmeyer 18 | */ 19 | class NotAuthorizedHandler extends AbstractErrorHandler 20 | { 21 | private $environment; 22 | 23 | public function __construct(Environment $environment) 24 | { 25 | $this->environment = $environment; 26 | } 27 | 28 | protected function getCode() 29 | { 30 | return 403; 31 | } 32 | 33 | protected function getData() 34 | { 35 | return [ 36 | 'homeUri' => $this->environment->buildUri('/'), 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/NotFoundHandler.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware\ErrorHandler; 13 | 14 | use Gobline\Environment\Environment; 15 | 16 | /** 17 | * @author Mathieu Decaffmeyer 18 | */ 19 | class NotFoundHandler extends AbstractErrorHandler 20 | { 21 | private $environment; 22 | 23 | public function __construct(Environment $environment) 24 | { 25 | $this->environment = $environment; 26 | } 27 | 28 | protected function getCode() 29 | { 30 | return 404; 31 | } 32 | 33 | protected function getData() 34 | { 35 | return [ 36 | 'homeUri' => $this->environment->buildUri('/'), 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/WhoopsHandler.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware\ErrorHandler; 13 | 14 | use Psr\Http\Message\ServerRequestInterface; 15 | use Psr\Http\Message\ResponseInterface; 16 | use Psr\Log\LoggerInterface; 17 | use Zend\Diactoros\Response\HtmlResponse; 18 | use Whoops\Handler\PrettyPageHandler; 19 | use Whoops\Run; 20 | 21 | /** 22 | * @author Mathieu Decaffmeyer 23 | */ 24 | class WhoopsHandler 25 | { 26 | private $logger; 27 | private $logLevel; 28 | 29 | public function setLogger(LoggerInterface $logger, $logLevel = 'error') 30 | { 31 | $this->logger = $logger; 32 | $this->logLevel = $logLevel; 33 | 34 | return $this; 35 | } 36 | 37 | public function __invoke(ServerRequestInterface $request, ResponseInterface $response, \Throwable $e) 38 | { 39 | if ($this->logger) { 40 | $this->logger->log($this->logLevel, $e->getMessage(), ['exception' => $e]); 41 | } 42 | 43 | $whoops = new Run(); 44 | $whoops->allowQuit(false); 45 | $whoops->pushHandler(new PrettyPageHandler()); 46 | $whoops->register(); 47 | 48 | $method = Run::EXCEPTION_HANDLER; 49 | 50 | ob_start(); 51 | $whoops->$method($e); 52 | $content = ob_get_clean(); 53 | 54 | return new HtmlResponse($content, 500); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/templates/401.html.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Error - 404 Not Found 7 | 8 | 20 | 21 | 22 |
23 |

Oops!

24 | 25 |

401 Authorization required

26 | 27 | 28 |

Your session has expired. Please login again.

29 | 30 |

Please login to access this page.

31 | 32 | 33 | 34 | Take Me Home 35 |
36 | 37 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/templates/401.json.php: -------------------------------------------------------------------------------- 1 | 'Not Authorized', 5 | ]; 6 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/templates/403.html.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Error - 404 Not Found 7 | 8 | 20 | 21 | 22 |
23 |

Oops!

24 | 25 |

403 Access denied

26 | 27 |

Access to this page has been denied!

28 | 29 | 30 | Take Me Home 31 |
32 | 33 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/templates/403.json.php: -------------------------------------------------------------------------------- 1 | 'Not Authorized', 5 | ]; 6 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/templates/404.html.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Error - 404 Not Found 7 | 8 | 20 | 21 | 22 |
23 |

Oops!

24 | 25 |

404 Page Not Found

26 | 27 |

Sorry, an error has occured. Requested page not found!

28 | 29 | 30 | Take Me Home 31 |
32 | 33 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/templates/404.json.php: -------------------------------------------------------------------------------- 1 | 'Not found', 5 | ]; 6 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/templates/500.html.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Error - 500 Internal Server Error 7 | 8 | 20 | 21 | 22 |
23 |

Oops!

24 | 25 |

500 Internal Server Error

26 | 27 |

Sorry, an error has occured. The server failed to load your request.

28 | 29 | 30 | Take Me Home 31 |
32 | 33 | -------------------------------------------------------------------------------- /src/Middleware/ErrorHandler/templates/500.json.php: -------------------------------------------------------------------------------- 1 | 'Error', 5 | ]; 6 | -------------------------------------------------------------------------------- /src/Middleware/FilterMiddleware.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware; 13 | 14 | use Psr\Http\Message\ServerRequestInterface; 15 | use Psr\Http\Message\ResponseInterface; 16 | use Gobline\Filter\FilterFunnel; 17 | use Gobline\Router\Exception\InvalidParameterException; 18 | 19 | /** 20 | * @author Mathieu Decaffmeyer 21 | */ 22 | class FilterMiddleware 23 | { 24 | private $filter; 25 | 26 | public function __construct(FilterFunnel $filter) 27 | { 28 | $this->filter = $filter; 29 | } 30 | 31 | public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) 32 | { 33 | $parmetersRules = $request->getAttribute('_filter'); 34 | 35 | if (!$parmetersRules) { 36 | return $next($request, $response); 37 | } 38 | 39 | foreach ($parmetersRules as $parameter => $rules) { 40 | $value = $request->getAttribute($parameter); 41 | 42 | if (!$this->filter->filter($value, $rules)) { 43 | throw new InvalidParameterException( 44 | 'parameter "'.$parameter.'" has an invalid value of "'.$value.'"'); 45 | } 46 | } 47 | 48 | return $next($request, $response); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Middleware/HtmlRendererMiddleware.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware; 13 | 14 | use Psr\Http\Message\ServerRequestInterface; 15 | use Psr\Http\Message\ResponseInterface; 16 | use Zend\Diactoros\Response\HtmlResponse; 17 | use Gobline\View\HtmlRenderer; 18 | 19 | /** 20 | * @author Mathieu Decaffmeyer 21 | */ 22 | class HtmlRendererMiddleware 23 | { 24 | private $renderer; 25 | 26 | public function __construct(HtmlRenderer $renderer) 27 | { 28 | $this->renderer = $renderer; 29 | } 30 | 31 | public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) 32 | { 33 | $template = $request->getAttribute('_view'); 34 | 35 | $template = isset($template['text/html']) ? $template['text/html'] : null; 36 | 37 | $layouts = $request->getAttribute('_layouts', []); 38 | 39 | if (!$template && !$layouts) { 40 | return $next($request, $response); 41 | } 42 | 43 | $this->renderer->enableLayouts(); 44 | $this->renderer->setLayouts($layouts); 45 | 46 | if ( 47 | $request->getAttribute('_isSubRequest') 48 | || strtolower($request->getHeaderLine('X-Requested-With')) === 'xmlhttprequest' 49 | ) { 50 | $this->renderer->disableLayouts(); 51 | } 52 | 53 | $model = $request->getAttribute('_model'); 54 | 55 | return new HtmlResponse($this->renderer->render($template, $model)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Middleware/JsonRendererMiddleware.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware; 13 | 14 | use Psr\Http\Message\ServerRequestInterface; 15 | use Psr\Http\Message\ResponseInterface; 16 | use Zend\Diactoros\Response\JsonResponse; 17 | 18 | /** 19 | * @author Mathieu Decaffmeyer 20 | */ 21 | class JsonRendererMiddleware 22 | { 23 | public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) 24 | { 25 | $accept = $request->getHeaderLine('Accept'); 26 | if (!$accept || !preg_match('#^application/([^+\s]+\+)?json#', $accept)) { 27 | return $next($request, $response); 28 | } 29 | 30 | $template = $request->getAttribute('_view', []); 31 | 32 | if (!isset($template['application/json'])) { 33 | return $next($request, $response); 34 | } 35 | 36 | $template = $template['application/json']; 37 | 38 | $model = $request->getAttribute('_model'); 39 | 40 | $content = include $template; 41 | 42 | return new JsonResponse($content); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Middleware/MiddlewareDispatcher.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware; 13 | 14 | use Psr\Http\Message\ServerRequestInterface; 15 | use Psr\Http\Message\ResponseInterface; 16 | use Relay\RelayBuilder; 17 | use Gobline\Container\ContainerInterface; 18 | use Gobline\Environment\Environment; 19 | use Gobline\Application\Middleware\ErrorHandler\WhoopsHandler; 20 | use Zend\Diactoros\Response\SapiEmitter; 21 | use Zend\Diactoros\Response\EmptyResponse; 22 | 23 | /** 24 | * @author Mathieu Decaffmeyer 25 | */ 26 | class MiddlewareDispatcher 27 | { 28 | private $container; 29 | private $environment; 30 | private $middlewares = []; 31 | private $errorHandlers = []; 32 | private $relayBuilder; 33 | private $relay; 34 | 35 | public function __construct(ContainerInterface $container, Environment $environment) 36 | { 37 | $this->container = $container; 38 | $this->environment = $environment; 39 | 40 | $resolver = function ($middleware) use ($container) { 41 | if (is_string($middleware)) { 42 | return $container->get($middleware); 43 | } 44 | 45 | return $middleware; 46 | }; 47 | $this->relayBuilder = new RelayBuilder($resolver); 48 | } 49 | 50 | public function dispatch(ServerRequestInterface $request, ResponseInterface $response, $suppressErrors = false) 51 | { 52 | if (!$this->relay) { 53 | $this->relay = $this->relayBuilder->newInstance($this->middlewares); 54 | } 55 | 56 | try { 57 | $response = $this->relay->__invoke($request, $response); 58 | } catch (\Exception $e) { 59 | $this->handleException($request, $response, $e, $suppressErrors); 60 | } 61 | 62 | return $response; 63 | } 64 | 65 | public function handleException(ServerRequestInterface $request, ResponseInterface $response, \Throwable $e, $suppressErrors = false) 66 | { 67 | if ($this->environment->isDebugMode()) { 68 | $handler = $this->container->get(WhoopsHandler::class); 69 | } else { 70 | foreach ($this->errorHandlers as $exceptionClassName => $handler) { 71 | if (is_a($e, $exceptionClassName)) { 72 | if (is_string($handler)) { 73 | $handler = $this->container->get($handler); 74 | } 75 | break; 76 | } 77 | } 78 | } 79 | $response = $handler($request, $response, $e); 80 | 81 | if ($suppressErrors) { 82 | return new EmptyResponse(); 83 | } 84 | 85 | while (ob_get_level()) { 86 | if ($this->environment->isDebugMode()) { 87 | echo ob_get_clean(); 88 | } else { 89 | ob_end_clean(); 90 | } 91 | } 92 | if (headers_sent()) { 93 | echo $response->getBody(); 94 | } else { 95 | (new SapiEmitter())->emit($response); 96 | } 97 | exit; 98 | } 99 | 100 | public function addErrorHandler($exceptionClassName, $handler) 101 | { 102 | $this->errorHandlers[$exceptionClassName] = $handler; 103 | 104 | return $this; 105 | } 106 | 107 | public function addMiddleware($middleware) 108 | { 109 | $this->middlewares[] = $middleware; 110 | 111 | return $this; 112 | } 113 | 114 | public function setErrorHandlers(array $handlers) 115 | { 116 | foreach ($handlers as $exceptionClassName => $handler) { 117 | $this->addErrorHandler($exceptionClassName, $handler); 118 | } 119 | } 120 | 121 | public function setMiddlewares(array $middlewares) 122 | { 123 | foreach ($middlewares as $middleware) { 124 | $this->addMiddleware($middleware); 125 | } 126 | } 127 | 128 | public function hasMiddlewares() 129 | { 130 | return (bool) $this->middlewares; 131 | } 132 | 133 | public function hasErrorHandlers() 134 | { 135 | return (bool) $this->errorHandlers; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/Middleware/RequestMatcherMiddleware.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Gobline\Application\Middleware; 13 | 14 | use Gobline\Router\RequestMatcher; 15 | use Gobline\Environment\Environment; 16 | use Psr\Http\Message\ServerRequestInterface; 17 | use Psr\Http\Message\ResponseInterface; 18 | 19 | /** 20 | * @author Mathieu Decaffmeyer 21 | */ 22 | class RequestMatcherMiddleware 23 | { 24 | private $requestMatcher; 25 | private $environment; 26 | 27 | public function __construct(RequestMatcher $requestMatcher, Environment $environment) 28 | { 29 | $this->requestMatcher = $requestMatcher; 30 | $this->environment = $environment; 31 | } 32 | 33 | public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) 34 | { 35 | $request = $request->withAttribute('_language', $this->environment->getLanguage()); 36 | 37 | $requestToMatch = $this->environment->getBasePathResolver()->removeBasePath($request); 38 | $requestToMatch = $this->environment->getLanguageResolver()->removeLanguage($requestToMatch); 39 | 40 | $routeData = $this->requestMatcher->match($requestToMatch); 41 | 42 | $this->environment->setMatchedRouteName($routeData->getName()); 43 | $this->environment->setMatchedRouteParams($routeData->getParams()); 44 | 45 | foreach ($routeData->getParams() as $name => $value) { 46 | if (isset($request->getAttributes()[$name])) { 47 | continue; 48 | } 49 | 50 | $request = $request->withAttribute($name, $value); 51 | } 52 | 53 | $handler = $routeData->getHandler(); 54 | $request = $request->withAttribute('_handler', $handler); 55 | 56 | return $next($request, $response); 57 | } 58 | } 59 | --------------------------------------------------------------------------------