getFile() . ' line ' . $this->getLine() . "\n"; echo $this->getTraceAsString() . "\n"; } elseif (_PS_MODE_DEV_) { // Display error message echo ''; echo '
'; echo '

[' . get_class($this) . ']

'; echo $this->getExtendedMessage(); $this->displayFileDebug($this->getFile(), $this->getLine()); // Display debug backtrace echo ''; echo '
'; } else { // If not in mode dev, display an error page if (file_exists(_PS_ROOT_DIR_ . '/error500.html')) { echo file_get_contents(_PS_ROOT_DIR_ . '/error500.html'); } } // Log the error in the disk $this->logError(); if ($dieAfterDisplay) { // We only need the error code 1 in cli context exit((int) ToolsCore::isPHPCLI()); } } /** * Display lines around current line. * * @param string $file * @param int $line * @param int|null $id */ protected function displayFileDebug($file, $line, $id = null) { $lines = file($file); $offset = $line - 6; $total = 11; if ($offset < 0) { $total += $offset; $offset = 0; } $lines = array_slice($lines, $offset, $total); ++$offset; echo '
';
        foreach ($lines as $k => $l) {
            $string = ($offset + $k) . '. ' . htmlspecialchars($l);
            if ($offset + $k == $line) {
                echo '' . $string . '';
            } else {
                echo $string;
            }
        }
        echo '
'; } /** * Prevent critical arguments to be displayed in the debug trace page (e.g. database password) * Returns the array of args with critical arguments replaced by placeholders. * * @param array $trace * * @return array */ protected function hideCriticalArgs(array $trace) { $args = $trace['args']; if (empty($trace['class']) || empty($trace['function'])) { return $args; } $criticalParameters = [ 'pwd', 'pass', 'passwd', 'password', 'database', 'server', ]; $hiddenArgs = []; try { $class = new ReflectionClass($trace['class']); /** @var ReflectionMethod $method */ $method = $class->getMethod($trace['function']); /** @var ReflectionParameter $parameter */ foreach ($method->getParameters() as $argIndex => $parameter) { if ($argIndex >= count($args)) { break; } if (in_array(strtolower($parameter->getName()), $criticalParameters)) { $hiddenArgs[] = '**hidden_' . $parameter->getName() . '**'; } else { $hiddenArgs[] = $args[$argIndex]; } } } catch (ReflectionException $e) { // In worst case scenario there are some critical args we could't detect so we return an empty array } return $hiddenArgs; } /** * Display arguments list of traced function. * * @param array $args List of arguments * @param int $id ID of argument */ protected function displayArgsDebug($args, $id) { echo '
';
        foreach ($args as $arg => $value) {
            echo 'Argument [' . Tools::safeOutput($arg) . "]\n";
            echo Tools::safeOutput(print_r($value, true));
            echo "\n";
        }
        echo '
'; } /** * Log the error on the disk. */ protected function logError() { $logger = new FileLogger(); $logger->setFilename(_PS_ROOT_DIR_ . '/var/logs/' . date('Ymd') . '_exception.log'); try { $logger->logError($this->getExtendedMessage(false)); } catch (PrestaShopException) { // Catch exception because there is a hook executed in the AbstractLogger that is bound to fail when the DB // is not accessible, there is no point adding some potential error in this method that is already logging // a previous error, it would only confuse the error messages and cause an unwanted fatal error } } /** * Return the content of the Exception. * * @return string content of the exception */ protected function getExtendedMessage($html = true) { $format = '

%s
at line %d in file %s

'; if (!$html) { $format = strip_tags(str_replace('
', ' ', $format)); } return sprintf( $format, Tools::safeOutput($this->getMessage(), true), $this->getLine(), ltrim(str_replace([_PS_ROOT_DIR_, '\\'], ['', '/'], $this->getFile()), '/') ); } }