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