You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
2.7 KiB

2 years ago
  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\Server;
  11. use Symfony\Component\VarDumper\Cloner\Data;
  12. use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
  13. /**
  14. * Forwards serialized Data clones to a server.
  15. *
  16. * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
  17. */
  18. class Connection
  19. {
  20. private $host;
  21. private $contextProviders;
  22. /**
  23. * @var resource|null
  24. */
  25. private $socket;
  26. /**
  27. * @param string $host The server host
  28. * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name
  29. */
  30. public function __construct(string $host, array $contextProviders = [])
  31. {
  32. if (!str_contains($host, '://')) {
  33. $host = 'tcp://'.$host;
  34. }
  35. $this->host = $host;
  36. $this->contextProviders = $contextProviders;
  37. }
  38. public function getContextProviders(): array
  39. {
  40. return $this->contextProviders;
  41. }
  42. public function write(Data $data): bool
  43. {
  44. $socketIsFresh = !$this->socket;
  45. if (!$this->socket = $this->socket ?: $this->createSocket()) {
  46. return false;
  47. }
  48. $context = ['timestamp' => microtime(true)];
  49. foreach ($this->contextProviders as $name => $provider) {
  50. $context[$name] = $provider->getContext();
  51. }
  52. $context = array_filter($context);
  53. $encodedPayload = base64_encode(serialize([$data, $context]))."\n";
  54. set_error_handler([self::class, 'nullErrorHandler']);
  55. try {
  56. if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
  57. return true;
  58. }
  59. if (!$socketIsFresh) {
  60. stream_socket_shutdown($this->socket, \STREAM_SHUT_RDWR);
  61. fclose($this->socket);
  62. $this->socket = $this->createSocket();
  63. }
  64. if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
  65. return true;
  66. }
  67. } finally {
  68. restore_error_handler();
  69. }
  70. return false;
  71. }
  72. private static function nullErrorHandler(int $t, string $m)
  73. {
  74. // no-op
  75. }
  76. private function createSocket()
  77. {
  78. set_error_handler([self::class, 'nullErrorHandler']);
  79. try {
  80. return stream_socket_client($this->host, $errno, $errstr, 3, \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT);
  81. } finally {
  82. restore_error_handler();
  83. }
  84. }
  85. }