GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#1071)
by Tom
03:14
created

core.php ➔ is_backup_possible()   C

Complexity

Conditions 9
Paths 6

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 9
eloc 12
nc 6
nop 0
dl 0
loc 24
rs 5.3563
c 3
b 1
f 0
1
<?php
2
3
namespace HM\BackUpWordPress;
4
5
/**
6
 * Handles anything that needs to be
7
 * done when the plugin is updated
8
 */
9
function update() {
10
11
	// Update from backUpWordPress 0.4.5
12
	if ( get_option( 'bkpwp_max_backups' ) ) {
13
14
		// Carry over the custom path
15
		if ( $legacy_path = get_option( 'bkpwppath' ) ) {
16
			update_option( 'hmbkp_path', $legacy_path );
17
		}
18
19
		// Options to remove
20
		$legacy_options = array(
21
			'bkpwp_archive_types',
22
			'bkpwp_automail_from',
23
			'bkpwp_domain',
24
			'bkpwp_domain_path',
25
			'bkpwp_easy_mode',
26
			'bkpwp_excludelists',
27
			'bkpwp_install_user',
28
			'bkpwp_listmax_backups',
29
			'bkpwp_max_backups',
30
			'bkpwp_presets',
31
			'bkpwp_reccurrences',
32
			'bkpwp_schedules',
33
			'bkpwp_calculation',
34
			'bkpwppath',
35
			'bkpwp_status_config',
36
			'bkpwp_status',
37
		);
38
39
		foreach ( $legacy_options as $option ) {
40
			delete_option( $option );
41
		}
42
43
		global $wp_roles;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
44
45
		$wp_roles->remove_cap( 'administrator', 'manage_backups' );
46
		$wp_roles->remove_cap( 'administrator', 'download_backups' );
47
48
		wp_clear_scheduled_hook( 'bkpwp_schedule_bkpwp_hook' );
49
50
	}
51
52
	// Version 1 to 2
53
	if ( get_option( 'hmbkp_plugin_version' ) && version_compare( '2.0', get_option( 'hmbkp_plugin_version' ), '>' ) ) {
54
55
		/**
56
		 * Setup a backwards compatible schedule
57
		 */
58
		$legacy_schedule = new Scheduled_Backup( 'backup' );
59
60
		// Backup type
61
		if ( ( defined( 'HMBKP_FILES_ONLY' ) && HMBKP_FILES_ONLY ) || get_option( 'hmbkp_files_only' ) ) {
62
			$legacy_schedule->set_type( 'file' );
63
		} elseif ( ( defined( 'HMBKP_DATABASE_ONLY' ) && HMBKP_DATABASE_ONLY ) || get_option( 'hmbkp_database_only' ) ) {
64
			$legacy_schedule->set_type( 'database' );
65
		} else {
66
			$legacy_schedule->set_type( 'complete' );
67
		}
68
69
		// Daily schedule time
70
		if ( defined( 'HMBKP_DAILY_SCHEDULE_TIME' ) && HMBKP_DAILY_SCHEDULE_TIME ) {
71
			$legacy_schedule->set_schedule_start_time( strtotime( HMBKP_DAILY_SCHEDULE_TIME ) );
72
		}
73
74
		// Backup schedule
75
		$legacy_schedule->set_reoccurrence( get_option( 'hmbkp_schedule_frequency', 'daily' ) );
76
77
		// Automatic backups disabled?
78
		if ( ( defined( 'HMBKP_DISABLE_AUTOMATIC_BACKUP' ) && HMBKP_DISABLE_AUTOMATIC_BACKUP ) || get_option( 'hmbkp_disable_automatic_backup' ) ) {
79
			$legacy_schedule->set_reoccurrence( 'manually' );
80
		}
81
82
		// Max backups
83
		if ( defined( 'HMBKP_MAX_BACKUPS' ) && is_numeric( HMBKP_MAX_BACKUPS ) ) {
84
			$legacy_schedule->set_max_backups( (int) HMBKP_MAX_BACKUPS );
85
		} else {
86
			$legacy_schedule->set_max_backups( (int) get_option( 'hmbkp_max_backups', 10 ) );
87
		}
88
89
		// Excludes
90
		if ( get_option( 'hmbkp_excludes' ) ) {
91
			$legacy_schedule->set_excludes( get_option( 'hmbkp_excludes' ) );
92
		}
93
94
		// Backup email
95
		if ( defined( 'HMBKP_EMAIL' ) && is_email( HMBKP_EMAIL ) ) {
96
			$legacy_schedule->set_service_options( 'HMBKP_Email_Service', array( 'email' => HMBKP_EMAIL ) );
97
		} elseif ( is_email( get_option( 'hmbkp_email_address' ) ) ) {
98
			$legacy_schedule->set_service_options( 'HMBKP_Email_Service', array( 'email' => get_option( 'hmbkp_email_address' ) ) );
99
		}
100
101
		// Set the archive filename to what it used to be
102
		$legacy_schedule->backup_filename = implode( '-', array( get_bloginfo( 'name' ), 'backup', current_time( 'Y-m-d-H-i-s' ) ) ) . '.zip';
103
104
		$legacy_schedule->save();
105
106
		$legacy_path = get_option( 'hmbkp_path' );
107
108
		if ( $legacy_path ) {
109
110
			// Prepend 'backup-' to the beginning of any legacy backups so they are picked up by the legacy schedule
111
			if ( $handle = opendir( $legacy_path ) ) {
112
				while ( false !== ( $file = readdir( $handle ) ) ) {
113
					if ( 'zip' === pathinfo( $file, PATHINFO_EXTENSION ) ) {
114
						rename( trailingslashit( $legacy_path ) . $file, trailingslashit( $legacy_path ) . 'backup-' . $file );
115
					}
116
				}
117
				closedir( $handle );
118
			}
119
120
			PATH::get_instance()->move_old_backups( $legacy_path );
121
122
		}
123
124
		// Remove the legacy options
125
		foreach ( array( 'hmbkp_database_only', 'hmbkp_files_only', 'hmbkp_max_backups', 'hmbkp_email_address', 'hmbkp_email', 'hmbkp_schedule_frequency', 'hmbkp_disable_automatic_backup' ) as $option_name ) {
126
			delete_option( $option_name );
127
		}
128
	}
129
130
	// Update from 2.x to 3.0
131
	if ( get_option( 'hmbkp_plugin_version' ) && version_compare( '2.0', get_option( 'hmbkp_plugin_version' ), '>' ) ) {
132
133
		// Remove the plugin data cache
134
		delete_transient( 'hmbkp_plugin_data' );
135
136
	}
137
138
	// Update to 3.1
139
	if ( get_option( 'hmbkp_plugin_version' ) && version_compare( '3.0', get_option( 'hmbkp_plugin_version' ), '>' ) ) {
140
141
		// Remove the plugin data cache
142
		delete_option( 'hmbkp_path' );
143
		delete_option( 'hmbkp_default_path' );
144
145
	}
146
147
	// update to 3.1.4
148
	if ( get_option( 'hmbkp_plugin_version' ) && version_compare( '3.1.4', get_option( 'hmbkp_plugin_version' ), '>' ) ) {
149
150
		$old_option_names = array(
151
			'HM\BackUpWordPressDropbox\Dropbox_Service'    => 'dropbox',
152
			'HMBKP_DX_Backup_Service'                      => 'dropbox',
153
			'HM\BackUpWordPressFTP\FTP_Backup_Service'     => 'ftp',
154
			'HMBKP_FTP_Backup_Service'                     => 'ftp',
155
			'HM\BackUpWordPressGDrive\Google_Drive_BackUp' => 'google-drive',
156
			'HMBKP_GDV_Backup_Service'                     => 'google-drive',
157
			'HM\BackUpWordPressRackspace\RackSpace_BackUp' => 'rackspace-cloud',
158
			'HMBKP_RSC_Backup_Service'                     => 'rackspace-cloud',
159
			'HM\BackUpWordPressS3\S3_Backup'               => 's3',
160
			'HMBKP_S3_Backup_Service'                      => 's3',
161
			'HM\BackUpWordPressWinAzure\WinAzure_Backup'   => 'azure',
162
			'HMBKP_WAZ_Backup_Service'                     => 'azure',
163
			'HM\BackUpWordPress\Email_Service'             => 'email',
164
		);
165
166
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
167
168
		// Get all schedule options with a SELECT query and delete them.
169
		$schedules = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s", 'hmbkp_schedule_%' ) );
170
171
		if ( 0 < count( $schedules ) ) {
172
173
			// Access each schedules settings to see if the addon settings names need to be be updated to the new naming convention which uses the service slug generated from the $name property.
174
			foreach ( $schedules as $schedule_id ) {
175
176
				// Load the settings for this schedule into an array
177
				// so we can loop through the different service settings
178
				$schedule_settings = get_option( $schedule_id );
179
180
				// Iterate over each schedule setting for this schedule and check its name against our array.
181
				foreach ( $schedule_settings as $key => $val ) {
182
					// Find the current element key in our control array and get its value. Set a new element in the settings array with the found value as its key. Aka rename the element key
183
					if ( array_key_exists( $key, $old_option_names ) ) {
184
185
						// move the value to our new key
186
						$schedule_settings[ $old_option_names[ $key ] ] = $schedule_settings[ $key ];
187
188
						unset( $schedule_settings[ $key ] );
189
190
					}
191
				}
192
193
				// Save back to the DB
194
				update_option( $schedule_id, $schedule_settings );
195
			}
196
		}
197
	}
198
199
	// Update to 3.1.5
200
	if ( get_option( 'hmbkp_plugin_version' ) && version_compare( '3.1.5', get_option( 'hmbkp_plugin_version' ), '>' ) ) {
201
202
		// Delete all transients
203
		$transients = array(
204
			'hmbkp_plugin_data',
205
			'hmbkp_directory_filesizes',
206
			'hmbkp_directory_filesizes_running',
207
			'hmbkp_wp_cron_test_beacon',
208
			'hm_backdrop',
209
		);
210
211
		array_map( 'delete_transient', $transients );
212
213
		// Clear duplicate schedules on multisite
214
		if ( is_multisite() ) {
215
216
			// get current blogs from DB
217
			$blogs = wp_get_sites();
218
219
			foreach ( $blogs as $blog ) {
220
221
				switch_to_blog( get_current_blog_id() );
222
223
				if ( is_main_site( get_current_blog_id() ) ) {
224
					continue;
225
				}
226
227
				global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
228
229
				// Get the schedule options
230
				$schedules = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s", 'hmbkp_schedule_%' ) );
231
232
				// clear schedules
233
				foreach ( array_map( function ( $item ) {
234
					return ltrim( $item, 'hmbkp_schedule_' );
235
				}, $schedules ) as $item ) {
236
					wp_clear_scheduled_hook( 'hmbkp_schedule_hook', array( 'id' => $item ) );
237
				}
238
239
				// delete options
240
				array_map( 'delete_option', $schedules );
241
242
				array_map( 'delete_option', array( 'hmbkp_enable_support', 'hmbkp_plugin_version', 'hmbkp_path', 'hmbkp_default_path', 'hmbkp_upsell' ) );
243
244
				// Delete all transients
245
				array_map( 'delete_transient', array( 'hmbkp_plugin_data', 'hmbkp_directory_filesizes', 'hmbkp_directory_filesize_running', 'timeout_hmbkp_wp_cron_test_beacon', 'hmbkp_wp_cron_test_beacon' ) );
246
247
			}
248
249
			restore_current_blog();
250
		}
251
	}
252
253
	// Update from 3.3.0
254
	if ( get_option( 'hmbkp_plugin_version' ) && version_compare( '3.3.0', get_option( 'hmbkp_plugin_version' ), '>' ) ) {
255
256
		$schedules = Schedules::get_instance();
257
258
		// Loop through all schedules and re-set the reccurrence to include hmbkp_
259
		foreach ( $schedules->get_schedules() as $schedule ) {
260
261
			$reoccurrence = $schedule->get_reoccurrence();
262
263
			if ( 'manually' !== $reoccurrence && strpos( $reoccurrence, 'hmbkp_' ) === 0 ) {
264
				$schedule->set_reoccurrence( substr( $reoccurrence, 6 ) );
265
			}
266
267
			$schedule->save();
268
269
		}
270
	}
271
272
	// Update from 3.3.4
273
	if ( get_option( 'hmbkp_plugin_version' ) && version_compare( '3.4.0', get_option( 'hmbkp_plugin_version' ), '>' ) ) {
274
		delete_transient( 'hmbkp_directory_filesizes' );
275
	}
276
277
	// Every update
278
	if ( get_option( 'hmbkp_plugin_version' ) && version_compare( Plugin::PLUGIN_VERSION, get_option( 'hmbkp_plugin_version' ), '>' ) ) {
279
280
		require_once( HMBKP_PLUGIN_PATH . 'classes/class-setup.php' );
281
282
		\HMBKP_Setup::deactivate();
283
284
		Path::get_instance()->protect_path( 'reset' );
285
286
	}
287
288
	// Update the stored version
289
	if ( get_option( 'hmbkp_plugin_version' ) !== Plugin::PLUGIN_VERSION ) {
290
		update_option( 'hmbkp_plugin_version', Plugin::PLUGIN_VERSION );
291
	}
292
293
}
294
295
/**
296
 * Setup the default backup schedules
297
 */
