1: <?php
2: namespace Ratchet\Session;
3: use Ratchet\ConnectionInterface;
4: use Ratchet\Http\HttpServerInterface;
5: use Psr\Http\Message\RequestInterface;
6: use Ratchet\Session\Storage\VirtualSessionStorage;
7: use Ratchet\Session\Serialize\HandlerInterface;
8: use Symfony\Component\HttpFoundation\Session\Session;
9: use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
10:
11: 12: 13: 14: 15: 16:
17: class SessionProvider implements HttpServerInterface {
18: 19: 20:
21: protected $_app;
22:
23: 24: 25: 26:
27: protected $_handler;
28:
29: 30: 31: 32:
33: protected $_null;
34:
35: 36: 37:
38: protected $_serializer;
39:
40: 41: 42: 43: 44: 45: 46:
47: public function __construct(HttpServerInterface $app, \SessionHandlerInterface $handler, array $options = array(), HandlerInterface $serializer = null) {
48: $this->_app = $app;
49: $this->_handler = $handler;
50: $this->_null = new NullSessionHandler;
51:
52: ini_set('session.auto_start', 0);
53: ini_set('session.cache_limiter', '');
54: ini_set('session.use_cookies', 0);
55:
56: $this->setOptions($options);
57:
58: if (null === $serializer) {
59: $serialClass = __NAMESPACE__ . "\\Serialize\\{$this->toClassCase(ini_get('session.serialize_handler'))}Handler";
60: if (!class_exists($serialClass)) {
61: throw new \RuntimeException('Unable to parse session serialize handler');
62: }
63:
64: $serializer = new $serialClass;
65: }
66:
67: $this->_serializer = $serializer;
68: }
69:
70: 71: 72:
73: public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
74: $sessionName = ini_get('session.name');
75:
76: $id = array_reduce($request->getHeader('Cookie'), function($accumulator, $cookie) use ($sessionName) {
77: if ($accumulator) {
78: return $accumulator;
79: }
80:
81: $crumbs = $this->parseCookie($cookie);
82:
83: return isset($crumbs['cookies'][$sessionName]) ? $crumbs['cookies'][$sessionName] : false;
84: }, false);
85:
86: if (null === $request || false === $id) {
87: $saveHandler = $this->_null;
88: $id = '';
89: } else {
90: $saveHandler = $this->_handler;
91: }
92:
93: $conn->Session = new Session(new VirtualSessionStorage($saveHandler, $id, $this->_serializer));
94:
95: if (ini_get('session.auto_start')) {
96: $conn->Session->start();
97: }
98:
99: return $this->_app->onOpen($conn, $request);
100: }
101:
102: 103: 104:
105: function onMessage(ConnectionInterface $from, $msg) {
106: return $this->_app->onMessage($from, $msg);
107: }
108:
109: 110: 111:
112: function onClose(ConnectionInterface $conn) {
113:
114:
115: return $this->_app->onClose($conn);
116: }
117:
118: 119: 120:
121: function onError(ConnectionInterface $conn, \Exception $e) {
122: return $this->_app->onError($conn, $e);
123: }
124:
125: 126: 127: 128: 129: 130:
131: protected function setOptions(array $options) {
132: $all = array(
133: 'auto_start', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
134: 'cookie_lifetime', 'cookie_path', 'cookie_secure',
135: 'entropy_file', 'entropy_length', 'gc_divisor',
136: 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
137: 'hash_function', 'name', 'referer_check',
138: 'serialize_handler', 'use_cookies',
139: 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
140: 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
141: 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags'
142: );
143:
144: foreach ($all as $key) {
145: if (!array_key_exists($key, $options)) {
146: $options[$key] = ini_get("session.{$key}");
147: } else {
148: ini_set("session.{$key}", $options[$key]);
149: }
150: }
151:
152: return $options;
153: }
154:
155: 156: 157: 158:
159: protected function toClassCase($langDef) {
160: return str_replace(' ', '', ucwords(str_replace('_', ' ', $langDef)));
161: }
162:
163: 164: 165:
166: private static $cookieParts = array(
167: 'domain' => 'Domain',
168: 'path' => 'Path',
169: 'max_age' => 'Max-Age',
170: 'expires' => 'Expires',
171: 'version' => 'Version',
172: 'secure' => 'Secure',
173: 'port' => 'Port',
174: 'discard' => 'Discard',
175: 'comment' => 'Comment',
176: 'comment_url' => 'Comment-Url',
177: 'http_only' => 'HttpOnly'
178: );
179:
180: 181: 182:
183: private function parseCookie($cookie, $host = null, $path = null, $decode = false) {
184:
185: $pieces = array_filter(array_map('trim', explode(';', $cookie)));
186:
187:
188: if (empty($pieces) || !strpos($pieces[0], '=')) {
189: return false;
190: }
191:
192:
193: $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array(
194: 'cookies' => array(),
195: 'data' => array(),
196: 'path' => $path ?: '/',
197: 'http_only' => false,
198: 'discard' => false,
199: 'domain' => $host
200: ));
201: $foundNonCookies = 0;
202:
203:
204: foreach ($pieces as $part) {
205:
206: $cookieParts = explode('=', $part, 2);
207: $key = trim($cookieParts[0]);
208:
209: if (count($cookieParts) == 1) {
210:
211: $value = true;
212: } else {
213:
214: $value = trim($cookieParts[1], " \n\r\t\0\x0B\"");
215: if ($decode) {
216: $value = urldecode($value);
217: }
218: }
219:
220:
221: if (!empty($data['cookies'])) {
222: foreach (self::$cookieParts as $mapValue => $search) {
223: if (!strcasecmp($search, $key)) {
224: $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value;
225: $foundNonCookies++;
226: continue 2;
227: }
228: }
229: }
230:
231:
232:
233: $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value;
234: }
235:
236:
237: if (!$data['expires'] && $data['max_age']) {
238: $data['expires'] = time() + (int) $data['max_age'];
239: }
240:
241: return $data;
242: }
243: }
244: