From 020290cce888b210ef5bfdf1cf7a41147f32ad6f Mon Sep 17 00:00:00 2001 From: e107steved Date: Fri, 25 Dec 2009 10:25:12 +0000 Subject: [PATCH] Logic for automatic generation of field processing definitions, with overrides --- e107_admin/sql/db_field_defs.php | 56 +++++++ e107_handlers/mysql_class.php | 247 +++++++++++++++++++++++++++---- 2 files changed, 277 insertions(+), 26 deletions(-) create mode 100644 e107_admin/sql/db_field_defs.php diff --git a/e107_admin/sql/db_field_defs.php b/e107_admin/sql/db_field_defs.php new file mode 100644 index 000000000..0b8167c00 --- /dev/null +++ b/e107_admin/sql/db_field_defs.php @@ -0,0 +1,56 @@ +/** +Override field definitions for core database tables. +If a table is included here, ALL fields must be defined. + +First level array key is the table name (without any kind of prefix) +Second level array keys are always '_FIELD_TYPES', '_NOTNULL' and suchlike. + +These definitions are just examples - there is no logic behind what they do! +*/ +array('comments' => + array ( + '_FIELD_TYPES' => + array ( + 'comment_id' => 'int', + 'comment_pid' => 'int', + 'comment_item_id' => 'int', + 'comment_subject' => 'todb', + 'comment_author_id' => 'int', + 'comment_author_name' => 'todb', + 'comment_author_email' => 'todb', + 'comment_datestamp' => 'int', + 'comment_comment' => 'string', + 'comment_blocked' => 'int', + 'comment_ip' => 'todb', + 'comment_type' => 'escape', + 'comment_lock' => 'int', + ), + '_NOTNULL' => + array ( + 'comment_id' => '', + 'comment_comment' => '', + ), + ), + 'links' => + array ( + '_FIELD_TYPES' => + array ( + 'link_id' => 'int', + 'link_name' => 'todb', + 'link_url' => 'todb', + 'link_description' => 'string', + 'link_button' => 'todb', + 'link_category' => 'int', + 'link_order' => 'int', + 'link_parent' => 'int', + 'link_open' => 'int', + 'link_class' => 'todb', + 'link_function' => 'escape', + ), + '_NOTNULL' => + array ( + 'link_id' => '', + 'link_description' => '', + ), + ) +) \ No newline at end of file diff --git a/e107_handlers/mysql_class.php b/e107_handlers/mysql_class.php index 8c7f443e0..b9dad9112 100644 --- a/e107_handlers/mysql_class.php +++ b/e107_handlers/mysql_class.php @@ -9,11 +9,29 @@ * mySQL Handler * * $Source: /cvs_backup/e107_0.8/e107_handlers/mysql_class.php,v $ - * $Revision: 1.70 $ - * $Date: 2009-12-15 22:34:04 $ + * $Revision: 1.71 $ + * $Date: 2009-12-25 10:25:11 $ * $Author: e107steved $ */ + +/** + * MySQL Abstraction class + * + * @package e107 + * @subpackage e107_handlers + * @version $Id: mysql_class.php,v 1.71 2009-12-25 10:25:11 e107steved Exp $; + * + * @todo separate cache for db type tables + */ + +/* + Parameters related to auto-generation of field definitions on db_Insert() and db_Update() +*/ + define('ALLOW_AUTO_FIELD_DEFS', TRUE); // Temporary so new functionality can be disabled if it causes problems + define('e_DB_CACHE', e_CACHE); // Use standard cache directory for now - should really be elsewhere + + if(defined('MYSQL_LIGHT')) { define('E107_DEBUG_LEVEL', 0); @@ -27,9 +45,6 @@ if(defined('MYSQL_LIGHT')) elseif(defined('E107_INSTALL')) { define('E107_DEBUG_LEVEL', 0); - //define('e_QUERY', ''); - //require_once("e107_config.php"); - //define('MPREFIX', $mySQLprefix); require('e107_config.php'); $sql_info = compact('mySQLserver', 'mySQLuser', 'mySQLpassword', 'mySQLdefaultdb', 'mySQLprefix'); e107::getInstance()->initInstallSql($sql_info); @@ -46,17 +61,9 @@ $db_mySQLQueryCount = 0; // Global total number of db object queries (all db's) $db_ConnectionID = NULL; // Stores ID for the first DB connection used - which should be the main E107 DB - then used as default -/** - * MySQL Abstraction class - * - * @package e107 - * @category e107_handlers - * @version $Revision: 1.70 $ - * @author $Author: e107steved $ - * - */ -class e_db_mysql { +class e_db_mysql +{ // TODO switch to protected vars where needed public $mySQLserver; public $mySQLuser; @@ -68,8 +75,8 @@ class e_db_mysql { public $mySQLrows; public $mySQLerror = ''; // Error reporting mode - TRUE shows messages - protected $mySQLlastErrNum = 0; // Number of last error - now protected, use getLastErrNum() - protected $mySQLlastErrText = ''; // Text of last error - now protected, use getLastErrText() + protected $mySQLlastErrNum = 0; // Number of last error - now protected, use getLastErrorNumber() + protected $mySQLlastErrText = ''; // Text of last error - now protected, use getLastErrorText() public $mySQLcurTable; public $mySQLlanguage; @@ -79,6 +86,7 @@ class e_db_mysql { public $mySQLtableListLanguage = array(); // Db table list for the currently selected language + protected $dbFieldDefs = array(); // Local cache - Field type definitions for _FIELD_DEFS and _NOTNULL arrays /** * MySQL Charset * @@ -365,7 +373,7 @@ class e_db_mysql { /** * @return int Last insert ID or false on error - * @param string $table + * @param string $tableName - Name of table to access, without any language or general DB prefix * @param string/array $arg * @param string $debug * @desc Insert a row into the table
@@ -375,9 +383,9 @@ class e_db_mysql { * * @access public */ - function db_Insert($table, $arg, $debug = FALSE, $log_type = '', $log_remark = '') + function db_Insert($tableName, $arg, $debug = FALSE, $log_type = '', $log_remark = '') { - $table = $this->db_IsLang($table); + $table = $this->db_IsLang($tableName); $this->mySQLcurTable = $table; if(is_array($arg)) { @@ -406,6 +414,13 @@ class e_db_mysql { if(!isset($arg['data'])) { return false; } + // See if we need to auto-add field types array + if(!isset($arg['_FIELD_TYPES']) && ALLOW_AUTO_FIELD_DEFS) + { + $arg = array_merge($arg, $this->getFieldDefs($tableName)); + } + + // Handle 'NOT NULL' fields without a default value if (isset($arg['_NOTNULL'])) { @@ -483,8 +498,8 @@ class e_db_mysql { /** * @return int number of affected rows, or false on error - * @param string $table - * @param string $arg + * @param string $tableName - Name of table to access, without any language or general DB prefix + * @param array|string $arg (array preferred) * @param bool $debug * @desc Update fields in ONE table of the database corresponding to your $arg variable
*
@@ -499,9 +514,9 @@ class e_db_mysql { * * @access public */ - function db_Update($table, $arg, $debug = FALSE, $log_type = '', $log_remark = '') + function db_Update($tableName, $arg, $debug = FALSE, $log_type = '', $log_remark = '') { - $table = $this->db_IsLang($table); + $table = $this->db_IsLang($tableName); $this->mySQLcurTable = $table; if(!$this->mySQLaccess) @@ -515,7 +530,7 @@ class e_db_mysql { $new_data = ''; if(!isset($arg['_FIELD_TYPES']) && !isset($arg['data'])) { - //Convert data if not using 'new' format (is this needed?) + //Convert data if not using 'new' format $_tmp = array(); if(isset($arg['WHERE'])) { @@ -528,6 +543,12 @@ class e_db_mysql { } if(!isset($arg['data'])) { return false; } + // See if we need to auto-add field types array + if(!isset($arg['_FIELD_TYPES']) && ALLOW_AUTO_FIELD_DEFS) + { + $arg = array_merge($arg, $this->getFieldDefs($tableName)); + } + $fieldTypes = $this->_getTypes($arg); foreach ($arg['data'] as $fn => $fv) { @@ -1452,7 +1473,7 @@ class e_db_mysql { * @TODO Simplify when the conversion script will be available * * @access public - * @param string MySQL charset may be forced in special occasion. + * @param string MySQL charset may be forced in special circumstances * UTF-8 encoding and decoding is left to the progammer * @param bool TRUE enter debug mode. default FALSE * @return string hardcoded error message @@ -1497,6 +1518,180 @@ class e_db_mysql { $this->mySQLcharset = $charset; return $message; } + + + + /** + * Get the _FIELD_DEFS and _NOTNULL definitions for a table + * + * The information is sought in a specific order: + * a) In our internal cache + * b) in the directory e_DB_CACHEDIR - file name $tableName.php + * c) An override file for a core or plugin-related table. If found, the information is copied to the cache directory + * For core overrides, e_ADMIN.'core_sql/db_field_defs.php' is searched + * For plugins, $pref['e_sql_list'] is used as a search list - any file 'db_field_defs.php' in the plugin directory is earched + * d) The table structure is read from the DB, and a definition created: + * AUTOINCREMENT fields - ignored (or integer) + * integer type fields - 'int' processing + * character/string type fields - todb processing + * fields which are 'NOT NULL' but have no default are added to the '_NOTNULL' list + * + * @param string $tableName - table name, without any prefixes (language or general) + * + * @return boolean|array - FALSE if not found/not to be used. Array of field names and processing types and null overrides if found + */ + public function getFieldDefs($tableName) + { + if (!isset($this->dbFieldDefs[$tableName])) + { + if (is_readable(e_DB_CACHE.$tableName.'.php')) + { + $temp = file_get_contents(e_DB_CACHE.$tableName.'.php', FILE_TEXT); + if ($temp !== FALSE) + { + $array = e107::getArrayStorage(); + $typeDefs = $array->ReadArray($temp); + unset($temp); + $this->dbFieldDefs[$tableName] = $typeDefs; + } + } + else + { // Need to try and find a table definition + $searchArray = array(e_ADMIN.'sql/db_field_defs.php'); + $sqlFiles = e107::getPref('e_sql_list'); + foreach ($sqlFiles as $p => $f) + { + $searchArray[] = e_PLUGIN.$p.'/db_field_defs.php'; + } + unset($sqlFiles); + $found = FALSE; + foreach ($searchArray as $defFile) + { + //echo "Check: {$defFile}, {$tableName}
"; + if ($this->loadTableDef($defFile, $tableName)) + { + echo "Found: {$defFile}, {$tableName}
"; + $found = TRUE; + break; + } + } + if (!$found) + { // Need to read table structure from DB and create the file + $this->makeTableDef($tableName); + } + } + } + return $this->dbFieldDefs[$tableName]; + } + + + /* + * Search the specified file for a field type definition of the specified table. + * + * If found, generate and save a cache file in the e_DB_CACHE directory, + * Always also update $this->dbFieldDefs[$tableName] - FALSE if not found, data if found + * + * @param string $defFile - file name, including path + * @param string $tableName - name of table sought + * + * @return boolean TRUE on success, FALSE on not found (some errors intentionally ignored) + */ + protected function loadTableDef($defFile, $tableName) + { + $result = FALSE; + // Read the file using the array handler routines + // File structure is a nested array - first level is table name, second level is either FALSE (for do nothing) or array(_FIELD_DEFS => array(), _NOTNULL => array()) + $temp = file_get_contents($defFile); + // Strip any comments (only /*...*/ supported + $temp = preg_replace("#\/\*.*?\*\/#mis", '', $temp); + //echo "Check: {$defFile}, {$tableName}
"; + if ($temp !== FALSE) + { + $array = e107::getArrayStorage(); + $typeDefs = $array->ReadArray($temp); + unset($temp); + if (isset($typeDefs[$tableName])) + { + $this->dbFieldDefs[$tableName] = $typeDefs[$tableName]; + $fileData = $array->WriteArray($typeDefs[$tableName], FALSE); + if (FALSE === file_put_contents(e_DB_CACHE.$tableName.'.php', $fileData)) + { // Could do something with error - but mustn't return FALSE - would trigger auto-generated structure + } + $result = TRUE; + } + } + + if (!$result) + { + $this->dbFieldDefs[$tableName] = FALSE; + } + return $result; + } + + + /** + * Creates a field type definition from the structure of the table in the DB + * + * Generate and save a cache file in the e_DB_CACHE directory, + * Also update $this->dbFieldDefs[$tableName] - FALSE if error, data if found + * + * @param string $tableName - name of table sought + * + * @return boolean TRUE on success, FALSE on not found (some errors intentionally ignored) + */ + protected function makeTableDef($tableName) + { + require_once(e_HANDLER.'db_table_admin_class.php'); + $dbAdm = new db_table_admin(); + + $baseStruct = $dbAdm->get_current_table($tableName); + $fieldDefs = $dbAdm->parse_field_defs($baseStruct[0][2]); // Required definitions + $outDefs = array(); + foreach ($fieldDefs as $k => $v) + { + switch ($v['type']) + { + case 'field' : + if (vartrue($v['autoinc'])) + { + //break; Probably include autoinc fields in array + } + $baseType = preg_replace('#\(\d+?\)#', '', $v['fieldtype']); // Should strip any length + switch ($baseType) + { + case 'int' : + case 'shortint' : + case 'tinyint' : + $outDefs['_FIELD_TYPES'][$v['name']] = 'int'; + break; + case 'char' : + case 'text' : + case 'varchar' : + $outDefs['_FIELD_TYPES'][$v['name']] = 'todb'; + break; + } + if (isset($v['nulltype']) && !isset($v['default'])) + { + $outDefs['_NOTNULL'][$v['name']] = ''; + } + break; + case 'pkey' : + case 'ukey' : + case 'key' : + break; // Do nothing with keys for now + default : + echo "Unexpected field type: {$k} => {$v['type']}
"; + } + } + $array = e107::getArrayStorage(); + $this->dbFieldDefs[$tableName] = $outDefs; + $toSave = $array->WriteArray($outDefs, FALSE); // 2nd parameter to TRUE if needs to be written to DB + if (FALSE === file_put_contents(e_DB_CACHE.$tableName.'.php', $toSave)) + { // Could do something with error - but mustn't return FALSE - would trigger auto-generated structure + echo "Error writing file: ".e_DB_CACHE.$tableName.'.php'.'
'; + } + } + } /**