Passed
Push — master ( a5594e...daaf78 )
by Henry
02:56
created

Session::__classLoaded()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the Divergence package.
4
 *
5
 * @author Henry Paradiz <[email protected]>
6
 * @copyright 2018 Henry Paradiz <[email protected]>
7
 * @license MIT For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
8
 *
9
 * @since 1.1
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
        ],
61
    ];
62
63
64
    /**
65
     * Gets or sets up a session based on current cookies.
66
     * Will always update the current session's LastIP and LastRequest fields.
67
     *
68
     * @param boolean $create
69
     * @return static|false
70
     */
71
    public static function getFromRequest($create = true)
72
    {
73
        $sessionData = [
74
            'LastIP' => inet_pton($_SERVER['REMOTE_ADDR']),
75
            'LastRequest' => time(),
76
        ];
77
78
        // try to load from cookie
79
        if (!empty($_COOKIE[static::$cookieName])) {
80
            if ($Session = static::getByHandle($_COOKIE[static::$cookieName])) {
81
                // update session & check expiration
82
                $Session = static::updateSession($Session, $sessionData);
83
            }
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
94
        if (!empty($Session)) {
95
            // session found
96
            return $Session;
97
        } elseif ($create) {
98
            // create session
99
            return static::create($sessionData, true);
100
        } else {
101
            // no session available
102
            return false;
103
        }
104
    }
105
106
    public static function updateSession(Session $Session, $sessionData)
107
    {
108
109
        // check timestamp
110
        if ($Session->LastRequest < (time() - static::$timeout)) {
111
            $Session->terminate();
112
113
            return false;
114
        } else {
115
            // update session
116
            $Session->setFields($sessionData);
117
            if (function_exists('fastcgi_finish_request')) {
118
                register_shutdown_function(function (&$Session) {
119
                    $Session->save();
120
                }, $Session);
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
    public function getData()
141
    {
142
        // embed related object(s)
143
        return array_merge(parent::getData(), [
144
            'Person' => $this->Person ? $this->Person->getData() : null,
0 ignored issues
show
Bug Best Practice introduced by
The property Person does not exist on Divergence\Models\Auth\Session. Since you implemented __get, consider adding a @property annotation.
Loading history...
145
        ]);
146
    }
147
148
    /**
149
     * @inheritDoc
150
     *
151
     * Saves the session to the database and sets the session cookie.
152
     * Will generate a unique handle for the session if none exists.
153
     *
154
     * @param boolean $deep
155
     * @return void
156
     */
157
    public function save($deep = true)
158
    {
159
        // set handle
160
        if (!$this->Handle) {
161
            $this->Handle = static::generateUniqueHandle();
0 ignored issues
show
Documentation Bug introduced by
It seems like static::generateUniqueHandle() of type void is incompatible with the declared type string of property $Handle.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Bug introduced by
Are you sure the assignment to $this->Handle is correct as static::generateUniqueHandle() targeting Divergence\Models\Auth\S...:generateUniqueHandle() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
162
        }
163
164
        // call parent
165
        parent::save($deep);
166
167
        // set cookie
168
        setcookie(
169
            static::$cookieName,
170
            $this->Handle,
171
            static::$cookieExpires ? (time() + static::$cookieExpires) : 0,
172
            static::$cookiePath,
173
            static::$cookieDomain,
174
            static::$cookieSecure
175
        );
176
    }
177
178
    /**
179
     * Attempts to unset the cookie.
180
     * Unsets the variable from the $_COOKIE global.
181
     * Deletes session from database.
182
     *
183
     * @return void
184
     */
185
    public function terminate()
186
    {
187
        setcookie(static::$cookieName, '', time() - 3600);
188
        unset($_COOKIE[static::$cookieName]);
189
190
        $this->destroy();
191
    }
192
193
194
195
    /**
196
     * Makes a random 32 digit string by generating 16 random bytes
197
     * Is cryptographically secure.
198
     * @see http://php.net/manual/en/function.random-bytes.php
199
     *
200
     * @return void
201
     */
202
    public static function generateUniqueHandle()
203
    {
204
        do {
205
            $handle = bin2hex(random_bytes(16));
206
        } while (static::getByHandle($handle));
207
        // just in case checks if the handle exists in the database and if it does makes a new one.
208
        // chance of happening is 1 in 2^128 though so might want to remove the database call
209
210
        return $handle;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $handle returns the type string which is incompatible with the documented return type void.
Loading history...
211
    }
212
}
213