Completed
Branch 0.4-dev (54e67b)
by Evgenij
02:52
created

LeBase::destroyResource()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.009

Importance

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