Browse Source

Portal: Improved debug informations on error page

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@4836 a333f486-631f-4898-b8df-5754b55c2be0
glajarige 8 years ago
parent
commit
27991881ac

+ 0 - 225
datamodels/2.x/itop-portal-base/portal/src/handlers/exceptionhandler.class.inc.php

@@ -1,225 +0,0 @@
-<?php
-
-// Copyright (C) 2010-2015 Combodo SARL
-//
-//   This file is part of iTop.
-//
-//   iTop is free software; you can redistribute it and/or modify
-//   it under the terms of the GNU Affero General Public License as published by
-//   the Free Software Foundation, either version 3 of the License, or
-//   (at your option) any later version.
-//
-//   iTop is distributed in the hope that it will be useful,
-//   but WITHOUT ANY WARRANTY; without even the implied warranty of
-//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//   GNU Affero General Public License for more details.
-//
-//   You should have received a copy of the GNU Affero General Public License
-//   along with iTop. If not, see <http://www.gnu.org/licenses/>
-
-namespace Combodo\iTop\Portal\Handler;
-
-use \Exception;
-use \Silex\Application;
-use \Symfony\Component\Debug\ExceptionHandler as BaseExceptionHandler;
-use \Symfony\Component\Debug\Exception\FlattenException;
-use \Symfony\Component\HttpFoundation\Request;
-use \IssueLog;
-use \Dict;
-
-/**
- * Extends the default ExceptionHandler to provide a better template.
- *
- * @author Guillaume Lajarige
- */
-class ExceptionHandler extends BaseExceptionHandler
-{
-    private $debug;
-    private $charset;
-    private $handler;
-    private $caughtBuffer;
-    private $caughtLength;
-    private $fileLinkFormat;
-
-    /**
-     * Sends a response for the given Exception.
-     *
-     * To be as fail-safe as possible, the exception is first handled
-     * by our simple exception handler, then by the user exception handler.
-     * The latter takes precedence and any output from the former is cancelled,
-     * if and only if nothing bad happens in this handling path.
-     */
-    public function handle(\Exception $exception)
-    {
-        IssueLog::Error('Portal: '.$exception->getMessage());
-
-        parent::handle($exception);
-    }
-
-    /**
-     * Gets the HTML content associated with the given exception.
-     *
-     * @param FlattenException $exception A FlattenException instance
-     *
-     * @return string The content as a string
-     */
-    public function getContent(FlattenException $exception)
-    {
-        switch ($exception->getStatusCode()) {
-            case 404:
-                $title = Dict::S('Error:HTTP:404');
-                break;
-            default:
-                $title = Dict::S('Error:HTTP:500');
-        }
-
-        $content = '';
-        if ($this->debug) {
-            try {
-                $count = count($exception->getAllPrevious());
-                $total = $count + 1;
-                foreach ($exception->toArray() as $position => $e) {
-                    $ind = $count - $position + 1;
-                    $class = $this->formatClass($e['class']);
-                    $message = nl2br($this->escapeHtml($e['message']));
-                    $content .= sprintf(<<<EOF
-                        <h2 class="block_exception clear_fix">
-                            <span class="exception_counter">%d/%d</span>
-                            <span class="exception_title">%s%s:</span>
-                            <span class="exception_message">%s</span>
-                        </h2>
-                        <div class="block">
-                            <ol class="traces list_exception">
-
-EOF
-                        , $ind, $total, $class, $this->formatPath($e['trace'][0]['file'], $e['trace'][0]['line']), $message);
-                    foreach ($e['trace'] as $trace) {
-                        $content .= '       <li>';
-                        if ($trace['function']) {
-                            $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args']));
-                        }
-                        if (isset($trace['file']) && isset($trace['line'])) {
-                            $content .= $this->formatPath($trace['file'], $trace['line']);
-                        }
-                        $content .= "</li>\n";
-                    }
-
-                    $content .= "    </ol>\n</div>\n";
-                }
-            } catch (\Exception $e) {
-                // something nasty happened and we cannot throw an exception anymore
-                if ($this->debug) {
-                    $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage()));
-                } else {
-                    $title = 'Whoops, looks like something went wrong.';
-                }
-            }
-        }
-        else{
-            $content = $exception->getMessage();
-        }
-
-        return <<<EOF
-            <div id="sf-resetcontent" class="sf-reset">
-                <h1>$title</h1>
-                <div class="content">$content</div>
-            </div>
-EOF;
-    }
-
-    /**
-     * Gets the stylesheet associated with the given exception.
-     *
-     * @param FlattenException $exception A FlattenException instance
-     *
-     * @return string The stylesheet as a string
-     */
-    public function getStylesheet(FlattenException $exception)
-    {
-        $parentStylesheet = parent::getStylesheet($exception);
-        $stylesheet = <<<EOF
-.sf-reset .content{ background-color: #FFFFFF; padding: 15px 28px; margin-bottom: 20px; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; border: 1px solid #ccc; font-size: 13px; }
-EOF;
-
-        return $stylesheet . $parentStylesheet;
-    }
-
-    /**
-     * Note: Duplicated as the original one is private.
-     *
-     * @param $class
-     * @return string
-     */
-    protected function formatClass($class)
-    {
-        $parts = explode('\\', $class);
-
-        return sprintf('<abbr title="%s">%s</abbr>', $class, array_pop($parts));
-    }
-
-    /**
-     * Note: Duplicated as the original one is private.
-     *
-     * Formats an array as a string.
-     *
-     * @param array $args The argument array
-     *
-     * @return string
-     */
-    private function formatArgs(array $args)
-    {
-        $result = array();
-        foreach ($args as $key => $item) {
-            if ('object' === $item[0]) {
-                $formattedValue = sprintf('<em>object</em>(%s)', $this->formatClass($item[1]));
-            } elseif ('array' === $item[0]) {
-                $formattedValue = sprintf('<em>array</em>(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
-            } elseif ('string' === $item[0]) {
-                $formattedValue = sprintf("'%s'", $this->escapeHtml($item[1]));
-            } elseif ('null' === $item[0]) {
-                $formattedValue = '<em>null</em>';
-            } elseif ('boolean' === $item[0]) {
-                $formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
-            } elseif ('resource' === $item[0]) {
-                $formattedValue = '<em>resource</em>';
-            } else {
-                $formattedValue = str_replace("\n", '', var_export($this->escapeHtml((string) $item[1]), true));
-            }
-
-            $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
-        }
-
-        return implode(', ', $result);
-    }
-
-    /**
-     * Note: Duplicated as the original one is private.
-     *
-     * HTML-encodes a string.
-     */
-    private function escapeHtml($str)
-    {
-        return htmlspecialchars($str, ENT_QUOTES | (PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), $this->charset);
-    }
-
-    /**
-     * Note: Duplicated as the original one is private.
-     *
-     * @param $path
-     * @param $line
-     * @return string
-     */
-    private function formatPath($path, $line)
-    {
-        $path = $this->escapeHtml($path);
-        $file = preg_match('#[^/\\\\]*$#', $path, $file) ? $file[0] : $path;
-
-        if ($linkFormat = $this->fileLinkFormat) {
-            $link = strtr($this->escapeHtml($linkFormat), array('%f' => $path, '%l' => (int) $line));
-
-            return sprintf(' in <a href="%s" title="Go to source">%s line %d</a>', $link, $file, $line);
-        }
-
-        return sprintf(' in <a title="%s line %3$d" ondblclick="var f=this.innerHTML;this.innerHTML=this.title;this.title=f;">%s line %d</a>', $path, $file, $line);
-    }
-}

+ 62 - 3
datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php

@@ -22,8 +22,7 @@ namespace Combodo\iTop\Portal\Helper;
 use \Exception;
 use \Silex\Application;
 use \Symfony\Component\Debug\ErrorHandler;
-//use \Symfony\Component\Debug\ExceptionHandler;
-use \Combodo\iTop\Portal\Handler\ExceptionHandler;
+use \Symfony\Component\Debug\ExceptionHandler;
 use \Symfony\Component\HttpFoundation\Request;
 use \Twig_Environment;
 use \Twig_SimpleFilter;
@@ -259,7 +258,7 @@ class ApplicationHelper
 		ExceptionHandler::register(($oApp['debug'] === true));
 
 		// Intercepting manually aborted request
-		if (!$oApp['debug'])
+		if (1 || !$oApp['debug'])
 		{
 			$oApp->error(function(Exception $e, $code) use ($oApp)
 			{
@@ -288,6 +287,66 @@ class ApplicationHelper
 				}
 				else
 				{
+				    // Preparing debug trace
+                    $aSteps = array();
+                    foreach($e->getTrace() as $aStep)
+                    {
+                        // - Default file name
+                        if(!isset($aStep['file']))
+                        {
+                            $aStep['file'] = '';
+                        }
+                        $aFileParts = explode('\\', $aStep['file']);
+                        // - Default line number
+                        if(!isset($aStep['line']))
+                        {
+                            $aStep['line'] = 'unknown';
+                        }
+                        // - Default class name
+                        if(isset($aStep['class']) && isset($aStep['function']) && isset($aStep['type']))
+                        {
+                            $aClassParts = explode('\\', $aStep['class']);
+                            $sClassName = $aClassParts[count($aClassParts)-1];
+                            $sClassFQ = $aStep['class'];
+
+                            $aArgsAsString = array();
+                            foreach($aStep['args'] as $arg)
+                            {
+                                if(is_array($arg))
+                                {
+                                    $aArgsAsString[] = 'array(...)';
+                                }
+                                elseif(is_object($arg))
+                                {
+                                    $aArgsAsString[] = 'object('.get_class($arg).')';
+                                }
+                                else
+                                {
+                                    $aArgsAsString[] = $arg;
+                                }
+                            }
+
+                            $sFunctionCall = $sClassName . $aStep['type'] . $aStep['function'] . '(' . implode(', ', $aArgsAsString) . ')';
+                        }
+                        else
+                        {
+                            $sClassName = null;
+                            $sClassFQ = null;
+                            $sFunctionCall = null;
+                        }
+
+                        $aSteps[] = array(
+                            'file_fq' => $aStep['file'],
+                            'file_name' => $aFileParts[count($aFileParts)-1],
+                            'line' => $aStep['line'],
+                            'class_name' => $sClassName,
+                            'class_fq' => $sClassFQ,
+                            'function_call' => $sFunctionCall,
+                        );
+                    }
+
+                    $aData['debug_trace_steps'] = $aSteps;
+
 					$oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/errors/layout.html.twig', $aData);
 				}
 

+ 21 - 1
datamodels/2.x/itop-portal-base/portal/src/views/errors/layout.html.twig

@@ -11,7 +11,8 @@
 			margin: 50px auto;
 			text-align: center;
 			padding: 25px;
-			max-width: 600px;
+			min-width: 600px;
+			max-width: 1000px;
 		}
 		h1, h2, h3, p {
 			margin: 0;
@@ -27,6 +28,12 @@
 			vertical-align: -5%;
 			margin-right: 5px;
 		}
+		abbr[title]{
+			border-bottom: none;
+		}
+		.traces.list_exception{
+			text-align: left;
+		}
 	</style>
 	
 	<div class="container">
@@ -40,5 +47,18 @@
 				<a class="btn btn-default" href="{{ app.url_generator.generate('p_home') }}"><span class="fa fa-home"></span> {{ 'Page:GoPortalHome'|dict_s }}</a>
 			</p>
 		</div>
+
+		{% if app['debug'] == true %}
+			<div class="well">
+				<ol class="traces list_exception">
+					{% for aStep in debug_trace_steps %}
+                        <li>
+							{% if aStep.function_call is not null %}at <abbr title="{{ aStep.class_fq }}">{{ aStep.function_call }}</abbr>{% endif %}
+							in <a title="{{ aStep.file_fq }}">{{ aStep.file_name }}</a> line {{ aStep.line }}
+						</li>
+					{% endfor %}
+				</ol>
+			</div>
+		{% endif %}
 	</div>
 {% endblock %}

+ 0 - 1
datamodels/2.x/itop-portal-base/portal/web/index.php

@@ -31,7 +31,6 @@ require_once APPROOT . '/core/moduledesign.class.inc.php';
 require_once APPROOT . '/application/loginwebpage.class.inc.php';
 require_once APPROOT . '/sources/autoload.php';
 // Portal
-require_once __DIR__ . '/../src/handlers/exceptionhandler.class.inc.php';
 require_once __DIR__ . '/../src/providers/urlgeneratorserviceprovider.class.inc.php';
 require_once __DIR__ . '/../src/helpers/urlgeneratorhelper.class.inc.php';
 require_once __DIR__ . '/../src/providers/contextmanipulatorserviceprovider.class.inc.php';