298
function setup_default_schedules() {
299
300
	$schedules = Schedules::get_instance();
301
302
	if ( $schedules->get_schedules() ) {
303
		return;
304
	}
305
306
	/**
307
	 * Schedule a database backup daily and store backups
308
	 * for the last 2 weeks
309
	 */
310
	$database_daily = new Scheduled_Backup( (string) time() );
311
	$database_daily->set_type( 'database' );
312
	$database_daily->set_schedule_start_time( determine_start_time( 'daily', array( 'hours' => '23', 'minutes' => '0' ) ) );
313
	$database_daily->set_reoccurrence( 'daily' );
314
	$database_daily->set_max_backups( 7 );
315
	$database_daily->save();
316
317
	/**
318
	 * Schedule a complete backup to run weekly and store backups for
319
	 * the last 3 months
320
	 */
321
	$complete_weekly = new Scheduled_Backup( (string) ( time() + 1 ) );
322
	$complete_weekly->set_type( 'complete' );
323
	$complete_weekly->set_schedule_start_time( determine_start_time( 'weekly', array( 'day_of_week' => 'sunday', 'hours' => '3', 'minutes' => '0' ) ) );
324
	$complete_weekly->set_reoccurrence( 'weekly' );
325
	$complete_weekly->set_max_backups( 3 );
326
	$complete_weekly->save();
327
328
	$schedules->refresh_schedules();
329
330
	add_action( 'admin_notices', function() {
331
		echo '<div id="hmbkp-warning" class="updated fade"><p><strong>' . __( 'BackUpWordPress has set up your default schedules.', 'backupwordpress' ) . '</strong> ' . __( 'By default BackUpWordPress performs a daily backup of your database and a weekly backup of your database &amp; files. You can modify these schedules.', 'backupwordpress' ) . '</p></div>';
332
	} );
333
334
}
335
336
add_action( 'admin_init', '\HM\BackUpWordPress\setup_default_schedules', 11 );
337
338
/**
339
 * Return an array of cron schedules
340
 *
341
 * @param $schedules
342
 * @return array $reccurrences
343
 */
