From e8997ce587e831b49b5611c8ac7691635bf1bc4b Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 5 Feb 2018 21:58:27 +0700 Subject: [PATCH 01/40] don't support zend-expressive v3 for version 1 as it will be support at version 2 --- README.md | 4 ++-- composer.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ce1b4c9a..112a109c 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,7 @@ return [ // php public/index.php error-preview // // for zf-expressive ^1.0, the disable error-preview page is by unregister 'error-preview' from this config under "routes", - // for zf-expressive ^2.0 or ^3.0.0-dev, the disable error-preview page is by unregister 'error-preview' from config/routes + // for zf-expressive ^2.0, the disable error-preview page is by unregister 'error-preview' from config/routes // // // otherwise(false), you can't see them, eg: on production env. @@ -269,7 +269,7 @@ return [ For [zend-expressive-skeleton](https://github.com/zendframework/zend-expressive-skeleton) ^1.0, It's should already just works! -For [zend-expressive-skeleton](https://github.com/zendframework/zend-expressive-skeleton) ^2.0 or ^3.0.0-dev, you need to open `config/pipeline.php` and add the `ErrorHeroModule\Middleware\Expressive::class` middleware after default `ErrorHandler::class` registration: +For [zend-expressive-skeleton](https://github.com/zendframework/zend-expressive-skeleton) ^2.0, you need to open `config/pipeline.php` and add the `ErrorHeroModule\Middleware\Expressive::class` middleware after default `ErrorHandler::class` registration: ```php $app->pipe(ErrorHandler::class); diff --git a/composer.json b/composer.json index 769910ac..02e6fcd7 100644 --- a/composer.json +++ b/composer.json @@ -28,8 +28,8 @@ "doctrine/doctrine-orm-module": "^1.1", "kahlan/kahlan": "^3.0.0", "satooshi/php-coveralls": "^1.0", - "zendframework/zend-expressive": "^1.1|^2.0|^3.0.0-dev", - "zendframework/zend-expressive-zendviewrenderer": "^1.4|^2.0.0-dev", + "zendframework/zend-expressive": "^1.1|^2.0", + "zendframework/zend-expressive-zendviewrenderer": "^1.4", "zendframework/zend-mvc": "^2.5|^3.0", "zendframework/zend-mvc-console": "^1.1" }, From ff5572edb17c19a8db21c4363c99462d8bc6c273 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 12 Feb 2018 10:25:35 +0700 Subject: [PATCH 02/40] add error_type to is exists check --- src/Handler/Logging.php | 7 ++++--- src/Handler/Writer/Db.php | 22 ++++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Handler/Logging.php b/src/Handler/Logging.php index 75745b09..c2cd6169 100644 --- a/src/Handler/Logging.php +++ b/src/Handler/Logging.php @@ -127,14 +127,14 @@ public function setServerRequestandRequestUri(ServerRequestInterface $request) * * @return bool */ - private function isExists($errorFile, $errorLine, $errorMessage, $url) + private function isExists($errorFile, $errorLine, $errorMessage, $url, $errorType) { $writers = $this->logger->getWriters()->toArray(); foreach ($writers as $writer) { if ($writer instanceof Db) { try { $handlerWriterDb = new Writer\Db($writer, $this->configLoggingSettings, $this->logWritersConfig); - if ($handlerWriterDb->isExists($errorFile, $errorLine, $errorMessage, $url)) { + if ($handlerWriterDb->isExists($errorFile, $errorLine, $errorMessage, $url, $errorType)) { return true; } break; @@ -288,7 +288,8 @@ public function handleErrorException($e) $collectedExceptionData['errorFile'], $collectedExceptionData['errorLine'], $collectedExceptionData['errorMessage'], - $this->serverUrl.$this->requestUri + $this->serverUrl.$this->requestUri, + $collectedExceptionData['error_type'] ) ) { return; diff --git a/src/Handler/Writer/Db.php b/src/Handler/Writer/Db.php index b8a602b1..16b60f9d 100644 --- a/src/Handler/Writer/Db.php +++ b/src/Handler/Writer/Db.php @@ -47,7 +47,7 @@ public function __construct( * * @return bool */ - public function isExists($errorFile, $errorLine, $errorMessage, $errorUrl) + public function isExists($errorFile, $errorLine, $errorMessage, $errorUrl, $errorType) { // db definition $reflectionProperty = new ReflectionProperty($this->dbWriter, 'db'); @@ -60,20 +60,22 @@ public function isExists($errorFile, $errorLine, $errorMessage, $errorUrl) $table = $writerConfig['options']['table']; // columns definition - $timestamp = $writerConfig['options']['column']['timestamp']; - $message = $writerConfig['options']['column']['message']; - $file = $writerConfig['options']['column']['extra']['file']; - $line = $writerConfig['options']['column']['extra']['line']; - $url = $writerConfig['options']['column']['extra']['url']; + $timestamp = $writerConfig['options']['column']['timestamp']; + $message = $writerConfig['options']['column']['message']; + $file = $writerConfig['options']['column']['extra']['file']; + $line = $writerConfig['options']['column']['extra']['line']; + $url = $writerConfig['options']['column']['extra']['url']; + $error_type = $writerConfig['options']['columns']['extra']['error_type']; $tableGateway = new TableGateway($table, $db, null, new ResultSet()); $select = $tableGateway->getSql()->select(); $select->columns([$timestamp]); $select->where([ - $message => $errorMessage, - $line => $errorLine, - $url => $errorUrl, - $file => $errorFile, + $message => $errorMessage, + $line => $errorLine, + $url => $errorUrl, + $file => $errorFile, + $error_type => $errorType, ]); $select->order($timestamp.' DESC'); $select->limit(1); From cd217a6f02dbe9ac90ee9caafdf9a544026c5bd0 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 12 Feb 2018 10:27:11 +0700 Subject: [PATCH 03/40] docblock for errorType parameter --- src/Handler/Logging.php | 1 + src/Handler/Writer/Db.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Handler/Logging.php b/src/Handler/Logging.php index c2cd6169..13913312 100644 --- a/src/Handler/Logging.php +++ b/src/Handler/Logging.php @@ -122,6 +122,7 @@ public function setServerRequestandRequestUri(ServerRequestInterface $request) * @param int $errorLine * @param string $errorMessage * @param string $url + * @param string $errorType * * @throws RuntimeException when cannot connect to DB in the first place * diff --git a/src/Handler/Writer/Db.php b/src/Handler/Writer/Db.php index 16b60f9d..c0233508 100644 --- a/src/Handler/Writer/Db.php +++ b/src/Handler/Writer/Db.php @@ -44,6 +44,7 @@ public function __construct( * @param int $errorLine * @param string $errorMessage * @param string $errorUrl + * @param string $errorType * * @return bool */ From 204d54879c894e04d74aedf17ae480d3f524f42d Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 12 Feb 2018 10:37:22 +0700 Subject: [PATCH 04/40] using errorType key --- src/Handler/Logging.php | 2 +- src/Handler/Writer/Db.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Handler/Logging.php b/src/Handler/Logging.php index 13913312..409637b1 100644 --- a/src/Handler/Logging.php +++ b/src/Handler/Logging.php @@ -290,7 +290,7 @@ public function handleErrorException($e) $collectedExceptionData['errorLine'], $collectedExceptionData['errorMessage'], $this->serverUrl.$this->requestUri, - $collectedExceptionData['error_type'] + $collectedExceptionData['errorType'] ) ) { return; diff --git a/src/Handler/Writer/Db.php b/src/Handler/Writer/Db.php index c0233508..fc10d1ed 100644 --- a/src/Handler/Writer/Db.php +++ b/src/Handler/Writer/Db.php @@ -66,7 +66,7 @@ public function isExists($errorFile, $errorLine, $errorMessage, $errorUrl, $erro $file = $writerConfig['options']['column']['extra']['file']; $line = $writerConfig['options']['column']['extra']['line']; $url = $writerConfig['options']['column']['extra']['url']; - $error_type = $writerConfig['options']['columns']['extra']['error_type']; + $error_type = $writerConfig['options']['column']['extra']['error_type']; $tableGateway = new TableGateway($table, $db, null, new ResultSet()); $select = $tableGateway->getSql()->select(); From e14212ea3a3d50e18da9b82115bbcc8d813bc5cc Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 12 Feb 2018 10:37:28 +0700 Subject: [PATCH 05/40] fix test --- spec/Handler/Writer/DbSpec.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/Handler/Writer/DbSpec.php b/spec/Handler/Writer/DbSpec.php index f0b9e2f4..13ed9e60 100644 --- a/spec/Handler/Writer/DbSpec.php +++ b/spec/Handler/Writer/DbSpec.php @@ -86,7 +86,7 @@ allow($resultSet)->toReceive('current')->andReturn(null); allow(TableGateway::class)->toReceive('selectWith')->with($select)->andReturn($resultSet); - $actual = $this->writerHandler->isExists('file', 1, 'Undefined offset: 1', 'http://serverUrl/uri'); + $actual = $this->writerHandler->isExists('file', 1, 'Undefined offset: 1', 'http://serverUrl/uri', 'E_NOTICE'); expect($actual)->toBe(false); }); @@ -117,7 +117,7 @@ ); allow(TableGateway::class)->toReceive('selectWith')->with($select)->andReturn($resultSet); - $actual = $this->writerHandler->isExists('file', 1, 'Undefined offset: 1', 'http://serverUrl/uri'); + $actual = $this->writerHandler->isExists('file', 1, 'Undefined offset: 1', 'http://serverUrl/uri', 'E_NOTICE'); expect($actual)->toBe(false); }); @@ -143,7 +143,7 @@ ); allow(TableGateway::class)->toReceive('selectWith')->with($select)->andReturn($resultSet); - $actual = $this->writerHandler->isExists('file', 1, 'Undefined offset: 1', 'http://serverUrl/uri'); + $actual = $this->writerHandler->isExists('file', 1, 'Undefined offset: 1', 'http://serverUrl/uri', 'E_NOTICE'); expect($actual)->toBe(true); }); From 82052a8569c360d1d3ea1350758e25fe2d6e5a67 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 25 Apr 2018 14:18:17 +0700 Subject: [PATCH 06/40] Fixes code suppress by @ should be ignored --- spec/Listener/MvcSpec.php | 9 +++++++++ spec/Middleware/ExpressiveSpec.php | 9 +++++++++ src/HeroTrait.php | 4 ++++ 3 files changed, 22 insertions(+) diff --git a/spec/Listener/MvcSpec.php b/spec/Listener/MvcSpec.php index 5849f856..cd317ef9 100644 --- a/spec/Listener/MvcSpec.php +++ b/spec/Listener/MvcSpec.php @@ -502,6 +502,15 @@ }); + it('error_reporting() returns 0', function () { + + allow('error_reporting')->tobeCalled()->andReturn(0); + $actual = $this->listener->phpErrorHandler(2, 'mkdir(): File exists', 'file.php', 6); + // null means use default mvc process + expect($actual)->toBeNull(); + + }); + it('exclude error type and match', function () { $actual = $this->listener->phpErrorHandler(\E_USER_DEPRECATED, 'deprecated', 'file.php', 1); diff --git a/spec/Middleware/ExpressiveSpec.php b/spec/Middleware/ExpressiveSpec.php index 55de6f5c..91aaa8ee 100644 --- a/spec/Middleware/ExpressiveSpec.php +++ b/spec/Middleware/ExpressiveSpec.php @@ -601,6 +601,15 @@ }); + it('error_reporting() returns 0', function () { + + allow('error_reporting')->tobeCalled()->andReturn(0); + $actual = $this->middleware->phpErrorHandler(2, 'mkdir(): File exists', 'file.php', 6); + // null means use default mvc process + expect($actual)->toBeNull(); + + }); + it('exclude error type and match', function () { $actual = $this->middleware->phpErrorHandler(\E_USER_DEPRECATED, 'deprecated', 'file.php', 1); diff --git a/src/HeroTrait.php b/src/HeroTrait.php index 53f8066e..f3d01b14 100644 --- a/src/HeroTrait.php +++ b/src/HeroTrait.php @@ -53,6 +53,10 @@ public function phpErrorHandler($errorType, $errorMessage, $errorFile, $errorLin return; } + if (! (error_reporting() & $errorType)) { + return; + } + if (! $this->errorHeroModuleConfig['display-settings']['display_errors']) { \error_reporting(\E_ALL | \E_STRICT); \ini_set('display_errors', 0); From 1f67e885b5ad7dae2f8c41f0beaf819568c75d54 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 25 Apr 2018 14:28:32 +0700 Subject: [PATCH 07/40] typo fix --- spec/Middleware/ExpressiveSpec.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Middleware/ExpressiveSpec.php b/spec/Middleware/ExpressiveSpec.php index 91aaa8ee..63cbe33c 100644 --- a/spec/Middleware/ExpressiveSpec.php +++ b/spec/Middleware/ExpressiveSpec.php @@ -605,7 +605,7 @@ allow('error_reporting')->tobeCalled()->andReturn(0); $actual = $this->middleware->phpErrorHandler(2, 'mkdir(): File exists', 'file.php', 6); - // null means use default mvc process + // null means use default next(req, resp) expect($actual)->toBeNull(); }); From 377bbeeddd27067616560f6ade85db22aeaf00fb Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 25 Apr 2018 22:59:26 +0700 Subject: [PATCH 08/40] remove unused check ! $errorLine Conflicts: spec/Middleware/ExpressiveSpec.php --- spec/Listener/MvcSpec.php | 8 -------- spec/Middleware/ExpressiveSpec.php | 8 -------- src/HeroTrait.php | 4 ---- 3 files changed, 20 deletions(-) diff --git a/spec/Listener/MvcSpec.php b/spec/Listener/MvcSpec.php index cd317ef9..a1e8f797 100644 --- a/spec/Listener/MvcSpec.php +++ b/spec/Listener/MvcSpec.php @@ -494,14 +494,6 @@ describe('->phpErrorHandler()', function () { - it('do not has error', function () { - - $actual = $this->listener->phpErrorHandler(0, '', '', 0); - // null means use default mvc process - expect($actual)->toBeNull(); - - }); - it('error_reporting() returns 0', function () { allow('error_reporting')->tobeCalled()->andReturn(0); diff --git a/spec/Middleware/ExpressiveSpec.php b/spec/Middleware/ExpressiveSpec.php index 63cbe33c..f99454da 100644 --- a/spec/Middleware/ExpressiveSpec.php +++ b/spec/Middleware/ExpressiveSpec.php @@ -593,14 +593,6 @@ describe('->phpErrorHandler()', function () { - it('do not has error', function () { - - $actual = $this->middleware->phpErrorHandler(0, '', '', 0); - // null means use default next(req, resp) - expect($actual)->toBeNull(); - - }); - it('error_reporting() returns 0', function () { allow('error_reporting')->tobeCalled()->andReturn(0); diff --git a/src/HeroTrait.php b/src/HeroTrait.php index f3d01b14..ef8325b2 100644 --- a/src/HeroTrait.php +++ b/src/HeroTrait.php @@ -49,10 +49,6 @@ public function execOnShutdown() */ public function phpErrorHandler($errorType, $errorMessage, $errorFile, $errorLine) { - if (! $errorLine) { - return; - } - if (! (error_reporting() & $errorType)) { return; } From 4f535b8f485ff20b0b517dcd69de9cdeb5cd9418 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 23 Jul 2018 15:08:44 +0700 Subject: [PATCH 09/40] backport using PHP_BINARY for "php" binary path information for console environment --- src/Handler/LoggingFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Handler/LoggingFactory.php b/src/Handler/LoggingFactory.php index 148bf0b5..eb8602ce 100644 --- a/src/Handler/LoggingFactory.php +++ b/src/Handler/LoggingFactory.php @@ -36,7 +36,7 @@ public function __invoke($container) } else { $serverUrl = \php_uname('n'); $request = new ConsoleRequest(); - $requestUri = ':'. \basename(\getcwd()) .' ' . \get_current_user() . '$ php ' . $request->getScriptName() . ' ' . $request->toString(); + $requestUri = ':'. \basename(\getcwd()) .' ' . \get_current_user() . '$ ' . \PHP_BINARY . ' ' . $request->getScriptName() . ' ' . $request->toString(); } $config = $container->get('config'); From b19b446f9b7fd9c0ed264562b0d4c5de6dd9d5e4 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 16 Sep 2018 21:06:24 +0700 Subject: [PATCH 10/40] backport handle fatal error on non existent class --- kahlan-config.php | 27 +++++++++++++++++++++++++++ src/HeroAutoload.php | 32 ++++++++++++++++++++++++++++++++ src/Listener/Mvc.php | 2 ++ src/Middleware/Expressive.php | 2 ++ 4 files changed, 63 insertions(+) create mode 100644 kahlan-config.php create mode 100644 src/HeroAutoload.php diff --git a/kahlan-config.php b/kahlan-config.php new file mode 100644 index 00000000..2786e567 --- /dev/null +++ b/kahlan-config.php @@ -0,0 +1,27 @@ +commandLine(); +$commandLine->option('coverage', 'default', 3); + +Filter::register('kahlan.coverage', function($chain) { + if (!extension_loaded('xdebug')) { + return; + } + $reporters = $this->reporters(); + $coverage = new Coverage([ + 'verbosity' => $this->commandLine()->get('coverage'), + 'driver' => new Xdebug(), + 'path' => $this->commandLine()->get('src'), + 'exclude' => [ + 'src/HeroAutoload.php', + ], + 'colors' => !$this->commandLine()->get('no-colors') + ]); + $reporters->add('coverage', $coverage); +}); + +Filter::apply($this, 'coverage', 'kahlan.coverage'); \ No newline at end of file diff --git a/src/HeroAutoload.php b/src/HeroAutoload.php new file mode 100644 index 00000000..71a8f112 --- /dev/null +++ b/src/HeroAutoload.php @@ -0,0 +1,32 @@ + Date: Sun, 16 Sep 2018 21:15:12 +0700 Subject: [PATCH 11/40] allow failures on nightly --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5f6bf21f..a5ee19eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,3 +27,8 @@ script: notifications: email: false + +matrix: + fast_finish: true + allow_failures: + - php: nightly From 747ecb210052a9f39b1e15134580978e6d20e063 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 17 Sep 2018 09:23:25 +0700 Subject: [PATCH 12/40] exclude some ZF version in Zray --- src/HeroAutoload.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/HeroAutoload.php b/src/HeroAutoload.php index 71a8f112..f5795297 100644 --- a/src/HeroAutoload.php +++ b/src/HeroAutoload.php @@ -8,7 +8,7 @@ class HeroAutoload { public static function handle($class) { - if (\class_exists($class)) { + if (\class_exists($class, false)) { return; } @@ -19,6 +19,8 @@ public static function handle($class) 'error_get_last', 'ErrorHeroModuleLogger', 'ZendDeveloperTools\\ProfilerEvent', + 'Zend\Version\Version', + 'ZF\MvcAuth\MvcAuthEvent', ] )) { return; From ee1f7efe8bfad6918f6ca9189e5580bfbc9993c0 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 17 Sep 2018 09:46:54 +0700 Subject: [PATCH 13/40] exclude zfray classes --- src/HeroAutoload.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/HeroAutoload.php b/src/HeroAutoload.php index f5795297..b05345a3 100644 --- a/src/HeroAutoload.php +++ b/src/HeroAutoload.php @@ -21,6 +21,8 @@ public static function handle($class) 'ZendDeveloperTools\\ProfilerEvent', 'Zend\Version\Version', 'ZF\MvcAuth\MvcAuthEvent', + 'Zend\EventManager\GlobalEventManager', + 'Zend\Session\ValidatorChain', ] )) { return; From 03dc082e0eb4bb561788fb7d58cc3b9ff7db774e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 17 Sep 2018 11:41:49 +0700 Subject: [PATCH 14/40] handle class_exists and interface_exists call in HeroAutoload --- src/HeroAutoload.php | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/HeroAutoload.php b/src/HeroAutoload.php index b05345a3..14342572 100644 --- a/src/HeroAutoload.php +++ b/src/HeroAutoload.php @@ -12,19 +12,14 @@ public static function handle($class) return; } - if (\in_array( - $class, - [ - 'error_reporting', - 'error_get_last', - 'ErrorHeroModuleLogger', - 'ZendDeveloperTools\\ProfilerEvent', - 'Zend\Version\Version', - 'ZF\MvcAuth\MvcAuthEvent', - 'Zend\EventManager\GlobalEventManager', - 'Zend\Session\ValidatorChain', - ] - )) { + $debugBacktrace = \debug_backtrace(); + if ( + isset($debugBacktrace[1]['function'], $debugBacktrace[2]['function']) && + $debugBacktrace[1]['function'] === 'spl_autoload_call' && + ( + $debugBacktrace[2]['function'] === 'class_exists' || $debugBacktrace[2]['function'] === 'interface_exists' + ) + ) { return; } From 7f69f56beb0fc47233827b1eacc560e2a9e6b4d5 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 17 Sep 2018 13:11:17 +0700 Subject: [PATCH 15/40] handle trait_exists in HeroAutoload --- src/HeroAutoload.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/HeroAutoload.php b/src/HeroAutoload.php index 14342572..adc5321e 100644 --- a/src/HeroAutoload.php +++ b/src/HeroAutoload.php @@ -6,26 +6,24 @@ class HeroAutoload { - public static function handle($class) + public static function handle($classOrTraitOrInterface) { - if (\class_exists($class, false)) { - return; - } - $debugBacktrace = \debug_backtrace(); if ( isset($debugBacktrace[1]['function'], $debugBacktrace[2]['function']) && $debugBacktrace[1]['function'] === 'spl_autoload_call' && ( - $debugBacktrace[2]['function'] === 'class_exists' || $debugBacktrace[2]['function'] === 'interface_exists' + $debugBacktrace[2]['function'] === 'class_exists' || + $debugBacktrace[2]['function'] === 'trait_exists' || + $debugBacktrace[2]['function'] === 'interface_exists' ) ) { return; } throw new RuntimeException(sprintf( - 'class %s not found', - $class + 'class or trait or interface %s not found', + $classOrTraitOrInterface )); } } \ No newline at end of file From 6ab70e144f2deece77a742baaef04b3e9e5e6879 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 03:28:54 +0700 Subject: [PATCH 16/40] backport handle more fatal errors --- kahlan-config.php | 4 +- .../ErrorPreviewConsoleControllerSpec.php | 47 --- .../Controller/ErrorPreviewControllerSpec.php | 47 --- spec/Listener/MvcSpec.php | 93 +++-- spec/Middleware/ExpressiveSpec.php | 341 +++++++++++++++++- .../Routed/Preview/ErrorPreviewActionSpec.php | 73 ---- src/HeroAutoload.php | 29 -- src/HeroTrait.php | 63 +++- src/Listener/Mvc.php | 7 +- src/Middleware/Expressive.php | 2 +- 10 files changed, 459 insertions(+), 247 deletions(-) delete mode 100644 spec/Controller/ErrorPreviewConsoleControllerSpec.php delete mode 100644 spec/Controller/ErrorPreviewControllerSpec.php delete mode 100644 spec/Middleware/Routed/Preview/ErrorPreviewActionSpec.php delete mode 100644 src/HeroAutoload.php diff --git a/kahlan-config.php b/kahlan-config.php index 2786e567..6cff0c47 100644 --- a/kahlan-config.php +++ b/kahlan-config.php @@ -17,7 +17,9 @@ 'driver' => new Xdebug(), 'path' => $this->commandLine()->get('src'), 'exclude' => [ - 'src/HeroAutoload.php', + 'src/Controller/ErrorPreviewConsoleController.php', + 'src/Controller/ErrorPreviewController.php', + 'src/Middleware/Routed/Preview/ErrorPreviewAction.php', ], 'colors' => !$this->commandLine()->get('no-colors') ]); diff --git a/spec/Controller/ErrorPreviewConsoleControllerSpec.php b/spec/Controller/ErrorPreviewConsoleControllerSpec.php deleted file mode 100644 index 48f3e5e3..00000000 --- a/spec/Controller/ErrorPreviewConsoleControllerSpec.php +++ /dev/null @@ -1,47 +0,0 @@ -exceptionAction()', function() { - - it('throw Exception', function() { - - $controller = $this->controller; - $closure = function() use ($controller) { - $controller->exceptionAction(); - }; - expect($closure)->toThrow(new \Exception('a sample error preview')); - - }); - - }); - - describe('->errorAction()', function() { - - it('Error', function() { - - skipIf(PHP_MAJOR_VERSION < 7); - - try { - $controller = $this->controller; - $controller->errorAction(); - } catch (\Throwable $error) { - expect($error)->toBeAnInstanceOf(\Throwable::class); - expect($error->getMessage())->toContain('E_NOTICE'); - } - - }); - - }); - -}); diff --git a/spec/Controller/ErrorPreviewControllerSpec.php b/spec/Controller/ErrorPreviewControllerSpec.php deleted file mode 100644 index d4744135..00000000 --- a/spec/Controller/ErrorPreviewControllerSpec.php +++ /dev/null @@ -1,47 +0,0 @@ -exceptionAction()', function() { - - it('throw Exception', function() { - - $controller = $this->controller; - $closure = function() use ($controller) { - $controller->exceptionAction(); - }; - expect($closure)->toThrow(new \Exception('a sample error preview')); - - }); - - }); - - describe('->errorAction()', function() { - - it('Error', function() { - - skipIf(PHP_MAJOR_VERSION < 7); - - try { - $controller = $this->controller; - $controller->errorAction(); - } catch (\Throwable $error) { - expect($error)->toBeAnInstanceOf(\Throwable::class); - expect($error->getMessage())->toContain('E_NOTICE'); - } - - }); - - }); - -}); diff --git a/spec/Listener/MvcSpec.php b/spec/Listener/MvcSpec.php index a1e8f797..09128cb9 100644 --- a/spec/Listener/MvcSpec.php +++ b/spec/Listener/MvcSpec.php @@ -2,6 +2,7 @@ namespace ErrorHeroModule\Spec\Listener; +use Closure; use ErrorException; use ErrorHeroModule\Handler\Logging; use ErrorHeroModule\Listener\Mvc; @@ -346,22 +347,58 @@ }); + describe('->phpFatalErrorHandler()', function () { + + it('returns buffer on no error', function () { + + allow('error_get_last')->toBeCalled()->andReturn(null); + expect($this->listener->phpFatalErrorHandler('test'))->toBe('test'); + + }); + + it('returns buffer on error has "Uncaught" prefix', function () { + + allow('error_get_last')->toBeCalled()->andReturn([ + 'message' => 'Uncaught', + ]); + expect($this->listener->phpFatalErrorHandler('Uncaught'))->toBe('Uncaught'); + + }); + + it('returns result property value on error not has "Uncaught" prefix', function () { + + allow('error_get_last')->toBeCalled()->andReturn([ + 'message' => 'Fatal', + ]); + expect($this->listener->phpFatalErrorHandler('Fatal'))->toBe(''); + + }); + + }); + describe('->execOnShutdown()', function () { - it('call error_get_last() and return nothing', function () { + it('call error_get_last() and return nothing and no result', function () { - allow('error_get_last')->toBeCalled(); - expect('error_get_last')->toBeCalled(); + allow('error_get_last')->toBeCalled()->andReturn(null); + expect($this->listener->execOnShutdown())->toBeNull(); - $this->listener->execOnShutdown(); + }); + + it('call error_get_last() and return nothing on result with "Uncaught" prefix', function () { + + allow('error_get_last')->toBeCalled()->andReturn([ + 'message' => 'Uncaught' + ]); + expect($this->listener->execOnShutdown())->toBeNull(); }); - it('call error_get_last() and return error', function () { + it('call error_get_last() and property_exists() after null check passed', function () { allow('error_get_last')->toBeCalled()->andReturn([ - 'type' => 8, - 'message' => 'Undefined variable: a', + 'type' => 3, + 'message' => 'class@anonymous cannot implement stdClass - it is not an interface', 'file' => '/var/www/zf/module/Application/Module.php', 'line' => 2 ]); @@ -403,7 +440,7 @@ [ 'name' => 'db', 'options' => [ - 'db' => 'Zend\Db\Adapter\Adapter', + 'db' => Adapter::class, 'table' => 'log', 'column' => [ 'timestamp' => 'date', @@ -423,21 +460,8 @@ ]; - $resolver = new Resolver\AggregateResolver(); - - $map = new Resolver\TemplateMapResolver([ - 'error' => __DIR__ . '/../Fixture/view/error.phtml', - 'layout/layout' => __DIR__ . '/../Fixture/view/layout/layout.phtml', - 'error-hero-module/error-default' => __DIR__ . '/../Fixture/view/error-hero-module/error-default.phtml', - ]); - $resolver->attach($map); - $this->renderer->setResolver($resolver); - $logging = new Logging( $logger, - 'http://serverUrl', - Double::instance(['extends' => Request::class, 'methods' => '__construct']), - '/', $this->config, $logWritersConfig, null, @@ -480,12 +504,16 @@ $logging, $this->renderer ); + allow('property_exists')->toBeCalled()->with($listener, 'request')->andReturn(false); + allow('property_exists')->toBeCalled()->with($listener, 'mvcEvent')->andReturn(true); - $closure = function () use ($listener) { - $listener->execOnShutdown(); - }; - expect($closure)->toThrow(new ErrorException('Undefined variable: a', 500, 1, '/var/www/zf/module/Application/Module.php', 2)); + $mvcEvent = & Closure::bind(function & ($listener) { + return $listener->mvcEvent; + }, null, $listener)($listener); + $mvcEvent = Double::instance(['extends' => MvcEvent::class, 'methods' => '__construct']); + allow($mvcEvent)->toReceive('getRequest')->andReturn(new Request()); + expect($listener->execOnShutdown())->toBeNull(); }); @@ -509,11 +537,20 @@ // null means use default mvc process expect($actual)->toBeNull(); - expect(error_reporting())->toBe(E_ALL | E_STRICT); - expect(ini_get('display_errors'))->toBe("0"); + expect(\error_reporting())->toBe(\E_ALL | \E_STRICT); + expect(\ini_get('display_errors'))->toBe("0"); + + }); + + it('throws ErrorException on non excluded php errors', function () { + + $closure = function () { + $this->listener->phpErrorHandler(\E_WARNING, 'warning', 'file.php', 1); + }; + expect($closure)->toThrow(new \ErrorException('warning', 0, \E_WARNING, 'file.php', 1)); }); }); -}); +}); \ No newline at end of file diff --git a/spec/Middleware/ExpressiveSpec.php b/spec/Middleware/ExpressiveSpec.php index f99454da..c5a2b45a 100644 --- a/spec/Middleware/ExpressiveSpec.php +++ b/spec/Middleware/ExpressiveSpec.php @@ -2,6 +2,7 @@ namespace ErrorHeroModule\Spec\Middleware; +use Closure; use ErrorHeroModule\Handler\Logging; use ErrorHeroModule\Middleware\Expressive; use Kahlan\Plugin\Double; @@ -591,25 +592,357 @@ }); + describe('->phpFatalErrorHandler()', function () { + + it('returns buffer on no error', function () { + + allow('error_get_last')->toBeCalled()->andReturn(null); + expect($this->middleware->phpFatalErrorHandler('test'))->toBe('test'); + + }); + + it('returns buffer on error has "Uncaught" prefix', function () { + + allow('error_get_last')->toBeCalled()->andReturn([ + 'message' => 'Uncaught', + ]); + expect($this->middleware->phpFatalErrorHandler('Uncaught'))->toBe('Uncaught'); + + }); + + it('returns result property value on error not has "Uncaught" prefix', function () { + + allow('error_get_last')->toBeCalled()->andReturn([ + 'message' => 'Fatal', + ]); + expect($this->middleware->phpFatalErrorHandler('Fatal'))->toBe(''); + + }); + + }); + + describe('->execOnShutdown()', function () { + + it('call error_get_last() and return nothing', function () { + + allow('error_get_last')->toBeCalled()->andReturn(null); + expect($this->middleware->execOnShutdown())->toBeNull(); + + }); + + it('call error_get_last() and property_exists() after null check passed and throws', function () { + + allow('error_get_last')->toBeCalled()->andReturn([ + 'type' => 3, + 'message' => 'class@anonymous cannot implement stdClass - it is not an interface', + 'file' => '/var/www/zf/templates/app/home-page.phtml', + 'line' => 2 + ]); + + $dbAdapter = new Adapter([ + 'username' => 'root', + 'password' => '', + 'driver' => 'Pdo', + 'dsn' => 'mysql:dbname=errorheromodule;host=127.0.0.1', + 'driver_options' => [ + \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'', + ], + ]); + + $writer = new DbWriter( + [ + 'db' => $dbAdapter, + 'table' => 'log', + 'column' => [ + 'timestamp' => 'date', + 'priority' => 'type', + 'message' => 'event', + 'extra' => [ + 'url' => 'url', + 'file' => 'file', + 'line' => 'line', + 'error_type' => 'error_type', + 'trace' => 'trace', + 'request_data' => 'request_data', + ], + ], + ] + ); + + $logger = new Logger(); + $logger->addWriter($writer); + $logWritersConfig = [ + + [ + 'name' => 'db', + 'options' => [ + 'db' => Adapter::class, + 'table' => 'log', + 'column' => [ + 'timestamp' => 'date', + 'priority' => 'type', + 'message' => 'event', + 'extra' => [ + 'url' => 'url', + 'file' => 'file', + 'line' => 'line', + 'error_type' => 'error_type', + 'trace' => 'trace', + 'request_data' => 'request_data', + ], + ], + ], + ], + + ]; + + $logging = new Logging( + $logger, + $this->config, + $logWritersConfig, + null, + null + ); + + $errorHeroModuleLocalConfig = [ + 'enable' => true, + 'display-settings' => [ + 'exclude-php-errors' => [ + \E_USER_DEPRECATED + ], + 'display_errors' => 1, + 'template' => [ + 'layout' => 'layout/layout', + 'view' => 'error-hero-module/error-default' + ], + 'console' => [ + 'message' => 'We have encountered a problem and we can not fulfill your request. An error report has been generated and sent to the support team and someone will attend to this problem urgently. Please try again later. Thank you for your patience.', + ], + + ], + 'logging-settings' => [ + 'same-error-log-time-range' => 86400, + ], + 'email-notification-settings' => [ + 'enable' => false, + 'mail-message' => 'YourMailMessageService', + 'mail-transport' => 'YourMailTransportService', + 'email-from' => 'Sender Name ', + 'email-to-send' => [ + 'developer1@foo.com', + 'developer2@foo.com', + ], + ], + ]; + + $middleware = new Expressive( + $errorHeroModuleLocalConfig, + $logging, + $this->renderer + ); + + allow('property_exists')->toBeCalled()->with($middleware, 'request')->andReturn(true); + allow('property_exists')->toBeCalled()->with($middleware, 'mvcEvent')->andReturn(false); + + $request = & Closure::bind(function & ($middleware) { + return $middleware->request; + }, null, $middleware)($middleware); + $request = new ServerRequest( + [], + [], + new Uri('http://example.com'), + 'GET', + 'php://memory', + [], + [], + [], + '', + '1.2' + ); + + $closure = function () use ($middleware) { + $middleware->execOnShutdown(); + }; + expect($closure)->toThrow(new \ErrorException( + 'class@anonymous cannot implement stdClass - it is not an interface' + )); + + }); + + it('call error_get_last() and property_exists() after null check passed', function () { + + allow('error_get_last')->toBeCalled()->andReturn([ + 'type' => 3, + 'message' => 'class@anonymous cannot implement stdClass - it is not an interface', + 'file' => '/var/www/zf/templates/app/home-page.phtml', + 'line' => 2 + ]); + + $dbAdapter = new Adapter([ + 'username' => 'root', + 'password' => '', + 'driver' => 'Pdo', + 'dsn' => 'mysql:dbname=errorheromodule;host=127.0.0.1', + 'driver_options' => [ + \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'', + ], + ]); + + $writer = new DbWriter( + [ + 'db' => $dbAdapter, + 'table' => 'log', + 'column' => [ + 'timestamp' => 'date', + 'priority' => 'type', + 'message' => 'event', + 'extra' => [ + 'url' => 'url', + 'file' => 'file', + 'line' => 'line', + 'error_type' => 'error_type', + 'trace' => 'trace', + 'request_data' => 'request_data', + ], + ], + ] + ); + + $logger = new Logger(); + $logger->addWriter($writer); + $logWritersConfig = [ + + [ + 'name' => 'db', + 'options' => [ + 'db' => Adapter::class, + 'table' => 'log', + 'column' => [ + 'timestamp' => 'date', + 'priority' => 'type', + 'message' => 'event', + 'extra' => [ + 'url' => 'url', + 'file' => 'file', + 'line' => 'line', + 'error_type' => 'error_type', + 'trace' => 'trace', + 'request_data' => 'request_data', + ], + ], + ], + ], + + ]; + + $logging = new Logging( + $logger, + $this->config, + $logWritersConfig, + null, + null + ); + + $errorHeroModuleLocalConfig = [ + 'enable' => true, + 'display-settings' => [ + 'exclude-php-errors' => [ + \E_USER_DEPRECATED + ], + 'display_errors' => 0, + 'template' => [ + 'layout' => 'layout/layout', + 'view' => 'error-hero-module/error-default' + ], + 'console' => [ + 'message' => 'We have encountered a problem and we can not fulfill your request. An error report has been generated and sent to the support team and someone will attend to this problem urgently. Please try again later. Thank you for your patience.', + ], + + ], + 'logging-settings' => [ + 'same-error-log-time-range' => 86400, + ], + 'email-notification-settings' => [ + 'enable' => false, + 'mail-message' => 'YourMailMessageService', + 'mail-transport' => 'YourMailTransportService', + 'email-from' => 'Sender Name ', + 'email-to-send' => [ + 'developer1@foo.com', + 'developer2@foo.com', + ], + ], + ]; + + $middleware = new Expressive( + $errorHeroModuleLocalConfig, + $logging, + $this->renderer + ); + + allow('property_exists')->toBeCalled()->with($middleware, 'request')->andReturn(true); + allow('property_exists')->toBeCalled()->with($middleware, 'mvcEvent')->andReturn(false); + + $request = & Closure::bind(function & ($middleware) { + return $middleware->request; + }, null, $middleware)($middleware); + $request = new ServerRequest( + [], + [], + new Uri('http://example.com'), + 'GET', + 'php://memory', + [], + [], + [], + '', + '1.2' + ); + + expect($middleware->execOnShutdown())->toBeNull(); + + }); + + + }); + describe('->phpErrorHandler()', function () { it('error_reporting() returns 0', function () { allow('error_reporting')->tobeCalled()->andReturn(0); $actual = $this->middleware->phpErrorHandler(2, 'mkdir(): File exists', 'file.php', 6); - // null means use default next(req, resp) + // null means use default $handler->handle($request) expect($actual)->toBeNull(); }); + it('call error_get_last() and return nothing on result with "Uncaught" prefix', function () { + + allow('error_get_last')->toBeCalled()->andReturn([ + 'message' => 'Uncaught' + ]); + expect($this->middleware->execOnShutdown())->toBeNull(); + + }); + it('exclude error type and match', function () { $actual = $this->middleware->phpErrorHandler(\E_USER_DEPRECATED, 'deprecated', 'file.php', 1); - // null means use default next(req, resp) + // null means use default $handler->handle($request) expect($actual)->toBeNull(); - expect(error_reporting())->toBe(E_ALL | E_STRICT); - expect(ini_get('display_errors'))->toBe("0"); + expect(\error_reporting())->toBe(\E_ALL | \E_STRICT); + expect(\ini_get('display_errors'))->toBe("0"); + + }); + + it('throws ErrorException on non excluded php errors', function () { + + $closure = function () { + $this->middleware->phpErrorHandler(\E_WARNING, 'warning', 'file.php', 1); + }; + expect($closure)->toThrow(new \ErrorException('warning', 0, \E_WARNING, 'file.php', 1)); }); diff --git a/spec/Middleware/Routed/Preview/ErrorPreviewActionSpec.php b/spec/Middleware/Routed/Preview/ErrorPreviewActionSpec.php deleted file mode 100644 index b8dad75b..00000000 --- a/spec/Middleware/Routed/Preview/ErrorPreviewActionSpec.php +++ /dev/null @@ -1,73 +0,0 @@ -__invoke()', function () { - - it('throw Exception', function () { - - $closure = function () { - $request = Double::instance(['implements' => ServerRequestInterface::class]); - allow($request)->toReceive('getAttribute')->with('action', 'exception')->andReturn('exception'); - - $response = Double::instance(['implements' => ResponseInterface::class]); - $next = function ($request, $response) {}; - - $this->middleware->__invoke($request, $response, $next); - }; - expect($closure)->toThrow(new \Exception('a sample error preview')); - - }); - - it('Error', function() { - - skipIf(PHP_MAJOR_VERSION < 7); - - try { - $request = Double::instance(['implements' => ServerRequestInterface::class]); - allow($request)->toReceive('getAttribute')->with('action', 'exception')->andReturn('error'); - - $response = Double::instance(['implements' => ResponseInterface::class]); - $next = function ($request, $response) {}; - - $this->middleware->__invoke($request, $response, $next); - } catch (\Throwable $error) { - expect($error)->toBeAnInstanceOf(\Throwable::class); - expect($error->getMessage())->toContain('E_NOTICE'); - } - - }); - - it('PHP7 Error', function() { - - skipIf(PHP_MAJOR_VERSION < 7); - - try { - $request = Double::instance(['implements' => ServerRequestInterface::class]); - allow($request)->toReceive('getAttribute')->with('action', 'exception')->andReturn('php7error'); - - $response = Double::instance(['implements' => ResponseInterface::class]); - $next = function ($request, $response) {}; - - $this->middleware->__invoke($request, $response, $next); - } catch (\Throwable $error) { - expect($error)->toBeAnInstanceOf(\Throwable::class); - expect($error->getMessage())->toContain('error of php 7'); - } - - }); - - }); - -}); diff --git a/src/HeroAutoload.php b/src/HeroAutoload.php deleted file mode 100644 index adc5321e..00000000 --- a/src/HeroAutoload.php +++ /dev/null @@ -1,29 +0,0 @@ -result; + } + + public function execOnShutdown() : void + { + $error = \error_get_last(); + if (! $error) { + return; + } + + if (0 === strpos($error['message'], 'Uncaught')) { return; } - $this->phpErrorHandler($error['type'], $error['message'], $error['file'], $error['line']); + $errorException = new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']); + try { + if (property_exists($this, 'request')) { + $result = $this->exceptionError($errorException, $this->request); + $this->result = (string) $result->getBody(); + + return; + } + + if (property_exists($this, 'mvcEvent')) { + ob_start(); + $this->mvcEvent->setParam('exception', $errorException); + $this->exceptionError($this->mvcEvent); + $this->result = ob_get_clean(); + + return; + } + } catch (ErrorException $t) { + throw $t; + } } /** - * @param int $errorType - * @param string $errorMessage - * @param string $errorFile - * @param int $errorLine - * * @throws ErrorException when php error happen and error type is not excluded in the config - * - * @return mixed */ - public function phpErrorHandler($errorType, $errorMessage, $errorFile, $errorLine) + public function phpErrorHandler(int $errorType, string $errorMessage, string $errorFile, int $errorLine) : void { - if (! (error_reporting() & $errorType)) { + if (! (\error_reporting() & $errorType)) { return; } if (! $this->errorHeroModuleConfig['display-settings']['display_errors']) { \error_reporting(\E_ALL | \E_STRICT); - \ini_set('display_errors', 0); + \ini_set('display_errors', '0'); } if (\in_array($errorType, $this->errorHeroModuleConfig['display-settings']['exclude-php-errors'])) { return; } - throw new ErrorException($errorMessage, 500, $errorType, $errorFile, $errorLine); + throw new ErrorException($errorMessage, 0, $errorType, $errorFile, $errorLine); } } diff --git a/src/Listener/Mvc.php b/src/Listener/Mvc.php index d890c75d..363a1ee2 100644 --- a/src/Listener/Mvc.php +++ b/src/Listener/Mvc.php @@ -22,6 +22,9 @@ class Mvc extends AbstractListenerAggregate { use HeroTrait; + /** @var MvcEvent */ + private $mvcEvent; + /** * @param array $errorHeroModuleConfig * @param Logging $logging @@ -64,9 +67,11 @@ public function attach(EventManagerInterface $events, $priority = 1) */ public function phpError(Event $e) { + $this->mvcEvent = $e; + + \ob_start([$this, 'phpFatalErrorHandler']); \register_shutdown_function([$this, 'execOnShutdown']); \set_error_handler([$this, 'phpErrorHandler']); - \spl_autoload_register([HeroAutoload::class, 'handle']); } /** diff --git a/src/Middleware/Expressive.php b/src/Middleware/Expressive.php index eec51815..d7f8e7cf 100644 --- a/src/Middleware/Expressive.php +++ b/src/Middleware/Expressive.php @@ -74,9 +74,9 @@ public function __invoke(ServerRequestInterface $request, ResponseInterface $res */ public function phpError() { + \ob_start([$this, 'phpFatalErrorHandler']); \register_shutdown_function([$this, 'execOnShutdown']); \set_error_handler([$this, 'phpErrorHandler']); - \spl_autoload_register([HeroAutoload::class, 'handle']); } /** From db3678ae5d6ff3238019a1e5b2e616109721dd9f Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 03:36:00 +0700 Subject: [PATCH 17/40] remove type hint --- src/HeroTrait.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HeroTrait.php b/src/HeroTrait.php index d00cad88..794d2196 100644 --- a/src/HeroTrait.php +++ b/src/HeroTrait.php @@ -42,7 +42,7 @@ public function phpFatalErrorHandler($buffer) return $this->result; } - public function execOnShutdown() : void + public function execOnShutdown() { $error = \error_get_last(); if (! $error) { @@ -78,7 +78,7 @@ public function execOnShutdown() : void /** * @throws ErrorException when php error happen and error type is not excluded in the config */ - public function phpErrorHandler(int $errorType, string $errorMessage, string $errorFile, int $errorLine) : void + public function phpErrorHandler($errorType, $errorMessage, $errorFile, $errorLine) { if (! (\error_reporting() & $errorType)) { return; From 4e630ac27d266695b76e3c54ef4d072ca736f639 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 03:46:53 +0700 Subject: [PATCH 18/40] remove unneeded http_response_code(500) --- src/HeroTrait.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/HeroTrait.php b/src/HeroTrait.php index 794d2196..319fc2e4 100644 --- a/src/HeroTrait.php +++ b/src/HeroTrait.php @@ -38,7 +38,6 @@ public function phpFatalErrorHandler($buffer) return $buffer; } - http_response_code(500); return $this->result; } From 1cbf1d816d6e1d46c3d118dd2c336857f881c2a9 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 04:03:53 +0700 Subject: [PATCH 19/40] cov --- spec/Listener/MvcSpec.php | 14 ++++++++++++++ spec/Middleware/ExpressiveSpec.php | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/spec/Listener/MvcSpec.php b/spec/Listener/MvcSpec.php index 09128cb9..8bed9848 100644 --- a/spec/Listener/MvcSpec.php +++ b/spec/Listener/MvcSpec.php @@ -460,8 +460,22 @@ ]; + + $resolver = new Resolver\AggregateResolver(); + + $map = new Resolver\TemplateMapResolver([ + 'error' => __DIR__ . '/../Fixture/view/error.phtml', + 'layout/layout' => __DIR__ . '/../Fixture/view/layout/layout.phtml', + 'error-hero-module/error-default' => __DIR__ . '/../Fixture/view/error-hero-module/error-default.phtml', + ]); + $resolver->attach($map); + $this->renderer->setResolver($resolver); + $logging = new Logging( $logger, + 'http://serverUrl', + Double::instance(['extends' => Request::class, 'methods' => '__construct']), + '/', $this->config, $logWritersConfig, null, diff --git a/spec/Middleware/ExpressiveSpec.php b/spec/Middleware/ExpressiveSpec.php index c5a2b45a..77765f19 100644 --- a/spec/Middleware/ExpressiveSpec.php +++ b/spec/Middleware/ExpressiveSpec.php @@ -9,6 +9,7 @@ use Zend\Db\Adapter\Adapter; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequest; +use Zend\Diactoros\Uri; use Zend\Expressive\ZendView\ZendViewRenderer; use Zend\Http\PhpEnvironment\Request; use Zend\Log\Logger; @@ -698,6 +699,9 @@ $logging = new Logging( $logger, + 'http://serverUrl', + null, + '/', $this->config, $logWritersConfig, null, @@ -837,6 +841,9 @@ $logging = new Logging( $logger, + 'http://serverUrl', + null, + '/', $this->config, $logWritersConfig, null, From 22335222fc19801109b95aee64cb2b836fe93df1 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 04:12:54 +0700 Subject: [PATCH 20/40] phpstan --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a5ee19eb..74b6377d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ before_script: - mysql -u root errorheromodule < spec/Fixture/data/sql.sql - composer self-update - composer install --prefer-source --no-interaction - - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then composer global require --dev phpstan/phpstan:^0.8; fi + - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then composer global require --dev phpstan/phpstan:~0.8.0; fi - composer dump-autoload -o before_install: From a76e45d5c47ed51c78bf2b1cad8fdb9bebba9870 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 04:21:45 +0700 Subject: [PATCH 21/40] lower phpstan --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 74b6377d..7d403324 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,14 +13,14 @@ before_script: - mysql -u root errorheromodule < spec/Fixture/data/sql.sql - composer self-update - composer install --prefer-source --no-interaction - - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then composer global require --dev phpstan/phpstan:~0.8.0; fi + - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then composer global require --dev phpstan/phpstan:~0.7.0; fi - composer dump-autoload -o before_install: - if [[ $TRAVIS_PHP_VERSION = 7.2 ]]; then git clone git://github.com/xdebug/xdebug.git && cd xdebug && phpize && ./configure --enable-xdebug && make && make install && echo "zend_extension = xdebug.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini && cd ..; fi script: - - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then ~/.composer/vendor/bin/phpstan analyse src/ --level=7; fi + - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then ~/.composer/vendor/bin/phpstan analyse src/ --level=5; fi - if [[ $TRAVIS_PHP_VERSION != "nightly" ]]; then bin/kahlan --coverage=4 --reporter=verbose --clover=build/logs/clover.xml; fi - if [[ $TRAVIS_PHP_VERSION = "nightly" ]]; then bin/kahlan; fi - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then bin/coveralls -v --exclude-no-stmt; fi From 0dfb93db4ee56575c9ef98de0167a0af6391c6e4 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 04:22:32 +0700 Subject: [PATCH 22/40] register php 5.6 to allow failures --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7d403324..185b94a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,4 +31,5 @@ notifications: matrix: fast_finish: true allow_failures: + - php: 5.6 - php: nightly From 7e6a54e9da63bca85289ad6a29384ca70e6c59f5 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 04:35:17 +0700 Subject: [PATCH 23/40] E_ERROR check --- src/HeroTrait.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HeroTrait.php b/src/HeroTrait.php index 319fc2e4..c8b2ed05 100644 --- a/src/HeroTrait.php +++ b/src/HeroTrait.php @@ -34,7 +34,7 @@ public function phpFatalErrorHandler($buffer) return $buffer; } - if (0 === strpos($error['message'], 'Uncaught')) { + if (0 === strpos($error['message'], 'Uncaught') && $error['type'] !== \E_ERROR) { return $buffer; } @@ -48,7 +48,7 @@ public function execOnShutdown() return; } - if (0 === strpos($error['message'], 'Uncaught')) { + if (0 === strpos($error['message'], 'Uncaught') && $error['type'] !== \E_ERROR) { return; } From cca3c38cccd3c4d4aabe5487c3f36821c39971ac Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 04:40:05 +0700 Subject: [PATCH 24/40] lower --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 185b94a2..cfa3016b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ before_install: - if [[ $TRAVIS_PHP_VERSION = 7.2 ]]; then git clone git://github.com/xdebug/xdebug.git && cd xdebug && phpize && ./configure --enable-xdebug && make && make install && echo "zend_extension = xdebug.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini && cd ..; fi script: - - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then ~/.composer/vendor/bin/phpstan analyse src/ --level=5; fi + - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then ~/.composer/vendor/bin/phpstan analyse src/ --level=1; fi - if [[ $TRAVIS_PHP_VERSION != "nightly" ]]; then bin/kahlan --coverage=4 --reporter=verbose --clover=build/logs/clover.xml; fi - if [[ $TRAVIS_PHP_VERSION = "nightly" ]]; then bin/kahlan; fi - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then bin/coveralls -v --exclude-no-stmt; fi From 1f44123a9eba87df41a89735b8635f39f8218033 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 04:44:03 +0700 Subject: [PATCH 25/40] test --- spec/Listener/MvcSpec.php | 4 +++- spec/Middleware/ExpressiveSpec.php | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/Listener/MvcSpec.php b/spec/Listener/MvcSpec.php index 8bed9848..d1ebaa7b 100644 --- a/spec/Listener/MvcSpec.php +++ b/spec/Listener/MvcSpec.php @@ -360,6 +360,7 @@ allow('error_get_last')->toBeCalled()->andReturn([ 'message' => 'Uncaught', + 'type' => 3, ]); expect($this->listener->phpFatalErrorHandler('Uncaught'))->toBe('Uncaught'); @@ -388,7 +389,8 @@ it('call error_get_last() and return nothing on result with "Uncaught" prefix', function () { allow('error_get_last')->toBeCalled()->andReturn([ - 'message' => 'Uncaught' + 'message' => 'Uncaught', + 'type' => 3, ]); expect($this->listener->execOnShutdown())->toBeNull(); diff --git a/spec/Middleware/ExpressiveSpec.php b/spec/Middleware/ExpressiveSpec.php index 77765f19..02603e19 100644 --- a/spec/Middleware/ExpressiveSpec.php +++ b/spec/Middleware/ExpressiveSpec.php @@ -606,6 +606,7 @@ allow('error_get_last')->toBeCalled()->andReturn([ 'message' => 'Uncaught', + 'type' => 3, ]); expect($this->middleware->phpFatalErrorHandler('Uncaught'))->toBe('Uncaught'); @@ -927,7 +928,8 @@ it('call error_get_last() and return nothing on result with "Uncaught" prefix', function () { allow('error_get_last')->toBeCalled()->andReturn([ - 'message' => 'Uncaught' + 'message' => 'Uncaught', + 'type' => 3, ]); expect($this->middleware->execOnShutdown())->toBeNull(); From 122e76b72195e6280838da6d9586f55dae38fa1c Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 04:49:25 +0700 Subject: [PATCH 26/40] remove phpstan --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index cfa3016b..5d298cbb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,14 +13,12 @@ before_script: - mysql -u root errorheromodule < spec/Fixture/data/sql.sql - composer self-update - composer install --prefer-source --no-interaction - - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then composer global require --dev phpstan/phpstan:~0.7.0; fi - composer dump-autoload -o before_install: - if [[ $TRAVIS_PHP_VERSION = 7.2 ]]; then git clone git://github.com/xdebug/xdebug.git && cd xdebug && phpize && ./configure --enable-xdebug && make && make install && echo "zend_extension = xdebug.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini && cd ..; fi script: - - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then ~/.composer/vendor/bin/phpstan analyse src/ --level=1; fi - if [[ $TRAVIS_PHP_VERSION != "nightly" ]]; then bin/kahlan --coverage=4 --reporter=verbose --clover=build/logs/clover.xml; fi - if [[ $TRAVIS_PHP_VERSION = "nightly" ]]; then bin/kahlan; fi - if [[ $TRAVIS_PHP_VERSION = 7.1 ]]; then bin/coveralls -v --exclude-no-stmt; fi From f2ddb48528c1a9c91a20b83dae0874901c0841ac Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 13:33:04 +0700 Subject: [PATCH 27/40] clean up throw --- src/HeroTrait.php | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/HeroTrait.php b/src/HeroTrait.php index c8b2ed05..cae65571 100644 --- a/src/HeroTrait.php +++ b/src/HeroTrait.php @@ -34,7 +34,7 @@ public function phpFatalErrorHandler($buffer) return $buffer; } - if (0 === strpos($error['message'], 'Uncaught') && $error['type'] !== \E_ERROR) { + if (0 === strpos($error['message'], 'Uncaught')) { return $buffer; } @@ -48,29 +48,25 @@ public function execOnShutdown() return; } - if (0 === strpos($error['message'], 'Uncaught') && $error['type'] !== \E_ERROR) { + if (0 === strpos($error['message'], 'Uncaught')) { return; } $errorException = new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']); - try { - if (property_exists($this, 'request')) { - $result = $this->exceptionError($errorException, $this->request); - $this->result = (string) $result->getBody(); - - return; - } - - if (property_exists($this, 'mvcEvent')) { - ob_start(); - $this->mvcEvent->setParam('exception', $errorException); - $this->exceptionError($this->mvcEvent); - $this->result = ob_get_clean(); - - return; - } - } catch (ErrorException $t) { - throw $t; + if (property_exists($this, 'request')) { + $result = $this->exceptionError($errorException, $this->request); + $this->result = (string) $result->getBody(); + + return; + } + + if (property_exists($this, 'mvcEvent')) { + ob_start(); + $this->mvcEvent->setParam('exception', $errorException); + $this->exceptionError($this->mvcEvent); + $this->result = ob_get_clean(); + + return; } } From 4eecb99d5fec0c481ecf87e4090edb23b331aa34 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 13:52:50 +0700 Subject: [PATCH 28/40] returns buffer on empty result --- src/HeroTrait.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/HeroTrait.php b/src/HeroTrait.php index cae65571..fd733876 100644 --- a/src/HeroTrait.php +++ b/src/HeroTrait.php @@ -38,6 +38,10 @@ public function phpFatalErrorHandler($buffer) return $buffer; } + if ($this->result === '') { + return $buffer; + } + return $this->result; } From dc55024c538f79f559399b34ddfa9b7b8cd110fe Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 14:02:09 +0700 Subject: [PATCH 29/40] test --- spec/Listener/MvcSpec.php | 11 +++++++++-- spec/Middleware/ExpressiveSpec.php | 21 +++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/spec/Listener/MvcSpec.php b/spec/Listener/MvcSpec.php index d1ebaa7b..9c98de4b 100644 --- a/spec/Listener/MvcSpec.php +++ b/spec/Listener/MvcSpec.php @@ -366,12 +366,19 @@ }); - it('returns result property value on error not has "Uncaught" prefix', function () { + it('returns result property value on error not has "Uncaught" prefix and result has value', function () { allow('error_get_last')->toBeCalled()->andReturn([ 'message' => 'Fatal', ]); - expect($this->listener->phpFatalErrorHandler('Fatal'))->toBe(''); + + $listener = & $this->listener; + $result = & Closure::bind(function & ($listener) { + return $listener->result; + }, null, $listener)($listener); + $result = 'Fatal error'; + + expect($this->listener->phpFatalErrorHandler('Fatal'))->toBe('Fatal error'); }); diff --git a/spec/Middleware/ExpressiveSpec.php b/spec/Middleware/ExpressiveSpec.php index 02603e19..8c1ba32c 100644 --- a/spec/Middleware/ExpressiveSpec.php +++ b/spec/Middleware/ExpressiveSpec.php @@ -612,12 +612,29 @@ }); - it('returns result property value on error not has "Uncaught" prefix', function () { + it('returns message value on error not has "Uncaught" prefix and result is empty', function () { allow('error_get_last')->toBeCalled()->andReturn([ 'message' => 'Fatal', ]); - expect($this->middleware->phpFatalErrorHandler('Fatal'))->toBe(''); + + expect($this->middleware->phpFatalErrorHandler('Fatal'))->toBe('Fatal'); + + }); + + it('returns result property value on error not has "Uncaught" prefix and result has value', function () { + + allow('error_get_last')->toBeCalled()->andReturn([ + 'message' => 'Fatal', + ]); + + $middleware = & $this->middleware; + $result = & Closure::bind(function & ($middleware) { + return $middleware->result; + }, null, $middleware)($middleware); + $result = 'Fatal error'; + + expect($this->middleware->phpFatalErrorHandler('Fatal'))->toBe('Fatal error'); }); From 60a1440d84b0f2a25a8fd7f7b71cc23fdff46070 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 14:03:16 +0700 Subject: [PATCH 30/40] test --- spec/Listener/MvcSpec.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/Listener/MvcSpec.php b/spec/Listener/MvcSpec.php index 9c98de4b..8920d1eb 100644 --- a/spec/Listener/MvcSpec.php +++ b/spec/Listener/MvcSpec.php @@ -366,6 +366,16 @@ }); + it('returns message value on error not has "Uncaught" prefix and result is empty', function () { + + allow('error_get_last')->toBeCalled()->andReturn([ + 'message' => 'Fatal', + ]); + + expect($this->listener->phpFatalErrorHandler('Fatal'))->toBe('Fatal'); + + }); + it('returns result property value on error not has "Uncaught" prefix and result has value', function () { allow('error_get_last')->toBeCalled()->andReturn([ From 42e96e8e44e3eeb676cf40550cf318a748d772af Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 14:35:35 +0700 Subject: [PATCH 31/40] clean up --- spec/Listener/MvcSpec.php | 16 ---------------- spec/Middleware/ExpressiveSpec.php | 10 ---------- 2 files changed, 26 deletions(-) diff --git a/spec/Listener/MvcSpec.php b/spec/Listener/MvcSpec.php index 8920d1eb..0d3d3e0b 100644 --- a/spec/Listener/MvcSpec.php +++ b/spec/Listener/MvcSpec.php @@ -376,22 +376,6 @@ }); - it('returns result property value on error not has "Uncaught" prefix and result has value', function () { - - allow('error_get_last')->toBeCalled()->andReturn([ - 'message' => 'Fatal', - ]); - - $listener = & $this->listener; - $result = & Closure::bind(function & ($listener) { - return $listener->result; - }, null, $listener)($listener); - $result = 'Fatal error'; - - expect($this->listener->phpFatalErrorHandler('Fatal'))->toBe('Fatal error'); - - }); - }); describe('->execOnShutdown()', function () { diff --git a/spec/Middleware/ExpressiveSpec.php b/spec/Middleware/ExpressiveSpec.php index 8c1ba32c..ec1ba4e9 100644 --- a/spec/Middleware/ExpressiveSpec.php +++ b/spec/Middleware/ExpressiveSpec.php @@ -612,16 +612,6 @@ }); - it('returns message value on error not has "Uncaught" prefix and result is empty', function () { - - allow('error_get_last')->toBeCalled()->andReturn([ - 'message' => 'Fatal', - ]); - - expect($this->middleware->phpFatalErrorHandler('Fatal'))->toBe('Fatal'); - - }); - it('returns result property value on error not has "Uncaught" prefix and result has value', function () { allow('error_get_last')->toBeCalled()->andReturn([ From 94a1644f979a527219d588df3de52855b4b68bff Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 21 Sep 2018 17:08:09 +0700 Subject: [PATCH 32/40] move display_errors check to early --- composer.json | 1 + src/HeroTrait.php | 5 ----- src/Listener/Mvc.php | 5 +++++ src/Middleware/Expressive.php | 5 +++++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 02e6fcd7..1b88e97a 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ ], "require": { "php": "^5.6|^7.0", + "http-interop/http-middleware": "^0.5.0", "seld/jsonlint": "^1.5", "zendframework/zend-console": "^2.5", "zendframework/zend-db": "^2.5", diff --git a/src/HeroTrait.php b/src/HeroTrait.php index fd733876..2fb476aa 100644 --- a/src/HeroTrait.php +++ b/src/HeroTrait.php @@ -83,11 +83,6 @@ public function phpErrorHandler($errorType, $errorMessage, $errorFile, $errorLin return; } - if (! $this->errorHeroModuleConfig['display-settings']['display_errors']) { - \error_reporting(\E_ALL | \E_STRICT); - \ini_set('display_errors', '0'); - } - if (\in_array($errorType, $this->errorHeroModuleConfig['display-settings']['exclude-php-errors'])) { return; } diff --git a/src/Listener/Mvc.php b/src/Listener/Mvc.php index 363a1ee2..f57bad6f 100644 --- a/src/Listener/Mvc.php +++ b/src/Listener/Mvc.php @@ -69,6 +69,11 @@ public function phpError(Event $e) { $this->mvcEvent = $e; + if (! $this->errorHeroModuleConfig['display-settings']['display_errors']) { + \error_reporting(\E_ALL | \E_STRICT); + \ini_set('display_errors', '0'); + } + \ob_start([$this, 'phpFatalErrorHandler']); \register_shutdown_function([$this, 'execOnShutdown']); \set_error_handler([$this, 'phpErrorHandler']); diff --git a/src/Middleware/Expressive.php b/src/Middleware/Expressive.php index d7f8e7cf..00ce6f60 100644 --- a/src/Middleware/Expressive.php +++ b/src/Middleware/Expressive.php @@ -74,6 +74,11 @@ public function __invoke(ServerRequestInterface $request, ResponseInterface $res */ public function phpError() { + if (! $this->errorHeroModuleConfig['display-settings']['display_errors']) { + \error_reporting(\E_ALL | \E_STRICT); + \ini_set('display_errors', '0'); + } + \ob_start([$this, 'phpFatalErrorHandler']); \register_shutdown_function([$this, 'execOnShutdown']); \set_error_handler([$this, 'phpErrorHandler']); From 7ea23c5e8456fffec52899bcf843c99545dc26f1 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 24 Sep 2018 16:50:39 +0700 Subject: [PATCH 33/40] add ip address to request data information for http request --- README.md | 2 +- spec/Handler/Formatter/JsonSpec.php | 14 +++++++++++--- spec/Handler/Writer/MailSpec.php | 2 ++ src/Handler/Logging.php | 3 +++ src/Listener/Mvc.php | 1 - src/Middleware/Expressive.php | 2 -- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 112a109c..3ff7e3fd 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Features - [x] Handle only once log error for same error per configured time range. - [x] Set default page (web access) or default message (console access) for error if configured 'display_errors' = 0. - [x] Set default content when request is XMLHttpRequest via 'ajax' configuration. -- [x] Provide request information ( http method, raw data, query data, files data, and cookie data ). +- [x] Provide request information ( http method, raw data, query data, files data, cookie data, and ip address). - [x] Send Mail - [x] many receivers to listed configured email - [x] with include $_FILES into attachments on upload error. diff --git a/spec/Handler/Formatter/JsonSpec.php b/spec/Handler/Formatter/JsonSpec.php index b2609e41..7e1b989b 100644 --- a/spec/Handler/Formatter/JsonSpec.php +++ b/spec/Handler/Formatter/JsonSpec.php @@ -45,16 +45,24 @@ 'raw_data' => '', 'files_data' => [], 'cookie_data' => [], + 'ip_address' => '10.1.1.1', ], ], ]; - expect('json_encode')->toBeCalled(); $formatter = new Json(); - $actual = $formatter->format($event); + expect('json_encode')->toBeCalled(); + $actualOld = $formatter->format($event); - expect($actual)->toBe("{\n \"timestamp\": \"2016-12-30T00:42:49+07:00\",\n \"priority\": 3,\n \"priorityName\": \"ERR\",\n \"message\": \"1: a sample error preview\",\n \"extra\": {\n \"url\": \"http://localhost/error-preview?foo=bar&page=1\",\n \"file\": \"/path/to/app/vendor/samsonasik/error-hero-module/src/Controller/ErrorPreviewController.php\",\n \"line\": 11,\n \"error_type\": \"Exception\",\n \"trace\": \"#0 /path/to/app/vendor/zendframework/zend-mvc/src/Controller/AbstractActionController.php(78): ErrorHeroModule\\\\Controller\\\\ErrorPreviewController->exceptionAction()\n #1 /path/to/app/vendor/zendframework/zend-eventmanager/src/EventManager.php(322): Zend\\\\Mvc\\\\Controller\\\\AbstractActionController->onDispatch(Object(Zend\\\\Mvc\\\\MvcEvent))\n #2 /path/to/app/vendor/zendframework/zend-eventmanager/src/EventManager.php(179): Zend\\\\EventManager\\\\EventManager->triggerListeners(Object(Zend\\\\Mvc\\\\MvcEvent), Object(Closure))\n #3 /path/to/app/vendor/zendframework/zend-mvc/src/Controller/AbstractController.php(105): Zend\\\\EventManager\\\\EventManager->triggerEventUntil(Object(Closure), Object(Zend\\\\Mvc\\\\MvcEvent))\n #4 /path/to/app/vendor/zendframework/zend-mvc/src/DispatchListener.php(119): Zend\\\\Mvc\\\\Controller\\\\AbstractController->dispatch(Object(Zend\\\\Http\\\\PhpEnvironment\\\\Request), Object(Zend\\\\Http\\\\PhpEnvironment\\\\Response))\n #5 /path/to/app/vendor/zendframework/zend-eventmanager/src/EventManager.php(322): Zend\\\\Mvc\\\\DispatchListener->onDispatch(Object(Zend\\\\Mvc\\\\MvcEvent))\n #6 /path/to/app/vendor/zendframework/zend-eventmanager/src/EventManager.php(179): Zend\\\\EventManager\\\\EventManager->triggerListeners(Object(Zend\\\\Mvc\\\\MvcEvent), Object(Closure))\n #7 /path/to/app/vendor/zendframework/zend-mvc/src/Application.php(332): Zend\\\\EventManager\\\\EventManager->triggerEventUntil(Object(Closure), Object(Zend\\\\Mvc\\\\MvcEvent))\n #8 /path/to/app/public/index.php(40): Zend\\\\Mvc\\\\Application->run()\n #9 {main}\",\n \"request_data\": {\n \"query\": {\n \"foo\": \"bar\",\n \"page\": \"1\"\n },\n \"request_method\": \"GET\",\n \"body_data\": [],\n \"raw_data\": \"\",\n \"files_data\": [],\n \"cookie_data\": []\n }\n }\n}"); + // idempotent format call will use old timestamp + $event['timestamp'] = DateTime::__set_state([ + 'date' => '2016-12-30 00:42:55.558706', + 'timezone_type' => 3, + 'timezone' => 'Asia/Jakarta', + ]); + $actualNew = $formatter->format($event); + expect($actualNew)->toBe($actualOld); }); diff --git a/spec/Handler/Writer/MailSpec.php b/spec/Handler/Writer/MailSpec.php index 1f87941a..e965a211 100644 --- a/spec/Handler/Writer/MailSpec.php +++ b/spec/Handler/Writer/MailSpec.php @@ -33,6 +33,7 @@ ], ], 'cookie_data' => [], + 'ip_address' => '10.1.1.1', ] ); }); @@ -90,6 +91,7 @@ ], ], 'cookie_data' => [], + 'ip_address' => '10.1.1.1', ] ); diff --git a/src/Handler/Logging.php b/src/Handler/Logging.php index 409637b1..ed75ac45 100644 --- a/src/Handler/Logging.php +++ b/src/Handler/Logging.php @@ -9,6 +9,7 @@ use Psr\Http\Message\ServerRequestInterface; use RuntimeException; use Zend\Console\Request as ConsoleRequest; +use Zend\Http\PhpEnvironment\RemoteAddress; use Zend\Http\Request as HttpRequest; use Zend\Log\Logger; use Zend\Log\Writer\Db; @@ -180,6 +181,7 @@ private function getRequestData() $cookie_data = []; } $cookie_data = (array) $cookie_data; + $ip_address = (new RemoteAddress())->getIpAddress(); return [ 'query' => $query, @@ -188,6 +190,7 @@ private function getRequestData() 'raw_data' => $raw_data, 'files_data' => $files_data, 'cookie_data' => $cookie_data, + 'ip_address' => $ip_address, ]; } diff --git a/src/Listener/Mvc.php b/src/Listener/Mvc.php index f57bad6f..3ad5b35e 100644 --- a/src/Listener/Mvc.php +++ b/src/Listener/Mvc.php @@ -3,7 +3,6 @@ namespace ErrorHeroModule\Listener; use ErrorHeroModule\Handler\Logging; -use ErrorHeroModule\HeroAutoload; use ErrorHeroModule\HeroTrait; use Seld\JsonLint\JsonParser; use Zend\Console\Console; diff --git a/src/Middleware/Expressive.php b/src/Middleware/Expressive.php index 00ce6f60..ff8e08c5 100644 --- a/src/Middleware/Expressive.php +++ b/src/Middleware/Expressive.php @@ -4,7 +4,6 @@ use Error; use ErrorHeroModule\Handler\Logging; -use ErrorHeroModule\HeroAutoload; use ErrorHeroModule\HeroTrait; use Exception; use Psr\Http\Message\ResponseInterface; @@ -13,7 +12,6 @@ use Seld\JsonLint\JsonParser; use Zend\Diactoros\Response; use Zend\Diactoros\Response\HtmlResponse; -use Zend\Expressive\Application; use Zend\Expressive\Template\TemplateRendererInterface; use Zend\View\Model\ViewModel; From ea2bc2b34bd632be198f911ddb36ee4ac541fa1c Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 11 Oct 2018 08:29:42 +0700 Subject: [PATCH 34/40] move require http-interop/http-middleware to require-dev --- composer.json | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/composer.json b/composer.json index 1b88e97a..1abe4dec 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,21 @@ "name": "samsonasik/error-hero-module", "type": "library", "description": "A Hero for your ZF2 and ZF3 application to trap php errors & exceptions", - "keywords": ["zf2", "zf3", "error", "expressive", "hero", "log", "logger", "logging", "mail", "db", "doctrine", "handler", "psr7"], + "keywords": [ + "zf2", + "zf3", + "error", + "expressive", + "hero", + "log", + "logger", + "logging", + "mail", + "db", + "doctrine", + "handler", + "psr7" + ], "homepage": "https://github.com/samsonasik/ErrorHeroModule", "license": "MIT", "authors": [ @@ -14,29 +28,38 @@ } ], "require": { - "php": "^5.6|^7.0", - "http-interop/http-middleware": "^0.5.0", + "php": "^5.6 || ^7.0", "seld/jsonlint": "^1.5", "zendframework/zend-console": "^2.5", "zendframework/zend-db": "^2.5", "zendframework/zend-log": "^2.5", "zendframework/zend-mail": "^2.5", - "zendframework/zend-psr7bridge": "^0.2.2|^1.0", - "zendframework/zend-servicemanager": "^2.5|^3.0", + "zendframework/zend-psr7bridge": "^0.2.2 || ^1.0", + "zendframework/zend-servicemanager": "^2.5 || ^3.0", "zendframework/zend-text": "^2.5" }, "require-dev": { "doctrine/doctrine-orm-module": "^1.1", + "http-interop/http-middleware": "^0.5.0", "kahlan/kahlan": "^3.0.0", "satooshi/php-coveralls": "^1.0", - "zendframework/zend-expressive": "^1.1|^2.0", + "zendframework/zend-expressive": "^1.1 || ^2.0", "zendframework/zend-expressive-zendviewrenderer": "^1.4", - "zendframework/zend-mvc": "^2.5|^3.0", + "zendframework/zend-mvc": "^2.5 || ^3.0", "zendframework/zend-mvc-console": "^1.1" }, "suggest": { "zendframework/zend-mvc-console": "^1.1 for zend-mvc ^3.0 usage to be able to use Console Controller" }, + "config": { + "bin-dir": "bin", + "sort-packages": true + }, + "extra": { + "zf": { + "module": "ErrorHeroModule" + } + }, "autoload": { "psr-4": { "ErrorHeroModule\\": "src/" @@ -46,14 +69,5 @@ "psr-4": { "ErrorHeroModule\\Spec\\": "spec/" } - }, - "config": { - "bin-dir": "bin", - "sort-packages": true - }, - "extra": { - "zf": { - "module": "ErrorHeroModule" - } } } From 1730d840310f766b42743613bf6aafed306624e1 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 17 Jan 2019 15:43:14 +0700 Subject: [PATCH 35/40] ensure \ob_end_flush() while ob_get_level() > 0 before \ob_start() to flush and turn off existing active output buffering if any. --- src/Listener/Mvc.php | 4 ++++ src/Middleware/Expressive.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Listener/Mvc.php b/src/Listener/Mvc.php index 3ad5b35e..b3f7eccf 100644 --- a/src/Listener/Mvc.php +++ b/src/Listener/Mvc.php @@ -73,6 +73,10 @@ public function phpError(Event $e) \ini_set('display_errors', '0'); } + while (\ob_get_level() > 0) { + \ob_end_flush(); + } + \ob_start([$this, 'phpFatalErrorHandler']); \register_shutdown_function([$this, 'execOnShutdown']); \set_error_handler([$this, 'phpErrorHandler']); diff --git a/src/Middleware/Expressive.php b/src/Middleware/Expressive.php index ff8e08c5..c50d2b15 100644 --- a/src/Middleware/Expressive.php +++ b/src/Middleware/Expressive.php @@ -77,6 +77,10 @@ public function phpError() \ini_set('display_errors', '0'); } + while (\ob_get_level() > 0) { + \ob_end_flush(); + } + \ob_start([$this, 'phpFatalErrorHandler']); \register_shutdown_function([$this, 'execOnShutdown']); \set_error_handler([$this, 'phpErrorHandler']); From 493899817dbb3bb5d469cb48ba6ae032672b9c39 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 9 Feb 2019 23:10:55 +0700 Subject: [PATCH 36/40] use $request->getHeaderLine(X-Requested-With) === XmlHttpRequest --- src/Middleware/Expressive.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Middleware/Expressive.php b/src/Middleware/Expressive.php index c50d2b15..62e53dac 100644 --- a/src/Middleware/Expressive.php +++ b/src/Middleware/Expressive.php @@ -120,7 +120,8 @@ public function exceptionError($e, $request) */ private function showDefaultViewWhenDisplayErrorSetttingIsDisabled() { - $isXmlHttpRequest = $this->request->hasHeader('X-Requested-With'); + $isXmlHttpRequest = $this->request->hasHeader('X-Requested-With') + && $this->request->getHeaderLine('X-Requested-With') === 'XmlHttpRequest'; if ($isXmlHttpRequest === true && isset($this->errorHeroModuleConfig['display-settings']['ajax']['message']) From 5c83bb4e15c2afd50337c475f293fb7fd1c9b8b4 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 9 Feb 2019 23:35:56 +0700 Subject: [PATCH 37/40] fix php 5.6 test --- spec/Middleware/ExpressiveSpec.php | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/spec/Middleware/ExpressiveSpec.php b/spec/Middleware/ExpressiveSpec.php index ec1ba4e9..5d671b1b 100644 --- a/spec/Middleware/ExpressiveSpec.php +++ b/spec/Middleware/ExpressiveSpec.php @@ -2,10 +2,10 @@ namespace ErrorHeroModule\Spec\Middleware; -use Closure; use ErrorHeroModule\Handler\Logging; use ErrorHeroModule\Middleware\Expressive; use Kahlan\Plugin\Double; +use ReflectionProperty; use Zend\Db\Adapter\Adapter; use Zend\Diactoros\Response; use Zend\Diactoros\ServerRequest; @@ -619,10 +619,9 @@ ]); $middleware = & $this->middleware; - $result = & Closure::bind(function & ($middleware) { - return $middleware->result; - }, null, $middleware)($middleware); - $result = 'Fatal error'; + $r = new ReflectionProperty($middleware, 'result'); + $r->setAccessible(true); + $r->setValue($middleware, 'Fatal error'); expect($this->middleware->phpFatalErrorHandler('Fatal'))->toBe('Fatal error'); @@ -756,10 +755,9 @@ allow('property_exists')->toBeCalled()->with($middleware, 'request')->andReturn(true); allow('property_exists')->toBeCalled()->with($middleware, 'mvcEvent')->andReturn(false); - $request = & Closure::bind(function & ($middleware) { - return $middleware->request; - }, null, $middleware)($middleware); - $request = new ServerRequest( + $r = new ReflectionProperty($middleware, 'request'); + $r->setAccessible(true); + $r->setValue($middleware, new ServerRequest( [], [], new Uri('http://example.com'), @@ -770,7 +768,7 @@ [], '', '1.2' - ); + )); $closure = function () use ($middleware) { $middleware->execOnShutdown(); @@ -898,10 +896,9 @@ allow('property_exists')->toBeCalled()->with($middleware, 'request')->andReturn(true); allow('property_exists')->toBeCalled()->with($middleware, 'mvcEvent')->andReturn(false); - $request = & Closure::bind(function & ($middleware) { - return $middleware->request; - }, null, $middleware)($middleware); - $request = new ServerRequest( + $r = new ReflectionProperty($middleware, 'request'); + $r->setAccessible(true); + $r->setValue($middleware, new ServerRequest( [], [], new Uri('http://example.com'), @@ -912,7 +909,7 @@ [], '', '1.2' - ); + )); expect($middleware->execOnShutdown())->toBeNull(); From 7108bd5f1109301983510598a3059990ebc24816 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 9 Feb 2019 23:43:19 +0700 Subject: [PATCH 38/40] fix php 5.6 test --- spec/Listener/MvcSpec.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/Listener/MvcSpec.php b/spec/Listener/MvcSpec.php index 0d3d3e0b..88992d68 100644 --- a/spec/Listener/MvcSpec.php +++ b/spec/Listener/MvcSpec.php @@ -2,13 +2,13 @@ namespace ErrorHeroModule\Spec\Listener; -use Closure; use ErrorException; use ErrorHeroModule\Handler\Logging; use ErrorHeroModule\Listener\Mvc; use Kahlan\Plugin\Double; use Kahlan\Plugin\Quit; use Kahlan\QuitException; +use ReflectionProperty; use Zend\Console\Console; use Zend\Db\Adapter\Adapter; use Zend\EventManager\EventManagerInterface; @@ -524,11 +524,11 @@ allow('property_exists')->toBeCalled()->with($listener, 'request')->andReturn(false); allow('property_exists')->toBeCalled()->with($listener, 'mvcEvent')->andReturn(true); - $mvcEvent = & Closure::bind(function & ($listener) { - return $listener->mvcEvent; - }, null, $listener)($listener); + $r = new ReflectionProperty($listener, 'mvcEvent'); + $r->setAccessible(true); $mvcEvent = Double::instance(['extends' => MvcEvent::class, 'methods' => '__construct']); allow($mvcEvent)->toReceive('getRequest')->andReturn(new Request()); + $r->setValue($listener, $mvcEvent); expect($listener->execOnShutdown())->toBeNull(); From 7cedc9f0dbfb6140aa473b353badd9f241ace949 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 9 Feb 2019 23:49:10 +0700 Subject: [PATCH 39/40] remove php 5.6 from allow_failures --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5d298cbb..8c992639 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,5 +29,4 @@ notifications: matrix: fast_finish: true allow_failures: - - php: 5.6 - php: nightly From 78820248cda215b521ebb07f70e4e904be5eabe4 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 28 Feb 2019 09:53:13 +0700 Subject: [PATCH 40/40] update zend-uri requirement to use ^2.7 for strtolower host --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1abe4dec..26ab76f7 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,8 @@ "zendframework/zend-mail": "^2.5", "zendframework/zend-psr7bridge": "^0.2.2 || ^1.0", "zendframework/zend-servicemanager": "^2.5 || ^3.0", - "zendframework/zend-text": "^2.5" + "zendframework/zend-text": "^2.5", + "zendframework/zend-uri": "^2.7" }, "require-dev": { "doctrine/doctrine-orm-module": "^1.1",