Quellcode durchsuchen

[FrameworkBundle] added support for previous exceptions in the exception pages

Fabien Potencier vor 14 Jahren
Ursprung
Commit
ec8500bd64

+ 10 - 1
src/Symfony/Bundle/FrameworkBundle/Controller/ExceptionController.php

@@ -32,9 +32,18 @@ class ExceptionController extends Controller
     {
         $this['request']->setRequestFormat($manager->getFormat());
 
+        $currentContent = '';
+        while (false !== $content = ob_get_clean()) {
+            $currentContent .= $content;
+        }
+
         $response = $this->render(
             'FrameworkBundle:Exception:'.($this['kernel']->isDebug() ? 'exception' : 'error'),
-            array('manager' => $manager)
+            array(
+                'manager'        => $manager,
+                'managers'       => $manager->getLinkedManagers(),
+                'currentContent' => $currentContent,
+            )
         );
         $response->setStatusCode($manager->getStatusCode());
 

+ 25 - 15
src/Symfony/Bundle/FrameworkBundle/Debug/ExceptionManager.php

@@ -27,18 +27,23 @@ class ExceptionManager
     protected $exception;
     protected $request;
     protected $logger;
-    protected $currentContent;
 
     public function __construct(\Exception $exception, Request $request, DebugLoggerInterface $logger = null)
     {
         $this->exception = $exception;
         $this->request = $request;
         $this->logger = $logger;
+    }
 
-        $this->currentContent = '';
-        while (false !== $content = ob_get_clean()) {
-            $this->currentContent .= $content;
+    public function getLinkedManagers()
+    {
+        $managers = array();
+        $e = $this->exception;
+        while ($e = $e->getPrevious()) {
+            $managers[] = new $this($e, $this->request);
         }
+
+        return $managers;
     }
 
     public function getException()
@@ -46,11 +51,6 @@ class ExceptionManager
         return $this->exception;
     }
 
-    public function getCurrentContent()
-    {
-        return $this->currentContent;
-    }
-
     public function getLogger()
     {
         return $this->logger;
@@ -126,13 +126,23 @@ class ExceptionManager
             'args'     => array(),
         );
         foreach ($this->exception->getTrace() as $entry) {
+            $class = '';
+            $namespace = '';
+            if (isset($entry['class'])) {
+                $parts = explode('\\', $entry['class']);
+                $class = array_pop($parts);
+                $namespace = implode('\\', $parts);
+            }
+
             $traces[] = array(
-                'class'    => isset($entry['class']) ? $entry['class'] : '',
-                'type'     => isset($entry['type']) ? $entry['type'] : '',
-                'function' => $entry['function'],
-                'file'     => isset($entry['file']) ? $entry['file'] : null,
-                'line'     => isset($entry['line']) ? $entry['line'] : null,
-                'args'     => isset($entry['args']) ? $entry['args'] : array(),
+                'namespace'   => $namespace,
+                'short_class' => $class,
+                'class'       => isset($entry['class']) ? $entry['class'] : '',
+                'type'        => isset($entry['type']) ? $entry['type'] : '',
+                'function'    => $entry['function'],
+                'file'        => isset($entry['file']) ? $entry['file'] : null,
+                'line'        => isset($entry['line']) ? $entry['line'] : null,
+                'args'        => isset($entry['args']) ? $entry['args'] : array(),
             );
         }
 

+ 1 - 0
src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml

@@ -92,6 +92,7 @@
         <service id="templating.helper.code" class="%templating.helper.code.class%">
             <tag name="templating.helper" alias="code" />
             <argument>%debug.file_link_format%</argument>
+            <argument>%kernel.root_dir%</argument>
         </service>
 
         <service id="templating.loader" alias="templating.loader.filesystem" />

+ 20 - 4
src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.json.php

@@ -1,9 +1,25 @@
-<?php echo json_encode(array(
+<?php
+
+$vars = array(
     'error'       => array(
         'code'      => $manager->getStatusCode(),
-        'message'   => $manager->getMessage(),
-        'debug'     => array(
+        'message'   => $manager->getName(),
+        'exception' => array(
             'name'    => $manager->getName(),
+            'message' => $manager->getMessage(),
             'traces'  => $manager->getTraces(),
         ),
-))) ?>
+));
+
+if (count($managers)) {
+    $vars['exceptions'] = array();
+    foreach ($managers as $i => $previous) {
+        $vars['exceptions'][] = array(
+            'name'    => $previous->getName(),
+            'message' => $previous->getMessage(),
+            'traces'  => $previous->getTraces(),
+        );
+    }
+}
+
+echo json_encode($vars);

Datei-Diff unterdrückt, da er zu groß ist
+ 41 - 13
src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.php


+ 13 - 8
src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.txt.php

@@ -1,14 +1,19 @@
-[exception]   <?php echo $manager->getStatusCode().' | '.$manager->getStatusText().' | '.$manager->getName() ?>
+[exception] <?php echo $manager->getStatusCode().' | '.$manager->getStatusText().' | '.$manager->getName() ?>
 
-[message]     <?php echo $manager->getMessage() ?>
+[message] <?php echo $manager->getMessage() ?>
 
 <?php if (count($manager->getTraces())): ?>
-[stack trace]
-<?php foreach ($manager->getTraces() as $i => $trace): ?>
-<?php echo $view->render('FrameworkBundle:Exception:trace.txt', array('i' => $i, 'trace' => $trace)) ?>
+<?php echo $view->render('FrameworkBundle:Exception:traces', array('manager' => $manager, 'position' => 0, 'count' => count($managers))) ?>
 
-<?php endforeach; ?>
+<?php endif; ?>
+<?php if (count($managers)): ?>
+<?php foreach ($managers as $i => $previous): ?>
+[linked exception] <?php echo $previous->getName() ?>: <?php echo $previous->getMessage() ?>
+
+<?php echo $view->render('FrameworkBundle:Exception:traces', array('manager' => $previous, 'position' => $i + 1, 'count' => count($managers))) ?>
 
+<?php endforeach; ?>
 <?php endif; ?>
-[symfony]     v. <?php echo \Symfony\Framework\Kernel::VERSION ?> (symfony-project.org)
-[PHP]         v. <?php echo PHP_VERSION ?>
+
+[symfony] v. <?php echo \Symfony\Framework\Kernel::VERSION ?> (symfony-project.org)
+[PHP]     v. <?php echo PHP_VERSION ?>

+ 9 - 11
src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.xml.php

@@ -1,15 +1,13 @@
 <?php echo sprintf('<?xml version="1.0" encoding="%s" ?>', $view->getCharset())."\n" ?>
 <error code="<?php echo $manager->getStatusCode() ?>" message="<?php echo $manager->getStatusText() ?>">
-    <debug>
-        <name><?php echo $manager->getName() ?></name>
-        <message><?php echo htmlspecialchars($manager->getMessage(), ENT_QUOTES, $view->getCharset()) ?></message>
-        <traces>
-<?php foreach ($manager->getTraces() as $i => $trace): ?>
-                <trace>
-                <?php echo $view->render('FrameworkBundle:Exception:trace.txt', array('i' => $i, 'trace' => $trace)) ?>
-
-                </trace>
+    <exception class="<?php echo $manager->getName() ?>" message="<?php echo $manager->getMessage() ?>">
+        <?php echo $view->render('FrameworkBundle:Exception:traces', array('manager' => $manager, 'position' => 0, 'count' => count($managers))) ?>
+    </exception>
+<?php if (count($managers)): ?>
+<?php foreach ($managers as $i => $previous): ?>
+    <exception class="<?php echo $previous->getName() ?>" message="<?php echo $previous->getMessage() ?>">
+        <?php echo $view->render('FrameworkBundle:Exception:traces', array('manager' => $previous, 'position' => $i + 1, 'count' => count($managers))) ?>
+    </exception>
 <?php endforeach; ?>
-        </traces>
-    </debug>
+<?php endif; ?>
 </error>

Datei-Diff unterdrückt, da er zu groß ist
+ 32 - 16
src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/styles.php


+ 3 - 3
src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/trace.php

@@ -1,10 +1,10 @@
 <?php if ($trace['function']): ?>
-    at <strong><?php echo $trace['class'] ?><?php echo $trace['type'] ?><?php echo $trace['function'] ?></strong>(<?php echo $view['code']->formatArgs($trace['args']) ?>)<br />
+    at <strong><abbr title="<?php echo $trace['class'] ?>"><?php echo $trace['short_class'] ?></abbr><?php echo $trace['type'] ?><?php echo $trace['function'] ?></strong>(<?php echo $view['code']->formatArgs($trace['args']) ?>)<br />
 <?php endif; ?>
 <?php if ($trace['file'] && $trace['line']): ?>
     in <em><?php echo $view['code']->formatFile($trace['file'], $trace['line']) ?></em> line <?php echo $trace['line'] ?>
-    <a href="#" onclick="toggle('trace_<?php echo $i ?>'); return false;">...</a><br />
-    <ul class="code" id="trace_<?php echo $i ?>" style="display: <?php echo 0 === $i ? 'block' : 'none' ?>">
+    <a href="#" onclick="toggle('trace_<?php echo $prefix.'_'.$i ?>'); return false;">&raquo;</a><br />
+    <ul class="code" id="trace_<?php echo $prefix.'_'.$i ?>" style="display: <?php echo 0 === $i ? 'block' : 'none' ?>">
         <?php echo $view['code']->fileExcerpt($trace['file'], $trace['line']) ?>
     </ul>
 <?php endif; ?>

+ 1 - 1
src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/trace.txt.php

@@ -1,5 +1,5 @@
 <?php if ($trace['function']): ?>
-    at <?php echo $trace['class'].$trace['type'].$trace['function'] ?>(<?php echo $view['code']->formatArgs($trace['args']) ?>)
+    at <?php echo $trace['class'].$trace['type'].$trace['function'] ?>(<?php echo $view['code']->formatArgsAsText($trace['args']) ?>)
 <?php else: ?>
     at n/a
 <?php endif; ?>

+ 20 - 7
src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/traces.php

@@ -1,7 +1,20 @@
-<ul>
-    <?php foreach ($traces as $i => $trace): ?>
-        <li>
-            <?php echo $view->render('FrameworkBundle:Exception:trace', array('i' => $i, 'trace' => $trace)) ?>
-        </li>
-    <?php endforeach; ?>
-</ul>
+<div class="block">
+    <?php if ($count > 0): ?>
+        <h3>
+            <span><?php echo $count - $position + 1 ?>/<?php echo $count + 1 ?></span>
+            <?php echo $manager->getName() ?>: <?php echo str_replace("\n", '<br />', htmlspecialchars($manager->getMessage(), ENT_QUOTES, $view->getCharset())) ?>
+            <a href="#" onclick="toggle('traces_<?php echo $position ?>', 'traces'); return false;">&raquo;</a><br />
+        </h3>
+    <?php else: ?>
+        <h3>Stack Trace</h3>
+    <?php endif; ?>
+
+    <a id="traces_link_<?php echo $position ?>"></a>
+    <ul class="traces" id="traces_<?php echo $position ?>" style="display: <?php echo 0 === $position ? 'block' : 'none' ?>">
+        <?php foreach ($manager->getTraces() as $i => $trace): ?>
+            <li>
+                <?php echo $view->render('FrameworkBundle:Exception:trace', array('prefix' => $position, 'i' => $i, 'trace' => $trace)) ?>
+            </li>
+        <?php endforeach; ?>
+    </ul>
+</div>

+ 6 - 0
src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/traces.txt.php

@@ -0,0 +1,6 @@
+<?php if (count($manager->getTraces())): ?>
+<?php foreach ($manager->getTraces() as $i => $trace): ?>
+<?php echo $view->render('FrameworkBundle:Exception:trace.txt', array('i' => $i, 'trace' => $trace)) ?>
+
+<?php endforeach; ?>
+<?php endif;?>

+ 8 - 0
src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/traces.xml.php

@@ -0,0 +1,8 @@
+<traces>
+<?php foreach ($manager->getTraces() as $i => $trace): ?>
+        <trace>
+        <?php echo $view->render('FrameworkBundle:Exception:trace.txt', array('i' => $i, 'trace' => $trace)) ?>
+
+        </trace>
+<?php endforeach; ?>
+    </traces>

+ 51 - 3
src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php

@@ -21,15 +21,18 @@ use Symfony\Component\Templating\Helper\Helper;
 class CodeHelper extends Helper
 {
     protected $fileLinkFormat;
+    protected $rootDir;
 
     /**
      * Constructor.
      *
      * @param string $fileLinkFormat The format for links to source files
+     * @param string $rootDir        The project root directory
      */
-    public function __construct($fileLinkFormat)
+    public function __construct($fileLinkFormat, $rootDir)
     {
         $this->fileLinkFormat = null !== $fileLinkFormat ? $fileLinkFormat : ini_get('xdebug.file_link_format');
+        $this->rootDir = str_replace('\\', '/', $rootDir).'/';
     }
 
     /**
@@ -39,7 +42,7 @@ class CodeHelper extends Helper
      *
      * @return string
      */
-    public function formatArgs($args)
+    public function formatArgsAsText($args)
     {
         $result = array();
         foreach ($args as $key => $value) {
@@ -51,6 +54,46 @@ class CodeHelper extends Helper
                 $formattedValue = sprintf("'%s'", $value);
             } elseif (null === $value) {
                 $formattedValue = 'null';
+            } elseif (false === $value) {
+                $formattedValue = 'false';
+            } elseif (true === $value) {
+                $formattedValue = 'true';
+            } else {
+                $formattedValue = $value;
+            }
+
+            $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
+        }
+
+        return implode(', ', $result);
+    }
+
+    /**
+     * Formats an array as a string.
+     *
+     * @param array $args The argument array
+     *
+     * @return string
+     */
+    public function formatArgs($args)
+    {
+        $result = array();
+        foreach ($args as $key => $value) {
+            if (is_object($value)) {
+                $class = get_class($value);
+                $parts = explode('\\', $class);
+                $short = array_pop($parts);
+                $formattedValue = sprintf("<em>object</em>(<abbr title=\"%s\">%s</abbr>)", $class, $short);
+            } elseif (is_array($value)) {
+                $formattedValue = sprintf("<em>array</em>(%s)", $this->formatArgs($value));
+            } elseif (is_string($value)) {
+                $formattedValue = sprintf("'%s'", $value);
+            } elseif (null === $value) {
+                $formattedValue = '<em>null</em>';
+            } elseif (false === $value) {
+                $formattedValue = '<em>false</em>';
+            } elseif (true === $value) {
+                $formattedValue = '<em>true</em>';
             } else {
                 $formattedValue = $value;
             }
@@ -95,13 +138,18 @@ class CodeHelper extends Helper
      */
     public function formatFile($file, $line)
     {
+        if (0 === strpos($file, $this->rootDir)) {
+            $file = str_replace($this->rootDir, '', str_replace('\\', '/', $file));
+            $file = sprintf('<abbr title="%s">kernel.root_dir</abbr>/%s', $this->rootDir, $file);
+        }
+
         if (!$this->fileLinkFormat) {
             return $file;
         }
 
         $link = strtr($this->fileLinkFormat, array('%f' => $file, '%l' => $line));
 
-        return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', $link, $file);
+        return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', $link, $this->rootDir, $file);
     }
 
     /**

+ 1 - 1
src/Symfony/Component/CssSelector/Parser.php

@@ -75,7 +75,7 @@ class Parser
         } catch (\Exception $e) {
             $class = get_class($e);
 
-            throw new $class(sprintf('%s at %s -> %s', $e->getMessage(), implode($stream->getUsed(), ''), $stream->peek()));
+            throw new $class(sprintf('%s at %s -> %s', $e->getMessage(), implode($stream->getUsed(), ''), $stream->peek()), 0, $e);
         }
     }
 

+ 1 - 1
src/Symfony/Component/DependencyInjection/ContainerBuilder.php

@@ -176,7 +176,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             return parent::get($id, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE);
         } catch (\InvalidArgumentException $e) {
             if (isset($this->loading[$id])) {
-                throw new \LogicException(sprintf('The service "%s" has a circular reference to itself.', $id));
+                throw new \LogicException(sprintf('The service "%s" has a circular reference to itself.', $id), 0, $e);
             }
 
             if (!$this->hasDefinition($id) && isset($this->aliases[$id])) {

+ 4 - 4
src/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php

@@ -108,7 +108,7 @@ class PdoSessionStorage extends NativeSessionStorage
             $stmt->bindParam(1, $id, \PDO::PARAM_STR);
             $stmt->execute();
         } catch (\PDOException $e) {
-            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data. Message: %s', $e->getMessage()));
+            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
         }
 
         return true;
@@ -135,7 +135,7 @@ class PdoSessionStorage extends NativeSessionStorage
         try {
             $this->db->query($sql);
         } catch (\PDOException $e) {
-            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data. Message: %s', $e->getMessage()));
+            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
         }
 
         return true;
@@ -184,7 +184,7 @@ class PdoSessionStorage extends NativeSessionStorage
                 return '';
             }
         } catch (\PDOException $e) {
-            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data. Message: %s', $e->getMessage()));
+            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
         }
     }
 
@@ -214,7 +214,7 @@ class PdoSessionStorage extends NativeSessionStorage
             $stmt->bindParam(2, $id, \PDO::PARAM_STR);
             $stmt->execute();
         } catch (\PDOException $e) {
-            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data. Message: %s', $e->getMessage()));
+            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
         }
 
         return true;

+ 1 - 1
src/Symfony/Component/Yaml/Yaml.php

@@ -89,7 +89,7 @@ class Yaml
         try {
             $ret = $yaml->parse($input);
         } catch (\Exception $e) {
-            throw new \InvalidArgumentException(sprintf('Unable to parse %s: %s', $file ? sprintf('file "%s"', $file) : 'string', $e->getMessage()));
+            throw new \InvalidArgumentException(sprintf('Unable to parse %s: %s', $file ? sprintf('file "%s"', $file) : 'string', $e->getMessage()), 0, $e);
         }
 
         return $ret;