Completed
Push — master ( 240506...474141 )
by Viktor
02:20
created

Server::run()   D

Complexity

Conditions 10
Paths 30

Size

Total Lines 42
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 10
eloc 27
c 2
b 0
f 1
nc 30
nop 0
dl 0
loc 42
rs 4.8196

1 Method

Rating   Name   Duplication   Size   Complexity  
A Server::prepare() 0 18 4

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 View Code Duplication
    public function init()
66
    {
67
        if (!$this->eventCollection instanceof EventCollectionInterface) {
68
            $this->eventCollection = Yii::createObject([
69
                'class' => $this->eventCollectionClass,
70
            ]);
71
        }
72
    }
73
74
    /**
75
     * Prepares for sending the response.
76
     * @throws InvalidConfigException
77
     */
78
    protected function prepare()
79
    {
80
        $events = $this->eventCollection->getEvents();
81
        if (empty($events)) {
82
            throw new InvalidConfigException('At least one event should be added to the poll.');
83
        }
84
        foreach ($events as $eventKey => $event) {
85
            if (!isset($this->lastStates[$eventKey])) {
86
                $this->lastStates[$eventKey] = (int) Yii::$app->getRequest()->getQueryParam($event->getParamName());
87
            }
88
        }
89
90
        $this->version = '1.1';
91
        $this->getHeaders()
92
            ->set('Transfer-Encoding', 'chunked')
93
            ->set('Content-Type', 'application/json; charset=UTF-8');
94
95
        Yii::$app->getSession()->close();
96
    }
97
98
    /**
99
     * Sends the response content to the client.
100
     */
101
    protected function sendContent()
102
    {
103
        $events = $this->eventCollection->getEvents();
104
        $endTime = time() + $this->timeout;
105
        $connectionTestTime = time() + 1;
106
        $this->clearOutputBuffers();
107
        do {
108
            $triggered = [];
109
            foreach ($events as $eventKey => $event) {
110
                $event->updateState();
111
                if ($event->getState() !== $this->lastStates[$eventKey]) {
112
                    $triggered[] = $event;
113
                }
114
            }
115
            if (!empty($triggered)) {
116
                break;
117
            }
118
            usleep($this->sleepTime);
119
            if (time() >= $connectionTestTime) {
120
                echo "0";
121
                flush();
122
                if (connection_aborted()) {
123
                    Yii::$app->end();
124
                }
125
                $connectionTestTime++;
126
            }
127
        } while (time() < $endTime);
128
129
        $this->_triggeredEvents = $triggered;
130
131
        if (!empty($triggered) && is_callable($this->callback)) {
132
            call_user_func($this->callback, $this);
133
        }
134
135
        $params = (array) $this->responseParams;
136
        $json = Json::encode([
137
            'data' => $this->responseData,
138
            'params' => LongPoll::createPollParams($this->eventCollection, $params)
139
        ]);
140
141
        echo dechex(strlen($json)), "\r\n", $json, "\r\n";
142
        echo "0\r\n\r\n";
143
    }
144
145
    /**
146
     * @param EventInterface|string $event
147
     * @param int|null $lastState
148
     */
149
    public function addEvent($event, $lastState = null)
150
    {
151
        $event = $this->eventCollection->addEvent($event);
152
        if ($lastState !== null) {
153
            if (!is_int($lastState)) {
154
                throw new InvalidParamException('$lastState must be an integer');
155
            }
156
            $this->lastStates[$event->getKey()] = $lastState;
157
        }
158
    }
159
160
    /**
161
     * @param array $events the events for waiting (any).
162
     */
163
    public function setEvents($events)
164
    {
165
        $this->eventCollection = Yii::createObject([
166
            'class' => $this->eventCollectionClass,
167
            'events' => $events,
168
        ]);
169
    }
170
171
    /**
172
     * @return EventInterface[]|null
173
     */
174
    public function getTriggeredEvents()
175
    {
176
        return $this->_triggeredEvents;
177
    }
178
179
}
180