diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ExceptionController.php index fa925de6a4..48d620d6ff 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ExceptionController.php @@ -3,10 +3,7 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller; -use Symfony\Bundle\FrameworkBundle\Debug\ExceptionFormatter; -use Symfony\Components\HttpFoundation\Request; -use Symfony\Components\HttpFoundation\Response; -use Symfony\Components\HttpKernel\Exception\HttpException; +use Symfony\Bundle\FrameworkBundle\Debug\ExceptionManager; /* * This file is part of the Symfony framework. @@ -27,60 +24,19 @@ class ExceptionController extends Controller /** * Converts an Exception to a Response. * - * @param \Exception $exception An Exception instance - * @param Request $request The original Request instance - * @param array $logs An array of logs + * @param ExceptionManager $manager An ExceptionManager instance * * @throws \InvalidArgumentException When the exception template does not exist */ - public function exceptionAction(\Exception $exception, Request $originalRequest, array $logs) + public function exceptionAction(ExceptionManager $manager) { - $template = $this->container->getParameter('kernel.debug') ? 'exception' : 'error'; + $this['request']->setRequestFormat($manager->getFormat()); - $format = $format = $originalRequest->getRequestFormat(); - - // when using CLI, we force the format to be TXT - if (0 === strncasecmp(PHP_SAPI, 'cli', 3)) { - $format = 'txt'; - } - - $template = $this['templating']->getLoader()->load($template, array( - 'bundle' => 'FrameworkBundle', - 'controller' => 'Exception', - 'format' => '.'.$format, - )); - - if (false === $template) { - throw new \InvalidArgumentException(sprintf('The exception template for format "%s" does not exist.', $format)); - } - - $code = $exception instanceof HttpException ? $exception->getCode() : 500; - $text = Response::$statusTexts[$code]; - $formatter = new ExceptionFormatter($this->container->getParameterBag()->has('debug.file_link_format') ? $this->container->getParameter('debug.file_link_format') : null, $this->container->getParameter('kernel.charset')); - $message = null === $exception->getMessage() ? 'n/a' : $exception->getMessage(); - $name = get_class($exception); - $traces = $formatter->getTraces($exception, 'html' === $format ? 'html' : 'text'); - $charset = $this->container->getParameter('kernel.charset'); - - $errors = 0; - foreach ($logs as $log) { - if ('ERR' === $log['priorityName']) { - ++$errors; - } - } - - $currentContent = ''; - while (false !== $content = ob_get_clean()) { - $currentContent .= $content; - } - - ob_start(); - require $template; - $content = ob_get_clean(); - - $response = $this['response']; - $response->setStatusCode($code); - $response->setContent($content); + $response = $this->render( + 'FrameworkBundle:Exception:'.($this['kernel']->isDebug() ? 'exception' : 'error'), + array('manager' => $manager) + ); + $response->setStatusCode($manager->getStatusCode()); return $response; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Debug/ExceptionFormatter.php b/src/Symfony/Bundle/FrameworkBundle/Debug/ExceptionFormatter.php deleted file mode 100644 index 5b42cf0453..0000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Debug/ExceptionFormatter.php +++ /dev/null @@ -1,179 +0,0 @@ - - * - * This source file is subject to the MIT license that is bundled - * with this source code in the file LICENSE. - */ - -/** - * ExceptionFormatter. - * - * @author Fabien Potencier - */ -class ExceptionFormatter -{ - protected $fileLinkFormat; - protected $charset; - - /** - * Constructor. - * - * @param string $fileLinkFormat The format for links to source files - * @param string $charset The current charset - */ - public function __construct($fileLinkFormat, $charset = 'UTF-8') - { - $this->fileLinkFormat = null !== $fileLinkFormat ? $fileLinkFormat : ini_get('xdebug.file_link_format'); - $this->charset = $charset; - } - - /** - * Returns an array of exception traces. - * - * @param Exception $exception An Exception implementation instance - * @param string $format The trace format (txt or html) - * - * @return array An array of traces - */ - public function getTraces(\Exception $exception, $format = 'txt') - { - $traceData = $exception->getTrace(); - array_unshift($traceData, array( - 'function' => '', - 'file' => $exception->getFile() != null ? $exception->getFile() : null, - 'line' => $exception->getLine() != null ? $exception->getLine() : null, - 'args' => array(), - )); - - $traces = array(); - if ($format == 'html') { - $lineFormat = 'at %s%s%s(%s)
in %s line %s ...
'; - } else { - $lineFormat = 'at %s%s%s(%s) in %s line %s'; - } - - for ($i = 0, $count = count($traceData); $i < $count; $i++) { - $line = isset($traceData[$i]['line']) ? $traceData[$i]['line'] : null; - $file = isset($traceData[$i]['file']) ? $traceData[$i]['file'] : null; - $args = isset($traceData[$i]['args']) ? $traceData[$i]['args'] : array(); - $traces[] = sprintf($lineFormat, - (isset($traceData[$i]['class']) ? $traceData[$i]['class'] : ''), - (isset($traceData[$i]['type']) ? $traceData[$i]['type'] : ''), - $traceData[$i]['function'], - $this->formatArgs($args, false, $format), - $this->formatFile($file, $line, $format, null === $file ? 'n/a' : $file), - null === $line ? 'n/a' : $line, - 'trace_'.$i, - 'trace_'.$i, - $i == 0 ? 'block' : 'none', - $this->fileExcerpt($file, $line) - ); - } - - return $traces; - } - - /** - * Returns an excerpt of a code file around the given line number. - * - * @param string $file A file path - * @param int $line The selected line number - * - * @return string An HTML string - */ - protected function fileExcerpt($file, $line) - { - if (is_readable($file)) { - $content = preg_split('#
#', highlight_file($file, true)); - - $lines = array(); - for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; $i++) { - $lines[] = ''.$content[$i - 1].''; - } - - return '
    '.implode("\n", $lines).'
'; - } - } - - /** - * Formats an array as a string. - * - * @param array $args The argument array - * @param boolean $single - * @param string $format The format string (html or txt) - * - * @return string - */ - protected function formatArgs($args, $single = false, $format = 'html') - { - $result = array(); - - $single and $args = array($args); - - foreach ($args as $key => $value) { - if (is_object($value)) { - $formattedValue = ($format == 'html' ? 'object' : 'object').sprintf("('%s')", get_class($value)); - } else if (is_array($value)) { - $formattedValue = ($format == 'html' ? 'array' : 'array').sprintf("(%s)", $this->formatArgs($value)); - } else if (is_string($value)) { - $formattedValue = ($format == 'html' ? sprintf("'%s'", $this->escape($value)) : "'$value'"); - } else if (null === $value) { - $formattedValue = ($format == 'html' ? 'null' : 'null'); - } else { - $formattedValue = $value; - } - - $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $this->escape($key), $formattedValue); - } - - return implode(', ', $result); - } - - /** - * Formats a file path. - * - * @param string $file An absolute file path - * @param integer $line The line number - * @param string $format The output format (txt or html) - * @param string $text Use this text for the link rather than the file path - * - * @return string - */ - protected function formatFile($file, $line, $format = 'html', $text = null) - { - if (null === $text) { - $text = $file; - } - - if ('html' === $format && $file && $line && $this->fileLinkFormat) { - $link = strtr($this->fileLinkFormat, array('%f' => $file, '%l' => $line)); - $text = sprintf('%s', $link, $text); - } - - return $text; - } - - /** - * Escapes a string value with html entities - * - * @param string $value - * - * @return string - */ - protected function escape($value) - { - if (!is_string($value)) { - return $value; - } - - return htmlspecialchars($value, ENT_QUOTES, $this->charset); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ExceptionListener.php b/src/Symfony/Bundle/FrameworkBundle/Debug/ExceptionListener.php similarity index 70% rename from src/Symfony/Bundle/FrameworkBundle/Controller/ExceptionListener.php rename to src/Symfony/Bundle/FrameworkBundle/Debug/ExceptionListener.php index 5009da9546..6ae0f6dd2b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ExceptionListener.php +++ b/src/Symfony/Bundle/FrameworkBundle/Debug/ExceptionListener.php @@ -1,6 +1,6 @@ container = $container; - $this->logger = $logger; - $this->controller = $controller; + $this->logger = $logger; } /** @@ -55,24 +54,21 @@ class ExceptionListener $exception = $event->getParameter('exception'); if (null !== $this->logger) { - $this->logger->err(sprintf('%s (uncaught %s exception)', $exception->getMessage(), get_class($exception))); + $this->logger->err(sprintf('%s: %s (uncaught exception)', get_class($exception), $exception->getMessage())); } - $parameters = array( - '_controller' => $this->controller, - 'exception' => $exception, - 'originalRequest' => $event->getParameter('request'), - 'logs' => $this->container->has('zend.logger.writer.debug') ? $this->container->get('zend.logger.writer.debug')->getLogs() : array(), + $class = $this->container->getParameter('exception_manager.class'); + $logger = $this->container->has('logger.debug') ? $this->container->get('logger.debug') : null; + + $attributes = array( + '_controller' => $this->controller, + 'manager' => new $class($exception, $event->getParameter('request'), $logger), ); - $request = $event->getParameter('request')->duplicate(null, null, $parameters); + $request = $event->getParameter('request')->duplicate(null, null, $attributes); try { $response = $event->getSubject()->handle($request, HttpKernelInterface::SUB_REQUEST, true); - - if (null !== $this->logger) { - $this->logger->err(sprintf('%s: %s', get_class($exception), $exception->getMessage())); - } } catch (\Exception $e) { if (null !== $this->logger) { $this->logger->err(sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage())); diff --git a/src/Symfony/Bundle/FrameworkBundle/Debug/ExceptionManager.php b/src/Symfony/Bundle/FrameworkBundle/Debug/ExceptionManager.php new file mode 100644 index 0000000000..486faad7cc --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Debug/ExceptionManager.php @@ -0,0 +1,137 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * ExceptionManager. + * + * @author Fabien Potencier + */ +class ExceptionManager +{ + protected $exception; + protected $request; + protected $logger; + protected $currentContent; + + public function __construct(\Exception $exception, Request $request, DebugLoggerInterface $logger) + { + $this->exception = $exception; + $this->request = $request; + $this->logger = $logger; + + $this->currentContent = ''; + while (false !== $content = ob_get_clean()) { + $this->currentContent .= $content; + } + } + + public function getException() + { + return $this->exception; + } + + public function getCurrentContent() + { + return $this->currentContent; + } + + public function getLogger() + { + return $this->logger; + } + + public function getLogs() + { + return $this->logger->getLogs(); + } + + public function countErrors() + { + $errors = 0; + foreach ($this->logger->getLogs() as $log) { + if ('ERR' === $log['priorityName']) { + ++$errors; + } + } + + return $errors; + } + + public function getFormat() + { + $format = $this->request->getRequestFormat(); + + // when using CLI, we force the format to be TXT + if (0 === strncasecmp(PHP_SAPI, 'cli', 3)) { + $format = 'txt'; + } + + return $format; + } + + public function getStatusCode() + { + return $this->exception instanceof HttpException ? $this->exception->getCode() : 500; + } + + public function getStatusText() + { + return Response::$statusTexts[$this->getStatusCode()]; + } + + public function getMessage() + { + return null === $this->exception->getMessage() ? 'n/a' : $this->exception->getMessage(); + } + + public function getName() + { + return get_class($this->exception); + } + + /** + * Returns an array of exception traces. + * + * @return array An array of traces + */ + public function getTraces() + { + $traces = array(); + $traces[] = array( + 'class' => '', + 'type' => '', + 'function' => '', + 'file' => $this->exception->getFile(), + 'line' => $this->exception->getLine(), + 'args' => array(), + ); + foreach ($this->exception->getTrace() as $entry) { + $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(), + ); + } + + return $traces; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml index 5eeeec1c0b..6855b7d962 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml @@ -17,8 +17,10 @@ Symfony\Bundle\FrameworkBundle\Templating\Helper\RouterHelper Symfony\Bundle\FrameworkBundle\Templating\Helper\RequestHelper Symfony\Bundle\FrameworkBundle\Templating\Helper\SessionHelper + Symfony\Bundle\FrameworkBundle\Templating\Helper\CodeHelper false null + null @@ -86,6 +88,11 @@ + + + %debug.file_link_format% + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml index fea06ca726..c87cfe32c2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml @@ -9,8 +9,9 @@ Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver Symfony\Bundle\FrameworkBundle\Controller\ControllerNameConverter Symfony\Components\HttpKernel\ResponseListener - Symfony\Bundle\FrameworkBundle\Controller\ExceptionListener + Symfony\Bundle\FrameworkBundle\Debug\ExceptionListener Symfony\Bundle\FrameworkBundle\Controller\ExceptionController::exceptionAction + Symfony\Bundle\FrameworkBundle\Debug\ExceptionManager Symfony\Components\HttpKernel\Cache\Esi Symfony\Components\HttpKernel\Cache\EsiListener @@ -47,8 +48,8 @@ - %exception_listener.controller% + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.atom.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.atom.php index 43cc506c36..9f586ebb0c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.atom.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.atom.php @@ -1 +1 @@ - +render('FrameworkBundle:Exception:error.xml', array('manager' => $manager)) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.css.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.css.php index 396c5410ea..de436e7949 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.css.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.css.php @@ -1,3 +1 @@ -/* - -*/ +render('FrameworkBundle:Exception:error.js', array('manager' => $manager)) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.js.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.js.php index 396c5410ea..12a34ec549 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.js.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.js.php @@ -1,3 +1,3 @@ /* - +getStatusCode().' '.$manager->getStatusText()."\n" ?> */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.json.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.json.php index df7045de85..92a565c5d3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.json.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.json.php @@ -1,5 +1,5 @@ array( - 'code' => $code, - 'message' => $text, + 'code' => $manager->getStatusCode(), + 'message' => $manager->getStatusText(), ))) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.php similarity index 94% rename from src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.html.php rename to src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.php index 952725c03c..26a62a2df1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.php @@ -27,7 +27,7 @@ page not found

Oops! An Error Occurred

-
The server returned a " ".
+
The server returned a "getStatusCode() ?> getStatusText() ?>".
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.rdf.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.rdf.php index 43cc506c36..9f586ebb0c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.rdf.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.rdf.php @@ -1 +1 @@ - +render('FrameworkBundle:Exception:error.xml', array('manager' => $manager)) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.txt.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.txt.php index f53d4fa295..215ed6331d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.txt.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.txt.php @@ -1,7 +1,7 @@ Oops! An Error Occurred ======================= -The server returned a " ". +The server returned a "getStatusCode() ?> getStatusText() ?>". Please e-mail us at [email] and let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.xml.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.xml.php index 8c5595cb40..6d7ca3320d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.xml.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/error.xml.php @@ -1,2 +1,2 @@ -', $charset)."\n" ?> - +', $view->getCharset())."\n" ?> + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.atom.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.atom.php index 35e80548c1..aecec94f31 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.atom.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.atom.php @@ -1 +1 @@ - +render('FrameworkBundle:Exception:exception.xml', array('manager' => $manager)) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.css.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.css.php index 27e5ae45fb..e7e60595e6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.css.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.css.php @@ -1,10 +1,3 @@ /* - - - - - - - - +render('FrameworkBundle:Exception:exception.txt', array('manager' => $manager)) ?> */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.js.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.js.php index 27e5ae45fb..e7e60595e6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.js.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.js.php @@ -1,10 +1,3 @@ /* - - - - - - - - +render('FrameworkBundle:Exception:exception.txt', array('manager' => $manager)) ?> */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.json.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.json.php index 4acc051712..4a4a2f6ecf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.json.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.json.php @@ -1,10 +1,9 @@ array( - 'code' => $code, - 'message' => $message, + 'code' => $manager->getStatusCode(), + 'message' => $manager->getMessage(), 'debug' => array( - 'name' => $name, - 'message' => $message, - 'traces' => $traces, + 'name' => $manager->getName(), + 'traces' => $manager->getTraces(), ), ))) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.php similarity index 62% rename from src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.html.php rename to src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.php index bbfc3f8c0b..001bc74633 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Exception/exception.php @@ -1,34 +1,10 @@ - - <?php echo htmlspecialchars($message, ENT_QUOTES, $charset) ?> (<?php echo $code ?> <?php echo $text ?>) + + <?php echo htmlspecialchars($manager->getMessage(), ENT_QUOTES, $view->getCharset()) ?> (<?php echo $manager->getStatusCode() ?> <?php echo $manager->getStatusText() ?>)