FlattenExceptionTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Debug\Tests\Exception;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\Debug\Exception\FlattenException;
  13. use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
  14. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  15. use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
  16. use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
  17. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  18. use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
  19. use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
  20. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  21. use Symfony\Component\HttpKernel\Exception\GoneHttpException;
  22. use Symfony\Component\HttpKernel\Exception\LengthRequiredHttpException;
  23. use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException;
  24. use Symfony\Component\HttpKernel\Exception\PreconditionRequiredHttpException;
  25. use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
  26. use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
  27. use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
  28. class FlattenExceptionTest extends TestCase
  29. {
  30. public function testStatusCode()
  31. {
  32. $flattened = FlattenException::create(new \RuntimeException(), 403);
  33. $this->assertEquals('403', $flattened->getStatusCode());
  34. $flattened = FlattenException::create(new \RuntimeException());
  35. $this->assertEquals('500', $flattened->getStatusCode());
  36. $flattened = FlattenException::create(new NotFoundHttpException());
  37. $this->assertEquals('404', $flattened->getStatusCode());
  38. $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"'));
  39. $this->assertEquals('401', $flattened->getStatusCode());
  40. $flattened = FlattenException::create(new BadRequestHttpException());
  41. $this->assertEquals('400', $flattened->getStatusCode());
  42. $flattened = FlattenException::create(new NotAcceptableHttpException());
  43. $this->assertEquals('406', $flattened->getStatusCode());
  44. $flattened = FlattenException::create(new ConflictHttpException());
  45. $this->assertEquals('409', $flattened->getStatusCode());
  46. $flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST')));
  47. $this->assertEquals('405', $flattened->getStatusCode());
  48. $flattened = FlattenException::create(new AccessDeniedHttpException());
  49. $this->assertEquals('403', $flattened->getStatusCode());
  50. $flattened = FlattenException::create(new GoneHttpException());
  51. $this->assertEquals('410', $flattened->getStatusCode());
  52. $flattened = FlattenException::create(new LengthRequiredHttpException());
  53. $this->assertEquals('411', $flattened->getStatusCode());
  54. $flattened = FlattenException::create(new PreconditionFailedHttpException());
  55. $this->assertEquals('412', $flattened->getStatusCode());
  56. $flattened = FlattenException::create(new PreconditionRequiredHttpException());
  57. $this->assertEquals('428', $flattened->getStatusCode());
  58. $flattened = FlattenException::create(new ServiceUnavailableHttpException());
  59. $this->assertEquals('503', $flattened->getStatusCode());
  60. $flattened = FlattenException::create(new TooManyRequestsHttpException());
  61. $this->assertEquals('429', $flattened->getStatusCode());
  62. $flattened = FlattenException::create(new UnsupportedMediaTypeHttpException());
  63. $this->assertEquals('415', $flattened->getStatusCode());
  64. if (class_exists(SuspiciousOperationException::class)) {
  65. $flattened = FlattenException::create(new SuspiciousOperationException());
  66. $this->assertEquals('400', $flattened->getStatusCode());
  67. }
  68. }
  69. public function testHeadersForHttpException()
  70. {
  71. $flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST')));
  72. $this->assertEquals(array('Allow' => 'POST'), $flattened->getHeaders());
  73. $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"'));
  74. $this->assertEquals(array('WWW-Authenticate' => 'Basic realm="My Realm"'), $flattened->getHeaders());
  75. $flattened = FlattenException::create(new ServiceUnavailableHttpException('Fri, 31 Dec 1999 23:59:59 GMT'));
  76. $this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders());
  77. $flattened = FlattenException::create(new ServiceUnavailableHttpException(120));
  78. $this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders());
  79. $flattened = FlattenException::create(new TooManyRequestsHttpException('Fri, 31 Dec 1999 23:59:59 GMT'));
  80. $this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders());
  81. $flattened = FlattenException::create(new TooManyRequestsHttpException(120));
  82. $this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders());
  83. }
  84. /**
  85. * @dataProvider flattenDataProvider
  86. */
  87. public function testFlattenHttpException(\Exception $exception, $statusCode)
  88. {
  89. $flattened = FlattenException::create($exception);
  90. $flattened2 = FlattenException::create($exception);
  91. $flattened->setPrevious($flattened2);
  92. $this->assertEquals($exception->getMessage(), $flattened->getMessage(), 'The message is copied from the original exception.');
  93. $this->assertEquals($exception->getCode(), $flattened->getCode(), 'The code is copied from the original exception.');
  94. $this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception');
  95. }
  96. /**
  97. * @dataProvider flattenDataProvider
  98. */
  99. public function testPrevious(\Exception $exception, $statusCode)
  100. {
  101. $flattened = FlattenException::create($exception);
  102. $flattened2 = FlattenException::create($exception);
  103. $flattened->setPrevious($flattened2);
  104. $this->assertSame($flattened2, $flattened->getPrevious());
  105. $this->assertSame(array($flattened2), $flattened->getAllPrevious());
  106. }
  107. /**
  108. * @requires PHP 7.0
  109. */
  110. public function testPreviousError()
  111. {
  112. $exception = new \Exception('test', 123, new \ParseError('Oh noes!', 42));
  113. $flattened = FlattenException::create($exception)->getPrevious();
  114. $this->assertEquals($flattened->getMessage(), 'Parse error: Oh noes!', 'The message is copied from the original exception.');
  115. $this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.');
  116. $this->assertEquals($flattened->getClass(), 'Symfony\Component\Debug\Exception\FatalThrowableError', 'The class is set to the class of the original exception');
  117. }
  118. /**
  119. * @dataProvider flattenDataProvider
  120. */
  121. public function testLine(\Exception $exception)
  122. {
  123. $flattened = FlattenException::create($exception);
  124. $this->assertSame($exception->getLine(), $flattened->getLine());
  125. }
  126. /**
  127. * @dataProvider flattenDataProvider
  128. */
  129. public function testFile(\Exception $exception)
  130. {
  131. $flattened = FlattenException::create($exception);
  132. $this->assertSame($exception->getFile(), $flattened->getFile());
  133. }
  134. /**
  135. * @dataProvider flattenDataProvider
  136. */
  137. public function testToArray(\Exception $exception, $statusCode)
  138. {
  139. $flattened = FlattenException::create($exception);
  140. $flattened->setTrace(array(), 'foo.php', 123);
  141. $this->assertEquals(array(
  142. array(
  143. 'message' => 'test',
  144. 'class' => 'Exception',
  145. 'trace' => array(array(
  146. 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123,
  147. 'args' => array(),
  148. )),
  149. ),
  150. ), $flattened->toArray());
  151. }
  152. public function flattenDataProvider()
  153. {
  154. return array(
  155. array(new \Exception('test', 123), 500),
  156. );
  157. }
  158. public function testArguments()
  159. {
  160. $dh = opendir(__DIR__);
  161. $fh = tmpfile();
  162. $incomplete = unserialize('O:14:"BogusTestClass":0:{}');
  163. $exception = $this->createException(array(
  164. (object) array('foo' => 1),
  165. new NotFoundHttpException(),
  166. $incomplete,
  167. $dh,
  168. $fh,
  169. function () {},
  170. array(1, 2),
  171. array('foo' => 123),
  172. null,
  173. true,
  174. false,
  175. 0,
  176. 0.0,
  177. '0',
  178. '',
  179. INF,
  180. NAN,
  181. ));
  182. $flattened = FlattenException::create($exception);
  183. $trace = $flattened->getTrace();
  184. $args = $trace[1]['args'];
  185. $array = $args[0][1];
  186. closedir($dh);
  187. fclose($fh);
  188. $i = 0;
  189. $this->assertSame(array('object', 'stdClass'), $array[$i++]);
  190. $this->assertSame(array('object', 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException'), $array[$i++]);
  191. $this->assertSame(array('incomplete-object', 'BogusTestClass'), $array[$i++]);
  192. $this->assertSame(array('resource', defined('HHVM_VERSION') ? 'Directory' : 'stream'), $array[$i++]);
  193. $this->assertSame(array('resource', 'stream'), $array[$i++]);
  194. $args = $array[$i++];
  195. $this->assertSame($args[0], 'object');
  196. $this->assertTrue('Closure' === $args[1] || is_subclass_of($args[1], '\Closure'), 'Expect object class name to be Closure or a subclass of Closure.');
  197. $this->assertSame(array('array', array(array('integer', 1), array('integer', 2))), $array[$i++]);
  198. $this->assertSame(array('array', array('foo' => array('integer', 123))), $array[$i++]);
  199. $this->assertSame(array('null', null), $array[$i++]);
  200. $this->assertSame(array('boolean', true), $array[$i++]);
  201. $this->assertSame(array('boolean', false), $array[$i++]);
  202. $this->assertSame(array('integer', 0), $array[$i++]);
  203. $this->assertSame(array('float', 0.0), $array[$i++]);
  204. $this->assertSame(array('string', '0'), $array[$i++]);
  205. $this->assertSame(array('string', ''), $array[$i++]);
  206. $this->assertSame(array('float', INF), $array[$i++]);
  207. // assertEquals() does not like NAN values.
  208. $this->assertEquals($array[$i][0], 'float');
  209. $this->assertTrue(is_nan($array[$i++][1]));
  210. }
  211. public function testRecursionInArguments()
  212. {
  213. $a = array('foo', array(2, &$a));
  214. $exception = $this->createException($a);
  215. $flattened = FlattenException::create($exception);
  216. $trace = $flattened->getTrace();
  217. $this->assertContains('*DEEP NESTED ARRAY*', serialize($trace));
  218. }
  219. public function testTooBigArray()
  220. {
  221. $a = array();
  222. for ($i = 0; $i < 20; ++$i) {
  223. for ($j = 0; $j < 50; ++$j) {
  224. for ($k = 0; $k < 10; ++$k) {
  225. $a[$i][$j][$k] = 'value';
  226. }
  227. }
  228. }
  229. $a[20] = 'value';
  230. $a[21] = 'value1';
  231. $exception = $this->createException($a);
  232. $flattened = FlattenException::create($exception);
  233. $trace = $flattened->getTrace();
  234. $this->assertSame($trace[1]['args'][0], array('array', array('array', '*SKIPPED over 10000 entries*')));
  235. $serializeTrace = serialize($trace);
  236. $this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace);
  237. $this->assertNotContains('*value1*', $serializeTrace);
  238. }
  239. private function createException($foo)
  240. {
  241. return new \Exception();
  242. }
  243. }