344
function cron_schedules( $schedules = array() ) {
345
346
	$schedules += array(
347
		'hourly'      => array( 'interval' => HOUR_IN_SECONDS, 'display' => __( 'Once Hourly', 'backupwordpress' ) ),
348
		'twicedaily'  => array( 'interval' => 12 * HOUR_IN_SECONDS, 'display' => __( 'Twice Daily', 'backupwordpress' ) ),
349
		'daily'       => array( 'interval' => DAY_IN_SECONDS, 'display' => __( 'Once Daily', 'backupwordpress' ) ),
350
		'weekly'      => array( 'interval' => WEEK_IN_SECONDS, 'display' => __( 'Once Weekly', 'backupwordpress' ) ),
351
		'fortnightly' => array( 'interval' => 2 * WEEK_IN_SECONDS, 'display' => __( 'Once Every Two Weeks', 'backupwordpress' ) ),
352
		'monthly'     => array( 'interval' => 30 * DAY_IN_SECONDS, 'display' => __( 'Once Monthly', 'backupwordpress' ) ),
353
	);
354
355
	return $schedules;
356
}
357
358
add_filter( 'cron_schedules', '\HM\BackUpWordPress\cron_schedules' );
359
360
/**
361
 * Recursively delete a directory including
362
 * all the files and sub-directories.
363
 *
364
 * @param string $dir
365
 * @return bool
366
 * @return bool|WP_Error
367
 */
