diff --git a/Emitter.php b/Emitter.php index 6113495..1cf4527 100755 --- a/Emitter.php +++ b/Emitter.php @@ -4,11 +4,12 @@ namespace MaplePHP\Handler; +use MaplePHP\Container\Interfaces\ContainerExceptionInterface; +use MaplePHP\Container\Interfaces\NotFoundExceptionInterface; use MaplePHP\Http\Interfaces\ResponseInterface; use MaplePHP\Http\Interfaces\RequestInterface; use MaplePHP\Http\Interfaces\StreamInterface; use MaplePHP\Handler\Exceptions\EmitterException; -use MaplePHP\Handler\ErrorHandler; use MaplePHP\Container\Interfaces\ContainerInterface; use MaplePHP\Output\SwiftRender; @@ -27,6 +28,10 @@ class Emitter private $errorHandler = false; private $errorHandlerMsg; + /** + * @throws NotFoundExceptionInterface + * @throws ContainerExceptionInterface + */ public function __construct(ContainerInterface $container) { $this->container = $container; @@ -45,7 +50,7 @@ public function __construct(ContainerInterface $container) * Se a default TTL cache save value (default 0) * @param int $ttl In seconds */ - public function setDefaultCacheTtl(int $ttl) + public function setDefaultCacheTtl(int $ttl): void { $this->cacheDefaultTtl = $ttl; } @@ -60,8 +65,8 @@ public function view(): SwiftRender } /** - * If you set a buffered response string it will get priorities agains all outher response - * @param string $output + * If you set a buffered response string it will get priorities against all other response + * @param string|null $output * @return void */ public function outputBuffer(?string $output): void @@ -70,10 +75,10 @@ public function outputBuffer(?string $output): void } /** - * Will try to get a output to read + * Will try to get an output to read * @return string */ - protected function createResponse() + protected function createResponse(): string { $stream = $this->response->getBody(); $this->view->findBind($this->response->getStatusCode()); @@ -113,10 +118,9 @@ private function buildStream(): StreamInterface } $responseBody = $this->createResponse(); - - //Accurate gzip implementation (major improvment for the apps preformance and load speed) - if (($acceptEnc = $this->request->getHeaderLine("Accept-Encoding")) && strpos($acceptEnc, 'gzip') !== false) { - $responseBody = gzencode($responseBody, 9, FORCE_GZIP); + //Accurate gzip implementation (major improvement for the apps performance and load speed) + if (function_exists('gzencode') && ($acceptEnc = $this->request->getHeaderLine("Accept-Encoding")) && str_contains($acceptEnc, 'gzip')) { + $responseBody = gzencode($responseBody, 9, (defined("FORCE_GZIP") ? FORCE_GZIP : 31)); $this->response = $this->response->withHeader('Content-Encoding', "gzip"); $this->isGzipped = true; } @@ -142,14 +146,12 @@ public function run(ResponseInterface $response, RequestInterface $request): voi { $this->response = $response; $this->request = $request; - $stream = $this->buildStream(); // Look for position instead of size, read has already been triggered once $size = $stream->tell(); - if ($size) { $this->response = $this->response->withHeader('Content-Length', $size); - // Set cache control if do not exist + // Set cache control if it does not exist if (!$this->response->hasHeader("Cache-Control")) { // Clear cache on dynamic content is a good standard to make sure // that no sensitive content will be cached. @@ -160,7 +162,7 @@ public function run(ResponseInterface $response, RequestInterface $request): voi // Will pre execute above headers (Can only be triggered once per instance) $this->response->createHeaders(); - // Detached Body from a HEAD request method but keep the meta data intact. + // Detached Body from a HEAD request method but keep the metadata intact. if ($this->request->getMethod() === "HEAD") { $stream->seek(0); $stream->write(""); @@ -175,33 +177,4 @@ public function run(ResponseInterface $response, RequestInterface $request): voi } } - /** - * Makes error handling easy - * @param bool $displayError Enables error handlning - * @param bool $niceError Nice error reporting (true) vs EmitterException (false) - * @param bool $logError Enable log message (With warning might log even if - * false with EmitterException, depending on you setup) - * @param string|null $logErrorFile Path to log file - * @return void - */ - public function errorHandler( - bool $displayError, - bool $niceError = true, - bool $logError = false, - ?string $logErrorFile = null - ): void { - $this->errorHandler = new ErrorHandler($displayError, $logError, $logErrorFile); - $this->errorHandler->setHandler(function ($msg, $number, $hasError, $displayError) use ($niceError) { - if ($hasError) { - $this->errorHandlerMsg = $msg; - if ($displayError) { - if ($niceError) { - $this->view->findBind(500); - } else { - throw new EmitterException($this->errorHandlerMsg, $number); - } - } - } - }, $this->errorHandler::CATCH_ALL); - } } diff --git a/Interfaces/MiddlewareInterface.php b/Interfaces/MiddlewareInterface.php index 8caba16..601a069 100755 --- a/Interfaces/MiddlewareInterface.php +++ b/Interfaces/MiddlewareInterface.php @@ -2,11 +2,11 @@ namespace MaplePHP\Handler\Interfaces; -use MaplePHP\Http\Interfaces\ResponseInterface; -use MaplePHP\Http\Interfaces\RequestInterface; + +use MaplePHP\Http\Response; interface MiddlewareInterface { - public function before(ResponseInterface $response, RequestInterface $request); - public function after(ResponseInterface $response, RequestInterface $request); + + } diff --git a/RouterDispatcher.php b/RouterDispatcher.php index 719f43c..e2b73b2 100755 --- a/RouterDispatcher.php +++ b/RouterDispatcher.php @@ -4,16 +4,19 @@ namespace MaplePHP\Handler; +//use MaplePHP\Handler\RoutingManager; use MaplePHP\Http\Interfaces\ResponseInterface; use MaplePHP\Http\Interfaces\RequestInterface; use MaplePHP\Handler\Interfaces\RouterDispatcherInterface; use MaplePHP\Handler\Exceptions\EmitterException; +use MaplePHP\Http\Interfaces\ServerRequestInterface; use MaplePHP\Http\Interfaces\UrlInterface; -use MaplePHP\Handler\RoutingManager; use MaplePHP\Container\Reflection; use MaplePHP\Http\Url; use FastRoute\Dispatcher; use FastRoute\RouteCollector; +use function FastRoute\cachedDispatcher; +use function FastRoute\simpleDispatcher; class RouterDispatcher implements RouterDispatcherInterface { @@ -21,15 +24,15 @@ class RouterDispatcher implements RouterDispatcherInterface public const NOT_FOUND = Dispatcher::NOT_FOUND; public const METHOD_NOT_ALLOWED = Dispatcher::METHOD_NOT_ALLOWED; - private $url; - private $request; + private UrlInterface $url; + private ResponseInterface $response; + private ServerRequestInterface $request; private $router; private $routerCacheFile; private $enableCache; private $dispatcher; private $dispatchPath; private $buffer; - private $response; private $method; private static $middleware; @@ -37,7 +40,7 @@ class RouterDispatcher implements RouterDispatcherInterface /** * Router Dispatcher, Used to make it easier to change out router library */ - public function __construct(ResponseInterface $response, RequestInterface $request) + public function __construct(ResponseInterface $response, ServerRequestInterface $request) { $this->response = $response; $this->request = $request; @@ -55,9 +58,9 @@ public function response(): ResponseInterface /** * Get request instance - * @return RequestInterface + * @return ServerRequestInterface */ - public function request(): RequestInterface + public function request(): ServerRequestInterface { return $this->request; } @@ -66,7 +69,7 @@ public function request(): RequestInterface * Get url instance * @return UrlInterface */ - public function url(): ?UrlInterface + public function url(): UrlInterface { if (is_null($this->url)) { $this->url = $this->setUrl(); @@ -80,8 +83,8 @@ public function setUrl(array $parts = array()) } /** - * Return possible data catched with output buffer - * @return string + * Return possible data caught with output buffer + * @return string|null */ public function getBufferedResponse(): ?string { @@ -119,6 +122,7 @@ public function setDispatchPath(string $path): void * @param string $cacheFile * @param bool $enableCache (Default true) * @return void + * @throws EmitterException */ public function setRouterCacheFile(string $cacheFile, bool $enableCache = true): void { @@ -127,17 +131,17 @@ public function setRouterCacheFile(string $cacheFile, bool $enableCache = true): $dir = dirname($this->routerCacheFile); if ($this->enableCache && !is_writable($dir)) { - throw new EmitterException("Directory (\"{$dir}/\") is not writable. " . - "Could not save \"{$this->routerCacheFile}\" file.", 1); + throw new EmitterException("Directory (\"$dir/\") is not writable. " . + "Could not save \"$this->routerCacheFile\" file.", 1); } } /** * The is used to nest group routes * The param "Order" here is important. - * Callable: Routes to be binded to pattern or middelwares - * Pattern: Routes binded to pattern - * Array: Middelwares + * Callable: Routes to be bound to pattern or middlewares + * Pattern: Routes bound to pattern + * Array: Middlewares * @param mixed $arg1 Callable/Pattern * @param mixed $arg2 Callable/array * @param array $arg3 array @@ -155,7 +159,7 @@ public function group($arg1, $arg2, $arg3 = array()): void $data = []; } if (!is_callable($call)) { - throw new \InvalidArgumentException("Either the argumnet 1 or 2 need to be callable.", 1); + throw new \InvalidArgumentException("Either the argument 1 or 2 need to be callable.", 1); } $this->router[] = function () use (&$inst, $pattern, $call, $data) { @@ -247,7 +251,7 @@ public function cli(string $pattern, $controller): void } /** - * The will feed the Dispatcher with routes + * Will feed the Dispatcher with routes * @return callable */ public function dispatcherCallback(): callable @@ -273,9 +277,9 @@ protected function registerDispatcher(): Dispatcher { if (is_null($this->dispatcher)) { if (is_null($this->routerCacheFile)) { - $this->dispatcher = \FastRoute\simpleDispatcher($this->dispatcherCallback()); + $this->dispatcher = simpleDispatcher($this->dispatcherCallback()); } else { - $this->dispatcher = \FastRoute\cachedDispatcher($this->dispatcherCallback(), [ + $this->dispatcher = cachedDispatcher($this->dispatcherCallback(), [ 'cacheFile' => $this->routerCacheFile, 'cacheDisabled' => !$this->enableCache ]); @@ -295,8 +299,10 @@ public function loadMid() /** * Dispatch results - * @param callable $call + * @param callable $call * @return ResponseInterface + * @throws EmitterException + * @throws \ReflectionException */ public function dispatch(callable $call): ResponseInterface { @@ -307,18 +313,17 @@ public function dispatch(callable $call): ResponseInterface $this->url = $this->setUrl($routeInfo[2]); $call($routeInfo[0], $this->response, $this->request, $this->url); - //ob_start(); if (is_array($routeInfo[1]['controller'])) { $this->dispatchMiddleware(($routeInfo[1]['data'] ?? null), function () use (&$response, $routeInfo) { $select = (isset($routeInfo[1]['controller'])) ? $routeInfo[1]['controller'] : $routeInfo[1]; if(!class_exists($select[0])) { - throw new EmitterException("You have specfied a controller ({$select[0]}) that do not exists in you router file!", 1); + throw new EmitterException("You have specified a controller ($select[0]) that do not exists in you router file!", 1); } $reflect = new Reflection($select[0]); $controller = $reflect->dependencyInjector(); - if (isset($select[1])) { - $response = $controller->{$select[1]}($this->response, $this->request); + // Add Dependency Injector on the class method. + $response = $reflect->dependencyInjector($controller, $select[1]); } else { if (is_callable($controller)) { $response = $controller($this->response, $this->request); @@ -333,7 +338,6 @@ public function dispatch(callable $call): ResponseInterface $response = $routeInfo[1]['controller']($this->response, $this->request); } - //$this->buffer = ob_get_clean(); } else { $response = $call($routeInfo[0], $this->response, $this->request, null); } @@ -342,13 +346,29 @@ public function dispatch(callable $call): ResponseInterface $this->response = $response; } - if (is_null($this->response)) { + if(is_array($response)) { + $this->response = $this->jsonResponse($response); + } + /* + if (is_null($this->response)) { throw new EmitterException("The response can not be null, it has to be instance of ResponseInterface.", 1); } - + */ return $this->response; } + /** + * Set response as json response + * @param array $data + * @return ResponseInterface + */ + protected function jsonResponse(array $data): ResponseInterface + { + $response = $this->response->withHeader("Content-type", "application/json; charset=UTF-8"); + $response->getBody()->write(json_encode($data)); + return $response; + } + /** * @dispatcherNest will handle all nested grouped routes * @param RouteCollector $route @@ -388,9 +408,10 @@ protected function dispatcherNest(RouteCollector $route, array $inst): void /** * Will dispatch the middleware at the right position - * @param ?array $data List of middlewares - * @param callable $call inject routers + * @param ?array $data List of middlewares + * @param callable $call inject routers * @return void + * @throws EmitterException */ protected function dispatchMiddleware(?array $data, $call): void { @@ -402,12 +423,13 @@ protected function dispatchMiddleware(?array $data, $call): void if (!isset(self::$middleware[$classData[0]])) { if(!class_exists($classData[0])) { - throw new EmitterException("You have specfied a middleware ({$classData[0]}) that do not exists in you router file!", 1); + throw new EmitterException("You have specified a middleware ($classData[0]) that do not exists in you router file!", 1); } + $reflect = new Reflection($classData[0]); self::$middleware[$classData[0]] = $reflect->dependencyInjector(); + $response = $this->selectMiddlewareBefore($reflect, $classData, $customAfter); } - $response = $this->selectMiddlewareBefore($classData, $customAfter); } } $call(); // Possible controller data @@ -418,59 +440,67 @@ protected function dispatchMiddleware(?array $data, $call): void } } - protected function selectMiddlewareBefore(array $classData, array &$customAfter) { - $response = self::$middleware[$classData[0]]->before($this->response, $this->request); + /** + * Will select and load dependency injector for before middleware + * And prepare after middlewares + * @param Reflection $reflect + * @param array $classData + * @param array $customAfter + * @return mixed + */ + protected function selectMiddlewareBefore(Reflection $reflect, array $classData, array &$customAfter): mixed + { + $response = self::$middleware[$classData[0]]->before($this->response, $this->request); if (!is_null($classData[1])) { if(is_array($classData[1])) { foreach($classData[1] as $beforeAfter => $middlewareMethod) { - if(is_array($middlewareMethod)) { - - foreach($middlewareMethod as $where => $mMethod) { + foreach($middlewareMethod as $mMethod) { if($beforeAfter === "before") { - $response = self::$middleware[$classData[0]]->{$mMethod}($this->response, $this->request); + $response = $reflect->dependencyInjector(self::$middleware[$classData[0]] , $mMethod); } else { - $customAfter[$classData[0]][] = $mMethod; + $customAfter[] = function() use ($reflect, $classData, $mMethod) { + return $reflect->dependencyInjector(self::$middleware[$classData[0]] , $mMethod); + }; } } - } else { - $response = self::$middleware[$classData[0]]->{$middlewareMethod}($this->response, $this->request); + $response = $reflect->dependencyInjector(self::$middleware[$classData[0]] , $middlewareMethod); } } } else { $response = self::$middleware[$classData[0]]->{$classData[1]}($this->response, $this->request); - } + } } + + $customAfter[] = function() use ($reflect, $classData) { + return $reflect->dependencyInjector(self::$middleware[$classData[0]] , "after"); + }; + return $response; } - protected function selectMiddlewareAfter($customAfter) { + /** + * Will select and load dependency injector for after middleware + * @param array $customAfter + * @return mixed + */ + protected function selectMiddlewareAfter(array $customAfter): mixed + { $response = null; if (is_array(self::$middleware)) { - foreach (self::$middleware as $kew => $middlewareInst) { - - if(isset($customAfter[$kew])) { - if(is_array($customAfter[$kew])) { - foreach($customAfter[$kew] as $customMethod) { - $response = $middlewareInst->{$customMethod}($this->response, $this->request); - } - } else { - $response = $middlewareInst->{$customAfter[$kew]}($this->response, $this->request); - } - } - - $response = $middlewareInst->after($this->response, $this->request); + foreach($customAfter as $func) { + $response = $func(); } } return $response; } /** - * Extract class and mthod from argument + * Extract class and method from argument * @param string|array $classMethod * @return array */ diff --git a/WhoopsHandler.php b/WhoopsHandler.php new file mode 100755 index 0000000..4cbf78c --- /dev/null +++ b/WhoopsHandler.php @@ -0,0 +1,33 @@ +getException(); + $inspector = $this->getInspector(); + + // Limit the size of function arguments in each frame + foreach ($inspector->getFrames() as $frame) { + $args = $frame->getArgs(); + foreach ($args as &$arg) { + if (is_string($arg) && strlen($arg) > 100) { + $arg = substr($arg, 0, 100) . '...'; + } elseif (is_array($arg) && count($arg) > 10) { + $arg = array_slice($arg, 0, 10); + $arg[] = '...'; + } + } + $frame->setArgs($args); + } + + return parent::handle(); + } +}