cvs: pear /DB_Table package2.xml /DB_Table/DB/Table Generator.php
| From: | Mark Wiesemann | Date: | Mon, 10 Dec 2007 22:03:00 +0000 |
| Subject: | cvs: pear /DB_Table package2.xml /DB_Table/DB/Table Generator.php | ||
| Groups: | php.pear.cvs | ||
| Request: | Send a blank email to pear-cvs+get-49708@lists.php.net to get a copy of this message | ||
wiesemann Mon Dec 10 22:03:00 2007 UTC
Modified files:
/pear/DB_Table package2.xml
/pear/DB_Table/DB/Table Generator.php
Log:
- Request #12510: various optimizations (mostly coding standards) and corrections (missing error
return call, change MDB2's portability mode only temporarily)
(thanks to Carsten Wiedmann for the patch)
http://cvs.php.net/viewvc.cgi/pear/DB_Table/package2.xml?r1=1.34&r2=1.35&diff_format=u Index: pear/DB_Table/package2.xml diff -u pear/DB_Table/package2.xml:1.34 pear/DB_Table/package2.xml:1.35 --- pear/DB_Table/package2.xml:1.34 Thu Nov 22 10:38:02 2007 +++ pear/DB_Table/package2.xml Mon Dec 10 22:02:59 2007 @@ -59,6 +59,10 @@ (thanks to Gregor Gramlich) - Bug #12497: Generator works only with DB, but not with MDB2 (thanks to Carsten Wiedmann for the patch) +- Request #12510: various optimizations (mostly coding standards) and + corrections (missing error return call, change MDB2's portability mode only + temporarily) + (thanks to Carsten Wiedmann for the patch) </notes> <contents> <dir name="/"> @@ -73,7 +77,9 @@ <file name="Manager.php" role="php" /> <file name="QuickForm.php" role="php" /> <file name="Valid.php" role="php" /> - <file name="Generator.php" role="php" /> + <file name="Generator.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info" /> + </file> <file name="XML.php" role="php" /> <dir name="Manager"> <file name="ibase.php" role="php" /> http://cvs.php.net/viewvc.cgi/pear/DB_Table/DB/Table/Generator.php?r1=1.12&r2=1.13&diff_format=u Index: pear/DB_Table/DB/Table/Generator.php diff -u pear/DB_Table/DB/Table/Generator.php:1.12 pear/DB_Table/DB/Table/Generator.php:1.13 --- pear/DB_Table/DB/Table/Generator.php:1.12 Thu Nov 22 10:38:02 2007 +++ pear/DB_Table/DB/Table/Generator.php Mon Dec 10 22:02:59 2007 @@ -10,11 +10,15 @@ * @package DB_Table * @author David C. Morse <morse@php.net> * @license http://www.gnu.org/copyleft/lesser.html LGPL - * @version $Id: Generator.php,v 1.12 2007/11/22 10:38:02 wiesemann Exp $ + * @version CVS: $Id: Generator.php,v 1.13 2007/12/10 22:02:59 wiesemann Exp $ + * @link http://pear.php.net/package/DB_Table */ // {{{ Includes +/**#@+ + * Include basic classes + */ /** * The PEAR class (used for errors) */ @@ -29,10 +33,14 @@ * DB_Table_Manager class (used to reverse engineer indices) */ require_once 'DB/Table/Manager.php'; +/**#@-*/ // }}} // {{{ Error code constants +/**#@+ + * Error codes + */ /** * Parameter is not a DB/MDB2 object */ @@ -43,17 +51,25 @@ */ define('DB_TABLE_GENERATOR_ERR_INDEX_COL', -302); +/** + * Error while creating file/directory + */ +define('DB_TABLE_GENERATOR_ERR_FILE', -303); +/**#@-*/ + // }}} // {{{ Error messages -/** - * US-English default error messages. +/** + * US-English default error messages. */ $GLOBALS['_DB_TABLE_GENERATOR']['default_error'] = array( DB_TABLE_GENERATOR_ERR_DB_OBJECT => - 'Invalid DB/MDB2 object parameter. Function', + 'Invalid DB/MDB2 object parameter. Function', DB_TABLE_GENERATOR_ERR_INDEX_COL => - 'Index column is not a valid column name. Index column' - ); + 'Index column is not a valid column name. Index column', + DB_TABLE_GENERATOR_ERR_FILE => + 'Can\'t create file/directory:' +); // merge default and user-defined error messages if (!isset($GLOBALS['_DB_TABLE_GENERATOR']['error'])) { @@ -73,32 +89,32 @@ * * This class generates the php code necessary to use the DB_Table * package to interact with an existing database. This requires the - * generation of a skeleton subclass definition be generated for each + * generation of a skeleton subclass definition be generated for each * table in the database, in which the $col, $idx, and $auto_inc_col * properties are constructed using a table schema that is obtained - * by querying the database. + * by querying the database. * * The class can also generate a file, named 'Database.php' by default, * that includes (require_once) each of the table subclass definitions, - * instantiates one object of each DB_Table subclass (i.e., one object - * for each table), instantiates a parent DB_Table_Database object, - * adds all the tables to that parent, attempts to guess foreign key + * instantiates one object of each DB_Table subclass (i.e., one object + * for each table), instantiates a parent DB_Table_Database object, + * adds all the tables to that parent, attempts to guess foreign key * relationships between tables based on the column names, and adds * the inferred references to the parent object. * * All of the code is written to a directory whose path is given by * the property $class_write_path. By default, this is the current - * directory. By default, the name of the class constructed for a - * table named 'thing' is "Thing_Table". That is, the class name is - * the table name, with the first letter upper case, with a suffix - * '_Table'. This suffix can be changed by setting the $class_suffix - * property. The file containing a subclass definition is the - * subclass name with a php extension, e.g., 'Thing_Table.php'. The - * object instantiated from that subclass is the same as the table + * directory. By default, the name of the class constructed for a + * table named 'thing' is "Thing_Table". That is, the class name is + * the table name, with the first letter upper case, with a suffix + * '_Table'. This suffix can be changed by setting the $class_suffix + * property. The file containing a subclass definition is the + * subclass name with a php extension, e.g., 'Thing_Table.php'. The + * object instantiated from that subclass is the same as the table * name, with no suffix, e.g., 'thing'. - * - * To generate the code for all of the tables in a database named - * $database, instantiate a MDB2 or DB object named $db that connects + * + * To generate the code for all of the tables in a database named + * $database, instantiate a MDB2 or DB object named $db that connects * to the database of interest, and execute the following code: * <code> * $generator = DB_Table_Generator($db, $database); @@ -106,20 +122,20 @@ * $generator->generateTableClassFiles(); * $generator->generateDatabaseFile(); * </code> - * Here $class_write_path should be the path (without a trailing - * separator) to a directory in which all of the code should be - * written. If this directory does not exist, it will be created. - * If the directory does already exist, exising files will not + * Here $class_write_path should be the path (without a trailing + * separator) to a directory in which all of the code should be + * written. If this directory does not exist, it will be created. + * If the directory does already exist, exising files will not * be overwritten. If $class_write_path is not set (i.e., if this - * line is omitted) all the code will be written to the current - * directory. If ->generateDatabaseFile() is called, it must be - * called after ->generateTableClassFiles(). + * line is omitted) all the code will be written to the current + * directory. If ->generateDatabaseFile() is called, it must be + * called after ->generateTableClassFiles(). * * By default, ->generateTableClassFiles() and ->generateDatabaseFiles() - * generate code for all of the tables in the current database. To - * generate code for a specified list of tables, set the value of the - * public $tables property to a sequential list of table names before - * calling either of these methods. Code can be generated for three + * generate code for all of the tables in the current database. To + * generate code for a specified list of tables, set the value of the + * public $tables property to a sequential list of table names before + * calling either of these methods. Code can be generated for three * tables named 'table1', 'table2', and 'table3' as follows: * <code> * $generator = DB_Table_Generator($db, $database); @@ -128,36 +144,37 @@ * $generator->generateTableClassFiles(); * $generator->generateDatabaseFile(); * </code> - * If the $tables property is not set to a non-null value prior - * to calling ->generateTableClassFiles() then, by default, the + * If the $tables property is not set to a non-null value prior + * to calling ->generateTableClassFiles() then, by default, the * database is queried for a list of all table names, by calling the * ->getTableNames() method from within ->generateTableClassFiles(). - * + * * PHP version 4 and 5 * * @category Database * @package DB_Table * @author David C. Morse <morse@php.net> * @license http://www.gnu.org/copyleft/lesser.html LGPL - * @version $Id: Generator.php,v 1.12 2007/11/22 10:38:02 wiesemann Exp $ + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB_Table */ class DB_Table_Generator { - // {{{ properties + // {{{ Properties /** * Name of the database * - * @var string + * @var string * @access public */ - var $name = null; + var $name = null; /** * The PEAR DB/MDB2 object that connects to the database. * - * @var object + * @var object * @access private */ var $db = null; @@ -165,7 +182,7 @@ /** * The backend type. May have values 'db' or 'mdb2' * - * @var string + * @var string * @access private */ var $backend = null; @@ -177,13 +194,13 @@ * at instantiation time. To check if there was an instantiation error... * * <code> - * $obj =& new DB_Table_Generator(); - * if ($obj->error) { - * // ... error handling code here ... - * } + * $obj =& new DB_Table_Generator(); + * if ($obj->error) { + * // ... error handling code here ... + * } * </code> * - * @var object PEAR_Error + * @var object PEAR_Error * @access public */ var $error = null; @@ -191,7 +208,7 @@ /** * Numerical array of table name strings * - * @var array + * @var array * @access public */ var $tables = array(); @@ -199,15 +216,15 @@ /** * Class being extended (DB_Table or generic subclass) * - * @var string + * @var string * @access public */ var $extends = 'DB_Table'; /** - * Path to definition of the class $this->extends + * Path to definition of the class $this->extends * - * @var string + * @var string * @access public */ var $extends_file = 'DB/Table.php'; @@ -215,7 +232,7 @@ /** * Suffix to add to table names to obtain corresponding class names * - * @var string + * @var string * @access public */ var $class_suffix = "_Table"; @@ -225,7 +242,7 @@ * * Value should not include a trailing "/". * - * @var string + * @var string * @access public */ var $class_write_path = ''; @@ -235,11 +252,11 @@ * * Used to create require_once statements in the Database.php file, * which is in the same directory as the class definition files. Leave - * as empty string if your PHP include_path contains ".". The value - * should not include a trailing "/", which is added automatically + * as empty string if your PHP include_path contains ".". The value + * should not include a trailing "/", which is added automatically * to values other than the empty string. * - * @var string + * @var string * @access public */ var $class_include_path = ''; @@ -248,22 +265,22 @@ * Array of column definitions * * Array $this->col[table_name][column_name] = column definition. - * Column definition is an array with the same format as the $col + * Column definition is an array with the same format as the $col * property of a DB_Table object * - * @var array + * @var array * @access public */ - var $col = array(); + var $col = array(); /** * Array of index/constraint definitions. * - * Array $this->idx[table_table][index_name] = Index definition. + * Array $this->idx[table_table][index_name] = Index definition. * The index definition is an array with the same format as the * DB_Table $idx property property array. * - * @var array + * @var array * @access public */ var $idx = array(); @@ -273,7 +290,7 @@ * * Array $this->auto_inc_col[table_name] = auto-increment column * - * @var array + * @var array * @access public */ var $auto_inc_col = array(); @@ -281,23 +298,25 @@ /** * Array of primary keys * - * @var array + * @var array * @access public */ var $primary_key = array(); /** - * MDB2 'idxname_format' option, format of index names + * MDB2 'idxname_format' option, format of index names * * For use in printf() formatting. Use '%s' to use index names as * returned by getTableConstraints/Indexes, and '%s_idx' to add an - * '_idx' suffix. For MySQL, use the default value '%'. + * '_idx' suffix. For MySQL, use the default value '%'. + * + * @var string + * @access public */ var $idxname_format = '%s'; -// }}} - -// {{{ function DB_Table_Generator(&$db, $name) + // }}} + // {{{ function DB_Table_Generator(&$db, $name) /** * Constructor @@ -305,10 +324,11 @@ * If an error is encountered during instantiation, the error * message is stored in the $this->error property of the resulting * object. See $error property docblock for a discussion of error - * handling. - * - * @param object $db DB/MDB2 database connection object - * @param string $name database name string + * handling. + * + * @param object &$db DB/MDB2 database connection object + * @param string $name database name string + * * @return object DB_Table_Generator * @access public */ @@ -320,28 +340,29 @@ } elseif (is_a($db, 'mdb2_driver_common')) { $this->backend = 'mdb2'; } else { - $this->error =& DB_Table_Generator::throwError( - DB_TABLE_GENERATOR_ERR_DB_OBJECT, - "DB_Table_Generator"); + $this->error =& + DB_Table_Generator::throwError(DB_TABLE_GENERATOR_ERR_DB_OBJECT, + 'DB_Table_Generator'); return; } - $this->db =& $db; - $this->name = $name; + $this->db =& $db; + $this->name = $name; } -// }}} -// {{{ function &throwError($code, $extra = null) + // }}} + // {{{ function &throwError($code, $extra = null) /** * Specialized version of throwError() modeled on PEAR_Error. - * - * Throws a PEAR_Error with a DB_Table_Generator error message based + * + * Throws a PEAR_Error with a DB_Table_Generator error message based * on a DB_Table_Generator constant error code. - * + * * @param string $code A DB_Table_Generator error code constant. - * @param string $extra Extra text for the error (in addition to the - * regular error message). + * @param string $extra Extra text for the error (in addition to the + * regular error message). + * * @return object PEAR_Error * @access public * @static @@ -349,38 +370,38 @@ function &throwError($code, $extra = null) { // get the error message text based on the error code - $text = 'DB_TABLE_GENERATOR ERROR - ' . "\n" . - $GLOBALS['_DB_TABLE_GENERATOR']['error'][$code]; - + $text = 'DB_TABLE_GENERATOR ERROR - ' . "\n" + . $GLOBALS['_DB_TABLE_GENERATOR']['error'][$code]; + // add any additional error text if ($extra) { $text .= ' ' . $extra; } - + // done! $error = PEAR::throwError($text, $code); return $error; } - -// }}} -// {{{ function setErrorMessage($code, $message = null) + + // }}} + // {{{ function setErrorMessage($code, $message = null) /** * Overwrites one or more error messages, e.g., to internationalize them. - * - * @param mixed $code If string, the error message with code $code will be - * overwritten by $message. If array, each key is a code - * and each value is a new message. - * + * + * @param mixed $code If string, the error message with code $code will be + * overwritten by $message. If array, each key is a + * code and each value is a new message. * @param string $message Only used if $key is not an array. + * * @return void * @access public */ - function setErrorMessage($code, $message = null) + function setErrorMessage($code, $message = null) { if (is_array($code)) { foreach ($code as $single_code => $single_message) { - $GLOBALS['_DB_TABLE_GENERATOR']['error'][$single_code] + $GLOBALS['_DB_TABLE_GENERATOR']['error'][$single_code] = $single_message; } } else { @@ -388,18 +409,18 @@ } } -// }}} -// {{{ function getTableNames() + // }}} + // {{{ function getTableNames() /** * Gets a list of tables from the database - * - * Upon successful completion, names are stored in the $this->tables - * array. If an error is encountered, a PEAR Error is returned, and - * $this->tables is reset to null. * - * @access public - * @return mixed true on success, PEAR Error on failure + * Upon successful completion, names are stored in the $this->tables + * array. If an error is encountered, a PEAR Error is returned, and + * $this->tables is reset to null. + * + * @return mixed true on success, PEAR Error on failure + * @access public */ function getTableNames() { @@ -416,56 +437,65 @@ $this->db->popExpect(); } } else { - $this->db->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE); + // Temporarily change 'portability' MDB2 option + $portability = $this->db->getOption('portability'); + $this->db->setOption('portability', + MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE); + $this->db->loadModule('Manager'); $this->db->loadModule('Reverse'); // Get list of tables $this->tables = $this->db->manager->listTables(); + // Restore original MDB2 'portability' + $db->setOption('portability', $portability); } if (PEAR::isError($this->tables)) { - $error = $this->tables; + $error = $this->tables; $this->tables = null; - return $error; + return $error; } else { return true; } } -// }}} -// {{{ function getTableDefinition($table) + // }}} + // {{{ function getTableDefinition($table) /** * Gets column and index definitions by querying database - * - * Upon return, column definitions are stored in $this->col[$table], + * + * Upon return, column definitions are stored in $this->col[$table], * and index definitions in $this->idx[$table]. * - * Calls DB/MDB2::tableInfo() for column definitions, and uses + * Calls DB/MDB2::tableInfo() for column definitions, and uses * the DB_Table_Manager class to obtain index definitions. * - * @param $table string name of table - * @return mixed true on success, PEAR Error on failure - * @access public + * @param string $table name of table + * + * @return mixed true on success, PEAR Error on failure + * @access public */ - function getTableDefinition($table) + function getTableDefinition($table) { - #// 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]; - # } - #} + /* + // 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]; + } + } + */ $db =& $this->db; if ($this->backend == 'db') { - $defs = $db->tableInfo($table); + $defs = $db->tableInfo($table); if (PEAR::isError($defs)) { return $defs; - } + } $this->columns[$table] = $defs; } else { @@ -478,10 +508,10 @@ $this->db->loadModule('Reverse'); // Columns - $defs = $db->reverse->tableInfo($table); + $defs = $db->reverse->tableInfo($table); if (PEAR::isError($defs)) { return $defs; - } + } // rename the 'length' key, so it matches db's return. foreach ($defs as $k => $v) { @@ -497,138 +527,138 @@ $db->setOption('idxname_format', $this->idxname_format); } - // Default - no auto increment column + // Default - no auto increment column $this->auto_inc_col[$table] = null; // Loop over columns to create $this->col[$table] $this->col[$table] = array(); - foreach($defs as $t) { + foreach ($defs as $t) { $name = $t['name']; $col = array(); - + switch (strtoupper($t['type'])) { - case 'INT2': // postgres - case 'TINYINT': - case 'TINY': //mysql - case 'SMALLINT': - $col['type'] = 'smallint'; - break; - case 'INT4': // postgres - case 'SERIAL4': // postgres - case 'INT': - case 'SHORT': // mysql - case 'INTEGER': - case 'MEDIUMINT': - case 'YEAR': - $col['type'] = 'integer'; - break; - case 'BIGINT': - case 'LONG': // mysql - case 'INT8': // postgres - case 'SERIAL8': // postgres - $col['type'] = 'bigint'; - break; - case 'REAL': - case 'NUMERIC': - case 'NUMBER': // oci8 - case 'FLOAT': // mysql - case 'FLOAT4': // real (postgres) - $col['type'] = 'single'; - break; - case 'DOUBLE': - case 'DOUBLE PRECISION': // double precision (firebird) - case 'FLOAT8': // double precision (postgres) - $col['type'] = 'double'; - break; - case 'DECIMAL': - case 'MONEY': // mssql and maybe others - $col['type'] = 'decimal'; - break; - case 'BIT': - case 'BOOL': - case 'BOOLEAN': - $col['type'] = 'boolean'; - break; - case 'STRING': - case 'CHAR': - $col['type'] = 'char'; - break; - case 'VARCHAR': - case 'VARCHAR2': - case 'TINYTEXT': - $col['type'] = 'varchar'; - break; - case 'TEXT': - case 'MEDIUMTEXT': - case 'LONGTEXT': - $col['type'] = 'clob'; - break; - case 'DATE': - $col['type'] = 'date'; - break; - case 'TIME': - $col['type'] = 'time'; - break; - case 'DATETIME': // mysql - case 'TIMESTAMP': - $col['type'] = 'timestamp'; - break; - 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 - $col['type'] = 'varchar'; - break; - default: - $col['type'] = $t['type'] . ' (Unknown type)'; - break; - } - - // Set length and scope if required - if (in_array($col['type'], array('char','varchar','decimal'))) { + case 'INT2': // postgres + case 'TINYINT': + case 'TINY': //mysql + case 'SMALLINT': + $col['type'] = 'smallint'; + break; + case 'INT4': // postgres + case 'SERIAL4': // postgres + case 'INT': + case 'SHORT': // mysql + case 'INTEGER': + case 'MEDIUMINT': + case 'YEAR': + $col['type'] = 'integer'; + break; + case 'BIGINT': + case 'LONG': // mysql + case 'INT8': // postgres + case 'SERIAL8': // postgres + $col['type'] = 'bigint'; + break; + case 'REAL': + case 'NUMERIC': + case 'NUMBER': // oci8 + case 'FLOAT': // mysql + case 'FLOAT4': // real (postgres) + $col['type'] = 'single'; + break; + case 'DOUBLE': + case 'DOUBLE PRECISION': // double precision (firebird) + case 'FLOAT8': // double precision (postgres) + $col['type'] = 'double'; + break; + case 'DECIMAL': + case 'MONEY': // mssql and maybe others + $col['type'] = 'decimal'; + break; + case 'BIT': + case 'BOOL': + case 'BOOLEAN': + $col['type'] = 'boolean'; + break; + case 'STRING': + case 'CHAR': + $col['type'] = 'char'; + break; + case 'VARCHAR': + case 'VARCHAR2': + case 'TINYTEXT': + $col['type'] = 'varchar'; + break; + case 'TEXT': + case 'MEDIUMTEXT': + case 'LONGTEXT': + $col['type'] = 'clob'; + break; + case 'DATE': + $col['type'] = 'date'; + break; + case 'TIME': + $col['type'] = 'time'; + break; + case 'DATETIME': // mysql + case 'TIMESTAMP': + $col['type'] = 'timestamp'; + break; + 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 + $col['type'] = 'varchar'; + break; + default: + $col['type'] = $t['type'] . ' (Unknown type)'; + break; + } + + // Set length and scope if required + if (in_array($col['type'], array('char','varchar','decimal'))) { if (isset($t['len'])) { $col['size'] = (int) $t['len']; - } elseif ($col['type'] == 'varchar') { + } elseif ($col['type'] == 'varchar') { $col['size'] = 255; // default length - } elseif ($col['type'] == 'char') { + } elseif ($col['type'] == 'char') { $col['size'] = 128; // default length - } elseif ($col['type'] == 'decimal') { - $col['size'] = 15; // default length + } elseif ($col['type'] == 'decimal') { + $col['size'] = 15; // default length } - if ($col['type'] == 'decimal') { - $col['scope'] = 2; + if ($col['type'] == 'decimal') { + $col['scope'] = 2; } } if (isset($t['notnull'])) { if ($t['notnull']) { - $col['require'] = true; + $col['require'] = true; } } if (isset($t['autoincrement'])) { $this->auto_inc_col[$table] = $name; } - if (isset($t['flags'])){ + if (isset($t['flags'])) { $flags = $t['flags']; - if (preg_match('/not[ _]null/i',$flags)) { + if (preg_match('/not[ _]null/i', $flags)) { $col['require'] = true; } if (preg_match("/(auto_increment|nextval\()/i", $flags)) { $this->auto_inc_col[$table] = $name; - } + } } $require = isset($col['require']) ? $col['require'] : false; if ($require) { if (isset($t['default'])) { $default = $t['default']; $type = $col['type']; - if (in_array($type, + if (in_array($type, array('smallint', 'integer', 'bigint'))) { $default = (int) $default; } elseif (in_array($type, array('single', 'double'))) { @@ -646,7 +676,7 @@ // Make array with lower case column array names as keys $col_lc = array(); foreach ($this->col[$table] as $name => $def) { - $name_lc = strtolower($name); + $name_lc = strtolower($name); $col_lc[$name_lc] = $name; } @@ -654,7 +684,7 @@ $DB_indexes = DB_Table_Manager::getIndexes($db, $table); if (PEAR::isError($DB_indexes)) { return $DB_indexes; - } + } // Check that index columns correspond to valid column names. // Try to correct problems with capitalization, if necessary. @@ -669,12 +699,12 @@ $field_lc = strtolower($field); if (isset($col_lc[$field_lc])) { $correct = $col_lc[$field_lc]; - $DB_indexes[$type][$name][$key] + $DB_indexes[$type][$name][$key] = $correct; } else { - $return =& DB_Table_Generator::throwError( - DB_TABLE_GENERATOR_ERR_INDEX_COL, - "$field"); + $code = DB_TABLE_GENERATOR_ERR_INDEX_COL; + $return =& + DB_Table_Generator::throwError($code, $field); } } @@ -684,13 +714,14 @@ // Generate index definitions, if any, as php code $n_idx = 0; - $u = array(); - $this->idx[$table] = array(); - $this->primary_key[$table] = null; + $u = array(); + + $this->idx[$table] = array(); + $this->primary_key[$table] = null; foreach ($DB_indexes as $type => $indexes) { if (count($indexes) > 0) { foreach ($indexes as $name => $fields) { - $this->idx[$table][$name] = array(); + $this->idx[$table][$name] = array(); $this->idx[$table][$name]['type'] = $type; if (count($fields) == 1) { $key = $fields[0]; @@ -717,18 +748,19 @@ return true; } -// }}} -// {{{ function buildTableClass($table, $indent = '') + // }}} + // {{{ function buildTableClass($table, $indent = '') /** * Returns one skeleton DB_Table subclass definition, as php code * - * The returned subclass definition string contains values for the + * The returned subclass definition string contains values for the * $col (column), $idx (index) and $auto_inc_col properties, with * no method definitions. * - * @param $table string name of table - * @param $indent string string of whitespace for base indentation + * @param string $table name of table + * @param string $indent string of whitespace for base indentation + * * @return string skeleton DB_Table subclass definition * @access public */ @@ -736,88 +768,126 @@ { $s = array(); $idx = array(); - $s[] = $indent . 'class ' . $this->className($table) . - ' extends ' . $this->extends . " {\n"; - $indent = $indent . ' '; - $s[] = $indent . 'var $col = array(' . "\n"; - $u = array(); - $indent = $indent . ' '; - + $u = array(); + $v = array(); + $l = 0; + + $s[] = $indent . '/*'; + $s[] = $indent . ' * Create the table object'; + $s[] = $indent . ' */'; + $s[] = $indent . 'class ' . $this->className($table) + . " extends {$this->extends} {\n"; + $indent .= ' '; + + $s[] = $indent . '/*'; + $s[] = $indent . ' * Column definitions'; + $s[] = $indent . ' */'; + $s[] = $indent . 'var $col = array(' . "\n"; + $indent .= ' '; + // Begin loop over columns - foreach($this->col[$table] as $name => $col) { + foreach ($this->col[$table] as $name => $col) { // Generate DB_Table column definitions as php code - $v = $indent . "'" . $name . "' => array(\n"; - $indent = $indent . ' '; - $t = array(); + $t = array(); + $t1 = array(); + $l1 = 0; + + $name = $indent . "'{$name}'"; + $l = max($l, strlen($name)); + $v[$name] = "array(\n"; + $indent .= ' '; foreach ($col as $key => $value) { if (is_string($value)) { - $value = "'" . $value . "'"; + $value = "'{$value}'"; } elseif (is_bool($value)) { $value = $value ? 'true' : 'false'; } else { $value = (string) $value; } - $t[] = $indent . "'" . $key . "'" . ' => ' . $value ; + $l1 = max($l1, strlen($key) + 2); + $t1[] = array("'{$key}'", $value) ; } - $v = $v . implode($t,",\n") . "\n"; - $indent = substr($indent, 0, -4); - $v = $v . $indent . ")"; - $u[] = $v; - + foreach ($t1 as $value) { + $t[] = $indent . str_pad($value[0], $l1, ' ', STR_PAD_RIGHT) + . ' => ' . $value[1]; + } + $v[$name] .= implode(",\n", $t) . "\n"; + $indent = substr($indent, 0, -4); + $v[$name] .= $indent . ')'; } //end loop over columns - $s[] = implode($u,",\n\n") . "\n"; + + foreach ($v as $key => $value) { + $u[] = str_pad($key, $l, ' ', STR_PAD_RIGHT) + . ' => ' . $value; + } + $s[] = implode(",\n\n", $u) . "\n"; $indent = substr($indent, 0, -4); - $s[] = $indent . ");\n"; + $s[] = $indent . ");\n"; // Generate index definitions, if any, as php code if (count($this->idx[$table]) > 0) { - $u = array(); - $s[] = $indent . 'var $idx = array(' . "\n"; - $indent = $indent . ' '; + $u = array(); + $v = array(); + $l = 0; + + $s[] = $indent . '/*'; + $s[] = $indent . ' * Index definitions'; + $s[] = $indent . ' */'; + $s[] = $indent . 'var $idx = array(' . "\n"; + $indent .= ' '; foreach ($this->idx[$table] as $name => $def) { - $type = $def['type']; - $cols = $def['cols']; - $v = $indent . "'" . $name . "' => array(\n"; - $indent = $indent . ' '; - $v = $v . $indent . "'type' => '$type',\n"; + $type = $def['type']; + $cols = $def['cols']; + $name = $indent . "'{$name}'"; + $l = max($l, strlen($name)); + $v[$name] = "array(\n"; + $indent .= ' '; + $v[$name] .= $indent . "'type' => '{$type}',\n"; if (is_array($cols)) { - $v = $v . $indent . "'cols' => array(\n"; - $indent = $indent . ' '; - $t = array(); + $v[$name] .= $indent . "'cols' => array(\n"; + $indent .= ' '; + $t = array(); foreach ($cols as $value) { $t[] = $indent . "'{$value}'"; } - $v = $v . implode($t,",\n") . "\n"; - $indent = substr($indent, 0, -4); - $v = $v . $indent . ")\n"; + $v[$name] .= implode(",\n", $t) . "\n"; + $indent = substr($indent, 0, -4); + $v[$name] .= $indent . ")\n"; } else { - $v = $v . $indent . "'cols' => '$cols'\n"; + $v[$name] = $v[$name] . $indent . "'cols' => '{$cols}'\n"; } - $indent = substr($indent, 0, -4); - $v = $v . $indent . ")"; - $u[] = $v; + $indent = substr($indent, 0, -4); + $v[$name] .= $indent . ")"; + } + + foreach ($v as $key => $value) { + $u[] = str_pad($key, $l, ' ', STR_PAD_RIGHT) + . ' => ' . $value; } - $s[] = implode($u,",\n\n") . "\n"; + $s[] = implode(",\n\n", $u) . "\n"; $indent = substr($indent, 0, -4); - $s[] = $indent . ");\n"; - } + $s[] = $indent . ");\n"; + } // end index generation // Write auto_inc_col if (isset($this->auto_inc_col[$table])) { - $s[] = $indent . 'var $auto_inc_col = ' - . "'{$this->auto_inc_col[$table]}';\n"; + $s[] = $indent . '/*'; + $s[] = $indent . ' * Auto-increment declaration'; + $s[] = $indent . ' */'; + $s[] = $indent . 'var $auto_inc_col = ' + . "'{$this->auto_inc_col[$table]}';\n"; } $indent = substr($indent, 0, -4); - $s[] = $indent . '}'; + $s[] = $indent . '}'; // Implode and return lines of class definition - return implode($s,"\n") . "\n"; - + return implode("\n", $s) . "\n"; + } -// }}} -// {{{ function buildTableClasses() + // }}} + // {{{ function buildTableClasses() /** * Returns a string containing all table class definitions in one file @@ -825,22 +895,25 @@ * The returned string contains the contents of a single php file with * definitions of DB_Table subclasses associated with all of the tables * in $this->tables. If $this->tables is initially null, method - * $this->getTableNames() is called internally to generate a list of - * table names. + * $this->getTableNames() is called internally to generate a list of + * table names. * - * The returned string includes the opening and closing <?php and ?> - * script elements, and the require_once line needed to include the + * The returned string includes the opening and closing <?php and ?> + * script elements, and the require_once line needed to include the * $this->extend_class (i.e., DB_Table or a subclass) that is being - * extended. To use, write this string to a new php file. + * extended. To use, write this string to a new php file. * * Usage: * <code> * $generator = DB_Table_Generator($db, $database); - * print $generator->buildTablesClasses(); + * echo $generator->buildTablesClasses(); * </code> - * + * + * @return mixed a string with all table class definitions, + * PEAR Error on failure + * @access public */ - function buildTableClasses() + function buildTableClasses() { // If $this->tables is null, call getTableNames() if (!$this->tables) { @@ -850,19 +923,25 @@ } } - $s = array(); - $s[] = "<?php"; + $s = array(); + $s[] = '<?php'; + $s[] = '/*'; + $s[] = ' * Include basic class'; + $s[] = ' */'; $s[] = "require_once '{$this->extends_file}';\n"; - foreach($this->tables as $table) { - $this->getTableDefinition($table); + foreach ($this->tables as $table) { + $return = $this->getTableDefinition($table); + if (PEAR::isError($return)) { + return $return; + } $s[] = $this->buildTableClass($table) . "\n"; } $s[] = '?>'; - return implode($s,"\n"); + return implode("\n", $s); } -// }}} -// {{{ function generateTableClassFiles() + // }}} + // {{{ function generateTableClassFiles() /** * Writes all table class definitions to separate files @@ -874,9 +953,9 @@ * </code> * * @return mixed true on success, PEAR Error on failure - * @access public + * @access public */ - function generateTableClassFiles() + function generateTableClassFiles() { // If $this->tables is null, call getTableNames() if (!$this->tables) { @@ -887,29 +966,40 @@ } // Write all table class definitions to separate files - foreach($this->tables as $table) { + foreach ($this->tables as $table) { $classname = $this->className($table); $filename = $this->classFileName($classname); $base = $this->class_write_path; if ($base) { if (!file_exists($base)) { - require_once 'System.php'; - System::mkdir(array('-p', $base)); + include_once 'System.php'; + if (!@System::mkdir(array('-p', $base))) { + return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE, + $base); + } + } - $filename = "$base/$filename"; + $filename = "{$base}/{$filename}"; } if (!file_exists($filename)) { - $s = array(); - $s[] = "<?php"; - $s[] = "require_once '{$this->extends_file}';\n"; + $s = array(); + $s[] = '<?php'; + $s[] = '/*'; + $s[] = ' * Include basic class'; + $s[] = ' */'; + $s[] = "require_once '{$this->extends_file}';\n"; $return = $this->getTableDefinition($table); if (PEAR::isError($return)) { return $return; } - $s[] = $this->buildTableClass($table) ; + $s[] = $this->buildTableClass($table); $s[] = '?>'; - $out = implode($s,"\n"); - $file = fopen( $filename, "w"); + $s[] = ''; + $out = implode("\n", $s); + if (!$file = @fopen($filename, 'wb')) { + return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE, + $filename); + } fputs($file, $out); fclose($file); } @@ -918,14 +1008,14 @@ return true; } -// }}} -// {{{ function generateDatabaseFile($object_name = null) + // }}} + // {{{ function generateDatabaseFile($object_name = null) /** * Writes a file to instantiate Table and Database objects * * After successful completion, a file named 'Database.php' will be - * have been created in the $this->class_write_path directory. This + * have been created in the $this->class_write_path directory. This * file should normally be included in application php scripts. It * can be renamed by the user. * @@ -936,186 +1026,248 @@ * $generator->generateDatabaseFile(); * </code> * - * @param string variable name for DB_Table_Database object - * @return void - * @access public + * @param string $object_name variable name for DB_Table_Database object + * + * @return mixed true on success, PEAR Error on failure + * @access public */ function generateDatabaseFile($object_name = null) { // Set name for DB_Table_Database object if ($object_name) { - $object_name = '$' . $object_name; + $object_name = "\${$object_name}"; } else { $object_name = '$db'; //default } $backend = strtoupper($this->backend); // 'DB' or 'MDB2' + if ('DB' == $backend) { + $dsn = $this->db->dsn; + } else { + $dsn = $this->db->getDSN('array'); + } + // Create array d[] containing lines of database php file - $d = array(); - $d[] = "<?php"; + $d = array(); + $d[] = '<?php'; + $d[] = '/*'; + $d[] = ' * Include basic classes'; + $d[] = ' */'; $d[] = "require_once '{$backend}.php';"; $d[] = "require_once 'DB/Table/Database.php';"; // Require_once statements for subclass definitions foreach ($this->tables as $table) { - $classname = $this->className($table); - $class_filename = $this->classFileName($classname); + $classname = $this->className($table); + $class_filename = $this->classFileName($classname); if ($this->class_include_path) { - $d[] = 'require_once ' . - "'{$this->class_include_path}/{$class_filename}';"; + $d[] = 'require_once ' + . "'{$this->class_include_path}/{$class_filename}';"; } else { $d[] = "require_once '{$class_filename}';"; } } - $d[] = ""; + $d[] = ''; - $d[] = '// NOTE: User must uncomment & edit code to create $dsn'; - $d[] = '# $phptype = ' . "'mysqli';"; - $d[] = '# $username = ' . "'root';"; - $d[] = '# $password = ' . "'password';"; - $d[] = '# $hostname = ' . "'localhost';"; - $d[] = '# $dsn = "$phptype://$username:$password@$hostname";'; - $d[] = ""; - - $d[] = '// Instantiate DB/MDB2 connection object $conn'; - $d[] = '$conn =& ' . $backend . '::connect($dsn);'; + $d[] = '/*'; + $d[] = ' * NOTE: User must uncomment & edit code to create $dsn'; + $d[] = ' */'; + $d[] = "//\$phptype = '{$dsn['phptype']}';"; + $d[] = "//\$username = '{$dsn['username']}';"; + $d[] = "//\$password = ''; // put your password here"; + $d[] = "//\$hostname = '{$dsn['hostspec']}';"; + $d[] = "//\$database = '{$dsn['database']}';"; + $d[] = "//\$create = false; // 'drop', 'safe', 'verify', 'alter'"; + $d[] = '//$dsn = "{$phptype}://{$username}:{$password}@{$hostname}' + . '/{$database}";'; + $d[] = ''; + + $d[] = '/*'; + $d[] = " * Instantiate {$backend} connection object \$conn"; + $d[] = ' */'; + $d[] = "\$conn =& {$backend}::connect(\$dsn);"; $d[] = 'if (PEAR::isError($conn)) {'; - $d[] = ' print "Error connecting to database server\n";'; - $d[] = ' print $conn->getMessage();'; + $d[] = ' echo "Error connecting to database server\n";'; + $d[] = ' echo $conn->getMessage();'; $d[] = ' die;'; $d[] = '}'; - $d[] = ""; + $d[] = ''; - $d[] = '// Create one instance of each DB_Table subclass'; + $d[] = '/*'; + $d[] = ' * Create one instance of each DB_Table subclass'; + $d[] = ' */'; foreach ($this->tables as $table) { $classname = $this->className($table); - $d[] = '$' . $table . " = new $classname(" - . '$conn, ' . "'{$table}');"; - } - $d[] = ""; - $d[] = '// Instantiate a parent DB_Table_Database object'; - $d[] = $object_name . ' = new DB_Table_Database($conn, ' - . "'{$this->name}');"; - $d[] = ""; + $d[] = "\${$table} = new {$classname}(" + . '$conn, ' . "'{$table}'" . ', $create);'; + $d[] = "if (PEAR::isError(\${$table}->error)) {"; + $d[] = ' echo "Can\'t create table object.\n";'; + $d[] = " echo \${$table}->error->getMessage();"; + $d[] = ' die;'; + $d[] = '}'; + + } + $d[] = ''; + + $d[] = '/*'; + $d[] = ' * Instantiate a parent DB_Table_Database object'; + $d[] = ' */'; + $d[] = "{$object_name} = new DB_Table_Database(\$conn, \$database);"; + $d[] = "if (PEAR::isError({$object_name}->error)) {"; + $d[] = ' echo "Can\'t create database object.\n";'; + $d[] = " echo {$object_name}->error->getMessage();"; + $d[] = ' die;'; + $d[] = '}'; + $d[] = ''; - $d[] = '// Add DB_Table objects to parent DB_Table_Database object'; + $d[] = '/*'; + $d[] = ' * Add DB_Table objects to parent DB_Table_Database object'; + $d[] = ' */'; foreach ($this->tables as $table) { $classname = $this->className($table); - $d[] = $object_name . '->addTable($' . $table . ');'; + + $d[] = "\$result = {$object_name}->addTable(\${$table});"; + $d[] = 'if (PEAR::isError($result)) {'; + $d[] = ' echo "Can\'t add table object to database object.\n";'; + $d[] = ' echo $result->getMessage();'; + $d[] = ' die;'; + $d[] = '}'; } - $d[] = ""; + $d[] = ''; - // Add foreign key references: If the name of an integer column + // Add foreign key references: If the name of an integer column // matches "/id$/i" (i.e., the names ends with id, ID, or Id), the // remainder of the name matches the name $rtable of another table, // and $rtable has an integer primary key, then the column is // assumed to be a foreign key that references $rtable. - $d[] = '// Add auto-guessed foreign references'; + $d[] = '/*'; + $d[] = ' * Add auto-guessed foreign references'; + $d[] = ' */'; foreach ($this->col as $table => $col) { foreach ($col as $col_name => $def) { - // Only consider integer columns - $ftype = $def['type']; - if (!in_array($ftype, array('integer','smallint','bigint'))) { - continue; - } - if (preg_match("/id$/i", $col_name)) { - $column_base = preg_replace('/_?id$/i', '', $col_name); - foreach ($this->tables as $rtable) { - if (!preg_match("/^{$rtable}$/i", $column_base)) { - continue; - } - if (preg_match("/^{$table}$/i", $column_base)) { - continue; - } - if (!isset($this->primary_key[$rtable])) { - continue; - } - $rkey = $this->primary_key[$rtable]; - if (is_array($rkey)) { - continue; - } - $rtype = $this->col[$rtable][$rkey]['type']; - if (!in_array($rtype, - array('integer','smallint','bigint'))) { - continue; - } - $d[] = $object_name - . "->addRef('$table', '$col_name', '$rtable');"; - } - } - } - } - $d[] = ""; - $d[] = '// Add any additional foreign key references here'; - $d[] = ""; - $d[] = '// Add any linking table declarations here'; - $d[] = '// Uncomment next line to add all possible linking tables;'; - $d[] = '# ' . $object_name . '->addAllLinks();'; - $d[] = ""; + // Only consider integer columns + $ftype = $def['type']; + if (!in_array($ftype, array('integer','smallint','bigint'))) { + continue; + } + if (preg_match("/id$/i", $col_name)) { + $column_base = preg_replace('/_?id$/i', '', $col_name); + foreach ($this->tables as $rtable) { + if (!preg_match("/^{$rtable}$/i", $column_base)) { + continue; + } + if (preg_match("/^{$table}$/i", $column_base)) { + continue; + } + if (!isset($this->primary_key[$rtable])) { + continue; + } + $rkey = $this->primary_key[$rtable]; + if (is_array($rkey)) { + continue; + } + $rtype = $this->col[$rtable][$rkey]['type']; + if (!in_array($rtype, + array('integer','smallint','bigint'))) { + continue; + } + $d[] = "\$result = {$object_name}->addRef('{$table}', " + . "'{$col_name}', '{$rtable}');"; + $d[] = 'if (PEAR::isError($result)) {'; + $d[] = ' echo "Can\'t add foreign key reference.\n";'; + $d[] = ' echo $result->getMessage();'; + $d[] = ' die;'; + $d[] = '}'; + } + } + } + } + $d[] = ''; + $d[] = '/*'; + $d[] = ' * Add any additional foreign key references here'; + $d[] = ' *'; + $d[] = ' * Add any linking table declarations here'; + $d[] = ' * Uncomment next line to add all possible linking tables;'; + $d[] = ' */'; + $d[] = "//\$result = {$object_name}->addAllLinks();"; + $d[] = '//if (PEAR::isError($result)) {'; + $d[] = '// echo "Can\'t add linking tables.\n";'; + $d[] = '// echo $result->getMessage();'; + $d[] = '// die;'; + $d[] = '//}'; + $d[] = ''; // Closing script element - $d[] = "?>"; + $d[] = '?>'; + $d[] = ''; // Open and write file $base = $this->class_write_path; if ($base) { if (!file_exists($base)) { - require_once 'System.php'; - System::mkdir(array('-p', $base)); + include_once 'System.php'; + if (!@System::mkdir(array('-p', $base))) { + return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE, $base); + } } - $filename = $base . "/Database.php"; + $filename = $base . '/Database.php'; } else { - $filename = "Database.php"; + $filename = 'Database.php'; + } + if (!$file = @fopen($filename, 'wb')) { + return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE, $filename); } - $file = fopen($filename, "w"); $out = implode("\n", $d); fputs($file, $out); fclose($file); + + return true; } -// }}} -// {{{ function className($table) + // }}} + // {{{ function className($table) /** - * Convert a table name into a class name + * Convert a table name into a class name * - * Converts all non-alphanumeric characters to '_', capitalizes - * first letter, and adds $this->class_suffix to end. Override + * Converts all non-alphanumeric characters to '_', capitalizes + * first letter, and adds $this->class_suffix to end. Override * this if you want something else. * - * @param string $class_name name of table - * @return string class name; - * @access public + * @param string $table name of table + * + * @return string class name; + * @access public */ function className($table) { - $name = preg_replace('/[^A-Z0-9]/i','_',ucfirst(trim($table))); + $name = preg_replace('/[^A-Z0-9]/i', '_', ucfirst(trim($table))); return $name . $this->class_suffix; } - -// }}} -// {{{ function classFileName($class_name) - + + // }}} + // {{{ function classFileName($class_name) + /** * Returns the path to a file containing a class definition * * Appends '.php' to class name. * - * @param string $class_name name of class - * @return string file name - * @access public + * @param string $class_name name of class + * + * @return string file name + * @access public */ function classFileName($class_name) { - $filename = $class_name . ".php" ; + $filename = $class_name . '.php'; return $filename; - } -// }}} + // }}} } // }}}
http://cvs.php.net/viewvc.cgi/pear/DB_Table/package2.xml?r1=1.34&r2=1.35&diff_format=u Index: pear/DB_Table/package2.xml diff -u pear/DB_Table/package2.xml:1.34 pear/DB_Table/package2.xml:1.35 --- pear/DB_Table/package2.xml:1.34 Thu Nov 22 10:38:02 2007 +++ pear/DB_Table/package2.xml Mon Dec 10 22:02:59 2007 @@ -59,6 +59,10 @@ (thanks to Gregor Gramlich) - Bug #12497: Generator works only with DB, but not with MDB2 (thanks to Carsten Wiedmann for the patch) +- Request #12510: various optimizations (mostly coding standards) and + corrections (missing error return call, change MDB2's portability mode only + temporarily) + (thanks to Carsten Wiedmann for the patch) </notes> <contents> <dir name="/"> @@ -73,7 +77,9 @@ <file name="Manager.php" role="php" /> <file name="QuickForm.php" role="php" /> <file name="Valid.php" role="php" /> - <file name="Generator.php" role="php" /> + <file name="Generator.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info" /> + </file> <file name="XML.php" role="php" /> <dir name="Manager"> <file name="ibase.php" role="php" /> http://cvs.php.net/viewvc.cgi/pear/DB_Table/DB/Table/Generator.php?r1=1.12&r2=1.13&diff_format=u Index: pear/DB_Table/DB/Table/Generator.php diff -u pear/DB_Table/DB/Table/Generator.php:1.12 pear/DB_Table/DB/Table/Generator.php:1.13 --- pear/DB_Table/DB/Table/Generator.php:1.12 Thu Nov 22 10:38:02 2007 +++ pear/DB_Table/DB/Table/Generator.php Mon Dec 10 22:02:59 2007 @@ -10,11 +10,15 @@ * @package DB_Table * @author David C. Morse <morse@php.net> * @license http://www.gnu.org/copyleft/lesser.html LGPL - * @version $Id: Generator.php,v 1.12 2007/11/22 10:38:02 wiesemann Exp $ + * @version CVS: $Id: Generator.php,v 1.13 2007/12/10 22:02:59 wiesemann Exp $ + * @link http://pear.php.net/package/DB_Table */ // {{{ Includes +/**#@+ + * Include basic classes + */ /** * The PEAR class (used for errors) */ @@ -29,10 +33,14 @@ * DB_Table_Manager class (used to reverse engineer indices) */ require_once 'DB/Table/Manager.php'; +/**#@-*/ // }}} // {{{ Error code constants +/**#@+ + * Error codes + */ /** * Parameter is not a DB/MDB2 object */ @@ -43,17 +51,25 @@ */ define('DB_TABLE_GENERATOR_ERR_INDEX_COL', -302); +/** + * Error while creating file/directory + */ +define('DB_TABLE_GENERATOR_ERR_FILE', -303); +/**#@-*/ + // }}} // {{{ Error messages -/** - * US-English default error messages. +/** + * US-English default error messages. */ $GLOBALS['_DB_TABLE_GENERATOR']['default_error'] = array( DB_TABLE_GENERATOR_ERR_DB_OBJECT => - 'Invalid DB/MDB2 object parameter. Function', + 'Invalid DB/MDB2 object parameter. Function', DB_TABLE_GENERATOR_ERR_INDEX_COL => - 'Index column is not a valid column name. Index column' - ); + 'Index column is not a valid column name. Index column', + DB_TABLE_GENERATOR_ERR_FILE => + 'Can\'t create file/directory:' +); // merge default and user-defined error messages if (!isset($GLOBALS['_DB_TABLE_GENERATOR']['error'])) { @@ -73,32 +89,32 @@ * * This class generates the php code necessary to use the DB_Table * package to interact with an existing database. This requires the - * generation of a skeleton subclass definition be generated for each + * generation of a skeleton subclass definition be generated for each * table in the database, in which the $col, $idx, and $auto_inc_col * properties are constructed using a table schema that is obtained - * by querying the database. + * by querying the database. * * The class can also generate a file, named 'Database.php' by default, * that includes (require_once) each of the table subclass definitions, - * instantiates one object of each DB_Table subclass (i.e., one object - * for each table), instantiates a parent DB_Table_Database object, - * adds all the tables to that parent, attempts to guess foreign key + * instantiates one object of each DB_Table subclass (i.e., one object + * for each table), instantiates a parent DB_Table_Database object, + * adds all the tables to that parent, attempts to guess foreign key * relationships between tables based on the column names, and adds * the inferred references to the parent object. * * All of the code is written to a directory whose path is given by * the property $class_write_path. By default, this is the current - * directory. By default, the name of the class constructed for a - * table named 'thing' is "Thing_Table". That is, the class name is - * the table name, with the first letter upper case, with a suffix - * '_Table'. This suffix can be changed by setting the $class_suffix - * property. The file containing a subclass definition is the - * subclass name with a php extension, e.g., 'Thing_Table.php'. The - * object instantiated from that subclass is the same as the table + * directory. By default, the name of the class constructed for a + * table named 'thing' is "Thing_Table". That is, the class name is + * the table name, with the first letter upper case, with a suffix + * '_Table'. This suffix can be changed by setting the $class_suffix + * property. The file containing a subclass definition is the + * subclass name with a php extension, e.g., 'Thing_Table.php'. The + * object instantiated from that subclass is the same as the table * name, with no suffix, e.g., 'thing'. - * - * To generate the code for all of the tables in a database named - * $database, instantiate a MDB2 or DB object named $db that connects + * + * To generate the code for all of the tables in a database named + * $database, instantiate a MDB2 or DB object named $db that connects * to the database of interest, and execute the following code: * <code> * $generator = DB_Table_Generator($db, $database); @@ -106,20 +122,20 @@ * $generator->generateTableClassFiles(); * $generator->generateDatabaseFile(); * </code> - * Here $class_write_path should be the path (without a trailing - * separator) to a directory in which all of the code should be - * written. If this directory does not exist, it will be created. - * If the directory does already exist, exising files will not + * Here $class_write_path should be the path (without a trailing + * separator) to a directory in which all of the code should be + * written. If this directory does not exist, it will be created. + * If the directory does already exist, exising files will not * be overwritten. If $class_write_path is not set (i.e., if this - * line is omitted) all the code will be written to the current - * directory. If ->generateDatabaseFile() is called, it must be - * called after ->generateTableClassFiles(). + * line is omitted) all the code will be written to the current + * directory. If ->generateDatabaseFile() is called, it must be + * called after ->generateTableClassFiles(). * * By default, ->generateTableClassFiles() and ->generateDatabaseFiles() - * generate code for all of the tables in the current database. To - * generate code for a specified list of tables, set the value of the - * public $tables property to a sequential list of table names before - * calling either of these methods. Code can be generated for three + * generate code for all of the tables in the current database. To + * generate code for a specified list of tables, set the value of the + * public $tables property to a sequential list of table names before + * calling either of these methods. Code can be generated for three * tables named 'table1', 'table2', and 'table3' as follows: * <code> * $generator = DB_Table_Generator($db, $database); @@ -128,36 +144,37 @@ * $generator->generateTableClassFiles(); * $generator->generateDatabaseFile(); * </code> - * If the $tables property is not set to a non-null value prior - * to calling ->generateTableClassFiles() then, by default, the + * If the $tables property is not set to a non-null value prior + * to calling ->generateTableClassFiles() then, by default, the * database is queried for a list of all table names, by calling the * ->getTableNames() method from within ->generateTableClassFiles(). - * + * * PHP version 4 and 5 * * @category Database * @package DB_Table * @author David C. Morse <morse@php.net> * @license http://www.gnu.org/copyleft/lesser.html LGPL - * @version $Id: Generator.php,v 1.12 2007/11/22 10:38:02 wiesemann Exp $ + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB_Table */ class DB_Table_Generator { - // {{{ properties + // {{{ Properties /** * Name of the database * - * @var string + * @var string * @access public */ - var $name = null; + var $name = null; /** * The PEAR DB/MDB2 object that connects to the database. * - * @var object + * @var object * @access private */ var $db = null; @@ -165,7 +182,7 @@ /** * The backend type. May have values 'db' or 'mdb2' * - * @var string + * @var string * @access private */ var $backend = null; @@ -177,13 +194,13 @@ * at instantiation time. To check if there was an instantiation error... * * <code> - * $obj =& new DB_Table_Generator(); - * if ($obj->error) { - * // ... error handling code here ... - * } + * $obj =& new DB_Table_Generator(); + * if ($obj->error) { + * // ... error handling code here ... + * } * </code> * - * @var object PEAR_Error + * @var object PEAR_Error * @access public */ var $error = null; @@ -191,7 +208,7 @@ /** * Numerical array of table name strings * - * @var array + * @var array * @access public */ var $tables = array(); @@ -199,15 +216,15 @@ /** * Class being extended (DB_Table or generic subclass) * - * @var string + * @var string * @access public */ var $extends = 'DB_Table'; /** - * Path to definition of the class $this->extends + * Path to definition of the class $this->extends * - * @var string + * @var string * @access public */ var $extends_file = 'DB/Table.php'; @@ -215,7 +232,7 @@ /** * Suffix to add to table names to obtain corresponding class names * - * @var string + * @var string * @access public */ var $class_suffix = "_Table"; @@ -225,7 +242,7 @@ * * Value should not include a trailing "/". * - * @var string + * @var string * @access public */ var $class_write_path = ''; @@ -235,11 +252,11 @@ * * Used to create require_once statements in the Database.php file, * which is in the same directory as the class definition files. Leave - * as empty string if your PHP include_path contains ".". The value - * should not include a trailing "/", which is added automatically + * as empty string if your PHP include_path contains ".". The value + * should not include a trailing "/", which is added automatically * to values other than the empty string. * - * @var string + * @var string * @access public */ var $class_include_path = ''; @@ -248,22 +265,22 @@ * Array of column definitions * * Array $this->col[table_name][column_name] = column definition. - * Column definition is an array with the same format as the $col + * Column definition is an array with the same format as the $col * property of a DB_Table object * - * @var array + * @var array * @access public */ - var $col = array(); + var $col = array(); /** * Array of index/constraint definitions. * - * Array $this->idx[table_table][index_name] = Index definition. + * Array $this->idx[table_table][index_name] = Index definition. * The index definition is an array with the same format as the * DB_Table $idx property property array. * - * @var array + * @var array * @access public */ var $idx = array(); @@ -273,7 +290,7 @@ * * Array $this->auto_inc_col[table_name] = auto-increment column * - * @var array + * @var array * @access public */ var $auto_inc_col = array(); @@ -281,23 +298,25 @@ /** * Array of primary keys * - * @var array + * @var array * @access public */ var $primary_key = array(); /** - * MDB2 'idxname_format' option, format of index names + * MDB2 'idxname_format' option, format of index names * * For use in printf() formatting. Use '%s' to use index names as * returned by getTableConstraints/Indexes, and '%s_idx' to add an - * '_idx' suffix. For MySQL, use the default value '%'. + * '_idx' suffix. For MySQL, use the default value '%'. + * + * @var string + * @access public */ var $idxname_format = '%s'; -// }}} - -// {{{ function DB_Table_Generator(&$db, $name) + // }}} + // {{{ function DB_Table_Generator(&$db, $name) /** * Constructor @@ -305,10 +324,11 @@ * If an error is encountered during instantiation, the error * message is stored in the $this->error property of the resulting * object. See $error property docblock for a discussion of error - * handling. - * - * @param object $db DB/MDB2 database connection object - * @param string $name database name string + * handling. + * + * @param object &$db DB/MDB2 database connection object + * @param string $name database name string + * * @return object DB_Table_Generator * @access public */ @@ -320,28 +340,29 @@ } elseif (is_a($db, 'mdb2_driver_common')) { $this->backend = 'mdb2'; } else { - $this->error =& DB_Table_Generator::throwError( - DB_TABLE_GENERATOR_ERR_DB_OBJECT, - "DB_Table_Generator"); + $this->error =& + DB_Table_Generator::throwError(DB_TABLE_GENERATOR_ERR_DB_OBJECT, + 'DB_Table_Generator'); return; } - $this->db =& $db; - $this->name = $name; + $this->db =& $db; + $this->name = $name; } -// }}} -// {{{ function &throwError($code, $extra = null) + // }}} + // {{{ function &throwError($code, $extra = null) /** * Specialized version of throwError() modeled on PEAR_Error. - * - * Throws a PEAR_Error with a DB_Table_Generator error message based + * + * Throws a PEAR_Error with a DB_Table_Generator error message based * on a DB_Table_Generator constant error code. - * + * * @param string $code A DB_Table_Generator error code constant. - * @param string $extra Extra text for the error (in addition to the - * regular error message). + * @param string $extra Extra text for the error (in addition to the + * regular error message). + * * @return object PEAR_Error * @access public * @static @@ -349,38 +370,38 @@ function &throwError($code, $extra = null) { // get the error message text based on the error code - $text = 'DB_TABLE_GENERATOR ERROR - ' . "\n" . - $GLOBALS['_DB_TABLE_GENERATOR']['error'][$code]; - + $text = 'DB_TABLE_GENERATOR ERROR - ' . "\n" + . $GLOBALS['_DB_TABLE_GENERATOR']['error'][$code]; + // add any additional error text if ($extra) { $text .= ' ' . $extra; } - + // done! $error = PEAR::throwError($text, $code); return $error; } - -// }}} -// {{{ function setErrorMessage($code, $message = null) + + // }}} + // {{{ function setErrorMessage($code, $message = null) /** * Overwrites one or more error messages, e.g., to internationalize them. - * - * @param mixed $code If string, the error message with code $code will be - * overwritten by $message. If array, each key is a code - * and each value is a new message. - * + * + * @param mixed $code If string, the error message with code $code will be + * overwritten by $message. If array, each key is a + * code and each value is a new message. * @param string $message Only used if $key is not an array. + * * @return void * @access public */ - function setErrorMessage($code, $message = null) + function setErrorMessage($code, $message = null) { if (is_array($code)) { foreach ($code as $single_code => $single_message) { - $GLOBALS['_DB_TABLE_GENERATOR']['error'][$single_code] + $GLOBALS['_DB_TABLE_GENERATOR']['error'][$single_code] = $single_message; } } else { @@ -388,18 +409,18 @@ } } -// }}} -// {{{ function getTableNames() + // }}} + // {{{ function getTableNames() /** * Gets a list of tables from the database - * - * Upon successful completion, names are stored in the $this->tables - * array. If an error is encountered, a PEAR Error is returned, and - * $this->tables is reset to null. * - * @access public - * @return mixed true on success, PEAR Error on failure + * Upon successful completion, names are stored in the $this->tables + * array. If an error is encountered, a PEAR Error is returned, and + * $this->tables is reset to null. + * + * @return mixed true on success, PEAR Error on failure + * @access public */ function getTableNames() { @@ -416,56 +437,65 @@ $this->db->popExpect(); } } else { - $this->db->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE); + // Temporarily change 'portability' MDB2 option + $portability = $this->db->getOption('portability'); + $this->db->setOption('portability', + MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE); + $this->db->loadModule('Manager'); $this->db->loadModule('Reverse'); // Get list of tables $this->tables = $this->db->manager->listTables(); + // Restore original MDB2 'portability' + $db->setOption('portability', $portability); } if (PEAR::isError($this->tables)) { - $error = $this->tables; + $error = $this->tables; $this->tables = null; - return $error; + return $error; } else { return true; } } -// }}} -// {{{ function getTableDefinition($table) + // }}} + // {{{ function getTableDefinition($table) /** * Gets column and index definitions by querying database - * - * Upon return, column definitions are stored in $this->col[$table], + * + * Upon return, column definitions are stored in $this->col[$table], * and index definitions in $this->idx[$table]. * - * Calls DB/MDB2::tableInfo() for column definitions, and uses + * Calls DB/MDB2::tableInfo() for column definitions, and uses * the DB_Table_Manager class to obtain index definitions. * - * @param $table string name of table - * @return mixed true on success, PEAR Error on failure - * @access public + * @param string $table name of table + * + * @return mixed true on success, PEAR Error on failure + * @access public */ - function getTableDefinition($table) + function getTableDefinition($table) { - #// 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]; - # } - #} + /* + // 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]; + } + } + */ $db =& $this->db; if ($this->backend == 'db') { - $defs = $db->tableInfo($table); + $defs = $db->tableInfo($table); if (PEAR::isError($defs)) { return $defs; - } + } $this->columns[$table] = $defs; } else { @@ -478,10 +508,10 @@ $this->db->loadModule('Reverse'); // Columns - $defs = $db->reverse->tableInfo($table); + $defs = $db->reverse->tableInfo($table); if (PEAR::isError($defs)) { return $defs; - } + } // rename the 'length' key, so it matches db's return. foreach ($defs as $k => $v) { @@ -497,138 +527,138 @@ $db->setOption('idxname_format', $this->idxname_format); } - // Default - no auto increment column + // Default - no auto increment column $this->auto_inc_col[$table] = null; // Loop over columns to create $this->col[$table] $this->col[$table] = array(); - foreach($defs as $t) { + foreach ($defs as $t) { $name = $t['name']; $col = array(); - + switch (strtoupper($t['type'])) { - case 'INT2': // postgres - case 'TINYINT': - case 'TINY': //mysql - case 'SMALLINT': - $col['type'] = 'smallint'; - break; - case 'INT4': // postgres - case 'SERIAL4': // postgres - case 'INT': - case 'SHORT': // mysql - case 'INTEGER': - case 'MEDIUMINT': - case 'YEAR': - $col['type'] = 'integer'; - break; - case 'BIGINT': - case 'LONG': // mysql - case 'INT8': // postgres - case 'SERIAL8': // postgres - $col['type'] = 'bigint'; - break; - case 'REAL': - case 'NUMERIC': - case 'NUMBER': // oci8 - case 'FLOAT': // mysql - case 'FLOAT4': // real (postgres) - $col['type'] = 'single'; - break; - case 'DOUBLE': - case 'DOUBLE PRECISION': // double precision (firebird) - case 'FLOAT8': // double precision (postgres) - $col['type'] = 'double'; - break; - case 'DECIMAL': - case 'MONEY': // mssql and maybe others - $col['type'] = 'decimal'; - break; - case 'BIT': - case 'BOOL': - case 'BOOLEAN': - $col['type'] = 'boolean'; - break; - case 'STRING': - case 'CHAR': - $col['type'] = 'char'; - break; - case 'VARCHAR': - case 'VARCHAR2': - case 'TINYTEXT': - $col['type'] = 'varchar'; - break; - case 'TEXT': - case 'MEDIUMTEXT': - case 'LONGTEXT': - $col['type'] = 'clob'; - break; - case 'DATE': - $col['type'] = 'date'; - break; - case 'TIME': - $col['type'] = 'time'; - break; - case 'DATETIME': // mysql - case 'TIMESTAMP': - $col['type'] = 'timestamp'; - break; - 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 - $col['type'] = 'varchar'; - break; - default: - $col['type'] = $t['type'] . ' (Unknown type)'; - break; - } - - // Set length and scope if required - if (in_array($col['type'], array('char','varchar','decimal'))) { + case 'INT2': // postgres + case 'TINYINT': + case 'TINY': //mysql + case 'SMALLINT': + $col['type'] = 'smallint'; + break; + case 'INT4': // postgres + case 'SERIAL4': // postgres + case 'INT': + case 'SHORT': // mysql + case 'INTEGER': + case 'MEDIUMINT': + case 'YEAR': + $col['type'] = 'integer'; + break; + case 'BIGINT': + case 'LONG': // mysql + case 'INT8': // postgres + case 'SERIAL8': // postgres + $col['type'] = 'bigint'; + break; + case 'REAL': + case 'NUMERIC': + case 'NUMBER': // oci8 + case 'FLOAT': // mysql + case 'FLOAT4': // real (postgres) + $col['type'] = 'single'; + break; + case 'DOUBLE': + case 'DOUBLE PRECISION': // double precision (firebird) + case 'FLOAT8': // double precision (postgres) + $col['type'] = 'double'; + break; + case 'DECIMAL': + case 'MONEY': // mssql and maybe others + $col['type'] = 'decimal'; + break; + case 'BIT': + case 'BOOL': + case 'BOOLEAN': + $col['type'] = 'boolean'; + break; + case 'STRING': + case 'CHAR': + $col['type'] = 'char'; + break; + case 'VARCHAR': + case 'VARCHAR2': + case 'TINYTEXT': + $col['type'] = 'varchar'; + break; + case 'TEXT': + case 'MEDIUMTEXT': + case 'LONGTEXT': + $col['type'] = 'clob'; + break; + case 'DATE': + $col['type'] = 'date'; + break; + case 'TIME': + $col['type'] = 'time'; + break; + case 'DATETIME': // mysql + case 'TIMESTAMP': + $col['type'] = 'timestamp'; + break; + 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 + $col['type'] = 'varchar'; + break; + default: + $col['type'] = $t['type'] . ' (Unknown type)'; + break; + } + + // Set length and scope if required + if (in_array($col['type'], array('char','varchar','decimal'))) { if (isset($t['len'])) { $col['size'] = (int) $t['len']; - } elseif ($col['type'] == 'varchar') { + } elseif ($col['type'] == 'varchar') { $col['size'] = 255; // default length - } elseif ($col['type'] == 'char') { + } elseif ($col['type'] == 'char') { $col['size'] = 128; // default length - } elseif ($col['type'] == 'decimal') { - $col['size'] = 15; // default length + } elseif ($col['type'] == 'decimal') { + $col['size'] = 15; // default length } - if ($col['type'] == 'decimal') { - $col['scope'] = 2; + if ($col['type'] == 'decimal') { + $col['scope'] = 2; } } if (isset($t['notnull'])) { if ($t['notnull']) { - $col['require'] = true; + $col['require'] = true; } } if (isset($t['autoincrement'])) { $this->auto_inc_col[$table] = $name; } - if (isset($t['flags'])){ + if (isset($t['flags'])) { $flags = $t['flags']; - if (preg_match('/not[ _]null/i',$flags)) { + if (preg_match('/not[ _]null/i', $flags)) { $col['require'] = true; } if (preg_match("/(auto_increment|nextval\()/i", $flags)) { $this->auto_inc_col[$table] = $name; - } + } } $require = isset($col['require']) ? $col['require'] : false; if ($require) { if (isset($t['default'])) { $default = $t['default']; $type = $col['type']; - if (in_array($type, + if (in_array($type, array('smallint', 'integer', 'bigint'))) { $default = (int) $default; } elseif (in_array($type, array('single', 'double'))) { @@ -646,7 +676,7 @@ // Make array with lower case column array names as keys $col_lc = array(); foreach ($this->col[$table] as $name => $def) { - $name_lc = strtolower($name); + $name_lc = strtolower($name); $col_lc[$name_lc] = $name; } @@ -654,7 +684,7 @@ $DB_indexes = DB_Table_Manager::getIndexes($db, $table); if (PEAR::isError($DB_indexes)) { return $DB_indexes; - } + } // Check that index columns correspond to valid column names. // Try to correct problems with capitalization, if necessary. @@ -669,12 +699,12 @@ $field_lc = strtolower($field); if (isset($col_lc[$field_lc])) { $correct = $col_lc[$field_lc]; - $DB_indexes[$type][$name][$key] + $DB_indexes[$type][$name][$key] = $correct; } else { - $return =& DB_Table_Generator::throwError( - DB_TABLE_GENERATOR_ERR_INDEX_COL, - "$field"); + $code = DB_TABLE_GENERATOR_ERR_INDEX_COL; + $return =& + DB_Table_Generator::throwError($code, $field); } } @@ -684,13 +714,14 @@ // Generate index definitions, if any, as php code $n_idx = 0; - $u = array(); - $this->idx[$table] = array(); - $this->primary_key[$table] = null; + $u = array(); + + $this->idx[$table] = array(); + $this->primary_key[$table] = null; foreach ($DB_indexes as $type => $indexes) { if (count($indexes) > 0) { foreach ($indexes as $name => $fields) { - $this->idx[$table][$name] = array(); + $this->idx[$table][$name] = array(); $this->idx[$table][$name]['type'] = $type; if (count($fields) == 1) { $key = $fields[0]; @@ -717,18 +748,19 @@ return true; } -// }}} -// {{{ function buildTableClass($table, $indent = '') + // }}} + // {{{ function buildTableClass($table, $indent = '') /** * Returns one skeleton DB_Table subclass definition, as php code * - * The returned subclass definition string contains values for the + * The returned subclass definition string contains values for the * $col (column), $idx (index) and $auto_inc_col properties, with * no method definitions. * - * @param $table string name of table - * @param $indent string string of whitespace for base indentation + * @param string $table name of table + * @param string $indent string of whitespace for base indentation + * * @return string skeleton DB_Table subclass definition * @access public */ @@ -736,88 +768,126 @@ { $s = array(); $idx = array(); - $s[] = $indent . 'class ' . $this->className($table) . - ' extends ' . $this->extends . " {\n"; - $indent = $indent . ' '; - $s[] = $indent . 'var $col = array(' . "\n"; - $u = array(); - $indent = $indent . ' '; - + $u = array(); + $v = array(); + $l = 0; + + $s[] = $indent . '/*'; + $s[] = $indent . ' * Create the table object'; + $s[] = $indent . ' */'; + $s[] = $indent . 'class ' . $this->className($table) + . " extends {$this->extends} {\n"; + $indent .= ' '; + + $s[] = $indent . '/*'; + $s[] = $indent . ' * Column definitions'; + $s[] = $indent . ' */'; + $s[] = $indent . 'var $col = array(' . "\n"; + $indent .= ' '; + // Begin loop over columns - foreach($this->col[$table] as $name => $col) { + foreach ($this->col[$table] as $name => $col) { // Generate DB_Table column definitions as php code - $v = $indent . "'" . $name . "' => array(\n"; - $indent = $indent . ' '; - $t = array(); + $t = array(); + $t1 = array(); + $l1 = 0; + + $name = $indent . "'{$name}'"; + $l = max($l, strlen($name)); + $v[$name] = "array(\n"; + $indent .= ' '; foreach ($col as $key => $value) { if (is_string($value)) { - $value = "'" . $value . "'"; + $value = "'{$value}'"; } elseif (is_bool($value)) { $value = $value ? 'true' : 'false'; } else { $value = (string) $value; } - $t[] = $indent . "'" . $key . "'" . ' => ' . $value ; + $l1 = max($l1, strlen($key) + 2); + $t1[] = array("'{$key}'", $value) ; } - $v = $v . implode($t,",\n") . "\n"; - $indent = substr($indent, 0, -4); - $v = $v . $indent . ")"; - $u[] = $v; - + foreach ($t1 as $value) { + $t[] = $indent . str_pad($value[0], $l1, ' ', STR_PAD_RIGHT) + . ' => ' . $value[1]; + } + $v[$name] .= implode(",\n", $t) . "\n"; + $indent = substr($indent, 0, -4); + $v[$name] .= $indent . ')'; } //end loop over columns - $s[] = implode($u,",\n\n") . "\n"; + + foreach ($v as $key => $value) { + $u[] = str_pad($key, $l, ' ', STR_PAD_RIGHT) + . ' => ' . $value; + } + $s[] = implode(",\n\n", $u) . "\n"; $indent = substr($indent, 0, -4); - $s[] = $indent . ");\n"; + $s[] = $indent . ");\n"; // Generate index definitions, if any, as php code if (count($this->idx[$table]) > 0) { - $u = array(); - $s[] = $indent . 'var $idx = array(' . "\n"; - $indent = $indent . ' '; + $u = array(); + $v = array(); + $l = 0; + + $s[] = $indent . '/*'; + $s[] = $indent . ' * Index definitions'; + $s[] = $indent . ' */'; + $s[] = $indent . 'var $idx = array(' . "\n"; + $indent .= ' '; foreach ($this->idx[$table] as $name => $def) { - $type = $def['type']; - $cols = $def['cols']; - $v = $indent . "'" . $name . "' => array(\n"; - $indent = $indent . ' '; - $v = $v . $indent . "'type' => '$type',\n"; + $type = $def['type']; + $cols = $def['cols']; + $name = $indent . "'{$name}'"; + $l = max($l, strlen($name)); + $v[$name] = "array(\n"; + $indent .= ' '; + $v[$name] .= $indent . "'type' => '{$type}',\n"; if (is_array($cols)) { - $v = $v . $indent . "'cols' => array(\n"; - $indent = $indent . ' '; - $t = array(); + $v[$name] .= $indent . "'cols' => array(\n"; + $indent .= ' '; + $t = array(); foreach ($cols as $value) { $t[] = $indent . "'{$value}'"; } - $v = $v . implode($t,",\n") . "\n"; - $indent = substr($indent, 0, -4); - $v = $v . $indent . ")\n"; + $v[$name] .= implode(",\n", $t) . "\n"; + $indent = substr($indent, 0, -4); + $v[$name] .= $indent . ")\n"; } else { - $v = $v . $indent . "'cols' => '$cols'\n"; + $v[$name] = $v[$name] . $indent . "'cols' => '{$cols}'\n"; } - $indent = substr($indent, 0, -4); - $v = $v . $indent . ")"; - $u[] = $v; + $indent = substr($indent, 0, -4); + $v[$name] .= $indent . ")"; + } + + foreach ($v as $key => $value) { + $u[] = str_pad($key, $l, ' ', STR_PAD_RIGHT) + . ' => ' . $value; } - $s[] = implode($u,",\n\n") . "\n"; + $s[] = implode(",\n\n", $u) . "\n"; $indent = substr($indent, 0, -4); - $s[] = $indent . ");\n"; - } + $s[] = $indent . ");\n"; + } // end index generation // Write auto_inc_col if (isset($this->auto_inc_col[$table])) { - $s[] = $indent . 'var $auto_inc_col = ' - . "'{$this->auto_inc_col[$table]}';\n"; + $s[] = $indent . '/*'; + $s[] = $indent . ' * Auto-increment declaration'; + $s[] = $indent . ' */'; + $s[] = $indent . 'var $auto_inc_col = ' + . "'{$this->auto_inc_col[$table]}';\n"; } $indent = substr($indent, 0, -4); - $s[] = $indent . '}'; + $s[] = $indent . '}'; // Implode and return lines of class definition - return implode($s,"\n") . "\n"; - + return implode("\n", $s) . "\n"; + } -// }}} -// {{{ function buildTableClasses() + // }}} + // {{{ function buildTableClasses() /** * Returns a string containing all table class definitions in one file @@ -825,22 +895,25 @@ * The returned string contains the contents of a single php file with * definitions of DB_Table subclasses associated with all of the tables * in $this->tables. If $this->tables is initially null, method - * $this->getTableNames() is called internally to generate a list of - * table names. + * $this->getTableNames() is called internally to generate a list of + * table names. * - * The returned string includes the opening and closing <?php and ?> - * script elements, and the require_once line needed to include the + * The returned string includes the opening and closing <?php and ?> + * script elements, and the require_once line needed to include the * $this->extend_class (i.e., DB_Table or a subclass) that is being - * extended. To use, write this string to a new php file. + * extended. To use, write this string to a new php file. * * Usage: * <code> * $generator = DB_Table_Generator($db, $database); - * print $generator->buildTablesClasses(); + * echo $generator->buildTablesClasses(); * </code> - * + * + * @return mixed a string with all table class definitions, + * PEAR Error on failure + * @access public */ - function buildTableClasses() + function buildTableClasses() { // If $this->tables is null, call getTableNames() if (!$this->tables) { @@ -850,19 +923,25 @@ } } - $s = array(); - $s[] = "<?php"; + $s = array(); + $s[] = '<?php'; + $s[] = '/*'; + $s[] = ' * Include basic class'; + $s[] = ' */'; $s[] = "require_once '{$this->extends_file}';\n"; - foreach($this->tables as $table) { - $this->getTableDefinition($table); + foreach ($this->tables as $table) { + $return = $this->getTableDefinition($table); + if (PEAR::isError($return)) { + return $return; + } $s[] = $this->buildTableClass($table) . "\n"; } $s[] = '?>'; - return implode($s,"\n"); + return implode("\n", $s); } -// }}} -// {{{ function generateTableClassFiles() + // }}} + // {{{ function generateTableClassFiles() /** * Writes all table class definitions to separate files @@ -874,9 +953,9 @@ * </code> * * @return mixed true on success, PEAR Error on failure - * @access public + * @access public */ - function generateTableClassFiles() + function generateTableClassFiles() { // If $this->tables is null, call getTableNames() if (!$this->tables) { @@ -887,29 +966,40 @@ } // Write all table class definitions to separate files - foreach($this->tables as $table) { + foreach ($this->tables as $table) { $classname = $this->className($table); $filename = $this->classFileName($classname); $base = $this->class_write_path; if ($base) { if (!file_exists($base)) { - require_once 'System.php'; - System::mkdir(array('-p', $base)); + include_once 'System.php'; + if (!@System::mkdir(array('-p', $base))) { + return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE, + $base); + } + } - $filename = "$base/$filename"; + $filename = "{$base}/{$filename}"; } if (!file_exists($filename)) { - $s = array(); - $s[] = "<?php"; - $s[] = "require_once '{$this->extends_file}';\n"; + $s = array(); + $s[] = '<?php'; + $s[] = '/*'; + $s[] = ' * Include basic class'; + $s[] = ' */'; + $s[] = "require_once '{$this->extends_file}';\n"; $return = $this->getTableDefinition($table); if (PEAR::isError($return)) { return $return; } - $s[] = $this->buildTableClass($table) ; + $s[] = $this->buildTableClass($table); $s[] = '?>'; - $out = implode($s,"\n"); - $file = fopen( $filename, "w"); + $s[] = ''; + $out = implode("\n", $s); + if (!$file = @fopen($filename, 'wb')) { + return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE, + $filename); + } fputs($file, $out); fclose($file); } @@ -918,14 +1008,14 @@ return true; } -// }}} -// {{{ function generateDatabaseFile($object_name = null) + // }}} + // {{{ function generateDatabaseFile($object_name = null) /** * Writes a file to instantiate Table and Database objects * * After successful completion, a file named 'Database.php' will be - * have been created in the $this->class_write_path directory. This + * have been created in the $this->class_write_path directory. This * file should normally be included in application php scripts. It * can be renamed by the user. * @@ -936,186 +1026,248 @@ * $generator->generateDatabaseFile(); * </code> * - * @param string variable name for DB_Table_Database object - * @return void - * @access public + * @param string $object_name variable name for DB_Table_Database object + * + * @return mixed true on success, PEAR Error on failure + * @access public */ function generateDatabaseFile($object_name = null) { // Set name for DB_Table_Database object if ($object_name) { - $object_name = '$' . $object_name; + $object_name = "\${$object_name}"; } else { $object_name = '$db'; //default } $backend = strtoupper($this->backend); // 'DB' or 'MDB2' + if ('DB' == $backend) { + $dsn = $this->db->dsn; + } else { + $dsn = $this->db->getDSN('array'); + } + // Create array d[] containing lines of database php file - $d = array(); - $d[] = "<?php"; + $d = array(); + $d[] = '<?php'; + $d[] = '/*'; + $d[] = ' * Include basic classes'; + $d[] = ' */'; $d[] = "require_once '{$backend}.php';"; $d[] = "require_once 'DB/Table/Database.php';"; // Require_once statements for subclass definitions foreach ($this->tables as $table) { - $classname = $this->className($table); - $class_filename = $this->classFileName($classname); + $classname = $this->className($table); + $class_filename = $this->classFileName($classname); if ($this->class_include_path) { - $d[] = 'require_once ' . - "'{$this->class_include_path}/{$class_filename}';"; + $d[] = 'require_once ' + . "'{$this->class_include_path}/{$class_filename}';"; } else { $d[] = "require_once '{$class_filename}';"; } } - $d[] = ""; + $d[] = ''; - $d[] = '// NOTE: User must uncomment & edit code to create $dsn'; - $d[] = '# $phptype = ' . "'mysqli';"; - $d[] = '# $username = ' . "'root';"; - $d[] = '# $password = ' . "'password';"; - $d[] = '# $hostname = ' . "'localhost';"; - $d[] = '# $dsn = "$phptype://$username:$password@$hostname";'; - $d[] = ""; - - $d[] = '// Instantiate DB/MDB2 connection object $conn'; - $d[] = '$conn =& ' . $backend . '::connect($dsn);'; + $d[] = '/*'; + $d[] = ' * NOTE: User must uncomment & edit code to create $dsn'; + $d[] = ' */'; + $d[] = "//\$phptype = '{$dsn['phptype']}';"; + $d[] = "//\$username = '{$dsn['username']}';"; + $d[] = "//\$password = ''; // put your password here"; + $d[] = "//\$hostname = '{$dsn['hostspec']}';"; + $d[] = "//\$database = '{$dsn['database']}';"; + $d[] = "//\$create = false; // 'drop', 'safe', 'verify', 'alter'"; + $d[] = '//$dsn = "{$phptype}://{$username}:{$password}@{$hostname}' + . '/{$database}";'; + $d[] = ''; + + $d[] = '/*'; + $d[] = " * Instantiate {$backend} connection object \$conn"; + $d[] = ' */'; + $d[] = "\$conn =& {$backend}::connect(\$dsn);"; $d[] = 'if (PEAR::isError($conn)) {'; - $d[] = ' print "Error connecting to database server\n";'; - $d[] = ' print $conn->getMessage();'; + $d[] = ' echo "Error connecting to database server\n";'; + $d[] = ' echo $conn->getMessage();'; $d[] = ' die;'; $d[] = '}'; - $d[] = ""; + $d[] = ''; - $d[] = '// Create one instance of each DB_Table subclass'; + $d[] = '/*'; + $d[] = ' * Create one instance of each DB_Table subclass'; + $d[] = ' */'; foreach ($this->tables as $table) { $classname = $this->className($table); - $d[] = '$' . $table . " = new $classname(" - . '$conn, ' . "'{$table}');"; - } - $d[] = ""; - $d[] = '// Instantiate a parent DB_Table_Database object'; - $d[] = $object_name . ' = new DB_Table_Database($conn, ' - . "'{$this->name}');"; - $d[] = ""; + $d[] = "\${$table} = new {$classname}(" + . '$conn, ' . "'{$table}'" . ', $create);'; + $d[] = "if (PEAR::isError(\${$table}->error)) {"; + $d[] = ' echo "Can\'t create table object.\n";'; + $d[] = " echo \${$table}->error->getMessage();"; + $d[] = ' die;'; + $d[] = '}'; + + } + $d[] = ''; + + $d[] = '/*'; + $d[] = ' * Instantiate a parent DB_Table_Database object'; + $d[] = ' */'; + $d[] = "{$object_name} = new DB_Table_Database(\$conn, \$database);"; + $d[] = "if (PEAR::isError({$object_name}->error)) {"; + $d[] = ' echo "Can\'t create database object.\n";'; + $d[] = " echo {$object_name}->error->getMessage();"; + $d[] = ' die;'; + $d[] = '}'; + $d[] = ''; - $d[] = '// Add DB_Table objects to parent DB_Table_Database object'; + $d[] = '/*'; + $d[] = ' * Add DB_Table objects to parent DB_Table_Database object'; + $d[] = ' */'; foreach ($this->tables as $table) { $classname = $this->className($table); - $d[] = $object_name . '->addTable($' . $table . ');'; + + $d[] = "\$result = {$object_name}->addTable(\${$table});"; + $d[] = 'if (PEAR::isError($result)) {'; + $d[] = ' echo "Can\'t add table object to database object.\n";'; + $d[] = ' echo $result->getMessage();'; + $d[] = ' die;'; + $d[] = '}'; } - $d[] = ""; + $d[] = ''; - // Add foreign key references: If the name of an integer column + // Add foreign key references: If the name of an integer column // matches "/id$/i" (i.e., the names ends with id, ID, or Id), the // remainder of the name matches the name $rtable of another table, // and $rtable has an integer primary key, then the column is // assumed to be a foreign key that references $rtable. - $d[] = '// Add auto-guessed foreign references'; + $d[] = '/*'; + $d[] = ' * Add auto-guessed foreign references'; + $d[] = ' */'; foreach ($this->col as $table => $col) { foreach ($col as $col_name => $def) { - // Only consider integer columns - $ftype = $def['type']; - if (!in_array($ftype, array('integer','smallint','bigint'))) { - continue; - } - if (preg_match("/id$/i", $col_name)) { - $column_base = preg_replace('/_?id$/i', '', $col_name); - foreach ($this->tables as $rtable) { - if (!preg_match("/^{$rtable}$/i", $column_base)) { - continue; - } - if (preg_match("/^{$table}$/i", $column_base)) { - continue; - } - if (!isset($this->primary_key[$rtable])) { - continue; - } - $rkey = $this->primary_key[$rtable]; - if (is_array($rkey)) { - continue; - } - $rtype = $this->col[$rtable][$rkey]['type']; - if (!in_array($rtype, - array('integer','smallint','bigint'))) { - continue; - } - $d[] = $object_name - . "->addRef('$table', '$col_name', '$rtable');"; - } - } - } - } - $d[] = ""; - $d[] = '// Add any additional foreign key references here'; - $d[] = ""; - $d[] = '// Add any linking table declarations here'; - $d[] = '// Uncomment next line to add all possible linking tables;'; - $d[] = '# ' . $object_name . '->addAllLinks();'; - $d[] = ""; + // Only consider integer columns + $ftype = $def['type']; + if (!in_array($ftype, array('integer','smallint','bigint'))) { + continue; + } + if (preg_match("/id$/i", $col_name)) { + $column_base = preg_replace('/_?id$/i', '', $col_name); + foreach ($this->tables as $rtable) { + if (!preg_match("/^{$rtable}$/i", $column_base)) { + continue; + } + if (preg_match("/^{$table}$/i", $column_base)) { + continue; + } + if (!isset($this->primary_key[$rtable])) { + continue; + } + $rkey = $this->primary_key[$rtable]; + if (is_array($rkey)) { + continue; + } + $rtype = $this->col[$rtable][$rkey]['type']; + if (!in_array($rtype, + array('integer','smallint','bigint'))) { + continue; + } + $d[] = "\$result = {$object_name}->addRef('{$table}', " + . "'{$col_name}', '{$rtable}');"; + $d[] = 'if (PEAR::isError($result)) {'; + $d[] = ' echo "Can\'t add foreign key reference.\n";'; + $d[] = ' echo $result->getMessage();'; + $d[] = ' die;'; + $d[] = '}'; + } + } + } + } + $d[] = ''; + $d[] = '/*'; + $d[] = ' * Add any additional foreign key references here'; + $d[] = ' *'; + $d[] = ' * Add any linking table declarations here'; + $d[] = ' * Uncomment next line to add all possible linking tables;'; + $d[] = ' */'; + $d[] = "//\$result = {$object_name}->addAllLinks();"; + $d[] = '//if (PEAR::isError($result)) {'; + $d[] = '// echo "Can\'t add linking tables.\n";'; + $d[] = '// echo $result->getMessage();'; + $d[] = '// die;'; + $d[] = '//}'; + $d[] = ''; // Closing script element - $d[] = "?>"; + $d[] = '?>'; + $d[] = ''; // Open and write file $base = $this->class_write_path; if ($base) { if (!file_exists($base)) { - require_once 'System.php'; - System::mkdir(array('-p', $base)); + include_once 'System.php'; + if (!@System::mkdir(array('-p', $base))) { + return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE, $base); + } } - $filename = $base . "/Database.php"; + $filename = $base . '/Database.php'; } else { - $filename = "Database.php"; + $filename = 'Database.php'; + } + if (!$file = @fopen($filename, 'wb')) { + return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE, $filename); } - $file = fopen($filename, "w"); $out = implode("\n", $d); fputs($file, $out); fclose($file); + + return true; } -// }}} -// {{{ function className($table) + // }}} + // {{{ function className($table) /** - * Convert a table name into a class name + * Convert a table name into a class name * - * Converts all non-alphanumeric characters to '_', capitalizes - * first letter, and adds $this->class_suffix to end. Override + * Converts all non-alphanumeric characters to '_', capitalizes + * first letter, and adds $this->class_suffix to end. Override * this if you want something else. * - * @param string $class_name name of table - * @return string class name; - * @access public + * @param string $table name of table + * + * @return string class name; + * @access public */ function className($table) { - $name = preg_replace('/[^A-Z0-9]/i','_',ucfirst(trim($table))); + $name = preg_replace('/[^A-Z0-9]/i', '_', ucfirst(trim($table))); return $name . $this->class_suffix; } - -// }}} -// {{{ function classFileName($class_name) - + + // }}} + // {{{ function classFileName($class_name) + /** * Returns the path to a file containing a class definition * * Appends '.php' to class name. * - * @param string $class_name name of class - * @return string file name - * @access public + * @param string $class_name name of class + * + * @return string file name + * @access public */ function classFileName($class_name) { - $filename = $class_name . ".php" ; + $filename = $class_name . '.php'; return $filename; - } -// }}} + // }}} } // }}}