8000 [Debug] Added ExceptionFlattener · symfony/symfony@e191df5 · GitHub
[go: up one dir, main page]

Skip to content

Commit e191df5

Browse files
committed
[Debug] Added ExceptionFlattener
1 parent 7b358a1 commit e191df5

File tree

6 files changed

+307
-6
lines changed

6 files changed

+307
-6
lines changed

src/Symfony/Component/Debug/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* added BufferingLogger for errors that happen before a proper logger is configured
88
* allow throwing from `__toString()` with `return trigger_error($e, E_USER_ERROR);`
99
* deprecate ExceptionHandler::createResponse
10+
* added ExceptionFlattener
1011

1112
2.7.0
1213
-----

src/Symfony/Component/Debug/Exception/FlattenException.php

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class FlattenException extends LegacyFlattenException
7272
private $headers;
7373
private $file;
7474
private $line;
75+
private $extras = array();
7576

7677
public static function create(\Exception $exception, $statusCode = null, array $headers = array())
7778
{
@@ -219,7 +220,8 @@ public function setTraceFromException(\Exception $exception)
219220
public function setTrace($trace, $file, $line)
220221
{
221222
$this->trace = array();
222-
$this->trace[] = array(
223+
224+
$this->trace[-1] = array(
223225
'namespace' => '',
224226
'short_class' => '',
225227
'class' => '',
@@ -229,7 +231,8 @@ public function setTrace($trace, $file, $line)
229231
'line' => $line,
230232
'args' => array(),
231233
);
232-
foreach ($trace as $entry) {
234+
235+
foreach ($trace as $key => $entry) {
233236
$class = '';
234237
$namespace = '';
235238
if (isset($entry['class'])) {
@@ -238,7 +241,7 @@ public function setTrace($trace, $file, $line)
238241
$namespace = implode('\\', $parts);
239242
}
240243

241-
$this->trace[] = array(
244+
$this->trace[$key] = array(
242245
'namespace' => $namespace,
243246
'short_class' => $class,
244247
'class' => isset($entry['class']) ? $entry['class'] : '',
@@ -251,6 +254,50 @@ public function setTrace($trace, $file, $line)
251254
}
252255
}
253256

257+
/**
258+
* Replaces trace.
259+
*
260+
* @param array $trace The trace
261+
*/
262+
public function replaceTrace($trace)
263+
{
264+
$this->trace = (array) $trace;
265+
}
266+
267+
/**
268+
* Returns all extras.
269+
*
270+
* @return array
271+
*/
272+
public function getExtras()
273+
{
274+
return $this->extras;
275+
}
276+
277+
/**
278+
* Returns an extra value.
279+
*
280+
* @param string $name The name of the extra
281+
* @param mixed $default The value to return if the extra doesn't exist
282+
*
283+
* @return mixed
284+
*/
285+
public function getExtra($name, $default = null)
286+
{
287+
return array_key_exists($name, $this->extras) ? $this->extras[$name] : $default;
288+
}
289+
290+
/**
291+
* Sets an extra value.
292+
*
293+
* @param string $name The name of the extra
294+
* @param mixed $value The value
295+
*/
296+
public function setExtra($name, $value)
297+
{
298+
$this->extras[$name] = $value;
299+
}
300+
254301
private function flattenArgs($args, $level = 0, &$count = 0)
255302
{
256303
$result = array();
Lines changed: 88 additions & 0 deletions
< 10000 th scope="col">Diff line change
Original file line numberDiff line number
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Debug;
13+
14+
use Symfony\Component\Debug\Exception\FlattenException;
15+
16+
/**
17+
* ExceptionFlattener converts an Exception to FlattenException.
18+
*
19+
* @author Martin Hasoň <martin.hason@gmail.com>
20+
*/
21+
class ExceptionFlattener
22+
{
23+
/**
24+
* @var FlattenExceptionProcessorInterface[]
25+
*/
26+
private $processors = array();
27+
28+
/**
29+
* Constructor.
30+
*
31+
* @param array $processors The collection of processors
32+
*/
33+
public function __construct($processors = array())
34+
{
35+
foreach ($processors as $processor) {
36+
$this->addProcessor($processor);
37+
}
38+
}
39+
40+
/**
41+
* Adds an exception processor.
42+
*
43+
* @param FlattenExceptionProcessorInterface $processor
44+
*/
45+
public function addProcessor(FlattenExceptionProcessorInterface $processor)
46+
{
47+
$this->processors[] = $processor;
48+
}
49+
50+
/**
51+
* Flattens an exception.
52+
*
53+
* @param \Exception $exception The raw exception
54+
*
55+
* @return FlattenException
56+
*/
57+
public function flatten(\Exception $exception)
58+
{
59+
$exceptions = array();
60+
do {
61+
$exceptions[] = $exception;
62+
} while ($exception = $exception->getPrevious());
63+
64+
$previous = null;
65+
foreach (array_reverse($exceptions, true) as $position => $exception) {
66+
$e = new FlattenException();
67+
$e->setMessage($exception->getMessage());
68+
$e->setCode($exception->getCode());
69+
$e->setClass(get_class($exception));
70+
$e->setFile($exception->getFile());
71+
$e->setLine($exception->getLine());
72+
$e->setTraceFromException($exception);
73+
if (null !== $previous) {
74+
$e->setPrevious($previous);
75+
}
76+
77+
foreach ($this->processors as $processor) {
78+
if ($newE = $processor->process($exception, $e, 0 === $position)) {
79+
$e = $newE;
80+
}
81+
}
82+
83+
$previous = $e;
84+
}
85+
86+
return $e;
87+
}
88+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Debug;
13+
14+
use Symfony\Component\Debug\Exception\FlattenException;
15+
16+
/**
17+
* @author Martin Hasoň <martin.hason@gmail.com>
18+
*/
19+
interface FlattenExceptionProcessorInterface
20+
{
21+
/**
22+
* Process a flattened exception.
23+
*
24+
* @param \Exception $exception The raw exception
25+
* @param FlattenException $flattenException The flattened exception
26+
* @param bool $master Whether it is a master exception
27+
*
28+
* @return FlattenException
29+
*/
30+
public function process(\Exception $exception, FlattenException $flattenException, $master);
31+
}

src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ public function testToArray(\Exception $exception, $statusCode)
161161
array(
162162
'message' => 'test',
163163
'class' => 'Exception',
164-
'trace' => array(array(
164+
'trace' => array(-1 => array(
165165
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123,
166166
'args' => array(),
167167
)),
@@ -235,12 +235,12 @@ public function testSetTraceIncompleteClass()
235235
'message' => 'test',
236236
'class' => 'Exception',
237237
'trace' => array(
238-
array(
238+
-1 => array(
239239
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '',
240240
'file' => 'foo.php', 'line' => 123,
241241
'args' => array(),
242242
),
243-
array(
243+
0 => array(
244244
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => 'test',
245245
'file' => __FILE__, 'line' => 123,
246246
'args' => array(
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Debug\Tests;
13+
14+
use Symfony\Component\Debug\Exception\FlattenException;
15+
use Symfony\Component\Debug\ExceptionFlattener;
16+
use Symfony\Component\Debug\FlattenExceptionProcessorInterface;
17+
18+
class ExceptionFlattenerTest extends \PHPUnit_Framework_TestCase
19+
{
20+
private $flattener;
21+
22+
protected function setUp()
23+
{
24+
$this->flattener = new ExceptionFlattener();
25+
}
26+
27+
public function testFlattenException()
28+
{
29+
$exception = new \RuntimeException('Runtime exception');
30+
$flattened = $this->flattener->flatten($exception);
31+
32+
$this->assertEquals($exception->getMessage(), $flattened->getMessage());
33+
$this->assertEquals($exception->getCode(), $flattened->getCode());
34+
$this->assertEquals($exception->getFile(), $flattened->getFile());
35+
$this->assertEquals($exception->getLine(), $flattened->getLine());
36+
$this->assertInstanceOf($flattened->getClass(), $exception);
37+
}
38+
39+
public function testFlattenPreviousException()
40+
{
41+
$exception1 = new \OutOfRangeException('Out of range exception');
42+
$exception2 = new \InvalidArgumentException('Invalid argument exception', null, $exception1);
43+
$exception3 = new \RuntimeException('Runtime exception', null, $exception2);
44+
45+
$flattened = $this->flattener->flatten($exception3);
46+
$this->assertCount(2, $flattened->getAllPrevious());
47+
$this->assertInstanceOf('Symfony\Component\Debug\Exception\FlattenException', $flattened->getPrevious());
48+
$this->assertInstanceOf(
49+
'Symfony\Component\Debug\Exception\FlattenException',
50+
$flattened->getPrevious()->getPrevious()
51+
);
52+
}
53+
54+
public function testFlattenWithProcessor()
55+
{
56+
$this->flattener->addProcessor(new TagTraceProcessor());
57+
58+
$exception = new \RuntimeException('Runtime exception');
59+
$flattened = $this->flattener->flatten($exception);
60+
foreach ($flattened->getTrace() as $position => $entry) {
61+
if (-1 === $position) {
62+
$this->assertFalse(array_key_exists('tag', $entry));
63+
} else {
64+
$this->assertArrayHasKey('tag', $entry);
65+
}
66+
}
67+
}
68+
69+
public function testProcessorReplaceException()
70+
{
71+
$this->flattener->addProcessor(new EmptyExceptionProcessor());
72+
73+
$exception = new \RuntimeException('Runtime exception');
74+
$flattened = $this->flattener->flatten($exception);
75+
76+
$this->assertNull($flattened->getMessage());
77+
$this->assertNull($flattened->getCode());
78+
$this->assertNull($flattened->getFile());
79+
$this->assertNull($flattened->getLine());
80+
}
81+
82+
public function testProcessOnlyMaterException()
83+
{
84+
$exception1 = new \OutOfRangeException('Out of range exception');
85+
$exception2 = new \InvalidArgumentException('Invalid argument exception', null, $exception1);
86+
$exception3 = new \RuntimeException('Runtime exception', null, $exception2);
87+
88+
$this->flattener->addProcessor(new MasterExtraProcessor());
89+
$flattened = $this->flattener->flatten($exception3);
90+
91+
$this->assertEquals(array('tags' => array('master')), $flattened->getExtras());
92+
foreach ($flattened->getAllPrevious() as $exception) {
93+
$this->assertEquals(array(), $exception->getExtras());
94+
}
95+
}
96+
}
97+
98+
class TagTraceProcessor implements FlattenExceptionProcessorInterface
99+
{
100+
public function process(\Exception $exception, FlattenException $flattenException, $master)
101+
{
102+
$trace = $flattenException->getTrace();
103+
104+
foreach ($exception->getTrace() as $key => $entry) {
105+
if (!isset($trace[$key])) {
106+
continue;
107+
}
108+
109+
$trace[$key]['tag'] = 'value';
110+
}
111+
112+
$flattenException->replaceTrace($trace);
113+
}
114+
}
115+
116+
class EmptyExceptionProcessor implements FlattenExceptionProcessorInterface
117+
{
118+
public function process(\Exception $exception, FlattenException $flattenException, $master)
119+
{
120+
return new FlattenException();
121+
}
122+
}
123+
124+
class MasterExtraProcessor implements FlattenExceptionProcessorInterface
125+
{
126+
public function process(\Exception $exception, FlattenException $flattenException, $master)
127+
{
128+
if (!$master) {
129+
return;
130+
}
131+
132+
$flattenException->setExtra('tags', array('master'));
133+
}
134+
}

0 commit comments

Comments
 (0)
0