← All changes
|
includes/abstracts/abstract-class-charitable-db.php
+97
-12
1.7.0.2
→
1.8.13
View file →
| @@ -6,14 +6,16 @@ | ||
| 6 | 6 | * https://github.com/easydigitaldownloads/Easy-Digital-Downloads/blob/master/includes/class-edd-db.php |
| 7 | 7 | * |
| 8 | 8 | * @package Charitable/Classes/Charitable_DB |
| 9 | 9 | * @author David Bisset |
| 10 | - * @copyright Copyright (c) 2022, WP Charitable LLC | |
| 10 | + * @copyright Copyright (c) 2023, WP Charitable LLC | |
| 11 | 11 | * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License |
| 12 | 12 | * @since 1.0.0 |
| 13 | - * @version 1.5.11 | |
| 13 | + * @version 1.8.9.1 | |
| 14 | 14 | */ |
| 15 | 15 | |
| 16 | +// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare | |
| 17 | + | |
| 16 | 18 | // Exit if accessed directly. |
| 17 | 19 | if ( ! defined( 'ABSPATH' ) ) { |
| 18 | 20 | exit; |
| 19 | 21 | } |
| @@ -83,8 +85,9 @@ | ||
| 83 | 85 | /** |
| 84 | 86 | * Return the format for the given column. |
| 85 | 87 | * |
| 86 | 88 | * @since 1.0.0 |
| 89 | + * @version 1.8.9.1 | |
| 87 | 90 | * |
| 88 | 91 | * @param string $column The column to get the format for. |
| 89 | 92 | * @return %s, %d or %f |
| 90 | 93 | */ |
| @@ -91,22 +94,39 @@ | ||
| 91 | 94 | public function get_column_format( $column ) { |
| 92 | 95 | $columns = $this->get_columns(); |
| 93 | 96 | $format = isset( $columns[ $column ] ) ? $columns[ $column ] : false; |
| 94 | 97 | |
| 95 | - // If the column isn't found, throw an exception. | |
| 96 | - if ( false === $format ) { | |
| 97 | - throw new Exception( sprintf( 'Invalid column passed %s', $column ) ); | |
| 98 | - } | |
| 98 | + // If the column isn't found, throw an exception. | |
| 99 | + if ( false === $format ) { | |
| 100 | + throw new Exception( sprintf( 'Invalid column passed %s', esc_html( $column ) ) ); | |
| 101 | + } | |
| 99 | 102 | |
| 100 | - // If the column format isn't valid, throw an exception. | |
| 101 | - if ( ! in_array( $format, array( '%s', '%d', '%f' ) ) ) { | |
| 102 | - throw new Exception( sprintf( 'Invalid column format for column %s. Format returned %s', $column, $format ) ); | |
| 103 | - } | |
| 103 | + // If the column format isn't valid, throw an exception. | |
| 104 | + if ( ! in_array( $format, array( '%s', '%d', '%f' ) ) ) { | |
| 105 | + throw new Exception( sprintf( 'Invalid column format for column %s. Format returned %s', esc_html( $column ), esc_html( $format ) ) ); | |
| 106 | + } | |
| 104 | 107 | |
| 105 | 108 | return $format; |
| 106 | 109 | } |
| 107 | 110 | |
| 108 | 111 | /** |
| 112 | + * Validate that a column name exists in the table schema. | |
| 113 | + * | |
| 114 | + * @since 1.8.8.6 | |
| 115 | + * | |
| 116 | + * @param string $column The column name to validate. | |
| 117 | + * @return string The validated column name. | |
| 118 | + * @throws Exception If column is invalid. | |
| 119 | + */ | |
| 120 | + protected function validate_column_name( $column ) { | |
| 121 | + $columns = $this->get_columns(); | |
| 122 | + if ( ! isset( $columns[ $column ] ) ) { | |
| 123 | + throw new Exception( sprintf( 'Invalid column passed %s', esc_html( $column ) ) ); | |
| 124 | + } | |
| 125 | + return $column; | |
| 126 | + } | |
| 127 | + | |
| 128 | + /** | |
| 109 | 129 | * Retrieve a row by the primary key |
| 110 | 130 | * |
| 111 | 131 | * @since 1.0.0 |
| 112 | 132 | * |
| @@ -114,8 +134,11 @@ | ||
| 114 | 134 | */ |
| 115 | 135 | public function get( $row_id ) { |
| 116 | 136 | global $wpdb; |
| 117 | 137 | |
| 138 | + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 139 | + // Direct database queries are expected in this database abstraction class. | |
| 140 | + // $this->table_name and $this->primary_key are class properties set at initialization and validated. | |
| 118 | 141 | return $wpdb->get_row( |
| 119 | 142 | $wpdb->prepare( |
| 120 | 143 | "SELECT * FROM $this->table_name WHERE $this->primary_key = %d LIMIT 1;", |
| 121 | 144 | $row_id |
| @@ -120,8 +143,9 @@ | ||
| 120 | 143 | "SELECT * FROM $this->table_name WHERE $this->primary_key = %d LIMIT 1;", |
| 121 | 144 | $row_id |
| 122 | 145 | ) |
| 123 | 146 | ); |
| 147 | + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 124 | 148 | } |
| 125 | 149 | |
| 126 | 150 | /** |
| 127 | 151 | * Retrieve a row by a specific column / value |
| @@ -131,8 +155,14 @@ | ||
| 131 | 155 | */ |
| 132 | 156 | public function get_by( $column, $row_id ) { |
| 133 | 157 | global $wpdb; |
| 134 | 158 | |
| 159 | + // Validate column name before use. | |
| 160 | + $column = $this->validate_column_name( $column ); | |
| 161 | + | |
| 162 | + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 163 | + // Direct database queries are expected in this database abstraction class. | |
| 164 | + // $column is validated above via validate_column_name(), $this->table_name is a class property set at initialization. | |
| 135 | 165 | return $wpdb->get_row( |
| 136 | 166 | $wpdb->prepare( |
| 137 | 167 | "SELECT * FROM $this->table_name WHERE $column = {$this->get_column_format($column)} LIMIT 1;", |
| 138 | 168 | $row_id |
| @@ -137,8 +167,9 @@ | ||
| 137 | 167 | "SELECT * FROM $this->table_name WHERE $column = {$this->get_column_format($column)} LIMIT 1;", |
| 138 | 168 | $row_id |
| 139 | 169 | ) |
| 140 | 170 | ); |
| 171 | + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 141 | 172 | } |
| 142 | 173 | |
| 143 | 174 | /** |
| 144 | 175 | * Retrieve a specific column's value by the primary key. |
| @@ -149,8 +180,14 @@ | ||
| 149 | 180 | */ |
| 150 | 181 | public function get_column( $column, $row_id ) { |
| 151 | 182 | global $wpdb; |
| 152 | 183 | |
| 184 | + // Validate column name before use. | |
| 185 | + $column = $this->validate_column_name( $column ); | |
| 186 | + | |
| 187 | + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 188 | + // Direct database queries are expected in this database abstraction class. | |
| 189 | + // $column is validated above via validate_column_name(), $this->table_name and $this->primary_key are class properties set at initialization. | |
| 153 | 190 | return $wpdb->get_var( |
| 154 | 191 | $wpdb->prepare( |
| 155 | 192 | "SELECT $column FROM $this->table_name WHERE $this->primary_key = %d LIMIT 1;", |
| 156 | 193 | $row_id |
| @@ -155,8 +192,9 @@ | ||
| 155 | 192 | "SELECT $column FROM $this->table_name WHERE $this->primary_key = %d LIMIT 1;", |
| 156 | 193 | $row_id |
| 157 | 194 | ) |
| 158 | 195 | ); |
| 196 | + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 159 | 197 | } |
| 160 | 198 | |
| 161 | 199 | /** |
| 162 | 200 | * Retrieve a specific column's value by the the specified column / value |
| @@ -170,8 +208,15 @@ | ||
| 170 | 208 | */ |
| 171 | 209 | public function get_column_by( $column, $column_where, $column_value ) { |
| 172 | 210 | global $wpdb; |
| 173 | 211 | |
| 212 | + // Validate column names before use. | |
| 213 | + $column = $this->validate_column_name( $column ); | |
| 214 | + $column_where = $this->validate_column_name( $column_where ); | |
| 215 | + | |
| 216 | + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 217 | + // Direct database queries are expected in this database abstraction class. | |
| 218 | + // $column and $column_where are validated above via validate_column_name(), $this->table_name is a class property set at initialization. | |
| 174 | 219 | return $wpdb->get_var( |
| 175 | 220 | $wpdb->prepare( |
| 176 | 221 | "SELECT $column FROM $this->table_name WHERE $column_where = {$this->get_column_format($column_where)} LIMIT 1;", |
| 177 | 222 | $column_value |
| @@ -176,8 +221,9 @@ | ||
| 176 | 221 | "SELECT $column FROM $this->table_name WHERE $column_where = {$this->get_column_format($column_where)} LIMIT 1;", |
| 177 | 222 | $column_value |
| 178 | 223 | ) |
| 179 | 224 | ); |
| 225 | + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 180 | 226 | } |
| 181 | 227 | |
| 182 | 228 | /** |
| 183 | 229 | * Retrieve all values in a specific column by the specified column / value. |
| @@ -191,8 +237,15 @@ | ||
| 191 | 237 | */ |
| 192 | 238 | public function get_column_all_by( $column, $column_where, $column_value ) { |
| 193 | 239 | global $wpdb; |
| 194 | 240 | |
| 241 | + // Validate column names before use. | |
| 242 | + $column = $this->validate_column_name( $column ); | |
| 243 | + $column_where = $this->validate_column_name( $column_where ); | |
| 244 | + | |
| 245 | + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 246 | + // Direct database queries are expected in this database abstraction class. | |
| 247 | + // $column and $column_where are validated above via validate_column_name(), $this->table_name is a class property set at initialization. | |
| 195 | 248 | return $wpdb->get_col( |
| 196 | 249 | $wpdb->prepare( |
| 197 | 250 | "SELECT $column FROM $this->table_name WHERE $column_where = {$this->get_column_format($column_where)};", |
| 198 | 251 | $column_value |
| @@ -197,8 +250,9 @@ | ||
| 197 | 250 | "SELECT $column FROM $this->table_name WHERE $column_where = {$this->get_column_format($column_where)};", |
| 198 | 251 | $column_value |
| 199 | 252 | ) |
| 200 | 253 | ); |
| 254 | + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 201 | 255 | } |
| 202 | 256 | |
| 203 | 257 | /** |
| 204 | 258 | * Count all rows. |
| @@ -209,11 +263,15 @@ | ||
| 209 | 263 | */ |
| 210 | 264 | public function count_all() { |
| 211 | 265 | global $wpdb; |
| 212 | 266 | |
| 267 | + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 268 | + // Direct database queries are expected in this database abstraction class. | |
| 269 | + // $this->table_name is a class property set at initialization. | |
| 213 | 270 | return $wpdb->get_var( |
| 214 | 271 | "SELECT COUNT( * ) FROM $this->table_name;" |
| 215 | 272 | ); |
| 273 | + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 216 | 274 | } |
| 217 | 275 | |
| 218 | 276 | /** |
| 219 | 277 | * Count all rows that certain criteria. |
| @@ -224,8 +282,14 @@ | ||
| 224 | 282 | */ |
| 225 | 283 | public function count_by( $column, $column_value ) { |
| 226 | 284 | global $wpdb; |
| 227 | 285 | |
| 286 | + // Validate column name before use. | |
| 287 | + $column = $this->validate_column_name( $column ); | |
| 288 | + | |
| 289 | + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 290 | + // Direct database queries are expected in this database abstraction class. | |
| 291 | + // $column is validated above via validate_column_name(), $this->table_name is a class property set at initialization. | |
| 228 | 292 | return $wpdb->get_var( |
| 229 | 293 | $wpdb->prepare( |
| 230 | 294 | "SELECT COUNT( * ) FROM $this->table_name WHERE $column = {$this->get_column_format($column)};", |
| 231 | 295 | $column_value |
| @@ -230,14 +294,16 @@ | ||
| 230 | 294 | "SELECT COUNT( * ) FROM $this->table_name WHERE $column = {$this->get_column_format($column)};", |
| 231 | 295 | $column_value |
| 232 | 296 | ) |
| 233 | 297 | ); |
| 298 | + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 234 | 299 | } |
| 235 | 300 | |
| 236 | 301 | /** |
| 237 | 302 | * Insert a new row |
| 238 | 303 | * |
| 239 | - * @since 1.0.0 | |
| 304 | + * @since 1.0.0 | |
| 305 | + * @version 1.8.8.6 | |
| 240 | 306 | * |
| 241 | 307 | * @return int |
| 242 | 308 | */ |
| 243 | 309 | public function insert( $data, $type = '' ) { |
| @@ -260,9 +326,13 @@ | ||
| 260 | 326 | /* Reorder $column_formats to match the order of columns given in $data */ |
| 261 | 327 | $data_keys = array_keys( $data ); |
| 262 | 328 | $column_formats = array_merge( array_flip( $data_keys ), $column_formats ); |
| 263 | 329 | |
| 330 | + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 331 | + // Direct database queries are expected in this database abstraction class. | |
| 332 | + // $this->table_name is a class property set at initialization, and $data/$column_formats are sanitized above. | |
| 264 | 333 | $inserted = $wpdb->insert( $this->table_name, $data, $column_formats ); |
| 334 | + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 265 | 335 | |
| 266 | 336 | /* If the insert failed, return 0 */ |
| 267 | 337 | if ( false === $inserted ) { |
| 268 | 338 | return 0; |
| @@ -276,8 +346,9 @@ | ||
| 276 | 346 | /** |
| 277 | 347 | * Update a row. |
| 278 | 348 | * |
| 279 | 349 | * @since 1.0.0 |
| 350 | + * @version 1.8.8.6 | |
| 280 | 351 | * |
| 281 | 352 | * @global WPDB $wpdb |
| 282 | 353 | * @param int $row_id The record to update. |
| 283 | 354 | * @param array $data Data to update. |
| @@ -310,9 +381,16 @@ | ||
| 310 | 381 | /* Reorder $column_formats to match the order of columns given in $data. */ |
| 311 | 382 | $data_keys = array_keys( $data ); |
| 312 | 383 | $column_formats = array_merge( array_flip( $data_keys ), $column_formats ); |
| 313 | 384 | |
| 314 | - if ( false === $wpdb->update( $this->table_name, $data, array( $where => $row_id ), $column_formats ) ) { | |
| 385 | + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 386 | + // Direct database queries are expected in this database abstraction class. | |
| 387 | + // $this->table_name is a class property set at initialization, $where is validated (defaults to primary_key or validated column), | |
| 388 | + // $row_id is sanitized via absint(), and $data/$column_formats are sanitized above. | |
| 389 | + $updated = $wpdb->update( $this->table_name, $data, array( $where => $row_id ), $column_formats ); | |
| 390 | + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 391 | + | |
| 392 | + if ( false === $updated ) { | |
| 315 | 393 | return false; |
| 316 | 394 | } |
| 317 | 395 | |
| 318 | 396 | return true; |
| @@ -349,9 +427,16 @@ | ||
| 349 | 427 | */ |
| 350 | 428 | public function delete_by( $column, $row_id = 0 ) { |
| 351 | 429 | global $wpdb; |
| 352 | 430 | |
| 431 | + // Validate column name before use. | |
| 432 | + $column = $this->validate_column_name( $column ); | |
| 433 | + | |
| 434 | + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 435 | + // Direct database queries are expected in this database abstraction class. | |
| 436 | + // $column is validated above via validate_column_name(), $this->table_name is a class property set at initialization. | |
| 353 | 437 | $result = $wpdb->query( $wpdb->prepare( "DELETE FROM $this->table_name WHERE $column = {$this->get_column_format($column)}", $row_id ) ); |
| 438 | + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 354 | 439 | |
| 355 | 440 | return false !== $result; |
| 356 | 441 | } |
| 357 | 442 | |