VarDumper.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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\VarDumper;
  11. use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\RequestStack;
  14. use Symfony\Component\VarDumper\Caster\ReflectionCaster;
  15. use Symfony\Component\VarDumper\Cloner\VarCloner;
  16. use Symfony\Component\VarDumper\Dumper\CliDumper;
  17. use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider;
  18. use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider;
  19. use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
  20. use Symfony\Component\VarDumper\Dumper\ContextualizedDumper;
  21. use Symfony\Component\VarDumper\Dumper\DataDumperInterface;
  22. use Symfony\Component\VarDumper\Dumper\HtmlDumper;
  23. use Symfony\Component\VarDumper\Dumper\ServerDumper;
  24. // Load the global dump() function
  25. require_once __DIR__.'/Resources/functions/dump.php';
  26. /**
  27. * @author Nicolas Grekas <p@tchwork.com>
  28. */
  29. class VarDumper
  30. {
  31. /**
  32. * @var callable|null
  33. */
  34. private static $handler;
  35. public static function dump(mixed $var, ?string $label = null): mixed
  36. {
  37. if (null === self::$handler) {
  38. self::register();
  39. }
  40. return (self::$handler)($var, $label);
  41. }
  42. public static function setHandler(?callable $callable): ?callable
  43. {
  44. $prevHandler = self::$handler;
  45. // Prevent replacing the handler with expected format as soon as the env var was set:
  46. if (isset($_SERVER['VAR_DUMPER_FORMAT'])) {
  47. return $prevHandler;
  48. }
  49. self::$handler = $callable;
  50. return $prevHandler;
  51. }
  52. private static function register(): void
  53. {
  54. $cloner = new VarCloner();
  55. $cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);
  56. $format = $_SERVER['VAR_DUMPER_FORMAT'] ?? null;
  57. $dumper = match ($format) {
  58. 'html' => new HtmlDumper(),
  59. 'cli' => new CliDumper(),
  60. 'server' => self::selectDumperForAccept($_SERVER['VAR_DUMPER_SERVER'] ?? '127.0.0.1:9912'),
  61. default => self::selectDumperForAccept(
  62. $format && 'tcp' === parse_url($format, \PHP_URL_SCHEME) ? $format : null,
  63. ),
  64. };
  65. if (!$dumper instanceof ServerDumper) {
  66. $dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]);
  67. }
  68. self::$handler = static function ($var, ?string $label = null) use ($cloner, $dumper) {
  69. $var = $cloner->cloneVar($var);
  70. if (null !== $label) {
  71. $var = $var->withContext(['label' => $label]);
  72. }
  73. $dumper->dump($var);
  74. };
  75. }
  76. private static function selectDumperForAccept(?string $serverHost): DataDumperInterface
  77. {
  78. $isCliSapi = \in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true);
  79. $accept = $_SERVER['HTTP_ACCEPT'] ?? ($isCliSapi ? 'txt' : 'html');
  80. $dumper = match (true) {
  81. str_contains($accept, 'html'), str_contains($accept, '*/*') => new HtmlDumper(),
  82. $isCliSapi => new CliDumper(),
  83. default => new CliDumper('php://output'),
  84. };
  85. if (null !== $serverHost) {
  86. $dumper = new ServerDumper($serverHost, $dumper, self::getDefaultContextProviders());
  87. }
  88. return $dumper;
  89. }
  90. private static function getDefaultContextProviders(): array
  91. {
  92. $contextProviders = [];
  93. if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && class_exists(Request::class)) {
  94. $requestStack = new RequestStack();
  95. $requestStack->push(Request::createFromGlobals());
  96. $contextProviders['request'] = new RequestContextProvider($requestStack);
  97. }
  98. $fileLinkFormatter = class_exists(FileLinkFormatter::class) ? new FileLinkFormatter(null, $requestStack ?? null) : null;
  99. return $contextProviders + [
  100. 'cli' => new CliContextProvider(),
  101. 'source' => new SourceContextProvider(null, null, $fileLinkFormatter),
  102. ];
  103. }
  104. }