Passed
Push — develop ( 33032a...666b96 )
by Henry
02:03
created

Session   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 183
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 72
c 2
b 0
f 0
dl 0
loc 183
rs 10
wmc 20

6 Methods

Rating   Name   Duplication   Size   Complexity  
A save() 0 20 4
A terminate() 0 10 2
B getFromRequest() 0 30 8
A getByHandle() 0 3 1
A updateSession() 0 21 3
A generateUniqueHandle() 0 9 2
1
<?php
2
/**
3
 * This file is part of the Divergence package.
4
 *
5
 * (c) Henry Paradiz <[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
11
namespace Divergence\Models\Auth;
12
13
use Divergence\Models\Model;
14
use Divergence\Models\Relations;
15
16
/**
17
 * Session object
18
 *
19
 * @author Henry Paradiz <[email protected]>
20
 * @author Chris Alfano <[email protected]>
21
 * @inheritDoc
22
 * @property string $Handle Unique identifier for this session used by the cookie.
23
 * @property string $LastRequest Timestamp of the last time this session was updated.
24
 * @property string $Binary Actually raw binary in the string
25
 */
26
class Session extends Model
27
{
28
    use Relations;
29
30
    // Session configurables
31
    public static $cookieName = 's';
32
    public static $cookieDomain = null;
33
    public static $cookiePath = '/';
34
    public static $cookieSecure = false;
35
    public static $cookieExpires = false;
36
    public static $timeout = 31536000; //3600;
37
38
    // support subclassing
39
    public static $rootClass = __CLASS__;
40
    public static $defaultClass = __CLASS__;
41
    public static $subClasses = [__CLASS__];
42
43
    // ActiveRecord configuration
44
    public static $tableName = 'sessions';
45
    public static $singularNoun = 'session';
46
    public static $pluralNoun = 'sessions';
47
48
    public static $fields = [
49
        'ContextClass' => null,
50
        'ContextID' => null,
51
        'Handle' => [
52
            'unique' => true,
53
        ],
54
        'LastRequest' => [
55
            'type' => 'timestamp',
56
            'notnull' => false,
57
        ],
58
        'LastIP' => [
59
            'type' => 'binary',
60
            'length' => 16,
61
        ],
62
    ];
63
64
65
    /**
66
     * Gets or sets up a session based on current cookies.
67
     * Will always update the current session's LastIP and LastRequest fields.
68
     *
69
     * @param boolean $create
70
     * @return static|false
71
     */
72
    public static function getFromRequest($create = true)
73
    {
74
        $sessionData = [
75
            'LastIP' => inet_pton($_SERVER['REMOTE_ADDR']),
76
            'LastRequest' => time(),
77
        ];
78
79
        // try to load from cookie
80
        if (!empty($_COOKIE[static::$cookieName])) {
81
            if ($Session = static::getByHandle($_COOKIE[static::$cookieName])) {
82
                // update session & check expiration
83
                $Session = static::updateSession($Session, $sessionData);
84
            }
85
        }
86
        // try to load from any request method
87
        if (empty($Session) && !empty($_REQUEST[static::$cookieName])) {
88
            if ($Session = static::getByHandle($_REQUEST[static::$cookieName])) {
89
                // update session & check expiration
90
                $Session = static::updateSession($Session, $sessionData);
91
            }
92
        }
93
        if (!empty($Session)) {
94
            // session found
95
            return $Session;
96
        } elseif ($create) {
97
            // create session
98
            return static::create($sessionData, true);
99
        } else {
100
            // no session available
101
            return false;
102
        }
103
    }
104
105
    public static function updateSession(Session $Session, $sessionData)
106
    {
107
        // check timestamp
108
        if (time() > $Session->LastRequest + static::$timeout) {
109
            $Session->terminate();
110
111
            return false;
112
        } else {
113
            // update session
114
            $Session->setFields($sessionData);
115
            if (function_exists('fastcgi_finish_request')) {
116
                // @codeCoverageIgnoreStart
117
                register_shutdown_function(function ($Session) {
118
                    $Session->save();
119
                }, $Session);
120
            // @codeCoverageIgnoreEnd
121
            } else {
122
                $Session->save();
123
            }
124
125
            return $Session;
126
        }
127
    }
128
129
    /**
130
     * Gets by handle.
131
     *
132
     * @param string $handle
133
     * @return static
134
     */
135
    public static function getByHandle($handle)
136
    {
137
        return static::getByField('Handle', $handle, true);
138
    }
139
140
    /**
141
     * @inheritDoc
142
     *
143
     * Saves the session to the database and sets the session cookie.
144
     * Will generate a unique handle for the session if none exists.
145
     *
146
     * @param boolean $deep
147
     * @return void
148
     */
149
    public function save($deep = true)
150
    {
151
        // set handle
152
        if (!$this->Handle) {
153
            $this->Handle = static::generateUniqueHandle();
154
        }
155
156
        // call parent
157
        parent::save($deep);
158
159
        // set cookie
160
        if (!headers_sent()) {
161
            // @codeCoverageIgnoreStart
162
            setcookie(
163
                static::$cookieName,
164
                $this->Handle,
165
                static::$cookieExpires ? (time() + static::$cookieExpires) : 0,
166
                static::$cookiePath,
167
                static::$cookieDomain,
168
                static::$cookieSecure
169
            );
170
            // @codeCoverageIgnoreEnd
171
        }
172
    }
173
174
    /**
175
     * Attempts to unset the cookie.
176
     * Unsets the variable from the $_COOKIE global.
177
     * Deletes session from database.
178
     *
179
     * @return void
180
     */
181
    public function terminate()
182
    {
183
        if (!headers_sent()) {
184
            // @codeCoverageIgnoreStart
185
            setcookie(static::$cookieName, '', time() - 3600);
186
            // @codeCoverageIgnoreEnd
187
        }
188
        unset($_COOKIE[static::$cookieName]);
189
190
        $this->destroy();
191
    }
192
193
    /**
194
     * Makes a random 32 digit string by generating 16 random bytes
195
     * Is cryptographically secure.
196
     * @see http://php.net/manual/en/function.random-bytes.php
197
     *
198
     * @return string
199
     */
200
    public static function generateUniqueHandle()
201
    {
202
        do {
203
            $handle = bin2hex(random_bytes(16));
204
        } while (static::getByHandle($handle));
205
        // just in case checks if the handle exists in the database and if it does makes a new one.
206
        // chance of happening is 1 in 2^128 though so might want to remove the database call
207
208
        return $handle;
209
    }
210
}
211