PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.82
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.82
5.5.84 5.5.83 5.5.82 5.5.81 5.5.80 5.5.79 5.5.77 5.5.76 5.5.75 5.5.73 5.5.72 5.5.22 5.5.23 5.5.29 5.5.3 5.5.31 5.5.32 5.5.34 5.5.35 5.5.36 5.5.37 5.5.4 5.5.40 5.5.41 5.5.42 All 160 releases
wp-data-access / WPDataAccess / Utilities / WPDA_Export_Sql.php

WPDA_Export_Sql.php in WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards 5.5.82, at WPDataAccess/Utilities/WPDA_Export_Sql.php

895 lines 28.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Suppress "error - 0 - No summary was found for this file" on phpdoc generation
5 *
6 * @package WPDataAccess\Utilities
7 */
8
9 namespace WPDataAccess\Utilities {
10
11 use http\Encoding\Stream;
12 use WPDataAccess\Connection\WPDADB;
13 use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Exist;
14 use WPDataAccess\Data_Dictionary\WPDA_List_Columns_Cache;
15 use WPDataAccess\Plugin_Table_Models\WPDA_Publisher_Model;
16 use WPDataAccess\Plugin_Table_Models\WPDA_Table_Settings_Model;
17 use WPDataAccess\WPDA;
18
19 /**
20 * Class WPDA_Export_Sql
21 *
22 * Exports tables or rows (depending on arguments)
23 *
24 * + Table export
25 * + One or more tables (batch)
26 * + Contains a create table statements for every tables exported
27 * + All records of a tables are exported as one insert statement
28 * + Contains comments (no reimport without editing possible)
29 * + Row export
30 * + All records are exported as one insert statement
31 * + Contains no comments (can be reimported without editing)
32 *
33 * @author Peter Schulz
34 * @since 1.0.0
35 */
36 class WPDA_Export_Sql {
37
38 /**
39 * Database schema name
40 *
41 * @var string
42 */
43 protected $schema_name = '';
44
45 /**
46 * Database schema name prefix used in strings
47 *
48 * @var string
49 */
50 protected $schema_name_prefix = '';
51
52 /**
53 * Table name(s) of table(s) to be exported
54 *
55 * @var string|array
56 */
57 protected $table_list = array();
58
59 /**
60 * Indicates whether to set MySQL environment
61 *
62 * @var string 'on' or 'off'
63 */
64 protected $mysql_set;
65
66 /**
67 * Indicates whether comments should be added
68 *
69 * @var string 'on' or 'off'
70 */
71 protected $show_comments;
72
73 /**
74 * Indicates whether to add a create table
75 *
76 * @var string 'on' or 'off'
77 */
78 protected $show_create;
79
80 /**
81 * Indicates whether table settings should be exported
82 *
83 * @var string 'on' or 'off'
84 */
85 protected $include_table_settings;
86
87 /**
88 * Use variable to write output
89 *
90 * @var string
91 */
92 protected $output_string;
93
94 /**
95 * Write output to stream if available
96 *
97 * @var null
98 */
99 protected $output_stream = null;
100
101 protected $export_with_prefix = false;
102
103 /**
104 * WPDA_Export constructor.
105 *
106 * Make sure the export procedure has sufficient memory.
107 *
108 * @since 2.0.11
109 */
110 public function __construct() {
111 if ( defined( 'WP_MAX_MEMORY_LIMIT' ) ) {
112 $wp_memory_limit = WP_MAX_MEMORY_LIMIT;
113 $current_memory_limit = @ini_get( 'memory_limit' );
114 if ( false === $current_memory_limit ||
115 WPDA::convert_memory_to_decimal( $current_memory_limit ) < WPDA::convert_memory_to_decimal( $wp_memory_limit )
116 ) {
117 @ini_set( 'memory_limit', $wp_memory_limit ); // phpcs:ignore
118 }
119 }
120
121 $this->export_with_prefix = 'on' === WPDA::get_option( WPDA::OPTION_BE_EXPORT_VARIABLE_PREFIX );
122 }
123
124 /**
125 * Use this method to startan export using method arguments
126 *
127 * @param string $mysql_set on|off
128 * @param string $show_comments on|off
129 * @param string $show_create on|off
130 * @param string $schema_name Database schema name
131 * @param string|array $table_names Single table name (string) | Table name list (array)
132 * @param string $export_type table|row
133 * @param string $include_table_settings on|off
134 *
135 * @since 2.0.7
136 */
137 public function export_with_arguments(
138 $mysql_set,
139 $show_comments,
140 $show_create,
141 $schema_name,
142 $table_names,
143 $export_type,
144 $include_table_settings = 'on'
145 ) {
146 $this->mysql_set = 'on' === $mysql_set ? 'on' : 'off';
147 $this->show_comments = 'on' === $show_comments ? 'on' : 'off';
148 $this->show_create = 'on' === $show_create ? 'on' : 'off';
149 $this->include_table_settings = 'on' === $include_table_settings ? 'on' : 'off';
150
151 if ( '' !== $schema_name ) {
152 $wpdadb = WPDADB::get_db_connection( $schema_name );
153 if ( null === $wpdadb ) {
154 /* translators: %s = database name */
155 die( sprintf( esc_attr__( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) );
156 }
157
158 $this->schema_name = $schema_name;
159 $this->schema_name_prefix = "`{$wpdadb->dbname}`.";
160 }
161
162 $this->table_list = $table_names;
163
164 if ( 'table' === $export_type ) {
165 // Table export.
166 $this->export_tables();
167 } else {
168 // Row export.
169 if ( is_array( $this->table_list ) ) {
170 // For row exports a single table name must be supplied as a string.
171 $this->wrong_arguments();
172 } else {
173 $this->export_rows();
174 }
175 }
176 }
177
178 /**
179 * Main method to start export
180 *
181 * This method checks arguments and starts the export according to the arguments provided.
182 *
183 * @since 1.0.0
184 */
185 public function export() {
186 // Check if export is allowed.
187 $table_names = isset( $_REQUEST['table_names'] ) ?
188 json_encode( WPDA::sanitize_text_field_array( $_REQUEST['table_names'] ) ) : ''; // phpcs:ignore
189 $wp_nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '?'; // input var okay.
190 if (
191 ! wp_verify_nonce( $wp_nonce, "wpda-export-{$table_names}" ) &&
192 ! (
193 WPDA::current_user_is_admin() && // Admins are allowed to export all tables
194 wp_verify_nonce( $wp_nonce, 'wpda-export-' . WPDA::get_current_user_login() )
195 )
196 ) {
197 wp_die();
198 }
199
200 // Get arguments.
201 $this->mysql_set = isset( $_REQUEST['mysql_set'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['mysql_set'] ) ) : 'on'; // input var okay.
202 $this->show_comments = isset( $_REQUEST['show_comments'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['show_comments'] ) ) : 'on'; // input var okay.
203 $this->show_create = isset( $_REQUEST['show_create'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['show_create'] ) ) : 'on'; // input var okay.
204 $this->include_table_settings = isset( $_REQUEST['include_table_settings'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['include_table_settings'] ) ) : 'off'; // input var okay.
205 $pub_id = isset( $_REQUEST['pub_id'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['pub_id'] ) ) : ''; // input var okay.
206
207 if ( isset( $_REQUEST['wpdaschema_name'] ) ) {
208 $this->schema_name = sanitize_text_field( wp_unslash( $_REQUEST['wpdaschema_name'] ) ); // input var okay.
209 } elseif ( '' !== $pub_id ) {
210 // Get schema name from publication
211 $publication = WPDA_Publisher_Model::get_publication( $pub_id );
212 if ( false === $publication ) {
213 wp_die();
214 }
215 $this->schema_name = $publication[0]['pub_schema_name'];
216 }
217
218 if ( '' !== $this->schema_name ) {
219 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
220 if ( null === $wpdadb ) {
221 wp_die();
222 }
223 $this->schema_name_prefix = "`{$wpdadb->dbname}`.";
224 }
225
226 if ( isset( $_REQUEST['type'] ) && isset( $_REQUEST['table_names'] ) ) { // input var okay.
227 // Get table_name(s) from URL. Type is string for single table export and array for multi table export.
228 // Row export implies single table export.
229 $this->table_list = WPDA::sanitize_text_field_array( $_REQUEST['table_names'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
230
231 if ( 'table' === sanitize_text_field( wp_unslash( $_REQUEST['type'] ) ) ) { // input var okay.
232 // Table export.
233 $this->export_tables();
234 } else {
235 // Row export.
236 if ( is_array( $this->table_list ) ) {
237 // For row exports a single table name must be supplied as a string.
238 $this->wrong_arguments();
239 } else {
240 $this->export_rows();
241 }
242 }
243 } else {
244 $this->wrong_arguments();
245 }
246
247 if ( '' !== $pub_id ) {
248 wp_die();
249 }
250 }
251
252 /**
253 * A table export was requested
254 *
255 * @since 1.0.0
256 */
257 protected function export_tables() {
258 if ( is_array( $this->table_list ) ) {
259 // Multiple table export.
260 $this->header( 'export' );
261 $this->db_begin();
262
263 foreach ( $this->table_list as $table_name ) {
264 // Check if table exists to prevent SQL injection.
265 $wpda_dictionary_exists = new WPDA_Dictionary_Exist( $this->schema_name, $table_name );
266 if ( ! $wpda_dictionary_exists->table_exists() ) {
267 wp_die();
268 }
269
270 $this->create_table( $table_name );
271 $this->insert_rows( $table_name );
272 }
273
274 $this->db_end();
275 } else {
276 // Single table export.
277 $table_name = $this->table_list;
278
279 // Check if table exists to prevent SQL injection.
280 $wpda_dictionary_exists = new WPDA_Dictionary_Exist( $this->schema_name, $table_name );
281 if ( ! $wpda_dictionary_exists->table_exists() ) {
282 wp_die();
283 }
284
285 $this->header( $table_name );
286 $this->db_begin();
287
288 $this->create_table( $table_name );
289 $this->insert_rows( $table_name );
290
291 $this->db_end();
292 }
293 }
294
295 /**
296 * Set export header (filename)
297 *
298 * @param string $file_name Export filename.
299 *
300 * @since 1.0.0
301 */
302 protected function header( $file_name ) {
303 if ( null === $this->output_stream ) {
304 WPDA::sent_header( 'text/plain; charset=utf-8', null, "wpda_{$file_name}.sql" );
305 }
306 }
307
308 /**
309 * Set MySQL environment
310 *
311 * @since 1.0.0
312 */
313 protected function db_begin() {
314 if ( 'off' === $this->mysql_set ) {
315 return;
316 }
317
318 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
319 if ( null === $wpdadb ) {
320 wp_die();
321 }
322
323 $this->output_string = '';
324
325 $this->output_string .= "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n";
326 $this->output_string .= "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n";
327 $this->output_string .= "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n";
328 $this->output_string .= '/*!40101 SET NAMES ' . esc_attr( $wpdadb->charset ) . " */;\n\n";
329
330 $this->write_output();
331 }
332
333 /**
334 * Write create table statement
335 *
336 * @param string $table_name Database table name.
337 *
338 * @return boolean Indicates whether table exists for further processing.
339 * @since 1.0.0
340 */
341 protected function create_table( $table_name ) {
342 global $wpdb;
343 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
344 if ( null === $wpdadb ) {
345 wp_die();
346 }
347
348 $save_suppress_errors = $wpdadb->suppress_errors;
349 $wpdadb->suppress_errors = true;
350
351 if ( 'off' !== $this->show_create ) {
352 $engine = WPDA::get_table_engine( $this->schema_name, $table_name );
353 if ( 'connect' === strtolower( $engine ) ) {
354 // Remove table options for CONNECT tables
355 // NO_TABLE_OPTIONS is deprecated in V8
356 // $wpdadb->query( "SET sql_mode = 'NO_TABLE_OPTIONS'" );
357 }
358 $query = "show create table {$this->schema_name_prefix}`" . str_replace( '`', '', (string) $table_name ) . '`';
359 $ctcmd = $wpdadb->get_results( $query, 'ARRAY_A' );
360 }
361
362 $this->output_string = '';
363 if ( $wpdadb->num_rows > 0 ) {
364 if ( 'off' !== $this->show_comments ) {
365 $this->output_string .= "--\n";
366 if (
367 $this->export_with_prefix &&
368 $wpdb->dbname === $this->schema_name &&
369 ( WPDA::is_wp_table( $table_name ) || WPDA::is_wpda_table( $table_name ) )
370 ) {
371 $this->output_string .= '-- Create table `{wp_prefix}' . esc_attr( substr( $table_name, strlen( $wpdb->prefix ) ) ) . "`\n";
372 } else {
373 $this->output_string .= '-- Create table `' . esc_attr( $table_name ) . "`\n";
374 }
375 $this->output_string .= "--\n";
376 }
377
378 if ( 'off' !== $this->show_create ) {
379 $create_table_statement = $ctcmd[0]['Create Table'];
380 if (
381 $this->export_with_prefix &&
382 $wpdb->dbname === $this->schema_name &&
383 ( WPDA::is_wp_table( $table_name ) || WPDA::is_wpda_table( $table_name ) )
384 ) {
385 $table_name_position = stripos( $create_table_statement, $table_name );
386 if ( $table_name_position > 0 ) {
387 $create_table_statement_saved = $create_table_statement;
388 $create_table_statement = substr( $create_table_statement, 0, $table_name_position );
389 $create_table_statement .= '{wp_prefix}' . esc_attr( substr( $table_name, strlen( $wpdb->prefix ) ) );
390 $create_table_statement .= substr( $create_table_statement_saved, $table_name_position + strlen( $table_name ) );
391 }
392 }
393 $this->output_string .= wp_kses_data( $create_table_statement ) . ";\n\n";
394 }
395
396 $table_exists = true;
397 } else {
398 if ( 'off' !== $this->show_comments ) {
399 $this->output_string .= "--\n";
400 $this->output_string .= '-- Table `' . esc_attr( $table_name ) . "` found\n";
401 $this->output_string .= "--\n\n";
402 }
403
404 $table_exists = false;
405 }
406
407 $this->write_output();
408
409 $wpdadb->suppress_errors = $save_suppress_errors;
410
411 return $table_exists;
412 }
413
414 /**
415 * Write insert into statement
416 *
417 * @param string $table_name Database table name.
418 * @param string $where Where clause.
419 * @param boolean $show_comments Argument is needed for direct call from WPDA_Repository! (do not remove).
420 *
421 * @since 1.0.0
422 */
423 public function insert_rows( $table_name, $where = '', $show_comments = true ) {
424 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
425 if ( null === $wpdadb ) {
426 wp_die();
427 }
428
429 $save_suppress_errors = $wpdadb->suppress_errors;
430 $wpdadb->suppress_errors = true;
431
432 $settings_db = WPDA_Table_Settings_Model::query( $table_name, $this->schema_name );
433 if ( isset( $settings_db[0]['wpda_table_settings'] ) ) {
434 $settings_db_custom = json_decode( $settings_db[0]['wpda_table_settings'] );
435 if ( isset( $settings_db_custom->table_settings->query_buffer_size ) ) {
436 $query_buffer_size = $settings_db_custom->table_settings->query_buffer_size;
437 } else {
438 $query_buffer_size = 0;
439 }
440 } else {
441 $query_buffer_size = 0;
442 }
443
444 $this->output_string = '';
445
446 $query = "select * from {$this->schema_name_prefix}`" . str_replace( '`', '', (string) $table_name ) . "` $where";
447 if ( is_numeric( $query_buffer_size ) && $query_buffer_size > 0 ) {
448 // phpcs:disable Squiz.PHP.DiscouragedFunctions.Discouraged
449 set_time_limit(0);
450 // phpcs:enable Squiz.PHP.DiscouragedFunctions.Discouraged
451 $i = 0;
452 $sql = $query . ' limit ' . $query_buffer_size;
453 $rows = $wpdadb->get_results( $sql, 'ARRAY_A' );
454 while ( $wpdadb->num_rows > 0 ) {
455 $this->insert_rows_buffer( $rows, $table_name, $where, $show_comments );
456
457 $i++;
458 $sql = $query . ' limit ' . $query_buffer_size . ' offset ' . ( $i * $query_buffer_size );
459 $rows = $wpdadb->get_results( $sql, 'ARRAY_A' );
460 }
461
462 if ( 1 === $i && 0 == $wpdadb->num_rows ) {
463 $this->empty_table( $table_name, $show_comments );
464 }
465 } else {
466 $rows = $wpdadb->get_results( $query, 'ARRAY_A' );
467
468 if ( $wpdadb->num_rows > 0 ) {
469 $this->insert_rows_buffer( $rows, $table_name, $where, $show_comments );
470 } else {
471 $this->empty_table( $table_name, $show_comments );
472 }
473 }
474
475 if ( 'on' === $this->include_table_settings ) {
476 // Export table settings
477 $this->export_table_settings( $table_name, $show_comments );
478 }
479
480 $this->write_output();
481
482 $wpdadb->suppress_errors = $save_suppress_errors;
483 }
484
485 private function empty_table( $table_name, $show_comments ) {
486 // Empty table, nothing to export.
487 if ( $show_comments && 'off' !== $this->show_comments ) {
488 $this->output_string .= "--\n";
489 $this->output_string .= '-- No rows to export from empty table `' . esc_attr( $table_name ) . "`\n";
490 $this->output_string .= "--\n\n";
491 }
492 }
493
494 /**
495 * Write insert into statements for buffered rows
496 *
497 * @param array $rows Buffered rows
498 * @param string $table_name Database table name.
499 * @param string $where Where clause.
500 * @param boolean $show_comments Argument is needed for direct call from WPDA_Repository! (do not remove).
501 *
502 * @since 1.0.0
503 */
504 private function insert_rows_buffer( $rows, $table_name, $where, $show_comments ) {
505 global $wpdb;
506
507 $this->output_string = '';
508
509 // Prepare row export: get column names and data types.
510 $wpda_list_columns = WPDA_List_Columns_Cache::get_list_columns( $this->schema_name, $table_name );
511 $table_columns = $wpda_list_columns->get_table_columns();
512
513 // Create array for fast column_name based access.
514 $column_data_types = $this->column_data_types( $table_columns );
515
516 // Exports rows.
517 if ( $show_comments && 'off' !== $this->show_comments ) {
518 $this->output_string .= "--\n";
519 if (
520 $this->export_with_prefix &&
521 $wpdb->dbname === $this->schema_name &&
522 ( WPDA::is_wp_table( $table_name ) || WPDA::is_wpda_table( $table_name ) )
523 ) {
524 $this->output_string .= '-- Export table `{wp_prefix}' . esc_attr( substr( $table_name, strlen( $wpdb->prefix ) ) ) . "`\n";
525 } else {
526 $this->output_string .= '-- Export table `' . esc_attr( $table_name ) . "`\n";
527 }
528 $this->output_string .= "--\n";
529 }
530
531 if ( ( $show_comments && '' !== $where ) && 'off' !== $this->show_comments ) {
532 $this->output_string .= '-- Condition: `' . esc_attr( $where ) . "`\n";
533 $this->output_string .= "--\n";
534 }
535
536 $this->write_output();
537
538 if (
539 $this->export_with_prefix &&
540 ( $wpdb->dbname === $this->schema_name || '' === $this->schema_name ) &&
541 ( WPDA::is_wp_table( $table_name ) || WPDA::is_wpda_table( $table_name ) )
542 ) {
543 $insert_statement = 'INSERT INTO `{wp_prefix}' . esc_attr( substr( str_replace( '`', '', (string) $table_name ), strlen( $wpdb->prefix ) ) ) . '` ';
544 } else {
545 $insert_statement = 'INSERT INTO `' . esc_attr( str_replace( '`', '', (string) $table_name ) ) . '` ';
546 }
547
548 $process_first_row = true;
549 foreach ( $rows[0] as $column_name => $column_value ) {
550 if (
551 ! (
552 WPDA::is_wpda_table( $table_name ) &&
553 (
554 'pub_id' === $column_name ||
555 'csv_id' === $column_name ||
556 'menu_id' === $column_name ||
557 'report_id' === $column_name
558 )
559 )
560 ) {
561 $insert_statement .= $process_first_row ? '(' : ', ';
562 $insert_statement .= '`' . esc_attr( str_replace( '`', '', (string) $column_name ) ) . '`';
563
564 $process_first_row = false;
565 }
566 }
567
568 $insert_statement .= ') VALUES ';
569
570 foreach ( $rows as $row ) {
571 $this->output_string .= $insert_statement . '(';
572
573 $keys = array_keys( $row ); // phpcs:ignore -- 8.1 proof
574 $last_column = end( $keys ); // phpcs:ignore -- 8.1 proof
575 foreach ( $row as $column_name => $column_value ) {
576 if (
577 ! (
578 WPDA::is_wpda_table( $table_name ) &&
579 (
580 'pub_id' === $column_name ||
581 'csv_id' === $column_name ||
582 'menu_id' === $column_name ||
583 'report_id' === $column_name
584 )
585 )
586 ) {
587 if ( $this->is_numeric( $column_data_types[ $column_name ] ) ) {
588 if ( is_null( $column_value ) ) {
589 $this->output_string .= 'null';
590 } else {
591 $this->output_string .= esc_attr( $column_value );
592 }
593 } else {
594 if (
595 WPDA::column_is_schema_name( $table_name, $column_name ) &&
596 ( '' === $column_value || $wpdb->dbname === $column_value )
597 ) {
598 $this->output_string .= "'{wp_schema}'";
599 } else {
600 if ( is_null( $column_value ) ) {
601 $this->output_string .= 'null';
602 } else {
603 $this->output_string .= "'" . esc_sql( $column_value ) . "'";
604 }
605 }
606 }
607 if ( $column_name !== $last_column ) {
608 $this->output_string .= ',';
609 }
610 }
611 }
612
613 $this->output_string .= ");\n";
614
615 $this->write_output();
616 }
617
618 if ( 'off' !== $this->show_comments ) {
619 $this->output_string .= "\n";
620 }
621 }
622
623 /**
624 * Export table settings
625 *
626 * @param $table_name Table for which settings will be exported
627 * @param boolean $show_comments Argument is needed for direct call from WPDA_Repository! (do not remove).
628 *
629 * @since 2.6.1
630 */
631 protected function export_table_settings( $table_name, $show_comments = true ) {
632 global $wpdb;
633
634 if ( $show_comments && 'off' !== $this->show_comments ) {
635 $this->output_string .= "--\n";
636 $this->output_string .= '-- Export table settings for table `' . esc_attr( $table_name ) . "`\n";
637 $this->output_string .= "--\n";
638 }
639
640 if ( '' === $this->schema_name || $wpdb->dbname === $this->schema_name ) {
641 $schema_name = '{wp_schema}';
642 } else {
643 $schema_name = $this->schema_name;
644 }
645
646 if ( 'on' === $this->include_table_settings ) {
647 // Export column labels
648 $rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- plugin table
649 $wpdb->prepare(
650 "select * from {$wpdb->prefix}wpda_table_settings where wpda_table_name = %s",
651 array(
652 $table_name,
653 )
654 ),
655 'ARRAY_A'
656 );
657 if ( 1 === $wpdb->num_rows ) {
658 $this->output_string .=
659 'DELETE FROM `{wp_prefix}wpda_table_settings` ' .
660 "WHERE `wpda_table_name` = '" . esc_attr( $table_name ) . "';";
661 $this->output_string .= "\n";
662 $this->output_string .=
663 'INSERT INTO `{wp_prefix}wpda_table_settings` ' .
664 '(`wpda_schema_name`, `wpda_table_name`, `wpda_table_settings`) ' .
665 'VALUES ' .
666 "('" . esc_sql( $schema_name ) . "', '" . esc_sql( $table_name ) . "', '" .
667 esc_sql( $rows[0]['wpda_table_settings'] ) . "');";
668 $this->output_string .= "\n";
669 }
670
671 // Export media columns
672 $rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- plugin table
673 $wpdb->prepare(
674 "select * from {$wpdb->prefix}wpda_media where media_table_name = %s",
675 array(
676 $table_name,
677 )
678 ),
679 'ARRAY_A'
680 );
681 $this->output_string .=
682 'DELETE FROM `{wp_prefix}wpda_media` ' .
683 "WHERE `media_table_name` = '" . esc_attr( $table_name ) . "';";
684 $this->output_string .= "\n";
685 foreach ( $rows as $row ) {
686 $this->output_string .=
687 'INSERT INTO `{wp_prefix}wpda_media` ' .
688 '(`media_schema_name`, `media_table_name`, `media_column_name`, `media_type`, `media_activated`) ' .
689 'VALUES ' .
690 "('" . esc_sql( $schema_name ) . "', '" . esc_sql( $table_name ) . "', '" .
691 esc_sql( $row['media_column_name'] ) . "', '" . esc_sql( $row['media_type'] ) . "', '" .
692 esc_sql( $row['media_activated'] ) . "');";
693 $this->output_string .= "\n";
694 }
695
696 // Export table menus
697 $rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- plugin table
698 $wpdb->prepare(
699 "select * from {$wpdb->prefix}wpda_menus where menu_table_name = %s",
700 array(
701 $table_name,
702 )
703 ),
704 'ARRAY_A'
705 );
706 $this->output_string .=
707 'DELETE FROM `{wp_prefix}wpda_menus` ' .
708 "WHERE `menu_table_name` = '" . esc_attr( $table_name ) . "';";
709 $this->output_string .= "\n";
710 foreach ( $rows as $row ) {
711 $this->output_string .=
712 'INSERT INTO `{wp_prefix}wpda_menus` ' .
713 '(`menu_schema_name`, `menu_table_name`, `menu_name`, `menu_slug`, `menu_role`) ' .
714 'VALUES ' .
715 "('" . esc_sql( $schema_name ) . "', '" . esc_sql( $table_name ) . "', '" .
716 esc_sql( $row['menu_name'] ) . "', '" . esc_sql( $row['menu_slug'] ) . "', '" .
717 esc_sql( $row['menu_role'] ) . "');";
718 $this->output_string .= "\n";
719 }
720 }
721
722 $this->output_string .= "\n";
723 }
724
725 /**
726 * Save column data types
727 *
728 * This method creates a named array for all column names of a table in form:
729 * 'column_name' => 'data_type'
730 *
731 * Argument $table_columns can be retrieved from WPDA_List_Columns->set_table_columns(). It must be prepared
732 * however with the idea that the instance of WPDA_List_Columns can be reused for best performance.
733 *
734 * In fact this is just an array conversion.
735 *
736 * @param array $table_columns Column_names and data_types of a table (table name not used here).
737 *
738 * @return array Named array 'column_name' => 'data_type' for all columns in the table.
739 * @since 1.0.0
740 */
741 protected function column_data_types( $table_columns ) {
742 $column_data_types = array();
743
744 foreach ( $table_columns as $column_value ) {
745 $column_data_types[ $column_value['column_name'] ] = $column_value['data_type'];
746 }
747
748 return $column_data_types;
749 }
750
751 /**
752 * Check if data type is numeric
753 *
754 * @param string $data_type Data type (simple).
755 *
756 * @return bool
757 * @since 1.0.0
758 */
759 protected function is_numeric( $data_type ) {
760 return ( 'number' === WPDA::get_type( $data_type ) );
761 }
762
763 /**
764 * Set back MySQL environment
765 *
766 * @since 1.0.0
767 */
768 protected function db_end() {
769 if ( 'off' === $this->mysql_set ) {
770 return;
771 }
772
773 $this->output_string = '';
774
775 $this->output_string .= "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n";
776 $this->output_string .= "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n";
777 $this->output_string .= "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n";
778
779 $this->write_output();
780 }
781
782 /**
783 * Processing on invalid arguments
784 *
785 * @since 1.0.0
786 */
787 protected function wrong_arguments() {
788 wp_die();
789 }
790
791 /**
792 * A row export was requested
793 *
794 * @since 1.0.0
795 */
796 protected function export_rows() {
797 // Check if table exists to prevent SQL injection.
798 $wpda_dictionary_exists = new WPDA_Dictionary_Exist( $this->schema_name, $this->table_list );
799 if ( ! $wpda_dictionary_exists->table_exists() ) {
800 wp_die();
801 }
802
803 // Get table columns and data types.
804 $wpda_list_columns = WPDA_List_Columns_Cache::get_list_columns( $this->schema_name, $this->table_list );
805 $table_columns = $wpda_list_columns->get_table_columns();
806
807 // Create array for fast column_name based access.
808 $column_data_types = $this->column_data_types( $table_columns );
809
810 // Get primary key columns.
811 $table_primary_key = $wpda_list_columns->get_table_primary_key();
812
813 // Check validity request. All primary key columns must be supplied. Return error if
814 // primary key columns are missing.
815 foreach ( $table_primary_key as $key ) {
816 if ( ! isset( $key ) ) {
817 $this->wrong_arguments();
818 }
819 }
820
821 global $wpdb;
822
823 // Build where clause based on primary key.
824 $where = '';
825
826 // Use first column of the primary key to loop through arguments. Add additional arguments in the loop.
827 // A mismatch in the number of argument is possible as long as the columns match based on the first column
828 // of the primary key. Other mismatches won't be taken into account.
829 $count_pk = count( ( array ) $_REQUEST[ $table_primary_key[0] ] ); // phpcs:ignore -- 8.1 proof
830 for ( $i = 0; $i < $count_pk; $i ++ ) {
831 $and = '';
832 foreach ( $table_primary_key as $key ) {
833 $and .= '' === $and ? '(' : ' and ';
834 if ( $this->is_numeric( $column_data_types[ $key ] ) ) {
835 $and .= $wpdb->prepare(
836 '`%1s` = %d', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders
837 array(
838 WPDA::remove_backticks( $key ),
839 sanitize_text_field( wp_unslash( $_REQUEST[ $key ][ $i ] ) ), // phpcs:ignore -- nonce veryfied in export function
840 )
841 );
842 } else {
843 $and .= $wpdb->prepare(
844 '`%1s` = %s', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders
845 array(
846 WPDA::remove_backticks( $key ),
847 sanitize_text_field( wp_unslash( $_REQUEST[ $key ][ $i ] ) ), // phpcs:ignore -- nonce veryfied in export function
848 )
849 );
850 }
851 }
852
853 $and .= '' === $and ? '' : ')';
854
855 $where .= '' === $where ? ' where ' : ' or ';
856 $where .= $and;
857 }
858
859 $this->header( $this->table_list );
860 $this->db_begin();
861 $this->insert_rows( $this->table_list, $where );
862 $this->db_end();
863 }
864
865 /**
866 * Define output stream
867 *
868 * Used to stream an export to a file. Streaming helps to support exports of large files.
869 *
870 * @param Stream $output_stream Handle to output stream
871 *
872 * @since 2.0.13
873 */
874 public function set_output_stream( $output_stream ) {
875 $this->output_stream = $output_stream;
876 }
877
878 /**
879 * Depending on the type of export (streaming or echoing) the output is written to the export file.
880 *
881 * @since 2.0.13
882 */
883 protected function write_output() {
884 if ( null === $this->output_stream ) {
885 echo $this->output_string; // phpcs:ignore WordPress.Security.EscapeOutput
886 } else {
887 fwrite( $this->output_stream, $this->output_string ); // phpcs:ignore
888 }
889 $this->output_string = '';
890 }
891
892 }
893
894 }
895