Completed
Branch 0.4-dev (79cc15)
by Evgenij
03:32
created

LeBase::getEventFlags()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 0
cts 13
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 3
nop 1
crap 12
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
    public function __construct()
46
    {
47
        $this->handle = event_base_new();
48
        if ($this->handle === false) {
49
            throw new \RuntimeException('Can not initialize libevent.');
50
        }
51
    }
52
53
    /**
54
     * Destructor
55
     */
56
    public function __destruct()
57
    {
58
        $this->destroyResource();
59
    }
60
61
    /**
62
     * Destroy libevent resource if it is opened
63
     *
64
     * @return void
65
     */
66
    private function destroyResource()
67
    {
68
        foreach ($this->events as $event) {
69
            $this->removeEvent($event);
70
        }
71
72
        if ($this->handle) {
73
            event_base_loopexit($this->handle, 1);
74
            event_base_free($this->handle);
75
            $this->handle = null;
76
        }
77
    }
78
79
    /**
80
     * Start event loop
81
     *
82
     * @return int
83
     */
84
    public function startLoop()
85
    {
86
        $this->isTerminating = false;
87
        return event_base_loop($this->handle);
88
    }
89
90
    /**
91
     * Break loop
92
     *
93
     * @return void
94
     */
95
    public function breakLoop()
96
    {
97
        $this->isTerminating = true;
98
        event_base_loopbreak($this->handle);
99
    }
100
101
    /**
102
     * Connect given array of sockets
103
     *
104
     * @param LeEvent $event Event object
105
     *
106
     * @return string
107
     */
108
    private function getEventKey(LeEvent $event)
109
    {
110
        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
    public function removeEvent(LeEvent $event)
121
    {
122
        $key = $this->getEventKey($event);
123
        if (isset($this->events[$key])) {
124
            unset($this->events[$key]);
125
        }
126
    }
127
128
    /**
129
     * Add event to list
130
     *
131
     * @param LeEvent $event LibEvent event object
132
     *
133
     * @return void
134
     */
135
    public function addEvent(LeEvent $event)
136
    {
137
        $this->removeEvent($event);
138
139
        $key                = $this->getEventKey($event);
140
        $this->events[$key] = $event;
141
142
        $timeout = $event->getTimeout();
143
        $flags   = $timeout !== null ? EV_TIMEOUT : 0;
144
145
        event_set(
146
            $event->getHandle(),
147
            $event->getRequestDescriptor()->getSocket()->getStreamResource(),
148
            $flags | $this->getEventFlags($event),
149
            [$this, 'libeventHandler'],
150
            $key
151
        );
152
153
        event_base_set($event->getHandle(), $this->handle);
154
        event_add($event->getHandle(), $timeout !== null ? $timeout * 1E6 : -1);
155
    }
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
    public function libeventHandler($streamResource, $eventFlags, $eventKey)
168
    {
169
        unset($streamResource); // make sensiolabs insight analyzer happy
170
        if (!isset($this->events[$eventKey])) {
171
            return;
172
        }
173
174
        $event = $this->events[$eventKey];
175
        $this->removeEvent($event);
176
        $this->onEvent($event, $eventFlags);
177
    }
178
179
    /**
180
     * Return set of flags for listening events
181
     *
182
     * @param LeEvent $event Libevent event object
183
     *
184
     * @return int
185
     */
186
    private function getEventFlags(LeEvent $event)
187
    {
188
        $operation = $event->getRequestDescriptor()->getOperation();
189
        if ($operation instanceof DelayedOperation) {
190
            return 0;
191
        }
192
193
        $map = [
194
            OperationInterface::OPERATION_READ  => EV_READ,
195
            OperationInterface::OPERATION_WRITE => EV_WRITE,
196
        ];
197
198
        $operation = $event->getRequestDescriptor()->getOperation()->getType();
199
200
        return isset($map[$operation]) ? $map[$operation] : 0;
201
    }
202
203
    /**
204
     * Process libevent event
205
     *
206
     * @param LeEvent $event Libevent event object
207
     * @param int     $eventFlags Event flag
208
     *
209
     * @return void
210
     */
211
    private function onEvent(LeEvent $event, $eventFlags)
212
    {
213
        $map = [
214
            LeCallbackInterface::EVENT_READ    => [ EV_READ, RequestDescriptor::RDS_READ ],
215
            LeCallbackInterface::EVENT_WRITE   => [ EV_WRITE, RequestDescriptor::RDS_WRITE ],
216
            LeCallbackInterface::EVENT_TIMEOUT => [ EV_TIMEOUT, 0 ],
217
        ];
218
219
        $descriptor = $event->getRequestDescriptor();
220
        foreach ($map as $eventType => $states) {
221
            if (!$this->isTerminating && $eventFlags & $states[0]) {
222
                $descriptor->setState($states[1]);
223
                $eventFlags &= ~EV_TIMEOUT;
224
                $event->fire($eventType);
225
            }
226
        }
227
    }
228
}
229