1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage Session
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 /**
13 * Interface for managing HTTP sessions
14 *
15 * @since 3.5
16 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package
17 */
18 class JSessionHandlerNative implements JSessionHandlerInterface
19 {
20 /**
21 * Has the session been started
22 *
23 * @var boolean
24 * @since 3.5
25 */
26 private $started = false;
27
28 /**
29 * Has the session been closed
30 *
31 * @var boolean
32 * @since 3.5
33 */
34 private $closed = false;
35
36 /**
37 * Starts the session
38 *
39 * @return boolean True if started
40 *
41 * @since 3.5
42 */
43 public function start()
44 {
45 if ($this->isStarted())
46 {
47 return true;
48 }
49
50 $this->doSessionStart();
51
52 return true;
53 }
54
55 /**
56 * Checks if the session is started.
57 *
58 * @return boolean True if started, false otherwise.
59 *
60 * @since 3.5
61 */
62 public function isStarted()
63 {
64 return $this->started;
65 }
66
67 /**
68 * Returns the session ID
69 *
70 * @return string The session ID
71 *
72 * @since 3.5
73 */
74 public function getId()
75 {
76 return session_id();
77 }
78
79 /**
80 * Sets the session ID
81 *
82 * @param string $id The session ID
83 *
84 * @return void
85 *
86 * @since 3.5
87 * @throws LogicException
88 */
89 public function setId($id)
90 {
91 if ($this->isStarted())
92 {
93 throw new LogicException('Cannot change the ID of an active session');
94 }
95
96 session_id($id);
97 }
98
99 /**
100 * Returns the session name
101 *
102 * @return mixed The session name
103 *
104 * @since 3.5
105 */
106 public function getName()
107 {
108 return session_name();
109 }
110
111 /**
112 * Sets the session name
113 *
114 * @param string $name The name of the session
115 *
116 * @return void
117 *
118 * @since 3.5
119 * @throws LogicException
120 */
121 public function setName($name)
122 {
123 if ($this->isStarted())
124 {
125 throw new LogicException('Cannot change the name of an active session');
126 }
127
128 session_name($name);
129 }
130
131 /**
132 * Regenerates ID that represents this storage.
133 *
134 * Note regenerate+destroy should not clear the session data in memory only delete the session data from persistent storage.
135 *
136 * @param boolean $destroy Destroy session when regenerating?
137 * @param integer $lifetime Sets the cookie lifetime for the session cookie. A null value will leave the system settings unchanged,
138 * 0 sets the cookie to expire with browser session. Time is in seconds, and is not a Unix timestamp.
139 *
140 * @return boolean True if session regenerated, false if error
141 *
142 * @since 3.5
143 */
144 public function regenerate($destroy = false, $lifetime = null)
145 {
146 if (null !== $lifetime)
147 {
148 ini_set('session.cookie_lifetime', $lifetime);
149 }
150
151 $return = session_regenerate_id($destroy);
152
153 // Workaround for https://bugs.php.net/bug.php?id=61470 as suggested by David Grudl
154 session_write_close();
155 $this->closed = true;
156
157 if (isset($_SESSION))
158 {
159 $backup = $_SESSION;
160 $this->doSessionStart();
161 $_SESSION = $backup;
162 }
163 else
164 {
165 $this->doSessionStart();
166 }
167
168 return $return;
169 }
170
171 /**
172 * Force the session to be saved and closed.
173 *
174 * This method must invoke session_write_close() unless this interface is used for a storage object design for unit or functional testing where
175 * a real PHP session would interfere with testing, in which case it should actually persist the session data if required.
176 *
177 * @return void
178 *
179 * @see session_write_close()
180 * @since 3.5
181 */
182 public function save()
183 {
184 // Verify if the session is active
185 if ((version_compare(PHP_VERSION, '5.4', 'ge') && PHP_SESSION_ACTIVE === session_status())
186 || (version_compare(PHP_VERSION, '5.4', 'lt') && $this->started && isset($_SESSION) && $this->getId()))
187 {
188 $session = JFactory::getSession();
189 $data = $session->getData();
190
191 // Before storing it, let's serialize and encode the Registry object
192 $_SESSION['joomla'] = base64_encode(serialize($data));
193
194 session_write_close();
195
196 $this->closed = true;
197 $this->started = false;
198 }
199 }
200
201 /**
202 * Clear all session data in memory.
203 *
204 * @return void
205 *
206 * @since 3.5
207 */
208 public function clear()
209 {
210 // Need to destroy any existing sessions started with session.auto_start
211 if ($this->getId())
212 {
213 session_unset();
214 session_destroy();
215 }
216
217 $this->closed = true;
218 $this->started = false;
219 }
220
221 /**
222 * Performs the session start mechanism
223 *
224 * @return void
225 *
226 * @since 3.5.1
227 * @throws RuntimeException If something goes wrong starting the session.
228 */
229 private function doSessionStart()
230 {
231 // Register our function as shutdown method, so we can manipulate it
232 register_shutdown_function(array($this, 'save'));
233
234 // Disable the cache limiter
235 session_cache_limiter('none');
236
237 /*
238 * Extended checks to determine if the session has already been started
239 */
240
241 // If running PHP 5.4, try to use the native API
242 if (version_compare(PHP_VERSION, '5.4', 'ge') && PHP_SESSION_ACTIVE === session_status())
243 {
244 throw new RuntimeException('Failed to start the session: already started by PHP.');
245 }
246
247 // Fallback check for PHP 5.3
248 if (version_compare(PHP_VERSION, '5.4', 'lt') && !$this->closed && isset($_SESSION) && $this->getId())
249 {
250 throw new RuntimeException('Failed to start the session: already started by PHP ($_SESSION is set).');
251 }
252
253 // If we are using cookies (default true) and headers have already been started (early output),
254 if (ini_get('session.use_cookies') && headers_sent($file, $line))
255 {
256 throw new RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
257 }
258
259 // Ok to try and start the session
260 if (!session_start())
261 {
262 throw new RuntimeException('Failed to start the session');
263 }
264
265 // Mark ourselves as started
266 $this->started = true;
267 }
268 }
269