1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Symfony\Component\HttpFoundation\Session\Storage;
13:
14: use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
15: use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
16: use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
17: use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
18: use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
19:
20: 21: 22: 23: 24:
25: class NativeSessionStorage implements SessionStorageInterface
26: {
27: 28: 29: 30: 31:
32: protected $bags;
33:
34: 35: 36:
37: protected $started = false;
38:
39: 40: 41:
42: protected $closed = false;
43:
44: 45: 46:
47: protected $saveHandler;
48:
49: 50: 51:
52: protected $metadataBag;
53:
54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103:
104: public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
105: {
106: session_cache_limiter('');
107: ini_set('session.use_cookies', 1);
108:
109: session_register_shutdown();
110:
111: $this->setMetadataBag($metaBag);
112: $this->setOptions($options);
113: $this->setSaveHandler($handler);
114: }
115:
116: 117: 118: 119: 120:
121: public function getSaveHandler()
122: {
123: return $this->saveHandler;
124: }
125:
126: 127: 128:
129: public function start()
130: {
131: if ($this->started) {
132: return true;
133: }
134:
135: if (\PHP_VERSION_ID >= 50400 && \PHP_SESSION_ACTIVE === session_status()) {
136: throw new \RuntimeException('Failed to start the session: already started by PHP.');
137: }
138:
139: if (\PHP_VERSION_ID < 50400 && !$this->closed && isset($_SESSION) && session_id()) {
140:
141: throw new \RuntimeException('Failed to start the session: already started by PHP ($_SESSION is set).');
142: }
143:
144: if (ini_get('session.use_cookies') && headers_sent($file, $line)) {
145: throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
146: }
147:
148:
149: if (!session_start()) {
150: throw new \RuntimeException('Failed to start the session');
151: }
152:
153: $this->loadSession();
154: if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
155:
156: $this->saveHandler->setActive(true);
157: }
158:
159: return true;
160: }
161:
162: 163: 164:
165: public function getId()
166: {
167: return $this->saveHandler->getId();
168: }
169:
170: 171: 172:
173: public function setId($id)
174: {
175: $this->saveHandler->setId($id);
176: }
177:
178: 179: 180:
181: public function getName()
182: {
183: return $this->saveHandler->getName();
184: }
185:
186: 187: 188:
189: public function setName($name)
190: {
191: $this->saveHandler->setName($name);
192: }
193:
194: 195: 196:
197: public function regenerate($destroy = false, $lifetime = null)
198: {
199:
200: if (\PHP_VERSION_ID >= 50400 && \PHP_SESSION_ACTIVE !== session_status()) {
201: return false;
202: }
203:
204:
205: if (\PHP_VERSION_ID < 50400 && '' === session_id()) {
206: return false;
207: }
208:
209: if (null !== $lifetime) {
210: ini_set('session.cookie_lifetime', $lifetime);
211: }
212:
213: if ($destroy) {
214: $this->metadataBag->stampNew();
215: }
216:
217: $isRegenerated = session_regenerate_id($destroy);
218:
219:
220:
221: $this->loadSession();
222:
223: return $isRegenerated;
224: }
225:
226: 227: 228:
229: public function save()
230: {
231: session_write_close();
232:
233: if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
234:
235: $this->saveHandler->setActive(false);
236: }
237:
238: $this->closed = true;
239: $this->started = false;
240: }
241:
242: 243: 244:
245: public function clear()
246: {
247:
248: foreach ($this->bags as $bag) {
249: $bag->clear();
250: }
251:
252:
253: $_SESSION = array();
254:
255:
256: $this->loadSession();
257: }
258:
259: 260: 261:
262: public function registerBag(SessionBagInterface $bag)
263: {
264: if ($this->started) {
265: throw new \LogicException('Cannot register a bag when the session is already started.');
266: }
267:
268: $this->bags[$bag->getName()] = $bag;
269: }
270:
271: 272: 273:
274: public function getBag($name)
275: {
276: if (!isset($this->bags[$name])) {
277: throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
278: }
279:
280: if ($this->saveHandler->isActive() && !$this->started) {
281: $this->loadSession();
282: } elseif (!$this->started) {
283: $this->start();
284: }
285:
286: return $this->bags[$name];
287: }
288:
289: 290: 291: 292: 293:
294: public function setMetadataBag(MetadataBag $metaBag = null)
295: {
296: if (null === $metaBag) {
297: $metaBag = new MetadataBag();
298: }
299:
300: $this->metadataBag = $metaBag;
301: }
302:
303: 304: 305: 306: 307:
308: public function getMetadataBag()
309: {
310: return $this->metadataBag;
311: }
312:
313: 314: 315:
316: public function isStarted()
317: {
318: return $this->started;
319: }
320:
321: 322: 323: 324: 325: 326: 327: 328: 329: 330:
331: public function setOptions(array $options)
332: {
333: $validOptions = array_flip(array(
334: 'cache_limiter', 'cookie_domain', 'cookie_httponly',
335: 'cookie_lifetime', 'cookie_path', 'cookie_secure',
336: 'entropy_file', 'entropy_length', 'gc_divisor',
337: 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
338: 'hash_function', 'name', 'referer_check',
339: 'serialize_handler', 'use_strict_mode', 'use_cookies',
340: 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
341: 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
342: 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags',
343: 'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
344: ));
345:
346: foreach ($options as $key => $value) {
347: if (isset($validOptions[$key])) {
348: ini_set('session.'.$key, $value);
349: }
350: }
351: }
352:
353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374:
375: public function setSaveHandler($saveHandler = null)
376: {
377: if (!$saveHandler instanceof AbstractProxy &&
378: !$saveHandler instanceof NativeSessionHandler &&
379: !$saveHandler instanceof \SessionHandlerInterface &&
380: null !== $saveHandler) {
381: throw new \InvalidArgumentException('Must be instance of AbstractProxy or NativeSessionHandler; implement \SessionHandlerInterface; or be null.');
382: }
383:
384:
385: if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
386: $saveHandler = new SessionHandlerProxy($saveHandler);
387: } elseif (!$saveHandler instanceof AbstractProxy) {
388: $saveHandler = \PHP_VERSION_ID >= 50400 ?
389: new SessionHandlerProxy(new \SessionHandler()) : new NativeProxy();
390: }
391: $this->saveHandler = $saveHandler;
392:
393: if ($this->saveHandler instanceof \SessionHandlerInterface) {
394: if (\PHP_VERSION_ID >= 50400) {
395: session_set_save_handler($this->saveHandler, false);
396: } else {
397: session_set_save_handler(
398: array($this->saveHandler, 'open'),
399: array($this->saveHandler, 'close'),
400: array($this->saveHandler, 'read'),
401: array($this->saveHandler, 'write'),
402: array($this->saveHandler, 'destroy'),
403: array($this->saveHandler, 'gc')
404: );
405: }
406: }
407: }
408:
409: 410: 411: 412: 413: 414: 415: 416: 417: 418:
419: protected function loadSession(array &$session = null)
420: {
421: if (null === $session) {
422: $session = &$_SESSION;
423: }
424:
425: $bags = array_merge($this->bags, array($this->metadataBag));
426:
427: foreach ($bags as $bag) {
428: $key = $bag->getStorageKey();
429: $session[$key] = isset($session[$key]) ? $session[$key] : array();
430: $bag->initialize($session[$key]);
431: }
432:
433: $this->started = true;
434: $this->closed = false;
435: }
436: }
437: