PageRenderTime 29ms CodeModel.GetById 54ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/wp-migrate-db/class/wpmdb-cli.php

https://gitlab.com/hector20091/digital_light
PHP | 522 lines | 289 code | 105 blank | 128 comment | 50 complexity | 19c053b11d9a9ed35d7532871facc3a1 MD5 | raw file
  1. <?php
  2. class WPMDB_CLI extends WPMDB_Base {
  3. /**
  4. * Instance of WPMDB.
  5. *
  6. * @var WPMDB
  7. */
  8. protected $wpmdb;
  9. /**
  10. * Migration profile.
  11. *
  12. * @var array
  13. */
  14. protected $profile;
  15. /**
  16. * Data to post during migration.
  17. *
  18. * @var array
  19. */
  20. protected $post_data = array();
  21. /**
  22. * Required PHP version
  23. *
  24. * @var string
  25. */
  26. protected $php_version_required;
  27. /**
  28. * Migration Data
  29. *
  30. * @var array
  31. */
  32. protected $migration;
  33. function __construct( $plugin_file_path ) {
  34. parent::__construct( $plugin_file_path );
  35. if ( ! version_compare( PHP_VERSION, $this->php_version_required, '>=' ) ) {
  36. return;
  37. }
  38. global $wpmdb;
  39. $this->wpmdb = $wpmdb;
  40. }
  41. /**
  42. * Checks profile data before CLI migration.
  43. *
  44. * @param int|array $profile Profile key or array.
  45. *
  46. * @return mixed|WP_Error
  47. */
  48. public function pre_cli_migration_check( $profile ) {
  49. if ( ! version_compare( PHP_VERSION, $this->php_version_required, '>=' ) ) {
  50. return $this->cli_error( sprintf( __( 'CLI addon requires PHP %1$s+', 'wp-migrate-db-cli' ), $this->php_version_required ) );
  51. }
  52. if ( is_array( $profile ) ) {
  53. $query_str = http_build_query( $profile );
  54. $profile = $this->wpmdb->parse_migration_form_data( $query_str );
  55. $profile = wp_parse_args(
  56. $profile,
  57. array(
  58. 'save_computer' => '0',
  59. 'gzip_file' => '0',
  60. 'replace_guids' => '0',
  61. 'exclude_transients' => '0',
  62. 'exclude_spam' => '0',
  63. 'keep_active_plugins' => '0',
  64. 'compatibility_older_mysql' => '0',
  65. )
  66. );
  67. }
  68. $this->profile = $profile = apply_filters( 'wpmdb_cli_profile_before_migration', $profile );
  69. if ( is_wp_error( $profile ) ) {
  70. return $profile;
  71. }
  72. return true;
  73. }
  74. /**
  75. * Performs CLI migration given a profile data.
  76. *
  77. * @param int|array $profile Profile key or array.
  78. *
  79. * @return bool|WP_Error Returns true if succeed or WP_Error if failed.
  80. */
  81. public function cli_migration( $profile ) {
  82. $pre_check = $this->pre_cli_migration_check( $profile );
  83. if ( is_wp_error( $pre_check ) ) {
  84. return $pre_check;
  85. }
  86. // At this point, $profile has been checked a retrieved into $this->profile, so should not be used in this function any further.
  87. if ( empty( $this->profile ) ) {
  88. return $this->cli_error( __( 'Profile not found or unable to be generated from params.', 'wp-migrate-db-cli' ) );
  89. }
  90. unset( $profile );
  91. $this->set_time_limit();
  92. $this->wpmdb->set_cli_migration();
  93. if ( 'savefile' === $this->profile['action'] ) {
  94. $this->post_data['intent'] = 'savefile';
  95. if ( ! empty( $this->profile['export_dest'] ) ) {
  96. $this->post_data['export_dest'] = $this->profile['export_dest'];
  97. } else {
  98. $this->post_data['export_dest'] = 'ORIGIN';
  99. }
  100. }
  101. // Ensure local site_details available.
  102. $this->post_data['site_details']['local'] = $this->site_details();
  103. // Check for tables specified in migration profile that do not exist in the source database
  104. if ( ! empty( $this->profile['select_tables'] ) ) {
  105. $source_tables = apply_filters( 'wpmdb_cli_filter_source_tables', $this->get_tables() );
  106. if ( ! empty( $source_tables ) ) {
  107. // Return error if selected tables do not exist in source database
  108. $nonexistent_tables = array();
  109. foreach ( $this->profile['select_tables'] as $table ) {
  110. if ( ! in_array( $table, $source_tables ) ) {
  111. $nonexistent_tables[] = $table;
  112. }
  113. }
  114. if ( ! empty( $nonexistent_tables ) ) {
  115. $local_or_remote = ( 'pull' === $this->profile['action'] ) ? 'remote' : 'local';
  116. return $this->cli_error( sprintf( __( 'The following table(s) do not exist in the %1$s database: %2$s', 'wp-migrate-db-cli' ), $local_or_remote, implode( ', ', $nonexistent_tables ) ) );
  117. }
  118. }
  119. }
  120. $this->profile = apply_filters( 'wpmdb_cli_filter_before_cli_initiate_migration', $this->profile );
  121. if ( is_wp_error( $this->profile ) ) {
  122. return $this->profile;
  123. }
  124. $this->migration = $this->cli_initiate_migration();
  125. if ( is_wp_error( $this->migration ) ) {
  126. return $this->migration;
  127. }
  128. $this->post_data['migration_state_id'] = $this->migration['migration_state_id'];
  129. $tables_to_process = $this->migrate_tables();
  130. if ( is_wp_error( $tables_to_process ) ) {
  131. return $tables_to_process;
  132. }
  133. $this->post_data['tables'] = implode( ',', $tables_to_process );
  134. $finalize = $this->finalize_migration();
  135. if ( is_wp_error( $finalize ) || 'savefile' === $this->profile['action'] ) {
  136. return $finalize;
  137. }
  138. return true;
  139. }
  140. /**
  141. * Verify CLI response from endpoint.
  142. *
  143. * @param string $response Response from endpoint.
  144. * @param string $function_name Name of called function.
  145. *
  146. * @return WP_Error|string
  147. */
  148. function verify_cli_response( $response, $function_name ) {
  149. $response = trim( $response );
  150. if ( false === $response ) {
  151. return $this->cli_error( $this->error );
  152. }
  153. if ( false === $this->wpmdb->is_json( $response ) ) {
  154. return $this->cli_error( sprintf( __( 'We were expecting a JSON response, instead we received: %2$s (function name: %1$s)', 'wp-migrate-db-cli' ), $function_name, $response ) );
  155. }
  156. $response = json_decode( $response, true );
  157. if ( isset( $response['wpmdb_error'] ) ) {
  158. return $this->cli_error( $response['body'] );
  159. }
  160. // Display warnings and non fatal error messages as CLI warnings without aborting.
  161. if ( isset( $response['wpmdb_warning'] ) || isset( $response['wpmdb_non_fatal_error'] ) ) {
  162. $body = ( isset ( $response['cli_body'] ) ) ? $response['cli_body'] : $response['body'];
  163. $messages = maybe_unserialize( $body );
  164. foreach ( ( array ) $messages as $message ) {
  165. if ( $message ) {
  166. WP_CLI::warning( self::cleanup_message( $message ) );
  167. }
  168. }
  169. }
  170. return $response;
  171. }
  172. /**
  173. * Return instance of WP_Error.
  174. *
  175. * @param string $message Error message.
  176. *
  177. * @return WP_Error.
  178. */
  179. function cli_error( $message ) {
  180. return new WP_Error( 'wpmdb_cli_error', self::cleanup_message( $message ) );
  181. }
  182. /**
  183. * Cleanup message, replacing <br> with \n and removing HTML.
  184. *
  185. * @param string $message Error message.
  186. *
  187. * @return string $message.
  188. */
  189. static function cleanup_message( $message ) {
  190. $message = html_entity_decode( $message, ENT_QUOTES );
  191. $message = preg_replace( '#<br\s*/?>#', "\n", $message );
  192. $message = trim( strip_tags( $message ) );
  193. return $message;
  194. }
  195. /**
  196. * Initiates migration and verifies result
  197. *
  198. * @return array|WP_Error
  199. */
  200. function cli_initiate_migration() {
  201. do_action( 'wpmdb_cli_before_initiate_migration', $this->profile );
  202. WP_CLI::log( __( 'Initiating migration...', 'wp-migrate-db-cli' ) );
  203. $migration_args = $this->post_data;
  204. $migration_args['form_data'] = http_build_query( $this->profile );
  205. $migration_args['stage'] = 'migrate';
  206. $migration_args['site_details']['local'] = $this->site_details();
  207. $this->post_data = apply_filters( 'wpmdb_cli_initiate_migration_args', $migration_args, $this->profile );
  208. $this->post_data['site_details'] = json_encode( $this->post_data['site_details'] );
  209. $response = $this->initiate_migration( $this->post_data );
  210. $initiate_migration_response = $this->verify_cli_response( $response, 'initiate_migration()' );
  211. if ( ! is_wp_error( $initiate_migration_response ) ) {
  212. $initiate_migration_response = apply_filters( 'wpmdb_cli_initiate_migration_response', $initiate_migration_response );
  213. }
  214. return $initiate_migration_response;
  215. }
  216. /**
  217. * Determine which tables to migrate
  218. *
  219. * @return array|WP_Error
  220. */
  221. function get_tables_to_migrate() {
  222. $tables_to_migrate = $this->get_tables( 'prefix' );
  223. return apply_filters( 'wpmdb_cli_tables_to_migrate', $tables_to_migrate, $this->profile, $this->migration );
  224. }
  225. /**
  226. * Returns a WP-CLI progress bar instance
  227. *
  228. * @param array $tables
  229. * @param int $stage
  230. *
  231. * @return \cli\progress\Bar
  232. */
  233. function get_progress_bar( $tables, $stage ) {
  234. $progress_label = __( 'Exporting tables', 'wp-migrate-db-cli' );
  235. $progress_label = apply_filters( 'wpmdb_cli_progress_label', $progress_label, $stage, $tables );
  236. $progress_label = str_pad( $progress_label, 20, ' ' );
  237. $count = $this->get_total_rows_from_table_list( $tables, $stage );
  238. return new \cli\progress\Bar( $progress_label, $count );
  239. }
  240. /**
  241. * Returns total rows from list of tables
  242. *
  243. * @param array $tables
  244. * @param int $stage
  245. *
  246. * @return Int
  247. */
  248. function get_total_rows_from_table_list( $tables, $stage ) {
  249. static $cached_results = array();
  250. if ( isset( $cached_results[ $stage ] ) ) {
  251. return $cached_results[ $stage ];
  252. }
  253. $table_rows = $this->get_row_counts_from_table_list( $tables, $stage );
  254. $cached_results[ $stage ] = array_sum( array_intersect_key( $table_rows, array_flip( $tables ) ) );
  255. return $cached_results[ $stage ];
  256. }
  257. /**
  258. * Returns row counts from list of tables
  259. *
  260. * @param array $tables
  261. * @param int $stage
  262. *
  263. * @return mixed
  264. */
  265. function get_row_counts_from_table_list( $tables, $stage ) {
  266. static $cached_results = array();
  267. if ( isset( $cached_results[ $stage ] ) ) {
  268. return $cached_results[ $stage ];
  269. }
  270. $local_table_rows = $this->wpmdb->get_table_row_count();
  271. $cached_results[ $stage ] = apply_filters( 'wpmdb_cli_get_row_counts_from_table_list', $local_table_rows, $stage );
  272. return $cached_results[ $stage ];
  273. }
  274. /**
  275. * @return array|mixed|string|void|WP_Error
  276. */
  277. function migrate_tables() {
  278. $tables_to_migrate = $this->get_tables_to_migrate();
  279. $tables = $tables_to_migrate;
  280. $stage_iterator = 2;
  281. $filtered_vars = apply_filters( 'wpmdb_cli_filter_before_migrate_tables', array( 'tables' => $tables, 'stage_iterator' => $stage_iterator ) );
  282. if ( ! is_array( $filtered_vars ) ) {
  283. return $filtered_vars;
  284. } else {
  285. extract( $filtered_vars, EXTR_OVERWRITE );
  286. }
  287. if ( empty( $tables ) ) {
  288. return $this->cli_error( __( 'No tables selected for migration.', 'wp-migrate-db' ) );
  289. }
  290. $table_rows = $this->get_row_counts_from_table_list( $tables, $stage_iterator );
  291. do_action( 'wpmdb_cli_before_migrate_tables', $this->profile, $this->migration );
  292. $notify = $this->get_progress_bar( $tables, $stage_iterator );
  293. $args = $this->post_data;
  294. do {
  295. $migration_progress = 0;
  296. foreach ( $tables as $key => $table ) {
  297. $current_row = -1;
  298. $primary_keys = '';
  299. $table_progress = 0;
  300. $table_progress_last = 0;
  301. $args['table'] = $table;
  302. $args['last_table'] = ( $key == count( $tables ) - 1 ) ? '1' : '0';
  303. do {
  304. // reset the current chunk
  305. $this->wpmdb->empty_current_chunk();
  306. $args['current_row'] = $current_row;
  307. $args['primary_keys'] = $primary_keys;
  308. $args = apply_filters( 'wpmdb_cli_migrate_table_args', $args, $this->profile, $this->migration );
  309. $response = $this->migrate_table( $args );
  310. $migrate_table_response = $this->verify_cli_response( $response, 'migrate_table()' );
  311. if ( is_wp_error( $migrate_table_response ) ) {
  312. return $migrate_table_response;
  313. }
  314. $migrate_table_response = apply_filters( 'wpmdb_cli_migrate_table_response', $migrate_table_response, $_POST, $this->profile, $this->migration );
  315. $current_row = $migrate_table_response['current_row'];
  316. $primary_keys = $migrate_table_response['primary_keys'];
  317. $last_migration_progress = $migration_progress;
  318. if ( -1 == $current_row ) {
  319. $migration_progress -= $table_progress;
  320. $migration_progress += $table_rows[ $table ];
  321. } else {
  322. if ( 0 === $table_progress_last ) {
  323. $table_progress_last = $current_row;
  324. $table_progress = $table_progress_last;
  325. $migration_progress += $table_progress_last;
  326. } else {
  327. $iteration_progress = $current_row - $table_progress_last;
  328. $table_progress_last = $current_row;
  329. $table_progress += $iteration_progress;
  330. $migration_progress += $iteration_progress;
  331. }
  332. }
  333. $increment = $migration_progress - $last_migration_progress;
  334. $notify->tick( $increment );
  335. } while ( -1 != $current_row );
  336. }
  337. $notify->finish();
  338. ++$stage_iterator;
  339. $args['stage'] = 'migrate';
  340. $tables = $tables_to_migrate;
  341. $table_rows = $this->get_row_counts_from_table_list( $tables, $stage_iterator );
  342. if ( $stage_iterator < 3 ) {
  343. $notify = $this->get_progress_bar( $tables, $stage_iterator );
  344. }
  345. } while ( $stage_iterator < 3 );
  346. $this->post_data = $args;
  347. return $tables;
  348. }
  349. /**
  350. * Finalize migration
  351. *
  352. * @return bool|WP_Error
  353. */
  354. function finalize_migration() {
  355. do_action( 'wpmdb_cli_before_finalize_migration', $this->profile, $this->migration );
  356. WP_CLI::log( __( 'Cleaning up...', 'wp-migrate-db-cli' ) );
  357. $finalize = apply_filters( 'wpmdb_cli_finalize_migration', true, $this->profile, $this->migration );
  358. if ( is_wp_error( $finalize ) ) {
  359. return $finalize;
  360. }
  361. $this->post_data = apply_filters( 'wpmdb_cli_finalize_migration_args', $this->post_data, $this->profile, $this->migration );
  362. if ( 'savefile' === $this->post_data['intent'] ) {
  363. return $this->finalize_export();
  364. }
  365. $response = null;
  366. $response = apply_filters( 'wpmdb_cli_finalize_migration_response', $response );
  367. if ( ! empty( $response ) && '1' !== $response ) {
  368. return $this->cli_error( $response );
  369. }
  370. do_action( 'wpmdb_cli_after_finalize_migration', $this->profile, $this->migration );
  371. return true;
  372. }
  373. /**
  374. * Stub for ajax_initiate_migration()
  375. *
  376. * @param array|bool $args
  377. *
  378. * @return string
  379. */
  380. function initiate_migration( $args = false ) {
  381. $_POST = $args;
  382. $response = $this->wpmdb->ajax_initiate_migration();
  383. return $response;
  384. }
  385. /**
  386. * stub for ajax_migrate_table()
  387. *
  388. * @param array|bool $args
  389. *
  390. * @return string
  391. */
  392. function migrate_table( $args = false ) {
  393. $_POST = $args;
  394. $response = $this->wpmdb->ajax_migrate_table();
  395. return $response;
  396. }
  397. /**
  398. * Finalize Export by moving file to specified destination
  399. *
  400. * @return string|error
  401. */
  402. function finalize_export() {
  403. $state_data = $this->wpmdb->state_data;
  404. $temp_file = $state_data['dump_path'];
  405. if ( 'ORIGIN' === $state_data['export_dest'] ) {
  406. $response = $temp_file;
  407. } else {
  408. $dest_file = $state_data['export_dest'];
  409. if ( file_exists( $temp_file ) && rename( $temp_file, $dest_file ) ) {
  410. $response = $dest_file;
  411. } else {
  412. $response = $this->cli_error( __( 'Unable to move exported file.', 'wp-migrate-db' ) );
  413. }
  414. }
  415. return $response;
  416. }
  417. }