368
function rmdirtree( $dir ) {
369
370
	if ( false !== strpos( Path::get_home_path(), $dir ) ) {
371
		return new WP_Error( 'hmbkp_invalid_action_error', sprintf( __( 'You can only delete directories inside your WordPress installation', 'backupwordpress' ) ) );
372
	}
373
374
	if ( is_file( $dir ) ) {
375
		@unlink( $dir );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
376
	}
377
378
	if ( ! is_dir( $dir ) || ! is_readable( $dir ) ) {
379
		return false;
380
	}
381
382
	$files = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $dir, \RecursiveDirectoryIterator::SKIP_DOTS ), \RecursiveIteratorIterator::CHILD_FIRST, \RecursiveIteratorIterator::CATCH_GET_CHILD );
383
384
	foreach ( $files as $file ) {
385
386
		if ( $file->isDir() ) {
387
			@rmdir( $file->getPathname() );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
388
		} else {
389
			@unlink( $file->getPathname() );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
390
		}
391
	}
392
393
	@rmdir( $dir );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
394
395
	return true;
396
}
397
398
/**
399
 * Check if a backup is possible with regards to file
400
 * permissions etc.
401
 *
402
 * @return bool
403
 */
404
function is_backup_possible() {
405
406
	if ( ! wp_is_writable( Path::get_path() ) || ! is_dir( Path::get_path() ) ) {
407
		return false;
408
	}
409
410
	if ( ! is_readable( Path::get_root() ) ) {
411
		return false;
412
	}
413
414
	if ( disk_space_low() ) {
415
		return false;
416
	}
417
418
	if ( ! Requirement_Mysqldump_Command_Path::test() && ! Requirement_PDO::test() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \HM\BackUpWordPress\Requ...mp_Command_Path::test() of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression \HM\BackUpWordPress\Requirement_PDO::test() of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
419
		return false;
420
	}
421
422
	if ( ! Requirement_Zip_Command_Path::test() && ! Requirement_Zip_Archive::test() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \HM\BackUpWordPress\Requ...ip_Command_Path::test() of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
423
		return false;
424
	}
425
426
	return true;
427
}
428
429
/**
430
 * Get the max email attachment filesize
431
 *
432
 * Can be overridden by defining HMBKP_ATTACHMENT_MAX_FILESIZE
433
 *
434
 * return int the filesize
435
 */
