home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / DB / DataObject / Generator.php < prev   
Encoding:
PHP Script  |  2008-07-02  |  52.2 KB  |  1,554 lines

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