1 | <?php |
||
42 | final class Connection implements ConnectionInterface |
||
43 | { |
||
44 | private $transport; |
||
45 | private $authority; |
||
46 | private $vhost; |
||
47 | private $protocol; |
||
48 | private $socket; |
||
49 | private $timeout; |
||
50 | private $select; |
||
51 | private $read; |
||
52 | private $closed = true; |
||
53 | private $opening = true; |
||
54 | private $maxChannels; |
||
55 | private $maxFrameSize; |
||
56 | private $heartbeat; |
||
57 | private $clock; |
||
58 | private $lastReceivedData; |
||
59 | |||
60 | 88 | public function __construct( |
|
61 | Transport $transport, |
||
62 | UrlInterface $server, |
||
63 | Protocol $protocol, |
||
64 | ElapsedPeriod $timeout, |
||
65 | TimeContinuumInterface $clock |
||
66 | ) { |
||
67 | 88 | $this->transport = $transport; |
|
68 | 88 | $this->authority = $server->authority(); |
|
69 | 88 | $this->vhost = $server->path(); |
|
70 | 88 | $this->protocol = $protocol; |
|
71 | 88 | $this->timeout = $timeout; |
|
72 | 88 | $this->buildSocket(); |
|
73 | 88 | $this->read = new FrameReader; |
|
74 | 88 | $this->maxChannels = new MaxChannels(0); |
|
75 | 88 | $this->maxFrameSize = new MaxFrameSize(0); |
|
76 | 88 | $this->heartbeat = $timeout; |
|
77 | 88 | $this->clock = $clock; |
|
78 | 88 | $this->lastReceivedData = $clock->now(); |
|
79 | |||
80 | 88 | $this->open(); |
|
81 | 88 | } |
|
82 | |||
83 | 80 | public function protocol(): Protocol |
|
84 | { |
||
85 | 80 | return $this->protocol; |
|
86 | } |
||
87 | |||
88 | 88 | public function send(Frame $frame): ConnectionInterface |
|
89 | { |
||
90 | 88 | if (!$this->maxChannels->allows($frame->channel()->toInt())) { |
|
91 | throw new FrameChannelExceedAllowedChannelNumber( |
||
92 | $frame->channel(), |
||
93 | $this->maxChannels |
||
94 | ); |
||
95 | } |
||
96 | |||
97 | 88 | $frame = (new Str((string) $frame))->toEncoding('ASCII'); |
|
98 | |||
99 | 88 | if (!$this->maxFrameSize->allows($frame->length())) { |
|
100 | throw new FrameExceedAllowedSize( |
||
101 | $frame->length(), |
||
102 | $this->maxFrameSize |
||
103 | ); |
||
104 | } |
||
105 | |||
106 | 88 | $this->socket->write($frame); |
|
107 | |||
108 | 88 | return $this; |
|
109 | } |
||
110 | |||
111 | /** |
||
112 | * {@inheritdoc} |
||
113 | */ |
||
114 | 88 | public function wait(string ...$names): Frame |
|
115 | { |
||
116 | do { |
||
117 | 88 | if (!$this->opening && $this->closed()) { |
|
118 | throw new ConnectionClosed; |
||
119 | } |
||
120 | |||
121 | 88 | $now = $this->clock->now(); |
|
122 | 88 | $elapsedPeriod = $now->elapsedSince($this->lastReceivedData); |
|
|
|||
123 | |||
124 | 88 | if ($elapsedPeriod->longerThan($this->heartbeat)) { |
|
125 | 17 | $this->send(Frame::heartbeat()); |
|
126 | } |
||
127 | |||
128 | 88 | $streams = ($this->select)(); |
|
129 | 88 | } while (!$streams->get('read')->contains($this->socket)); |
|
130 | |||
131 | 88 | $frame = ($this->read)($this->socket, $this->protocol); |
|
132 | 88 | $this->lastReceivedData = $this->clock->now(); |
|
133 | |||
134 | 88 | if ($frame->type() === Type::heartbeat()) { |
|
135 | return $this->wait(...$names); |
||
136 | } |
||
137 | |||
138 | 88 | if (count($names) === 0) { |
|
139 | 32 | return $frame; |
|
140 | } |
||
141 | |||
142 | 88 | if ($frame->type() !== Type::method()) { |
|
143 | //someone must have forgot a wait() call |
||
144 | throw new ExpectedMethodFrame($frame->type()); |
||
145 | } |
||
146 | |||
147 | 88 | foreach ($names as $name) { |
|
148 | 88 | if ($frame->is($this->protocol->method($name))) { |
|
149 | 88 | return $frame; |
|
150 | } |
||
151 | } |
||
152 | |||
153 | 4 | if ($frame->is($this->protocol->method('connection.close'))) { |
|
154 | 2 | $this->send($this->protocol->connection()->closeOk()); |
|
155 | 2 | $this->closed = true; |
|
156 | |||
157 | 2 | throw ConnectionClosed::byServer( |
|
158 | 2 | (string) $frame->values()->get(1)->original(), |
|
159 | 2 | $frame->values()->get(0)->original()->value(), |
|
160 | 2 | new Method( |
|
161 | 2 | $frame->values()->get(2)->original()->value(), |
|
162 | 2 | $frame->values()->get(3)->original()->value() |
|
163 | ) |
||
164 | ); |
||
165 | } |
||
166 | |||
167 | 2 | throw new UnexpectedFrame($frame, ...$names); |
|
168 | } |
||
169 | |||
170 | 36 | public function maxFrameSize(): MaxFrameSize |
|
171 | { |
||
172 | 36 | return $this->maxFrameSize; |
|
173 | } |
||
174 | |||
175 | 72 | public function close(): void |
|
176 | { |
||
177 | 72 | if ($this->closed()) { |
|
178 | return; |
||
179 | } |
||
180 | |||
181 | $this |
||
182 | 72 | ->send($this->protocol->connection()->close(new Close)) |
|
183 | 72 | ->wait('connection.close-ok'); |
|
184 | 72 | $this->socket->close(); |
|
185 | 72 | $this->closed = true; |
|
186 | 72 | } |
|
187 | |||
188 | 88 | public function closed(): bool |
|
189 | { |
||
190 | 88 | return $this->closed || $this->socket->closed(); |
|
191 | } |
||
192 | |||
193 | 88 | private function buildSocket(): void |
|
201 | |||
202 | 88 | private function open(): void |
|
203 | { |
||
204 | 88 | if (!$this->closed()) { |
|
205 | return; |
||
206 | } |
||
207 | |||
208 | 88 | $this->start(); |
|
215 | |||
216 | 88 | private function start(): void |
|
258 | |||
259 | 88 | private function handshake(): void |
|
291 | |||
292 | 88 | private function openVHost(): void |
|
300 | } |
||
301 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: