forked from GNUsocial/gnu-social
7d8e199a3f
Now there's definitely no PHP4 support whatsoever, if there even was little of it before this commit.
1778 lines
61 KiB
PHP
Executable File
1778 lines
61 KiB
PHP
Executable File
<?php
|
||
/**
|
||
* Generation tools for DB_DataObject
|
||
*
|
||
* PHP versions 4 and 5
|
||
*
|
||
* LICENSE: This source file is subject to version 3.01 of the PHP license
|
||
* that is available through the world-wide-web at the following URI:
|
||
* http://www.php.net/license/3_01.txt. If you did not receive a copy of
|
||
* the PHP License and are unable to obtain it through the web, please
|
||
* send a note to license@php.net so we can mail you a copy immediately.
|
||
*
|
||
* @category Database
|
||
* @package DB_DataObject
|
||
* @author Alan Knowles <alan@akbkhome.com>
|
||
* @copyright 1997-2006 The PHP Group
|
||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
||
* @version CVS: $Id: Generator.php 327926 2012-10-08 02:42:09Z alan_k $
|
||
* @link http://pear.php.net/package/DB_DataObject
|
||
*/
|
||
|
||
/*
|
||
* Security Notes:
|
||
* This class uses eval to create classes on the fly.
|
||
* The table name and database name are used to check the database before writing the
|
||
* class definitions, we now check for quotes and semi-colon's in both variables
|
||
* so I cant see how it would be possible to generate code even if
|
||
* for some crazy reason you took the classname and table name from User Input.
|
||
*
|
||
* If you consider that wrong, or can prove it.. let me know!
|
||
*/
|
||
|
||
/**
|
||
*
|
||
* Config _$ptions
|
||
* [DB_DataObject]
|
||
* ; optional default = DB/DataObject.php
|
||
* extends_location =
|
||
* ; optional default = DB_DataObject
|
||
* extends =
|
||
* ; alter the extends field when updating a class (defaults to only replacing DB_DataObject)
|
||
* generator_class_rewrite = ANY|specific_name // default is DB_DataObject
|
||
*
|
||
*/
|
||
|
||
/**
|
||
* Needed classes
|
||
* We lazy load here, due to problems with the tests not setting up include path correctly.
|
||
* FIXME!
|
||
*/
|
||
class_exists('DB_DataObject') ? '' : require_once 'DB/DataObject.php';
|
||
//require_once('Config.php');
|
||
|
||
/**
|
||
* Generator class
|
||
*
|
||
* @package DB_DataObject
|
||
*/
|
||
class DB_DataObject_Generator extends DB_DataObject
|
||
{
|
||
/* =========================================================== */
|
||
/* Utility functions - for building db config files */
|
||
/* =========================================================== */
|
||
|
||
/**
|
||
* Array of table names
|
||
*
|
||
* @var array
|
||
* @access private
|
||
*/
|
||
var $tables;
|
||
|
||
/**
|
||
* associative array table -> array of table row objects
|
||
*
|
||
* @var array
|
||
* @access private
|
||
*/
|
||
var $_definitions;
|
||
|
||
/**
|
||
* active table being output
|
||
*
|
||
* @var string
|
||
* @access private
|
||
*/
|
||
var $table; // active tablename
|
||
|
||
/**
|
||
* links (generated)
|
||
*
|
||
* @var array
|
||
* @access private
|
||
*/
|
||
var $_fkeys; // active tablename
|
||
|
||
/**
|
||
* The 'starter' = call this to start the process
|
||
*
|
||
* @access public
|
||
* @return none
|
||
*/
|
||
function start()
|
||
{
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
$db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
|
||
|
||
$databases = array();
|
||
foreach($options as $k=>$v) {
|
||
if (substr($k,0,9) == 'database_') {
|
||
$databases[substr($k,9)] = $v;
|
||
}
|
||
}
|
||
|
||
if (isset($options['database'])) {
|
||
if ($db_driver == 'DB') {
|
||
require_once 'DB.php';
|
||
$dsn = DB::parseDSN($options['database']);
|
||
} else {
|
||
require_once 'MDB2.php';
|
||
$dsn = MDB2::parseDSN($options['database']);
|
||
}
|
||
|
||
if (!isset($database[$dsn['database']])) {
|
||
$databases[$dsn['database']] = $options['database'];
|
||
}
|
||
}
|
||
|
||
foreach($databases as $databasename => $database) {
|
||
if (!$database) {
|
||
continue;
|
||
}
|
||
$this->debug("CREATING FOR $databasename\n");
|
||
$class = get_class($this);
|
||
$t = new $class;
|
||
$t->_database_dsn = $database;
|
||
|
||
|
||
$t->_database = $databasename;
|
||
if ($db_driver == 'DB') {
|
||
require_once 'DB.php';
|
||
$dsn = DB::parseDSN($database);
|
||
} else {
|
||
require_once 'MDB2.php';
|
||
$dsn = MDB2::parseDSN($database);
|
||
}
|
||
|
||
if (($dsn['phptype'] == 'sqlite') && is_file($databasename)) {
|
||
$t->_database = basename($t->_database);
|
||
}
|
||
$t->_createTableList();
|
||
$t->_createForiegnKeys();
|
||
|
||
foreach(get_class_methods($class) as $method) {
|
||
if (substr($method,0,8 ) != 'generate') {
|
||
continue;
|
||
}
|
||
$this->debug("calling $method");
|
||
$t->$method();
|
||
}
|
||
}
|
||
$this->debug("DONE\n\n");
|
||
}
|
||
|
||
/**
|
||
* Output File was config object, now just string
|
||
* Used to generate the Tables
|
||
*
|
||
* @var string outputbuffer for table definitions
|
||
* @access private
|
||
*/
|
||
var $_newConfig;
|
||
|
||
/**
|
||
* Build a list of tables;
|
||
* and store it in $this->tables and $this->_definitions[tablename];
|
||
*
|
||
* @access private
|
||
* @return none
|
||
*/
|
||
function _createTableList()
|
||
{
|
||
$this->_connect();
|
||
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
|
||
|
||
|
||
$__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
|
||
|
||
$db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
|
||
$is_MDB2 = ($db_driver != 'DB') ? true : false;
|
||
|
||
if (is_object($__DB) && is_a($__DB , 'PEAR_Error')) {
|
||
return PEAR::raiseError($__DB->toString(), null, PEAR_ERROR_DIE);
|
||
}
|
||
|
||
if (!$is_MDB2) {
|
||
// try getting a list of schema tables first. (postgres)
|
||
$__DB->expectError(DB_ERROR_UNSUPPORTED);
|
||
$this->tables = $__DB->getListOf('schema.tables');
|
||
$__DB->popExpect();
|
||
} else {
|
||
/**
|
||
* set portability and some modules to fetch the informations
|
||
*/
|
||
$db_options = PEAR::getStaticProperty('MDB2','options');
|
||
if (empty($db_options)) {
|
||
$__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
|
||
}
|
||
|
||
$__DB->loadModule('Manager');
|
||
$__DB->loadModule('Reverse');
|
||
}
|
||
|
||
if ((empty($this->tables) || (is_object($this->tables) && is_a($this->tables , 'PEAR_Error')))) {
|
||
//if that fails fall back to clasic tables list.
|
||
if (!$is_MDB2) {
|
||
// try getting a list of schema tables first. (postgres)
|
||
$__DB->expectError(DB_ERROR_UNSUPPORTED);
|
||
$this->tables = $__DB->getListOf('tables');
|
||
$__DB->popExpect();
|
||
} else {
|
||
$this->tables = $__DB->manager->listTables();
|
||
$sequences = $__DB->manager->listSequences();
|
||
foreach ($sequences as $k => $v) {
|
||
$this->tables[] = $__DB->getSequenceName($v);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (is_object($this->tables) && is_a($this->tables , 'PEAR_Error')) {
|
||
return PEAR::raiseError($this->tables->toString(), null, PEAR_ERROR_DIE);
|
||
}
|
||
|
||
// build views as well if asked to.
|
||
if (!empty($options['build_views'])) {
|
||
if (!$is_MDB2) {
|
||
$views = $__DB->getListOf(is_string($options['build_views']) ?
|
||
$options['build_views'] : 'views');
|
||
} else {
|
||
$views = $__DB->manager->listViews();
|
||
}
|
||
if (is_object($views) && is_a($views,'PEAR_Error')) {
|
||
return PEAR::raiseError(
|
||
'Error getting Views (check the PEAR bug database for the fix to DB), ' .
|
||
$views->toString(),
|
||
null,
|
||
PEAR_ERROR_DIE
|
||
);
|
||
}
|
||
$this->tables = array_merge ($this->tables, $views);
|
||
}
|
||
|
||
// declare a temporary table to be filled with matching tables names
|
||
$tmp_table = array();
|
||
|
||
|
||
foreach($this->tables as $table) {
|
||
if (isset($options['generator_include_regex']) &&
|
||
!preg_match($options['generator_include_regex'],$table)) {
|
||
$this->debug("SKIPPING (generator_include_regex) : $table");
|
||
continue;
|
||
}
|
||
|
||
if (isset($options['generator_exclude_regex']) &&
|
||
preg_match($options['generator_exclude_regex'],$table)) {
|
||
continue;
|
||
}
|
||
|
||
$strip = empty($options['generator_strip_schema']) ? false : $options['generator_strip_schema'];
|
||
$strip = is_numeric($strip) ? (bool) $strip : $strip;
|
||
$strip = (is_string($strip) && strtolower($strip) == 'true') ? true : $strip;
|
||
|
||
// postgres strip the schema bit from the
|
||
if (!empty($strip) ) {
|
||
|
||
if (!is_string($strip) || preg_match($strip, $table)) {
|
||
$bits = explode('.', $table,2);
|
||
$table = $bits[0];
|
||
if (count($bits) > 1) {
|
||
$table = $bits[1];
|
||
}
|
||
}
|
||
}
|
||
$this->debug("EXTRACTING : $table");
|
||
|
||
$quotedTable = !empty($options['quote_identifiers_tableinfo']) ?
|
||
$__DB->quoteIdentifier($table) : $table;
|
||
|
||
if (!$is_MDB2) {
|
||
|
||
$defs = $__DB->tableInfo($quotedTable);
|
||
} else {
|
||
$defs = $__DB->reverse->tableInfo($quotedTable);
|
||
// rename the length value, so it matches db's return.
|
||
|
||
}
|
||
|
||
if (is_object($defs) && is_a($defs,'PEAR_Error')) {
|
||
// running in debug mode should pick this up as a big warning..
|
||
$this->debug("Error reading tableInfo: $table");
|
||
$this->raiseError('Error reading tableInfo, '. $defs->toString());
|
||
continue;
|
||
}
|
||
// cast all definitions to objects - as we deal with that better.
|
||
|
||
|
||
|
||
foreach($defs as $def) {
|
||
if (!is_array($def)) {
|
||
continue;
|
||
}
|
||
// rename the length value, so it matches db's return.
|
||
if (isset($def['length']) && !isset($def['len'])) {
|
||
$def['len'] = $def['length'];
|
||
}
|
||
$this->_definitions[$table][] = (object) $def;
|
||
|
||
}
|
||
// we find a matching table, just store it into a temporary array
|
||
$tmp_table[] = $table;
|
||
|
||
|
||
}
|
||
// the temporary table array is now the right one (tables names matching
|
||
// with regex expressions have been removed)
|
||
$this->tables = $tmp_table;
|
||
|
||
//print_r($this->_definitions);
|
||
}
|
||
|
||
/**
|
||
* Auto generation of table data.
|
||
*
|
||
* it will output to db_oo_{database} the table definitions
|
||
*
|
||
* @access private
|
||
* @return none
|
||
*/
|
||
function generateDefinitions()
|
||
{
|
||
$this->debug("Generating Definitions file: ");
|
||
if (!$this->tables) {
|
||
$this->debug("-- NO TABLES -- \n");
|
||
return;
|
||
}
|
||
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
|
||
|
||
//$this->_newConfig = new Config('IniFile');
|
||
$this->_newConfig = '';
|
||
foreach($this->tables as $this->table) {
|
||
$this->_generateDefinitionsTable();
|
||
}
|
||
$this->_connect();
|
||
// dont generate a schema if location is not set
|
||
// it's created on the fly!
|
||
if (empty($options['schema_location']) && empty($options["ini_{$this->_database}"]) ) {
|
||
return;
|
||
}
|
||
if (!empty($options['generator_no_ini'])) { // built in ini files..
|
||
return;
|
||
}
|
||
$base = @$options['schema_location'];
|
||
if (isset($options["ini_{$this->_database}"])) {
|
||
$file = $options["ini_{$this->_database}"];
|
||
} else {
|
||
$file = "{$base}/{$this->_database}.ini";
|
||
}
|
||
|
||
if (!file_exists(dirname($file))) {
|
||
require_once 'System.php';
|
||
System::mkdir(array('-p','-m',0755,dirname($file)));
|
||
}
|
||
$this->debug("Writing ini as {$file}\n");
|
||
//touch($file);
|
||
$tmpname = tempnam(session_save_path(),'DataObject_');
|
||
//print_r($this->_newConfig);
|
||
$fh = fopen($tmpname,'w');
|
||
if (!$fh) {
|
||
return PEAR::raiseError(
|
||
"Failed to create temporary file: $tmpname\n".
|
||
"make sure session.save_path is set and is writable\n"
|
||
,null, PEAR_ERROR_DIE);
|
||
}
|
||
fwrite($fh,$this->_newConfig);
|
||
fclose($fh);
|
||
$perms = file_exists($file) ? fileperms($file) : 0755;
|
||
// windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
|
||
|
||
if (!@rename($tmpname, $file)) {
|
||
unlink($file);
|
||
rename($tmpname, $file);
|
||
}
|
||
chmod($file,$perms);
|
||
//$ret = $this->_newConfig->writeInput($file,false);
|
||
|
||
//if (PEAR::isError($ret) ) {
|
||
// return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE);
|
||
// }
|
||
}
|
||
/**
|
||
* create the data for Foreign Keys (for links.ini)
|
||
* Currenly only works with mysql / mysqli / posgtreas
|
||
* to use, you must set option: generate_links=true
|
||
*
|
||
* @author Pascal Sch<63>ni
|
||
*/
|
||
|
||
function _createForiegnKeys()
|
||
{
|
||
$options = PEAR::getStaticProperty('DB_DataObject','options');
|
||
if (empty($options['generate_links'])) {
|
||
return false;
|
||
}
|
||
$__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
|
||
if (!in_array($__DB->phptype, array('mysql', 'mysqli', 'pgsql'))) {
|
||
echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
|
||
return; // cant handle non-mysql introspection for defaults.
|
||
}
|
||
$this->debug("generateForeignKeys: Start");
|
||
$DB = $this->getDatabaseConnection();
|
||
|
||
$fk = array();
|
||
|
||
|
||
switch ($DB->phptype) {
|
||
|
||
|
||
case 'pgsql':
|
||
foreach($this->tables as $this->table) {
|
||
$quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($table) : $this->table;
|
||
$res =& $DB->query("SELECT
|
||
pg_catalog.pg_get_constraintdef(r.oid, true) AS condef
|
||
FROM pg_catalog.pg_constraint r,
|
||
pg_catalog.pg_class c
|
||
WHERE c.oid=r.conrelid
|
||
AND r.contype = 'f'
|
||
AND c.relname = '" . $quotedTable . "'");
|
||
if (PEAR::isError($res)) {
|
||
die($res->getMessage());
|
||
}
|
||
|
||
while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
|
||
$treffer = array();
|
||
// this only picks up one of these.. see this for why: http://pear.php.net/bugs/bug.php?id=17049
|
||
preg_match(
|
||
"/FOREIGN KEY \((\w*)\) REFERENCES (\w*)\((\w*)\)/i",
|
||
$row['condef'],
|
||
$treffer);
|
||
if (!count($treffer)) {
|
||
continue;
|
||
}
|
||
$fk[$this->table][$treffer[1]] = $treffer[2] . ":" . $treffer[3];
|
||
}
|
||
}
|
||
break;
|
||
|
||
|
||
case 'mysql':
|
||
case 'mysqli':
|
||
default:
|
||
|
||
foreach($this->tables as $this->table) {
|
||
$quotedTable = !empty($options['quote_identifiers_tableinfo']) ? $DB->quoteIdentifier($table) : $this->table;
|
||
|
||
$res =& $DB->query('SHOW CREATE TABLE ' . $quotedTable );
|
||
|
||
if (PEAR::isError($res)) {
|
||
die($res->getMessage());
|
||
}
|
||
|
||
$text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);
|
||
$treffer = array();
|
||
// Extract FOREIGN KEYS
|
||
preg_match_all(
|
||
"/FOREIGN KEY \(`(\w*)`\) REFERENCES `(\w*)` \(`(\w*)`\)/i",
|
||
$text[1],
|
||
$treffer,
|
||
PREG_SET_ORDER);
|
||
|
||
if (!count($treffer)) {
|
||
continue;
|
||
}
|
||
foreach($treffer as $i=> $tref) {
|
||
$fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
$this->_fkeys = $fk;
|
||
|
||
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* generate Foreign Keys (for links.ini)
|
||
* Currenly only works with mysql / mysqli
|
||
* to use, you must set option: generate_links=true
|
||
*
|
||
* @author Pascal Sch<63>ni
|
||
*/
|
||
function generateForeignKeys()
|
||
{
|
||
$options = PEAR::getStaticProperty('DB_DataObject','options');
|
||
if (empty($options['generate_links'])) {
|
||
return false;
|
||
}
|
||
$__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
|
||
if (!in_array($__DB->phptype, array('mysql', 'mysqli', 'pgsql'))) {
|
||
echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
|
||
return; // cant handle non-mysql introspection for defaults.
|
||
}
|
||
$this->debug("generateForeignKeys: Start");
|
||
|
||
$fk = $this->_fkeys;
|
||
$links_ini = "";
|
||
|
||
foreach($fk as $table => $details) {
|
||
$links_ini .= "[$table]\n";
|
||
foreach ($details as $col => $ref) {
|
||
$links_ini .= "$col = $ref\n";
|
||
}
|
||
$links_ini .= "\n";
|
||
}
|
||
|
||
// dont generate a schema if location is not set
|
||
// it's created on the fly!
|
||
$options = PEAR::getStaticProperty('DB_DataObject','options');
|
||
|
||
if (!empty($options['schema_location'])) {
|
||
$file = "{$options['schema_location']}/{$this->_database}.links.ini";
|
||
} elseif (isset($options["ini_{$this->_database}"])) {
|
||
$file = preg_replace('/\.ini/','.links.ini',$options["ini_{$this->_database}"]);
|
||
} else {
|
||
$this->debug("generateForeignKeys: SKIP - schema_location or ini_{database} was not set");
|
||
return;
|
||
}
|
||
|
||
|
||
if (!file_exists(dirname($file))) {
|
||
mkdir(dirname($file),0755, true);
|
||
}
|
||
|
||
$this->debug("Writing ini as {$file}\n");
|
||
|
||
//touch($file); // not sure why this is needed?
|
||
$tmpname = tempnam(session_save_path(),'DataObject_');
|
||
|
||
$fh = fopen($tmpname,'w');
|
||
if (!$fh) {
|
||
return PEAR::raiseError(
|
||
"Failed to create temporary file: $tmpname\n".
|
||
"make sure session.save_path is set and is writable\n"
|
||
,null, PEAR_ERROR_DIE);
|
||
}
|
||
fwrite($fh,$links_ini);
|
||
fclose($fh);
|
||
$perms = file_exists($file) ? fileperms($file) : 0755;
|
||
// windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
|
||
if (!@rename($tmpname, $file)) {
|
||
unlink($file);
|
||
rename($tmpname, $file);
|
||
}
|
||
chmod($file, $perms);
|
||
}
|
||
|
||
|
||
/**
|
||
* The table geneation part
|
||
*
|
||
* @access private
|
||
* @return tabledef and keys array.
|
||
*/
|
||
function _generateDefinitionsTable()
|
||
{
|
||
global $_DB_DATAOBJECT;
|
||
$options = PEAR::getStaticProperty('DB_DataObject','options');
|
||
$defs = $this->_definitions[$this->table];
|
||
$this->_newConfig .= "\n[{$this->table}]\n";
|
||
$keys_out = "\n[{$this->table}__keys]\n";
|
||
$keys_out_primary = '';
|
||
$keys_out_secondary = '';
|
||
if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
|
||
echo "TABLE STRUCTURE FOR {$this->table}\n";
|
||
print_r($defs);
|
||
}
|
||
$DB = $this->getDatabaseConnection();
|
||
$dbtype = $DB->phptype;
|
||
|
||
$ret = array(
|
||
'table' => array(),
|
||
'keys' => array(),
|
||
);
|
||
|
||
$ret_keys_primary = array();
|
||
$ret_keys_secondary = array();
|
||
|
||
|
||
|
||
foreach($defs as $t) {
|
||
|
||
$n=0;
|
||
$write_ini = true;
|
||
|
||
|
||
switch (strtoupper($t->type)) {
|
||
|
||
case 'INT':
|
||
case 'INT2': // postgres
|
||
case 'INT4': // postgres
|
||
case 'INT8': // postgres
|
||
case 'SERIAL4': // postgres
|
||
case 'SERIAL8': // postgres
|
||
case 'INTEGER':
|
||
case 'TINYINT':
|
||
case 'SMALLINT':
|
||
case 'MEDIUMINT':
|
||
case 'BIGINT':
|
||
$type = DB_DATAOBJECT_INT;
|
||
if ($t->len == 1) {
|
||
$type += DB_DATAOBJECT_BOOL;
|
||
}
|
||
break;
|
||
|
||
case 'REAL':
|
||
case 'DOUBLE':
|
||
case 'DOUBLE PRECISION': // double precision (firebird)
|
||
case 'FLOAT':
|
||
case 'FLOAT4': // real (postgres)
|
||
case 'FLOAT8': // double precision (postgres)
|
||
case 'DECIMAL':
|
||
case 'MONEY': // mssql and maybe others
|
||
case 'NUMERIC':
|
||
case 'NUMBER': // oci8
|
||
$type = DB_DATAOBJECT_INT; // should really by FLOAT!!! / MONEY...
|
||
break;
|
||
|
||
case 'YEAR':
|
||
$type = DB_DATAOBJECT_INT;
|
||
break;
|
||
|
||
case 'BIT':
|
||
case 'BOOL':
|
||
case 'BOOLEAN':
|
||
|
||
$type = DB_DATAOBJECT_BOOL;
|
||
// postgres needs to quote '0'
|
||
if ($dbtype == 'pgsql') {
|
||
$type += DB_DATAOBJECT_STR;
|
||
}
|
||
break;
|
||
|
||
case 'STRING':
|
||
case 'CHAR':
|
||
case 'VARCHAR':
|
||
case 'VARCHAR2':
|
||
case 'TINYTEXT':
|
||
|
||
case 'ENUM':
|
||
case 'SET': // not really but oh well
|
||
|
||
case 'POINT': // mysql geometry stuff - not really string - but will do..
|
||
|
||
case 'TIMESTAMPTZ': // postgres
|
||
case 'BPCHAR': // postgres
|
||
case 'INTERVAL': // postgres (eg. '12 days')
|
||
|
||
case 'CIDR': // postgres IP net spec
|
||
case 'INET': // postgres IP
|
||
case 'MACADDR': // postgress network Mac address.
|
||
|
||
case 'INTEGER[]': // postgres type
|
||
case 'BOOLEAN[]': // postgres type
|
||
|
||
$type = DB_DATAOBJECT_STR;
|
||
break;
|
||
|
||
case 'TEXT':
|
||
case 'MEDIUMTEXT':
|
||
case 'LONGTEXT':
|
||
case '_TEXT': //postgres (?? view ??)
|
||
|
||
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT;
|
||
break;
|
||
|
||
|
||
case 'DATE':
|
||
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE;
|
||
break;
|
||
|
||
case 'TIME':
|
||
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TIME;
|
||
break;
|
||
|
||
|
||
case 'DATETIME':
|
||
|
||
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
|
||
break;
|
||
|
||
case 'TIMESTAMP': // do other databases use this???
|
||
|
||
$type = ($dbtype == 'mysql') ?
|
||
DB_DATAOBJECT_MYSQLTIMESTAMP :
|
||
DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
|
||
break;
|
||
|
||
|
||
case 'BLOB': /// these should really be ignored!!!???
|
||
case 'TINYBLOB':
|
||
case 'MEDIUMBLOB':
|
||
case 'LONGBLOB':
|
||
|
||
case 'CLOB': // oracle character lob support
|
||
|
||
case 'BYTEA': // postgres blob support..
|
||
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB;
|
||
break;
|
||
|
||
default:
|
||
echo "*****************************************************************\n".
|
||
"** WARNING UNKNOWN TYPE **\n".
|
||
"** Found column '{$t->name}', of type '{$t->type}' **\n".
|
||
"** Please submit a bug, describe what type you expect this **\n".
|
||
"** column to be **\n".
|
||
"** ---------POSSIBLE FIX / WORKAROUND -------------------------**\n".
|
||
"** Try using MDB2 as the backend - eg set the config option **\n".
|
||
"** db_driver = MDB2 **\n".
|
||
"*****************************************************************\n";
|
||
$write_ini = false;
|
||
break;
|
||
}
|
||
|
||
if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
|
||
echo "*****************************************************************\n".
|
||
"** WARNING COLUMN NAME UNUSABLE **\n".
|
||
"** Found column '{$t->name}', of type '{$t->type}' **\n".
|
||
"** Since this column name can't be converted to a php variable **\n".
|
||
"** name, and the whole idea of mapping would result in a mess **\n".
|
||
"** This column has been ignored... **\n".
|
||
"*****************************************************************\n";
|
||
continue;
|
||
}
|
||
|
||
if (!strlen(trim($t->name))) {
|
||
continue; // is this a bug?
|
||
}
|
||
|
||
if (preg_match('/not[ _]null/i',$t->flags)) {
|
||
$type += DB_DATAOBJECT_NOTNULL;
|
||
}
|
||
|
||
|
||
if (in_array($t->name,array('null','yes','no','true','false'))) {
|
||
echo "*****************************************************************\n".
|
||
"** WARNING **\n".
|
||
"** Found column '{$t->name}', which is invalid in an .ini file **\n".
|
||
"** This line will not be writen to the file - you will have **\n".
|
||
"** define the keys()/method manually. **\n".
|
||
"*****************************************************************\n";
|
||
$write_ini = false;
|
||
} else {
|
||
$this->_newConfig .= "{$t->name} = $type\n";
|
||
}
|
||
|
||
$ret['table'][$t->name] = $type;
|
||
// i've no idea if this will work well on other databases?
|
||
// only use primary key or nextval(), cause the setFrom blocks you setting all key items...
|
||
// if no keys exist fall back to using unique
|
||
//echo "\n{$t->name} => {$t->flags}\n";
|
||
$secondary_key_match = isset($options['generator_secondary_key_match']) ? $options['generator_secondary_key_match'] : 'primary|unique';
|
||
|
||
$m = array();
|
||
if (preg_match('/(auto_increment|nextval\(([^)]*))/i',rawurldecode($t->flags),$m)
|
||
|| (isset($t->autoincrement) && ($t->autoincrement === true))) {
|
||
|
||
$sn = 'N';
|
||
if ($DB->phptype == 'pgsql' && !empty($m[2])) {
|
||
$sn = preg_replace('/[("]+/','', $m[2]);
|
||
//echo urldecode($t->flags) . "\n" ;
|
||
}
|
||
// native sequences = 2
|
||
if ($write_ini) {
|
||
$keys_out_primary .= "{$t->name} = $sn\n";
|
||
}
|
||
$ret_keys_primary[$t->name] = $sn;
|
||
|
||
} else if ($secondary_key_match && preg_match('/('.$secondary_key_match.')/i',$t->flags)) {
|
||
// keys.. = 1
|
||
$key_type = 'K';
|
||
if (!preg_match("/(primary)/i",$t->flags)) {
|
||
$key_type = 'U';
|
||
}
|
||
|
||
if ($write_ini) {
|
||
$keys_out_secondary .= "{$t->name} = {$key_type}\n";
|
||
}
|
||
$ret_keys_secondary[$t->name] = $key_type;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
$this->_newConfig .= $keys_out . (empty($keys_out_primary) ? $keys_out_secondary : $keys_out_primary);
|
||
$ret['keys'] = empty($keys_out_primary) ? $ret_keys_secondary : $ret_keys_primary;
|
||
|
||
if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
|
||
print_r(array("dump for {$this->table}", $ret));
|
||
}
|
||
|
||
return $ret;
|
||
|
||
|
||
}
|
||
|
||
/**
|
||
* Convert a table name into a class name -> override this if you want a different mapping
|
||
*
|
||
* @access public
|
||
* @return string class name;
|
||
*/
|
||
|
||
|
||
function getClassNameFromTableName($table)
|
||
{
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
$class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
|
||
return $class_prefix.preg_replace('/[^A-Z0-9]/i','_',ucfirst(trim($this->table)));
|
||
}
|
||
|
||
|
||
/**
|
||
* Convert a table name into a file name -> override this if you want a different mapping
|
||
*
|
||
* @access public
|
||
* @return string file name;
|
||
*/
|
||
|
||
|
||
function getFileNameFromTableName($table)
|
||
{
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
$base = $options['class_location'];
|
||
if (strpos($base,'%s') !== false) {
|
||
$base = dirname($base);
|
||
}
|
||
if (!file_exists($base)) {
|
||
require_once 'System.php';
|
||
System::mkdir(array('-p',$base));
|
||
}
|
||
if (strpos($options['class_location'],'%s') !== false) {
|
||
$outfilename = sprintf($options['class_location'],
|
||
preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)));
|
||
} else {
|
||
$outfilename = "{$base}/".preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)).".php";
|
||
}
|
||
return $outfilename;
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* Convert a column name into a method name (usually prefixed by get/set/validateXXXXX)
|
||
*
|
||
* @access public
|
||
* @return string method name;
|
||
*/
|
||
|
||
|
||
function getMethodNameFromColumnName($col)
|
||
{
|
||
return ucfirst($col);
|
||
}
|
||
|
||
|
||
|
||
|
||
/*
|
||
* building the class files
|
||
* for each of the tables output a file!
|
||
*/
|
||
function generateClasses()
|
||
{
|
||
//echo "Generating Class files: \n";
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
|
||
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
|
||
$this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
|
||
|
||
|
||
foreach($this->tables as $this->table) {
|
||
$this->table = trim($this->table);
|
||
$this->classname = $this->getClassNameFromTableName($this->table);
|
||
$i = '';
|
||
$outfilename = $this->getFileNameFromTableName($this->table);
|
||
|
||
$oldcontents = '';
|
||
if (file_exists($outfilename)) {
|
||
// file_get_contents???
|
||
$oldcontents = implode('',file($outfilename));
|
||
}
|
||
|
||
$out = $this->_generateClassTable($oldcontents);
|
||
$this->debug( "writing $this->classname\n");
|
||
$tmpname = tempnam(session_save_path(),'DataObject_');
|
||
|
||
$fh = fopen($tmpname, "w");
|
||
if (!$fh) {
|
||
return PEAR::raiseError(
|
||
"Failed to create temporary file: $tmpname\n".
|
||
"make sure session.save_path is set and is writable\n"
|
||
,null, PEAR_ERROR_DIE);
|
||
}
|
||
fputs($fh,$out);
|
||
fclose($fh);
|
||
$perms = file_exists($outfilename) ? fileperms($outfilename) : 0755;
|
||
|
||
// windows can fail doing this. - not a perfect solution but otherwise it's getting really kludgy..
|
||
if (!@rename($tmpname, $outfilename)) {
|
||
unlink($outfilename);
|
||
rename($tmpname, $outfilename);
|
||
}
|
||
|
||
chmod($outfilename, $perms);
|
||
}
|
||
//echo $out;
|
||
}
|
||
|
||
/**
|
||
* class being extended (can be overridden by [DB_DataObject] extends=xxxx
|
||
*
|
||
* @var string
|
||
* @access private
|
||
*/
|
||
var $_extends = 'DB_DataObject';
|
||
|
||
/**
|
||
* line to use for require('DB/DataObject.php');
|
||
*
|
||
* @var string
|
||
* @access private
|
||
*/
|
||
var $_extendsFile = "DB/DataObject.php";
|
||
|
||
/**
|
||
* class being generated
|
||
*
|
||
* @var string
|
||
* @access private
|
||
*/
|
||
var $_className;
|
||
|
||
/**
|
||
* The table class geneation part - single file.
|
||
*
|
||
* @access private
|
||
* @return none
|
||
*/
|
||
function _generateClassTable($input = '')
|
||
{
|
||
// title = expand me!
|
||
$foot = "";
|
||
$head = "<?php\n/**\n * Table Definition for {$this->table}\n";
|
||
$head .= $this->derivedHookPageLevelDocBlock();
|
||
$head .= " */\n";
|
||
$head .= $this->derivedHookExtendsDocBlock();
|
||
|
||
|
||
// requires
|
||
$head .= "require_once '{$this->_extendsFile}';\n\n";
|
||
// add dummy class header in...
|
||
// class
|
||
$head .= $this->derivedHookClassDocBlock();
|
||
$head .= "class {$this->classname} extends {$this->_extends} \n{";
|
||
|
||
$body = "\n ###START_AUTOCODE\n";
|
||
$body .= " /* the code below is auto generated do not remove the above tag */\n\n";
|
||
// table
|
||
|
||
$p = str_repeat(' ',max(2, (18 - strlen($this->table)))) ;
|
||
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
|
||
|
||
$var = (substr(phpversion(),0,1) > 4) ? 'public' : 'var';
|
||
$var = !empty($options['generator_var_keyword']) ? $options['generator_var_keyword'] : $var;
|
||
|
||
|
||
$body .= " {$var} \$__table = '{$this->table}'; {$p}// table name\n";
|
||
|
||
// if we are using the option database_{databasename} = dsn
|
||
// then we should add var $_database = here
|
||
// as database names may not always match..
|
||
|
||
if (empty($GLOBALS['_DB_DATAOBJECT']['CONFIG'])) {
|
||
DB_DataObject::_loadConfig();
|
||
}
|
||
|
||
// Only include the $_database property if the omit_database_var is unset or false
|
||
|
||
if (isset($options["database_{$this->_database}"]) && empty($GLOBALS['_DB_DATAOBJECT']['CONFIG']['generator_omit_database_var'])) {
|
||
$p = str_repeat(' ', max(2, (16 - strlen($this->_database))));
|
||
$body .= " {$var} \$_database = '{$this->_database}'; {$p}// database name (used with database_{*} config)\n";
|
||
}
|
||
|
||
|
||
if (!empty($options['generator_novars'])) {
|
||
$var = '//'.$var;
|
||
}
|
||
|
||
$defs = $this->_definitions[$this->table];
|
||
|
||
// show nice information!
|
||
$connections = array();
|
||
$sets = array();
|
||
|
||
foreach($defs as $t) {
|
||
if (!strlen(trim($t->name))) {
|
||
continue;
|
||
}
|
||
if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $t->name)) {
|
||
echo "*****************************************************************\n".
|
||
"** WARNING COLUMN NAME UNUSABLE **\n".
|
||
"** Found column '{$t->name}', of type '{$t->type}' **\n".
|
||
"** Since this column name can't be converted to a php variable **\n".
|
||
"** name, and the whole idea of mapping would result in a mess **\n".
|
||
"** This column has been ignored... **\n".
|
||
"*****************************************************************\n";
|
||
continue;
|
||
}
|
||
|
||
$p = str_repeat(' ',max(2, (30 - strlen($t->name))));
|
||
|
||
$length = empty($t->len) ? '' : '('.$t->len.')';
|
||
$body .=" {$var} \${$t->name}; {$p}// {$t->type}$length {$t->flags}\n";
|
||
|
||
// can not do set as PEAR::DB table info doesnt support it.
|
||
//if (substr($t->Type,0,3) == "set")
|
||
// $sets[$t->Field] = "array".substr($t->Type,3);
|
||
$body .= $this->derivedHookVar($t,strlen($p));
|
||
}
|
||
|
||
$body .= $this->derivedHookPostVar($defs);
|
||
|
||
// THIS IS TOTALLY BORKED old FC creation
|
||
// IT WILL BE REMOVED!!!!! in DataObjects 1.6
|
||
// grep -r __clone * to find all it's uses
|
||
// and replace them with $x = clone($y);
|
||
// due to the change in the PHP5 clone design.
|
||
$static = 'static';
|
||
if ( substr(phpversion(),0,1) < 5) {
|
||
$body .= "\n";
|
||
$body .= " /* ZE2 compatibility trick*/\n";
|
||
$body .= " function __clone() { return \$this;}\n";
|
||
}
|
||
|
||
|
||
// depricated - in here for BC...
|
||
if (!empty($options['static_get'])) {
|
||
|
||
// simple creation tools ! (static stuff!)
|
||
$body .= "\n";
|
||
$body .= " /* Static get */\n";
|
||
$body .= " $static function staticGet(\$k,\$v=NULL) { " .
|
||
"return DB_DataObject::staticGet('{$this->classname}',\$k,\$v = null); }\n";
|
||
}
|
||
// generate getter and setter methods
|
||
$body .= $this->_generateGetters($input);
|
||
$body .= $this->_generateSetters($input);
|
||
$body .= $this->_generateLinkMethods($input);
|
||
/*
|
||
theoretically there is scope here to introduce 'list' methods
|
||
based up 'xxxx_up' column!!! for heiracitcal trees..
|
||
*/
|
||
|
||
// set methods
|
||
//foreach ($sets as $k=>$v) {
|
||
// $kk = strtoupper($k);
|
||
// $body .=" function getSets{$k}() { return {$v}; }\n";
|
||
//}
|
||
|
||
if (!empty($options['generator_no_ini'])) {
|
||
$def = $this->_generateDefinitionsTable(); // simplify this!?
|
||
$body .= $this->_generateTableFunction($def['table']);
|
||
$body .= $this->_generateKeysFunction($def['keys']);
|
||
$body .= $this->_generateSequenceKeyFunction($def);
|
||
$body .= $this->_generateDefaultsFunction($this->table, $def['table']);
|
||
} else if (!empty($options['generator_add_defaults'])) {
|
||
// I dont really like doing it this way (adding another option)
|
||
// but it helps on older projects.
|
||
$def = $this->_generateDefinitionsTable(); // simplify this!?
|
||
$body .= $this->_generateDefaultsFunction($this->table,$def['table']);
|
||
|
||
}
|
||
$body .= $this->derivedHookFunctions($input);
|
||
|
||
$body .= "\n /* the code above is auto generated do not remove the tag below */";
|
||
$body .= "\n ###END_AUTOCODE\n";
|
||
|
||
|
||
// stubs..
|
||
|
||
if (!empty($options['generator_add_validate_stubs'])) {
|
||
foreach($defs as $t) {
|
||
if (!strlen(trim($t->name))) {
|
||
continue;
|
||
}
|
||
$validate_fname = 'validate' . $this->getMethodNameFromColumnName($t->name);
|
||
// dont re-add it..
|
||
if (preg_match('/\s+function\s+' . $validate_fname . '\s*\(/i', $input)) {
|
||
continue;
|
||
}
|
||
$body .= "\n function {$validate_fname}()\n {\n return false;\n }\n";
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
$foot .= "}\n";
|
||
$full = $head . $body . $foot;
|
||
|
||
if (!$input) {
|
||
return $full;
|
||
}
|
||
if (!preg_match('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n)/s',$input)) {
|
||
return $full;
|
||
}
|
||
if (!preg_match('/(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',$input)) {
|
||
return $full;
|
||
}
|
||
|
||
|
||
/* this will only replace extends DB_DataObject by default,
|
||
unless use set generator_class_rewrite to ANY or a name*/
|
||
|
||
$class_rewrite = 'DB_DataObject';
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
if (empty($options['generator_class_rewrite']) || !($class_rewrite = $options['generator_class_rewrite'])) {
|
||
$class_rewrite = 'DB_DataObject';
|
||
}
|
||
if ($class_rewrite == 'ANY') {
|
||
$class_rewrite = '[a-z_]+';
|
||
}
|
||
|
||
$input = preg_replace(
|
||
'/(\n|\r\n)class\s*[a-z0-9_]+\s*extends\s*' .$class_rewrite . '\s*(\n|\r\n)\{(\n|\r\n)/si',
|
||
"\nclass {$this->classname} extends {$this->_extends} \n{\n",
|
||
$input);
|
||
|
||
$ret = preg_replace(
|
||
'/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',
|
||
$body,$input);
|
||
|
||
if (!strlen($ret)) {
|
||
return PEAR::raiseError(
|
||
"PREG_REPLACE failed to replace body, - you probably need to set these in your php.ini\n".
|
||
"pcre.backtrack_limit=1000000\n".
|
||
"pcre.recursion_limit=1000000\n"
|
||
,null, PEAR_ERROR_DIE);
|
||
}
|
||
|
||
return $ret;
|
||
}
|
||
|
||
/**
|
||
* hook to add extra methods to all classes
|
||
*
|
||
* called once for each class, use with $this->table and
|
||
* $this->_definitions[$this->table], to get data out of the current table,
|
||
* use it to add extra methods to the default classes.
|
||
*
|
||
* @access public
|
||
* @return string added to class eg. functions.
|
||
*/
|
||
function derivedHookFunctions($input = "")
|
||
{
|
||
// This is so derived generator classes can generate functions
|
||
// It MUST NOT be changed here!!!
|
||
return "";
|
||
}
|
||
|
||
/**
|
||
* hook for var lines
|
||
* called each time a var line is generated, override to add extra var
|
||
* lines
|
||
*
|
||
* @param object t containing type,len,flags etc. from tableInfo call
|
||
* @param int padding number of spaces
|
||
* @access public
|
||
* @return string added to class eg. functions.
|
||
*/
|
||
function derivedHookVar(&$t,$padding)
|
||
{
|
||
// This is so derived generator classes can generate variabels
|
||
// It MUST NOT be changed here!!!
|
||
return "";
|
||
}
|
||
/**
|
||
* hook for after var lines (
|
||
* called at the end of the output of var line have generated, override to add extra var
|
||
* lines
|
||
*
|
||
* @param array cols containing array of objects with type,len,flags etc. from tableInfo call
|
||
* @access public
|
||
* @return string added to class eg. functions.
|
||
*/
|
||
function derivedHookPostVar($t)
|
||
{
|
||
// This is so derived generator classes can generate variabels
|
||
// It MUST NOT be changed here!!!
|
||
return "";
|
||
}
|
||
/**
|
||
* hook to add extra page-level (in terms of phpDocumentor) DocBlock
|
||
*
|
||
* called once for each class, use it add extra page-level docs
|
||
* @access public
|
||
* @return string added to class eg. functions.
|
||
*/
|
||
function derivedHookPageLevelDocBlock() {
|
||
return '';
|
||
}
|
||
|
||
/**
|
||
* hook to add extra doc block (in terms of phpDocumentor) to extend string
|
||
*
|
||
* called once for each class, use it add extra comments to extends
|
||
* string (require_once...)
|
||
* @access public
|
||
* @return string added to class eg. functions.
|
||
*/
|
||
function derivedHookExtendsDocBlock() {
|
||
return '';
|
||
}
|
||
|
||
/**
|
||
* hook to add extra class level DocBlock (in terms of phpDocumentor)
|
||
*
|
||
* called once for each class, use it add extra comments to class
|
||
* string (require_once...)
|
||
* @access public
|
||
* @return string added to class eg. functions.
|
||
*/
|
||
function derivedHookClassDocBlock() {
|
||
return '';
|
||
}
|
||
|
||
/**
|
||
|
||
/**
|
||
* getProxyFull - create a class definition on the fly and instantate it..
|
||
*
|
||
* similar to generated files - but also evals the class definitoin code.
|
||
*
|
||
*
|
||
* @param string database name
|
||
* @param string table name of table to create proxy for.
|
||
*
|
||
*
|
||
* @return object Instance of class. or PEAR Error
|
||
* @access public
|
||
*/
|
||
function getProxyFull($database,$table)
|
||
{
|
||
|
||
if ($err = $this->fillTableSchema($database,$table)) {
|
||
return $err;
|
||
}
|
||
|
||
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
$class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
|
||
|
||
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
|
||
$this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
|
||
|
||
$classname = $this->classname = $this->getClassNameFromTableName($this->table);
|
||
|
||
$out = $this->_generateClassTable();
|
||
//echo $out;
|
||
eval('?>'.$out);
|
||
return new $classname;
|
||
|
||
}
|
||
|
||
/**
|
||
* fillTableSchema - set the database schema on the fly
|
||
*
|
||
*
|
||
*
|
||
* @param string database name
|
||
* @param string table name of table to create schema info for
|
||
*
|
||
* @return none | PEAR::error()
|
||
* @access public
|
||
*/
|
||
function fillTableSchema($database,$table)
|
||
{
|
||
global $_DB_DATAOBJECT;
|
||
// a little bit of sanity testing.
|
||
if ((false !== strpos($database,"'")) || (false !== strpos($database,";"))) {
|
||
return PEAR::raiseError("Error: Database name contains a quote or semi-colon", null, PEAR_ERROR_DIE);
|
||
}
|
||
|
||
$this->_database = $database;
|
||
|
||
$this->_connect();
|
||
$table = trim($table);
|
||
|
||
// a little bit of sanity testing.
|
||
if ((false !== strpos($table,"'")) || (false !== strpos($table,";"))) {
|
||
return PEAR::raiseError("Error: Table contains a quote or semi-colon", null, PEAR_ERROR_DIE);
|
||
}
|
||
$__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
|
||
|
||
|
||
$options = PEAR::getStaticProperty('DB_DataObject','options');
|
||
$db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
|
||
$is_MDB2 = ($db_driver != 'DB') ? true : false;
|
||
|
||
if (!$is_MDB2) {
|
||
// try getting a list of schema tables first. (postgres)
|
||
$__DB->expectError(DB_ERROR_UNSUPPORTED);
|
||
$this->tables = $__DB->getListOf('schema.tables');
|
||
$__DB->popExpect();
|
||
} else {
|
||
/**
|
||
* set portability and some modules to fetch the informations
|
||
*/
|
||
$__DB->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE);
|
||
$__DB->loadModule('Manager');
|
||
$__DB->loadModule('Reverse');
|
||
}
|
||
$quotedTable = !empty($options['quote_identifiers_tableinfo']) ?
|
||
$__DB->quoteIdentifier($table) : $table;
|
||
|
||
if (!$is_MDB2) {
|
||
$defs = $__DB->tableInfo($quotedTable);
|
||
} else {
|
||
$defs = $__DB->reverse->tableInfo($quotedTable);
|
||
foreach ($defs as $k => $v) {
|
||
if (!isset($defs[$k]['length'])) {
|
||
continue;
|
||
}
|
||
$defs[$k]['len'] = $defs[$k]['length'];
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
if (PEAR::isError($defs)) {
|
||
return $defs;
|
||
}
|
||
if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
|
||
$this->debug("getting def for $database/$table",'fillTable');
|
||
$this->debug(print_r($defs,true),'defs');
|
||
}
|
||
// cast all definitions to objects - as we deal with that better.
|
||
|
||
|
||
foreach($defs as $def) {
|
||
if (is_array($def)) {
|
||
$this->_definitions[$table][] = (object) $def;
|
||
}
|
||
}
|
||
|
||
$this->table = trim($table);
|
||
$ret = $this->_generateDefinitionsTable();
|
||
|
||
$_DB_DATAOBJECT['INI'][$database][$table] = $ret['table'];
|
||
$_DB_DATAOBJECT['INI'][$database][$table.'__keys'] = $ret['keys'];
|
||
return false;
|
||
|
||
}
|
||
|
||
/**
|
||
* Generate getter methods for class definition
|
||
*
|
||
* @param string $input Existing class contents
|
||
* @return string
|
||
* @access public
|
||
*/
|
||
function _generateGetters($input)
|
||
{
|
||
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
$getters = '';
|
||
|
||
// only generate if option is set to true
|
||
if (empty($options['generate_getters'])) {
|
||
return '';
|
||
}
|
||
|
||
// remove auto-generated code from input to be able to check if the method exists outside of the auto-code
|
||
$input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
|
||
|
||
$getters .= "\n\n";
|
||
$defs = $this->_definitions[$this->table];
|
||
|
||
// loop through properties and create getter methods
|
||
foreach ($defs = $defs as $t) {
|
||
|
||
// build mehtod name
|
||
$methodName = 'get' . $this->getMethodNameFromColumnName($t->name);
|
||
|
||
if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
|
||
continue;
|
||
}
|
||
|
||
$getters .= " /**\n";
|
||
$getters .= " * Getter for \${$t->name}\n";
|
||
$getters .= " *\n";
|
||
$getters .= (stristr($t->flags, 'multiple_key')) ? " * @return object\n"
|
||
: " * @return {$t->type}\n";
|
||
$getters .= " * @access public\n";
|
||
$getters .= " */\n";
|
||
$getters .= (substr(phpversion(),0,1) > 4) ? ' public '
|
||
: ' ';
|
||
$getters .= "function $methodName() {\n";
|
||
$getters .= " return \$this->{$t->name};\n";
|
||
$getters .= " }\n\n";
|
||
}
|
||
|
||
|
||
return $getters;
|
||
}
|
||
/**
|
||
* Generate link setter/getter methods for class definition
|
||
*
|
||
* @param string Existing class contents
|
||
* @return string
|
||
* @access public
|
||
*/
|
||
function _generateLinkMethods($input)
|
||
{
|
||
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
$setters = '';
|
||
|
||
// only generate if option is set to true
|
||
|
||
// generate_link_methods true::
|
||
|
||
|
||
if (empty($options['generate_link_methods'])) {
|
||
//echo "skip lm? - not set";
|
||
return '';
|
||
}
|
||
|
||
if (empty($this->_fkeys)) {
|
||
// echo "skip lm? - fkyes empty";
|
||
return '';
|
||
}
|
||
if (empty($this->_fkeys[$this->table])) {
|
||
//echo "skip lm? - no fkeys for {$this->table}";
|
||
return '';
|
||
}
|
||
|
||
// remove auto-generated code from input to be able to check if the method exists outside of the auto-code
|
||
$input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
|
||
|
||
$setters .= "\n";
|
||
$defs = $this->_fkeys[$this->table];
|
||
|
||
|
||
// $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
|
||
|
||
// loop through properties and create setter methods
|
||
foreach ($defs as $k => $info) {
|
||
|
||
// build mehtod name
|
||
$methodName = is_callable($options['generate_link_methods']) ?
|
||
$options['generate_link_methods']($k) : $k;
|
||
|
||
if (!strlen(trim($k)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
|
||
continue;
|
||
}
|
||
|
||
$setters .= " /**\n";
|
||
$setters .= " * Getter / Setter for \${$k}\n";
|
||
$setters .= " *\n";
|
||
$setters .= " * @param mixed (optional) value to assign\n";
|
||
$setters .= " * @access public\n";
|
||
|
||
$setters .= " */\n";
|
||
$setters .= (substr(phpversion(),0,1) > 4) ? ' public '
|
||
: ' ';
|
||
$setters .= "function $methodName() {\n";
|
||
$setters .= " return \$this->link('$k', func_get_args());\n";
|
||
$setters .= " }\n\n";
|
||
}
|
||
|
||
return $setters;
|
||
}
|
||
|
||
/**
|
||
* Generate setter methods for class definition
|
||
*
|
||
* @param string Existing class contents
|
||
* @return string
|
||
* @access public
|
||
*/
|
||
function _generateSetters($input)
|
||
{
|
||
|
||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||
$setters = '';
|
||
|
||
// only generate if option is set to true
|
||
if (empty($options['generate_setters'])) {
|
||
return '';
|
||
}
|
||
|
||
// remove auto-generated code from input to be able to check if the method exists outside of the auto-code
|
||
$input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
|
||
|
||
$setters .= "\n";
|
||
$defs = $this->_definitions[$this->table];
|
||
|
||
// loop through properties and create setter methods
|
||
foreach ($defs = $defs as $t) {
|
||
|
||
// build mehtod name
|
||
$methodName = 'set' . $this->getMethodNameFromColumnName($t->name);
|
||
|
||
if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
|
||
continue;
|
||
}
|
||
|
||
$setters .= " /**\n";
|
||
$setters .= " * Setter for \${$t->name}\n";
|
||
$setters .= " *\n";
|
||
$setters .= " * @param mixed input value\n";
|
||
$setters .= " * @access public\n";
|
||
$setters .= " */\n";
|
||
$setters .= (substr(phpversion(),0,1) > 4) ? ' public '
|
||
: ' ';
|
||
$setters .= "function $methodName(\$value) {\n";
|
||
$setters .= " \$this->{$t->name} = \$value;\n";
|
||
$setters .= " }\n\n";
|
||
}
|
||
|
||
|
||
return $setters;
|
||
}
|
||
/**
|
||
* Generate table Function - used when generator_no_ini is set.
|
||
*
|
||
* @param array table array.
|
||
* @return string
|
||
* @access public
|
||
*/
|
||
function _generateTableFunction($def)
|
||
{
|
||
$defines = explode(',','INT,STR,DATE,TIME,BOOL,TXT,BLOB,NOTNULL,MYSQLTIMESTAMP');
|
||
|
||
$ret = "\n" .
|
||
" function table()\n" .
|
||
" {\n" .
|
||
" return array(\n";
|
||
|
||
foreach($def as $k=>$v) {
|
||
$str = '0';
|
||
foreach($defines as $dn) {
|
||
if ($v & constant('DB_DATAOBJECT_' . $dn)) {
|
||
$str .= ' + DB_DATAOBJECT_' . $dn;
|
||
}
|
||
}
|
||
if (strlen($str) > 1) {
|
||
$str = substr($str,3); // strip the 0 +
|
||
}
|
||
// hopefully addslashes is good enough here!!!
|
||
$ret .= ' \''.addslashes($k).'\' => ' . $str . ",\n";
|
||
}
|
||
return $ret . " );\n" .
|
||
" }\n";
|
||
|
||
|
||
|
||
}
|
||
/**
|
||
* Generate keys Function - used generator_no_ini is set.
|
||
*
|
||
* @param array keys array.
|
||
* @return string
|
||
* @access public
|
||
*/
|
||
function _generateKeysFunction($def)
|
||
{
|
||
|
||
$ret = "\n" .
|
||
" function keys()\n" .
|
||
" {\n" .
|
||
" return array(";
|
||
|
||
foreach($def as $k=>$type) {
|
||
// hopefully addslashes is good enough here!!!
|
||
$ret .= '\''.addslashes($k).'\', ';
|
||
}
|
||
$ret = preg_replace('#, $#', '', $ret);
|
||
return $ret . ");\n" .
|
||
" }\n";
|
||
|
||
|
||
|
||
}
|
||
/**
|
||
* Generate sequenceKey Function - used generator_no_ini is set.
|
||
*
|
||
* @param array table and key definition.
|
||
* @return string
|
||
* @access public
|
||
*/
|
||
function _generateSequenceKeyFunction($def)
|
||
{
|
||
|
||
//print_r($def);
|
||
// DB_DataObject::debugLevel(5);
|
||
global $_DB_DATAOBJECT;
|
||
// print_r($def);
|
||
|
||
|
||
$dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
|
||
$realkeys = $def['keys'];
|
||
$keys = array_keys($realkeys);
|
||
$usekey = isset($keys[0]) ? $keys[0] : false;
|
||
$table = $def['table'];
|
||
|
||
|
||
$seqname = false;
|
||
|
||
|
||
|
||
|
||
$ar = array(false,false,false);
|
||
if ($usekey !== false) {
|
||
if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) {
|
||
$usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
|
||
if (strpos($usekey,':') !== false) {
|
||
list($usekey,$seqname) = explode(':',$usekey);
|
||
}
|
||
}
|
||
|
||
if (in_array($dbtype , array( 'mysql', 'mysqli', 'mssql', 'ifx')) &&
|
||
($table[$usekey] & DB_DATAOBJECT_INT) &&
|
||
isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
|
||
) {
|
||
// use native sequence keys.
|
||
$ar = array($usekey,true,$seqname);
|
||
} else {
|
||
// use generated sequence keys..
|
||
if ($table[$usekey] & DB_DATAOBJECT_INT) {
|
||
$ar = array($usekey,false,$seqname);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
$ret = "\n" .
|
||
" function sequenceKey() // keyname, use native, native name\n" .
|
||
" {\n" .
|
||
" return array(";
|
||
foreach($ar as $v) {
|
||
switch (gettype($v)) {
|
||
case 'boolean':
|
||
$ret .= ($v ? 'true' : 'false') . ', ';
|
||
break;
|
||
|
||
case 'string':
|
||
$ret .= "'" . $v . "', ";
|
||
break;
|
||
|
||
default: // eak
|
||
$ret .= "null, ";
|
||
|
||
}
|
||
}
|
||
$ret = preg_replace('#, $#', '', $ret);
|
||
return $ret . ");\n" .
|
||
" }\n";
|
||
|
||
}
|
||
/**
|
||
* Generate defaults Function - used generator_add_defaults or generator_no_ini is set.
|
||
* Only supports mysql and mysqli ... welcome ideas for more..
|
||
*
|
||
*
|
||
* @param array table and key definition.
|
||
* @return string
|
||
* @access public
|
||
*/
|
||
function _generateDefaultsFunction($table,$defs)
|
||
{
|
||
$__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
|
||
if (!in_array($__DB->phptype, array('mysql','mysqli'))) {
|
||
return; // cant handle non-mysql introspection for defaults.
|
||
}
|
||
$options = PEAR::getStaticProperty('DB_DataObject','options');
|
||
$db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
|
||
$method = $db_driver == 'DB' ? 'getAll' : 'queryAll';
|
||
$res = $__DB->$method('DESCRIBE ' . $table,DB_FETCHMODE_ASSOC);
|
||
$defaults = array();
|
||
foreach($res as $ar) {
|
||
// this is initially very dumb... -> and it may mess up..
|
||
$type = $defs[$ar['Field']];
|
||
|
||
switch (true) {
|
||
|
||
case (is_null( $ar['Default'])):
|
||
$defaults[$ar['Field']] = 'null';
|
||
break;
|
||
|
||
case ($type & DB_DATAOBJECT_DATE):
|
||
case ($type & DB_DATAOBJECT_TIME):
|
||
case ($type & DB_DATAOBJECT_MYSQLTIMESTAMP): // not supported yet..
|
||
break;
|
||
|
||
case ($type & DB_DATAOBJECT_BOOL):
|
||
$defaults[$ar['Field']] = (int)(boolean) $ar['Default'];
|
||
break;
|
||
|
||
|
||
case ($type & DB_DATAOBJECT_STR):
|
||
$defaults[$ar['Field']] = "'" . addslashes($ar['Default']) . "'";
|
||
break;
|
||
|
||
|
||
default: // hopefully eveything else... - numbers etc.
|
||
if (!strlen($ar['Default'])) {
|
||
continue;
|
||
}
|
||
if (is_numeric($ar['Default'])) {
|
||
$defaults[$ar['Field']] = $ar['Default'];
|
||
}
|
||
break;
|
||
|
||
}
|
||
//var_dump(array($ar['Field'], $ar['Default'], $defaults[$ar['Field']]));
|
||
}
|
||
if (empty($defaults)) {
|
||
return;
|
||
}
|
||
|
||
$ret = "\n" .
|
||
" function defaults() // column default values \n" .
|
||
" {\n" .
|
||
" return array(\n";
|
||
foreach($defaults as $k=>$v) {
|
||
$ret .= ' \''.addslashes($k).'\' => ' . $v . ",\n";
|
||
}
|
||
return $ret . " );\n" .
|
||
" }\n";
|
||
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
}
|