695 lines
23 KiB
PHP
695 lines
23 KiB
PHP
<?php
|
|
/**
|
|
* This file is part of Avatar Privacy.
|
|
*
|
|
* Copyright 2018-2023 Peter Putzer.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* ***
|
|
*
|
|
* @package mundschenk-at/avatar-privacy
|
|
* @license http://www.gnu.org/licenses/gpl-2.0.html
|
|
*/
|
|
|
|
namespace Avatar_Privacy\Data_Storage\Database;
|
|
|
|
use Avatar_Privacy\Exceptions\Database_Exception;
|
|
|
|
/**
|
|
* A plugin-specific database handler.
|
|
*
|
|
* @since 2.1.0
|
|
* @since 2.4.0 Renamed to Avatar_Privacy\Data_Storage\Database\Table and
|
|
* refactored as abstract base class.
|
|
*
|
|
* @author Peter Putzer <github@mundschenk.at>
|
|
*
|
|
* @phpstan-type SQLValue int|string|null
|
|
* @phpstan-type ColumnValueTuples array<string,SQLValue>
|
|
* @phpstan-type ColumnFormats array<string,string>
|
|
*/
|
|
abstract class Table {
|
|
|
|
/**
|
|
* The basename (without site prefix) of the table.
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @var string
|
|
*/
|
|
private string $table_basename;
|
|
|
|
/**
|
|
* The minimum version number for which the table does not need to be updated.
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @var string
|
|
*/
|
|
private string $update_threshold;
|
|
|
|
/**
|
|
* A column/field to placeholder mapping.
|
|
*
|
|
* @since 2.3.0
|
|
*
|
|
* @var string[]
|
|
*
|
|
* @phpstan-var ColumnFormats
|
|
*/
|
|
private array $column_formats;
|
|
|
|
/**
|
|
* A list auto-update columns (e.g. date-/timestamps), stored in reverse format
|
|
* for fast read access (i.e as array<column,int>).
|
|
*
|
|
* @since 2.6.0
|
|
*
|
|
* @var array<string,int>
|
|
*/
|
|
private array $auto_update_cols;
|
|
|
|
/**
|
|
* Creates a new instance.
|
|
*
|
|
* @since 2.3.0 Parameter $core added.
|
|
* @since 2.4.0 Parameters replaced with $table_basename, $update_threshold,
|
|
* and $column_formats.
|
|
* @since 2.6.0 Parameter $auto_update_cols added.
|
|
*
|
|
* @param string $table_basename The basename (without site prefix) of the table.
|
|
* @param string $update_threshold The minimum version number for which the table does not need to be updated.
|
|
* @param array $column_formats A mapping from column to placeholder characters.
|
|
* @param string[] $auto_update_cols A list of auto-update columns.
|
|
*
|
|
* @phpstan-param ColumnFormats $column_formats
|
|
*/
|
|
public function __construct( $table_basename, $update_threshold, array $column_formats, array $auto_update_cols ) {
|
|
$this->table_basename = $table_basename;
|
|
$this->update_threshold = $update_threshold;
|
|
$this->column_formats = $column_formats;
|
|
$this->auto_update_cols = \array_flip( $auto_update_cols );
|
|
}
|
|
|
|
/**
|
|
* Sets up the table, including necessary data upgrades. The method is called
|
|
* on every page load.
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @param string $previous_version The previously installed plugin version.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setup( $previous_version ) {
|
|
if ( $this->maybe_create_table( $previous_version ) ) {
|
|
// We may need to fix the schema manually.
|
|
$this->maybe_upgrade_schema( $previous_version );
|
|
|
|
// We may need to update the contents as well.
|
|
$this->maybe_upgrade_data( $previous_version );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the table prefix to use (for a given site or the current site).
|
|
*
|
|
* @global \wpdb $wpdb The WordPress Database Access Abstraction.
|
|
*
|
|
* @param int|null $site_id Optional. The site ID. Null means the current $blog_id. Default null.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function get_table_prefix( $site_id = null ) {
|
|
global $wpdb;
|
|
|
|
if ( ! $this->use_global_table() ) {
|
|
return $wpdb->get_blog_prefix( $site_id );
|
|
} else {
|
|
return $wpdb->base_prefix;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the table name to use (for a given site or the current site).
|
|
*
|
|
* @since 2.3.0 Visibility changed to public.
|
|
*
|
|
* @param int|null $site_id Optional. The site ID. Null means the current $blog_id. Default null.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_table_name( $site_id = null ) {
|
|
return $this->get_table_prefix( $site_id ) . $this->table_basename;
|
|
}
|
|
|
|
/**
|
|
* Checks if the given table exists.
|
|
*
|
|
* @since 2.3.0 Visibility changed to public.
|
|
*
|
|
* @global \wpdb $wpdb The WordPress Database Access Abstraction.
|
|
*
|
|
* @param string $table_name A table name.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function table_exists( $table_name ) {
|
|
global $wpdb;
|
|
|
|
return $table_name === $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
|
|
}
|
|
|
|
/**
|
|
* Determines whether this (multisite) installation uses the global table.
|
|
* Result is ignored for single-site installations.
|
|
*
|
|
* @since 2.3.0 Visibility changed to public.
|
|
* @since 2.4.0 Made abstract.
|
|
*
|
|
* @return bool
|
|
*/
|
|
abstract public function use_global_table();
|
|
|
|
/**
|
|
* Creates the plugin's database table if it doesn't already exist. The
|
|
* table may be created as a global table for legacy multisite installations.
|
|
* Makes the name of the table available through $wpdb->avatar_privacy.
|
|
*
|
|
* @global \wpdb $wpdb The WordPress Database Access Abstraction.
|
|
*
|
|
* @param string $previous_version The previously installed plugin version.
|
|
*
|
|
* @return bool Returns true if the table was created/updated.
|
|
*/
|
|
public function maybe_create_table( $previous_version ) {
|
|
global $wpdb;
|
|
|
|
// Force DB update?
|
|
$db_needs_update = \version_compare( $previous_version, $this->update_threshold, '<' );
|
|
|
|
// Check if the table has already been registered.
|
|
if ( ! $db_needs_update && \property_exists( $wpdb, $this->table_basename ) ) {
|
|
return false;
|
|
}
|
|
|
|
// Set up table name and result status.
|
|
$table_name = $this->get_table_name();
|
|
$updated = false;
|
|
|
|
// Just fix $wpdb object if table already exists, unless we need an update.
|
|
if ( ! $db_needs_update && $this->table_exists( $table_name ) ) {
|
|
$this->register_table( $wpdb, $table_name );
|
|
} else {
|
|
// Create/update the table.
|
|
$this->db_delta( $this->get_table_definition( $table_name ) . " {$wpdb->get_charset_collate()};" );
|
|
|
|
if ( ! $this->table_exists( $table_name ) ) {
|
|
// There was an error creating the table.
|
|
// TODO: Signal catastrophic error to the adminstrator.
|
|
return false;
|
|
}
|
|
|
|
$this->register_table( $wpdb, $table_name );
|
|
$updated = true;
|
|
}
|
|
|
|
// We may need to update the charset/collation.
|
|
return $this->maybe_upgrade_charset_and_collation( $table_name ) || $updated;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the CREATE TABLE definition formatted for use by \db_delta(),
|
|
* without the charset collate clause.
|
|
*
|
|
* Example:
|
|
* `CREATE TABLE some_table (
|
|
* id mediumint(9) NOT NULL AUTO_INCREMENT,
|
|
* some_column varchar(100) NOT NULL,
|
|
* PRIMARY KEY (id)
|
|
* )`
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @param string $table_name The table name including any prefixes.
|
|
*
|
|
* @return string
|
|
*/
|
|
abstract protected function get_table_definition( $table_name );
|
|
|
|
/**
|
|
* Fixes the table schema when dbDelta cannot cope with the changes.
|
|
*
|
|
* The table itself is already guaranteed to exist.
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @param string $previous_version The previously installed plugin version.
|
|
*
|
|
* @return bool True if the schema was modified, false otherwise.
|
|
*/
|
|
abstract public function maybe_upgrade_schema( $previous_version );
|
|
|
|
/**
|
|
* Sometimes, the table data needs to updated when upgrading.
|
|
*
|
|
* The table itself is already guaranteed to exist and have the correct schema.
|
|
*
|
|
* @param string $previous_version The previously installed plugin version.
|
|
*
|
|
* @return int The number of upgraded rows.
|
|
*/
|
|
abstract public function maybe_upgrade_data( $previous_version );
|
|
|
|
/**
|
|
* Registers the table with the given \wpdb instance.
|
|
*
|
|
* @param \wpdb $db The database instance.
|
|
* @param string $table_name The table name (with prefix).
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function register_table( \wpdb $db, $table_name ) {
|
|
$basename = $this->table_basename;
|
|
|
|
// Make sure that $wpdb knows about our table.
|
|
if ( \is_multisite() && $this->use_global_table() ) {
|
|
$db->ms_global_tables[] = $basename;
|
|
} else {
|
|
$db->tables[] = $basename;
|
|
}
|
|
|
|
// Also register the "shortcut" property.
|
|
$db->$basename = $table_name;
|
|
}
|
|
|
|
/**
|
|
* Fixes the table's charset and/or collation if the WordPress default has
|
|
* changed since the table was created (to prevent "Illegal mix of collations"
|
|
* errors in joins). Unfortunately, `dbDelta()` does not do that for us (viz.
|
|
* [Trac ticket #45697](https://core.trac.wordpress.org/ticket/45697)).
|
|
*
|
|
* The table itself is already guaranteed to exist.
|
|
*
|
|
* @since 2.4.4
|
|
*
|
|
* @global \wpdb $wpdb The WordPress Database Access Abstraction.
|
|
*
|
|
* @param string $table_name The table name (with prefix).
|
|
*
|
|
* @return bool True if the collation was modified, false otherwise.
|
|
*/
|
|
protected function maybe_upgrade_charset_and_collation( $table_name ) {
|
|
global $wpdb;
|
|
|
|
// Check if the charset and collation set for the table are the same as
|
|
// WordPress' default.
|
|
$collation = $wpdb->get_var( $wpdb->prepare( 'SELECT table_collation FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE() AND table_name = %s', $table_name ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
|
if ( $wpdb->collate === $collation ) {
|
|
return false;
|
|
}
|
|
|
|
$columns_clause = '';
|
|
$query_args = [ $table_name ];
|
|
|
|
// Update existing columns first.
|
|
$columns = $wpdb->get_results( $wpdb->prepare( "SELECT column_name AS 'name', character_set_name AS 'charset', collation_name AS 'collate', column_type AS 'type', is_nullable AS 'nullable', column_default AS 'default' FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() AND table_name = %s AND collation_name = %s", $table_name, $collation ), \ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQLPlaceholders
|
|
if ( ! empty( $columns ) ) {
|
|
$alter_table = [];
|
|
foreach ( $columns as $c ) {
|
|
$default = 'YES' !== $c['nullable'] ? 'NOT NULL ' : '';
|
|
if ( isset( $c['default'] ) ) {
|
|
$default .= 'DEFAULT %s';
|
|
$query_args[] = $c['default'];
|
|
}
|
|
$alter_table[] = "MODIFY `{$c['name']}` {$c['type']} CHARACTER SET {$wpdb->charset} COLLATE {$wpdb->collate} {$default}";
|
|
}
|
|
|
|
$columns_clause = ', ' . \join( ', ', $alter_table );
|
|
}
|
|
|
|
// Then set the default charset and collation for the table.
|
|
return (bool) $wpdb->query( $wpdb->prepare( "ALTER TABLE `%1s` CHARSET {$wpdb->charset} COLLATE {$wpdb->collate}" . $columns_clause, $query_args ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders
|
|
}
|
|
|
|
/**
|
|
* Applies the `dbDelta` function to the given queries.
|
|
*
|
|
* @param string|string[] $queries The query to run. Can be multiple queries in an array, or a string of queries separated by semicolons.
|
|
* @param bool $execute Optional. Whether or not to execute the query right away. Default `true`.
|
|
*
|
|
* @return string[] Strings containing the results of the various update queries.
|
|
*/
|
|
protected function db_delta( $queries, $execute = true ) {
|
|
if ( ! function_exists( 'dbDelta' ) ) {
|
|
// Load upgrade.php for the dbDelta function.
|
|
require_once \ABSPATH . 'wp-admin/includes/upgrade.php';
|
|
}
|
|
|
|
return \dbDelta( $queries, $execute );
|
|
}
|
|
|
|
/**
|
|
* Drops the table for the given site.
|
|
*
|
|
* @global \wpdb $wpdb The WordPress Database Access Abstraction.
|
|
*
|
|
* @param int|null $site_id Optional. The site ID. Null means the current $blog_id. Ddefault null.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function drop_table( $site_id = null ) {
|
|
global $wpdb;
|
|
|
|
$table_name = $this->get_table_name( $site_id );
|
|
$wpdb->query( "DROP TABLE IF EXISTS {$table_name};" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
|
}
|
|
|
|
/**
|
|
* Retrieves the correct format strings for the given columns.
|
|
*
|
|
* @since 2.4.0 Moved from Avatar_Privacy\Core\Comment_Author_Fields and renamed to get_format.
|
|
*
|
|
* @param array $columns An array of values index by column name.
|
|
*
|
|
* @return string[]
|
|
*
|
|
* @throws Database_Exception An exception is raised when invalid column names are used.
|
|
*
|
|
* @phpstan-param ColumnValueTuples $columns
|
|
*/
|
|
protected function get_format( array $columns ) {
|
|
$format_strings = [];
|
|
|
|
foreach ( $columns as $key => $value ) {
|
|
if ( ! empty( $this->column_formats[ $key ] ) ) {
|
|
$format_strings[] = null === $value ? 'NULL' : $this->column_formats[ $key ];
|
|
} else {
|
|
throw new Database_Exception( "Invalid column name '{$key}'." );
|
|
}
|
|
}
|
|
|
|
return $format_strings;
|
|
}
|
|
|
|
/**
|
|
* Inserts a row into the table.
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @global \wpdb $wpdb The WordPress Database Access Abstraction.
|
|
*
|
|
* @param array $data The data to insert (in column => value pairs).
|
|
* Both $data columns and $data values should be
|
|
* "raw" (neither should be SQL escaped). Sending
|
|
* a null value will cause the column to be set to
|
|
* NULL - the corresponding format is ignored in
|
|
* this case.
|
|
* @param int|null $site_id Optional. The site ID. Null means the current
|
|
* $blog_id. Default null.
|
|
*
|
|
* @return int|false The number of rows inserted, or false on error.
|
|
*
|
|
* @phpstan-param ColumnValueTuples $data
|
|
*/
|
|
public function insert( array $data, $site_id = null ) {
|
|
try {
|
|
global $wpdb;
|
|
|
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
|
|
return $wpdb->insert( $this->get_table_name( $site_id ), $data, $this->get_format( $data ) );
|
|
} catch ( \RuntimeException $e ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replaces a row into the table (i.e. it inserts the row if it does not exist
|
|
* or deletes and existing row and then inserts the new data).
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @global \wpdb $wpdb The WordPress Database Access Abstraction.
|
|
*
|
|
* @param array $data The data to insert (in column => value pairs).
|
|
* Both $data columns and $data values should be
|
|
* "raw" (neither should be SQL escaped). Sending
|
|
* a null value will cause the column to be set to
|
|
* NULL - the corresponding format is ignored in
|
|
* this case.
|
|
* @param int|null $site_id Optional. The site ID. Null means the current
|
|
* $blog_id. Default null.
|
|
*
|
|
* @return int|false The number of rows updated, or false on error.
|
|
*
|
|
* @phpstan-param ColumnValueTuples $data
|
|
*/
|
|
public function replace( array $data, $site_id = null ) {
|
|
try {
|
|
global $wpdb;
|
|
|
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
|
return $wpdb->replace( $this->get_table_name( $site_id ), $data, $this->get_format( $data ) );
|
|
} catch ( \RuntimeException $e ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates a row in the table.
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @global \wpdb $wpdb The WordPress Database Access Abstraction.
|
|
*
|
|
* @param array $data The data to insert (in column => value pairs).
|
|
* Both $data columns and $data values should be
|
|
* "raw" (neither should be SQL escaped). Sending
|
|
* a null value will cause the column to be set to
|
|
* NULL - the corresponding format is ignored in
|
|
* this case.
|
|
* @param array $where A named array of WHERE clauses (in column => value
|
|
* pairs). Multiple clauses will be joined with ANDs.
|
|
* Both $where columns and $where values should be
|
|
* "raw". Sending a null value will create an IS NULL
|
|
* comparison - the corresponding format will be
|
|
* ignored in this case.
|
|
* @param int|null $site_id Optional. The site ID. Null means the current
|
|
* $blog_id. Default null.
|
|
*
|
|
* @return int|false The number of rows updated, or false on error.
|
|
*
|
|
* @phpstan-param ColumnValueTuples $data
|
|
* @phpstan-param ColumnValueTuples $where
|
|
*/
|
|
public function update( array $data, array $where, $site_id = null ) {
|
|
try {
|
|
global $wpdb;
|
|
|
|
return $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
|
$this->get_table_name( $site_id ),
|
|
$data,
|
|
$where,
|
|
$this->get_format( $data ),
|
|
$this->get_format( $where )
|
|
);
|
|
} catch ( \RuntimeException $e ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes a row from the table.
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @global \wpdb $wpdb The WordPress Database Access Abstraction.
|
|
*
|
|
* @param array $where A named array of WHERE clauses (in column => value
|
|
* pairs). Multiple clauses will be joined with ANDs.
|
|
* Both $where columns and $where values should be
|
|
* "raw". Sending a null value will create an IS NULL
|
|
* comparison - the corresponding format will be
|
|
* ignored in this case.
|
|
* @param int|null $site_id Optional. The site ID. Null means the current
|
|
* $blog_id. Default null.
|
|
*
|
|
* @return int|false The number of rows deleted, or false on error.
|
|
*
|
|
* @phpstan-param ColumnValueTuples $where
|
|
*/
|
|
public function delete( array $where, $site_id = null ) {
|
|
try {
|
|
global $wpdb;
|
|
|
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
|
return $wpdb->delete( $this->get_table_name( $site_id ), $where, $this->get_format( $where ) );
|
|
} catch ( \RuntimeException $e ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Inserts or updates multiple rows, as required.
|
|
*
|
|
* @param string[] $fields An array of database columns.
|
|
* @param array $rows An array of row objects or arrays (containing
|
|
* field => value tuples).
|
|
* @param int|null $site_id Optional. The site ID. Null means the current
|
|
* $blog_id. Default null.
|
|
*
|
|
* @return int|false The number of rows inserted or updated, or false
|
|
* on error.
|
|
*
|
|
* @phpstan-param \stdClass[]|ColumnValueTuples[] $rows
|
|
*/
|
|
public function insert_or_update( array $fields, array $rows, $site_id = null ) {
|
|
try {
|
|
global $wpdb;
|
|
|
|
// Allow only valid fields.
|
|
$fields = \array_intersect( $fields, \array_keys( $this->column_formats ) );
|
|
|
|
if ( empty( $rows ) || empty( $fields ) ) {
|
|
return false;
|
|
}
|
|
|
|
$rows = $this->prepare_rows( $rows, $fields );
|
|
$columns = \join( ',', $fields );
|
|
$values_clause = \join( ',', \array_map( function( $data ) {
|
|
return '(' . \join( ',', $this->get_format( $data ) ) . ')';
|
|
}, $rows ) );
|
|
|
|
return $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
|
$wpdb->prepare( // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
|
|
"INSERT INTO `{$this->get_table_name( $site_id )}` ( {$columns} )
|
|
VALUES {$values_clause}
|
|
ON DUPLICATE KEY UPDATE {$this->get_update_clause( $fields )}",
|
|
$this->prepare_values( $rows )
|
|
)
|
|
); // phpcs:enable WordPress.DB
|
|
} catch ( \RuntimeException $e ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Inserts or updates a single row, as required.
|
|
*
|
|
* @since 2.6.0
|
|
*
|
|
* @param array $data The data to insert (in column => value pairs).
|
|
* Both $data columns and $data values should be
|
|
* "raw" (neither should be SQL escaped). Sending
|
|
* a null value will cause the column to be set to
|
|
* NULL - the corresponding format is ignored in
|
|
* this case.
|
|
* @param int|null $site_id Optional. The site ID. Null means the current
|
|
* $blog_id. Default null.
|
|
*
|
|
* @return int|false The number of rows inserted or updated, or false
|
|
* on error.
|
|
*
|
|
* @phpstan-param ColumnValueTuples $data
|
|
*/
|
|
public function insert_or_update_row( array $data, $site_id = null ) {
|
|
return $this->insert_or_update( \array_keys( $data ), [ $data ], $site_id );
|
|
}
|
|
|
|
/**
|
|
* Retrieves the update clause based on the updated fields.
|
|
*
|
|
* @since 2.3.0
|
|
*
|
|
* @param string[] $fields An array of database columns.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function get_update_clause( array $fields ) {
|
|
|
|
$updated_fields = \array_flip( $fields );
|
|
$update_clause_parts = [];
|
|
|
|
foreach ( \array_keys( $this->column_formats ) as $field ) {
|
|
if ( isset( $updated_fields[ $field ] ) ) {
|
|
$update_clause_parts[] = "{$field} = VALUES({$field})";
|
|
} elseif ( ! isset( $this->auto_update_cols[ $field ] ) ) {
|
|
$update_clause_parts[] = "{$field} = {$field}";
|
|
}
|
|
}
|
|
|
|
return \join( ",\n", $update_clause_parts );
|
|
}
|
|
|
|
/**
|
|
* Prepares an array of rows for use in queries (i.e. add missing values and
|
|
* correctly sort the columns).
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @param array $rows An array of row objects or arrays (containing
|
|
* field => value tuples).
|
|
* @param string[] $fields An array of database columns.
|
|
*
|
|
* @return array
|
|
*
|
|
* @phpstan-param \stdClass[]|ColumnValueTuples[] $rows
|
|
* @phpstan-return ColumnValueTuples[]
|
|
*/
|
|
protected function prepare_rows( array $rows, array $fields ) {
|
|
$result = [];
|
|
|
|
foreach ( $rows as $data ) {
|
|
// Force array syntax (in case we were given an array of row objects).
|
|
$data = (array) $data;
|
|
$row = [];
|
|
foreach ( $fields as $column ) {
|
|
$row[ $column ] = isset( $data[ $column ] ) ? $data[ $column ] : null;
|
|
}
|
|
|
|
$result[] = $row;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Filters non-null values from a prepared database rows array.
|
|
*
|
|
* @since 2.4.0
|
|
*
|
|
* @param array $prepared_rows An array of arrays containing $field => $value tuples.
|
|
*
|
|
* @return array A flat array containing all non-null values.
|
|
*
|
|
* @phpstan-param ColumnValueTuples[] $prepared_rows
|
|
* @phpstan-return array<int|string>
|
|
*/
|
|
protected function prepare_values( array $prepared_rows ) {
|
|
$values = [];
|
|
|
|
foreach ( $prepared_rows as $row ) {
|
|
foreach ( $row as $value ) {
|
|
if ( null !== $value ) {
|
|
$values[] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
}
|