From 2087ed383e56d769e5911a3b5c3c6a1c188e671d Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Fri, 16 Aug 2019 19:15:27 +0100 Subject: [PATCH 01/29] Updated README Signed-off-by: Tom Wright --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7be7b20..67e28d5 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ class Amazeballs { public function doStuff() { throw \Glitch::{'ENotFound,EFailedService'}( - 'Server "doStuff" cannot be found' + 'Service "doStuff" cannot be found' ); } } @@ -146,6 +146,19 @@ throw \Glitch::ECompletelyMadeUpMeaning([ ]); ``` +Catch a Glitch in the normal using whichever scope you require + +```php +try { + throw \Glitch::{'ENotFound,EBadMethodCall'}( + 'Didn\'t find a thing, couldn\'t call the other thing' + ); +} catch(\EGlitch | \ENotFound | MyLibrary\EGlitch | MyLibrary\AThingThatDoesStuff\EBadMethodCall $e) { + // All these types will catch +} +``` + + ## Licensing Glitch is licensed under the MIT License. See [LICENSE](https://github.com/decodelabs/glitch/blob/master/LICENSE) for the full license text. From 81b4052192da7c8c5c3e16c5a510fa8657253faa Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Fri, 16 Aug 2019 19:16:02 +0100 Subject: [PATCH 02/29] Updated README Signed-off-by: Tom Wright --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 67e28d5..72c011d 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ throw \Glitch::ECompletelyMadeUpMeaning([ ]); ``` -Catch a Glitch in the normal using whichever scope you require +Catch a Glitch in the normal way using whichever scope you require: ```php try { From 1c692c1307358f85c4425a41a6a44c53816c2753 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Fri, 16 Aug 2019 19:19:08 +0100 Subject: [PATCH 03/29] Updated README Signed-off-by: Tom Wright --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 72c011d..409657a 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ becomes ### Usage -Throw Glitches rather than Exceptions, passing mixed in interfaces as the method name (error interfaces must begin with E) +Throw Glitches rather than Exceptions, passing mixed in interfaces as the method name (generated error interfaces must begin with E) ```php throw \Glitch::EOutOfBounds('This is out of bounds'); @@ -139,11 +139,16 @@ throw \Glitch::{'ENotFound,EBadMethodCall'}( ); // You can associate a http code too.. -throw \Glitch::ECompletelyMadeUpMeaning([ - 'message' => 'My message', +throw \Glitch::ECompletelyMadeUpMeaning('My message', [ 'code' => 1234, 'http' => 501 ]); + +throw \Glitch::{'EInvalidArgument,Psr\\Cache\\InvalidArgumentException'}( + 'Cache items must implement Cache\\IItem', + ['http' => 500], // params + $item // data +); ``` Catch a Glitch in the normal way using whichever scope you require: From a925116b39a8f7e1139ac5079d98e54031c78070 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Fri, 16 Aug 2019 19:25:08 +0100 Subject: [PATCH 04/29] Updated class docs Signed-off-by: Tom Wright --- src/Glitch/Factory.php | 4 ++++ src/Glitch/PathHandler.php | 19 +++++++++++++++++++ src/Glitch/TException.php | 3 +++ 3 files changed, 26 insertions(+) diff --git a/src/Glitch/Factory.php b/src/Glitch/Factory.php index 349ebc4..974052e 100644 --- a/src/Glitch/Factory.php +++ b/src/Glitch/Factory.php @@ -6,6 +6,10 @@ declare(strict_types=1); namespace Glitch; +/** + * Automatically generate Exceptions on the fly based on scope and + * requested interface types + */ class Factory { const STANDARD = [ diff --git a/src/Glitch/PathHandler.php b/src/Glitch/PathHandler.php index e799d9d..fe60853 100644 --- a/src/Glitch/PathHandler.php +++ b/src/Glitch/PathHandler.php @@ -6,10 +6,17 @@ declare(strict_types=1); namespace Glitch; +/** + * A very simple global list of path lookup aliases used to + * simplify human readable paths in a debug context + */ class PathHandler { protected static $aliases = null; + /** + * Register global path replacement alias + */ public static function registerAlias(string $name, string $path): void { self::$aliases[$name] = $path; @@ -19,6 +26,10 @@ public static function registerAlias(string $name, string $path): void }); } + + /** + * Register list of global path replacement aliases + */ public static function registerAliases(array $aliases): void { foreach ($aliases as $name => $path) { @@ -30,11 +41,19 @@ public static function registerAliases(array $aliases): void }); } + + /** + * Inspect list of registered aliases + */ public static function getAliases(): array { return array_flip(self::$aliases); } + + /** + * Lookup and replace path prefix + */ public static function normalizePath(string $path): string { foreach (self::$aliases as $name => $test) { diff --git a/src/Glitch/TException.php b/src/Glitch/TException.php index 63a27a3..92db8bf 100644 --- a/src/Glitch/TException.php +++ b/src/Glitch/TException.php @@ -21,6 +21,9 @@ trait TException protected $rewind; protected $stackTrace; + /** + * Override the standard Exception constructor to simplify instantiation + */ public function __construct($message, array $params=[]) { parent::__construct( From 979480189d47386663396e1195e331716bbddb8f Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Mon, 19 Aug 2019 16:33:24 +0100 Subject: [PATCH 05/29] Moved PathHandler to shareable Context Signed-off-by: Tom Wright --- README.md | 4 +- src/Glitch/Context.php | 96 ++++++++++++++++++++++++++++++++++++++ src/Glitch/PathHandler.php | 67 -------------------------- src/Glitch/Stack/Frame.php | 4 +- src/Glitch/Stack/Trace.php | 6 +-- src/Glitch/TException.php | 2 +- src/helpers.php | 3 -- 7 files changed, 104 insertions(+), 78 deletions(-) create mode 100644 src/Glitch/Context.php delete mode 100644 src/Glitch/PathHandler.php diff --git a/README.md b/README.md index 409657a..1455554 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ composer require decodelabs/glitch Register base paths for easier reading of file names ```php -\Glitch\PathHandler::registerAlias('app', '/path/to/my/app'); +\Glitch\Context::getDefault()->registerPathAlias('app', '/path/to/my/app'); /* /path/to/my/app/models/MyModel.php @@ -147,7 +147,7 @@ throw \Glitch::ECompletelyMadeUpMeaning('My message', [ throw \Glitch::{'EInvalidArgument,Psr\\Cache\\InvalidArgumentException'}( 'Cache items must implement Cache\\IItem', ['http' => 500], // params - $item // data + $item // data ); ``` diff --git a/src/Glitch/Context.php b/src/Glitch/Context.php new file mode 100644 index 0000000..422d6ac --- /dev/null +++ b/src/Glitch/Context.php @@ -0,0 +1,96 @@ +pathAliases['glitch'] = dirname(__DIR__); + } + + + /** + * Register path replacement alias + */ + public function registerPathAlias(string $name, string $path): Context + { + $this->pathAliases[$name] = $path; + + uasort($this->pathAliases, function ($a, $b) { + return strlen($b) - strlen($a); + }); + + return $this; + } + + /** + * Register list of path replacement aliases + */ + public function registerPathAliases(array $aliases): Context + { + foreach ($aliases as $name => $path) { + $this->pathAliases[$name] = $path; + } + + uasort($this->pathAliases, function ($a, $b) { + return strlen($b) - strlen($a); + }); + + return $this; + } + + /** + * Inspect list of registered path aliases + */ + public function getPathAliases(): array + { + return $this->pathAliases; + } + + /** + * Lookup and replace path prefix + */ + public function normalizePath(string $path): string + { + foreach ($this->pathAliases as $name => $test) { + if (0 === strpos($path, $test)) { + return '{'.$name.'}'.substr($path, strlen($test)); + } + } + + return $path; + } +} diff --git a/src/Glitch/PathHandler.php b/src/Glitch/PathHandler.php deleted file mode 100644 index fe60853..0000000 --- a/src/Glitch/PathHandler.php +++ /dev/null @@ -1,67 +0,0 @@ - $path) { - self::$aliases[$name] = $path; - } - - uasort(self::$aliases, function ($a, $b) { - return strlen($b) - strlen($a); - }); - } - - - /** - * Inspect list of registered aliases - */ - public static function getAliases(): array - { - return array_flip(self::$aliases); - } - - - /** - * Lookup and replace path prefix - */ - public static function normalizePath(string $path): string - { - foreach (self::$aliases as $name => $test) { - if (0 === strpos($path, $test)) { - return '{'.$name.'}'.substr($path, strlen($test)); - } - } - - return $path; - } -} diff --git a/src/Glitch/Stack/Frame.php b/src/Glitch/Stack/Frame.php index 00df6f2..8c5b9d0 100644 --- a/src/Glitch/Stack/Frame.php +++ b/src/Glitch/Stack/Frame.php @@ -6,7 +6,7 @@ declare(strict_types=1); namespace Glitch\Stack; -use Glitch\PathHandler; +use Glitch\Context; /** * Represents a single entry in a stack trace @@ -378,7 +378,7 @@ public function getCallingLine(): ?int public function toArray(): array { return [ - 'file' => PathHandler::normalizePath($this->getFile()), + 'file' => Context::getDefault()->normalizePath($this->getFile()), 'line' => $this->getLine(), 'function' => $this->function, 'class' => $this->className, diff --git a/src/Glitch/Stack/Trace.php b/src/Glitch/Stack/Trace.php index 234d3f3..ba1f29a 100644 --- a/src/Glitch/Stack/Trace.php +++ b/src/Glitch/Stack/Trace.php @@ -6,7 +6,7 @@ declare(strict_types=1); namespace Glitch\Stack; -use Glitch\PathHandler; +use Glitch\Context; /** * Represents a normalized stack trace @@ -171,12 +171,12 @@ public function __debugInfo(): array foreach ($frames as $i => $frame) { if ($i === 0) { $output[($count + 1).': Glitch'] = [ - 'file' => PathHandler::normalizePath($frame->getFile()).' : '.$frame->getLine() + 'file' => GlitchContext::getDefault()->normalizePath($frame->getFile()).' : '.$frame->getLine() ]; } $output[($count - $i).': '.$frame->getSignature(true)] = [ - 'file' => PathHandler::normalizePath($frame->getCallingFile()).' : '.$frame->getCallingLine() + 'file' => GlitchContext::getDefault()->normalizePath($frame->getCallingFile()).' : '.$frame->getCallingLine() ]; } diff --git a/src/Glitch/TException.php b/src/Glitch/TException.php index 92db8bf..0db89ef 100644 --- a/src/Glitch/TException.php +++ b/src/Glitch/TException.php @@ -136,7 +136,7 @@ public function __debugInfo(): array $output['types'] = array_merge($types, array_values(class_implements($this))); sort($output['types']); - $output['file'] = PathHandler::normalizePath($this->file).' : '.$this->line; + $output['file'] = Context::getDefault()->normalizePath($this->file).' : '.$this->line; // Trace diff --git a/src/helpers.php b/src/helpers.php index 21634c3..0ac3427 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -12,7 +12,6 @@ namespace { use Glitch\Factory; - use Glitch\PathHandler; /** * Direct facade for generating IError based exceptions @@ -27,6 +26,4 @@ function Glitch($message, ?array $params=[], $data=null): \EGlitch $data ); } - - PathHandler::registerAlias('glitch', __DIR__); } From ddf39d1e828cb2a12281a96329ee33b935aab69d Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Mon, 19 Aug 2019 18:11:10 +0100 Subject: [PATCH 06/29] Imported Df debug functionality Signed-off-by: Tom Wright --- composer.json | 5 +- src/Glitch.php | 40 +++++++++ src/Glitch/Context.php | 156 ++++++++++++++++++++++++++++++++-- src/Glitch/Dumper/Symfony.php | 128 ++++++++++++++++++++++++++++ src/Glitch/IContext.php | 42 +++++++++ src/helpers.php | 37 ++++++++ 6 files changed, 401 insertions(+), 7 deletions(-) create mode 100644 src/Glitch/Dumper/Symfony.php create mode 100644 src/Glitch/IContext.php diff --git a/composer.json b/composer.json index 130d0c7..356d64c 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,10 @@ ], "require": { "php": "^7.1.3", - "decodelabs/df-base": "dev-develop" + "symfony/polyfill-php72": "~1.7", + "symfony/polyfill-mbstring": "~1.7", + + "symfony/var-dumper": "~4.1.0", }, "autoload": { "psr-4": { diff --git a/src/Glitch.php b/src/Glitch.php index fc76d63..0adb5a3 100644 --- a/src/Glitch.php +++ b/src/Glitch.php @@ -6,6 +6,8 @@ declare(strict_types=1); use Glitch\Factory; +use Glitch\Context; +use Glitch\IContext; /** * This is just a facade. @@ -14,6 +16,9 @@ final class Glitch { const TYPE = null; + /** + * Redirect type list to Factory + */ public static function __callStatic(string $method, array $args): \EGlitch { return Factory::create( @@ -23,6 +28,41 @@ public static function __callStatic(string $method, array $args): \EGlitch ); } + /** + * Shortcut to Context + */ + public static function getContext(): IContext + { + return Context::getDefault(); + } + + /** + * Shortcut to incomplete context method + */ + public static function incomplete($data=null) + { + Context::getDefault()->incomplete($data, 1); + } + + /** + * Shortcut to normalizePath context method + */ + public static function normalizePath(string $path): string + { + return Context::getDefault()->normalizePath($path); + } + + /** + * Shortcut to logException context method + */ + public function logException(\Throwable $e): void + { + Context::getDefault()->logException($e); + } + + /** + * Private instanciation + */ private function __construct() { } diff --git a/src/Glitch/Context.php b/src/Glitch/Context.php index 422d6ac..5acc9db 100644 --- a/src/Glitch/Context.php +++ b/src/Glitch/Context.php @@ -6,16 +6,21 @@ declare(strict_types=1); namespace Glitch; -class Context +use Glitch\Stack\Frame as StackFrame; + +class Context implements IContext { protected static $default; + protected $startTime; + protected $runMode = 'development'; protected $pathAliases = []; + /** * Create / fetch default context */ - public static function getDefault(): Context + public static function getDefault(): IContext { if (!self::$default) { self::setDefault(new self()); @@ -27,7 +32,7 @@ public static function getDefault(): Context /** * Set custom default context */ - public static function setDefault(Context $default): void + public static function setDefault(IContext $default): void { self::$default = $default; } @@ -38,14 +43,151 @@ public static function setDefault(Context $default): void */ public function __construct() { + $this->startTime = microtime(true); $this->pathAliases['glitch'] = dirname(__DIR__); } + + /** + * Set active run mode + */ + public function setRunMode(string $mode): IContext + { + switch ($mode) { + case 'production': + case 'testing': + case 'development': + $this->runMode = $mode; + break; + + default: + throw \Glitch::EInvalidArgument('Invalid run mode', null, $mode); + } + + return $this; + } + + /** + * Get current run mode + */ + public function getRunMode(): string + { + return $this->runMode; + } + + /** + * Is Glitch in development mode? + */ + public function isDevelopment(): bool + { + return $this->runMode == 'development'; + } + + /** + * Is Glitch in testing mode? + */ + public function isTesting(): bool + { + return $this->runMode == 'testing' + || $this->runMode == 'development'; + } + + /** + * Is Glitch in production mode? + */ + public function isProduction(): bool + { + return $this->runMode == 'production'; + } + + + + /** + * Send variables to dump, carry on execution + */ + public function dump(array $vars, int $rewind=null): void + { + $this->tempHandler()->dump(array_shift($vars), ...$vars); + } + + /** + * Send variables to dump, exit and render + */ + public function dumpDie(array $vars, int $rewind=null): void + { + $this->tempHandler()->dumpDie(array_shift($vars), ...$vars); + } + + + protected function tempHandler() + { + static $output; + + if (!isset($output)) { + $output = new \Glitch\Dumper\Symfony(); + } + + return $output; + } + + + public function dd2(array $vars, int $rewind=null): void + { + dd($vars, $rewind); + } + + /** + * Quit a stubbed method + */ + public function incomplete($data=null, int $rewind=null): void + { + $frame = StackFrame::create($rewind + 1); + + throw \Glitch::EImplementation( + $frame->getSignature().' has not been implemented yet', + null, + $data + ); + } + + + + /** + * Log an exception... somewhere :) + */ + public function logException(\Throwable $e): void + { + // TODO: put this somewhere + } + + + + /** + * Override app start time + */ + public function setStartTime(float $time): IContext + { + $this->startTime = $time; + return $this; + } + + /** + * Get app start time + */ + public function getStartTime(): float + { + return $this->startTime; + } + + + + + /** * Register path replacement alias */ - public function registerPathAlias(string $name, string $path): Context + public function registerPathAlias(string $name, string $path): IContext { $this->pathAliases[$name] = $path; @@ -59,7 +201,7 @@ public function registerPathAlias(string $name, string $path): Context /** * Register list of path replacement aliases */ - public function registerPathAliases(array $aliases): Context + public function registerPathAliases(array $aliases): IContext { foreach ($aliases as $name => $path) { $this->pathAliases[$name] = $path; @@ -85,9 +227,11 @@ public function getPathAliases(): array */ public function normalizePath(string $path): string { + $path = str_replace('\\', '/', $path); + foreach ($this->pathAliases as $name => $test) { if (0 === strpos($path, $test)) { - return '{'.$name.'}'.substr($path, strlen($test)); + return $name.'://'.ltrim(substr($path, strlen($test)), '/'); } } diff --git a/src/Glitch/Dumper/Symfony.php b/src/Glitch/Dumper/Symfony.php new file mode 100644 index 0000000..85e3038 --- /dev/null +++ b/src/Glitch/Dumper/Symfony.php @@ -0,0 +1,128 @@ +setStyles([ + 'default' => 'background-color:#fff; color:#bbb; line-height:1.2; font-weight:normal; font:12px Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:100000', + 'num' => 'color:#a814e3', + 'const' => 'color:#b50acc', + 'str' => 'color:#cc2123', + 'cchr' => 'color:#222', + 'note' => 'color:#0cb300', + 'ref' => 'color:#a0a0a0', + 'public' => 'color:#795da3', + 'protected' => 'color:#795da3', + 'private' => 'color:#795da3', + 'meta' => 'color:#0cb300', + 'key' => 'color:#df5000', + 'index' => 'color:#a71d5d', + ]); + + $dumper->setDisplayOptions([ + 'maxDepth' => 3 + ]); + } + + + $cloner = new Cloner\VarCloner(); + $dumper->dump($cloner->cloneVar($var)); + } + + /** + * Dump a list of vars + */ + public function dump(...$vars): void + { + $trace = Trace::create(); + + foreach ($trace as $frame) { + if (substr($frame->getFunctionName(), 0, 4) != 'dump' + && false === strpos($frame->getCallingFile() ?? '', 'var-dumper')) { + break; + } + } + + + $attrs = [ + 'time' => self::formatMicrotime(microtime(true) - \Glitch::getContext()->getStartTime()), + 'memory' => self::formatFilesize(memory_get_usage()), + 'location' => \Glitch::normalizePath($frame->getCallingFile()).' : '.$frame->getCallingLine() + ]; + + if ('cli' === PHP_SAPI) { + echo implode(' | ', $attrs)."\n\n"; + } else { + echo '
'.implode(' | ', $attrs).'
'; + } + + foreach ($vars as $var) { + $this->dumpOne($var); + } + } + + /** + * Dump vars and die + */ + public function dumpDie(...$vars): void + { + while (ob_get_level()) { + ob_end_clean(); + } + + http_response_code(500); + $this->dump(...$vars); + die(1); + } + + + + /** + * TODO: move these to a shared location + */ + private static function formatFilesize($bytes) + { + $units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']; + + for ($i = 0; $bytes > 1024; $i++) { + $bytes /= 1024; + } + + return round($bytes, 2).' '.$units[$i]; + } + + private static function formatMicrotime($time) + { + return number_format($time * 1000, 2).' ms'; + } +} diff --git a/src/Glitch/IContext.php b/src/Glitch/IContext.php new file mode 100644 index 0000000..788f4b6 --- /dev/null +++ b/src/Glitch/IContext.php @@ -0,0 +1,42 @@ +dumpDie(func_get_args(), 1); + } + } + + if (!function_exists('dump')) { + /** + * Quick dump + */ + function dump($var, ...$vars): void + { + Context::getDefault()->dump(func_get_args(), 1); + } + } elseif (class_exists(VarDumper::class)) { + VarDumper::setHandler(function ($var, ...$vars) { + Context::getDefault()->dump(func_get_args(), 1); + }); + } + + + /** + * Temporary dump handler + */ + function dd2($var, ...$vars): void + { + Context::getDefault()->dd2(func_get_args()); + } + /** * Direct facade for generating IError based exceptions From 7d2429bc270958b27387ef1b43ea7aeea11d3c21 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Mon, 19 Aug 2019 18:11:59 +0100 Subject: [PATCH 07/29] Fixed typo in composer.json Signed-off-by: Tom Wright --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 356d64c..2c6f9b1 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "symfony/polyfill-php72": "~1.7", "symfony/polyfill-mbstring": "~1.7", - "symfony/var-dumper": "~4.1.0", + "symfony/var-dumper": "~4.1.0" }, "autoload": { "psr-4": { From 224d916178f9ca48c648120080ad09418801d89b Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Tue, 20 Aug 2019 10:25:07 +0100 Subject: [PATCH 08/29] Added dump inspector library Signed-off-by: Tom Wright --- src/Glitch/Context.php | 51 ++- src/Glitch/Dumper/Dump.php | 59 +++ src/Glitch/Dumper/Entity.php | 430 ++++++++++++++++++ src/Glitch/Dumper/Inspector.php | 412 +++++++++++++++++ .../Dumper/ObjectInspect/Reflection.php | 27 ++ src/Glitch/Dumper/ObjectInspect/Spl.php | 11 + src/Glitch/IContext.php | 5 + src/Glitch/IInspectable.php | 15 + src/Glitch/Stack/Trace.php | 4 +- 9 files changed, 1004 insertions(+), 10 deletions(-) create mode 100644 src/Glitch/Dumper/Dump.php create mode 100644 src/Glitch/Dumper/Entity.php create mode 100644 src/Glitch/Dumper/Inspector.php create mode 100644 src/Glitch/Dumper/ObjectInspect/Reflection.php create mode 100644 src/Glitch/Dumper/ObjectInspect/Spl.php create mode 100644 src/Glitch/IInspectable.php diff --git a/src/Glitch/Context.php b/src/Glitch/Context.php index 5acc9db..e0b3635 100644 --- a/src/Glitch/Context.php +++ b/src/Glitch/Context.php @@ -6,7 +6,10 @@ declare(strict_types=1); namespace Glitch; -use Glitch\Stack\Frame as StackFrame; +use Glitch\Stack\Frame; +use Glitch\Stack\Trace; +use Glitch\Dumper\Inspector; +use Glitch\Dumper\Dump; class Context implements IContext { @@ -15,6 +18,7 @@ class Context implements IContext protected $startTime; protected $runMode = 'development'; protected $pathAliases = []; + protected $objectInspectors = []; /** @@ -106,17 +110,17 @@ public function isProduction(): bool /** * Send variables to dump, carry on execution */ - public function dump(array $vars, int $rewind=null): void + public function dump(array $values, int $rewind=null): void { - $this->tempHandler()->dump(array_shift($vars), ...$vars); + $this->tempHandler()->dump(array_shift($values), ...$values); } /** * Send variables to dump, exit and render */ - public function dumpDie(array $vars, int $rewind=null): void + public function dumpDie(array $values, int $rewind=null): void { - $this->tempHandler()->dumpDie(array_shift($vars), ...$vars); + $this->tempHandler()->dumpDie(array_shift($values), ...$values); } @@ -132,9 +136,21 @@ protected function tempHandler() } - public function dd2(array $vars, int $rewind=null): void + public function dd2(array $values, int $rewind=null): void { - dd($vars, $rewind); + $trace = Trace::create($rewind + 1); + $inspector = new Inspector($this); + + $dump = new Dump( + $trace, + microtime(true) - $this->getStartTime(), + memory_get_peak_usage() + ); + + foreach ($values as $value) { + $dump->addEntity($inspector->inspectValue($value)); + } + dd($dump); } /** @@ -142,7 +158,7 @@ public function dd2(array $vars, int $rewind=null): void */ public function incomplete($data=null, int $rewind=null): void { - $frame = StackFrame::create($rewind + 1); + $frame = Frame::create($rewind + 1); throw \Glitch::EImplementation( $frame->getSignature().' has not been implemented yet', @@ -237,4 +253,23 @@ public function normalizePath(string $path): string return $path; } + + + + /** + * Register callable inspector for a specific class + */ + public function registerObjectInspector(string $class, callable $inspector): IContext + { + $this->objectInspectors[$class] = $inspector; + return $this; + } + + /** + * Get list of registered inspectors + */ + public function getObjectInspectors(): array + { + return $this->objectInspectors; + } } diff --git a/src/Glitch/Dumper/Dump.php b/src/Glitch/Dumper/Dump.php new file mode 100644 index 0000000..f2936f3 --- /dev/null +++ b/src/Glitch/Dumper/Dump.php @@ -0,0 +1,59 @@ +trace = $trace; + $this->time = $time; + $this->memory = $memory; + } + + + /** + * Get active trace + */ + public function getTrace(): Trace + { + return $this->trace; + } + + /** + * Add an entity to the list + */ + public function addEntity($entity): Dump + { + $this->entities[] = $entity; + return $this; + } + + /** + * Get list of entities + */ + public function getEntities(): array + { + return $this->entities; + } + + public function getIterator() + { + return new ArrayIterator($this->entities); + } +} diff --git a/src/Glitch/Dumper/Entity.php b/src/Glitch/Dumper/Entity.php new file mode 100644 index 0000000..82fda82 --- /dev/null +++ b/src/Glitch/Dumper/Entity.php @@ -0,0 +1,430 @@ +type = $type; + $this->id = str_replace('.', '-', uniqid($type.'-', true)); + } + + /** + * Static entity type name + */ + public function getType(): string + { + return $this->type; + } + + + + /** + * Set entity instance name + */ + public function setName(?string $name): Entity + { + $this->name = $name; + return $this; + } + + /** + * Get entity instance name + */ + public function getName(): ?string + { + return $this->name; + } + + + /** + * Set object id + */ + public function setId(?string $id): Entity + { + $this->id = $id; + return $this; + } + + + /** + * Get object id + */ + public function getId(): ?string + { + return $this->id; + } + + + /** + * Set object id + */ + public function setObjectId(?int $id): Entity + { + $this->objectId = $id; + return $this; + } + + /** + * Get object id + */ + public function getObjectId(): ?int + { + return $this->objectId; + } + + + /** + * Set object class + */ + public function setClass(?string $class): Entity + { + $this->class = $class; + return $this; + } + + /** + * Get object class + */ + public function getClass(): ?string + { + return $this->class; + } + + + /** + * Set parent classes + */ + public function setParentClasses(string ...$parents): Entity + { + if (empty($parents)) { + $parents = null; + } + + $this->parents = $parents; + return $this; + } + + /** + * Get parent classes + */ + public function getParentClasses(): ?array + { + return $this->parents; + } + + + /** + * Set interfaces + */ + public function setInterfaces(string ...$interfaces): Entity + { + if (empty($interfaces)) { + $interfaces = null; + } + + $this->interfaces = $interfaces; + return $this; + } + + /** + * Get interfaces + */ + public function getInterfaces(): ?array + { + return $this->interfaces; + } + + + /** + * Set traits + */ + public function setTraits(string ...$traits): Entity + { + if (empty($traits)) { + $traits = null; + } + + $this->traits = $traits; + return $this; + } + + /** + * Get traits + */ + public function getTraits(): ?array + { + return $this->traits; + } + + + + /** + * Set source file + */ + public function setFile(?string $file): Entity + { + $this->file = $file; + return $this; + } + + /** + * Get source file + */ + public function getFile(): ?string + { + return $this->file; + } + + /** + * Set source line + */ + public function setStartLine(?int $line): Entity + { + $this->startLine = $line; + return $this; + } + + /** + * Get source line + */ + public function getStartLine(): ?int + { + return $this->startLine; + } + + /** + * Set source end line + */ + public function setEndLine(?int $line): Entity + { + $this->endLine = $line; + return $this; + } + + /** + * Get source end line + */ + public function getEndLine(): ?int + { + return $this->endLine; + } + + + + + /** + * Set object text + */ + public function setText(?string $text): Entity + { + $this->text = $text; + return $this; + } + + /** + * Get object text + */ + public function getText(): ?string + { + return $this->text; + } + + + /** + * Set item length + */ + public function setLength(?int $length): Entity + { + $this->length = $length; + return $this; + } + + /** + * Get item length + */ + public function getLength(): ?int + { + return $this->length; + } + + + + + /** + * Set meta value + */ + public function setMeta(string $key, $value): Entity + { + $this->checkValidity($value); + + $this->meta[$key] = $value; + return $this; + } + + /** + * Get meta value + */ + public function getMeta(string $key) + { + return $this->meta[$key] ?? null; + } + + /** + * Get all meta data + */ + public function getAllMeta(): ?array + { + return $this->meta; + } + + + + /** + * Set values list + */ + public function setValues(?array $values): Entity + { + foreach ($values as $value) { + $this->checkValidity($value); + } + + $this->values = $values; + return $this; + } + + /** + * Get values list + */ + public function getValues(): ?array + { + return $this->values; + } + + + /** + * Set show keys + */ + public function setShowKeys(bool $show): Entity + { + $this->showValueKeys = $show; + return $this; + } + + /** + * Should show values keys? + */ + public function shouldShowKeys(): bool + { + return $this->showValueKeys; + } + + + + /** + * Set properties + */ + public function setProperties(array $properties): Entity + { + foreach ($properties as $key => $value) { + $this->setProperty($key, $value); + } + + return $this; + } + + /** + * Get properties + */ + public function getProperties(): ?array + { + return $this->properties; + } + + + /** + * Set property + */ + public function setProperty(string $key, $value): Entity + { + $this->checkValidity($value); + $this->properties[$key] = $value; + return $this; + } + + /** + * Get property + */ + public function getProperty(string $key) + { + return $this->properties[$key] ?? null; + } + + /** + * Has property + */ + public function hasProperty(string $key): bool + { + if (empty($this->properties)) { + return false; + } + + return array_key_exists($key, $this->properties); + } + + + + /** + * Check value for Entity validity + */ + protected function checkValidity($value): void + { + switch (true) { + case $value === null: + case is_bool($value): + case is_int($value): + case is_float($value): + case is_string($value): + case $value instanceof Entity: + return; + + default: + throw \Glitch::EUnexpectedValue( + 'Invalid sub-entity type - must be scalar or Entity', + null, + $value + ); + } + } +} diff --git a/src/Glitch/Dumper/Inspector.php b/src/Glitch/Dumper/Inspector.php new file mode 100644 index 0000000..e349dad --- /dev/null +++ b/src/Glitch/Dumper/Inspector.php @@ -0,0 +1,412 @@ + [Obj\Reflection::class, 'inspectClosure'], + 'Generator' => [Obj\Reflection::class, 'inspectGenerator'] + ]; + + protected $objectInspectors = []; + + + /** + * Construct with context to generate object inspectors + */ + public function __construct(IContext $context) + { + $this->objectInspectors = static::OBJECTS; + + foreach ($context->getObjectInspectors() as $class => $inspector) { + $this->objectInspectors[$class] = $inspector; + } + } + + + /** + * Inspect single value + */ + public function inspectValue($value) + { + switch (true) { + case $value === null: + case is_bool($value): + case is_int($value): + case is_float($value): + return $value; + + case is_string($value): + return $this->inspectString($value); + + case is_resource($value): + return $this->inspectResource($value); + + case is_array($value): + return $this->inspectArray($value); + + case is_object($value): + return $this->inspectObject($value); + + default: + throw \Glitch::EUnexpectedValue( + 'Unknown entity type', + null, + $value + ); + } + } + + /** + * Inspect values list + */ + public function inspectValues(array $values): array + { + $output = []; + + foreach ($values as $key => $value) { + $output[$key] = $this->inspectValue($value); + } + + return $output; + } + + + /** + * Convert string into Entity + */ + public function inspectString(string $string) + { + // Binary string + if ($string !== '' && !preg_match('//u', $string)) { + return (new Entity('binary')) + ->setName('Binary') + ->setText(bin2hex($string)) + ->setLength(strlen($string)); + + // Class name + } elseif (class_exists($string)) { + return (new Entity('class')) + ->setClass($string); + + // Interface name + } elseif (interface_exists($string)) { + return (new Entity('interface')) + ->setClass($string); + + + // Standard string + } else { + return $string; + } + } + + /** + * Convert resource into Entity + */ + public function inspectResource($resource): Entity + { + $output = (new Entity('resource')) + ->setName((string)$resource) + ->setClass($rType = get_resource_type($resource)); + + $typeName = str_replace(' ', '', ucwords($rType)); + $method = 'inspect'.ucfirst($typeName).'Resource'; + + if (method_exists($this, $method)) { + $this->{$method}($resource, $output); + } + + return $output; + } + + /** + * Inspect curl resource + */ + public function inspectCurlResource($resource, Entity $entity): void + { + foreach (curl_getinfo($resource) as $key => $value) { + $entity->setMeta($key, $this->inspectValue($value)); + } + } + + /** + * Inspect dba resource + */ + public function inspectDbaResource($resource, Entity $entity): void + { + $list = dba_list(); + $entity->setMeta('file', $this->inspectValue($list[(int)$resource])); + } + + /** + * Inspect dba persistent resource + */ + public function inspectDbaPersistentResource($resource, Entity $entity): void + { + $this->inspectDbaResource($resource, $entity); + } + + /** + * Inspect gd resource + */ + public function inspectGdResource($resource, Entity $entity): void + { + $entity + ->setMeta('width', $this->inspectValue(imagesx($resource))) + ->setMeta('height', $this->inspectValue(imagesy($resource))); + } + + /** + * Inspect pgsql resource + */ + public function inspectPgsqlLinkResource($resource, Entity $entity): void + { + } + + /** + * Inspect pgsql persistent resource + */ + public function inspectPgsqlPersistentLinkResource($resource, Entity $entity): void + { + } + + /** + * Inspect pgsql large object resource + */ + public function inspectPgsqlLargeObjectResource($resource, Entity $entity): void + { + } + + /** + * Inspect pgsql result resource + */ + public function inspectPgsqlResultResource($resource, Entity $entity): void + { + } + + /** + * Inspect process resource + */ + public function inspectProcessResource($resource, Entity $entity): void + { + foreach (proc_get_status($resource) as $key => $value) { + $entity->setMeta($key, $this->inspectValue($value)); + } + } + + /** + * Inspect stream resource + */ + public function inspectStreamResource($resource, Entity $entity): void + { + foreach (stream_get_meta_data($resource) as $key => $value) { + $entity->setMeta($key, $this->inspectValue($value)); + } + + $this->inspectStreamContextResource($resource, $entity); + } + + /** + * Inspect persistent stream resource + */ + public function inspectPersistentStreamResource($resource, Entity $entity): void + { + $this->inspectStreamResource($resource, $entity); + } + + /** + * Inspect stream context resource + */ + public function inspectStreamContextResource($resource, Entity $entity): void + { + if (!$params = @stream_context_get_params($resource)) { + return; + } + + foreach ($params as $key => $value) { + $entity->setMeta($key, $this->inspectValue($value)); + } + } + + /** + * Inspect xml resource + */ + public function inspectXmlResource($resource, Entity $entity): void + { + $entity + ->setMeta('current_byte_index', $this->inspectValue(xml_get_current_byte_index($resource))) + ->setMeta('current_column_number', $this->inspectValue(xml_get_current_column_number($resource))) + ->setMeta('current_line_number', $this->inspectValue(xml_get_current_line_number($resource))) + ->setMeta('error_code', $this->inspectValue(xml_get_error_code($resource))); + } + + + + + + + /** + * Convert array into Entity + */ + public function inspectArray(array $array): ?Entity + { + return (new Entity('array')) + ->setClass('array') + ->setLength(count($array)) + ->setValues($this->inspectValues($array)); + } + + + + /** + * Convert object into Entity + */ + public function inspectObject(object $object): ?Entity + { + $reflection = new \ReflectionObject($object); + + $entity = (new Entity('object')) + ->setName($reflection->getShortName()) + ->setClass($this->normalizeClassName($className = $reflection->getName(), $reflection)) + ->setObjectId(spl_object_id($object)); + + if (!$reflection->isInternal()) { + $entity + ->setFile($reflection->getFileName()) + ->setStartLine($reflection->getStartLine()) + ->setEndLine($reflection->getEndLine()); + } + + $parents = $this->inspectObjectParents($reflection, $entity); + + $reflections = [ + $className => $reflection + ] + $parents; + + $this->inspectObjectProperties($object, $reflections, $entity); + + return $entity; + } + + /** + * Normalize virtual class name + */ + protected function normalizeClassName(string $class, \ReflectionObject $reflection): string + { + if (0 === strpos($class, "class@anonymous\x00")) { + $class = $reflection->getParentClass()->getShortName().'@anonymous'; + } + + return $class; + } + + + /** + * Inspect object parents + */ + protected function inspectObjectParents(\ReflectionObject $reflection, Entity $entity): array + { + // Parents + $reflectionBase = $reflection; + $parents = []; + + while (true) { + if (!$ref = $reflectionBase->getParentClass()) { + break; + } + + $parents[$ref->getName()] = $ref; + $reflectionBase = $ref; + } + + $entity + ->setParentClasses(...array_keys($parents)) + ->setInterfaces(...$reflection->getInterfaceNames()) + ->setTraits(...$reflection->getTraitNames()); + + return $parents; + } + + /** + * Find object property provider + */ + protected function inspectObjectProperties(object $object, array $reflections, Entity $entity): void + { + $className = get_class($object); + + // Object inspector + if (isset($this->objectInspectors[$className])) { + call_user_func($this->objectInspectors[$className], $object, $entity, $this); + return; + + // Inspectable + } elseif ($object instanceof IInspectable) { + $object->glitchInspect($entity, $this); + return; + + // Debug info + } elseif (method_exists($object, '__debugInfo')) { + $entity->setValues($this->inspectValues($object->__debugInfo())); + return; + } + + + // Members + foreach (array_reverse($reflections) as $className => $reflection) { + // Parent object inspectors + if (isset($this->objectInspectors[$className])) { + call_user_func($this->objectInspectors[$className], $object, $entity, $this); + continue; + } + + // Reflection + $this->inspectClassMembers($object, $reflection, $entity); + } + } + + /** + * Inspect class members + */ + protected function inspectClassMembers(object $object, \ReflectionClass $reflection, Entity $entity): void + { + foreach ($reflection->getProperties() as $property) { + if ($property->isStatic()) { + continue; + } + + $property->setAccessible(true); + $name = $property->getName(); + $prefix = null; + + switch (true) { + case $property->isProtected(): + $prefix = '*'; + break; + + case $property->isPrivate(): + $prefix = '!'; + break; + } + + if ($entity->hasProperty($name)) { + continue; + } + + $value = $property->getValue($object); + $entity->setProperty($name, $this->inspectValue($value)); + } + } +} diff --git a/src/Glitch/Dumper/ObjectInspect/Reflection.php b/src/Glitch/Dumper/ObjectInspect/Reflection.php new file mode 100644 index 0000000..b2cc636 --- /dev/null +++ b/src/Glitch/Dumper/ObjectInspect/Reflection.php @@ -0,0 +1,27 @@ + $frame) { if ($i === 0) { $output[($count + 1).': Glitch'] = [ - 'file' => GlitchContext::getDefault()->normalizePath($frame->getFile()).' : '.$frame->getLine() + 'file' => Context::getDefault()->normalizePath($frame->getFile()).' : '.$frame->getLine() ]; } $output[($count - $i).': '.$frame->getSignature(true)] = [ - 'file' => GlitchContext::getDefault()->normalizePath($frame->getCallingFile()).' : '.$frame->getCallingLine() + 'file' => Context::getDefault()->normalizePath($frame->getCallingFile()).' : '.$frame->getCallingLine() ]; } From 382bbdcba39d98614953051b470737c5a1ab23e0 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Tue, 20 Aug 2019 23:26:09 +0100 Subject: [PATCH 09/29] Added core ObjectInspect Signed-off-by: Tom Wright --- src/Glitch/Dumper/Entity.php | 21 +++ src/Glitch/Dumper/Inspector.php | 44 +++++- src/Glitch/Dumper/ObjectInspect/Core.php | 58 +++++++ .../Dumper/ObjectInspect/Reflection.php | 145 +++++++++++++++++- 4 files changed, 262 insertions(+), 6 deletions(-) create mode 100644 src/Glitch/Dumper/ObjectInspect/Core.php diff --git a/src/Glitch/Dumper/Entity.php b/src/Glitch/Dumper/Entity.php index 82fda82..f6d915d 100644 --- a/src/Glitch/Dumper/Entity.php +++ b/src/Glitch/Dumper/Entity.php @@ -22,6 +22,7 @@ class Entity protected $endLine; protected $text; + protected $definition; protected $length; protected $meta; @@ -262,6 +263,26 @@ public function getText(): ?string } + + /** + * Set definition code + */ + public function setDefinition(?string $definition): Entity + { + $this->definition = $definition; + return $this; + } + + /** + * Get definition code + */ + public function getDefinition(): ?string + { + return $this->definition; + } + + + /** * Set item length */ diff --git a/src/Glitch/Dumper/Inspector.php b/src/Glitch/Dumper/Inspector.php index e349dad..94ed891 100644 --- a/src/Glitch/Dumper/Inspector.php +++ b/src/Glitch/Dumper/Inspector.php @@ -16,8 +16,23 @@ class Inspector { const OBJECTS = [ - 'Closure' => [Obj\Reflection::class, 'inspectClosure'], - 'Generator' => [Obj\Reflection::class, 'inspectGenerator'] + // Core + 'Closure' => [Obj\Core::class, 'inspectClosure'], + 'Generator' => [Obj\Core::class, 'inspectGenerator'], + '__PHP_Incomplete_Class' => [Obj\Core::class, 'inspectIncompleteClass'], + + // Reflection + 'ReflectionClass' => [Obj\Reflection::class, 'inspectReflectionClass'], + 'ReflectionClassConstant' => [Obj\Reflection::class, 'inspectReflectionClassConstant'], + 'ReflectionZendExtension' => [Obj\Reflection::class, 'inspectReflectionZendExtension'], + 'ReflectionExtension' => [Obj\Reflection::class, 'inspectReflectionExtension'], + 'ReflectionFunction' => [Obj\Reflection::class, 'inspectReflectionFunction'], + 'ReflectionFunctionAbstract' => [Obj\Reflection::class, 'inspectReflectionFunction'], + 'ReflectionMethod' => [Obj\Reflection::class, 'inspectReflectionMethod'], + 'ReflectionParameter' => [Obj\Reflection::class, 'inspectReflectionParameter'], + 'ReflectionProperty' => [Obj\Reflection::class, 'inspectReflectionProperty'], + 'ReflectionType' => [Obj\Reflection::class, 'inspectReflectionType'], + 'ReflectionGenerator' => [Obj\Reflection::class, 'inspectReflectionGenerator'], ]; protected $objectInspectors = []; @@ -409,4 +424,29 @@ protected function inspectClassMembers(object $object, \ReflectionClass $reflect $entity->setProperty($name, $this->inspectValue($value)); } } + + + /** + * Convert a scalar value to a string + */ + public static function scalarToString($value): ?string + { + switch (true) { + case $value === null: + return 'null'; + + case is_bool($value): + return $value ? 'true' : 'false'; + + case is_int($value): + case is_float($value): + return (string)$value; + + case is_string($value): + return '"'.$value.'"'; + + default: + return (string)$value; + } + } } diff --git a/src/Glitch/Dumper/ObjectInspect/Core.php b/src/Glitch/Dumper/ObjectInspect/Core.php new file mode 100644 index 0000000..874315d --- /dev/null +++ b/src/Glitch/Dumper/ObjectInspect/Core.php @@ -0,0 +1,58 @@ +setDefinition(Reflection::getFunctionDefinition($reflection)) + ->setFile($reflection->getFileName()) + ->setStartLine($reflection->getStartLine()) + ->setEndLine($reflection->getEndLine()); + } + + /** + * Inspect Generator + */ + public static function inspectGenerator(\Generator $generator, Entity $entity, Inspector $inspector): void + { + try { + $reflection = new \ReflectionGenerator($generator); + } catch (\Exception $e) { + return; + } + + $function = $reflection->getFunction(); + + $entity + ->setDefinition(Reflection::getFunctionDefinition($function)) + ->setFile($function->getFileName()) + ->setStartLine($function->getStartLine()) + ->setEndLine($function->getEndLine()); + } + + /** + * Inspect __PHP_Incomplete_Class + */ + public static function inspectIncompleteClass(\__PHP_Incomplete_Class $class, Entity $entity, Inspector $inspector): void + { + $vars = (array)$class; + $entity->setDefinition($vars['__PHP_Incomplete_Class_Name']); + unset($vars['__PHP_Incomplete_Class_Name']); + $entity->setValues($inspector->inspectValues($vars)); + } +} diff --git a/src/Glitch/Dumper/ObjectInspect/Reflection.php b/src/Glitch/Dumper/ObjectInspect/Reflection.php index b2cc636..913fc95 100644 --- a/src/Glitch/Dumper/ObjectInspect/Reflection.php +++ b/src/Glitch/Dumper/ObjectInspect/Reflection.php @@ -12,16 +12,153 @@ class Reflection { /** - * Inspect Closure + * Inspect ReflectionClass */ - public static function inspectClosure(\Closure $closure, Entity $entity, Inspector $inspector): void + public static function inspectReflectionClass(\ReflectionClass $reflection, Entity $entity, Inspector $inspector): void { } /** - * Inspect Generator + * Inspect ReflectionClassConstant */ - public static function inspectGenerator(\Generator $generator, Entity $entity, Inspector $inspector): void + public static function inspectReflectionClassConstant(\ReflectionClassConstant $reflection, Entity $entity, Inspector $inspector): void { } + + /** + * Inspect ReflectionZendExtension + */ + public static function inspectReflectionZendExtension(\ReflectionZendExtension $reflection, Entity $entity, Inspector $inspector): void + { + } + + /** + * Inspect ReflectionExtension + */ + public static function inspectReflectionExtension(\ReflectionExtension $reflection, Entity $entity, Inspector $inspector): void + { + } + + /** + * Inspect ReflectionFunction + */ + public static function inspectReflectionFunction(\ReflectionFunction $reflection, Entity $entity, Inspector $inspector): void + { + } + + /** + * Inspect ReflectionMethod + */ + public static function inspectReflectionMethod(\ReflectionMethod $reflection, Entity $entity, Inspector $inspector): void + { + } + + /** + * Inspect ReflectionParameter + */ + public static function inspectReflectionParameter(\ReflectionParameter $reflection, Entity $entity, Inspector $inspector): void + { + } + + /** + * Inspect ReflectionProperty + */ + public static function inspectReflectionProperty(\ReflectionProperty $reflection, Entity $entity, Inspector $inspector): void + { + } + + /** + * Inspect ReflectionType + */ + public static function inspectReflectionType(\ReflectionType $reflection, Entity $entity, Inspector $inspector): void + { + } + + /** + * Inspect ReflectionGenerator + */ + public static function inspectReflectionGenerator(\ReflectionGenerator $reflection, Entity $entity, Inspector $inspector): void + { + } + + + + /** + * Export function definition + */ + public static function getFunctionDefinition(\ReflectionFunctionAbstract $reflection): string + { + $output = ''; + + if ($reflection instanceof \ReflectionMethod) { + $output = implode(' ', $reflection->getModifiers()); + + if (!empty($output)) { + $output .= ' '; + } + } + + $output .= 'function '; + + if ($reflection->returnsReference()) { + $output .= '& '; + } + + if (!$reflection->isClosure()) { + $output .= $reflection->getName().' '; + } + + $output .= '('; + $params = []; + + foreach ($reflection->getParameters() as $parameter) { + $params[] = self::getParameterDefinition($parameter); + } + + $output .= implode(', ', $params).')'; + + if ($returnType = $reflection->getReturnType()) { + $output .= ': '; + + if ($returnType->allowsNull()) { + $output .= '?'; + } + + $output .= (string)$returnType; + } + + return $output; + } + + /** + * Export parameter definition + */ + public static function getParameterDefinition(\ReflectionParameter $parameter): string + { + $output = ''; + + if ($parameter->allowsNull()) { + $output .= '?'; + } + + if ($type = $parameter->getType()) { + $output .= (string)$type.' '; + } + + if ($parameter->isPassedByReference()) { + $output .= '& '; + } + + if ($parameter->isVariadic()) { + $output .= '...'; + } + + $output .= '$'.$parameter->getName(); + + if ($parameter->isDefaultValueAvailable()) { + $output .= '='.(Inspector::scalarToString($parameter->getDefaultValue()) ?? '??'); + } + + return $output; + } } From a1551279fc6ee2fd2b71d8520e39cc363253a3bf Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Wed, 21 Aug 2019 00:39:06 +0100 Subject: [PATCH 10/29] Added Reflection ObjectInspect Signed-off-by: Tom Wright --- .../Dumper/ObjectInspect/Reflection.php | 155 +++++++++++++++++- 1 file changed, 151 insertions(+), 4 deletions(-) diff --git a/src/Glitch/Dumper/ObjectInspect/Reflection.php b/src/Glitch/Dumper/ObjectInspect/Reflection.php index 913fc95..1e26f2a 100644 --- a/src/Glitch/Dumper/ObjectInspect/Reflection.php +++ b/src/Glitch/Dumper/ObjectInspect/Reflection.php @@ -16,6 +16,14 @@ class Reflection */ public static function inspectReflectionClass(\ReflectionClass $reflection, Entity $entity, Inspector $inspector): void { + $entity->setDefinition(Reflection::getClassDefinition($reflection)); + + if (!$reflection->isInternal()) { + $entity + ->setFile($reflection->getFileName()) + ->setStartLine($reflection->getStartLine()) + ->setEndLine($reflection->getEndLine()); + } } /** @@ -23,6 +31,11 @@ public static function inspectReflectionClass(\ReflectionClass $reflection, Enti */ public static function inspectReflectionClassConstant(\ReflectionClassConstant $reflection, Entity $entity, Inspector $inspector): void { + $entity + ->setDefinition(Reflection::getConstantDefinition($reflection)) + ->setProperties([ + 'class' => $reflection->class + ]); } /** @@ -30,6 +43,12 @@ public static function inspectReflectionClassConstant(\ReflectionClassConstant $ */ public static function inspectReflectionZendExtension(\ReflectionZendExtension $reflection, Entity $entity, Inspector $inspector): void { + $entity->setProperties($inspector->inspectValues([ + 'version' => $reflection->getVersion(), + 'author' => $reflection->getAuthor(), + 'copyright' => $reflection->getCopyright(), + 'url' => $reflection->getURL() + ])); } /** @@ -37,13 +56,28 @@ public static function inspectReflectionZendExtension(\ReflectionZendExtension $ */ public static function inspectReflectionExtension(\ReflectionExtension $reflection, Entity $entity, Inspector $inspector): void { + $entity->setProperties($inspector->inspectValues([ + 'version' => $reflection->getVersion(), + 'dependencies' => $reflection->getDependencies(), + 'iniEntries' => $reflection->getIniEntries(), + 'isPersistent' => $reflection->isPersistent(), + 'isTemporary' => $reflection->isTemporary(), + 'constants' => $reflection->getConstants(), + 'functions' => $reflection->getFunctions(), + 'classes' => $reflection->getClasses() + ])); } /** * Inspect ReflectionFunction */ - public static function inspectReflectionFunction(\ReflectionFunction $reflection, Entity $entity, Inspector $inspector): void + public static function inspectReflectionFunction(\ReflectionFunctionAbstract $reflection, Entity $entity, Inspector $inspector): void { + $entity + ->setDefinition(Reflection::getFunctionDefinition($reflection)) + ->setFile($reflection->getFileName()) + ->setStartLine($reflection->getStartLine()) + ->setEndLine($reflection->getEndLine()); } /** @@ -51,6 +85,11 @@ public static function inspectReflectionFunction(\ReflectionFunction $reflection */ public static function inspectReflectionMethod(\ReflectionMethod $reflection, Entity $entity, Inspector $inspector): void { + self::inspectReflectionFunction($reflection, $entity, $inspector); + + $entity->setProperties([ + 'class' => $reflection->getDeclaringClass()->getName() + ]); } /** @@ -58,6 +97,7 @@ public static function inspectReflectionMethod(\ReflectionMethod $reflection, En */ public static function inspectReflectionParameter(\ReflectionParameter $reflection, Entity $entity, Inspector $inspector): void { + $entity->setDefinition(self::getParameterDefinition($reflection)); } /** @@ -65,6 +105,11 @@ public static function inspectReflectionParameter(\ReflectionParameter $reflecti */ public static function inspectReflectionProperty(\ReflectionProperty $reflection, Entity $entity, Inspector $inspector): void { + $entity + ->setDefinition(self::getPropertyDefinition($reflection)) + ->setProperties([ + 'class' => $reflection->getDeclaringClass()->getName() + ]); } /** @@ -72,6 +117,11 @@ public static function inspectReflectionProperty(\ReflectionProperty $reflection */ public static function inspectReflectionType(\ReflectionType $reflection, Entity $entity, Inspector $inspector): void { + $entity->setProperties([ + 'name' => $reflection->getName(), + 'allowsNull' => $reflection->allowsNull(), + 'isBuiltin' => $reflection->isBuiltin() + ]); } /** @@ -79,9 +129,106 @@ public static function inspectReflectionType(\ReflectionType $reflection, Entity */ public static function inspectReflectionGenerator(\ReflectionGenerator $reflection, Entity $entity, Inspector $inspector): void { + $function = $reflection->getFunction(); + + $entity + ->setDefinition(Reflection::getFunctionDefinition($function)) + ->setFile($function->getFileName()) + ->setStartLine($function->getStartLine()) + ->setEndLine($function->getEndLine()); + } + + + + + /** + * Export class definitoin + */ + public static function getClassDefinition(\ReflectionClass $reflection): string + { + $output = 'class '; + $name = $reflection->getName(); + + if (0 === strpos($name, "class@anonymous\x00")) { + $output .= '() '; + } else { + $output .= $name.' '; + } + + if ($parent = $reflection->getParentClass()) { + $output .= 'extends '.$parent->getName(); + } + + $interfaces = []; + + foreach ($reflection->getInterfaces() as $interface) { + $interfaces[] = $interface->getName(); + } + + if (!empty($interfaces)) { + $output .= 'implements '.implode(', ', $interfaces).' '; + } + + $output .= '{'."\n"; + + foreach ($reflection->getReflectionConstants() as $const) { + $output .= ' '.self::getConstantDefinition($const)."\n"; + } + + foreach ($reflection->getProperties() as $property) { + $output .= ' '.self::getPropertyDefinition($property)."\n"; + } + + foreach ($reflection->getMethods() as $method) { + $output .= ' '.self::getFunctionDefinition($method)."\n"; + } + + $output .= '}'; + + return $output; + } + + + /** + * Export property definition + */ + public static function getPropertyDefinition(\ReflectionProperty $reflection): string + { + $output = implode(' ', \Reflection::getModifierNames($reflection->getModifiers())); + $name = $reflection->getName(); + $output .= ' $'.$name.' = '; + $reflection->setAccessible(true); + $props = $reflection->getDeclaringClass()->getDefaultProperties(); + $value = $prop[$name] ?? null; + + if (is_array($value)) { + $output .= '[...]'; + } else { + $output .= Inspector::scalarToString($value); + } + + return $output; } + /** + * Export class constant definition + */ + public static function getConstantDefinition(\ReflectionClassConstant $reflection): string + { + $output = implode(' ', \Reflection::getModifierNames($reflection->getModifiers())); + $output .= ' const '.$reflection->getName().' = '; + $value = $reflection->getValue(); + + if (is_array($value)) { + $output .= '[...]'; + } else { + $output .= Inspector::scalarToString($value); + } + + return $output; + } + /** * Export function definition @@ -91,7 +238,7 @@ public static function getFunctionDefinition(\ReflectionFunctionAbstract $reflec $output = ''; if ($reflection instanceof \ReflectionMethod) { - $output = implode(' ', $reflection->getModifiers()); + $output = implode(' ', \Reflection::getModifierNames($reflection->getModifiers())); if (!empty($output)) { $output .= ' '; @@ -124,7 +271,7 @@ public static function getFunctionDefinition(\ReflectionFunctionAbstract $reflec $output .= '?'; } - $output .= (string)$returnType; + $output .= $returnType->getName(); } return $output; @@ -142,7 +289,7 @@ public static function getParameterDefinition(\ReflectionParameter $parameter): } if ($type = $parameter->getType()) { - $output .= (string)$type.' '; + $output .= $type->getName().' '; } if ($parameter->isPassedByReference()) { From fa1c53691113aff5effa643f85e6ab885d1f64e1 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Wed, 21 Aug 2019 01:43:23 +0100 Subject: [PATCH 11/29] Added reference handling to Inspector Signed-off-by: Tom Wright --- src/Glitch/Dumper/Entity.php | 18 +++++++++++ src/Glitch/Dumper/Inspector.php | 55 ++++++++++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/Glitch/Dumper/Entity.php b/src/Glitch/Dumper/Entity.php index f6d915d..88a5fe0 100644 --- a/src/Glitch/Dumper/Entity.php +++ b/src/Glitch/Dumper/Entity.php @@ -13,6 +13,7 @@ class Entity protected $id; protected $objectId; + protected $hash; protected $class; protected $parents; protected $interfaces; @@ -105,6 +106,23 @@ public function getObjectId(): ?int return $this->objectId; } + /** + * Set object / array hash + */ + public function setHash(?string $hash): Entity + { + $this->hash = $hash; + return $this; + } + + /** + * Get object / array hash + */ + public function getHash(): ?string + { + return $this->hash; + } + /** * Set object class diff --git a/src/Glitch/Dumper/Inspector.php b/src/Glitch/Dumper/Inspector.php index 94ed891..ea4ff9e 100644 --- a/src/Glitch/Dumper/Inspector.php +++ b/src/Glitch/Dumper/Inspector.php @@ -36,6 +36,9 @@ class Inspector ]; protected $objectInspectors = []; + protected $objectRefs = []; + protected $arrayRefs = []; + protected $arrayIds = []; /** @@ -278,10 +281,28 @@ public function inspectXmlResource($resource, Entity $entity): void */ public function inspectArray(array $array): ?Entity { - return (new Entity('array')) + $hash = $this->hashArray($array); + $isRef = isset($this->arrayRefs[$hash]); + + $entity = (new Entity($isRef ? 'arrayReference' : 'array')) ->setClass('array') + ->setHash($hash); + + if ($isRef) { + return $entity + ->setId($this->arrayRefs[$hash]) + ->setObjectId($this->arrayIds[$hash]); + } + + $this->arrayRefs[$hash] = $entity->getId(); + $this->arrayIds[$hash] = $id = count($this->arrayIds) + 1; + + $entity + ->setObjectId($id) ->setLength(count($array)) ->setValues($this->inspectValues($array)); + + return $entity; } @@ -291,12 +312,23 @@ public function inspectArray(array $array): ?Entity */ public function inspectObject(object $object): ?Entity { + $id = spl_object_id($object); $reflection = new \ReflectionObject($object); + $className = $reflection->getName(); + $isRef = isset($this->objectRefs[$id]); - $entity = (new Entity('object')) + $entity = (new Entity($isRef ? 'objectReference' : 'object')) ->setName($reflection->getShortName()) - ->setClass($this->normalizeClassName($className = $reflection->getName(), $reflection)) - ->setObjectId(spl_object_id($object)); + ->setClass($this->normalizeClassName($className, $reflection)) + ->setObjectId($id) + ->setHash(spl_object_hash($object)); + + if ($isRef) { + $entity->setId($this->objectRefs[$id]); + return $entity; + } + + $this->objectRefs[$id] = $entity->getId(); if (!$reflection->isInternal()) { $entity @@ -312,7 +344,6 @@ public function inspectObject(object $object): ?Entity ] + $parents; $this->inspectObjectProperties($object, $reflections, $entity); - return $entity; } @@ -449,4 +480,18 @@ public static function scalarToString($value): ?string return (string)$value; } } + + + + /** + * Dirty way to get a hash for an array + */ + public static function hashArray(array $array): ?string + { + if (empty($array)) { + return null; + } + + return md5(serialize($array)); + } } From 85e4e7eb372cd66a319680339116e34cb2e63869 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Wed, 21 Aug 2019 11:11:40 +0100 Subject: [PATCH 12/29] Converted resource inspectors to separate Inspect classes Signed-off-by: Tom Wright --- src/Glitch/Context.php | 20 ++ .../{ObjectInspect => Inspect}/Core.php | 2 +- src/Glitch/Dumper/Inspect/Curl.php | 23 ++ src/Glitch/Dumper/Inspect/Dba.php | 22 ++ src/Glitch/Dumper/Inspect/Gd.php | 33 ++ src/Glitch/Dumper/Inspect/Process.php | 23 ++ .../{ObjectInspect => Inspect}/Reflection.php | 2 +- .../Dumper/{ObjectInspect => Inspect}/Spl.php | 2 +- src/Glitch/Dumper/Inspect/Stream.php | 39 +++ src/Glitch/Dumper/Inspect/Xml.php | 25 ++ src/Glitch/Dumper/Inspector.php | 314 ++++++++++-------- src/Glitch/IContext.php | 4 +- 12 files changed, 361 insertions(+), 148 deletions(-) rename src/Glitch/Dumper/{ObjectInspect => Inspect}/Core.php (97%) create mode 100644 src/Glitch/Dumper/Inspect/Curl.php create mode 100644 src/Glitch/Dumper/Inspect/Dba.php create mode 100644 src/Glitch/Dumper/Inspect/Gd.php create mode 100644 src/Glitch/Dumper/Inspect/Process.php rename src/Glitch/Dumper/{ObjectInspect => Inspect}/Reflection.php (99%) rename src/Glitch/Dumper/{ObjectInspect => Inspect}/Spl.php (78%) create mode 100644 src/Glitch/Dumper/Inspect/Stream.php create mode 100644 src/Glitch/Dumper/Inspect/Xml.php diff --git a/src/Glitch/Context.php b/src/Glitch/Context.php index e0b3635..a14c4cb 100644 --- a/src/Glitch/Context.php +++ b/src/Glitch/Context.php @@ -18,7 +18,9 @@ class Context implements IContext protected $startTime; protected $runMode = 'development'; protected $pathAliases = []; + protected $objectInspectors = []; + protected $resourceInspectors = []; /** @@ -272,4 +274,22 @@ public function getObjectInspectors(): array { return $this->objectInspectors; } + + + /** + * Register callable inspector for a specific resource type + */ + public function registerResourceInspector(string $type, callable $inspector): IContext + { + $this->resourceInspectors[$type] = $inspector; + return $this; + } + + /** + * Get list of registered inspectors + */ + public function getResourceInspectors(): array + { + return $this->resourceInspectors; + } } diff --git a/src/Glitch/Dumper/ObjectInspect/Core.php b/src/Glitch/Dumper/Inspect/Core.php similarity index 97% rename from src/Glitch/Dumper/ObjectInspect/Core.php rename to src/Glitch/Dumper/Inspect/Core.php index 874315d..304f451 100644 --- a/src/Glitch/Dumper/ObjectInspect/Core.php +++ b/src/Glitch/Dumper/Inspect/Core.php @@ -4,7 +4,7 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\ObjectInspect; +namespace Glitch\Dumper\Inspect; use Glitch\Dumper\Entity; use Glitch\Dumper\Inspector; diff --git a/src/Glitch/Dumper/Inspect/Curl.php b/src/Glitch/Dumper/Inspect/Curl.php new file mode 100644 index 0000000..b58240c --- /dev/null +++ b/src/Glitch/Dumper/Inspect/Curl.php @@ -0,0 +1,23 @@ + $value) { + $entity->setMeta($key, $inspector->inspectValue($value)); + } + } +} diff --git a/src/Glitch/Dumper/Inspect/Dba.php b/src/Glitch/Dumper/Inspect/Dba.php new file mode 100644 index 0000000..b19df8b --- /dev/null +++ b/src/Glitch/Dumper/Inspect/Dba.php @@ -0,0 +1,22 @@ +setMeta('file', $inspector->inspectValue($list[(int)$resource])); + } +} diff --git a/src/Glitch/Dumper/Inspect/Gd.php b/src/Glitch/Dumper/Inspect/Gd.php new file mode 100644 index 0000000..30b6620 --- /dev/null +++ b/src/Glitch/Dumper/Inspect/Gd.php @@ -0,0 +1,33 @@ +setMeta('width', $inspector->inspectValue(imagesx($resource))) + ->setMeta('height', $inspector->inspectValue(imagesy($resource))); + } + + /** + * Inspect GD font resource + */ + public static function inspectGdFont($resource, Entity $entity, Inspector $inspector): void + { + $entity + ->setMeta('width', $inspector->inspectValue(imagesfontwidth($resource))) + ->setMeta('height', $inspector->inspectValue(imagesfontheight($resource))); + } +} diff --git a/src/Glitch/Dumper/Inspect/Process.php b/src/Glitch/Dumper/Inspect/Process.php new file mode 100644 index 0000000..fe0d371 --- /dev/null +++ b/src/Glitch/Dumper/Inspect/Process.php @@ -0,0 +1,23 @@ + $value) { + $entity->setMeta($key, $inspector->inspectValue($value)); + } + } +} diff --git a/src/Glitch/Dumper/ObjectInspect/Reflection.php b/src/Glitch/Dumper/Inspect/Reflection.php similarity index 99% rename from src/Glitch/Dumper/ObjectInspect/Reflection.php rename to src/Glitch/Dumper/Inspect/Reflection.php index 1e26f2a..070f76f 100644 --- a/src/Glitch/Dumper/ObjectInspect/Reflection.php +++ b/src/Glitch/Dumper/Inspect/Reflection.php @@ -4,7 +4,7 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\ObjectInspect; +namespace Glitch\Dumper\Inspect; use Glitch\Dumper\Entity; use Glitch\Dumper\Inspector; diff --git a/src/Glitch/Dumper/ObjectInspect/Spl.php b/src/Glitch/Dumper/Inspect/Spl.php similarity index 78% rename from src/Glitch/Dumper/ObjectInspect/Spl.php rename to src/Glitch/Dumper/Inspect/Spl.php index 744f1fa..6a8c6da 100644 --- a/src/Glitch/Dumper/ObjectInspect/Spl.php +++ b/src/Glitch/Dumper/Inspect/Spl.php @@ -4,7 +4,7 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\ObjectInspect; +namespace Glitch\Dumper\Inspect; class Spl { diff --git a/src/Glitch/Dumper/Inspect/Stream.php b/src/Glitch/Dumper/Inspect/Stream.php new file mode 100644 index 0000000..c6c5df9 --- /dev/null +++ b/src/Glitch/Dumper/Inspect/Stream.php @@ -0,0 +1,39 @@ + $value) { + $entity->setMeta($key, $inspector->inspectValue($value)); + } + + self::inspectStreamContext($resource, $entity, $inspector); + } + + /** + * Inspect stream context resource + */ + public static function inspectStreamContext($resource, Entity $entity, Inspector $inspector): void + { + if (!$params = @stream_context_get_params($resource)) { + return; + } + + foreach ($params as $key => $value) { + $entity->setMeta($key, $inspector->inspectValue($value)); + } + } +} diff --git a/src/Glitch/Dumper/Inspect/Xml.php b/src/Glitch/Dumper/Inspect/Xml.php new file mode 100644 index 0000000..3fefc94 --- /dev/null +++ b/src/Glitch/Dumper/Inspect/Xml.php @@ -0,0 +1,25 @@ +setMeta('current_byte_index', $inspector->inspectValue(xml_get_current_byte_index($resource))) + ->setMeta('current_column_number', $inspector->inspectValue(xml_get_current_column_number($resource))) + ->setMeta('current_line_number', $inspector->inspectValue(xml_get_current_line_number($resource))) + ->setMeta('error_code', $inspector->inspectValue(xml_get_error_code($resource))); + } +} diff --git a/src/Glitch/Dumper/Inspector.php b/src/Glitch/Dumper/Inspector.php index ea4ff9e..85d60f3 100644 --- a/src/Glitch/Dumper/Inspector.php +++ b/src/Glitch/Dumper/Inspector.php @@ -11,31 +11,167 @@ use Glitch\IInspectable; use Glitch\Stack\Trace; -use Glitch\Dumper\ObjectInspect as Obj; +use Glitch\Dumper\Inspect; class Inspector { const OBJECTS = [ // Core - 'Closure' => [Obj\Core::class, 'inspectClosure'], - 'Generator' => [Obj\Core::class, 'inspectGenerator'], - '__PHP_Incomplete_Class' => [Obj\Core::class, 'inspectIncompleteClass'], + 'Closure' => [Inspect\Core::class, 'inspectClosure'], + 'Generator' => [Inspect\Core::class, 'inspectGenerator'], + '__PHP_Incomplete_Class' => [Inspect\Core::class, 'inspectIncompleteClass'], // Reflection - 'ReflectionClass' => [Obj\Reflection::class, 'inspectReflectionClass'], - 'ReflectionClassConstant' => [Obj\Reflection::class, 'inspectReflectionClassConstant'], - 'ReflectionZendExtension' => [Obj\Reflection::class, 'inspectReflectionZendExtension'], - 'ReflectionExtension' => [Obj\Reflection::class, 'inspectReflectionExtension'], - 'ReflectionFunction' => [Obj\Reflection::class, 'inspectReflectionFunction'], - 'ReflectionFunctionAbstract' => [Obj\Reflection::class, 'inspectReflectionFunction'], - 'ReflectionMethod' => [Obj\Reflection::class, 'inspectReflectionMethod'], - 'ReflectionParameter' => [Obj\Reflection::class, 'inspectReflectionParameter'], - 'ReflectionProperty' => [Obj\Reflection::class, 'inspectReflectionProperty'], - 'ReflectionType' => [Obj\Reflection::class, 'inspectReflectionType'], - 'ReflectionGenerator' => [Obj\Reflection::class, 'inspectReflectionGenerator'], + 'ReflectionClass' => [Inspect\Reflection::class, 'inspectReflectionClass'], + 'ReflectionClassConstant' => [Inspect\Reflection::class, 'inspectReflectionClassConstant'], + 'ReflectionZendExtension' => [Inspect\Reflection::class, 'inspectReflectionZendExtension'], + 'ReflectionExtension' => [Inspect\Reflection::class, 'inspectReflectionExtension'], + 'ReflectionFunction' => [Inspect\Reflection::class, 'inspectReflectionFunction'], + 'ReflectionFunctionAbstract' => [Inspect\Reflection::class, 'inspectReflectionFunction'], + 'ReflectionMethod' => [Inspect\Reflection::class, 'inspectReflectionMethod'], + 'ReflectionParameter' => [Inspect\Reflection::class, 'inspectReflectionParameter'], + 'ReflectionProperty' => [Inspect\Reflection::class, 'inspectReflectionProperty'], + 'ReflectionType' => [Inspect\Reflection::class, 'inspectReflectionType'], + 'ReflectionGenerator' => [Inspect\Reflection::class, 'inspectReflectionGenerator'], + ]; + + const RESOURCES = [ + // Bzip + 'bzip2' => null, + + // Cubrid + 'cubrid connection' => null, + 'persistent cubrid connection' => null, + 'cubrid request' => null, + 'cubrid lob' => null, + 'cubrid lob2' => null, + + // Curl + 'curl' => [Inspect\Curl::class, 'inspectCurl'], + + // Dba + 'dba' => [Inspect\Dba::class, 'inspectDba'], + 'dba persistent' => [Inspect\Dba::class, 'inspectDba'], + + // Dbase + 'dbase' => null, + + // DBX + 'dbx_link_object' => null, + 'dbx_result_object' => null, + + // Firebird + 'fbsql link' => null, + 'fbsql plink' => null, + 'fbsql result' => null, + + // FDF + 'fdf' => null, + + // FTP + 'ftp' => null, + + // GD + 'gd' => [Inspect\Gd::class, 'inspectGd'], + 'gd font' => [Inspect\Gd::class, 'inspectGdFont'], + + // Imap + 'imap' => null, + + // Ingres + 'ingres' => null, + 'ingres persistent' => null, + + // Interbase + 'interbase link' => null, + 'interbase link persistent' => null, + 'interbase query' => null, + 'interbase result' => null, + + // Ldap + 'ldap link' => null, + 'ldap result' => null, + + // mSQL + 'msql link' => null, + 'msql link persistent' => null, + 'msql query' => null, + + // msSQL + 'mssql link' => null, + 'mssql link persistent' => null, + 'mssql result' => null, + + // Oci8 + 'oci8 collection' => null, + 'oci8 connection' => null, + 'oci8 lob' => null, + 'oci8 statement' => null, + + // Odbc + 'odbc link' => null, + 'odbc link persistent' => null, + 'odbc result' => null, + + // OpenSSL + 'OpenSSL key' => null, + 'OpenSSL X.509' => null, + + // PDF + 'pdf document' => null, + 'pdf image' => null, + 'pdf object' => null, + 'pdf outline' => null, + + // PgSQL + 'pgsql large object' => null, + 'pgsql link' => null, + 'pgsql link persistent' => null, + 'pgsql result' => null, + + // Process + 'process' => [Inspect\Process::class, 'inspectProcess'], + + // Pspell + 'pspell' => null, + 'pspell config' => null, + + // Shmop + 'shmop' => null, + + // Stream + 'stream' => [Inspect\Stream::class, 'inspectStream'], + + // Socket + 'socket' => null, + + // Sybase + 'sybase-db link' => null, + 'sybase-db link persistent' => null, + 'sybase-db result' => null, + 'sybase-ct link' => null, + 'sybase-ct link persistent' => null, + 'sybase-ct result' => null, + + // Sysv + 'sysvsem' => null, + 'sysvshm' => null, + + // Wddx + 'wddx' => null, + + // Xml + 'xml' => [Inspect\Xml::class, 'inspectXmlResource'], + + // Zlib + 'zlib' => null, + 'zlib.deflate' => null, + 'zlib.inflate' => null ]; protected $objectInspectors = []; + protected $resourceInspectors = []; + protected $objectRefs = []; protected $arrayRefs = []; protected $arrayIds = []; @@ -46,11 +182,25 @@ class Inspector */ public function __construct(IContext $context) { - $this->objectInspectors = static::OBJECTS; + foreach (static::OBJECTS as $class => $inspector) { + if ($inspector !== null) { + $this->objectInspectors[$class] = $inspector; + } + } + + foreach (static::RESOURCES as $type => $inspector) { + if ($inspector !== null) { + $this->resourceInspectors[$type] = $inspector; + } + } foreach ($context->getObjectInspectors() as $class => $inspector) { $this->objectInspectors[$class] = $inspector; } + + foreach ($context->getResourceInspectors() as $type => $inspector) { + $this->resourceInspectors[$type] = $inspector; + } } @@ -136,146 +286,22 @@ public function inspectString(string $string) */ public function inspectResource($resource): Entity { - $output = (new Entity('resource')) + $entity = (new Entity('resource')) ->setName((string)$resource) ->setClass($rType = get_resource_type($resource)); $typeName = str_replace(' ', '', ucwords($rType)); $method = 'inspect'.ucfirst($typeName).'Resource'; - if (method_exists($this, $method)) { - $this->{$method}($resource, $output); - } - - return $output; - } - - /** - * Inspect curl resource - */ - public function inspectCurlResource($resource, Entity $entity): void - { - foreach (curl_getinfo($resource) as $key => $value) { - $entity->setMeta($key, $this->inspectValue($value)); - } - } - - /** - * Inspect dba resource - */ - public function inspectDbaResource($resource, Entity $entity): void - { - $list = dba_list(); - $entity->setMeta('file', $this->inspectValue($list[(int)$resource])); - } - - /** - * Inspect dba persistent resource - */ - public function inspectDbaPersistentResource($resource, Entity $entity): void - { - $this->inspectDbaResource($resource, $entity); - } - - /** - * Inspect gd resource - */ - public function inspectGdResource($resource, Entity $entity): void - { - $entity - ->setMeta('width', $this->inspectValue(imagesx($resource))) - ->setMeta('height', $this->inspectValue(imagesy($resource))); - } - - /** - * Inspect pgsql resource - */ - public function inspectPgsqlLinkResource($resource, Entity $entity): void - { - } - - /** - * Inspect pgsql persistent resource - */ - public function inspectPgsqlPersistentLinkResource($resource, Entity $entity): void - { - } - - /** - * Inspect pgsql large object resource - */ - public function inspectPgsqlLargeObjectResource($resource, Entity $entity): void - { - } - - /** - * Inspect pgsql result resource - */ - public function inspectPgsqlResultResource($resource, Entity $entity): void - { - } - - /** - * Inspect process resource - */ - public function inspectProcessResource($resource, Entity $entity): void - { - foreach (proc_get_status($resource) as $key => $value) { - $entity->setMeta($key, $this->inspectValue($value)); + if (isset($this->resourceInspectors[$rType])) { + call_user_func($this->resourceInspectors[$rType], $resource, $entity, $this); } - } - - /** - * Inspect stream resource - */ - public function inspectStreamResource($resource, Entity $entity): void - { - foreach (stream_get_meta_data($resource) as $key => $value) { - $entity->setMeta($key, $this->inspectValue($value)); - } - - $this->inspectStreamContextResource($resource, $entity); - } - /** - * Inspect persistent stream resource - */ - public function inspectPersistentStreamResource($resource, Entity $entity): void - { - $this->inspectStreamResource($resource, $entity); - } - - /** - * Inspect stream context resource - */ - public function inspectStreamContextResource($resource, Entity $entity): void - { - if (!$params = @stream_context_get_params($resource)) { - return; - } - - foreach ($params as $key => $value) { - $entity->setMeta($key, $this->inspectValue($value)); - } - } - - /** - * Inspect xml resource - */ - public function inspectXmlResource($resource, Entity $entity): void - { - $entity - ->setMeta('current_byte_index', $this->inspectValue(xml_get_current_byte_index($resource))) - ->setMeta('current_column_number', $this->inspectValue(xml_get_current_column_number($resource))) - ->setMeta('current_line_number', $this->inspectValue(xml_get_current_line_number($resource))) - ->setMeta('error_code', $this->inspectValue(xml_get_error_code($resource))); + return $entity; } - - - /** * Convert array into Entity */ diff --git a/src/Glitch/IContext.php b/src/Glitch/IContext.php index 70df3f4..3667c36 100644 --- a/src/Glitch/IContext.php +++ b/src/Glitch/IContext.php @@ -41,7 +41,9 @@ public function getPathAliases(): array; public function normalizePath(string $path): string; - // Object inspectors + // Inspectors public function registerObjectInspector(string $class, callable $inspector): IContext; public function getObjectInspectors(): array; + public function registerResourceInspector(string $type, callable $inspector): IContext; + public function getResourceInspectors(): array; } From eec75621ac7d637e23a857e48c0b3d804fbccf2f Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Wed, 21 Aug 2019 15:39:49 +0100 Subject: [PATCH 13/29] Added front-end packages to composer Signed-off-by: Tom Wright --- composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composer.json b/composer.json index 2c6f9b1..72ffc77 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,8 @@ "symfony/polyfill-php72": "~1.7", "symfony/polyfill-mbstring": "~1.7", + "components/jquery": "~3.3", + "symfony/var-dumper": "~4.1.0" }, "autoload": { From 1407c480f53228dcb3f807697e26991a368eb2f9 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Wed, 21 Aug 2019 15:40:53 +0100 Subject: [PATCH 14/29] Added bootstrap Signed-off-by: Tom Wright --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 72ffc77..b7210ca 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "symfony/polyfill-mbstring": "~1.7", "components/jquery": "~3.3", + "components/bootstrap": "~4.3", "symfony/var-dumper": "~4.1.0" }, From 52acfc5e3954d6e219c62367e7b8817ae0817e0d Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Wed, 21 Aug 2019 15:42:47 +0100 Subject: [PATCH 15/29] Updated min PHP version Signed-off-by: Tom Wright --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index b7210ca..485cc5d 100644 --- a/composer.json +++ b/composer.json @@ -10,8 +10,7 @@ } ], "require": { - "php": "^7.1.3", - "symfony/polyfill-php72": "~1.7", + "php": "^7.2", "symfony/polyfill-mbstring": "~1.7", "components/jquery": "~3.3", From 5c91836e32a7755b25911cf1c16be19065ba19f5 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Thu, 22 Aug 2019 00:38:37 +0100 Subject: [PATCH 16/29] Added html dump renderer Signed-off-by: Tom Wright --- src/Glitch.php | 3 +- src/Glitch/Context.php | 159 +++++- src/Glitch/Dumper/Dump.php | 56 ++- src/Glitch/Dumper/IRenderer.php | 14 + src/Glitch/Dumper/Inspector.php | 17 +- src/Glitch/Dumper/Renderer/Html.php | 551 +++++++++++++++++++++ src/Glitch/Dumper/Renderer/assets/dump.css | 272 ++++++++++ src/Glitch/Dumper/Renderer/assets/dump.js | 3 + src/Glitch/IContext.php | 49 -- src/Glitch/Stat.php | 94 ++++ 10 files changed, 1141 insertions(+), 77 deletions(-) create mode 100644 src/Glitch/Dumper/IRenderer.php create mode 100644 src/Glitch/Dumper/Renderer/Html.php create mode 100644 src/Glitch/Dumper/Renderer/assets/dump.css create mode 100644 src/Glitch/Dumper/Renderer/assets/dump.js delete mode 100644 src/Glitch/IContext.php create mode 100644 src/Glitch/Stat.php diff --git a/src/Glitch.php b/src/Glitch.php index 0adb5a3..b2b5388 100644 --- a/src/Glitch.php +++ b/src/Glitch.php @@ -7,7 +7,6 @@ use Glitch\Factory; use Glitch\Context; -use Glitch\IContext; /** * This is just a facade. @@ -31,7 +30,7 @@ public static function __callStatic(string $method, array $args): \EGlitch /** * Shortcut to Context */ - public static function getContext(): IContext + public static function getContext(): Context { return Context::getDefault(); } diff --git a/src/Glitch/Context.php b/src/Glitch/Context.php index a14c4cb..3c1a00c 100644 --- a/src/Glitch/Context.php +++ b/src/Glitch/Context.php @@ -11,7 +11,9 @@ use Glitch\Dumper\Inspector; use Glitch\Dumper\Dump; -class Context implements IContext +use Composer\Autoload\ClassLoader; + +class Context { protected static $default; @@ -19,14 +21,18 @@ class Context implements IContext protected $runMode = 'development'; protected $pathAliases = []; + protected $statGatherers = []; + protected $objectInspectors = []; protected $resourceInspectors = []; + protected $dumpRenderer; + /** * Create / fetch default context */ - public static function getDefault(): IContext + public static function getDefault(): Context { if (!self::$default) { self::setDefault(new self()); @@ -38,7 +44,7 @@ public static function getDefault(): IContext /** * Set custom default context */ - public static function setDefault(IContext $default): void + public static function setDefault(Context $default): void { self::$default = $default; } @@ -51,6 +57,8 @@ public function __construct() { $this->startTime = microtime(true); $this->pathAliases['glitch'] = dirname(__DIR__); + + $this->dumpRenderer = new \Glitch\Dumper\Renderer\Html($this); } @@ -58,7 +66,7 @@ public function __construct() /** * Set active run mode */ - public function setRunMode(string $mode): IContext + public function setRunMode(string $mode): Context { switch ($mode) { case 'production': @@ -141,20 +149,106 @@ protected function tempHandler() public function dd2(array $values, int $rewind=null): void { $trace = Trace::create($rewind + 1); + $frame = $trace->getFirstFrame(); $inspector = new Inspector($this); - $dump = new Dump( - $trace, - microtime(true) - $this->getStartTime(), - memory_get_peak_usage() + $dump = new Dump($trace); + + + $dump->addStats( + // Time + (new Stat('time', 'Running time', microtime(true) - $this->getStartTime())) + ->applyClass(function ($value) { + switch (true) { + case $value > 0.1: + return 'danger'; + + case $value > 0.01: + return 'warning'; + + default: + return 'success'; + } + }) + ->setRenderer('text', function ($time) { + return self::formatMicrotime($time); + }), + + // Memory + (new Stat('memory', 'Memory usage', memory_get_usage())) + ->applyClass($memApp = function ($value) { + $mb = 1024 * 1024; + + switch (true) { + case $value > (10 * $mb): + return 'danger'; + + case $value > (5 * $mb): + return 'warning'; + + default: + return 'success'; + } + }) + ->setRenderer('text', function ($memory) { + return self::formatFilesize($memory); + }), + + // Peak memory + (new Stat('peakMemory', 'Peak memory usage', memory_get_peak_usage())) + ->applyClass($memApp) + ->setRenderer('text', function ($memory) { + return self::formatFilesize($memory); + }), + + // Location + (new Stat('location', 'Dump location', $frame)) + ->setRenderer('text', function ($frame) { + return $this->normalizePath($frame->getFile()).' : '.$frame->getLine(); + }) ); + + foreach ($this->statGatherers as $gatherer) { + $gatherer($dump, $this); + } + foreach ($values as $value) { $dump->addEntity($inspector->inspectValue($value)); } - dd($dump); + + + while (ob_get_level()) { + ob_end_clean(); + } + + + echo $this->dumpRenderer->render($dump, true); + exit; } + + /** + * TODO: move these to a shared location + */ + private static function formatFilesize($bytes) + { + $units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']; + + for ($i = 0; $bytes > 1024; $i++) { + $bytes /= 1024; + } + + return round($bytes, 2).' '.$units[$i]; + } + + private static function formatMicrotime($time) + { + return number_format($time * 1000, 2).' ms'; + } + + + /** * Quit a stubbed method */ @@ -184,7 +278,7 @@ public function logException(\Throwable $e): void /** * Override app start time */ - public function setStartTime(float $time): IContext + public function setStartTime(float $time): Context { $this->startTime = $time; return $this; @@ -205,7 +299,7 @@ public function getStartTime(): float /** * Register path replacement alias */ - public function registerPathAlias(string $name, string $path): IContext + public function registerPathAlias(string $name, string $path): Context { $this->pathAliases[$name] = $path; @@ -219,7 +313,7 @@ public function registerPathAlias(string $name, string $path): IContext /** * Register list of path replacement aliases */ - public function registerPathAliases(array $aliases): IContext + public function registerPathAliases(array $aliases): Context { foreach ($aliases as $name => $path) { $this->pathAliases[$name] = $path; @@ -258,10 +352,29 @@ public function normalizePath(string $path): string + /** + * Register stat gatherer + */ + public function registerStatGatherer(string $name, callable $gatherer): Context + { + $this->statGatherers[$name] = $gatherer; + return $this; + } + + /** + * Get stat gatherers + */ + public function getStatGatherers(): array + { + return $this->statGatherers; + } + + + /** * Register callable inspector for a specific class */ - public function registerObjectInspector(string $class, callable $inspector): IContext + public function registerObjectInspector(string $class, callable $inspector): Context { $this->objectInspectors[$class] = $inspector; return $this; @@ -279,7 +392,7 @@ public function getObjectInspectors(): array /** * Register callable inspector for a specific resource type */ - public function registerResourceInspector(string $type, callable $inspector): IContext + public function registerResourceInspector(string $type, callable $inspector): Context { $this->resourceInspectors[$type] = $inspector; return $this; @@ -292,4 +405,22 @@ public function getResourceInspectors(): array { return $this->resourceInspectors; } + + + + + /** + * Get composer vendor path + */ + public function getVendorPath(): string + { + static $output; + + if (!isset($output)) { + $ref = new \ReflectionClass(ClassLoader::class); + $output = dirname(dirname($ref->getFileName())); + } + + return $output; + } } diff --git a/src/Glitch/Dumper/Dump.php b/src/Glitch/Dumper/Dump.php index f2936f3..09b0d7f 100644 --- a/src/Glitch/Dumper/Dump.php +++ b/src/Glitch/Dumper/Dump.php @@ -7,23 +7,68 @@ namespace Glitch\Dumper; use \ArrayIterator; +use Glitch\Stat; use Glitch\Stack\Trace; class Dump implements \IteratorAggregate { + protected $stats = []; protected $entities = []; protected $trace; - protected $time; - protected $memory; /** * Construct with stack trace of invoking call */ - public function __construct(Trace $trace, float $time, int $memory) + public function __construct(Trace $trace) { $this->trace = $trace; - $this->time = $time; - $this->memory = $memory; + } + + + /** + * Set named statistic + */ + public function addStats(Stat ...$stats): Dump + { + foreach ($stats as $stat) { + $this->stats[$stat->getKey()] = $stat; + } + + return $this; + } + + /** + * Get named statistic + */ + public function getStat(string $key): ?Stat + { + return $this->stats[$name] ?? null; + } + + /** + * Remove named statistic + */ + public function removeStat(string $key): Dump + { + unset($this->stats[$key]); + return $this; + } + + /** + * Get all named statistics + */ + public function getStats(): array + { + return $this->stats; + } + + /** + * Clear all named statistics + */ + public function clearStats(): Dump + { + $this->stats = []; + return $this; } @@ -35,6 +80,7 @@ public function getTrace(): Trace return $this->trace; } + /** * Add an entity to the list */ diff --git a/src/Glitch/Dumper/IRenderer.php b/src/Glitch/Dumper/IRenderer.php new file mode 100644 index 0000000..bc22ef5 --- /dev/null +++ b/src/Glitch/Dumper/IRenderer.php @@ -0,0 +1,14 @@ + $inspector) { if ($inspector !== null) { @@ -308,10 +307,11 @@ public function inspectResource($resource): Entity public function inspectArray(array $array): ?Entity { $hash = $this->hashArray($array); - $isRef = isset($this->arrayRefs[$hash]); + $isRef = $hash !== null && isset($this->arrayRefs[$hash]); $entity = (new Entity($isRef ? 'arrayReference' : 'array')) ->setClass('array') + ->setLength(count($array)) ->setHash($hash); if ($isRef) { @@ -320,12 +320,13 @@ public function inspectArray(array $array): ?Entity ->setObjectId($this->arrayIds[$hash]); } - $this->arrayRefs[$hash] = $entity->getId(); - $this->arrayIds[$hash] = $id = count($this->arrayIds) + 1; + if ($hash !== null) { + $this->arrayRefs[$hash] = $entity->getId(); + $this->arrayIds[$hash] = $id = count($this->arrayIds) + 1; + $entity->setObjectId($id); + } $entity - ->setObjectId($id) - ->setLength(count($array)) ->setValues($this->inspectValues($array)); return $entity; @@ -473,6 +474,8 @@ protected function inspectClassMembers(object $object, \ReflectionClass $reflect break; } + $name = $prefix.$name; + if ($entity->hasProperty($name)) { continue; } diff --git a/src/Glitch/Dumper/Renderer/Html.php b/src/Glitch/Dumper/Renderer/Html.php new file mode 100644 index 0000000..b7ea3a2 --- /dev/null +++ b/src/Glitch/Dumper/Renderer/Html.php @@ -0,0 +1,551 @@ +context = $context; + + if (headers_sent()) { + $this->fullRender = false; + } + } + + + /** + * Convert Dump object to HTML string + */ + public function render(Dump $dump, bool $isFinal=false): string + { + $this->output = []; + $space = str_repeat(' ', self::SPACES); + + // Header + if (!$this->headerRendered) { + $this->renderHeader(); + } + + + // Stats + $this->output[] = '
'; + + foreach ($dump->getStats() as $key => $stat) { + $this->output[] = $space.''.$stat->render('html').''; + } + + $this->output[] = '
'; + + + // Entities + $this->output[] = '
'; + + foreach ($dump->getEntities() as $value) { + $this->output[] = ''; + + if ($value instanceof Entity) { + $this->renderEntity($value); + } else { + $this->renderScalar($value); + } + + $this->output[] = ''; + } + + $this->output[] = '
'; + + + // Footer + if ($isFinal && $this->fullRender) { + $this->output[] = ''; + $this->output[] = ''; + } + + $html = implode("\n", $this->output); + $this->output = []; + + + // Wrap in iframe + $id = uniqid('glitch-dump'); + + $output = []; + $output[] = ''; + $output[] = ''; + $output[] = ''; + + return implode("\n", $output); + } + + + /** + * Render scripts and styles + */ + protected function renderHeader(): void + { + if (!$sent = headers_sent()) { + $this->output[] = ''; + $this->output[] = ''; + $this->output[] = ''; + } + + $vendor = $this->context->getVendorPath(); + + $css = [ + //'bootstrap-reboot' => $vendor.'/components/bootstrap/css/bootstrap-reboot.min.css', + 'bootstrap' => $vendor.'/components/bootstrap/css/bootstrap.min.css', + 'glitch' => __DIR__.'/assets/dump.css' + ]; + + $js = [ + 'jQuery' => $vendor.'/components/jquery/jquery.slim.min.js', + 'bootstrap' => $vendor.'/components/bootstrap/js/bootstrap.bundle.min.js', + 'glitch' => __DIR__.'/assets/dump.js' + ]; + + + // Meta + $this->output[] = ''; + + + // Css + foreach ($css as $name => $path) { + $this->output[] = ''; + } + + // Js + foreach ($js as $name => $path) { + $this->output[] = ''; + } + + + if (!$sent) { + $output[] = ''; + $output[] = ''; + } + + $this->headerRendered = true; + } + + + /** + * Render a scalar value + */ + protected function renderScalar($value, ?string $class=null): void + { + switch (true) { + case $value === null: + $output = $this->renderNull($class); + break; + + case is_bool($value): + $output = $this->renderBool($value, $class); + break; + + case is_int($value): + $output = $this->renderInt($value, $class); + break; + + case is_float($value): + $output = $this->renderFloat($value, $class); + break; + + case is_string($value): + $output = $this->renderString($value, $class); + break; + + default: + $output = ''; + break; + } + + $this->output[] = $output; + } + + + + /** + * Render a null scalar + */ + protected function renderNull(?string $class=null): string + { + return 'null'; + } + + /** + * Render a boolean scalar + */ + protected function renderBool(bool $value, ?string $class=null): string + { + return ''.($value ? 'true' : 'false').''; + } + + /** + * Render a integer scalar + */ + protected function renderInt(int $value, ?string $class=null): string + { + return ''.$value.''; + } + + /** + * Render a float scalar + */ + protected function renderFloat(float $value, ?string $class=null): string + { + return ''.$this->formatFloat($value).''; + } + + protected function formatFloat(float $number): string + { + $output = (string)$number; + + if (false === strpos($output, '.')) { + $output .= '.0'; + } + + return $output; + } + + + /** + * Render standard string + */ + protected function renderString(string $string, ?string $class=null): string + { + $isMultiLine = false !== strpos($string, "\n"); + + if ($class !== null) { + return ''.$this->esc($string).''; + } elseif ($isMultiLine) { + $string = str_replace("\r", '', $string); + $parts = explode("\n", $string); + + $output = []; + $output[] = '
'; + + foreach ($parts as $part) { + $output[] = '
'.$this->esc($part).'
'; + } + + $output[] = '
'; + return implode('', $output); + } else { + return ''.$this->esc($string).''; + } + } + + + + /** + * Render an individual entity + */ + protected function renderEntity(Entity $entity): void + { + $id = $entity->getId(); + $name = $this->esc($entity->getName() ?? $entity->getType()); + $showInfo = true; + $isRef = false; + + switch ($entity->getType()) { + case 'arrayReference': + $name = 'array'; + $isRef = true; + break; + + case 'objectReference': + $name = ''.$name.''; + $isRef = true; + break; + + case 'array': + case 'resource': + $showInfo = false; + break; + } + + $this->output[] = '
'; + + // Name + $this->output[] = ''.$name.''; + + // Length + if (null !== ($length = $entity->getLength())) { + $this->output[] = ''.$length.''; + } + + // Info + if ($showInfo) { + $this->output[] = 'i'; + } + + // Meta + if ($showMeta = (bool)$entity->getAllMeta()) { + $this->output[] = 'm'; + } + + // Bracket + $this->output[] = '{'; + + // Object id + if (null !== ($objectId = $entity->getObjectId())) { + if ($isRef) { + $this->output[] = ''.$this->esc((string)$objectId).''; + } else { + $this->output[] = ''.$this->esc((string)$objectId).''; + } + } + + + + $this->output[] = '
'; + + + // Info + if ($showInfo) { + $this->renderInfoBlock($entity); + } + + // Meta + if ($showMeta) { + $this->renderMetaBlock($entity); + } + + // Text + if ($entity->getText() !== null) { + $this->renderTextBlock($entity); + } + + // Properties + if ((bool)$entity->getProperties()) { + $this->renderPropertiesBlock($entity); + } + + // Values + if ((bool)$entity->getValues()) { + $this->renderValuesBlock($entity); + } + + // Footer + $this->output[] = ''; + } + + /** + * Render entity info block + */ + protected function renderInfoBlock(Entity $entity): void + { + $id = $entity->getId(); + $this->output[] = '
'; + + $type = $entity->getType(); + $info = []; + + // Type + if ($type !== 'object') { + $info['type'] = $type; + } + + // Class + if ($type == 'object') { + $info['class'] = $entity->getClass(); + } + + // Location + if ($file = $entity->getFile()) { + $info['location'] = $this->context->normalizePath($file).' : '.$entity->getStartLine(); + } + + // Parents + if ($parents = $entity->getParentClasses()) { + $info['parentClasses'] = $parents; + } + + // Interfaces + if ($interfaces = $entity->getInterfaces()) { + $info['interfaces'] = $interfaces; + } + + // Traits + if ($traits = $entity->getTraits()) { + $info['traits'] = $traits; + } + + // Hash + if ($hash = $entity->getHash()) { + $info['hash'] = $hash; + } + + $this->renderList($info, 'info'); + + $this->output[] = '
'; + } + + /** + * Render entity meta block + */ + protected function renderMetaBlock(Entity $entity): void + { + $id = $entity->getId(); + $this->output[] = '
'; + $this->renderList($entity->getAllMeta(), 'meta'); + $this->output[] = '
'; + } + + /** + * Render entity text block + */ + protected function renderTextBlock(Entity $entity): void + { + $id = $entity->getId(); + $type = $entity->getType(); + + $this->output[] = '
'; + + if ($type === 'binary') { + $chunks = trim(chunk_split($entity->getText(), 2, "\n")); + $this->output[] = ''.str_replace("\n", '', $chunks).''; + } else { + $this->output[] = $this->esc($entity->getText()); + } + + $this->output[] = '
'; + } + + /** + * Render entity properties block + */ + protected function renderPropertiesBlock(Entity $entity): void + { + $id = $entity->getId(); + $this->output[] = '
'; + $this->renderList($entity->getProperties(), 'properties'); + $this->output[] = '
'; + } + + /** + * Render entity values block + */ + protected function renderValuesBlock(Entity $entity): void + { + $id = $entity->getId(); + $this->output[] = '
'; + $this->renderList($entity->getValues(), 'values'); + $this->output[] = '
'; + } + + + /** + * Render list + */ + protected function renderList(array $items, string $style, bool $includeKeys=true, string $class=null): void + { + $this->output[] = '
    '; + $pointer = '=>'; + $asIdentifier = $access = false; + + switch ($style) { + case 'info': + case 'meta': + $pointer = ':'; + $asIdentifier = true; + break; + + case 'properties': + $access = true; + break; + } + + foreach ($items as $key => $value) { + $this->output[] = '
  • '; + + if ($includeKeys) { + $mod = 'public'; + + if ($access) { + $first = substr($key, 0, 1); + + if ($first == '*') { + $key = substr($key, 1); + $mod = 'protected'; + } elseif ($first == '!') { + $key = substr($key, 1); + $mod = 'private'; + } + } + + $this->renderScalar($key, 'identifier key '.$mod); + $this->output[] = ''.$pointer.' '; + } + + if ($value instanceof Entity) { + $this->renderEntity($value); + } elseif (is_array($value)) { + $isAssoc = $this->arrayIsAssoc($value); + $this->output[] = '{'; + $this->renderList($value, $style, $isAssoc, $isAssoc ? 'map' : 'inline'); + $this->output[] = '}'; + } else { + $this->renderScalar($value, $asIdentifier ? 'identifier' : null); + } + + $this->output[] = '
  • '; + } + + $this->output[] = '
'; + } + + protected function arrayIsAssoc(array $arr): bool + { + if (array() === $arr) { + return false; + } + + return array_keys($arr) !== range(0, count($arr) - 1); + } + + + /** + * Escape a value for HTML + */ + public function esc(?string $value): string + { + if ($value === null) { + return ''; + } + + return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); + } +} diff --git a/src/Glitch/Dumper/Renderer/assets/dump.css b/src/Glitch/Dumper/Renderer/assets/dump.css new file mode 100644 index 0000000..a4a8249 --- /dev/null +++ b/src/Glitch/Dumper/Renderer/assets/dump.css @@ -0,0 +1,272 @@ +body { + padding: 2rem 1rem; +} + +header.stats { + margin-bottom: 2rem; +} + + + +.tooltip-inner { + background-color: #DCD9BB; + color: black; + max-width: 30rem; +} +.tooltip.bs-tooltip-right .arrow:before { + border-right-color: #DCD9BB !important; +} +.tooltip.bs-tooltip-left .arrow:before { + border-left-color: #DCD9BB !important; +} +.tooltip.bs-tooltip-bottom .arrow:before { + border-bottom-color: #DCD9BB !important; +} +.tooltip.bs-tooltip-top .arrow:before { + border-top-color: #DCD9BB !important; +} + + +samp.dump { + display: block; + font-size: 0.75rem; + margin-bottom: 1rem; +} + + +/* SCALARS */ +.null { + color: var(--pink); + font-weight: 600; +} +.bool { + color: var(--pink); + font-style: italic; + font-weight: 600; +} +.int { + color: var(--indigo); +} +.float { + color: var(--indigo); +} +.float:before { + content: 'f'; + color: var(--gray); + opacity: 0.5; + margin-right: 0.2em; +} + +/* STRING */ +.string { + color: var(--red); +} +.string.identifier { + color: var(--orange); +} +.string.multi-line { + display: inline-block; + padding-right: 1rem; +} +.string:before, .string:after { + content: '"'; + color: var(--gray); + opacity: 0.5; +} +.string.identifier:before, .string.identifier:after { + display: none; +} +.string > .line:after { + content: '⏎'; + color: var(--gray); + opacity: 0.5; + font-size: 1.3em; + line-height: 1; + margin-left: 0.2em; +} +.string > .line:last-child:after { + display: none; +} + + +.pointer { + color: var(--gray); + opacity: 0.5; +} + +.g { + color: var(--gray); +} + + + +/* ENTITY */ +.entity.title, +.entity.footer { + display: inline-flex; + align-items: center; + position: relative; +} +.entity.title > * { + margin-right: 0.5em; +} +.entity.title .name { + color: var(--green); + cursor: pointer; + text-decoration: none; +} +.entity.title .length { + cursor: pointer; + color: var(--teal); + margin-left: -0.5em; +} +.entity.title .length:before { + content: ':'; + color: var(--gray); + opacity: 0.5; +} +.entity.title .badge { + cursor: help; + font-size: 0.9em; +} +.popup { + position: absolute; +} +.oid { + margin-right: 0 !important; + color: var(--gray); +} +.oid:before { + content: '#'; + color: var(--gray); + opacity: 0.5; + margin-right: 0.2em; +} + +.entity:target > .oid { + background: var(--purple); + color: white; + border-radius: 0.3em; + padding: 0 0.3em; + margin: 0 -0.3em; +} +.entity:target > .oid:before { + color: white; +} + +a.info + div { + position: relative; + left: -0.2em; +} +div.info div + h6 { + margin-top: 1rem; +} + +div.inner { + padding: 0 0 0 2rem; + display: block; +} +div.inner > .card { + max-width: 40rem; + padding: 0.5rem 0.75rem; + font-size: 0.9em; +} + +ul.list { + list-style: none; + padding: 0; + margin: 0; +} + +ul.list > li > ul.list { + margin-left: 2rem; +} +ul.list.inline { + display: inline-block; + margin: 0 !important; +} + + +ul.list.inline { + display: inline-flex; + flex-wrap: wrap; +} +ul.list > li { + position: relative; +} +ul.list.inline > li:after { + content: ','; + color: var(--gray); + opacity: 0.5; + margin-right: 1em; +} +ul.list.inline > li:last-child:after { + display: none; +} + +ul.list > li > .identifier.key { + position: relative; +} +ul.list > li > .identifier.key:before { + content: 'ⓟ'; + color: var(--green); + opacity: 0; + position: absolute; + display: block; + margin-left: -1.5em; + margin-top: 0.3em; + font-size: 0.8em; +} + +ul.list.info > li > .string.identifier { + color: var(--cyan); +} +ul.list.info > li > .identifier.key { + color: var(--gray); + opacity: 0.75; +} +ul.list.info > li > .identifier.key:before { + content: 'ⓘ'; + color: var(--cyan); + opacity: 0.8; +} + + +ul.list.meta > li > .string.identifier { + color: var(--red); +} +ul.list.meta > li > .identifier.key { + color: var(--grey); + opacity: 0.75; +} +ul.list.meta > li > .identifier.key:before { + content: 'ⓜ'; + color: var(--red); + opacity: 0.8; +} + +ul.list.properties > li > .identifier.key { + color: var(--blue); + opacity: 0.75; +} + +ul.list.properties > li > .identifier.key:before { + content: 'ⓟ'; + color: var(--green); +} +ul.list.properties > li > .identifier.key.protected:before { + color: var(--yellow); + opacity: 1; +} +ul.list.properties > li > .identifier.key.private:before { + color: var(--red); + opacity: 1; +} + +.inner > div.text { + color: var(--red); +} +.inner > div.text.binary > i { + margin-right: 0.5em; + color: var(--indigo); +} diff --git a/src/Glitch/Dumper/Renderer/assets/dump.js b/src/Glitch/Dumper/Renderer/assets/dump.js new file mode 100644 index 0000000..7696f99 --- /dev/null +++ b/src/Glitch/Dumper/Renderer/assets/dump.js @@ -0,0 +1,3 @@ +$(function() { + $('[title]').tooltip(); +}); diff --git a/src/Glitch/IContext.php b/src/Glitch/IContext.php deleted file mode 100644 index 3667c36..0000000 --- a/src/Glitch/IContext.php +++ /dev/null @@ -1,49 +0,0 @@ -key = $key; + $this->name = $name; + $this->value = $value; + } + + + /** + * Get key + */ + public function getKey(): string + { + return $this->key; + } + + /** + * Get name + */ + public function getName(): string + { + return $this->name; + } + + + /** + * Set badge class + */ + public function setClass(string $class): Stat + { + $this->class = $class; + return $this; + } + + /** + * Get badge class + */ + public function getClass(): string + { + return $this->class; + } + + /** + * Apply class with value + */ + public function applyClass(callable $applicator): Stat + { + return $this->setClass($applicator($this->value)); + } + + + /** + * Add a named renderer + */ + public function setRenderer(string $type, callable $renderer): Stat + { + $this->renderers[$type] = $renderer; + return $this; + } + + /** + * Render to string using stack of named renderers + */ + public function render(string $type): string + { + if (isset($this->renderers[$type])) { + return $this->renderers[$type]($this->value); + } elseif ($type !== 'text' && isset($this->renderers['text'])) { + return $this->renderers['text']($this->value); + } else { + return (string)$this->value; + } + } +} From c46cb96ec289dece0640dbf76372fd5129d3ff61 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Thu, 22 Aug 2019 01:05:52 +0100 Subject: [PATCH 17/29] Updated dump string handling Signed-off-by: Tom Wright --- src/Glitch/Dumper/Renderer/Html.php | 43 +++++++++++++++------- src/Glitch/Dumper/Renderer/assets/dump.css | 14 ++++++- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/Glitch/Dumper/Renderer/Html.php b/src/Glitch/Dumper/Renderer/Html.php index b7ea3a2..3bd7f85 100644 --- a/src/Glitch/Dumper/Renderer/Html.php +++ b/src/Glitch/Dumper/Renderer/Html.php @@ -272,32 +272,35 @@ protected function renderString(string $string, ?string $class=null): string */ protected function renderEntity(Entity $entity): void { - $id = $entity->getId(); + $id = $linkId = $entity->getId(); $name = $this->esc($entity->getName() ?? $entity->getType()); $showInfo = true; $isRef = false; switch ($entity->getType()) { case 'arrayReference': - $name = 'array'; - $isRef = true; - break; + $name = 'array'; + // no break case 'objectReference': + $linkId = 'ref-'.$id; $name = ''.$name.''; $isRef = true; break; - case 'array': case 'resource': $showInfo = false; break; } - $this->output[] = '
'; + $this->output[] = '
'; // Name - $this->output[] = ''.$name.''; + if ($isRef) { + $this->output[] = ''.$name.''; + } else { + $this->output[] = ''.$name.''; + } // Length if (null !== ($length = $entity->getLength())) { @@ -306,12 +309,12 @@ protected function renderEntity(Entity $entity): void // Info if ($showInfo) { - $this->output[] = 'i'; + $this->output[] = 'i'; } // Meta if ($showMeta = (bool)$entity->getAllMeta()) { - $this->output[] = 'm'; + $this->output[] = 'm'; } // Bracket @@ -367,15 +370,29 @@ protected function renderEntity(Entity $entity): void */ protected function renderInfoBlock(Entity $entity): void { - $id = $entity->getId(); - $this->output[] = '
'; + $id = $linkId = $entity->getId(); + + switch ($entity->getType()) { + case 'arrayReference': + case 'objectReference': + $linkId = 'ref-'.$id; + break; + } + + $this->output[] = '
'; $type = $entity->getType(); $info = []; // Type - if ($type !== 'object') { - $info['type'] = $type; + switch ($type) { + case 'object': + case 'array': + break; + + default: + $info['type'] = $type; + break; } // Class diff --git a/src/Glitch/Dumper/Renderer/assets/dump.css b/src/Glitch/Dumper/Renderer/assets/dump.css index a4a8249..5ab2c7a 100644 --- a/src/Glitch/Dumper/Renderer/assets/dump.css +++ b/src/Glitch/Dumper/Renderer/assets/dump.css @@ -65,17 +65,25 @@ samp.dump { color: var(--orange); } .string.multi-line { - display: inline-block; padding-right: 1rem; + display: inline; } .string:before, .string:after { content: '"'; color: var(--gray); opacity: 0.5; } +.string.multi-line:before, .string.multi-line:after { + content: '"""'; + color: var(--gray); + opacity: 0.5; +} .string.identifier:before, .string.identifier:after { display: none; } +.string > .line { + padding-left: 1rem; +} .string > .line:after { content: '⏎'; color: var(--gray); @@ -142,6 +150,9 @@ samp.dump { opacity: 0.5; margin-right: 0.2em; } +a.oid:before { + content: '&'; +} .entity:target > .oid { background: var(--purple); @@ -206,6 +217,7 @@ ul.list.inline > li:last-child:after { ul.list > li > .identifier.key { position: relative; + white-space: pre; } ul.list > li > .identifier.key:before { content: 'ⓟ'; From f3932edc93ad2f5ac316016c4471c086533cf367 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Thu, 22 Aug 2019 01:27:45 +0100 Subject: [PATCH 18/29] Added support to default entities as closed Signed-off-by: Tom Wright --- src/Glitch/Context.php | 2 +- src/Glitch/Dumper/Entity.php | 19 +++++++++++++ src/Glitch/Dumper/Inspector.php | 31 +++++++++++++++++++++- src/Glitch/Dumper/Renderer/Html.php | 11 +++++--- src/Glitch/Dumper/Renderer/assets/dump.css | 7 +++++ 5 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/Glitch/Context.php b/src/Glitch/Context.php index 3c1a00c..3dcd10e 100644 --- a/src/Glitch/Context.php +++ b/src/Glitch/Context.php @@ -163,7 +163,7 @@ public function dd2(array $values, int $rewind=null): void case $value > 0.1: return 'danger'; - case $value > 0.01: + case $value > 0.025: return 'warning'; default: diff --git a/src/Glitch/Dumper/Entity.php b/src/Glitch/Dumper/Entity.php index 88a5fe0..1856a54 100644 --- a/src/Glitch/Dumper/Entity.php +++ b/src/Glitch/Dumper/Entity.php @@ -11,6 +11,7 @@ class Entity protected $type; protected $name; protected $id; + protected $open = true; protected $objectId; protected $hash; @@ -70,6 +71,24 @@ public function getName(): ?string } + /** + * Set default open state + */ + public function setOpen(bool $open): Entity + { + $this->open = $open; + return $this; + } + + /** + * Get default open state + */ + public function getOpen(): bool + { + return $this->open; + } + + /** * Set object id */ diff --git a/src/Glitch/Dumper/Inspector.php b/src/Glitch/Dumper/Inspector.php index 9ceb5f1..ba2127f 100644 --- a/src/Glitch/Dumper/Inspector.php +++ b/src/Glitch/Dumper/Inspector.php @@ -463,6 +463,7 @@ protected function inspectClassMembers(object $object, \ReflectionClass $reflect $property->setAccessible(true); $name = $property->getName(); $prefix = null; + $open = false; switch (true) { case $property->isProtected(): @@ -472,6 +473,10 @@ protected function inspectClassMembers(object $object, \ReflectionClass $reflect case $property->isPrivate(): $prefix = '!'; break; + + default: + $open = true; + break; } $name = $prefix.$name; @@ -481,7 +486,13 @@ protected function inspectClassMembers(object $object, \ReflectionClass $reflect } $value = $property->getValue($object); - $entity->setProperty($name, $this->inspectValue($value)); + $propValue = $this->inspectValue($value); + + if ($propValue instanceof Entity) { + $propValue->setOpen($open); + } + + $entity->setProperty($name, $propValue); } } @@ -521,6 +532,24 @@ public static function hashArray(array $array): ?string return null; } + $array = self::smashArray($array); + return md5(serialize($array)); } + + /** + * Normalize values for serialize + */ + public static function smashArray(array $array): array + { + foreach ($array as $key => $value) { + if (is_object($value)) { + $array[$key] = spl_object_id($value); + } elseif (is_array($value)) { + $array[$key] = self::smashArray($value); + } + } + + return $array; + } } diff --git a/src/Glitch/Dumper/Renderer/Html.php b/src/Glitch/Dumper/Renderer/Html.php index 3bd7f85..32737e2 100644 --- a/src/Glitch/Dumper/Renderer/Html.php +++ b/src/Glitch/Dumper/Renderer/Html.php @@ -297,7 +297,7 @@ protected function renderEntity(Entity $entity): void // Name if ($isRef) { - $this->output[] = ''.$name.''; + $this->output[] = ''.$name.''; } else { $this->output[] = ''.$name.''; } @@ -449,7 +449,8 @@ protected function renderTextBlock(Entity $entity): void $id = $entity->getId(); $type = $entity->getType(); - $this->output[] = '
'; + $open = $entity->getOpen(); + $this->output[] = '
'; if ($type === 'binary') { $chunks = trim(chunk_split($entity->getText(), 2, "\n")); @@ -467,7 +468,8 @@ protected function renderTextBlock(Entity $entity): void protected function renderPropertiesBlock(Entity $entity): void { $id = $entity->getId(); - $this->output[] = '
'; + $open = $entity->getOpen(); + $this->output[] = '
'; $this->renderList($entity->getProperties(), 'properties'); $this->output[] = '
'; } @@ -478,7 +480,8 @@ protected function renderPropertiesBlock(Entity $entity): void protected function renderValuesBlock(Entity $entity): void { $id = $entity->getId(); - $this->output[] = '
'; + $open = $entity->getOpen(); + $this->output[] = '
'; $this->renderList($entity->getValues(), 'values'); $this->output[] = '
'; } diff --git a/src/Glitch/Dumper/Renderer/assets/dump.css b/src/Glitch/Dumper/Renderer/assets/dump.css index 5ab2c7a..3f45b67 100644 --- a/src/Glitch/Dumper/Renderer/assets/dump.css +++ b/src/Glitch/Dumper/Renderer/assets/dump.css @@ -67,6 +67,7 @@ samp.dump { .string.multi-line { padding-right: 1rem; display: inline; + white-space: pre; } .string:before, .string:after { content: '"'; @@ -123,6 +124,12 @@ samp.dump { cursor: pointer; text-decoration: none; } +.entity.title .name.ref:before { + content: '&'; + color: var(--gray); + opacity: 0.5; + margin-right: 0.2em; +} .entity.title .length { cursor: pointer; color: var(--teal); From bb77913b19dfceb99b7e73c42e9d88deae3d9cbd Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Thu, 22 Aug 2019 01:40:37 +0100 Subject: [PATCH 19/29] Updated string length handling Signed-off-by: Tom Wright --- src/Glitch/Dumper/Renderer/Html.php | 4 ++-- src/Glitch/Dumper/Renderer/assets/dump.css | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/Glitch/Dumper/Renderer/Html.php b/src/Glitch/Dumper/Renderer/Html.php index 32737e2..f05259d 100644 --- a/src/Glitch/Dumper/Renderer/Html.php +++ b/src/Glitch/Dumper/Renderer/Html.php @@ -252,7 +252,7 @@ protected function renderString(string $string, ?string $class=null): string $parts = explode("\n", $string); $output = []; - $output[] = '
'; + $output[] = '
'.mb_strlen($string).''; foreach ($parts as $part) { $output[] = '
'.$this->esc($part).'
'; @@ -261,7 +261,7 @@ protected function renderString(string $string, ?string $class=null): string $output[] = '
'; return implode('', $output); } else { - return ''.$this->esc($string).''; + return ''.$this->esc($string).''.mb_strlen($string).''; } } diff --git a/src/Glitch/Dumper/Renderer/assets/dump.css b/src/Glitch/Dumper/Renderer/assets/dump.css index 3f45b67..4df008b 100644 --- a/src/Glitch/Dumper/Renderer/assets/dump.css +++ b/src/Glitch/Dumper/Renderer/assets/dump.css @@ -64,17 +64,25 @@ samp.dump { .string.identifier { color: var(--orange); } -.string.multi-line { +.string.m { padding-right: 1rem; display: inline; white-space: pre; } -.string:before, .string:after { +.string > .length { + color: var(--teal); + margin-left: 0.5em; + display: none; +} +.string:hover > .length { + display: inline; +} +.string.s > .line:before, .string.s > .line:after { content: '"'; color: var(--gray); opacity: 0.5; } -.string.multi-line:before, .string.multi-line:after { +.string.m:before, .string.m:after { content: '"""'; color: var(--gray); opacity: 0.5; @@ -82,10 +90,10 @@ samp.dump { .string.identifier:before, .string.identifier:after { display: none; } -.string > .line { +.string.m > .line { padding-left: 1rem; } -.string > .line:after { +.string.m > .line:after { content: '⏎'; color: var(--gray); opacity: 0.5; @@ -93,7 +101,7 @@ samp.dump { line-height: 1; margin-left: 0.2em; } -.string > .line:last-child:after { +.string.m > .line:last-child:after { display: none; } From 5c41e4bd519fd2ad771377357551a5ddc716e1b6 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Thu, 22 Aug 2019 12:16:24 +0100 Subject: [PATCH 20/29] Updated dump collapsing and swapped with Symfony dumper Signed-off-by: Tom Wright --- src/Glitch/Context.php | 168 ++++++++++----------- src/Glitch/Dumper/Entity.php | 2 +- src/Glitch/Dumper/Inspector.php | 25 ++- src/Glitch/Dumper/Renderer/Html.php | 114 +++++++------- src/Glitch/Dumper/Renderer/assets/dump.css | 80 ++++++++-- src/Glitch/Dumper/Renderer/assets/dump.js | 87 +++++++++++ src/helpers.php | 30 +++- 7 files changed, 354 insertions(+), 152 deletions(-) diff --git a/src/Glitch/Context.php b/src/Glitch/Context.php index 3dcd10e..bbc7bd3 100644 --- a/src/Glitch/Context.php +++ b/src/Glitch/Context.php @@ -58,6 +58,7 @@ public function __construct() $this->startTime = microtime(true); $this->pathAliases['glitch'] = dirname(__DIR__); + $this->registerStatGatherer('default', [$this, 'gatherDefaultStats']); $this->dumpRenderer = new \Glitch\Dumper\Renderer\Html($this); } @@ -117,98 +118,21 @@ public function isProduction(): bool - /** - * Send variables to dump, carry on execution - */ - public function dump(array $values, int $rewind=null): void - { - $this->tempHandler()->dump(array_shift($values), ...$values); - } - /** - * Send variables to dump, exit and render - */ - public function dumpDie(array $values, int $rewind=null): void - { - $this->tempHandler()->dumpDie(array_shift($values), ...$values); - } - - - protected function tempHandler() - { - static $output; - - if (!isset($output)) { - $output = new \Glitch\Dumper\Symfony(); - } - return $output; - } - public function dd2(array $values, int $rewind=null): void + /** + * Send variables to dump, carry on execution + */ + public function dump(array $values, int $rewind=null): void { $trace = Trace::create($rewind + 1); - $frame = $trace->getFirstFrame(); $inspector = new Inspector($this); $dump = new Dump($trace); - $dump->addStats( - // Time - (new Stat('time', 'Running time', microtime(true) - $this->getStartTime())) - ->applyClass(function ($value) { - switch (true) { - case $value > 0.1: - return 'danger'; - - case $value > 0.025: - return 'warning'; - - default: - return 'success'; - } - }) - ->setRenderer('text', function ($time) { - return self::formatMicrotime($time); - }), - - // Memory - (new Stat('memory', 'Memory usage', memory_get_usage())) - ->applyClass($memApp = function ($value) { - $mb = 1024 * 1024; - - switch (true) { - case $value > (10 * $mb): - return 'danger'; - - case $value > (5 * $mb): - return 'warning'; - - default: - return 'success'; - } - }) - ->setRenderer('text', function ($memory) { - return self::formatFilesize($memory); - }), - - // Peak memory - (new Stat('peakMemory', 'Peak memory usage', memory_get_peak_usage())) - ->applyClass($memApp) - ->setRenderer('text', function ($memory) { - return self::formatFilesize($memory); - }), - - // Location - (new Stat('location', 'Dump location', $frame)) - ->setRenderer('text', function ($frame) { - return $this->normalizePath($frame->getFile()).' : '.$frame->getLine(); - }) - ); - - foreach ($this->statGatherers as $gatherer) { $gatherer($dump, $this); } @@ -217,14 +141,25 @@ public function dd2(array $values, int $rewind=null): void $dump->addEntity($inspector->inspectValue($value)); } + echo $this->dumpRenderer->render($dump, true); + } - while (ob_get_level()) { - ob_end_clean(); - } + /** + * Send variables to dump, exit and render + */ + public function dumpDie(array $values, int $rewind=null): void + { + $this->dump($values, $rewind + 1); + exit(1); + } - echo $this->dumpRenderer->render($dump, true); - exit; + /** + * Temporary Symfony dump handler + */ + public function dd2(array $values, int $rewind=null): void + { + (new \Glitch\Dumper\Symfony())->dumpDie(array_shift($values), ...$values); } @@ -369,6 +304,67 @@ public function getStatGatherers(): array return $this->statGatherers; } + /** + * Default stat gatherer + */ + public function gatherDefaultStats(Dump $dump, Context $context): void + { + $frame = $dump->getTrace()->getFirstFrame(); + + $dump->addStats( + // Time + (new Stat('time', 'Running time', microtime(true) - $this->getStartTime())) + ->applyClass(function ($value) { + switch (true) { + case $value > 0.1: + return 'danger'; + + case $value > 0.025: + return 'warning'; + + default: + return 'success'; + } + }) + ->setRenderer('text', function ($time) { + return self::formatMicrotime($time); + }), + + // Memory + (new Stat('memory', 'Memory usage', memory_get_usage())) + ->applyClass($memApp = function ($value) { + $mb = 1024 * 1024; + + switch (true) { + case $value > (10 * $mb): + return 'danger'; + + case $value > (5 * $mb): + return 'warning'; + + default: + return 'success'; + } + }) + ->setRenderer('text', function ($memory) { + return self::formatFilesize($memory); + }), + + // Peak memory + (new Stat('peakMemory', 'Peak memory usage', memory_get_peak_usage())) + ->applyClass($memApp) + ->setRenderer('text', function ($memory) { + return self::formatFilesize($memory); + }), + + // Location + (new Stat('location', 'Dump location', $frame)) + ->setRenderer('text', function ($frame) { + return $this->normalizePath($frame->getFile()).' : '.$frame->getLine(); + }) + ); + } + /** diff --git a/src/Glitch/Dumper/Entity.php b/src/Glitch/Dumper/Entity.php index 1856a54..de28f26 100644 --- a/src/Glitch/Dumper/Entity.php +++ b/src/Glitch/Dumper/Entity.php @@ -83,7 +83,7 @@ public function setOpen(bool $open): Entity /** * Get default open state */ - public function getOpen(): bool + public function isOpen(): bool { return $this->open; } diff --git a/src/Glitch/Dumper/Inspector.php b/src/Glitch/Dumper/Inspector.php index ba2127f..902f0f6 100644 --- a/src/Glitch/Dumper/Inspector.php +++ b/src/Glitch/Dumper/Inspector.php @@ -203,6 +203,29 @@ public function __construct(Context $context) } + /** + * Inspect and report + */ + public function inspect($value, callable $entityCallback=null) + { + $output = $this->inspectValue($value); + + if ($output instanceof Entity) { + $entityCallback($output, $value, $this); + } + + return $output; + } + + /** + * Invoke wrapper + */ + public function __invoke($value, callable $entityCallback=null) + { + return $this->inspect($value, $entityCallback); + } + + /** * Inspect single value */ @@ -491,7 +514,7 @@ protected function inspectClassMembers(object $object, \ReflectionClass $reflect if ($propValue instanceof Entity) { $propValue->setOpen($open); } - + $entity->setProperty($name, $propValue); } } diff --git a/src/Glitch/Dumper/Renderer/Html.php b/src/Glitch/Dumper/Renderer/Html.php index f05259d..12fb3f3 100644 --- a/src/Glitch/Dumper/Renderer/Html.php +++ b/src/Glitch/Dumper/Renderer/Html.php @@ -15,9 +15,6 @@ class Html implements IRenderer { const SPACES = 2; - protected $headerRendered = false; - protected $fullRender = true; - protected $output = []; protected $context; @@ -28,10 +25,6 @@ class Html implements IRenderer public function __construct(Context $context) { $this->context = $context; - - if (headers_sent()) { - $this->fullRender = false; - } } @@ -44,9 +37,7 @@ public function render(Dump $dump, bool $isFinal=false): string $space = str_repeat(' ', self::SPACES); // Header - if (!$this->headerRendered) { - $this->renderHeader(); - } + $this->renderHeader(); // Stats @@ -78,10 +69,8 @@ public function render(Dump $dump, bool $isFinal=false): string // Footer - if ($isFinal && $this->fullRender) { - $this->output[] = ''; - $this->output[] = ''; - } + $this->output[] = ''; + $this->output[] = ''; $html = implode("\n", $this->output); $this->output = []; @@ -93,7 +82,8 @@ public function render(Dump $dump, bool $isFinal=false): string $output = []; $output[] = ''; $output[] = ''; $output[] = ''; diff --git a/src/Glitch/Dumper/Renderer/assets/dump.css b/src/Glitch/Dumper/Renderer/assets/dump.css index d2f3cce..dcbdd67 100644 --- a/src/Glitch/Dumper/Renderer/assets/dump.css +++ b/src/Glitch/Dumper/Renderer/assets/dump.css @@ -31,6 +31,7 @@ samp.dump { display: block; font-size: 0.75rem; margin-bottom: 1rem; + min-width: 700px; } From 9e8600ce12e40e0c2dfc820f87b2da387a19c73a Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Thu, 22 Aug 2019 17:52:35 +0100 Subject: [PATCH 26/29] Removed I prefix from interfaces Signed-off-by: Tom Wright --- src/Glitch/Context.php | 9 ++++----- src/Glitch/Dumper/Inspector.php | 4 ++-- src/Glitch/Dumper/{IRenderer.php => Renderer.php} | 2 +- src/Glitch/Dumper/Renderer/Html.php | 4 ++-- src/Glitch/Factory.php | 2 +- src/Glitch/{IInspectable.php => Inspectable.php} | 2 +- src/Glitch/Stack/Trace.php | 4 ++-- src/Glitch/TException.php | 2 +- src/Glitch/{ITransport.php => Transport.php} | 2 +- src/Glitch/Transport/Stdout.php | 4 ++-- 10 files changed, 17 insertions(+), 18 deletions(-) rename src/Glitch/Dumper/{IRenderer.php => Renderer.php} (92%) rename src/Glitch/{IInspectable.php => Inspectable.php} (92%) rename src/Glitch/{ITransport.php => Transport.php} (90%) diff --git a/src/Glitch/Context.php b/src/Glitch/Context.php index dfe1c28..e07961d 100644 --- a/src/Glitch/Context.php +++ b/src/Glitch/Context.php @@ -11,7 +11,6 @@ use Glitch\Dumper\Inspector; use Glitch\Dumper\Dump; -use Glitch\Dumper\IRenderer; use Glitch\Dumper\Renderer; use Glitch\Transport; @@ -424,7 +423,7 @@ public function getVendorPath(): string /** * Set dump renderer */ - public function setDumpRenderer(IRenderer $renderer): Context + public function setDumpRenderer(Renderer $renderer): Context { $this->dumpRenderer = $renderer; return $this; @@ -433,7 +432,7 @@ public function setDumpRenderer(IRenderer $renderer): Context /** * Get dump renderer */ - public function getDumpRenderer(): IRenderer + public function getDumpRenderer(): Renderer { if (!$this->dumpRenderer) { $this->dumpRenderer = new Renderer\Html($this); @@ -446,7 +445,7 @@ public function getDumpRenderer(): IRenderer /** * Set transport */ - public function setTransport(ITransport $transport): Context + public function setTransport(Transport $transport): Context { $this->transport = $transport; return $this; @@ -455,7 +454,7 @@ public function setTransport(ITransport $transport): Context /** * Get transport */ - public function getTransport(): ITransport + public function getTransport(): Transport { if (!$this->transport) { $this->transport = new \Glitch\Transport\Stdout($this); diff --git a/src/Glitch/Dumper/Inspector.php b/src/Glitch/Dumper/Inspector.php index f583fb9..1783301 100644 --- a/src/Glitch/Dumper/Inspector.php +++ b/src/Glitch/Dumper/Inspector.php @@ -7,7 +7,7 @@ namespace Glitch\Dumper; use Glitch\Context; -use Glitch\IInspectable; +use Glitch\Inspectable; use Glitch\Stack\Trace; use Glitch\Dumper\Inspect; @@ -460,7 +460,7 @@ protected function inspectObjectProperties(object $object, array $reflections, E return; // Inspectable - } elseif ($object instanceof IInspectable) { + } elseif ($object instanceof Inspectable) { $object->glitchInspect($entity, $this); return; diff --git a/src/Glitch/Dumper/IRenderer.php b/src/Glitch/Dumper/Renderer.php similarity index 92% rename from src/Glitch/Dumper/IRenderer.php rename to src/Glitch/Dumper/Renderer.php index bc22ef5..a1b0c7e 100644 --- a/src/Glitch/Dumper/IRenderer.php +++ b/src/Glitch/Dumper/Renderer.php @@ -8,7 +8,7 @@ use Glitch\Context; -interface IRenderer +interface Renderer { public function render(Dump $dump, bool $isFinal=false): string; } diff --git a/src/Glitch/Dumper/Renderer/Html.php b/src/Glitch/Dumper/Renderer/Html.php index 0b861a5..8a67549 100644 --- a/src/Glitch/Dumper/Renderer/Html.php +++ b/src/Glitch/Dumper/Renderer/Html.php @@ -7,11 +7,11 @@ namespace Glitch\Dumper\Renderer; use Glitch\Context; -use Glitch\Dumper\IRenderer; +use Glitch\Dumper\Renderer; use Glitch\Dumper\Dump; use Glitch\Dumper\Entity; -class Html implements IRenderer +class Html implements Renderer { const SPACES = 2; diff --git a/src/Glitch/Factory.php b/src/Glitch/Factory.php index 5b4391e..d077e59 100644 --- a/src/Glitch/Factory.php +++ b/src/Glitch/Factory.php @@ -193,7 +193,7 @@ protected function build(string $message, array $interfaces): \EGlitch $this->namespace = null; } - $interfaces[] = '\\Glitch\\IInspectable'; + $interfaces[] = '\\Glitch\\Inspectable'; $this->buildDefinitions($interfaces); diff --git a/src/Glitch/IInspectable.php b/src/Glitch/Inspectable.php similarity index 92% rename from src/Glitch/IInspectable.php rename to src/Glitch/Inspectable.php index e4ae420..ff760fd 100644 --- a/src/Glitch/IInspectable.php +++ b/src/Glitch/Inspectable.php @@ -9,7 +9,7 @@ use Glitch\Dumper\Entity; use Glitch\Dumper\Inspector; -interface IInspectable +interface Inspectable { public function glitchInspect(Entity $entity, Inspector $inspector): void; } diff --git a/src/Glitch/Stack/Trace.php b/src/Glitch/Stack/Trace.php index 4d9f9dc..f35314f 100644 --- a/src/Glitch/Stack/Trace.php +++ b/src/Glitch/Stack/Trace.php @@ -8,14 +8,14 @@ use Glitch\Context; -use Glitch\IInspectable; +use Glitch\Inspectable; use Glitch\Dumper\Inspector; use Glitch\Dumper\Entity; /** * Represents a normalized stack trace */ -class Trace implements \IteratorAggregate, \Countable, IInspectable +class Trace implements \IteratorAggregate, \Countable, Inspectable { protected $frames = []; diff --git a/src/Glitch/TException.php b/src/Glitch/TException.php index 5f1093a..40aa407 100644 --- a/src/Glitch/TException.php +++ b/src/Glitch/TException.php @@ -9,7 +9,7 @@ use Glitch\Stack\Frame; use Glitch\Stack\Trace; -use Glitch\IInspectable; +use Glitch\Inspectable; use Glitch\Dumper\Inspector; use Glitch\Dumper\Entity; diff --git a/src/Glitch/ITransport.php b/src/Glitch/Transport.php similarity index 90% rename from src/Glitch/ITransport.php rename to src/Glitch/Transport.php index a355a57..00cfaac 100644 --- a/src/Glitch/ITransport.php +++ b/src/Glitch/Transport.php @@ -6,7 +6,7 @@ declare(strict_types=1); namespace Glitch; -interface ITransport +interface Transport { public function sendDump(string $packet): void; } diff --git a/src/Glitch/Transport/Stdout.php b/src/Glitch/Transport/Stdout.php index aad463a..583c0a7 100644 --- a/src/Glitch/Transport/Stdout.php +++ b/src/Glitch/Transport/Stdout.php @@ -6,9 +6,9 @@ declare(strict_types=1); namespace Glitch\Transport; -use Glitch\ITransport; +use Glitch\Transport; -class Stdout implements ITransport +class Stdout implements Transport { /** * Send dump straight to output From 572d28a40d6c74e275055fed71f5ff09a0ac8aa7 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Thu, 22 Aug 2019 17:56:04 +0100 Subject: [PATCH 27/29] Removed Symfony dep Signed-off-by: Tom Wright --- composer.json | 4 +- src/Glitch/Context.php | 10 --- src/Glitch/Dumper/Symfony.php | 128 ---------------------------------- src/helpers.php | 9 --- 4 files changed, 1 insertion(+), 150 deletions(-) delete mode 100644 src/Glitch/Dumper/Symfony.php diff --git a/composer.json b/composer.json index 485cc5d..f47f39c 100644 --- a/composer.json +++ b/composer.json @@ -14,9 +14,7 @@ "symfony/polyfill-mbstring": "~1.7", "components/jquery": "~3.3", - "components/bootstrap": "~4.3", - - "symfony/var-dumper": "~4.1.0" + "components/bootstrap": "~4.3" }, "autoload": { "psr-4": { diff --git a/src/Glitch/Context.php b/src/Glitch/Context.php index e07961d..d6ef69d 100644 --- a/src/Glitch/Context.php +++ b/src/Glitch/Context.php @@ -153,16 +153,6 @@ public function dumpDie(array $values, int $rewind=null): void } - /** - * Temporary Symfony dump handler - */ - public function dd2(array $values, int $rewind=null): void - { - (new \Glitch\Dumper\Symfony())->dumpDie(array_shift($values), ...$values); - } - - - /** * Quit a stubbed method diff --git a/src/Glitch/Dumper/Symfony.php b/src/Glitch/Dumper/Symfony.php deleted file mode 100644 index 85e3038..0000000 --- a/src/Glitch/Dumper/Symfony.php +++ /dev/null @@ -1,128 +0,0 @@ -setStyles([ - 'default' => 'background-color:#fff; color:#bbb; line-height:1.2; font-weight:normal; font:12px Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:100000', - 'num' => 'color:#a814e3', - 'const' => 'color:#b50acc', - 'str' => 'color:#cc2123', - 'cchr' => 'color:#222', - 'note' => 'color:#0cb300', - 'ref' => 'color:#a0a0a0', - 'public' => 'color:#795da3', - 'protected' => 'color:#795da3', - 'private' => 'color:#795da3', - 'meta' => 'color:#0cb300', - 'key' => 'color:#df5000', - 'index' => 'color:#a71d5d', - ]); - - $dumper->setDisplayOptions([ - 'maxDepth' => 3 - ]); - } - - - $cloner = new Cloner\VarCloner(); - $dumper->dump($cloner->cloneVar($var)); - } - - /** - * Dump a list of vars - */ - public function dump(...$vars): void - { - $trace = Trace::create(); - - foreach ($trace as $frame) { - if (substr($frame->getFunctionName(), 0, 4) != 'dump' - && false === strpos($frame->getCallingFile() ?? '', 'var-dumper')) { - break; - } - } - - - $attrs = [ - 'time' => self::formatMicrotime(microtime(true) - \Glitch::getContext()->getStartTime()), - 'memory' => self::formatFilesize(memory_get_usage()), - 'location' => \Glitch::normalizePath($frame->getCallingFile()).' : '.$frame->getCallingLine() - ]; - - if ('cli' === PHP_SAPI) { - echo implode(' | ', $attrs)."\n\n"; - } else { - echo '
'.implode(' | ', $attrs).'
'; - } - - foreach ($vars as $var) { - $this->dumpOne($var); - } - } - - /** - * Dump vars and die - */ - public function dumpDie(...$vars): void - { - while (ob_get_level()) { - ob_end_clean(); - } - - http_response_code(500); - $this->dump(...$vars); - die(1); - } - - - - /** - * TODO: move these to a shared location - */ - private static function formatFilesize($bytes) - { - $units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']; - - for ($i = 0; $bytes > 1024; $i++) { - $bytes /= 1024; - } - - return round($bytes, 2).' '.$units[$i]; - } - - private static function formatMicrotime($time) - { - return number_format($time * 1000, 2).' ms'; - } -} diff --git a/src/helpers.php b/src/helpers.php index d2ff34d..2f44bba 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -65,15 +65,6 @@ function dump($var, ...$vars): void } - /** - * Temporary dump handler - */ - function dd2($var, ...$vars): void - { - Context::getDefault()->dd2(func_get_args(), 1); - } - - /** * Direct facade for generating IError based exceptions */ From 2fb16aa8e2a981048c7dab6528cc221282555b76 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Thu, 22 Aug 2019 18:50:50 +0100 Subject: [PATCH 28/29] Refactored to DecodeLabs namespace Signed-off-by: Tom Wright --- src/{ => DecodeLabs}/Glitch/Context.php | 16 ++++++++-------- src/{ => DecodeLabs}/Glitch/Dumper/Dump.php | 7 ++++--- src/{ => DecodeLabs}/Glitch/Dumper/Entity.php | 4 ++-- .../Glitch/Dumper/Inspect/Core.php | 6 +++--- .../Glitch/Dumper/Inspect/Curl.php | 6 +++--- .../Glitch/Dumper/Inspect/Dba.php | 6 +++--- .../Glitch/Dumper/Inspect/Gd.php | 6 +++--- .../Glitch/Dumper/Inspect/Process.php | 6 +++--- .../Glitch/Dumper/Inspect/Reflection.php | 6 +++--- .../Glitch/Dumper/Inspect/Spl.php | 2 +- .../Glitch/Dumper/Inspect/Stream.php | 6 +++--- .../Glitch/Dumper/Inspect/Xml.php | 6 +++--- src/{ => DecodeLabs}/Glitch/Dumper/Inspector.php | 10 +++++----- src/{ => DecodeLabs}/Glitch/Dumper/Renderer.php | 4 ++-- .../Glitch/Dumper/Renderer/Html.php | 10 +++++----- .../Glitch/Dumper/Renderer/assets/dump.css | 0 .../Glitch/Dumper/Renderer/assets/dump.js | 0 src/{ => DecodeLabs}/Glitch/Factory.php | 6 +++--- src/{ => DecodeLabs}/Glitch/Inspectable.php | 6 +++--- src/{ => DecodeLabs}/Glitch/Stack/Frame.php | 4 ++-- src/{ => DecodeLabs}/Glitch/Stack/Trace.php | 12 ++++++------ src/{ => DecodeLabs}/Glitch/Stat.php | 2 +- src/{ => DecodeLabs}/Glitch/TException.php | 12 ++++++------ src/{ => DecodeLabs}/Glitch/Transport.php | 2 +- src/{ => DecodeLabs}/Glitch/Transport/Stdout.php | 4 ++-- src/EGlitch.php | 6 +++--- src/Glitch.php | 4 ++-- src/helpers.php | 6 +++--- 28 files changed, 83 insertions(+), 82 deletions(-) rename src/{ => DecodeLabs}/Glitch/Context.php (96%) rename src/{ => DecodeLabs}/Glitch/Dumper/Dump.php (94%) rename src/{ => DecodeLabs}/Glitch/Dumper/Entity.php (99%) rename src/{ => DecodeLabs}/Glitch/Dumper/Inspect/Core.php (93%) rename src/{ => DecodeLabs}/Glitch/Dumper/Inspect/Curl.php (78%) rename src/{ => DecodeLabs}/Glitch/Dumper/Inspect/Dba.php (76%) rename src/{ => DecodeLabs}/Glitch/Dumper/Inspect/Gd.php (86%) rename src/{ => DecodeLabs}/Glitch/Dumper/Inspect/Process.php (78%) rename src/{ => DecodeLabs}/Glitch/Dumper/Inspect/Reflection.php (98%) rename src/{ => DecodeLabs}/Glitch/Dumper/Inspect/Spl.php (76%) rename src/{ => DecodeLabs}/Glitch/Dumper/Inspect/Stream.php (88%) rename src/{ => DecodeLabs}/Glitch/Dumper/Inspect/Xml.php (86%) rename src/{ => DecodeLabs}/Glitch/Dumper/Inspector.php (98%) rename src/{ => DecodeLabs}/Glitch/Dumper/Renderer.php (76%) rename src/{ => DecodeLabs}/Glitch/Dumper/Renderer/Html.php (98%) rename src/{ => DecodeLabs}/Glitch/Dumper/Renderer/assets/dump.css (100%) rename src/{ => DecodeLabs}/Glitch/Dumper/Renderer/assets/dump.js (100%) rename src/{ => DecodeLabs}/Glitch/Factory.php (98%) rename src/{ => DecodeLabs}/Glitch/Inspectable.php (69%) rename src/{ => DecodeLabs}/Glitch/Stack/Frame.php (99%) rename src/{ => DecodeLabs}/Glitch/Stack/Trace.php (95%) rename src/{ => DecodeLabs}/Glitch/Stat.php (98%) rename src/{ => DecodeLabs}/Glitch/TException.php (94%) rename src/{ => DecodeLabs}/Glitch/Transport.php (87%) rename src/{ => DecodeLabs}/Glitch/Transport/Stdout.php (80%) diff --git a/src/Glitch/Context.php b/src/DecodeLabs/Glitch/Context.php similarity index 96% rename from src/Glitch/Context.php rename to src/DecodeLabs/Glitch/Context.php index d6ef69d..0c67476 100644 --- a/src/Glitch/Context.php +++ b/src/DecodeLabs/Glitch/Context.php @@ -4,15 +4,15 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch; +namespace DecodeLabs\Glitch; -use Glitch\Stack\Frame; -use Glitch\Stack\Trace; -use Glitch\Dumper\Inspector; -use Glitch\Dumper\Dump; +use DecodeLabs\Glitch\Stack\Frame; +use DecodeLabs\Glitch\Stack\Trace; +use DecodeLabs\Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Dump; -use Glitch\Dumper\Renderer; -use Glitch\Transport; +use DecodeLabs\Glitch\Dumper\Renderer; +use DecodeLabs\Glitch\Transport; use Composer\Autoload\ClassLoader; @@ -447,7 +447,7 @@ public function setTransport(Transport $transport): Context public function getTransport(): Transport { if (!$this->transport) { - $this->transport = new \Glitch\Transport\Stdout($this); + $this->transport = new Transport\Stdout($this); } return $this->transport; diff --git a/src/Glitch/Dumper/Dump.php b/src/DecodeLabs/Glitch/Dumper/Dump.php similarity index 94% rename from src/Glitch/Dumper/Dump.php rename to src/DecodeLabs/Glitch/Dumper/Dump.php index 09b0d7f..b7e550a 100644 --- a/src/Glitch/Dumper/Dump.php +++ b/src/DecodeLabs/Glitch/Dumper/Dump.php @@ -4,11 +4,12 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper; +namespace DecodeLabs\Glitch\Dumper; use \ArrayIterator; -use Glitch\Stat; -use Glitch\Stack\Trace; + +use DecodeLabs\Glitch\Stat; +use DecodeLabs\Glitch\Stack\Trace; class Dump implements \IteratorAggregate { diff --git a/src/Glitch/Dumper/Entity.php b/src/DecodeLabs/Glitch/Dumper/Entity.php similarity index 99% rename from src/Glitch/Dumper/Entity.php rename to src/DecodeLabs/Glitch/Dumper/Entity.php index 5cacb53..ead3364 100644 --- a/src/Glitch/Dumper/Entity.php +++ b/src/DecodeLabs/Glitch/Dumper/Entity.php @@ -4,9 +4,9 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper; +namespace DecodeLabs\Glitch\Dumper; -use Glitch\Stack\Trace; +use DecodeLabs\Glitch\Stack\Trace; class Entity { diff --git a/src/Glitch/Dumper/Inspect/Core.php b/src/DecodeLabs/Glitch/Dumper/Inspect/Core.php similarity index 93% rename from src/Glitch/Dumper/Inspect/Core.php rename to src/DecodeLabs/Glitch/Dumper/Inspect/Core.php index 304f451..f643b2f 100644 --- a/src/Glitch/Dumper/Inspect/Core.php +++ b/src/DecodeLabs/Glitch/Dumper/Inspect/Core.php @@ -4,10 +4,10 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\Inspect; +namespace DecodeLabs\Glitch\Dumper\Inspect; -use Glitch\Dumper\Entity; -use Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Dumper\Inspector; class Core { diff --git a/src/Glitch/Dumper/Inspect/Curl.php b/src/DecodeLabs/Glitch/Dumper/Inspect/Curl.php similarity index 78% rename from src/Glitch/Dumper/Inspect/Curl.php rename to src/DecodeLabs/Glitch/Dumper/Inspect/Curl.php index b58240c..6608e4b 100644 --- a/src/Glitch/Dumper/Inspect/Curl.php +++ b/src/DecodeLabs/Glitch/Dumper/Inspect/Curl.php @@ -4,10 +4,10 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\Inspect; +namespace DecodeLabs\Glitch\Dumper\Inspect; -use Glitch\Dumper\Entity; -use Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Dumper\Inspector; class Curl { diff --git a/src/Glitch/Dumper/Inspect/Dba.php b/src/DecodeLabs/Glitch/Dumper/Inspect/Dba.php similarity index 76% rename from src/Glitch/Dumper/Inspect/Dba.php rename to src/DecodeLabs/Glitch/Dumper/Inspect/Dba.php index b19df8b..11b3802 100644 --- a/src/Glitch/Dumper/Inspect/Dba.php +++ b/src/DecodeLabs/Glitch/Dumper/Inspect/Dba.php @@ -4,10 +4,10 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\Inspect; +namespace DecodeLabs\Glitch\Dumper\Inspect; -use Glitch\Dumper\Entity; -use Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Dumper\Inspector; class Dba { diff --git a/src/Glitch/Dumper/Inspect/Gd.php b/src/DecodeLabs/Glitch/Dumper/Inspect/Gd.php similarity index 86% rename from src/Glitch/Dumper/Inspect/Gd.php rename to src/DecodeLabs/Glitch/Dumper/Inspect/Gd.php index 30b6620..7f168fe 100644 --- a/src/Glitch/Dumper/Inspect/Gd.php +++ b/src/DecodeLabs/Glitch/Dumper/Inspect/Gd.php @@ -4,10 +4,10 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\Inspect; +namespace DecodeLabs\Glitch\Dumper\Inspect; -use Glitch\Dumper\Entity; -use Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Dumper\Inspector; class Gd { diff --git a/src/Glitch/Dumper/Inspect/Process.php b/src/DecodeLabs/Glitch/Dumper/Inspect/Process.php similarity index 78% rename from src/Glitch/Dumper/Inspect/Process.php rename to src/DecodeLabs/Glitch/Dumper/Inspect/Process.php index fe0d371..044fe9c 100644 --- a/src/Glitch/Dumper/Inspect/Process.php +++ b/src/DecodeLabs/Glitch/Dumper/Inspect/Process.php @@ -4,10 +4,10 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\Inspect; +namespace DecodeLabs\Glitch\Dumper\Inspect; -use Glitch\Dumper\Entity; -use Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Dumper\Inspector; class Process { diff --git a/src/Glitch/Dumper/Inspect/Reflection.php b/src/DecodeLabs/Glitch/Dumper/Inspect/Reflection.php similarity index 98% rename from src/Glitch/Dumper/Inspect/Reflection.php rename to src/DecodeLabs/Glitch/Dumper/Inspect/Reflection.php index 070f76f..ea8b840 100644 --- a/src/Glitch/Dumper/Inspect/Reflection.php +++ b/src/DecodeLabs/Glitch/Dumper/Inspect/Reflection.php @@ -4,10 +4,10 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\Inspect; +namespace DecodeLabs\Glitch\Dumper\Inspect; -use Glitch\Dumper\Entity; -use Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Dumper\Inspector; class Reflection { diff --git a/src/Glitch/Dumper/Inspect/Spl.php b/src/DecodeLabs/Glitch/Dumper/Inspect/Spl.php similarity index 76% rename from src/Glitch/Dumper/Inspect/Spl.php rename to src/DecodeLabs/Glitch/Dumper/Inspect/Spl.php index 6a8c6da..1897fbd 100644 --- a/src/Glitch/Dumper/Inspect/Spl.php +++ b/src/DecodeLabs/Glitch/Dumper/Inspect/Spl.php @@ -4,7 +4,7 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\Inspect; +namespace DecodeLabs\Glitch\Dumper\Inspect; class Spl { diff --git a/src/Glitch/Dumper/Inspect/Stream.php b/src/DecodeLabs/Glitch/Dumper/Inspect/Stream.php similarity index 88% rename from src/Glitch/Dumper/Inspect/Stream.php rename to src/DecodeLabs/Glitch/Dumper/Inspect/Stream.php index c6c5df9..f0fd3a2 100644 --- a/src/Glitch/Dumper/Inspect/Stream.php +++ b/src/DecodeLabs/Glitch/Dumper/Inspect/Stream.php @@ -4,10 +4,10 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\Inspect; +namespace DecodeLabs\Glitch\Dumper\Inspect; -use Glitch\Dumper\Entity; -use Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Dumper\Inspector; class Stream { diff --git a/src/Glitch/Dumper/Inspect/Xml.php b/src/DecodeLabs/Glitch/Dumper/Inspect/Xml.php similarity index 86% rename from src/Glitch/Dumper/Inspect/Xml.php rename to src/DecodeLabs/Glitch/Dumper/Inspect/Xml.php index 3fefc94..657eeaa 100644 --- a/src/Glitch/Dumper/Inspect/Xml.php +++ b/src/DecodeLabs/Glitch/Dumper/Inspect/Xml.php @@ -4,10 +4,10 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\Inspect; +namespace DecodeLabs\Glitch\Dumper\Inspect; -use Glitch\Dumper\Entity; -use Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Dumper\Inspector; class Xml { diff --git a/src/Glitch/Dumper/Inspector.php b/src/DecodeLabs/Glitch/Dumper/Inspector.php similarity index 98% rename from src/Glitch/Dumper/Inspector.php rename to src/DecodeLabs/Glitch/Dumper/Inspector.php index 1783301..535cd68 100644 --- a/src/Glitch/Dumper/Inspector.php +++ b/src/DecodeLabs/Glitch/Dumper/Inspector.php @@ -4,13 +4,13 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper; +namespace DecodeLabs\Glitch\Dumper; -use Glitch\Context; -use Glitch\Inspectable; -use Glitch\Stack\Trace; +use DecodeLabs\Glitch\Context; +use DecodeLabs\Glitch\Inspectable; +use DecodeLabs\Glitch\Stack\Trace; -use Glitch\Dumper\Inspect; +use DecodeLabs\Glitch\Dumper\Inspect; class Inspector { diff --git a/src/Glitch/Dumper/Renderer.php b/src/DecodeLabs/Glitch/Dumper/Renderer.php similarity index 76% rename from src/Glitch/Dumper/Renderer.php rename to src/DecodeLabs/Glitch/Dumper/Renderer.php index a1b0c7e..8cac145 100644 --- a/src/Glitch/Dumper/Renderer.php +++ b/src/DecodeLabs/Glitch/Dumper/Renderer.php @@ -4,9 +4,9 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper; +namespace DecodeLabs\Glitch\Dumper; -use Glitch\Context; +use DecodeLabs\Glitch\Context; interface Renderer { diff --git a/src/Glitch/Dumper/Renderer/Html.php b/src/DecodeLabs/Glitch/Dumper/Renderer/Html.php similarity index 98% rename from src/Glitch/Dumper/Renderer/Html.php rename to src/DecodeLabs/Glitch/Dumper/Renderer/Html.php index 8a67549..da3b0d1 100644 --- a/src/Glitch/Dumper/Renderer/Html.php +++ b/src/DecodeLabs/Glitch/Dumper/Renderer/Html.php @@ -4,12 +4,12 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Dumper\Renderer; +namespace DecodeLabs\Glitch\Dumper\Renderer; -use Glitch\Context; -use Glitch\Dumper\Renderer; -use Glitch\Dumper\Dump; -use Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Context; +use DecodeLabs\Glitch\Dumper\Renderer; +use DecodeLabs\Glitch\Dumper\Dump; +use DecodeLabs\Glitch\Dumper\Entity; class Html implements Renderer { diff --git a/src/Glitch/Dumper/Renderer/assets/dump.css b/src/DecodeLabs/Glitch/Dumper/Renderer/assets/dump.css similarity index 100% rename from src/Glitch/Dumper/Renderer/assets/dump.css rename to src/DecodeLabs/Glitch/Dumper/Renderer/assets/dump.css diff --git a/src/Glitch/Dumper/Renderer/assets/dump.js b/src/DecodeLabs/Glitch/Dumper/Renderer/assets/dump.js similarity index 100% rename from src/Glitch/Dumper/Renderer/assets/dump.js rename to src/DecodeLabs/Glitch/Dumper/Renderer/assets/dump.js diff --git a/src/Glitch/Factory.php b/src/DecodeLabs/Glitch/Factory.php similarity index 98% rename from src/Glitch/Factory.php rename to src/DecodeLabs/Glitch/Factory.php index d077e59..f2316d9 100644 --- a/src/Glitch/Factory.php +++ b/src/DecodeLabs/Glitch/Factory.php @@ -4,7 +4,7 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch; +namespace DecodeLabs\Glitch; /** * Automatically generate Exceptions on the fly based on scope and @@ -193,7 +193,7 @@ protected function build(string $message, array $interfaces): \EGlitch $this->namespace = null; } - $interfaces[] = '\\Glitch\\Inspectable'; + $interfaces[] = '\\DecodeLabs\\Glitch\\Inspectable'; $this->buildDefinitions($interfaces); @@ -231,7 +231,7 @@ protected function buildDefinitions(array $interfaces): void } $directType = null; - $this->traits[] = 'Glitch\\TException'; + $this->traits[] = 'DecodeLabs\\Glitch\\TException'; // Create initial interface list diff --git a/src/Glitch/Inspectable.php b/src/DecodeLabs/Glitch/Inspectable.php similarity index 69% rename from src/Glitch/Inspectable.php rename to src/DecodeLabs/Glitch/Inspectable.php index ff760fd..fb6f184 100644 --- a/src/Glitch/Inspectable.php +++ b/src/DecodeLabs/Glitch/Inspectable.php @@ -4,10 +4,10 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch; +namespace DecodeLabs\Glitch; -use Glitch\Dumper\Entity; -use Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Dumper\Inspector; interface Inspectable { diff --git a/src/Glitch/Stack/Frame.php b/src/DecodeLabs/Glitch/Stack/Frame.php similarity index 99% rename from src/Glitch/Stack/Frame.php rename to src/DecodeLabs/Glitch/Stack/Frame.php index b749071..913598f 100644 --- a/src/Glitch/Stack/Frame.php +++ b/src/DecodeLabs/Glitch/Stack/Frame.php @@ -4,9 +4,9 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Stack; +namespace DecodeLabs\Glitch\Stack; -use Glitch\Context; +use DecodeLabs\Glitch\Context; /** * Represents a single entry in a stack trace diff --git a/src/Glitch/Stack/Trace.php b/src/DecodeLabs/Glitch/Stack/Trace.php similarity index 95% rename from src/Glitch/Stack/Trace.php rename to src/DecodeLabs/Glitch/Stack/Trace.php index f35314f..7e6c783 100644 --- a/src/Glitch/Stack/Trace.php +++ b/src/DecodeLabs/Glitch/Stack/Trace.php @@ -4,13 +4,13 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Stack; +namespace DecodeLabs\Glitch\Stack; -use Glitch\Context; +use DecodeLabs\Glitch\Context; -use Glitch\Inspectable; -use Glitch\Dumper\Inspector; -use Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Inspectable; +use DecodeLabs\Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; /** * Represents a normalized stack trace @@ -88,7 +88,7 @@ public function __construct(array $frames) foreach ($frames as $frame) { if (!$frame instanceof Frame) { throw \Glitch::EUnexpectedValue([ - 'message' => 'Trace frame is not an instance of Glitch\\Frame', + 'message' => 'Trace frame is not an instance of DecodeLabs\\Glitch\\Frame', 'data' => $frame ]); } diff --git a/src/Glitch/Stat.php b/src/DecodeLabs/Glitch/Stat.php similarity index 98% rename from src/Glitch/Stat.php rename to src/DecodeLabs/Glitch/Stat.php index 716c1be..a40c798 100644 --- a/src/Glitch/Stat.php +++ b/src/DecodeLabs/Glitch/Stat.php @@ -4,7 +4,7 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch; +namespace DecodeLabs\Glitch; class Stat { diff --git a/src/Glitch/TException.php b/src/DecodeLabs/Glitch/TException.php similarity index 94% rename from src/Glitch/TException.php rename to src/DecodeLabs/Glitch/TException.php index 40aa407..dd0ceb5 100644 --- a/src/Glitch/TException.php +++ b/src/DecodeLabs/Glitch/TException.php @@ -4,14 +4,14 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch; +namespace DecodeLabs\Glitch; -use Glitch\Stack\Frame; -use Glitch\Stack\Trace; +use DecodeLabs\Glitch\Stack\Frame; +use DecodeLabs\Glitch\Stack\Trace; -use Glitch\Inspectable; -use Glitch\Dumper\Inspector; -use Glitch\Dumper\Entity; +use DecodeLabs\Glitch\Inspectable; +use DecodeLabs\Glitch\Dumper\Inspector; +use DecodeLabs\Glitch\Dumper\Entity; /** * Main root exception inheritance diff --git a/src/Glitch/Transport.php b/src/DecodeLabs/Glitch/Transport.php similarity index 87% rename from src/Glitch/Transport.php rename to src/DecodeLabs/Glitch/Transport.php index 00cfaac..e2cab96 100644 --- a/src/Glitch/Transport.php +++ b/src/DecodeLabs/Glitch/Transport.php @@ -4,7 +4,7 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch; +namespace DecodeLabs\Glitch; interface Transport { diff --git a/src/Glitch/Transport/Stdout.php b/src/DecodeLabs/Glitch/Transport/Stdout.php similarity index 80% rename from src/Glitch/Transport/Stdout.php rename to src/DecodeLabs/Glitch/Transport/Stdout.php index 583c0a7..28a3988 100644 --- a/src/Glitch/Transport/Stdout.php +++ b/src/DecodeLabs/Glitch/Transport/Stdout.php @@ -4,9 +4,9 @@ * @license http://opensource.org/licenses/MIT */ declare(strict_types=1); -namespace Glitch\Transport; +namespace DecodeLabs\Glitch\Transport; -use Glitch\Transport; +use DecodeLabs\Glitch\Transport; class Stdout implements Transport { diff --git a/src/EGlitch.php b/src/EGlitch.php index 405cb3a..535f033 100644 --- a/src/EGlitch.php +++ b/src/EGlitch.php @@ -4,10 +4,10 @@ * @license http://opensource.org/licenses/MIT */ -use Glitch\Stack\Frame; -use Glitch\Stack\Trace; +use DecodeLabs\Glitch\Stack\Frame; +use DecodeLabs\Glitch\Stack\Trace; -interface EGlitch +interface EGlitch extends \Throwable { public function setData($data); public function getData(); diff --git a/src/Glitch.php b/src/Glitch.php index b2b5388..5ed69e3 100644 --- a/src/Glitch.php +++ b/src/Glitch.php @@ -5,8 +5,8 @@ */ declare(strict_types=1); -use Glitch\Factory; -use Glitch\Context; +use DecodeLabs\Glitch\Factory; +use DecodeLabs\Glitch\Context; /** * This is just a facade. diff --git a/src/helpers.php b/src/helpers.php index 2f44bba..d3a023b 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -11,9 +11,9 @@ */ namespace { - use Glitch\Factory; - use Glitch\Context; - use Glitch\Stack\Frame; + use DecodeLabs\Glitch\Factory; + use DecodeLabs\Glitch\Context; + use DecodeLabs\Glitch\Stack\Frame; use Symfony\Component\VarDumper\VarDumper; From c3df7e3530769f44977b75d4dfc24a49d1f0d133 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Thu, 22 Aug 2019 19:21:02 +0100 Subject: [PATCH 29/29] Added trace to foot of dump Signed-off-by: Tom Wright --- src/DecodeLabs/Glitch/Context.php | 6 ++++- src/DecodeLabs/Glitch/Dumper/Dump.php | 24 +++++++++++++++++++ .../Glitch/Dumper/Renderer/Html.php | 20 +++++++++++++--- .../Glitch/Dumper/Renderer/assets/dump.css | 5 ++++ src/DecodeLabs/Glitch/Inspectable.php | 3 +-- src/DecodeLabs/Glitch/Stack/Trace.php | 3 +-- src/DecodeLabs/Glitch/TException.php | 9 ++++--- 7 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/DecodeLabs/Glitch/Context.php b/src/DecodeLabs/Glitch/Context.php index 0c67476..96c7bb2 100644 --- a/src/DecodeLabs/Glitch/Context.php +++ b/src/DecodeLabs/Glitch/Context.php @@ -136,9 +136,13 @@ public function dump(array $values, int $rewind=null): void } foreach ($values as $value) { - $dump->addEntity($inspector->inspectValue($value)); + $dump->addEntity($inspector($value)); } + $dump->setTraceEntity($inspector($trace, function ($entity) { + $entity->setOpen(false); + })); + $packet = $this->getDumpRenderer()->render($dump, true); $this->getTransport()->sendDump($packet); } diff --git a/src/DecodeLabs/Glitch/Dumper/Dump.php b/src/DecodeLabs/Glitch/Dumper/Dump.php index b7e550a..568aebe 100644 --- a/src/DecodeLabs/Glitch/Dumper/Dump.php +++ b/src/DecodeLabs/Glitch/Dumper/Dump.php @@ -15,7 +15,9 @@ class Dump implements \IteratorAggregate { protected $stats = []; protected $entities = []; + protected $trace; + protected $traceEntity; /** * Construct with stack trace of invoking call @@ -99,6 +101,28 @@ public function getEntities(): array return $this->entities; } + + /** + * Set trace entity + */ + public function setTraceEntity(Entity $entity): Dump + { + $this->traceEntity = $entity; + return $this; + } + + /** + * Get trace entity + */ + public function getTraceEntity(): ?Entity + { + return $this->traceEntity; + } + + + /** + * Loop all entities + */ public function getIterator() { return new ArrayIterator($this->entities); diff --git a/src/DecodeLabs/Glitch/Dumper/Renderer/Html.php b/src/DecodeLabs/Glitch/Dumper/Renderer/Html.php index da3b0d1..e3b3f09 100644 --- a/src/DecodeLabs/Glitch/Dumper/Renderer/Html.php +++ b/src/DecodeLabs/Glitch/Dumper/Renderer/Html.php @@ -7,6 +7,7 @@ namespace DecodeLabs\Glitch\Dumper\Renderer; use DecodeLabs\Glitch\Context; +use DecodeLabs\Glitch\Stack\Trace; use DecodeLabs\Glitch\Dumper\Renderer; use DecodeLabs\Glitch\Dumper\Dump; use DecodeLabs\Glitch\Dumper\Entity; @@ -65,6 +66,13 @@ public function render(Dump $dump, bool $isFinal=false): string $this->output[] = ''; } + + if ($traceEntity = $dump->getTraceEntity()) { + $this->output[] = ''; + $this->renderEntity($traceEntity); + $this->output[] = ''; + } + $this->output[] = '
'; @@ -533,9 +541,16 @@ protected function renderStackBlock(Entity $entity): void { $id = $entity->getId(); $this->output[] = '
'; - $this->output[] = '
    '; + $this->renderStackList($entity->getStackTrace()); + $this->output[] = '
'; + } - $trace = $entity->getStackTrace(); + /** + * Render entity stack list + */ + protected function renderStackList(Trace $trace): void + { + $this->output[] = '
    '; $count = count($trace); foreach ($trace as $i => $frame) { @@ -548,7 +563,6 @@ protected function renderStackBlock(Entity $entity): void } $this->output[] = '
'; - $this->output[] = '
'; } diff --git a/src/DecodeLabs/Glitch/Dumper/Renderer/assets/dump.css b/src/DecodeLabs/Glitch/Dumper/Renderer/assets/dump.css index dcbdd67..7b368d7 100644 --- a/src/DecodeLabs/Glitch/Dumper/Renderer/assets/dump.css +++ b/src/DecodeLabs/Glitch/Dumper/Renderer/assets/dump.css @@ -34,6 +34,11 @@ samp.dump { min-width: 700px; } +samp.dump.trace { + padding: 1rem 0; + border-top: 1px solid #EEE; +} + /* SCALARS */ .null { diff --git a/src/DecodeLabs/Glitch/Inspectable.php b/src/DecodeLabs/Glitch/Inspectable.php index fb6f184..241e1bb 100644 --- a/src/DecodeLabs/Glitch/Inspectable.php +++ b/src/DecodeLabs/Glitch/Inspectable.php @@ -7,9 +7,8 @@ namespace DecodeLabs\Glitch; use DecodeLabs\Glitch\Dumper\Entity; -use DecodeLabs\Glitch\Dumper\Inspector; interface Inspectable { - public function glitchInspect(Entity $entity, Inspector $inspector): void; + public function glitchInspect(Entity $entity, callable $inspector): void; } diff --git a/src/DecodeLabs/Glitch/Stack/Trace.php b/src/DecodeLabs/Glitch/Stack/Trace.php index 7e6c783..0a025ba 100644 --- a/src/DecodeLabs/Glitch/Stack/Trace.php +++ b/src/DecodeLabs/Glitch/Stack/Trace.php @@ -9,7 +9,6 @@ use DecodeLabs\Glitch\Context; use DecodeLabs\Glitch\Inspectable; -use DecodeLabs\Glitch\Dumper\Inspector; use DecodeLabs\Glitch\Dumper\Entity; /** @@ -193,7 +192,7 @@ public function __debugInfo(): array /** * Inspect for Glitch */ - public function glitchInspect(Entity $entity, Inspector $inspector): void + public function glitchInspect(Entity $entity, callable $inspector): void { $entity->setStackTrace($this); } diff --git a/src/DecodeLabs/Glitch/TException.php b/src/DecodeLabs/Glitch/TException.php index dd0ceb5..a01a241 100644 --- a/src/DecodeLabs/Glitch/TException.php +++ b/src/DecodeLabs/Glitch/TException.php @@ -10,7 +10,6 @@ use DecodeLabs\Glitch\Stack\Trace; use DecodeLabs\Glitch\Inspectable; -use DecodeLabs\Glitch\Dumper\Inspector; use DecodeLabs\Glitch\Dumper\Entity; /** @@ -151,13 +150,13 @@ public function __debugInfo(): array /** * Inspect for Glitch */ - public function glitchInspect(Entity $entity, Inspector $inspector): void + public function glitchInspect(Entity $entity, callable $inspector): void { $entity ->setText($this->message) - ->setProperty('*code', $inspector->inspect($this->code)) - ->setProperty('*http', $inspector->inspect($this->http)) - ->setValues($inspector->inspect($this->data)) + ->setProperty('*code', $inspector($this->code)) + ->setProperty('*http', $inspector($this->http)) + ->setValues($inspector($this->data)) ->setFile($this->file) ->setStartLine($this->line) ->setStackTrace($this->getStackTrace());