]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiDB/adodb/adodb.inc.php
rename fix
[SourceForge/phpwiki.git] / lib / WikiDB / adodb / adodb.inc.php
1 <?php 
2
3 /** 
4  * @version V1.71 18 Jan 2001 (c) 2000, 2001 John Lim (jlim@natsoft.com.my). All rights reserved.
5  * Released under both BSD license and Lesser GPL library license. 
6  * Whenever there is any discrepancy between the two licenses, 
7  * the BSD license will take precedence. 
8  *
9  * Set tabs to 4 for best viewing.
10  * 
11  * Latest version is available at http://php.weblogs.com
12  * 
13  * This is the main include file for ADODB.
14  * It has all the generic functionality of ADODB. 
15  * Database specific drivers are stored in the adodb-*.inc.php files.
16  *
17  * Requires PHP4.01pl2 or later because it uses include_once
18 */
19
20  if (!defined('_ADODB_LAYER')) {
21         define('_ADODB_LAYER',1);
22         
23         //==============================================================================================        
24         // CONSTANT DEFINITIONS
25         //==============================================================================================        
26
27         define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
28         
29         define('ADODB_FETCH_DEFAULT',0);
30         define('ADODB_FETCH_NUM',1);
31         define('ADODB_FETCH_ASSOC',2);
32         define('ADODB_FETCH_BOTH',3);
33         
34         // allow [ ] @ and . in table names
35         define('ADODB_TABLE_REGEX','([]0-9a-z_\.\@\[-]*)');
36         
37         if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10);
38
39         /** 
40          * Set ADODB_DIR to the directory where this file resides...
41          * This constant was formerly called $ADODB_RootPath
42          */
43         if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__));
44         
45         //==============================================================================================        
46         // GLOBAL VARIABLES
47         //==============================================================================================        
48
49         GLOBAL 
50                 $ADODB_vers,            // database version
51                 $ADODB_Database,        // last database driver used
52                 $ADODB_COUNTRECS,       // count number of records returned - slows down query
53                 $ADODB_CACHE_DIR,       // directory to cache recordsets
54                 $ADODB_FETCH_MODE;      // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
55         
56         //==============================================================================================        
57         // GLOBAL SETUP
58         //==============================================================================================        
59         
60         $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
61         
62         if (!isset($ADODB_CACHE_DIR)) {
63                 $ADODB_CACHE_DIR = '/tmp';
64         } else {
65                 // do not accept url based paths, eg. http:/ or ftp:/
66                 if (strpos($ADODB_CACHE_DIR,':/') !== false) 
67                         die("Illegal \$ADODB_CACHE_DIR");
68         }
69         
70         //==============================================================================================        
71         // CHANGE NOTHING BELOW UNLESS YOU ARE CODING
72         //==============================================================================================        
73
74         
75         // Initialize random number generator for randomizing cache flushes
76         srand(((double)microtime())*1000000);
77         
78         /**
79          * Name of last database driver loaded into memory. Set by ADOLoadCode().
80          */
81         $ADODB_Database = '';
82         
83         /**
84          * ADODB version as a string.
85          */
86         $ADODB_vers = 'V1.71 18 Jan 2001 (c) 2000, 2001 John Lim (jlim@natsoft.com.my). All rights reserved. Released BSD & LGPL.';
87
88         /**
89          * Determines whether recordset->RecordCount() is used. 
90          * Set to false for highest performance -- RecordCount() will always return -1 then
91          * for databases that provide "virtual" recordcounts...
92          */
93         $ADODB_COUNTRECS = true; 
94
95         //==============================================================================================        
96         // CLASS ADOFieldObject
97         //==============================================================================================        
98
99         /**
100          * Helper class for FetchFields -- holds info on a column
101          */
102         class ADOFieldObject { 
103                 var $name = '';
104                 var $max_length=0;
105                 var $type="";
106
107                 // additional fields by dannym... (danny_milo@yahoo.com)
108                 var $not_null = false; 
109                 // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
110                 // so we can as well make not_null standard (leaving it at "false" does not harm anyways)
111
112                 var $has_default = false; // this one I have done only in mysql and postgres for now ... 
113                         // others to come (dannym)
114                 var $default_value; // default, if any, and supported. Check has_default first.
115         }
116         
117         
118         //==============================================================================================        
119         // CLASS ADOConnection
120         //==============================================================================================        
121         
122         /**
123          * Connection object. For connecting to databases, and executing queries.
124          */ 
125         class ADOConnection {
126         /*
127          * PUBLIC VARS 
128          */
129         var $dataProvider = 'native';
130         var $databaseType = '';         // RDBMS currently in use, eg. odbc, mysql, mssql                                       
131         var $database = '';                     // Name of database to be used. 
132         var $host = '';                         // The hostname of the database server  
133         var $user = '';                         // The username which is used to connect to the database server. 
134         var $password = '';             // Password for the username
135         var $debug = false;             // if set to true will output sql statements
136         var $maxblobsize = 8000;        // maximum size of blobs or large text fields -- some databases die otherwise like foxpro
137         var $concat_operator = '+'; // default concat operator -- change to || for Oracle/Interbase     
138         var $fmtDate = "'Y-m-d'";       // used by DBDate() as the default date format used by the database
139         var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; // used by DBTimeStamp as the default timestamp fmt.
140         var $true = '1';                        // string that represents TRUE for a database
141         var $false = '0';                       // string that represents FALSE for a database
142         var $replaceQuote = "\\'";      // string to use to replace quotes
143     var $hasInsertID = false;   // supports autoincrement ID?
144     var $hasAffectedRows = false;       // supports affected rows for update/delete?
145     var $autoCommit = true; 
146         var $charSet=false;             // character set to use - only for interbase
147         var $metaTablesSQL = '';
148         var $hasTop = false;            // support mssql/access SELECT TOP 10 * FROM TABLE
149         var $hasLimit = false;          // support pgsql/mysql SELECT * FROM TABLE LIMIT 10
150         var $readOnly = false;          // this is a readonly database - used by phpLens
151         var $hasMoveFirst = false;  // has ability to run MoveFirst(), scrolling backwards
152         var $hasGenID = false;          // can generate sequences using GenID();
153         var $genID = 0;                         // sequence id used by GenID();
154         var $raiseErrorFn = false;      // error function to call
155         var $upperCase = false;         // uppercase function to call for searching/where
156         var $isoDates = false; // accepts dates in ISO format
157         
158         /*
159          * PRIVATE VARS
160          */
161         var $_connectionID      = false;        // The returned link identifier whenever a successful database connection is made.      */
162                 
163         var $_errorMsg = '';            // A variable which was used to keep the returned last error message.  The value will
164                                         //then returned by the errorMsg() function      
165                                                 
166         var $_queryID = false;          // This variable keeps the last created result link identifier.         */
167         
168         var $_isPersistentConnection = false;   // A boolean variable to state whether its a persistent connection or normal connection.        */
169         
170         var $_bindInputArray = false; // set to true if ADOConnection.Execute() permits binding of array parameters.
171         
172         /**
173          * Constructor
174          */
175         function ADOConnection()                        
176         {
177                 die('Virtual Class -- cannot instantiate');
178         }
179         
180
181         /**
182          * Connect to database
183          *
184          * @param [argHostname]         Host to connect to
185          * @param [argUsername]         Userid to login
186          * @param [argPassword]         Associated password
187          * @param [argDatabaseName]     database
188          *
189          * @return true or false
190          */       
191         function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") {
192                 if ($argHostname != "") $this->host = $argHostname;
193                 if ($argUsername != "") $this->user = $argUsername;
194                 if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons
195                 if ($argDatabaseName != "") $this->database = $argDatabaseName;         
196                 
197                 $this->_isPersistentConnection = false; 
198                 if ($fn = $this->raiseErrorFn) {
199                         if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
200                         $err = $this->ErrorMsg();
201                         if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
202                         $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database);
203                 } else 
204                         if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
205
206                 if ($this->debug) print $this->host.': '.$this->ErrorMsg().'<br>';
207                 
208                 return false;
209         }       
210         
211
212         /**
213          * Establish persistent connect to database
214          *
215          * @param [argHostname]         Host to connect to
216          * @param [argUsername]         Userid to login
217          * @param [argPassword]         Associated password
218          * @param [argDatabaseName]     database
219          *
220          * @return return true or false
221          */     
222         function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
223         {
224                 if ($argHostname != "") $this->host = $argHostname;
225                 if ($argUsername != "") $this->user = $argUsername;
226                 if ($argPassword != "") $this->password = $argPassword;
227                 if ($argDatabaseName != "") $this->database = $argDatabaseName;         
228                         
229                 $this->_isPersistentConnection = true;  
230                 
231                 if ($fn = $this->raiseErrorFn) {
232                         if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
233                         $err = $this->ErrorMsg();
234                         if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
235                         $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database);
236                 } else 
237                         if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
238
239                 if ($this->debug) print $this->host.': '.$this->ErrorMsg().'<br>';
240                 
241                 return false;
242         }
243
244         
245         /**
246          * Should prepare the sql statement and return the stmt resource.
247          * For databases that do not support this, we return the $sql. To ensure
248          * compatibility with databases that do not support prepare:
249          *
250          *   $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
251          *   $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
252          *   $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
253          *
254          * @param sql   SQL to send to database
255          *
256          * @return return TRUE or FALSE, or the $sql.
257          *
258          */     
259         function Prepare($sql)
260         {
261                 return $sql;
262         }
263
264         
265         /**
266         * PEAR DB Compat - do not use internally. 
267         */
268         function Quote($s)
269         {
270                 return $this->qstr($s);
271         }
272
273         
274         /**
275         * PEAR DB Compat - do not use internally. 
276         */
277         function ErrorNative()
278     {
279         return $this->ErrorNo();
280     }
281
282         
283    /**
284         * PEAR DB Compat - do not use internally. 
285         */
286     function nextId($seq_name)
287         {
288                 return $this->GenID($seq_name);
289         }
290
291         
292         /**
293         * PEAR DB Compat - do not use internally. 
294         *
295         * Appears that the fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB 
296         * are the same numeric values!
297         */
298         function SetFetchMode($mode)
299         {
300         global $ADODB_FETCH_MODE;
301                 $ADODB_FETCH_MODE = $mode;
302         }
303         
304
305         /**
306         * PEAR DB Compat - do not use internally. 
307         */
308         function &Query($sql, $inputarr=false)
309         {
310                 $rs = &$this->Execute($sql, $inputarr);
311                 if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
312                 return $rs;
313         }
314
315         
316         /**
317         * PEAR DB Compat - do not use internally
318         */
319         function &LimitQuery($sql, $offset, $count)
320         {
321                 $rs = &$this->SelectLimit($sql, $count, $offset); // swap 
322                 if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
323                 return $rs;
324         }
325
326         
327         /**
328         * PEAR DB Compat - do not use internally
329         */
330         function Disconnect()
331         {
332                 return $this->Close();
333         }
334
335         
336         /**
337          * Execute SQL 
338          *
339          * @param sql           SQL statement to execute
340          * @param [inputarr]    holds the input data to bind to. Null elements will be set to null.
341          * @param [arg3]        reserved for john lim for future use
342          * @return              RecordSet or false
343          */
344         function &Execute($sql,$inputarr=false,$arg3=false) 
345         {
346                 if (!$this->_bindInputArray && $inputarr) {
347                         $sqlarr = explode('?',$sql);
348                         $sql = '';
349                         $i = 0;
350                         foreach($inputarr as $v) {
351
352                                 $sql .= $sqlarr[$i];
353                                 // from Ron Baldwin <ron.baldwin@sourceprose.com>
354                                 // Only quote string types      
355                                 if (gettype($v) == 'string')
356                                         $sql .= "'".$v."'";
357                                 else if ($v === null)
358                                         $sql .= 'NULL';
359                                 else
360                                         $sql .= $v;
361                                 $i += 1;
362         
363                         }
364                         $sql .= $sqlarr[$i];
365                         if ($i+1 != sizeof($sqlarr))    
366                                 print "Input Array does not match ?: ".htmlspecialchars($sql);
367                         $inputarr = false;
368                 }
369                 
370                 if ($this->debug) {
371                         $ss = '';
372                         if ($inputarr) {
373                                 foreach ($inputarr as $kk => $vv)  {
374                                         if (is_string($vv) && strlen($vv)>64) $vv = substr($vv,0,64).'...';
375                                         $ss .= "($kk=>'$vv') ";
376                                 }
377                                 $ss = "[ $ss ]";
378                         }
379                         print "<hr>($this->databaseType): ".htmlspecialchars($sql)." &nbsp; <code>$ss</code><hr>";
380                         $this->_queryID = $this->_query($sql,$inputarr,$arg3);
381
382                         if ($this->databaseType == 'mssql') { // ErrorNo is a slow function call in mssql, and not reliable
383                                 if($this->ErrorMsg()) {
384                                         $err = $this->ErrorNo();
385                                         if ($err) print $err.': '.$this->ErrorMsg().'<br>';
386                                 }
387                         } else 
388                                 if (!$this->_queryID) print $this->ErrorNo().': '.$this->ErrorMsg().'<br>';
389                                 
390                 } else 
391                         $this->_queryID =@$this->_query($sql,$inputarr,$arg3);
392                 
393                 if ($this->_queryID === false) {
394                         if ($fn = $this->raiseErrorFn) {
395                                 $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr);
396                         }
397                         return false;
398                 } else if ($this->_queryID === true){
399                         $rs = new ADORecordSet_empty();
400                         return $rs;
401                 }
402                 $rsclass = "ADORecordSet_".$this->databaseType;
403                 $rs = new $rsclass($this->_queryID); // &new not supported by older PHP versions
404                 $rs->connection = &$this; // Pablo suggestion
405                 $rs->Init();
406                 //$this->_insertQuery(&$rs); PHP4 handles closing automatically
407
408                 if (is_string($sql)) $rs->sql = $sql;
409                 return $rs;
410         }
411
412         
413         /**
414          * Generates a sequence id and stores it in $this->genID;
415          * GenID is only available if $this->hasGenID = true;
416          *
417          * @seqname             name of sequence to use
418          * @startID             if sequence does not exist, start at this ID
419          * @return              0 if not supported, otherwise a sequence id
420          */
421         
422         function GenID($seqname='adodbseq',$startID=1)
423         {
424                 if (!$this->hasGenID) {
425                         return 0; // formerly returns false pre 1.60
426                 }
427                 
428                 $getnext = sprintf($this->_genIDSQL,$seqname);
429                 $rs = @$this->Execute($getnext);
430                 if (!$rs) {
431                         $u = strtoupper($seqname);
432                         $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
433                         $rs = $this->Execute($getnext);
434                 }
435                 if ($rs && !$rs->EOF) $this->genID = (integer) reset($rs->fields);
436                 else $this->genID = 0; // false
437                 
438                 if ($rs) $rs->Close();
439                 
440                 return $this->genID;
441         }
442         
443         
444         /**
445          * @return  the last inserted ID. Not all databases support this.
446          */ 
447         function Insert_ID()
448         {
449                 if ($this->hasInsertID) return $this->_insertid();
450                 if ($this->debug) print '<p>Insert_ID error</p>';
451                 return false;
452         }
453     
454         
455     /**
456          * Portable Insert ID. Pablo Roca <pabloroca@mvps.org>
457          *
458          * @return  the last inserted ID. All databases support this. But aware possible
459          * problems in multiuser environments. Heavy test this before deploying.
460          */ 
461         function PO_Insert_ID($table="", $id="") 
462                 {
463            if ($this->hasInsertID){
464                return $this->Insert_ID();
465            } else {
466                return $this->GetOne("SELECT MAX($id) FROM $table");
467            }
468         }       
469         
470                 
471      /**
472          * @return  # rows affected by UPDATE/DELETE
473          */ 
474         function Affected_Rows()
475         {
476                 if ($this->hasAffectedRows) {
477                        $val = $this->_affectedrows();
478                        return ($val < 0) ? false : $val;
479                 }
480                         
481                 if ($this->debug) print '<p>Affected_Rows error</p>';
482                 return false;
483         }
484         
485         
486     /**
487          * @return  the last error message
488          */
489         function ErrorMsg()
490         {
491                 return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
492         }
493         
494         
495         /**
496          * @return the last error number. Normally 0 means no error.
497          */
498         function ErrorNo() 
499         {
500                 return ($this->_errorMsg) ? -1 : 0;
501         }
502         
503         
504         /**
505          * @returns an array with the primary key columns in it.
506          */
507         function MetaPrimaryKeys($table)
508         {
509                 return false;
510         }
511         
512         
513         /**
514          * Choose a database to connect to. Many databases do not support this.
515          *
516          * @param dbName        is the name of the database to select
517          * @return              true or false
518          */
519         function SelectDB($dbName) 
520         {return false;}
521         
522         
523         /**
524         * Will select, getting rows from $offset (1-based), for $nrows. 
525         * This simulates the MySQL "select * from table limit $offset,$nrows" , and
526         * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
527         * MySQL and PostgreSQL parameter ordering is the opposite of the other.
528         * eg. 
529         *  SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
530         *  SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
531         *
532         * Uses SELECT TOP for Microsoft databases, and FIRST_ROWS CBO hint for Oracle 8+
533         * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
534         *
535         * @param sql
536         * @param [offset]       is the row to start calculations from (1-based)
537         * @param [rows]         is the number of rows to get
538         * @param [inputarr]     array of bind variables
539         * @param [arg3]         is a private parameter only used by jlim
540         * @param [secs2cache]           is a private parameter only used by jlim
541         * @return               the recordset ($rs->databaseType == 'array')
542         */
543         function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0)
544         {
545                 if ($this->hasTop && $nrows > 0) {
546                 // suggested by Reinhard Balling. Access requires top after distinct 
547                 
548                         if ($offset <= 0) {
549                                 $sql = preg_replace(
550                                 '/(^select[\\t\\n ]*(distinctrow|distinct)?)/i','\\1 top '.$nrows.' ',$sql);
551                                 
552                                         if ($secs2cache>0) return $this->CacheExecute($secs2cache, $sql,$inputarr,$arg3);
553                                         else return $this->Execute($sql,$inputarr,$arg3);
554                         } else {
555                                 $nrows += $offset;
556                                 $sql = preg_replace(
557                                 '/(^select[\\t\\n ]*(distinctrow|distinct)?)/i','\\1 top '.$nrows.' ',$sql);
558                                 $nrows = -1;
559                         }
560          
561                 }
562                 if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr,$arg3);
563                 else $rs = &$this->Execute($sql,$inputarr,$arg3);
564                 if ($rs && !$rs->EOF) {
565                         return $this->_rs2rs($rs,$nrows,$offset);
566                 }
567                 //print_r($rs);
568                 return $rs;
569         }
570         
571         
572         /**
573         * Convert recordset to an array recordset
574         * input recordset's cursor should be at beginning, and
575         * old $rs will be closed.
576         *
577         * @param rs                     the recordset to copy
578         * @param [nrows]        number of rows to retrieve (optional)
579         * @param [offset]       offset by number of rows (optional)
580         * @return                       the new recordset
581         */
582         function &_rs2rs(&$rs,$nrows=-1,$offset=-1)
583         {
584                 
585                 $arr = &$rs->GetArrayLimit($nrows,$offset);
586                 $flds = array();
587                 for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++)
588                         $flds[] = &$rs->FetchField($i);
589                 $rs->Close();
590                 
591                 $rs2 = new ADORecordSet_array();
592                 $rs2->connection = &$this;
593                 $rs2->InitArrayFields($arr,$flds);
594                 return $rs2;
595         }
596         
597         
598         /**
599         * Return first element of first row of sql statement. Recordset is disposed
600         * for you.
601         *
602         * @param sql                    SQL statement
603         * @param [inputarr]             input bind array
604         */
605         function GetOne($sql,$inputarr=false)
606         {
607                 $ret = false;
608                 $rs = $this->Execute($sql,$inputarr);
609                 if ($rs) {              
610                         if (!$rs->EOF) $ret = reset($rs->fields);
611                         $rs->Close();
612                 } 
613                 
614                 return $ret;
615         }
616         
617         
618         /**
619         * Return all rows. Compat with PEAR DB
620         *
621         * @param sql                    SQL statement
622         * @param [inputarr]             input bind array
623         */
624         function &GetAll($sql,$inputarr=false)
625         {
626                 $rs = $this->Execute($sql,$inputarr);
627                 if (!$rs) 
628                         if (defined('ADODB_PEAR')) return ADODB_PEAR_Error();
629                         else return false;
630                 return $rs->GetArray();
631         }
632         
633         
634         /**
635         * Return one row of sql statement. Recordset is disposed for you.
636         *
637         * @param sql                    SQL statement
638         * @param [inputarr]             input bind array
639         */
640         function GetRow($sql,$inputarr=false)
641         {
642                 $rs = $this->Execute($sql,$inputarr);
643                 if ($rs) {
644                         $arr = false;
645                         if (!$rs->EOF) $arr = $rs->fields;
646                         $rs->Close();
647                         return $arr;
648                 }
649                 return false;
650         }
651         
652         
653         
654         /**
655         * Will select, getting rows from $offset (1-based), for $nrows. 
656         * This simulates the MySQL "select * from table limit $offset,$nrows" , and
657         * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
658         * MySQL and PostgreSQL parameter ordering is the opposite of the other.
659         * eg. 
660         *  CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
661         *  CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
662         *
663         * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
664         *
665         * @param secs2cache     seconds to cache data, set to 0 to force query
666         * @param sql
667         * @param [offset]       is the row to start calculations from (1-based)
668         * @param [nrows]        is the number of rows to get
669         * @param [inputarr]     array of bind variables
670         * @param [arg3]         is a private parameter only used by jlim
671         * @return               the recordset ($rs->databaseType == 'array')
672         */
673         function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false)
674     {
675                 return $this->SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache);
676         }
677         
678         
679         function CacheFlush($sql)
680         {
681                 $f = $this->_gencachename($sql);
682                 adodb_write_file($f,''); // is adodb_write_file needed?
683                 @unlink($f);
684         }
685         
686         
687         function _gencachename($sql)
688         {
689         global $ADODB_CACHE_DIR;
690         
691                 return $ADODB_CACHE_DIR.'/adodb_'.md5($sql.$this->databaseType.$this->database.$this->user).'.cache';
692         }
693         
694         
695         /**
696          * Execute SQL, caching recordsets.
697          *
698          * @param secs2cache    seconds to cache data, set to 0 to force query
699          * @param sql           SQL statement to execute
700          * @param [inputarr]    holds the input data  to bind to
701          * @param [arg3]        reserved for john lim for future use
702          * @return              RecordSet or false
703          */
704         function &CacheExecute($secs2cache,$sql,$inputarr=false,$arg3=false)
705         {
706                 include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
707                 // cannot cache if $inputarr set
708                 if ($inputarr) return $this->Execute($sql, $inputarr, $arg3); 
709                 
710                 $md5file = $this->_gencachename($sql);
711                 $err = '';
712                 
713                 if ($secs2cache > 0)$rs = &csv2rs($md5file,$err,$secs2cache);
714                 else {
715                         $err='Timeout 1';
716                         $rs = false;
717                 }
718                 
719                 if (!$rs) {
720                 // no cached rs found
721                         if ($this->debug) print " $md5file cache failure: $err<br>";
722                         $rs = &$this->Execute($sql,$inputarr,$arg3);
723                         if ($rs) {
724                                 $eof = $rs->EOF;
725                                 $rs = &$this->_rs2rs($rs);
726                                 $txt = &rs2csv($rs,false,$sql);
727                                 
728                                 if (!adodb_write_file($md5file,$txt,$this->debug) && $this->debug) print ' Cache write error<br>';
729                                 if ($rs->EOF && !$eof) {
730                                         $rs = &csv2rs($md5file,$err);           
731                                         $rs->connection = &$this; // Pablo suggestion
732                                 }  
733                                 
734                         } else
735                                 @unlink($md5file);
736                 } else { 
737                 // ok, set cached object found
738                         $rs->connection = &$this; // Pablo suggestion
739                         if ($this->debug){ 
740                         $ttl = $rs->timeCreated + $secs2cache - time();
741                         print " $md5file success ttl=$ttl<br>";
742                         }
743                 }
744                 return $rs;
745         }
746         
747         
748     /**
749          * Generates an Update Query based on an existing recordset.
750          * $arrFields is an associative array of fields with the value
751          * that should be assigned.
752          *
753          * Note: This function should only be used on a recordset
754          *       that is run against a single table.
755          *
756          * "Jonathan Younger" <jyounger@unilab.com>
757          */
758         function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false)
759         {
760                 include_once(ADODB_DIR.'/adodb-lib.inc.php');
761                 return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq);
762         }
763
764
765         /**
766          * Generates an Insert Query based on an existing recordset.
767          * $arrFields is an associative array of fields with the value
768          * that should be assigned.
769          *
770          * Note: This function should only be used on a recordset
771          *       that is run against a single table.
772          */
773         function GetInsertSQL(&$rs, $arrFields,$magicq=false)
774         {       
775                 include_once(ADODB_DIR.'/adodb-lib.inc.php');
776                 return _adodb_getinsertsql($this,$rs,$arrFields,$magicq);
777         }
778         
779
780         /**
781         * Usage:
782         *       UpdateBlob('TABLE', 'COLUMN', $var, 'ID=1', 'BLOB');
783         *       
784         *       $blobtype supports 'BLOB' and 'CLOB'
785         *
786         *       $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
787         *       $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
788         */
789         function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
790         {
791                 $sql = "UPDATE $table SET $column=".$this->qstr($val)." where $where";
792                 $rs = $this->Execute($sql);
793                 
794                 $rez = !empty($rs);
795                 if ($rez) $rs->Close();
796                 return $rez;
797         }
798         
799         
800         /**
801         * Usage:
802         *       UpdateBlob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
803         *
804         *       $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
805         *       $conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
806         */
807         function UpdateClob($table,$column,$val,$where)
808         {
809                 return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
810         }
811         
812         
813         /**
814         * not used - will probably remove in future
815         */
816         function BlankRecordSet($id=false)
817         {
818                 $rsclass = "ADORecordSet_".$this->databaseType;
819                 return new $rsclass($id);
820         }
821         
822         
823         /**
824          *  @meta       contains the desired type, which could be...
825          *      C for character. You will have to define the precision yourself.
826          *      X for teXt. For unlimited character lengths.
827          *      B for Binary
828          *  F for floating point, with no need to define scale and precision
829          *      N for decimal numbers, you will have to define the (scale, precision) yourself
830          *      D for date
831          *      T for timestamp
832          *      L for logical/Boolean
833          *      I for integer
834          *      R for autoincrement counter/integer
835          *  and if you want to use double-byte, add a 2 to the end, like C2 or X2.
836          * 
837          *
838          * @return the actual type of the data or false if no such type available
839         */
840         function ActualType($meta)
841         {
842                 switch($meta) {
843                 case 'C':
844                 case 'X':
845                         return 'VARCHAR';
846                 case 'B':
847                         
848                 case 'D':
849                 case 'T':
850                 case 'L':
851                 
852                 case 'R':
853                         
854                 case 'I':
855                 case 'N':
856                         return false;
857                 }
858         }
859         
860         
861         /*
862         * Maximum size of C field
863         */
864         function CharMax()
865         {
866                 return 255; // make it conservative if not defined
867         }
868         
869         
870         /*
871         * Maximum size of X field
872         */
873         function TextMax()
874         {
875                 return 4000; // make it conservative if not defined
876         }
877         
878         
879         /**
880          * Close Connection
881          */
882         function Close() 
883         {
884                 return $this->_close();
885                 
886                 // "Simon Lee" <simon@mediaroad.com> reports that persistent connections need 
887                 // to be closed too!
888                 //if ($this->_isPersistentConnection != true) return $this->_close();
889                 //else return true;     
890         }
891         
892         
893         /**
894          * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
895          *
896          * @return true if succeeded or false if database does not support transactions
897          */
898         function BeginTrans() {return false;}
899         
900         
901         /**
902          * If database does not support transactions, always return true as data always commited
903          *
904          * @return true/false.
905          */
906         function CommitTrans() 
907         { return true;}
908         
909         
910         /**
911          * If database does not support transactions, rollbacks always fail, so return false
912          *
913          * @return true/false.
914          */
915         function RollbackTrans() 
916         { return false;}
917
918
919     /**
920          * return the databases that the driver can connect to. 
921          * Some databases will return an empty array.
922          *
923          * @return an array of database names.
924          */
925         function &MetaDatabases() 
926                 {return false;}
927         
928         /**
929          * @return  array of tables for current database.
930          */ 
931     function &MetaTables() 
932         {
933                 if ($this->metaTablesSQL) {
934                         $rs = $this->Execute($this->metaTablesSQL);
935                         if ($rs === false) return false;
936                         $arr = $rs->GetArray();
937                         $arr2 = array();
938                         for ($i=0; $i < sizeof($arr); $i++) {
939                                 $arr2[] = $arr[$i][0];
940                         }
941                         $rs->Close();
942                         return $arr2;
943                 }
944                 return false;
945         }
946         
947         
948         /**
949          * List columns in a database as an array of ADOFieldObjects. 
950          * See top of file for definition of object.
951          *
952          * @params table        table name to query
953          *
954          * @return  array of ADOFieldObjects for current table.
955          */ 
956     function &MetaColumns($table) 
957         {
958         global $ADODB_FETCH_MODE;
959         
960                 if (!empty($this->metaColumnsSQL)) {
961                         $save = $ADODB_FETCH_MODE;
962                         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
963                         $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
964                         $ADODB_FETCH_MODE = $save;
965                         if ($rs === false) return false;
966
967                         $retarr = array();
968                         while (!$rs->EOF) { //print_r($rs->fields);
969                                 $fld = new ADOFieldObject();
970                                 $fld->name = $rs->fields[0];
971                                 $fld->type = $rs->fields[1];
972                                 $fld->max_length = $rs->fields[2];
973                                 $retarr[strtoupper($fld->name)] = $fld; 
974                                 
975                                 $rs->MoveNext();
976                         }
977                         $rs->Close();
978                         return $retarr; 
979                 }
980                 return false;
981         }
982       
983                 
984         /**
985          * Different SQL databases used different methods to combine strings together.
986          * This function provides a wrapper. 
987          * 
988          * @param s     variable number of string parameters
989          *
990          * Usage: $db->Concat($str1,$str2);
991          * 
992          * @return concatenated string
993          */      
994         function Concat()
995         {       
996                 $arr = func_get_args();
997                 return implode($this->concat_operator, $arr);
998         }
999         
1000         
1001         /**
1002          * Converts a date "d" to a string that the database can understand.
1003          *
1004          * @param d     a date in Unix date time format.
1005          *
1006          * @return  date string in database date format
1007          */
1008         function DBDate($d)
1009         {
1010         // note that we are limited to 1970 to 2038
1011                 if (empty($d) && $d !== 0) return 'null';
1012
1013                 if (is_string($d)) 
1014                         if ($this->isoDates) return "'$d'";
1015                         else $d = ADORecordSet::UnixDate($d);
1016                         
1017                 return date($this->fmtDate,$d);
1018         }
1019         
1020         
1021         /**
1022          * Converts a timestamp "ts" to a string that the database can understand.
1023          *
1024          * @param ts    a timestamp in Unix date time format.
1025          *
1026          * @return  timestamp string in database timestamp format
1027          */
1028         function DBTimeStamp($ts)
1029         {
1030                 if (empty($ts) && $ts !== 0) return 'null';
1031
1032                 if (is_string($ts)) 
1033                         if ($this->isoDates) return "'$ts'";
1034                         else $ts = ADORecordSet::UnixTimeStamp($ts);
1035                 return date($this->fmtTimeStamp,$ts);
1036         }
1037         
1038         
1039         /**
1040          * Converts a timestamp "ts" to a string that the database can understand.
1041          * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
1042          * 
1043          * @param s                     the string to quote
1044          * @param [magic_quotes]        if $s is GET/POST var, set to get_magic_quotes_gpc().
1045          *                              This undoes the stupidity of magic quotes for GPC.
1046          *
1047          * @return  quoted string to be sent back to database
1048          */
1049         function qstr($s,$magic_quotes=false)
1050         {       
1051         $nofixquotes=false;
1052                 if (!$magic_quotes) {
1053                 
1054                         if ($this->replaceQuote[0] == '\\'){
1055                                 $s = str_replace('\\','\\\\',$s);
1056                         }
1057                         return  "'".str_replace("'",$this->replaceQuote,$s)."'";
1058                 }
1059                 
1060                 // undo magic quotes for "
1061                 $s = str_replace('\\"','"',$s);
1062                 
1063                 if ($this->replaceQuote == "\\'")  // ' already quoted, no need to change anything
1064                         return "'$s'";
1065                 else {// change \' to '' for sybase/mssql
1066                         $s = str_replace('\\\\','\\',$s);
1067                         return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
1068                 }
1069         }
1070
1071         
1072         /**
1073         * Will select the supplied $page number from a recordset, given that it is paginated in pages of 
1074         * $nrows rows per page. It also saves two boolean values saying if the given page is the first 
1075         * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
1076         *
1077         * See readme.htm#ex8 for an example of usage.
1078         *
1079         * @param sql
1080         * @param nrows          is the number of rows per page to get
1081         * @param page           is the page number to get (1-based)
1082         * @param [inputarr]     array of bind variables
1083         * @param [arg3]         is a private parameter only used by jlim
1084         * @param [secs2cache]           is a private parameter only used by jlim
1085         * @return               the recordset ($rs->databaseType == 'array')
1086         *
1087         * NOTE: phpLens uses a different algorithm and does not use PageExecute().
1088         *
1089         */
1090         function &PageExecute($sql, $nrows, $page, $inputarr=false, $arg3=false, $secs2cache=0) 
1091         {
1092                 include_once(ADODB_DIR.'/adodb-lib.inc.php');
1093                 return _adodb_pageexecute($this, $sql, $nrows, $page, $inputarr, $arg3, $secs2cache);
1094
1095         }
1096         
1097                 
1098         /**
1099         * Will select the supplied $page number from a recordset, given that it is paginated in pages of 
1100         * $nrows rows per page. It also saves two boolean values saying if the given page is the first 
1101         * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
1102         *
1103         * @param secs2cache     seconds to cache data, set to 0 to force query
1104         * @param sql
1105         * @param nrows          is the number of rows per page to get
1106         * @param page           is the page number to get (1-based)
1107         * @param [inputarr]     array of bind variables
1108         * @param [arg3]         is a private parameter only used by jlim
1109         * @return               the recordset ($rs->databaseType == 'array')
1110         */
1111         function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false, $arg3=false) {
1112                 include_once(ADODB_DIR.'/adodb-lib.inc.php');
1113                 return _adodb_pageexecute($this, $sql, $nrows, $page, $inputarr, $arg3,$secs2cache);
1114         }
1115
1116 } // end class ADOConnection
1117         
1118         
1119         
1120         //==============================================================================================        
1121         // CLASS ADOFetchObj
1122         //==============================================================================================        
1123                 
1124         /**
1125         * Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
1126         */
1127         class ADOFetchObj {
1128         };
1129         
1130         //==============================================================================================        
1131         // CLASS ADORecordSet_empty
1132         //==============================================================================================        
1133         
1134         /**
1135         * Lightweight recordset when there are no records to be returned
1136         */
1137         class ADORecordSet_empty
1138         {
1139                 var $dataProvider = 'empty';
1140                 var $EOF = true;
1141                 var $_numOfRows = 0;
1142                 var $fields = false;
1143                 var $connection = false;
1144                 function RowCount() {return 0;}
1145                 function RecordCount() {return 0;}
1146                 function Close(){return true;}
1147         }
1148         
1149         //==============================================================================================        
1150         // CLASS ADORecordSet
1151         //==============================================================================================        
1152         
1153         /**
1154          * RecordSet class that represents the dataset returned by the database.
1155          * To keep memory overhead low, this class holds only the current row in memory.
1156          * No prefetching of data is done, so the RecordCount() can return -1 ( which
1157          * means recordcount not known).
1158          */
1159         class ADORecordSet {
1160         /*
1161          * public variables     
1162          */
1163         var $dataProvider = "native";
1164         var $fields = false;    // holds the current row data
1165         var $blobSize = 64;     // any varchar/char field this size or greater is treated as a blob
1166                                                         // in other words, we use a text area for editting.
1167         var $canSeek = false;   // indicates that seek is supported
1168         var $sql;                               // sql text
1169         var $EOF = false;               /* Indicates that the current record position is after the last record in a Recordset object. */
1170         
1171         var $emptyTimeStamp = '&nbsp;'; // what to display when $time==0
1172         var $emptyDate = '&nbsp;'; // what to display when $time==0
1173         var $debug = false;
1174         var $timeCreated=0;     // datetime in Unix format rs created -- for cached recordsets
1175
1176         var $bind = false;              // used by Fields() to hold array - should be private?
1177         var $fetchMode;                 // default fetch mode
1178         var $connection = false; // the parent connection
1179         /*
1180          *      private variables       
1181          */
1182         var $_numOfRows = -1;   
1183         var $_numOfFields = -1; 
1184         var $_queryID = -1;     /* This variable keeps the result link identifier.      */
1185         var $_currentRow = -1;  /* This variable keeps the current row in the Recordset.        */
1186         var $_closed = false;   /* has recordset been closed */
1187         var $_inited = false;   /* Init() should only be called once */
1188         var $_obj;              /* Used by FetchObj */
1189         var $_names;
1190         
1191         var $_currentPage = -1; /* Added by Iván Oliva to implement recordset pagination */
1192         var $_atFirstPage = false;      /* Added by Iván Oliva to implement recordset pagination */
1193         var $_atLastPage = false;       /* Added by Iván Oliva to implement recordset pagination */
1194
1195         
1196         /**
1197          * Constructor
1198          *
1199          * @param queryID       this is the queryID returned by ADOConnection->_query()
1200          *
1201          */
1202         function ADORecordSet(&$queryID) 
1203         {
1204                 $this->_queryID = $queryID;
1205         }
1206         
1207         
1208         function Init()
1209         {
1210                 if ($this->_inited) return;
1211                 $this->_inited = true;
1212                 
1213                 if ($this->_queryID) @$this->_initrs();
1214                 else {
1215                         $this->_numOfRows = 0;
1216                         $this->_numOfFields = 0;
1217                 }
1218                 if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
1219                         $this->_currentRow = 0;
1220                         $this->EOF = ($this->_fetch() === false);
1221                 } else 
1222                         $this->EOF = true;
1223         }
1224         
1225         
1226         /**
1227          * Generate a <SELECT> string from a recordset, and return the string.
1228          * If the recordset has 2 cols, we treat the 1st col as the containing 
1229          * the text to display to the user, and 2nd col as the return value. Default
1230          * strings are compared with the FIRST column.
1231          *
1232          * @param name                  name of <SELECT>
1233          * @param [defstr]              the value to hilite. Use an array for multiple hilites for listbox.
1234          * @param [blank1stItem]        true to leave the 1st item in list empty
1235          * @param [multiple]            true for listbox, false for popup
1236          * @param [size]                #rows to show for listbox. not used by popup
1237          * @param [selectAttr]          additional attributes to defined for <SELECT>.
1238          *                              useful for holding javascript onChange='...' handlers.
1239          & @param [compareFields0]      when we have 2 cols in recordset, we compare the defstr with 
1240          *                              column 0 (1st col) if this is true. This is not documented.
1241          *
1242          * @return HTML
1243          *
1244          * changes by glen.davies@cce.ac.nz to support multiple hilited items
1245          */
1246          
1247         function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
1248                         $size=0, $selectAttr='',$compareFields0=true)
1249         {
1250                 include_once(ADODB_DIR.'/adodb-lib.inc.php');
1251                 return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
1252                         $size, $selectAttr,$compareFields0);
1253         }
1254         
1255         /**
1256          * Generate a <SELECT> string from a recordset, and return the string.
1257          * If the recordset has 2 cols, we treat the 1st col as the containing 
1258          * the text to display to the user, and 2nd col as the return value. Default
1259          * strings are compared with the SECOND column.
1260          *
1261          */
1262         function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='')  
1263         {
1264                 include_once(ADODB_DIR.'/adodb-lib.inc.php');
1265                 return _adodb_getmenu($this,$name,$defstr,$blank1stItem,$multiple,
1266                         $size, $selectAttr,false);
1267         }
1268
1269
1270         /**
1271          * return recordset as a 2-dimensional array.
1272          *
1273          * @param [nRows]  is the number of rows to return. -1 means every row.
1274          *
1275          * @return an array indexed by the rows (0-based) from the recordset
1276          */
1277         function &GetArray($nRows = -1) 
1278         {
1279                 $results = array();
1280                 $cnt = 0;
1281                 while (!$this->EOF && $nRows != $cnt) {
1282                         $results[$cnt++] = $this->fields;
1283                         $this->MoveNext();
1284                 }
1285                 
1286                 return $results;
1287         }
1288         /**
1289          * return recordset as a 2-dimensional array. 
1290          * Helper function for ADOConnection->SelectLimit()
1291          *
1292          * @param offset        is the row to start calculations from (1-based)
1293          * @param [nrows]       is the number of rows to return
1294          *
1295          * @return an array indexed by the rows (0-based) from the recordset
1296          */
1297         function &GetArrayLimit($nrows,$offset=-1) 
1298         {
1299                 if ($offset <= 0) return $this->GetArray($nrows);
1300                 $this->Move($offset);
1301                 
1302                 $results = array();
1303                 $cnt = 0;
1304                 while (!$this->EOF && $nrows != $cnt) {
1305                         $results[$cnt++] = $this->fields;
1306                         $this->MoveNext();
1307                 }
1308                 
1309                 return $results;
1310         }
1311         
1312         
1313         /**
1314          * Synonym for GetArray() for compatibility with ADO.
1315          *
1316          * @param [nRows]  is the number of rows to return. -1 means every row.
1317          *
1318          * @return an array indexed by the rows (0-based) from the recordset
1319          */
1320         function &GetRows($nRows = -1) 
1321         {
1322                 return $this->GetArray($nRows);
1323         }
1324         
1325         /**
1326          * return whole recordset as a 2-dimensional associative array if there are more than 2 columns. 
1327          * The first column is treated as the key and is not included in the array. 
1328          * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
1329          * $force_array == true.
1330          *
1331          * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
1332          *      array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
1333          *      read the source.
1334          *
1335          * @return an associative array indexed by the first column of the array, 
1336          *      or false if the  data has less than 2 cols.
1337          */
1338         function &GetAssoc($force_array = false) {
1339                 $cols = $this->_numOfFields;
1340                 if ($cols < 2) {
1341                         return false;
1342                 }
1343                 $numIndex = isset($this->fields[0]);
1344                 $results = array();
1345                 if ($cols > 2 || $force_array) {
1346                         if ($numIndex) {
1347                                 while (!$this->EOF) {
1348                                 $results[trim($this->fields[0])] = array_slice($this->fields, 1);
1349                                 $this->MoveNext();
1350                                 }
1351                         } else {
1352                                 while (!$this->EOF) {
1353                                         $results[trim(reset($this->fields))] = array_slice($this->fields, 1);
1354                                         $this->MoveNext();
1355                                 }
1356                         }
1357                 } else {
1358                         // return scalar values
1359                         if ($numIndex) {
1360                                 while (!$this->EOF) {
1361                                 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
1362                                         $results[trim(($this->fields[0]))] = $this->fields[1];
1363                                         $this->MoveNext();
1364                                 }
1365                         } else {
1366                                 while (!$this->EOF) {
1367                                 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
1368                                         $v1 = trim(reset($this->fields));
1369                                         $v2 = ''.next($this->fields); 
1370                                         $results[$v1] = $v2;
1371                                         $this->MoveNext();
1372                                 }
1373                         }
1374                 }
1375                 return $results; 
1376         }
1377         
1378         
1379         /**
1380          *
1381          * @param v     is the character timestamp in YYYY-MM-DD hh:mm:ss format
1382          * @param fmt   is the format to apply to it, using date()
1383          *
1384          * @return a timestamp formated as user desires
1385          */
1386         function UserTimeStamp($v,$fmt='Y-m-d H:i:s')
1387         {
1388                 $tt = $this->UnixTimeStamp($v);
1389                 // $tt == -1 if pre 1970
1390                 if (($tt === false || $tt == -1) && $v != false) return $v;
1391                 if ($tt == 0) return $this->emptyTimeStamp;
1392                 
1393                 return date($fmt,$tt);
1394         }
1395         
1396         
1397     /**
1398          * @param v     is the character date in YYYY-MM-DD format
1399          * @param fmt   is the format to apply to it, using date()
1400          *
1401          * @return a date formated as user desires
1402          */
1403         function UserDate($v,$fmt='Y-m-d')
1404         {
1405                 $tt = $this->UnixDate($v);
1406                 // $tt == -1 if pre 1970
1407                 if (($tt === false || $tt == -1) && $v != false) return $v;
1408                 else if ($tt == 0) return $this->emptyDate;
1409                 else if ($tt == -1) { // pre-1970
1410                 }
1411                 return date($fmt,$tt);
1412         
1413         }
1414         
1415         
1416         /**
1417          * @param $v is a date string in YYYY-MM-DD format
1418          *
1419          * @return date in unix timestamp format, or 0 if before 1970, or false if invalid date format
1420          */
1421         function UnixDate($v)
1422         {
1423                 if (!preg_match( "|([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", 
1424                         $v, $rr)) return false;
1425                         
1426                 if ($rr[1] <= 1970) return 0;
1427                 // h-m-s-MM-DD-YY
1428                 return mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
1429         }
1430         
1431
1432         /**
1433          * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
1434          *
1435          * @return date in unix timestamp format, or 0 if before 1970, or false if invalid date format
1436          */
1437         function UnixTimeStamp($v)
1438         {
1439                 if (!preg_match( 
1440                         "|([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9]{1,2}))?|", 
1441                         $v, $rr)) return false;
1442                 if ($rr[1] <= 1970 && $rr[2]<= 1) return 0;
1443         
1444                 // h-m-s-MM-DD-YY
1445                 return  @mktime($rr[4],$rr[5],$rr[6],$rr[2],$rr[3],$rr[1]);
1446         }
1447         
1448         
1449         /**
1450         * PEAR DB Compat - do not use internally
1451         */
1452         function Free()
1453         {
1454                 return $this->Close();
1455         }
1456         
1457         
1458         /**
1459         * PEAR DB compat, number of rows
1460         */
1461         function NumRows()
1462         {
1463                 return $this->_numOfRows;
1464         }
1465         
1466         
1467         /**
1468         * PEAR DB compat, number of cols
1469         */
1470         function NumCols()
1471         {
1472                 return $this->_numOfCols;
1473         }
1474         
1475         /**
1476         * Fetch a row, returning false if no more rows. 
1477         * This is PEAR DB compat mode.
1478         *
1479         * @return false or array containing the current record
1480         */
1481         function &FetchRow()
1482         {
1483                 if ($this->EOF) return false;
1484                 $arr = $this->fields;
1485                 $this->MoveNext();
1486                 return $arr;
1487         }
1488         
1489         
1490         /**
1491         * Fetch a row, returning PEAR_Error if no more rows. 
1492         * This is PEAR DB compat mode.
1493         *
1494         * @return DB_OK or error object
1495         */
1496         function FetchInto(&$arr)
1497         {
1498                 if ($this->EOF) return new PEAR_Error('EOF',-1);;
1499                 $arr = $this->fields;
1500                 $this->MoveNext();
1501                 return 1; // DB_OK
1502         }
1503         
1504         
1505         /**
1506          * Move to the first row in the recordset. Many databases do NOT support this.
1507          *
1508          * @return true or false
1509          */
1510         function MoveFirst() 
1511         {
1512                 if ($this->_currentRow == 0) return true;
1513                 return $this->Move(0);                  
1514         }                       
1515
1516         
1517         /**
1518          * Move to the last row in the recordset. 
1519          *
1520          * @return true or false
1521          */
1522         function MoveLast() 
1523         {
1524                 if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1);
1525                 while (!$this->EOF) $this->MoveNext();
1526                 return true;
1527         }
1528         
1529         
1530         /**
1531          * Move to next record in the recordset.
1532          *
1533          * @return true if there still rows available, or false if there are no more rows (EOF).
1534          */
1535         function MoveNext() 
1536         {
1537                 if (!$this->EOF) {
1538                         $this->_currentRow++;
1539                         if ($this->_fetch()) return true;
1540                 }
1541                 $this->EOF = true;
1542                 /* -- tested error handling when scrolling cursor -- seems useless.
1543                 $conn = $this->connection;
1544                 if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
1545                         $fn = $conn->raiseErrorFn;
1546                         $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
1547                 }
1548                 */
1549                 return false;
1550         }       
1551         
1552         /**
1553          * Random access to a specific row in the recordset. Some databases do not support
1554          * access to previous rows in the databases (no scrolling backwards).
1555          *
1556          * @param rowNumber is the row to move to (0-based)
1557          *
1558          * @return true if there still rows available, or false if there are no more rows (EOF).
1559          */
1560         function Move($rowNumber = 0) 
1561         {
1562                 if ($rowNumber == $this->_currentRow) return true;
1563                 if ($rowNumber > $this->_numOfRows)
1564                 if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-1;
1565    
1566         if ($this->canSeek) {
1567                 if ($this->_seek($rowNumber)) {
1568                                 $this->_currentRow = $rowNumber;
1569                                 if ($this->_fetch()) {
1570                                         $this->EOF = false;     
1571                                    //  $this->_currentRow += 1;                 
1572                                         return true;
1573                                 }
1574                         } else 
1575                                 return false;
1576         } else {
1577             if ($rowNumber < $this->_currentRow) return false;
1578             while (! $this->EOF && $this->_currentRow < $rowNumber) {
1579                                 $this->_currentRow++;
1580                 if (!$this->_fetch()) $this->EOF = true;
1581                         }
1582             return !($this->EOF);
1583         }
1584                 
1585                 $this->fields = null;   
1586                 $this->EOF = true;
1587                 return false;
1588         }
1589         
1590                 
1591         /**
1592          * Get the value of a field in the current row by column name.
1593          * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
1594          * 
1595          * @param colname  is the field to access
1596          *
1597          * @return the value of $colname column
1598          */
1599         function Fields($colname)
1600         {
1601                 return $this->fields[$colname];
1602         }
1603         
1604         
1605   /**
1606    * Use associative array to get fields array for databases that do not support
1607    * associative arrays. Submitted by Paolo S. Asioli paolo.asioli@libero.it
1608    *
1609    * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC
1610    * before you execute your SQL statement, and access $rs->fields['col'] directly.
1611    */
1612         function &GetRowAssoc($upper=true)
1613         {
1614          
1615                 if (!$this->bind) {
1616                         $this->bind = array();
1617                         for ($i=0; $i < $this->_numOfFields; $i++) {
1618                                 $o = $this->FetchField($i);
1619                                 $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i;
1620                         }
1621                 }
1622                 
1623                 $record = array();
1624                 foreach($this->bind as $k => $v) {
1625             $record[$k] = $this->fields[$v];
1626         }
1627
1628         return $record;
1629     }
1630         
1631         
1632         /**
1633          * Clean up recordset
1634          *
1635          * @return true or false
1636          */
1637         function Close() 
1638         {
1639                 // free connection object - this seems to globally free the object
1640                 // and not merely the reference, so don't do this...
1641                 // $this->connection = false; 
1642                 if (!$this->_closed) {
1643                         $this->_closed = true;
1644                         return $this->_close();         
1645                 } else
1646                         return true;
1647         }
1648         
1649         /**
1650          * synonyms RecordCount and RowCount    
1651          *
1652          * @return the number of rows or -1 if this is not supported
1653          */
1654         function RecordCount() {return $this->_numOfRows;}
1655         
1656         
1657         /**
1658          * synonyms RecordCount and RowCount    
1659          *
1660          * @return the number of rows or -1 if this is not supported
1661          */
1662         function RowCount() {return $this->_numOfRows;} 
1663         
1664
1665          /**
1666          * Portable RecordCount. Pablo Roca <pabloroca@mvps.org>
1667          *
1668      * @return  the number of records from a previous SELECT. All databases support this.
1669          *
1670          * But aware possible problems in multiuser environments. For better speed the table
1671          * must be indexed by the condition. Heavy test this before deploying.
1672      */ 
1673     function PO_RecordCount($table="", $condition="") {
1674         
1675         $lnumrows = $this->_numOfRows;
1676         // the database doesn't support native recordcount, so we do a workaround
1677         if ($lnumrows == -1 && $this->connection) {
1678             IF ($table) {
1679                 if ($condition) $condition = " WHERE " . $condition; 
1680                 $resultrows = &$this->connection->Execute("SELECT COUNT(*) FROM $table $condition");
1681                 if ($resultrows) $lnumrows = reset($resultrows->fields);
1682             }
1683         }
1684         return $lnumrows;
1685     }
1686         
1687         /**
1688          * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
1689          */
1690         function CurrentRow() {return $this->_currentRow;}
1691         
1692         /**
1693          * synonym for CurrentRow -- for ADO compat
1694          *
1695          * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
1696          */
1697         function AbsolutePosition() {return $this->_currentRow;}
1698         
1699         /**
1700          * @return the number of columns in the recordset. Some databases will set this to 0
1701          * if no records are returned, others will return the number of columns in the query.
1702          */
1703         function FieldCount() {return $this->_numOfFields;}   
1704
1705
1706         /**
1707          * Get the ADOFieldObject of a specific column.
1708          *
1709          * @param fieldoffset   is the column position to access(0-based).
1710          *
1711          * @return the ADOFieldObject for that column, or false.
1712          */
1713         function &FetchField($fieldoffset) 
1714         {
1715                 // must be defined by child class
1716         }       
1717         
1718         /**
1719         * Return the fields array of the current row as an object for convenience.
1720         * 
1721         * @param $isupper to set the object property names to uppercase
1722         *
1723         * @return the object with the properties set to the fields of the current row
1724         */
1725         function &FetchObject($isupper=true)
1726         {
1727                 if (empty($this->_obj)) {
1728                         $this->_obj = new ADOFetchObj();
1729                         $this->_names = array();
1730                         for ($i=0; $i <$this->_numOfFields; $i++) {
1731                                 $f = $this->FetchField($i);
1732                                 $this->_names[] = $f->name;
1733                         }
1734                 }
1735                 $i = 0;
1736                 $o = &$this->_obj;
1737                 for ($i=0; $i <$this->_numOfFields; $i++) {
1738                         $name = $this->_names[$i];
1739                         if ($isupper) $n = strtoupper($name);
1740                         else $n = $name;
1741                         
1742                         $o->$n = $this->Fields($name);
1743                 }
1744                 return $o;
1745         }
1746         /**
1747         * Return the fields array of the current row as an object for convenience.
1748         * 
1749         * @param $isupper to set the object property names to uppercase
1750         *
1751         * @return the object with the properties set to the fields of the current row,
1752         *       or false if EOF
1753         *
1754         * Fixed bug reported by tim@orotech.net
1755         */
1756         function &FetchNextObject($isupper=true)
1757         {
1758                 $o = false;
1759                 if ($this->_numOfRows != 0 && !$this->EOF) {
1760                         $o = $this->FetchObject($isupper);      
1761                         $this->_currentRow++;
1762                         if ($this->_fetch()) return $o;
1763                 }
1764                 $this->EOF = true;
1765                 return $o;
1766         }
1767         
1768         /**
1769          * Get the metatype of the column. This is used for formatting. This is because
1770          * many databases use different names for the same type, so we transform the original
1771          * type to our standardised version which uses 1 character codes:
1772          *
1773          * @param t  is the type passed in. Normally is ADOFieldObject->type.
1774          * @param len is the maximum length of that field. This is because we treat character
1775          *      fields bigger than a certain size as a 'B' (blob).
1776          * @param fieldobj is the field object returned by the database driver. Can hold
1777          *      additional info (eg. primary_key for mysql).
1778          * 
1779          * @return the general type of the data: 
1780          *      C for character < 200 chars
1781          *      X for teXt (>= 200 chars)
1782          *      B for Binary
1783          *      N for numeric floating point
1784          *      D for date
1785          *      T for timestamp
1786          *      L for logical/Boolean
1787          *      I for integer
1788          *      R for autoincrement counter/integer
1789          * 
1790          *
1791         */
1792         function MetaType($t,$len=-1,$fieldobj=false)
1793         {
1794                 switch (strtoupper($t)) {
1795                 case 'VARCHAR':
1796                 case 'VARCHAR2':
1797                 case 'CHAR':
1798                 case 'STRING':
1799                 case 'C':
1800                 case 'NCHAR':
1801                 case 'NVARCHAR':
1802                 case 'VARYING':
1803                 case 'BPCHAR':
1804                         if (!empty($this)) if ($len <= $this->blobSize) return 'C';
1805                         else if ($len <= 250) return 'C';
1806                 
1807                 case 'LONGCHAR':
1808                 case 'TEXT':
1809                 case 'M':
1810                 case 'X':
1811                 case 'CLOB':
1812                 case 'NCLOB':
1813                 case 'LONG':
1814                         return 'X';
1815                 
1816                 case 'BLOB':
1817                 case 'NTEXT':
1818                 case 'BINARY':
1819                 case 'VARBINARY':
1820                 case 'LONGBINARY':
1821                 case 'B':
1822                         return 'B';
1823                         
1824                 case 'DATE':
1825                 case 'D':
1826                         return 'D';
1827                 
1828                 
1829                 case 'TIME':
1830                 case 'TIMESTAMP':
1831                 case 'DATETIME':
1832                 case 'T':
1833                         return 'T';
1834                 
1835                 case 'BOOLEAN': 
1836                 case 'BIT':
1837                 case 'L':
1838                         return 'L';
1839                         
1840                 case 'COUNTER':
1841                 case 'R':
1842                         return 'R';
1843                         
1844                 case 'INT':
1845                 case 'INTEGER':
1846                 case 'SHORT':
1847                 case 'TINYINT':
1848                 case 'SMALLINT':
1849                 case 'I':
1850                         if (!empty($fieldobj->primary_key)) return 'R';
1851                         return 'I';
1852                         
1853                 default: return 'N';
1854                 }
1855         }
1856         
1857         function _close() {}
1858         
1859         /**
1860          * set/returns the current recordset page when paginating
1861          */
1862         function AbsolutePage($page=-1)
1863         {
1864                 if ($page != -1) $this->_currentPage = $page;
1865                 return $this->_currentPage;
1866         }
1867         
1868         /**
1869          * set/returns the status of the atFirstPage flag when paginating
1870          */
1871         function AtFirstPage($status=false)
1872         {
1873                 if ($status != false) $this->_atFirstPage = $status;
1874                 return $this->_atFirstPage;
1875         }
1876         
1877         /**
1878          * set/returns the status of the atLastPage flag when paginating
1879          */
1880         function AtLastPage($status=false)
1881         {
1882                 if ($status != false) $this->_atLastPage = $status;
1883                 return $this->_atLastPage;
1884         }
1885 } // end class ADORecordSet
1886         
1887         //==============================================================================================        
1888         // CLASS ADORecordSet_array
1889         //==============================================================================================        
1890         
1891         /**
1892          * This class encapsulates the concept of a recordset created in memory
1893          * as an array. This is useful for the creation of cached recordsets.
1894          * 
1895          * Note that the constructor is different from the standard ADORecordSet
1896          */
1897         
1898         class ADORecordSet_array extends ADORecordSet
1899         {
1900                 var $databaseType = "array";
1901         
1902                 var $_array;    // holds the 2-dimensional data array
1903                 var $_types;    // the array of types of each column (C B I L M)
1904                 var $_colnames; // names of each column in array
1905                 var $_skiprow1; // skip 1st row because it holds column names
1906                 var $_fieldarr; // holds array of field objects
1907                 var $canSeek = true;
1908                 var $affectedrows = false;
1909                 var $insertid = false;
1910                 var $sql = '';
1911                 /**
1912                  * Constructor
1913                  *
1914                  */
1915                 function ADORecordSet_array($fakeid=1)
1916                 {
1917                         $this->ADORecordSet($fakeid); // fake queryID
1918                 }
1919                 
1920                 
1921                 /**
1922                  * Setup the Array. Later we will have XML-Data and CSV handlers
1923                  *
1924                  * @param array         is a 2-dimensional array holding the data.
1925                  *                      The first row should hold the column names 
1926                  *                      unless paramter $colnames is used.
1927                  * @param typearr       holds an array of types. These are the same types 
1928                  *                      used in MetaTypes (C,B,L,I,N).
1929                  * @param [colnames]    array of column names. If set, then the first row of
1930                  *                      $array should not hold the column names.
1931                  */
1932                 function InitArray(&$array,$typearr,$colnames=false)
1933                 {
1934                         $this->_array = $array;
1935                         $this->_types = &$typearr;      
1936                         if ($colnames) {
1937                                 $this->_skiprow1 = false;
1938                                 $this->_colnames = $colnames;
1939                         } else $this->_colnames = $array[0];
1940                         
1941                         $this->Init();
1942                 }
1943                 /**
1944                  * Setup the Array and datatype file objects
1945                  *
1946                  * @param array         is a 2-dimensional array holding the data.
1947                  *                      The first row should hold the column names 
1948                  *                      unless paramter $colnames is used.
1949                  * @param fieldarr      holds an array of ADOFieldObject's.
1950                  */
1951                 function InitArrayFields(&$array,&$fieldarr)
1952                 {
1953                         $this->_array = &$array;
1954                         $this->_skiprow1= false;
1955                         if ($fieldarr) {
1956                                 $this->_fieldobjects = &$fieldarr;
1957                         } 
1958                         
1959                         $this->Init();
1960                 }
1961                 
1962                 function _initrs()
1963                 {
1964                         $this->_numOfRows =  sizeof($this->_array);
1965                         if ($this->_skiprow1) $this->_numOfRows -= 1;
1966                 
1967                         $this->_numOfFields =(isset($this->_fieldobjects)) ?
1968                                  sizeof($this->_fieldobjects):sizeof($this->_types);
1969                 }
1970                 
1971                 /* Use associative array to get fields array */
1972                 function Fields($colname)
1973                 {
1974                         if (!$this->bind) {
1975                                 $this->bind = array();
1976                                 for ($i=0; $i < $this->_numOfFields; $i++) {
1977                                         $o = $this->FetchField($i);
1978                                         $this->bind[strtoupper($o->name)] = $i;
1979                                 }
1980                         }
1981                         
1982                          return $this->fields[$this->bind[strtoupper($colname)]];
1983                 }
1984                 
1985                 function &FetchField($fieldOffset = -1) 
1986                 {
1987                         if (isset($this->_fieldobjects)) {
1988                                 return $this->_fieldobjects[$fieldOffset];
1989                         }
1990                         $o =  new ADOFieldObject();
1991                         $o->name = $this->_colnames[$fieldOffset];
1992                         $o->type =  $this->_types[$fieldOffset];
1993                         $o->max_length = -1; // length not known
1994                         
1995                         return $o;
1996                 }
1997                         
1998                 function _seek($row)
1999                 {
2000                         return true;
2001                 }
2002                 
2003                 function _fetch()
2004                 {
2005                         $pos = $this->_currentRow;
2006                         
2007                         if ($this->_skiprow1) {
2008                                 if ($this->_numOfRows <= $pos-1) return false;
2009                                 $pos += 1;
2010                         } else {
2011                                 if ($this->_numOfRows <= $pos) return false;
2012                         }
2013                         
2014                         $this->fields = $this->_array[$pos];
2015                         return true;
2016                 }
2017                 
2018                 function _close() 
2019                 {
2020                         return true;    
2021                 }
2022         
2023         } // ADORecordSet_array
2024
2025         //==============================================================================================        
2026         // HELPER FUNCTIONS
2027         //==============================================================================================                        
2028         
2029     /**
2030          * Synonym for ADOLoadCode.
2031          *
2032          * @deprecated
2033          */
2034         function ADOLoadDB($dbType) 
2035         { 
2036                 return ADOLoadCode($dbType);
2037         }
2038         
2039     /**
2040          * Load the code for a specific database driver
2041          */
2042     function ADOLoadCode($dbType) 
2043         {
2044         GLOBAL $ADODB_Database;
2045         
2046                 if (!$dbType) return false;
2047                 $ADODB_Database = strtolower($dbType);
2048                 switch ($ADODB_Database) {
2049                         case 'maxsql': $ADODB_Database = 'mysqlt'; break;
2050                         case 'pgsql': $ADODB_Database = 'postgres7'; break;
2051                 }
2052                 include_once(ADODB_DIR."/adodb-$ADODB_Database.inc.php");               
2053                 return true;            
2054         }
2055
2056         /**
2057          * synonym for ADONewConnection for people who cannot remember the correct name
2058          */
2059         function &NewADOConnection($db='')
2060         {
2061                 return ADONewConnection($db);
2062         }
2063         
2064         /**
2065          * Instantiate a new Connection class for a specific database driver.
2066          *
2067          * @param [db]  is the database Connection object to create. If undefined,
2068          *      use the last database driver that was loaded by ADOLoadCode().
2069          *
2070          * @return the freshly created instance of the Connection class.
2071          */
2072         function &ADONewConnection($db='')
2073         {
2074         GLOBAL $ADODB_Database;
2075         
2076                 if ($db) {
2077                         if ($ADODB_Database != $db) ADOLoadCode($db);
2078                 } else { 
2079                         if (!empty($ADODB_Database)) ADOLoadCode($ADODB_Database);
2080                         else print "<p>ADONewConnection: No database driver defined</p>";
2081                 }
2082                 
2083                 $cls = 'ADODB_'.$ADODB_Database;
2084                 $obj = new $cls();
2085                 if (defined('ADODB_ERROR_HANDLER')) {
2086                         $obj->raiseErrorFn = ADODB_ERROR_HANDLER;
2087                 }
2088                 return $obj;
2089         }
2090         
2091         /**
2092         * Save a file $filename and its $contents (normally for caching) with file locking
2093         */
2094         function adodb_write_file($filename, $contents,$debug=false)
2095         { 
2096         # http://www.php.net/bugs.php?id=9203 Bug that flock fails on Windows
2097         # So to simulate locking, we assume that rename is an atomic operation.
2098         # First we delete $filename, then we create a $tempfile write to it and 
2099         # rename to the desired $filename. If the rename works, then we successfully 
2100         # modified the file exclusively.
2101         # What a stupid need - having to simulate locking.
2102         # Risks:
2103         # 1. $tempfile name is not unique -- very very low
2104         # 2. unlink($filename) fails -- ok, rename will fail
2105         # 3. adodb reads stale file because unlink fails -- ok, $rs timeout occurs
2106         # 4. another process creates $filename between unlink() and rename() -- ok, rename() fails and  cache updated
2107                 if (strpos(strtoupper(PHP_OS),'WIN') !== false) {
2108                         // skip the decimal place
2109                         $mtime = substr(str_replace(' ','_',microtime()),2); 
2110                         // unlink will let some latencies develop, so uniqid() is more random
2111                         @unlink($filename);
2112                         // getmypid() actually returns 0 on Win98 - never mind!
2113                         $tmpname = $filename.uniqid($mtime).getmypid();
2114                         if (!($fd = fopen($tmpname,'a'))) return false;
2115                         $ok = ftruncate($fd,0);                 
2116                         if (!fwrite($fd,$contents)) $ok = false;
2117                         fclose($fd);
2118                         chmod($tmpname,0644);
2119                         if (!@rename($tmpname,$filename)) {
2120                                 unlink($tmpname);
2121                                 $ok = false;
2122                         }
2123                         if ($debug && !$ok) print " Rename $tmpname ".($ok? 'ok' : 'failed')." <br>";
2124                         return $ok;
2125                 }
2126                 if (!($fd = fopen($filename, 'a'))) return false;
2127                 if (flock($fd, LOCK_EX) && ftruncate($fd, 0)) {
2128                         $ok = fwrite( $fd, $contents );
2129                         fclose($fd);
2130                         chmod($filename,0644);
2131                 }else {
2132                         fclose($fd);
2133                         if ($debug)print " Failed acquiring lock for $filename<br>";
2134                         $ok = false;
2135                 }
2136         
2137                 return $ok;
2138         }
2139
2140 } // defined
2141
2142 // For emacs users
2143 // Local Variables:
2144 // mode: php
2145 // tab-width: 4
2146 // c-basic-offset: 4
2147 // c-hanging-comment-ender-p: nil
2148 // indent-tabs-mode: nil
2149 // End:
2150 ?>