436
function get_max_attachment_size() {
437
438
	$max_size = '10mb';
439
440
	if ( defined( 'HMBKP_ATTACHMENT_MAX_FILESIZE' ) && wp_convert_hr_to_bytes( HMBKP_ATTACHMENT_MAX_FILESIZE ) ) {
441
		$max_size = HMBKP_ATTACHMENT_MAX_FILESIZE;
442
	}
443
444
	return wp_convert_hr_to_bytes( $max_size );
445
446
}
447
448
function is_path_accessible( $dir ) {
449
450
	// Path is inaccessible
451
	if ( strpos( $dir, Path::get_home_path() ) === false ) {
452
		return false;
453
	}
454
455
	return true;
456
}
457
458
/**
459
 * List of schedules
460
 *
461
 * @return array
462
 */
463
function get_cron_schedules() {
464
	return cron_schedules();
465
}
466
467
/**
468
 * @param string $type the type of the schedule
469
 * @param array $times {
470
 *     An array of time arguments. Optional.
471
 *
472
 *     @type int $minutes          The minute to start the schedule on. Defaults to current time + 10 minutes. Accepts
473
 *                                 any valid `date( 'i' )` output.
474
 *     @type int $hours            The hour to start the schedule on. Defaults to current time + 10 minutes. Accepts
475
 *                                 any valid `date( 'G' )` output.
476
 *     @type string $day_of_week   The day of the week to start the schedule on. Defaults to current time + 10 minutes. Accepts
477
 *                                 any valid `date( 'l' )` output.
478
 *     @type int $day_of_month     The day of the month to start the schedule on. Defaults to current time + 10 minutes. Accepts
479
 *                                 any valid `date( 'j' )` output.
480
 *     @type int $now              The current time. Defaults to `time()`. Accepts any valid timestamp.
481
 *
482
 * }
483
 * @return int $timestamp Returns the resulting timestamp on success and Int 0 on failure
484
 */
