payload.php 7.87 KB
Newer Older
Taylor Otwell committed
1
<?php namespace Laravel\Session;
2 3

use Closure;
Taylor Otwell committed
4 5 6
use Laravel\Str;
use Laravel\Config;
use Laravel\Cookie;
7
use Laravel\Session;
8 9 10
use Laravel\Session\Drivers\Driver;
use Laravel\Session\Drivers\Sweeper;

11
class Payload {
12 13 14 15 16 17

	/**
	 * The session array that is stored by the driver.
	 *
	 * @var array
	 */
18
	public $session;
19 20

	/**
21
	 * The session driver used to retrieve and store the session payload.
22
	 *
23
	 * @var Driver
24
	 */
25
	public $driver;
26 27

	/**
28
	 * Indicates if the session already exists in storage.
29
	 *
30
	 * @var bool
31
	 */
32
	public $exists = true;
33 34 35

	/**
	 * Create a new session payload instance.
36 37
	 *
	 * @param  Driver  $driver
38 39 40 41 42 43 44 45 46 47
	 * @return void
	 */
	public function __construct(Driver $driver)
	{
		$this->driver = $driver;
	}

	/**
	 * Load the session for the current request.
	 *
48
	 * @param  string  $id
49 50
	 * @return void
	 */
51
	public function load($id)
52
	{
53
		if ( ! is_null($id)) $this->session = $this->driver->load($id);
54

55 56 57
		// If the session doesn't exist or is invalid, we will create a new session
		// array and mark the session as being non-existent. Some drivers, such as
		// the database driver, need to know whether the session exists in storage
58 59
		// so they can know whether to insert or update the session.
		if (is_null($this->session) or static::expired($this->session))
60
		{
61
			$this->exists = false;
62

63 64 65 66
			$this->session = array('id' => Str::random(40), 'data' => array(
				':new:' => array(),
				':old:' => array(),
			));
67 68
		}

69 70 71
		// A CSRF token is stored in every session. The token is used by the Form
		// class and the "csrf" filter to protect the application from cross-site
		// request forgery attacks. The token is simply a long, random string
72 73
		// which should be posted with each request to the application.
		if ( ! $this->has(Session::csrf_token))
74
		{
75
			$this->put(Session::csrf_token, Str::random(40));
76
		}		
77 78 79 80 81 82 83
	}

	/**
	 * Deteremine if the session payload instance is valid.
	 *
	 * The session is considered valid if it exists and has not expired.
	 *
84
	 * @param  array  $session
85 86
	 * @return bool
	 */
87
	protected static function expired($session)
88
	{
89
		$lifetime = Config::get('session.lifetime');
90

91
		return (time() - $session['last_activity']) > ($lifetime * 60);
92 93 94
	}

	/**
95 96 97 98 99
	 * Determine if the session or flash data contains an item.
	 *
	 * @param  string  $key
	 * @return bool
	 */
100
	public function has($key)
101
	{
102
		return ( ! is_null($this->get($key)));
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	}

	/**
	 * Get an item from the session.
	 *
	 * The session flash data will also be checked for the requested item.
	 *
	 * <code>
	 *		// Get an item from the session
	 *		$name = Session::get('name');
	 *
	 *		// Return a default value if the item doesn't exist
	 *		$name = Session::get('name', 'Taylor');
	 * </code>
	 *
	 * @param  string  $key
	 * @param  mixed   $default
	 * @return mixed
	 */
122
	public function get($key, $default = null)
123
	{
124 125 126 127 128 129
		$session = $this->session['data'];

		// We check for the item in the general session data first, and if it
		// does not exist in that data, we will attempt to find it in the new
		// and old flash data. If none of those arrays contain the requested
		// item, we will just return the default value.
130
		if ( ! is_null($value = array_get($session, $key)))
131
		{
132
			return $value;
133
		}
134
		elseif ( ! is_null($value = array_get($session[':new:'], $key)))
135
		{
136
			return $value;
137
		}
138
		elseif ( ! is_null($value = array_get($session[':old:'], $key)))
139
		{
140
			return $value;
141 142
		}

143
		return value($default);
144 145 146 147 148
	}

	/**
	 * Write an item to the session.
	 *
149 150 151 152 153
	 * <code>
	 *		// Write an item to the session payload
	 *		Session::put('name', 'Taylor');
	 * </code>
	 *
154 155 156 157
	 * @param  string  $key
	 * @param  mixed   $value
	 * @return void
	 */
158
	public function put($key, $value)
159
	{
160
		array_set($this->session['data'], $key, $value);
161 162 163 164 165
	}

	/**
	 * Write an item to the session flash data.
	 *
166 167 168 169 170 171
	 * Flash data only exists for the current and next request to the application.
	 *
	 * <code>
	 *		// Write an item to the session payload's flash data
	 *		Session::flash('name', 'Taylor');
	 * </code>
172 173 174 175 176
	 *
	 * @param  string  $key
	 * @param  mixed   $value
	 * @return void
	 */
177
	public function flash($key, $value)
178
	{
179
		array_set($this->session['data'][':new:'], $key, $value);
180 181 182
	}

