1<?php 2 3namespace React\Datagram; 4 5use React\EventLoop\LoopInterface; 6use Evenement\EventEmitter; 7use Exception; 8 9class Socket extends EventEmitter implements SocketInterface 10{ 11 protected $loop; 12 protected $socket; 13 14 protected $buffer; 15 16 public $bufferSize = 65536; 17 18 public function __construct(LoopInterface $loop, $socket, Buffer $buffer = null) 19 { 20 $this->loop = $loop; 21 $this->socket = $socket; 22 23 if ($buffer === null) { 24 $buffer = new Buffer($loop, $socket); 25 } 26 $this->buffer = $buffer; 27 28 $that = $this; 29 $this->buffer->on('error', function ($error) use ($that) { 30 $that->emit('error', array($error, $that)); 31 }); 32 $this->buffer->on('close', array($this, 'close')); 33 34 $this->resume(); 35 } 36 37 public function getLocalAddress() 38 { 39 if ($this->socket === false) { 40 return null; 41 } 42 43 return $this->sanitizeAddress(@\stream_socket_get_name($this->socket, false)); 44 } 45 46 public function getRemoteAddress() 47 { 48 if ($this->socket === false) { 49 return null; 50 } 51 52 return $this->sanitizeAddress(@\stream_socket_get_name($this->socket, true)); 53 } 54 55 public function send($data, $remoteAddress = null) 56 { 57 $this->buffer->send($data, $remoteAddress); 58 } 59 60 public function pause() 61 { 62 $this->loop->removeReadStream($this->socket); 63 } 64 65 public function resume() 66 { 67 if ($this->socket !== false) { 68 $this->loop->addReadStream($this->socket, array($this, 'onReceive')); 69 } 70 } 71 72 public function onReceive() 73 { 74 try { 75 $data = $this->handleReceive($peer); 76 } 77 catch (Exception $e) { 78 // emit error message and local socket 79 $this->emit('error', array($e, $this)); 80 return; 81 } 82 83 $this->emit('message', array($data, $peer, $this)); 84 } 85 86 public function close() 87 { 88 if ($this->socket === false) { 89 return; 90 } 91 92 $this->emit('close', array($this)); 93 $this->pause(); 94 95 $this->handleClose(); 96 $this->socket = false; 97 $this->buffer->close(); 98 99 $this->removeAllListeners(); 100 } 101 102 public function end() 103 { 104 $this->buffer->end(); 105 } 106 107 private function sanitizeAddress($address) 108 { 109 if ($address === false) { 110 return null; 111 } 112 113 // check if this is an IPv6 address which includes multiple colons but no square brackets (PHP < 7.3) 114 $pos = \strrpos($address, ':'); 115 if ($pos !== false && \strpos($address, ':') < $pos && \substr($address, 0, 1) !== '[') { 116 $address = '[' . \substr($address, 0, $pos) . ']:' . \substr($address, $pos + 1); // @codeCoverageIgnore 117 } 118 return $address; 119 } 120 121 protected function handleReceive(&$peerAddress) 122 { 123 $data = \stream_socket_recvfrom($this->socket, $this->bufferSize, 0, $peerAddress); 124 125 if ($data === false) { 126 // receiving data failed => remote side rejected one of our packets 127 // due to the nature of UDP, there's no way to tell which one exactly 128 // $peer is not filled either 129 130 throw new Exception('Invalid message'); 131 } 132 133 $peerAddress = $this->sanitizeAddress($peerAddress); 134 135 return $data; 136 } 137 138 protected function handleClose() 139 { 140 \fclose($this->socket); 141 } 142} 143