485
function determine_start_time( $type, $times = array() ) {
486
487
	// Default to in 10 minutes
488
	if ( ! empty( $times['now'] ) ) {
489
		$default_timestamp = $times['now'] + 600;
490
491
	} else {
492
		$default_timestamp = time() + 600;
493
	}
494
495
	$default_times = array(
496
		'minutes'      => date( 'i', $default_timestamp ),
497
		'hours'        => date( 'G', $default_timestamp ),
498
		'day_of_week'  => date( 'l', $default_timestamp ),
499
		'day_of_month' => date( 'j', $default_timestamp ),
500
		'now'          => time(),
501
	);
502
503
	$args = wp_parse_args( $times, $default_times );
504
505
	$intervals = get_cron_schedules();
506
507
	// Allow the hours and minutes to be overwritten by a constant
508
	if ( defined( 'HMBKP_SCHEDULE_TIME' ) && HMBKP_SCHEDULE_TIME ) {
509
		$hm = HMBKP_SCHEDULE_TIME;
510
	} else { // The hour and minute that the schedule should start on
511
		$hm = $args['hours'] . ':' . $args['minutes'] . ':00';
512
	}
513
514
	switch ( $type ) {
515
516
		case 'hourly' :
517
		case 'daily' :
518
		case 'twicedaily':
519
520
			// The next occurance of the specified time
521
			$schedule_start = $hm;
522
			break;
523
524
		case 'weekly' :
525
		case 'fortnightly' :
526
527
			// The next day of the week at the specified time
528
			$schedule_start = $args['day_of_week'] . ' ' . $hm;
529
			break;
530
531
		case 'monthly' :
532
533
			// The occurance of the time on the specified day of the month
534
			$schedule_start = date( 'F', $args['now'] ) . ' ' . $args['day_of_month'] . ' ' . $hm;
535
536
			// If we've already gone past that day this month then we'll need to start next month
537
			if ( strtotime( $schedule_start, $args['now'] ) <= $args['now'] ) {
538
				$schedule_start = date( 'F', strtotime( '+ 1 month', $args['now'] ) )  . ' ' . $args['day_of_month'] . ' ' . $hm;
539
			}
540
541
			// If that's still in the past then we'll need to jump to next year
542
			if ( strtotime( $schedule_start, $args['now'] ) <= $args['now'] ) {
543
				$schedule_start = date( 'F', strtotime( '+ 1 month', $args['now'] ) )  . ' ' . $args['day_of_month'] . ' ' . date( 'Y', strtotime( '+ 1 year', $args['now'] ) ) . ' ' . $hm;
544
			}
545
546
			break;
547
548
		default :
549
550
			return 0;
551
552
	}
553
554
	$timestamp = strtotime( $schedule_start, $args['now'] );
555
556
	// Convert to UTC
557
	$timestamp -= get_option( 'gmt_offset' ) * 3600;
558
559
	// If the scheduled time already passed then keep adding the interval until we get to a future date
560
	while ( $timestamp <= $args['now'] ) {
561
		$timestamp += $intervals[ $type ]['interval'];
562
	}
563
564
	return $timestamp;
565
566
}
567
568
/**
569
 * Helper function for creating safe action URLs.
570
 *
571
 * @param string $action Callback function name.
572
 * @param array $query_args Additional GET params.
573
 *
574
 * @return string
575
 */
