]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/DB/DataObject/Generator.php
ff6e42c7dbb999dca9158095e8c189b805a1588c
[quix0rs-gnu-social.git] / extlib / DB / DataObject / Generator.php
1 <?php
2 /**
3  * Generation tools for DB_DataObject
4  *
5  * PHP versions 4 and 5
6  *
7  * LICENSE: This source file is subject to version 3.01 of the PHP license
8  * that is available through the world-wide-web at the following URI:
9  * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
10  * the PHP License and are unable to obtain it through the web, please
11  * send a note to license@php.net so we can mail you a copy immediately.
12  *
13  * @category   Database
14  * @package    DB_DataObject
15  * @author     Alan Knowles <alan@akbkhome.com>
16  * @copyright  1997-2006 The PHP Group
17  * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
18  * @version    CVS: $Id: Generator.php 284150 2009-07-15 23:27:59Z alan_k $
19  * @link       http://pear.php.net/package/DB_DataObject
20  */
21  
22  /*
23  * Security Notes:
24  *   This class uses eval to create classes on the fly.
25  *   The table name and database name are used to check the database before writing the
26  *   class definitions, we now check for quotes and semi-colon's in both variables
27  *   so I cant see how it would be possible to generate code even if
28  *   for some crazy reason you took the classname and table name from User Input.
29  *   
30  *   If you consider that wrong, or can prove it.. let me know!
31  */
32  
33  /**
34  * 
35  * Config _$ptions
36  * [DB_DataObject_Generator]
37  * ; optional default = DB/DataObject.php
38  * extends_location =
39  * ; optional default = DB_DataObject
40  * extends =
41  * ; alter the extends field when updating a class (defaults to only replacing DB_DataObject)
42  * generator_class_rewrite = ANY|specific_name   // default is DB_DataObject
43  *
44  */
45
46 /**
47  * Needed classes
48  * We lazy load here, due to problems with the tests not setting up include path correctly.
49  * FIXME!
50  */
51 class_exists('DB_DataObject') ? '' : require_once 'DB/DataObject.php';
52 //require_once('Config.php');
53
54 /**
55  * Generator class
56  *
57  * @package DB_DataObject
58  */
59 class DB_DataObject_Generator extends DB_DataObject
60 {
61     /* =========================================================== */
62     /*  Utility functions - for building db config files           */
63     /* =========================================================== */
64
65     /**
66      * Array of table names
67      *
68      * @var array
69      * @access private
70      */
71     var $tables;
72
73     /**
74      * associative array table -> array of table row objects
75      *
76      * @var array
77      * @access private
78      */
79     var $_definitions;
80
81     /**
82      * active table being output
83      *
84      * @var string
85      * @access private
86      */
87     var $table; // active tablename
88
89
90     /**
91      * The 'starter' = call this to start the process
92      *
93      * @access  public
94      * @return  none
95      */
96     function start()
97     {
98         $options = &PEAR::getStaticProperty('DB_DataObject','options');
99         $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
100
101         $databases = array();
102         foreach($options as $k=>$v) {
103             if (substr($k,0,9) == 'database_') {
104                 $databases[substr($k,9)] = $v;
105             }
106         }
107
108         if (isset($options['database'])) {
109             if ($db_driver == 'DB') {
110                 require_once 'DB.php';
111                 $dsn = DB::parseDSN($options['database']);
112             } else {
113                 require_once 'MDB2.php';
114                 $dsn = MDB2::parseDSN($options['database']);
115             }
116
117             if (!isset($database[$dsn['database']])) {
118                 $databases[$dsn['database']] = $options['database'];
119             }
120         }
121
122         foreach($databases as $databasename => $database) {
123             if (!$database) {
124                 continue;
125             }
126             $this->debug("CREATING FOR $databasename\n");
127             $class = get_class($this);
128             $t = new $class;
129             $t->_database_dsn = $database;
130
131
132             $t->_database = $databasename;
133             if ($db_driver == 'DB') {
134                 require_once 'DB.php';
135                 $dsn = DB::parseDSN($database);
136             } else {
137                 require_once 'MDB2.php';
138                 $dsn = MDB2::parseDSN($database);
139             }
140
141             if (($dsn['phptype'] == 'sqlite') && is_file($databasename)) {
142                 $t->_database = basename($t->_database);
143             }
144             $t->_createTableList();
145
146             foreach(get_class_methods($class) as $method) {
147                 if (substr($method,0,8 ) != 'generate') {
148                     continue;
149                 }
150                 $this->debug("calling $method");
151                 $t->$method();
152             }
153         }
154         $this->debug("DONE\n\n");
155     }
156
157     /**
158      * Output File was config object, now just string
159      * Used to generate the Tables
160      *
161      * @var    string outputbuffer for table definitions
162      * @access private
163      */
164     var $_newConfig;
165
166     /**
167      * Build a list of tables;
168      * and store it in $this->tables and $this->_definitions[tablename];
169      *
170      * @access  private
171      * @return  none
172      */
173     function _createTableList()
174     {
175         $this->_connect();
176         $options = &PEAR::getStaticProperty('DB_DataObject','options');
177
178         $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
179
180         $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
181         $is_MDB2 = ($db_driver != 'DB') ? true : false;
182
183         if (is_a($__DB , 'PEAR_Error')) {
184             return PEAR::raiseError($__DB->toString(), null, PEAR_ERROR_DIE);
185         }
186         
187         if (!$is_MDB2) {
188             // try getting a list of schema tables first. (postgres)
189             $__DB->expectError(DB_ERROR_UNSUPPORTED);
190             $this->tables = $__DB->getListOf('schema.tables');
191             $__DB->popExpect();
192         } else {
193             /**
194              * set portability and some modules to fetch the informations
195              */
196             $db_options = PEAR::getStaticProperty('MDB2','options');
197             if (empty($db_options)) {
198                 $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
199             }
200             
201             $__DB->loadModule('Manager');
202             $__DB->loadModule('Reverse');
203         }
204
205         if ((empty($this->tables) || is_a($this->tables , 'PEAR_Error'))) {
206             //if that fails fall back to clasic tables list.
207             if (!$is_MDB2) {
208                 // try getting a list of schema tables first. (postgres)
209                 $__DB->expectError(DB_ERROR_UNSUPPORTED);
210                 $this->tables = $__DB->getListOf('tables');
211                 $__DB->popExpect();
212             } else  {
213                 $this->tables = $__DB->manager->listTables();
214                 $sequences = $__DB->manager->listSequences();
215                 foreach ($sequences as $k => $v) {
216                     $this->tables[] = $__DB->getSequenceName($v);
217                 }
218             }
219         }
220
221         if (is_a($this->tables , 'PEAR_Error')) {
222             return PEAR::raiseError($this->tables->toString(), null, PEAR_ERROR_DIE);
223         }
224
225         // build views as well if asked to.
226         if (!empty($options['build_views'])) {
227             if (!$is_MDB2) {
228                 $views = $__DB->getListOf('views');
229             } else {
230                 $views = $__DB->manager->listViews();
231             }
232             if (is_a($views,'PEAR_Error')) {
233                 return PEAR::raiseError(
234                 'Error getting Views (check the PEAR bug database for the fix to DB), ' .
235                 $views->toString(),
236                 null,
237                 PEAR_ERROR_DIE
238                 );
239             }
240             $this->tables = array_merge ($this->tables, $views);
241         }
242
243         // declare a temporary table to be filled with matching tables names
244         $tmp_table = array();
245
246
247         foreach($this->tables as $table) {
248             if (isset($options['generator_include_regex']) &&
249             !preg_match($options['generator_include_regex'],$table)) {
250                 continue;
251             } else if (isset($options['generator_exclude_regex']) &&
252             preg_match($options['generator_exclude_regex'],$table)) {
253                 continue;
254             }
255             // postgres strip the schema bit from the
256             if (!empty($options['generator_strip_schema'])) {
257                 $bits = explode('.', $table,2);
258                 $table = $bits[0];
259                 if (count($bits) > 1) {
260                     $table = $bits[1];
261                 }
262             }
263             $quotedTable = !empty($options['quote_identifiers_tableinfo']) ? 
264                 $__DB->quoteIdentifier($table) : $table;
265                 
266             if (!$is_MDB2) {
267                 
268                 $defs =  $__DB->tableInfo($quotedTable);
269             } else {
270                 $defs =  $__DB->reverse->tableInfo($quotedTable);
271                 // rename the length value, so it matches db's return.
272                 
273             }
274
275             if (is_a($defs,'PEAR_Error')) {
276                 // running in debug mode should pick this up as a big warning..
277                 $this->raiseError('Error reading tableInfo, '. $defs->toString());
278                 continue;
279             }
280             // cast all definitions to objects - as we deal with that better.
281
282
283
284             foreach($defs as $def) {
285                 if (!is_array($def)) {
286                     continue;
287                 }
288                 // rename the length value, so it matches db's return.
289                 if (isset($def['length']) && !isset($def['len'])) {
290                     $def['len'] = $def['length'];
291                 }
292                 $this->_definitions[$table][] = (object) $def;
293
294             }
295             // we find a matching table, just  store it into a temporary array
296             $tmp_table[] = $table;
297
298
299         }
300         // the temporary table array is now the right one (tables names matching
301         // with regex expressions have been removed)
302         $this->tables = $tmp_table;
303         //print_r($this->_definitions);
304     }
305     
306     /**
307      * Auto generation of table data.
308      *
309      * it will output to db_oo_{database} the table definitions
310      *
311      * @access  private
312      * @return  none
313      */
314     function generateDefinitions()
315     {
316         $this->debug("Generating Definitions file:        ");
317         if (!$this->tables) {
318             $this->debug("-- NO TABLES -- \n");
319             return;
320         }
321
322         $options = &PEAR::getStaticProperty('DB_DataObject','options');
323
324
325         //$this->_newConfig = new Config('IniFile');
326         $this->_newConfig = '';
327         foreach($this->tables as $this->table) {
328             $this->_generateDefinitionsTable();
329         }
330         $this->_connect();
331         // dont generate a schema if location is not set
332         // it's created on the fly!
333         if (empty($options['schema_location']) && empty($options["ini_{$this->_database}"]) ) {
334             return;
335         }
336         if (!empty($options['generator_no_ini'])) { // built in ini files..
337             return;
338         }
339         $base =  @$options['schema_location'];
340         if (isset($options["ini_{$this->_database}"])) {
341             $file = $options["ini_{$this->_database}"];
342         } else {
343             $file = "{$base}/{$this->_database}.ini";
344         }
345         
346         if (!file_exists(dirname($file))) {
347             require_once 'System.php';
348             System::mkdir(array('-p','-m',0755,dirname($file)));
349         }
350         $this->debug("Writing ini as {$file}\n");
351         //touch($file);
352         $tmpname = tempnam(session_save_path(),'DataObject_');
353         //print_r($this->_newConfig);
354         $fh = fopen($tmpname,'w');
355         fwrite($fh,$this->_newConfig);
356         fclose($fh);
357         $perms = file_exists($file) ? fileperms($file) : 0755;
358         // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
359         
360         if (!@rename($tmpname, $file)) { 
361             unlink($file); 
362             rename($tmpname, $file);
363         }
364         chmod($file,$perms);
365         //$ret = $this->_newConfig->writeInput($file,false);
366
367         //if (PEAR::isError($ret) ) {
368         //    return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE);
369         // }
370     }
371
372     /**
373      * generate Foreign Keys (for links.ini) 
374      * Currenly only works with mysql / mysqli
375      * to use, you must set option: generate_links=true
376      * 
377      * @author Pascal Schöni 
378      */
379     function generateForeignKeys() 
380     {
381         $options = PEAR::getStaticProperty('DB_DataObject','options');
382         if (empty($options['generate_links'])) {
383             return false;
384         }
385         $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
386         if (!in_array($__DB->phptype, array('mysql','mysqli'))) {
387             echo "WARNING: cant handle non-mysql introspection for defaults.";
388             return; // cant handle non-mysql introspection for defaults.
389         }
390
391         $DB = $this->getDatabaseConnection();
392
393         $fk = array();
394
395         foreach($this->tables as $this->table) {
396             $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?  $DB->quoteIdentifier($table)  : $this->table;
397             
398             $res =& $DB->query('SHOW CREATE TABLE ' . $quotedTable );
399
400             if (PEAR::isError($res)) {
401                 die($res->getMessage());
402             }
403
404             $text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);
405             $treffer = array();
406             // Extract FOREIGN KEYS
407             preg_match_all(
408                 "/FOREIGN KEY \(`(\w*)`\) REFERENCES `(\w*)` \(`(\w*)`\)/i", 
409                 $text[1], 
410                 $treffer, 
411                 PREG_SET_ORDER);
412
413             if (count($treffer) < 1) {
414                 continue;
415             }
416             for ($i = 0; $i < count($treffer); $i++) {
417                 $fk[$this->table][$treffer[$i][1]] = $treffer[$i][2] . ":" . $treffer[$i][3];
418             }
419             
420         }
421
422         $links_ini = "";
423
424         foreach($fk as $table => $details) {
425             $links_ini .= "[$table]\n";
426             foreach ($details as $col => $ref) {
427                 $links_ini .= "$col = $ref\n";
428             }
429             $links_ini .= "\n";
430         }
431
432         // dont generate a schema if location is not set
433         // it's created on the fly!
434         $options = PEAR::getStaticProperty('DB_DataObject','options');
435
436         if (empty($options['schema_location'])) {
437             return;
438         }
439
440         
441         $file = "{$options['schema_location']}/{$this->_database}.links.ini";
442
443         if (!file_exists(dirname($file))) {
444             require_once 'System.php';
445             System::mkdir(array('-p','-m',0755,dirname($file)));
446         }
447
448         $this->debug("Writing ini as {$file}\n");
449         
450         //touch($file); // not sure why this is needed?
451         $tmpname = tempnam(session_save_path(),'DataObject_');
452        
453         $fh = fopen($tmpname,'w');
454         fwrite($fh,$links_ini);
455         fclose($fh);
456         $perms = file_exists($file) ? fileperms($file) : 0755;
457         // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
458         if (!@rename($tmpname, $file)) { 
459             unlink($file); 
460             rename($tmpname, $file);
461         }
462         chmod($file, $perms);
463     }
464
465       
466     /**
467      * The table geneation part
468      *
469      * @access  private
470      * @return  tabledef and keys array.
471      */
472     function _generateDefinitionsTable()
473     {
474         global $_DB_DATAOBJECT;
475         $options = PEAR::getStaticProperty('DB_DataObject','options');
476         $defs = $this->_definitions[$this->table];
477         $this->_newConfig .= "\n[{$this->table}]\n";
478         $keys_out =  "\n[{$this->table}__keys]\n";
479         $keys_out_primary = '';
480         $keys_out_secondary = '';
481         if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
482             echo "TABLE STRUCTURE FOR {$this->table}\n";
483             print_r($defs);
484         }
485         $DB = $this->getDatabaseConnection();
486         $dbtype = $DB->phptype;
487         
488         $ret = array(
489                 'table' => array(),
490                 'keys' => array(),
491             );
492             
493         $ret_keys_primary = array();
494         $ret_keys_secondary = array();
495         
496         
497         
498         foreach($defs as $t) {
499              
500             $n=0;
501             $write_ini = true;
502             
503             
504             switch (strtoupper($t->type)) {
505
506                 case 'INT':
507                 case 'INT2':    // postgres
508                 case 'INT4':    // postgres
509                 case 'INT8':    // postgres
510                 case 'SERIAL4': // postgres
511                 case 'SERIAL8': // postgres
512                 case 'INTEGER':
513                 case 'TINYINT':
514                 case 'SMALLINT':
515                 case 'MEDIUMINT':
516                 case 'BIGINT':
517                     $type = DB_DATAOBJECT_INT;
518                     if ($t->len == 1) {
519                         $type +=  DB_DATAOBJECT_BOOL;
520                     }
521                     break;
522                
523                 case 'REAL':
524                 case 'DOUBLE':
525                 case 'DOUBLE PRECISION': // double precision (firebird)
526                 case 'FLOAT':
527                 case 'FLOAT4': // real (postgres)
528                 case 'FLOAT8': // double precision (postgres)
529                 case 'DECIMAL':
530                 case 'MONEY':  // mssql and maybe others
531                 case 'NUMERIC':
532                 case 'NUMBER': // oci8 
533                     $type = DB_DATAOBJECT_INT; // should really by FLOAT!!! / MONEY...
534                     break;
535                     
536                 case 'YEAR':
537                     $type = DB_DATAOBJECT_INT; 
538                     break;
539                     
540                 case 'BIT':
541                 case 'BOOL':   
542                 case 'BOOLEAN':   
543                 
544                     $type = DB_DATAOBJECT_BOOL;
545                     // postgres needs to quote '0'
546                     if ($dbtype == 'pgsql') {
547                         $type +=  DB_DATAOBJECT_STR;
548                     }
549                     break;
550                     
551                 case 'STRING':
552                 case 'CHAR':
553                 case 'VARCHAR':
554                 case 'VARCHAR2':
555                 case 'TINYTEXT':
556                 
557                 case 'ENUM':
558                 case 'SET':         // not really but oh well
559                 
560                 case 'POINT':       // mysql geometry stuff - not really string - but will do..
561                 
562                 case 'TIMESTAMPTZ': // postgres
563                 case 'BPCHAR':      // postgres
564                 case 'INTERVAL':    // postgres (eg. '12 days')
565                 
566                 case 'CIDR':        // postgres IP net spec
567                 case 'INET':        // postgres IP
568                 case 'MACADDR':     // postgress network Mac address.
569                 
570                 case 'INTEGER[]':   // postgres type
571                 case 'BOOLEAN[]':   // postgres type
572                 
573                     $type = DB_DATAOBJECT_STR;
574                     break;
575                 
576                 case 'TEXT':
577                 case 'MEDIUMTEXT':
578                 case 'LONGTEXT':
579                     
580                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT;
581                     break;
582                 
583                 
584                 case 'DATE':    
585                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE;
586                     break;
587                     
588                 case 'TIME':    
589                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TIME;
590                     break;    
591                     
592                 
593                 case 'DATETIME': 
594                      
595                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
596                     break;    
597                     
598                 case 'TIMESTAMP': // do other databases use this???
599                     
600                     $type = ($dbtype == 'mysql') ?
601                         DB_DATAOBJECT_MYSQLTIMESTAMP : 
602                         DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
603                     break;    
604                     
605                 
606                 case 'BLOB':       /// these should really be ignored!!!???
607                 case 'TINYBLOB':
608                 case 'MEDIUMBLOB':
609                 case 'LONGBLOB':
610                 
611                 case 'CLOB': // oracle character lob support
612                 
613                 case 'BYTEA':   // postgres blob support..
614                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB;
615                     break;
616                     
617                 default:     
618                     echo "*****************************************************************\n".
619                          "**               WARNING UNKNOWN TYPE                          **\n".
620                          "** Found column '{$t->name}', of type  '{$t->type}'            **\n".
621                          "** Please submit a bug, describe what type you expect this     **\n".
622                          "** column  to be                                               **\n".
623                          "** ---------POSSIBLE FIX / WORKAROUND -------------------------**\n".
624                          "** Try using MDB2 as the backend - eg set the config option    **\n".
625                          "** db_driver = MDB2                                            **\n".
626                          "*****************************************************************\n";
627                     $write_ini = false;
628                     break;
629             }
630             
631             if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
632                 echo "*****************************************************************\n".
633                      "**               WARNING COLUMN NAME UNUSABLE                  **\n".
634                      "** Found column '{$t->name}', of type  '{$t->type}'            **\n".
635                      "** Since this column name can't be converted to a php variable **\n".
636                      "** name, and the whole idea of mapping would result in a mess  **\n".
637                      "** This column has been ignored...                             **\n".
638                      "*****************************************************************\n";
639                 continue;
640             }
641             
642             if (!strlen(trim($t->name))) {
643                 continue; // is this a bug?
644             }
645             
646             if (preg_match('/not[ _]null/i',$t->flags)) {
647                 $type += DB_DATAOBJECT_NOTNULL;
648             }
649            
650            
651             if (in_array($t->name,array('null','yes','no','true','false'))) {
652                 echo "*****************************************************************\n".
653                      "**                             WARNING                         **\n".
654                      "** Found column '{$t->name}', which is invalid in an .ini file **\n".
655                      "** This line will not be writen to the file - you will have    **\n".
656                      "** define the keys()/method manually.                          **\n".
657                      "*****************************************************************\n";
658                 $write_ini = false;
659             } else {
660                 $this->_newConfig .= "{$t->name} = $type\n";
661             }
662             
663             $ret['table'][$t->name] = $type;
664             // i've no idea if this will work well on other databases?
665             // only use primary key or nextval(), cause the setFrom blocks you setting all key items...
666             // if no keys exist fall back to using unique
667             //echo "\n{$t->name} => {$t->flags}\n";
668             $secondary_key_match = isset($options['generator_secondary_key_match']) ? $options['generator_secondary_key_match'] : 'primary|unique';
669             
670             if (preg_match('/(auto_increment|nextval\()/i',rawurldecode($t->flags)) 
671                 || (isset($t->autoincrement) && ($t->autoincrement === true))) {
672                     
673                 // native sequences = 2
674                 if ($write_ini) {
675                     $keys_out_primary .= "{$t->name} = N\n";
676                 }
677                 $ret_keys_primary[$t->name] = 'N';
678             
679             } else if ($secondary_key_match && preg_match('/('.$secondary_key_match.')/i',$t->flags)) {
680                 // keys.. = 1
681                 $key_type = 'K';
682                 if (!preg_match("/(primary)/i",$t->flags)) {
683                     $key_type = 'U';
684                 }
685                 
686                 if ($write_ini) {
687                     $keys_out_secondary .= "{$t->name} = {$key_type}\n";
688                 }
689                 $ret_keys_secondary[$t->name] = $key_type;
690             }
691             
692         
693         }
694         
695         $this->_newConfig .= $keys_out . (empty($keys_out_primary) ? $keys_out_secondary : $keys_out_primary);
696         $ret['keys'] = empty($keys_out_primary) ? $ret_keys_secondary : $ret_keys_primary;
697         
698         if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
699             print_r(array("dump for {$this->table}", $ret));
700         }
701         
702         return $ret;
703         
704         
705     }
706
707     /**
708     * Convert a table name into a class name -> override this if you want a different mapping
709     *
710     * @access  public
711     * @return  string class name;
712     */
713     
714     
715     function getClassNameFromTableName($table)
716     {
717         $options = &PEAR::getStaticProperty('DB_DataObject','options');
718         $class_prefix  = empty($options['class_prefix']) ? '' : $options['class_prefix'];
719         return  $class_prefix.preg_replace('/[^A-Z0-9]/i','_',ucfirst(trim($this->table)));
720     }
721     
722     
723     /**
724     * Convert a table name into a file name -> override this if you want a different mapping
725     *
726     * @access  public
727     * @return  string file name;
728     */
729     
730     
731     function getFileNameFromTableName($table)
732     {
733         $options = &PEAR::getStaticProperty('DB_DataObject','options');
734         $base = $options['class_location'];
735         if (strpos($base,'%s') !== false) {
736             $base = dirname($base);
737         } 
738         if (!file_exists($base)) {
739             require_once 'System.php';
740             System::mkdir(array('-p',$base));
741         }
742         if (strpos($options['class_location'],'%s') !== false) {
743             $outfilename   = sprintf($options['class_location'], 
744                     preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)));
745         } else { 
746             $outfilename = "{$base}/".preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)).".php";
747         }
748         return $outfilename;
749         
750     }
751     
752     
753      /**
754     * Convert a column name into a method name (usually prefixed by get/set/validateXXXXX)
755     *
756     * @access  public
757     * @return  string method name;
758     */
759     
760     
761     function getMethodNameFromColumnName($col)
762     {
763         return ucfirst($col);
764     }
765     
766     
767     
768     
769     /*
770      * building the class files
771      * for each of the tables output a file!
772      */
773     function generateClasses()
774     {
775         //echo "Generating Class files:        \n";
776         $options = &PEAR::getStaticProperty('DB_DataObject','options');
777        
778         
779         if ($extends = @$options['extends']) {
780             $this->_extends = $extends;
781             $this->_extendsFile = $options['extends_location'];
782         }
783
784         foreach($this->tables as $this->table) {
785             $this->table        = trim($this->table);
786             $this->classname    = $this->getClassNameFromTableName($this->table);
787             $i = '';
788             $outfilename        = $this->getFileNameFromTableName($this->table);
789             
790             $oldcontents = '';
791             if (file_exists($outfilename)) {
792                 // file_get_contents???
793                 $oldcontents = implode('',file($outfilename));
794             }
795             
796             $out = $this->_generateClassTable($oldcontents);
797             $this->debug( "writing $this->classname\n");
798             $tmpname = tempnam(session_save_path(),'DataObject_');
799        
800             $fh = fopen($tmpname, "w");
801             fputs($fh,$out);
802             fclose($fh);
803             $perms = file_exists($outfilename) ? fileperms($outfilename) : 0755;
804             
805             // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
806             if (!@rename($tmpname, $outfilename)) {
807                 unlink($outfilename); 
808                 rename($tmpname, $outfilename);
809             }
810             
811             chmod($outfilename, $perms);
812         }
813         //echo $out;
814     }
815
816     /**
817      * class being extended (can be overridden by [DB_DataObject_Generator] extends=xxxx
818      *
819      * @var    string
820      * @access private
821      */
822     var $_extends = 'DB_DataObject';
823
824     /**
825      * line to use for require('DB/DataObject.php');
826      *
827      * @var    string
828      * @access private
829      */
830     var $_extendsFile = "DB/DataObject.php";
831
832     /**
833      * class being generated
834      *
835      * @var    string
836      * @access private
837      */
838     var $_className;
839
840     /**
841      * The table class geneation part - single file.
842      *
843      * @access  private
844      * @return  none
845      */
846     function _generateClassTable($input = '')
847     {
848         // title = expand me!
849         $foot = "";
850         $head = "<?php\n/**\n * Table Definition for {$this->table}\n";
851         $head .= $this->derivedHookPageLevelDocBlock();
852         $head .= " */\n";
853         $head .= $this->derivedHookExtendsDocBlock();
854
855         
856         // requires
857         $head .= "require_once '{$this->_extendsFile}';\n\n";
858         // add dummy class header in...
859         // class 
860         $head .= $this->derivedHookClassDocBlock();
861         $head .= "class {$this->classname} extends {$this->_extends} \n{";
862
863         $body =  "\n    ###START_AUTOCODE\n";
864         $body .= "    /* the code below is auto generated do not remove the above tag */\n\n";
865         // table
866         $padding = (30 - strlen($this->table));
867         $padding  = ($padding < 2) ? 2 : $padding;
868         
869         $p =  str_repeat(' ',$padding) ;
870         
871         $options = &PEAR::getStaticProperty('DB_DataObject','options');
872         
873         
874         $var = (substr(phpversion(),0,1) > 4) ? 'public' : 'var';
875         $var = !empty($options['generator_var_keyword']) ? $options['generator_var_keyword'] : $var;
876         
877         
878         $body .= "    {$var} \$__table = '{$this->table}';  {$p}// table name\n";
879     
880         
881         // if we are using the option database_{databasename} = dsn
882         // then we should add var $_database = here
883         // as database names may not always match.. 
884         
885         if (empty($GLOBALS['_DB_DATAOBJECT']['CONFIG'])) {
886             DB_DataObject::_loadConfig();
887         }
888
889          // Only include the $_database property if the omit_database_var is unset or false
890         
891         if (isset($options["database_{$this->_database}"]) && empty($GLOBALS['_DB_DATAOBJECT']['CONFIG']['generator_omit_database_var'])) {
892             $body .= "    {$var} \$_database = '{$this->_database}';  {$p}// database name (used with database_{*} config)\n";
893         }
894         
895         
896         if (!empty($options['generator_novars'])) {
897             $var = '//'.$var;
898         }
899         
900         $defs = $this->_definitions[$this->table];
901
902         // show nice information!
903         $connections = array();
904         $sets = array();
905         foreach($defs as $t) {
906             if (!strlen(trim($t->name))) {
907                 continue;
908             }
909             if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
910                 echo "*****************************************************************\n".
911                      "**               WARNING COLUMN NAME UNUSABLE                  **\n".
912                      "** Found column '{$t->name}', of type  '{$t->type}'            **\n".
913                      "** Since this column name can't be converted to a php variable **\n".
914                      "** name, and the whole idea of mapping would result in a mess  **\n".
915                      "** This column has been ignored...                             **\n".
916                      "*****************************************************************\n";
917                 continue;
918             }
919             
920             
921             $padding = (30 - strlen($t->name));
922             if ($padding < 2) $padding =2;
923             $p =  str_repeat(' ',$padding) ;
924             
925             $length = empty($t->len) ? '' : '('.$t->len.')';
926             $body .="    {$var} \${$t->name};  {$p}// {$t->type}$length  {$t->flags}\n";
927             
928             // can not do set as PEAR::DB table info doesnt support it.
929             //if (substr($t->Type,0,3) == "set")
930             //    $sets[$t->Field] = "array".substr($t->Type,3);
931             $body .= $this->derivedHookVar($t,$padding);
932         }
933
934         // THIS IS TOTALLY BORKED old FC creation
935         // IT WILL BE REMOVED!!!!! in DataObjects 1.6
936         // grep -r __clone * to find all it's uses
937         // and replace them with $x = clone($y);
938         // due to the change in the PHP5 clone design.
939         
940         if ( substr(phpversion(),0,1) < 5) {
941             $body .= "\n";
942             $body .= "    /* ZE2 compatibility trick*/\n";
943             $body .= "    function __clone() { return \$this;}\n";
944         }
945
946         // simple creation tools ! (static stuff!)
947         $body .= "\n";
948         $body .= "    /* Static get */\n";
949         $body .= "    function staticGet(\$k,\$v=NULL) { return DB_DataObject::staticGet('{$this->classname}',\$k,\$v); }\n";
950         
951         // generate getter and setter methods
952         $body .= $this->_generateGetters($input);
953         $body .= $this->_generateSetters($input);
954         
955         /*
956         theoretically there is scope here to introduce 'list' methods
957         based up 'xxxx_up' column!!! for heiracitcal trees..
958         */
959
960         // set methods
961         //foreach ($sets as $k=>$v) {
962         //    $kk = strtoupper($k);
963         //    $body .="    function getSets{$k}() { return {$v}; }\n";
964         //}
965         
966         if (!empty($options['generator_no_ini'])) {
967             $def = $this->_generateDefinitionsTable();  // simplify this!?
968             $body .= $this->_generateTableFunction($def['table']);
969             $body .= $this->_generateKeysFunction($def['keys']);
970             $body .= $this->_generateSequenceKeyFunction($def);
971             $body .= $this->_generateDefaultsFunction($this->table, $def['table']);
972         }  else if (!empty($options['generator_add_defaults'])) {   
973             // I dont really like doing it this way (adding another option)
974             // but it helps on older projects.
975             $def = $this->_generateDefinitionsTable();  // simplify this!?
976             $body .= $this->_generateDefaultsFunction($this->table,$def['table']);
977              
978         }
979         $body .= $this->derivedHookFunctions($input);
980
981         $body .= "\n    /* the code above is auto generated do not remove the tag below */";
982         $body .= "\n    ###END_AUTOCODE\n";
983
984
985         // stubs..
986         
987         if (!empty($options['generator_add_validate_stubs'])) {
988             foreach($defs as $t) {
989                 if (!strlen(trim($t->name))) {
990                     continue;
991                 }
992                 $validate_fname = 'validate' . $this->getMethodNameFromColumnName($t->name);
993                 // dont re-add it..
994                 if (preg_match('/\s+function\s+' . $validate_fname . '\s*\(/i', $input)) {
995                     continue;
996                 }
997                 $body .= "\n    function {$validate_fname}()\n    {\n        return false;\n    }\n";
998             }
999         }
1000
1001
1002
1003
1004         $foot .= "}\n";
1005         $full = $head . $body . $foot;
1006
1007         if (!$input) {
1008             return $full;
1009         }
1010         if (!preg_match('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n)/s',$input))  {
1011             return $full;
1012         }
1013         if (!preg_match('/(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',$input)) {
1014             return $full;
1015         }
1016
1017
1018         /* this will only replace extends DB_DataObject by default,
1019             unless use set generator_class_rewrite to ANY or a name*/
1020
1021         $class_rewrite = 'DB_DataObject';
1022         $options = &PEAR::getStaticProperty('DB_DataObject','options');
1023         if (empty($options['generator_class_rewrite']) || !($class_rewrite = $options['generator_class_rewrite'])) {
1024             $class_rewrite = 'DB_DataObject';
1025         }
1026         if ($class_rewrite == 'ANY') {
1027             $class_rewrite = '[a-z_]+';
1028         }
1029
1030         $input = preg_replace(
1031             '/(\n|\r\n)class\s*[a-z0-9_]+\s*extends\s*' .$class_rewrite . '\s*(\n|\r\n)\{(\n|\r\n)/si',
1032             "\nclass {$this->classname} extends {$this->_extends} \n{\n",
1033             $input);
1034
1035         $ret =  preg_replace(
1036             '/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',
1037             $body,$input);
1038         
1039         if (!strlen($ret)) {
1040             return PEAR::raiseError(
1041                 "PREG_REPLACE failed to replace body, - you probably need to set these in your php.ini\n".
1042                 "pcre.backtrack_limit=1000000\n".
1043                 "pcre.recursion_limit=1000000\n"
1044                 ,null, PEAR_ERROR_DIE);
1045        }
1046         
1047         return $ret;
1048     }
1049
1050     /**
1051      * hook to add extra methods to all classes
1052      *
1053      * called once for each class, use with $this->table and
1054      * $this->_definitions[$this->table], to get data out of the current table,
1055      * use it to add extra methods to the default classes.
1056      *
1057      * @access   public
1058      * @return  string added to class eg. functions.
1059      */
1060     function derivedHookFunctions($input = "")
1061     {
1062         // This is so derived generator classes can generate functions
1063         // It MUST NOT be changed here!!!
1064         return "";
1065     }
1066
1067     /**
1068      * hook for var lines
1069      * called each time a var line is generated, override to add extra var
1070      * lines
1071      *
1072      * @param object t containing type,len,flags etc. from tableInfo call
1073      * @param int padding number of spaces
1074      * @access   public
1075      * @return  string added to class eg. functions.
1076      */
1077     function derivedHookVar(&$t,$padding)
1078     {
1079         // This is so derived generator classes can generate variabels
1080         // It MUST NOT be changed here!!!
1081         return "";
1082     }
1083
1084     /**
1085      * hook to add extra page-level (in terms of phpDocumentor) DocBlock
1086      *
1087      * called once for each class, use it add extra page-level docs
1088      * @access public
1089      * @return string added to class eg. functions.
1090      */
1091     function derivedHookPageLevelDocBlock() {
1092         return '';
1093     }
1094
1095     /**
1096      * hook to add extra doc block (in terms of phpDocumentor) to extend string
1097      *
1098      * called once for each class, use it add extra comments to extends
1099      * string (require_once...)
1100      * @access public
1101      * @return string added to class eg. functions.
1102      */
1103     function derivedHookExtendsDocBlock() {
1104         return '';
1105     }
1106
1107     /**
1108      * hook to add extra class level DocBlock (in terms of phpDocumentor)
1109      *
1110      * called once for each class, use it add extra comments to class
1111      * string (require_once...)
1112      * @access public
1113      * @return string added to class eg. functions.
1114      */
1115     function derivedHookClassDocBlock() {
1116         return '';
1117     }
1118
1119     /**
1120
1121     /**
1122     * getProxyFull - create a class definition on the fly and instantate it..
1123     *
1124     * similar to generated files - but also evals the class definitoin code.
1125     * 
1126     * 
1127     * @param   string database name
1128     * @param   string  table   name of table to create proxy for.
1129     * 
1130     *
1131     * @return   object    Instance of class. or PEAR Error
1132     * @access   public
1133     */
1134     function getProxyFull($database,$table) 
1135     {
1136         
1137         if ($err = $this->fillTableSchema($database,$table)) {
1138             return $err;
1139         }
1140         
1141         
1142         $options = &PEAR::getStaticProperty('DB_DataObject','options');
1143         $class_prefix  = empty($options['class_prefix']) ? '' : $options['class_prefix'];
1144         
1145         if ($extends = @$options['extends']) {
1146             $this->_extends = $extends;
1147             $this->_extendsFile = $options['extends_location'];
1148         }
1149         $classname = $this->classname = $this->getClassNameFromTableName($this->table);
1150         
1151         $out = $this->_generateClassTable();
1152         //echo $out;
1153         eval('?>'.$out);
1154         return new $classname;
1155         
1156     }
1157     
1158      /**
1159     * fillTableSchema - set the database schema on the fly
1160     *
1161     * 
1162     * 
1163     * @param   string database name
1164     * @param   string  table   name of table to create schema info for
1165     *
1166     * @return   none | PEAR::error()
1167     * @access   public
1168     */
1169     function fillTableSchema($database,$table) 
1170     {
1171         global $_DB_DATAOBJECT;
1172          // a little bit of sanity testing.
1173         if ((false !== strpos($database,"'")) || (false !== strpos($database,";"))) {   
1174             return PEAR::raiseError("Error: Database name contains a quote or semi-colon", null, PEAR_ERROR_DIE);
1175         }
1176         
1177         $this->_database  = $database; 
1178         
1179         $this->_connect();
1180         $table = trim($table);
1181         
1182         // a little bit of sanity testing.
1183         if ((false !== strpos($table,"'")) || (false !== strpos($table,";"))) {   
1184             return PEAR::raiseError("Error: Table contains a quote or semi-colon", null, PEAR_ERROR_DIE);
1185         }
1186         $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
1187         
1188         
1189         $options   = PEAR::getStaticProperty('DB_DataObject','options');
1190         $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
1191         $is_MDB2   = ($db_driver != 'DB') ? true : false;
1192         
1193         if (!$is_MDB2) {
1194             // try getting a list of schema tables first. (postgres)
1195             $__DB->expectError(DB_ERROR_UNSUPPORTED);
1196             $this->tables = $__DB->getListOf('schema.tables');
1197             $__DB->popExpect();
1198         } else {
1199             /**
1200              * set portability and some modules to fetch the informations
1201              */
1202             $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
1203             $__DB->loadModule('Manager');
1204             $__DB->loadModule('Reverse');
1205         }
1206         $quotedTable = !empty($options['quote_identifiers_tableinfo']) ? 
1207                 $__DB->quoteIdentifier($table) : $table;
1208           
1209         if (!$is_MDB2) {
1210             $defs =  $__DB->tableInfo($quotedTable);
1211         } else {
1212             $defs =  $__DB->reverse->tableInfo($quotedTable);
1213             foreach ($defs as $k => $v) {
1214                 if (!isset($defs[$k]['length'])) {
1215                     continue;
1216                 }
1217                 $defs[$k]['len'] = $defs[$k]['length'];
1218             }
1219         }
1220         
1221          
1222         
1223         
1224         if (PEAR::isError($defs)) {
1225             return $defs;
1226         }
1227         if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
1228             $this->debug("getting def for $database/$table",'fillTable');
1229             $this->debug(print_r($defs,true),'defs');
1230         }
1231         // cast all definitions to objects - as we deal with that better.
1232         
1233             
1234         foreach($defs as $def) {
1235             if (is_array($def)) {
1236                 $this->_definitions[$table][] = (object) $def;
1237             }
1238         }
1239
1240         $this->table = trim($table);
1241         $ret = $this->_generateDefinitionsTable();
1242         
1243         $_DB_DATAOBJECT['INI'][$database][$table] = $ret['table'];
1244         $_DB_DATAOBJECT['INI'][$database][$table.'__keys'] = $ret['keys'];
1245         return false;
1246         
1247     }
1248     
1249     /**
1250     * Generate getter methods for class definition
1251     *
1252     * @param    string  $input  Existing class contents
1253     * @return   string
1254     * @access   public
1255     */
1256     function _generateGetters($input) 
1257     {
1258
1259         $options = &PEAR::getStaticProperty('DB_DataObject','options');
1260         $getters = '';
1261
1262         // only generate if option is set to true
1263         if  (empty($options['generate_getters'])) {
1264             return '';
1265         }
1266
1267         // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
1268         $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
1269
1270         $getters .= "\n\n";
1271         $defs     = $this->_definitions[$this->table];
1272
1273         // loop through properties and create getter methods
1274         foreach ($defs = $defs as $t) {
1275
1276             // build mehtod name
1277             $methodName = 'get' . $this->getMethodNameFromColumnName($t->name);
1278
1279             if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
1280                 continue;
1281             }
1282
1283             $getters .= "   /**\n";
1284             $getters .= "    * Getter for \${$t->name}\n";
1285             $getters .= "    *\n";
1286             $getters .= (stristr($t->flags, 'multiple_key')) ? "    * @return   object\n"
1287                                                              : "    * @return   {$t->type}\n";
1288             $getters .= "    * @access   public\n";
1289             $getters .= "    */\n";
1290             $getters .= (substr(phpversion(),0,1) > 4) ? '    public '
1291                                                        : '    ';
1292             $getters .= "function $methodName() {\n";
1293             $getters .= "        return \$this->{$t->name};\n";
1294             $getters .= "    }\n\n";
1295         }
1296    
1297
1298         return $getters;
1299     }
1300
1301
1302    /**
1303     * Generate setter methods for class definition
1304     *
1305     * @param    string  Existing class contents
1306     * @return   string
1307     * @access   public
1308     */
1309     function _generateSetters($input) 
1310     {
1311
1312         $options = &PEAR::getStaticProperty('DB_DataObject','options');
1313         $setters = '';
1314
1315         // only generate if option is set to true
1316         if  (empty($options['generate_setters'])) {
1317             return '';
1318         }
1319
1320         // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
1321         $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
1322
1323         $setters .= "\n";
1324         $defs     = $this->_definitions[$this->table];
1325
1326         // loop through properties and create setter methods
1327         foreach ($defs = $defs as $t) {
1328
1329             // build mehtod name
1330             $methodName = 'set' . $this->getMethodNameFromColumnName($t->name);
1331
1332             if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
1333                 continue;
1334             }
1335
1336             $setters .= "   /**\n";
1337             $setters .= "    * Setter for \${$t->name}\n";
1338             $setters .= "    *\n";
1339             $setters .= "    * @param    mixed   input value\n";
1340             $setters .= "    * @access   public\n";
1341             $setters .= "    */\n";
1342             $setters .= (substr(phpversion(),0,1) > 4) ? '    public '
1343                                                        : '    ';
1344             $setters .= "function $methodName(\$value) {\n";
1345             $setters .= "        \$this->{$t->name} = \$value;\n";
1346             $setters .= "    }\n\n";
1347         }
1348         
1349
1350         return $setters;
1351     }
1352     /**
1353     * Generate table Function - used when generator_no_ini is set.
1354     *
1355     * @param    array  table array.
1356     * @return   string
1357     * @access   public
1358     */
1359     function _generateTableFunction($def) 
1360     {
1361         $defines = explode(',','INT,STR,DATE,TIME,BOOL,TXT,BLOB,NOTNULL,MYSQLTIMESTAMP');
1362     
1363         $ret = "\n" .
1364                "    function table()\n" .
1365                "    {\n" .
1366                "         return array(\n";
1367         
1368         foreach($def as $k=>$v) {
1369             $str = '0';
1370             foreach($defines as $dn) {
1371                 if ($v & constant('DB_DATAOBJECT_' . $dn)) {
1372                     $str .= ' + DB_DATAOBJECT_' . $dn;
1373                 }
1374             }
1375             if (strlen($str) > 1) {
1376                 $str = substr($str,3); // strip the 0 +
1377             }
1378             // hopefully addslashes is good enough here!!!
1379             $ret .= '             \''.addslashes($k).'\' => ' . $str . ",\n";
1380         }
1381         return $ret . "         );\n" .
1382                       "    }\n";
1383             
1384     
1385     
1386     }
1387     /**
1388     * Generate keys Function - used generator_no_ini is set.
1389     *
1390     * @param    array  keys array.
1391     * @return   string
1392     * @access   public
1393     */
1394     function _generateKeysFunction($def) 
1395     {
1396          
1397         $ret = "\n" .
1398                "    function keys()\n" .
1399                "    {\n" .
1400                "         return array(";
1401             
1402         foreach($def as $k=>$type) {
1403             // hopefully addslashes is good enough here!!!
1404             $ret .= '\''.addslashes($k).'\', ';
1405         }
1406         $ret = preg_replace('#, $#', '', $ret);
1407         return $ret . ");\n" .
1408                       "    }\n";
1409             
1410     
1411     
1412     }
1413     /**
1414     * Generate sequenceKey Function - used generator_no_ini is set.
1415     *
1416     * @param    array  table and key definition.
1417     * @return   string
1418     * @access   public
1419     */
1420     function _generateSequenceKeyFunction($def)
1421     {
1422     
1423         //print_r($def);
1424         // DB_DataObject::debugLevel(5);
1425         global $_DB_DATAOBJECT;
1426         // print_r($def);
1427         
1428         
1429         $dbtype     = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
1430         $realkeys   = $def['keys'];
1431         $keys       = array_keys($realkeys);
1432         $usekey     = isset($keys[0]) ? $keys[0] : false;
1433         $table      = $def['table'];
1434         
1435          
1436         $seqname = false;
1437         
1438         
1439         
1440         
1441         $ar = array(false,false,false);
1442         if ($usekey !== false) {
1443             if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) {
1444                 $usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
1445                 if (strpos($usekey,':') !== false) {
1446                     list($usekey,$seqname) = explode(':',$usekey);
1447                 }
1448             }  
1449         
1450             if (in_array($dbtype , array( 'mysql', 'mysqli', 'mssql', 'ifx')) && 
1451                 ($table[$usekey] & DB_DATAOBJECT_INT) && 
1452                 isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
1453                 ) {
1454                 // use native sequence keys.
1455                 $ar =  array($usekey,true,$seqname);
1456             } else {
1457                 // use generated sequence keys..
1458                 if ($table[$usekey] & DB_DATAOBJECT_INT) {
1459                     $ar = array($usekey,false,$seqname);
1460                 }
1461             }
1462         }
1463     
1464     
1465       
1466      
1467         $ret = "\n" .
1468                "    function sequenceKey() // keyname, use native, native name\n" .
1469                "    {\n" .
1470                "         return array(";
1471         foreach($ar as $v) {
1472             switch (gettype($v)) {
1473                 case 'boolean':
1474                     $ret .= ($v ? 'true' : 'false') . ', ';
1475                     break;
1476                     
1477                 case 'string':
1478                     $ret .= "'" . $v . "', ";
1479                     break;
1480                     
1481                 default:    // eak
1482                     $ret .= "null, ";
1483         
1484             }
1485         }
1486         $ret = preg_replace('#, $#', '', $ret);
1487         return $ret . ");\n" .
1488                       "    }\n";
1489         
1490     }
1491     /**
1492     * Generate defaults Function - used generator_add_defaults or generator_no_ini is set.
1493     * Only supports mysql and mysqli ... welcome ideas for more..
1494     * 
1495     *
1496     * @param    array  table and key definition.
1497     * @return   string
1498     * @access   public
1499     */
1500     function _generateDefaultsFunction($table,$defs)
1501     {
1502         $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
1503         if (!in_array($__DB->phptype, array('mysql','mysqli'))) {
1504             return; // cant handle non-mysql introspection for defaults.
1505         }
1506         $options = PEAR::getStaticProperty('DB_DataObject','options'); 
1507         $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
1508         $method = $db_driver == 'DB' ? 'getAll' : 'queryAll'; 
1509         $res = $__DB->$method('DESCRIBE ' . $table,DB_FETCHMODE_ASSOC);
1510         $defaults = array();
1511         foreach($res as $ar) {
1512             // this is initially very dumb... -> and it may mess up..
1513             $type = $defs[$ar['Field']];
1514             
1515             switch (true) {
1516                 
1517                 case (is_null( $ar['Default'])):
1518                     $defaults[$ar['Field']]  = 'null';
1519                     break;
1520                 
1521                 case ($type & DB_DATAOBJECT_DATE): 
1522                 case ($type & DB_DATAOBJECT_TIME): 
1523                 case ($type & DB_DATAOBJECT_MYSQLTIMESTAMP): // not supported yet..
1524                     break;
1525                     
1526                 case ($type & DB_DATAOBJECT_BOOL): 
1527                     $defaults[$ar['Field']] = (int)(boolean) $ar['Default'];
1528                     break;
1529                     
1530                 
1531                 case ($type & DB_DATAOBJECT_STR): 
1532                     $defaults[$ar['Field']] =  "'" . addslashes($ar['Default']) . "'";
1533                     break;
1534                 
1535                  
1536                 default:    // hopefully eveything else...  - numbers etc.
1537                     if (!strlen($ar['Default'])) {
1538                         continue;
1539                     }
1540                     if (is_numeric($ar['Default'])) {
1541                         $defaults[$ar['Field']] =   $ar['Default'];
1542                     }
1543                     break;
1544             
1545             }
1546             //var_dump(array($ar['Field'], $ar['Default'], $defaults[$ar['Field']]));
1547         }
1548         if (empty($defaults)) {
1549             return;
1550         }
1551         
1552         $ret = "\n" .
1553                "    function defaults() // column default values \n" .
1554                "    {\n" .
1555                "         return array(\n";
1556         foreach($defaults as $k=>$v) {
1557             $ret .= '             \''.addslashes($k).'\' => ' . $v . ",\n";
1558         }
1559         return $ret . "         );\n" .
1560                       "    }\n";
1561          
1562      
1563     
1564     
1565     }
1566     
1567     
1568      
1569     
1570     
1571 }