Public feed #727514

Mine

public php v1 · immutable
Raw Edit
·published 2007-10-06 02:36 UTC
rendered paste body
<?php	require('connect.php');	require_once('upgrade_db.php');		class Sql {		var $logfile = null;		var $echo = false;		var $common_fields = true;		var $user_id = null;		var $warning = null;		var $warnings = array();		var $db = null;				function __construct($logfile = null, $echo = false) {			if ($logfile) {				if (is_string($logfile)) {					// Assume it's a file name					$this->logfile = fopen($logfile, 'w') or die("Can't open logfile '$logfile'.");				} else {					// Assume it's an open file resource					$this->logfile = $logfile;				}			}						// By default, print out all statements executed if and only if SQL_DEBUG is defined as a true value.			$this->echo = $echo;		}				function logline($msg) {			if ($this->echo) {				echo (is_string($this->echo)? $this->echo: "\n<br>");				echo htmlentities($msg);				//echo $msg;			}						if ($this->logfile) {				$find = array("\r", "\t", "\n\n");				$repl = array('', ' ', "\n");				fwrite($this->logfile, trim(str_replace($find, $repl, $msg))."\n\n");			}		}				function warn($msg, $fatal = false) {			$this->logline('-- WARNING: '.$msg);			$this->warnings[] = $this->warning = $msg;			if ($fatal) exit(1);		}				function sum_query($result_field, $fields, $tables, $conditions = null, $allow_deleted = false) {			if (!$allow_deleted) $conditions = $this->check_deleted($this->primary_table($tables), $conditions);			$query = 'SELECT SUM(' . $this->escape($fields) . ') AS ' .  $this->escape($result_field) .				$this->from($tables, $allow_deleted) . $this->conditions($conditions);			return $query;		}				function count_query($result_field, $fields, $tables, $conditions = null, $allow_deleted = false) {			if (!$allow_deleted) $conditions = $this->check_deleted($this->primary_table($tables), $conditions);			$query = 'SELECT COUNT(' . $this->escape($fields) . ') AS ' .  $this->escape($result_field) .				$this->from($tables, $allow_deleted) . $this->conditions($conditions);			return $query;		}				function select_query($fields, $tables, $conditions = null, $order = null, $group = null, $limit = null, $page = 1, $allow_deleted = false) {			$order_clause = $this->order_clause($order);			$group_clause = $group? ' GROUP BY ' . $this->escape($group): '';			if (!$allow_deleted) $conditions = $this->check_deleted($this->primary_table($tables), $conditions);			$skip = (intval($page) > 1)? (intval($limit) * (intval($page) - 1)).', ': '';			$limit_clause = intval($limit)? ' LIMIT '.$skip.intval($limit): '';						$query = 'SELECT ' . $this->escape($fields) . $this->from($tables, $allow_deleted) .				$this->conditions($conditions) . $group_clause . $order_clause . $limit_clause;			return $query;		}				function insert_query($table, $fields) {			if ($this->common_fields) {				// Automatically insert common fields				$fields['creator_id'] = $this->user_id;				$fields['date_created'] = $this->datetime();			}						$query = 'INSERT INTO ' . $table . $this->conditions($fields, ' SET ', ', ');			return $query;		}				function update_query($table, $updates, $conditions, $allow_deleted = false) {			if (!$allow_deleted) $conditions = $this->check_deleted($this->primary_table($table), $conditions);			$query = 'UPDATE ' . $this->escape($table) . $this->conditions($updates, ' SET ', ', ') . $this->conditions($conditions);			return $query;		}				function delete_query($table, $conditions) {			// Actually removes the rows from the database.			$query = 'DELETE FROM ' . $this->escape($table) . $this->conditions($conditions);			return $query;		}				function create_table_query($table, $fields, $keys = array(), $engine = 'InnoDB', $charset = 'utf8') {			// Constructs a query to create the table.			//			// $fields should be an array of 'column_name' => array(options), where options can be:			// * 'reference': 'table.field'; specifies that this is a foreign key to the given table.			// * 'type':      Either 'primary', or a valid MySQL type.  Required unless 'reference' is given.			// * 'signed':    For numeric types, specifies whether the field is signed (true) or unsigned (false).			// * 'length':    An optional length for string column types, and for years (2 or 4).			// * 'precision': Number of total digits for fixed-point decimal column types.  Defaults to 10.			// * 'scale':     Number of digits after the decimal point for fixed-point column types.  Defaults to 2.			// * 'values':    A required array of values for 'set' and 'enum' column types.			// * 'null':      Whether to allow null values.  Defaults to NOT NULL, unless 'default' is null.			// * 'default':   Default value for the column.  If null, overrides the 'null' option.			// * 'unique':    Whether to disallow duplicate values in this column.  Defaults to false.			//			// $keys is an array of arrays, where each array is a list of column names in the key.			// Each array may also contain the key 'unique', with the same meaning as above.			//			// $engine and $charset should usually not be specified.						$common_fields = array(				'id' => array('type' => 'primary'),				'sequence' => array('type' => 'int', 'signed' => true, 'default' => 1),				'creator_id' => array('reference' => 'users.id', 'default' => null),				'date_created' => array('type' => 'datetime', 'default' => null),				'date_deleted' => array('type' => 'datetime', 'default' => null),				'description_id' => array('reference' => 'descriptions.id', 'default' => null),			);						$lines = array();			$indexes = array();			$references = array();			if (!is_array($keys)) $keys = array();			foreach (array_merge($common_fields, $fields) as $column => $field) {				$signed = $null = $default = null;				$items = array($this->escape($column));								if (array_key_exists('unique', $field) and !empty($field['unique'])) {					$keys[] = array($column, 'unique' => true);				}								if (array_key_exists('default', $field)) {					$default = 'DEFAULT '.$this->sanitize($field['default']);					if (!is_null($field['default']) and !array_key_exists('null', $field)) $null = 'NOT NULL';				} else if (empty($field['null'])) {					// Note: This makes each field not null unless otherwise specified.					$null = 'NOT NULL';				}								if (!empty($field['reference'])) {					$type = 'INT';					$signed = 'UNSIGNED';					$references[$column] = $field['reference'];				}								if (!empty($field['type'])) {					switch ($field['type']) {						// Aliases to set several attributes at once						case 'primary':							$type = 'INT';  // Todo: Consider bigint for primary keys							$null = 'NOT NULL';							$signed = 'UNSIGNED';							$default = 'AUTO_INCREMENT PRIMARY KEY';							break;						case 'serial':							$type = 'BIGINT';							$null = 'NOT NULL';							$signed = 'UNSIGNED';							$default = 'AUTO_INCREMENT UNIQUE';							break;													// Numeric types						case 'int':						case 'float':						case 'double':						case 'bigint':						case 'integer':						case 'tinyint':						case 'smallint':						case 'mediumint':							$type = strtoupper($field['type']);							if (array_key_exists('signed', $field)) $signed = $field['signed']? 'SIGNED': 'UNSIGNED';							break;						case 'bool':						case 'boolean':							if (array_key_exists('default', $field)) {								$val = is_null($field['default'])? 'NULL': empty($field['default'])? 'FALSE': 'TRUE';								$default = 'DEFAULT '.$val;							}														// Fall through						case 'bit':							$type = strtoupper($field['type']);							//$signed = 'UNSIGNED';							break;						case 'dec':						case 'fixed':						case 'decimal':							if (array_key_exists('signed', $field)) $signed = $field['signed']? 'SIGNED': 'UNSIGNED';							$precision = array_key_exists('precision', $field)? intval($field['precision']): 10;							$scale = array_key_exists('scale', $field)? intval($field['scale']): 2;							$type = "DECIMAL($precision,$scale)";							break;													// Date and time types						case 'year':							$length = array_key_exists('length', $field)? intval($field['length']): 4;							$type = "YEAR($length)";							break;						case 'date':						case 'time':						case 'datetime':						case 'timestamp':							$type = strtoupper($field['type']);							break;													// String types						case 'char':						case 'blob':						case 'text':						case 'binary':						case 'varchar':						case 'tinyblob':						case 'tinytext':						case 'longblob':						case 'longtext':						case 'varbinary':						case 'mediumblob':						case 'mediumtext':							$type = strtoupper($field['type']);							if (array_key_exists('length', $field)) $type .= '('.intval($field['length']).')';							break;						case 'set':						case 'enum':							$values = array();							if (array_key_exists('values', $field) and is_array($field['values'])) {								foreach ($field['values'] as $val) $values[] = $this->sanitize($val);							} else {								$this->warn("Missing $field[type] values for column $column.");							}														$type = strtoupper($field['type']).'('.join(',', $values).')';							break;												default:							$this->warn("Unsupported type '$field[type]' for column $column.");							continue 2;					}				} else if (empty($type)) {					$this->warn("Missing type for column $column.");					continue;				}								$items[] = $type;				if (!empty($signed)) $items[] = $signed;				if (!empty($null)) $items[] = $null;				if (!empty($default)) $items[] = $default;				$lines[] = join(' ', $items);			}						foreach ($keys as $columns) {				if (!is_array($columns)) $columns = array($columns);								$unique = '';				if (array_key_exists('unique', $columns)) {					if (empty($columns['unique'])) {						unset($columns['unique']);					} else {						$columns['unique'] = 'date_deleted';						$unique = 'UNIQUE ';					}				}								$indexes[$columns[0]] = true;				$lines[] = $unique.'KEY ('.join(',', array_map(array($this, 'escape'), $columns)).')';			}						foreach ($references as $column => $ref) {				$col = $this->escape($column);				$info = explode('.', $ref);				switch (count($info)) {					case 1:						$reftab = $this->escape($ref);						$refcol = $this->escape('id');						break;					case 2:						$reftab = $this->escape($info[0]);						$refcol = $this->escape($info[1]);						break;					default:						$this->warn("Unparsable reference '$ref' in column $column.");						continue 2;				}								$cn = $this->escape($table.'__'.$column);				$key = array_key_exists($column, $indexes)? '': "KEY ($col), ";				$lines[] = $key."CONSTRAINT $cn FOREIGN KEY ($col) REFERENCES $reftab ($refcol)";			}						$query = "CREATE TABLE ".$this->escape($table)." (\n  ".join(",\n  ", $lines)."\n) ENGINE=$engine DEFAULT CHARSET=$charset";						return $query;		}				function is_list($array) {			foreach (array_keys($array) as $key) if (!is_int($key)) return false;			return true;		}				function conditions($fields, $clause = ' WHERE ', $join = ' AND ') {			$inserting = (trim($clause) == 'SET');			$joining = (trim($clause) == 'ON');			if (empty($fields)) {				if ($inserting) $this->warn('Trying to update nothing');				return '';			}						$results = array();			$operator = $inserting? '=': 'IS';			foreach ($fields as $key => $value) {				if (is_null($value)) {					$results[] = $this->escape($key)." $operator NULL";				} else if (is_array($value)) {					if (empty($value)) {						$results[] = 'FALSE';					} else if (is_numeric($key)) {						$results[] = $this->conditions($value, '', (trim($join) == 'AND')? ' OR ': ' AND ');					} else if ($this->is_list($value)) {						$vals = array();						$has_null = false;						foreach ($value as $item) {							if (is_null($item)) {								$has_null = true;							} else {								$vals[] = $this->sanitize($item);							}						}												$column = $this->escape($key);						switch (count($vals)) {							case 0: $main = 'FALSE'; break;							case 1: $main = $column.' = '.$vals[0]; break;							default: $main = $column.' IN ('.join(', ', $vals).')'; break;						}												$results[] = $has_null? "($column IS NULL) OR ($main)": $main;					} else {						$results[] = $this->conditions($value, '', ' '.trim(strval($key)).' ');					}				} else if (is_numeric($key)) {					$results[] = $value;				} else if ($joining and is_string($value) and preg_match("/^[a-z]\\w+\\.\\w+$/", $value)) {					$results[] = $this->escape($key).' <=> '.$this->escape($value);				} else {					$results[] = $this->escape($key).' = '.$this->sanitize($value);				}			}						if (!$inserting) $results = array_map(array($this, 'wrap'), $results);			return $clause . implode($join, $results);		}				function wrap($item) {			return "($item)";		}				function from($tables, $allow_deleted = false) {			$primary = '';			$joining = '';			if (is_array($tables)) {				foreach ($tables as $table => $conditions) {					if (is_array($conditions)) {						if (!$allow_deleted) $conditions = $this->check_deleted($table, $conditions);						if (array_key_exists('on', $conditions)) {							$table = array(strval($conditions['on']) => $table);							unset($conditions['on']);						}												$joining .= ' LEFT JOIN ' . $this->escape($table) . $this->conditions($conditions, ' ON ');					} else {						$primary = $this->escape(is_numeric($table)? strval($conditions): array(strval($conditions) => $table));					}				}			} else {				$primary = $this->escape(strval($tables));			}						return ' FROM ' . $primary . $joining;		}				function primary_table($tables) {			$table = null;			if (is_string($tables)) {				$table = $tables;			} else if (is_array($tables)) {				if (array_key_exists(0, $tables)) {					$table = $tables[0];				} else {					foreach ($tables as $key => $value) {						if (is_string($value)) {							$table = $key;							break;						}					}				}			}						if (empty($table)) $this->warn('Invalid table specification: '.print_r($tables, true), true);			return $table;		}				function check_deleted($table, $conditions) {			if (!$this->common_fields) {				$result = $conditions;			} else if (is_null($conditions)) {				$result = array($table.'.date_deleted' => null);			} else if (is_array($conditions)) {				$result = $conditions;				$result[$table.'.date_deleted'] = null;			} else if (is_numeric($conditions)) {				$result = array($table.'.date_deleted' => null, $table.'.id' => intval($conditions));			} else {				$result = array($table.'.date_deleted' => null, $conditions);			}						return $result;		}				function operator($left, $op, $right, $allow_null = false) {			if (count($right) or is_null($right)) {				$result = $this->escape($left).' '.$op.' '.$this->sanitize($right);				if ($allow_null) $result = $this->conditions(array($left => null, $result), '', ' OR ');			} else if ($allow_null) {				$result = $this->conditions(array($left => null), '');			} else {				$result = 'FALSE';			}						return $result;		}				function order_clause($order) {			$clause = ' ORDER BY ';			if (empty($order)) {				$result = '';			} else if (is_array($order)) {				$escaped = array();				foreach ($order as $key => $name) {					$direction = strtoupper(trim($name));					if (in_array($direction, array('ASC', 'DESC'))) {						$escaped[] = $this->escape($key).' '.$direction;					} else {						$escaped[] = $this->escape($name);					}				}								$result = $clause . join(', ', $escaped);			} else {				$result = $clause . $this->escape($order);			}						return $result;		}				function escape($columns) {			if (empty($columns)) {				$result = '*';			} else if (is_array($columns)) {				$escaped = array();				$result = '';				foreach ($columns as $key => $name) {					if (is_numeric($key) and strtolower($name) == 'distinct') {						$result = 'DISTINCT ';					} else {						$escaped[] = (is_numeric($key)? '': $this->escape($key).' AS ').$this->escape($name);					}				}								$result .= join(', ', $escaped);			} else {				$result = '`'.str_replace('.', '`.`', $columns).'`';			}						return $result;		}				function date($timestamp = null) {			// Formats a date, defaulting to today, as expected by the MySQL server			return is_null($timestamp)? date('Y-m-d'): date('Y-m-d', $timestamp);		}				function datetime($timestamp = null) {			// Formats a time, defaulting to now, as expected by the MySQL server			return is_null($timestamp)? date('Y-m-d H:i:s'): date('Y-m-d H:i:s', $timestamp);		}				function sanitize($value) {			if (is_null($value)) {				return 'NULL';			} else if (is_int($value)) {				return intval($value);			} else if (is_numeric($value)) {				return "'$value'";			} else if ($value === false) {				return 0;			} else if ($value === true) {				return 1;			} else if (is_array($value)) {				return '('.join(',', array_map(array($this, 'sanitize'), $value)).')';			} else if ($this->db) {				return "'".mysql_real_escape_string(trim(strval($value)), $this->db)."'";			} else {				return "'".mysql_escape_string(trim(strval($value)))."'";			}		}	}		class Database extends Sql {		var $prefix = '';				function __construct($details, $logfile = null) {			// By default, print out all statements executed if and only if SQL_DEBUG is defined as a true value.			$echo = (defined('SQL_DEBUG') and SQL_DEBUG and !empty($details['database']))? "\n<br>$details[database]: ": false;			parent::__construct($logfile, $echo);						foreach (array('username', 'password', 'host', 'database') as $key) {				if (empty($details[$key])) $this->warn("Missing $key in connection details.", true);			}						$this->logline("-- Connecting to $details[database] on $details[username]:$details[password]@$details[host]");			$this->db = mysql_connect($details['host'], $details['username'], $details['password'], true) or die("Can't connect: " . mysql_error());			mysql_select_db($details['database'], $this->db) or die("Can't select database: " . mysql_error($this->db));			if (!empty($details['prefix'])) $this->prefix = $details['prefix'];		}				function query($query) {			$this->logline($query.';');			$results = mysql_query($query, $this->db);			if (!$results) $this->warn('Query failed: ' . mysql_error($this->db));			return $results;		}				function sum($fields, $tables, $conditions = null, $allow_deleted = false) {			$result_field = 'num';			$query = $this->sum_query($result_field, $fields, $tables, $conditions, $allow_deleted);			$results = $this->query($query);			if (!$results) return null;			$returned = mysql_num_rows($results);						$request = null;			if ($returned == 1) {				$row = mysql_fetch_assoc($results);				if (array_key_exists($result_field, $row)) {					$request = intval($row[$result_field]);				} else {					$this->warn("Can't find '$result_field' in sum results.");				}			} else {				$this->warn("$returned rows returned for a sum.");			}						return $request;		}				function count($fields, $tables, $conditions = null, $allow_deleted = false) {			$result_field = 'num';			$query = $this->count_query($result_field, $fields, $tables, $conditions, $allow_deleted);			$results = $this->query($query);			if (!$results) return null;			$returned = mysql_num_rows($results);						$request = null;			if ($returned == 1) {				$row = mysql_fetch_assoc($results);				if (array_key_exists($result_field, $row)) {					$request = intval($row[$result_field]);					$this->logline('  -- '.$request);				} else {					$this->warn("Can't find '$result_field' in count results.");				}			} else {				$this->warn("$returned rows returned for a count.");			}						return $request;		}				function select($fields, $tables, $conditions = null, $allow_deleted = false, $group = null) {			$query = $this->select_query($fields, $tables, $conditions, $order = null, $group, $limit = 1, $page = 1, $allow_deleted);			$results = $this->query($query);			if (!$results) return null;			$returned = mysql_num_rows($results);						if ($returned) {				$row = mysql_fetch_assoc($results);				$items = array();				foreach ($row as $key => $value) $items[] = $this->escape($key).' => '.$this->sanitize($value);				$this->logline('  -- ( ' . join(', ', $items) . ' )');								if (!is_string($fields)) {					$request = $row;				} else if (array_key_exists($fields, $row)) {					$request = $row[$fields];				} else if ($dot = strrpos($fields, '.')) {					$fieldname = substr($fields, $dot + 1);					if (array_key_exists($fieldname, $row)) {						$request = $row[$fieldname];					} else {						$this->warn("Can't find '$fields' or '$fieldname' in results.");					}				} else {					$this->warn("Can't find '$fields' in results.");				}			} else {				$this->logline('  -- Row not found.');				$request = null;			}						return $request;		}				function select_all($fields, $tables, $conditions = null, $order = null, $allow_deleted = false, $limit = null, $page = 1, $group = null) {			$query = $this->select_query($fields, $tables, $conditions, $order, $group, $limit, $page, $allow_deleted);			$results = $this->query($query);			if (!$results) return null;			$returned = mysql_num_rows($results);			$s = ($returned == 1)? '': 's';			$this->logline("  -- Returned $returned row$s.");						$request = array();			if (is_string($fields)) $fetch_field = $fields;			$fieldname = is_string($fields)? ($dot = strrpos($fields, '.'))? substr($fields, $dot + 1): $fields: null;			while ($row = mysql_fetch_assoc($results)) {				$request[] = array_key_exists($fieldname, $row)? $row[$fieldname]: $row;			}						return $request;		}				function get($fields, $tables, $id) {			return $this->select($fields, $tables, array($this->primary_table($tables).'.id' => intval($id)));		}				function insert($table, $fields) {			//returns id of the new record, null if more than 1 row was inserted/modified. 			$result = null;			$query = $this->insert_query($table, $fields);			$results = $this->query($query);			$affected = mysql_affected_rows($this->db);			if ($affected == 1) {				$result = mysql_insert_id($this->db);				$this->logline('  --  Inserted as #'.$result);			} else if ($affected > 1) {				$this->warn("Inserted $affected rows.");			} else {				$this->warn('Insert failed.');			}						return $result;		}				function update($table, $updates, $conditions) {			//returns the number of affected rows			if (!isset($conditions)) die('Empty $conditions in update!');			$this->query($this->update_query($table, $updates, $conditions));			$affected = mysql_affected_rows($this->db);			$s = ($affected == 1)? '': 's';			$this->logline("  -- Updated $affected row$s.");			return $affected;		}				function delete($table, $conditions) {			if ($this->common_fields) {				// Doesn't actually remove the rows from the database; sets the date_deleted field instead.				$deleted = $this->update($table, array('date_deleted' => $this->datetime()), $conditions);			} else {				$deleted = $this->purge($table, $conditions);			}			//returns number of affected rows			return $deleted;		}				function purge($table, $conditions) {			// Actually removes the rows from the database; use delete() unless you're certain this is what you want.			// returns: the number of affected (removed) rows			$this->query($this->delete_query($table, $conditions));			$affected = mysql_affected_rows($this->db);			$s = ($affected == 1)? '': 's';			$this->logline("  -- Deleted $affected row$s.");			return $affected;		}				function save($table, $updates, $conditions) {			// Update the record in the database, adding a new one if necessary			// returns: the id of the new or old row			if (!isset($conditions)) die('Empty $conditions in save!');			$old_vals = $this->select(is_array($updates)? array_keys($updates): $updates, $table, $conditions);			if (is_null($old_vals)) {				$this->logline("  -- Inserting new row.");				if (is_array($conditions)) $updates = is_array($updates)? array_merge($updates, $conditions): $conditions;				return $this->insert($table, $updates);			} else if (empty($updates) or ($old_vals == $updates)) {				$this->logline("  -- Nothing needs to be updated.");				$result = true;			} else {				$this->logline("  -- Updating old rows.");				$result = $this->update($table, $updates, $conditions);			}						if ($result and $this->common_fields) {				if (is_array($conditions) and array_key_exists('id', $conditions)) {					$result = $conditions['id'];				} else if (array_key_exists('id', $updates)) {					$result = $updates['id'];				} else if (array_key_exists('id', $old_vals)) {					$result = $old_vals['id'];				} else {					$result = $this->select('id', $table, $conditions);				}			}						return $result;		}				function get_max($table, $field, $conditions = null, $allow_deleted = false) {			if (!$allow_deleted) $conditions = $this->check_deleted($this->primary_table($table), $conditions);			$result = $this->query('SELECT MAX('.$field.') AS `maximum_value` FROM '.$table.' '.$this->conditions($conditions));						$value = 0;			if (!empty($result)) {				$row = mysql_fetch_assoc($result);				if (array_key_exists('maximum_value', $row)) {					$value = $row['maximum_value'];				}			}						return $value;		}				function get_property($table, $list_name, $item_id) {			$result = $this->select('property_options.value', array(				$table,				'property_options' => array('property_options.id' => $table.'.property_id'),				'property_lists' => array('property_lists.id' => 'property_options.list_id'),			), array(				$table.'.item_id' => $item_id,				'property_lists.list_name' => $list_name,			));						if (is_null($result)) {				$result = $this->select('property_options.value', array(					'property_options',					'property_lists' => array('property_lists.id' => 'property_options.list_id'),				), array(					'property_options.is_default' => true,					'property_lists.list_name' => $list_name,				));			}						return $result;		}				function set_property($table, $list_name, $value, $item_id) {			// Yes, this is messy.  Can it be cleaned up?			$list = $this->select(array('id', 'allow_multiple', 'allow_custom'), 'property_lists',				array('list_name' => $list_name));			if (empty($list)) {				$this->warn('Missing property list: '.$list_name);				return false;			}						$options = $this->select_all(array($table.'.id', 'property_options.value'), array(				$table,				'property_options' => array('property_options.id' => $table.'.property_id'),				'property_lists' => array('property_lists.id' => 'property_options.list_id'),			), array(				$table.'.item_id' => $item_id,				'property_lists.list_name' => $list_name,			));						$option_ids = array();			foreach ($options as $option) {				if ($option['value'] == strval($value)) {					// Option already set.					$this->logline("-- Option '$value' already set for property list '$list_name'.");					return true;				} else {					$option_ids[] = $option['id'];				}			}						$option_id = $this->select('property_options.id', array(				'property_options',				'property_lists' => array('property_lists.id' => 'property_options.list_id'),			), array('property_lists.list_name' => $list_name, 'property_options.value' => $value));						if (empty($option_id)) {				if ($list['allow_custom']) {					$option_id = $this->insert('property_options', array('list_id' => $list['id'], 'value' => strval($value)), true);					if (empty($option_id)) return false;				} else {					$this->warn("Missing option '$value' in property list '$list_name'.");					return false;				}			}						if ($list['allow_multiple']) {				// Add a new row to the group				$result = $this->insert($table, array('item_id' => $item_id, 'property_id' => $option_id));			} else if (count($option_ids) == 1) {				// Update the old row directly				$result = $this->update($table, array('property_id' => $option_id), array('item_id' => $item_id));			} else {				// Delete any old rows, then add a new one				if ($option_ids) $this->delete($table, array('property_id' => $option_ids));				$result = $this->insert($table, array('item_id' => $item_id, 'property_id' => $option_id));			}						return $result;		}				function set_description($table, $row, $description) {			$desc_id = $this->get('description_id', $table, $row);			if ($desc_id and empty($description)) {				$result = $this->update('descriptions', array('value' => ''), array('id' => $desc_id));			} else if (empty($description)) {				$result = true;			} else if (is_null($desc_id)) {				$desc_id = $this->insert('descriptions', array('value' => $description), true);				if (empty($desc_id)) return false;				$result = $this->update($table, array('description_id' => $desc_id), array('id' => $row));			} else {				$result = $this->save('descriptions', array('value' => $description), array('id' => $desc_id));			}						return $result;		}				function has_column($table, $column) {			$result = false;			$description = $this->query('DESCRIBE ' . $this->escape($table));			if (!$results) return null;			while ($table_row = mysql_fetch_assoc($description)) {				if ($table_row['Field'] == $column) {					$result = true;					break;				}			}						return $result;		}	}		$db = new Database($db_info);	//$db->echo = true;	check_db_version();	//rollback_db(29);?>