diff --git a/Application/Component/DebugBarComponent.php b/Application/Component/DebugBarComponent.php index 04b76b1..7433bb7 100644 --- a/Application/Component/DebugBarComponent.php +++ b/Application/Component/DebugBarComponent.php @@ -22,6 +22,7 @@ use D3\DebugBar\Application\Models\Collectors\SmartyCollector; use D3\DebugBar\Application\Models\TimeDataCollectorHandler; use DebugBar\Bridge\DoctrineCollector; use DebugBar\Bridge\MonologCollector; +use DebugBar\DataCollector\ExceptionsCollector; use DebugBar\DataCollector\MemoryCollector; use DebugBar\DataCollector\MessagesCollector; use DebugBar\DataCollector\PhpInfoCollector; @@ -163,6 +164,7 @@ class DebugBarComponent extends BaseController $debugbar->addCollector(new RequestDataCollector()); $debugbar->addCollector(new TimeDataCollector()); $debugbar->addCollector(new MemoryCollector()); + $debugbar->addCollector(new ExceptionsCollector()); // add custom collectors $debugbar->addCollector($this->getOxidShopCollector()); diff --git a/Core/DebugBarExceptionHandler.php b/Core/DebugBarExceptionHandler.php new file mode 100644 index 0000000..26dbd2a --- /dev/null +++ b/Core/DebugBarExceptionHandler.php @@ -0,0 +1,98 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DebugBar\Core; + +use D3\DebugBar\Application\Component\DebugBarComponent; +use DebugBar\DataCollector\ExceptionsCollector; +use DebugBar\DebugBarException; +use OxidEsales\Eshop\Application\Controller\FrontendController; +use OxidEsales\Eshop\Core\Exception\DatabaseException; +use OxidEsales\Eshop\Core\Exception\ExceptionHandler; +use OxidEsales\Eshop\Core\Registry; +use OxidEsales\EshopCommunity\Internal\Framework\Logger\LoggerServiceFactory; +use OxidEsales\EshopCommunity\Internal\Transition\Utility\Context; +use Throwable; + +class DebugBarExceptionHandler +{ + /** + * Handler for uncaught exceptions. + * + * @param Throwable $exception exception object + * @throws DebugBarException + */ + public function handleUncaughtException(Throwable $exception) + { + //dumpvar(__METHOD__.__LINE__); + try { + $debugMode = (bool) \OxidEsales\Eshop\Core\Registry::get(\OxidEsales\Eshop\Core\ConfigFile::class)->getVar('iDebug'); + $defaultExceptionHandler = new ExceptionHandler($debugMode); + $defaultExceptionHandler->writeExceptionToLog($exception); + } catch (Throwable $loggerException) { + /** + * Its not possible to get the logger from the DI container. + * Try again to log original exception (without DI container) in order to show the root cause of a problem. + */ + try { + $loggerServiceFactory = new LoggerServiceFactory(new Context(Registry::getConfig())); + $logger = $loggerServiceFactory->getLogger(); + $logger->error($exception->getTraceAsString()); + } catch (Throwable $throwableWithoutPossibilityToWriteToLogFile) { + // It is not possible to log because e.g. the log file is not writable. + } + } + + global $debugBarSet; + if ($debugBarSet !== 1) { + /** @var FrontendController $activeView */ + $activeView = Registry::getConfig()->getTopActiveView(); + /** @var DebugBarComponent|null $debugBarComponent */ + $debugBarComponent = $activeView->getComponent(DebugBarComponent::class) ?: oxNew(DebugBarComponent::class); + + /** @var ExceptionsCollector $excCollector */ + $excCollector = $debugBarComponent->getDebugBar()->getCollector('exceptions'); + $excCollector->addThrowable($exception); + + echo << + + +HTML; + echo $debugBarComponent->getRenderer()->renderHead(); + $debugBarComponent->addTimelineMessures(); + echo << + +HTML; + $debugBarSet = 1; + echo $debugBarComponent->getRenderer()->render(); + echo << + +HTML; + } + } + + /** + * @param DatabaseException $exception + * @return void + * @throws DebugBarException + */ + public function handleDatabaseException(DatabaseException $exception) + { + $this->handleUncaughtException($exception); + } +} \ No newline at end of file diff --git a/IntelliSenseHelper.php b/IntelliSenseHelper.php index 01b6fa5..9a3cc12 100644 --- a/IntelliSenseHelper.php +++ b/IntelliSenseHelper.php @@ -16,6 +16,11 @@ declare(strict_types=1); namespace D3\DebugBar\Modules\Core { use OxidEsales\Eshop\Core\ShopControl; + use OxidEsales\EshopCommunity\Core\Config; + + class Config_DebugBar_parent extends Config + { + } class ShopControl_DebugBar_parent extends ShopControl { diff --git a/Modules/Core/Config_DebugBar.php b/Modules/Core/Config_DebugBar.php new file mode 100644 index 0000000..e43feeb --- /dev/null +++ b/Modules/Core/Config_DebugBar.php @@ -0,0 +1,29 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DebugBar\Modules\Core; + +use D3\DebugBar\Core\DebugBarExceptionHandler; + +class Config_DebugBar extends Config_DebugBar_parent +{ + /** + * @return DebugBarExceptionHandler + */ + protected function getExceptionHandler() + { + return new DebugBarExceptionHandler(); + } +} \ No newline at end of file diff --git a/Modules/Core/ShopControl_DebugBar.php b/Modules/Core/ShopControl_DebugBar.php index 2444195..2171d9a 100644 --- a/Modules/Core/ShopControl_DebugBar.php +++ b/Modules/Core/ShopControl_DebugBar.php @@ -16,22 +16,78 @@ declare(strict_types=1); namespace D3\DebugBar\Modules\Core; use D3\DebugBar\Application\Component\DebugBarComponent; +use D3\DebugBar\Core\DebugBarExceptionHandler; +use DebugBar\DataCollector\ExceptionsCollector; +use ErrorException; use OxidEsales\Eshop\Application\Controller\FrontendController; use OxidEsales\Eshop\Core\Registry; class ShopControl_DebugBar extends ShopControl_DebugBar_parent { + /** + * @throws ErrorException + */ public function __construct() { - $this->_d3AddDebugBarComponent(); + $this->d3DebugBarSetErrorHandler();; + $this->d3DebugBarSetExceptionHandler(); + $this->d3AddDebugBarComponent(); parent::__construct(); } /** * @return void + * @throws ErrorException */ - protected function _d3AddDebugBarComponent(): void + public function d3DebugBarSetErrorHandler() + { + set_error_handler(function ($severity, $message, $file, $line) { + if (!(error_reporting() & $severity)) { + // This error code is not included in error_reporting. + return; + } + + $smartyTemplate = $this->getSmartyTemplateLocationFromError($message); + if (is_array($smartyTemplate)) { + [$file, $line] = $smartyTemplate; + } + + throw new ErrorException($message, 0, $severity, $file, $line); + }); + } + + /** + * @return void + */ + protected function d3DebugBarSetExceptionHandler(): void + { + set_exception_handler([ + new DebugBarExceptionHandler(), + 'handleUncaughtException' + ]); + } + + /** + * @param $messsage + * @return array|null + */ + protected function getSmartyTemplateLocationFromError($messsage) + { + if (stristr($messsage, 'Smarty error: [in ')) { + $start = strpos($messsage, '[')+1; + $end = strpos($messsage, ']'); + $parts = explode(' ', substr($messsage, $start, $end - $start)); + return [Registry::getConfig()->getTemplateDir(isAdmin()).$parts[1], (int) $parts[3]]; + } + + return null; + } + + /** + * @return void + */ + protected function d3AddDebugBarComponent(): void { $userComponentNames = Registry::getConfig()->getConfigParam('aUserComponentNames'); $d3CmpName = DebugBarComponent::class; @@ -49,8 +105,8 @@ class ShopControl_DebugBar extends ShopControl_DebugBar_parent public function __destruct() { - if (!isAdmin()) { - /** @var FrontendController $activeView */ + global $debugBarSet; + if (!isAdmin() && $debugBarSet !== 1) { $activeView = Registry::getConfig()->getTopActiveView(); /** @var DebugBarComponent|null $debugBarComponent */ $debugBarComponent = $activeView->getComponent(DebugBarComponent::class); diff --git a/metadata.php b/metadata.php index ed9672a..f6b7d8a 100644 --- a/metadata.php +++ b/metadata.php @@ -13,7 +13,9 @@ declare(strict_types=1); +use D3\DebugBar\Modules\Core\Config_DebugBar; use D3\DebugBar\Modules\Core\ShopControl_DebugBar; +use OxidEsales\Eshop\Core\Config; use OxidEsales\Eshop\Core\ShopControl; $sMetadataVersion = '2.1'; @@ -37,6 +39,7 @@ $aModule = [ 'url' => 'https://www.oxidmodule.com/', 'controllers' => [], 'extend' => [ + Config::class => Config_DebugBar::class, ShopControl::class => ShopControl_DebugBar::class, ], 'events' => [],