Completed
Push — master ( 1cb211...95ea2a )
by Evgenij
03:22
created

LeBase::onEvent()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

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