diff --git a/app/Handlers/Error.php b/app/Handlers/Error.php index 4ba5819..de66ac1 100644 --- a/app/Handlers/Error.php +++ b/app/Handlers/Error.php @@ -10,6 +10,15 @@ final class Error extends \Slim\Handlers\Error { + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + * @param \Exception $exception + * + * @return \Psr\Http\Message\ResponseInterface + * @throws \ReflectionException + * @throws \Exception + */ public function __invoke(Request $request, Response $response, \Exception $exception) { $app = app(); @@ -20,7 +29,9 @@ public function __invoke(Request $request, Response $response, \Exception $excep $app->resolve(LoggerInterface::class)->error($exception); } - if (class_exists(SlashTrace::class) && ($app->isConsole() || $this->displayErrorDetails)) { + if (app()->has('slashtrace') && ($app->isConsole() || $this->displayErrorDetails)) { + app()->resolve('slashtrace')->register(); + http_response_code(500); throw $exception; } diff --git a/app/Handlers/PhpError.php b/app/Handlers/PhpError.php index 697f6b7..7ecbeee 100644 --- a/app/Handlers/PhpError.php +++ b/app/Handlers/PhpError.php @@ -10,6 +10,15 @@ final class PhpError extends \Slim\Handlers\PhpError { + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + * @param \Throwable $error + * + * @return \Psr\Http\Message\ResponseInterface + * @throws \ReflectionException + * @throws \Throwable + */ public function __invoke(Request $request, Response $response, \Throwable $error) { $app = app(); @@ -20,7 +29,9 @@ public function __invoke(Request $request, Response $response, \Throwable $error $app->resolve(LoggerInterface::class)->error($error); } - if (class_exists(SlashTrace::class) && ($app->isConsole() || $this->displayErrorDetails)) { + if (app()->has('slashtrace') && ($app->isConsole() || $this->displayErrorDetails)) { + app()->resolve('slashtrace')->register(); + http_response_code(500); throw $error; } diff --git a/app/ServiceProviders/SlashTrace.php b/app/ServiceProviders/SlashTrace.php index a75cda0..d6d8acd 100644 --- a/app/ServiceProviders/SlashTrace.php +++ b/app/ServiceProviders/SlashTrace.php @@ -3,21 +3,16 @@ namespace App\ServiceProviders; use SlashTrace\SlashTrace as ST; use SlashTrace\EventHandler\DebugHandler; -use SlashTrace\DebugRenderer\DebugCliRenderer; class SlashTrace implements ProviderInterface { - public static function register() - { - $d = new DebugHandler(); - if (php_sapi_name() === "cli") $d->setRenderer(new DebugCliRenderer()); - + public static function register() + { $st = new ST(); - $st->addHandler($d); - $st->register(); + $st->addHandler(new DebugHandler()); app()->getContainer()['slashtrace'] = $st; - } + } } \ No newline at end of file diff --git a/lib/Framework/App.php b/lib/Framework/App.php index 51be778..9dd8b73 100644 --- a/lib/Framework/App.php +++ b/lib/Framework/App.php @@ -11,25 +11,25 @@ class App { - public $appName; + public $appName; - const DEVELOPMENT = 'development'; - const STAGING = 'staging'; - const PRODUCTION = 'production'; - public $env = self::DEVELOPMENT; + const DEVELOPMENT = 'development'; + const STAGING = 'staging'; + const PRODUCTION = 'production'; + public $env = self::DEVELOPMENT; - /** @var \Slim\App */ - private $slim = null; - private $settings = []; - private static $instance = null; + /** @var \Slim\App */ + private $slim = null; + private $settings = []; + private static $instance = null; - /** - * @param string $appName - * @param array $settings - */ - protected function __construct($appName = '', $settings = []) - { + /** + * @param string $appName + * @param array $settings + */ + protected function __construct($appName = '', $settings = []) + { $this->appName = $appName; $this->settings = $settings; $this->slim = new \Slim\App($settings); @@ -40,42 +40,42 @@ protected function __construct($appName = '', $settings = []) date_default_timezone_set($settings['settings']['timezone']); \Locale::setDefault($settings['settings']['locale']); - set_error_handler(function($errno, $errstr, $errfile, $errline) { - if (!($errno & error_reporting())) { - return; - } - throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); - }); - - $container[RequestInterface::class] = $container['request']; - $container[ResponseInterface::class] = $container['response']; - - $container['errorHandler'] = function() use($displayErrorDetails) { - return new Error($displayErrorDetails); - }; - $container['phpErrorHandler'] = function() use($displayErrorDetails) { - return new PhpError($displayErrorDetails); - }; - $container['notFoundHandler'] = function() { - return new NotFound(); - }; - } - - /** - * Application Singleton Factory - * - * @param string $appName - * @param array $settings - * @return static - */ - final public static function instance($appName = '', $settings = []) - { - if (null === static::$instance) { - static::$instance = new static($appName, $settings); - } - - return static::$instance; - } + set_error_handler(function($errno, $errstr, $errfile, $errline) { + if (!($errno & error_reporting())) { + return; + } + throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); + }); + + $container[RequestInterface::class] = $container['request']; + $container[ResponseInterface::class] = $container['response']; + + $container['errorHandler'] = function() use($displayErrorDetails) { + return new Error($displayErrorDetails); + }; + $container['phpErrorHandler'] = function() use($displayErrorDetails) { + return new PhpError($displayErrorDetails); + }; + $container['notFoundHandler'] = function() { + return new NotFound(); + }; + } + + /** + * Application Singleton Factory + * + * @param string $appName + * @param array $settings + * @return static + */ + final public static function instance($appName = '', $settings = []) + { + if (null === static::$instance) { + static::$instance = new static($appName, $settings); + } + + return static::$instance; + } /** @@ -89,274 +89,317 @@ public function isConsole() } - /** - * set configuration param - * - * @return \Interop\Container\ContainerInterface - */ - public function getContainer() - { - return $this->slim->getContainer(); - } - - /** - * set configuration param - * - * @param string $param - * @param mixed $value - */ - public function setConfig($param, $value) - { - $dn = new DotNotation($this->settings); - $dn->set($param, $value); - } - - /** - * get configuration param - * - * @param string $param - * @param string $defaultValue - * @return mixed - */ - public function getConfig($param, $defaultValue = null) - { - $dn = new DotNotation($this->settings); - return $dn->get($param, $defaultValue); - } - - /** - * register providers - * - * @return void - */ - public function registerProviders() - { - $providers = (array)$this->getConfig('providers'); - array_walk($providers, function(&$appName, $provider) { - if (strpos($appName, $this->appName) !== false) { + /** + * set configuration param + * + * @return \Interop\Container\ContainerInterface + */ + public function getContainer() + { + return $this->slim->getContainer(); + } + + + /** + * set configuration param + * + * @param string $param + * @param mixed $value + */ + public function setConfig($param, $value) + { + $dn = new DotNotation($this->settings); + $dn->set($param, $value); + } + + + /** + * get configuration param + * + * @param string $param + * @param string $defaultValue + * @return mixed + */ + public function getConfig($param, $defaultValue = null) + { + $dn = new DotNotation($this->settings); + return $dn->get($param, $defaultValue); + } + + + /** + * register providers + * + * @return void + */ + public function registerProviders() + { + $providers = (array)$this->getConfig('providers'); + array_walk($providers, function(&$appName, $provider) { + if (strpos($appName, $this->appName) !== false) { /** @var $provider \App\ServiceProviders\ProviderInterface */ $provider::register(); } - }); - } - - /** - * register providers - * - * @return void - */ - public function registerMiddleware() - { - $middlewares = array_reverse((array)$this->getConfig('middleware')); + }); + } + + + /** + * register providers + * + * @return void + */ + public function registerMiddleware() + { + $middlewares = array_reverse((array)$this->getConfig('middleware')); array_walk($middlewares, function($appName, $middleware) { if (strpos($appName, $this->appName) !== false) { $this->slim->add(new $middleware); } }); - } - - - //proxy all gets to slim - public function __get($name) - { - $c = $this->getContainer(); - - if ($c->has($name)) { - return $c->get($name); - } - return $this->resolve($name); - } - - //proxy all sets to slim - public function __set($k, $v) - { - $this->slim->{$k} = $v; - } - - // proxy calls to slim - public function __call($fn, $args = []) - { - if (method_exists($this->slim, $fn)) { - return call_user_func_array([$this->slim, $fn], $args); - } - throw new \Exception('Method not found :: '.$fn); - } - - - /** - * generate a url - * - * @param string $url - * @param boolean|null $showIndex pass null to assume config file value - * @param boolean $includeBaseUrl - * @return string - */ - public function url($url = '', $showIndex = null, $includeBaseUrl = true) - { - $baseUrl = $includeBaseUrl ? $this->getConfig('settings.baseUrl') : ''; - - $indexFile = ''; - if ($showIndex || ($showIndex === null && (bool)$this->getConfig('settings.indexFile'))) { - $indexFile = 'index.php/'; - } - if (strlen($url) > 0 && $url[0] == '/') { - $url = ltrim($url, '/'); - } - - return strtolower($baseUrl.$indexFile.$url); - } - - - /** - * return a response object - * - * @param mixed $resp - * @return \Psr\Http\Message\ResponseInterface + } + + + /** + * @param $name + * @return bool + */ + public function has($name) + { + $c = $this->getContainer(); + return $c->has($name); + } + + + /** + * @param $name + * @return mixed + * @throws \ReflectionException + */ + public function __get($name) + { + $c = $this->getContainer(); + + if ($c->has($name)) { + return $c->get($name); + } + return $this->resolve($name); + } + + + /** + * @param $k + * @param $v + */ + public function __set($k, $v) + { + $this->slim->{$k} = $v; + } + + + /** + * @param $fn + * @param array $args + * @return mixed + * @throws \Exception + */ + public function __call($fn, $args = []) + { + if (method_exists($this->slim, $fn)) { + return call_user_func_array([$this->slim, $fn], $args); + } + throw new \Exception('Method not found :: '.$fn); + } + + + /** + * generate a url + * + * @param string $url + * @param boolean|null $showIndex pass null to assume config file value + * @param boolean $includeBaseUrl + * @return string + */ + public function url($url = '', $showIndex = null, $includeBaseUrl = true) + { + $baseUrl = $includeBaseUrl ? $this->getConfig('settings.baseUrl') : ''; + + $indexFile = ''; + if ($showIndex || ($showIndex === null && (bool)$this->getConfig('settings.indexFile'))) { + $indexFile = 'index.php/'; + } + if (strlen($url) > 0 && $url[0] == '/') { + $url = ltrim($url, '/'); + } + + return strtolower($baseUrl.$indexFile.$url); + } + + + /** + * return a response object + * + * @param mixed $resp + * @return \Psr\Http\Message\ResponseInterface * * @throws \ReflectionException - */ - public function sendResponse($resp) - { - $response = $this->resolve('response'); - - if ($resp instanceof ResponseInterface) { - $response = $resp; - } elseif (is_array($resp) || is_object($resp)) { + */ + public function sendResponse($resp) + { + $response = $this->resolve('response'); + + if ($resp instanceof ResponseInterface) { + $response = $resp; + } elseif (is_array($resp) || is_object($resp)) { $response = $response->withJson($resp); - } else { + } else { $response = $response->write($resp); - } - - return $response; - } - - - /** - * resolve and call a given class / method - * - * @param string $namespace - * @param string $className - * @param string $methodName - * @param array $requestParams - * @return \Psr\Http\Message\ResponseInterface - */ - public function resolveRoute($namespace = '\App\Http', $className, $methodName, $requestParams = []) - { - try { - $class = new \ReflectionClass($namespace.'\\'.$className); - - if (!$class->isInstantiable() || !$class->hasMethod($methodName)) { - throw new \ReflectionException("route class is not instantiable or method does not exist"); - } - } catch (\ReflectionException $e) { - return $this->notFound(); - } - - $constructorArgs = $this->resolveMethodDependencies($class->getConstructor()); - $controller = $class->newInstanceArgs($constructorArgs); - - $method = $class->getMethod($methodName); - $args = $this->resolveMethodDependencies($method, $requestParams); - - $ret = $method->invokeArgs($controller, $args); - - return $this->sendResponse($ret); - } - - - /** - * resolve a dependency from the container - * - * @throws \ReflectionException - * @param string $name - * @param array $params - * @param mixed - * @return mixed - */ - public function resolve($name, $params = []) - { - $c = $this->getContainer(); - if ($c->has($name)) { - return is_callable($c[$name]) ? call_user_func_array($c[$name], $params) : $c[$name]; - } - - if (!class_exists($name)) { - throw new \ReflectionException("Unable to resolve {$name}"); - } - - $reflector = new \ReflectionClass($name); - - if (!$reflector->isInstantiable()) { - throw new \ReflectionException("Class {$name} is not instantiable"); - } - - if ($constructor = $reflector->getConstructor()) { - $dependencies = $this->resolveMethodDependencies($constructor); - return $reflector->newInstanceArgs($dependencies); - } - - return new $name(); - } - - - /** - * resolve dependencies for a given class method - * - * @param \ReflectionMethod $method - * @param array $urlParams - * @return array - */ - private function resolveMethodDependencies(\ReflectionMethod $method, $urlParams = []) - { - return array_map(function ($dependency) use($urlParams) { - return $this->resolveDependency($dependency, $urlParams); - }, $method->getParameters()); - } - - - /** - * resolve a dependency parameter - * - * @param \ReflectionParameter $param - * @param array $urlParams - * @return mixed + } + + return $response; + } + + + /** + * resolve and call a given class / method * + * @param string $namespace + * @param string $className + * @param string $methodName + * @param array $requestParams + * @return \Psr\Http\Message\ResponseInterface * @throws \ReflectionException */ - private function resolveDependency(\ReflectionParameter $param, $urlParams = []) - { - // for controller method para injection from $_GET - if (count($urlParams) && array_key_exists($param->name, $urlParams)) { - return $urlParams[$param->name]; - } + public function resolveRoute($namespace = '\App\Http', $className, $methodName, $requestParams = []) + { + + try { + $class = new \ReflectionClass($namespace.'\\'.$className); + + if (!$class->isInstantiable() || !$class->hasMethod($methodName)) { + throw new \ReflectionException("route class is not instantiable or method does not exist"); + } + } catch (\ReflectionException $e) { + return $this->notFound(); + } + + $constructorArgs = $this->resolveMethodDependencies($class->getConstructor()); + $controller = $class->newInstanceArgs($constructorArgs); + + $method = $class->getMethod($methodName); + $args = $this->resolveMethodDependencies($method, $requestParams); + + $ret = $method->invokeArgs($controller, $args); + + return $this->sendResponse($ret); + } - // param is instantiable - if ($param->isDefaultValueAvailable()) { - return $param->getDefaultValue(); - } - if (!$param->getClass()) { - throw new \ReflectionException("Unable to resolve method param {$param->name}"); - } + /** + * resolve a dependency from the container + * + * @throws \ReflectionException + * @param string $name + * @param array $params + * @param mixed + * @return mixed + */ + public function resolve($name, $params = []) + { + $c = $this->getContainer(); + if ($c->has($name)) { + return is_callable($c[$name]) ? call_user_func_array($c[$name], $params) : $c[$name]; + } - // try to resolve from container - return $this->resolve($param->getClass()->name); - } + if (!class_exists($name)) { + throw new \ReflectionException("Unable to resolve {$name}"); + } + $reflector = new \ReflectionClass($name); - /** - * @return \Psr\Http\Message\ResponseInterface - */ - public function notFound() - { - $handler = $this->getContainer()['notFoundHandler']; + if (!$reflector->isInstantiable()) { + throw new \ReflectionException("Class {$name} is not instantiable"); + } - return $handler($this->getContainer()['request'], $this->getContainer()['response']); - } + if ($constructor = $reflector->getConstructor()) { + $dependencies = $this->resolveMethodDependencies($constructor); + return $reflector->newInstanceArgs($dependencies); + } + + return new $name(); + } + + + /** + * resolve dependencies for a given class method + * + * @param \ReflectionMethod $method + * @param array $urlParams + * @return array + */ + private function resolveMethodDependencies(\ReflectionMethod $method, $urlParams = []) + { + return array_map(function ($dependency) use($urlParams) { + return $this->resolveDependency($dependency, $urlParams); + }, $method->getParameters()); + } + + + /** + * resolve a dependency parameter + * + * @param \ReflectionParameter $param + * @param array $urlParams + * @return mixed + * + * @throws \ReflectionException + */ + private function resolveDependency(\ReflectionParameter $param, $urlParams = []) + { + // for controller method para injection from $_GET + if (count($urlParams) && array_key_exists($param->name, $urlParams)) { + return $urlParams[$param->name]; + } + + // param is instantiable + if ($param->isDefaultValueAvailable()) { + return $param->getDefaultValue(); + } + + if (!$param->getClass()) { + throw new \ReflectionException("Unable to resolve method param {$param->name}"); + } + + // try to resolve from container + return $this->resolve($param->getClass()->name); + } + + + /** + * @return \Psr\Http\Message\ResponseInterface + */ + public function notFound() + { + $handler = $this->getContainer()['notFoundHandler']; + + return $handler($this->getContainer()['request'], $this->getContainer()['response']); + } + + + /** + * @param int $httpCode + * @return mixed + * + * @throws \ReflectionException + */ + public function code($httpCode = 200) + { + return $this->resolve('response')->withStatus($httpCode); + } /** - * @param string $msg + * @param mixed $msg * @param int $code * @return \Psr\Http\Message\ResponseInterface * @@ -382,7 +425,7 @@ function error($msg, $code = 500) ->withJson($msg); } - $resp = $this->resolve(\League\Plates\Engine::class)->render('error::500', ['code' => $code, 'message' => $msg]); + $resp = $this->resolve('view')->render('http::error', ['code' => $code, 'message' => $msg]); return $this->resolve('response') ->withStatus($code)