home *** CD-ROM | disk | FTP | other *** search
- <?php
-
- /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
-
- /**
- * DB_Table_Generator - Generates DB_Table subclass skeleton code
- *
- * Parts of this class were adopted from the DB_DataObject PEAR package.
- *
- * PHP versions 4 and 5
- *
- * LICENSE:
- *
- * Copyright (c) 1997-2007, Paul M. Jones <pmjones@php.net>
- * Alan Knowles <alan@akbkhome.com>
- * David C. Morse <morse@php.net>
- * Mark Wiesemann <wiesemann@php.net>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The names of the authors may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * @category Database
- * @package DB_Table
- * @author Alan Knowles <alan@akbkhome.com>
- * @author David C. Morse <morse@php.net>
- * @license http://opensource.org/licenses/bsd-license.php New BSD License
- * @version CVS: $Id: Generator.php,v 1.17 2008/05/14 18:36:27 wiesemann Exp $
- * @link http://pear.php.net/package/DB_Table
- */
-
- // {{{ Includes
-
- /**#@+
- * Include basic classes
- */
- /**
- * The PEAR class (used for errors)
- */
- require_once 'PEAR.php';
-
- /**
- * DB_Table table abstraction class
- */
- require_once 'DB/Table.php';
-
- /**
- * 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
- */
- define('DB_TABLE_GENERATOR_ERR_DB_OBJECT', -301);
-
- /**
- * Parameter is not a DB/MDB2 object
- */
- 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.
- */
- $GLOBALS['_DB_TABLE_GENERATOR']['default_error'] = array(
- DB_TABLE_GENERATOR_ERR_DB_OBJECT =>
- 'Invalid DB/MDB2 object parameter. Function',
- DB_TABLE_GENERATOR_ERR_INDEX_COL =>
- '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'])) {
- $GLOBALS['_DB_TABLE_GENERATOR']['error'] = array();
- }
- foreach ($GLOBALS['_DB_TABLE_GENERATOR']['default_error'] as $code => $message) {
- if (!array_key_exists($code, $GLOBALS['_DB_TABLE_GENERATOR']['error'])) {
- $GLOBALS['_DB_TABLE_GENERATOR']['error'][$code] = $message;
- }
- }
-
- // }}}
- // {{{ class DB_Table_Generator
-
- /**
- * class DB_Table_Generator - Generates DB_Table subclass skeleton code
- *
- * 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
- * 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.
- *
- * 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
- * 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
- * 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 the database of interest, and execute the following code:
- * <code>
- * $generator = new DB_Table_Generator($db, $database);
- * $generator->class_write_path = $class_write_path;
- * $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
- * 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().
- *
- * 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
- * tables named 'table1', 'table2', and 'table3' as follows:
- * <code>
- * $generator = new DB_Table_Generator($db, $database);
- * $generator->class_write_path = $class_write_path;
- * $generator->tables = array('table1', 'table2', 'table3');
- * $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
- * 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 Alan Knowles <alan@akbkhome.com>
- * @author David C. Morse <morse@php.net>
- * @license http://www.gnu.org/copyleft/lesser.html LGPL
- * @version Release: 1.5.5
- * @link http://pear.php.net/package/DB_Table
- */
- class DB_Table_Generator
- {
-
- // {{{ Properties
-
- /**
- * Name of the database
- *
- * @var string
- * @access public
- */
- var $name = null;
-
- /**
- * The PEAR DB/MDB2 object that connects to the database.
- *
- * @var object
- * @access private
- */
- var $db = null;
-
- /**
- * The backend type. May have values 'db' or 'mdb2'
- *
- * @var string
- * @access private
- */
- var $backend = null;
-
- /**
- * If there is an error on instantiation, this captures that error.
- *
- * This property is used only for errors encountered in the constructor
- * at instantiation time. To check if there was an instantiation error...
- *
- * <code>
- * $obj =& new DB_Table_Generator();
- * if ($obj->error) {
- * // ... error handling code here ...
- * }
- * </code>
- *
- * @var object PEAR_Error
- * @access public
- */
- var $error = null;
-
- /**
- * Numerical array of table name strings
- *
- * @var array
- * @access public
- */
- var $tables = array();
-
- /**
- * Class being extended (DB_Table or generic subclass)
- *
- * @var string
- * @access public
- */
- var $extends = 'DB_Table';
-
- /**
- * Path to definition of the class $this->extends
- *
- * @var string
- * @access public
- */
- var $extends_file = 'DB/Table.php';
-
- /**
- * Suffix to add to table names to obtain corresponding class names
- *
- * @var string
- * @access public
- */
- var $class_suffix = "_Table";
-
- /**
- * Path to directory in which subclass definitions should be written
- *
- * Value should not include a trailing "/".
- *
- * @var string
- * @access public
- */
- var $class_write_path = '';
-
- /**
- * Include path to subclass definition files from database file
- *
- * 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
- * to values other than the empty string.
- *
- * @var string
- * @access public
- */
- var $class_include_path = '';
-
- /**
- * 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
- * property of a DB_Table object
- *
- * @var array
- * @access public
- */
- var $col = array();
-
- /**
- * Array of index/constraint definitions.
- *
- * 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
- * @access public
- */
- var $idx = array();
-
- /**
- * Array of auto_increment column names
- *
- * Array $this->auto_inc_col[table_name] = auto-increment column
- *
- * @var array
- * @access public
- */
- var $auto_inc_col = array();
-
- /**
- * Array of primary keys
- *
- * @var array
- * @access public
- */
- var $primary_key = array();
-
- /**
- * 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 '%'.
- *
- * @var string
- * @access public
- */
- var $idxname_format = '%s';
-
- // }}}
- // {{{ function DB_Table_Generator(&$db, $name)
-
- /**
- * Constructor
- *
- * 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
- *
- * @return object DB_Table_Generator
- * @access public
- */
- function DB_Table_Generator(&$db, $name)
- {
- // Is $db an DB/MDB2 object or null?
- if (is_a($db, 'db_common')) {
- $this->backend = 'db';
- } 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');
- return;
- }
- $this->db =& $db;
- $this->name = $name;
-
- }
-
- // }}}
- // {{{ 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
- * 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).
- *
- * @return object PEAR_Error
- * @access public
- * @static
- */
- 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];
-
- // add any additional error text
- if ($extra) {
- $text .= ' ' . $extra;
- }
-
- // done!
- $error = PEAR::throwError($text, $code);
- return $error;
- }
-
- // }}}
- // {{{ 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 string $message Only used if $key is not an array.
- *
- * @return void
- * @access public
- */
- function setErrorMessage($code, $message = null)
- {
- if (is_array($code)) {
- foreach ($code as $single_code => $single_message) {
- $GLOBALS['_DB_TABLE_GENERATOR']['error'][$single_code]
- = $single_message;
- }
- } else {
- $GLOBALS['_DB_TABLE_GENERATOR']['error'][$code] = $message;
- }
- }
-
- // }}}
- // {{{ 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.
- *
- * @return mixed true on success, PEAR Error on failure
- * @access public
- */
- function getTableNames()
- {
-
- if ($this->backend == 'db') {
- // try getting a list of schema tables first. (postgres)
- $this->db->expectError(DB_ERROR_UNSUPPORTED);
- $this->tables = $this->db->getListOf('schema.tables');
- $this->db->popExpect();
- if (PEAR::isError($this->tables)) {
- // try a list of tables, not qualified by 'schema'
- $this->db->expectError(DB_ERROR_UNSUPPORTED);
- $this->tables = $this->db->getListOf('tables');
- $this->db->popExpect();
- }
- } else {
- // 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'
- $this->db->setOption('portability', $portability);
- }
- if (PEAR::isError($this->tables)) {
- $error = $this->tables;
- $this->tables = null;
- return $error;
- } else {
- $this->tables = array_map(array($this, 'tableName'),
- $this->tables);
- return true;
- }
- }
-
- // }}}
- // {{{ function getTableDefinition($table)
-
- /**
- * Gets column and index definitions by querying database
- *
- * 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
- * the DB_Table_Manager class to obtain index definitions.
- *
- * @param string $table name of table
- *
- * @return mixed true on success, PEAR Error on failure
- * @access public
- */
- 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];
- }
- }
- */
-
- if ($this->backend == 'db') {
-
- $defs = $this->db->tableInfo($table);
- if (PEAR::isError($defs)) {
- return $defs;
- }
- $this->columns[$table] = $defs;
-
- } else {
-
- // 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');
-
- // Columns
- $defs = $this->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) {
- if (isset($defs[$k]['length'])) {
- $defs[$k]['len'] = $defs[$k]['length'];
- }
- }
-
- $this->columns[$table] = $defs;
-
- // Temporarily set 'idxname_format' MDB2 option to $this->idx_format
- $idxname_format = $this->db->getOption('idxname_format');
- $this->db->setOption('idxname_format', $this->idxname_format);
- }
-
- // 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) {
-
- $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'))) {
- if (isset($t['len'])) {
- $col['size'] = (int) $t['len'];
- } elseif ($col['type'] == 'varchar') {
- $col['size'] = 255; // default length
- } elseif ($col['type'] == 'char') {
- $col['size'] = 128; // default length
- } elseif ($col['type'] == 'decimal') {
- $col['size'] = 15; // default length
- }
- if ($col['type'] == 'decimal') {
- $col['scope'] = 2;
- }
- }
- if (isset($t['notnull'])) {
- if ($t['notnull']) {
- $col['require'] = true;
- }
- }
- if (isset($t['autoincrement'])) {
- $this->auto_inc_col[$table] = $name;
- }
- if (isset($t['flags'])) {
- $flags = $t['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,
- array('smallint', 'integer', 'bigint'))) {
- $default = (int) $default;
- } elseif (in_array($type, array('single', 'double'))) {
- $default = (float) $default;
- } elseif ($type == 'boolean') {
- $default = (int) $default ? 1 : 0;
- }
- $col['default'] = $default;
- }
- }
- $this->col[$table][$name] = $col;
-
- }
-
- // Make array with lower case column array names as keys
- $col_lc = array();
- foreach ($this->col[$table] as $name => $def) {
- $name_lc = strtolower($name);
- $col_lc[$name_lc] = $name;
- }
-
- // Constraints/Indexes
- $DB_indexes = DB_Table_Manager::getIndexes($this->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.
- foreach ($DB_indexes as $type => $indexes) {
- foreach ($indexes as $name => $fields) {
- foreach ($fields as $key => $field) {
-
- // If index column is not a valid column name
- if (!array_key_exists($field, $this->col[$table])) {
-
- // Try a case-insensitive match
- $field_lc = strtolower($field);
- if (isset($col_lc[$field_lc])) {
- $correct = $col_lc[$field_lc];
- $DB_indexes[$type][$name][$key]
- = $correct;
- } else {
- $code = DB_TABLE_GENERATOR_ERR_INDEX_COL;
- $return =&
- DB_Table_Generator::throwError($code, $field);
- }
-
- }
- }
- }
- }
-
- // Generate index definitions, if any, as php code
- $n_idx = 0;
- $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]['type'] = $type;
- if (count($fields) == 1) {
- $key = $fields[0];
- } else {
- $key = array();
- foreach ($fields as $value) {
- $key[] = $value;
- }
- }
- $this->idx[$table][$name]['cols'] = $key;
- if ($type == 'primary') {
- $this->primary_key[$table] = $key;
- }
- }
- }
- }
-
- if ($this->backend == 'mdb2') {
- // Restore original MDB2 'idxname_format' and 'portability'
- $this->db->setOption('idxname_format', $idxname_format);
- $this->db->setOption('portability', $portability);
- }
-
- return true;
- }
-
- // }}}
- // {{{ function buildTableClass($table, $indent = '')
-
- /**
- * Returns one skeleton DB_Table subclass definition, as php code
- *
- * The returned subclass definition string contains values for the
- * $col (column), $idx (index) and $auto_inc_col properties, with
- * no method definitions.
- *
- * @param string $table name of table
- * @param string $indent string of whitespace for base indentation
- *
- * @return string skeleton DB_Table subclass definition
- * @access public
- */
- function buildTableClass($table, $indent = '')
- {
- $s = array();
- $idx = array();
- $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) {
-
- // Generate DB_Table column definitions as php code
- $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}'";
- } elseif (is_bool($value)) {
- $value = $value ? 'true' : 'false';
- } else {
- $value = (string) $value;
- }
- $l1 = max($l1, strlen($key) + 2);
- $t1[] = array("'{$key}'", $value) ;
- }
- 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
-
- 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";
-
- // Generate index definitions, if any, as php code
- if (count($this->idx[$table]) > 0) {
- $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'];
- $name = $indent . "'{$name}'";
- $l = max($l, strlen($name));
- $v[$name] = "array(\n";
- $indent .= ' ';
- $v[$name] .= $indent . "'type' => '{$type}',\n";
- if (is_array($cols)) {
- $v[$name] .= $indent . "'cols' => array(\n";
- $indent .= ' ';
- $t = array();
- foreach ($cols as $value) {
- $t[] = $indent . "'{$value}'";
- }
- $v[$name] .= implode(",\n", $t) . "\n";
- $indent = substr($indent, 0, -4);
- $v[$name] .= $indent . ")\n";
- } else {
- $v[$name] = $v[$name] . $indent . "'cols' => '{$cols}'\n";
- }
- $indent = substr($indent, 0, -4);
- $v[$name] .= $indent . ")";
- }
-
- 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";
- } // end index generation
-
- // Write auto_inc_col
- if (isset($this->auto_inc_col[$table])) {
- $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 . '}';
-
- // Implode and return lines of class definition
- return implode("\n", $s) . "\n";
-
- }
-
- // }}}
- // {{{ function buildTableClasses()
-
- /**
- * Returns a string containing all table class definitions in one file
- *
- * 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.
- *
- * 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.
- *
- * Usage:
- * <code>
- * $generator = new DB_Table_Generator($db, $database);
- * echo $generator->buildTablesClasses();
- * </code>
- *
- * @return mixed a string with all table class definitions,
- * PEAR Error on failure
- * @access public
- */
- function buildTableClasses()
- {
- // If $this->tables is null, call getTableNames()
- if (!$this->tables) {
- $return = $this->getTableNames();
- if (PEAR::isError($return)) {
- return $return;
- }
- }
-
- $s = array();
- $s[] = '<?php';
- $s[] = '/*';
- $s[] = ' * Include basic class';
- $s[] = ' */';
- $s[] = "require_once '{$this->extends_file}';\n";
- foreach ($this->tables as $table) {
- $return = $this->getTableDefinition($table);
- if (PEAR::isError($return)) {
- return $return;
- }
- $s[] = $this->buildTableClass($table) . "\n";
- }
- $s[] = '?>';
- return implode("\n", $s);
- }
-
- // }}}
- // {{{ function generateTableClassFiles()
-
- /**
- * Writes all table class definitions to separate files
- *
- * Usage:
- * <code>
- * $generator = new DB_Table_Generator($db, $database);
- * $generator->generateTableClassFiles();
- * </code>
- *
- * @return mixed true on success, PEAR Error on failure
- * @access public
- */
- function generateTableClassFiles()
- {
- // If $this->tables is null, call getTableNames()
- if (!$this->tables) {
- $return = $this->getTableNames();
- if (PEAR::isError($return)) {
- return $return;
- }
- }
-
- // Write all table class definitions to separate files
- foreach ($this->tables as $table) {
- $classname = $this->className($table);
- $filename = $this->classFileName($classname);
- $base = $this->class_write_path;
- if ($base) {
- if (!file_exists($base)) {
- include_once 'System.php';
- if (!@System::mkdir(array('-p', $base))) {
- return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE,
- $base);
- }
-
- }
- $filename = "{$base}/{$filename}";
- }
- if (!file_exists($filename)) {
- $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[] = '?>';
- $s[] = '';
- $out = implode("\n", $s);
- if (!$file = @fopen($filename, 'wb')) {
- return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE,
- $filename);
- }
- fputs($file, $out);
- fclose($file);
- }
- }
-
- return true;
- }
-
- // }}}
- // {{{ 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
- * file should normally be included in application php scripts. It
- * can be renamed by the user.
- *
- * Usage:
- * <code>
- * $generator = new DB_Table_Generator($db, $database);
- * $generator->generateTableClassFiles();
- * $generator->generateDatabaseFile();
- * </code>
- *
- * @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}";
- } 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[] = '/*';
- $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);
- if ($this->class_include_path) {
- $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[] = ' */';
- $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[] = ' 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[] = ' */';
- foreach ($this->tables as $table) {
- $classname = $this->className($table);
-
- $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[] = '/*';
- $d[] = ' * Add DB_Table objects to parent DB_Table_Database object';
- $d[] = ' */';
- foreach ($this->tables as $table) {
- $classname = $this->className($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[] = '';
-
- // 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[] = '/*';
- $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[] = "\$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[] = '';
-
- // Open and write file
- $base = $this->class_write_path;
- if ($base) {
- if (!file_exists($base)) {
- include_once 'System.php';
- if (!@System::mkdir(array('-p', $base))) {
- return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE, $base);
- }
- }
- $filename = $base . '/Database.php';
- } else {
- $filename = 'Database.php';
- }
- if (!$file = @fopen($filename, 'wb')) {
- return $this->throwError(DB_TABLE_GENERATOR_ERR_FILE, $filename);
- }
- $out = implode("\n", $d);
- fputs($file, $out);
- fclose($file);
-
- return true;
- }
-
- // }}}
- // {{{ function className($table)
-
- /**
- * 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
- * this if you want something else.
- *
- * @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)));
- return $name . $this->class_suffix;
- }
-
- // }}}
- // {{{ function tableName($table)
-
- /**
- * Returns a valid variable name from a table name
- *
- * Converts all non-alphanumeric characters to '_'. Override
- * this if you want something else.
- *
- * @param string $table name of table
- *
- * @return string variable name;
- * @access public
- */
- function tableName($table)
- {
- return preg_replace('/[^A-Z0-9]/i', '_', trim($table));
- }
-
- // }}}
- // {{{ 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
- */
- function classFileName($class_name)
- {
- $filename = $class_name . '.php';
- return $filename;
- }
-
- // }}}
-
- }
- // }}}
-