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.

95 lines
2.7 KiB

3 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. private $socket;
  23. /**
  24. * @param string $host The server host
  25. * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name
  26. */
  27. public function __construct(string $host, array $contextProviders = [])
  28. {
  29. if (false === strpos($host, '://')) {
  30. $host = 'tcp://'.$host;
  31. }
  32. $this->host = $host;
  33. $this->contextProviders = $contextProviders;
  34. }
  35. public function getContextProviders(): array
  36. {
  37. return $this->contextProviders;
  38. }
  39. public function write(Data $data): bool
  40. {
  41. $socketIsFresh = !$this->socket;
  42. if (!$this->socket = $this->socket ?: $this->createSocket()) {
  43. return false;
  44. }
  45. $context = ['timestamp' => microtime(true)];
  46. foreach ($this->contextProviders as $name => $provider) {
  47. $context[$name] = $provider->getContext();
  48. }
  49. $context = array_filter($context);
  50. $encodedPayload = base64_encode(serialize([$data, $context]))."\n";
  51. set_error_handler([self::class, 'nullErrorHandler']);
  52. try {
  53. if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
  54. return true;
  55. }
  56. if (!$socketIsFresh) {
  57. stream_socket_shutdown($this->socket, \STREAM_SHUT_RDWR);
  58. fclose($this->socket);
  59. $this->socket = $this->createSocket();
  60. }
  61. if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
  62. return true;
  63. }
  64. } finally {
  65. restore_error_handler();
  66. }
  67. return false;
  68. }
  69. private static function nullErrorHandler($t, $m)
  70. {
  71. // no-op
  72. }
  73. private function createSocket()
  74. {
  75. set_error_handler([self::class, 'nullErrorHandler']);
  76. try {
  77. return stream_socket_client($this->host, $errno, $errstr, 3, \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT);
  78. } finally {
  79. restore_error_handler();
  80. }
  81. }
  82. }