db->get_results( $prepare, $this->output ); } if ( $reset ) { $this->reset(); } $this->cache[ $cacheTableName ][ $queryHash ][ $return ] = $this->result; // Reset the cache trigger for the next run. $this->shouldResetCache = false; return $this; } /** * Inject a count select statement and return the result. * * @since 4.1.0 * * @param string $countColumn The column to count with. Defaults to '*' all. * @return int The number of rows that were found. */ public function count( $countColumn = '*' ) { $usingGroup = ! empty( $this->group ); $results = $this->reset( [ 'select', 'order', 'limit' ] ) ->select( 'count(' . $countColumn . ') as count' ) ->run() ->result(); return 1 === $this->numRows() && ! $usingGroup ? (int) $results[0]->count : $this->numRows(); } /** * Inject a count group select statement and return the result. * * @since 4.6.1 * * @param string $countDistinctColumn The column to count with. Defaults to '*' all. * @return int The number of rows that were found. */ public function countDistinct( $countDistinctColumn = '*' ) { $countDistinctColumn = '*' !== $countDistinctColumn ? 'distinct( ' . $countDistinctColumn . ' )' : $countDistinctColumn; return $this->reset( [ 'select', 'order', 'limit' ] ) ->select( 'count(' . $countDistinctColumn . ') as count' ) ->run( true, 'var' ) ->result(); } /** * Returns the query results based on the value of the output property. * * @since 4.0.0 * * @return mixed This depends on what was set in the output property. */ public function result() { return $this->result; } /** * Return a model model from a row. * * @since 4.0.0 * * @param string $class The name of the model class to call. * @return object The model class instance. */ public function model( $class ) { $result = $this->result(); return ! empty( $result ) ? ( is_array( $result ) ? new $class( (array) current( $result ) ) : $result ) : new $class(); } /** * Return an array of model class instancnes from the result. * * @since 4.0.0 * * @param string $class The name of the model class to call. * @param string $id The ID of the index to use. * @param bool $toJson The index if necessary. * @return array An array of model class instances. */ public function models( $class, $id = null, $toJson = false ) { if ( ! empty( $this->models ) ) { return $this->models; } $i = 0; $models = []; foreach ( $this->result() as $row ) { $var = ( null === $id ) ? $row : $row[ $id ]; $class = new $class( $var ); // Lets add the class to the array using the class ID. $models[ $class->id ] = $toJson ? $class->jsonSerialize() : $class; $i++; } $this->models = $models; return $this->models; } /** * Returns the last error reported by MySQL. * * @since 4.0.0 * * @return string The last error message. */ public function lastError() { return $this->db->last_error; } /** * Return the $wpdb insert_id from the last query. * * @since 4.0.0 * * @return int The ID of the most recent INSERT query. */ public function insertId() { return $this->db->insert_id; } /** * Return the $wpdb rows_affected from the last query. * * @since 4.0.0 * * @return int The number of rows affected. */ public function rowsAffected() { return $this->db->rows_affected; } /** * Return the $wpdb num_rows from the last query. * * @since 4.0.0 * * @return int The count for the number of rows in the last query. */ public function numRows() { return $this->db->num_rows; } /** * Check if the last query had any rows. * * @since 4.0.0 * * @return bool Whether there were any rows retrived by the last query. */ public function nullSet() { return ( $this->numRows() < 1 ); } /** * This will start a MySQL transaction. Be sure to commit or rollback! * * @since 4.0.0 */ public function startTransaction() { $this->db->query( 'START TRANSACTION' ); } /** * This will commit a MySQL transaction. Used in conjunction with startTransaction. * * @since 4.0.0 */ public function commit() { $this->db->query( 'COMMIT' ); } /** * This will rollback a MySQL transaction. Used in conjunction with startTransaction. * * @since 4.0.0 */ public function rollback() { $this->db->query( 'ROLLBACK' ); } /** * Fast way to execute raw queries. * NOTE: When using this method, all arguments must be sanitized manually! * * @since 4.0.0 * * @param string $sql The sql query to execute. * @param bool $results Whether to return the results or not. * @param bool $useCache Whether to use the cache or not. * @return mixed Could be an array or object depending on the result set. */ public function execute( $sql, $results = false, $useCache = false ) { $this->lastQuery = $sql; $queryHash = sha1( $sql ); $cacheTableName = $this->getCacheTableName(); // Pull the result from the in-memory cache if everything checks out. if ( $useCache && ! $this->shouldResetCache && isset( $this->cache[ $cacheTableName ][ $queryHash ] ) ) { if ( $results ) { $this->result = $this->cache[ $cacheTableName ][ $queryHash ]; } return $this; } if ( $results ) { $this->result = $this->db->get_results( $sql, $this->output ); if ( $useCache ) { $this->cache[ $cacheTableName ][ $queryHash ] = $this->result; // Reset the cache trigger for the next run. $this->shouldResetCache = false; } return $this; } return $this->db->query( $sql ); } /** * Escape a value for safe use in SQL queries. * * @param string $value The value to be escaped. * @param int|null $options The escape options. * @return string The escaped SQL value. */ public function escape( $value, $options = null ) { if ( is_array( $value ) ) { foreach ( $value as &$val ) { $val = $this->escape( $val, $options ); } return $value; } $options = ( is_null( $options ) ) ? $this->getEscapeOptions() : $options; if ( ( $options & self::ESCAPE_STRIP_HTML ) !== 0 && isset( $this->stripTags ) && true === $this->stripTags ) { $value = wp_strip_all_tags( $value ); } if ( ( ( $options & self::ESCAPE_FORCE ) !== 0 || php_sapi_name() === 'cli' ) || ( ( $options & self::ESCAPE_QUOTE ) !== 0 && ! is_int( $value ) ) ) { $value = esc_sql( $value ); if ( ! is_int( $value ) ) { $value = "'$value'"; } } return $value; } /** * Returns the current escape options value. * * @since 4.0.0 * * @return int The current escape options value. */ public function getEscapeOptions() { return $this->escapeOptions; } /** * Sets the current escape options value. * * @since 4.0.0 * * @param int $options The escape options value. */ public function setEscapeOptions( $options ) { $this->escapeOptions = $options; } /** * Backtick-escapes an array of column and/or table names. * * @since 4.0.0 * * @param array $cols An array of column names to be escaped. * @return array An array of escaped column names. */ private function escapeColNames( $cols ) { if ( ! is_array( $cols ) ) { $cols = [ $cols ]; } foreach ( $cols as &$col ) { if ( false === stripos( $col, '(' ) && false === stripos( $col, ' ' ) && false === stripos( $col, '*' ) ) { if ( stripos( $col, '.' ) ) { list( $table, $c ) = explode( '.', $col ); $col = "`$table`.`$c`"; continue; } $col = "`$col`"; } } return $cols; } /** * Gets a variable list of function arguments and reformats them as needed for many of the functions of this class. * * @since 4.0.0 * * @param mixed $values This could be anything, but if used properly it usually is a string or an array. * @return mixed If the preparation was successful, it will return an array of arguments. Otherwise it could be anything. */ private function prepArgs( $values ) { $values = (array) $values; if ( ! is_array( $values[0] ) && count( $values ) === 2 ) { $values = [ $values[0] => $values[1] ]; } elseif ( is_array( $values[0] ) && count( $values ) === 1 ) { $values = $values[0]; } return $values; } /** * Resets all the variables that make up the query. * * @since 4.0.0 * * @param array $what Set which properties you want to reset. All are selected by default. * @return Database Returns the Database instance. */ public function reset( $what = [ 'table', 'statement', 'limit', 'group', 'order', 'select', 'set', 'onDuplicate', 'ignore', 'where', 'union', 'distinct', 'orderDirection', 'query', 'output', 'stripTags', 'models', 'join' ] ) { // If we are not running a select query, let's bust the cache for this table. $selectStatements = [ 'SELECT', 'SELECT DISTINCT' ]; if ( ! empty( $this->statement ) && ! in_array( $this->statement, $selectStatements, true ) ) { $this->bustCache( $this->getCacheTableName() ); } foreach ( (array) $what as $var ) { switch ( $var ) { case 'group': case 'order': case 'select': case 'set': case 'onDuplicate': case 'where': case 'union': case 'join': $this->$var = []; break; case 'orderDirection': $this->$var = 'ASC'; break; case 'ignore': case 'stripTags': $this->$var = false; break; case 'output': $this->$var = 'OBJECT'; break; default: if ( isset( $this->$var ) ) { $this->$var = null; } break; } } return $this; } /** * Returns the current value of one or more query properties. * * @since 4.0.0 * * @param string|array $what You can pass in an array of options to retrieve. By default it selects all if them. * @return string|array Returns the value of whichever variables are passed in. */ public function getQueryProperty( $what = [ 'table', 'statement', 'limit', 'group', 'order', 'select', 'set', 'onDuplicate', 'where', 'union', 'distinct', 'orderDirection', 'query', 'output', 'result' ] ) { if ( is_array( $what ) ) { $return = []; foreach ( (array) $what as $which ) { $return[ $which ] = $this->$which; } return $return; } return $this->$what; } /** * Get a table name for the cache key. * * @since 4.1.6 * * @param string $cacheTableName The table name to check against. * @return string The cache key table name. */ private function getCacheTableName( $cacheTableName = '' ) { $cacheTableName = empty( $cacheTableName ) ? $this->table : $cacheTableName; foreach ( $this->customTables as $tableName ) { if ( false !== stripos( (string) $cacheTableName, $this->prefix . $tableName ) ) { $cacheTableName = $tableName; break; } } return $cacheTableName; } /** * Busts the cache for the given table name. * * @since 4.1.6 * * @param string $tableName The table name. * @return void */ public function bustCache( $tableName = '' ) { if ( ! $tableName ) { // Bust all the cache. $this->cache = []; return; } unset( $this->cache[ $tableName ] ); } /** * In order to not have a conflict, we need to return a clone. * * @since 4.1.0 * * @return Database The cloned Database instance. */ public function noConflict() { return clone $this; } /** * Checks whether the given index exists on the given table. * * @since 4.4.8 * * @param string $tableName The table name. * @param string $indexName The index name. * @param bool $includesPrefix Whether the table name includes the WordPress prefix or not. * @return bool Whether the index exists or not. */ public function indexExists( $tableName, $indexName, $includesPrefix = false ) { $prefix = $includesPrefix ? '' : $this->prefix; $tableName = strtolower( $prefix . $tableName ); $indexName = strtolower( $indexName ); $indexes = $this->db->get_results( "SHOW INDEX FROM `$tableName`" ); foreach ( $indexes as $index ) { if ( empty( $index->Key_name ) ) { continue; } if ( strtolower( $index->Key_name ) === $indexName ) { return true; } } return false; } }