	/**
183
	 * Keep all of the session flash data from expiring after the request.
184 185 186
	 *
	 * @return void
	 */
187
	public function reflash()
188
	{
Taylor Otwell committed
189 190 191
		$old = $this->session['data'][':old:'];

		$this->session['data'][':new:'] = array_merge($this->session['data'][':new:'], $old);
192 193 194 195 196
	}

	/**
	 * Keep a session flash item from expiring at the end of the request.
	 *
197 198 199 200 201 202 203 204
	 * <code>
	 *		// Keep the "name" item from expiring from the flash data
	 *		Session::keep('name');
	 *
	 *		// Keep the "name" and "email" items from expiring from the flash data
	 *		Session::keep(array('name', 'email'));
	 * </code>
	 *
Phill Sparks committed
205
	 * @param  string|array  $keys
206 207
	 * @return void
	 */
208
	public function keep($keys)
209
	{
210 211
		foreach ((array) $keys as $key)
		{
212
			$this->flash($key, $this->get($key));
213
		}
214 215 216 217 218 219
	}

	/**
	 * Remove an item from the session data.
	 *
	 * @param  string  $key
220
	 * @return void
221
	 */
222
	public function forget($key)
223
	{
224
		array_forget($this->session['data'], $key);
225 226 227 228 229
	}

	/**
	 * Remove all of the items from the session.
	 *
230 231
	 * The CSRF token will not be removed from the session.
	 *
232 233
	 * @return void
	 */
234
	public function flush()
235
	{
236 237 238 239 240
		$token = $this->token();

		$session = array(Session::csrf_token => $token, ':new:' => array(), ':old:' => array());

		$this->session['data'] = $session;
241 242 243 244 245 246 247
	}

	/**
	 * Assign a new, random ID to the session.
	 *
	 * @return void
	 */
248
	public function regenerate()
249
	{
250
		$this->session['id'] = Str::random(40);
251

252
		$this->exists = false;
253 254 255
	}

	/**
Taylor Otwell committed
256 257 258 259
	 * Get the CSRF token that is stored in the session data.
	 *
	 * @return string
	 */
260
	public function token()
Taylor Otwell committed
261
	{
262
		return $this->get(Session::csrf_token);
Taylor Otwell committed
263 264 265
	}

	/**
266 267 268 269 270 271 272 273 274 275
	 * Get the last activity for the session.
	 *
	 * @return int
	 */
	public function activity()
	{
		return $this->session['last_activity'];
	}

	/**
276 277
	 * Store the session payload in storage.
	 *
278
	 * This method will be called automatically at the end of the request.
279
	 *
280 281
	 * @return void
	 */
282
	public function save()
283
	{
284
		$this->session['last_activity'] = time();
285

286 287 288
		// Session flash data is only available during the request in which it
		// was flashed and the following request. We will age the data so that
		// it expires at the end of the user's next request.
289
		$this->age();
290

291
		$config = Config::get('session');
292

293 294 295 296 297 298 299
		// The responsibility of actually storing the session information in
		// persistent storage is delegated to the driver instance being used
		// by the session payload.
		//
		// This allows us to keep the payload very generic, while moving the
		// platform or storage mechanism code into the specialized drivers,
		// keeping our code very dry and organized.
300
		$this->driver->save($this->session, $config, $this->exists);
301

302 303 304 305
		// Next we'll write out the session cookie. This cookie contains the
		// ID of the session, and will be used to determine the owner of the
		// session on the user's subsequent requests to the application.
		$this->cookie($config);
306

307 308 309 310
		// Some session drivers implement the Sweeper interface, meaning that
		// they must clean up expired sessions manually. If the driver is a
		// sweeper, we need to determine if garbage collection should be
		// run for the request.
311 312
		$sweepage = $config['sweepage'];

313
		if ($this->driver instanceof Sweeper and (mt_rand(1, $sweepage[1]) <= $sweepage[0]))
314
		{
315
			$this->driver->sweep(time() - ($config['lifetime'] * 60));
316 317 318 319 320 321 322 323
		}
	}

	/**
	 * Age the session flash data.
	 *
	 * @return void
	 */
324
	protected function age()
325
	{
326
		$this->session['data'][':old:'] = $this->session['data'][':new:'];
327

328
		$this->session['data'][':new:'] = array();
329 330 331 332 333
	}

	/**
	 * Send the session ID cookie to the browser.
	 *
334
	 * @param  array  $config
335 336
	 * @return void
	 */
337
	protected function cookie($config)
338 339 340 341 342
	{
		extract($config, EXTR_SKIP);

		$minutes = ( ! $expire_on_close) ? $lifetime : 0;

343
		Cookie::put($cookie, $this->session['id'], $minutes, $path, $domain, $secure);	
344 345
	}

346
}