]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/DB/DataObject/Generator.php
utility functions for setting config options in memory
[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 298560 2010-04-25 23:01:51Z 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]
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', 'pgsql'))) {
387             echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
388             return; // cant handle non-mysql introspection for defaults.
389         }
390
391         $DB = $this->getDatabaseConnection();
392
393         $fk = array();
394
395
396         switch ($DB->phptype) {
397
398
399             case 'pgsql':
400                 foreach($this->tables as $this->table) {
401                     $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?  $DB->quoteIdentifier($table)  : $this->table;
402                     $res =& $DB->query("SELECT
403                                 pg_catalog.pg_get_constraintdef(r.oid, true) AS condef
404                             FROM pg_catalog.pg_constraint r,
405                                  pg_catalog.pg_class c
406                             WHERE c.oid=r.conrelid
407                                   AND r.contype = 'f'
408                                   AND c.relname = '" . $quotedTable . "'");
409                     if (PEAR::isError($res)) {
410                         die($res->getMessage());
411                     }
412
413                     while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
414                         $treffer = array();
415                         // this only picks up one of these.. see this for why: http://pear.php.net/bugs/bug.php?id=17049
416                         preg_match(
417                             "/FOREIGN KEY \((\w*)\) REFERENCES (\w*)\((\w*)\)/i",
418                             $row['condef'],
419                             $treffer);
420                         if (!count($treffer)) {
421                             continue;
422                         }
423                         $fk[$this->table][$treffer[1]] = $treffer[2] . ":" . $treffer[3];
424                     }
425                 }
426                 break;
427  
428  
429             case 'mysql':
430             case 'mysqli':
431             default: 
432
433                 foreach($this->tables as $this->table) {
434                     $quotedTable = !empty($options['quote_identifiers_tableinfo']) ?  $DB->quoteIdentifier($table)  : $this->table;
435                     
436                     $res =& $DB->query('SHOW CREATE TABLE ' . $quotedTable );
437
438                     if (PEAR::isError($res)) {
439                         die($res->getMessage());
440                     }
441
442                     $text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);
443                     $treffer = array();
444                     // Extract FOREIGN KEYS
445                     preg_match_all(
446                         "/FOREIGN KEY \(`(\w*)`\) REFERENCES `(\w*)` \(`(\w*)`\)/i", 
447                         $text[1], 
448                         $treffer, 
449                         PREG_SET_ORDER);
450
451                     if (!count($treffer)) {
452                         continue;
453                     }
454                     foreach($treffer as $i=> $tref) {
455                         $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
456                     }
457                     
458                 }
459
460         }
461         $links_ini = "";
462
463         foreach($fk as $table => $details) {
464             $links_ini .= "[$table]\n";
465             foreach ($details as $col => $ref) {
466                 $links_ini .= "$col = $ref\n";
467             }
468             $links_ini .= "\n";
469         }
470
471         // dont generate a schema if location is not set
472         // it's created on the fly!
473         $options = PEAR::getStaticProperty('DB_DataObject','options');
474
475         if (empty($options['schema_location'])) {
476             return;
477         }
478
479         
480         $file = "{$options['schema_location']}/{$this->_database}.links.ini";
481
482         if (!file_exists(dirname($file))) {
483             require_once 'System.php';
484             System::mkdir(array('-p','-m',0755,dirname($file)));
485         }
486
487         $this->debug("Writing ini as {$file}\n");
488         
489         //touch($file); // not sure why this is needed?
490         $tmpname = tempnam(session_save_path(),'DataObject_');
491        
492         $fh = fopen($tmpname,'w');
493         fwrite($fh,$links_ini);
494         fclose($fh);
495         $perms = file_exists($file) ? fileperms($file) : 0755;
496         // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
497         if (!@rename($tmpname, $file)) { 
498             unlink($file); 
499             rename($tmpname, $file);
500         }
501         chmod($file, $perms);
502     }
503
504       
505     /**
506      * The table geneation part
507      *
508      * @access  private
509      * @return  tabledef and keys array.
510      */
511     function _generateDefinitionsTable()
512     {
513         global $_DB_DATAOBJECT;
514         $options = PEAR::getStaticProperty('DB_DataObject','options');
515         $defs = $this->_definitions[$this->table];
516         $this->_newConfig .= "\n[{$this->table}]\n";
517         $keys_out =  "\n[{$this->table}__keys]\n";
518         $keys_out_primary = '';
519         $keys_out_secondary = '';
520         if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
521             echo "TABLE STRUCTURE FOR {$this->table}\n";
522             print_r($defs);
523         }
524         $DB = $this->getDatabaseConnection();
525         $dbtype = $DB->phptype;
526         
527         $ret = array(
528                 'table' => array(),
529                 'keys' => array(),
530             );
531             
532         $ret_keys_primary = array();
533         $ret_keys_secondary = array();
534         
535         
536         
537         foreach($defs as $t) {
538              
539             $n=0;
540             $write_ini = true;
541             
542             
543             switch (strtoupper($t->type)) {
544
545                 case 'INT':
546                 case 'INT2':    // postgres
547                 case 'INT4':    // postgres
548                 case 'INT8':    // postgres
549                 case 'SERIAL4': // postgres
550                 case 'SERIAL8': // postgres
551                 case 'INTEGER':
552                 case 'TINYINT':
553                 case 'SMALLINT':
554                 case 'MEDIUMINT':
555                 case 'BIGINT':
556                     $type = DB_DATAOBJECT_INT;
557                     if ($t->len == 1) {
558                         $type +=  DB_DATAOBJECT_BOOL;
559                     }
560                     break;
561                
562                 case 'REAL':
563                 case 'DOUBLE':
564                 case 'DOUBLE PRECISION': // double precision (firebird)
565                 case 'FLOAT':
566                 case 'FLOAT4': // real (postgres)
567                 case 'FLOAT8': // double precision (postgres)
568                 case 'DECIMAL':
569                 case 'MONEY':  // mssql and maybe others
570                 case 'NUMERIC':
571                 case 'NUMBER': // oci8 
572                     $type = DB_DATAOBJECT_INT; // should really by FLOAT!!! / MONEY...
573                     break;
574                     
575                 case 'YEAR':
576                     $type = DB_DATAOBJECT_INT; 
577                     break;
578                     
579                 case 'BIT':
580                 case 'BOOL':   
581                 case 'BOOLEAN':   
582                 
583                     $type = DB_DATAOBJECT_BOOL;
584                     // postgres needs to quote '0'
585                     if ($dbtype == 'pgsql') {
586                         $type +=  DB_DATAOBJECT_STR;
587                     }
588                     break;
589                     
590                 case 'STRING':
591                 case 'CHAR':
592                 case 'VARCHAR':
593                 case 'VARCHAR2':
594                 case 'TINYTEXT':
595                 
596                 case 'ENUM':
597                 case 'SET':         // not really but oh well
598                 
599                 case 'POINT':       // mysql geometry stuff - not really string - but will do..
600                 
601                 case 'TIMESTAMPTZ': // postgres
602                 case 'BPCHAR':      // postgres
603                 case 'INTERVAL':    // postgres (eg. '12 days')
604                 
605                 case 'CIDR':        // postgres IP net spec
606                 case 'INET':        // postgres IP
607                 case 'MACADDR':     // postgress network Mac address.
608                 
609                 case 'INTEGER[]':   // postgres type
610                 case 'BOOLEAN[]':   // postgres type
611                 
612                     $type = DB_DATAOBJECT_STR;
613                     break;
614                 
615                 case 'TEXT':
616                 case 'MEDIUMTEXT':
617                 case 'LONGTEXT':
618                     
619                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT;
620                     break;
621                 
622                 
623                 case 'DATE':    
624                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE;
625                     break;
626                     
627                 case 'TIME':    
628                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TIME;
629                     break;    
630                     
631                 
632                 case 'DATETIME': 
633                      
634                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
635                     break;    
636                     
637                 case 'TIMESTAMP': // do other databases use this???
638                     
639                     $type = ($dbtype == 'mysql') ?
640                         DB_DATAOBJECT_MYSQLTIMESTAMP : 
641                         DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
642                     break;    
643                     
644                 
645                 case 'BLOB':       /// these should really be ignored!!!???
646                 case 'TINYBLOB':
647                 case 'MEDIUMBLOB':
648                 case 'LONGBLOB':
649                 
650                 case 'CLOB': // oracle character lob support
651                 
652                 case 'BYTEA':   // postgres blob support..
653                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB;
654                     break;
655                     
656                 default:     
657                     echo "*****************************************************************\n".
658                          "**               WARNING UNKNOWN TYPE                          **\n".
659                          "** Found column '{$t->name}', of type  '{$t->type}'            **\n".
660                          "** Please submit a bug, describe what type you expect this     **\n".
661                          "** column  to be                                               **\n".
662                          "** ---------POSSIBLE FIX / WORKAROUND -------------------------**\n".
663                          "** Try using MDB2 as the backend - eg set the config option    **\n".
664                          "** db_driver = MDB2                                            **\n".
665                          "*****************************************************************\n";
666                     $write_ini = false;
667                     break;
668             }
669             
670             if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
671                 echo "*****************************************************************\n".
672                      "**               WARNING COLUMN NAME UNUSABLE                  **\n".
673                      "** Found column '{$t->name}', of type  '{$t->type}'            **\n".
674                      "** Since this column name can't be converted to a php variable **\n".
675                      "** name, and the whole idea of mapping would result in a mess  **\n".
676                      "** This column has been ignored...                             **\n".
677                      "*****************************************************************\n";
678                 continue;
679             }
680             
681             if (!strlen(trim($t->name))) {
682                 continue; // is this a bug?
683             }
684             
685             if (preg_match('/not[ _]null/i',$t->flags)) {
686                 $type += DB_DATAOBJECT_NOTNULL;
687             }
688            
689            
690             if (in_array($t->name,array('null','yes','no','true','false'))) {
691                 echo "*****************************************************************\n".
692                      "**                             WARNING                         **\n".
693                      "** Found column '{$t->name}', which is invalid in an .ini file **\n".
694                      "** This line will not be writen to the file - you will have    **\n".
695                      "** define the keys()/method manually.                          **\n".
696                      "*****************************************************************\n";
697                 $write_ini = false;
698             } else {
699                 $this->_newConfig .= "{$t->name} = $type\n";
700             }
701             
702             $ret['table'][$t->name] = $type;
703             // i've no idea if this will work well on other databases?
704             // only use primary key or nextval(), cause the setFrom blocks you setting all key items...
705             // if no keys exist fall back to using unique
706             //echo "\n{$t->name} => {$t->flags}\n";
707             $secondary_key_match = isset($options['generator_secondary_key_match']) ? $options['generator_secondary_key_match'] : 'primary|unique';
708             
709             if (preg_match('/(auto_increment|nextval\()/i',rawurldecode($t->flags)) 
710                 || (isset($t->autoincrement) && ($t->autoincrement === true))) {
711                     
712                 // native sequences = 2
713                 if ($write_ini) {
714                     $keys_out_primary .= "{$t->name} = N\n";
715                 }
716                 $ret_keys_primary[$t->name] = 'N';
717             
718             } else if ($secondary_key_match && preg_match('/('.$secondary_key_match.')/i',$t->flags)) {
719                 // keys.. = 1
720                 $key_type = 'K';
721                 if (!preg_match("/(primary)/i",$t->flags)) {
722                     $key_type = 'U';
723                 }
724                 
725                 if ($write_ini) {
726                     $keys_out_secondary .= "{$t->name} = {$key_type}\n";
727                 }
728                 $ret_keys_secondary[$t->name] = $key_type;
729             }
730             
731         
732         }
733         
734         $this->_newConfig .= $keys_out . (empty($keys_out_primary) ? $keys_out_secondary : $keys_out_primary);
735         $ret['keys'] = empty($keys_out_primary) ? $ret_keys_secondary : $ret_keys_primary;
736         
737         if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
738             print_r(array("dump for {$this->table}", $ret));
739         }
740         
741         return $ret;
742         
743         
744     }
745
746     /**
747     * Convert a table name into a class name -> override this if you want a different mapping
748     *
749     * @access  public
750     * @return  string class name;
751     */
752     
753     
754     function getClassNameFromTableName($table)
755     {
756         $options = &PEAR::getStaticProperty('DB_DataObject','options');
757         $class_prefix  = empty($options['class_prefix']) ? '' : $options['class_prefix'];
758         return  $class_prefix.preg_replace('/[^A-Z0-9]/i','_',ucfirst(trim($this->table)));
759     }
760     
761     
762     /**
763     * Convert a table name into a file name -> override this if you want a different mapping
764     *
765     * @access  public
766     * @return  string file name;
767     */
768     
769     
770     function getFileNameFromTableName($table)
771     {
772         $options = &PEAR::getStaticProperty('DB_DataObject','options');
773         $base = $options['class_location'];
774         if (strpos($base,'%s') !== false) {
775             $base = dirname($base);
776         } 
777         if (!file_exists($base)) {
778             require_once 'System.php';
779             System::mkdir(array('-p',$base));
780         }
781         if (strpos($options['class_location'],'%s') !== false) {
782             $outfilename   = sprintf($options['class_location'], 
783                     preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)));
784         } else { 
785             $outfilename = "{$base}/".preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)).".php";
786         }
787         return $outfilename;
788         
789     }
790     
791     
792      /**
793     * Convert a column name into a method name (usually prefixed by get/set/validateXXXXX)
794     *
795     * @access  public
796     * @return  string method name;
797     */
798     
799     
800     function getMethodNameFromColumnName($col)
801     {
802         return ucfirst($col);
803     }
804     
805     
806     
807     
808     /*
809      * building the class files
810      * for each of the tables output a file!
811      */
812     function generateClasses()
813     {
814         //echo "Generating Class files:        \n";
815         $options = &PEAR::getStaticProperty('DB_DataObject','options');
816        
817         $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
818         $this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
819  
820
821         foreach($this->tables as $this->table) {
822             $this->table        = trim($this->table);
823             $this->classname    = $this->getClassNameFromTableName($this->table);
824             $i = '';
825             $outfilename        = $this->getFileNameFromTableName($this->table);
826             
827             $oldcontents = '';
828             if (file_exists($outfilename)) {
829                 // file_get_contents???
830                 $oldcontents = implode('',file($outfilename));
831             }
832             
833             $out = $this->_generateClassTable($oldcontents);
834             $this->debug( "writing $this->classname\n");
835             $tmpname = tempnam(session_save_path(),'DataObject_');
836        
837             $fh = fopen($tmpname, "w");
838             fputs($fh,$out);
839             fclose($fh);
840             $perms = file_exists($outfilename) ? fileperms($outfilename) : 0755;
841             
842             // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
843             if (!@rename($tmpname, $outfilename)) {
844                 unlink($outfilename); 
845                 rename($tmpname, $outfilename);
846             }
847             
848             chmod($outfilename, $perms);
849         }
850         //echo $out;
851     }
852
853     /**
854      * class being extended (can be overridden by [DB_DataObject] extends=xxxx
855      *
856      * @var    string
857      * @access private
858      */
859     var $_extends = 'DB_DataObject';
860
861     /**
862      * line to use for require('DB/DataObject.php');
863      *
864      * @var    string
865      * @access private
866      */
867     var $_extendsFile = "DB/DataObject.php";
868
869     /**
870      * class being generated
871      *
872      * @var    string
873      * @access private
874      */
875     var $_className;
876
877     /**
878      * The table class geneation part - single file.
879      *
880      * @access  private
881      * @return  none
882      */
883     function _generateClassTable($input = '')
884     {
885         // title = expand me!
886         $foot = "";
887         $head = "<?php\n/**\n * Table Definition for {$this->table}\n";
888         $head .= $this->derivedHookPageLevelDocBlock();
889         $head .= " */\n";
890         $head .= $this->derivedHookExtendsDocBlock();
891
892         
893         // requires
894         $head .= "require_once '{$this->_extendsFile}';\n\n";
895         // add dummy class header in...
896         // class 
897         $head .= $this->derivedHookClassDocBlock();
898         $head .= "class {$this->classname} extends {$this->_extends} \n{";
899
900         $body =  "\n    ###START_AUTOCODE\n";
901         $body .= "    /* the code below is auto generated do not remove the above tag */\n\n";
902         // table
903
904         $p = str_repeat(' ',max(2, (18 - strlen($this->table)))) ;
905         
906         $options = &PEAR::getStaticProperty('DB_DataObject','options');
907         
908         
909         $var = (substr(phpversion(),0,1) > 4) ? 'public' : 'var';
910         $var = !empty($options['generator_var_keyword']) ? $options['generator_var_keyword'] : $var;
911         
912         
913         $body .= "    {$var} \$__table = '{$this->table}';  {$p}// table name\n";
914     
915         
916         // if we are using the option database_{databasename} = dsn
917         // then we should add var $_database = here
918         // as database names may not always match.. 
919         
920         if (empty($GLOBALS['_DB_DATAOBJECT']['CONFIG'])) {
921             DB_DataObject::_loadConfig();
922         }
923
924          // Only include the $_database property if the omit_database_var is unset or false
925         
926         if (isset($options["database_{$this->_database}"]) && empty($GLOBALS['_DB_DATAOBJECT']['CONFIG']['generator_omit_database_var'])) {
927             $p = str_repeat(' ',   max(2, (16 - strlen($this->table))));
928             $body .= "    {$var} \$_database = '{$this->_database}';  {$p}// database name (used with database_{*} config)\n";
929         }
930         
931         
932         if (!empty($options['generator_novars'])) {
933             $var = '//'.$var;
934         }
935         
936         $defs = $this->_definitions[$this->table];
937
938         // show nice information!
939         $connections = array();
940         $sets = array();
941
942         foreach($defs as $t) {
943             if (!strlen(trim($t->name))) {
944                 continue;
945             }
946             if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
947                 echo "*****************************************************************\n".
948                      "**               WARNING COLUMN NAME UNUSABLE                  **\n".
949                      "** Found column '{$t->name}', of type  '{$t->type}'            **\n".
950                      "** Since this column name can't be converted to a php variable **\n".
951                      "** name, and the whole idea of mapping would result in a mess  **\n".
952                      "** This column has been ignored...                             **\n".
953                      "*****************************************************************\n";
954                 continue;
955             }
956             
957             $p = str_repeat(' ',max(2,  (30 - strlen($t->name))));
958
959             $length = empty($t->len) ? '' : '('.$t->len.')';
960             $body .="    {$var} \${$t->name};  {$p}// {$t->type}$length  {$t->flags}\n";
961             
962             // can not do set as PEAR::DB table info doesnt support it.
963             //if (substr($t->Type,0,3) == "set")
964             //    $sets[$t->Field] = "array".substr($t->Type,3);
965             $body .= $this->derivedHookVar($t,strlen($p));
966         }
967          
968         $body .= $this->derivedHookPostVar($defs);
969
970         // THIS IS TOTALLY BORKED old FC creation
971         // IT WILL BE REMOVED!!!!! in DataObjects 1.6
972         // grep -r __clone * to find all it's uses
973         // and replace them with $x = clone($y);
974         // due to the change in the PHP5 clone design.
975         
976         if ( substr(phpversion(),0,1) < 5) {
977             $body .= "\n";
978             $body .= "    /* ZE2 compatibility trick*/\n";
979             $body .= "    function __clone() { return \$this;}\n";
980         }
981
982         // simple creation tools ! (static stuff!)
983         $body .= "\n";
984         $body .= "    /* Static get */\n";
985         $body .= "    function staticGet(\$k,\$v=NULL) { return DB_DataObject::staticGet('{$this->classname}',\$k,\$v); }\n";
986         
987         // generate getter and setter methods
988         $body .= $this->_generateGetters($input);
989         $body .= $this->_generateSetters($input);
990         
991         /*
992         theoretically there is scope here to introduce 'list' methods
993         based up 'xxxx_up' column!!! for heiracitcal trees..
994         */
995
996         // set methods
997         //foreach ($sets as $k=>$v) {
998         //    $kk = strtoupper($k);
999         //    $body .="    function getSets{$k}() { return {$v}; }\n";
1000         //}
1001         
1002         if (!empty($options['generator_no_ini'])) {
1003             $def = $this->_generateDefinitionsTable();  // simplify this!?
1004             $body .= $this->_generateTableFunction($def['table']);
1005             $body .= $this->_generateKeysFunction($def['keys']);
1006             $body .= $this->_generateSequenceKeyFunction($def);
1007             $body .= $this->_generateDefaultsFunction($this->table, $def['table']);
1008         }  else if (!empty($options['generator_add_defaults'])) {   
1009             // I dont really like doing it this way (adding another option)
1010             // but it helps on older projects.
1011             $def = $this->_generateDefinitionsTable();  // simplify this!?
1012             $body .= $this->_generateDefaultsFunction($this->table,$def['table']);
1013              
1014         }
1015         $body .= $this->derivedHookFunctions($input);
1016
1017         $body .= "\n    /* the code above is auto generated do not remove the tag below */";
1018         $body .= "\n    ###END_AUTOCODE\n";
1019
1020
1021         // stubs..
1022         
1023         if (!empty($options['generator_add_validate_stubs'])) {
1024             foreach($defs as $t) {
1025                 if (!strlen(trim($t->name))) {
1026                     continue;
1027                 }
1028                 $validate_fname = 'validate' . $this->getMethodNameFromColumnName($t->name);
1029                 // dont re-add it..
1030                 if (preg_match('/\s+function\s+' . $validate_fname . '\s*\(/i', $input)) {
1031                     continue;
1032                 }
1033                 $body .= "\n    function {$validate_fname}()\n    {\n        return false;\n    }\n";
1034             }
1035         }
1036
1037
1038
1039
1040         $foot .= "}\n";
1041         $full = $head . $body . $foot;
1042
1043         if (!$input) {
1044             return $full;
1045         }
1046         if (!preg_match('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n)/s',$input))  {
1047             return $full;
1048         }
1049         if (!preg_match('/(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',$input)) {
1050             return $full;
1051         }
1052
1053
1054         /* this will only replace extends DB_DataObject by default,
1055             unless use set generator_class_rewrite to ANY or a name*/
1056
1057         $class_rewrite = 'DB_DataObject';
1058         $options = &PEAR::getStaticProperty('DB_DataObject','options');
1059         if (empty($options['generator_class_rewrite']) || !($class_rewrite = $options['generator_class_rewrite'])) {
1060             $class_rewrite = 'DB_DataObject';
1061         }
1062         if ($class_rewrite == 'ANY') {
1063             $class_rewrite = '[a-z_]+';
1064         }
1065
1066         $input = preg_replace(
1067             '/(\n|\r\n)class\s*[a-z0-9_]+\s*extends\s*' .$class_rewrite . '\s*(\n|\r\n)\{(\n|\r\n)/si',
1068             "\nclass {$this->classname} extends {$this->_extends} \n{\n",
1069             $input);
1070
1071         $ret =  preg_replace(
1072             '/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',
1073             $body,$input);
1074         
1075         if (!strlen($ret)) {
1076             return PEAR::raiseError(
1077                 "PREG_REPLACE failed to replace body, - you probably need to set these in your php.ini\n".
1078                 "pcre.backtrack_limit=1000000\n".
1079                 "pcre.recursion_limit=1000000\n"
1080                 ,null, PEAR_ERROR_DIE);
1081        }
1082         
1083         return $ret;
1084     }
1085
1086     /**
1087      * hook to add extra methods to all classes
1088      *
1089      * called once for each class, use with $this->table and
1090      * $this->_definitions[$this->table], to get data out of the current table,
1091      * use it to add extra methods to the default classes.
1092      *
1093      * @access   public
1094      * @return  string added to class eg. functions.
1095      */
1096     function derivedHookFunctions($input = "")
1097     {
1098         // This is so derived generator classes can generate functions
1099         // It MUST NOT be changed here!!!
1100         return "";
1101     }
1102
1103     /**
1104      * hook for var lines
1105      * called each time a var line is generated, override to add extra var
1106      * lines
1107      *
1108      * @param object t containing type,len,flags etc. from tableInfo call
1109      * @param int padding number of spaces
1110      * @access   public
1111      * @return  string added to class eg. functions.
1112      */
1113     function derivedHookVar(&$t,$padding)
1114     {
1115         // This is so derived generator classes can generate variabels
1116         // It MUST NOT be changed here!!!
1117         return "";
1118     }
1119     /**
1120      * hook for after var lines (
1121      * called at the end of the output of var line have generated, override to add extra var
1122      * lines
1123      *
1124      * @param array cols containing array of objects with type,len,flags etc. from tableInfo call
1125      * @access   public
1126      * @return  string added to class eg. functions.
1127      */
1128     function derivedHookPostVar($t)
1129     {
1130         // This is so derived generator classes can generate variabels
1131         // It MUST NOT be changed here!!!
1132         return "";
1133     }
1134     /**
1135      * hook to add extra page-level (in terms of phpDocumentor) DocBlock
1136      *
1137      * called once for each class, use it add extra page-level docs
1138      * @access public
1139      * @return string added to class eg. functions.
1140      */
1141     function derivedHookPageLevelDocBlock() {
1142         return '';
1143     }
1144
1145     /**
1146      * hook to add extra doc block (in terms of phpDocumentor) to extend string
1147      *
1148      * called once for each class, use it add extra comments to extends
1149      * string (require_once...)
1150      * @access public
1151      * @return string added to class eg. functions.
1152      */
1153     function derivedHookExtendsDocBlock() {
1154         return '';
1155     }
1156
1157     /**
1158      * hook to add extra class level DocBlock (in terms of phpDocumentor)
1159      *
1160      * called once for each class, use it add extra comments to class
1161      * string (require_once...)
1162      * @access public
1163      * @return string added to class eg. functions.
1164      */
1165     function derivedHookClassDocBlock() {
1166         return '';
1167     }
1168
1169     /**
1170
1171     /**
1172     * getProxyFull - create a class definition on the fly and instantate it..
1173     *
1174     * similar to generated files - but also evals the class definitoin code.
1175     * 
1176     * 
1177     * @param   string database name
1178     * @param   string  table   name of table to create proxy for.
1179     * 
1180     *
1181     * @return   object    Instance of class. or PEAR Error
1182     * @access   public
1183     */
1184     function getProxyFull($database,$table) 
1185     {
1186         
1187         if ($err = $this->fillTableSchema($database,$table)) {
1188             return $err;
1189         }
1190         
1191         
1192         $options = &PEAR::getStaticProperty('DB_DataObject','options');
1193         $class_prefix  = empty($options['class_prefix']) ? '' : $options['class_prefix'];
1194         
1195         $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
1196         $this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
1197  
1198         $classname = $this->classname = $this->getClassNameFromTableName($this->table);
1199         
1200         $out = $this->_generateClassTable();
1201         //echo $out;
1202         eval('?>'.$out);
1203         return new $classname;
1204         
1205     }
1206     
1207      /**
1208     * fillTableSchema - set the database schema on the fly
1209     *
1210     * 
1211     * 
1212     * @param   string database name
1213     * @param   string  table   name of table to create schema info for
1214     *
1215     * @return   none | PEAR::error()
1216     * @access   public
1217     */
1218     function fillTableSchema($database,$table) 
1219     {
1220         global $_DB_DATAOBJECT;
1221          // a little bit of sanity testing.
1222         if ((false !== strpos($database,"'")) || (false !== strpos($database,";"))) {   
1223             return PEAR::raiseError("Error: Database name contains a quote or semi-colon", null, PEAR_ERROR_DIE);
1224         }
1225         
1226         $this->_database  = $database; 
1227         
1228         $this->_connect();
1229         $table = trim($table);
1230         
1231         // a little bit of sanity testing.
1232         if ((false !== strpos($table,"'")) || (false !== strpos($table,";"))) {   
1233             return PEAR::raiseError("Error: Table contains a quote or semi-colon", null, PEAR_ERROR_DIE);
1234         }
1235         $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
1236         
1237         
1238         $options   = PEAR::getStaticProperty('DB_DataObject','options');
1239         $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
1240         $is_MDB2   = ($db_driver != 'DB') ? true : false;
1241         
1242         if (!$is_MDB2) {
1243             // try getting a list of schema tables first. (postgres)
1244             $__DB->expectError(DB_ERROR_UNSUPPORTED);
1245             $this->tables = $__DB->getListOf('schema.tables');
1246             $__DB->popExpect();
1247         } else {
1248             /**
1249              * set portability and some modules to fetch the informations
1250              */
1251             $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
1252             $__DB->loadModule('Manager');
1253             $__DB->loadModule('Reverse');
1254         }
1255         $quotedTable = !empty($options['quote_identifiers_tableinfo']) ? 
1256                 $__DB->quoteIdentifier($table) : $table;
1257           
1258         if (!$is_MDB2) {
1259             $defs =  $__DB->tableInfo($quotedTable);
1260         } else {
1261             $defs =  $__DB->reverse->tableInfo($quotedTable);
1262             foreach ($defs as $k => $v) {
1263                 if (!isset($defs[$k]['length'])) {
1264                     continue;
1265                 }
1266                 $defs[$k]['len'] = $defs[$k]['length'];
1267             }
1268         }
1269         
1270          
1271         
1272         
1273         if (PEAR::isError($defs)) {
1274             return $defs;
1275         }
1276         if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
1277             $this->debug("getting def for $database/$table",'fillTable');
1278             $this->debug(print_r($defs,true),'defs');
1279         }
1280         // cast all definitions to objects - as we deal with that better.
1281         
1282             
1283         foreach($defs as $def) {
1284             if (is_array($def)) {
1285                 $this->_definitions[$table][] = (object) $def;
1286             }
1287         }
1288
1289         $this->table = trim($table);
1290         $ret = $this->_generateDefinitionsTable();
1291         
1292         $_DB_DATAOBJECT['INI'][$database][$table] = $ret['table'];
1293         $_DB_DATAOBJECT['INI'][$database][$table.'__keys'] = $ret['keys'];
1294         return false;
1295         
1296     }
1297     
1298     /**
1299     * Generate getter methods for class definition
1300     *
1301     * @param    string  $input  Existing class contents
1302     * @return   string
1303     * @access   public
1304     */
1305     function _generateGetters($input) 
1306     {
1307
1308         $options = &PEAR::getStaticProperty('DB_DataObject','options');
1309         $getters = '';
1310
1311         // only generate if option is set to true
1312         if  (empty($options['generate_getters'])) {
1313             return '';
1314         }
1315
1316         // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
1317         $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
1318
1319         $getters .= "\n\n";
1320         $defs     = $this->_definitions[$this->table];
1321
1322         // loop through properties and create getter methods
1323         foreach ($defs = $defs as $t) {
1324
1325             // build mehtod name
1326             $methodName = 'get' . $this->getMethodNameFromColumnName($t->name);
1327
1328             if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
1329                 continue;
1330             }
1331
1332             $getters .= "   /**\n";
1333             $getters .= "    * Getter for \${$t->name}\n";
1334             $getters .= "    *\n";
1335             $getters .= (stristr($t->flags, 'multiple_key')) ? "    * @return   object\n"
1336                                                              : "    * @return   {$t->type}\n";
1337             $getters .= "    * @access   public\n";
1338             $getters .= "    */\n";
1339             $getters .= (substr(phpversion(),0,1) > 4) ? '    public '
1340                                                        : '    ';
1341             $getters .= "function $methodName() {\n";
1342             $getters .= "        return \$this->{$t->name};\n";
1343             $getters .= "    }\n\n";
1344         }
1345    
1346
1347         return $getters;
1348     }
1349
1350
1351    /**
1352     * Generate setter methods for class definition
1353     *
1354     * @param    string  Existing class contents
1355     * @return   string
1356     * @access   public
1357     */
1358     function _generateSetters($input) 
1359     {
1360
1361         $options = &PEAR::getStaticProperty('DB_DataObject','options');
1362         $setters = '';
1363
1364         // only generate if option is set to true
1365         if  (empty($options['generate_setters'])) {
1366             return '';
1367         }
1368
1369         // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
1370         $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
1371
1372         $setters .= "\n";
1373         $defs     = $this->_definitions[$this->table];
1374
1375         // loop through properties and create setter methods
1376         foreach ($defs = $defs as $t) {
1377
1378             // build mehtod name
1379             $methodName = 'set' . $this->getMethodNameFromColumnName($t->name);
1380
1381             if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
1382                 continue;
1383             }
1384
1385             $setters .= "   /**\n";
1386             $setters .= "    * Setter for \${$t->name}\n";
1387             $setters .= "    *\n";
1388             $setters .= "    * @param    mixed   input value\n";
1389             $setters .= "    * @access   public\n";
1390             $setters .= "    */\n";
1391             $setters .= (substr(phpversion(),0,1) > 4) ? '    public '
1392                                                        : '    ';
1393             $setters .= "function $methodName(\$value) {\n";
1394             $setters .= "        \$this->{$t->name} = \$value;\n";
1395             $setters .= "    }\n\n";
1396         }
1397         
1398
1399         return $setters;
1400     }
1401     /**
1402     * Generate table Function - used when generator_no_ini is set.
1403     *
1404     * @param    array  table array.
1405     * @return   string
1406     * @access   public
1407     */
1408     function _generateTableFunction($def) 
1409     {
1410         $defines = explode(',','INT,STR,DATE,TIME,BOOL,TXT,BLOB,NOTNULL,MYSQLTIMESTAMP');
1411     
1412         $ret = "\n" .
1413                "    function table()\n" .
1414                "    {\n" .
1415                "         return array(\n";
1416         
1417         foreach($def as $k=>$v) {
1418             $str = '0';
1419             foreach($defines as $dn) {
1420                 if ($v & constant('DB_DATAOBJECT_' . $dn)) {
1421                     $str .= ' + DB_DATAOBJECT_' . $dn;
1422                 }
1423             }
1424             if (strlen($str) > 1) {
1425                 $str = substr($str,3); // strip the 0 +
1426             }
1427             // hopefully addslashes is good enough here!!!
1428             $ret .= '             \''.addslashes($k).'\' => ' . $str . ",\n";
1429         }
1430         return $ret . "         );\n" .
1431                       "    }\n";
1432             
1433     
1434     
1435     }
1436     /**
1437     * Generate keys Function - used generator_no_ini is set.
1438     *
1439     * @param    array  keys array.
1440     * @return   string
1441     * @access   public
1442     */
1443     function _generateKeysFunction($def) 
1444     {
1445          
1446         $ret = "\n" .
1447                "    function keys()\n" .
1448                "    {\n" .
1449                "         return array(";
1450             
1451         foreach($def as $k=>$type) {
1452             // hopefully addslashes is good enough here!!!
1453             $ret .= '\''.addslashes($k).'\', ';
1454         }
1455         $ret = preg_replace('#, $#', '', $ret);
1456         return $ret . ");\n" .
1457                       "    }\n";
1458             
1459     
1460     
1461     }
1462     /**
1463     * Generate sequenceKey Function - used generator_no_ini is set.
1464     *
1465     * @param    array  table and key definition.
1466     * @return   string
1467     * @access   public
1468     */
1469     function _generateSequenceKeyFunction($def)
1470     {
1471     
1472         //print_r($def);
1473         // DB_DataObject::debugLevel(5);
1474         global $_DB_DATAOBJECT;
1475         // print_r($def);
1476         
1477         
1478         $dbtype     = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
1479         $realkeys   = $def['keys'];
1480         $keys       = array_keys($realkeys);
1481         $usekey     = isset($keys[0]) ? $keys[0] : false;
1482         $table      = $def['table'];
1483         
1484          
1485         $seqname = false;
1486         
1487         
1488         
1489         
1490         $ar = array(false,false,false);
1491         if ($usekey !== false) {
1492             if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) {
1493                 $usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
1494                 if (strpos($usekey,':') !== false) {
1495                     list($usekey,$seqname) = explode(':',$usekey);
1496                 }
1497             }  
1498         
1499             if (in_array($dbtype , array( 'mysql', 'mysqli', 'mssql', 'ifx')) && 
1500                 ($table[$usekey] & DB_DATAOBJECT_INT) && 
1501                 isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
1502                 ) {
1503                 // use native sequence keys.
1504                 $ar =  array($usekey,true,$seqname);
1505             } else {
1506                 // use generated sequence keys..
1507                 if ($table[$usekey] & DB_DATAOBJECT_INT) {
1508                     $ar = array($usekey,false,$seqname);
1509                 }
1510             }
1511         }
1512     
1513     
1514       
1515      
1516         $ret = "\n" .
1517                "    function sequenceKey() // keyname, use native, native name\n" .
1518                "    {\n" .
1519                "         return array(";
1520         foreach($ar as $v) {
1521             switch (gettype($v)) {
1522                 case 'boolean':
1523                     $ret .= ($v ? 'true' : 'false') . ', ';
1524                     break;
1525                     
1526                 case 'string':
1527                     $ret .= "'" . $v . "', ";
1528                     break;
1529                     
1530                 default:    // eak
1531                     $ret .= "null, ";
1532         
1533             }
1534         }
1535         $ret = preg_replace('#, $#', '', $ret);
1536         return $ret . ");\n" .
1537                       "    }\n";
1538         
1539     }
1540     /**
1541     * Generate defaults Function - used generator_add_defaults or generator_no_ini is set.
1542     * Only supports mysql and mysqli ... welcome ideas for more..
1543     * 
1544     *
1545     * @param    array  table and key definition.
1546     * @return   string
1547     * @access   public
1548     */
1549     function _generateDefaultsFunction($table,$defs)
1550     {
1551         $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
1552         if (!in_array($__DB->phptype, array('mysql','mysqli'))) {
1553             return; // cant handle non-mysql introspection for defaults.
1554         }
1555         $options = PEAR::getStaticProperty('DB_DataObject','options'); 
1556         $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
1557         $method = $db_driver == 'DB' ? 'getAll' : 'queryAll'; 
1558         $res = $__DB->$method('DESCRIBE ' . $table,DB_FETCHMODE_ASSOC);
1559         $defaults = array();
1560         foreach($res as $ar) {
1561             // this is initially very dumb... -> and it may mess up..
1562             $type = $defs[$ar['Field']];
1563             
1564             switch (true) {
1565                 
1566                 case (is_null( $ar['Default'])):
1567                     $defaults[$ar['Field']]  = 'null';
1568                     break;
1569                 
1570                 case ($type & DB_DATAOBJECT_DATE): 
1571                 case ($type & DB_DATAOBJECT_TIME): 
1572                 case ($type & DB_DATAOBJECT_MYSQLTIMESTAMP): // not supported yet..
1573                     break;
1574                     
1575                 case ($type & DB_DATAOBJECT_BOOL): 
1576                     $defaults[$ar['Field']] = (int)(boolean) $ar['Default'];
1577                     break;
1578                     
1579                 
1580                 case ($type & DB_DATAOBJECT_STR): 
1581                     $defaults[$ar['Field']] =  "'" . addslashes($ar['Default']) . "'";
1582                     break;
1583                 
1584                  
1585                 default:    // hopefully eveything else...  - numbers etc.
1586                     if (!strlen($ar['Default'])) {
1587                         continue;
1588                     }
1589                     if (is_numeric($ar['Default'])) {
1590                         $defaults[$ar['Field']] =   $ar['Default'];
1591                     }
1592                     break;
1593             
1594             }
1595             //var_dump(array($ar['Field'], $ar['Default'], $defaults[$ar['Field']]));
1596         }
1597         if (empty($defaults)) {
1598             return;
1599         }
1600         
1601         $ret = "\n" .
1602                "    function defaults() // column default values \n" .
1603                "    {\n" .
1604                "         return array(\n";
1605         foreach($defaults as $k=>$v) {
1606             $ret .= '             \''.addslashes($k).'\' => ' . $v . ",\n";
1607         }
1608         return $ret . "         );\n" .
1609                       "    }\n";
1610          
1611      
1612     
1613     
1614     }
1615     
1616     
1617      
1618     
1619     
1620 }