]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - classes/Session.php
[SESSION] Increase type strictness for full PHP adherence
[quix0rs-gnu-social.git] / classes / Session.php
1 <?php
2 /**
3  * Table Definition for session
4  *
5  * StatusNet - the distributed open-source microblogging tool
6  * Copyright (C) 2009, StatusNet, Inc.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Affero General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Affero General Public License for more details.
17  *
18  * You should have received a copy of the GNU Affero General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 if (!defined('STATUSNET') && !defined('LACONICA')) {
23     exit(1);
24 }
25
26 require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
27
28 /**
29  * Table definition for Session
30  *
31  * Superclass representing a saved session as it exists in the database and the associated interfaces.
32  *
33  * @author GNU social
34  */
35 class Session extends Managed_DataObject
36 {
37     ###START_AUTOCODE
38     /* the code below is auto generated do not remove the above tag */
39
40     public $__table = 'session';             // table name
41     public $id;                              // varchar(32)  primary_key not_null
42     public $session_data;                    // text()
43     public $created;                         // datetime()   not_null
44     public $modified;                        // timestamp()  not_null default_CURRENT_TIMESTAMP
45
46     /* the code above is auto generated do not remove the tag below */
47     ###END_AUTOCODE
48
49     /**
50      * Returns an array describing how the session is stored in the database.
51      */
52     public static function schemaDef()
53     {
54         return [
55             'fields' => [
56                 'id'           => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'session ID'],
57                 'session_data' => ['type' => 'text', 'description' => 'session data'],
58                 'created'      => ['type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'],
59                 'modified'     => ['type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'],
60             ],
61             'primary key' => ['id'],
62             'indexes' => [
63                 'session_modified_idx' => ['modified'],
64             ],
65         ];
66     }
67
68     /**
69      * A helper function to print a session-related message to the debug log if
70      * the site session debug configuration option is enabled.
71      * @param $msg
72      * @return void
73      */
74     public static function logdeb($msg)
75     {
76         if (common_config('sessions', 'debug')) {
77             common_debug("Session: " . $msg);
78         }
79     }
80
81     /**
82      * Dummy option for saving to file needed for full PHP adherence.
83      *
84      * @param $save_path
85      * @param $session_name
86      * @return bool true
87      */
88     public static function open($save_path, $session_name)
89     {
90         return true;
91     }
92
93     /**
94      * Dummy option for saving to file needed for full PHP adherence.
95      *
96      * @return bool true
97      */
98     public static function close()
99     {
100         return true;
101     }
102
103     /**
104      * Fetch the session data for the session with the given $id.
105      *
106      * @param $id
107      * @return string Returns an encoded string of the read data. If nothing was read, it must return an empty string. Note this value is returned internally to PHP for processing.
108      */
109     public static function read($id)
110     {
111         self::logdeb("Fetching session '$id'");
112
113         $session = Session::getKV('id', $id);
114
115         if (empty($session)) {
116             self::logdeb("Couldn't find '$id'");
117             return '';
118         } else {
119             self::logdeb("Found '$id', returning " .
120                          strlen($session->session_data) .
121                          " chars of data");
122             return (string)$session->session_data;
123         }
124     }
125
126     /**
127      * Write the session data for session with given $id as $session_data.
128      *
129      * @param $id
130      * @param $session_data
131      * @return bool Returns TRUE on success or FALSE on failure.
132      */
133     public static function write($id, $session_data)
134     {
135         self::logdeb("Writing session '$id'");
136
137         $session = Session::getKV('id', $id);
138
139         if (empty($session)) {
140             self::logdeb("'$id' doesn't yet exist; inserting.");
141             $session = new Session();
142
143             $session->id = $id;
144             $session->session_data = $session_data;
145             $session->created = common_sql_now();
146
147             $result = $session->insert();
148
149             if (!$result) {
150                 common_log_db_error($session, 'INSERT', __FILE__);
151                 self::logdeb("Failed to insert '$id'.");
152             } else {
153                 self::logdeb("Successfully inserted '$id' (result = $result).");
154             }
155             return (bool) $result;
156         } else {
157             self::logdeb("'$id' already exists; updating.");
158             if (strcmp($session->session_data, $session_data) == 0) {
159                 self::logdeb("Not writing session '$id'; unchanged");
160                 return true;
161             } else {
162                 self::logdeb("Session '$id' data changed; updating");
163
164                 $orig = clone($session);
165
166                 $session->session_data = $session_data;
167
168                 $result = $session->update($orig);
169
170                 if (!$result) {
171                     common_log_db_error($session, 'UPDATE', __FILE__);
172                     self::logdeb("Failed to update '$id'.");
173                 } else {
174                     self::logdeb("Successfully updated '$id' (result = $result).");
175                 }
176
177                 return (bool) $result;
178             }
179         }
180     }
181
182     /**
183      * Find sessions that have persisted beyond $maxlifetime and delete them.
184      * This will be limited by config['sessions']['gc_limit'] - it won't delete
185      * more than the number of sessions specified there at a single pass.
186      *
187      * @param $maxlifetime
188      * @return bool Returns TRUE on success or FALSE on failure.
189      */
190     public static function gc($maxlifetime)
191     {
192         self::logdeb("garbage collection (maxlifetime = $maxlifetime)");
193
194         $epoch = common_sql_date(time() - $maxlifetime);
195
196         $ids = [];
197
198         $session = new Session();
199         $session->whereAdd('modified < "' . $epoch . '"');
200         $session->selectAdd();
201         $session->selectAdd('id');
202
203         $limit = common_config('sessions', 'gc_limit');
204         if ($limit > 0) {
205             // On large sites, too many sessions to expire
206             // at once will just result in failure.
207             $session->limit($limit);
208         }
209
210         $session->find();
211
212         while ($session->fetch()) {
213             $ids[] = $session->id;
214         }
215
216         $session->free();
217
218         self::logdeb("Found " . count($ids) . " ids to delete.");
219
220         foreach ($ids as $id) {
221             self::logdeb("Destroying session '$id'.");
222             self::destroy($id);
223         }
224
225         return true;
226     }
227
228     /**
229      * Deletes session with given id $id.
230      *
231      * @param $id
232      * @return bool Returns TRUE on success or FALSE on failure.
233      */
234     public static function destroy($id)
235     {
236         self::logdeb("Deleting session $id");
237
238         $session = Session::getKV('id', $id);
239
240         if (empty($session)) {
241             self::logdeb("Can't find '$id' to delete.");
242             return false;
243         } else {
244             $result = $session->delete();
245             if (!$result) {
246                 common_log_db_error($session, 'DELETE', __FILE__);
247                 self::logdeb("Failed to delete '$id'.");
248             } else {
249                 self::logdeb("Successfully deleted '$id' (result = $result).");
250             }
251             return (bool) $result;
252         }
253     }
254
255     /**
256      * Set our session handler as the handler for PHP session handling in the context of GNU social.
257      *
258      * @return bool Returns TRUE on success or FALSE on failure.
259      */
260     public static function setSaveHandler()
261     {
262         self::logdeb("setting save handlers");
263         $result = session_set_save_handler(
264             'Session::open',
265             'Session::close',
266             'Session::read',
267             'Session::write',
268             'Session::destroy',
269             'Session::gc'
270         );
271         self::logdeb("save handlers result = $result");
272
273         // PHP 5.3 with APC ends up destroying a bunch of object stuff before the session
274         // save handlers get called on request teardown.
275         // Registering an explicit shutdown function should take care of this before
276         // everything breaks on us.
277         register_shutdown_function('Session::cleanup');
278
279         return $result;
280     }
281
282     /**
283      * Stuff to do before the request teardown.
284      *
285      * @return void
286      */
287     public static function cleanup()
288     {
289         session_write_close();
290     }
291 }