Total Complexity | 50 |
Total Lines | 255 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like StreamSelectLoop often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use StreamSelectLoop, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
52 | final class StreamSelectLoop implements LoopInterface |
||
53 | { |
||
54 | /** @internal */ |
||
55 | const MICROSECONDS_PER_SECOND = 1000000; |
||
56 | |||
57 | private $futureTickQueue; |
||
58 | private $timers; |
||
59 | private $readStreams = array(); |
||
60 | private $readListeners = array(); |
||
61 | private $writeStreams = array(); |
||
62 | private $writeListeners = array(); |
||
63 | private $running; |
||
64 | private $pcntl = false; |
||
65 | private $pcntlPoll = false; |
||
66 | private $signals; |
||
67 | |||
68 | public function __construct() |
||
69 | { |
||
70 | $this->futureTickQueue = new FutureTickQueue(); |
||
71 | $this->timers = new Timers(); |
||
72 | $this->pcntl = \function_exists('pcntl_signal') && \function_exists('pcntl_signal_dispatch'); |
||
73 | $this->pcntlPoll = $this->pcntl && !\function_exists('pcntl_async_signals'); |
||
74 | $this->signals = new SignalsHandler(); |
||
75 | |||
76 | // prefer async signals if available (PHP 7.1+) or fall back to dispatching on each tick |
||
77 | if ($this->pcntl && !$this->pcntlPoll) { |
||
78 | \pcntl_async_signals(true); |
||
79 | } |
||
80 | } |
||
81 | |||
82 | public function addReadStream($stream, $listener) |
||
83 | { |
||
84 | $key = (int) $stream; |
||
85 | |||
86 | if (!isset($this->readStreams[$key])) { |
||
87 | $this->readStreams[$key] = $stream; |
||
88 | $this->readListeners[$key] = $listener; |
||
89 | } |
||
90 | } |
||
91 | |||
92 | public function addWriteStream($stream, $listener) |
||
93 | { |
||
94 | $key = (int) $stream; |
||
95 | |||
96 | if (!isset($this->writeStreams[$key])) { |
||
97 | $this->writeStreams[$key] = $stream; |
||
98 | $this->writeListeners[$key] = $listener; |
||
99 | } |
||
100 | } |
||
101 | |||
102 | public function removeReadStream($stream) |
||
103 | { |
||
104 | $key = (int) $stream; |
||
105 | |||
106 | unset( |
||
107 | $this->readStreams[$key], |
||
108 | $this->readListeners[$key] |
||
109 | ); |
||
110 | } |
||
111 | |||
112 | public function removeWriteStream($stream) |
||
113 | { |
||
114 | $key = (int) $stream; |
||
115 | |||
116 | unset( |
||
117 | $this->writeStreams[$key], |
||
118 | $this->writeListeners[$key] |
||
119 | ); |
||
120 | } |
||
121 | |||
122 | public function addTimer($interval, $callback) |
||
123 | { |
||
124 | $timer = new Timer($interval, $callback, false); |
||
125 | |||
126 | $this->timers->add($timer); |
||
127 | |||
128 | return $timer; |
||
129 | } |
||
130 | |||
131 | public function addPeriodicTimer($interval, $callback) |
||
138 | } |
||
139 | |||
140 | public function cancelTimer(TimerInterface $timer) |
||
141 | { |
||
142 | $this->timers->cancel($timer); |
||
143 | } |
||
144 | |||
145 | public function futureTick($listener) |
||
146 | { |
||
147 | $this->futureTickQueue->add($listener); |
||
148 | } |
||
149 | |||
150 | public function addSignal($signal, $listener) |
||
151 | { |
||
152 | if ($this->pcntl === false) { |
||
153 | throw new \BadMethodCallException('Event loop feature "signals" isn\'t supported by the "StreamSelectLoop"'); |
||
154 | } |
||
155 | |||
156 | $first = $this->signals->count($signal) === 0; |
||
157 | $this->signals->add($signal, $listener); |
||
158 | |||
159 | if ($first) { |
||
160 | \pcntl_signal($signal, array($this->signals, 'call')); |
||
161 | } |
||
162 | } |
||
163 | |||
164 | public function removeSignal($signal, $listener) |
||
165 | { |
||
166 | if (!$this->signals->count($signal)) { |
||
167 | return; |
||
168 | } |
||
169 | |||
170 | $this->signals->remove($signal, $listener); |
||
171 | |||
172 | if ($this->signals->count($signal) === 0) { |
||
173 | \pcntl_signal($signal, \SIG_DFL); |
||
174 | } |
||
175 | } |
||
176 | |||
177 | public function run() |
||
213 | } |
||
214 | } |
||
215 | |||
216 | public function stop() |
||
217 | { |
||
218 | $this->running = false; |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * Wait/check for stream activity, or until the next timer is due. |
||
223 | * |
||
224 | * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever. |
||
225 | */ |
||
226 | private function waitForStreamActivity($timeout) |
||
254 | } |
||
255 | } |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * Emulate a stream_select() implementation that does not break when passed |
||
260 | * empty stream arrays. |
||
261 | * |
||
262 | * @param array $read An array of read streams to select upon. |
||
263 | * @param array $write An array of write streams to select upon. |
||
264 | * @param int|null $timeout Activity timeout in microseconds, or null to wait forever. |
||
265 | * |
||
266 | * @return int|false The total number of streams that are ready for read/write. |
||
267 | * Can return false if stream_select() is interrupted by a signal. |
||
268 | */ |
||
269 | private function streamSelect(array &$read, array &$write, $timeout) |
||
307 | } |
||
308 | } |
||
309 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.