TraceableEventDispatcher.php 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  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('xxh128', 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. $this->stopwatch->stopSection($sectionId);
  65. break;
  66. case KernelEvents::TERMINATE:
  67. // In the special case described in the `preDispatch` method above, the `$token` section
  68. // does not exist, then closing it throws an exception which must be caught.
  69. $sectionId = $event->getRequest()->attributes->get('_stopwatch_token');
  70. if (null === $sectionId) {
  71. break;
  72. }
  73. try {
  74. $this->stopwatch->stopSection($sectionId);
  75. } catch (\LogicException) {
  76. }
  77. break;
  78. }
  79. }
  80. }