DB_DataObject
[ class tree: DB_DataObject ] [ index: DB_DataObject ] [ all elements ]

Source for file Generator.php

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

Documentation generated on Fri, 05 Apr 2013 09:00:15 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.