database.php

来自「php 开发的内容管理系统」· PHP 代码 · 共 2,046 行 · 第 1/4 页

PHP
2,046
字号
	 * $obj- >page_id is the ID of the Astronomy article
	 *
	 * @todo migrate documentation to phpdocumentor format
	 */
	function selectRow( $table, $vars, $conds, $fname = 'mwDatabase::selectRow', $options = array() ) {
		$options['LIMIT'] = 1;
		$res = $this->select( $table, $vars, $conds, $fname, $options );
		if ( $res === false )
			return false;
		if ( !$this->numRows($res) ) {
			$this->freeResult($res);
			return false;
		}
		$obj = $this->fetchObject( $res );
		$this->freeResult( $res );
		return $obj;

	}

	/**
	 * Removes most variables from an SQL query and replaces them with X or N for numbers.
	 * It's only slightly flawed. Don't use for anything important.
	 *
	 * @param string $sql A SQL Query
	 * @static
	 */
	static function generalizeSQL( $sql ) {
		# This does the same as the regexp below would do, but in such a way
		# as to avoid crashing php on some large strings.
		# $sql = preg_replace ( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql);

		$sql = str_replace ( "\\\\", '', $sql);
		$sql = str_replace ( "\\'", '', $sql);
		$sql = str_replace ( "\\\"", '', $sql);
		$sql = preg_replace ("/'.*'/s", "'X'", $sql);
		$sql = preg_replace ('/".*"/s', "'X'", $sql);

		# All newlines, tabs, etc replaced by single space
		$sql = preg_replace ( "/\s+/", ' ', $sql);

		# All numbers => N
		$sql = preg_replace ('/-?[0-9]+/s', 'N', $sql);

		return $sql;
	}

	/**
	 * Determines whether a field exists in a table
	 * Usually aborts on failure
	 * If errors are explicitly ignored, returns NULL on failure
	 */
	function fieldExists( $table, $field, $fname = 'mwDatabase::fieldExists' ) {
		$table = $this->tableName( $table );
		$res = $this->query( 'DESCRIBE '.$table, $fname );
		if ( !$res ) {
			return NULL;
		}

		$found = false;

		while ( $row = $this->fetchObject( $res ) ) {
			if ( $row->Field == $field ) {
				$found = true;
				break;
			}
		}
		return $found;
	}

	/**
	 * Determines whether an index exists
	 * Usually aborts on failure
	 * If errors are explicitly ignored, returns NULL on failure
	 */
	function indexExists( $table, $index, $fname = 'mwDatabase::indexExists' ) {
		$info = $this->indexInfo( $table, $index, $fname );
		if ( is_null( $info ) ) {
			return NULL;
		} else {
			return $info !== false;
		}
	}


	/**
	 * Get information about an index into an object
	 * Returns false if the index does not exist
	 */
	function indexInfo( $table, $index, $fname = 'mwDatabase::indexInfo' ) {
		# SHOW INDEX works in MySQL 3.23.58, but SHOW INDEXES does not.
		# SHOW INDEX should work for 3.x and up:
		# http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html
		$table = $this->tableName( $table );
		$sql = 'SHOW INDEX FROM '.$table;
		$res = $this->query( $sql, $fname );
		if ( !$res ) {
			return NULL;
		}

		while ( $row = $this->fetchObject( $res ) ) {
			if ( $row->Key_name == $index ) {
				return $row;
			}
		}
		return false;
	}

	/**
	 * Query whether a given table exists
	 */
	function tableExists( $table ) {
		$table = $this->tableName( $table );
		$old = $this->ignoreErrors( true );
		$res = $this->query( "SELECT 1 FROM $table LIMIT 1" );
		$this->ignoreErrors( $old );
		if( $res ) {
			$this->freeResult( $res );
			return true;
		} else {
			return false;
		}
	}

	/**
	 * mysql_fetch_field() wrapper
	 * Returns false if the field doesn't exist
	 *
	 * @param $table
	 * @param $field
	 */
	function fieldInfo( $table, $field ) {
		$table = $this->tableName( $table );
		$res = $this->query( "SELECT * FROM $table LIMIT 1" );
		$n = mysql_num_fields( $res );
		for( $i = 0; $i < $n; $i++ ) {
			$meta = mysql_fetch_field( $res, $i );
			if( $field == $meta->name ) {
				return $meta;
			}
		}
		return false;
	}

	/**
	 * mysql_field_type() wrapper
	 */
	function fieldType( $res, $index ) {
		return mysql_field_type( $res, $index );
	}

	/**
	 * Determines if a given index is unique
	 */
	function indexUnique( $table, $index ) {
		$indexInfo = $this->indexInfo( $table, $index );
		if ( !$indexInfo ) {
			return NULL;
		}
		return !$indexInfo->Non_unique;
	}

	/**
	 * INSERT wrapper, inserts an array into a table
	 *
	 * $a may be a single associative array, or an array of these with numeric keys, for
	 * multi-row insert.
	 *
	 * Usually aborts on failure
	 * If errors are explicitly ignored, returns success
	 */
	function insert( $table, $a, $fname = 'mwDatabase::insert', $options = array() ) {
		# No rows to insert, easy just return now
		if ( !count( $a ) ) {
			return true;
		}

		$table = $this->tableName( $table );
		if ( !is_array( $options ) ) {
			$options = array( $options );
		}
		if ( isset( $a[0] ) && is_array( $a[0] ) ) {
			$multi = true;
			$keys = array_keys( $a[0] );
		} else {
			$multi = false;
			$keys = array_keys( $a );
		}

		$sql = 'INSERT ' . implode( ' ', $options ) .
			" INTO $table (" . implode( ',', $keys ) . ') VALUES ';

		if ( $multi ) {
			$first = true;
			foreach ( $a as $row ) {
				if ( $first ) {
					$first = false;
				} else {
					$sql .= ',';
				}
				$sql .= '(' . $this->makeList( $row ) . ')';
			}
		} else {
			$sql .= '(' . $this->makeList( $a ) . ')';
		}
		return (bool)$this->query( $sql, $fname );
	}

	/**
	 * Make UPDATE options for the Database::update function
	 *
	 * @private
	 * @param array $options The options passed to Database::update
	 * @return string
	 */
	function makeUpdateOptions( $options ) {
		if( !is_array( $options ) ) {
			$options = array( $options );
		}
		$opts = array();
		if ( in_array( 'LOW_PRIORITY', $options ) )
			$opts[] = $this->lowPriorityOption();
		if ( in_array( 'IGNORE', $options ) )
			$opts[] = 'IGNORE';
		return implode(' ', $opts);
	}

	/**
	 * UPDATE wrapper, takes a condition array and a SET array
	 *
	 * @param string $table  The table to UPDATE
	 * @param array  $values An array of values to SET
	 * @param array  $conds  An array of conditions (WHERE). Use '*' to update all rows.
	 * @param string $fname  The Class::Function calling this function
	 *                       (for the log)
	 * @param array  $options An array of UPDATE options, can be one or
	 *                        more of IGNORE, LOW_PRIORITY
	 */
	function update( $table, $values, $conds, $fname = 'mwDatabase::update', $options = array() ) {
		$table = $this->tableName( $table );
		$opts = $this->makeUpdateOptions( $options );
		$sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET );
		if ( $conds != '*' ) {
			$sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
		}
		$this->query( $sql, $fname );
	}

	/**
	 * Makes a wfStrencoded list from an array
	 * $mode:
	 *        LIST_COMMA         - comma separated, no field names
	 *        LIST_AND           - ANDed WHERE clause (without the WHERE)
	 *        LIST_OR            - ORed WHERE clause (without the WHERE)
	 *        LIST_SET           - comma separated with field names, like a SET clause
	 *        LIST_NAMES         - comma separated field names
	 */
	function makeList( $a, $mode = LIST_COMMA ) {
		if ( !is_array( $a ) ) {
			throw new DBUnexpectedError( $this, 'mwDatabase::makeList called with incorrect parameters' );
		}

		$first = true;
		$list = '';
		foreach ( $a as $field => $value ) {
			if ( !$first ) {
				if ( $mode == LIST_AND ) {
					$list .= ' AND ';
				} elseif($mode == LIST_OR) {
					$list .= ' OR ';
				} else {
					$list .= ',';
				}
			} else {
				$first = false;
			}
			if ( ($mode == LIST_AND || $mode == LIST_OR) && is_numeric( $field ) ) {
				$list .= "($value)";
			} elseif ( ($mode == LIST_AND || $mode == LIST_OR) && is_array ($value) ) {
				$list .= $field." IN (".$this->makeList($value).") ";
			} else {
				if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
					$list .= "$field = ";
				}
				$list .= $mode == LIST_NAMES ? $value : $this->addQuotes( $value );
			}
		}
		return $list;
	}

	/**
	 * Change the current database
	 */
	function selectDB( $db ) {
		$this->mDBname = $db;
		return mysql_select_db( $db, $this->mConn );
	}

	/**
	 * Format a table name ready for use in constructing an SQL query
	 *
	 * This does two important things: it quotes table names which as necessary,
	 * and it adds a table prefix if there is one.
	 *
	 * All functions of this object which require a table name call this function
	 * themselves. Pass the canonical name to such functions. This is only needed
	 * when calling query() directly.
	 *
	 * @param string $name database table name
	 */
	function tableName( $name ) {
		global $wgSharedDB;
		# Skip quoted literals
		if ( $name{0} != '`' ) {
			if ( $this->mTablePrefix !== '' &&  strpos( '.', $name ) === false ) {
				$name = "{$this->mTablePrefix}$name";
			}
			if ( isset( $wgSharedDB ) && "{$this->mTablePrefix}user" == $name ) {
				$name = "`$wgSharedDB`.`$name`";
			} else {
				# Standard quoting
				$name = "`$name`";
			}
		}
		return $name;
	}

	/**
	 * Fetch a number of table names into an array
	 * This is handy when you need to construct SQL for joins
	 *
	 * Example:
	 * extract($dbr->tableNames('user','watchlist'));
	 * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
	 *         WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
	 */
	function tableNames() {
		$inArray = func_get_args();
		$retVal = array();
		foreach ( $inArray as $name ) {
			$retVal[$name] = $this->tableName( $name );
		}
		return $retVal;
	}

	/**
	 * @private
	 */
	function tableNamesWithUseIndex( $tables, $use_index ) {
		$ret = array();

		foreach ( $tables as $table )
			if ( @$use_index[$table] !== null )
				$ret[] = $this->tableName( $table ) . ' ' . $this->useIndexClause( implode( ',', (array)$use_index[$table] ) );
			else
				$ret[] = $this->tableName( $table );

		return implode( ',', $ret );
	}

	/**
	 * Wrapper for addslashes()
	 * @param string $s String to be slashed.
	 * @return string slashed string.
	 */
	function strencode( $s ) {
		return mysql_real_escape_string( $s, $this->mConn );
	}

	/**
	 * If it's a string, adds quotes and backslashes
	 * Otherwise returns as-is
	 */
	function addQuotes( $s ) {
		if ( is_null( $s ) ) {
			return 'NULL';
		} else {
			# This will also quote numeric values. This should be harmless,
			# and protects against weird problems that occur when they really
			# _are_ strings such as article titles and string->number->string
			# conversion is not 1:1.
			return "'" . $this->strencode( $s ) . "'";
		}
	}

	/**
	 * Escape string for safe LIKE usage
	 */
	function escapeLike( $s ) {
		$s=$this->strencode( $s );
		$s=str_replace(array('%','_'),array('\%','\_'),$s);
		return $s;
	}

	/**
	 * Returns an appropriately quoted sequence value for inserting a new row.
	 * MySQL has autoincrement fields, so this is just NULL. But the PostgreSQL
	 * subclass will return an integer, and save the value for insertId()
	 */
	function nextSequenceValue( $seqName ) {
		return NULL;
	}

	/**
	 * USE INDEX clause
	 * PostgreSQL doesn't have them and returns ""
	 */
	function useIndexClause( $index ) {
		return "FORCE INDEX ($index)";
	}

	/**
	 * REPLACE query wrapper
	 * PostgreSQL simulates this with a DELETE followed by INSERT
	 * $row is the row to insert, an associative array
	 * $uniqueIndexes is an array of indexes. Each element may be either a
	 * field name or an array of field names
	 *
	 * It may be more efficient to leave off unique indexes which are unlikely to collide.
	 * However if you do this, you run the risk of encountering errors which wouldn't have
	 * occurred in MySQL
	 *
	 * @todo migrate comment to phodocumentor format
	 */
	function replace( $table, $uniqueIndexes, $rows, $fname = 'mwDatabase::replace' ) {
		$table = $this->tableName( $table );

		# Single row case
		if ( !is_array( reset( $rows ) ) ) {
			$rows = array( $rows );
		}

		$sql = "REPLACE INTO $table (" . implode( ',', array_keys( $rows[0] ) ) .') VALUES ';
		$first = true;
		foreach ( $rows as $row ) {
			if ( $first ) {
				$first = false;
			} else {
				$sql .= ',';
			}
			$sql .= '(' . $this->makeList( $row ) . ')';
		}
		return $this->query( $sql, $fname );
	}

	/**
	 * DELETE where the condition is a join
	 * MySQL does this with a multi-table DELETE syntax, PostgreSQL does it with sub-selects
	 *
	 * For safety, an empty $conds will not delete everything. If you want to delete all rows where the
	 * join condition matches, set $conds='*'
	 *
	 * DO NOT put the join condition in $conds
	 *
	 * @param string $delTable The table to delete from.
	 * @param string $joinTable The other table.
	 * @param string $delVar The variable to join on, in the first table.
	 * @param string $joinVar The variable to join on, in the second table.
	 * @param array $conds Condition array of field names mapped to variables, ANDed together in the WHERE clause
	 */
	function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'mwDatabase::deleteJoin' ) {
		if ( !$conds ) {
			throw new DBUnexpectedError( $this, 'mwDatabase::deleteJoin() called with empty $conds' );
		}

		$delTable = $this->tableName( $delTable );
		$joinTable = $this->tableName( $joinTable );
		$sql = "DELETE $delTable FROM $delTable, $joinTable WHERE $delVar=$joinVar ";
		if ( $conds != '*' ) {
			$sql .= ' AND ' . $this->makeList( $conds, LIST_AND );
		}

		return $this->query( $sql, $fname );
	}

	/**
	 * Returns the size of a text field, or -1 for "unlimited"
	 */
	function textFieldSize( $table, $field ) {
		$table = $this->tableName( $table );
		$sql = "SHOW COLUMNS FROM $table LIKE \"$field\";";
		$res = $this->query( $sql, 'mwDatabase::textFieldSize' );
		$row = $this->fetchObject( $res );
		$this->freeResult( $res );

		if ( preg_match( "/\((.*)\)/", $row->Type, $m ) ) {
			$size = $m[1];
		} else {
			$size = -1;
		}
		return $size;
	}

	/**
	 * @return string Returns the text of the low priority option if it is supported, or a blank string otherwise
	 */
	function lowPriorityOption() {
		return 'LOW_PRIORITY';
	}

	/**
	 * DELETE query wrapper
	 *
	 * Use $conds == "*" to delete all rows
	 */
	function delete( $table, $conds, $fname = 'mwDatabase::delete' ) {
		if ( !$conds ) {
			throw new DBUnexpectedError( $this, 'mwDatabase::delete() called with no conditions' );
		}
		$table = $this->tableName( $table );
		$sql = "DELETE FROM $table";
		if ( $conds != '*' ) {
			$sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?