576
function admin_action_url( $action, array $query_args = array() ) {
577
578
	$query_args = array_merge( $query_args, array( 'action' => 'hmbkp_' . $action ) );
579
580
	return esc_url( wp_nonce_url( add_query_arg( $query_args, admin_url( 'admin-post.php' ) ), 'hmbkp_' . $action, 'hmbkp-' . $action . '_nonce' ) );
581
}
582
583
/**
584
 * OS dependant way to pipe stderr to null
585
 *
586
 * @return string The exec argument to pipe stderr to null
587
 */
588
function ignore_stderr() {
589
590
	// If we're on Windows
591
	if ( DIRECTORY_SEPARATOR == '\\' ) {
592
		return '2>nul';
593
	}
594
595
	// Or Unix
596
	return '2>/dev/null';
597
598
}
599
600
/**
601
 * Return the contents of `$directory` as a single depth list ordered by total filesize.
602
 *
603
 * Will schedule background threads to recursively calculate the filesize of subdirectories.
604
 * The total filesize of each directory and subdirectory is cached in a transient for 1 week.
605
 *
606
 * @param string $directory The directory to list
607
 *
608
 * @todo doesn't really belong in this class, should just be a function
609
 * @return array            returns an array of files ordered by filesize
610
 */
611
function list_directory_by_total_filesize( $directory, Excludes $excludes ) {
612
613
	$files = $files_with_no_size = $empty_files = $files_with_size = $unreadable_files = array();
614
615
	if ( ! is_dir( $directory ) ) {
616
		return $files;
617
	}
618
619
	$finder = new \Symfony\Component\Finder\Finder();
620
	$finder->followLinks();
621
	$finder->ignoreDotFiles( false );
622
	$finder->ignoreUnreadableDirs();
623
	$finder->depth( '== 0' );
624
625
	$site_size = new Site_Size( 'file', $excludes );
626
627
	$files = $finder->in( $directory );
628
629
	foreach ( $files as $entry ) {
630
631
		// Get the total filesize for each file and directory
632
		$filesize = $site_size->filesize( $entry );
633
634
		if ( $filesize ) {
635
636
			// If there is already a file with exactly the same filesize then let's keep increasing the filesize of this one until we don't have a clash
637
			while ( array_key_exists( $filesize, $files_with_size ) ) {
638
				$filesize ++;
639
			}
640
641
			$files_with_size[ $filesize ] = $entry;
642
643
		} elseif ( 0 === $filesize ) {
644
			$empty_files[] = $entry;
645
		} else {
646
			$files_with_no_size[] = $entry;
647
		}
648
	}
649
650
	// Sort files by filesize, largest first
651
	krsort( $files_with_size );
652
653
	// Add 0 byte files / directories to the bottom
654
	$files = $files_with_size + array_merge( $empty_files, $unreadable_files );
655
656
	// Add directories that are still calculating to the top
657
	if ( $files_with_no_size ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $files_with_no_size of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
658
659
		// We have to loop as merging or concatenating the array would re-flow the keys which we don't want because the filesize is stored in the key
660
		foreach ( $files_with_no_size as $entry ) {
661
			array_unshift( $files, $entry );
662
		}
663
	}
664
665
	return $files;
666
667
}
668