Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 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
 * This component will allow access to session data from your website for each user connected
13
 * Symfony HttpFoundation is required for this component to work
14
 * Your website must also use Symfony HttpFoundation Sessions to read your sites session data
15
 * If your are not using at least PHP 5.4 you must include a SessionHandlerInterface stub (is included in Symfony HttpFoundation, loaded w/ composer)
16
 */
17
class SessionProvider implements HttpServerInterface {
18
    /**
19
     * @var \Ratchet\MessageComponentInterface
20
     */
21
    protected $_app;
22
 
23
    /**
24
     * Selected handler storage assigned by the developer
25
     * @var \SessionHandlerInterface
26
     */
27
    protected $_handler;
28
 
29
    /**
30
     * Null storage handler if no previous session was found
31
     * @var \SessionHandlerInterface
32
     */
33
    protected $_null;
34
 
35
    /**
36
     * @var \Ratchet\Session\Serialize\HandlerInterface
37
     */
38
    protected $_serializer;
39
 
40
    /**
41
     * @param \Ratchet\Http\HttpServerInterface           $app
42
     * @param \SessionHandlerInterface                    $handler
43
     * @param array                                       $options
44
     * @param \Ratchet\Session\Serialize\HandlerInterface $serializer
45
     * @throws \RuntimeException
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"; // awesome/terrible hack, eh?
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
     * {@inheritdoc}
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
     * {@inheritdoc}
104
     */
105
    function onMessage(ConnectionInterface $from, $msg) {
106
        return $this->_app->onMessage($from, $msg);
107
    }
108
 
109
    /**
110
     * {@inheritdoc}
111
     */
112
    function onClose(ConnectionInterface $conn) {
113
        // "close" session for Connection
114
 
115
        return $this->_app->onClose($conn);
116
    }
117
 
118
    /**
119
     * {@inheritdoc}
120
     */
121
    function onError(ConnectionInterface $conn, \Exception $e) {
122
        return $this->_app->onError($conn, $e);
123
    }
124
 
125
    /**
126
     * Set all the php session. ini options
127
     * © Symfony
128
     * @param array $options
129
     * @return array
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
     * @param string $langDef Input to convert
157
     * @return string
158
     */
159
    protected function toClassCase($langDef) {
160
        return str_replace(' ', '', ucwords(str_replace('_', ' ', $langDef)));
161
    }
162
 
163
    /**
164
     * Taken from Guzzle3
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
     * Taken from Guzzle3
182
     */
183
    private function parseCookie($cookie, $host = null, $path = null, $decode = false) {
184
        // Explode the cookie string using a series of semicolons
185
        $pieces = array_filter(array_map('trim', explode(';', $cookie)));
186
 
187
        // The name of the cookie (first kvp) must include an equal sign.
188
        if (empty($pieces) || !strpos($pieces[0], '=')) {
189
            return false;
190
        }
191
 
192
        // Create the default return array
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
        // Add the cookie pieces into the parsed data array
204
        foreach ($pieces as $part) {
205
 
206
            $cookieParts = explode('=', $part, 2);
207
            $key = trim($cookieParts[0]);
208
 
209
            if (count($cookieParts) == 1) {
210
                // Can be a single value (e.g. secure, httpOnly)
211
                $value = true;
212
            } else {
213
                // Be sure to strip wrapping quotes
214
                $value = trim($cookieParts[1], " \n\r\t\0\x0B\"");
215
                if ($decode) {
216
                    $value = urldecode($value);
217
                }
218
            }
219
 
220
            // Only check for non-cookies when cookies have been found
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
            // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a
232
            // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data.
233
            $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value;
234
        }
235
 
236
        // Calculate the expires date
237
        if (!$data['expires'] && $data['max_age']) {
238
            $data['expires'] = time() + (int) $data['max_age'];
239
        }
240
 
241
        return $data;
242
    }
243
}