Completed
Branch 0.4-dev (d01bc8)
by Evgenij
02:48
created

LeBase::onEvent()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 5

Importance

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