Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/*** Generation tools for DB_DataObject** PHP versions 4 and 5** LICENSE: This source file is subject to version 3.01 of the PHP license* that is available through the world-wide-web at the following URI:* http://www.php.net/license/3_01.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_01.txt PHP License 3.01* @version CVS: $Id: Generator.php 315531 2011-08-26 02:21:29Z alan_k $* @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]* ; 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_object($__DB) && 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_options = PEAR::getStaticProperty('MDB2','options');if (empty($db_options)) {$__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);}$__DB->loadModule('Manager');$__DB->loadModule('Reverse');}if ((empty($this->tables) || (is_object($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_object($this->tables) && 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_object($views) && 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 theif (!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.}if (is_object($defs) && 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;}// rename the length value, so it matches db's return.if (isset($def['length']) && !isset($def['len'])) {$def['len'] = $def['length'];}$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', 'pgsql'))) {echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";return; // cant handle non-mysql introspection for defaults.}$DB = $this->getDatabaseConnection();$fk = array();switch ($DB->phptype) {case 'pgsql':foreach($this->tables as $this->table) {$quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($table) : $this->table;$res =& $DB->query("SELECTpg_catalog.pg_get_constraintdef(r.oid, true) AS condefFROM pg_catalog.pg_constraint r,pg_catalog.pg_class cWHERE c.oid=r.conrelidAND r.contype = 'f'AND c.relname = '" . $quotedTable . "'");if (PEAR::isError($res)) {die($res->getMessage());}while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {$treffer = array();// this only picks up one of these.. see this for why: http://pear.php.net/bugs/bug.php?id=17049preg_match("/FOREIGN KEY \((\w*)\) REFERENCES (\w*)\((\w*)\)/i",$row['condef'],$treffer);if (!count($treffer)) {continue;}$fk[$this->table][$treffer[1]] = $treffer[2] . ":" . $treffer[3];}}break;case 'mysql':case 'mysqli':default:foreach($this->tables as $this->table) {$quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($table) : $this->table;$res =& $DB->query('SHOW CREATE TABLE ' . $quotedTable );if (PEAR::isError($res)) {die($res->getMessage());}$text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);$treffer = array();// Extract FOREIGN KEYSpreg_match_all("/FOREIGN KEY \(`(\w*)`\) REFERENCES `(\w*)` \(`(\w*)`\)/i",$text[1],$treffer,PREG_SET_ORDER);if (!count($treffer)) {continue;}foreach($treffer as $i=> $tref) {$fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[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;$options = PEAR::getStaticProperty('DB_DataObject','options');$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': // postgrescase 'INT4': // postgrescase 'INT8': // postgrescase 'SERIAL4': // postgrescase 'SERIAL8': // postgrescase '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 otherscase '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 wellcase 'POINT': // mysql geometry stuff - not really string - but will do..case 'TIMESTAMPTZ': // postgrescase 'BPCHAR': // postgrescase 'INTERVAL': // postgres (eg. '12 days')case 'CIDR': // postgres IP net speccase 'INET': // postgres IPcase 'MACADDR': // postgress network Mac address.case 'INTEGER[]': // postgres typecase '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 'BLOB': /// these should really be ignored!!!???case 'TINYBLOB':case 'MEDIUMBLOB':case 'LONGBLOB':case 'CLOB': // oracle character lob supportcase '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";$secondary_key_match = isset($options['generator_secondary_key_match']) ? $options['generator_secondary_key_match'] : 'primary|unique';if (preg_match('/(auto_increment|nextval\()/i',rawurldecode($t->flags))|| (isset($t->autoincrement) && ($t->autoincrement === true))) {// native sequences = 2if ($write_ini) {$keys_out_primary .= "{$t->name} = N\n";}$ret_keys_primary[$t->name] = 'N';} else if ($secondary_key_match && preg_match('/('.$secondary_key_match.')/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');$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];$this->_extendsFile = empty($options['extends_location']) ? $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] 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$p = str_repeat(' ',max(2, (18 - strlen($this->table)))) ;$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 (empty($GLOBALS['_DB_DATAOBJECT']['CONFIG'])) {DB_DataObject::_loadConfig();}// Only include the $_database property if the omit_database_var is unset or falseif (isset($options["database_{$this->_database}"]) && empty($GLOBALS['_DB_DATAOBJECT']['CONFIG']['generator_omit_database_var'])) {$p = str_repeat(' ', max(2, (16 - strlen($this->table))));$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;}$p = str_repeat(' ',max(2, (30 - strlen($t->name))));$length = empty($t->len) ? '' : '('.$t->len.')';$body .=" {$var} \${$t->name}; {$p}// {$t->type}$length {$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,strlen($p));}$body .= $this->derivedHookPostVar($defs);// 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' methodsbased 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 for after var lines (* called at the end of the output of var line have generated, override to add extra var* lines** @param array cols containing array of objects with type,len,flags etc. from tableInfo call* @access public* @return string added to class eg. functions.*/function derivedHookPostVar($t){// 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'];$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];$this->_extendsFile = empty($options['extends_location']) ? $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_tableinfo']) ?$__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 trueif (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 methodsforeach ($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 trueif (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 methodsforeach ($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";}}