Passed
Push — master ( e18794...0b8861 )
by smiley
01:21
created

SessionHandlerAbstract::id()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
/**
3
 * Class SessionHandlerAbstract
4
 *
5
 * @filesource   SessionHandlerAbstract.php
6
 * @created      06.03.2017
7
 * @package      chillerlan\Session
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2017 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\Session;
14
15
use chillerlan\Logger\Output\NullLogger;
16
use chillerlan\Settings\SettingsContainerInterface;
17
use Psr\Log\{LoggerAwareInterface, LoggerAwareTrait, LoggerInterface};
18
19
abstract class SessionHandlerAbstract implements SessionInterface, LoggerAwareInterface{
20
	use LoggerAwareTrait;
21
22
	/**
23
	 * @var bool
24
	 */
25
	protected $started = false;
26
27
	/**
28
	 * @var \chillerlan\Session\SessionHandlerOptions
29
	 */
30
	protected $options;
31
32
	/**
33
	 * SessionHandlerAbstract constructor.
34
	 *
35
	 * @param \chillerlan\Settings\SettingsContainerInterface $options
36
	 * @param \Psr\Log\LoggerInterface|null                  $logger
37
	 */
38
	public function __construct(SettingsContainerInterface $options = null, LoggerInterface $logger = null){
39
		$this->options = $options ?? new SessionHandlerOptions;
40
		$this->logger  = $logger ?? new NullLogger;
0 ignored issues
show
Documentation Bug introduced by
It seems like $logger ?? new chillerla...ger\Output\NullLogger() can also be of type chillerlan\Logger\Output\NullLogger. However, the property $logger is declared as type Psr\Log\LoggerInterface. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
41
42
		$this->setOptions($options);
0 ignored issues
show
Bug introduced by
It seems like $options can also be of type null; however, parameter $options of chillerlan\Session\Sessi...rAbstract::setOptions() does only seem to accept chillerlan\Settings\SettingsContainerInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

42
		$this->setOptions(/** @scrutinizer ignore-type */ $options);
Loading history...
43
44
		\session_set_save_handler($this, true);
45
	}
46
47
	/**
48
	 * @inheritdoc
49
	 * @throws \chillerlan\Session\SessionHandlerException
50
	 */
51
	public function start():SessionInterface{
52
53
		if($this->active()){
54
			throw new SessionHandlerException('session already running');
55
		}
56
57
		$cookie_params = \session_get_cookie_params();
58
59
		\session_start();
60
		\session_regenerate_id(true);
61
62
		\setcookie(
63
			\session_name(),
64
			\session_id(),
65
			\time() + $this->options->cookie_lifetime,
66
			$this->options->cookie_path,
67
			$cookie_params['domain']
68
		);
69
70
		return $this;
71
	}
72
73
	/** @inheritdoc */
74
	public function end():SessionInterface{
75
76
		if($this->active()){
77
			\session_regenerate_id(true);
78
			\setcookie(session_name(), '', 0, $this->options->cookie_path);
79
			\session_unset();
80
			\session_destroy();
81
			\session_write_close();
82
		}
83
84
		return $this;
85
	}
86
87
	/** @inheritdoc */
88
	public function active():bool{
89
		return \session_status() === \PHP_SESSION_ACTIVE;
90
	}
91
92
	/** @inheritdoc */
93
	public function get(string $name){
94
		return $_SESSION[$name] ?? null;
95
	}
96
97
	/** @inheritdoc */
98
	public function set(string $name, $value):SessionInterface{
99
		$_SESSION[$name] = $value;
100
101
		return $this;
102
	}
103
104
	/** @inheritdoc */
105
	public function unset(string $name):SessionInterface{
106
		unset($_SESSION[$name]);
107
108
		return $this;
109
	}
110
111
	/** @inheritdoc */
112
	public function isset(string $name):bool{
113
		return isset($_SESSION[$name]);
114
	}
115
116
	/** @inheritdoc */
117
	public function id(string $newID = null):string{
118
		return session_id($newID);
119
	}
120
121
	/**
122
	 * @param string $data
123
	 *
124
	 * @return string
125
	 * @throws \chillerlan\Session\SessionHandlerException
126
	 */
127
	protected function encrypt(string &$data):string {
128
129
		if(\function_exists('sodium_crypto_secretbox')){
130
			$box = \sodium_crypto_secretbox($data, $this::SESSION_NONCE, \sodium_hex2bin($this->options->sessionCryptoKey));
131
132
			\sodium_memzero($data);
0 ignored issues
show
Bug introduced by
The call to sodium_memzero() has too few arguments starting with length. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

132
			/** @scrutinizer ignore-call */ 
133
   \sodium_memzero($data);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
133
134
			return \sodium_bin2hex($box);
135
		}
136
137
		throw new SessionHandlerException('sodium not installed'); // @codeCoverageIgnore
138
	}
139
140
	/**
141
	 * @param string $box
142
	 *
143
	 * @return string
144
	 * @throws \chillerlan\Session\SessionHandlerException
145
	 */
146
	protected function decrypt(string $box):string {
147
148
		if(\function_exists('sodium_crypto_secretbox_open')){
149
			return \sodium_crypto_secretbox_open(\sodium_hex2bin($box), $this::SESSION_NONCE, \sodium_hex2bin($this->options->sessionCryptoKey));
150
		}
151
152
		throw new SessionHandlerException('sodium not installed'); // @codeCoverageIgnore
153
	}
154
155
	/**
156
	 * @param \chillerlan\Settings\SettingsContainerInterface $options
157
	 *
158
	 * @return \chillerlan\Session\SessionInterface
159
	 */
160
	public function setOptions(SettingsContainerInterface $options):SessionInterface{
161
162
		// end an active session before setting new options
163
		if($this->active()){
164
			$this->end();
165
		}
166
167
		if(\is_writable($options->save_path)){
168
			\ini_set('session.save_path', $options->save_path);
169
		}
170
171
		// @todo http://php.net/manual/session.configuration.php
172
		\ini_set('session.name', $options->session_name);
173
174
		\ini_set('session.gc_maxlifetime', $options->gc_maxlifetime);
175
		\ini_set('session.gc_probability', '1');
176
		\ini_set('session.gc_divisor', '100');
177
178
		\ini_set('session.use_strict_mode', 'true');
179
		\ini_set('session.use_only_cookies', 'true');
180
		\ini_set('session.cookie_secure', 'false'); // @todo
181
		\ini_set('session.cookie_httponly', 'true');
182
		\ini_set('session.cookie_lifetime', '0');
183
#		\ini_set('session.referer_check', '');
184
185
		\ini_set('session.sid_bits_per_character', '6');
186
		\ini_set('session.sid_length', '128');
187
188
		return $this;
189
	}
190
191
}
192