1
+ <?php
2
+
3
+ /**
4
+ * MongoSessionHandler.php
5
+ *
6
+ * @version 1.0
7
+ * @since 2018
8
+ * @author Mandar Dhasal ([email protected] )
9
+ * @date 25 April 2018
10
+ */
11
+
12
+ /**
13
+ * SessionHandler Class
14
+ * A Handler class for implementation of PHP sessions in mongo db with ttl
15
+ *
16
+ * @subpackage Classes
17
+ * @category Sessions
18
+ * @author Mandar Dhasal ([email protected] )
19
+ */
20
+
21
+
22
+ class MongoSessionHandler implements SessionHandlerInterface
23
+ {
24
+
25
+ private $ docExist = false ;
26
+
27
+
28
+ /**
29
+ * __construct
30
+ * Constructor
31
+ * verify library
32
+ * initialize mongo connections with DB
33
+ */
34
+ public function __construct (){
35
+
36
+ $ this ->sess_id_prefix = 'mongo_sess_development_ ' ; //set according to environment
37
+
38
+ //connect to mongodb
39
+ if ( !extension_loaded ("MongoDB " ) )
40
+ trigger_error ("Cannot create sessions. MongoDB extension error. " , E_USER_ERROR );
41
+
42
+
43
+ if (!class_exists ("MongoDB\Client " ))
44
+ trigger_error ("Cannot create sessions. MongoDB lib error. " , E_USER_ERROR );
45
+
46
+
47
+ //require_once <your - config - file - with connection config>;
48
+
49
+ $ params = array (
50
+
51
+ 'host ' =>'192.168.2.77 ' , //required
52
+
53
+ 'port ' =>'27017 ' , //required
54
+
55
+ 'database ' =>'php_sessions ' , //required
56
+
57
+ 'collection ' =>'php_sessions ' , // required
58
+
59
+ 'sessionTimeout ' => 86400 , //TTL //1 day of inactivity
60
+
61
+ 'connectTimeoutMS ' => 3000 , //required
62
+
63
+ 'socketTimeoutMS ' => 3000 , //required
64
+
65
+ 'serverSelectionTimeoutMS ' =>3000 , //required
66
+
67
+ 'user ' =>'php_sess_user ' , //optional
68
+
69
+ 'password ' =>'php_sess_pass ' , // optional
70
+
71
+ 'authSource ' =>'admin ' , // optional
72
+
73
+ //'replicaSet' => 'my-replication-set', //optional
74
+
75
+ //'readPreference' => 'primaryPreferred' //optional
76
+ );
77
+
78
+ $ uri = 'mongodb:// ' .$ params ['host ' ].': ' .$ params ['port ' ].'/ ' .$ params ['database ' ];
79
+
80
+ if ( !empty ($ params ['user ' ]) ){
81
+
82
+ $ uriOptions ['username ' ] = $ params ['user ' ];
83
+ $ uriOptions ['password ' ] = $ params ['password ' ];
84
+ $ uriOptions ['authSource ' ] = $ params ['authSource ' ];
85
+ }
86
+
87
+ if ( !empty ($ params ['replicaSet ' ]) ){
88
+
89
+ // $uri = 'mongodb://'.$params['host'].':'.$params['port'].','.$params['host2'].':'.$params['port'].'/'.$params['database'];
90
+
91
+ $ uriOptions ['replicaSet ' ] = $ params ['replicaSet ' ];
92
+ $ uriOptions ['readPreference ' ] = $ params ['readPreference ' ];
93
+ }
94
+
95
+ $ uriOptions ['connectTimeoutMS ' ] = $ params ['connectTimeoutMS ' ];
96
+ $ uriOptions ['socketTimeoutMS ' ] = $ params ['socketTimeoutMS ' ];
97
+ $ uriOptions ['serverSelectionTimeoutMS ' ] = $ params ['serverSelectionTimeoutMS ' ];
98
+
99
+ try {
100
+
101
+ $ this ->client = new MongoDB \Client ($ uri ,$ uriOptions );
102
+
103
+ $ this ->client ->selectDatabase ($ params ['database ' ]);
104
+
105
+ $ this ->client ->{$ params ['database ' ]}->listCollections ();
106
+
107
+ $ this ->coll = $ this ->client ->selectCollection ($ params ['database ' ], $ params ['collection ' ]);
108
+
109
+
110
+ // checks collection exist or not. and creates if not exist
111
+ $ indexes = $ this ->coll ->listIndexes ();
112
+
113
+ $ indexFound = false ;
114
+
115
+ foreach ($ indexes as $ indexInfo ) {
116
+
117
+ if ( isset ($ indexInfo ['expireAfterSeconds ' ]) && isset ($ indexInfo ['key ' ]['expireAt ' ]) ){
118
+ $ indexFound = true ;
119
+
120
+ }
121
+ }
122
+
123
+ if ($ indexFound == false ){
124
+ $ this ->coll ->createIndex ( array ('expireAt ' => 1 ), array ( 'expireAfterSeconds ' => 0 ));
125
+ }
126
+
127
+ }catch (Exception $ e ){
128
+
129
+ // Cannot create sessions. MongoDB
130
+ //print_r($e);
131
+ trigger_error ('Connot connect to mongodb. ' , E_USER_ERROR );
132
+
133
+ }
134
+
135
+ // counting expire time directly by adding seconds, / can also do $date->add(new DateInterval('PT60S'));
136
+ $ this ->expireAt = new MongoDB \BSON \UTCDateTime ( ( (new DateTime ())->getTimestamp ()+$ params ['sessionTimeout ' ]) *1000 );
137
+
138
+ }
139
+
140
+ /**
141
+ * open
142
+ * opens a file to write but no needed here
143
+ * @param savePath <string>
144
+ * @param sessionName <string>
145
+ * @return Boolean
146
+ */
147
+ public function open ($ savePath , $ sessionName ){
148
+ return true ;
149
+ }
150
+
151
+ /**
152
+ * open
153
+ * closes a file opened for write but no needed here
154
+ * @param savePath <string>
155
+ * @param sessionName <string>
156
+ * @return Boolean
157
+ */
158
+ public function close (){
159
+ return true ;
160
+ }
161
+
162
+ /**
163
+ * read
164
+ * reads the data from database
165
+ * @param id <string>
166
+ * @return array
167
+ */
168
+ public function read ($ id ){
169
+ $ sess_id = $ this ->sess_id_prefix .$ id ;
170
+
171
+ $ findResult = $ this ->coll ->findOne (array ('sess_id ' => $ sess_id ));
172
+
173
+ if ($ findResult ==false )
174
+ {
175
+
176
+ $ this ->docExist = false ;
177
+ return '' ;
178
+ }
179
+
180
+ $ this ->docExist = true ;
181
+
182
+
183
+ // in case of read operation we.. update only after 5 mins .
184
+ // as we are already updating on write operations
185
+ /*
186
+ $expireAt = $findResult->expireAt->toDateTime()->getTimestamp();
187
+ if( time() - ($expireAt - SESS_TIMEOUT) > 300 ){
188
+
189
+ $updateResult = $this->coll->updateOne(
190
+ array('sess_id' => $sess_id),
191
+
192
+ array('$set' => array( 'expireAt'=> $this->expireAt ) ) ) ;
193
+
194
+ }
195
+ */
196
+ return $ findResult ->data ;
197
+ }
198
+
199
+ /**
200
+ * write
201
+ * writes the data to database
202
+ * @param id <string>
203
+ * @param data array
204
+ * @return Boolean
205
+ */
206
+ public function write ($ id , $ data ){
207
+
208
+ $ sess_id = $ this ->sess_id_prefix .$ id ;
209
+
210
+
211
+ // create if row / document not exist
212
+ if ($ this ->docExist === false )
213
+ {
214
+
215
+
216
+ $ insertResult = $ this ->coll ->insertOne ( array ('sess_id ' => $ sess_id , 'data ' => $ data , 'expireAt ' => $ this ->expireAt ) );
217
+
218
+ return ( $ insertResult ->getInsertedCount () == 1 );
219
+
220
+
221
+ }
222
+ else
223
+ {
224
+
225
+ $ updateResult = $ this ->coll ->updateOne (
226
+ array ('sess_id ' => $ sess_id ),
227
+
228
+ array ('$set ' => array ('data ' => $ data , 'expireAt ' => $ this ->expireAt ) )
229
+
230
+ );
231
+
232
+ return ( $ updateResult ->getMatchedCount () == 1 );
233
+
234
+ }
235
+ }
236
+
237
+ /**
238
+ * destroy
239
+ * destroy the data from database
240
+ * @param id <string>
241
+ * @return Boolean
242
+ */
243
+ public function destroy ($ id ){
244
+ $ sess_id = $ this ->sess_id_prefix .$ id ;
245
+ $ deleteResult = $ this ->coll ->deleteOne (array ('sess_id ' =>$ sess_id ));
246
+ return true ;
247
+ }
248
+
249
+ /**
250
+ * gc
251
+ * garbage collection of the old unused / inactive data.
252
+ * @param id <string>
253
+ * @return Boolean
254
+ */
255
+ public function gc ($ maxlifetime ){
256
+ $ time = new MongoDB \BSON \UTCDateTime ( ( (new DateTime ())->getTimestamp () - $ maxlifetime ) *1000 );
257
+ $ deleteResult = $ this ->coll ->deleteOne ( array ('expireAt ' => array ('$lt ' => $ time ) ) );
258
+ return true ;
259
+ }
260
+
261
+
262
+ }
263
+
264
+
265
+
266
+ ?>
0 commit comments