home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / Generator.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  20.9 KB  |  671 lines

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PHP Version 4                                                        |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1997-2003 The PHP Group                                |
  6. // +----------------------------------------------------------------------+
  7. // | This source file is subject to version 2.02 of the PHP license,      |
  8. // | that is bundled with this package in the file LICENSE, and is        |
  9. // | available at through the world-wide-web at                           |
  10. // | http://www.php.net/license/2_02.txt.                                 |
  11. // | If you did not receive a copy of the PHP license and are unable to   |
  12. // | obtain it through the world-wide-web, please send a note to          |
  13. // | license@php.net so we can mail you a copy immediately.               |
  14. // +----------------------------------------------------------------------+
  15. // | Author:  Alan Knowles <alan@akbkhome.com>
  16. // +----------------------------------------------------------------------+
  17. // $Id: Generator.php,v 1.48 2004/01/29 09:53:19 alan_k Exp $
  18.  
  19. /**
  20.  * Generation tools for DB_DataObject
  21.  *
  22.  * Config _$ptions
  23.  * [DB_DataObject_Generator]
  24.  * ; optional default = DB/DataObject.php
  25.  * extends_location =
  26.  * ; optional default = DB_DataObject
  27.  * extends =
  28.  * ; leave blank to not generate template stuff.
  29.  * make_template = display,list,edit
  30.  *
  31.  * ; options for Template Generation (using FlexyTemplate
  32.  * [DB_DataObject_Generator_Template_Flexy]
  33.  * templateDir = /home/xxx/templates
  34.  * compileDir = /home/xxx/compiled",
  35.  * filters   = php,simpletags,bodyonly
  36.  * forceCompile = 0
  37.  *
  38.  * ; fileds to flags as hidden for template generation(preg_match format)
  39.  * hideFields = password
  40.  * ; fields to flag as read only.. (preg_match format)
  41.  * readOnlyFields = created|person_created|modified|person_modified
  42.  * ; fields to flag as links (from lists->edit/view) (preg_match formate)
  43.  * linkFields = id|username|name
  44.  * ; alter the extends field when updating a class (defaults to only replacing DB_DataObject)
  45.  * generator_class_rewrite = ANY|specific_name   // default is DB_DataObject
  46.  *
  47.  * @package  DB_DataObject
  48.  * @category DB
  49.  */
  50.  
  51. /**
  52.  * Needed classes
  53.  */
  54. require_once 'DB/DataObject.php';
  55. //require_once('Config.php');
  56.  
  57. /**
  58.  * Generator class
  59.  *
  60.  * @package DB_DataObject
  61.  */
  62. class DB_DataObject_Generator extends DB_DataObject
  63. {
  64.     /* =========================================================== */
  65.     /*  Utility functions - for building db config files           */
  66.     /* =========================================================== */
  67.  
  68.     /**
  69.      * Array of table names
  70.      *
  71.      * @var array
  72.      * @access private
  73.      */
  74.     var $tables;
  75.  
  76.     /**
  77.      * associative array table -> array of table row objects
  78.      *
  79.      * @var array
  80.      * @access private
  81.      */
  82.     var $_definitions;
  83.  
  84.     /**
  85.      * active table being output
  86.      *
  87.      * @var string
  88.      * @access private
  89.      */
  90.     var $table; // active tablename
  91.  
  92.  
  93.     /**
  94.      * The 'starter' = call this to start the process
  95.      *
  96.      * @access  public
  97.      * @return  none
  98.      */
  99.     function start()
  100.     {
  101.         $options = &PEAR::getStaticProperty('DB_DataObject','options');
  102.         $databases = array();
  103.         foreach($options as $k=>$v) {
  104.             if (substr($k,0,9) == 'database_') {
  105.                 $databases[] = $v;
  106.             }
  107.         }
  108.  
  109.         if (@$options['database'] && !in_array($options['database'],$databases)) {
  110.             $databases[] = $options['database'];
  111.         }
  112.  
  113.         foreach($databases as $database) {
  114.             if (!$database) continue;
  115.             echo "CREATING FOR $database\n";
  116.             $class = get_class($this);
  117.             $t = new $class;
  118.             $t->_database_dsn = $database;
  119.  
  120.             $t->_createTableList();
  121.  
  122.             foreach(get_class_methods($class) as $method) {
  123.                 if (substr($method,0,8 ) != 'generate') {
  124.                     continue;
  125.                 }
  126.                 $t->$method();
  127.             }
  128.         }
  129.         echo "DONE\n\n";
  130.     }
  131.  
  132.     /**
  133.      * Output File was config object, now just string
  134.      * Used to generate the Tables
  135.      *
  136.      * @var    string outputbuffer for table definitions
  137.      * @access private
  138.      */
  139.     var $_newConfig;
  140.  
  141.     /**
  142.      * Build a list of tables;
  143.      * Currently this is very Mysql Specific - ideas for more generic stiff welcome
  144.      *
  145.      * @access  private
  146.      * @return  none
  147.      */
  148.     function _createTableList()
  149.     {
  150.         $this->_connect();
  151.  
  152.  
  153.         $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
  154.  
  155.         $this->tables = $__DB->getListOf('tables');
  156.  
  157.         foreach($this->tables as $table) {
  158.             $defs =  $__DB->tableInfo($table);
  159.             
  160.             // cast all definitions to objects - as we deal with that better.
  161.             foreach($defs as $def) {
  162.                 if (is_array($def)) {
  163.                     $this->_definitions[$table][] = (object) $def;
  164.                 }
  165.             }
  166.         }
  167.         //print_r($this->_definitions);
  168.     }
  169.  
  170.     /**
  171.      * Auto generation of table data.
  172.      *
  173.      * it will output to db_oo_{database} the table definitions
  174.      *
  175.      * @access  private
  176.      * @return  none
  177.      */
  178.     function generateDefinitions()
  179.     {
  180.         echo "Generating Definitions file:        ";
  181.         if (!$this->tables) {
  182.             echo "-- NO TABLES -- \n";
  183.             return;
  184.         }
  185.  
  186.         $options = &PEAR::getStaticProperty('DB_DataObject','options');
  187.  
  188.  
  189.         //$this->_newConfig = new Config('IniFile');
  190.         $this->_newConfig = '';
  191.         foreach($this->tables as $this->table) {
  192.             $this->_generateDefinitionsTable();
  193.         }
  194.         $this->_connect();
  195.         // dont generate a schema if location is not set
  196.         // it's created on the fly!
  197.         if (!@$options['schema_location']) {
  198.             return;
  199.         }
  200.         $base =  $options['schema_location'];
  201.         $file = "{$base}/{$this->_database}.ini";
  202.         if (!file_exists($base))
  203.             mkdir($base,0755);
  204.         echo "{$file}\n";
  205.         touch($file);
  206.         //print_r($this->_newConfig);
  207.         $fh = fopen($file,'w');
  208.         fwrite($fh,$this->_newConfig);
  209.         fclose($fh);
  210.         //$ret = $this->_newConfig->writeInput($file,false);
  211.  
  212.         //if (PEAR::isError($ret) ) {
  213.         //    return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE);
  214.         // }
  215.     }
  216.  
  217.     /**
  218.      * The table geneation part
  219.      *
  220.      * @access  private
  221.      * @return  tabledef and keys array.
  222.      */
  223.     function _generateDefinitionsTable()
  224.     {
  225.         global $_DB_DATAOBJECT;
  226.         
  227.         $defs = $this->_definitions[$this->table];
  228.         $this->_newConfig .= "\n[{$this->table}]\n";
  229.         $keys_out =  "\n[{$this->table}__keys]\n";
  230.         $keys_out_primary = '';
  231.         $keys_out_secondary = '';
  232.         if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
  233.             echo "TABLE STRUCTURE FOR {$this->table}\n";
  234.             print_r($defs);
  235.         }
  236.         $DB = $this->getDatabaseConnection();
  237.         $dbtype = $DB->phptype;
  238.         
  239.         $ret = array(
  240.                 'table' => array(),
  241.                 'keys' => array(),
  242.             );
  243.             
  244.         $ret_keys_primary = array();
  245.         $ret_keys_secondary = array();
  246.         
  247.         
  248.         
  249.         foreach($defs as $t) {
  250.              
  251.             $n=0;
  252.  
  253.             switch (strtoupper($t->type)) {
  254.  
  255.                 case 'INT':
  256.                 case 'INT2':    // postgres
  257.                 case 'INT4':    // postgres
  258.                 case 'INT8':    // postgres
  259.                 case 'SERIAL4': // postgres
  260.                 case 'SERIAL8': // postgres
  261.                 case 'INTEGER':
  262.                 case 'TINYINT':
  263.                 case 'SMALLINT':
  264.                 case 'MEDIUMINT':
  265.                 case 'BIGINT':
  266.                     $type = DB_DATAOBJECT_INT;
  267.                     if ($t->len == 1) {
  268.                         $type +=  DB_DATAOBJECT_BOOL;
  269.                     }
  270.                     break;
  271.                
  272.                 case 'REAL':
  273.                 case 'DOUBLE':
  274.                 case 'FLOAT':
  275.                 case 'DECIMAL':
  276.                 case 'NUMERIC':
  277.                     $type = DB_DATAOBJECT_INT; // should really by FLOAT!!! / MONEY...
  278.                     break;
  279.                     
  280.                 case 'YEAR':
  281.                     $type = DB_DATAOBJECT_INT; 
  282.                     break;
  283.                     
  284.                 case 'BIT':
  285.                 case 'BOOL':   
  286.                 case 'BOOLEAN':   
  287.                 
  288.                     $type = DB_DATAOBJECT_BOOL;
  289.                     // postgres needs to quote '0'
  290.                     if ($dbtype == 'pgsql') {
  291.                         $type +=  DB_DATAOBJECT_STR;
  292.                     }
  293.                     break;
  294.                     
  295.                 case 'STRING':
  296.                 case 'CHAR':
  297.                 case 'VARCHAR':
  298.                 case 'VARCHAR2':
  299.                 case 'TINYTEXT':
  300.                 case 'TEXT':
  301.                 case 'MEDIUMTEXT':
  302.                 case 'LONGTEXT':
  303.                 case 'ENUM':
  304.                 case 'SET':         // not really but oh well
  305.                 case 'TIMESTAMPTZ': // postgres
  306.                 case 'BPCHAR':      // postgres
  307.                 case 'INTERVAL':    // postgres (eg. '12 days')
  308.                     $type = DB_DATAOBJECT_STR;
  309.                     break;
  310.                     
  311.                 case 'DATE':    
  312.                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE;
  313.                     break;
  314.                     
  315.                 case 'TIME':    
  316.                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TIME;
  317.                     break;    
  318.                     
  319.                 
  320.                 case 'DATETIME': 
  321.                      
  322.                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
  323.                     break;    
  324.                     
  325.                 case 'TIMESTAMP': // do other databases use this???
  326.                     
  327.                     $type = ($dbtype == 'mysql') ?
  328.                         DB_DATAOBJECT_MYSQLTIMESTAMP : 
  329.                         DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
  330.                     break;    
  331.                     
  332.                     
  333.                 case 'TINYBLOB':
  334.                 case 'BLOB':       /// these should really be ignored!!!???
  335.                 case 'MEDIUMBLOB':
  336.                 case 'LONGBLOB':
  337.                 case 'BYTEA':   // postgres blob support..
  338.                     $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB;
  339.                     break;
  340.                     
  341.                     
  342.             }
  343.             
  344.             
  345.             if (!strlen(trim($t->name))) {
  346.                 continue;
  347.             }
  348.             
  349.             if (preg_match('/not_null/i',$t->flags)) {
  350.                 $type += DB_DATAOBJECT_NOTNULL;
  351.             }
  352.             
  353.             $this->_newConfig .= "{$t->name} = $type\n";
  354.             $ret['table'][$t->name] = $type;
  355.             // i've no idea if this will work well on other databases?
  356.             // only use primary key or nextval(), cause the setFrom blocks you setting all key items...
  357.             // if no keys exist fall back to using unique
  358.             //echo "\n{$t->name} => {$t->flags}\n";
  359.             if (preg_match("/(auto_increment|nextval\()/i",$t->flags)) {
  360.                 // native sequences = 2
  361.                 $keys_out_primary .= "{$t->name} = N\n";
  362.                 $ret_keys_primary[$t->name] = 'N';
  363.             } else if (preg_match("/(primary|unique)/i",$t->flags)) {
  364.                 // keys.. = 1
  365.                 $keys_out_secondary .= "{$t->name} = K\n";
  366.                 $ret_keys_secondary[$t->name] = 'K';
  367.             }
  368.             
  369.             
  370.             
  371.  
  372.         }
  373.         
  374.         $this->_newConfig .= $keys_out . (empty($keys_out_primary) ? $keys_out_secondary : $keys_out_primary);
  375.         $ret['keys'] = empty($keys_out_primary) ? $ret_keys_secondary : $ret_keys_primary;
  376.         
  377.         
  378.         //print_r(array("dump for {$this->table}", $ret));
  379.         
  380.         return $ret;
  381.         
  382.         
  383.     }
  384.  
  385.     /*
  386.      * building the class files
  387.      * for each of the tables output a file!
  388.      */
  389.     function generateClasses()
  390.     {
  391.         //echo "Generating Class files:        \n";
  392.         $options = &PEAR::getStaticProperty('DB_DataObject','options');
  393.         $base = $options['class_location'];
  394.         if (!file_exists($base))
  395.             mkdir($base,0755);
  396.         $class_prefix  = $options['class_prefix'];
  397.         if ($extends = @$options['extends']) {
  398.             $this->_extends = $extends;
  399.             $this->_extendsFile = $options['extends_location'];
  400.         }
  401.  
  402.         foreach($this->tables as $this->table) {
  403.             $this->table = trim($this->table);
  404.             $this->classname = $class_prefix.preg_replace('/[^A-Z]/i','_',ucfirst($this->table));
  405.             $i = '';
  406.             $outfilename = "{$base}/".preg_replace('/[^A-Z]/i','_',ucfirst($this->table)).".php";
  407.             if (file_exists($outfilename))
  408.                 $i = implode('',file($outfilename));
  409.             $out = $this->_generateClassTable($i);
  410.             //echo "writing $this->classname\n";
  411.             $fh = fopen($outfilename, "w");
  412.             fputs($fh,$out);
  413.             fclose($fh);
  414.         }
  415.         //echo $out;
  416.     }
  417.  
  418.     /**
  419.      * class being extended (can be overridden by [DB_DataObject_Generator] extends=xxxx
  420.      *
  421.      * @var    string
  422.      * @access private
  423.      */
  424.     var $_extends = 'DB_DataObject';
  425.  
  426.     /**
  427.      * line to use for require('DB/DataObject.php');
  428.      *
  429.      * @var    string
  430.      * @access private
  431.      */
  432.     var $_extendsFile = "DB/DataObject.php";
  433.  
  434.     /**
  435.      * class being generated
  436.      *
  437.      * @var    string
  438.      * @access private
  439.      */
  440.     var $_className;
  441.  
  442.     /**
  443.      * The table class geneation part - single file.
  444.      *
  445.      * @access  private
  446.      * @return  none
  447.      */
  448.     function _generateClassTable($input = '')
  449.     {
  450.         // title = expand me!
  451.         $foot = "";
  452.         $head = "<?php\n/**\n * Table Definition for {$this->table}\n */\n";
  453.         // requires
  454.         $head .= "require_once '{$this->_extendsFile}';\n\n";
  455.         // add dummy class header in...
  456.         // class
  457.         $head .= "class {$this->classname} extends {$this->_extends} \n{\n";
  458.  
  459.         $body =  "\n    ###START_AUTOCODE\n";
  460.         $body .= "    /* the code below is auto generated do not remove the above tag */\n\n";
  461.         // table
  462.         $padding = (30 - strlen($this->table));
  463.         if ($padding < 2) $padding =2;
  464.         $p =  str_repeat(' ',$padding) ;
  465.         $body .= "    var \$__table = '{$this->table}';  {$p}// table name\n";
  466.  
  467.         $defs = $this->_definitions[$this->table];
  468.  
  469.         // show nice information!
  470.         $connections = array();
  471.         $sets = array();
  472.         foreach($defs as $t) {
  473.             if (!strlen(trim($t->name))) {
  474.                 continue;
  475.             }
  476.             $padding = (30 - strlen($t->name));
  477.             if ($padding < 2) $padding =2;
  478.             $p =  str_repeat(' ',$padding) ;
  479.             $body .="    var \${$t->name};  {$p}// {$t->type}({$t->len})  {$t->flags}\n";
  480.             // can not do set as PEAR::DB table info doesnt support it.
  481.             //if (substr($t->Type,0,3) == "set")
  482.             //    $sets[$t->Field] = "array".substr($t->Type,3);
  483.             $body .= $this->derivedHookVar($t,$padding);
  484.         }
  485.  
  486.         /* FC/BC compatible with ZE2 */
  487.         $x = new StdClass;
  488.         if (!method_exists($x,'__clone')) {
  489.             $body .= "\n";
  490.             $body .= "    /* ZE2 compatibility trick*/\n";
  491.             $body .= "    function __clone() { return \$this;}\n";
  492.         }
  493.  
  494.         // simple creation tools ! (static stuff!)
  495.         $body .= "\n";
  496.         $body .= "    /* Static get */\n";
  497.         $body .= "    function staticGet(\$k,\$v=NULL) { return DB_DataObject::staticGet('{$this->classname}',\$k,\$v); }\n";
  498.  
  499.         /*
  500.         theoretically there is scope here to introduce 'list' methods
  501.         based up 'xxxx_up' column!!! for heiracitcal trees..
  502.         */
  503.  
  504.         // set methods
  505.         //foreach ($sets as $k=>$v) {
  506.         //    $kk = strtoupper($k);
  507.         //    $body .="    function getSets{$k}() { return {$v}; }\n";
  508.         //}
  509.         $body .= $this->derivedHookFunctions();
  510.  
  511.         $body .= "\n    /* the code above is auto generated do not remove the tag below */";
  512.         $body .= "\n    ###END_AUTOCODE\n";
  513.  
  514.         $foot .= "}\n?>";
  515.         $full = $head . $body . $foot;
  516.  
  517.         if (!$input) {
  518.             return $full;
  519.         }
  520.         if (!preg_match('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n)/s',$input))  {
  521.             return $full;
  522.         }
  523.         if (!preg_match('/(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',$input)) {
  524.             return $full;
  525.         }
  526.  
  527.  
  528.         /* this will only replace extends DB_DataObject by default,
  529.             unless use set generator_class_rewrite to ANY or a name*/
  530.  
  531.         $class_rewrite = 'DB_DataObject';
  532.         $options = &PEAR::getStaticProperty('DB_DataObject','options');
  533.         if (!($class_rewrite = @$options['generator_class_rewrite'])) {
  534.             $class_rewrite = 'DB_DataObject';
  535.         }
  536.         if ($class_rewrite == 'ANY') {
  537.             $class_rewrite = '[a-z_]+';
  538.         }
  539.  
  540.         $input = preg_replace(
  541.             '/(\n|\r\n)class\s*[a-z_]+\s*extends\s*' .$class_rewrite . '\s*\{(\n|\r\n)/si',
  542.             "\nclass {$this->classname} extends {$this->_extends} \n{\n",
  543.             $input);
  544.  
  545.         return preg_replace(
  546.             '/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',
  547.             $body,$input);
  548.     }
  549.  
  550.     /**
  551.      * hook to add extra methods to all classes
  552.      *
  553.      * called once for each class, use with $this->table and
  554.      * $this->_definitions[$this->table], to get data out of the current table,
  555.      * use it to add extra methods to the default classes.
  556.      *
  557.      * @access   public
  558.      * @return  string added to class eg. functions.
  559.      */
  560.     function derivedHookFunctions()
  561.     {
  562.         // This is so derived generator classes can generate functions
  563.         // It MUST NOT be changed here!!!
  564.         return "";
  565.     }
  566.  
  567.     /**
  568.      * hook for var lines
  569.      * called each time a var line is generated, override to add extra var
  570.      * lines
  571.      *
  572.      * @param object t containing type,len,flags etc. from tableInfo call
  573.      * @param int padding number of spaces
  574.      * @access   public
  575.      * @return  string added to class eg. functions.
  576.      */
  577.     function derivedHookVar(&$t,$padding)
  578.     {
  579.         // This is so derived generator classes can generate variabels
  580.         // It MUST NOT be changed here!!!
  581.         return "";
  582.     }
  583.  
  584.  
  585.     /**
  586.     * getProxyFull - create a class definition on the fly and instantate it..
  587.     *
  588.     * similar to generated files - but also evals the class definitoin code.
  589.     * 
  590.     * 
  591.     * @param   string database name
  592.     * @param   string  table   name of table to create proxy for.
  593.     * 
  594.     *
  595.     * @return   object    Instance of class. or PEAR Error
  596.     * @access   public
  597.     */
  598.     function getProxyFull($database,$table) {
  599.         
  600.         if ($err = $this->fillTableSchema($database,$table)) {
  601.             return $err;
  602.         }
  603.         
  604.         
  605.         $options = &PEAR::getStaticProperty('DB_DataObject','options');
  606.         $class_prefix  = $options['class_prefix'];
  607.         
  608.         if ($extends = @$options['extends']) {
  609.             $this->_extends = $extends;
  610.             $this->_extendsFile = $options['extends_location'];
  611.         }
  612.  
  613.         
  614.         $classname = $this->classname = $class_prefix.preg_replace('/[^A-Z]/i','_',ucfirst(trim($this->table)));
  615.  
  616.         $out = $this->_generateClassTable();
  617.         //echo $out;
  618.         eval('?>'.$out);
  619.         return new $classname;
  620.         
  621.     }
  622.     
  623.      /**
  624.     * fillTableSchema - set the database schema on the fly
  625.     *
  626.     * 
  627.     * 
  628.     * @param   string database name
  629.     * @param   string  table   name of table to create schema info for
  630.     *
  631.     * @return   none | PEAR::error()
  632.     * @access   public
  633.     */
  634.     function fillTableSchema($database,$table) {
  635.         global $_DB_DATAOBJECT;
  636.         $this->_database  = $database; 
  637.         $this->_connect();
  638.         $table = trim($table);
  639.         
  640.         $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
  641.         
  642.         $defs =  $__DB->tableInfo($table);
  643.         if (PEAR::isError($defs)) {
  644.             return $defs;
  645.         }
  646.         if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
  647.             $this->debug("getting def for $database/$table",'fillTable');
  648.             $this->debug(print_r($defs,true),'defs');
  649.         }
  650.         // cast all definitions to objects - as we deal with that better.
  651.         
  652.             
  653.         foreach($defs as $def) {
  654.             if (is_array($def)) {
  655.                 $this->_definitions[$table][] = (object) $def;
  656.             }
  657.         }
  658.  
  659.         $this->table = trim($table);
  660.         $ret = $this->_generateDefinitionsTable();
  661.         
  662.         $_DB_DATAOBJECT['INI'][$database][$table] = $ret['table'];
  663.         $_DB_DATAOBJECT['INI'][$database][$table.'__keys'] = $ret['keys'];
  664.         return false;
  665.         
  666.     }
  667.     
  668.  
  669.  
  670. }
  671.