Completed
Push — master ( 5adbcd...41097b )
by Viktor
03:52 queued 43s
created

Server::setEvents()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 6
Ratio 100 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 6
loc 6
ccs 4
cts 4
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @link https://github.com/Izumi-kun/yii2-longpoll
4
 * @copyright Copyright (c) 2017 Viktor Khokhryakov
5
 * @license http://opensource.org/licenses/BSD-3-Clause
6
 */
7
8
namespace izumi\longpoll;
9
10
use izumi\longpoll\widgets\LongPoll;
11
use Yii;
12
use yii\base\InvalidConfigException;
13
use yii\base\InvalidParamException;
14
use yii\helpers\Json;
15
use yii\web\Response;
16
17
/**
18
 * Class implements long polling connection.
19
 *
20
 * @property EventInterface[]|null $triggeredEvents
21
 * @author Viktor Khokhryakov <[email protected]>
22
 */
23
class Server extends Response
24
{
25
    /**
26
     * @var callable
27
     */
28
    public $callback;
29
    /**
30
     * @var mixed response data
31
     */
32
    public $responseData;
33
    /**
34
     * @var array query params
35
     */
36
    public $responseParams = [];
37
    /**
38
     * @var int how long poll will be (in seconds).
39
     */
40
    public $timeout = 25;
41
    /**
42
     * @var int time between events check (in microseconds).
43
     */
44
    public $sleepTime = 250000;
45
    /**
46
     * @var EventCollectionInterface events for waiting (any).
47
     */
48
    public $eventCollection;
49
    /**
50
     * @var string event collection class name.
51
     */
52
    public $eventCollectionClass = 'izumi\longpoll\EventCollection';
53
    /**
54
     * @var array events (string eventId => int lastState) for waiting (any).
55
     */
56
    protected $lastStates = [];
57
    /**
58
     * @var EventInterface[]|null
59
     */
60
    protected $_triggeredEvents;
61
62
    /**
63
     * @inheritdoc
64
     */
65 8
    public function init()
66
    {
67 8
        if (!$this->eventCollection instanceof EventCollectionInterface) {
68 7
            $this->setEvents([]);
69
        }
70 8
    }
71
72
    /**
73
     * Prepares for sending the response.
74
     * @throws InvalidConfigException
75
     */
76 4
    protected function prepare()
77
    {
78 4
        $events = $this->eventCollection->getEvents();
79 4
        if (empty($events)) {
80 1
            throw new InvalidConfigException('At least one event should be added to the poll.');
81
        }
82 3
        foreach ($events as $eventKey => $event) {
83 3
            if (!isset($this->lastStates[$eventKey])) {
84 3
                $this->lastStates[$eventKey] = (int) Yii::$app->getRequest()->getQueryParam($event->getParamName());
85
            }
86
        }
87
88 3
        $this->version = '1.1';
89 3
        $this->getHeaders()
90 3
            ->set('Transfer-Encoding', 'chunked')
91 3
            ->set('Content-Type', 'application/json; charset=UTF-8');
92
93 3
        Yii::$app->getSession()->close();
94 3
    }
95
96
    /**
97
     * Sends the response content to the client.
98
     */
99 3
    protected function sendContent()
100
    {
101 3
        $events = $this->eventCollection->getEvents();
102 3
        $endTime = time() + $this->timeout;
103 3
        $connectionTestTime = time() + 1;
104 3
        if (!YII_ENV_TEST) {
105
            $this->clearOutputBuffers();
106
        }
107 3
        ignore_user_abort(true);
108
        do {
109 3
            $triggered = [];
110 3
            foreach ($events as $eventKey => $event) {
111 3
                $event->updateState();
112 3
                if ($event->getState() !== $this->lastStates[$eventKey]) {
113 3
                    $triggered[$eventKey] = $event;
114
                }
115
            }
116 3
            if (!empty($triggered)) {
117 3
                break;
118
            }
119 3
            usleep($this->sleepTime);
120 3
            if (time() >= $connectionTestTime) {
121 3
                echo '0';
122 3
                flush();
123 3
                if (connection_aborted()) {
124
                    Yii::trace('Client disconnected', __METHOD__);
125
                    Yii::$app->end();
126
                }
127 3
                $connectionTestTime++;
128
            }
129 3
        } while (time() < $endTime);
130
131 3
        $this->_triggeredEvents = $triggered;
132
133 3
        if (!empty($triggered) && is_callable($this->callback)) {
134 3
            call_user_func($this->callback, $this);
135
        }
136
137 3
        $params = (array) $this->responseParams;
138 3
        $json = Json::encode([
139 3
            'data' => $this->responseData,
140 3
            'params' => LongPoll::createPollParams($this->eventCollection, $params)
141
        ]);
142
143 3
        echo dechex(strlen($json)), "\r\n", $json, "\r\n";
144 3
        echo "0\r\n\r\n";
145 3
    }
146
147
    /**
148
     * @param EventInterface|string $event
149
     * @param int|null $lastState
150
     */
151 5
    public function addEvent($event, $lastState = null)
152
    {
153 5
        $event = $this->eventCollection->addEvent($event);
154 5
        if ($lastState !== null) {
155 5
            if (!is_int($lastState)) {
156 1
                throw new InvalidParamException('$lastState must be an integer');
157
            }
158 4
            $this->lastStates[$event->getKey()] = $lastState;
159
        }
160 4
    }
161
162
    /**
163
     * @param array|EventInterface[]|string $events the events for waiting (any).
164
     */
165 8 View Code Duplication
    public function setEvents($events)
166
    {
167 8
        if (!$this->eventCollection instanceof EventCollectionInterface) {
168 8
            $this->eventCollection = Yii::createObject($this->eventCollectionClass);
169
        }
170 8
        $this->eventCollection->setEvents($events);
171 8
    }
172
173
    /**
174
     * @return EventInterface[]|null triggered events during poll run (key => event)
175
     */
176 3
    public function getTriggeredEvents()
177
    {
178 3
        return $this->_triggeredEvents;
179
    }
180
181
}
182