LeBase::addEvent()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 15
cts 15
cp 1
rs 9.3142
c 0
b 0
f 0
cc 3
eloc 14
nc 2
nop 1
crap 3
1
<?php
2
/**
3
 * Async sockets
4
 *
5
 * @copyright Copyright (c) 2015-2017, Efimov Evgenij <[email protected]>
6
 *
7
 * This source file is subject to the MIT license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
namespace AsyncSockets\RequestExecutor\LibEvent;
11
12
use AsyncSockets\Operation\DelayedOperation;
13
use AsyncSockets\Operation\OperationInterface;
14
use AsyncSockets\RequestExecutor\Metadata\RequestDescriptor;
15
16
/**
17
 * Class LeBase
18
 */
19
class LeBase
20
{
21
    /**
22
     * Handle to libevent handle
23
     *
24
     * @var resource
25
     */
26
    private $handle;
27
28
    /**
29
     * Array of registered events indexed by object id
30
     *
31
     * @var LeEvent[]
32
     */
33
    private $events = [];
34
35
    /**
36
     * Flag, whether loop is about to terminate
37
     *
38
     * @var bool
39
     */
40
    private $isTerminating = false;
41
42
    /**
43
     * LeBase constructor.
44
     */
45 68
    public function __construct()
46
    {
47 68
        $this->handle = event_base_new();
48 68
        if ($this->handle === false) {
49
            throw new \RuntimeException('Can not initialize libevent.');
50
        }
51 68
    }
52
53
    /**
54
     * Destructor
55
     */
56 68
    public function __destruct()
57
    {
58 68
        $this->destroyResource();
59 68
    }
60
61
    /**
62
     * Destroy libevent resource if it is opened
63
     *
64
     * @return void
65
     */
66 68
    private function destroyResource()
67
    {
68 68
        foreach ($this->events as $event) {
69
            $this->removeEvent($event);
70 68
        }
71
72 68
        if ($this->handle) {
73 68
            event_base_loopexit($this->handle, 1);
74 68
            event_base_free($this->handle);
75 68
            $this->handle = null;
76 68
        }
77 68
    }
78
79
    /**
80
     * Start event loop
81
     *
82
     * @return int
83
     */
84 60
    public function startLoop()
85
    {
86 60
        $this->isTerminating = false;
87 60
        return event_base_loop($this->handle);
88
    }
89
90
    /**
91
     * Break loop
92
     *
93
     * @return void
94
     */
95 2
    public function breakLoop()
96
    {
97 2
        $this->isTerminating = true;
98 2
        event_base_loopbreak($this->handle);
99 2
    }
100
101
    /**
102
     * Connect given array of sockets
103
     *
104
     * @param LeEvent $event Event object
105
     *
106
     * @return string
107
     */
108 53
    private function getEventKey(LeEvent $event)
109
    {
110 53
        return spl_object_hash($event->getRequestDescriptor());
111
    }
112
113
    /**
114
     * Remove event form list
115
     *
116
     * @param LeEvent $event LibEvent event object
117
     *
118
     * @return void
119
     */
120 53
    public function removeEvent(LeEvent $event)
121
    {
122 53
        $key = $this->getEventKey($event);
123 53
        if (isset($this->events[$key])) {
124 53
            unset($this->events[$key]);
125 53
        }
126 53
    }
127
128
    /**
129
     * Add event to list
130
     *
131
     * @param LeEvent $event LibEvent event object
132
     *
133
     * @return void
134
     */
135 53
    public function addEvent(LeEvent $event)
136
    {
137 53
        $this->removeEvent($event);
138
139 53
        $key                = $this->getEventKey($event);
140 53
        $this->events[$key] = $event;
141
142 53
        $timeout = $event->getTimeout();
143 53
        $flags   = $timeout !== null ? EV_TIMEOUT : 0;
144
145 53
        event_set(
146 53
            $event->getHandle(),
147 53
            $event->getRequestDescriptor()->getSocket()->getStreamResource(),
148 53
            $flags | $this->getEventFlags($event),
149 53
            [$this, 'libeventHandler'],
150
            $key
151 53
        );
152
153 53
        event_base_set($event->getHandle(), $this->handle);
154 53
        event_add($event->getHandle(), $timeout !== null ? $timeout * 1E6 : -1);
155 53
    }
156
157
    /**
158
     * Libevent event handler
159
     *
160
     * @param resource $streamResource Stream resource caused event
161
     * @param int      $eventFlags Occurred event flags
162
     * @param string   $eventKey Our internal event key
163
     *
164
     * @return void
165
     * @internal
166
     */
167 53
    public function libeventHandler($streamResource, $eventFlags, $eventKey)
168
    {
169 53
        unset($streamResource); // make sensiolabs insight analyzer happy
170 53
        if (!isset($this->events[$eventKey])) {
171 4
            return;
172
        }
173
174 53
        $event = $this->events[$eventKey];
175 53
        $this->removeEvent($event);
176 53
        $this->onEvent($event, $eventFlags);
177 23
    }
178
179
    /**
180
     * Return set of flags for listening events
181
     *
182
     * @param LeEvent $event Libevent event object
183
     *
184
     * @return int
185
     */
186 53
    private function getEventFlags(LeEvent $event)
187
    {
188 53
        $operation = $event->getRequestDescriptor()->getOperation();
189 53
        if ($operation instanceof DelayedOperation) {
190
            return 0;
191
        }
192
193
        $map = [
194 53
            OperationInterface::OPERATION_READ  => EV_READ,
195 53
            OperationInterface::OPERATION_WRITE => EV_WRITE,
196 53
        ];
197
198 53
        $operations = $event->getRequestDescriptor()->getOperation()->getTypes();
199
200 53
        $result = 0;
201 53
        foreach ($operations as $operation) {
202 53
            $result |= isset($map[$operation]) ? $map[$operation] : 0;
203 53
        }
204
205 53
        return $result;
206
    }
207
208
    /**
209
     * Process libevent event
210
     *
211
     * @param LeEvent $event Libevent event object
212
     * @param int     $eventFlags Event flag
213
     *
214
     * @return void
215
     */
216 53
    private function onEvent(LeEvent $event, $eventFlags)
217
    {
218
        $map = [
219 53
            LeCallbackInterface::EVENT_READ    => [ EV_READ, RequestDescriptor::RDS_READ ],
220 53
            LeCallbackInterface::EVENT_WRITE   => [ EV_WRITE, RequestDescriptor::RDS_WRITE ],
221 53
            LeCallbackInterface::EVENT_TIMEOUT => [ EV_TIMEOUT, 0 ],
222 53
        ];
223
224 53
        $descriptor = $event->getRequestDescriptor();
225 53
        foreach ($map as $eventType => $states) {
226 53
            if (!$this->isTerminating && $eventFlags & $states[0]) {
227 53
                $descriptor->setState($states[1]);
228 53
                $eventFlags &= ~EV_TIMEOUT;
229 53
                $event->fire($eventType);
230 23
            }
231 38
        }
232 23
    }
233
}
234