PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/backupbuddy/destinations/site/init.php

https://gitlab.com/mattswann/launch-housing
PHP | 257 lines | 175 code | 51 blank | 31 comment | 38 complexity | 973005609593d0dd805d765cdd9048ae MD5 | raw file
  1. <?php
  2. // DO NOT CALL THIS CLASS DIRECTLY. CALL VIA: pb_backupbuddy_destination in bootstrap.php.
  3. class pb_backupbuddy_destination_site {
  4. const TIME_WIGGLE_ROOM = 5; // Number of seconds to fudge up the time elapsed to give a little wiggle room so we don't accidently hit the edge and time out.
  5. public static $destination_info = array(
  6. 'name' => 'BackupBuddy Deployment',
  7. 'description' => 'Push to or Pull from another instance of this WordPress site running BackupBuddy. Great for rapidly copying a site to and from a development version back and forth with a live site.',
  8. 'category' => 'best', // best, normal, legacy
  9. );
  10. // Default settings. Should be public static for auto-merging.
  11. public static $default_settings = array(
  12. 'type' => 'site', // MUST MATCH your destination slug.
  13. 'title' => '', // Required destination field.
  14. 'api_key' => '',
  15. 'max_payload' => '10', // Max payload in MB to send per chunk. This WILL be read into memory.
  16. 'max_time' => '30', // Default max time in seconds to allow a send to run for. This should be set on the fly prior to calling send overriding this default.
  17. 'resume_point' => '', // fseek resume point (via ftell).
  18. 'chunks_total' => 1,
  19. 'chunks_sent' => 0,
  20. 'sendType' => '', // Set on the fly prior to calling send. Valid types: backup, media, theme, plugin. These determine the destination root location for a file.
  21. 'sendFilePath' => '', // Location to store file on remote server relative to the root storage location based on send type. Optional.
  22. 'disabled' => '0', // When 1, disable this destination.
  23. );
  24. private static $_timeStart = 0;
  25. /* send()
  26. *
  27. * Send one or more files.
  28. *
  29. * @param array $files Array of one or more files to send. IMPORTANT: Currently only supports ONE file.
  30. * @return boolean True on success, else false.
  31. */
  32. public static function send( $settings = array(), $files = array(), $send_id = '', $delete_after = false ) {
  33. global $pb_backupbuddy_destination_errors;
  34. if ( '1' == $settings['disabled'] ) {
  35. $pb_backupbuddy_destination_errors[] = __( 'Error #48933: This destination is currently disabled. Enable it under this destination\'s Advanced Settings.', 'it-l10n-backupbuddy' );
  36. return false;
  37. }
  38. if ( ! is_array( $files ) ) {
  39. $files = array( $files );
  40. }
  41. self::$_timeStart = microtime( true );
  42. if ( count( $files ) > 1 ) {
  43. $message = 'Error #84545894585: This destination currently only supports one file per send.';
  44. pb_backupbuddy::status( 'error', $message );
  45. global $pb_backupbuddy_destination_errors;
  46. $pb_backupbuddy_destination_errors[] = $message;
  47. return false;
  48. }
  49. require_once( pb_backupbuddy::plugin_path() . '/classes/remote_api.php' );
  50. $apiSettings = backupbuddy_remote_api::key_to_array( $settings['api_key'] );
  51. if ( site_url() == $apiSettings['siteurl'] ) {
  52. $message = 'Error #4389843. You are trying to use this site\'s own API key. You must use the API key from the remote destination site.';
  53. pb_backupbuddy::status( 'error', $message );
  54. global $pb_backupbuddy_destination_errors;
  55. $pb_backupbuddy_destination_errors[] = $message;
  56. return false;
  57. }
  58. $apiURL = $apiSettings['siteurl'];
  59. $file = $files[0];
  60. $filePath = '';
  61. if ( '' != $settings['sendFilePath'] ) {
  62. $filePath = $settings['sendFilePath'];
  63. }
  64. $maxPayload = $settings['max_payload'] * 1024 * 1024; // Convert to bytes.
  65. $encodeReducedPayload = floor( ( $settings['max_payload'] - ( $settings['max_payload'] * 0.37 ) ) * 1024 * 1024 ); // Take into account 37% base64 encoding overhead. Convert to bytes. Remove any decimals down via floor.
  66. // Open file for reading.
  67. if ( FALSE === ( $fs = @fopen( $file, 'r' ) ) ) {
  68. pb_backupbuddy::status( 'error', 'Error #438934894: Unable to open file `' . $file . '` for reading.' );
  69. return false;
  70. }
  71. // If chunked resuming then seek to the correct place in the file.
  72. if ( ( '' != $settings['resume_point'] ) && ( $settings['resume_point'] > 0 ) ) { // Resuming send of a partially transferred file.
  73. if ( 0 !== fseek( $fs, $settings['resume_point'] ) ) { // Returns 0 on success.
  74. pb_backupbuddy::status( 'error', 'Error #327834783: Failed to seek file to resume point `' . $settings['resume_point'] . '` via fseek().' );
  75. return false;
  76. }
  77. $prevPointer = $settings['resume_point'];
  78. } else { // New file send.
  79. $size = filesize( $file );
  80. $encodedSize = ( $size * 0.37 ) + $size;
  81. pb_backupbuddy::status( 'details', 'File size of file to send: ' . pb_backupbuddy::$format->file_size( $size ) . '. After encoding overhead: ' . pb_backupbuddy::$format->file_size( $encodedSize ) );
  82. if ( $encodedSize > $maxPayload ) {
  83. $settings['chunks_total'] = ceil( $encodedSize / $maxPayload ); // $maxPayload );
  84. pb_backupbuddy::status( 'details', 'This file + encoding exceeds the maximum per-chunk payload size so will be read in and sent in chunks of ' . $settings['max_payload'] . 'MB (' . $maxPayload . ' bytes) totaling approximately ' . $settings['chunks_total'] . ' chunks.' );
  85. } else {
  86. pb_backupbuddy::status( 'details', 'This file + encoding does not exceed per-chunk payload size of ' . $settings['max_payload'] . 'MB (' . $maxPayload . ' bytes) so sending in one pass.' );
  87. }
  88. $prevPointer = 0;
  89. }
  90. pb_backupbuddy::status( 'details', 'Reading in `' . $encodeReducedPayload . '` bytes at a time to send.' );
  91. $dataRemains = true;
  92. //$loopCount = 0;
  93. //$loopTimeSum = 0; // Used for average send time per chunk.
  94. while ( ( TRUE === $dataRemains ) && ( FALSE !== ( $fileData = fread( $fs, $encodeReducedPayload ) ) ) ) { // Grab one chunk of data at a time.
  95. pb_backupbuddy::status( 'details', 'Read in file data.' );
  96. if ( feof( $fs ) ) {
  97. pb_backupbuddy::status( 'details', 'Read to end of file (feof true). No more chunks left after this send.' );
  98. $dataRemains = false;
  99. }
  100. $isFileTest = false;
  101. if ( false !== stristr( basename( $file ), 'remote-send-test.php' ) ) {
  102. $isFileTest = true;
  103. $settings['sendType'] = 'test';
  104. }
  105. if ( true === $dataRemains ) {
  106. $isFileDone = false;
  107. } else {
  108. $isFileDone = true;
  109. }
  110. if ( ! isset( $size ) ) {
  111. $size = '';
  112. }
  113. pb_backupbuddy::status( 'details', 'Connecting to remote server to send data.' );
  114. $response = backupbuddy_remote_api::remoteCall( $apiSettings, 'sendFile_' . $settings['sendType'], array(), $settings['max_time'], $file, $fileData, $prevPointer, $isFileTest, $isFileDone, $size, $filePath );
  115. unset( $fileData ); // Free up memory.
  116. $settings['chunks_sent']++;
  117. if ( true === $dataRemains ) { // More chunks remain.
  118. pb_backupbuddy::status( 'details', 'Connection finished sending part ' . $settings['chunks_sent'] . ' of ~' . $settings['chunks_total'] . '.' );
  119. } else { // No more chunks remain.
  120. pb_backupbuddy::status( 'details', 'Connection finished sending final part ' . $settings['chunks_sent'] . '.' );
  121. }
  122. if ( false === $response ) {
  123. echo implode( ', ', backupbuddy_remote_api::getErrors() ) . ' ';
  124. pb_backupbuddy::status( 'error', 'Errors encountered details: ' . implode( ', ', backupbuddy_remote_api::getErrors() ) );
  125. global $pb_backupbuddy_destination_errors;
  126. $pb_backupbuddy_destination_errors[] = backupbuddy_remote_api::getErrors();
  127. return false; //implode( ', ', backupbuddy_remote_api::getErrors() );
  128. }
  129. if ( FALSE === ( $prevPointer = ftell( $fs ) ) ) {
  130. pb_backupbuddy::status( 'error', 'Error #438347844: Unable to get ftell pointer of file handle for passing to prevPointer.' );
  131. @fclose( $fs );
  132. return false;
  133. } else {
  134. pb_backupbuddy::status( 'details', 'File pointer: `' . $prevPointer . '`.' );
  135. }
  136. if ( true === $dataRemains ) { // More data remains so see if we need to consider chunking to a new PHP process.
  137. // If we are within X second of reaching maximum PHP runtime then stop here so that it can be picked up in another PHP process...
  138. if ( ( ( microtime( true ) - self::$_timeStart ) + self::TIME_WIGGLE_ROOM ) >= $settings['max_time'] ) {
  139. pb_backupbuddy::status( 'message', 'Approaching limit of available PHP chunking time of `' . $settings['max_time'] . '` sec. Ran for ' . round( microtime( true ) - self::$_timeStart, 3 ) . ' sec. Proceeding to use chunking.' );
  140. @fclose( $fs );
  141. // Tells next chunk where to pick up.
  142. $settings['resume_point'] = $prevPointer;
  143. // Schedule cron.
  144. $cronTime = time();
  145. $cronArgs = array( $settings, $files, $send_id, $delete_after );
  146. $cronHashID = md5( $cronTime . serialize( $cronArgs ) );
  147. $cronArgs[] = $cronHashID;
  148. $schedule_result = backupbuddy_core::schedule_single_event( $cronTime, 'destination_send', $cronArgs );
  149. if ( true === $schedule_result ) {
  150. pb_backupbuddy::status( 'details', 'Next Site chunk step cron event scheduled.' );
  151. } else {
  152. pb_backupbuddy::status( 'error', 'Next Site chunk step cron event FAILED to be scheduled.' );
  153. }
  154. if ( '1' != pb_backupbuddy::$options['skip_spawn_cron_call'] ) {
  155. update_option( '_transient_doing_cron', 0 ); // Prevent cron-blocking for next item.
  156. spawn_cron( time() + 150 ); // Adds > 60 seconds to get around once per minute cron running limit.
  157. }
  158. return array( $prevPointer, 'Sent part ' . $settings['chunks_sent'] . ' of ~' . $settings['chunks_total'] . ' parts.' ); // filepointer location, elapsed time during the import
  159. } else { // End if.
  160. pb_backupbuddy::status( 'details', 'Not approaching time limit.' );
  161. }
  162. } else {
  163. pb_backupbuddy::status( 'details', 'No more data remains (eg for chunking) so finishing up.' );
  164. }
  165. } // end while data remains in file.
  166. // Update fileoptions stats.
  167. pb_backupbuddy::status( 'details', 'About to load fileoptions data.' );
  168. require_once( pb_backupbuddy::plugin_path() . '/classes/fileoptions.php' );
  169. pb_backupbuddy::status( 'details', 'Fileoptions instance #20.' );
  170. $fileoptions_obj = new pb_backupbuddy_fileoptions( backupbuddy_core::getLogDirectory() . 'fileoptions/send-' . $send_id . '.txt', $read_only = false, $ignore_lock = false, $create_file = false );
  171. if ( true !== ( $result = $fileoptions_obj->is_ok() ) ) {
  172. pb_backupbuddy::status( 'error', __('Fatal Error #9034.279327. Unable to access fileoptions data.', 'it-l10n-backupbuddy' ) . ' Error: ' . $result );
  173. return false;
  174. }
  175. pb_backupbuddy::status( 'details', 'Fileoptions data loaded.' );
  176. $fileoptions = &$fileoptions_obj->options;
  177. $fileoptions['finish_time'] = microtime(true);
  178. $fileoptions['status'] = 'success';
  179. $fileoptions['_multipart_status'] = 'Sent all parts.';
  180. if ( isset( $uploaded_speed ) ) {
  181. $fileoptions['write_speed'] = $uploaded_speed;
  182. }
  183. $fileoptions_obj->save();
  184. unset( $fileoptions );
  185. // Made it this far so completed!
  186. pb_backupbuddy::status( 'message', 'Finished sending file. Took ' . round( microtime( true ) - self::$_timeStart, 3 ) . ' seconds this round.' );
  187. pb_backupbuddy::status( 'deployFileSent', 'File sent.' );
  188. return true;
  189. } // End send().
  190. /* test()
  191. *
  192. * function description
  193. *
  194. * @param array $settings Destination settings.
  195. * @return bool|string True on success, string error message on failure.
  196. */
  197. public static function test( $settings ) {
  198. /*
  199. if ( ( $settings['address'] == '' ) || ( $settings['username'] == '' ) || ( $settings['password'] == '' ) ) {
  200. return __('Missing required input.', 'it-l10n-backupbuddy' );
  201. }
  202. */
  203. // Try sending a file.
  204. return pb_backupbuddy_destinations::send( $settings, dirname( dirname( __FILE__ ) ) . '/remote-send-test.php', $send_id = 'TEST-' . pb_backupbuddy::random_string( 12 ) ); // 3rd param true forces clearing of any current uploads.
  205. } // End test().
  206. } // End class.