SessionTrait::sessionHandler()   B
last analyzed

Complexity

Conditions 10
Paths 14

Size

Total Lines 78
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 10

Importance

Changes 0
Metric Value
eloc 34
c 0
b 0
f 0
dl 0
loc 78
ccs 32
cts 32
cp 1
rs 7.6666
cc 10
nc 14
nop 1
crap 10

How to fix   Long Method    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
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * php version 7.1.0
11
 *
12
 * @category  Web-security
13
 * @package   Shieldon
14
 * @author    Terry Lin <[email protected]>
15
 * @copyright 2019 terrylinooo
16
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
17
 * @link      https://github.com/terrylinooo/shieldon
18
 * @see       https://shieldon.io
19
 */
20
21
declare(strict_types=1);
22
23
namespace Shieldon\Firewall\Kernel;
24
25
use Shieldon\Firewall\Kernel\Enum;
26
use function Shieldon\Firewall\get_session_instance;
27
use function Shieldon\Firewall\create_new_session_instance;
28
use function time;
29
30
/*
31
 * The main functionality for this trait is to limit the online session amount.
32
 */
33
trait SessionTrait
34
{
35
    /**
36
     *   Public methods       | Desctiotion
37
     *  ----------------------|---------------------------------------------
38
     *   limitSession         | Limit the amount of the online users.
39
     *   getSessionCount      | Get the amount of the sessions.
40
     *   removeSessionsByIp   | Remove sessions using the same IP address.
41
     *  ----------------------|---------------------------------------------
42
     */
43
44
    /**
45
     * Are you willing to limit the online session amount?
46
     *
47
     * @var array
48
     */
49
    protected $sessionLimit = [
50
51
        // How many sessions will be available?
52
        // 0 = no limit.
53
        'count' => 0,
54
55
        // How many minutes will a session be availe to visit?
56
        // 0 = no limit.
57
        'period' => 0,
58
59
        // Only allow one session per IP address.
60
        // If this option is set to true, user with multiple sessions will be
61
        // removed from the session table.
62
        'unique_only' => false,
63
    ];
64
65
    /**
66
     * Record the online session status.
67
     * This will be enabled when $sessionLimit[count] > 0
68
     *
69
     * This array is recording a live data, not a setting value.
70
     *
71
     * @var array
72
     */
73
    protected $sessionStatus = [
74
75
        // Online session count.
76
        'count' => 0,
77
78
        // Current session order.
79
        'order' => 0,
80
81
        // Current waiting queue.
82
        'queue' => 0,
83
    ];
84
85
86
    /**
87
     * Current user's session data.
88
     *
89
     * @var array
90
     */
91
    protected $sessionData = [];
92
93
    /**
94
     * Limt online sessions.
95
     *
96
     * @param int $count   The amount of online users. If reached, users will be
97
     *                     in queue.
98
     * @param int $period  The period of time allows users browsing.
99
     *                     (unit: second)
100
     * @param bool $unique Allow only one session per IP address.
101
     *
102
     * @return void
103
     */
104
    public function limitSession(int $count = 1000, int $period = 300, bool $unique = false): void
105 100
    {
106
        $this->sessionLimit = [
107 100
            'count' => $count,
108 100
            'period' => $period,
109 100
            'unique_only' => $unique,
110 100
        ];
111 100
    }
112
113
    /**
114
     * Get online people count. If enable limitSession.
115
     *
116
     * @return int
117
     */
118
    public function getSessionCount(): int
119 5
    {
120
        return $this->sessionStatus['count'];
121 5
    }
122
123
    /**
124
     * Deal with online sessions.
125
     *
126
     * @param int $statusCode The response code.
127
     *
128
     * @return int The response code.
129
     */
130
    protected function sessionHandler($statusCode): int
131 68
    {
132
        if (Enum::RESPONSE_ALLOW !== $statusCode) {
133 68
            return $statusCode;
134 23
        }
135
136
        // If you don't enable `limit traffic`, ignore the following steps.
137
        if (empty($this->sessionLimit['count'])) {
138 67
            return Enum::RESPONSE_ALLOW;
139 39
        } else {
140
            // Get the proerties.
141
            $limit = (int) ($this->sessionLimit['count'] ?? 0);
142
            $period = (int) ($this->sessionLimit['period'] ?? 300);
143
            $now = time();
144 28
145 28
            $this->sessionData = $this->driver->getAll('session');
146 28
147
            $sessionPools = [];
148 28
149
            $i = 1;
150 28
            $sessionOrder = 0;
151
152 28
            $sessionId = get_session_instance()->getId();
153 28
154
            if (!empty($this->sessionData)) {
155 28
                foreach ($this->sessionData as $v) {
156
                    $sessionPools[] = $v['id'];
157 28
                    $lasttime = (int) $v['time'];
158 22
159 22
                    if ($sessionId === $v['id']) {
160 22
                        $sessionOrder = $i;
161
                    }
162 22
    
163 11
                    // Remove session if it expires.
164
                    if ($now - $lasttime > $period) {
165
                        $this->driver->delete($v['id'], 'session');
166
                    }
167 22
                    $i++;
168 4
                }
169
170 22
                if (0 === $sessionOrder) {
171
                    $sessionOrder = $i;
172
                }
173 22
            } else {
174 22
                $sessionOrder = 0;
175
            }
176
177 24
            // Count the online sessions.
178
            $this->sessionStatus['count'] = count($sessionPools);
179
            $this->sessionStatus['order'] = $sessionOrder;
180
            $this->sessionStatus['queue'] = $sessionOrder - $limit;
181 28
182 28
            if (!in_array($sessionId, $sessionPools)) {
183 28
                $this->sessionStatus['count']++;
184
            }
185 28
186 28
            /*
187
            if (!in_array($sessionId, $sessionPools)) {
188
                $this->sessionStatus['count']++;
189
190
                $data = [];
191
192
                // New session, record this data.
193
                $data['id'] = $sessionId;
194
                $data['ip'] = $this->ip;
195
                $data['time'] = $now;
196
                $data['microtimestamp'] = get_microtimestamp();
197
198
                $this->driver->save($sessionId, $data, 'session');
199
            }*/
200
201
            // Online session count reached the limit. So return RESPONSE_LIMIT_SESSION response code.
202
            if ($sessionOrder >= $limit) {
203
                return Enum::RESPONSE_LIMIT_SESSION;
204
            }
205 28
        }
206 5
207
        return Enum::RESPONSE_ALLOW;
208
    }
209
210 28
    /**
211
     * Remove sessions using the same IP address.
212
     * This method must be run after `sessionHandler`.
213
     *
214
     * @param string $ip An IP address
215
     *
216
     * @return void
217
     */
218
    protected function removeSessionsByIp(string $ip): void
219
    {
220
        if ($this->sessionLimit['unique_only']) {
221 40
            foreach ($this->sessionData as $v) {
222
                if ($v['ip'] === $ip) {
223 40
                    $this->driver->delete($v['id'], 'session');
224 1
                }
225 1
            }
226 1
        }
227
    }
228
229
    // @codeCoverageIgnoreStart
230
231
    /**
232
     * For testing propose. This method will create new Session.
233
     *
234
     * @param string $sessionId The session Id.
235
     *
236
     * @return void
237
     */
238
    protected function setSessionId(string $sessionId = ''): void
239
    {
240
        if ('' !== $sessionId) {
241
            create_new_session_instance($sessionId);
242
        }
243
    }
244
    // @codeCoverageIgnoreEnd
245
}
246