TraceableEventDispatcher.php 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  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\HttpKernel\Debug;
  11. use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher as BaseTraceableEventDispatcher;
  12. use Symfony\Component\HttpKernel\KernelEvents;
  13. /**
  14. * Collects some data about event listeners.
  15. *
  16. * This event dispatcher delegates the dispatching to another one.
  17. *
  18. * @author Fabien Potencier <fabien@symfony.com>
  19. */
  20. class TraceableEventDispatcher extends BaseTraceableEventDispatcher
  21. {
  22. protected function beforeDispatch(string $eventName, object $event): void
  23. {
  24. switch ($eventName) {
  25. case KernelEvents::REQUEST:
  26. $event->getRequest()->attributes->set('_stopwatch_token', substr(hash('sha256', uniqid(mt_rand(), true)), 0, 6));
  27. $this->stopwatch->openSection();
  28. break;
  29. case KernelEvents::VIEW:
  30. case KernelEvents::RESPONSE:
  31. // stop only if a controller has been executed
  32. if ($this->stopwatch->isStarted('controller')) {
  33. $this->stopwatch->stop('controller');
  34. }
  35. break;
  36. case KernelEvents::TERMINATE:
  37. $sectionId = $event->getRequest()->attributes->get('_stopwatch_token');
  38. if (null === $sectionId) {
  39. break;
  40. }
  41. // There is a very special case when using built-in AppCache class as kernel wrapper, in the case
  42. // of an ESI request leading to a `stale` response [B] inside a `fresh` cached response [A].
  43. // In this case, `$token` contains the [B] debug token, but the open `stopwatch` section ID
  44. // is equal to the [A] debug token. Trying to reopen section with the [B] token throws an exception
  45. // which must be caught.
  46. try {
  47. $this->stopwatch->openSection($sectionId);
  48. } catch (\LogicException) {
  49. }
  50. break;
  51. }
  52. }
  53. protected function afterDispatch(string $eventName, object $event): void
  54. {
  55. switch ($eventName) {
  56. case KernelEvents::CONTROLLER_ARGUMENTS:
  57. $this->stopwatch->start('controller', 'section');
  58. break;
  59. case KernelEvents::RESPONSE:
  60. $sectionId = $event->getRequest()->attributes->get('_stopwatch_token');
  61. if (null === $sectionId) {
  62. break;
  63. }
  64. try {
  65. $this->stopwatch->stopSection($sectionId);
  66. } catch (\LogicException) {
  67. // The stop watch service might have been reset in the meantime
  68. }
  69. break;
  70. case KernelEvents::TERMINATE:
  71. // In the special case described in the `preDispatch` method above, the `$token` section
  72. // does not exist, then closing it throws an exception which must be caught.
  73. $sectionId = $event->getRequest()->attributes->get('_stopwatch_token');
  74. if (null === $sectionId) {
  75. break;
  76. }
  77. try {
  78. $this->stopwatch->stopSection($sectionId);
  79. } catch (\LogicException) {
  80. }
  81. break;
  82. }
  83. }
  84. }