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.

Issues (217)

Security Analysis    no vulnerabilities found

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

classes/class-scheduled-backup.php (15 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace HM\BackUpWordPress;
4
5
/**
6
 * The Backup Scheduler
7
 *
8
 * Handles everything related to managing and running a backup schedule
9
 *
10
 * @uses Backup
11
 * @uses
12
 */
13
class Scheduled_Backup {
14
15
	/**
16
	 * The unique schedule id
17
	 *
18
	 * @var string
19
	 * @access private
20
	 */
21
	private $id = '';
22
23
	/**
24
	 * The slugified version of the schedule name
25
	 *
26
	 * @var string
27
	 * @access private
28
	 */
29
	private $slug = '';
30
31
	/**
32
	 * The raw schedule options from the database
33
	 *
34
	 * @var array
35
	 * @access private
36
	 */
37
	private $options = array(
38
		'max_backups'   => 3,
39
		'excludes'      => array(),
40
		'type'          => 'complete',
41
		'reoccurrence'  => 'manually',
42
	);
43
44
	/**
45
	 * Setup the schedule object
46
	 * Loads the options from the database and populates properties
47
	 *
48
	 * @param string $id
49
	 *
50
	 * @throws Exception
51
	 */
52
53
	public function __construct( $id ) {
54
55
		// Verify the schedule id
56
		if ( ! is_string( $id ) || ! trim( $id ) ) {
57
			throw new \Exception( 'Argument 1 for ' . __METHOD__ . ' must be a non-empty string' );
58
		}
59
60
		// Store id for later
61
		$this->id = $id;
62
63
		// Load the options
64
		$this->options = array_merge( $this->options, array_filter( (array) get_option( 'hmbkp_schedule_' . $this->get_id() ) ) );
65
66
		if ( defined( 'HMBKP_SCHEDULE_START_TIME' ) && strtotime( 'HMBKP_SCHEDULE_START_TIME' ) ) {
67
			$this->set_schedule_start_time( strtotime( 'HMBKP_SCHEDULE_START_TIME' ) );
68
		}
69
70
		// Setup the schedule if it isn't set
71
		if ( ( ! $this->is_cron_scheduled() && $this->get_reoccurrence() !== 'manually' ) ) {
72
			$this->schedule();
73
		}
74
75
		$this->backup_filename = implode( '-', array(
0 ignored issues
show
The property backup_filename does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
76
			sanitize_title( str_ireplace( array(
77
				'http://',
78
				'https://',
79
				'www',
80
			), '', home_url() ) ),
81
			$this->get_id(),
82
			$this->get_type(),
83
			current_time( 'Y-m-d-H-i-s' ),
84
		) ) . '.zip';
85
86
		$this->database_dump_filename = implode( '-', array(
0 ignored issues
show
The property database_dump_filename does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
87
			'database',
88
			sanitize_title( str_ireplace( array( 'http://', 'https://', 'www' ), '', home_url() ) ),
89
			$this->get_id(),
90
		) ) . '.sql';
91
92
		$this->status = new Backup_Status( $this->get_id() );
0 ignored issues
show
The property status does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
93
94
	}
95
96
	/**
97
	 * Get the id for this schedule
98
	 */
99
	public function get_id() {
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
100
		return esc_attr( $this->id );
101
	}
102
103
	/**
104
	 * Get a slugified version of name
105
	 */
106
	public function get_slug() {
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
107
108
		// We cache slug in $this to save expensive calls to sanitize_title
109
		if ( ! empty( $this->slug ) ) {
110
			return $this->slug;
111
		}
112
113
		return $this->slug = sanitize_title( $this->get_name() );
114
115
	}
116
117
	/**
118
	 * Returns the given option value
119
	 *
120
	 * @param $option_name
121
	 * @return mixed The option value
122
	 */
123
	public function get_schedule_option( $option_name ) {
124
		if ( isset( $this->options[ $option_name ] ) ) {
125
			return $this->options[ $option_name ];
126
		}
127
	}
128
129
	/**
130
	 * Get the name of this backup schedule
131
	 *
132
	 * @return string
133
	 */
134
	public function get_name() {
135
		return ucwords( $this->get_type() ) . ' ' . $this->get_reoccurrence();
136
	}
137
138
	/**
139
	 * Get the type of backup
140
	 *
141
	 * @return string
142
	 */
143
	public function get_type() {
144
		return $this->options['type'];
145
	}
146
147
	/**
148
	 * Set the type of backup
149
	 *
150
	 * @param string $type
151
	 */
152
	public function set_type( $type ) {
153
		if ( ! isset( $this->options['type'] ) || $this->options['type'] !== $type ) {
154
			$this->options['type'] = $type;
155
		}
156
	}
157
158
	/**
159
	 * Get the exclude rules
160
	 *
161
	 * @return array
0 ignored issues
show
Should the return type not be Excludes?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
162
	 */
163
	public function get_excludes() {
164
		return new Excludes( $this->options['excludes'] );
165
	}
166
167
	/**
168
	 * Set the exclude rules
169
	 *
170
	 * @param mixed $excludes A comma separated list or array of exclude rules
0 ignored issues
show
There is no parameter named $excludes. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
171
	 * @param bool $append Whether to replace or append to existing rules
172
	 *
173
	 * @return string
0 ignored issues
show
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
174
	 */
175
	public function set_excludes( $exclude_rules, $append = false ) {
176
177
		// Normalize the exclude rules before we save them
178
		$excludes = new Excludes;
179
		$excludes = $excludes->normalize( (array) $exclude_rules );
180
181
		// If these are valid excludes and they are different save them
182
		if ( empty( $this->options['excludes'] ) || $this->options['excludes'] !== $excludes ) {
183
			$this->options['excludes'] = $append && ! empty( $this->options['excludes'] ) ? array_merge( (array) $this->options['excludes'], (array) $excludes ) : (array) $excludes;
184
		}
185
186
	}
187
188
	/**
189
	 * Get the maximum number of backups to keep
190
	 *
191
	 * @return int
192
	 */
193
	public function get_max_backups() {
194
		return (int) $this->options['max_backups'];
195
	}
196
197
	/**
198
	 * Set the maximum number of backups to keep
199
	 *
200
	 * @param int $max
201
	 *
202
	 * @return WP_Error|boolean
0 ignored issues
show
Should the return type not be WP_Error|boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
203
	 */
204
	public function set_max_backups( $max ) {
205
		$this->options['max_backups'] = $max;
206
	}
207
208
	public function get_status() {
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
209
		return $this->status;
210
	}
211
212
	/**
213
	 * Back compat with old set_status mathod
214
	 *
215
	 * @deprecated 3.4 Backup->status->set_status()
216
	 */
217
	public function set_status( $message ) {
218
		_deprecated_function( __FUNCTION__, '3.4', 'Backup->status->set_status()' );
219
		$this->status->set_status( $message );
220
	}
221
222
	/**
223
	 * Get the array of services options for this schedule
224
	 *
225
	 * @param      $service
226
	 * @param null $option
227
	 *
228
	 * @return array
229
	 */
230
	public function get_service_options( $service, $option = null ) {
231
232
		if ( ! is_null( $option ) ) {
233
234
			if ( isset( $this->options[ $service ][ $option ] ) ) {
235
				return $this->options[ $service ][ $option ];
236
			}
237
238
			return array();
239
240
		}
241
242
		if ( isset( $this->options[ $service ] ) ) {
243
			return $this->options[ $service ];
244
		}
245
246
		return array();
247
248
	}
249
250
	/**
251
	 * Set the service options for this schedule
252
	 *
253
	 * @param $service
254
	 * @param array $options
255
	 */
256
	public function set_service_options( $service, array $options ) {
257
		$this->options[ $service ] = $options;
258
	}
259
260
	/**
261
	 * Get the start time for the schedule
262
	 *
263
	 * @return int timestamp || 0 for manual only schedules
0 ignored issues
show
Should the return type not be integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
264
	 */
265
	public function get_schedule_start_time( $gmt = true ) {
266
267
		if ( 'manually' === $this->get_reoccurrence() ) {
268
			return 0;
269
		}
270
271
		if ( ! $gmt ) {
272
			$offset = get_option( 'gmt_offset' ) * 3600;
273
		} else {
274
			$offset = 0;
275
		}
276
277
		if ( ! empty( $this->options['schedule_start_time'] ) ) {
278
			return $this->options['schedule_start_time'] + $offset;
279
		}
280
281
		$this->set_schedule_start_time( time() );
282
283
		return time() + $offset;
284
285
	}
286
287
	/**
288
	 * Set the schedule start time.
289
	 *
290
	 * @param Int $time A valid timestamp
291
	 */
292
	public function set_schedule_start_time( $time ) {
293
294
		$this->options['schedule_start_time'] = $time;
295
296
		$this->schedule();
297
298
	}
299
300
	/**
301
	 * Get the schedule reoccurrence
302
	 *
303
	 */
304
	public function get_reoccurrence() {
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
305
		return $this->options['reoccurrence'];
306
	}
307
308
	/**
309
	 * Set the schedule reoccurrence
310
	 *
311
	 * @param string $reoccurrence
312
	 *
313
	 * @return \WP_Error|null|boolean
314
	 */
315
	public function set_reoccurrence( $reoccurrence ) {
316
317
		$hmbkp_schedules = cron_schedules();
318
319
		// Check it's valid
320
		if ( ! is_string( $reoccurrence ) || ! trim( $reoccurrence ) || ( ! in_array( $reoccurrence, array_keys( $hmbkp_schedules ) ) ) && 'manually' !== $reoccurrence ) {
321
			return new \WP_Error( 'hmbkp_invalid_argument_error', sprintf( __( 'Argument 1 for %s must be a valid cron recurrence or "manually"', 'backupwordpress' ), __METHOD__ ) );
322
		}
323
324
		// If the recurrence is already set to the same thing then there's no need to continue
325
		if ( isset( $this->options['reoccurrence'] ) && $this->options['reoccurrence'] === $reoccurrence && $this->is_cron_scheduled() ) {
326
			return;
327
		}
328
329
		$this->options['reoccurrence'] = $reoccurrence;
330
331
		if ( 'manually' === $reoccurrence ) {
332
			$this->unschedule();
333
334
		} else {
335
			$this->schedule();
336
		}
337
338
		return true;
339
340
	}
341
342
	/**
343
	 * Get the interval between backups
344
	 *
345
	 * @return int
346
	 */
347
	public function get_interval() {
348
349
		$hmbkp_schedules = cron_schedules();
350
351
		if ( 'manually' === $this->get_reoccurrence() ) {
352
			return 0;
353
		}
354
355
		return $hmbkp_schedules[ $this->get_reoccurrence() ]['interval'];
356
357
	}
358
359
	/**
360
	 * Get the next occurrence of this scheduled backup
361
	 *
362
	 */
363
	public function get_next_occurrence( $gmt = true ) {
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
364
365
		$time = wp_next_scheduled( 'hmbkp_schedule_hook', array( 'id' => $this->get_id() ) );
366
367
		if ( ! $time ) {
368
			$time = 0;
369
		}
370
371
		if ( ! $gmt ) {
372
			$time += get_option( 'gmt_offset' ) * 3600;
373
		}
374
375
		return $time;
376
377
	}
378
379
	public function is_cron_scheduled() {
380
		return (bool) $this->get_next_occurrence();
381
	}
382
383
	/**
384
	 * Schedule the backup cron
385
	 *
386
	 */
387
	public function schedule() {
388
389
		// Clear any existing hooks
390
		$this->unschedule();
391
392
		$schedule_timestamp = $this->get_schedule_start_time();
393
394
		wp_schedule_event( $schedule_timestamp, $this->get_reoccurrence(), 'hmbkp_schedule_hook', array( 'id' => $this->get_id() ) );
395
396
	}
397
398
399
	/**
400
	 * Unschedule the backup cron.
401
	 *
402
	 * @return void
403
	 */
404
	public function unschedule() {
405
		wp_clear_scheduled_hook( 'hmbkp_schedule_hook', array( 'id' => $this->get_id() ) );
406
	}
407
408
	/**
409
	 * Run the backup
410
	 *
411
	 */
412
	public function run() {
413
414
		// Don't run if this schedule is already running
415
		if ( $this->status->is_started() ) {
416
			return;
417
		}
418
419
		// Setup our Site Backup Object
420
		$backup = new Backup( $this->get_backup_filename(), $this->get_database_dump_filename() );
421
		$backup->set_type( $this->get_type() );
422
		$backup->set_excludes( $this->get_excludes() );
423
		$backup->set_status( $this->status );
424
425
		$this->do_action( 'hmbkp_backup_started', $backup );
426
427
		$this->status->start( $this->get_backup_filename(), __( 'Starting backup...', 'backupwordpress' ) );
428
429
		$this->status->set_status( __( 'Deleting old backups...', 'backupwordpress' ) );
430
431
		// Delete old backups now in-case we fatal error during the backup process
432
		$this->delete_old_backups();
433
434
		$backup->run();
435
436
		$errors = array_merge( $backup->errors, $backup->warnings );
437
		$notices = array();
438
		foreach ( $errors as $key => $error ) {
439
			$key = str_replace( array( __NAMESPACE__ . '\\', '_File_Backup_Engine', '_Database_Backup_Engine' ), array( '', '', '' ), $key );
440
			$notices[] = $key . ': ' . implode( ', ', $error );
441
		}
442
		Notices::get_instance()->set_notices( 'backup_errors', $notices );
443
444
		$this->status->set_status( __( 'Deleting old backups...', 'backupwordpress' ) );
445
446
		// Delete old backups again
447
		$this->delete_old_backups();
448
449
		$this->do_action( 'hmbkp_backup_complete', $backup );
450
451
		$this->status->finish();
452
		$this->update_average_schedule_run_time( $this->status->get_start_time(), time() );
453
454
	}
455
456
	public function get_backup_filename() {
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
457
458
		if ( $this->status->is_started() ) {
459
			$this->backup_filename = $this->status->get_backup_filename();
460
		}
461
462
		return $this->backup_filename;
463
	}
464
465
	public function get_database_dump_filename() {
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
466
		return $this->database_dump_filename;
467
	}
468
469
	/**
470
	 * Hook into the actions fired in the Backup class and set the status
471
	 *
472
	 * @param $action
473
	 */
474
	public function do_action( $action, Backup $backup ) {
475
476
		// Pass the actions to all the services
477
		// Todo should be decoupled into the service class
478
		foreach ( Services::get_services( $this ) as $service ) {
479
			if ( is_wp_error( $service ) ) {
480
				return $service;
481
			}
482
			$service->action( $action, $backup );
483
		}
484
485
	}
486
487
	/**
488
	 * Calculate schedule run time.
489
	 *
490
	 * @param int Timestamp $end
491
	 */
492
	public function update_average_schedule_run_time( $start, $end ) {
493
494
		if ( $end <= $start ) {
495
			// Something went wrong, ignore.
496
			return;
497
		}
498
499
		$diff = (int) abs( $end - $start );
500
501
		if ( isset( $this->options['duration_total'] ) && isset( $this->options['backup_run_count'] ) ) {
502
503
			$this->options['duration_total'] += $diff;
504
			$this->options['backup_run_count'] ++;
505
506
		} else {
507
508
			$this->options['duration_total'] = $diff;
509
			$this->options['backup_run_count'] = 1;
510
511
		}
512
513
		$this->save();
514
	}
515
516
	/**
517
	 * Calculates the average run time for this schedule.
518
	 *
519
	 * @return string
520
	 */
521
	public function get_schedule_average_duration() {
522
523
		$duration = 'Unknown';
524
525
		if ( ! isset( $this->options['duration_total'] ) || ! isset( $this->options['backup_run_count'] ) ) {
526
			return $duration;
527
		}
528
529
		if ( 0 === (int) $this->options['backup_run_count'] ) {
530
			return $duration;
531
		}
532
533
		$average_run_time = (int) $this->options['duration_total'] / (int) $this->options['backup_run_count'];
534
535
		if ( $average_run_time < HOUR_IN_SECONDS ) {
536
537
			$mins = round( $average_run_time / MINUTE_IN_SECONDS );
538
539
			if ( $mins <= 1 ) {
540
				$mins = 1;
541
			}
542
543
			/* translators: min=minute */
544
			$duration = sprintf( _n( '%s min', '%s mins', $mins, 'backupwordpress' ), $mins );
545
546
		} elseif ( $average_run_time < DAY_IN_SECONDS && $average_run_time >= HOUR_IN_SECONDS ) {
547
548
			$hours = round( $average_run_time / HOUR_IN_SECONDS );
549
550
			if ( $hours <= 1 ) {
551
				$hours = 1;
552
			}
553
554
			$duration = sprintf( _n( '%s hour', '%s hours', $hours, 'backupwordpress' ), $hours );
555
		}
556
557
		return $duration;
558
	}
559
560
	/**
561
	 * Get the backups created by this schedule
562
	 *
563
	 * @todo   look into using recursiveDirectoryIterator and recursiveRegexIterator
564
	 * @return string[] - file paths of the backups
565
	 */
566
	public function get_backups() {
567
568
		$files = array();
569
570
		if ( $handle = @opendir( Path::get_path() ) ) {
571
572
			while ( false !== ( $file = readdir( $handle ) ) ) {
573
574
				if ( pathinfo( $file, PATHINFO_EXTENSION ) === 'zip' && strpos( $file, $this->get_id() ) !== false && ( isset( $this->status ) && $this->get_backup_filename() !== $file ) ) {
575
					$files[ @filemtime( trailingslashit( Path::get_path() ) . $file ) ] = trailingslashit( Path::get_path() ) . $file;
576
				}
577
			}
578
579
			closedir( $handle );
580
581
		}
582
583
		krsort( $files );
584
585
		return $files;
586
587
	}
588
589
	/**
590
	 * Delete old backups
591
	 *
592
	 * @access private
593
	 */
594
	public function delete_old_backups() {
595
596
		if ( count( $this->get_backups() ) <= $this->get_max_backups() ) {
597
			return;
598
		}
599
600
		array_map( array( $this, 'delete_backup' ), array_slice( $this->get_backups(), $this->get_max_backups() ) );
601
602
	}
603
604
	/**
605
	 * Delete a specific back up file created by this schedule
606
	 *
607
	 * @param string $filepath
608
	 *
609
	 * @return \WP_Error|boolean
610
	 */
611
	public function delete_backup( $filepath ) {
612
613
		// Check that it's a valid filepath
614
		if ( empty( $filepath ) || ! is_string( $filepath ) ) {
615
			return new \WP_Error( 'hmbkp_empty_string_error', sprintf( __( 'Argument 1 for %s must be a non-empty string', 'backupwordpress' ), __METHOD__ ) );
616
		}
617
618
		// Make sure it exists
619
		if ( ! file_exists( $filepath ) ) {
620
			return new \WP_Error( 'hmbkp_file_error', sprintf( __( '%s doesn\'t exist', 'backupwordpress' ), $filepath ) );
621
		}
622
623
		// Make sure it was created by this schedule
624
		if ( strpos( $filepath, $this->get_id() ) === false ) {
625
			return new \WP_Error( 'hmbkp_backup_error', __( 'That backup wasn\'t created by this schedule', 'backupwordpress' ) );
626
		}
627
628
		unlink( $filepath );
629
630
		return true;
631
632
	}
633
634
	/**
635
	 * Delete all back up files created by this schedule
636
	 *
637
	 */
638
	public function delete_backups() {
639
		array_map( array( $this, 'delete_backup' ), $this->get_backups() );
640
	}
641
642
	/**
643
	 * Save the schedules options.
644
	 *
645
	 */
646
	public function save() {
647
648
		// Only save them if they have changed
649
		if ( get_option( 'hmbkp_schedule_' . $this->get_id() ) !== $this->options ) {
650
			update_option( 'hmbkp_schedule_' . $this->get_id(), $this->options );
651
652
			// Delete the schedule cache.
653
			delete_transient( 'hmbkp_schedules' );
654
		}
655
656
	}
657
658
	/**
659
	 * Cancel this schedule
660
	 *
661
	 * Cancels the cron job, removes the schedules options
662
	 * and optionally deletes all backups created by
663
	 * this schedule.
664
	 *
665
	 */
666
	public function cancel( $delete_backups = false ) {
667
668
		// Delete the schedule options
669
		delete_option( 'hmbkp_schedule_' . $this->get_id() );
670
671
		// Delete the schedule cache.
672
		delete_transient( 'hmbkp_schedules' );
673
674
		// Clear any existing schedules
675
		$this->unschedule();
676
677
		// Delete it's backups
678
		if ( $delete_backups ) {
679
			$this->delete_backups();
680
		}
681
682
	}
683
}
684