From 05ed4cb1c1b34c3111523d0f65bdeccdccf4782a Mon Sep 17 00:00:00 2001 From: Juan M Date: Thu, 16 Oct 2014 14:25:44 -0300 Subject: [PATCH 1/6] Override container values from the app constructor Something like this should be fine: ```php $app = new \Cicada\Application(array('debug' => true)); ``` --- src/Application.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Application.php b/src/Application.php index f0e50f4..244e581 100644 --- a/src/Application.php +++ b/src/Application.php @@ -29,7 +29,7 @@ class Application extends \Pimple\Container { use RequestProcessorTrait; - public function __construct() + public function __construct(array $values = array()) { parent::__construct(); @@ -53,6 +53,10 @@ public function __construct() $this['emitter'] = function () { return new EventEmitter(); }; + + foreach ($values as $key => $value) { + $this[$key] = $value; + } } public function get($pattern, $callback) From 567920ddd1d7c3f417a5677409b55ea34485311c Mon Sep 17 00:00:00 2001 From: Juan M Date: Thu, 16 Oct 2014 23:29:43 -0300 Subject: [PATCH 2/6] New `debug` parameter in the application This will be injected into the exception handler so, when in debug mode and an exception is raised, it can be catched and display a nice page with more information. --- src/Application.php | 6 ++++-- src/ExceptionHandler.php | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Application.php b/src/Application.php index 244e581..67d95f6 100644 --- a/src/Application.php +++ b/src/Application.php @@ -33,6 +33,8 @@ public function __construct(array $values = array()) { parent::__construct(); + $this['debug'] = false; + $this['router'] = function () { return new Routing\Router(); }; @@ -46,8 +48,8 @@ public function __construct(array $values = array()) return new RouteCollection($route); }); - $this['exception_handler'] = function () { - return new ExceptionHandler(); + $this['exception_handler'] = function ($app) { + return new ExceptionHandler($app['debug']); }; $this['emitter'] = function () { diff --git a/src/ExceptionHandler.php b/src/ExceptionHandler.php index 7e30c41..81a24ac 100644 --- a/src/ExceptionHandler.php +++ b/src/ExceptionHandler.php @@ -18,8 +18,14 @@ class ExceptionHandler { + private $debug = false; private $callbacks = []; + public function __construct($debug = false) + { + $this->debug = $debug; + } + /** * Adds an exception callback. * From d602b4ba6e3f425634712466959211533429eb4f Mon Sep 17 00:00:00 2001 From: Juan M Date: Thu, 16 Oct 2014 23:34:19 -0300 Subject: [PATCH 3/6] When in debug mode, generate page for exceptions Exception are catched and if in debug mode, will not call the subscripted handlers but generate a nice page showing the exception instead. --- src/ExceptionDebugWrapper.php | 252 ++++++++++++++++++++++++++++++++++ src/ExceptionHandler.php | 13 ++ 2 files changed, 265 insertions(+) create mode 100644 src/ExceptionDebugWrapper.php diff --git a/src/ExceptionDebugWrapper.php b/src/ExceptionDebugWrapper.php new file mode 100644 index 0000000..1f5e7f8 --- /dev/null +++ b/src/ExceptionDebugWrapper.php @@ -0,0 +1,252 @@ +exception = $exception; + + if (($previous = $exception->getPrevious()) !== null) { + $this->previous = new static($previous); + } + + // TODO Retrieve valuable information that the exception could + // have. For example, if it's an HttpException could be a + // not found exception and the $statusCode should change. + } + + public function getMessage() + { + return $this->exception->getMessage(); + } + + public function getCode() + { + return $this->exception->getCode(); + } + + public function getFile() + { + return $this->exception->getFile(); + } + + public function getLine() + { + return $this->exception->getLine(); + } + + public function getTrace() + { + return $this->exception->getTrace(); + } + + public function getTraceAsString() + { + return $this->exception->getTraceAsString(); + } + + public function getPrevious() + { + return $this->previous; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function setStatusCode($statusCode) + { + $this->statusCode = $statusCode; + } + + public function toHTML() + { + return << + + + + + {$this->getMessage()} - Error {$this->getStatusCode()} + + + +

{$this->getMessage()} + + {$this->formatClassName()} in {$this->getFile()} at line {$this->getLine()} + +

+{$this->toHTMLException()} + + + +EOF; + } + + public function toHTMLException() + { + $list = ''; + + foreach ($this->exception->getTrace() as $trace) { + $class = ''; + $line = ''; + $args = ''; + + if (isset($trace['class']) && isset($trace['type'])) { + $class = $this->formatClassName($trace['class']) . $trace['type']; + } + + if (isset($trace['file']) && isset($trace['line'])) { + $line = "in {$trace['file']} at line {$trace['line']}"; + } + + if (isset($trace['args'])) { + $args = implode(', ', $this->formatArguments($trace['args'])); + } + + $list .= << +
{$class}{$trace['function']}($args)
+
$line
+ +EOF; + } + + if ($this->getPrevious() !== null) { + $previous = $this->getPrevious()->toHTMLException(); + } + + return << +

{$this->formatClassName()}: {$this->getMessage()}

+
    {$list} +
+ +{$previous} +EOF; + } + + protected function formatClassName($class = null) + { + if ($class === null) { + $class = get_class($this->exception); + } + + $className = ltrim(strrchr($class, '\\'), '\\'); + + if (empty($className)) { + return $class; + } + + return "$className"; + } + + protected function formatArguments(array $arguments) + { + $args = []; + + foreach ($arguments as $key => $argument) { + if (is_object($argument)) { + if (!method_exists($argument, '__debugInfo')) { + $args[] = $this->formatClassName( + get_class($argument) + ); + continue; + } + $argument = $arg->__debugInfo(); + } + + switch (gettype($argument)) { + case 'integer': + case 'double': + $args[] = $argument; + break; + + case 'string': + $args[] = '"' . $argument . '"'; + break; + + case 'boolean': + $args[] = $argument ? 'true' : 'false'; + break; + + case 'array': + if (empty($argument)) { + $args[] = '[]'; + break; + } + + $keys = array_keys($argument); + $array = $this->formatArguments($argument); + + if (!is_int(current($keys))) { + $array = array_map(function($key, $value) { + return "\"$key\"=>$value"; + }, $keys, $array); + + $args[] = '{' . implode(', ', $array) . '}'; + break; + } + + $args[] = '[' . implode(', ', $array) . ']'; + break; + + default: + $args[] = gettype($argument); + } + } + + return $args; + } +} \ No newline at end of file diff --git a/src/ExceptionHandler.php b/src/ExceptionHandler.php index 81a24ac..00a81f6 100644 --- a/src/ExceptionHandler.php +++ b/src/ExceptionHandler.php @@ -16,6 +16,8 @@ */ namespace Cicada; +use Symfony\Component\HttpFoundation\Response; + class ExceptionHandler { private $debug = false; @@ -81,6 +83,10 @@ public function add(callable $callback) */ public function handle(\Exception $ex) { + if ($this->debug) { + return $this->handleDebug($ex); + } + foreach ($this->callbacks as $exClass => $callback) { if ($ex instanceof $exClass) { return $callback($ex); @@ -88,6 +94,13 @@ public function handle(\Exception $ex) } } + public function handleDebug(\Exception $ex) + { + $wrapped = new ExceptionDebugWrapper($ex); + + return new Response($wrapped->toHTML(), $wrapped->getStatusCode()); + } + /** Returns the handlers array */ public function getCallbacks() { From dd6278cf01dd7dd82effeb747407df0fb86942ab Mon Sep 17 00:00:00 2001 From: Juan M Date: Fri, 17 Oct 2014 01:01:37 -0300 Subject: [PATCH 4/6] Missing variable declaration --- src/ExceptionDebugWrapper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ExceptionDebugWrapper.php b/src/ExceptionDebugWrapper.php index 1f5e7f8..9c80215 100644 --- a/src/ExceptionDebugWrapper.php +++ b/src/ExceptionDebugWrapper.php @@ -163,6 +163,7 @@ public function toHTMLException() EOF; } + $previous = ''; if ($this->getPrevious() !== null) { $previous = $this->getPrevious()->toHTMLException(); } From 0e6f143ccb14e6a150c64ff193ae7256b9da7ed3 Mon Sep 17 00:00:00 2001 From: Juan M Date: Fri, 17 Oct 2014 01:02:22 -0300 Subject: [PATCH 5/6] Basic test for the returned response when debug --- tests/ExceptionHandlerTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/ExceptionHandlerTest.php b/tests/ExceptionHandlerTest.php index 9c9af0f..d663211 100644 --- a/tests/ExceptionHandlerTest.php +++ b/tests/ExceptionHandlerTest.php @@ -87,4 +87,17 @@ public function testNoMaches() $actual = $handler->handle(new \BadMethodCallException()); $this->assertNull($actual); } + + public function testDebug() + { + $handler = new ExceptionHandler(true); + $handler->add(function(\Exception $ex) { return 1; }); + + $actual = $handler->handle(new \Exception('Help', 123)); + + $this->assertNotEquals($actual, 1); + $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $actual); + $this->assertRegExp('/Help/', $actual->getContent()); + $this->assertEquals(500, $actual->getStatusCode()); + } } From 40cdc357791309048da50f146da81fc6bfe3cbd6 Mon Sep 17 00:00:00 2001 From: Juan M Date: Fri, 17 Oct 2014 01:12:26 -0300 Subject: [PATCH 6/6] Update CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3116cd..86a1cfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ Cicada changelog ================ +Next release +------------ + +Features: + +* Application accepts an array of configuration values in the constructor +* New `debug` parameter, when true all exception will be catched and a nice formatted page will be shown + 0.4.9 (2014-10-15) ------------------