diff options
Diffstat (limited to 'extlib/DB/DataObject')
-rw-r--r-- | extlib/DB/DataObject/Cast.php | 546 | ||||
-rw-r--r-- | extlib/DB/DataObject/Error.php | 53 | ||||
-rw-r--r-- | extlib/DB/DataObject/Generator.php | 1553 | ||||
-rwxr-xr-x | extlib/DB/DataObject/createTables.php | 59 |
4 files changed, 2211 insertions, 0 deletions
diff --git a/extlib/DB/DataObject/Cast.php b/extlib/DB/DataObject/Cast.php new file mode 100644 index 000000000..616abb55e --- /dev/null +++ b/extlib/DB/DataObject/Cast.php @@ -0,0 +1,546 @@ +<?php +/** + * Prototype Castable Object.. for DataObject queries + * + * Storage for Data that may be cast into a variety of formats. + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB_DataObject + * @author Alan Knowles <alan@akbkhome.com> + * @copyright 1997-2006 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Cast.php,v 1.15 2005/07/07 05:30:53 alan_k Exp $ + * @link http://pear.php.net/package/DB_DataObject + */ + +/** +* +* Common usages: +* // blobs +* $data = DB_DataObject_Cast::blob($somefile); +* $data = DB_DataObject_Cast::string($somefile); +* $dataObject->someblobfield = $data +* +* // dates? +* $d1 = new DB_DataObject_Cast::date('12/12/2000'); +* $d2 = new DB_DataObject_Cast::date(2000,12,30); +* $d3 = new DB_DataObject_Cast::date($d1->year, $d1->month+30, $d1->day+30); +* +* // time, datetime.. ????????? +* +* // raw sql???? +* $data = DB_DataObject_Cast::sql('cast("123123",datetime)'); +* $data = DB_DataObject_Cast::sql('NULL'); +* +* // int's/string etc. are proably pretty pointless..!!!! +* +* +* inside DB_DataObject, +* if (is_a($v,'db_dataobject_class')) { +* $value .= $v->toString(DB_DATAOBJECT_INT,'mysql'); +* } +* +* +* +* + +*/ +class DB_DataObject_Cast { + + /** + * Type of data Stored in the object.. + * + * @var string (date|blob|.....?) + * @access public + */ + var $type; + + /** + * Data For date representation + * + * @var int day/month/year + * @access public + */ + var $day; + var $month; + var $year; + + + /** + * Generic Data.. + * + * @var string + * @access public + */ + + var $value; + + + + /** + * Blob consructor + * + * create a Cast object from some raw data.. (binary) + * + * + * @param string (with binary data!) + * + * @return object DB_DataObject_Cast + * @access public + */ + + function blob($value) { + $r = new DB_DataObject_Cast; + $r->type = 'blob'; + $r->value = $value; + return $r; + } + + + /** + * String consructor (actually use if for ints and everything else!!! + * + * create a Cast object from some string (not binary) + * + * + * @param string (with binary data!) + * + * @return object DB_DataObject_Cast + * @access public + */ + + function string($value) { + $r = new DB_DataObject_Cast; + $r->type = 'string'; + $r->value = $value; + return $r; + } + + /** + * SQL constructor (for raw SQL insert) + * + * create a Cast object from some sql + * + * @param string (with binary data!) + * + * @return object DB_DataObject_Cast + * @access public + */ + + function sql($value) + { + $r = new DB_DataObject_Cast; + $r->type = 'sql'; + $r->value = $value; + return $r; + } + + + /** + * Date Constructor + * + * create a Cast object from some string (not binary) + * NO VALIDATION DONE, although some crappy re-calcing done! + * + * @param vargs... accepts + * dd/mm + * dd/mm/yyyy + * yyyy-mm + * yyyy-mm-dd + * array(yyyy,dd) + * array(yyyy,dd,mm) + * + * + * + * @return object DB_DataObject_Cast + * @access public + */ + + function date() + { + $args = func_get_args(); + switch(count($args)) { + case 0: // no args = today! + $bits = explode('-',date('Y-m-d')); + break; + case 1: // one arg = a string + + if (strpos($args[0],'/') !== false) { + $bits = array_reverse(explode('/',$args[0])); + } else { + $bits = explode('-',$args[0]); + } + break; + default: // 2 or more.. + $bits = $args; + } + if (count($bits) == 1) { // if YYYY set day = 1st.. + $bits[] = 1; + } + + if (count($bits) == 2) { // if YYYY-DD set day = 1st.. + $bits[] = 1; + } + + // if year < 1970 we cant use system tools to check it... + // so we make a few best gueses.... + // basically do date calculations for the year 2000!!! + // fix me if anyone has more time... + if (($bits[0] < 1975) || ($bits[0] > 2030)) { + $oldyear = $bits[0]; + $bits = explode('-',date('Y-m-d',mktime(1,1,1,$bits[1],$bits[2],2000))); + $bits[0] = ($bits[0] - 2000) + $oldyear; + } else { + // now mktime + $bits = explode('-',date('Y-m-d',mktime(1,1,1,$bits[1],$bits[2],$bits[0]))); + } + $r = new DB_DataObject_Cast; + $r->type = 'date'; + list($r->year,$r->month,$r->day) = $bits; + return $r; + } + + + + /** + * Data For time representation ** does not handle timezones!! + * + * @var int hour/minute/second + * @access public + */ + var $hour; + var $minute; + var $second; + + + /** + * DateTime Constructor + * + * create a Cast object from a Date/Time + * Maybe should accept a Date object.! + * NO VALIDATION DONE, although some crappy re-calcing done! + * + * @param vargs... accepts + * noargs (now) + * yyyy-mm-dd HH:MM:SS (Iso) + * array(yyyy,mm,dd,HH,MM,SS) + * + * + * @return object DB_DataObject_Cast + * @access public + * @author therion 5 at hotmail + */ + + function dateTime() + { + $args = func_get_args(); + switch(count($args)) { + case 0: // no args = now! + $datetime = date('Y-m-d G:i:s', mktime()); + + case 1: + // continue on from 0 args. + if (!isset($datetime)) { + $datetime = $args[0]; + } + + $parts = explode(' ', $datetime); + $bits = explode('-', $parts[0]); + $bits = array_merge($bits, explode(':', $parts[1])); + break; + + default: // 2 or more.. + $bits = $args; + + } + + if (count($bits) != 6) { + // PEAR ERROR? + return false; + } + + $r = DB_DataObject_Cast::date($bits[0], $bits[1], $bits[2]); + if (!$r) { + return $r; // pass thru error (False) - doesnt happen at present! + } + // change the type! + $r->type = 'datetime'; + + // should we mathematically sort this out.. + // (or just assume that no-one's dumb enough to enter 26:90:90 as a time! + $r->hour = $bits[3]; + $r->minute = $bits[4]; + $r->second = $bits[5]; + return $r; + + } + + + + /** + * time Constructor + * + * create a Cast object from a Date/Time + * Maybe should accept a Date object.! + * NO VALIDATION DONE, and no-recalcing done! + * + * @param vargs... accepts + * noargs (now) + * HH:MM:SS (Iso) + * array(HH,MM,SS) + * + * + * @return object DB_DataObject_Cast + * @access public + * @author therion 5 at hotmail + */ + function time() + { + $args = func_get_args(); + switch (count($args)) { + case 0: // no args = now! + $time = date('G:i:s', mktime()); + + case 1: + // continue on from 0 args. + if (!isset($time)) { + $time = $args[0]; + } + $bits = explode(':', $time); + break; + + default: // 2 or more.. + $bits = $args; + + } + + if (count($bits) != 3) { + return false; + } + + // now take data from bits into object fields + $r = new DB_DataObject_Cast; + $r->type = 'time'; + $r->hour = $bits[0]; + $r->minute = $bits[1]; + $r->second = $bits[2]; + return $r; + + } + + + + /** + * get the string to use in the SQL statement for this... + * + * + * @param int $to Type (DB_DATAOBJECT_* + * @param object $db DB Connection Object + * + * + * @return string + * @access public + */ + + function toString($to=false,$db) + { + // if $this->type is not set, we are in serious trouble!!!! + // values for to: + $method = 'toStringFrom'.$this->type; + return $this->$method($to,$db); + } + + /** + * get the string to use in the SQL statement from a blob of binary data + * ** Suppots only blob->postgres::bytea + * + * @param int $to Type (DB_DATAOBJECT_* + * @param object $db DB Connection Object + * + * + * @return string + * @access public + */ + function toStringFromBlob($to,$db) + { + // first weed out invalid casts.. + // in blobs can only be cast to blobs.! + + // perhaps we should support TEXT fields??? + + if (!($to & DB_DATAOBJECT_BLOB)) { + return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::blob to something other than a blob!'); + } + + switch ($db->dsn["phptype"]) { + case 'pgsql': + return "'".pg_escape_bytea($this->value)."'::bytea"; + + case 'mysql': + return "'".mysql_real_escape_string($this->value,$db->connection)."'"; + + case 'mysqli': + // this is funny - the parameter order is reversed ;) + return "'".mysqli_real_escape_string($db->connection, $this->value)."'"; + + + + default: + return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet"); + } + + } + + /** + * get the string to use in the SQL statement for a blob from a string! + * ** Suppots only string->postgres::bytea + * + * + * @param int $to Type (DB_DATAOBJECT_* + * @param object $db DB Connection Object + * + * + * @return string + * @access public + */ + function toStringFromString($to,$db) + { + // first weed out invalid casts.. + // in blobs can only be cast to blobs.! + + // perhaps we should support TEXT fields??? + // + + if (!($to & DB_DATAOBJECT_BLOB)) { + return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::string to something other than a blob!'. + ' (why not just use native features)'); + } + + switch ($db->dsn['phptype']) { + case 'pgsql': + return "'".pg_escape_string($this->value)."'::bytea"; + + case 'mysql': + return "'".mysql_real_escape_string($this->value,$db->connection)."'"; + + + case 'mysqli': + return "'".mysqli_real_escape_string($db->connection, $this->value)."'"; + + + default: + return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet"); + } + + } + + + /** + * get the string to use in the SQL statement for a date + * + * + * + * @param int $to Type (DB_DATAOBJECT_* + * @param object $db DB Connection Object + * + * + * @return string + * @access public + */ + function toStringFromDate($to,$db) + { + // first weed out invalid casts.. + // in blobs can only be cast to blobs.! + // perhaps we should support TEXT fields??? + // + + if (($to !== false) && !($to & DB_DATAOBJECT_DATE)) { + return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::string to something other than a date!'. + ' (why not just use native features)'); + } + return "'{$this->year}-{$this->month}-{$this->day}'"; + } + + /** + * get the string to use in the SQL statement for a datetime + * + * + * + * @param int $to Type (DB_DATAOBJECT_* + * @param object $db DB Connection Object + * + * + * @return string + * @access public + * @author therion 5 at hotmail + */ + + function toStringFromDateTime($to,$db) + { + // first weed out invalid casts.. + // in blobs can only be cast to blobs.! + // perhaps we should support TEXT fields??? + if (($to !== false) && + !($to & (DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME))) { + return PEAR::raiseError('Invalid Cast from a ' . + ' DB_DataObject_Cast::dateTime to something other than a datetime!' . + ' (try using native features)'); + } + return "'{$this->year}-{$this->month}-{$this->day} {$this->hour}:{$this->minute}:{$this->second}'"; + } + + /** + * get the string to use in the SQL statement for a time + * + * + * + * @param int $to Type (DB_DATAOBJECT_* + * @param object $db DB Connection Object + * + * + * @return string + * @access public + * @author therion 5 at hotmail + */ + + function toStringFromTime($to,$db) + { + // first weed out invalid casts.. + // in blobs can only be cast to blobs.! + // perhaps we should support TEXT fields??? + if (($to !== false) && !($to & DB_DATAOBJECT_TIME)) { + return PEAR::raiseError('Invalid Cast from a' . + ' DB_DataObject_Cast::time to something other than a time!'. + ' (try using native features)'); + } + return "'{$this->hour}:{$this->minute}:{$this->second}'"; + } + + /** + * get the string to use in the SQL statement for a raw sql statement. + * + * @param int $to Type (DB_DATAOBJECT_* + * @param object $db DB Connection Object + * + * + * @return string + * @access public + */ + function toStringFromSql($to,$db) + { + return $this->value; + } + + + + +} + diff --git a/extlib/DB/DataObject/Error.php b/extlib/DB/DataObject/Error.php new file mode 100644 index 000000000..05a741408 --- /dev/null +++ b/extlib/DB/DataObject/Error.php @@ -0,0 +1,53 @@ +<?php +/** + * DataObjects error handler, loaded on demand... + * + * DB_DataObject_Error is a quick wrapper around pear error, so you can distinguish the + * error code source. + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB_DataObject + * @author Alan Knowles <alan@akbkhome.com> + * @copyright 1997-2006 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Error.php,v 1.3 2005/03/23 02:35:35 alan_k Exp $ + * @link http://pear.php.net/package/DB_DataObject + */ + + +class DB_DataObject_Error extends PEAR_Error +{ + + /** + * DB_DataObject_Error constructor. + * + * @param mixed $code DB error code, or string with error message. + * @param integer $mode what "error mode" to operate in + * @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER + * @param mixed $debuginfo additional debug info, such as the last query + * + * @access public + * + * @see PEAR_Error + */ + function DB_DataObject_Error($message = '', $code = DB_ERROR, $mode = PEAR_ERROR_RETURN, + $level = E_USER_NOTICE) + { + $this->PEAR_Error('DB_DataObject Error: ' . $message, $code, $mode, $level); + + } + + + // todo : - support code -> message handling, and translated error messages... + + + +} diff --git a/extlib/DB/DataObject/Generator.php b/extlib/DB/DataObject/Generator.php new file mode 100644 index 000000000..de16af692 --- /dev/null +++ b/extlib/DB/DataObject/Generator.php @@ -0,0 +1,1553 @@ +<?php +/** + * Generation tools for DB_DataObject + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB_DataObject + * @author Alan Knowles <alan@akbkhome.com> + * @copyright 1997-2006 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Generator.php,v 1.141 2008/01/30 02:29:39 alan_k Exp $ + * @link http://pear.php.net/package/DB_DataObject + */ + + /* + * Security Notes: + * This class uses eval to create classes on the fly. + * The table name and database name are used to check the database before writing the + * class definitions, we now check for quotes and semi-colon's in both variables + * so I cant see how it would be possible to generate code even if + * for some crazy reason you took the classname and table name from User Input. + * + * If you consider that wrong, or can prove it.. let me know! + */ + + /** + * + * Config _$ptions + * [DB_DataObject_Generator] + * ; optional default = DB/DataObject.php + * extends_location = + * ; optional default = DB_DataObject + * extends = + * ; alter the extends field when updating a class (defaults to only replacing DB_DataObject) + * generator_class_rewrite = ANY|specific_name // default is DB_DataObject + * + */ + +/** + * Needed classes + * We lazy load here, due to problems with the tests not setting up include path correctly. + * FIXME! + */ +class_exists('DB_DataObject') ? '' : require_once 'DB/DataObject.php'; +//require_once('Config.php'); + +/** + * Generator class + * + * @package DB_DataObject + */ +class DB_DataObject_Generator extends DB_DataObject +{ + /* =========================================================== */ + /* Utility functions - for building db config files */ + /* =========================================================== */ + + /** + * Array of table names + * + * @var array + * @access private + */ + var $tables; + + /** + * associative array table -> array of table row objects + * + * @var array + * @access private + */ + var $_definitions; + + /** + * active table being output + * + * @var string + * @access private + */ + var $table; // active tablename + + + /** + * The 'starter' = call this to start the process + * + * @access public + * @return none + */ + function start() + { + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver']; + + $databases = array(); + foreach($options as $k=>$v) { + if (substr($k,0,9) == 'database_') { + $databases[substr($k,9)] = $v; + } + } + + if (isset($options['database'])) { + if ($db_driver == 'DB') { + require_once 'DB.php'; + $dsn = DB::parseDSN($options['database']); + } else { + require_once 'MDB2.php'; + $dsn = MDB2::parseDSN($options['database']); + } + + if (!isset($database[$dsn['database']])) { + $databases[$dsn['database']] = $options['database']; + } + } + + foreach($databases as $databasename => $database) { + if (!$database) { + continue; + } + $this->debug("CREATING FOR $databasename\n"); + $class = get_class($this); + $t = new $class; + $t->_database_dsn = $database; + + + $t->_database = $databasename; + if ($db_driver == 'DB') { + require_once 'DB.php'; + $dsn = DB::parseDSN($database); + } else { + require_once 'MDB2.php'; + $dsn = MDB2::parseDSN($database); + } + + if (($dsn['phptype'] == 'sqlite') && is_file($databasename)) { + $t->_database = basename($t->_database); + } + $t->_createTableList(); + + foreach(get_class_methods($class) as $method) { + if (substr($method,0,8 ) != 'generate') { + continue; + } + $this->debug("calling $method"); + $t->$method(); + } + } + $this->debug("DONE\n\n"); + } + + /** + * Output File was config object, now just string + * Used to generate the Tables + * + * @var string outputbuffer for table definitions + * @access private + */ + var $_newConfig; + + /** + * Build a list of tables; + * and store it in $this->tables and $this->_definitions[tablename]; + * + * @access private + * @return none + */ + function _createTableList() + { + $this->_connect(); + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + + $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5]; + + $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver']; + $is_MDB2 = ($db_driver != 'DB') ? true : false; + + if (is_a($__DB , 'PEAR_Error')) { + return PEAR::raiseError($__DB->toString(), null, PEAR_ERROR_DIE); + } + + if (!$is_MDB2) { + // try getting a list of schema tables first. (postgres) + $__DB->expectError(DB_ERROR_UNSUPPORTED); + $this->tables = $__DB->getListOf('schema.tables'); + $__DB->popExpect(); + } else { + /** + * set portability and some modules to fetch the informations + */ + $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE); + $__DB->loadModule('Manager'); + $__DB->loadModule('Reverse'); + } + + if ((empty($this->tables) || is_a($this->tables , 'PEAR_Error'))) { + //if that fails fall back to clasic tables list. + if (!$is_MDB2) { + // try getting a list of schema tables first. (postgres) + $__DB->expectError(DB_ERROR_UNSUPPORTED); + $this->tables = $__DB->getListOf('tables'); + $__DB->popExpect(); + } else { + $this->tables = $__DB->manager->listTables(); + $sequences = $__DB->manager->listSequences(); + foreach ($sequences as $k => $v) { + $this->tables[] = $__DB->getSequenceName($v); + } + } + } + + if (is_a($this->tables , 'PEAR_Error')) { + return PEAR::raiseError($this->tables->toString(), null, PEAR_ERROR_DIE); + } + + // build views as well if asked to. + if (!empty($options['build_views'])) { + if (!$is_MDB2) { + $views = $__DB->getListOf('views'); + } else { + $views = $__DB->manager->listViews(); + } + if (is_a($views,'PEAR_Error')) { + return PEAR::raiseError( + 'Error getting Views (check the PEAR bug database for the fix to DB), ' . + $views->toString(), + null, + PEAR_ERROR_DIE + ); + } + $this->tables = array_merge ($this->tables, $views); + } + + // declare a temporary table to be filled with matching tables names + $tmp_table = array(); + + + foreach($this->tables as $table) { + if (isset($options['generator_include_regex']) && + !preg_match($options['generator_include_regex'],$table)) { + continue; + } else if (isset($options['generator_exclude_regex']) && + preg_match($options['generator_exclude_regex'],$table)) { + continue; + } + // postgres strip the schema bit from the + if (!empty($options['generator_strip_schema'])) { + $bits = explode('.', $table,2); + $table = $bits[0]; + if (count($bits) > 1) { + $table = $bits[1]; + } + } + $quotedTable = !empty($options['quote_identifiers_tableinfo']) ? + $__DB->quoteIdentifier($table) : $table; + + if (!$is_MDB2) { + + $defs = $__DB->tableInfo($quotedTable); + } else { + $defs = $__DB->reverse->tableInfo($quotedTable); + // rename the length value, so it matches db's return. + foreach ($defs as $k => $v) { + if (!isset($defs[$k]['length'])) { + continue; + } + $defs[$k]['len'] = $defs[$k]['length']; + } + } + + if (is_a($defs,'PEAR_Error')) { + // running in debug mode should pick this up as a big warning.. + $this->raiseError('Error reading tableInfo, '. $defs->toString()); + continue; + } + // cast all definitions to objects - as we deal with that better. + + + + foreach($defs as $def) { + if (!is_array($def)) { + continue; + } + + $this->_definitions[$table][] = (object) $def; + + } + // we find a matching table, just store it into a temporary array + $tmp_table[] = $table; + + + } + // the temporary table array is now the right one (tables names matching + // with regex expressions have been removed) + $this->tables = $tmp_table; + //print_r($this->_definitions); + } + + /** + * Auto generation of table data. + * + * it will output to db_oo_{database} the table definitions + * + * @access private + * @return none + */ + function generateDefinitions() + { + $this->debug("Generating Definitions file: "); + if (!$this->tables) { + $this->debug("-- NO TABLES -- \n"); + return; + } + + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + + + //$this->_newConfig = new Config('IniFile'); + $this->_newConfig = ''; + foreach($this->tables as $this->table) { + $this->_generateDefinitionsTable(); + } + $this->_connect(); + // dont generate a schema if location is not set + // it's created on the fly! + if (empty($options['schema_location']) && empty($options["ini_{$this->_database}"]) ) { + return; + } + if (!empty($options['generator_no_ini'])) { // built in ini files.. + return; + } + $base = @$options['schema_location']; + if (isset($options["ini_{$this->_database}"])) { + $file = $options["ini_{$this->_database}"]; + } else { + $file = "{$base}/{$this->_database}.ini"; + } + + if (!file_exists(dirname($file))) { + require_once 'System.php'; + System::mkdir(array('-p','-m',0755,dirname($file))); + } + $this->debug("Writing ini as {$file}\n"); + //touch($file); + $tmpname = tempnam(session_save_path(),'DataObject_'); + //print_r($this->_newConfig); + $fh = fopen($tmpname,'w'); + fwrite($fh,$this->_newConfig); + fclose($fh); + $perms = file_exists($file) ? fileperms($file) : 0755; + // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy.. + + if (!@rename($tmpname, $file)) { + unlink($file); + rename($tmpname, $file); + } + chmod($file,$perms); + //$ret = $this->_newConfig->writeInput($file,false); + + //if (PEAR::isError($ret) ) { + // return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE); + // } + } + + /** + * generate Foreign Keys (for links.ini) + * Currenly only works with mysql / mysqli + * to use, you must set option: generate_links=true + * + * @author Pascal Schöni + */ + function generateForeignKeys() + { + $options = PEAR::getStaticProperty('DB_DataObject','options'); + if (empty($options['generate_links'])) { + return false; + } + $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5]; + if (!in_array($__DB->phptype, array('mysql','mysqli'))) { + echo "WARNING: cant handle non-mysql introspection for defaults."; + return; // cant handle non-mysql introspection for defaults. + } + + $DB = $this->getDatabaseConnection(); + + $fk = array(); + + foreach($this->tables as $this->table) { + $res =& $DB->query('SHOW CREATE TABLE ' . $this->table); + if (PEAR::isError($res)) { + die($res->getMessage()); + } + + $text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0); + $treffer = array(); + // Extract FOREIGN KEYS + preg_match_all( + "/FOREIGN KEY \(`(\w*)`\) REFERENCES `(\w*)` \(`(\w*)`\)/i", + $text[1], + $treffer, + PREG_SET_ORDER); + + if (count($treffer) < 1) { + continue; + } + for ($i = 0; $i < count($treffer); $i++) { + $fk[$this->table][$treffer[$i][1]] = $treffer[$i][2] . ":" . $treffer[$i][3]; + } + + } + + $links_ini = ""; + + foreach($fk as $table => $details) { + $links_ini .= "[$table]\n"; + foreach ($details as $col => $ref) { + $links_ini .= "$col = $ref\n"; + } + $links_ini .= "\n"; + } + + // dont generate a schema if location is not set + // it's created on the fly! + $options = PEAR::getStaticProperty('DB_DataObject','options'); + + if (empty($options['schema_location'])) { + return; + } + + + $file = "{$options['schema_location']}/{$this->_database}.links.ini"; + + if (!file_exists(dirname($file))) { + require_once 'System.php'; + System::mkdir(array('-p','-m',0755,dirname($file))); + } + + $this->debug("Writing ini as {$file}\n"); + + //touch($file); // not sure why this is needed? + $tmpname = tempnam(session_save_path(),'DataObject_'); + + $fh = fopen($tmpname,'w'); + fwrite($fh,$links_ini); + fclose($fh); + $perms = file_exists($file) ? fileperms($file) : 0755; + // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy.. + if (!@rename($tmpname, $file)) { + unlink($file); + rename($tmpname, $file); + } + chmod($file, $perms); + } + + + /** + * The table geneation part + * + * @access private + * @return tabledef and keys array. + */ + function _generateDefinitionsTable() + { + global $_DB_DATAOBJECT; + + $defs = $this->_definitions[$this->table]; + $this->_newConfig .= "\n[{$this->table}]\n"; + $keys_out = "\n[{$this->table}__keys]\n"; + $keys_out_primary = ''; + $keys_out_secondary = ''; + if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) { + echo "TABLE STRUCTURE FOR {$this->table}\n"; + print_r($defs); + } + $DB = $this->getDatabaseConnection(); + $dbtype = $DB->phptype; + + $ret = array( + 'table' => array(), + 'keys' => array(), + ); + + $ret_keys_primary = array(); + $ret_keys_secondary = array(); + + + + foreach($defs as $t) { + + $n=0; + $write_ini = true; + + + switch (strtoupper($t->type)) { + + case 'INT': + case 'INT2': // postgres + case 'INT4': // postgres + case 'INT8': // postgres + case 'SERIAL4': // postgres + case 'SERIAL8': // postgres + case 'INTEGER': + case 'TINYINT': + case 'SMALLINT': + case 'MEDIUMINT': + case 'BIGINT': + $type = DB_DATAOBJECT_INT; + if ($t->len == 1) { + $type += DB_DATAOBJECT_BOOL; + } + break; + + case 'REAL': + case 'DOUBLE': + case 'DOUBLE PRECISION': // double precision (firebird) + case 'FLOAT': + case 'FLOAT4': // real (postgres) + case 'FLOAT8': // double precision (postgres) + case 'DECIMAL': + case 'MONEY': // mssql and maybe others + case 'NUMERIC': + case 'NUMBER': // oci8 + $type = DB_DATAOBJECT_INT; // should really by FLOAT!!! / MONEY... + break; + + case 'YEAR': + $type = DB_DATAOBJECT_INT; + break; + + case 'BIT': + case 'BOOL': + case 'BOOLEAN': + + $type = DB_DATAOBJECT_BOOL; + // postgres needs to quote '0' + if ($dbtype == 'pgsql') { + $type += DB_DATAOBJECT_STR; + } + break; + + case 'STRING': + case 'CHAR': + case 'VARCHAR': + case 'VARCHAR2': + case 'TINYTEXT': + + case 'ENUM': + case 'SET': // not really but oh well + case 'TIMESTAMPTZ': // postgres + case 'BPCHAR': // postgres + case 'INTERVAL': // postgres (eg. '12 days') + + case 'CIDR': // postgres IP net spec + case 'INET': // postgres IP + case 'MACADDR': // postgress network Mac address. + + case 'INTEGER[]': // postgres type + case 'BOOLEAN[]': // postgres type + + $type = DB_DATAOBJECT_STR; + break; + + case 'TEXT': + case 'MEDIUMTEXT': + case 'LONGTEXT': + + $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT; + break; + + + case 'DATE': + $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE; + break; + + case 'TIME': + $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TIME; + break; + + + case 'DATETIME': + + $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME; + break; + + case 'TIMESTAMP': // do other databases use this??? + + $type = ($dbtype == 'mysql') ? + DB_DATAOBJECT_MYSQLTIMESTAMP : + DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME; + break; + + + case 'TINYBLOB': + case 'BLOB': /// these should really be ignored!!!??? + case 'MEDIUMBLOB': + case 'LONGBLOB': + case 'BYTEA': // postgres blob support.. + $type = DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB; + break; + default: + echo "*****************************************************************\n". + "** WARNING UNKNOWN TYPE **\n". + "** Found column '{$t->name}', of type '{$t->type}' **\n". + "** Please submit a bug, describe what type you expect this **\n". + "** column to be **\n". + "** ---------POSSIBLE FIX / WORKAROUND -------------------------**\n". + "** Try using MDB2 as the backend - eg set the config option **\n". + "** db_driver = MDB2 **\n". + "*****************************************************************\n"; + $write_ini = false; + break; + } + + if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) { + echo "*****************************************************************\n". + "** WARNING COLUMN NAME UNUSABLE **\n". + "** Found column '{$t->name}', of type '{$t->type}' **\n". + "** Since this column name can't be converted to a php variable **\n". + "** name, and the whole idea of mapping would result in a mess **\n". + "** This column has been ignored... **\n". + "*****************************************************************\n"; + continue; + } + + if (!strlen(trim($t->name))) { + continue; // is this a bug? + } + + if (preg_match('/not[ _]null/i',$t->flags)) { + $type += DB_DATAOBJECT_NOTNULL; + } + + + if (in_array($t->name,array('null','yes','no','true','false'))) { + echo "*****************************************************************\n". + "** WARNING **\n". + "** Found column '{$t->name}', which is invalid in an .ini file **\n". + "** This line will not be writen to the file - you will have **\n". + "** define the keys()/method manually. **\n". + "*****************************************************************\n"; + $write_ini = false; + } else { + $this->_newConfig .= "{$t->name} = $type\n"; + } + + $ret['table'][$t->name] = $type; + // i've no idea if this will work well on other databases? + // only use primary key or nextval(), cause the setFrom blocks you setting all key items... + // if no keys exist fall back to using unique + //echo "\n{$t->name} => {$t->flags}\n"; + if (preg_match("/(auto_increment|nextval\()/i",rawurldecode($t->flags)) + || (isset($t->autoincrement) && ($t->autoincrement === true))) { + + // native sequences = 2 + if ($write_ini) { + $keys_out_primary .= "{$t->name} = N\n"; + } + $ret_keys_primary[$t->name] = 'N'; + + } else if (preg_match("/(primary|unique)/i",$t->flags)) { + // keys.. = 1 + $key_type = 'K'; + if (!preg_match("/(primary)/i",$t->flags)) { + $key_type = 'U'; + } + + if ($write_ini) { + $keys_out_secondary .= "{$t->name} = {$key_type}\n"; + } + $ret_keys_secondary[$t->name] = $key_type; + } + + + } + + $this->_newConfig .= $keys_out . (empty($keys_out_primary) ? $keys_out_secondary : $keys_out_primary); + $ret['keys'] = empty($keys_out_primary) ? $ret_keys_secondary : $ret_keys_primary; + + if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) { + print_r(array("dump for {$this->table}", $ret)); + } + + return $ret; + + + } + + /** + * Convert a table name into a class name -> override this if you want a different mapping + * + * @access public + * @return string class name; + */ + + + function getClassNameFromTableName($table) + { + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + $class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix']; + return $class_prefix.preg_replace('/[^A-Z0-9]/i','_',ucfirst(trim($this->table))); + } + + + /** + * Convert a table name into a file name -> override this if you want a different mapping + * + * @access public + * @return string file name; + */ + + + function getFileNameFromTableName($table) + { + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + $base = $options['class_location']; + if (strpos($base,'%s') !== false) { + $base = dirname($base); + } + if (!file_exists($base)) { + require_once 'System.php'; + System::mkdir(array('-p',$base)); + } + if (strpos($options['class_location'],'%s') !== false) { + $outfilename = sprintf($options['class_location'], + preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table))); + } else { + $outfilename = "{$base}/".preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)).".php"; + } + return $outfilename; + + } + + + /** + * Convert a column name into a method name (usually prefixed by get/set/validateXXXXX) + * + * @access public + * @return string method name; + */ + + + function getMethodNameFromColumnName($col) + { + return ucfirst($col); + } + + + + + /* + * building the class files + * for each of the tables output a file! + */ + function generateClasses() + { + //echo "Generating Class files: \n"; + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + + + if ($extends = @$options['extends']) { + $this->_extends = $extends; + $this->_extendsFile = $options['extends_location']; + } + + foreach($this->tables as $this->table) { + $this->table = trim($this->table); + $this->classname = $this->getClassNameFromTableName($this->table); + $i = ''; + $outfilename = $this->getFileNameFromTableName($this->table); + + $oldcontents = ''; + if (file_exists($outfilename)) { + // file_get_contents??? + $oldcontents = implode('',file($outfilename)); + } + + $out = $this->_generateClassTable($oldcontents); + $this->debug( "writing $this->classname\n"); + $tmpname = tempnam(session_save_path(),'DataObject_'); + + $fh = fopen($tmpname, "w"); + fputs($fh,$out); + fclose($fh); + $perms = file_exists($outfilename) ? fileperms($outfilename) : 0755; + + // windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy.. + if (!@rename($tmpname, $outfilename)) { + unlink($outfilename); + rename($tmpname, $outfilename); + } + + chmod($outfilename, $perms); + } + //echo $out; + } + + /** + * class being extended (can be overridden by [DB_DataObject_Generator] extends=xxxx + * + * @var string + * @access private + */ + var $_extends = 'DB_DataObject'; + + /** + * line to use for require('DB/DataObject.php'); + * + * @var string + * @access private + */ + var $_extendsFile = "DB/DataObject.php"; + + /** + * class being generated + * + * @var string + * @access private + */ + var $_className; + + /** + * The table class geneation part - single file. + * + * @access private + * @return none + */ + function _generateClassTable($input = '') + { + // title = expand me! + $foot = ""; + $head = "<?php\n/**\n * Table Definition for {$this->table}\n"; + $head .= $this->derivedHookPageLevelDocBlock(); + $head .= " */\n"; + $head .= $this->derivedHookExtendsDocBlock(); + + + // requires + $head .= "require_once '{$this->_extendsFile}';\n\n"; + // add dummy class header in... + // class + $head .= $this->derivedHookClassDocBlock(); + $head .= "class {$this->classname} extends {$this->_extends} \n{"; + + $body = "\n ###START_AUTOCODE\n"; + $body .= " /* the code below is auto generated do not remove the above tag */\n\n"; + // table + $padding = (30 - strlen($this->table)); + $padding = ($padding < 2) ? 2 : $padding; + + $p = str_repeat(' ',$padding) ; + + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + + + $var = (substr(phpversion(),0,1) > 4) ? 'public' : 'var'; + $var = !empty($options['generator_var_keyword']) ? $options['generator_var_keyword'] : $var; + + + $body .= " {$var} \$__table = '{$this->table}'; {$p}// table name\n"; + + + // if we are using the option database_{databasename} = dsn + // then we should add var $_database = here + // as database names may not always match.. + + + + + if (isset($options["database_{$this->_database}"])) { + $body .= " {$var} \$_database = '{$this->_database}'; {$p}// database name (used with database_{*} config)\n"; + } + + + if (!empty($options['generator_novars'])) { + $var = '//'.$var; + } + + $defs = $this->_definitions[$this->table]; + + // show nice information! + $connections = array(); + $sets = array(); + foreach($defs as $t) { + if (!strlen(trim($t->name))) { + continue; + } + if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) { + echo "*****************************************************************\n". + "** WARNING COLUMN NAME UNUSABLE **\n". + "** Found column '{$t->name}', of type '{$t->type}' **\n". + "** Since this column name can't be converted to a php variable **\n". + "** name, and the whole idea of mapping would result in a mess **\n". + "** This column has been ignored... **\n". + "*****************************************************************\n"; + continue; + } + + + $padding = (30 - strlen($t->name)); + if ($padding < 2) $padding =2; + $p = str_repeat(' ',$padding) ; + + $body .=" {$var} \${$t->name}; {$p}// {$t->type}({$t->len}) {$t->flags}\n"; + + // can not do set as PEAR::DB table info doesnt support it. + //if (substr($t->Type,0,3) == "set") + // $sets[$t->Field] = "array".substr($t->Type,3); + $body .= $this->derivedHookVar($t,$padding); + } + + // THIS IS TOTALLY BORKED old FC creation + // IT WILL BE REMOVED!!!!! in DataObjects 1.6 + // grep -r __clone * to find all it's uses + // and replace them with $x = clone($y); + // due to the change in the PHP5 clone design. + + if ( substr(phpversion(),0,1) < 5) { + $body .= "\n"; + $body .= " /* ZE2 compatibility trick*/\n"; + $body .= " function __clone() { return \$this;}\n"; + } + + // simple creation tools ! (static stuff!) + $body .= "\n"; + $body .= " /* Static get */\n"; + $body .= " function staticGet(\$k,\$v=NULL) { return DB_DataObject::staticGet('{$this->classname}',\$k,\$v); }\n"; + + // generate getter and setter methods + $body .= $this->_generateGetters($input); + $body .= $this->_generateSetters($input); + + /* + theoretically there is scope here to introduce 'list' methods + based up 'xxxx_up' column!!! for heiracitcal trees.. + */ + + // set methods + //foreach ($sets as $k=>$v) { + // $kk = strtoupper($k); + // $body .=" function getSets{$k}() { return {$v}; }\n"; + //} + + if (!empty($options['generator_no_ini'])) { + $def = $this->_generateDefinitionsTable(); // simplify this!? + $body .= $this->_generateTableFunction($def['table']); + $body .= $this->_generateKeysFunction($def['keys']); + $body .= $this->_generateSequenceKeyFunction($def); + $body .= $this->_generateDefaultsFunction($this->table, $def['table']); + } else if (!empty($options['generator_add_defaults'])) { + // I dont really like doing it this way (adding another option) + // but it helps on older projects. + $def = $this->_generateDefinitionsTable(); // simplify this!? + $body .= $this->_generateDefaultsFunction($this->table,$def['table']); + + } + $body .= $this->derivedHookFunctions($input); + + $body .= "\n /* the code above is auto generated do not remove the tag below */"; + $body .= "\n ###END_AUTOCODE\n"; + + + // stubs.. + + if (!empty($options['generator_add_validate_stubs'])) { + foreach($defs as $t) { + if (!strlen(trim($t->name))) { + continue; + } + $validate_fname = 'validate' . $this->getMethodNameFromColumnName($t->name); + // dont re-add it.. + if (preg_match('/\s+function\s+' . $validate_fname . '\s*\(/i', $input)) { + continue; + } + $body .= "\n function {$validate_fname}()\n {\n return false;\n }\n"; + } + } + + + + + $foot .= "}\n"; + $full = $head . $body . $foot; + + if (!$input) { + return $full; + } + if (!preg_match('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n)/s',$input)) { + return $full; + } + if (!preg_match('/(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',$input)) { + return $full; + } + + + /* this will only replace extends DB_DataObject by default, + unless use set generator_class_rewrite to ANY or a name*/ + + $class_rewrite = 'DB_DataObject'; + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + if (empty($options['generator_class_rewrite']) || !($class_rewrite = $options['generator_class_rewrite'])) { + $class_rewrite = 'DB_DataObject'; + } + if ($class_rewrite == 'ANY') { + $class_rewrite = '[a-z_]+'; + } + + $input = preg_replace( + '/(\n|\r\n)class\s*[a-z0-9_]+\s*extends\s*' .$class_rewrite . '\s*(\n|\r\n)\{(\n|\r\n)/si', + "\nclass {$this->classname} extends {$this->_extends} \n{\n", + $input); + + $ret = preg_replace( + '/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', + $body,$input); + + if (!strlen($ret)) { + return PEAR::raiseError( + "PREG_REPLACE failed to replace body, - you probably need to set these in your php.ini\n". + "pcre.backtrack_limit=1000000\n". + "pcre.recursion_limit=1000000\n" + ,null, PEAR_ERROR_DIE); + } + + return $ret; + } + + /** + * hook to add extra methods to all classes + * + * called once for each class, use with $this->table and + * $this->_definitions[$this->table], to get data out of the current table, + * use it to add extra methods to the default classes. + * + * @access public + * @return string added to class eg. functions. + */ + function derivedHookFunctions($input = "") + { + // This is so derived generator classes can generate functions + // It MUST NOT be changed here!!! + return ""; + } + + /** + * hook for var lines + * called each time a var line is generated, override to add extra var + * lines + * + * @param object t containing type,len,flags etc. from tableInfo call + * @param int padding number of spaces + * @access public + * @return string added to class eg. functions. + */ + function derivedHookVar(&$t,$padding) + { + // This is so derived generator classes can generate variabels + // It MUST NOT be changed here!!! + return ""; + } + + /** + * hook to add extra page-level (in terms of phpDocumentor) DocBlock + * + * called once for each class, use it add extra page-level docs + * @access public + * @return string added to class eg. functions. + */ + function derivedHookPageLevelDocBlock() { + return ''; + } + + /** + * hook to add extra doc block (in terms of phpDocumentor) to extend string + * + * called once for each class, use it add extra comments to extends + * string (require_once...) + * @access public + * @return string added to class eg. functions. + */ + function derivedHookExtendsDocBlock() { + return ''; + } + + /** + * hook to add extra class level DocBlock (in terms of phpDocumentor) + * + * called once for each class, use it add extra comments to class + * string (require_once...) + * @access public + * @return string added to class eg. functions. + */ + function derivedHookClassDocBlock() { + return ''; + } + + /** + + /** + * getProxyFull - create a class definition on the fly and instantate it.. + * + * similar to generated files - but also evals the class definitoin code. + * + * + * @param string database name + * @param string table name of table to create proxy for. + * + * + * @return object Instance of class. or PEAR Error + * @access public + */ + function getProxyFull($database,$table) + { + + if ($err = $this->fillTableSchema($database,$table)) { + return $err; + } + + + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + $class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix']; + + if ($extends = @$options['extends']) { + $this->_extends = $extends; + $this->_extendsFile = $options['extends_location']; + } + $classname = $this->classname = $this->getClassNameFromTableName($this->table); + + $out = $this->_generateClassTable(); + //echo $out; + eval('?>'.$out); + return new $classname; + + } + + /** + * fillTableSchema - set the database schema on the fly + * + * + * + * @param string database name + * @param string table name of table to create schema info for + * + * @return none | PEAR::error() + * @access public + */ + function fillTableSchema($database,$table) + { + global $_DB_DATAOBJECT; + // a little bit of sanity testing. + if ((false !== strpos($database,"'")) || (false !== strpos($database,";"))) { + return PEAR::raiseError("Error: Database name contains a quote or semi-colon", null, PEAR_ERROR_DIE); + } + + $this->_database = $database; + + $this->_connect(); + $table = trim($table); + + // a little bit of sanity testing. + if ((false !== strpos($table,"'")) || (false !== strpos($table,";"))) { + return PEAR::raiseError("Error: Table contains a quote or semi-colon", null, PEAR_ERROR_DIE); + } + $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5]; + + + $options = PEAR::getStaticProperty('DB_DataObject','options'); + $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver']; + $is_MDB2 = ($db_driver != 'DB') ? true : false; + + if (!$is_MDB2) { + // try getting a list of schema tables first. (postgres) + $__DB->expectError(DB_ERROR_UNSUPPORTED); + $this->tables = $__DB->getListOf('schema.tables'); + $__DB->popExpect(); + } else { + /** + * set portability and some modules to fetch the informations + */ + $__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE); + $__DB->loadModule('Manager'); + $__DB->loadModule('Reverse'); + } + $quotedTable = !empty($options['quote_identifiers']) ? + $__DB->quoteIdentifier($table) : $table; + + if (!$is_MDB2) { + $defs = $__DB->tableInfo($quotedTable); + } else { + $defs = $__DB->reverse->tableInfo($quotedTable); + foreach ($defs as $k => $v) { + if (!isset($defs[$k]['length'])) { + continue; + } + $defs[$k]['len'] = $defs[$k]['length']; + } + } + + + + + if (PEAR::isError($defs)) { + return $defs; + } + if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) { + $this->debug("getting def for $database/$table",'fillTable'); + $this->debug(print_r($defs,true),'defs'); + } + // cast all definitions to objects - as we deal with that better. + + + foreach($defs as $def) { + if (is_array($def)) { + $this->_definitions[$table][] = (object) $def; + } + } + + $this->table = trim($table); + $ret = $this->_generateDefinitionsTable(); + + $_DB_DATAOBJECT['INI'][$database][$table] = $ret['table']; + $_DB_DATAOBJECT['INI'][$database][$table.'__keys'] = $ret['keys']; + return false; + + } + + /** + * Generate getter methods for class definition + * + * @param string $input Existing class contents + * @return string + * @access public + */ + function _generateGetters($input) + { + + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + $getters = ''; + + // only generate if option is set to true + if (empty($options['generate_getters'])) { + return ''; + } + + // remove auto-generated code from input to be able to check if the method exists outside of the auto-code + $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input); + + $getters .= "\n\n"; + $defs = $this->_definitions[$this->table]; + + // loop through properties and create getter methods + foreach ($defs = $defs as $t) { + + // build mehtod name + $methodName = 'get' . $this->getMethodNameFromColumnName($t->name); + + if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) { + continue; + } + + $getters .= " /**\n"; + $getters .= " * Getter for \${$t->name}\n"; + $getters .= " *\n"; + $getters .= (stristr($t->flags, 'multiple_key')) ? " * @return object\n" + : " * @return {$t->type}\n"; + $getters .= " * @access public\n"; + $getters .= " */\n"; + $getters .= (substr(phpversion(),0,1) > 4) ? ' public ' + : ' '; + $getters .= "function $methodName() {\n"; + $getters .= " return \$this->{$t->name};\n"; + $getters .= " }\n\n"; + } + + + return $getters; + } + + + /** + * Generate setter methods for class definition + * + * @param string Existing class contents + * @return string + * @access public + */ + function _generateSetters($input) + { + + $options = &PEAR::getStaticProperty('DB_DataObject','options'); + $setters = ''; + + // only generate if option is set to true + if (empty($options['generate_setters'])) { + return ''; + } + + // remove auto-generated code from input to be able to check if the method exists outside of the auto-code + $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input); + + $setters .= "\n"; + $defs = $this->_definitions[$this->table]; + + // loop through properties and create setter methods + foreach ($defs = $defs as $t) { + + // build mehtod name + $methodName = 'set' . $this->getMethodNameFromColumnName($t->name); + + if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) { + continue; + } + + $setters .= " /**\n"; + $setters .= " * Setter for \${$t->name}\n"; + $setters .= " *\n"; + $setters .= " * @param mixed input value\n"; + $setters .= " * @access public\n"; + $setters .= " */\n"; + $setters .= (substr(phpversion(),0,1) > 4) ? ' public ' + : ' '; + $setters .= "function $methodName(\$value) {\n"; + $setters .= " \$this->{$t->name} = \$value;\n"; + $setters .= " }\n\n"; + } + + + return $setters; + } + /** + * Generate table Function - used when generator_no_ini is set. + * + * @param array table array. + * @return string + * @access public + */ + function _generateTableFunction($def) + { + $defines = explode(',','INT,STR,DATE,TIME,BOOL,TXT,BLOB,NOTNULL,MYSQLTIMESTAMP'); + + $ret = "\n" . + " function table()\n" . + " {\n" . + " return array(\n"; + + foreach($def as $k=>$v) { + $str = '0'; + foreach($defines as $dn) { + if ($v & constant('DB_DATAOBJECT_' . $dn)) { + $str .= ' + DB_DATAOBJECT_' . $dn; + } + } + if (strlen($str) > 1) { + $str = substr($str,3); // strip the 0 + + } + // hopefully addslashes is good enough here!!! + $ret .= ' \''.addslashes($k).'\' => ' . $str . ",\n"; + } + return $ret . " );\n" . + " }\n"; + + + + } + /** + * Generate keys Function - used generator_no_ini is set. + * + * @param array keys array. + * @return string + * @access public + */ + function _generateKeysFunction($def) + { + + $ret = "\n" . + " function keys()\n" . + " {\n" . + " return array("; + + foreach($def as $k=>$type) { + // hopefully addslashes is good enough here!!! + $ret .= '\''.addslashes($k).'\', '; + } + $ret = preg_replace('#, $#', '', $ret); + return $ret . ");\n" . + " }\n"; + + + + } + /** + * Generate sequenceKey Function - used generator_no_ini is set. + * + * @param array table and key definition. + * @return string + * @access public + */ + function _generateSequenceKeyFunction($def) + { + + //print_r($def); + // DB_DataObject::debugLevel(5); + global $_DB_DATAOBJECT; + // print_r($def); + + + $dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype']; + $realkeys = $def['keys']; + $keys = array_keys($realkeys); + $usekey = isset($keys[0]) ? $keys[0] : false; + $table = $def['table']; + + + $seqname = false; + + + + + $ar = array(false,false,false); + if ($usekey !== false) { + if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) { + $usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table]; + if (strpos($usekey,':') !== false) { + list($usekey,$seqname) = explode(':',$usekey); + } + } + + if (in_array($dbtype , array( 'mysql', 'mysqli', 'mssql', 'ifx')) && + ($table[$usekey] & DB_DATAOBJECT_INT) && + isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N') + ) { + // use native sequence keys. + $ar = array($usekey,true,$seqname); + } else { + // use generated sequence keys.. + if ($table[$usekey] & DB_DATAOBJECT_INT) { + $ar = array($usekey,false,$seqname); + } + } + } + + + + + $ret = "\n" . + " function sequenceKey() // keyname, use native, native name\n" . + " {\n" . + " return array("; + foreach($ar as $v) { + switch (gettype($v)) { + case 'boolean': + $ret .= ($v ? 'true' : 'false') . ', '; + break; + + case 'string': + $ret .= "'" . $v . "', "; + break; + + default: // eak + $ret .= "null, "; + + } + } + $ret = preg_replace('#, $#', '', $ret); + return $ret . ");\n" . + " }\n"; + + } + /** + * Generate defaults Function - used generator_add_defaults or generator_no_ini is set. + * Only supports mysql and mysqli ... welcome ideas for more.. + * + * + * @param array table and key definition. + * @return string + * @access public + */ + function _generateDefaultsFunction($table,$defs) + { + $__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5]; + if (!in_array($__DB->phptype, array('mysql','mysqli'))) { + return; // cant handle non-mysql introspection for defaults. + } + $options = PEAR::getStaticProperty('DB_DataObject','options'); + $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver']; + $method = $db_driver == 'DB' ? 'getAll' : 'queryAll'; + $res = $__DB->$method('DESCRIBE ' . $table,DB_FETCHMODE_ASSOC); + $defaults = array(); + foreach($res as $ar) { + // this is initially very dumb... -> and it may mess up.. + $type = $defs[$ar['Field']]; + + switch (true) { + + case (is_null( $ar['Default'])): + $defaults[$ar['Field']] = 'null'; + break; + + case ($type & DB_DATAOBJECT_DATE): + case ($type & DB_DATAOBJECT_TIME): + case ($type & DB_DATAOBJECT_MYSQLTIMESTAMP): // not supported yet.. + break; + + case ($type & DB_DATAOBJECT_BOOL): + $defaults[$ar['Field']] = (int)(boolean) $ar['Default']; + break; + + + case ($type & DB_DATAOBJECT_STR): + $defaults[$ar['Field']] = "'" . addslashes($ar['Default']) . "'"; + break; + + + default: // hopefully eveything else... - numbers etc. + if (!strlen($ar['Default'])) { + continue; + } + if (is_numeric($ar['Default'])) { + $defaults[$ar['Field']] = $ar['Default']; + } + break; + + } + //var_dump(array($ar['Field'], $ar['Default'], $defaults[$ar['Field']])); + } + if (empty($defaults)) { + return; + } + + $ret = "\n" . + " function defaults() // column default values \n" . + " {\n" . + " return array(\n"; + foreach($defaults as $k=>$v) { + $ret .= ' \''.addslashes($k).'\' => ' . $v . ",\n"; + } + return $ret . " );\n" . + " }\n"; + + + + + } + + + + + +} diff --git a/extlib/DB/DataObject/createTables.php b/extlib/DB/DataObject/createTables.php new file mode 100755 index 000000000..c0659574e --- /dev/null +++ b/extlib/DB/DataObject/createTables.php @@ -0,0 +1,59 @@ +#!/usr/bin/php -q +<?php +// +----------------------------------------------------------------------+ +// | PHP Version 4 | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1997-2003 The PHP Group | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 2.02 of the PHP license, | +// | that is bundled with this package in the file LICENSE, and is | +// | available at through the world-wide-web at | +// | http://www.php.net/license/2_02.txt. | +// | If you did not receive a copy of the PHP license and are unable to | +// | obtain it through the world-wide-web, please send a note to | +// | license@php.net so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Author: Alan Knowles <alan@akbkhome.com> +// +----------------------------------------------------------------------+ +// +// $Id: createTables.php,v 1.24 2006/01/13 01:27:55 alan_k Exp $ +// + +// since this version doesnt use overload, +// and I assume anyone using custom generators should add this.. + +define('DB_DATAOBJECT_NO_OVERLOAD',1); + +//require_once 'DB/DataObject/Generator.php'; +require_once 'DB/DataObject/Generator.php'; + +if (!ini_get('register_argc_argv')) { + PEAR::raiseError("\nERROR: You must turn register_argc_argv On in you php.ini file for this to work\neg.\n\nregister_argc_argv = On\n\n", null, PEAR_ERROR_DIE); + exit; +} + +if (!@$_SERVER['argv'][1]) { + PEAR::raiseError("\nERROR: createTable.php usage:\n\nC:\php\pear\DB\DataObjects\createTable.php example.ini\n\n", null, PEAR_ERROR_DIE); + exit; +} + +$config = parse_ini_file($_SERVER['argv'][1], true); +foreach($config as $class=>$values) { + $options = &PEAR::getStaticProperty($class,'options'); + $options = $values; +} + + +$options = &PEAR::getStaticProperty('DB_DataObject','options'); +if (empty($options)) { + PEAR::raiseError("\nERROR: could not read ini file\n\n", null, PEAR_ERROR_DIE); + exit; +} +set_time_limit(0); + +// use debug level from file if set.. +DB_DataObject::debugLevel(isset($options['debug']) ? $options['debug'] : 1); + +$generator = new DB_DataObject_Generator; +$generator->start(); + |