| 1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
|---|
| 2 | <html> |
|---|
| 3 | <head> |
|---|
| 4 | <title>ADODB Session Management Manual</title> |
|---|
| 5 | <meta http-equiv="Content-Type" |
|---|
| 6 | content="text/html; charset=iso-8859-1"> |
|---|
| 7 | <style type="text/css"> |
|---|
| 8 | body, td { |
|---|
| 9 | /*font-family: Arial, Helvetica, sans-serif;*/ |
|---|
| 10 | font-size: 11pt; |
|---|
| 11 | } |
|---|
| 12 | pre { |
|---|
| 13 | font-size: 9pt; |
|---|
| 14 | background-color: #EEEEEE; padding: .5em; margin: 0px; |
|---|
| 15 | } |
|---|
| 16 | .toplink { |
|---|
| 17 | font-size: 8pt; |
|---|
| 18 | } |
|---|
| 19 | </style> |
|---|
| 20 | </head> |
|---|
| 21 | <body style="background-color: rgb(255, 255, 255);"> |
|---|
| 22 | <h1>ADODB Session 2 Management Manual</h1> |
|---|
| 23 | <p> |
|---|
| 24 | v4.991 16 Oct 2008 (c) 2000-2008 John Lim (jlim#natsoft.com) |
|---|
| 25 | </p> |
|---|
| 26 | <p> <font size="1">This software is dual licensed using BSD-Style and |
|---|
| 27 | LGPL. This means you can use it in compiled proprietary and commercial |
|---|
| 28 | products. </font> |
|---|
| 29 | <p>Useful ADOdb links: <a href="http://adodb.sourceforge.net/#download">Download</a> |
|---|
| 30 | <a href="http://adodb.sourceforge.net/#docs">Other Docs</a> |
|---|
| 31 | </p> |
|---|
| 32 | <h2>Introduction</h2> |
|---|
| 33 | <p> This document discusses the newer session handler adodb-session2.php. If |
|---|
| 34 | you have used the older adodb-session.php, then be forewarned that you will |
|---|
| 35 | need to alter your session table format. Otherwise everything is <a href="#compat">backward |
|---|
| 36 | compatible</a>. |
|---|
| 37 | Here are the <a href="docs-session.old.htm">older |
|---|
| 38 | docs</a> for |
|---|
| 39 | adodb-session.php.</p> |
|---|
| 40 | <h2>Why Session Variables in a Database? </h2> |
|---|
| 41 | <p>We store state information specific to a user or web |
|---|
| 42 | client in session variables. These session variables persist throughout a |
|---|
| 43 | session, as the user moves from page to page. </p> |
|---|
| 44 | <p>To use session variables, call session_start() at the beginning of |
|---|
| 45 | your web page, before your HTTP headers are sent. Then for every |
|---|
| 46 | variable you want to keep alive for the duration of the session, call |
|---|
| 47 | session_register($variable_name). By default, the session handler will |
|---|
| 48 | keep track of the session by using a cookie. You can save objects or |
|---|
| 49 | arrays in session variables also. |
|---|
| 50 | </p> |
|---|
| 51 | <p>The default method of storing sessions is to store it in a file. |
|---|
| 52 | However if you have special needs such as you: |
|---|
| 53 | </p> |
|---|
| 54 | <ul> |
|---|
| 55 | <li>Have multiple web servers that need to share session info</li> |
|---|
| 56 | <li>Need to do special processing of each session</li> |
|---|
| 57 | <li>Require notification when a session expires</li> |
|---|
| 58 | </ul> |
|---|
| 59 | <p>The ADOdb session handler provides you with the above |
|---|
| 60 | additional capabilities by storing the session information as records |
|---|
| 61 | in a database table that can be shared across multiple servers. </p> |
|---|
| 62 | <p>These records will be garbage collected based on the php.ini [session] timeout settings. |
|---|
| 63 | You can register a notification function to notify you when the record has expired and |
|---|
| 64 | is about to be freed by the garbage collector.</p> |
|---|
| 65 | <p>An alternative to using a database backed session handler is to use <a href="http://www.danga.com/memcached/">memcached</a>. |
|---|
| 66 | This is a distributed memory based caching system suitable for storing session |
|---|
| 67 | information. |
|---|
| 68 | </p> |
|---|
| 69 | <h2> The Improved Session Handler</h2> |
|---|
| 70 | <p>In ADOdb 4.91, we added a new session handler, in adodb-session2.php. |
|---|
| 71 | It features the following improvements: |
|---|
| 72 | <ul> |
|---|
| 73 | <li>Fully supports server farms using a new database table format. The |
|---|
| 74 | previous version used the web server time for timestamps, which can cause problems |
|---|
| 75 | on a system with multiple web servers with possibly inconsistent |
|---|
| 76 | times. The new version uses the database server time instead for all timestamps. |
|---|
| 77 | <li>The older database table format is obsolete. The database table must be modified |
|---|
| 78 | to support storage of the database server time mentioned above. Also the field |
|---|
| 79 | named DATA has been changed to SESSDATA. In some databases, DATA is a reserved |
|---|
| 80 | word. |
|---|
| 81 | <li>The functions dataFieldName() and syncSeconds() is obsolete. |
|---|
| 82 | </ul> |
|---|
| 83 | |
|---|
| 84 | <p>Usage is |
|---|
| 85 | |
|---|
| 86 | <pre> |
|---|
| 87 | include_once("adodb/session/adodb-session2.php"); |
|---|
| 88 | ADOdb_Session::config($driver, $host, $user, $password, $database,$options=false); |
|---|
| 89 | session_start(); |
|---|
| 90 | |
|---|
| 91 | <font |
|---|
| 92 | color="#004040">#<br># Test session vars, the following should increment on refresh<br>#<br>$_SESSION['AVAR'] += 1;<br>print "<p>\$_SESSION['AVAR']={$_SESSION['AVAR']}</p>";</font> |
|---|
| 93 | </pre> |
|---|
| 94 | |
|---|
| 95 | <p>When the session is created in session_start( ), the global variable $<b>ADODB_SESS_CONN</b> holds |
|---|
| 96 | the connection object. |
|---|
| 97 | <p>The default name of the table is sessions2. If you want to override it: |
|---|
| 98 | |
|---|
| 99 | <pre> |
|---|
| 100 | include_once("adodb/session/adodb-session2.php"); |
|---|
| 101 | $options['table'] = 'mytablename'; |
|---|
| 102 | ADOdb_Session::config($driver, $host, $user, $password, $database,$options); |
|---|
| 103 | session_start(); |
|---|
| 104 | </pre> |
|---|
| 105 | |
|---|
| 106 | |
|---|
| 107 | <h3>ADOdb Session Handler Features</h3> |
|---|
| 108 | <ul> |
|---|
| 109 | <li>Ability to define a notification function that is called when a |
|---|
| 110 | session expires. Typically |
|---|
| 111 | used to detect session logout and release global resources. </li> |
|---|
| 112 | <li>Optimization of database writes. We crc32 the session data and |
|---|
| 113 | only perform an update |
|---|
| 114 | to the session data if there is a data change. </li> |
|---|
| 115 | <li>Support for large amounts of session data with CLOBs (see |
|---|
| 116 | adodb-session-clob2.php). Useful |
|---|
| 117 | for Oracle. </li> |
|---|
| 118 | <li>Support for encrypted session data, see |
|---|
| 119 | adodb-cryptsession2.php. Enabling encryption is simply a matter of |
|---|
| 120 | including adodb-cryptsession2.php instead of adodb-session2.php. </li> |
|---|
| 121 | </ul> |
|---|
| 122 | <h3>Session Handler Files </h3> |
|---|
| 123 | <p>There are 3 session management files that you can use: |
|---|
| 124 | </p> |
|---|
| 125 | <pre>adodb-session2.php : The default<br>adodb-cryptsession2.php : Use this if you want to store encrypted session data in the database<br>adodb-session-clob2.php : Use this if you are storing DATA in clobs and you are NOT using oci8 driver</pre> |
|---|
| 126 | <h2><strong>Usage Examples</strong></h2> |
|---|
| 127 | <p>To force non-persistent connections, call <font color="#004040"><b>Persist</b></font>() first before session_start(): |
|---|
| 128 | |
|---|
| 129 | |
|---|
| 130 | <pre> |
|---|
| 131 | <font color="#004040"> |
|---|
| 132 | include_once("adodb/session/adodb-session2.php"); |
|---|
| 133 | $driver = 'mysql'; $host = 'localhost'; $user = 'auser'; $pwd = 'secret'; $database = 'sessiondb'; |
|---|
| 134 | ADOdb_Session::config($driver, $host, $user, $password, $database,$options=false);<b><br>ADOdb_session::Persist($connectMode=false);</b> |
|---|
| 135 | session_start();<br> </font> |
|---|
| 136 | </pre> |
|---|
| 137 | <p> The parameter to the Persist( ) method sets the connection mode. You can |
|---|
| 138 | pass the following:</p> |
|---|
| 139 | <table width="50%" border="1"> |
|---|
| 140 | <tr> |
|---|
| 141 | <td><b>$connectMode</b></td> |
|---|
| 142 | <td><b>Connection Method</b></td> |
|---|
| 143 | </tr> |
|---|
| 144 | <tr> |
|---|
| 145 | <td>true</td> |
|---|
| 146 | <td><p>PConnect( )</p></td> |
|---|
| 147 | </tr> |
|---|
| 148 | <tr> |
|---|
| 149 | <td>false</td> |
|---|
| 150 | <td>Connect( )</td> |
|---|
| 151 | </tr> |
|---|
| 152 | <tr> |
|---|
| 153 | <td>'N'</td> |
|---|
| 154 | <td>NConnect( )</td> |
|---|
| 155 | </tr> |
|---|
| 156 | <tr> |
|---|
| 157 | <td>'P'</td> |
|---|
| 158 | <td>PConnect( )</td> |
|---|
| 159 | </tr> |
|---|
| 160 | <tr> |
|---|
| 161 | <td>'C'</td> |
|---|
| 162 | <td>Connect( )</td> |
|---|
| 163 | </tr> |
|---|
| 164 | </table> |
|---|
| 165 | <p>To use a encrypted sessions, simply replace the file adodb-session2.php:</p> |
|---|
| 166 | <pre> <font |
|---|
| 167 | color="#004040"><b><br>include('adodb/session/adodb-cryptsession2.php');</b><br>$driver = 'mysql'; $host = 'localhost'; $user = 'auser'; $pwd = 'secret'; $database = 'sessiondb'; |
|---|
| 168 | ADOdb_Session::config($driver, $host, $user, $password, $database,$options=false);<b><br>adodb_sess_open(false,false,$connectMode=false);</b> |
|---|
| 169 | session_start();<br></font></pre> |
|---|
| 170 | <p>And the same technique for adodb-session-clob2.php:</p> |
|---|
| 171 | <pre> <font |
|---|
| 172 | color="#004040"><br><b>include('adodb/session/adodb-session2-clob2.php');</b><br>$driver = 'oci8'; $host = 'localhost'; $user = 'auser'; $pwd = 'secret'; $database = 'sessiondb'; |
|---|
| 173 | ADOdb_Session::config($driver, $host, $user, $password, $database,$options=false);<b><br>adodb_sess_open(false,false,$connectMode=false);</b> |
|---|
| 174 | session_start();</font></pre> |
|---|
| 175 | <h2>Installation</h2> |
|---|
| 176 | <p>1. Create this table in your database. Here is the MySQL version: |
|---|
| 177 | <pre> <a |
|---|
| 178 | name="sessiontab"></a> <font color="#004040"> |
|---|
| 179 | CREATE TABLE sessions2( |
|---|
| 180 | sesskey VARCHAR( 64 ) NOT NULL DEFAULT '', |
|---|
| 181 | expiry DATETIME NOT NULL , |
|---|
| 182 | expireref VARCHAR( 250 ) DEFAULT '', |
|---|
| 183 | created DATETIME NOT NULL , |
|---|
| 184 | modified DATETIME NOT NULL , |
|---|
| 185 | sessdata LONGTEXT DEFAULT '', |
|---|
| 186 | PRIMARY KEY ( sesskey ) , |
|---|
| 187 | INDEX sess2_expiry( expiry ), |
|---|
| 188 | INDEX sess2_expireref( expireref ) |
|---|
| 189 | )</font></pre> |
|---|
| 190 | |
|---|
| 191 | <p> For PostgreSQL, use: |
|---|
| 192 | <pre>CREATE TABLE sessions2( |
|---|
| 193 | sesskey VARCHAR( 64 ) NOT NULL DEFAULT '', |
|---|
| 194 | expiry TIMESTAMP NOT NULL , |
|---|
| 195 | expireref VARCHAR( 250 ) DEFAULT '', |
|---|
| 196 | created TIMESTAMP NOT NULL , |
|---|
| 197 | modified TIMESTAMP NOT NULL , |
|---|
| 198 | sessdata TEXT DEFAULT '', |
|---|
| 199 | PRIMARY KEY ( sesskey ) |
|---|
| 200 | ); |
|---|
| 201 | </pre> |
|---|
| 202 | <pre>create INDEX sess2_expiry on sessions2( expiry ); |
|---|
| 203 | create INDEX sess2_expireref on sessions2 ( expireref );</pre> |
|---|
| 204 | <p>Here is the Oracle definition, which uses a CLOB for the SESSDATA field: |
|---|
| 205 | <pre> |
|---|
| 206 | <font |
|---|
| 207 | color="#004040">CREATE TABLE SESSIONS2<br>(<br> SESSKEY VARCHAR2(48 BYTE) NOT NULL,<br> EXPIRY DATE NOT NULL,<br> EXPIREREF VARCHAR2(200 BYTE),<br> CREATED DATE NOT NULL,<br> MODIFIED DATE NOT NULL,<br> SESSDATA CLOB,<br> PRIMARY KEY(SESSKEY)<br>); |
|---|
| 208 | <br>CREATE INDEX SESS2_EXPIRY ON SESSIONS2(EXPIRY); |
|---|
| 209 | CREATE INDEX SESS2_EXPIREREF ON SESSIONS2(EXPIREREF);</font></pre> |
|---|
| 210 | <p> We need to use a CLOB here because for text greater than 4000 bytes long, |
|---|
| 211 | Oracle requires you to use the CLOB data type. If you are using the oci8 driver, |
|---|
| 212 | ADOdb will automatically enable CLOB handling. So you can use either adodb-session2.php |
|---|
| 213 | or adodb-session-clob2.php - in this case it doesn't matter. <br> |
|---|
| 214 | <h2>Notifications</h2> |
|---|
| 215 | <p>You can receive notification when your session is cleaned up by the session garbage collector or |
|---|
| 216 | when you call session_destroy(). |
|---|
| 217 | <p>PHP's session extension will automatically run a special garbage collection function based on |
|---|
| 218 | your php.ini session.cookie_lifetime and session.gc_probability settings. This will in turn call |
|---|
| 219 | adodb's garbage collection function, which can be setup to do notification. |
|---|
| 220 | <p> |
|---|
| 221 | <pre> |
|---|
| 222 | PHP Session --> ADOdb Session --> Find all recs --> Send --> Delete queued |
|---|
| 223 | GC Function GC Function to be deleted notification records |
|---|
| 224 | executed at called by for all recs |
|---|
| 225 | random time Session Extension queued for deletion |
|---|
| 226 | </pre> |
|---|
| 227 | <p>When a session is created, we need to store a value in the session record (in the EXPIREREF field), typically |
|---|
| 228 | the userid of the session. Later when the session has expired, just before the record is deleted, |
|---|
| 229 | we reload the EXPIREREF field and call the notification function with the value of EXPIREREF, which |
|---|
| 230 | is the userid of the person being logged off. |
|---|
| 231 | <p>ADOdb uses a global variable $ADODB_SESSION_EXPIRE_NOTIFY that you must predefine before session |
|---|
| 232 | start to store the notification configuration. |
|---|
| 233 | $ADODB_SESSION_EXPIRE_NOTIFY is an array with 2 elements, the |
|---|
| 234 | first being the name of the session variable you would like to store in |
|---|
| 235 | the EXPIREREF field, and the 2nd is the notification function's name. </p> |
|---|
| 236 | <p>For example, suppose we want to be notified when a user's session has expired, |
|---|
| 237 | based on the userid. When the user logs in, we store the id in the global session variable |
|---|
| 238 | $USERID. The function name is 'NotifyFn'. |
|---|
| 239 | <p> |
|---|
| 240 | So we define (before session_start() is called): </p> |
|---|
| 241 | <pre> <font color="#004040"> |
|---|
| 242 | $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); |
|---|
| 243 | </font></pre> |
|---|
| 244 | And when the NotifyFn is called (when the session expires), the |
|---|
| 245 | $USERID is passed in as the first parameter, eg. NotifyFn($userid, $sesskey). The |
|---|
| 246 | session key (which is the primary key of the record in the sessions |
|---|
| 247 | table) is the 2nd parameter. |
|---|
| 248 | <p> Here is an example of a Notification function that deletes some |
|---|
| 249 | records in the database and temporary files: </p> |
|---|
| 250 | <pre><font color="#004040"> |
|---|
| 251 | function NotifyFn($expireref, $sesskey) |
|---|
| 252 | { |
|---|
| 253 | global $ADODB_SESS_CONN; # the session connection object |
|---|
| 254 | $user = $ADODB_SESS_CONN->qstr($expireref); |
|---|
| 255 | |
|---|
| 256 | $ADODB_SESS_CONN->Execute("delete from shopping_cart where user=$user"); |
|---|
| 257 | system("rm /work/tmpfiles/$expireref/*"); |
|---|
| 258 | }</font> |
|---|
| 259 | </pre> |
|---|
| 260 | <p> NOTE 1: If you have register_globals disabled in php.ini, then you |
|---|
| 261 | will have to manually set the EXPIREREF. E.g. </p> |
|---|
| 262 | <pre> <font color="#004040"> |
|---|
| 263 | $GLOBALS['USERID'] = GetUserID(); |
|---|
| 264 | $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn');</font> |
|---|
| 265 | </pre> |
|---|
| 266 | <p> NOTE 2: If you want to change the EXPIREREF after the session |
|---|
| 267 | record has been created, you will need to modify any session variable |
|---|
| 268 | to force a database record update. |
|---|
| 269 | </p> |
|---|
| 270 | <h3>Synchronizing Page Frames</h3> |
|---|
| 271 | <p>Sometimes you need to load multiple pages in a frameset and make sure that the session variables are synchronized. |
|---|
| 272 | For example we have a page frame.php with an iframe child1.php: |
|---|
| 273 | <pre> |
|---|
| 274 | frame.php |
|---|
| 275 | child1.php |
|---|
| 276 | </pre> |
|---|
| 277 | <p>Assume frame.php receives some $_GET variables and it updates the $_SESSION variables in memory. However the $_SESSION variables are only |
|---|
| 278 | updated in the database when the page ends. So is possible that child1.php |
|---|
| 279 | will still have the old $_SESSION variables. |
|---|
| 280 | <ul> |
|---|
| 281 | <li> $_GET variables are posted to frame.php |
|---|
| 282 | <li> frame.php updates $_SESSION variables, but not the database |
|---|
| 283 | <li> child1.php is sent to browser, using old $_SESSION variables it loads from database |
|---|
| 284 | <li> frame.php completes it's processing and saves new $_SESSION variables into database -- too late! |
|---|
| 285 | </ul> |
|---|
| 286 | <p>We can fix this by using the php function <a href=http://php.net/session_commit>session_commit</a>: |
|---|
| 287 | <pre> |
|---|
| 288 | <?php |
|---|
| 289 | session_start(); |
|---|
| 290 | . |
|---|
| 291 | # do any required processing... |
|---|
| 292 | |
|---|
| 293 | session_commit(); |
|---|
| 294 | |
|---|
| 295 | # now session variables have been saved to the database, and child1.php will get the latest $_SESSION variables. |
|---|
| 296 | ?> |
|---|
| 297 | <html> |
|---|
| 298 | <body> |
|---|
| 299 | <iframe src=child1.php> |
|---|
| 300 | </iframe> |
|---|
| 301 | </body> |
|---|
| 302 | </html> |
|---|
| 303 | </pre> |
|---|
| 304 | <h3>Neat Notification Tricks</h3> |
|---|
| 305 | <p><i>ExpireRef</i> normally holds the user id of the current session. |
|---|
| 306 | </p> |
|---|
| 307 | <p>1. You can then write a session monitor, scanning expireref to see |
|---|
| 308 | who is currently logged on. |
|---|
| 309 | </p> |
|---|
| 310 | <p>2. If you delete the sessions record for a specific user, eg. |
|---|
| 311 | </p> |
|---|
| 312 | <pre>delete from sessions where expireref = '$USER'<br></pre> |
|---|
| 313 | then the user is logged out. Useful for ejecting someone from a |
|---|
| 314 | site. |
|---|
| 315 | <p>3. You can scan the sessions table to ensure no user |
|---|
| 316 | can be logged in twice. Useful for security reasons. |
|---|
| 317 | </p> |
|---|
| 318 | <h2>Compression/Encryption Schemes</h2> |
|---|
| 319 | Since ADOdb 4.05, thanks to Ross Smith, multiple encryption and |
|---|
| 320 | compression schemes are supported. Currently, supported are: |
|---|
| 321 | <p> |
|---|
| 322 | <pre> MD5Crypt (crypt.inc.php)<br> MCrypt<br> Secure (Horde's emulation of MCrypt, if MCrypt module is not available.)<br> GZip<br> BZip2<br></pre> |
|---|
| 323 | <p>These are stackable. E.g. |
|---|
| 324 | <pre>ADODB_Session::filter(new ADODB_Compress_Bzip2());<br>ADODB_Session::filter(new ADODB_Encrypt_MD5());<br></pre> |
|---|
| 325 | will compress and then encrypt the record in the database. |
|---|
| 326 | <h2>Session Cookie Regeneration: adodb_session_regenerate_id()</h2> |
|---|
| 327 | <p>Dynamically change the current session id with a newly generated one and update |
|---|
| 328 | database. Currently only works with cookies. Useful to improve security by |
|---|
| 329 | reducing the risk of session-hijacking. See this article on <a href=http://shiflett.org/articles/security-corner-feb2004>Session |
|---|
| 330 | Fixation</a> for more info |
|---|
| 331 | on the theory behind this feature. Usage:<pre> |
|---|
| 332 | include('path/to/adodb/session/adodb-session2.php'); |
|---|
| 333 | |
|---|
| 334 | session_start(); |
|---|
| 335 | # Approximately every 10 page loads, reset cookie for safety. |
|---|
| 336 | # This is extremely simplistic example, better |
|---|
| 337 | # to regenerate only when the user logs in or changes |
|---|
| 338 | # user privilege levels. |
|---|
| 339 | if ((rand()%10) == 0) adodb_session_regenerate_id(); |
|---|
| 340 | </pre> |
|---|
| 341 | <p>This function calls session_regenerate_id() internally or simulates it if the function does not exist. |
|---|
| 342 | <h2>Vacuum/Optimize Database</h2> |
|---|
| 343 | <p>During session garbage collection, if postgresql is detected, |
|---|
| 344 | ADOdb can be set to run VACUUM. If mysql is detected, then optimize database |
|---|
| 345 | could be called.You can turn this on or off using:</p> |
|---|
| 346 | <pre>$turnOn = true; # or false |
|---|
| 347 | ADODB_Session::optimize($turnOn); |
|---|
| 348 | </pre> |
|---|
| 349 | <p>The default is optimization is disabled.</p> |
|---|
| 350 | <h2><a name=compat></a>Backwards Compatability </h2> |
|---|
| 351 | <p>The older method of connecting to ADOdb using global variables is still supported:</p> |
|---|
| 352 | <pre> $ADODB_SESSION_DRIVER='mysql'; |
|---|
| 353 | $ADODB_SESSION_CONNECT='localhost'; |
|---|
| 354 | $ADODB_SESSION_USER ='root'; |
|---|
| 355 | $ADODB_SESSION_PWD ='abc'; |
|---|
| 356 | $ADODB_SESSION_DB ='phplens'; |
|---|
| 357 | |
|---|
| 358 | include('path/to/adodb/session/adodb-<strong>session2</strong>.php'); </pre> |
|---|
| 359 | <p>In the above example, the only things you need to change in your code to upgrade |
|---|
| 360 | is </p> |
|---|
| 361 | <ul> |
|---|
| 362 | <li>your session table format to the new one.</li> |
|---|
| 363 | <li>the include file from adodb-session.php to adodb-session2.php. </li> |
|---|
| 364 | </ul> |
|---|
| 365 | <h2>More Info</h2> |
|---|
| 366 | <p>Also see the <a href="docs-adodb.htm">core ADOdb documentation</a>. And if |
|---|
| 367 | you are interested in the obsolete adodb-session.php, see <a href="docs-session.old.htm">old |
|---|
| 368 | session documentation</a>. </p> |
|---|
| 369 | </body> |
|---|
| 370 | </html> |
|---|