Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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 315531 2011-08-26 02:21:29Z 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_object($__DB) && 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_object($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_object($this->tables) && 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_object($views) && 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_object($defs) && 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
}