BackUpWordPress - Version 3.4.0

Version Description

= 3.4 =

This version introduces a major refactoring code responsible for the files and database backup. We made sure to write unit tests for the new code, and we have tested it on several user's sites. It fixes a lot of old bugs, and Windows users should see major improvments to reliablity.

= 3.3.4 =

  • WordPress 4.4 compatibility.

= 3.3.1 =

  • Fixes a bug that would prevent downloading backups since 3.3.0 - please update.

= 3.2.5 =

  • Security fixes related to add_query_arg

= 3.2.1 =

  • Important bug fixes. Please upgrade to this version to avoid incomplete or broken backups.

= 3.1.3 =

  • Fixes backwards compatibility for add-ons and avoids a Fatal Error. Please upgrade straight to this version before upgrading your add-ons.

= 3.0.4 =

  • Fixes a few minor bugs. Immediate update is recommended.

= 3.0.2 =

  • Important: we have dropped support for PHP 5.2, you will not be able to activate BackUpWordPress on a server running PHP versions older than PHP 5.3.29

= 3.0.1 =

  • This is a critical update. Fixes a bug in the core backup library. Please update immediately.
Download this release

Release Info

Developer pauldewouters
Plugin Icon 128x128 BackUpWordPress
Version 3.4.0
Comparing to
See all releases

Code changes from version 3.3.4 to 3.4.0

Files changed (113) hide show
  1. admin/actions.php +101 -114
  2. admin/backups-table.php +8 -2
  3. admin/backups.php +10 -7
  4. admin/constants.php +9 -3
  5. admin/menu.php +12 -14
  6. admin/page.php +7 -1
  7. admin/schedule-form-excludes.php +72 -65
  8. admin/schedule-form.php +18 -11
  9. admin/schedule-sentence.php +25 -17
  10. admin/schedule-settings.php +10 -8
  11. assets/hmbkp.js +10 -46
  12. assets/hmbkp.min.css +1 -1
  13. assets/hmbkp.min.js +24 -1
  14. backupwordpress.php +1 -1
  15. classes/backup/class-backup-engine-database-imysqldump.php +82 -0
  16. classes/backup/class-backup-engine-database-mysqldump.php +215 -0
  17. classes/backup/class-backup-engine-database.php +218 -0
  18. classes/backup/class-backup-engine-file-zip-archive.php +55 -0
  19. classes/backup/class-backup-engine-file-zip.php +166 -0
  20. classes/backup/class-backup-engine-file.php +112 -0
  21. classes/backup/class-backup-engine.php +197 -0
  22. classes/backup/class-backup-status.php +120 -0
  23. classes/backup/class-backup-utilities.php +129 -0
  24. classes/backup/class-backup.php +200 -0
  25. classes/class-backup.php +0 -1663
  26. classes/class-backupwordpress-wp-cli-command.php +22 -32
  27. classes/class-email-service.php +3 -3
  28. classes/class-excludes.php +186 -0
  29. classes/class-path.php +109 -27
  30. classes/class-plugin.php +21 -6
  31. classes/class-requirement.php +18 -18
  32. classes/class-schedule.php +0 -1173
  33. classes/class-scheduled-backup.php +680 -0
  34. classes/class-service.php +1 -1
  35. classes/class-services.php +1 -1
  36. classes/class-site-size.php +231 -0
  37. classes/class-webhook-service.php +2 -2
  38. classes/class-wpremote-webhook-service.php +1 -1
  39. composer.json +25 -0
  40. composer.lock +120 -0
  41. functions/core.php +132 -46
  42. functions/interface.php +59 -100
  43. languages/backupwordpress.pot +194 -281
  44. readme.md +52 -0
  45. readme.txt +55 -1
  46. vendor/composer/installed.json +11 -8
  47. vendor/symfony/finder/Adapter/AbstractAdapter.php +4 -0
  48. vendor/symfony/finder/Adapter/AbstractFindAdapter.php +4 -0
  49. vendor/symfony/finder/Adapter/AdapterInterface.php +2 -0
  50. vendor/symfony/finder/Adapter/BsdFindAdapter.php +4 -0
  51. vendor/symfony/finder/Adapter/GnuFindAdapter.php +4 -0
  52. vendor/symfony/finder/Adapter/PhpAdapter.php +4 -0
  53. vendor/symfony/finder/CHANGELOG.md +5 -0
  54. vendor/symfony/finder/Exception/AdapterFailureException.php +4 -0
  55. vendor/symfony/finder/Exception/OperationNotPermitedException.php +4 -0
  56. vendor/symfony/finder/Exception/ShellCommandFailureException.php +4 -0
  57. vendor/symfony/finder/Expression/Expression.php +2 -0
  58. vendor/symfony/finder/Expression/Glob.php +2 -0
  59. vendor/symfony/finder/Expression/Regex.php +2 -0
  60. vendor/symfony/finder/Expression/ValueInterface.php +2 -0
  61. vendor/symfony/finder/Finder.php +125 -18
  62. vendor/symfony/finder/Iterator/FilePathsIterator.php +4 -0
  63. vendor/symfony/finder/Iterator/FilecontentFilterIterator.php +1 -19
  64. vendor/symfony/finder/Iterator/FilenameFilterIterator.php +3 -23
  65. vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php +51 -3
  66. vendor/symfony/finder/Iterator/PathFilterIterator.php +1 -19
  67. vendor/symfony/finder/Iterator/SortableIterator.php +3 -3
  68. vendor/symfony/finder/LICENSE +1 -1
  69. vendor/symfony/finder/Shell/Command.php +4 -0
  70. vendor/symfony/finder/Shell/Shell.php +4 -0
  71. vendor/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php +0 -236
  72. vendor/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php +0 -327
  73. vendor/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php +0 -144
  74. vendor/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php +0 -103
  75. vendor/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php +0 -104
  76. vendor/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php +0 -98
  77. vendor/symfony/finder/Symfony/Component/Finder/CHANGELOG.md +0 -34
  78. vendor/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php +0 -98
  79. vendor/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php +0 -53
  80. vendor/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php +0 -81
  81. vendor/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php +0 -19
  82. vendor/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php +0 -46
  83. vendor/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php +0 -23
  84. vendor/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php +0 -19
  85. vendor/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php +0 -45
  86. vendor/symfony/finder/Symfony/Component/Finder/Expression/Expression.php +0 -146
  87. vendor/symfony/finder/Symfony/Component/Finder/Expression/Glob.php +0 -157
  88. vendor/symfony/finder/Symfony/Component/Finder/Expression/Regex.php +0 -321
  89. vendor/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php +0 -60
  90. vendor/symfony/finder/Symfony/Component/Finder/Finder.php +0 -840
  91. vendor/symfony/finder/Symfony/Component/Finder/Glob.php +0 -103
  92. vendor/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php +0 -63
  93. vendor/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php +0 -60
  94. vendor/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php +0 -47
  95. vendor/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php +0 -55
  96. vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php +0 -131
  97. vendor/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php +0 -55
  98. vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php +0 -76
  99. vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php +0 -67
  100. vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php +0 -49
  101. vendor/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php +0 -66
  102. vendor/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php +0 -74
  103. vendor/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php +0 -126
  104. vendor/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php +0 -59
  105. vendor/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php +0 -82
  106. vendor/symfony/finder/Symfony/Component/Finder/LICENSE +0 -19
  107. vendor/symfony/finder/Symfony/Component/Finder/README.md +0 -53
  108. vendor/symfony/finder/Symfony/Component/Finder/Shell/Command.php +0 -294
  109. vendor/symfony/finder/Symfony/Component/Finder/Shell/Shell.php +0 -97
  110. vendor/symfony/finder/Symfony/Component/Finder/SplFileInfo.php +0 -77
  111. vendor/symfony/finder/Symfony/Component/Finder/composer.json +0 -31
  112. vendor/symfony/finder/Symfony/Component/Finder/phpunit.xml.dist +0 -28
  113. vendor/symfony/finder/composer.json +5 -2
admin/actions.php CHANGED
@@ -1,13 +1,15 @@
1
  <?php
2
 
 
 
3
  /**
4
  * Delete the backup and then redirect back to the backups page
5
  */
6
- function hmbkp_request_delete_backup() {
7
 
8
  check_admin_referer( 'hmbkp_delete_backup', 'hmbkp_delete_backup_nonce' );
9
 
10
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( urldecode( $_GET['hmbkp_schedule_id'] ) ) );
11
 
12
  $deleted = $schedule->delete_backup( sanitize_text_field( base64_decode( $_GET['hmbkp_backup_archive'] ) ) );
13
 
@@ -15,54 +17,54 @@ function hmbkp_request_delete_backup() {
15
  wp_die( $deleted->get_error_message() );
16
  }
17
 
18
- wp_safe_redirect( hmbkp_get_settings_url(), 303 );
19
 
20
  die;
21
 
22
  }
23
- add_action( 'admin_post_hmbkp_request_delete_backup', 'hmbkp_request_delete_backup' );
24
 
25
  /**
26
  * Enable support and then redirect back to the backups page
27
  */
28
- function hmbkp_request_enable_support() {
29
 
30
  check_admin_referer( 'hmbkp_enable_support', 'hmbkp_enable_support_nonce' );
31
 
32
  update_option( 'hmbkp_enable_support', true );
33
 
34
- wp_safe_redirect( hmbkp_get_settings_url(), 303 );
35
 
36
  die;
37
 
38
  }
39
- add_action( 'admin_post_hmbkp_request_enable_support', 'hmbkp_request_enable_support' );
40
 
41
  /**
42
  * Delete a schedule and all it's backups and then redirect back to the backups page
43
  */
44
- function hmbkp_request_delete_schedule() {
45
 
46
  check_admin_referer( 'hmbkp_delete_schedule', 'hmbkp_delete_schedule_nonce' );
47
 
48
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( urldecode( $_GET['hmbkp_schedule_id'] ) ) );
49
  $schedule->cancel( true );
50
 
51
- wp_safe_redirect( hmbkp_get_settings_url(), 303 );
52
 
53
  die;
54
 
55
  }
56
- add_action( 'admin_post_hmbkp_request_delete_schedule', 'hmbkp_request_delete_schedule' );
57
 
58
  /**
59
  * Perform a manual backup
60
  *
61
  * Handles ajax requests as well as standard GET requests
62
  */
63
- function hmbkp_request_do_backup() {
64
 
65
- if ( empty( $_POST['hmbkp_schedule_id'] ) ) {
66
  die;
67
  }
68
 
@@ -72,41 +74,29 @@ function hmbkp_request_do_backup() {
72
  check_admin_referer( 'hmbkp_run_schedule', 'hmbkp_run_schedule_nonce' );
73
  }
74
 
75
- HM\BackUpWordPress\Path::get_instance()->cleanup();
76
 
77
  // Fixes an issue on servers which only allow a single session per client
78
  session_write_close();
79
 
80
- $schedule_id = sanitize_text_field( urldecode( $_POST['hmbkp_schedule_id'] ) );
81
- $task = new \HM\Backdrop\Task( 'hmbkp_run_schedule_async', $schedule_id );
82
  $task->schedule();
83
 
84
  die;
85
 
86
  }
87
- add_action( 'wp_ajax_hmbkp_run_schedule', 'hmbkp_request_do_backup' );
88
-
89
- function hmbkp_run_schedule_async( $schedule_id ) {
90
-
91
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( $schedule_id );
92
 
 
 
93
  $schedule->run();
94
-
95
- $errors = array_merge( $schedule->backup->get_errors(), $schedule->backup->get_warnings() );
96
-
97
- $notices = array();
98
-
99
- foreach ( $errors as $key => $error ) {
100
- $notices[] = implode( ', ', $error );
101
- }
102
-
103
- \HM\BackUpWordPress\Notices::get_instance()->set_notices( 'backup_errors', $notices );
104
  }
105
 
106
  /**
107
  * Send the download file to the browser and then redirect back to the backups page
108
  */
109
- function hmbkp_request_download_backup() {
110
 
111
  check_admin_referer( 'hmbkp_download_backup', 'hmbkp_download_backup_nonce' );
112
 
@@ -114,13 +104,13 @@ function hmbkp_request_download_backup() {
114
  return;
115
  }
116
 
117
- $url = str_replace( wp_normalize_path( HM\BackUpWordPress\Backup::get_home_path() ), home_url( '/' ), trailingslashit( dirname( sanitize_text_field( base64_decode( $_GET['hmbkp_backup_archive'] ) ) ) ) ) . urlencode( pathinfo( sanitize_text_field( base64_decode( $_GET['hmbkp_backup_archive'] ) ), PATHINFO_BASENAME ) );
118
 
119
  global $is_apache;
120
 
121
  if ( $is_apache ) {
122
 
123
- HM\BackUpWordPress\Path::get_instance()->protect_path( 'reset' );
124
 
125
  $url = add_query_arg( 'key', HMBKP_SECURE_KEY, $url );
126
 
@@ -131,59 +121,58 @@ function hmbkp_request_download_backup() {
131
  die;
132
 
133
  }
134
- add_action( 'admin_post_hmbkp_request_download_backup', 'hmbkp_request_download_backup' );
135
 
136
  /**
137
  * Cancels a running backup then redirect back to the backups page
138
  */
139
- function hmbkp_request_cancel_backup() {
140
 
141
  check_admin_referer( 'hmbkp_request_cancel_backup', 'hmbkp-request_cancel_backup_nonce' );
142
 
143
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( urldecode( $_GET['hmbkp_schedule_id'] ) ) );
 
144
 
145
  // Delete the running backup
146
- if ( $schedule->get_running_backup_filename() && file_exists( trailingslashit( hmbkp_path() ) . $schedule->get_running_backup_filename() ) ) {
147
- unlink( trailingslashit( hmbkp_path() ) . $schedule->get_running_backup_filename() );
148
  }
149
 
150
- if ( $schedule->get_schedule_running_path() && file_exists( $schedule->get_schedule_running_path() ) ) {
151
- unlink( $schedule->get_schedule_running_path() );
152
  }
153
 
154
- HM\BackUpWordPress\Path::get_instance()->cleanup();
155
 
156
- wp_safe_redirect( hmbkp_get_settings_url(), 303 );
157
 
158
  die;
159
 
160
  }
161
- add_action( 'admin_post_hmbkp_request_cancel_backup', 'hmbkp_request_cancel_backup' );
162
 
163
  /**
164
  * Dismiss an error and then redirect back to the backups page
165
  */
166
- function hmbkp_dismiss_error() {
167
 
168
- check_admin_referer( 'hmbkp_dismiss_error', 'hmbkp_dismiss_error_nonce' );
169
 
170
- HM\BackUpWordPress\Path::get_instance()->cleanup();
171
-
172
- HM\BackUpWordPress\Notices::get_instance()->clear_all_notices();
173
 
174
  wp_safe_redirect( wp_get_referer(), 303 );
175
 
176
  die;
177
 
178
  }
179
- add_action( 'admin_post_hmbkp_dismiss_error', 'hmbkp_dismiss_error' );
180
 
181
  /**
182
  * Catch the schedule service settings form submission
183
  *
184
  * Validate and either return errors or update the schedule
185
  */
186
- function hmbkp_edit_schedule_services_submit() {
187
 
188
  check_admin_referer( 'hmbkp-edit-schedule-services', 'hmbkp-edit-schedule-services-nonce' );
189
 
@@ -191,12 +180,12 @@ function hmbkp_edit_schedule_services_submit() {
191
  wp_die( __( 'The schedule ID was not provided. Aborting.', 'backupwordpress' ) );
192
  }
193
 
194
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( $_POST['hmbkp_schedule_id'] ) );
195
 
196
  $errors = array();
197
 
198
  // Save the service options
199
- foreach ( HM\BackUpWordPress\Services::get_services( $schedule ) as $service ) {
200
  $errors = array_merge( $errors, $service->save() );
201
  }
202
 
@@ -204,22 +193,28 @@ function hmbkp_edit_schedule_services_submit() {
204
 
205
  if ( $errors ) {
206
  foreach ( $errors as $error ) {
207
- hmbkp_add_settings_error( $error );
208
  }
209
  }
210
 
211
- wp_safe_redirect( wp_get_referer(), '303' );
 
 
 
 
 
 
212
  die;
213
 
214
  }
215
- add_action( 'admin_post_hmbkp_edit_schedule_services_submit', 'hmbkp_edit_schedule_services_submit' );
216
 
217
  /**
218
  * Catch the schedule settings form submission
219
  *
220
  * Validate and either return errors or update the schedule
221
  */
222
- function hmbkp_edit_schedule_submit() {
223
 
224
  check_admin_referer( 'hmbkp-edit-schedule', 'hmbkp-edit-schedule-nonce' );
225
 
@@ -227,7 +222,7 @@ function hmbkp_edit_schedule_submit() {
227
  die;
228
  }
229
 
230
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( $_POST['hmbkp_schedule_id'] ) );
231
 
232
  $errors = array();
233
 
@@ -259,7 +254,7 @@ function hmbkp_edit_schedule_submit() {
259
  $errors['hmbkp_schedule_recurrence']['hmbkp_type'] = __( 'Schedule cannot be empty', 'backupwordpress' );
260
  }
261
 
262
- elseif ( ! in_array( $schedule_recurrence_type, array_keys( hmbkp_cron_schedules() ) ) && 'manually' !== $schedule_recurrence_type ) {
263
  $errors['hmbkp_schedule_recurrence']['hmbkp_type'] = __( 'Invalid schedule', 'backupwordpress' );
264
  }
265
 
@@ -363,14 +358,14 @@ function hmbkp_edit_schedule_submit() {
363
  }
364
 
365
  // Save the service options
366
- foreach ( HM\BackUpWordPress\Services::get_services( $schedule ) as $service ) {
367
  $errors = array_merge( $errors, $service->save() );
368
  }
369
 
370
  if ( ! empty( $settings['recurrence'] ) && ! empty( $settings['start_time'] ) ) {
371
 
372
  // Calculate the start time depending on the recurrence
373
- $start_time = hmbkp_determine_start_time( $settings['recurrence'], $settings['start_time'] );
374
 
375
  if ( $start_time ) {
376
  $schedule->set_schedule_start_time( $start_time );
@@ -399,7 +394,7 @@ function hmbkp_edit_schedule_submit() {
399
  if ( $errors ) {
400
 
401
  foreach ( $errors as $error ) {
402
- hmbkp_add_settings_error( $error );
403
  }
404
 
405
  }
@@ -414,7 +409,7 @@ function hmbkp_edit_schedule_submit() {
414
  die;
415
 
416
  }
417
- add_action( 'admin_post_hmbkp_edit_schedule_submit', 'hmbkp_edit_schedule_submit' );
418
 
419
  /**
420
  * Add an exclude rule
@@ -422,7 +417,7 @@ add_action( 'admin_post_hmbkp_edit_schedule_submit', 'hmbkp_edit_schedule_submit
422
  * @access public
423
  * @return void
424
  */
425
- function hmbkp_add_exclude_rule() {
426
 
427
  check_admin_referer( 'hmbkp-add-exclude-rule', 'hmbkp-add-exclude-rule-nonce' );
428
 
@@ -430,7 +425,7 @@ function hmbkp_add_exclude_rule() {
430
  return;
431
  }
432
 
433
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( $_GET['hmbkp_schedule_id'] ) );
434
 
435
  $exclude_rule = sanitize_text_field( $_GET['hmbkp_exclude_pathname'] );
436
 
@@ -443,7 +438,7 @@ function hmbkp_add_exclude_rule() {
443
  die;
444
 
445
  }
446
- add_action( 'admin_post_hmbkp_add_exclude_rule', 'hmbkp_add_exclude_rule' );
447
 
448
  /**
449
  * Delete an exclude rule
@@ -451,7 +446,7 @@ add_action( 'admin_post_hmbkp_add_exclude_rule', 'hmbkp_add_exclude_rule' );
451
  * @access public
452
  * @return void
453
  */
454
- function hmbkp_remove_exclude_rule() {
455
 
456
  check_admin_referer( 'hmbkp_remove_exclude_rule', 'hmbkp-remove_exclude_rule_nonce' );
457
 
@@ -459,11 +454,12 @@ function hmbkp_remove_exclude_rule() {
459
  die;
460
  }
461
 
462
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( $_GET['hmbkp_schedule_id'] ) );
463
 
464
  $excludes = $schedule->get_excludes();
 
465
 
466
- $schedule->set_excludes( array_diff( $excludes, (array) stripslashes( sanitize_text_field( $_GET['hmbkp_remove_exclude'] ) ) ) );
467
 
468
  $schedule->save();
469
 
@@ -472,13 +468,13 @@ function hmbkp_remove_exclude_rule() {
472
  die;
473
 
474
  }
475
- add_action( 'admin_post_hmbkp_remove_exclude_rule', 'hmbkp_remove_exclude_rule' );
476
 
477
  /**
478
  *
479
  * @param null
480
  */
481
- function hmbkp_recalculate_directory_filesize() {
482
 
483
  if ( ! isset( $_GET['hmbkp_recalculate_directory_filesize'] ) || ! check_admin_referer( 'hmbkp-recalculate_directory_filesize' ) ) {
484
  return;
@@ -487,7 +483,7 @@ function hmbkp_recalculate_directory_filesize() {
487
  // Delete the cached directory size
488
  delete_transient( 'hmbkp_directory_filesizes' );
489
 
490
- $url = add_query_arg( array( 'action' => 'hmbkp_edit_schedule', 'hmbkp_panel' => 'hmbkp_edit_schedule_excludes' ), hmbkp_get_settings_url() );
491
 
492
  if ( isset( $_GET['hmbkp_directory_browse'] ) ) {
493
  $url = add_query_arg( 'hmbkp_directory_browse', sanitize_text_field( $_GET['hmbkp_directory_browse'] ), $url );
@@ -497,54 +493,42 @@ function hmbkp_recalculate_directory_filesize() {
497
  die;
498
 
499
  }
500
- add_action( 'load-' . HMBKP_ADMIN_PAGE, 'hmbkp_recalculate_directory_filesize' );
501
-
502
- function hmbkp_calculate_site_size() {
503
-
504
- if ( isset( $_GET['hmbkp_schedule_id'] ) ) {
505
-
506
- $current_schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( $_GET['hmbkp_schedule_id'] ) );
507
 
508
- } else {
509
-
510
- // Refresh the schedules from the database to make sure we have the latest changes
511
- HM\BackUpWordPress\Schedules::get_instance()->refresh_schedules();
512
 
513
- $schedules = HM\BackUpWordPress\Schedules::get_instance()->get_schedules();
514
-
515
- $current_schedule = reset( $schedules );
516
 
517
- }
518
-
519
- if ( ! $current_schedule->is_site_size_cached() ) {
520
- $root = new SplFileInfo( $current_schedule->backup->get_root() );
521
- $current_schedule->filesize( $root );
522
  }
523
 
524
  }
525
- add_action( 'load-' . HMBKP_ADMIN_PAGE, 'hmbkp_calculate_site_size' );
526
 
527
  /**
528
  * Receive the heartbeat and return backup status
529
  */
530
- function hmbkp_heartbeat_received( $response, $data ) {
531
 
532
  $response['heartbeat_interval'] = 'fast';
533
 
534
  if ( ! empty( $data['hmbkp_schedule_id'] ) ) {
535
 
536
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( urldecode( $data['hmbkp_schedule_id'] ) ) );
 
537
 
538
  if ( ! empty( $data['hmbkp_is_in_progress'] ) ) {
539
 
540
- if ( ! $schedule->get_status() ) {
541
  $response['hmbkp_schedule_status'] = 0;
542
 
543
  // Slow the heartbeat back down
544
  $response['heartbeat_interval'] = 'slow';
545
 
546
  } else {
547
- $response['hmbkp_schedule_status'] = hmbkp_schedule_status( $schedule, false );
548
  }
549
 
550
  }
@@ -552,9 +536,11 @@ function hmbkp_heartbeat_received( $response, $data ) {
552
  if ( ! empty( $data['hmbkp_client_request'] ) ) {
553
 
554
  // Pass the site size to be displayed when it's ready.
555
- if ( $schedule->is_site_size_cached() ) {
 
 
556
 
557
- $response['hmbkp_site_size'] = $schedule->get_formatted_site_size();
558
 
559
  ob_start();
560
  require( HMBKP_PLUGIN_PATH . 'admin/schedule-form-excludes.php' );
@@ -566,13 +552,14 @@ function hmbkp_heartbeat_received( $response, $data ) {
566
  }
567
 
568
  }
 
569
  return $response;
570
 
571
  }
572
- add_filter( 'heartbeat_received', 'hmbkp_heartbeat_received', 10, 2 );
573
 
574
  // TODO needs work
575
- function hmbkp_display_error_and_offer_to_email_it() {
576
 
577
  check_ajax_referer( 'hmbkp_nonce', 'nonce' );
578
 
@@ -582,15 +569,15 @@ function hmbkp_display_error_and_offer_to_email_it() {
582
 
583
  $errors = explode( "\n", wp_strip_all_tags( stripslashes( $_POST['hmbkp_error'] ) ) );
584
 
585
- HM\BackUpWordPress\Notices::get_instance()->set_notices( 'backup_errors', $errors );
586
 
587
  wp_send_json_success( wp_get_referer() );
588
 
589
  }
590
- add_action( 'wp_ajax_hmbkp_backup_error', 'hmbkp_display_error_and_offer_to_email_it' );
591
 
592
  // TODO needs work
593
- function hmbkp_send_error_via_email() {
594
 
595
  check_ajax_referer( 'hmbkp_nonce', 'nonce' );
596
 
@@ -605,14 +592,14 @@ function hmbkp_send_error_via_email() {
605
  die;
606
 
607
  }
608
- add_action( 'wp_ajax_hmbkp_email_error', 'hmbkp_send_error_via_email' );
609
 
610
  /**
611
  * Load the enable support modal contents
612
  *
613
  * @return void
614
  */
615
- function hmbkp_load_enable_support() {
616
 
617
  check_ajax_referer( 'hmbkp_nonce', '_wpnonce' );
618
 
@@ -621,12 +608,12 @@ function hmbkp_load_enable_support() {
621
  die;
622
 
623
  }
624
- add_action( 'wp_ajax_load_enable_support', 'hmbkp_load_enable_support' );
625
 
626
  /**
627
  * Display the running status via ajax
628
  */
629
- function hmbkp_ajax_is_backup_in_progress() {
630
 
631
  check_ajax_referer( 'hmbkp_nonce', 'nonce' );
632
 
@@ -634,7 +621,7 @@ function hmbkp_ajax_is_backup_in_progress() {
634
  die;
635
  }
636
 
637
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( urldecode( $_POST['hmbkp_schedule_id'] ) ) );
638
 
639
  if ( ! $schedule->get_status() ) {
640
  echo 0;
@@ -645,12 +632,12 @@ function hmbkp_ajax_is_backup_in_progress() {
645
  die;
646
 
647
  }
648
- add_action( 'wp_ajax_hmbkp_is_in_progress', 'hmbkp_ajax_is_backup_in_progress' );
649
 
650
  /**
651
  * Display the calculated size via ajax
652
  */
653
- function hmbkp_ajax_calculate_backup_size() {
654
 
655
  check_ajax_referer( 'hmbkp_nonce', 'nonce' );
656
 
@@ -658,7 +645,7 @@ function hmbkp_ajax_calculate_backup_size() {
658
  die;
659
  }
660
 
661
- $schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( urldecode( $_POST['hmbkp_schedule_id'] ) ) );
662
 
663
  $recalculate_filesize = true;
664
 
@@ -667,12 +654,12 @@ function hmbkp_ajax_calculate_backup_size() {
667
  die;
668
 
669
  }
670
- add_action( 'wp_ajax_hmbkp_calculate', 'hmbkp_ajax_calculate_backup_size' );
671
 
672
  /**
673
  * Test the cron response and if it's not 200 show a warning message
674
  */
675
- function hmbkp_ajax_cron_test() {
676
 
677
  check_ajax_referer( 'hmbkp_nonce', 'nonce' );
678
 
@@ -727,4 +714,4 @@ function hmbkp_ajax_cron_test() {
727
  die;
728
 
729
  }
730
- add_action( 'wp_ajax_hmbkp_cron_test', 'hmbkp_ajax_cron_test' );
1
  <?php
2
 
3
+ namespace HM\BackUpWordPress;
4
+
5
  /**
6
  * Delete the backup and then redirect back to the backups page
7
  */
8
+ function request_delete_backup() {
9
 
10
  check_admin_referer( 'hmbkp_delete_backup', 'hmbkp_delete_backup_nonce' );
11
 
12
+ $schedule = new Scheduled_Backup( sanitize_text_field( urldecode( $_GET['hmbkp_schedule_id'] ) ) );
13
 
14
  $deleted = $schedule->delete_backup( sanitize_text_field( base64_decode( $_GET['hmbkp_backup_archive'] ) ) );
15
 
17
  wp_die( $deleted->get_error_message() );
18
  }
19
 
20
+ wp_safe_redirect( get_settings_url(), 303 );
21
 
22
  die;
23
 
24
  }
25
+ add_action( 'admin_post_hmbkp_request_delete_backup', 'HM\BackUpWordPress\request_delete_backup' );
26
 
27
  /**
28
  * Enable support and then redirect back to the backups page
29
  */
30
+ function request_enable_support() {
31
 
32
  check_admin_referer( 'hmbkp_enable_support', 'hmbkp_enable_support_nonce' );
33
 
34
  update_option( 'hmbkp_enable_support', true );
35
 
36
+ wp_safe_redirect( get_settings_url(), 303 );
37
 
38
  die;
39
 
40
  }
41
+ add_action( 'admin_post_hmbkp_request_enable_support', 'HM\BackUpWordPress\request_enable_support' );
42
 
43
  /**
44
  * Delete a schedule and all it's backups and then redirect back to the backups page
45
  */
46
+ function request_delete_schedule() {
47
 
48
  check_admin_referer( 'hmbkp_delete_schedule', 'hmbkp_delete_schedule_nonce' );
49
 
50
+ $schedule = new Scheduled_Backup( sanitize_text_field( urldecode( $_GET['hmbkp_schedule_id'] ) ) );
51
  $schedule->cancel( true );
52
 
53
+ wp_safe_redirect( get_settings_url(), 303 );
54
 
55
  die;
56
 
57
  }
58
+ add_action( 'admin_post_hmbkp_request_delete_schedule', 'HM\BackUpWordPress\request_delete_schedule' );
59
 
60
  /**
61
  * Perform a manual backup
62
  *
63
  * Handles ajax requests as well as standard GET requests
64
  */
65
+ function request_do_backup() {
66
 
67
+ if ( empty( $_REQUEST['hmbkp_schedule_id'] ) ) {
68
  die;
69
  }
70
 
74
  check_admin_referer( 'hmbkp_run_schedule', 'hmbkp_run_schedule_nonce' );
75
  }
76
 
77
+ Path::get_instance()->cleanup();
78
 
79
  // Fixes an issue on servers which only allow a single session per client
80
  session_write_close();
81
 
82
+ $schedule_id = sanitize_text_field( urldecode( $_REQUEST['hmbkp_schedule_id'] ) );
83
+ $task = new \HM\Backdrop\Task( '\HM\BackUpWordPress\run_schedule_async', $schedule_id );
84
  $task->schedule();
85
 
86
  die;
87
 
88
  }
89
+ add_action( 'wp_ajax_hmbkp_run_schedule', 'HM\BackUpWordPress\request_do_backup' );
 
 
 
 
90
 
91
+ function run_schedule_async( $schedule_id ) {
92
+ $schedule = new Scheduled_Backup( $schedule_id );
93
  $schedule->run();
 
 
 
 
 
 
 
 
 
 
94
  }
95
 
96
  /**
97
  * Send the download file to the browser and then redirect back to the backups page
98
  */
99
+ function request_download_backup() {
100
 
101
  check_admin_referer( 'hmbkp_download_backup', 'hmbkp_download_backup_nonce' );
102
 
104
  return;
105
  }
106
 
107
+ $url = str_replace( wp_normalize_path( Path::get_home_path() ), home_url( '/' ), trailingslashit( dirname( sanitize_text_field( base64_decode( $_GET['hmbkp_backup_archive'] ) ) ) ) ) . urlencode( pathinfo( sanitize_text_field( base64_decode( $_GET['hmbkp_backup_archive'] ) ), PATHINFO_BASENAME ) );
108
 
109
  global $is_apache;
110
 
111
  if ( $is_apache ) {
112
 
113
+ Path::get_instance()->protect_path( 'reset' );
114
 
115
  $url = add_query_arg( 'key', HMBKP_SECURE_KEY, $url );
116
 
121
  die;
122
 
123
  }
124
+ add_action( 'admin_post_hmbkp_request_download_backup', 'HM\BackUpWordPress\request_download_backup' );
125
 
126
  /**
127
  * Cancels a running backup then redirect back to the backups page
128
  */
129
+ function request_cancel_backup() {
130
 
131
  check_admin_referer( 'hmbkp_request_cancel_backup', 'hmbkp-request_cancel_backup_nonce' );
132
 
133
+ $schedule = new Scheduled_Backup( sanitize_text_field( urldecode( $_GET['hmbkp_schedule_id'] ) ) );
134
+ $status = $schedule->get_status();
135
 
136
  // Delete the running backup
137
+ if ( $status->get_backup_filename() && file_exists( trailingslashit( Path::get_path() ) . $status->get_backup_filename() ) ) {
138
+ unlink( trailingslashit( Path::get_path() ) . $status->get_backup_filename() );
139
  }
140
 
141
+ if ( file_exists( $status->get_status_filepath() ) ) {
142
+ unlink( $status->get_status_filepath() );
143
  }
144
 
145
+ Path::get_instance()->cleanup();
146
 
147
+ wp_safe_redirect( get_settings_url(), 303 );
148
 
149
  die;
150
 
151
  }
152
+ add_action( 'admin_post_hmbkp_request_cancel_backup', 'HM\BackUpWordPress\request_cancel_backup' );
153
 
154
  /**
155
  * Dismiss an error and then redirect back to the backups page
156
  */
157
+ function dismiss_error() {
158
 
159
+ Path::get_instance()->cleanup();
160
 
161
+ Notices::get_instance()->clear_all_notices();
 
 
162
 
163
  wp_safe_redirect( wp_get_referer(), 303 );
164
 
165
  die;
166
 
167
  }
168
+ add_action( 'wp_ajax_hmbkp_dismiss_error', 'HM\BackUpWordPress\dismiss_error' );
169
 
170
  /**
171
  * Catch the schedule service settings form submission
172
  *
173
  * Validate and either return errors or update the schedule
174
  */
175
+ function edit_schedule_services_submit() {
176
 
177
  check_admin_referer( 'hmbkp-edit-schedule-services', 'hmbkp-edit-schedule-services-nonce' );
178
 
180
  wp_die( __( 'The schedule ID was not provided. Aborting.', 'backupwordpress' ) );
181
  }
182
 
183
+ $schedule = new Scheduled_Backup( sanitize_text_field( $_POST['hmbkp_schedule_id'] ) );
184
 
185
  $errors = array();
186
 
187
  // Save the service options
188
+ foreach ( Services::get_services( $schedule ) as $service ) {
189
  $errors = array_merge( $errors, $service->save() );
190
  }
191
 
193
 
194
  if ( $errors ) {
195
  foreach ( $errors as $error ) {
196
+ add_settings_error( $error );
197
  }
198
  }
199
 
200
+ $redirect = remove_query_arg( array( 'hmbkp_panel', 'action' ), wp_get_referer() );
201
+
202
+ if ( $errors ) {
203
+ $redirect = wp_get_referer();
204
+ }
205
+
206
+ wp_safe_redirect( $redirect, '303' );
207
  die;
208
 
209
  }
210
+ add_action( 'admin_post_hmbkp_edit_schedule_services_submit', 'HM\BackUpWordPress\edit_schedule_services_submit' );
211
 
212
  /**
213
  * Catch the schedule settings form submission
214
  *
215
  * Validate and either return errors or update the schedule
216
  */
217
+ function edit_schedule_submit() {
218
 
219
  check_admin_referer( 'hmbkp-edit-schedule', 'hmbkp-edit-schedule-nonce' );
220
 
222
  die;
223
  }
224
 
225
+ $schedule = new Scheduled_Backup( sanitize_text_field( $_POST['hmbkp_schedule_id'] ) );
226
 
227
  $errors = array();
228
 
254
  $errors['hmbkp_schedule_recurrence']['hmbkp_type'] = __( 'Schedule cannot be empty', 'backupwordpress' );
255
  }
256
 
257
+ elseif ( ! in_array( $schedule_recurrence_type, array_keys( cron_schedules() ) ) && 'manually' !== $schedule_recurrence_type ) {
258
  $errors['hmbkp_schedule_recurrence']['hmbkp_type'] = __( 'Invalid schedule', 'backupwordpress' );
259
  }
260
 
358
  }
359
 
360
  // Save the service options
361
+ foreach ( Services::get_services( $schedule ) as $service ) {
362
  $errors = array_merge( $errors, $service->save() );
363
  }
364
 
365
  if ( ! empty( $settings['recurrence'] ) && ! empty( $settings['start_time'] ) ) {
366
 
367
  // Calculate the start time depending on the recurrence
368
+ $start_time = determine_start_time( $settings['recurrence'], $settings['start_time'] );
369
 
370
  if ( $start_time ) {
371
  $schedule->set_schedule_start_time( $start_time );
394
  if ( $errors ) {
395
 
396
  foreach ( $errors as $error ) {
397
+ add_settings_error( $error );
398
  }
399
 
400
  }
409
  die;
410
 
411
  }
412
+ add_action( 'admin_post_hmbkp_edit_schedule_submit', 'HM\BackUpWordPress\edit_schedule_submit' );
413
 
414
  /**
415
  * Add an exclude rule
417
  * @access public
418
  * @return void
419
  */
420
+ function add_exclude_rule() {
421
 
422
  check_admin_referer( 'hmbkp-add-exclude-rule', 'hmbkp-add-exclude-rule-nonce' );
423
 
425
  return;
426
  }
427
 
428
+ $schedule = new Scheduled_Backup( sanitize_text_field( $_GET['hmbkp_schedule_id'] ) );
429
 
430
  $exclude_rule = sanitize_text_field( $_GET['hmbkp_exclude_pathname'] );
431
 
438
  die;
439
 
440
  }
441
+ add_action( 'admin_post_hmbkp_add_exclude_rule', 'HM\BackUpWordPress\add_exclude_rule' );
442
 
443
  /**
444
  * Delete an exclude rule
446
  * @access public
447
  * @return void
448
  */
449
+ function remove_exclude_rule() {
450
 
451
  check_admin_referer( 'hmbkp_remove_exclude_rule', 'hmbkp-remove_exclude_rule_nonce' );
452
 
454
  die;
455
  }
456
 
457
+ $schedule = new Scheduled_Backup( sanitize_text_field( $_GET['hmbkp_schedule_id'] ) );
458
 
459
  $excludes = $schedule->get_excludes();
460
+ $exclude_rule_to_remove = stripslashes( sanitize_text_field( $_GET['hmbkp_remove_exclude'] ) );
461
 
462
+ $schedule->set_excludes( array_diff( $excludes->get_user_excludes(), (array) $exclude_rule_to_remove ) );
463
 
464
  $schedule->save();
465
 
468
  die;
469
 
470
  }
471
+ add_action( 'admin_post_hmbkp_remove_exclude_rule', 'HM\BackUpWordPress\remove_exclude_rule' );
472
 
473
  /**
474
  *
475
  * @param null
476
  */
477
+ function recalculate_directory_filesize() {
478
 
479
  if ( ! isset( $_GET['hmbkp_recalculate_directory_filesize'] ) || ! check_admin_referer( 'hmbkp-recalculate_directory_filesize' ) ) {
480
  return;
483
  // Delete the cached directory size
484
  delete_transient( 'hmbkp_directory_filesizes' );
485
 
486
+ $url = add_query_arg( array( 'action' => 'hmbkp_edit_schedule', 'hmbkp_panel' => 'hmbkp_edit_schedule_excludes' ), get_settings_url() );
487
 
488
  if ( isset( $_GET['hmbkp_directory_browse'] ) ) {
489
  $url = add_query_arg( 'hmbkp_directory_browse', sanitize_text_field( $_GET['hmbkp_directory_browse'] ), $url );
493
  die;
494
 
495
  }
496
+ add_action( 'load-' . HMBKP_ADMIN_PAGE, 'HM\BackUpWordPress\recalculate_directory_filesize' );
 
 
 
 
 
 
497
 
498
+ function calculate_site_size() {
 
 
 
499
 
500
+ $site_size = new Site_Size;
 
 
501
 
502
+ if ( ! $site_size::is_site_size_cached() ) {
503
+ $root = new \SplFileInfo( Path::get_root() );
504
+ $site_size->filesize( $root );
 
 
505
  }
506
 
507
  }
508
+ add_action( 'load-' . HMBKP_ADMIN_PAGE, 'HM\BackUpWordPress\calculate_site_size' );
509
 
510
  /**
511
  * Receive the heartbeat and return backup status
512
  */
513
+ function heartbeat_received( $response, $data ) {
514
 
515
  $response['heartbeat_interval'] = 'fast';
516
 
517
  if ( ! empty( $data['hmbkp_schedule_id'] ) ) {
518
 
519
+ $schedule = new Scheduled_Backup( sanitize_text_field( urldecode( $data['hmbkp_schedule_id'] ) ) );
520
+ $status = new Backup_Status( $schedule->get_id() );
521
 
522
  if ( ! empty( $data['hmbkp_is_in_progress'] ) ) {
523
 
524
+ if ( ! $status->get_status() ) {
525
  $response['hmbkp_schedule_status'] = 0;
526
 
527
  // Slow the heartbeat back down
528
  $response['heartbeat_interval'] = 'slow';
529
 
530
  } else {
531
+ $response['hmbkp_schedule_status'] = schedule_status( $schedule, false );
532
  }
533
 
534
  }
536
  if ( ! empty( $data['hmbkp_client_request'] ) ) {
537
 
538
  // Pass the site size to be displayed when it's ready.
539
+ if ( Site_Size::is_site_size_cached() ) {
540
+
541
+ $site_size = new Site_Size( $schedule->get_type(), $schedule->get_excludes() );
542
 
543
+ $response['hmbkp_site_size'] = $site_size->get_formatted_site_size();
544
 
545
  ob_start();
546
  require( HMBKP_PLUGIN_PATH . 'admin/schedule-form-excludes.php' );
552
  }
553
 
554
  }
555
+
556
  return $response;
557
 
558
  }
559
+ add_filter( 'heartbeat_received', 'HM\BackUpWordPress\heartbeat_received', 10, 2 );
560
 
561
  // TODO needs work
562
+ function display_error_and_offer_to_email_it() {
563
 
564
  check_ajax_referer( 'hmbkp_nonce', 'nonce' );
565
 
569
 
570
  $errors = explode( "\n", wp_strip_all_tags( stripslashes( $_POST['hmbkp_error'] ) ) );
571
 
572
+ Notices::get_instance()->set_notices( 'backup_errors', $errors );
573
 
574
  wp_send_json_success( wp_get_referer() );
575
 
576
  }
577
+ add_action( 'wp_ajax_hmbkp_backup_error', 'HM\BackUpWordPress\display_error_and_offer_to_email_it' );
578
 
579
  // TODO needs work
580
+ function send_error_via_email() {
581
 
582
  check_ajax_referer( 'hmbkp_nonce', 'nonce' );
583
 
592
  die;
593
 
594
  }
595
+ add_action( 'wp_ajax_hmbkp_email_error', 'HM\BackUpWordPress\send_error_via_email' );
596
 
597
  /**
598
  * Load the enable support modal contents
599
  *
600
  * @return void
601
  */
602
+ function load_enable_support() {
603
 
604
  check_ajax_referer( 'hmbkp_nonce', '_wpnonce' );
605
 
608
  die;
609
 
610
  }
611
+ add_action( 'wp_ajax_load_enable_support', 'HM\BackUpWordPress\load_enable_support' );
612
 
613
  /**
614
  * Display the running status via ajax
615
  */
616
+ function ajax_is_backup_in_progress() {
617
 
618
  check_ajax_referer( 'hmbkp_nonce', 'nonce' );
619
 
621
  die;
622
  }
623
 
624
+ $schedule = new Scheduled_Backup( sanitize_text_field( urldecode( $_POST['hmbkp_schedule_id'] ) ) );
625
 
626
  if ( ! $schedule->get_status() ) {
627
  echo 0;
632
  die;
633
 
634
  }
635
+ add_action( 'wp_ajax_hmbkp_is_in_progress', 'HM\BackUpWordPress\ajax_is_backup_in_progress' );
636
 
637
  /**
638
  * Display the calculated size via ajax
639
  */
640
+ function ajax_calculate_backup_size() {
641
 
642
  check_ajax_referer( 'hmbkp_nonce', 'nonce' );
643
 
645
  die;
646
  }
647
 
648
+ $schedule = new Scheduled_Backup( sanitize_text_field( urldecode( $_POST['hmbkp_schedule_id'] ) ) );
649
 
650
  $recalculate_filesize = true;
651
 
654
  die;
655
 
656
  }
657
+ add_action( 'wp_ajax_hmbkp_calculate', 'HM\BackUpWordPress\ajax_calculate_backup_size' );
658
 
659
  /**
660
  * Test the cron response and if it's not 200 show a warning message
661
  */
662
+ function ajax_cron_test() {
663
 
664
  check_ajax_referer( 'hmbkp_nonce', 'nonce' );
665
 
714
  die;
715
 
716
  }
717
+ add_action( 'wp_ajax_hmbkp_cron_test', 'HM\BackUpWordPress\ajax_cron_test' );
admin/backups-table.php CHANGED
@@ -1,10 +1,16 @@
 
 
 
 
 
 
1
  <table class="widefat">
2
 
3
  <thead>
4
 
5
  <tr>
6
 
7
- <th scope="col"><?php hmbkp_backups_number( $schedule ); ?></th>
8
  <th scope="col"><?php _e( 'Size', 'backupwordpress' ); ?></th>
9
  <th scope="col"><?php _e( 'Type', 'backupwordpress' ); ?></th>
10
  <th scope="col"><?php _e( 'Actions', 'backupwordpress' ); ?></th>
@@ -25,7 +31,7 @@
25
  continue;
26
  }
27
 
28
- hmbkp_get_backup_row( $file, $schedule );
29
 
30
  }
31
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ ?>
6
+
7
  <table class="widefat">
8
 
9
  <thead>
10
 
11
  <tr>
12
 
13
+ <th scope="col"><?php backups_number( $schedule ); ?></th>
14
  <th scope="col"><?php _e( 'Size', 'backupwordpress' ); ?></th>
15
  <th scope="col"><?php _e( 'Type', 'backupwordpress' ); ?></th>
16
  <th scope="col"><?php _e( 'Actions', 'backupwordpress' ); ?></th>
31
  continue;
32
  }
33
 
34
+ get_backup_row( $file, $schedule );
35
 
36
  }
37
 
admin/backups.php CHANGED
@@ -1,25 +1,28 @@
1
  <?php
2
 
 
 
3
  // Refresh the schedules from the database to make sure we have the latest changes
4
- HM\BackUpWordPress\Schedules::get_instance()->refresh_schedules();
5
 
6
- $schedules = HM\BackUpWordPress\Schedules::get_instance()->get_schedules();
7
 
8
  if ( ! empty( $_GET['hmbkp_schedule_id'] ) ) {
9
- $current_schedule = new HM\BackUpWordPress\Scheduled_Backup( sanitize_text_field( $_GET['hmbkp_schedule_id'] ) );
10
  } else {
11
  $current_schedule = reset( $schedules );
12
  } ?>
13
 
14
  <h2 class="nav-tab-wrapper">
15
 
16
- <?php foreach ( $schedules as $schedule ) : ?>
 
17
 
18
- <a class="nav-tab<?php if ( $schedule->get_status() ) { ?> hmbkp-running<?php } ?><?php if ( $schedule->get_id() === $current_schedule->get_id() ) { ?> nav-tab-active<?php } ?>" <?php if ( $schedule->get_status() ) { ?>title="<?php echo esc_attr( strip_tags( $schedule->get_status() ) ); ?>"<?php } ?> href="<?php echo esc_url( add_query_arg( 'hmbkp_schedule_id', $schedule->get_id(), HMBKP_ADMIN_URL ) ); ?> "><?php echo esc_html( hmbkp_translated_schedule_title( $schedule->get_slug(), $schedule->get_name() ) ); ?> <span class="count">(<?php echo esc_html( count( $schedule->get_backups() ) ); ?>)</span></a>
19
 
20
  <?php endforeach; ?>
21
 
22
- <a class="nav-tab<?php if ( ! HM\BackUpWordPress\Schedules::get_instance()->get_schedule( $current_schedule->get_id() ) ) { ?> nav-tab-active<?php } ?>" href="<?php echo esc_url( add_query_arg( array( 'hmbkp_add_schedule' => '1', 'action' => 'hmbkp_edit_schedule', 'hmbkp_schedule_id' => time(), 'hmbkp_panel' => 'hmbkp_edit_schedule_settings' ), HMBKP_ADMIN_URL ) ); ?>"> + <?php _e( 'add schedule', 'backupwordpress' ); ?></a>
23
 
24
  </h2>
25
 
@@ -34,4 +37,4 @@ if ( ! $schedule = $current_schedule ) {
34
 
35
  <?php require( HMBKP_PLUGIN_PATH . 'admin/backups-table.php' ); ?>
36
 
37
- </div>
1
  <?php
2
 
3
+ namespace HM\BackUpWordPress;
4
+
5
  // Refresh the schedules from the database to make sure we have the latest changes
6
+ Schedules::get_instance()->refresh_schedules();
7
 
8
+ $schedules = Schedules::get_instance()->get_schedules();
9
 
10
  if ( ! empty( $_GET['hmbkp_schedule_id'] ) ) {
11
+ $current_schedule = new Scheduled_Backup( sanitize_text_field( $_GET['hmbkp_schedule_id'] ) );
12
  } else {
13
  $current_schedule = reset( $schedules );
14
  } ?>
15
 
16
  <h2 class="nav-tab-wrapper">
17
 
18
+ <?php foreach ( $schedules as $schedule ) :
19
+ $status = new Backup_Status( $schedule->get_id() ); ?>
20
 
21
+ <a class="nav-tab<?php if ( $status->get_status() ) { ?> hmbkp-running<?php } ?><?php if ( $schedule->get_id() === $current_schedule->get_id() ) { ?> nav-tab-active<?php } ?>" <?php if ( $status->get_status() ) { ?>title="<?php echo esc_attr( strip_tags( $status->get_status() ) ); ?>"<?php } ?> href="<?php echo esc_url( add_query_arg( 'hmbkp_schedule_id', $schedule->get_id(), HMBKP_ADMIN_URL ) ); ?> "><?php echo esc_html( translated_schedule_title( $schedule->get_slug(), $schedule->get_name() ) ); ?> <span class="count">(<?php echo esc_html( count( $schedule->get_backups() ) ); ?>)</span></a>
22
 
23
  <?php endforeach; ?>
24
 
25
+ <a class="nav-tab<?php if ( ! Schedules::get_instance()->get_schedule( $current_schedule->get_id() ) ) { ?> nav-tab-active<?php } ?>" href="<?php echo esc_url( add_query_arg( array( 'hmbkp_add_schedule' => '1', 'action' => 'hmbkp_edit_schedule', 'hmbkp_schedule_id' => time(), 'hmbkp_panel' => 'hmbkp_edit_schedule_settings' ), HMBKP_ADMIN_URL ) ); ?>"> + <?php _e( 'add schedule', 'backupwordpress' ); ?></a>
26
 
27
  </h2>
28
 
37
 
38
  <?php require( HMBKP_PLUGIN_PATH . 'admin/backups-table.php' ); ?>
39
 
40
+ </div>
admin/constants.php CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  <div id="hmbkp-constants">
2
 
3
  <p><?php printf( __( 'You can %1$s any of the following %2$s in your %3$s to control advanced settings. %4$s. Defined %5$s will be highlighted.', 'backupwordpress' ), '<code>define</code>', '<code>' . __( 'Constants', 'backupwordpress' ) . '</code>', '<code>wp-config.php</code>', '<a href="http://codex.wordpress.org/Editing_wp-config.php">' . __( 'The Codex can help', 'backupwordpress' ) . '</a>', '<code>' . __( 'Constants', 'backupwordpress' ) . '</code>' ); ?></p>
@@ -14,7 +20,7 @@
14
  <p><?php printf( __( 'You\'ve set it to: %s', 'backupwordpress' ), '<code>' . esc_html( HMBKP_PATH ) . '</code>' ); ?></p>
15
  <?php } ?>
16
 
17
- <p><?php printf( __( 'The path to the folder you would like to store your backup files in, defaults to %s.', 'backupwordpress' ), '<code>' . esc_html( hmbkp_path() ) . '</code>' ); ?> <?php _e( 'e.g.', 'backupwordpress' ); ?> <code>define( 'HMBKP_PATH', '/home/willmot/backups' );</code></p>
18
 
19
  </td>
20
 
@@ -94,7 +100,7 @@
94
  <p><?php printf( __( 'You\'ve set it to: %s', 'backupwordpress' ), '<code>' . esc_html( HMBKP_ROOT ) . '</code>' ); ?></p>
95
  <?php } ?>
96
 
97
- <p><?php printf( __( 'The root directory that is backed up. Defaults to %s.', 'backupwordpress' ), '<code>' . HM\BackUpWordPress\Backup::get_home_path() . '</code>' ); ?> <?php _e( 'e.g.', 'backupwordpress' ); ?> <code>define( 'HMBKP_ROOT', ABSPATH . 'wp/' );</code></p>
98
 
99
  </td>
100
 
@@ -116,7 +122,7 @@
116
 
117
  </tr>
118
 
119
- <?php foreach ( HM\BackUpWordPress\Services::get_services() as $file => $service ) {
120
  echo wp_kses_post( call_user_func( array( $service, 'constant' ) ) );
121
  } ?>
122
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ ?>
6
+
7
  <div id="hmbkp-constants">
8
 
9
  <p><?php printf( __( 'You can %1$s any of the following %2$s in your %3$s to control advanced settings. %4$s. Defined %5$s will be highlighted.', 'backupwordpress' ), '<code>define</code>', '<code>' . __( 'Constants', 'backupwordpress' ) . '</code>', '<code>wp-config.php</code>', '<a href="http://codex.wordpress.org/Editing_wp-config.php">' . __( 'The Codex can help', 'backupwordpress' ) . '</a>', '<code>' . __( 'Constants', 'backupwordpress' ) . '</code>' ); ?></p>
20
  <p><?php printf( __( 'You\'ve set it to: %s', 'backupwordpress' ), '<code>' . esc_html( HMBKP_PATH ) . '</code>' ); ?></p>
21
  <?php } ?>
22
 
23
+ <p><?php printf( __( 'The path to the folder you would like to store your backup files in, defaults to %s.', 'backupwordpress' ), '<code>' . esc_html( Path::get_path() ) . '</code>' ); ?> <?php _e( 'e.g.', 'backupwordpress' ); ?> <code>define( 'HMBKP_PATH', '/home/willmot/backups' );</code></p>
24
 
25
  </td>
26
 
100
  <p><?php printf( __( 'You\'ve set it to: %s', 'backupwordpress' ), '<code>' . esc_html( HMBKP_ROOT ) . '</code>' ); ?></p>
101
  <?php } ?>
102
 
103
+ <p><?php printf( __( 'The root directory that is backed up. Defaults to %s.', 'backupwordpress' ), '<code>' . Path::get_home_path() . '</code>' ); ?> <?php _e( 'e.g.', 'backupwordpress' ); ?> <code>define( 'HMBKP_ROOT', ABSPATH . 'wp/' );</code></p>
104
 
105
  </td>
106
 
122
 
123
  </tr>
124
 
125
+ <?php foreach ( Services::get_services() as $file => $service ) {
126
  echo wp_kses_post( call_user_func( array( $service, 'constant' ) ) );
127
  } ?>
128
 
admin/menu.php CHANGED
@@ -1,26 +1,24 @@
1
  <?php
2
 
 
 
3
  /**
4
  * Add the backups menu item
5
  * to the tools menu
6
  *
7
  * @return null
8
  */
9
- function hmbkp_admin_menu() {
10
 
11
  if ( is_multisite() ) {
12
-
13
- add_submenu_page( 'settings.php', __( 'Manage Backups', 'backupwordpress' ), __( 'Backups', 'backupwordpress' ), ( defined( 'HMBKP_CAPABILITY' ) && HMBKP_CAPABILITY ) ? HMBKP_CAPABILITY : 'manage_options', HMBKP_PLUGIN_SLUG, 'hmbkp_manage_backups' );
14
-
15
  } else {
16
-
17
- add_management_page( __( 'Manage Backups', 'backupwordpress' ), __( 'Backups', 'backupwordpress' ), ( defined( 'HMBKP_CAPABILITY' ) && HMBKP_CAPABILITY ) ? HMBKP_CAPABILITY : 'manage_options', HMBKP_PLUGIN_SLUG, 'hmbkp_manage_backups' );
18
-
19
  }
20
  }
21
 
22
- add_action( 'network_admin_menu', 'hmbkp_admin_menu' );
23
- add_action( 'admin_menu', 'hmbkp_admin_menu' );
24
 
25
  /**
26
  * Load the backups admin page
@@ -28,7 +26,7 @@ add_action( 'admin_menu', 'hmbkp_admin_menu' );
28
  *
29
  * @return null
30
  */
31
- function hmbkp_manage_backups() {
32
  require_once( HMBKP_PLUGIN_PATH . 'admin/page.php' );
33
  }
34
 
@@ -40,7 +38,7 @@ function hmbkp_manage_backups() {
40
  *
41
  * @return array $links
42
  */
43
- function hmbkp_plugin_action_link( $links, $file ) {
44
 
45
  if ( false !== strpos( $file, HMBKP_PLUGIN_SLUG ) ) {
46
  array_push( $links, '<a href="' . esc_url( HMBKP_ADMIN_URL ) . '">' . __( 'Backups', 'backupwordpress' ) . '</a>' );
@@ -50,7 +48,7 @@ function hmbkp_plugin_action_link( $links, $file ) {
50
 
51
  }
52
 
53
- add_filter( 'plugin_action_links', 'hmbkp_plugin_action_link', 10, 2 );
54
 
55
  /**
56
  * Add Contextual Help to Backups tools page.
@@ -59,7 +57,7 @@ add_filter( 'plugin_action_links', 'hmbkp_plugin_action_link', 10, 2 );
59
  *
60
  * @return null
61
  */
62
- function hmbkp_contextual_help() {
63
 
64
  // Pre WordPress 3.3 compat
65
  if ( ! method_exists( get_current_screen(), 'add_help_tab' ) ) {
@@ -104,4 +102,4 @@ function hmbkp_contextual_help() {
104
 
105
  }
106
 
107
- add_action( 'load-' . HMBKP_ADMIN_PAGE, 'hmbkp_contextual_help' );
1
  <?php
2
 
3
+ namespace HM\BackUpWordPress;
4
+
5
  /**
6
  * Add the backups menu item
7
  * to the tools menu
8
  *
9
  * @return null
10
  */
11
+ function admin_menu() {
12
 
13
  if ( is_multisite() ) {
14
+ add_submenu_page( 'settings.php', __( 'Manage Backups', 'backupwordpress' ), __( 'Backups', 'backupwordpress' ), ( defined( 'HMBKP_CAPABILITY' ) && HMBKP_CAPABILITY ) ? HMBKP_CAPABILITY : 'manage_options', HMBKP_PLUGIN_SLUG, 'HM\BackUpWordPress\manage_backups' );
 
 
15
  } else {
16
+ add_management_page( __( 'Manage Backups', 'backupwordpress' ), __( 'Backups', 'backupwordpress' ), ( defined( 'HMBKP_CAPABILITY' ) && HMBKP_CAPABILITY ) ? HMBKP_CAPABILITY : 'manage_options', HMBKP_PLUGIN_SLUG, 'HM\BackUpWordPress\manage_backups' );
 
 
17
  }
18
  }
19
 
20
+ add_action( 'network_admin_menu', 'HM\BackUpWordPress\admin_menu' );
21
+ add_action( 'admin_menu', 'HM\BackUpWordPress\admin_menu' );
22
 
23
  /**
24
  * Load the backups admin page
26
  *
27
  * @return null
28
  */
29
+ function manage_backups() {
30
  require_once( HMBKP_PLUGIN_PATH . 'admin/page.php' );
31
  }
32
 
38
  *
39
  * @return array $links
40
  */
41
+ function plugin_action_link( $links, $file ) {
42
 
43
  if ( false !== strpos( $file, HMBKP_PLUGIN_SLUG ) ) {
44
  array_push( $links, '<a href="' . esc_url( HMBKP_ADMIN_URL ) . '">' . __( 'Backups', 'backupwordpress' ) . '</a>' );
48
 
49
  }
50
 
51
+ add_filter( 'plugin_action_links', 'HM\BackUpWordPress\plugin_action_link', 10, 2 );
52
 
53
  /**
54
  * Add Contextual Help to Backups tools page.
57
  *
58
  * @return null
59
  */
60
+ function contextual_help() {
61
 
62
  // Pre WordPress 3.3 compat
63
  if ( ! method_exists( get_current_screen(), 'add_help_tab' ) ) {
102
 
103
  }
104
 
105
+ add_action( 'load-' . HMBKP_ADMIN_PAGE, 'HM\BackUpWordPress\contextual_help' );
admin/page.php CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  <div class="wrap">
2
 
3
  <h1>
@@ -12,7 +18,7 @@
12
 
13
  </h1>
14
 
15
- <?php if ( hmbkp_possible() ) : ?>
16
 
17
  <?php include_once( HMBKP_PLUGIN_PATH . 'admin/backups.php' ); ?>
18
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ ?>
6
+
7
  <div class="wrap">
8
 
9
  <h1>
18
 
19
  </h1>
20
 
21
+ <?php if ( is_backup_possible() ) : ?>
22
 
23
  <?php include_once( HMBKP_PLUGIN_PATH . 'admin/backups.php' ); ?>
24
 
admin/schedule-form-excludes.php CHANGED
@@ -1,71 +1,76 @@
 
 
 
 
 
 
 
1
  <div class="hmbkp-exclude-settings">
2
 
3
- <?php if ( $schedule->get_excludes() ) : ?>
 
 
4
 
5
- <h3>
6
- <?php _e( 'Currently Excluded', 'backupwordpress' ); ?>
7
- </h3>
8
 
9
- <p><?php _e( 'We automatically detect and ignore common <abbr title="Version Control Systems">VCS</abbr> folders and other backup plugin folders.', 'backupwordpress' ); ?></p>
10
 
11
- <table class="widefat">
12
 
13
- <tbody>
14
 
15
- <?php foreach ( array_diff( $schedule->get_excludes(), $schedule->backup->default_excludes() ) as $key => $exclude ) :
16
 
17
- $exclude_path = new SplFileInfo( trailingslashit( $schedule->backup->get_root() ) . ltrim( str_ireplace( $schedule->backup->get_root(), '', $exclude ), '/' ) ); ?>
18
 
19
- <tr>
20
 
21
- <th scope="row">
22
 
23
- <?php if ( $exclude_path->isFile() ) { ?>
24
 
25
- <div class="dashicons dashicons-media-default"></div>
26
 
27
- <?php } elseif ( $exclude_path->isDir() ) { ?>
28
 
29
- <div class="dashicons dashicons-portfolio"></div>
30
 
31
- <?php } ?>
32
 
33
- </th>
34
 
35
- <td>
36
- <code><?php echo esc_html( str_ireplace( $schedule->backup->get_root(), '', $exclude ) ); ?></code>
37
- </td>
38
 
39
- <td>
40
 
41
- <?php if ( ( in_array( $exclude, $schedule->backup->default_excludes() ) ) || ( hmbkp_path() === untrailingslashit( $exclude ) ) ) : ?>
42
 
43
- <?php _e( 'Default rule', 'backupwordpress' ); ?>
44
 
45
- <?php elseif ( defined( 'HMBKP_EXCLUDE' ) && false !== strpos( HMBKP_EXCLUDE, $exclude ) ) : ?>
46
 
47
- <?php _e( 'Defined in wp-config.php', 'backupwordpress' ); ?>
48
 
49
- <?php else : ?>
50
 
51
- <a href="<?php echo hmbkp_admin_action_url( 'remove_exclude_rule', array(
52
- 'hmbkp_remove_exclude' => $exclude,
53
- 'hmbkp_schedule_id' => $schedule->get_id()
54
- ) ); ?>" class="delete-action"><?php _e( 'Stop excluding', 'backupwordpress' ); ?></a>
55
 
56
- <?php endif; ?>
 
 
 
57
 
58
- </td>
59
 
60
- </tr>
61
 
62
- <?php endforeach; ?>
63
 
64
- </tbody>
65
 
66
- </table>
67
 
68
- <?php endif; ?>
69
 
70
  <h3 id="directory-listing"><?php _e( 'Your Site', 'backupwordpress' ); ?></h3>
71
 
@@ -74,23 +79,25 @@
74
  <?php
75
 
76
  // The directory to display
77
- $directory = $schedule->backup->get_root();
78
 
79
  if ( isset( $_GET['hmbkp_directory_browse'] ) ) {
80
 
81
  $untrusted_directory = urldecode( $_GET['hmbkp_directory_browse'] );
82
 
83
  // Only allow real sub directories of the site root to be browsed
84
- if ( false !== strpos( $untrusted_directory, $schedule->backup->get_root() ) && is_dir( $untrusted_directory ) ) {
85
  $directory = $untrusted_directory;
86
  }
87
 
88
  }
89
 
90
- $exclude_string = $schedule->backup->exclude_string( 'regex' );
 
 
91
 
92
  // Kick off a recursive filesize scan
93
- $files = $schedule->list_directory_by_total_filesize( $directory ); ?>
94
 
95
  <table class="widefat">
96
 
@@ -113,12 +120,12 @@
113
 
114
  <th scope="col">
115
 
116
- <?php if ( $schedule->backup->get_root() !== $directory ) { ?>
117
 
118
- <a href="<?php echo esc_url( remove_query_arg( 'hmbkp_directory_browse' ) ); ?>"><?php echo esc_html( $schedule->backup->get_root() ); ?></a>
119
  <code>/</code>
120
 
121
- <?php $parents = array_filter( explode( '/', str_replace( trailingslashit( $schedule->backup->get_root() ), '', trailingslashit( dirname( $directory ) ) ) ) );
122
 
123
  foreach ( $parents as $directory_basename ) { ?>
124
 
@@ -131,7 +138,7 @@
131
 
132
  <?php } else { ?>
133
 
134
- <?php echo esc_html( $schedule->backup->get_root() ); ?>
135
 
136
  <?php } ?>
137
 
@@ -139,15 +146,15 @@
139
 
140
  <td class="column-filesize">
141
 
142
- <?php if ( $schedule->is_site_size_being_calculated() ) { ?>
143
 
144
- <span class="spinner"></span>
145
 
146
  <?php } else {
147
 
148
- $root = new SplFileInfo( $schedule->backup->get_root() );
149
 
150
- $size = $schedule->filesize( $root, true );
151
 
152
  if ( false !== $size ) {
153
 
@@ -162,7 +169,7 @@
162
  <?php echo esc_html( $size ); ?>
163
 
164
  <a class="dashicons dashicons-update"
165
- href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'hmbkp_recalculate_directory_filesize', urlencode( $schedule->backup->get_root() ) ), 'hmbkp-recalculate_directory_filesize' ) ); ?>"><span><?php _e( 'Refresh', 'backupwordpress' ); ?></span></a>
166
 
167
  </code>
168
 
@@ -172,16 +179,16 @@
172
  <?php } ?>
173
 
174
  <td>
175
- <?php echo esc_html( substr( sprintf( '%o', fileperms( $schedule->backup->get_root() ) ), - 4 ) ); ?>
176
  </td>
177
 
178
  <td>
179
 
180
- <?php if ( is_link( $schedule->backup->get_root() ) ) {
181
 
182
  _e( 'Symlink', 'backupwordpress' );
183
 
184
- } elseif ( is_dir( $schedule->backup->get_root() ) ) {
185
 
186
  _e( 'Folder', 'backupwordpress' );
187
 
@@ -204,7 +211,7 @@
204
  $is_excluded = $is_unreadable = false;
205
 
206
  // Check if the file is excluded
207
- if ( $exclude_string && preg_match( '(' . $exclude_string . ')', str_ireplace( trailingslashit( $schedule->backup->get_root() ), '', wp_normalize_path( $file->getPathname() ) ) ) ) {
208
  $is_excluded = true;
209
  }
210
 
@@ -238,17 +245,17 @@
238
  <?php if ( $is_unreadable ) { ?>
239
 
240
  <code class="strikethrough"
241
- title="<?php echo esc_attr( $file->getRealPath() ); ?>"><?php echo esc_html( $file->getBasename() ); ?></code>
242
 
243
  <?php } elseif ( $file->isFile() ) { ?>
244
 
245
  <code
246
- title="<?php echo esc_attr( $file->getRealPath() ); ?>"><?php echo esc_html( $file->getBasename() ); ?></code>
247
 
248
  <?php } elseif ( $file->isDir() ) { ?>
249
 
250
- <code title="<?php echo esc_attr( $file->getRealPath() ); ?>"><a
251
- href="<?php echo esc_url( add_query_arg( 'hmbkp_directory_browse', urlencode( $file->getPathname() ) ) ); ?>"><?php echo esc_html( $file->getBasename() ); ?></a></code>
252
 
253
  <?php } ?>
254
 
@@ -256,13 +263,13 @@
256
 
257
  <td class="column-format column-filesize">
258
 
259
- <?php if ( $file->isDir() && $schedule->is_site_size_being_calculated() ) { ?>
260
 
261
- <span class="spinner"></span>
262
 
263
  <?php } else {
264
 
265
- $size = $schedule->filesize( $file );
266
 
267
  if ( false !== $size ) {
268
 
@@ -280,7 +287,7 @@
280
 
281
  <a title="<?php _e( 'Recalculate the size of this directory', 'backupwordpress' ); ?>"
282
  class="dashicons dashicons-update"
283
- href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'hmbkp_recalculate_directory_filesize', urlencode( $file->getPathname() ) ), 'hmbkp-recalculate_directory_filesize' ) ); ?>"><span><?php _e( 'Refresh', 'backupwordpress' ); ?></span></a>
284
 
285
  <?php } ?>
286
 
@@ -304,7 +311,7 @@
304
  <?php if ( $file->isLink() ) { ?>
305
 
306
  <span
307
- title="<?php echo esc_attr( $file->GetRealPath() ); ?>"><?php _e( 'Symlink', 'backupwordpress' ); ?></span>
308
 
309
  <?php } elseif ( $file->isDir() ) {
310
 
@@ -335,7 +342,7 @@
335
 
336
  // Excluded directories need to be trailingslashed
337
  if ( $file->isDir() ) {
338
- $exclude_path = trailingslashit( $file->getPathname() );
339
  } ?>
340
 
341
  <a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array(
@@ -367,7 +374,7 @@
367
 
368
 
369
  <p class="submit">
370
- <a href="<?php echo esc_url( hmbkp_get_settings_url() ) ?>"
371
  class="button-primary"><?php _e( 'Done', 'backupwordpress' ); ?></a>
372
  </p>
373
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ $excludes = $schedule->get_excludes();
6
+ $user_excludes = $excludes->get_user_excludes(); ?>
7
+
8
  <div class="hmbkp-exclude-settings">
9
 
10
+ <h3>
11
+ <?php _e( 'Currently Excluded', 'backupwordpress' ); ?>
12
+ </h3>
13
 
14
+ <p><?php _e( 'We automatically detect and ignore common <abbr title="Version Control Systems">VCS</abbr> folders and other backup plugin folders.', 'backupwordpress' ); ?></p>
 
 
15
 
16
+ <table class="widefat">
17
 
18
+ <tbody>
19
 
20
+ <?php foreach ( $user_excludes as $key => $exclude ) :
21
 
22
+ $exclude_path = new \SplFileInfo( trailingslashit( Path::get_root() ) . ltrim( str_ireplace( Path::get_root(), '', $exclude ), '/' ) ); ?>
23
 
24
+ <tr>
25
 
26
+ <th scope="row">
27
 
28
+ <?php if ( $exclude_path->isFile() ) { ?>
29
 
30
+ <div class="dashicons dashicons-media-default"></div>
31
 
32
+ <?php } elseif ( $exclude_path->isDir() ) { ?>
33
 
34
+ <div class="dashicons dashicons-portfolio"></div>
35
 
36
+ <?php } ?>
37
 
38
+ </th>
39
 
40
+ <td>
41
 
42
+ <code><?php echo esc_html( str_ireplace( Path::get_root(), '', $exclude ) ); ?></code>
 
 
43
 
44
+ </td>
45
 
46
+ <td>
47
 
48
+ <?php if ( ( in_array( $exclude, $excludes->get_default_excludes() ) ) || ( Path::get_path() === trailingslashit( Path::get_root() ) . untrailingslashit( $exclude ) ) ) : ?>
49
 
50
+ <?php _e( 'Default rule', 'backupwordpress' ); ?>
51
 
52
+ <?php elseif ( defined( 'HMBKP_EXCLUDE' ) && false !== strpos( HMBKP_EXCLUDE, $exclude ) ) : ?>
53
 
54
+ <?php _e( 'Defined in wp-config.php', 'backupwordpress' ); ?>
55
 
56
+ <?php else : ?>
 
 
 
57
 
58
+ <a href="<?php echo admin_action_url( 'remove_exclude_rule', array(
59
+ 'hmbkp_remove_exclude' => $exclude,
60
+ 'hmbkp_schedule_id' => $schedule->get_id()
61
+ ) ); ?>" class="delete-action"><?php _e( 'Stop excluding', 'backupwordpress' ); ?></a>
62
 
63
+ <?php endif; ?>
64
 
65
+ </td>
66
 
67
+ </tr>
68
 
69
+ <?php endforeach; ?>
70
 
71
+ </tbody>
72
 
73
+ </table>
74
 
75
  <h3 id="directory-listing"><?php _e( 'Your Site', 'backupwordpress' ); ?></h3>
76
 
79
  <?php
80
 
81
  // The directory to display
82
+ $directory = Path::get_root();
83
 
84
  if ( isset( $_GET['hmbkp_directory_browse'] ) ) {
85
 
86
  $untrusted_directory = urldecode( $_GET['hmbkp_directory_browse'] );
87
 
88
  // Only allow real sub directories of the site root to be browsed
89
+ if ( false !== strpos( $untrusted_directory, Path::get_root() ) && is_dir( $untrusted_directory ) ) {
90
  $directory = $untrusted_directory;
91
  }
92
 
93
  }
94
 
95
+ $exclude_string = implode( '|', $excludes->get_excludes_for_regex() );
96
+
97
+ $site_size = new Site_Size;
98
 
99
  // Kick off a recursive filesize scan
100
+ $files = list_directory_by_total_filesize( $directory ); ?>
101
 
102
  <table class="widefat">
103
 
120
 
121
  <th scope="col">
122
 
123
+ <?php if ( Path::get_root() !== $directory ) { ?>
124
 
125
+ <a href="<?php echo esc_url( remove_query_arg( 'hmbkp_directory_browse' ) ); ?>"><?php echo esc_html( Path::get_root() ); ?></a>
126
  <code>/</code>
127
 
128
+ <?php $parents = array_filter( explode( '/', str_replace( trailingslashit( Path::get_root() ), '', trailingslashit( dirname( $directory ) ) ) ) );
129
 
130
  foreach ( $parents as $directory_basename ) { ?>
131
 
138
 
139
  <?php } else { ?>
140
 
141
+ <?php echo esc_html( Path::get_root() ); ?>
142
 
143
  <?php } ?>
144
 
146
 
147
  <td class="column-filesize">
148
 
149
+ <?php if ( Site_Size::is_site_size_being_calculated() ) { ?>
150
 
151
+ <span class="spinner is-active"></span>
152
 
153
  <?php } else {
154
 
155
+ $root = new \SplFileInfo( Path::get_root() );
156
 
157
+ $size = $site_size->filesize( $root );
158
 
159
  if ( false !== $size ) {
160
 
169
  <?php echo esc_html( $size ); ?>
170
 
171
  <a class="dashicons dashicons-update"
172
+ href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'hmbkp_recalculate_directory_filesize', urlencode( Path::get_root() ) ), 'hmbkp-recalculate_directory_filesize' ) ); ?>"><span><?php _e( 'Refresh', 'backupwordpress' ); ?></span></a>
173
 
174
  </code>
175
 
179
  <?php } ?>
180
 
181
  <td>
182
+ <?php echo esc_html( substr( sprintf( '%o', fileperms( Path::get_root() ) ), - 4 ) ); ?>
183
  </td>
184
 
185
  <td>
186
 
187
+ <?php if ( is_link( Path::get_root() ) ) {
188
 
189
  _e( 'Symlink', 'backupwordpress' );
190
 
191
+ } elseif ( is_dir( Path::get_root() ) ) {
192
 
193
  _e( 'Folder', 'backupwordpress' );
194
 
211
  $is_excluded = $is_unreadable = false;
212
 
213
  // Check if the file is excluded
214
+ if ( $exclude_string && preg_match( '(' . $exclude_string . ')', str_ireplace( trailingslashit( Path::get_root() ), '', wp_normalize_path( $file->getPathname() ) ) ) ) {
215
  $is_excluded = true;
216
  }
217
 
245
  <?php if ( $is_unreadable ) { ?>
246
 
247
  <code class="strikethrough"
248
+ title="<?php echo esc_attr( wp_normalize_path( $file->getRealPath() ) ); ?>"><?php echo esc_html( $file->getBasename() ); ?></code>
249
 
250
  <?php } elseif ( $file->isFile() ) { ?>
251
 
252
  <code
253
+ title="<?php echo esc_attr( wp_normalize_path( $file->getRealPath() ) ); ?>"><?php echo esc_html( $file->getBasename() ); ?></code>
254
 
255
  <?php } elseif ( $file->isDir() ) { ?>
256
 
257
+ <code title="<?php echo esc_attr( wp_normalize_path( $file->getRealPath() ) ); ?>"><a
258
+ href="<?php echo esc_url( add_query_arg( 'hmbkp_directory_browse', urlencode( wp_normalize_path( $file->getPathname() ) ) ) ); ?>"><?php echo esc_html( $file->getBasename() ); ?></a></code>
259
 
260
  <?php } ?>
261
 
263
 
264
  <td class="column-format column-filesize">
265
 
266
+ <?php if ( $file->isDir() && Site_Size::is_site_size_being_calculated() ) { ?>
267
 
268
+ <span class="spinner is-active"></span>
269
 
270
  <?php } else {
271
 
272
+ $size = $site_size->filesize( $file );
273
 
274
  if ( false !== $size ) {
275
 
287
 
288
  <a title="<?php _e( 'Recalculate the size of this directory', 'backupwordpress' ); ?>"
289
  class="dashicons dashicons-update"
290
+ href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'hmbkp_recalculate_directory_filesize', urlencode( wp_normalize_path( $file->getPathname() ) ) ), 'hmbkp-recalculate_directory_filesize' ) ); ?>"><span><?php _e( 'Refresh', 'backupwordpress' ); ?></span></a>
291
 
292
  <?php } ?>
293
 
311
  <?php if ( $file->isLink() ) { ?>
312
 
313
  <span
314
+ title="<?php echo esc_attr( wp_normalize_path( $file->getRealPath() ) ); ?>"><?php _e( 'Symlink', 'backupwordpress' ); ?></span>
315
 
316
  <?php } elseif ( $file->isDir() ) {
317
 
342
 
343
  // Excluded directories need to be trailingslashed
344
  if ( $file->isDir() ) {
345
+ $exclude_path = trailingslashit( wp_normalize_path( $file->getPathname() ) );
346
  } ?>
347
 
348
  <a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array(
374
 
375
 
376
  <p class="submit">
377
+ <a href="<?php echo esc_url( get_settings_url() ) ?>"
378
  class="button-primary"><?php _e( 'Done', 'backupwordpress' ); ?></a>
379
  </p>
380
 
admin/schedule-form.php CHANGED
@@ -1,6 +1,12 @@
 
 
 
 
 
 
1
  <h3><?php esc_html_e( 'Settings', 'backupwordpress' ); ?></h3>
2
 
3
- <?php $hmbkp_form_errors = hmbkp_get_settings_errors(); ?>
4
 
5
  <?php if ( ! empty( $hmbkp_form_errors ) ) { ?>
6
 
@@ -15,7 +21,7 @@
15
  <?php }
16
 
17
  // We can clear them now we've displayed them
18
- hmbkp_clear_settings_errors();
19
 
20
  ?>
21
 
@@ -64,7 +70,7 @@ hmbkp_clear_settings_errors();
64
 
65
  <option value="manually"><?php _e( 'Manual Only', 'backupwordpress' ); ?></option>
66
 
67
- <?php foreach ( $schedule->get_cron_schedules() as $cron_schedule => $cron_details ) : ?>
68
 
69
  <option <?php selected( $schedule->get_reoccurrence(), $cron_schedule ); ?> value="<?php echo esc_attr( $cron_schedule ); ?>">
70
 
@@ -149,7 +155,7 @@ hmbkp_clear_settings_errors();
149
  <?php _e( 'Minutes', 'backupwordpress' ); ?></label>
150
 
151
  </span>
152
-
153
  <p class="description">
154
  <?php esc_html_e( '24-hour format.', 'backupwordpress' ); ?>
155
  <span class="twice-js <?php if ( $schedule->get_reoccurrence() !== 'fortnightly' ) { ?> hidden<?php } ?>"><?php _e( 'The second backup will run 12 hours after the first.', 'backupwordpress' ); ?><span>
@@ -173,8 +179,12 @@ hmbkp_clear_settings_errors();
173
 
174
  <?php printf( __( 'Past this limit older backups will be deleted automatically.', 'backupwordpress' ) ); ?>
175
 
176
- <?php if ( $schedule->is_site_size_cached() ) {
177
- printf( __( 'This schedule will store a maximum of %s of backups.', 'backupwordpress' ), '<code>' . esc_html( size_format( $schedule->get_site_size() * $schedule->get_max_backups() ) ) . '</code>' );
 
 
 
 
178
  } ?>
179
 
180
  </p>
@@ -183,7 +193,7 @@ hmbkp_clear_settings_errors();
183
 
184
  </tr>
185
 
186
- <?php foreach ( HM\BackUpWordPress\Services::get_services( $schedule ) as $service ) {
187
  $service->field();
188
  } ?>
189
 
@@ -191,9 +201,6 @@ hmbkp_clear_settings_errors();
191
 
192
  </table>
193
 
194
- <p class="submit">
195
- <button type="submit" class="button-primary"><?php _e( 'Done', 'backupwordpress' ); ?></button>
196
- </p>
197
-
198
 
199
  </form>
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ ?>
6
+
7
  <h3><?php esc_html_e( 'Settings', 'backupwordpress' ); ?></h3>
8
 
9
+ <?php $hmbkp_form_errors = get_settings_errors(); ?>
10
 
11
  <?php if ( ! empty( $hmbkp_form_errors ) ) { ?>
12
 
21
  <?php }
22
 
23
  // We can clear them now we've displayed them
24
+ clear_settings_errors();
25
 
26
  ?>
27
 
70
 
71
  <option value="manually"><?php _e( 'Manual Only', 'backupwordpress' ); ?></option>
72
 
73
+ <?php foreach ( get_cron_schedules() as $cron_schedule => $cron_details ) : ?>
74
 
75
  <option <?php selected( $schedule->get_reoccurrence(), $cron_schedule ); ?> value="<?php echo esc_attr( $cron_schedule ); ?>">
76
 
155
  <?php _e( 'Minutes', 'backupwordpress' ); ?></label>
156
 
157
  </span>
158
+
159
  <p class="description">
160
  <?php esc_html_e( '24-hour format.', 'backupwordpress' ); ?>
161
  <span class="twice-js <?php if ( $schedule->get_reoccurrence() !== 'fortnightly' ) { ?> hidden<?php } ?>"><?php _e( 'The second backup will run 12 hours after the first.', 'backupwordpress' ); ?><span>
179
 
180
  <?php printf( __( 'Past this limit older backups will be deleted automatically.', 'backupwordpress' ) ); ?>
181
 
182
+ <?php
183
+
184
+ $site_size = new Site_Size;
185
+
186
+ if ( Site_Size::is_site_size_cached() ) {
187
+ printf( __( 'This schedule will store a maximum of %s of backups.', 'backupwordpress' ), '<code>' . esc_html( size_format( $site_size->get_site_size( $schedule->get_type(), $schedule->get_excludes() ) * $schedule->get_max_backups() ) ) . '</code>' );
188
  } ?>
189
 
190
  </p>
193
 
194
  </tr>
195
 
196
+ <?php foreach ( Services::get_services( $schedule ) as $service ) {
197
  $service->field();
198
  } ?>
199
 
201
 
202
  </table>
203
 
204
+ <?php submit_button( __( 'Done', 'backupwordpress' ) ); ?>
 
 
 
205
 
206
  </form>
admin/schedule-sentence.php CHANGED
@@ -1,15 +1,21 @@
1
  <?php
2
 
3
- $filesize = hmbkp_get_site_size_text( $schedule );
 
 
4
 
5
  // Backup Type
6
- $type = strtolower( hmbkp_human_get_type( $schedule->get_type() ) );
7
 
8
  // Backup Time
9
  $day = date_i18n( 'l', $schedule->get_next_occurrence( false ) );
10
 
 
11
  $next_backup = 'title="' . esc_attr( sprintf( __( 'The next backup will be on %1$s at %2$s %3$s', 'backupwordpress' ), date_i18n( get_option( 'date_format' ), $schedule->get_next_occurrence( false ) ), date_i18n( get_option( 'time_format' ), $schedule->get_next_occurrence( false ) ), date_i18n( 'T', $schedule->get_next_occurrence( false ) ) ) ) . '"';
12
 
 
 
 
13
  // Backup Re-occurrence
14
  switch ( $schedule->get_reoccurrence() ) :
15
 
@@ -67,8 +73,8 @@ switch ( $schedule->get_reoccurrence() ) :
67
 
68
  endswitch;
69
 
70
- $server = '<span title="' . esc_attr( hmbkp_path() ) . '">' . __( 'this server', 'backupwordpress' ) . '</span>';
71
- $server = '<code>' . esc_attr( str_replace( HM\BackUpWordPress\Backup::get_home_path(), '', hmbkp_path() ) ) . '</code>';
72
 
73
  // Backup to keep
74
  switch ( $schedule->get_max_backups() ) :
@@ -81,7 +87,7 @@ switch ( $schedule->get_max_backups() ) :
81
 
82
  case 0 :
83
 
84
- $backup_to_keep = sprintf( __( 'don\'t store any backups in on this server', 'backupwordpress' ), hmbkp_path() );
85
 
86
  break;
87
 
@@ -93,7 +99,7 @@ endswitch;
93
 
94
  $email_msg = $services = '';
95
 
96
- foreach ( HM\BackUpWordPress\Services::get_services( $schedule ) as $file => $service ) {
97
 
98
  if ( is_wp_error( $service ) ) {
99
  $email_msg = $service->get_error_message();
@@ -113,22 +119,22 @@ if ( ! empty( $services ) && count( $services ) > 1 ) {
113
 
114
  } ?>
115
 
116
- <div class="hmbkp-schedule-sentence<?php if ( $schedule->get_status() ) { ?> hmbkp-running<?php } ?>">
117
 
118
  <?php $sentence = sprintf( _x( 'Backup my %1$s %2$s %3$s, %4$s.', '1: Backup Type 2: Total size of backup 3: Schedule 4: Number of backups to store', 'backupwordpress' ), '<span>' . esc_html( $type ) . '</span>', $filesize, $reoccurrence, $backup_to_keep );
119
 
120
  if ( $email_msg ) {
121
- $sentence .= sprintf( __( '%s. ', 'backupwordpress' ), $email_msg );
122
  }
123
 
124
- if ( $services ) {
125
- $sentence .= sprintf( __( 'Send a copy of each backup to %s.', 'backupwordpress' ), implode( ', ', array_filter( $services ) ) );
126
  }
127
 
128
  echo $sentence; ?>
129
 
130
- <?php if ( HM\BackUpWordPress\Schedules::get_instance()->get_schedule( $schedule->get_id() ) ) {
131
- hmbkp_schedule_status( $schedule );
132
  } ?>
133
 
134
  <?php require( HMBKP_PLUGIN_PATH . 'admin/schedule-settings.php' ); ?>
@@ -145,19 +151,21 @@ if ( ! empty( $services ) && count( $services ) > 1 ) {
145
  *
146
  * @return string
147
  */
148
- function hmbkp_get_site_size_text( HM\BackUpWordPress\Scheduled_Backup $schedule ) {
149
 
150
  if ( isset( $_GET['hmbkp_add_schedule'] ) ) {
151
-
152
  return '';
 
 
 
153
 
154
- } elseif ( ( 'database' === $schedule->get_type() ) || $schedule->is_site_size_cached() ) {
155
 
156
- return sprintf( '(<code title="' . __( 'Backups will be compressed and should be smaller than this.', 'backupwordpress' ) . '">%s</code>)', esc_attr( $schedule->get_formatted_site_size( true ) ) );
157
 
158
  } else {
159
 
160
- return sprintf( '(<code class="calculating" title="' . __( 'this shouldn\'t take long&hellip;', 'backupwordpress' ) . '">' . __( 'calculating the size of your backup&hellip;', 'backupwordpress' ) . '</code>)' );
161
 
162
  }
163
 
1
  <?php
2
 
3
+ namespace HM\BackUpWordPress;
4
+
5
+ $filesize = get_site_size_text( $schedule );
6
 
7
  // Backup Type
8
+ $type = strtolower( human_get_type( $schedule->get_type() ) );
9
 
10
  // Backup Time
11
  $day = date_i18n( 'l', $schedule->get_next_occurrence( false ) );
12
 
13
+ // Next Backup
14
  $next_backup = 'title="' . esc_attr( sprintf( __( 'The next backup will be on %1$s at %2$s %3$s', 'backupwordpress' ), date_i18n( get_option( 'date_format' ), $schedule->get_next_occurrence( false ) ), date_i18n( get_option( 'time_format' ), $schedule->get_next_occurrence( false ) ), date_i18n( 'T', $schedule->get_next_occurrence( false ) ) ) ) . '"';
15
 
16
+ // Backup status
17
+ $status = new Backup_Status( $schedule->get_id() );
18
+
19
  // Backup Re-occurrence
20
  switch ( $schedule->get_reoccurrence() ) :
21
 
73
 
74
  endswitch;
75
 
76
+ $server = '<span title="' . esc_attr( Path::get_path() ) . '">' . __( 'this server', 'backupwordpress' ) . '</span>';
77
+ $server = '<code>' . esc_attr( str_replace( Path::get_home_path(), '', Path::get_path() ) ) . '</code>';
78
 
79
  // Backup to keep
80
  switch ( $schedule->get_max_backups() ) :
87
 
88
  case 0 :
89
 
90
+ $backup_to_keep = sprintf( __( 'don\'t store any backups in on this server', 'backupwordpress' ), Path::get_path() );
91
 
92
  break;
93
 
99
 
100
  $email_msg = $services = '';
101
 
102
+ foreach ( Services::get_services( $schedule ) as $file => $service ) {
103
 
104
  if ( is_wp_error( $service ) ) {
105
  $email_msg = $service->get_error_message();
119
 
120
  } ?>
121
 
122
+ <div class="hmbkp-schedule-sentence<?php if ( $status->get_status() ) { ?> hmbkp-running<?php } ?>">
123
 
124
  <?php $sentence = sprintf( _x( 'Backup my %1$s %2$s %3$s, %4$s.', '1: Backup Type 2: Total size of backup 3: Schedule 4: Number of backups to store', 'backupwordpress' ), '<span>' . esc_html( $type ) . '</span>', $filesize, $reoccurrence, $backup_to_keep );
125
 
126
  if ( $email_msg ) {
127
+ $sentence .= ' ' . $email_msg;
128
  }
129
 
130
+ if ( array_filter( $services ) ) {
131
+ $sentence .= ' ' . sprintf( __( 'Send a copy of each backup to %s.', 'backupwordpress' ), implode( ', ', array_filter( $services ) ) );
132
  }
133
 
134
  echo $sentence; ?>
135
 
136
+ <?php if ( Schedules::get_instance()->get_schedule( $schedule->get_id() ) ) {
137
+ schedule_status( $schedule );
138
  } ?>
139
 
140
  <?php require( HMBKP_PLUGIN_PATH . 'admin/schedule-settings.php' ); ?>
151
  *
152
  * @return string
153
  */
154
+ function get_site_size_text( Scheduled_Backup $schedule ) {
155
 
156
  if ( isset( $_GET['hmbkp_add_schedule'] ) ) {
 
157
  return '';
158
+ }
159
+
160
+ $site_size = new Site_Size( $schedule->get_type(), $schedule->get_excludes() );
161
 
162
+ if ( ( 'database' === $schedule->get_type() ) || $site_size->is_site_size_cached() ) {
163
 
164
+ return sprintf( '(<code title="' . __( 'Backups will be compressed and should be smaller than this.', 'backupwordpress' ) . '">%s</code>)', esc_attr( $site_size->get_formatted_site_size() ) );
165
 
166
  } else {
167
 
168
+ return sprintf( '(<code class="calculating" title="' . __( 'this shouldn\'t take long&hellip;', 'backupwordpress' ) . '">' . __( 'calculating the size of your site&hellip;', 'backupwordpress' ) . '</code>)' );
169
 
170
  }
171
 
admin/schedule-settings.php CHANGED
@@ -1,26 +1,28 @@
1
  <?php
2
 
3
- if ( HM\BackUpWordPress\Schedules::get_instance()->get_schedule( $schedule->get_id() ) ) { ?>
 
 
4
 
5
  <div class="hmbkp-schedule-actions row-actions">
6
 
7
  <a class="hmbkp-run" href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'hmbkp_run_schedule', 'hmbkp_schedule_id' => $schedule->get_id() ), admin_url( 'admin-ajax.php' ) ), 'hmbkp_run_schedule', 'hmbkp_run_schedule_nonce' ) ); ?>"><?php _e( 'Run now', 'backupwordpress' ); ?></a> |
8
 
9
- <a href="<?php echo esc_url( add_query_arg( array( 'action' => 'hmbkp_edit_schedule', 'hmbkp_panel' => 'hmbkp_edit_schedule_settings', 'hmbkp_schedule_id' => $schedule->get_id() ), hmbkp_get_settings_url() ), 'hmbkp-edit-schedule' ); ?>"><?php _e( 'Settings', 'backupwordpress' ); ?></a> |
10
 
11
  <?php
12
 
13
  // Only show excludes if we are backing up files
14
  if ( 'database' !== $schedule->get_type() ) : ?>
15
- <a href="<?php echo esc_url( add_query_arg( array( 'action' => 'hmbkp_edit_schedule', 'hmbkp_panel' => 'hmbkp_edit_schedule_excludes', 'hmbkp_schedule_id' => $schedule->get_id() ), hmbkp_get_settings_url() ) ); ?>"><?php _e( 'Excludes', 'backupwordpress' ); ?></a> |
16
  <?php endif; ?>
17
 
18
- <?php foreach ( HM\BackUpWordPress\Services::get_services( $schedule ) as $service ) :
19
 
20
  if ( ! $service->has_form() )
21
  continue; ?>
22
 
23
- <a href="<?php echo esc_url( add_query_arg( array( 'action' => 'hmbkp_edit_schedule', 'hmbkp_panel' => 'hmbkp_edit_schedule_settings_' . $service->get_slug() , 'hmbkp_schedule_id' => $schedule->get_id() ), hmbkp_get_settings_url() ) ); ?>"><?php echo esc_html( $service->name ); ?></a> |
24
 
25
  <?php endforeach; ?>
26
 
@@ -45,7 +47,7 @@ if ( HM\BackUpWordPress\Schedules::get_instance()->get_schedule( $schedule->get_
45
  } ?>
46
 
47
  <?php // Show the service form if we are viewing one
48
- foreach ( HM\BackUpWordPress\Services::get_services( $schedule ) as $service ) : ?>
49
 
50
  <?php if ( $_GET['action'] === 'hmbkp_edit_schedule' && $_GET['hmbkp_panel'] === 'hmbkp_edit_schedule_settings_' . $service->get_slug() ) { ?>
51
 
@@ -53,7 +55,7 @@ if ( HM\BackUpWordPress\Schedules::get_instance()->get_schedule( $schedule->get_
53
 
54
  <?php
55
 
56
- $hmbkp_form_errors = hmbkp_get_settings_errors();
57
 
58
  if ( ! empty( $hmbkp_form_errors ) ) :
59
 
@@ -72,7 +74,7 @@ if ( HM\BackUpWordPress\Schedules::get_instance()->get_schedule( $schedule->get_
72
  endif;
73
 
74
  // We can clear them now we've displayed them
75
- hmbkp_clear_settings_errors();
76
 
77
  ?>
78
 
1
  <?php
2
 
3
+ namespace HM\BackUpWordPress;
4
+
5
+ if ( Schedules::get_instance()->get_schedule( $schedule->get_id() ) ) { ?>
6
 
7
  <div class="hmbkp-schedule-actions row-actions">
8
 
9
  <a class="hmbkp-run" href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'hmbkp_run_schedule', 'hmbkp_schedule_id' => $schedule->get_id() ), admin_url( 'admin-ajax.php' ) ), 'hmbkp_run_schedule', 'hmbkp_run_schedule_nonce' ) ); ?>"><?php _e( 'Run now', 'backupwordpress' ); ?></a> |
10
 
11
+ <a href="<?php echo esc_url( add_query_arg( array( 'action' => 'hmbkp_edit_schedule', 'hmbkp_panel' => 'hmbkp_edit_schedule_settings', 'hmbkp_schedule_id' => $schedule->get_id() ), get_settings_url() ), 'hmbkp-edit-schedule' ); ?>"><?php _e( 'Settings', 'backupwordpress' ); ?></a> |
12
 
13
  <?php
14
 
15
  // Only show excludes if we are backing up files
16
  if ( 'database' !== $schedule->get_type() ) : ?>
17
+ <a href="<?php echo esc_url( add_query_arg( array( 'action' => 'hmbkp_edit_schedule', 'hmbkp_panel' => 'hmbkp_edit_schedule_excludes', 'hmbkp_schedule_id' => $schedule->get_id() ), get_settings_url() ) ); ?>"><?php _e( 'Excludes', 'backupwordpress' ); ?></a> |
18
  <?php endif; ?>
19
 
20
+ <?php foreach ( Services::get_services( $schedule ) as $service ) :
21
 
22
  if ( ! $service->has_form() )
23
  continue; ?>
24
 
25
+ <a href="<?php echo esc_url( add_query_arg( array( 'action' => 'hmbkp_edit_schedule', 'hmbkp_panel' => 'hmbkp_edit_schedule_settings_' . $service->get_slug() , 'hmbkp_schedule_id' => $schedule->get_id() ), get_settings_url() ) ); ?>"><?php echo esc_html( $service->name ); ?></a> |
26
 
27
  <?php endforeach; ?>
28
 
47
  } ?>
48
 
49
  <?php // Show the service form if we are viewing one
50
+ foreach ( Services::get_services( $schedule ) as $service ) : ?>
51
 
52
  <?php if ( $_GET['action'] === 'hmbkp_edit_schedule' && $_GET['hmbkp_panel'] === 'hmbkp_edit_schedule_settings_' . $service->get_slug() ) { ?>
53
 
55
 
56
  <?php
57
 
58
+ $hmbkp_form_errors = get_settings_errors();
59
 
60
  if ( ! empty( $hmbkp_form_errors ) ) :
61
 
74
  endif;
75
 
76
  // We can clear them now we've displayed them
77
+ clear_settings_errors();
78
 
79
  ?>
80
 
assets/hmbkp.js CHANGED
@@ -98,7 +98,7 @@ jQuery( document ).ready( function ( $ ) {
98
  $( '.hmbkp-status' ).replaceWith( data.hmbkp_schedule_status );
99
  }
100
 
101
- if ( (data.hmbkp_site_size !== undefined ) && ( $( 'code.calculating' ).length ) ) {
102
  $( 'code.calculating' ).text( data.hmbkp_site_size );
103
  var excludes = $( '.hmbkp-exclude-settings' );
104
  if ( excludes.length ) {
@@ -116,6 +116,15 @@ jQuery( document ).ready( function ( $ ) {
116
 
117
  } );
118
 
 
 
 
 
 
 
 
 
 
119
  } );
120
 
121
  function hmbkpToggleScheduleFields( recurrence ) {
@@ -168,48 +177,3 @@ function hmbkpToggleScheduleFields( recurrence ) {
168
  }
169
 
170
  }
171
-
172
- function hmbkpCatchResponseAndOfferToEmail( data ) {
173
-
174
- // Backup Succeeded
175
- if ( ! data || data === 0 ) {
176
- location.reload( true );
177
- }
178
-
179
- // The backup failed, show the error and offer to have it emailed back
180
- else {
181
-
182
- jQuery( '.hmbkp-schedule-sentence.hmbkp-running' ).removeClass( 'hmbkp-running' ).addClass( 'hmbkp-error' );
183
-
184
- jQuery.post(
185
- ajaxurl,
186
- {'nonce': hmbkp.nonce, 'action': 'hmbkp_backup_error', 'hmbkp_error': data},
187
- function ( data ) {
188
-
189
- if ( ! data || data === 0 ) {
190
- return;
191
- } else {
192
- location.reload( true );
193
- }
194
- }
195
- );
196
-
197
- }
198
-
199
- jQuery( document ).one( 'click', '.hmbkp_send_error_via_email', function ( e ) {
200
-
201
- e.preventDefault();
202
-
203
- jQuery( this ).addClass( 'hmbkp-ajax-loading' ).attr( 'disabled', 'disabled' );
204
-
205
- jQuery.post(
206
- ajaxurl,
207
- {'nonce': hmbkp.nonce, 'action': 'hmbkp_email_error', 'hmbkp_error': data},
208
- function () {
209
- //jQuery.colorbox.close();
210
- }
211
- );
212
-
213
- } );
214
-
215
- }
98
  $( '.hmbkp-status' ).replaceWith( data.hmbkp_schedule_status );
99
  }
100
 
101
+ if ( ( data.hmbkp_site_size !== undefined ) && ( $( 'code.calculating' ).length ) ) {
102
  $( 'code.calculating' ).text( data.hmbkp_site_size );
103
  var excludes = $( '.hmbkp-exclude-settings' );
104
  if ( excludes.length ) {
116
 
117
  } );
118
 
119
+ $( document ).on( 'click', '#hmbkp-warning-backup .notice-dismiss', function(){
120
+ $.post(
121
+ ajaxurl,
122
+ {
123
+ 'action': 'hmbkp_dismiss_error'
124
+ }
125
+ );
126
+ } )
127
+
128
  } );
129
 
130
  function hmbkpToggleScheduleFields( recurrence ) {
177
  }
178
 
179
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/hmbkp.min.css CHANGED
@@ -1 +1 @@
1
- .hmbkp_active td:first-child>code:before{content:"\00a0 \2713 ";font-size:11px}.hmbkp_active{background:#E5F7E8}div#hmbkp-warning,h2.nav-tab-wrapper+div[id^=hmbkp]{margin:20px 0 15px}h2+div[id^=hmbkp] input{max-width:100%}.nav-tab.hmbkp-running:not(.nav-tab-active):before{width:20px;height:20px;margin:0 10px -4px 0;content:"";background:transparent url(spinner-2x.gif) no-repeat 0 0;background-size:20px;display:inline-block}.hmbkp-upsell{margin-top:40px;padding-top:16px;border-top:1px solid #ccc}.hmbkp-upsell a{color:#57792b}.hmbkp-upsell a:hover{color:#98c063}.hmbkp-upsell-sep{color:#bbb}.hmbkp-upsell ul{overflow:hidden}.hmbkp-upsell li{float:left;margin:0 20px 20px 0}.hmbkp-upsell img{display:block;margin:0 auto;max-width:120px;min-height:60px}.hmbkp-upsell ul a{display:block;margin-bottom:0;width:120px}.hmbkp-upsell .howto{font-weight:400;display:inline}.hmbkp-upsell .hmbkp_hide{float:right}.hmbkp-schedule-sentence{font-size:16px;font-weight:lighter;margin:20px 0;background-color:#FBFBFB;padding:20px;border:1px solid #e5e5e5;box-shadow:0 1px 1px rgba(0,0,0,.04)}.hmbkp-schedule-sentence::before{content:"\2714";margin-right:10px;width:16px;height:60px;display:block;float:left}.hmbkp-schedule-sentence.hmbkp-error:before{content:"\2718"}.hmbkp-schedule-sentence.hmbkp-running:before{width:20px;height:80px;margin:-1px 10px 0 0;content:"";background:transparent url(spinner-2x.gif) no-repeat 0 0;background-size:20px;display:inline-block}.hmbkp-schedule-sentence .hmbkp-status{display:none;font-size:12px;color:#666;margin:2px 0 0 30px}.hmbkp-schedule-sentence.hmbkp-running .hmbkp-status{display:block}.hmbkp-schedule-sentence.hmbkp-running .hmbkp-schedule-actions{display:none}.hmbkp-schedule-sentence :not(a)[title]{border-bottom:1px dotted #CCC;cursor:help}.hmbkp-schedule-sentence .hmbkp-status[title]{border-bottom:none}.hmbkp-schedule-sentence .submit{padding:0}.hmbkp-schedule-sentence .hmbkp-schedule-actions{visibility:visible;font-size:12px;font-weight:400;margin:0 0 0 26px}.hmbkp-schedule-actions a{white-space:nowrap}.hmbkp-schedule-sentence .row-actions{position:static}.hmbkp-schedule-settings{border-top:1px solid #e5e5e5;margin:20px -20px -20px;background-color:#f5f5f5;padding:0 20px 20px}.hmbkp-ajax-loading,button.hmbkp-ajax-loading{padding-left:20px;position:relative}.hmbkp-ajax-loading::after{content:"";width:16px;height:16px;background-image:url(spinner-2x.gif);background-size:16px 16px;background-repeat:no-repeat;background-position:0 0;position:absolute;right:-30px;top:5px}.delete-action{color:#a00;-webkit-transition:all 300ms ease;transition:all 300ms ease}.delete-action:hover .delete-action:focus{color:red;-webkit-transition:all 300ms ease;transition:all 300ms ease}.strikethrough{text-decoration:line-through}table.widefat tbody tr:nth-child(odd){background-color:#f9f9f9}.hmbkp-exclude-settings td:first-child,.hmbkp-exclude-settings th:first-child{width:20px;padding-right:0}.hmbkp-exclude-settings thead tr:last-child{background-color:#f9f9f9}.hmbkp-exclude-settings table .button-secondary{line-height:18px;height:20px}thead td{border-bottom:1px solid #e1e1e1}.hmbkp-exclude-settings table .spinner{display:block;float:left;margin:0}.hmbkp-schedule-settings .column-format code{white-space:nowrap}.column-filesize code{position:relative}.column-filesize .dashicons-update{display:none;overflow:hidden;position:absolute;width:100%;left:0;text-align:center;background-color:rgba(255,255,255,.8)}.column-filesize .dashicons-update span{display:none}.column-filesize:hover .dashicons-update{display:inline-block}.hmbkp-exclude-settings td span.reason{color:#CCC}.server-info{overflow:auto;max-height:50%;outline:#000 1px solid}.server-info pre{max-height:100px;overflow-x:hidden}.page-title-action span.dashicons-admin-users{position:relative;display:inline-block;vertical-align:middle;top:-2px}pre{background-color:#eee;padding:10px;white-space:pre;max-height:320px;overflow:auto;word-wrap:normal!important}@media screen and (max-width:768px){.wrap h2{padding:10px 0 0}.hmbkp-schedule-sentence::before{height:80px}h2 .nav-tab{display:block;padding:10px;margin:0}.hmbkp-schedule-sentence{margin:10px 0;padding:10px}#intercom-info{display:none}.hmbkp-schedule-settings{padding:0 10px 10px}.hmbkp-exclude-settings table{margin:0 -10px;border-left:none;border-right:none;width:calc(100% + 20px)}.hmbkp-exclude-settings tr>:first-child,.hmbkp-schedule-settings thead tr:nth-child(2),.hmbkp-schedule-settings tr :nth-child(4),.hmbkp-schedule-settings tr :nth-child(5){display:none}.hmbkp-schedule-settings{margin-left:-10px;margin-right:-10px;margin-bottom:-10px}table.widefat tbody tr:nth-child(even){background-color:#fff}table.widefat tbody tr:nth-child(odd){background-color:#f9f9f9}}
1
+ .hmbkp-schedule-actions a,.hmbkp-schedule-settings .column-format code{white-space:nowrap}.hmbkp_active td:first-child>code:before{content:"\00a0 \2713 ";font-size:11px}.hmbkp_active{background:#E5F7E8}div#hmbkp-warning,h2.nav-tab-wrapper+div[id^=hmbkp]{margin:20px 0 15px}h2+div[id^=hmbkp] input{max-width:100%}.nav-tab.hmbkp-running:not(.nav-tab-active):before{width:20px;height:20px;margin:0 10px -4px 0;content:"";background:url(spinner-2x.gif) no-repeat;background-size:20px;display:inline-block}.hmbkp-upsell{margin-top:40px;padding-top:16px;border-top:1px solid #ccc}.hmbkp-upsell a{color:#57792b}.hmbkp-upsell a:hover{color:#98c063}.hmbkp-upsell-sep{color:#bbb}.hmbkp-upsell ul{overflow:hidden}.hmbkp-upsell li{float:left;margin:0 20px 20px 0}.hmbkp-upsell img{display:block;margin:0 auto;max-width:120px;min-height:60px}.hmbkp-upsell ul a{display:block;margin-bottom:0;width:120px}.hmbkp-upsell .howto{font-weight:400;display:inline}.hmbkp-upsell .hmbkp_hide{float:right}.hmbkp-schedule-sentence{font-size:16px;font-weight:lighter;margin:20px 0;background-color:#FBFBFB;padding:20px;border:1px solid #e5e5e5;box-shadow:0 1px 1px rgba(0,0,0,.04)}.hmbkp-schedule-sentence::before{content:"\2714";margin-right:10px;width:16px;height:60px;display:block;float:left}.hmbkp-schedule-sentence.hmbkp-error:before{content:"\2718"}.hmbkp-schedule-sentence.hmbkp-running:before{width:20px;height:80px;margin:-1px 10px 0 0;content:"";background:url(spinner-2x.gif) no-repeat;background-size:20px;display:inline-block}.hmbkp-schedule-sentence .hmbkp-status{display:none;font-size:12px;color:#666;margin:2px 0 0 30px}.hmbkp-schedule-sentence.hmbkp-running .hmbkp-status{display:block}.hmbkp-schedule-sentence.hmbkp-running .hmbkp-schedule-actions{display:none}.hmbkp-schedule-sentence :not(a)[title]{border-bottom:1px dotted #CCC;cursor:help}.hmbkp-schedule-sentence .hmbkp-status[title]{border-bottom:none}.hmbkp-schedule-sentence .submit{padding:0}.hmbkp-schedule-sentence .hmbkp-schedule-actions{visibility:visible;font-size:12px;font-weight:400;margin:0 0 0 26px}.hmbkp-schedule-sentence .row-actions{position:static}.hmbkp-schedule-settings{border-top:1px solid #e5e5e5;margin:20px -20px -20px;background-color:#f5f5f5;padding:0 20px 20px}.hmbkp-exclude-settings thead tr:last-child,table.widefat tbody tr:nth-child(odd){background-color:#f9f9f9}.hmbkp-ajax-loading,button.hmbkp-ajax-loading{padding-left:20px;position:relative}.hmbkp-ajax-loading::after{content:"";width:16px;height:16px;background-image:url(spinner-2x.gif);background-size:16px 16px;background-repeat:no-repeat;background-position:0 0;position:absolute;right:-30px;top:5px}.delete-action{color:#a00;-webkit-transition:all .3s ease;transition:all .3s ease}.delete-action:hover .delete-action:focus{color:red;-webkit-transition:all .3s ease;transition:all .3s ease}.strikethrough{text-decoration:line-through}.hmbkp-exclude-settings td:first-child,.hmbkp-exclude-settings th:first-child{width:20px;padding-right:0}.hmbkp-exclude-settings table .button-secondary{line-height:18px;height:20px}thead td{border-bottom:1px solid #e1e1e1}.hmbkp-exclude-settings table .spinner{display:block;float:left;margin:0}.column-filesize code{position:relative}.column-filesize .dashicons-update{display:none;overflow:hidden;position:absolute;width:100%;left:0;text-align:center;background-color:rgba(255,255,255,.8)}.column-filesize .dashicons-update span{display:none}.column-filesize:hover .dashicons-update{display:inline-block}.hmbkp-exclude-settings td span.reason{color:#CCC}.server-info{overflow:auto;max-height:50%;outline:#000 solid 1px}.server-info pre{max-height:100px;overflow-x:hidden}.page-title-action span.dashicons-admin-users{position:relative;display:inline-block;vertical-align:middle;top:-2px}pre{background-color:#eee;padding:10px;white-space:pre;max-height:320px;overflow:auto;word-wrap:normal!important}@media screen and (max-width:768px){.wrap h2{padding:10px 0 0}.hmbkp-schedule-sentence::before{height:80px}h2 .nav-tab{display:block;padding:10px;margin:0}#intercom-info,.hmbkp-exclude-settings tr>:first-child,.hmbkp-schedule-settings thead tr:nth-child(2),.hmbkp-schedule-settings tr :nth-child(4),.hmbkp-schedule-settings tr :nth-child(5){display:none}.hmbkp-schedule-sentence{margin:10px 0;padding:10px}.hmbkp-exclude-settings table{margin:0 -10px;border-left:none;border-right:none;width:calc(100% + 20px)}.hmbkp-schedule-settings{padding:0 10px 10px;margin-left:-10px;margin-right:-10px;margin-bottom:-10px}table.widefat tbody tr:nth-child(even){background-color:#fff}table.widefat tbody tr:nth-child(odd){background-color:#f9f9f9}}
assets/hmbkp.min.js CHANGED
@@ -1 +1,24 @@
1
- function hmbkpToggleScheduleFields(a){a="undefined"!=typeof a?a:"manually";var b=jQuery(".recurring-setting"),c=jQuery("#schedule-start"),d=jQuery(".twice-js");switch(a){case"manually":b.hide();break;case"hourly":b.hide();break;case"daily":b.hide(),c.show(),d.hide();break;case"twicedaily":b.hide(),c.show(),d.show();break;case"weekly":case"fortnightly":b.hide(),jQuery("#start-day").show(),c.show(),d.hide();break;case"monthly":b.hide(),c.show(),jQuery("#start-date").show(),d.hide()}}function hmbkpCatchResponseAndOfferToEmail(a){a&&0!==a?(jQuery(".hmbkp-schedule-sentence.hmbkp-running").removeClass("hmbkp-running").addClass("hmbkp-error"),jQuery.post(ajaxurl,{nonce:hmbkp.nonce,action:"hmbkp_backup_error",hmbkp_error:a},function(a){a&&0!==a&&location.reload(!0)})):location.reload(!0),jQuery(document).one("click",".hmbkp_send_error_via_email",function(b){b.preventDefault(),jQuery(this).addClass("hmbkp-ajax-loading").attr("disabled","disabled"),jQuery.post(ajaxurl,{nonce:hmbkp.nonce,action:"hmbkp_email_error",hmbkp_error:a},function(){})})}jQuery(document).ready(function(a){"use strict";var b=a("select#hmbkp_schedule_recurrence_type");a.ajaxSetup({cache:!1}),b.length&&(hmbkpToggleScheduleFields(b.val()),a(document).on("change","select#hmbkp_schedule_recurrence_type",function(){hmbkpToggleScheduleFields(a(this).val())})),a(document).on("click",".hmbkp-schedule-actions .delete-action",function(a){confirm(hmbkp.delete_schedule)||a.preventDefault()}),a(document).on("click",".hmbkp_manage_backups_row .delete-action",function(a){confirm(hmbkp.delete_backup)||a.preventDefault()}),a(document).on("click",".hmbkp-edit-schedule-excludes-form .delete-action",function(a){confirm(hmbkp.remove_exclude_rule)||a.preventDefault()}),a.post(ajaxurl,{nonce:hmbkp.nonce,action:"hmbkp_cron_test"},function(b){"1"!==b&&a(".wrap > h2").after(b)}),a(document).on("click",".hmbkp-run",function(b){wp.heartbeat.interval("fast"),a(this).closest(".hmbkp-schedule-sentence").addClass("hmbkp-running"),a(".hmbkp-error").removeClass("hmbkp-error");var c=a("[data-hmbkp-schedule-id]").attr("data-hmbkp-schedule-id");a.post(ajaxurl,{hmbkp_run_schedule_nonce:hmbkp.hmbkp_run_schedule_nonce,action:"hmbkp_run_schedule",hmbkp_schedule_id:c}),b.preventDefault()}),a(document).on("heartbeat-send",function(b,c){c.hmbkp_schedule_id=a("[data-hmbkp-schedule-id]").attr("data-hmbkp-schedule-id"),a(".hmbkp-schedule-sentence.hmbkp-running").length?c.hmbkp_is_in_progress=!0:c.hmbkp_client_request="site_size"}),a(document).on("heartbeat-tick",function(b,c){if(0!==c.hmbkp_schedule_status||a(".hmbkp-error").length||location.reload(!0),0!==c.hmbkp_schedule_status&&void 0!==c.hmbkp_schedule_status&&a(".hmbkp-status").replaceWith(c.hmbkp_schedule_status),void 0!==c.hmbkp_site_size&&a("code.calculating").length){a("code.calculating").text(c.hmbkp_site_size);var d=a(".hmbkp-exclude-settings");d.length&&d.replaceWith(c.hmbkp_dir_sizes)}}),a(document).on("click",".hmbkp-thickbox-close",function(a){a.preventDefault(),window.parent.tb_remove()})});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function hmbkpToggleScheduleFields(a){a="undefined"!=typeof a?a:"manually";var b=jQuery(".recurring-setting"),c=jQuery("#schedule-start"),d=jQuery(".twice-js");switch(a){case"manually":b.hide();break;case"hourly":b.hide();break;case"daily":b.hide(),c.show(),d.hide();break;case"twicedaily":b.hide(),c.show(),d.show();break;case"weekly":// fall through
2
+ case"fortnightly":b.hide(),jQuery("#start-day").show(),c.show(),d.hide();break;case"monthly":b.hide(),c.show(),jQuery("#start-date").show(),d.hide()}}jQuery(document).ready(function(a){"use strict";var b=a("select#hmbkp_schedule_recurrence_type");
3
+ // Don't ever cache ajax requests
4
+ a.ajaxSetup({cache:!1}),b.length&&(hmbkpToggleScheduleFields(b.val()),a(document).on("change","select#hmbkp_schedule_recurrence_type",function(){hmbkpToggleScheduleFields(a(this).val())})),
5
+ // Show delete confirm message for delete schedule
6
+ a(document).on("click",".hmbkp-schedule-actions .delete-action",function(a){confirm(hmbkp.delete_schedule)||a.preventDefault()}),
7
+ // Show delete confirm message for delete backup
8
+ a(document).on("click",".hmbkp_manage_backups_row .delete-action",function(a){confirm(hmbkp.delete_backup)||a.preventDefault()}),
9
+ // Show delete confirm message for remove exclude rule
10
+ a(document).on("click",".hmbkp-edit-schedule-excludes-form .delete-action",function(a){confirm(hmbkp.remove_exclude_rule)||a.preventDefault()}),
11
+ // Test the cron response using ajax
12
+ a.post(ajaxurl,{nonce:hmbkp.nonce,action:"hmbkp_cron_test"},function(b){"1"!==b&&a(".wrap > h2").after(b)}),
13
+ // Run a backup
14
+ a(document).on("click",".hmbkp-run",function(b){wp.heartbeat.interval("fast"),a(this).closest(".hmbkp-schedule-sentence").addClass("hmbkp-running"),a(".hmbkp-error").removeClass("hmbkp-error");var c=a("[data-hmbkp-schedule-id]").attr("data-hmbkp-schedule-id");a.post(ajaxurl,{hmbkp_run_schedule_nonce:hmbkp.hmbkp_run_schedule_nonce,action:"hmbkp_run_schedule",hmbkp_schedule_id:c}),b.preventDefault()}),
15
+ // Send the schedule id with the heartbeat
16
+ a(document).on("heartbeat-send",function(b,c){c.hmbkp_schedule_id=a("[data-hmbkp-schedule-id]").attr("data-hmbkp-schedule-id"),a(".hmbkp-schedule-sentence.hmbkp-running").length?c.hmbkp_is_in_progress=!0:c.hmbkp_client_request="site_size"}),
17
+ // Update schedule status on heartbeat tick
18
+ a(document).on("heartbeat-tick",function(b,c){if(
19
+ // If the schedule has finished then reload the page
20
+ 0!==c.hmbkp_schedule_status||a(".hmbkp-error").length||location.reload(!0),
21
+ // If the schedule is still running then update the schedule status
22
+ 0!==c.hmbkp_schedule_status&&void 0!==c.hmbkp_schedule_status&&a(".hmbkp-status").replaceWith(c.hmbkp_schedule_status),void 0!==c.hmbkp_site_size&&a("code.calculating").length){a("code.calculating").text(c.hmbkp_site_size);var d=a(".hmbkp-exclude-settings");d.length&&d.replaceWith(c.hmbkp_dir_sizes)}}),
23
+ // Closing ThickBox Modal Window
24
+ a(document).on("click",".hmbkp-thickbox-close",function(a){a.preventDefault(),window.parent.tb_remove()}),a(document).on("click","#hmbkp-warning-backup .notice-dismiss",function(){a.post(ajaxurl,{action:"hmbkp_dismiss_error"})})});
backupwordpress.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: BackUpWordPress
4
  Plugin URI: http://bwp.hmn.md/
5
  Description: Simple automated backups of your WordPress powered website. Once activated you'll find me under <strong>Tools &rarr; Backups</strong>. On multisite, you'll find me under the Network Settings menu.
6
- Version: 3.3.4
7
  Author: Human Made Limited
8
  Author URI: http://hmn.md/
9
  License: GPL-2+
3
  Plugin Name: BackUpWordPress
4
  Plugin URI: http://bwp.hmn.md/
5
  Description: Simple automated backups of your WordPress powered website. Once activated you'll find me under <strong>Tools &rarr; Backups</strong>. On multisite, you'll find me under the Network Settings menu.
6
+ Version: 3.4.0
7
  Author: Human Made Limited
8
  Author URI: http://hmn.md/
9
  License: GPL-2+
classes/backup/class-backup-engine-database-imysqldump.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ use Ifsnop\Mysqldump as IMysqldump;
6
+
7
+ /**
8
+ * Perform a database backup using the mysqldump-php library
9
+ *
10
+ * @see https://github.com/ifsnop/mysqldump-php
11
+ */
12
+ class IMysqldump_Database_Backup_Engine extends Database_Backup_Engine {
13
+
14
+ public function __construct() {
15
+ parent::__construct();
16
+ }
17
+
18
+ /**
19
+ * Perform the database backupwordpress
20
+ *
21
+ * @return bool True if the backup completed successfully, else false.
22
+ */
23
+ public function backup() {
24
+
25
+ try {
26
+
27
+ $dump = new IMysqldump\Mysqldump( $this->get_dsn(), $this->get_user(), $this->get_password(), $this->get_dump_settings() );
28
+ $dump->start( $this->get_backup_filepath() );
29
+
30
+ } catch ( \Exception $e ) {
31
+ $this->error( __CLASS__, $e->getMessage() );
32
+ }
33
+
34
+ return $this->verify_backup();
35
+
36
+ }
37
+
38
+ /**
39
+ * Get the settings for the database bump.
40
+ *
41
+ * @return array The array of database dump settings.
42
+ */
43
+ public function get_dump_settings() {
44
+
45
+ /**
46
+ * Allow additional settings to be added.
47
+ *
48
+ * @param string[] $settings The array of settings.
49
+ * @todo can these be standardised across all database backup engines
50
+ */
51
+ return apply_filters( 'hmbkp_imysqldump_command', array(
52
+ 'default-character-set' => $this->get_charset(),
53
+ 'hex-blob' => true,
54
+ 'single-transaction' => defined( 'HMBKP_MYSQLDUMP_SINGLE_TRANSACTION' ) && HMBKP_MYSQLDUMP_SINGLE_TRANSACTION
55
+ ) );
56
+
57
+ }
58
+
59
+ /**
60
+ * Correctly calculates the DSN string for the various mysql
61
+ * connection variations including simplt hostname, non-standard ports
62
+ * and socket connections.
63
+ *
64
+ * @return string The DSN connection string
65
+ */
66
+ public function get_dsn() {
67
+
68
+ $dsn = 'mysql:host=' . $this->get_host() . ';dbname=' . $this->get_name();
69
+
70
+ if ( $this->get_host() && $this->get_port() ) {
71
+ $dsn = 'mysql:host=' . $this->get_host() . ';port=' . $this->get_port() . ';dbname=' . $this->get_name();
72
+ }
73
+
74
+ elseif ( $this->get_socket() ) {
75
+ $dsn = 'mysql:unix_socket=' . $this->get_socket() . ';dbname=' . $this->get_name();
76
+ }
77
+
78
+ return $dsn;
79
+
80
+ }
81
+
82
+ }
classes/backup/class-backup-engine-database-mysqldump.php ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ /**
6
+ * Perform a database backup using the mysqldump cli command
7
+ */
8
+ class Mysqldump_Database_Backup_Engine extends Database_Backup_Engine {
9
+
10
+ /**
11
+ * The path to the mysqldump executable
12
+ *
13
+ * @var string
14
+ */
15
+ private $mysqldump_executable_path = '';
16
+
17
+ public function __construct() {
18
+ parent::__construct();
19
+ }
20
+
21
+ /**
22
+ * Calculate the path to the mysqldump executable.
23
+ *
24
+ * The executable path can be overridden using either the `HMBKP_MYSQLDUMP_PATH`
25
+ * Constant or the `hmbkp_mysqldump_executable_path` filter.
26
+ *
27
+ * If neither of those are set then we fallback to checking a number of
28
+ * common locations.
29
+ *
30
+ * @return string|false The path to the executable or false.
31
+ */
32
+ public function get_mysqldump_executable_path() {
33
+
34
+ // Return now if it's set in a Constant
35
+ if ( defined( 'HMBKP_MYSQLDUMP_PATH' ) && HMBKP_MYSQLDUMP_PATH ) {
36
+ $this->mysqldump_executable_path = HMBKP_MYSQLDUMP_PATH;
37
+ }
38
+
39
+ /**
40
+ * Allow the executable path to be set via a filter
41
+ *
42
+ * @param string The path to the mysqldump executable
43
+ */
44
+ $this->mysqldump_executable_path = apply_filters( 'hmbkp_mysqldump_executable_path', '' );
45
+
46
+ if ( ! $this->mysqldump_executable_path ) {
47
+
48
+ // List of possible mysqldump locations
49
+ $paths = array(
50
+ 'mysqldump',
51
+ '/usr/local/bin/mysqldump',
52
+ '/usr/local/mysql/bin/mysqldump',
53
+ '/usr/mysql/bin/mysqldump',
54
+ '/usr/bin/mysqldump',
55
+ '/opt/local/lib/mysql6/bin/mysqldump',
56
+ '/opt/local/lib/mysql5/bin/mysqldump',
57
+ '/opt/local/lib/mysql4/bin/mysqldump',
58
+ '/xampp/mysql/bin/mysqldump',
59
+ '/Program Files/xampp/mysql/bin/mysqldump',
60
+ '/Program Files/MySQL/MySQL Server 6.0/bin/mysqldump',
61
+ '/Program Files/MySQL/MySQL Server 5.7/bin/mysqldump',
62
+ '/Program Files/MySQL/MySQL Server 5.6/bin/mysqldump',
63
+ '/Program Files/MySQL/MySQL Server 5.5/bin/mysqldump',
64
+ '/Program Files/MySQL/MySQL Server 5.4/bin/mysqldump',
65
+ '/Program Files/MySQL/MySQL Server 5.1/bin/mysqldump',
66
+ '/Program Files/MySQL/MySQL Server 5.0/bin/mysqldump',
67
+ '/Program Files/MySQL/MySQL Server 4.1/bin/mysqldump',
68
+ '/opt/local/bin/mysqldump'
69
+ );
70
+
71
+ $this->mysqldump_executable_path = Backup_Utilities::get_executable_path( $paths );
72
+
73
+ }
74
+
75
+ return $this->mysqldump_executable_path;
76
+
77
+ }
78
+
79
+ /**
80
+ * Check whether it's possible to connect to the database with the
81
+ * credentials we have.
82
+ *
83
+ * @return bool Whether the database connection was successful.
84
+ */
85
+ public function check_user_can_connect_to_database_via_cli() {
86
+
87
+ if ( ! $this->get_mysqldump_executable_path() ) {
88
+ return false;
89
+ }
90
+
91
+ $args = $this->get_mysql_connection_args();
92
+
93
+ $args[] = escapeshellarg( $this->get_name() );
94
+
95
+ // Quit immediately as we're only interesting in testing the connection
96
+ $args[] = '--execute="quit"';
97
+
98
+ // Pipe STDERR to STDOUT
99
+ $args[] = ' 2>&1';
100
+
101
+ $output = $return_status = '';
102
+ $args = implode( ' ', $args );
103
+ exec( 'mysql ' . $args, $output, $return_status );
104
+
105
+ $output = $this->ignore_mysql_password_warning( $output );
106
+
107
+ // If there were errors connecting then track them
108
+ if ( $output ) {
109
+ if ( $return_status === 0 ) {
110
+ $this->warning( __CLASS__, implode( ', ', $output ) );
111
+ } else {
112
+ $this->error( __CLASS__, implode( ', ', $output ) );
113
+ return false;
114
+ }
115
+ }
116
+
117
+ return true;
118
+
119
+ }
120
+
121
+ /**
122
+ * Perform the database backup.
123
+ *
124
+ * @return bool Whether the backup completed successfully or not.
125
+ */
126
+ public function backup() {
127
+
128
+ if ( ! $this->check_user_can_connect_to_database_via_cli() || ! $this->get_mysqldump_executable_path() ) {
129
+ return false;
130
+ }
131
+
132
+ $output = $return_status = '';
133
+
134
+ // Grab the database connections args
135
+ $args = $this->get_mysql_connection_args();
136
+
137
+ // We don't want to create a new DB
138
+ $args[] = '--no-create-db';
139
+
140
+ // Allow lock-tables to be overridden
141
+ if ( defined( 'HMBKP_MYSQLDUMP_SINGLE_TRANSACTION' ) && HMBKP_MYSQLDUMP_SINGLE_TRANSACTION ) {
142
+ $args[] = '--single-transaction';
143
+ }
144
+
145
+ // Make sure binary data is exported properly
146
+ $args[] = '--hex-blob';
147
+
148
+ // The file we're saving too
149
+ $args[] = '-r ' . escapeshellarg( $this->get_backup_filepath() );
150
+
151
+ // The database we're dumping
152
+ $args[] = escapeshellarg( $this->get_name() );
153
+
154
+ // Pipe STDERR to STDOUT
155
+ $args[] = '2>&1';
156
+
157
+ exec( escapeshellcmd( $this->get_mysqldump_executable_path() ) . ' ' . implode( ' ', $args ), $output, $return_status );
158
+
159
+ $output = $this->ignore_mysql_password_warning( $output );
160
+
161
+ // Track any errors
162
+ if ( $output ) {
163
+ if ( $return_status === 0 ) {
164
+ $this->warning( __CLASS__, implode( ', ', $output ) );
165
+ } else {
166
+ $this->error( __CLASS__, implode( ', ', $output ) );
167
+ }
168
+ }
169
+
170
+ return $this->verify_backup();
171
+
172
+ }
173
+
174
+ /**
175
+ * Get the connection args for the mysqldump command
176
+ *
177
+ * @return array The array of connection args
178
+ */
179
+ public function get_mysql_connection_args() {
180
+
181
+ $args = array();
182
+
183
+ $args[] = '-u ' . escapeshellarg( $this->get_user() );
184
+
185
+ if ( $this->get_password() ) {
186
+ $args[] = '-p' . escapeshellarg( $this->get_password() );
187
+ }
188
+
189
+ $args[] = '-h ' . escapeshellarg( $this->get_host() );
190
+
191
+ if ( $this->get_port() ) {
192
+ $args[] = '-P ' . escapeshellarg( $this->get_port() );
193
+ }
194
+
195
+ if ( $this->get_socket() ) {
196
+ $args[] = '--protocol=socket -S ' . escapeshellarg( $this->get_socket() );
197
+ }
198
+
199
+ return $args;
200
+
201
+ }
202
+
203
+ private function ignore_mysql_password_warning( $output ) {
204
+
205
+ $key = array_search( 'Warning: Using a password on the command line interface can be insecure.', $output );
206
+
207
+ if ( $key !== false ) {
208
+ unset( $output[ $key ] );
209
+ }
210
+
211
+ return $output;
212
+
213
+ }
214
+
215
+ }
classes/backup/class-backup-engine-database.php ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ /**
6
+ * The Database Backup Engine type
7
+ *
8
+ * All Database Backup Engine implementations should extend this class
9
+ */
10
+ abstract class Database_Backup_Engine extends Backup_Engine {
11
+
12
+ /**
13
+ * The filename for the resulting Backup
14
+ *
15
+ * @var string
16
+ */
17
+ public $backup_filename = '';
18
+
19
+ /**
20
+ * The database host string, typically the value of
21
+ * the `DB_HOST` Constant.
22
+ *
23
+ * @var string
24
+ */
25
+ private $host = '';
26
+
27
+ /**
28
+ * The database socket, if it's using a socket connection
29
+ *
30
+ * @var string
31
+ */
32
+ private $socket = '';
33
+
34
+ /**
35
+ * The database port, if a custom one is set
36
+ *
37
+ * @var integer
38
+ */
39
+ private $port = 0;
40
+
41
+ /**
42
+ * Individual Database Backup Engine implementations must include
43
+ * a backup method at a minimum.
44
+ *
45
+ * @return [type] [description]
46
+ */
47
+ abstract public function backup();
48
+
49
+ /**
50
+ * Setup some general database backup settings
51
+ *
52
+ * Child classes must call `parent::__construct` in their own constructor.
53
+ */
54
+ public function __construct() {
55
+
56
+ parent::__construct();
57
+
58
+ $this->parse_db_host_constant();
59
+
60
+ // Set a default backup filename
61
+ $this->set_backup_filename( 'database-' . $this->get_name() . '.sql' );
62
+
63
+ }
64
+
65
+ /**
66
+ * Get the database charset setting.
67
+ *
68
+ * @return [string The database charset.
69
+ */
70
+ public function get_charset() {
71
+ global $wpdb;
72
+ return $wpdb->charset;
73
+ }
74
+
75
+ /**
76
+ * Get the database collate setting.
77
+ *
78
+ * @return string The database collage setting.
79
+ */
80
+ public function get_collate() {
81
+ global $wpdb;
82
+ return $wpdb->collate;
83
+ }
84
+
85
+ /**
86
+ * Get the database name.
87
+ *
88
+ * @return string The database name.
89
+ */
90
+ public function get_name() {
91
+ global $wpdb;
92
+ return $wpdb->dbname;
93
+ }
94
+
95
+ /**
96
+ * Get the database user.
97
+ *
98
+ * @return string The database user.
99
+ */
100
+ public function get_user() {
101
+ global $wpdb;
102
+ return $wpdb->dbuser;
103
+ }
104
+
105
+ /**
106
+ * Get the database password.
107
+ *
108
+ * @return string The database password.
109
+ */
110
+ public function get_password() {
111
+ global $wpdb;
112
+ return $wpdb->dbpassword;
113
+ }
114
+
115
+ /**
116
+ * Get the database hostname.
117
+ *
118
+ * @return string The database hostname.
119
+ */
120
+ public function get_host() {
121
+ return $this->host;
122
+ }
123
+
124
+ /**
125
+ * Get the database port.
126
+ *
127
+ * @return int The database port.
128
+ */
129
+ public function get_port() {
130
+ return $this->port;
131
+ }
132
+
133
+ /**
134
+ * Get the database socket.
135
+ *
136
+ * @return string The database socket.
137
+ */
138
+ public function get_socket() {
139
+ return $this->socket;
140
+ }
141
+
142
+ /**
143
+ * Parse the `DB_HOST` constant.
144
+ *
145
+ * The `DB_HOST` constant potentially contains the hostname, port or socket.
146
+ * We need to parse it to figure out the type of mysql connection to make.
147
+ *
148
+ * @param string $constant The Constant to parse. If the string isn't a
149
+ * defined Constant then it will be parsed directly.
150
+ */
151
+ public function parse_db_host_constant( $constant = 'DB_HOST' ) {
152
+
153
+ // If we've been passed a Constant then grab it's contents
154
+ if ( defined( $constant ) ) {
155
+ $constant = constant( $constant );
156
+ }
157
+
158
+ // If we weren't passed a Constant then just parse the string directly.
159
+ $this->host = (string) $constant;
160
+
161
+ // Grab the part after :, it could either be a port or a socket
162
+ $port_or_socket = strstr( $constant, ':' );
163
+
164
+ if ( $port_or_socket ) {
165
+
166
+ // The host is the bit up to the :
167
+ $this->host = substr( $constant, 0, strpos( $constant, ':' ) );
168
+
169
+ // Strip the :
170
+ $port_or_socket = substr( $port_or_socket, 1 );
171
+
172
+ if ( 0 !== strpos( $port_or_socket, '/' ) ) {
173
+
174
+ $this->port = intval( $port_or_socket );
175
+ $maybe_socket = strstr( $port_or_socket, ':' );
176
+
177
+ if ( ! empty( $maybe_socket ) ) {
178
+ $this->socket = substr( $maybe_socket, 1 );
179
+ }
180
+
181
+ } else {
182
+ $this->socket = $port_or_socket;
183
+ }
184
+
185
+ }
186
+
187
+ }
188
+
189
+ /**
190
+ * Verify that the database backup was successful.
191
+ *
192
+ * It's important this function is performant as it's called after every
193
+ * backup.
194
+ *
195
+ * @return bool Whether the backup completed successfully
196
+ */
197
+ public function verify_backup() {
198
+
199
+ // If there are errors delete the database dump file
200
+ if ( $this->get_errors( __CLASS__ ) && file_exists( $this->get_backup_filepath() ) ) {
201
+ unlink( $this->get_backup_filepath() );
202
+ }
203
+
204
+ // If we have an empty file delete it
205
+ if ( @filesize( $this->get_backup_filepath() ) === 0 ) {
206
+ unlink( $this->get_backup_filepath() );
207
+ }
208
+
209
+ // If the database backup doesn't exist then the backup must have failed
210
+ if ( ! file_exists( $this->get_backup_filepath() ) ) {
211
+ return false;
212
+ }
213
+
214
+ return true;
215
+
216
+ }
217
+
218
+ }
classes/backup/class-backup-engine-file-zip-archive.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ /**
6
+ * Perform a file backup using the native PHP ZipArchive extension
7
+ */
8
+ class Zip_Archive_File_Backup_Engine extends File_Backup_Engine {
9
+
10
+ public function __construct() {
11
+ parent::__construct();
12
+ }
13
+
14
+ public function backup() {
15
+
16
+ if ( ! class_exists( 'ZipArchive' ) ) {
17
+ return false;
18
+ }
19
+
20
+ $zip = new \ZipArchive();
21
+
22
+ // Attempt to create the zip file.
23
+ if ( $zip->open( $this->get_backup_filepath(), \ZIPARCHIVE::CREATE ) ) {
24
+
25
+ foreach ( $this->get_files() as $file ) {
26
+
27
+ // Create an empty directory for each directory in the filesystem
28
+ if ( $file->isDir() ) {
29
+ $zip->addEmptyDir( $file->getRelativePathname() );
30
+ }
31
+
32
+ // Archive the file with a relative path
33
+ elseif ( $file->isFile() ) {
34
+ $zip->addFile( $file->getPathname(), $file->getRelativePathname() );
35
+ }
36
+ }
37
+
38
+ // Track any internal warnings
39
+ if ( $zip->status ) {
40
+ $this->warning( __CLASS__, $zip->status );
41
+ }
42
+
43
+ if ( $zip->statusSys ) {
44
+ $this->warning( __CLASS__, $zip->statusSys );
45
+ }
46
+
47
+ $zip->close();
48
+
49
+ }
50
+
51
+ return $this->verify_backup();
52
+
53
+ }
54
+
55
+ }
classes/backup/class-backup-engine-file-zip.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ /**
6
+ * Perform a file backup using the zip cli command
7
+ */
8
+ class Zip_File_Backup_Engine extends File_Backup_Engine {
9
+
10
+ /**
11
+ * The path to the zip executable
12
+ *
13
+ * @var string
14
+ */
15
+ private $zip_executable_path = '';
16
+
17
+ public function __construct() {
18
+ parent::__construct();
19
+ }
20
+
21
+ /**
22
+ * Calculate the path to the zip executable.
23
+ *
24
+ * The executable path can be overridden using either the `HMBKP_ZIP_PATH`
25
+ * Constant or the `hmbkp_zip_executable_path` filter.
26
+ *
27
+ * If neither of those are set then we fallback to checking a number of
28
+ * common locations.
29
+ *
30
+ * @return string|false The path to the executable or false.
31
+ */
32
+ public function get_zip_executable_path() {
33
+
34
+ if ( defined( 'HMBKP_ZIP_PATH' ) ) {
35
+ return HMBKP_ZIP_PATH;
36
+ }
37
+
38
+ /**
39
+ * Allow the executable path to be set via a filter
40
+ *
41
+ * @param string The path to the zip executable
42
+ */
43
+ $this->zip_executable_path = apply_filters( 'hmbkp_zip_executable_path', '' );
44
+
45
+ if ( ! $this->zip_executable_path ) {
46
+
47
+ // List of possible zip locations
48
+ $paths = array(
49
+ 'zip',
50
+ '/usr/bin/zip',
51
+ '/opt/local/bin/zip'
52
+ );
53
+
54
+ $this->zip_executable_path = Backup_Utilities::get_executable_path( $paths );
55
+
56
+ }
57
+
58
+ return $this->zip_executable_path;
59
+
60
+ }
61
+
62
+ /**
63
+ * Perform the file backup.
64
+ *
65
+ * @return bool Whether the backup completed successfully or not.
66
+ */
67
+ public function backup() {
68
+
69
+ if ( ! Backup_Utilities::is_exec_available() || ! $this->get_zip_executable_path() ) {
70
+ return false;
71
+ }
72
+
73
+ // cd to the site root
74
+ $command[] = 'cd ' . escapeshellarg( Path::get_root() );
75
+
76
+ // Run the zip command with the recursive and quiet flags
77
+ $command[] = '&& ' . escapeshellcmd( $this->get_zip_executable_path() ) . ' -rq';
78
+
79
+ // Save the zip file to the correct path
80
+ $command[] = escapeshellarg( $this->get_backup_filepath() ) . ' ./';
81
+
82
+ // Pass exclude rules in if we have them
83
+ if ( $this->get_exclude_string() ) {
84
+ $command[] = '-x ' . $this->get_exclude_string();
85
+ }
86
+
87
+ // Push all output to STDERR
88
+ $command[] = '2>&1';
89
+
90
+ $command = implode( ' ', $command );
91
+ $output = $return_status = 0;
92
+
93
+ exec( $command, $output, $return_status );
94
+
95
+ // Track any errors
96
+ if ( $output ) {
97
+ if ( $return_status === 0 ) {
98
+ $this->warning( __CLASS__, implode( ', ', $output ) );
99
+ } else {
100
+ $this->error( __CLASS__, implode( ', ', $output ) );
101
+ }
102
+ }
103
+
104
+ return $this->verify_backup();
105
+
106
+ }
107
+
108
+ /**
109
+ * Convert the exclude rules to a format zip accepts
110
+ *
111
+ * @return string The exclude string ready to pass to `zip -x`
112
+ */
113
+ public function get_exclude_string() {
114
+
115
+ if ( ! $this->excludes ) {
116
+ return '';
117
+ }
118
+
119
+ $excludes = $this->excludes->get_excludes();
120
+
121
+ foreach ( $excludes as $key => &$rule ) {
122
+
123
+ $file = $absolute = $fragment = false;
124
+
125
+ // Files don't end with /
126
+ if ( ! in_array( substr( $rule, - 1 ), array( '\\', '/' ) ) ) {
127
+ $file = true;
128
+ }
129
+
130
+ // If rule starts with a / then treat as absolute path
131
+ elseif ( in_array( substr( $rule, 0, 1 ), array( '\\', '/' ) ) ) {
132
+ $absolute = true;
133
+ }
134
+
135
+ // Otherwise treat as dir fragment
136
+ else {
137
+ $fragment = true;
138
+ }
139
+
140
+ $rule = str_ireplace( Path::get_root(), '', untrailingslashit( wp_normalize_path( $rule ) ) );
141
+
142
+ // Strip the preceeding slash
143
+ if ( in_array( substr( $rule, 0, 1 ), array( '\\', '/' ) ) ) {
144
+ $rule = substr( $rule, 1 );
145
+ }
146
+
147
+ // Wrap directory fragments and files in wildcards for zip
148
+ if ( $fragment || $file ) {
149
+ $rule = '*' . $rule . '*';
150
+ }
151
+
152
+ // Add a wildcard to the end of absolute url for zips
153
+ if ( $absolute ) {
154
+ $rule .= '*';
155
+ }
156
+
157
+ }
158
+
159
+ // Escape shell args for zip command
160
+ $excludes = array_map( 'escapeshellarg', array_unique( $excludes ) );
161
+
162
+ return implode( ' -x ', $excludes );
163
+
164
+ }
165
+
166
+ }
classes/backup/class-backup-engine-file.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ use Symfony\Component\Finder\Finder as Finder;
6
+
7
+ /**
8
+ * The File Backup Engine type
9
+ *
10
+ * All File Backup Engine implementations should extend this class
11
+ */
12
+ abstract class File_Backup_Engine extends Backup_Engine {
13
+
14
+ /**
15
+ * The array of excludes rules
16
+ *
17
+ * @var array
18
+ */
19
+ protected $excludes;
20
+
21
+ /**
22
+ * Set the default backup filename.
23
+ */
24
+ public function __construct() {
25
+
26
+ parent::__construct();
27
+
28
+ $this->set_backup_filename( implode( '-', array(
29
+ str_ireplace( array( 'http://', 'https://', 'www' ), '', home_url() ),
30
+ 'backup',
31
+ current_time( 'Y-m-d-H-i-s' )
32
+ ) ) . '.zip' );
33
+
34
+ $this->excludes = new Excludes;
35
+
36
+ }
37
+
38
+ /**
39
+ * Set the excludes rules for the backup.
40
+ *
41
+ * @param array $excludes The exclude rules.
42
+ */
43
+ public function set_excludes( Excludes $excludes ) {
44
+ $this->excludes = $excludes;
45
+ }
46
+
47
+ /**
48
+ * Returns a Finder instance for the files that will be included in the
49
+ * backup.
50
+ *
51
+ * By default we ignore unreadable files and directories as well as, common
52
+ * version control folders / files, "Dot" files and anything matching the
53
+ * exclude rules.
54
+ *
55
+ * @uses Finder
56
+ * @return Finder The Finder iterator of all files to be included
57
+ */
58
+ public function get_files() {
59
+
60
+ $finder = new Finder();
61
+
62
+ $finder->followLinks( true );
63
+ $finder->ignoreDotFiles( false );
64
+ $finder->ignoreVCS( true );
65
+ $finder->ignoreUnreadableDirs( true );
66
+
67
+ // Skip unreadable files too
68
+ $finder->filter(
69
+ function ( \SplFileInfo $file ) {
70
+ if ( ! $file->isReadable() ) {
71
+ return false;
72
+ }
73
+ }
74
+ );
75
+
76
+ // Finder expects exclude rules to be in a regex format
77
+ $exclude_rules = $this->excludes->get_excludes_for_regex();
78
+
79
+ // Skips folders/files that match default exclude patterns
80
+ foreach ( $exclude_rules as $exclude ) {
81
+ $finder->notPath( $exclude );
82
+ }
83
+
84
+ return $finder->in( Path::get_root() );
85
+
86
+ }
87
+
88
+ /**
89
+ * Verify that the file backup completed successfully.
90
+ *
91
+ * This should be called from backup method of any final file backup engine
92
+ * implementations.
93
+ *
94
+ * @return bool Whether the backup completed successfully.
95
+ */
96
+ public function verify_backup() {
97
+
98
+ // If there are errors delete the backup file.
99
+ if ( $this->get_errors( __CLASS__ ) && file_exists( $this->get_backup_filepath() ) ) {
100
+ unlink( $this->get_backup_filepath() );
101
+ }
102
+
103
+ // If the backup doesn't exist then we must have failed.
104
+ if ( ! file_exists( $this->get_backup_filepath() ) ) {
105
+ return false;
106
+ }
107
+
108
+ return true;
109
+
110
+ }
111
+
112
+ }
classes/backup/class-backup-engine.php ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ /**
6
+ * The base Backup Engine
7
+ *
8
+ * Base Backup Engine types should extend this class and call parent::__construct in
9
+ * there constructor.
10
+ *
11
+ * Defines base functionality shared across all types of backups
12
+ */
13
+ abstract class Backup_Engine {
14
+
15
+ /**
16
+ * An array of backup errors.
17
+ *
18
+ * @var array
19
+ */
20
+ private $errors = array();
21
+
22
+ /**
23
+ * An array of backup warnings.
24
+ *
25
+ * @var array
26
+ */
27
+ private $warnings = array();
28
+
29
+ public function __construct() {
30
+
31
+ /**
32
+ * Raise the `memory_limit` and `max_execution time`
33
+ *
34
+ * Respects the WP_MAX_MEMORY_LIMIT Constant and the `admin_memory_limit`
35
+ * filter.
36
+ */
37
+ @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
38
+ @set_time_limit( 0 );
39
+
40
+ // Set a custom error handler so we can track errors
41
+ set_error_handler( array( $this, 'error_handler' ) );
42
+
43
+ }
44
+
45
+ /**
46
+ * Backup Engine Types should always implement the `verify_backup` method.
47
+ *
48
+ * @return bool Whether the backup completed successfully or not.
49
+ */
50
+ abstract public function verify_backup();
51
+
52
+ /**
53
+ * Get the full filepath to the backup file.
54
+ *
55
+ * @return string The backup filepath.
56
+ */
57
+ public function get_backup_filepath() {
58
+ return trailingslashit( Path::get_path() ) . $this->get_backup_filename();
59
+ }
60
+
61
+ /**
62
+ * Get the filename of the backup.
63
+ *
64
+ * @return string The backup filename.
65
+ */
66
+ public function get_backup_filename() {
67
+ return $this->backup_filename;
68
+ }
69
+
70
+ /**
71
+ * Set the filename of the backup.
72
+ *
73
+ * @param string $filename The backup filename.
74
+ */
75
+ public function set_backup_filename( $filename ) {
76
+ $this->backup_filename = strtolower( sanitize_file_name( remove_accents( $filename ) ) );
77
+ }
78
+
79
+ /**
80
+ * Get the array of errors encountered during the backup process.
81
+ *
82
+ * @param string $context The context for the error, usually the Backup
83
+ * Engine that encountered the error.
84
+ *
85
+ * @return array The array of errors.
86
+ */
87
+ public function get_errors( $context = null ) {
88
+
89
+ // Only return a specific contexts errors.
90
+ if ( ! empty( $context ) ) {
91
+ return isset( $this->errors[ $context ] ) ? $this->errors[ $context ] : array();
92
+ }
93
+
94
+ return $this->errors;
95
+
96
+ }
97
+
98
+ /**
99
+ * Add an error to the errors array.
100
+ *
101
+ * An error is always treat as fatal and should only be used for unrecoverable
102
+ * issues with the backup process.
103
+ *
104
+ * @param string $context The context for the error.
105
+ * @param string $error The error that was encountered.
106
+ */
107
+ public function error( $context, $error ) {
108
+
109
+ if ( empty( $context ) || empty( $error ) ) {
110
+ return;
111
+ }
112
+
113
+ // Ensure we don't store duplicate errors by md5'ing the error as the key
114
+ $this->errors[ $context ][ $_key = md5( implode( ':', (array) $error ) ) ] = $error;
115
+
116
+ }
117
+
118
+ /**
119
+ * Get the array of warnings encountered during the backup process.
120
+ *
121
+ * @param string $context The context for the warning, usually the Backup
122
+ * Engine that encountered the warning.
123
+ *
124
+ * @return array The array of warnings.
125
+ */
126
+ public function get_warnings( $context = null ) {
127
+
128
+ // Only return a specific contexts errors.
129
+ if ( ! empty( $context ) ) {
130
+ return isset( $this->warnings[ $context ] ) ? $this->warnings[ $context ] : array();
131
+ }
132
+
133
+ return $this->warnings;
134
+
135
+ }
136
+
137
+ /**
138
+ * Add an warning to the errors warnings.
139
+ *
140
+ * A warning is always treat as non-fatal and should only be used for recoverable
141
+ * issues with the backup process.
142
+ *
143
+ * @param string $context The context for the warning.
144
+ * @param string $error The warning that was encountered.
145
+ */
146
+ public function warning( $context, $warning ) {
147
+
148
+ if ( empty( $context ) || empty( $warning ) ) {
149
+ return;
150
+ }
151
+
152
+ // Ensure we don't store duplicate warnings by md5'ing the error as the key
153
+ $this->warnings[ $context ][ $_key = md5( implode( ':', (array) $warning ) ) ] = $warning;
154
+
155
+ }
156
+
157
+ /**
158
+ * Hooked into `set_error_handler` to catch any PHP errors that happen during
159
+ * the backup process.
160
+ *
161
+ * PHP errors are always treat as warnings rather than errors.
162
+ *
163
+ * @param int $type The level of error raised
164
+ *
165
+ * @return false Return false to pass the error back to PHP so it can
166
+ * be handled natively.
167
+ */
168
+ public function error_handler( $type ) {
169
+
170
+ // Skip strict & deprecated warnings
171
+ if ( ( defined( 'E_DEPRECATED' ) && $type === E_DEPRECATED ) || ( defined( 'E_STRICT' ) && $type === E_STRICT ) || error_reporting() === 0 ) {
172
+ return false;
173
+ }
174
+
175
+ /**
176
+ * Get the details of the error.
177
+ *
178
+ * These are:
179
+ *
180
+ * @param int $errorno The error level expressed as an integer/
181
+ * @param string $errstr The error message.
182
+ * @param string $errfile The file that the error raised in.
183
+ * @param string $errorline The line number the error was raised on.
184
+ */
185
+ $args = func_get_args();
186
+
187
+ // Strip the error level
188
+ array_shift( $args );
189
+
190
+ // Fire a warning for the PHP error passing the message, file and line number.
191
+ $this->warning( 'php', implode( ', ', array_splice( $args, 0, 3 ) ) );
192
+
193
+ return false;
194
+
195
+ }
196
+
197
+ }
classes/backup/class-backup-status.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ /**
6
+ * Manages status and progress of a backup
7
+ */
8
+ class Backup_Status {
9
+
10
+ private $filename = '';
11
+
12
+ public function __construct( $id ) {
13
+ $this->id = $id;
14
+ }
15
+
16
+ public function start( $backup_filename, $status_message ) {
17
+ $this->filename = $backup_filename;
18
+ $this->set_status( $status_message );
19
+ }
20
+
21
+ public function get_backup_filename() {
22
+
23
+ if ( $this->is_started() ) {
24
+ $status = json_decode( file_get_contents( $this->get_status_filepath() ) );
25
+
26
+ if ( ! empty( $status->filename ) ) {
27
+ $this->filename = $status->filename;
28
+ }
29
+ }
30
+
31
+ return $this->filename;
32
+ }
33
+
34
+ public function is_started() {
35
+ return (bool) file_exists( $this->get_status_filepath() );
36
+ }
37
+
38
+ public function finish() {
39
+ // Delete the backup running file
40
+ if ( file_exists( $this->get_status_filepath() ) ) {
41
+ unlink( $this->get_status_filepath() );
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Get the status of the running backup.
47
+ *
48
+ * @return string
49
+ */
50
+ public function get_status() {
51
+
52
+ if ( ! file_exists( $this->get_status_filepath() ) ) {
53
+ return '';
54
+ }
55
+
56
+ $status = json_decode( file_get_contents( $this->get_status_filepath() ) );
57
+
58
+ if ( ! empty( $status->status ) ) {
59
+ return $status->status;
60
+ }
61
+
62
+ return '';
63
+
64
+ }
65
+
66
+ /**
67
+ * Set the status of the running backup
68
+ *
69
+ * @param string $message
70
+ *
71
+ * @return null
72
+ */
73
+ public function set_status( $message ) {
74
+
75
+ // If start hasn't been called yet then we wont' have a backup filename
76
+ if ( ! $this->filename ) {
77
+ return '';
78
+ }
79
+
80
+ $status = json_encode( (object) array(
81
+ 'filename' => $this->filename,
82
+ 'started' => $this->get_start_time(),
83
+ 'status' => $message,
84
+ ) );
85
+
86
+ file_put_contents( $this->get_status_filepath(), $status );
87
+
88
+ }
89
+
90
+ /**
91
+ * Get the time that the current running backup was started
92
+ *
93
+ * @return int $timestamp
94
+ */
95
+ public function get_start_time() {
96
+
97
+ if ( ! file_exists( $this->get_status_filepath() ) ) {
98
+ return 0;
99
+ }
100
+
101
+ $status = json_decode( file_get_contents( $this->get_status_filepath() ) );
102
+
103
+ if ( ! empty( $status->started ) && (int) (string) $status->started === $status->started ) {
104
+ return $status->started;
105
+ }
106
+
107
+ return time();
108
+
109
+ }
110
+
111
+ /**
112
+ * Get the path to the backup running file that stores the running backup status
113
+ *
114
+ * @return string
115
+ */
116
+ public function get_status_filepath() {
117
+ return Path::get_path() . '/.backup-' . $this->id . '-running';
118
+ }
119
+
120
+ }
classes/backup/class-backup-utilities.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ /**
6
+ * A set of Backup Utility functions
7
+ */
8
+ class Backup_Utilities {
9
+
10
+ /**
11
+ * Checks whether Safe Mode is currently on
12
+ *
13
+ * @param string $ini_get_callback By default we use `ini_get` to check for
14
+ * the Safe Mode setting but this can be
15
+ * overridden for testing purposes.
16
+ *
17
+ * @return boolean Whether Safe Mode is on or off.
18
+ */
19
+ public static function is_safe_mode_on( $ini_get_callback = 'ini_get' ) {
20
+
21
+ $safe_mode = @call_user_func( $ini_get_callback, 'safe_mode' );
22
+
23
+ if ( $safe_mode && strtolower( $safe_mode ) !== 'off' ) {
24
+ return true;
25
+ }
26
+
27
+ return false;
28
+
29
+ }
30
+
31
+ /**
32
+ * Check whether it's possible to use `exec`.
33
+ *
34
+ * @return boolean [description]
35
+ */
36
+ public static function is_exec_available() {
37
+
38
+ // You can't use exec if Safe Mode is on.
39
+ if ( self::is_safe_mode_on() ) {
40
+ return false;
41
+ }
42
+
43
+ // Check if exec is specifically disabled
44
+ if ( self::is_function_disabled( 'exec' ) ) {
45
+ return false;
46
+ }
47
+
48
+ // Some servers seem to disable escapeshellcmd / escapeshellarg separately to exec, in
49
+ // that instance we don't want to use exec as it's insecure
50
+ if ( self::is_function_disabled( 'escapeshellcmd' ) || self::is_function_disabled( 'escapeshellarg' ) ) {
51
+ return false;
52
+ }
53
+
54
+ // Can we issue a simple echo command?
55
+ exec( 'echo backupwordpress', $output, $return );
56
+
57
+ if ( $return !== 0 ) {
58
+ return false;
59
+ }
60
+
61
+ return true;
62
+
63
+ }
64
+
65
+ /**
66
+ * Check whether a PHP function has been disabled.
67
+ *
68
+ * @param string $function The function you want to test for.
69
+ * @param string $ini_get_callback By default we check with ini_get, but
70
+ * it's possible to overridde this for
71
+ * testing purposes.
72
+ *
73
+ * @return boolean Whether the function is disabled or not.
74
+ */
75
+ public static function is_function_disabled( $function, $ini_get_callback = 'ini_get' ) {
76
+
77
+ // Suhosin stores it's disabled functions in `suhosin.executor.func.blacklist`
78
+ $suhosin_blacklist = array_map( 'trim', explode( ',', @call_user_func( $ini_get_callback, 'suhosin.executor.func.blacklist' ) ) );
79
+
80
+ // PHP supports disabling functions by adding them to `disable_functions` in php.ini.
81
+ $disabled_functions = array_map( 'trim', explode( ',', @call_user_func( $ini_get_callback, 'disable_functions' ) ) );
82
+
83
+ if ( in_array( $function, array_merge( $suhosin_blacklist, $disabled_functions ) ) ) {
84
+ return true;
85
+ }
86
+
87
+ return false;
88
+
89
+ }
90
+
91
+ /**
92
+ * Attempt to work out path to a cli executable.
93
+ *
94
+ * @param array $paths An array of paths to check against.
95
+ *
96
+ * @return string|false The path to the executable.
97
+ */
98
+ public static function get_executable_path( $paths ) {
99
+
100
+ if ( ! self::is_exec_available() ) {
101
+ return false;
102
+ }
103
+
104
+ $paths = array_map( 'wp_normalize_path', $paths );
105
+
106
+ foreach ( $paths as $path ) {
107
+
108
+ $output = $result = 0;
109
+
110
+ /**
111
+ * Attempt to call `--version` on each path, the one which works
112
+ * must be the correct path.
113
+ *
114
+ * We pipe STDERR to /dev/null so we don't leak errors.
115
+ */
116
+ exec( escapeshellarg( $path ) . ' --version ' . ignore_stderr(), $output, $result );
117
+
118
+ // If the command executed successfully then this must be the correct path
119
+ if ( $result === 0 ) {
120
+ return $path;
121
+ }
122
+
123
+ }
124
+
125
+ return false;
126
+
127
+ }
128
+
129
+ }
classes/backup/class-backup.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ class Backup {
6
+
7
+ private $excludes;
8
+ public $warnings = array();
9
+ public $errors = array();
10
+ private $backup_filename;
11
+ private $database_dump_filename;
12
+ private $backup_filepath = '';
13
+ private $database_dump_filepath = '';
14
+ private $status = null;
15
+ private $type = 'complete';
16
+
17
+ public function __construct( $backup_filename, $database_dump_filename = null ) {
18
+ $this->backup_filename = $backup_filename;
19
+ $this->database_dump_filename = $database_dump_filename;
20
+ }
21
+
22
+ public function set_type( $type ) {
23
+ $this->type = $type;
24
+ }
25
+
26
+ public function set_backup_filename( $filename ) {
27
+ $this->backup_filename = $filename;
28
+ }
29
+
30
+ public function set_status( Backup_Status $status ) {
31
+ $this->status = $status;
32
+ }
33
+
34
+ public function set_excludes( Excludes $excludes ) {
35
+ $this->excludes = $excludes;
36
+ }
37
+
38
+ public function run() {
39
+
40
+ if ( $this->type !== 'file' ) {
41
+ $this->backup_database();
42
+ }
43
+
44
+ if ( $this->type !== 'database' ) {
45
+ $this->backup_files();
46
+ }
47
+
48
+ }
49
+
50
+ public function backup_database() {
51
+
52
+ if ( $this->status ) {
53
+ $this->status->set_status( __( 'Backing up database...', 'backupwordpress' ) );
54
+ }
55
+
56
+ $database_backup_engines = apply_filters( 'hmbkp_database_backup_engines', array(
57
+ new Mysqldump_Database_Backup_Engine,
58
+ new IMysqldump_Database_Backup_Engine
59
+ ) );
60
+
61
+ // Set the file backup engine settings
62
+ if ( $this->database_dump_filename ) {
63
+ foreach( $database_backup_engines as &$backup_engine ) {
64
+ $backup_engine->set_backup_filename( $this->database_dump_filename );
65
+ }
66
+ }
67
+
68
+ // Dump the database
69
+ $database_dump = $this->perform_backup( $database_backup_engines );
70
+
71
+ if ( is_a( $database_dump, __NAMESPACE__ . '\\Backup_Engine' ) ) {
72
+ $this->database_dump_filepath = $database_dump->get_backup_filepath();
73
+ }
74
+
75
+ // Fire up the file backup engines
76
+ $file_backup_engines = apply_filters( 'hmbkp_file_backup_engines', array(
77
+ new Zip_File_Backup_Engine,
78
+ new Zip_Archive_File_Backup_Engine
79
+ ) );
80
+
81
+ // Set the file backup engine settings
82
+ foreach( $file_backup_engines as &$backup_engine ) {
83
+ $backup_engine->set_backup_filename( $this->backup_filename );
84
+ $backup_engine->set_excludes( new Excludes( array( '*.zip', 'index.html', '.htaccess', '.*-running' ) ) );
85
+ }
86
+
87
+ // Zip up the database dump
88
+ $root = Path::get_root();
89
+ Path::get_instance()->set_root( Path::get_path() );
90
+ $file_backup = $this->perform_backup( $file_backup_engines );
91
+ Path::get_instance()->set_root( $root );
92
+
93
+ if ( is_a( $file_backup, __NAMESPACE__ . '\\Backup_Engine' ) ) {
94
+ $this->backup_filepath = $file_backup->get_backup_filepath();
95
+ }
96
+
97
+ // Delete the Database Backup now that we've zipped it up
98
+ if ( file_exists( $this->database_dump_filepath ) ) {
99
+ unlink( $this->database_dump_filepath );
100
+ }
101
+
102
+ }
103
+
104
+ public function backup_files() {
105
+
106
+ if ( $this->status ) {
107
+ $this->status->set_status( __( 'Backing up files...', 'backupwordpress' ) );
108
+ }
109
+
110
+ // Fire up the file backup engines
111
+ $backup_engines = apply_filters( 'hmbkp_file_backup_engines', array(
112
+ new Zip_File_Backup_Engine,
113
+ new Zip_Archive_File_Backup_Engine
114
+ ) );
115
+
116
+ // Set the file backup engine settings
117
+ foreach( $backup_engines as &$backup_engine ) {
118
+ $backup_engine->set_backup_filename( $this->backup_filename );
119
+ if ( $this->excludes ) {
120
+ $backup_engine->set_excludes( $this->excludes );
121
+ }
122
+ }
123
+
124
+ $file_backup = $this->perform_backup( $backup_engines );
125
+
126
+ if ( is_a( $file_backup, __NAMESPACE__ . '\\Backup_Engine' ) ) {
127
+ $this->backup_filepath = $file_backup->get_backup_filepath();
128
+ }
129
+
130
+ }
131
+
132
+ /**
133
+ * Perform the backup by iterating through each Backup_Engine in turn until
134
+ * we find one which works. If a backup filename or any excludes have been
135
+ * set then those are passed to each Backup_Engine.
136
+ */
137
+ public function perform_backup( Array $backup_engines ) {
138
+
139
+ foreach ( $backup_engines as $backup_engine ) {
140
+
141
+ if ( $backup_engine->backup() ) {
142
+ $this->warnings = array_merge( $this->warnings, $backup_engine->get_warnings() );
143
+ return $backup_engine;
144
+ }
145
+ $this->warnings = array_merge( $this->warnings, $backup_engine->get_warnings() );
146
+ $this->errors = array_merge( $this->errors, $backup_engine->get_errors() );
147
+ }
148
+
149
+ return false;
150
+
151
+ }
152
+
153
+ public function get_warnings() {
154
+ return $this->warnings;
155
+ }
156
+
157
+ public function get_errors() {
158
+ return $this->errors;
159
+ }
160
+
161
+ /**
162
+ * Add an warning to the errors warnings.
163
+ *
164
+ * A warning is always treat as non-fatal and should only be used for recoverable
165
+ * issues with the backup process.
166
+ *
167
+ * @param string $context The context for the warning.
168
+ * @param string $error The warning that was encountered.
169
+ */
170
+ public function warning( $context, $warning ) {
171
+
172
+ if ( empty( $context ) || empty( $warning ) ) {
173
+ return;
174
+ }
175
+
176
+ // Ensure we don't store duplicate warnings by md5'ing the error as the key
177
+ $this->warnings[ $context ][ $_key = md5( implode( ':', (array) $warning ) ) ] = $warning;
178
+
179
+ }
180
+
181
+ public function get_database_backup_filepath() {
182
+ return $this->database_dump_filepath;
183
+ }
184
+
185
+ public function get_backup_filepath() {
186
+ return $this->backup_filepath;
187
+ }
188
+
189
+ /**
190
+ * Back compat with old method name
191
+ *
192
+ * @see Backup::get_backup_filepath()
193
+ * @deprecated 3.4 Use Backup::get_backup_filepath()
194
+ */
195
+ public function get_archive_filepath() {
196
+ _deprecated_function( __FUNCTION__, '3.4', 'get_backup_filepath()' );
197
+ return $this->get_backup_filepath();
198
+ }
199
+
200
+ }
classes/class-backup.php DELETED
@@ -1,1663 +0,0 @@
1
- <?php
2
-
3
- namespace HM\BackUpWordPress;
4
- use Symfony\Component\Finder\Finder;
5
- use Ifsnop\Mysqldump as IMysqldump;
6
-
7
- /**
8
- * Generic file and database backup class
9
- *
10
- * @version 2.3
11
- */
12
- class Backup {
13
-
14
- /**
15
- * The backup type, must be either complete, file or database
16
- *
17
- * @string
18
- */
19
- private $type = '';
20
-
21
- /**
22
- * The filename of the backup file
23
- *
24
- * @string
25
- */
26
- private $archive_filename = '';
27
-
28
- /**
29
- * The filename of the database dump
30
- *
31
- * @string
32
- */
33
- private $database_dump_filename = '';
34
-
35
- /**
36
- * The path to the zip command
37
- *
38
- * @string
39
- */
40
- private $zip_command_path;
41
-
42
- /**
43
- * The path to the mysqldump command
44
- *
45
- * @string
46
- */
47
- private $mysqldump_command_path;
48
-
49
- /**
50
- * The filename of the existing backup file
51
- *
52
- * @string
53
- */
54
- private $existing_archive_filepath = '';
55
-
56
- /**
57
- * An array of exclude rules
58
- *
59
- * @array
60
- */
61
- private $excludes = array();
62
-
63
- /**
64
- * The path that should be backed up
65
- *
66
- * @var string
67
- */
68
- private $root = '';
69
-
70
- /**
71
- * An array of all the files in root
72
- * excluding excludes and unreadable files
73
- *
74
- * @var array
75
- */
76
- private $files = array();
77
-
78
- /**
79
- * An array of all the files in root
80
- * that are unreadable
81
- *
82
- * @var array
83
- */
84
- private $unreadable_files = array();
85
-
86
- /**
87
- * An array of all the files in root
88
- * that will be included in the backup
89
- *
90
- * @var array
91
- */
92
- protected $included_files = array();
93
-
94
- /**
95
- * An array of all the files in root
96
- * that match the exclude rules
97
- *
98
- * @var array
99
- */
100
- private $excluded_files = array();
101
-
102
- /**
103
- * Contains an array of errors
104
- *
105
- * @var mixed
106
- */
107
- private $errors = array();
108
-
109
- /**
110
- * Contains an array of warnings
111
- *
112
- * @var mixed
113
- */
114
- private $warnings = array();
115
-
116
- /**
117
- * The archive method used
118
- *
119
- * @var string
120
- */
121
- private $archive_method = '';
122
-
123
- /**
124
- * The mysqldump method used
125
- *
126
- * @var string
127
- */
128
- private $mysqldump_method = '';
129
-
130
- /**
131
- * @var bool
132
- */
133
- protected $mysqldump_verified = false;
134
-
135
- /**
136
- * @var bool
137
- */
138
- protected $archive_verified = false;
139
-
140
- /**
141
- * @var string
142
- */
143
- protected $action_callback = '';
144
-
145
- /**
146
- * List of patterns we want to exclude by default.
147
- * @var array
148
- */
149
- protected $default_excludes = array(
150
- '.git/',
151
- '.svn/',
152
- '.DS_Store',
153
- '.idea/',
154
- 'backwpup-*',
155
- 'updraft',
156
- 'wp-snapshots',
157
- 'backupbuddy_backups',
158
- 'pb_backupbuddy',
159
- 'backup-db',
160
- 'Envato-backups',
161
- 'managewp',
162
- 'backupwordpress-*-backups',
163
- );
164
-
165
- /**
166
- * Returns a filterable array of excluded directories and files.
167
- *
168
- * @return mixed|void
169
- */
170
- public function default_excludes() {
171
- return apply_filters( 'hmbkp_default_excludes', $this->default_excludes );
172
- }
173
-
174
- /**
175
- * Check whether safe mode is active or not
176
- *
177
- * @param string $ini_get_callback
178
- *
179
- * @return bool
180
- */
181
- public static function is_safe_mode_active( $ini_get_callback = 'ini_get' ) {
182
-
183
- $safe_mode = @call_user_func( $ini_get_callback, 'safe_mode' );
184
-
185
- if ( $safe_mode && strtolower( $safe_mode ) != 'off' ) {
186
- return true;
187
- }
188
-
189
- return false;
190
-
191
- }
192
-
193
- /**
194
- * Check whether shell_exec has been disabled.
195
- *
196
- * @return bool
197
- */
198
- public static function is_shell_exec_available() {
199
-
200
- // Are we in Safe Mode
201
- if ( self::is_safe_mode_active() ) {
202
- return false;
203
- }
204
-
205
- // Is shell_exec or escapeshellcmd or escapeshellarg disabled?
206
- if ( self::is_function_disabled( 'suhosin.executor.func.blacklist' ) ) {
207
- return false;
208
- }
209
-
210
- // Functions can also be disabled via suhosin
211
- if ( self::is_function_disabled( 'disable_functions' ) ) {
212
- return false;
213
- }
214
-
215
- // Can we issue a simple echo command?
216
- if ( ! @shell_exec( 'echo backupwordpress' ) ) {
217
- return false;
218
- }
219
-
220
- return true;
221
-
222
- }
223
-
224
- protected static function is_function_disabled( $ini_setting ) {
225
-
226
- if ( array_intersect( array(
227
- 'shell_exec',
228
- 'escapeshellarg',
229
- 'escapeshellcmd'
230
- ), array_map( 'trim', explode( ',', @ini_get( $ini_setting ) ) ) ) ) {
231
- return false;
232
- }
233
-
234
- }
235
-
236
-
237
- /**
238
- * Attempt to work out the root directory of the site, that
239
- * is, the path equivelant of home_url().
240
- *
241
- * @return string $home_path
242
- */
243
- public static function get_home_path() {
244
-
245
- if ( defined( 'HMBKP_ROOT' ) && HMBKP_ROOT ) {
246
- return wp_normalize_path( HMBKP_ROOT );
247
- }
248
-
249
- $home_url = home_url();
250
- $site_url = site_url();
251
-
252
- $home_path = ABSPATH;
253
-
254
- // If site_url contains home_url and they differ then assume WordPress is installed in a sub directory
255
- if ( $home_url !== $site_url && strpos( $site_url, $home_url ) === 0 ) {
256
- $home_path = trailingslashit( substr( wp_normalize_path( ABSPATH ), 0, strrpos( wp_normalize_path( ABSPATH ), str_replace( $home_url, '', $site_url ) ) ) );
257
- }
258
-
259
- return wp_normalize_path( $home_path );
260
-
261
- }
262
-
263
- /**
264
- * Sets up the default properties
265
- */
266
- public function __construct() {
267
-
268
- // Raise the memory limit and max_execution time
269
- @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
270
- @set_time_limit( 0 );
271
-
272
- // Set a custom error handler so we can track errors
273
- set_error_handler( array( $this, 'error_handler' ) );
274
-
275
- // Some properties can be overridden with defines
276
- if ( defined( 'HMBKP_EXCLUDE' ) && HMBKP_EXCLUDE ) {
277
- $this->set_excludes( HMBKP_EXCLUDE, true );
278
- }
279
-
280
- if ( defined( 'HMBKP_MYSQLDUMP_PATH' ) ) {
281
- $this->set_mysqldump_command_path( HMBKP_MYSQLDUMP_PATH );
282
- }
283
-
284
- if ( defined( 'HMBKP_ZIP_PATH' ) ) {
285
- $this->set_zip_command_path( HMBKP_ZIP_PATH );
286
- }
287
-
288
- }
289
-
290
- /**
291
- * Simple class wrapper for Path::get_path()
292
- *
293
- * @return string
294
- */
295
- private function get_path() {
296
- return Path::get_instance()->get_path();
297
- }
298
-
299
- /**
300
- * Get the full filepath to the archive file
301
- *
302
- * @return string
303
- */
304
- public function get_archive_filepath() {
305
- return trailingslashit( $this->get_path() ) . $this->get_archive_filename();
306
- }
307
-
308
- /**
309
- * Get the filename of the archive file
310
- *
311
- * @return string
312
- */
313
- public function get_archive_filename() {
314
-
315
- if ( empty( $this->archive_filename ) ) {
316
- $this->set_archive_filename( implode( '-', array(
317
- sanitize_title( str_ireplace( array(
318
- 'http://',
319
- 'https://',
320
- 'www'
321
- ), '', home_url() ) ),
322
- 'backup',
323
- current_time( 'Y-m-d-H-i-s' )
324
- ) ) . '.zip' );
325
- }
326
-
327
- return $this->archive_filename;
328
-
329
- }
330
-
331
- /**
332
- * Set the filename of the archive file
333
- *
334
- * @param string $filename
335
- *
336
- * @return \WP_Error|null
337
- */
338
- public function set_archive_filename( $filename ) {
339
-
340
- if ( empty( $filename ) || ! is_string( $filename ) ) {
341
- return new \WP_Error( 'invalid_file_name', __( 'archive filename must be a non-empty string', 'backupwordpress' ) );
342
- }
343
-
344
- if ( pathinfo( $filename, PATHINFO_EXTENSION ) !== 'zip' ) {
345
- return new \WP_Error( 'invalid_file_extension', sprintf( __( 'invalid file extension for archive filename <code>%s</code>', 'backupwordpress' ), $filename ) );
346
- }
347
-
348
- $this->archive_filename = strtolower( sanitize_file_name( remove_accents( $filename ) ) );
349
-
350
- }
351
-
352
- /**
353
- * Get the full filepath to the database dump file.
354
- *
355
- * @return string
356
- */
357
- public function get_database_dump_filepath() {
358
- return trailingslashit( $this->get_path() ) . $this->get_database_dump_filename();
359
- }
360
-
361
- /**
362
- * Get the filename of the database dump file
363
- *
364
- * @return string
365
- */
366
- public function get_database_dump_filename() {
367
-
368
- if ( empty( $this->database_dump_filename ) ) {
369
- $this->set_database_dump_filename( 'database_' . DB_NAME . '.sql' );
370
- }
371
-
372
- return $this->database_dump_filename;
373
-
374
- }
375
-
376
- /**
377
- * Set the filename of the database dump file
378
- *
379
- * @param string $filename
380
- *
381
- * @return \WP_Error|null
382
- */
383
- public function set_database_dump_filename( $filename ) {
384
-
385
- if ( empty( $filename ) || ! is_string( $filename ) ) {
386
- return new \WP_Error( 'invalid_file_name', __( 'database dump filename must be a non-empty string', 'backupwordpress' ) );
387
- }
388
-
389
- if ( pathinfo( $filename, PATHINFO_EXTENSION ) !== 'sql' ) {
390
- return new \WP_Error( 'invalid_file_extension', sprintf( __( 'invalid file extension for database dump filename <code>%s</code>', 'backupwordpress' ), $filename ) );
391
- }
392
-
393
- $this->database_dump_filename = strtolower( sanitize_file_name( remove_accents( $filename ) ) );
394
-
395
- }
396
-
397
- /**
398
- * Get the root directory to backup from
399
- *
400
- * Defaults to the root of the path equivalent of your home_url
401
- *
402
- * @return string
403
- */
404
- public function get_root() {
405
-
406
- if ( empty( $this->root ) ) {
407
- $this->set_root( wp_normalize_path( self::get_home_path() ) );
408
- }
409
-
410
- return $this->root;
411
-
412
- }
413
-
414
- /**
415
- * Set the root directory to backup from
416
- *
417
- * @param string $path
418
- *
419
- * @return \WP_Error|null
420
- */
421
- public function set_root( $path ) {
422
-
423
- if ( empty( $path ) || ! is_string( $path ) || ! is_dir( $path ) ) {
424
- return new \WP_Error( 'invalid_directory_path', sprintf( __( 'Invalid root path <code>%s</code> must be a valid directory path', 'backupwordpress' ), $path ) );
425
- }
426
-
427
- $this->root = wp_normalize_path( $path );
428
-
429
- }
430
-
431
- /**
432
- * Get the filepath for the existing archive
433
- *
434
- * @return string
435
- */
436
- public function get_existing_archive_filepath() {
437
- return $this->existing_archive_filepath;
438
- }
439
-
440
- /**
441
- * Set the filepath for the existing archive
442
- *
443
- * @param string $existing_archive_filepath
444
- *
445
- * @return null
446
- */
447
- public function set_existing_archive_filepath( $existing_archive_filepath ) {
448
-
449
- if ( empty( $existing_archive_filepath ) || ! is_string( $existing_archive_filepath ) ) {
450
- return new \WP_Error( 'invalid_existing_archive_filepath', sprintf( __( 'Invalid existing archive filepath <code>%s</code> must be a non-empty (string)', 'backupwordpress' ), $existing_archive_filepath ) );
451
- }
452
-
453
- $this->existing_archive_filepath = wp_normalize_path( $existing_archive_filepath );
454
-
455
- }
456
-
457
- /**
458
- * Get the archive method that was used for the backup
459
- *
460
- * Will be either zip, ZipArchive or PclZip
461
- *
462
- */
463
- public function get_archive_method() {
464
- return $this->archive_method;
465
- }
466
-
467
- /**
468
- * Get the database dump method that was used for the backup
469
- *
470
- * Will be either mysqldump or mysqldump_fallback
471
- *
472
- */
473
- public function get_mysqldump_method() {
474
- return $this->mysqldump_method;
475
- }
476
-
477
- /**
478
- * Get the backup type
479
- *
480
- * Defaults to complete
481
- *
482
- */
483
- public function get_type() {
484
-
485
- if ( empty( $this->type ) ) {
486
- $this->set_type( 'complete' );
487
- }
488
-
489
- return $this->type;
490
-
491
- }
492
-
493
- /**
494
- * Set the backup type
495
- *
496
- * $type must be one of complete, database or file
497
- *
498
- * @param string $type
499
- *
500
- * @return \WP_Error|null
501
- */
502
- public function set_type( $type ) {
503
-
504
- if ( ! is_string( $type ) || ! in_array( $type, array( 'file', 'database', 'complete' ) ) ) {
505
- return new \WP_Error( 'invalid_backup_type', sprintf( __( 'Invalid backup type <code>%s</code> must be one of (string) file, database or complete', 'backupwordpress' ), $type ) );
506
- }
507
-
508
- $this->type = $type;
509
-
510
- }
511
-
512
- /**
513
- * Get the path to the mysqldump bin
514
- *
515
- * If not explicitly set will attempt to work
516
- * it out by checking common locations
517
- *
518
- * @return string
519
- */
520
- public function get_mysqldump_command_path() {
521
-
522
- // Check shell_exec is available
523
- if ( ! self::is_shell_exec_available() ) {
524
- return '';
525
- }
526
-
527
- // Return now if it's already been set
528
- if ( isset( $this->mysqldump_command_path ) ) {
529
- return $this->mysqldump_command_path;
530
- }
531
-
532
- $this->mysqldump_command_path = '';
533
-
534
- // Does mysqldump work
535
- if ( is_null( shell_exec( 'hash mysqldump 2>&1' ) ) ) {
536
-
537
- // If so store it for later
538
- $this->set_mysqldump_command_path( 'mysqldump' );
539
-
540
- // And return now
541
- return $this->mysqldump_command_path;
542
-
543
- }
544
-
545
- // List of possible mysqldump locations
546
- $mysqldump_locations = array(
547
- '/usr/local/bin/mysqldump',
548
- '/usr/local/mysql/bin/mysqldump',
549
- '/usr/mysql/bin/mysqldump',
550
- '/usr/bin/mysqldump',
551
- '/opt/local/lib/mysql6/bin/mysqldump',
552
- '/opt/local/lib/mysql5/bin/mysqldump',
553
- '/opt/local/lib/mysql4/bin/mysqldump',
554
- '/xampp/mysql/bin/mysqldump',
555
- '/Program Files/xampp/mysql/bin/mysqldump',
556
- '/Program Files/MySQL/MySQL Server 6.0/bin/mysqldump',
557
- '/Program Files/MySQL/MySQL Server 5.7/bin/mysqldump',
558
- '/Program Files/MySQL/MySQL Server 5.6/bin/mysqldump',
559
- '/Program Files/MySQL/MySQL Server 5.5/bin/mysqldump',
560
- '/Program Files/MySQL/MySQL Server 5.4/bin/mysqldump',
561
- '/Program Files/MySQL/MySQL Server 5.1/bin/mysqldump',
562
- '/Program Files/MySQL/MySQL Server 5.0/bin/mysqldump',
563
- '/Program Files/MySQL/MySQL Server 4.1/bin/mysqldump',
564
- '/opt/local/bin/mysqldump'
565
- );
566
-
567
- // Find the first one which works
568
- foreach ( $mysqldump_locations as $location ) {
569
- if ( (is_null( shell_exec( 'hash ' . wp_normalize_path( $location ) . ' 2>&1' ) ) ) && @is_executable( wp_normalize_path( $location ) ) ) {
570
- $this->set_mysqldump_command_path( $location );
571
- break; // Found one
572
- }
573
- }
574
-
575
- return $this->mysqldump_command_path;
576
-
577
- }
578
-
579
- /**
580
- * Set the path to the mysqldump bin
581
- *
582
- * Setting the path to false will cause the database
583
- * dump to use the php fallback
584
- *
585
- * @param mixed $path
586
- */
587
- public function set_mysqldump_command_path( $path ) {
588
- $this->mysqldump_command_path = $path;
589
- }
590
-
591
- /**
592
- * Get the path to the zip bin
593
- *
594
- * If not explicitly set will attempt to work
595
- * it out by checking common locations
596
- *
597
- * @return string
598
- */
599
- public function get_zip_command_path() {
600
-
601
- // Check shell_exec is available
602
- if ( ! self::is_shell_exec_available() ) {
603
- return '';
604
- }
605
-
606
- // Return now if it's already been set
607
- if ( isset( $this->zip_command_path ) ) {
608
- return $this->zip_command_path;
609
- }
610
-
611
- $this->zip_command_path = '';
612
-
613
- // Does zip work
614
- if ( is_null( shell_exec( 'hash zip 2>&1' ) ) ) {
615
-
616
- // If so store it for later
617
- $this->set_zip_command_path( 'zip' );
618
-
619
- // And return now
620
- return $this->zip_command_path;
621
-
622
- }
623
-
624
- // List of possible zip locations
625
- $zip_locations = array(
626
- '/usr/bin/zip',
627
- '/opt/local/bin/zip'
628
- );
629
-
630
- // Find the first one which works
631
- foreach ( $zip_locations as $location ) {
632
- if ( @is_executable( wp_normalize_path( $location ) ) ) {
633
- $this->set_zip_command_path( $location );
634
- break; // Found one
635
- }
636
- }
637
-
638
- return $this->zip_command_path;
639
-
640
- }
641
-
642
- /**
643
- * Set the path to the zip bin
644
- *
645
- * Setting the path to false will cause the database
646
- * dump to use the php fallback
647
- *
648
- * @param mixed $path
649
- */
650
- public function set_zip_command_path( $path ) {
651
- $this->zip_command_path = $path;
652
- }
653
-
654
- /**
655
- * Fire actions for the various backup stages
656
- *
657
- * Callers can register callbacks to be called using `set_action_callback`
658
- * Both the action and the instance on Backup are then passed to the callback function
659
- *
660
- * @see set_action_callback
661
- *
662
- * @param string $action The event to fire
663
- */
664
- protected function do_action( $action ) {
665
-
666
- // If we have any callbacks then let's fire them
667
- if ( ! empty( $this->action_callback ) ) {
668
-
669
- // Order them by priority, lowest priority first
670
- ksort( $this->action_callback );
671
-
672
- foreach ( $this->action_callback as $priority ) {
673
- foreach ( $priority as $callback ) {
674
- call_user_func( $callback, $action, $this );
675
- }
676
- }
677
-
678
- }
679
-
680
- // Also fire a global WordPress action
681
- do_action( $action, $this );
682
-
683
- }
684
-
685
- /**
686
- * Allow the caller to set a callback function that will be invoked whenever
687
- * an action fires
688
- *
689
- * @see do_action
690
- * @see /do_action
691
- *
692
- * @param callable $callback The function or method to be called
693
- * @param int $priority The priority of the callback
694
- */
695
- public function set_action_callback( $callback, $priority = 10 ) {
696
- $this->action_callback[ $priority ][] = $callback;
697
- }
698
-
699
- /**
700
- * Kick off a backup
701
- *
702
- * @todo should be renamed so it's not same as class
703
- * @return null
704
- */
705
- public function backup() {
706
-
707
- $this->do_action( 'hmbkp_backup_started' );
708
-
709
- // Backup database
710
- if ( $this->get_type() !== 'file' ) {
711
- $this->dump_database();
712
- }
713
-
714
- // Zip everything up
715
- $this->archive();
716
-
717
- $this->do_action( 'hmbkp_backup_complete' );
718
-
719
- }
720
-
721
- /**
722
- * Create the mysql backup
723
- *
724
- * Uses mysqldump if available, falls back to PHP
725
- * if not.
726
- *
727
- */
728
- public function dump_database() {
729
-
730
- // Attempt to use native mysqldump
731
- if ( self::is_shell_exec_available() && $this->get_mysqldump_command_path() && ! is_wp_error( $this->user_can_connect() ) ) {
732
- $this->mysqldump();
733
- }
734
-
735
- // If we cannot run mysqldump via CLI, fallback to PHP
736
- if ( empty( $this->mysqldump_verified ) ) {
737
- $this->mysqldump_fallback();
738
- }
739
-
740
- $this->do_action( 'hmbkp_mysqldump_finished' );
741
-
742
- }
743
-
744
- /**
745
- * Export the database to an .sql file via the command line with mysqldump
746
- */
747
- public function mysqldump() {
748
-
749
- $this->mysqldump_method = 'mysqldump';
750
-
751
- $this->do_action( 'hmbkp_mysqldump_started' );
752
-
753
- // Guess port or socket connection type
754
- $port_or_socket = strstr( DB_HOST, ':' );
755
-
756
- $host = DB_HOST;
757
-
758
- if ( ! empty( $port_or_socket ) ) {
759
-
760
- $host = substr( DB_HOST, 0, strpos( DB_HOST, ':' ) );
761
-
762
- $port_or_socket = substr( $port_or_socket, 1 );
763
-
764
- if ( 0 !== strpos( $port_or_socket, '/' ) ) {
765
-
766
- $port = intval( $port_or_socket );
767
-
768
- $maybe_socket = strstr( $port_or_socket, ':' );
769
-
770
- if ( ! empty( $maybe_socket ) ) {
771
-
772
- $socket = substr( $maybe_socket, 1 );
773
-
774
- }
775
-
776
- } else {
777
-
778
- $socket = $port_or_socket;
779
-
780
- }
781
- }
782
-
783
- // Path to the mysqldump executable
784
- $cmd = escapeshellarg( $this->get_mysqldump_command_path() );
785
-
786
- // We don't want to create a new DB
787
- $cmd .= ' --no-create-db';
788
-
789
- // Allow lock-tables to be overridden
790
- if ( ! defined( 'HMBKP_MYSQLDUMP_SINGLE_TRANSACTION' ) || false !== HMBKP_MYSQLDUMP_SINGLE_TRANSACTION ) {
791
- $cmd .= ' --single-transaction';
792
- }
793
-
794
- // Make sure binary data is exported properly
795
- $cmd .= ' --hex-blob';
796
-
797
- // Username
798
- $cmd .= ' -u ' . escapeshellarg( DB_USER );
799
-
800
- // Don't pass the password if it's blank
801
- if ( DB_PASSWORD ) {
802
- $cmd .= ' -p' . escapeshellarg( DB_PASSWORD );
803
- }
804
-
805
- // Set the host
806
- $cmd .= ' -h ' . escapeshellarg( $host );
807
-
808
- // Set the port if it was set
809
- if ( ! empty( $port ) && is_numeric( $port ) ) {
810
- $cmd .= ' -P ' . $port;
811
- }
812
-
813
- // Set the socket path
814
- if ( ! empty( $socket ) && ! is_numeric( $socket ) ) {
815
- $cmd .= ' --protocol=socket -S ' . $socket;
816
- }
817
-
818
- // The file we're saving too
819
- $cmd .= ' -r ' . escapeshellarg( $this->get_database_dump_filepath() );
820
-
821
- // The database we're dumping
822
- $cmd .= ' ' . escapeshellarg( DB_NAME );
823
-
824
- // Pipe STDERR to STDOUT
825
- $cmd .= ' 2>&1';
826
-
827
- // Store any returned data in an error
828
- $stderr = shell_exec( $cmd );
829
-
830
- // Skip the new password warning that is output in mysql > 5.6 (@see http://bugs.mysql.com/bug.php?id=66546)
831
- if ( 'Warning: Using a password on the command line interface can be insecure.' === trim( $stderr ) ) {
832
- $stderr = '';
833
- }
834
-
835
- if ( $stderr ) {
836
- $this->error( $this->get_mysqldump_method(), $stderr );
837
- }
838
-
839
- $this->verify_mysqldump();
840
-
841
- }
842
-
843
- /**
844
- * PHP mysqldump fallback functions, exports the database to a .sql file
845
- *
846
- */
847
- public function mysqldump_fallback() {
848
-
849
- $this->errors_to_warnings( $this->get_mysqldump_method() );
850
-
851
- $this->mysqldump_method = 'mysqldump_fallback';
852
-
853
- $this->do_action( 'hmbkp_mysqldump_started' );
854
-
855
- // Guess port or socket connection type
856
- $port_or_socket = strstr( DB_HOST, ':' );
857
-
858
- $host = DB_HOST;
859
-
860
- if ( ! empty( $port_or_socket ) ) {
861
-
862
- $host = substr( DB_HOST, 0, strpos( DB_HOST, ':' ) );
863
-
864
- $port_or_socket = substr( $port_or_socket, 1 );
865
-
866
- if ( 0 !== strpos( $port_or_socket, '/' ) ) {
867
-
868
- $port = intval( $port_or_socket );
869
-
870
- $maybe_socket = strstr( $port_or_socket, ':' );
871
-
872
- if ( ! empty( $maybe_socket ) ) {
873
-
874
- $socket = substr( $maybe_socket, 1 );
875
-
876
- }
877
-
878
- } else {
879
-
880
- $socket = $port_or_socket;
881
-
882
- }
883
- }
884
-
885
- // PDO connection string formats:
886
- // mysql:host=localhost;port=3307;dbname=testdb
887
- // mysql:unix_socket=/tmp/mysql.sock;dbname=testdb
888
-
889
- if ( $port_or_socket ) {
890
- if ( isset( $port ) ) {
891
- $dsn = 'mysql:host=' . DB_HOST . ';port=' . $port . ';dbname=' . DB_NAME;
892
- } elseif ( isset( $socket ) ) {
893
- $dsn = 'mysql:unix_socket=' . $socket . ';dbname=' . DB_NAME;
894
- }
895
- } else {
896
- $dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME;
897
- }
898
-
899
- // Get character set from constant if it is declared.
900
- if ( defined( 'DB_CHARSET' ) && DB_CHARSET ) {
901
- $charset = DB_CHARSET;
902
- } else {
903
- $charset = 'utf8';
904
- }
905
-
906
- if ( defined( 'DB_PASSWORD' ) && DB_PASSWORD ) {
907
- $pwd = DB_PASSWORD;
908
- } else {
909
- $pwd = '';
910
- }
911
-
912
- if ( ! defined( 'HMBKP_MYSQLDUMP_SINGLE_TRANSACTION' ) || false !== HMBKP_MYSQLDUMP_SINGLE_TRANSACTION ) {
913
- $single_transaction = true;
914
- } else {
915
- $single_transaction = false;
916
- }
917
-
918
- $dump_settings = array(
919
- 'default-character-set' => $charset,
920
- 'hex-blob' => true,
921
- 'single-transaction' => $single_transaction,
922
- );
923
-
924
- try {
925
-
926
- // Allow passing custom options to dump process.
927
- $dump_settings = apply_filters( 'hmbkp_mysqldump_fallback_dump_settings', $dump_settings );
928
-
929
- $dump = new IMysqldump\Mysqldump( $dsn, DB_USER, $pwd, $dump_settings );
930
-
931
- $dump->start( $this->get_database_dump_filepath() );
932
-
933
- } catch ( \Exception $e ) {
934
-
935
- return new \WP_Error( 'mysql-fallback-error', sprintf( __( 'mysqldump fallback error %s', 'backupwordpress' ), $e->getMessage() ) );
936
-
937
- }
938
-
939
- }
940
-
941
- /**
942
- * Zip up all the files.
943
- *
944
- * Attempts to use the shell zip command, if
945
- * thats not available then it falls back to
946
- * PHP ZipArchive.
947
- *
948
- */
949
- public function archive() {
950
-
951
- if ( defined( 'HMBKP_FORCE_ZIP_METHOD' ) ) {
952
- switch ( HMBKP_FORCE_ZIP_METHOD ) {
953
- case 'zip':
954
- if ( $this->get_zip_command_path() ) {
955
- $this->zip();
956
- } else {
957
- $this->warning( $this->get_archive_method(), __( 'Zip command is not available.', 'backupwordpress' ) );
958
- }
959
- break;
960
- case 'ziparchive':
961
- if ( class_exists( 'ZipArchive' ) ) {
962
- $this->zip_archive();
963
- } else {
964
- $this->warning( $this->get_archive_method(), __( 'ZipArchive method is not available.', 'backupwordpress' ) );
965
- }
966
- break;
967
- default:
968
- $this->warning( $this->get_archive_method(), __( 'No valid archive method found.', 'backupwordpress' ) );
969
- break;
970
- }
971
- } else {
972
- // Is zip available
973
- if ( $this->get_zip_command_path() ) {
974
- $this->zip();
975
- } else {
976
- // If the shell zip failed then use ZipArchive
977
- if ( empty( $this->archive_verified ) && class_exists( 'ZipArchive' ) ) {
978
- $this->zip_archive();
979
- } else {
980
- $this->warning( $this->get_archive_method(), __( 'No valid archive method found.', 'backupwordpress' ) );
981
- }
982
- }
983
-
984
- }
985
-
986
- // Delete the database dump file
987
- if ( file_exists( $this->get_database_dump_filepath() ) ) {
988
- unlink( $this->get_database_dump_filepath() );
989
- }
990
-
991
- $this->do_action( 'hmbkp_archive_finished' );
992
-
993
- }
994
-
995
- /**
996
- * Zip using the native zip command
997
- */
998
- public function zip() {
999
-
1000
- $this->archive_method = 'zip';
1001
-
1002
- $this->do_action( 'hmbkp_archive_started' );
1003
-
1004
- // Add the database dump to the archive
1005
- if ( 'file' !== $this->get_type() && file_exists( $this->get_database_dump_filepath() ) ) {
1006
- $stderr = shell_exec( 'cd ' . escapeshellarg( $this->get_path() ) . ' && ' . escapeshellcmd( $this->get_zip_command_path() ) . ' -q ' . escapeshellarg( $this->get_archive_filepath() ) . ' ' . escapeshellarg( $this->get_database_dump_filename() ) . ' 2>&1' );
1007
-
1008
- if ( ! empty ( $stderr ) ) {
1009
- $this->warning( $this->get_archive_method(), $stderr );
1010
- }
1011
- }
1012
-
1013
- // Zip up $this->root
1014
- if ( 'database' !== $this->get_type() ) {
1015
-
1016
- // cd to the site root
1017
- $command = 'cd ' . escapeshellarg( $this->get_root() );
1018
-
1019
- // Run the zip command with the recursive and quiet flags
1020
- $command .= ' && ' . escapeshellcmd( $this->get_zip_command_path() ) . ' -rq ';
1021
-
1022
- if ( defined( 'HMBKP_ENABLE_SYNC' ) && HMBKP_ENABLE_SYNC ) {
1023
-
1024
- // If the destination zip file already exists then let's just add changed files to save time
1025
- if ( file_exists( $this->get_archive_filepath() ) && $this->get_existing_archive_filepath() ) {
1026
- $command .= ' -FS ';
1027
- }
1028
-
1029
- }
1030
-
1031
- // Save the zip file to the correct path
1032
- $command .= escapeshellarg( $this->get_archive_filepath() ) . ' ./';
1033
-
1034
- // Pass exclude rules in if we have them
1035
- if ( $this->exclude_string( 'zip' ) ) {
1036
- $command .= ' -x ' . $this->exclude_string( 'zip' );
1037
- }
1038
-
1039
- // Push all output to STDERR
1040
- $command .= ' 2>&1';
1041
-
1042
- $stderr = shell_exec( $command );
1043
-
1044
- }
1045
-
1046
- if ( ! empty( $stderr ) ) {
1047
- $this->warning( $this->get_archive_method(), $stderr );
1048
- }
1049
-
1050
- $this->verify_archive();
1051
-
1052
- }
1053
-
1054
- /**
1055
- * Fallback for creating zip archives if zip command is
1056
- * unavailable.
1057
- */
1058
- public function zip_archive() {
1059
-
1060
- $this->errors_to_warnings( $this->get_archive_method() );
1061
- $this->archive_method = 'ziparchive';
1062
-
1063
- $this->do_action( 'hmbkp_archive_started' );
1064
-
1065
- $zip = new \ZipArchive();
1066
-
1067
- if ( ! class_exists( 'ZipArchive' ) || ! $zip->open( $this->get_archive_filepath(), \ZIPARCHIVE::CREATE ) ) {
1068
- return;
1069
- }
1070
-
1071
- $excludes = $this->exclude_string( 'regex' );
1072
-
1073
- // Add the database
1074
- if ( $this->get_type() !== 'file' && file_exists( $this->get_database_dump_filepath() ) ) {
1075
- $zip->addFile( $this->get_database_dump_filepath(), $this->get_database_dump_filename() );
1076
- }
1077
-
1078
- if ( $this->get_type() !== 'database' ) {
1079
-
1080
- $files_added = 0;
1081
-
1082
- foreach ( $this->get_files() as $file ) {
1083
-
1084
- // Skip dot files, they should only exist on versions of PHP between 5.2.11 -> 5.3
1085
- if ( method_exists( $file, 'isDot' ) && $file->isDot() ) {
1086
- continue;
1087
- }
1088
-
1089
- // Skip unreadable files
1090
- if ( ! @realpath( $file->getPathname() ) || ! $file->isReadable() ) {
1091
- continue;
1092
- }
1093
-
1094
- // Excludes
1095
- if ( $excludes && preg_match( '(' . $excludes . ')', str_ireplace( trailingslashit( $this->get_root() ), '', wp_normalize_path( $file->getPathname() ) ) ) ) {
1096
- continue;
1097
- }
1098
-
1099
- if ( $file->isDir() ) {
1100
- $zip->addEmptyDir( trailingslashit( str_ireplace( trailingslashit( $this->get_root() ), '', wp_normalize_path( $file->getPathname() ) ) ) );
1101
- } elseif ( $file->isFile() ) {
1102
- $zip->addFile( $file->getPathname(), str_ireplace( trailingslashit( $this->get_root() ), '', wp_normalize_path( $file->getPathname() ) ) );
1103
- }
1104
-
1105
- if ( ++ $files_added % 500 === 0 ) {
1106
- if ( ! $zip->close() || ! $zip->open( $this->get_archive_filepath(), \ZIPARCHIVE::CREATE ) ) {
1107
- return;
1108
- }
1109
- }
1110
-
1111
- }
1112
-
1113
- }
1114
-
1115
- if ( $zip->status ) {
1116
- $this->warning( $this->get_archive_method(), $zip->status );
1117
- }
1118
-
1119
- if ( $zip->statusSys ) {
1120
- $this->warning( $this->get_archive_method(), $zip->statusSys );
1121
- }
1122
-
1123
- $zip->close();
1124
-
1125
- $this->verify_archive();
1126
-
1127
- }
1128
-
1129
- public function verify_mysqldump() {
1130
-
1131
- $this->do_action( 'hmbkp_mysqldump_verify_started' );
1132
-
1133
- // If we've already passed then no need to check again
1134
- if ( ! empty( $this->mysqldump_verified ) ) {
1135
- return true;
1136
- }
1137
-
1138
- // If there are mysqldump errors delete the database dump file as mysqldump will still have written one
1139
- if ( $this->get_errors( $this->get_mysqldump_method() ) && file_exists( $this->get_database_dump_filepath() ) ) {
1140
- unlink( $this->get_database_dump_filepath() );
1141
- }
1142
-
1143
- // If we have an empty file delete it
1144
- if ( @filesize( $this->get_database_dump_filepath() ) === 0 ) {
1145
- unlink( $this->get_database_dump_filepath() );
1146
- }
1147
-
1148
- // If the file still exists then it must be good
1149
- if ( file_exists( $this->get_database_dump_filepath() ) ) {
1150
- return $this->mysqldump_verified = true;
1151
- }
1152
-
1153
- return false;
1154
-
1155
- }
1156
-
1157
- /**
1158
- * Verify that the archive is valid and contains all the files it should contain.
1159
- *
1160
- * @return bool
1161
- */
1162
- public function verify_archive() {
1163
-
1164
- $this->do_action( 'hmbkp_archive_verify_started' );
1165
-
1166
- // If we've already passed then no need to check again
1167
- if ( ! empty( $this->archive_verified ) ) {
1168
- return true;
1169
- }
1170
-
1171
- // If there are errors delete the backup file.
1172
- if ( $this->get_errors( $this->get_archive_method() ) && file_exists( $this->get_archive_filepath() ) ) {
1173
- unlink( $this->get_archive_filepath() );
1174
- }
1175
-
1176
- // If the archive file still exists assume it's good
1177
- if ( file_exists( $this->get_archive_filepath() ) ) {
1178
- return $this->archive_verified = true;
1179
- }
1180
-
1181
- return false;
1182
-
1183
- }
1184
-
1185
- /**
1186
- * Return an array of all files in the filesystem.
1187
- *
1188
- * @param bool $ignore_default_exclude_rules If true then will return all files under root. Otherwise returns all files except those matching default exclude rules.
1189
- *
1190
- * @return array
1191
- */
1192
- public function get_files( $ignore_default_exclude_rules = false ) {
1193
-
1194
- if ( ! empty( $this->files ) ) {
1195
- return $this->files;
1196
- }
1197
-
1198
- $finder = new Finder();
1199
- $finder->followLinks();
1200
- $finder->ignoreDotFiles( false );
1201
- $finder->ignoreUnreadableDirs();
1202
-
1203
- if ( ! $ignore_default_exclude_rules ) {
1204
- // Skips folders/files that match default exclude patterns
1205
- foreach ( $this->default_excludes() as $exclude ) {
1206
- $finder->notPath( $exclude );
1207
- }
1208
- }
1209
-
1210
- foreach ( $finder->in( $this->get_root() ) as $entry ) {
1211
- $this->files[] = $entry;
1212
- }
1213
-
1214
- return $this->files;
1215
-
1216
- }
1217
-
1218
- /**
1219
- * Returns an array of files that will be included in the backup.
1220
- *
1221
- * @return array
1222
- */
1223
- public function get_included_files() {
1224
-
1225
- if ( ! empty( $this->included_files ) ) {
1226
- return $this->included_files;
1227
- }
1228
-
1229
- $this->included_files = array();
1230
-
1231
- $excludes = $this->exclude_string( 'regex' );
1232
-
1233
- foreach ( $this->get_files( true ) as $file ) {
1234
-
1235
- // Skip dot files, they should only exist on versions of PHP between 5.2.11 -> 5.3
1236
- if ( method_exists( $file, 'isDot' ) && $file->isDot() ) {
1237
- continue;
1238
- }
1239
-
1240
- // Skip unreadable files
1241
- if ( ! @realpath( $file->getPathname() ) || ! $file->isReadable() ) {
1242
- continue;
1243
- }
1244
-
1245
- // Excludes
1246
- if ( $excludes && preg_match( '(' . $excludes . ')', str_ireplace( trailingslashit( $this->get_root() ), '', wp_normalize_path( $file->getPathname() ) ) ) ) {
1247
- continue;
1248
- }
1249
-
1250
- $this->included_files[] = $file;
1251
-
1252
- }
1253
-
1254
- return $this->included_files;
1255
-
1256
- }
1257
-
1258
- /**
1259
- * Returns an array of files that match the exclude rules.
1260
- *
1261
- * @return array
1262
- */
1263
- public function get_excluded_files() {
1264
-
1265
- if ( ! empty( $this->excluded_files ) ) {
1266
- return $this->excluded_files;
1267
- }
1268
-
1269
- $this->excluded_files = array();
1270
-
1271
- $excludes = $this->exclude_string( 'regex' );
1272
-
1273
- foreach ( $this->get_files( true ) as $file ) {
1274
-
1275
- // Skip dot files, they should only exist on versions of PHP between 5.2.11 -> 5.3
1276
- if ( method_exists( $file, 'isDot' ) && $file->isDot() ) {
1277
- continue;
1278
- }
1279
-
1280
- // Skip unreadable files
1281
- if ( ! @realpath( $file->getPathname() ) || ! $file->isReadable() ) {
1282
- continue;
1283
- }
1284
-
1285
- // Excludes
1286
- if ( $excludes && preg_match( '(' . $excludes . ')', str_ireplace( trailingslashit( $this->get_root() ), '', wp_normalize_path( $file->getPathname() ) ) ) ) {
1287
- $this->excluded_files[] = $file;
1288
- }
1289
-
1290
- }
1291
-
1292
- return $this->excluded_files;
1293
-
1294
- }
1295
-
1296
- /**
1297
- * Returns an array of unreadable files.
1298
- *
1299
- * @return array
1300
- */
1301
- public function get_unreadable_files() {
1302
-
1303
- if ( ! empty( $this->unreadable_files ) ) {
1304
- return $this->unreadable_files;
1305
- }
1306
-
1307
- $this->unreadable_files = array();
1308
-
1309
- foreach ( $this->get_files( true ) as $file ) {
1310
-
1311
- // Skip dot files, they should only exist on versions of PHP between 5.2.11 -> 5.3
1312
- if ( method_exists( $file, 'isDot' ) && $file->isDot() ) {
1313
- continue;
1314
- }
1315
-
1316
- if ( ! @realpath( $file->getPathname() ) || ! $file->isReadable() ) {
1317
- $this->unreadable_files[] = $file;
1318
- }
1319
-
1320
- }
1321
-
1322
- return $this->unreadable_files;
1323
-
1324
- }
1325
-
1326
- /**
1327
- * Get an array of exclude rules
1328
- *
1329
- * The backup path is automatically excluded
1330
- *
1331
- * @return array
1332
- */
1333
- public function get_excludes() {
1334
-
1335
- $excludes = array();
1336
-
1337
- if ( isset( $this->excludes ) ) {
1338
- $excludes = $this->excludes;
1339
- }
1340
-
1341
- // If path() is inside root(), exclude it
1342
- if ( strpos( $this->get_path(), $this->get_root() ) !== false ) {
1343
- array_unshift( $excludes, trailingslashit( $this->get_path() ) );
1344
- }
1345
-
1346
- return array_unique( $excludes );
1347
-
1348
- }
1349
-
1350
- /**
1351
- * Set the excludes, expects and array
1352
- *
1353
- * @param Array $excludes
1354
- * @param Bool $append
1355
- */
1356
- public function set_excludes( $excludes, $append = false ) {
1357
-
1358
- if ( is_string( $excludes ) ) {
1359
- $excludes = explode( ',', $excludes );
1360
- }
1361
-
1362
- if ( $append ) {
1363
- $excludes = array_merge( $this->excludes, $excludes );
1364
- }
1365
-
1366
- $this->excludes = array_filter( array_unique( array_map( 'trim', $excludes ) ) );
1367
-
1368
- }
1369
-
1370
- /**
1371
- * Generate the exclude param string for the zip backup
1372
- *
1373
- * Takes the exclude rules and formats them for use with either
1374
- * the shell zip command or pclzip
1375
- *
1376
- * @param string $context . (default: 'zip')
1377
- *
1378
- * @return string
1379
- */
1380
- public function exclude_string( $context = 'zip' ) {
1381
-
1382
- // Return a comma separated list by default
1383
- $separator = ', ';
1384
- $wildcard = '';
1385
-
1386
- // The zip command
1387
- if ( $context === 'zip' ) {
1388
- $wildcard = '*';
1389
- $separator = ' -x ';
1390
-
1391
- // The PclZip fallback library
1392
- } elseif ( $context === 'regex' ) {
1393
- $wildcard = '([\s\S]*?)';
1394
- $separator = '|';
1395
- }
1396
-
1397
- $excludes = $this->get_excludes();
1398
-
1399
- foreach ( $excludes as $key => &$rule ) {
1400
-
1401
- $file = $absolute = $fragment = false;
1402
-
1403
- // Files don't end with /
1404
- if ( ! in_array( substr( $rule, - 1 ), array( '\\', '/' ) ) ) {
1405
- $file = true;
1406
- } // If rule starts with a / then treat as absolute path
1407
- elseif ( in_array( substr( $rule, 0, 1 ), array( '\\', '/' ) ) ) {
1408
- $absolute = true;
1409
- } // Otherwise treat as dir fragment
1410
- else {
1411
- $fragment = true;
1412
- }
1413
-
1414
- // Strip $this->root and conform
1415
- $rule = str_ireplace( $this->get_root(), '', untrailingslashit( wp_normalize_path( $rule ) ) );
1416
-
1417
- // Strip the preceeding slash
1418
- if ( in_array( substr( $rule, 0, 1 ), array( '\\', '/' ) ) ) {
1419
- $rule = substr( $rule, 1 );
1420
- }
1421
-
1422
- // Escape string for regex
1423
- if ( $context === 'regex' ) {
1424
- $rule = str_replace( '.', '\.', $rule );
1425
- }
1426
-
1427
- // Convert any existing wildcards
1428
- if ( $wildcard !== '*' && false !== strpos( $rule, '*' ) ) {
1429
- $rule = str_replace( '*', $wildcard, $rule );
1430
- }
1431
-
1432
- // Wrap directory fragments and files in wildcards for zip
1433
- if ( 'zip' === $context && ( $fragment || $file ) ) {
1434
- $rule = $wildcard . $rule . $wildcard;
1435
- }
1436
-
1437
- // Add a wildcard to the end of absolute url for zips
1438
- if ( 'zip' === $context && $absolute ) {
1439
- $rule .= $wildcard;
1440
- }
1441
-
1442
- // Add and end carrot to files for pclzip but only if it doesn't end in a wildcard
1443
- if ( $file && 'regex' === $context ) {
1444
- $rule .= '$';
1445
- }
1446
-
1447
- // Add a start carrot to absolute urls for pclzip
1448
- if ( $absolute && 'regex' === $context ) {
1449
- $rule = '^' . $rule;
1450
- }
1451
-
1452
- }
1453
-
1454
- // Escape shell args for zip command
1455
- if ( $context === 'zip' ) {
1456
- $excludes = array_map( 'escapeshellarg', array_unique( $excludes ) );
1457
- }
1458
-
1459
- return implode( $separator, $excludes );
1460
-
1461
- }
1462
-
1463
- /**
1464
- * Get the errors
1465
- *
1466
- */
1467
- public function get_errors( $context = null ) {
1468
-
1469
- if ( ! empty( $context ) ) {
1470
- return isset( $this->errors[ $context ] ) ? $this->errors[ $context ] : array();
1471
- }
1472
-
1473
- return $this->errors;
1474
-
1475
- }
1476
-
1477
- /**
1478
- * Add an error to the errors stack
1479
- *
1480
- * @param string $context
1481
- * @param mixed $error
1482
- */
1483
- public function error( $context, $error ) {
1484
-
1485
- if ( empty( $context ) || empty( $error ) ) {
1486
- return;
1487
- }
1488
-
1489
- $this->do_action( 'hmbkp_error' );
1490
-
1491
- $this->errors[ $context ][ $_key = md5( implode( ':', (array) $error ) ) ] = $error;
1492
-
1493
- }
1494
-
1495
- /**
1496
- * Migrate errors to warnings
1497
- *
1498
- * @param null $context
1499
- */
1500
- private function errors_to_warnings( $context = null ) {
1501
-
1502
- $errors = empty( $context ) ? $this->get_errors() : array( $context => $this->get_errors( $context ) );
1503
-
1504
- if ( empty( $errors ) ) {
1505
- return;
1506
- }
1507
-
1508
- foreach ( $errors as $error_context => $context_errors ) {
1509
- foreach ( $context_errors as $error ) {
1510
- $this->warning( $error_context, $error );
1511
- }
1512
- }
1513
-
1514
- if ( $context ) {
1515
- unset( $this->errors[ $context ] );
1516
- } else {
1517
- $this->errors = array();
1518
- }
1519
-
1520
- }
1521
-
1522
- /**
1523
- * Get the warnings
1524
- *
1525
- */
1526
- public function get_warnings( $context = null ) {
1527
-
1528
- if ( ! empty( $context ) ) {
1529
- return isset( $this->warnings[ $context ] ) ? $this->warnings[ $context ] : array();
1530
- }
1531
-
1532
- return $this->warnings;
1533
-
1534
- }
1535
-
1536
- /**
1537
- * Add an warning to the warnings stack
1538
- *
1539
- * @param string $context
1540
- * @param mixed $warning
1541
- */
1542
- private function warning( $context, $warning ) {
1543
-
1544
- if ( empty( $context ) || empty( $warning ) ) {
1545
- return;
1546
- }
1547
-
1548
- $this->do_action( 'hmbkp_warning' );
1549
-
1550
- $this->warnings[ $context ][ $_key = md5( implode( ':', (array) $warning ) ) ] = $warning;
1551
-
1552
- }
1553
-
1554
- /**
1555
- * Custom error handler for catching php errors
1556
- *
1557
- * @param $type
1558
- *
1559
- * @return bool
1560
- */
1561
- public function error_handler( $type ) {
1562
-
1563
- // Skip strict & deprecated warnings
1564
- if ( ( defined( 'E_DEPRECATED' ) && $type === E_DEPRECATED ) || ( defined( 'E_STRICT' ) && $type === E_STRICT ) || error_reporting() === 0 ) {
1565
- return false;
1566
- }
1567
-
1568
- $args = func_get_args();
1569
-
1570
- array_shift( $args );
1571
-
1572
- $this->warning( 'php', implode( ', ', array_splice( $args, 0, 3 ) ) );
1573
-
1574
- return false;
1575
-
1576
- }
1577
-
1578
- /**
1579
- * Determine if user can connect via the CLI
1580
- *
1581
- * @return \WP_Error
1582
- */
1583
- public function user_can_connect() {
1584
-
1585
- // mysql --host=localhost --user=myname --password=mypass mydb
1586
-
1587
- // Guess port or socket connection type
1588
- $port_or_socket = strstr( DB_HOST, ':' );
1589
-
1590
- $host = DB_HOST;
1591
-
1592
- if ( ! empty( $port_or_socket ) ) {
1593
-
1594
- $host = substr( DB_HOST, 0, strpos( DB_HOST, ':' ) );
1595
-
1596
- $port_or_socket = substr( $port_or_socket, 1 );
1597
-
1598
- if ( 0 !== strpos( $port_or_socket, '/' ) ) {
1599
-
1600
- $port = intval( $port_or_socket );
1601
-
1602
- $maybe_socket = strstr( $port_or_socket, ':' );
1603
-
1604
- if ( ! empty( $maybe_socket ) ) {
1605
-
1606
- $socket = substr( $maybe_socket, 1 );
1607
-
1608
- }
1609
-
1610
- } else {
1611
-
1612
- $socket = $port_or_socket;
1613
-
1614
- }
1615
- }
1616
-
1617
- // Path to the mysqldump executable
1618
- $cmd = 'mysql ';
1619
-
1620
- // Username
1621
- $cmd .= ' -u ' . escapeshellarg( DB_USER );
1622
-
1623
- // Don't pass the password if it's blank
1624
- if ( DB_PASSWORD ) {
1625
- $cmd .= ' -p' . escapeshellarg( DB_PASSWORD );
1626
- }
1627
-
1628
- // Set the host
1629
- $cmd .= ' -h ' . escapeshellarg( $host );
1630
-
1631
- // Set the port if it was set
1632
- if ( ! empty( $port ) && is_numeric( $port ) ) {
1633
- $cmd .= ' -P ' . $port;
1634
- }
1635
-
1636
- // Set the socket path
1637
- if ( ! empty( $socket ) && ! is_numeric( $socket ) ) {
1638
- $cmd .= ' --protocol=socket -S ' . $socket;
1639
- }
1640
-
1641
- // The database we're dumping
1642
- $cmd .= ' ' . escapeshellarg( DB_NAME );
1643
-
1644
- // Quit immediately
1645
- $cmd .= ' --execute="quit"';
1646
-
1647
- // Pipe STDERR to STDOUT
1648
- $cmd .= ' 2>&1';
1649
-
1650
- // Store any returned data in an error
1651
- $stderr = shell_exec( $cmd );
1652
-
1653
- // Skip the new password warning that is output in mysql > 5.6 (@see http://bugs.mysql.com/bug.php?id=66546)
1654
- if ( 'Warning: Using a password on the command line interface can be insecure.' === trim( $stderr ) ) {
1655
- $stderr = '';
1656
- }
1657
-
1658
- if ( $stderr ) {
1659
- return new \WP_Error( 'mysql-cli-connect-error', __( 'Could not connect to mysql', 'backupwordpress' ) );
1660
- }
1661
- }
1662
-
1663
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/class-backupwordpress-wp-cli-command.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  /**
4
  * Implement backup command
5
  *
@@ -7,7 +9,7 @@
7
  * @package wp-cli
8
  * @subpackage commands/third-party
9
  */
10
- class BackUpWordPress_WP_CLI_Command extends WP_CLI_Command {
11
 
12
  /**
13
  * Perform a Backup.
@@ -26,12 +28,6 @@ class BackUpWordPress_WP_CLI_Command extends WP_CLI_Command {
26
  * [--root]
27
  * : dir that should be backed up, defaults to site root.
28
  *
29
- * [--zip_command_path]
30
- * : path to your zip binary, standard locations are automatically used
31
- *
32
- * [--mysqldump_command_path]
33
- * : path to your mysqldump binary, standard locations are automatically used
34
- *
35
  * [--archive_filename]
36
  * : filename for the resulting zip file
37
  *
@@ -47,39 +43,41 @@ class BackUpWordPress_WP_CLI_Command extends WP_CLI_Command {
47
  public function backup( $args, $assoc_args ) {
48
 
49
  add_action( 'hmbkp_mysqldump_started', function () {
50
- WP_CLI::line( __( 'Backup: Dumping database...', 'backupwordpress' ) );
51
  } );
52
 
53
  add_action( 'hmbkp_archive_started', function () {
54
- WP_CLI::line( __( 'Backup: Zipping everything up...', 'backupwordpress' ) );
55
  } );
56
 
57
- $hm_backup = new HM\BackUpWordPress\Backup();
58
-
59
  if ( ! empty( $assoc_args['destination'] ) ) {
60
- HM\BackUpWordPress\Path::get_instance()->set_path( $assoc_args['destination'] );
61
  }
62
 
63
- HM\BackUpWordPress\Path::get_instance()->cleanup();
64
 
65
  if ( ! empty( $assoc_args['root'] ) ) {
66
- $hm_backup->set_root( $assoc_args['root'] );
67
  }
68
 
69
- if ( ( ! is_dir( hmbkp_path() ) ) ) {
70
- WP_CLI::error( __( 'Invalid backup path', 'backupwordpress' ) );
71
  return false;
72
  }
73
 
74
- if ( ! is_dir( $hm_backup->get_root() ) || ! is_readable( $hm_backup->get_root() ) ) {
75
- WP_CLI::error( __( 'Invalid root path', 'backupwordpress' ) );
76
  return false;
77
  }
78
 
 
 
79
  if ( isset( $assoc_args['archive_filename'] ) ) {
80
- $hm_backup->set_archive_filename( $assoc_args['archive_filename'] );
81
  }
82
 
 
 
83
  if ( ! empty( $assoc_args['files_only'] ) ) {
84
  $hm_backup->set_type( 'file' );
85
  }
@@ -88,28 +86,20 @@ class BackUpWordPress_WP_CLI_Command extends WP_CLI_Command {
88
  $hm_backup->set_type( 'database' );
89
  }
90
 
91
- if ( isset( $assoc_args['mysqldump_command_path'] ) ) {
92
- $hm_backup->set_mysqldump_command_path( $assoc_args['mysqldump_command_path'] );
93
- }
94
-
95
- if ( isset( $assoc_args['zip_command_path'] ) ) {
96
- $hm_backup->set_zip_command_path( $assoc_args['zip_command_path'] );
97
- }
98
-
99
  if ( ! empty( $assoc_args['excludes'] ) ) {
100
  $hm_backup->set_excludes( $assoc_args['excludes'] );
101
  }
102
 
103
- $hm_backup->backup();
104
 
105
- if ( file_exists( $hm_backup->get_archive_filepath() ) ) {
106
- WP_CLI::success( __( 'Backup Complete: ', 'backupwordpress' ) . $hm_backup->get_archive_filepath() );
107
  } else {
108
- WP_CLI::error( __( 'Backup Failed', 'backupwordpress' ) );
109
  }
110
 
111
  }
112
 
113
  }
114
 
115
- WP_CLI::add_command( 'backupwordpress', 'BackUpWordPress_WP_CLI_Command' );
1
  <?php
2
 
3
+ namespace HM\BackUpWordPress;
4
+
5
  /**
6
  * Implement backup command
7
  *
9
  * @package wp-cli
10
  * @subpackage commands/third-party
11
  */
12
+ class CLI extends \WP_CLI_Command {
13
 
14
  /**
15
  * Perform a Backup.
28
  * [--root]
29
  * : dir that should be backed up, defaults to site root.
30
  *
 
 
 
 
 
 
31
  * [--archive_filename]
32
  * : filename for the resulting zip file
33
  *
43
  public function backup( $args, $assoc_args ) {
44
 
45
  add_action( 'hmbkp_mysqldump_started', function () {
46
+ \WP_CLI::line( __( 'Backup: Dumping database...', 'backupwordpress' ) );
47
  } );
48
 
49
  add_action( 'hmbkp_archive_started', function () {
50
+ \WP_CLI::line( __( 'Backup: Zipping everything up...', 'backupwordpress' ) );
51
  } );
52
 
 
 
53
  if ( ! empty( $assoc_args['destination'] ) ) {
54
+ Path::get_instance()->set_path( $assoc_args['destination'] );
55
  }
56
 
57
+ Path::get_instance()->cleanup();
58
 
59
  if ( ! empty( $assoc_args['root'] ) ) {
60
+ Path::get_instance()->set_root( $assoc_args['root'] );
61
  }
62
 
63
+ if ( ( ! is_dir( Path::get_path() ) ) ) {
64
+ \WP_CLI::error( __( 'Invalid backup path', 'backupwordpress' ) );
65
  return false;
66
  }
67
 
68
+ if ( ! is_dir( Path::get_root() ) || ! is_readable( Path::get_root() ) ) {
69
+ \WP_CLI::error( __( 'Invalid root path', 'backupwordpress' ) );
70
  return false;
71
  }
72
 
73
+ $filename = 'backup.zip';
74
+
75
  if ( isset( $assoc_args['archive_filename'] ) ) {
76
+ $filename = $assoc_args['archive_filename'];
77
  }
78
 
79
+ $hm_backup = new Backup( $filename );
80
+
81
  if ( ! empty( $assoc_args['files_only'] ) ) {
82
  $hm_backup->set_type( 'file' );
83
  }
86
  $hm_backup->set_type( 'database' );
87
  }
88
 
 
 
 
 
 
 
 
 
89
  if ( ! empty( $assoc_args['excludes'] ) ) {
90
  $hm_backup->set_excludes( $assoc_args['excludes'] );
91
  }
92
 
93
+ $hm_backup->run();
94
 
95
+ if ( file_exists( $hm_backup->get_backup_filepath() ) ) {
96
+ \WP_CLI::success( __( 'Backup Complete: ', 'backupwordpress' ) . $hm_backup->get_backup_filepath() );
97
  } else {
98
+ \WP_CLI::error( __( 'Backup Failed', 'backupwordpress' ) );
99
  }
100
 
101
  }
102
 
103
  }
104
 
105
+ \WP_CLI::add_command( 'backupwordpress', 'HM\BackUpWordPress\CLI' );
classes/class-email-service.php CHANGED
@@ -31,7 +31,7 @@ class Email_Service extends Service {
31
  <td>
32
  <input type="text" id="<?php echo esc_attr( $this->get_field_name( 'email' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'email' ) ); ?>" value="<?php echo esc_attr( $this->get_field_value( 'email' ) ); ?>" placeholder="name@youremail.com" />
33
 
34
- <p class="description"><?php printf( __( 'Receive a notification email when a backup completes. If the backup is small enough (&lt; %s), then it will be attached to the email. Separate multiple email addresses with a comma.', 'backupwordpress' ), '<code>' . size_format( hmbkp_get_max_attachment_size() ) . '</code>' ); ?></p>
35
  </td>
36
 
37
  </tr>
@@ -154,7 +154,7 @@ class Email_Service extends Service {
154
 
155
  if ( $action === 'hmbkp_backup_complete' && $this->get_email_address_array() ) {
156
 
157
- $file = $backup->get_archive_filepath();
158
 
159
  $sent = false;
160
 
@@ -189,7 +189,7 @@ class Email_Service extends Service {
189
  $subject = sprintf( __( 'Backup of %s', 'backupwordpress' ), $domain );
190
 
191
  // If it's larger than the max attachment size limit assume it's not going to be able to send the backup
192
- if ( @filesize( $file ) < hmbkp_get_max_attachment_size() ) {
193
 
194
  $message = sprintf( __( 'BackUpWordPress has completed a backup of your site %1$s.', 'backupwordpress' ) . "\n\n" . __( 'The backup file should be attached to this email.', 'backupwordpress' ) . "\n\n" . __( 'You can download the backup file by clicking the link below:', 'backupwordpress' ) . "\n\n" . '%2$s' . "\n\n" . __( "Kind Regards,\nThe Happy BackUpWordPress Backup Emailing Robot", 'backupwordpress' ), home_url(), $download );
195
 
31
  <td>
32
  <input type="text" id="<?php echo esc_attr( $this->get_field_name( 'email' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'email' ) ); ?>" value="<?php echo esc_attr( $this->get_field_value( 'email' ) ); ?>" placeholder="name@youremail.com" />
33
 
34
+ <p class="description"><?php printf( __( 'Receive a notification email when a backup completes. If the backup is small enough (&lt; %s), then it will be attached to the email. Separate multiple email addresses with a comma.', 'backupwordpress' ), '<code>' . size_format( get_max_attachment_size() ) . '</code>' ); ?></p>
35
  </td>
36
 
37
  </tr>
154
 
155
  if ( $action === 'hmbkp_backup_complete' && $this->get_email_address_array() ) {
156
 
157
+ $file = $backup->get_backup_filepath();
158
 
159
  $sent = false;
160
 
189
  $subject = sprintf( __( 'Backup of %s', 'backupwordpress' ), $domain );
190
 
191
  // If it's larger than the max attachment size limit assume it's not going to be able to send the backup
192
+ if ( @filesize( $file ) < get_max_attachment_size() ) {
193
 
194
  $message = sprintf( __( 'BackUpWordPress has completed a backup of your site %1$s.', 'backupwordpress' ) . "\n\n" . __( 'The backup file should be attached to this email.', 'backupwordpress' ) . "\n\n" . __( 'You can download the backup file by clicking the link below:', 'backupwordpress' ) . "\n\n" . '%2$s' . "\n\n" . __( "Kind Regards,\nThe Happy BackUpWordPress Backup Emailing Robot", 'backupwordpress' ), home_url(), $download );
195
 
classes/class-excludes.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ /**
6
+ * Manages exclude rules
7
+ */
8
+ class Excludes {
9
+
10
+ /**
11
+ * The array of exclude rules.
12
+ *
13
+ * @var array
14
+ */
15
+ private $excludes;
16
+
17
+ /**
18
+ * The array of default exclude rules
19
+ *
20
+ * @var array
21
+ */
22
+ private $default_excludes = array(
23
+ '.svn',
24
+ '_svn',
25
+ 'CVS',
26
+ '_darcs',
27
+ '.arch-params',
28
+ '.monotone',
29
+ '.bzr',
30
+ '.git',
31
+ '.hg',
32
+ 'backwpup-*',
33
+ 'updraft',
34
+ 'wp-snapshots',
35
+ 'backupbuddy_backups',
36
+ 'pb_backupbuddy',
37
+ 'backup-db',
38
+ 'Envato-backups',
39
+ 'managewp',
40
+ 'backupwordpress-*-backups'
41
+ );
42
+
43
+ public function __construct( $excludes = array() ) {
44
+ $this->set_excludes( $excludes );
45
+ }
46
+
47
+ /**
48
+ * Set the exclude rules.
49
+ *
50
+ * Excludes rules should be a complete or partial directory or file path.
51
+ * Wildcards can be specified with the * character.
52
+ *
53
+ * @param string|array $excludes The list of exclude rules, accepts either
54
+ * a comma separated list or an array.
55
+ */
56
+ public function set_excludes( $excludes ) {
57
+
58
+ if ( is_string( $excludes ) ) {
59
+ $excludes = explode( ',', $excludes );
60
+ }
61
+
62
+ $this->excludes = $excludes;
63
+
64
+ }
65
+
66
+ /**
67
+ * Get the excludes
68
+ *
69
+ * Returns any user set excludes as well as the default list.
70
+ *
71
+ * @return array The array of exclude rules.
72
+ */
73
+ public function get_excludes() {
74
+ return array_merge( $this->get_default_excludes(), $this->get_user_excludes() );
75
+ }
76
+
77
+ /**
78
+ * Get the excludes prepared for use with regex.
79
+ *
80
+ * The primary difference being that any wildcard (*) rules are converted to the regex
81
+ * fragment `[\s\S]*?`.
82
+ *
83
+ * @return array The array of exclude rules
84
+ */
85
+ public function get_excludes_for_regex() {
86
+
87
+ $excludes = $this->get_excludes();
88
+
89
+ // Prepare the exclude rules
90
+ foreach ( $excludes as &$exclude ) {
91
+
92
+ if ( strpos( $exclude, '*' ) !== false ) {
93
+
94
+ // Escape slashes
95
+ $exclude = str_replace( '/', '\/', $exclude );
96
+
97
+ // Convert WildCards to regex
98
+ $exclude = str_replace( '*', '[\s\S]*?', $exclude );
99
+
100
+ // Wrap in slashes
101
+ $exclude = '/' . $exclude . '/';
102
+
103
+ }
104
+
105
+ }
106
+
107
+ return $excludes;
108
+
109
+ }
110
+
111
+ /**
112
+ * Get the user defined excludes.
113
+ *
114
+ * @return array The array of excludes.
115
+ */
116
+ public function get_user_excludes() {
117
+
118
+ $excludes = $this->excludes;
119
+
120
+ // If path() is inside root(), exclude it
121
+ if ( strpos( Path::get_path(), Path::get_root() ) !== false && Path::get_root() !== Path::get_path() ) {
122
+ array_unshift( $excludes, trailingslashit( Path::get_path() ) );
123
+ }
124
+
125
+ return $this->normalize( $excludes );
126
+ }
127
+
128
+ /**
129
+ * Get the array of default excludes.
130
+ *
131
+ * @return array The array of excludes.
132
+ */
133
+ public function get_default_excludes() {
134
+
135
+ $excludes = array();
136
+
137
+ // Back compat with the old Constant
138
+ if ( defined( 'HMBKP_EXCLUDE' ) && HMBKP_EXCLUDE ) {
139
+ $excludes = explode( ',', implode( ',', (array) HMBKP_EXCLUDE ) );
140
+ }
141
+
142
+ $excludes = array_merge( $this->default_excludes, $excludes );
143
+
144
+ /**
145
+ * Allow the default excludes list to be modified.
146
+ *
147
+ * @param $excludes The array of exclude rules.
148
+ */
149
+ $excludes = apply_filters( 'hmbkp_default_excludes', $excludes );
150
+
151
+ return $this->normalize( $excludes );
152
+
153
+ }
154
+
155
+ /**
156
+ * normalise the exclude rules so they are ready to work with.
157
+ *
158
+ * @param array $excludes The array of exclude rules to normalise.
159
+ *
160
+ * @return array The array of normalised rules.
161
+ */
162
+ public function normalize( $excludes ) {
163
+
164
+ $excludes = array_map( function( $exclude ) {
165
+
166
+ // Convert absolute paths to relative
167
+ $exclude = str_replace( PATH::get_root(), '', wp_normalize_path( $exclude ) );
168
+
169
+ // Trim the slashes
170
+ $exclude = trim( $exclude );
171
+ $exclude = ltrim( $exclude, '/' );
172
+ $exclude = untrailingslashit( $exclude );
173
+
174
+ return $exclude;
175
+
176
+ }, $excludes );
177
+
178
+ // Remove duplicate or empty rules
179
+ $excludes = array_unique( $excludes );
180
+ $excludes = array_filter( $excludes );
181
+
182
+ return $excludes;
183
+
184
+ }
185
+
186
+ }
classes/class-path.php CHANGED
@@ -1,17 +1,12 @@
1
  <?php
2
- /**
3
- * @package BackUpWordPress
4
- * @subpackage BackUpWordPress/classes
5
- */
6
 
7
  namespace HM\BackUpWordPress;
8
 
9
  /**
10
- * The Backup Path class
11
  *
12
  * Handles calculating & protecting the directory that backups will be stored in
13
- *
14
- * @todo Should be a singleton?
15
  */
16
  class Path {
17
 
@@ -20,15 +15,27 @@ class Path {
20
  *
21
  * @var string $this->path
22
  */
23
- protected $path;
 
 
 
 
 
 
 
24
 
25
  /**
26
  * The path to the directory that backup files are stored in
27
  *
28
  * @var string $this->path
29
  */
30
- protected $custom_path;
31
 
 
 
 
 
 
32
  private static $instance;
33
 
34
  /**
@@ -40,16 +47,12 @@ class Path {
40
  /**
41
  * Private clone method to prevent cloning of the instance of the
42
  * *Singleton* instance.
43
- *
44
- * @return void
45
  */
46
  private function __clone() {}
47
 
48
  /**
49
  * Private unserialize method to prevent unserializing of the *Singleton*
50
  * instance.
51
- *
52
- * @return void
53
  */
54
  private function __wakeup() {}
55
 
@@ -70,9 +73,54 @@ class Path {
70
  }
71
 
72
  /**
73
- * Get the path to the directory where backups will be stored
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  */
75
- public function get_path() {
76
 
77
  // Calculate the path if needed
78
  if ( empty( $this->path ) || ! wp_is_writable( $this->path ) ) {
@@ -100,6 +148,34 @@ class Path {
100
 
101
  }
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  public function reset_path() {
104
  $this->set_path( false );
105
  }
@@ -108,7 +184,7 @@ class Path {
108
  * Get the path to the default backup location in wp-content
109
  */
110
  public function get_default_path() {
111
- return trailingslashit( WP_CONTENT_DIR ) . 'backupwordpress-' . substr( HMBKP_SECURE_KEY, 0, 10 ) . '-backups';
112
  }
113
 
114
  /**
@@ -118,7 +194,7 @@ class Path {
118
 
119
  $upload_dir = wp_upload_dir();
120
 
121
- return trailingslashit( $upload_dir['basedir'] ) . 'backupwordpress-' . substr( HMBKP_SECURE_KEY, 0, 10 ) . '-backups';
122
 
123
  }
124
 
@@ -158,6 +234,8 @@ class Path {
158
 
159
  $paths = array_merge( $default, $fallback );
160
 
 
 
161
  return $paths;
162
 
163
  }
@@ -208,18 +286,23 @@ class Path {
208
 
209
  // Loop through possible paths, use the first one that exists/can be created and is writable
210
  foreach ( $paths as $path ) {
211
- if ( wp_mkdir_p( $path ) ) { // Also handles fixing perms / directory already exists
212
  break;
213
  }
214
  }
215
 
216
- if ( isset( $path ) ) {
217
  $this->path = $path;
218
  }
219
 
220
  }
221
 
222
  /**
 
 
 
 
 
223
  * @param string $reset
224
  */
225
  public function protect_path( $reset = 'no' ) {
@@ -229,7 +312,7 @@ class Path {
229
  // Protect against directory browsing by including an index.html file
230
  $index = $this->path . '/index.html';
231
 
232
- if ( ( 'reset' === $reset ) && file_exists( $index ) ) {
233
  @unlink( $index );
234
  }
235
 
@@ -283,7 +366,6 @@ class Path {
283
  * location
284
  *
285
  * @param string $path The path to move the backups from
286
- * @return void
287
  */
288
  public function move_old_backups( $from ) {
289
 
@@ -291,7 +373,7 @@ class Path {
291
  return;
292
  }
293
 
294
- if ( ! wp_is_writable( $this->get_path() ) ) {
295
  return;
296
  }
297
 
@@ -305,11 +387,11 @@ class Path {
305
  if ( 'zip' === pathinfo( $file, PATHINFO_EXTENSION ) ) {
306
 
307
  // Try to move them
308
- if ( ! @rename( trailingslashit( $from ) . $file, trailingslashit( $this->get_path() ) . $file ) ) {
309
 
310
 
311
  // If we can't move them then try to copy them
312
- copy( trailingslashit( $from ) . $file, trailingslashit( $this->get_path() ) . $file );
313
 
314
  }
315
 
@@ -321,8 +403,8 @@ class Path {
321
  }
322
 
323
  // Delete the old directory if it's inside WP_CONTENT_DIR
324
- if ( false !== strpos( $from, WP_CONTENT_DIR ) && $from !== $this->get_path() ) {
325
- hmbkp_rmdirtree( $from );
326
  }
327
 
328
  }
@@ -333,7 +415,7 @@ class Path {
333
  public function cleanup() {
334
 
335
  // Don't cleanup a custom path, who knows what other stuff is there
336
- if ( $this->get_path() === $this->get_custom_path() ) {
337
  return;
338
  }
339
 
1
  <?php
 
 
 
 
2
 
3
  namespace HM\BackUpWordPress;
4
 
5
  /**
6
+ * Manages both the backup path and site root
7
  *
8
  * Handles calculating & protecting the directory that backups will be stored in
9
+ * as well as the directory that is being backed up
 
10
  */
11
  class Path {
12
 
15
  *
16
  * @var string $this->path
17
  */
18
+ private $path;
19
+
20
+ /**
21
+ * The path to the directory that will be backed up
22
+ *
23
+ * @var string $this->root
24
+ */
25
+ private $root;
26
 
27
  /**
28
  * The path to the directory that backup files are stored in
29
  *
30
  * @var string $this->path
31
  */
32
+ private $custom_path;
33
 
34
+ /**
35
+ * Contains the instantiated Path instance
36
+ *
37
+ * @var Path $this->instance
38
+ */
39
  private static $instance;
40
 
41
  /**
47
  /**
48
  * Private clone method to prevent cloning of the instance of the
49
  * *Singleton* instance.
 
 
50
  */
51
  private function __clone() {}
52
 
53
  /**
54
  * Private unserialize method to prevent unserializing of the *Singleton*
55
  * instance.
 
 
56
  */
57
  private function __wakeup() {}
58
 
73
  }
74
 
75
  /**
76
+ * Convenience method for quickly grabbing the path
77
+ */
78
+ public static function get_path() {
79
+ return self::get_instance()->get_calculated_path();
80
+ }
81
+
82
+ /**
83
+ * Convenience method for quickly grabbing the root
84
+ */
85
+ public static function get_root() {
86
+ return self::get_instance()->get_calculated_root();
87
+ }
88
+
89
+ /**
90
+ * Calculate the path to the site "home" directory.
91
+ *
92
+ * The home directory is the path equivalent to the home_url. That is,
93
+ * the path to the true root of the website. In situations where WordPress is
94
+ * installed in a subdirectory the home path is different to ABSPATH
95
+ *
96
+ * @param string $site_path The site_path to use when calculating the home path, defaults to ABSPATH
97
+ */
98
+ public static function get_home_path( $site_path = ABSPATH ) {
99
+
100
+ if ( defined( 'HMBKP_ROOT' ) && HMBKP_ROOT ) {
101
+ return wp_normalize_path( HMBKP_ROOT );
102
+ }
103
+
104
+ $home_path = $site_path;
105
+
106
+ // Handle wordpress installed in a subdirectory
107
+ if ( file_exists( dirname( $site_path ) . '/wp-config.php' ) && ! file_exists( $site_path . '/wp-config.php' ) && file_exists( dirname( $site_path ) . '/index.php' ) ) {
108
+ $home_path = dirname( $site_path );
109
+ }
110
+
111
+ // Handle wp-config.php being above site_path
112
+ if ( file_exists( dirname( $site_path ) . '/wp-config.php' ) && ! file_exists( $site_path . '/wp-config.php' ) && ! file_exists( dirname( $site_path ) . '/index.php' ) ) {
113
+ $home_path = $site_path;
114
+ }
115
+
116
+ return wp_normalize_path( untrailingslashit( $home_path ) );
117
+
118
+ }
119
+
120
+ /**
121
+ * get the calculated path to the directory where backups will be stored
122
  */
123
+ private function get_calculated_path() {
124
 
125
  // Calculate the path if needed
126
  if ( empty( $this->path ) || ! wp_is_writable( $this->path ) ) {
148
 
149
  }
150
 
151
+ /**
152
+ * get the calculated path to the directory that will be backed up
153
+ */
154
+ private function get_calculated_root() {
155
+
156
+ $root = self::get_home_path();
157
+
158
+ if ( defined( 'HMBKP_ROOT' ) && HMBKP_ROOT ) {
159
+ $root = HMBKP_ROOT;
160
+ }
161
+
162
+ if ( $this->root ) {
163
+ $root = $this->root;
164
+ }
165
+
166
+ return wp_normalize_path( $root );
167
+
168
+ }
169
+
170
+ /**
171
+ * Set the root path directly, overriding the default
172
+ *
173
+ * @param $root
174
+ */
175
+ public function set_root( $root ) {
176
+ $this->root = $root;
177
+ }
178
+
179
  public function reset_path() {
180
  $this->set_path( false );
181
  }
184
  * Get the path to the default backup location in wp-content
185
  */
186
  public function get_default_path() {
187
+ return trailingslashit( wp_normalize_path( WP_CONTENT_DIR ) ) . 'backupwordpress-' . substr( HMBKP_SECURE_KEY, 0, 10 ) . '-backups';
188
  }
189
 
190
  /**
194
 
195
  $upload_dir = wp_upload_dir();
196
 
197
+ return trailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . 'backupwordpress-' . substr( HMBKP_SECURE_KEY, 0, 10 ) . '-backups';
198
 
199
  }
200
 
234
 
235
  $paths = array_merge( $default, $fallback );
236
 
237
+ $paths = array_map( 'wp_normalize_path', $paths );
238
+
239
  return $paths;
240
 
241
  }
286
 
287
  // Loop through possible paths, use the first one that exists/can be created and is writable
288
  foreach ( $paths as $path ) {
289
+ if ( wp_mkdir_p( $path ) && wp_is_writable( $path ) ) { // Also handles fixing perms / directory already exists
290
  break;
291
  }
292
  }
293
 
294
+ if ( file_exists( $path ) && wp_is_writable( $path ) ) {
295
  $this->path = $path;
296
  }
297
 
298
  }
299
 
300
  /**
301
+ * Protect the directory that backups are stored in
302
+ *
303
+ * - Adds an index.html file in an attempt to disable directory browsing
304
+ * - Adds a .httaccess file to deny direct access if on Apache
305
+ *
306
  * @param string $reset
307
  */
308
  public function protect_path( $reset = 'no' ) {
312
  // Protect against directory browsing by including an index.html file
313
  $index = $this->path . '/index.html';
314
 
315
+ if ( 'reset' === $reset && file_exists( $index ) ) {
316
  @unlink( $index );
317
  }
318
 
366
  * location
367
  *
368
  * @param string $path The path to move the backups from
 
369
  */
370
  public function move_old_backups( $from ) {
371
 
373
  return;
374
  }
375
 
376
+ if ( ! wp_is_writable( Path::get_path() ) ) {
377
  return;
378
  }
379
 
387
  if ( 'zip' === pathinfo( $file, PATHINFO_EXTENSION ) ) {
388
 
389
  // Try to move them
390
+ if ( ! @rename( trailingslashit( $from ) . $file, trailingslashit( Path::get_path() ) . $file ) ) {
391
 
392
 
393
  // If we can't move them then try to copy them
394
+ copy( trailingslashit( $from ) . $file, trailingslashit( Path::get_path() ) . $file );
395
 
396
  }
397
 
403
  }
404
 
405
  // Delete the old directory if it's inside WP_CONTENT_DIR
406
+ if ( false !== strpos( $from, WP_CONTENT_DIR ) && $from !== Path::get_path() ) {
407
+ rmdirtree( $from );
408
  }
409
 
410
  }
415
  public function cleanup() {
416
 
417
  // Don't cleanup a custom path, who knows what other stuff is there
418
+ if ( Path::get_path() === $this->get_custom_path() ) {
419
  return;
420
  }
421
 
classes/class-plugin.php CHANGED
@@ -6,7 +6,7 @@ namespace HM\BackUpWordPress;
6
  * Class Plugin
7
  */
8
  final class Plugin {
9
- const PLUGIN_VERSION = '3.3.4';
10
 
11
  /**
12
  * @var Plugin The singleton instance.
@@ -131,12 +131,26 @@ final class Plugin {
131
  require_once( HMBKP_PLUGIN_PATH . 'classes/class-requirement.php' );
132
 
133
  require_once( HMBKP_PLUGIN_PATH . 'classes/class-path.php' );
 
 
134
 
135
- // Load the core backup class
136
- require_once( HMBKP_PLUGIN_PATH . 'classes/class-backup.php' );
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
  // Load the backup scheduling classes
139
- require_once( HMBKP_PLUGIN_PATH . 'classes/class-schedule.php' );
140
  require_once( HMBKP_PLUGIN_PATH . 'classes/class-schedules.php' );
141
 
142
  // Load the core functions
@@ -253,7 +267,7 @@ final class Plugin {
253
 
254
  // Fire the update action
255
  if ( self::PLUGIN_VERSION != get_option( 'hmbkp_plugin_version' ) ) {
256
- hmbkp_update();
257
  }
258
 
259
  }
@@ -319,7 +333,7 @@ final class Plugin {
319
  */
320
  public function schedule_hook_run( $schedule_id ) {
321
 
322
- if ( ! hmbkp_possible() ) {
323
  return;
324
  }
325
 
@@ -399,4 +413,5 @@ final class Plugin {
399
  if ( is_multisite() && ! is_main_site() ) {
400
  return;
401
  }
 
402
  Plugin::get_instance();
6
  * Class Plugin
7
  */
8
  final class Plugin {
9
+ const PLUGIN_VERSION = '3.4.0';
10
 
11
  /**
12
  * @var Plugin The singleton instance.
131
  require_once( HMBKP_PLUGIN_PATH . 'classes/class-requirement.php' );
132
 
133
  require_once( HMBKP_PLUGIN_PATH . 'classes/class-path.php' );
134
+ require_once( HMBKP_PLUGIN_PATH . 'classes/class-excludes.php' );
135
+ require_once( HMBKP_PLUGIN_PATH . 'classes/class-site-size.php' );
136
 
137
+ require_once( HMBKP_PLUGIN_PATH . 'classes/backup/class-backup-utilities.php' );
138
+ require_once( HMBKP_PLUGIN_PATH . 'classes/backup/class-backup-status.php' );
139
+
140
+ require_once( HMBKP_PLUGIN_PATH . 'classes/backup/class-backup-engine.php' );
141
+
142
+ require_once( HMBKP_PLUGIN_PATH . 'classes/backup/class-backup-engine-database.php' );
143
+ require_once( HMBKP_PLUGIN_PATH . 'classes/backup/class-backup-engine-database-mysqldump.php' );
144
+ require_once( HMBKP_PLUGIN_PATH . 'classes/backup/class-backup-engine-database-imysqldump.php' );
145
+
146
+ require_once( HMBKP_PLUGIN_PATH . 'classes/backup/class-backup-engine-file.php' );
147
+ require_once( HMBKP_PLUGIN_PATH . 'classes/backup/class-backup-engine-file-zip.php' );
148
+ require_once( HMBKP_PLUGIN_PATH . 'classes/backup/class-backup-engine-file-zip-archive.php' );
149
+
150
+ require_once( HMBKP_PLUGIN_PATH . 'classes/backup/class-backup.php' );
151
 
152
  // Load the backup scheduling classes
153
+ require_once( HMBKP_PLUGIN_PATH . 'classes/class-scheduled-backup.php' );
154
  require_once( HMBKP_PLUGIN_PATH . 'classes/class-schedules.php' );
155
 
156
  // Load the core functions
267
 
268
  // Fire the update action
269
  if ( self::PLUGIN_VERSION != get_option( 'hmbkp_plugin_version' ) ) {
270
+ update();
271
  }
272
 
273
  }
333
  */
334
  public function schedule_hook_run( $schedule_id ) {
335
 
336
+ if ( ! is_backup_possible() ) {
337
  return;
338
  }
339
 
413
  if ( is_multisite() && ! is_main_site() ) {
414
  return;
415
  }
416
+
417
  Plugin::get_instance();
classes/class-requirement.php CHANGED
@@ -126,9 +126,9 @@ class Requirement_Zip_Command_Path extends Requirement {
126
  */
127
  protected function test() {
128
 
129
- $hm_backup = new Backup;
130
 
131
- return $hm_backup->get_zip_command_path();
132
 
133
  }
134
 
@@ -152,9 +152,9 @@ class Requirement_Mysqldump_Command_Path extends Requirement {
152
  */
153
  protected function test() {
154
 
155
- $hm_backup = new Backup;
156
 
157
- return $hm_backup->get_mysqldump_command_path();
158
 
159
  }
160
 
@@ -176,7 +176,7 @@ class Requirement_PHP_User extends Requirement {
176
  */
177
  protected function test() {
178
 
179
- if ( ! Backup::is_shell_exec_available() ) {
180
  return '';
181
  }
182
 
@@ -202,7 +202,7 @@ class Requirement_PHP_Group extends Requirement {
202
  */
203
  protected function test() {
204
 
205
- if ( ! Backup::is_shell_exec_available() ) {
206
  return '';
207
  }
208
 
@@ -308,7 +308,7 @@ class Requirement_Safe_Mode extends Requirement {
308
  * @return bool
309
  */
310
  protected function test() {
311
- return Backup::is_safe_mode_active();
312
  }
313
 
314
  }
@@ -328,7 +328,7 @@ class Requirement_Shell_Exec extends Requirement {
328
  * @return bool
329
  */
330
  protected function test() {
331
- return Backup::is_shell_exec_available();
332
  }
333
 
334
  }
@@ -368,7 +368,7 @@ class Requirement_Backup_Path extends Requirement {
368
  * @return string
369
  */
370
  protected function test() {
371
- return Path::get_instance()->get_path();
372
  }
373
 
374
  }
@@ -388,7 +388,7 @@ class Requirement_Backup_Path_Permissions extends Requirement {
388
  * @return string
389
  */
390
  protected function test() {
391
- return substr( sprintf( '%o', fileperms( hmbkp_path() ) ), - 4 );
392
  }
393
 
394
  }
@@ -462,17 +462,13 @@ class Requirement_Backup_Root_Path extends Requirement {
462
  /**
463
  * @var string
464
  */
465
- var $name = 'Backup Root Path';
466
 
467
  /**
468
  * @return string
469
  */
470
  protected function test() {
471
-
472
- $hm_backup = new Backup();
473
-
474
- return $hm_backup->get_root();
475
-
476
  }
477
 
478
  }
@@ -498,9 +494,13 @@ class Requirement_Calculated_Size extends Requirement {
498
  $schedules = Schedules::get_instance();
499
 
500
  foreach ( $schedules->get_schedules() as $schedule ) {
501
- if ( $schedule->is_site_size_cached() ) {
502
- $backup_sizes[ $schedule->get_type() ] = $schedule->get_formatted_site_size();
 
 
 
503
  }
 
504
  }
505
 
506
  return $backup_sizes;
126
  */
127
  protected function test() {
128
 
129
+ $backup = new Zip_File_Backup_Engine;
130
 
131
+ return $backup->get_zip_executable_path();
132
 
133
  }
134
 
152
  */
153
  protected function test() {
154
 
155
+ $backup = new Mysqldump_Database_Backup_Engine;
156
 
157
+ return $backup->get_mysqldump_executable_path();
158
 
159
  }
160
 
176
  */
177
  protected function test() {
178
 
179
+ if ( ! Backup_Utilities::is_exec_available() ) {
180
  return '';
181
  }
182
 
202
  */
203
  protected function test() {
204
 
205
+ if ( ! Backup_Utilities::is_exec_available() ) {
206
  return '';
207
  }
208
 
308
  * @return bool
309
  */
310
  protected function test() {
311
+ return Backup_Utilities::is_safe_mode_on();
312
  }
313
 
314
  }
328
  * @return bool
329
  */
330
  protected function test() {
331
+ return Backup_Utilities::is_exec_available();
332
  }
333
 
334
  }
368
  * @return string
369
  */
370
  protected function test() {
371
+ return Path::get_path();
372
  }
373
 
374
  }
388
  * @return string
389
  */
390
  protected function test() {
391
+ return substr( sprintf( '%o', fileperms( Path::get_path() ) ), - 4 );
392
  }
393
 
394
  }
462
  /**
463
  * @var string
464
  */
465
+ var $name = 'Site Root Path';
466
 
467
  /**
468
  * @return string
469
  */
470
  protected function test() {
471
+ return Path::get_root();
 
 
 
 
472
  }
473
 
474
  }
494
  $schedules = Schedules::get_instance();
495
 
496
  foreach ( $schedules->get_schedules() as $schedule ) {
497
+
498
+ $site_size = new Site_Size( $schedule->get_type(), $schedule->get_excludes() );
499
+
500
+ if ( $site_size->is_site_size_cached() ) {
501
+ $backup_sizes[ $schedule->get_type() ] = $site_size->get_formatted_site_size();
502
  }
503
+
504
  }
505
 
506
  return $backup_sizes;
classes/class-schedule.php DELETED
@@ -1,1173 +0,0 @@
1
- <?php
2
-
3
- namespace HM\BackUpWordPress;
4
-
5
- use Symfony\Component\Finder\Finder;
6
-
7
- /**
8
- * The Backup Scheduler
9
- *
10
- * Handles everything related to managing and running a backup schedule
11
- *
12
- * @uses Backup
13
- * @uses
14
- */
15
- class Scheduled_Backup {
16
-
17
- /**
18
- * The unique schedule id
19
- *
20
- * @var string
21
- * @access private
22
- */
23
- private $id = '';
24
-
25
- /**
26
- * The slugified version of the schedule name
27
- *
28
- * @var string
29
- * @access private
30
- */
31
- private $slug = '';
32
-
33
- /**
34
- * The raw schedule options from the database
35
- *
36
- * @var array
37
- * @access private
38
- */
39
- private $options = array();
40
-
41
- /**
42
- * The Backup instance
43
- *
44
- * @var Backup
45
- */
46
- public $backup;
47
-
48
- /**
49
- * Setup the schedule object
50
- * Loads the options from the database and populates properties
51
- *
52
- * @param string $id
53
- *
54
- * @throws Exception
55
- */
56
-
57
- public function __construct( $id ) {
58
-
59
- // Verify the schedule id
60
- if ( ! is_string( $id ) || ! trim( $id ) ) {
61
- throw new \Exception( 'Argument 1 for ' . __METHOD__ . ' must be a non-empty string' );
62
- }
63
-
64
- // Store id for later
65
- $this->id = $id;
66
-
67
- // Load the options
68
- $this->options = array_filter( (array) get_option( 'hmbkp_schedule_' . $this->get_id() ) );
69
-
70
- // Setup The Backup class
71
- $this->backup = new Backup();
72
-
73
- // Set the archive filename to site name + schedule slug + date
74
- $this->backup->set_archive_filename( implode( '-', array(
75
- sanitize_title( str_ireplace( array(
76
- 'http://',
77
- 'https://',
78
- 'www'
79
- ), '', home_url() ) ),
80
- $this->get_id(),
81
- $this->get_type(),
82
- current_time( 'Y-m-d-H-i-s' )
83
- ) ) . '.zip' );
84
-
85
- $this->backup->set_database_dump_filename( implode( '-', array(
86
- 'database',
87
- sanitize_title( str_ireplace( array( 'http://', 'https://', 'www' ), '', home_url() ) ),
88
- $this->get_id()
89
- ) ) . '.sql' );
90
-
91
- $this->backup->set_type( $this->get_type() );
92
- $this->backup->set_excludes( $this->backup->default_excludes(), true );
93
- $this->backup->set_excludes( $this->get_excludes() );
94
- $this->backup->set_action_callback( array( $this, 'do_action' ) );
95
-
96
- if ( defined( 'HMBKP_SCHEDULE_START_TIME' ) && strtotime( 'HMBKP_SCHEDULE_START_TIME' ) ) {
97
- $this->set_schedule_start_time( strtotime( 'HMBKP_SCHEDULE_START_TIME' ) );
98
- }
99
-
100
- // Setup the schedule if it isn't set
101
- if ( ( ! $this->is_cron_scheduled() && $this->get_reoccurrence() !== 'manually' ) ) {
102
- $this->schedule();
103
- }
104
-
105
- }
106
-
107
- /**
108
- * Simple class wrapper for Path::get_path()
109
- *
110
- * @return string
111
- */
112
- private function get_path() {
113
- return Path::get_instance()->get_path();
114
- }
115
-
116
- /**
117
- * Get the id for this schedule
118
- *
119
- */
120
- public function get_id() {
121
- return esc_attr( $this->id );
122
- }
123
-
124
- /**
125
- * Get a slugified version of name
126
- *
127
- */
128
- public function get_slug() {
129
-
130
- // We cache slug in $this to save expensive calls to sanitize_title
131
- if ( ! empty( $this->slug ) ) {
132
- return $this->slug;
133
- }
134
-
135
- return $this->slug = sanitize_title( $this->get_name() );
136
-
137
- }
138
-
139
- /**
140
- * Returns the given option value or WP_Error if it doesn't exist
141
- *
142
- * @param $option_name
143
- * @return \WP_Error
144
- */
145
- public function get_schedule_option( $option_name ) {
146
-
147
- if ( isset( $this->options[ $option_name ] ) ) {
148
- return $this->options[ $option_name ];
149
- } else {
150
- return new \WP_Error( 'invalid_option_name', __( 'Invalid Option Name', 'backupwordpress' ) );
151
- }
152
- }
153
-
154
- /**
155
- * Get the name of this backup schedule
156
- *
157
- * @return string
158
- */
159
- public function get_name() {
160
-
161
- return ucwords( $this->get_type() ) . ' ' . $this->get_reoccurrence();
162
-
163
- }
164
-
165
- /**
166
- * Get the type of backup
167
- *
168
- * @return string
169
- */
170
- public function get_type() {
171
-
172
- if ( empty( $this->options['type'] ) ) {
173
- $this->set_type( 'complete' );
174
- }
175
-
176
- return $this->options['type'];
177
-
178
- }
179
-
180
- /**
181
- * Set the type of backup
182
- *
183
- * @param string $type
184
- */
185
- public function set_type( $type ) {
186
-
187
- if ( isset( $this->options['type'] ) && $this->options['type'] === $type ) {
188
- return;
189
- }
190
-
191
- $this->backup->set_type( $type );
192
-
193
- $this->options['type'] = $type;
194
-
195
- }
196
-
197
- /**
198
- * Get the exclude rules
199
- *
200
- * @return array
201
- */
202
- public function get_excludes() {
203
-
204
- if ( ! empty( $this->options['excludes'] ) ) {
205
- $this->backup->set_excludes( $this->options['excludes'] );
206
- }
207
-
208
- return $this->backup->get_excludes();
209
-
210
- }
211
-
212
- /**
213
- * Set the exclude rules
214
- *
215
- * @param mixed $excludes A comma separated list or array of exclude rules
216
- * @param bool $append Whether to replace or append to existing rules
217
- *
218
- * @return string
219
- */
220
- public function set_excludes( $excludes, $append = false ) {
221
-
222
- // Use the validation from Backup::set_excludes
223
- $this->backup->set_excludes( $excludes, $append );
224
-
225
- // If these are valid excludes and they are different save them
226
- if ( $this->backup->get_excludes() && ( empty( $this->options['excludes'] ) || $this->options['excludes'] !== $this->backup->get_excludes() ) ) {
227
-
228
- $this->options['excludes'] = $append && ! empty( $this->options['excludes'] ) ? array_merge( (array) $this->options['excludes'], $this->backup->get_excludes() ) : $this->backup->get_excludes();;
229
-
230
- $this->backup->set_excludes( $this->options['excludes'] );
231
-
232
- }
233
-
234
- }
235
-
236
- /**
237
- * Get the maximum number of backups to keep
238
- *
239
- * @return int
240
- */
241
- public function get_max_backups() {
242
-
243
- if ( empty( $this->options['max_backups'] ) ) {
244
- $this->set_max_backups( 3 );
245
- }
246
-
247
- return (int) esc_attr( $this->options['max_backups'] );
248
-
249
- }
250
-
251
- /**
252
- * Set the maximum number of backups to keep
253
- *
254
- * @param int $max
255
- *
256
- * @return WP_Error|boolean
257
- */
258
- public function set_max_backups( $max ) {
259
-
260
- if ( empty( $max ) || ! is_int( $max ) ) {
261
- return new \WP_Error( 'hmbkp_invalid_type_error', sprintf( __( 'Argument 1 for %s must be a valid integer', 'backupwordpress' ), __METHOD__ ) );
262
- }
263
-
264
- $this->options['max_backups'] = $max;
265
-
266
- return true;
267
- }
268
-
269
- /**
270
- * Get the array of services options for this schedule
271
- *
272
- * @param $service
273
- * @param null $option
274
- *
275
- * @return array
276
- */
277
- public function get_service_options( $service, $option = null ) {
278
-
279
- if ( ! is_null( $option ) ) {
280
-
281
- if ( isset( $this->options[ $service ][ $option ] ) ) {
282
- return $this->options[ $service ][ $option ];
283
- }
284
-
285
- return array();
286
-
287
- }
288
-
289
- if ( isset( $this->options[ $service ] ) ) {
290
- return $this->options[ $service ];
291
- }
292
-
293
- return array();
294
-
295
- }
296
-
297
- /**
298
- * Set the service options for this schedule
299
- *
300
- * @param $service
301
- * @param array $options
302
- */
303
- public function set_service_options( $service, Array $options ) {
304
-
305
- $this->options[ $service ] = $options;
306
- }
307
-
308
- /**
309
- * Calculate the size total size of the database + files
310
- *
311
- * Doesn't account for compression
312
- *
313
- * @return string
314
- */
315
- public function get_site_size( $skip_excluded_files = false ) {
316
-
317
- $size = 0;
318
-
319
- // Include database size except for file only schedule.
320
- if ( 'file' !== $this->get_type() ) {
321
-
322
- global $wpdb;
323
-
324
- $tables = $wpdb->get_results( 'SHOW TABLE STATUS FROM `' . DB_NAME . '`', ARRAY_A );
325
-
326
- foreach ( $tables as $table ) {
327
- $size += (float) $table['Data_length'];
328
- }
329
- }
330
-
331
- // Include total size of dirs/files except for database only schedule.
332
- if ( 'database' !== $this->get_type() ) {
333
-
334
- $root = new \SplFileInfo( $this->backup->get_root() );
335
-
336
- $size += $this->filesize( $root, $skip_excluded_files );
337
-
338
- }
339
-
340
- return $size;
341
-
342
- }
343
-
344
- /**
345
- * Convenience function to format the file size
346
- *
347
- * @param bool $cached
348
- *
349
- * @return bool|string
350
- */
351
- public function get_formatted_site_size( $skip_excluded_files = false ) {
352
- return size_format( $this->get_site_size( $skip_excluded_files ) );
353
- }
354
-
355
- /**
356
- * Whether the total filesize is being calculated
357
- *
358
- * @return int The total of the file or directory
359
- */
360
- public function is_site_size_being_calculated() {
361
- return false !== get_transient( 'hmbkp_directory_filesizes_running' );
362
- }
363
-
364
- /**
365
- * Whether the total filesize is being calculated
366
- *
367
- * @return bool The total of the file or directory
368
- */
369
- public function is_site_size_cached() {
370
- return false !== get_transient( 'hmbkp_directory_filesizes' );
371
- }
372
-
373
- /**
374
- * Return the single depth list of files and subdirectories in $directory ordered by total filesize
375
- *
376
- * Will schedule background threads to recursively calculate the filesize of subdirectories.
377
- * The total filesize of each directory and subdirectory is cached in a transient for 1 week.
378
- *
379
- * @param string $directory The directory to scan
380
- *
381
- * @return array returns an array of files ordered by filesize
382
- */
383
- public function list_directory_by_total_filesize( $directory ) {
384
-
385
- $files = $files_with_no_size = $empty_files = $files_with_size = $unreadable_files = array();
386
-
387
- if ( ! is_dir( $directory ) ) {
388
- return $files;
389
- }
390
-
391
- $found = array();
392
-
393
- if ( ! empty( $this->files ) ) {
394
- return $this->files;
395
- }
396
-
397
- $default_excludes = $this->backup->default_excludes();
398
-
399
- $finder = new Finder();
400
- $finder->ignoreDotFiles( false );
401
- $finder->ignoreUnreadableDirs();
402
- $finder->followLinks();
403
- $finder->depth( '== 0' );
404
-
405
- foreach ( $default_excludes as $exclude ) {
406
- $finder->notPath( $exclude );
407
- }
408
-
409
- foreach ( $finder->in( $directory ) as $entry ) {
410
- $files[] = $entry;
411
- // Get the total filesize for each file and directory
412
- $filesize = $this->filesize( $entry );
413
-
414
- if ( $filesize ) {
415
-
416
- // 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
417
- while ( array_key_exists( $filesize, $files_with_size ) ) {
418
- $filesize ++;
419
- }
420
-
421
- $files_with_size[ $filesize ] = $entry;
422
-
423
- } elseif ( 0 === $filesize ) {
424
-
425
- $empty_files[] = $entry;
426
-
427
- } else {
428
-
429
- $files_with_no_size[] = $entry;
430
-
431
- }
432
- }
433
-
434
- // Add 0 byte files / directories to the bottom
435
- $files = $files_with_size + array_merge( $empty_files, $unreadable_files );
436
-
437
- // Add directories that are still calculating to the top
438
- if ( $files_with_no_size ) {
439
-
440
- // 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
441
- foreach ( $files_with_no_size as $entry ) {
442
- array_unshift( $files, $entry );
443
- }
444
- }
445
-
446
- return $files;
447
-
448
- }
449
-
450
- /**
451
- * Recursively scans a directory to calculate the total filesize
452
- *
453
- * Locks should be set by the caller with `set_transient( 'hmbkp_directory_filesizes_running', true, HOUR_IN_SECONDS );`
454
- *
455
- * @return array $directory_sizes An array of directory paths => filesize sum of all files in directory
456
- */
457
- public function recursive_filesize_scanner() {
458
-
459
- // Use the cached array directory sizes if available
460
- $directory_sizes = get_transient( 'hmbkp_directory_filesizes' );
461
-
462
- // If we do have it in cache then let's use it and also clear the lock
463
- if ( is_array( $directory_sizes ) ) {
464
-
465
- delete_transient( 'hmbkp_directory_filesizes_running' );
466
-
467
- return $directory_sizes;
468
-
469
- }
470
-
471
- // If we don't have it cached then we'll need to re-calculate
472
- $files = $this->backup->get_files();
473
-
474
- foreach ( $files as $file ) {
475
-
476
- if ( $file->isReadable() ) {
477
- $directory_sizes[ wp_normalize_path( $file->getRealpath() ) ] = $file->getSize();
478
- } else {
479
- $directory_sizes[ wp_normalize_path( $file->getRealpath() ) ] = 0;
480
- }
481
-
482
- }
483
-
484
- set_transient( 'hmbkp_directory_filesizes', $directory_sizes, DAY_IN_SECONDS );
485
-
486
- delete_transient( 'hmbkp_directory_filesizes_running' );
487
-
488
- return $directory_sizes;
489
-
490
- }
491
-
492
- /**
493
- * Get the total filesize for a given file or directory
494
- *
495
- * If $file is a file then just return the result of `filesize()`.
496
- * If $file is a directory then schedule a recursive filesize scan.
497
- *
498
- * @param \SplFileInfo $file The file or directory you want to know the size of
499
- * @param bool $skip_excluded_files Skip excluded files when calculating a directories total size
500
- *
501
- * @return int The total of the file or directory
502
- */
503
- public function filesize( \SplFileInfo $file, $skip_excluded_files = false ) {
504
-
505
- // Skip missing or unreadable files
506
- if ( ! file_exists( $file->getPathname() ) || ! $file->getRealpath() || ! $file->isReadable() ) {
507
- return 0;
508
- }
509
-
510
- // If it's a file then just pass back the filesize
511
- if ( $file->isFile() && $file->isReadable() ) {
512
- return $file->getSize();
513
- }
514
-
515
- // If it's a directory then pull it from the cached filesize array
516
- if ( $file->isDir() ) {
517
-
518
- // If we haven't calculated the site size yet then kick it off in a thread
519
- $directory_sizes = get_transient( 'hmbkp_directory_filesizes' );
520
-
521
- if ( ! is_array( $directory_sizes ) ) {
522
-
523
- if ( ! $this->is_site_size_being_calculated() ) {
524
-
525
- // Mark the filesize as being calculated
526
- set_transient( 'hmbkp_directory_filesizes_running', true, HOUR_IN_SECONDS );
527
-
528
- // Schedule a Backdrop task to trigger a recalculation
529
- $task = new \HM\Backdrop\Task( array( $this, 'recursive_filesize_scanner' ) );
530
- $task->schedule();
531
-
532
- }
533
-
534
- return 0;
535
-
536
- }
537
-
538
- $current_pathname = trailingslashit( $file->getPathname() );
539
- $root = trailingslashit( $this->backup->get_root() );
540
-
541
- foreach ( $directory_sizes as $path => $size ) {
542
-
543
- // Remove any files that aren't part of the current tree
544
- if ( false === strpos( $path, $current_pathname ) ) {
545
- unset( $directory_sizes[ $path ] );
546
- }
547
-
548
- }
549
-
550
- if ( $skip_excluded_files ) {
551
-
552
- $excludes = $this->backup->exclude_string( 'regex' );
553
-
554
- foreach ( $directory_sizes as $path => $size ) {
555
-
556
- // Skip excluded files if we have excludes
557
- if ( $excludes && preg_match( '(' . $excludes . ')', str_ireplace( $root, '', wp_normalize_path( $path ) ) ) ) {
558
- unset( $directory_sizes[ $path ] );
559
- }
560
-
561
- }
562
-
563
- }
564
-
565
- // Directory size is now just a sum of all files across all sub directories
566
- return absint( array_sum( $directory_sizes ) );
567
-
568
- }
569
-
570
- }
571
-
572
- /**
573
- * Get the start time for the schedule
574
- *
575
- * @return int timestamp || 0 for manual only schedules
576
- */
577
- public function get_schedule_start_time( $gmt = true ) {
578
-
579
- if ( 'manually' === $this->get_reoccurrence() ) {
580
- return 0;
581
- }
582
-
583
- if ( ! $gmt ) {
584
- $offset = get_option( 'gmt_offset' ) * 3600;
585
- } else {
586
- $offset = 0;
587
- }
588
- if ( ! empty( $this->options['schedule_start_time'] ) ) {
589
- return $this->options['schedule_start_time'] + $offset;
590
- }
591
-
592
- $this->set_schedule_start_time( time() );
593
-
594
- return time() + $offset;
595
-
596
- }
597
-
598
- /**
599
- * Set the schedule start time.
600
- *
601
- * @param array $args
602
- */
603
- public function set_schedule_start_time( $time ) {
604
-
605
- // Don't allow setting the start time in the past
606
- if ( (int) $time <= time() ) {
607
- return new \WP_Error( 'hmbkp_invalid_argument_error', sprintf( __( 'Argument 1 for %s must be a valid future timestamp', 'backupwordpress' ), __METHOD__ ) );
608
- }
609
-
610
- $this->options['schedule_start_time'] = $time;
611
-
612
- $this->schedule();
613
-
614
- }
615
-
616
- /**
617
- * Get the schedule reoccurrence
618
- *
619
- */
620
- public function get_reoccurrence() {
621
-
622
- // Default to no reoccurrence
623
- if ( empty( $this->options['reoccurrence'] ) ) {
624
- $this->set_reoccurrence( 'manually' );
625
- }
626
-
627
- return $this->options['reoccurrence'];
628
-
629
- }
630
-
631
- /**
632
- * Set the schedule reoccurrence
633
- *
634
- * @param string $reoccurrence
635
- *
636
- * @return \WP_Error|null|boolean
637
- */
638
- public function set_reoccurrence( $reoccurrence ) {
639
-
640
- $hmbkp_schedules = $this->get_cron_schedules();
641
-
642
- // Check it's valid
643
- if ( ! is_string( $reoccurrence ) || ! trim( $reoccurrence ) || ( ! in_array( $reoccurrence, array_keys( $hmbkp_schedules ) ) ) && 'manually' !== $reoccurrence ) {
644
- return new \WP_Error( 'hmbkp_invalid_argument_error', sprintf( __( 'Argument 1 for %s must be a valid cron recurrence or "manually"', 'backupwordpress' ), __METHOD__ ) );
645
- }
646
-
647
- // If the recurrence is already set to the same thing then there's no need to continue
648
- if ( isset( $this->options['reoccurrence'] ) && $this->options['reoccurrence'] === $reoccurrence && $this->is_cron_scheduled() ) {
649
- return;
650
- }
651
-
652
-
653
- $this->options['reoccurrence'] = $reoccurrence;
654
-
655
- if ( 'manually' === $reoccurrence ) {
656
- $this->unschedule();
657
- } else {
658
- $this->schedule();
659
- }
660
-
661
- return true;
662
-
663
- }
664
-
665
- /**
666
- * Get the interval between backups
667
- *
668
- * @return int
669
- */
670
- public function get_interval() {
671
-
672
- $hmbkp_schedules = $this->get_cron_schedules();
673
-
674
- if ( 'manually' === $this->get_reoccurrence() ) {
675
- return 0;
676
- }
677
-
678
- return $hmbkp_schedules[ $this->get_reoccurrence() ]['interval'];
679
-
680
- }
681
-
682
- /**
683
- * Return an array of BackUpWordPress cron schedules
684
- *
685
- * @return array
686
- */
687
- public static function get_cron_schedules() {
688
- return hmbkp_cron_schedules();
689
- }
690
-
691
- /**
692
- * Get the next occurrence of this scheduled backup
693
- *
694
- */
695
- public function get_next_occurrence( $gmt = true ) {
696
-
697
- $time = wp_next_scheduled( 'hmbkp_schedule_hook', array( 'id' => $this->get_id() ) );
698
-
699
- if ( ! $time ) {
700
- $time = 0;
701
- }
702
-
703
-
704
- if ( ! $gmt ) {
705
- $time += get_option( 'gmt_offset' ) * 3600;
706
- }
707
-
708
- return $time;
709
-
710
- }
711
-
712
- public function is_cron_scheduled() {
713
- return (bool) $this->get_next_occurrence();
714
- }
715
-
716
-
717
- /**
718
- * Get the path to the backup running file that stores the running backup status
719
- *
720
- * @return string
721
- */
722
- public function get_schedule_running_path() {
723
- return $this->get_path() . '/.schedule-' . $this->get_id() . '-running';
724
- }
725
-
726
- /**
727
- * Schedule the backup cron
728
- *
729
- */
730
- public function schedule() {
731
-
732
- // Clear any existing hooks
733
- $this->unschedule();
734
-
735
- $schedule_timestamp = $this->get_schedule_start_time();
736
-
737
- wp_schedule_event( $schedule_timestamp, $this->get_reoccurrence(), 'hmbkp_schedule_hook', array( 'id' => $this->get_id() ) );
738
-
739
- }
740
-
741
-
742
- /**
743
- * Unschedule the backup cron.
744
- *
745
- * @return void
746
- */
747
- public function unschedule() {
748
- wp_clear_scheduled_hook( 'hmbkp_schedule_hook', array( 'id' => $this->get_id() ) );
749
- }
750
-
751
- /**
752
- * Run the backup
753
- *
754
- */
755
- public function run() {
756
-
757
- // Don't run if this schedule is already running
758
- if ( $this->get_running_backup_filename() ) {
759
- return;
760
- }
761
-
762
- // Mark the backup as started
763
- $this->set_status( __( 'Starting Backup', 'backupwordpress' ) );
764
-
765
- // Delete old backups now in-case we fatal error during the backup process
766
- $this->delete_old_backups();
767
-
768
- if ( $this->get_backups() ) {
769
-
770
- // If we already have a previous backup then pass it in so it can be re-used
771
- list( $existing_backup ) = array_values( $this->get_backups() );
772
-
773
- if ( $existing_backup && file_exists( $existing_backup ) ) {
774
- $this->backup->set_existing_archive_filepath( $existing_backup );
775
- }
776
-
777
- }
778
-
779
- $this->backup->backup();
780
-
781
- // Delete the backup running file
782
- if ( file_exists( $this->get_schedule_running_path() ) ) {
783
- unlink( $this->get_schedule_running_path() );
784
- }
785
-
786
- // Delete old backups again
787
- $this->delete_old_backups();
788
-
789
- }
790
-
791
- /**
792
- * Get the filename that the running status is stored in.
793
- *
794
- * @return string
795
- */
796
- public function get_running_backup_filename() {
797
-
798
- if ( ! file_exists( $this->get_schedule_running_path() ) ) {
799
- return '';
800
- }
801
-
802
- $status = json_decode( file_get_contents( $this->get_schedule_running_path() ) );
803
-
804
- if ( ! empty( $status->filename ) ) {
805
- return $status->filename;
806
- }
807
-
808
- return '';
809
-
810
- }
811
-
812
- /**
813
- * Get the status of the running backup.
814
- *
815
- * @return string
816
- */
817
- public function get_status() {
818
-
819
- if ( ! file_exists( $this->get_schedule_running_path() ) ) {
820
- return '';
821
- }
822
-
823
-
824
- $status = json_decode( file_get_contents( $this->get_schedule_running_path() ) );
825
-
826
- if ( ! empty( $status->status ) ) {
827
- return $status->status;
828
- }
829
-
830
- return '';
831
-
832
- }
833
-
834
- /**
835
- * Set the status of the running backup
836
- *
837
- * @param string $message
838
- *
839
- * @return null
840
- */
841
- public function set_status( $message ) {
842
-
843
- $status = json_encode( (object) array(
844
- 'filename' => $this->backup->get_archive_filename(),
845
- 'started' => $this->get_schedule_running_start_time(),
846
- 'status' => $message,
847
- ) );
848
-
849
- if ( false === @file_put_contents( $this->get_schedule_running_path(), $status ) ) {
850
- throw new \RuntimeException( sprintf( __( 'Error writing to file. (%s)', 'backupwordpress' ), $this->get_schedule_running_path() ) );
851
- }
852
-
853
- }
854
-
855
- /**
856
- * Set the time that the current running backup was started
857
- *
858
- * @return int $timestamp
859
- */
860
- public function get_schedule_running_start_time() {
861
-
862
- if ( ! file_exists( $this->get_schedule_running_path() ) ) {
863
- return 0;
864
- }
865
-
866
- $status = json_decode( file_get_contents( $this->get_schedule_running_path() ) );
867
-
868
- if ( ! empty( $status->started ) && (int) (string) $status->started === $status->started ) {
869
- return $status->started;
870
- }
871
-
872
- return time();
873
-
874
- }
875
-
876
- /**
877
- * Hook into the actions fired in the Backup class and set the status
878
- *
879
- * @param $action
880
- */
881
- public function do_action( $action, Backup $backup ) {
882
-
883
- // Pass the actions to all the services
884
- // Todo should be decoupled into the service class
885
- foreach ( Services::get_services( $this ) as $service ) {
886
- if ( is_wp_error( $service ) ) {
887
- return $service;
888
- }
889
- $service->action( $action, $backup );
890
- }
891
-
892
- switch ( $action ) :
893
-
894
- case 'hmbkp_backup_started':
895
- case 'hmbkp_mysqldump_finished':
896
- case 'hmbkp_archive_finished':
897
- break;
898
-
899
- case 'hmbkp_mysqldump_started' :
900
-
901
- $this->set_status( sprintf( __( 'Dumping Database %s', 'backupwordpress' ), '(<code>' . $this->backup->get_mysqldump_method() . '</code>)' ) );
902
- break;
903
-
904
- case 'hmbkp_mysqldump_verify_started' :
905
-
906
- $this->set_status( sprintf( __( 'Verifying Database Dump %s', 'backupwordpress' ), '(<code>' . $this->backup->get_mysqldump_method() . '</code>)' ) );
907
- break;
908
-
909
- case 'hmbkp_archive_started' :
910
-
911
- $this->set_status( sprintf( __( 'Creating zip archive %s', 'backupwordpress' ), '(<code>' . $this->backup->get_archive_method() . '</code>)' ) );
912
- break;
913
-
914
- case 'hmbkp_archive_verify_started' :
915
-
916
- $this->set_status( sprintf( __( 'Verifying Zip Archive %s', 'backupwordpress' ), '(<code>' . $this->backup->get_archive_method() . '</code>)' ) );
917
- break;
918
-
919
- case 'hmbkp_backup_complete' :
920
-
921
- $this->set_status( __( 'Finishing Backup', 'backupwordpress' ) );
922
- $this->update_average_schedule_run_time( $this->get_schedule_running_start_time(), time() );
923
-
924
- break;
925
-
926
- case 'hmbkp_error' :
927
-
928
- if ( $this->backup->get_errors() ) {
929
-
930
- $file = $this->get_path() . '/.backup_errors';
931
-
932
- if ( file_exists( $file ) ) {
933
- @unlink( $file );
934
- }
935
-
936
- if ( ! $handle = @fopen( $file, 'w' ) ) {
937
- return;
938
- }
939
-
940
- fwrite( $handle, json_encode( $this->backup->get_errors() ) );
941
-
942
- fclose( $handle );
943
-
944
- }
945
-
946
- break;
947
-
948
- case 'hmbkp_warning' :
949
-
950
- if ( $this->backup->get_warnings() ) {
951
-
952
- $file = $this->get_path() . '/.backup_warnings';
953
-
954
- if ( file_exists( $file ) ) {
955
- @unlink( $file );
956
- }
957
-
958
- if ( ! $handle = @fopen( $file, 'w' ) ) {
959
- return;
960
- }
961
-
962
- fwrite( $handle, json_encode( $this->backup->get_warnings() ) );
963
-
964
- fclose( $handle );
965
-
966
- }
967
-
968
- break;
969
-
970
- default:
971
-
972
- return new \WP_Error( 'unexpected-error', __( 'An unexpected error occurred', 'backupwordpress' ) );
973
-
974
- endswitch;
975
-
976
- }
977
-
978
- /**
979
- * Calculate schedule run time.
980
- *
981
- * @param int Timestamp $end
982
- */
983
- public function update_average_schedule_run_time( $start, $end ) {
984
-
985
- if ( $end <= $start ) {
986
- // Something went wrong, ignore.
987
- return;
988
- }
989
-
990
- $diff = (int) abs( $end - $start );
991
-
992
- if ( isset( $this->options['duration_total'] ) && isset( $this->options['backup_run_count'] ) ) {
993
-
994
- $this->options['duration_total'] += $diff;
995
- $this->options['backup_run_count'] ++;
996
-
997
- } else {
998
-
999
- $this->options['duration_total'] = $diff;
1000
- $this->options['backup_run_count'] = 1;
1001
-
1002
- }
1003
-
1004
- $this->save();
1005
- }
1006
-
1007
- /**
1008
- * Calculates the average run time for this schedule.
1009
- *
1010
- * @return string
1011
- */
1012
- public function get_schedule_average_duration() {
1013
-
1014
- $duration = 'Unknown';
1015
-
1016
- if ( ! isset( $this->options['duration_total'] ) || ! isset( $this->options['backup_run_count'] ) ) {
1017
- return $duration;
1018
- }
1019
-
1020
- if ( 0 === (int) $this->options['backup_run_count'] ) {
1021
- return $duration;
1022
- }
1023
-
1024
- $average_run_time = (int) $this->options['duration_total'] / (int) $this->options['backup_run_count'];
1025
-
1026
- if ( $average_run_time < HOUR_IN_SECONDS ) {
1027
-
1028
- $mins = round( $average_run_time / MINUTE_IN_SECONDS );
1029
-
1030
- if ( $mins <= 1 ) {
1031
- $mins = 1;
1032
- }
1033
-
1034
- /* translators: min=minute */
1035
- $duration = sprintf( _n( '%s min', '%s mins', $mins, 'backupwordpress' ), $mins );
1036
-
1037
- } elseif ( $average_run_time < DAY_IN_SECONDS && $average_run_time >= HOUR_IN_SECONDS ) {
1038
-
1039
- $hours = round( $average_run_time / HOUR_IN_SECONDS );
1040
-
1041
- if ( $hours <= 1 ) {
1042
- $hours = 1;
1043
- }
1044
-
1045
- $duration = sprintf( _n( '%s hour', '%s hours', $hours, 'backupwordpress' ), $hours );
1046
- }
1047
-
1048
- return $duration;
1049
- }
1050
-
1051
- /**
1052
- * Get the backups created by this schedule
1053
- *
1054
- * @todo look into using recursiveDirectoryIterator and recursiveRegexIterator
1055
- * @return string[] - file paths of the backups
1056
- */
1057
- public function get_backups() {
1058
-
1059
- $files = array();
1060
-
1061
- if ( $handle = @opendir( $this->get_path() ) ) {
1062
-
1063
- while ( false !== ( $file = readdir( $handle ) ) ) {
1064
-
1065
- if ( pathinfo( $file, PATHINFO_EXTENSION ) === 'zip' && strpos( $file, $this->get_id() ) !== false && $this->get_running_backup_filename() !== $file ) {
1066
- $files[ @filemtime( trailingslashit( $this->get_path() ) . $file ) ] = trailingslashit( $this->get_path() ) . $file;
1067
- }
1068
-
1069
- }
1070
-
1071
- closedir( $handle );
1072
-
1073
- }
1074
-
1075
- krsort( $files );
1076
-
1077
- return $files;
1078
-
1079
- }
1080
-
1081
- /**
1082
- * Delete old backups
1083
- *
1084
- * @access private
1085
- */
1086
- public function delete_old_backups() {
1087
-
1088
- if ( count( $this->get_backups() ) <= $this->get_max_backups() ) {
1089
- return;
1090
- }
1091
-
1092
- array_map( array( $this, 'delete_backup' ), array_slice( $this->get_backups(), $this->get_max_backups() ) );
1093
-
1094
- }
1095
-
1096
- /**
1097
- * Delete a specific back up file created by this schedule
1098
- *
1099
- * @param string $filepath
1100
- *
1101
- * @return \WP_Error|boolean
1102
- */
1103
- public function delete_backup( $filepath ) {
1104
-
1105
- // Check that it's a valid filepath
1106
- if ( empty( $filepath ) || ! is_string( $filepath ) ) {
1107
- return new \WP_Error( 'hmbkp_empty_string_error', sprintf( __( 'Argument 1 for %s must be a non-empty string', 'backupwordpress' ), __METHOD__ ) );
1108
- }
1109
-
1110
- // Make sure it exists
1111
- if ( ! file_exists( $filepath ) ) {
1112
- return new \WP_Error( 'hmbkp_file_error', sprintf( __( '%s doesn\'t exist', 'backupwordpress' ), $filepath ) );
1113
- }
1114
-
1115
- // Make sure it was created by this schedule
1116
- if ( strpos( $filepath, $this->get_id() ) === false ) {
1117
- return new \WP_Error( 'hmbkp_backup_error', __( 'That backup wasn\'t created by this schedule', 'backupwordpress' ) );
1118
- }
1119
-
1120
- unlink( $filepath );
1121
-
1122
- return true;
1123
-
1124
- }
1125
-
1126
- /**
1127
- * Delete all back up files created by this schedule
1128
- *
1129
- */
1130
- public function delete_backups() {
1131
-
1132
- array_map( array( $this, 'delete_backup' ), $this->get_backups() );
1133
-
1134
- }
1135
-
1136
- /**
1137
- * Save the schedules options.
1138
- *
1139
- */
1140
- public function save() {
1141
-
1142
- // Only save them if they have changed
1143
- if ( $this->options !== get_option( 'hmbkp_schedule_' . $this->get_id() ) ) {
1144
- update_option( 'hmbkp_schedule_' . $this->get_id(), $this->options );
1145
- }
1146
-
1147
- }
1148
-
1149
- /**
1150
- * Cancel this schedule
1151
- *
1152
- * Cancels the cron job, removes the schedules options
1153
- * and optionally deletes all backups created by
1154
- * this schedule.
1155
- *
1156
- */
1157
- public function cancel( $delete_backups = false ) {
1158
-
1159
- // Delete the schedule options
1160
- delete_option( 'hmbkp_schedule_' . $this->get_id() );
1161
-
1162
- // Clear any existing schedules
1163
- $this->unschedule();
1164
-
1165
- // Delete it's backups
1166
- if ( $delete_backups ) {
1167
- $this->delete_backups();
1168
- }
1169
-
1170
- }
1171
-
1172
- }
1173
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/class-scheduled-backup.php ADDED
@@ -0,0 +1,680 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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(
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(
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() );
93
+
94
+ }
95
+
96
+ /**
97
+ * Get the id for this schedule
98
+ */
99
+ public function get_id() {
100
+ return esc_attr( $this->id );
101
+ }
102
+
103
+ /**
104
+ * Get a slugified version of name
105
+ */
106
+ public function get_slug() {
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
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
171
+ * @param bool $append Whether to replace or append to existing rules
172
+ *
173
+ * @return string
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
203
+ */
204
+ public function set_max_backups( $max ) {
205
+ $this->options['max_backups'] = $max;
206
+ }
207
+
208
+ public function get_status() {
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
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 timestamp $time
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() {
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
+
330
+ $this->options['reoccurrence'] = $reoccurrence;
331
+
332
+ if ( 'manually' === $reoccurrence ) {
333
+ $this->unschedule();
334
+
335
+ } else {
336
+ $this->schedule();
337
+ }
338
+
339
+ return true;
340
+
341
+ }
342
+
343
+ /**
344
+ * Get the interval between backups
345
+ *
346
+ * @return int
347
+ */
348
+ public function get_interval() {
349
+
350
+ $hmbkp_schedules = cron_schedules();
351
+
352
+ if ( 'manually' === $this->get_reoccurrence() ) {
353
+ return 0;
354
+ }
355
+
356
+ return $hmbkp_schedules[ $this->get_reoccurrence() ]['interval'];
357
+
358
+ }
359
+
360
+ /**
361
+ * Get the next occurrence of this scheduled backup
362
+ *
363
+ */
364
+ public function get_next_occurrence( $gmt = true ) {
365
+
366
+ $time = wp_next_scheduled( 'hmbkp_schedule_hook', array( 'id' => $this->get_id() ) );
367
+
368
+ if ( ! $time ) {
369
+ $time = 0;
370
+ }
371
+
372
+ if ( ! $gmt ) {
373
+ $time += get_option( 'gmt_offset' ) * 3600;
374
+ }
375
+
376
+ return $time;
377
+
378
+ }
379
+
380
+ public function is_cron_scheduled() {
381
+ return (bool) $this->get_next_occurrence();
382
+ }
383
+
384
+ /**
385
+ * Schedule the backup cron
386
+ *
387
+ */
388
+ public function schedule() {
389
+
390
+ // Clear any existing hooks
391
+ $this->unschedule();
392
+
393
+ $schedule_timestamp = $this->get_schedule_start_time();
394
+
395
+ wp_schedule_event( $schedule_timestamp, $this->get_reoccurrence(), 'hmbkp_schedule_hook', array( 'id' => $this->get_id() ) );
396
+
397
+ }
398
+
399
+
400
+ /**
401
+ * Unschedule the backup cron.
402
+ *
403
+ * @return void
404
+ */
405
+ public function unschedule() {
406
+ wp_clear_scheduled_hook( 'hmbkp_schedule_hook', array( 'id' => $this->get_id() ) );
407
+ }
408
+
409
+ /**
410
+ * Run the backup
411
+ *
412
+ */
413
+ public function run() {
414
+
415
+ // Don't run if this schedule is already running
416
+ if ( $this->status->is_started() ) {
417
+ return;
418
+ }
419
+
420
+ // Setup our Site Backup Object
421
+ $backup = new Backup( $this->get_backup_filename(), $this->get_database_dump_filename() );
422
+ $backup->set_type( $this->get_type() );
423
+ $backup->set_excludes( $this->get_excludes() );
424
+ $backup->set_status( $this->status );
425
+
426
+ $this->do_action( 'hmbkp_backup_started', $backup );
427
+
428
+ $this->status->start( $this->get_backup_filename(), __( 'Starting backup...', 'backupwordpress' ) );
429
+
430
+ $this->status->set_status( __( 'Deleting old backups...', 'backupwordpress' ) );
431
+
432
+ // Delete old backups now in-case we fatal error during the backup process
433
+ $this->delete_old_backups();
434
+
435
+ $backup->run();
436
+
437
+ $errors = array_merge( $backup->errors, $backup->warnings );
438
+ $notices = array();
439
+ foreach ( $errors as $key => $error ) {
440
+ $key = str_replace( array( __NAMESPACE__ . '\\', '_File_Backup_Engine', '_Database_Backup_Engine' ), array( '', '', '' ), $key );
441
+ $notices[] = $key . ': ' . implode( ', ', $error );
442
+ }
443
+ Notices::get_instance()->set_notices( 'backup_errors', $notices );
444
+
445
+ $this->status->set_status( __( 'Deleting old backups...', 'backupwordpress' ) );
446
+
447
+ // Delete old backups again
448
+ $this->delete_old_backups();
449
+
450
+ $this->do_action( 'hmbkp_backup_complete', $backup );
451
+
452
+ $this->status->finish();
453
+ $this->update_average_schedule_run_time( $this->status->get_start_time(), time() );
454
+
455
+ }
456
+
457
+ public function get_backup_filename() {
458
+
459
+ if ( $this->status->is_started() ) {
460
+ $this->backup_filename = $this->status->get_backup_filename();
461
+ }
462
+
463
+ return $this->backup_filename;
464
+ }
465
+
466
+ public function get_database_dump_filename() {
467
+ return $this->database_dump_filename;
468
+ }
469
+
470
+ /**
471
+ * Hook into the actions fired in the Backup class and set the status
472
+ *
473
+ * @param $action
474
+ */
475
+ public function do_action( $action, Backup $backup ) {
476
+
477
+ // Pass the actions to all the services
478
+ // Todo should be decoupled into the service class
479
+ foreach ( Services::get_services( $this ) as $service ) {
480
+ if ( is_wp_error( $service ) ) {
481
+ return $service;
482
+ }
483
+ $service->action( $action, $backup );
484
+ }
485
+
486
+ }
487
+
488
+ /**
489
+ * Calculate schedule run time.
490
+ *
491
+ * @param int Timestamp $end
492
+ */
493
+ public function update_average_schedule_run_time( $start, $end ) {
494
+
495
+ if ( $end <= $start ) {
496
+ // Something went wrong, ignore.
497
+ return;
498
+ }
499
+
500
+ $diff = (int) abs( $end - $start );
501
+
502
+ if ( isset( $this->options['duration_total'] ) && isset( $this->options['backup_run_count'] ) ) {
503
+
504
+ $this->options['duration_total'] += $diff;
505
+ $this->options['backup_run_count'] ++;
506
+
507
+ } else {
508
+
509
+ $this->options['duration_total'] = $diff;
510
+ $this->options['backup_run_count'] = 1;
511
+
512
+ }
513
+
514
+ $this->save();
515
+ }
516
+
517
+ /**
518
+ * Calculates the average run time for this schedule.
519
+ *
520
+ * @return string
521
+ */
522
+ public function get_schedule_average_duration() {
523
+
524
+ $duration = 'Unknown';
525
+
526
+ if ( ! isset( $this->options['duration_total'] ) || ! isset( $this->options['backup_run_count'] ) ) {
527
+ return $duration;
528
+ }
529
+
530
+ if ( 0 === (int) $this->options['backup_run_count'] ) {
531
+ return $duration;
532
+ }
533
+
534
+ $average_run_time = (int) $this->options['duration_total'] / (int) $this->options['backup_run_count'];
535
+
536
+ if ( $average_run_time < HOUR_IN_SECONDS ) {
537
+
538
+ $mins = round( $average_run_time / MINUTE_IN_SECONDS );
539
+
540
+ if ( $mins <= 1 ) {
541
+ $mins = 1;
542
+ }
543
+
544
+ /* translators: min=minute */
545
+ $duration = sprintf( _n( '%s min', '%s mins', $mins, 'backupwordpress' ), $mins );
546
+
547
+ } elseif ( $average_run_time < DAY_IN_SECONDS && $average_run_time >= HOUR_IN_SECONDS ) {
548
+
549
+ $hours = round( $average_run_time / HOUR_IN_SECONDS );
550
+
551
+ if ( $hours <= 1 ) {
552
+ $hours = 1;
553
+ }
554
+
555
+ $duration = sprintf( _n( '%s hour', '%s hours', $hours, 'backupwordpress' ), $hours );
556
+ }
557
+
558
+ return $duration;
559
+ }
560
+
561
+ /**
562
+ * Get the backups created by this schedule
563
+ *
564
+ * @todo look into using recursiveDirectoryIterator and recursiveRegexIterator
565
+ * @return string[] - file paths of the backups
566
+ */
567
+ public function get_backups() {
568
+
569
+ $files = array();
570
+
571
+ if ( $handle = @opendir( Path::get_path() ) ) {
572
+
573
+ while ( false !== ( $file = readdir( $handle ) ) ) {
574
+
575
+ if ( pathinfo( $file, PATHINFO_EXTENSION ) === 'zip' && strpos( $file, $this->get_id() ) !== false && ( isset( $this->status ) && $this->get_backup_filename() !== $file ) ) {
576
+ $files[ @filemtime( trailingslashit( Path::get_path() ) . $file ) ] = trailingslashit( Path::get_path() ) . $file;
577
+ }
578
+
579
+ }
580
+
581
+ closedir( $handle );
582
+
583
+ }
584
+
585
+ krsort( $files );
586
+
587
+ return $files;
588
+
589
+ }
590
+
591
+ /**
592
+ * Delete old backups
593
+ *
594
+ * @access private
595
+ */
596
+ public function delete_old_backups() {
597
+
598
+ if ( count( $this->get_backups() ) <= $this->get_max_backups() ) {
599
+ return;
600
+ }
601
+
602
+ array_map( array( $this, 'delete_backup' ), array_slice( $this->get_backups(), $this->get_max_backups() ) );
603
+
604
+ }
605
+
606
+ /**
607
+ * Delete a specific back up file created by this schedule
608
+ *
609
+ * @param string $filepath
610
+ *
611
+ * @return \WP_Error|boolean
612
+ */
613
+ public function delete_backup( $filepath ) {
614
+
615
+ // Check that it's a valid filepath
616
+ if ( empty( $filepath ) || ! is_string( $filepath ) ) {
617
+ return new \WP_Error( 'hmbkp_empty_string_error', sprintf( __( 'Argument 1 for %s must be a non-empty string', 'backupwordpress' ), __METHOD__ ) );
618
+ }
619
+
620
+ // Make sure it exists
621
+ if ( ! file_exists( $filepath ) ) {
622
+ return new \WP_Error( 'hmbkp_file_error', sprintf( __( '%s doesn\'t exist', 'backupwordpress' ), $filepath ) );
623
+ }
624
+
625
+ // Make sure it was created by this schedule
626
+ if ( strpos( $filepath, $this->get_id() ) === false ) {
627
+ return new \WP_Error( 'hmbkp_backup_error', __( 'That backup wasn\'t created by this schedule', 'backupwordpress' ) );
628
+ }
629
+
630
+ unlink( $filepath );
631
+
632
+ return true;
633
+
634
+ }
635
+
636
+ /**
637
+ * Delete all back up files created by this schedule
638
+ *
639
+ */
640
+ public function delete_backups() {
641
+ array_map( array( $this, 'delete_backup' ), $this->get_backups() );
642
+ }
643
+
644
+ /**
645
+ * Save the schedules options.
646
+ *
647
+ */
648
+ public function save() {
649
+
650
+ // Only save them if they have changed
651
+ if ( $this->options !== get_option( 'hmbkp_schedule_' . $this->get_id() ) ) {
652
+ update_option( 'hmbkp_schedule_' . $this->get_id(), $this->options );
653
+ }
654
+
655
+ }
656
+
657
+ /**
658
+ * Cancel this schedule
659
+ *
660
+ * Cancels the cron job, removes the schedules options
661
+ * and optionally deletes all backups created by
662
+ * this schedule.
663
+ *
664
+ */
665
+ public function cancel( $delete_backups = false ) {
666
+
667
+ // Delete the schedule options
668
+ delete_option( 'hmbkp_schedule_' . $this->get_id() );
669
+
670
+ // Clear any existing schedules
671
+ $this->unschedule();
672
+
673
+ // Delete it's backups
674
+ if ( $delete_backups ) {
675
+ $this->delete_backups();
676
+ }
677
+
678
+ }
679
+
680
+ }
classes/class-service.php CHANGED
@@ -86,7 +86,7 @@ abstract class Service {
86
  *
87
  * @return mixed
88
  */
89
- abstract public function action( $action, Backup $backup );
90
 
91
  public function get_slug() {
92
  return sanitize_key( $this->name );
86
  *
87
  * @return mixed
88
  */
89
+ public function action( $action, Backup $backup ) {}
90
 
91
  public function get_slug() {
92
  return sanitize_key( $this->name );
classes/class-services.php CHANGED
@@ -123,4 +123,4 @@ class Services {
123
 
124
  }
125
 
126
- }
123
 
124
  }
125
 
126
+ }
classes/class-site-size.php ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace HM\BackUpWordPress;
4
+
5
+ use HM\Backdrop\Task;
6
+ use Symfony\Component\Finder\Finder;
7
+
8
+ /**
9
+ * Site Size class
10
+ *
11
+ * Use to calculate the total or partial size of the sites database and files.
12
+ */
13
+ class Site_Size {
14
+
15
+ private $size = 0;
16
+ private $type = '';
17
+ private $excludes = array();
18
+
19
+ /**
20
+ * Constructor
21
+ *
22
+ * Set up some initial conditions including whether we want to calculate the
23
+ * size of the database, files or both and whether to exclude any files from
24
+ * the file size calculation.
25
+ *
26
+ * @param string $type Whether to calculate the size of the database, files
27
+ * or both. Should be one of 'file', 'database' or 'complete'
28
+ * @param array $excludes An array of exclude rules
29
+ */
30
+ public function __construct( $type = 'complete', Excludes $excludes = null ) {
31
+ $this->type = $type;
32
+ $this->excludes = $excludes;
33
+ }
34
+
35
+ /**
36
+ * Calculate the size total size of the database + files.
37
+ *
38
+ * Doesn't account for any compression that would be gained by zipping.
39
+ *
40
+ * @return string
41
+ */
42
+ public function get_site_size() {
43
+
44
+ if ( $this->size ) {
45
+ return $this->size;
46
+ }
47
+
48
+ $size = 0;
49
+
50
+ // Include database size except for file only schedule.
51
+ if ( 'file' !== $this->type ) {
52
+
53
+ global $wpdb;
54
+ $tables = $wpdb->get_results( 'SHOW TABLE STATUS FROM `' . DB_NAME . '`', ARRAY_A );
55
+
56
+ foreach ( $tables as $table ) {
57
+ $size += (float) $table['Data_length'];
58
+ }
59
+ }
60
+
61
+ // Include total size of dirs/files except for database only schedule.
62
+ if ( 'database' !== $this->type ) {
63
+
64
+ $root = new \SplFileInfo( Path::get_root() );
65
+ $size += $this->filesize( $root );
66
+
67
+ }
68
+
69
+ $this->size = $size;
70
+
71
+ return $size;
72
+
73
+ }
74
+
75
+ /**
76
+ * Get the site size formatted
77
+ *
78
+ * @see size_format
79
+ *
80
+ * @return string
81
+ */
82
+ public function get_formatted_site_size() {
83
+ return size_format( $this->get_site_size() );
84
+ }
85
+
86
+ /**
87
+ * Whether the total filesize is being calculated
88
+ *
89
+ * @return bool
90
+ */
91
+ public static function is_site_size_being_calculated() {
92
+ return false !== get_transient( 'hmbkp_directory_filesizes_running' );
93
+ }
94
+
95
+ /**
96
+ * Whether the total filesize is cached
97
+ *
98
+ * @return bool
99
+ */
100
+ public static function is_site_size_cached() {
101
+ return false !== get_transient( 'hmbkp_directory_filesizes' );
102
+ }
103
+
104
+ /**
105
+ * Recursively scans a directory to calculate the total filesize
106
+ *
107
+ * Locks should be set by the caller with `set_transient( 'hmbkp_directory_filesizes_running', true, HOUR_IN_SECONDS );`
108
+ *
109
+ * @return array $directory_sizes An array of directory paths => filesize sum of all files in directory
110
+ */
111
+ public function recursive_filesize_scanner() {
112
+
113
+ /**
114
+ * Raise the `memory_limit` and `max_execution time`
115
+ *
116
+ * Respects the WP_MAX_MEMORY_LIMIT Constant and the `admin_memory_limit`
117
+ * filter.
118
+ */
119
+ @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
120
+ @set_time_limit( 0 );
121
+
122
+ // Use the cached array directory sizes if available
123
+ $directory_sizes = get_transient( 'hmbkp_directory_filesizes' );
124
+
125
+ // If we do have it in cache then let's use it and also clear the lock
126
+ if ( is_array( $directory_sizes ) ) {
127
+ delete_transient( 'hmbkp_directory_filesizes_running' );
128
+ return $directory_sizes;
129
+ }
130
+
131
+ // If we don't have it cached then we'll need to re-calculate
132
+ $finder = new Finder();
133
+ $finder->followLinks();
134
+ $finder->ignoreDotFiles( false );
135
+ $finder->ignoreUnreadableDirs( true );
136
+
137
+ $files = $finder->in( Path::get_root() );
138
+
139
+ foreach ( $files as $file ) {
140
+
141
+ if ( $file->isReadable() ) {
142
+ $directory_sizes[ wp_normalize_path( $file->getRealpath() ) ] = $file->getSize();
143
+ } else {
144
+ $directory_sizes[ wp_normalize_path( $file->getRealpath() ) ] = 0;
145
+ }
146
+
147
+ }
148
+
149
+ set_transient( 'hmbkp_directory_filesizes', $directory_sizes, DAY_IN_SECONDS );
150
+
151
+ // Remove the lock
152
+ delete_transient( 'hmbkp_directory_filesizes_running' );
153
+
154
+ return $directory_sizes;
155
+
156
+ }
157
+
158
+ /**
159
+ * Get the total filesize for a given file or directory
160
+ *
161
+ * If $file is a file then just return the result of `filesize()`.
162
+ * If $file is a directory then recursively calculate the size.
163
+ *
164
+ * @param \SplFileInfo $file The file or directory you want to know the size of
165
+ *
166
+ * @return int The total filesize of the file or directory
167
+ */
168
+ public function filesize( \SplFileInfo $file ) {
169
+
170
+ // Skip missing or unreadable files
171
+ if ( ! file_exists( $file->getPathname() ) || ! $file->getRealpath() || ! $file->isReadable() ) {
172
+ return 0;
173
+ }
174
+
175
+ // If it's a file then just pass back the filesize
176
+ if ( $file->isFile() ) {
177
+ return $file->getSize();
178
+ }
179
+
180
+ // If it's a directory then pull it from the cached filesize array
181
+ if ( $file->isDir() ) {
182
+ return $this->directory_filesize( $file );
183
+ }
184
+
185
+ }
186
+
187
+ public function directory_filesize( \SplFileInfo $file ) {
188
+
189
+ // If we haven't calculated the site size yet then kick it off in a thread
190
+ $directory_sizes = get_transient( 'hmbkp_directory_filesizes' );
191
+
192
+
193
+ if ( ! is_array( $directory_sizes ) ) {
194
+ $this->rebuild_directory_filesizes();
195
+
196
+ // Intentionally return null so the caller can tell that the size is being calculated
197
+ return null;
198
+ }
199
+
200
+ // The filepaths are stored in keys so we need to flip for use with preg_grep
201
+ $directory_sizes = array_flip( preg_grep( '(' . wp_normalize_path( $file->getRealPath() ) . ')', array_flip( $directory_sizes ) ) );
202
+
203
+ if ( $this->excludes ) {
204
+ $excludes = implode( '|', $this->excludes->get_excludes_for_regex() );
205
+ if ( $excludes ) {
206
+ // Use PREG_GREP_INVERT to remove any filepaths which match an exclude rule
207
+ $directory_sizes = array_flip( preg_grep( '(' . $excludes . ')', array_flip( $directory_sizes ), PREG_GREP_INVERT ) );
208
+ }
209
+ }
210
+
211
+ // Directory size is now just a sum of all files across all sub directories
212
+ return absint( array_sum( $directory_sizes ) );
213
+
214
+ }
215
+
216
+ public function rebuild_directory_filesizes() {
217
+
218
+ if ( $this->is_site_size_being_calculated() ) {
219
+ return false;
220
+ }
221
+
222
+ // Mark the filesize as being calculated
223
+ set_transient( 'hmbkp_directory_filesizes_running', true, HOUR_IN_SECONDS );
224
+
225
+ // Schedule a Backdrop task to trigger a recalculation
226
+ $task = new Task( array( $this, 'recursive_filesize_scanner' ) );
227
+ $task->schedule();
228
+
229
+ }
230
+
231
+ }
classes/class-webhook-service.php CHANGED
@@ -39,7 +39,7 @@ abstract class Webhook_Service extends Service {
39
  }
40
 
41
  $webhook_url = $this->get_url();
42
- $file = $backup->get_archive_filepath();
43
  $download = add_query_arg( 'hmbkp_download', base64_encode( $file ), HMBKP_ADMIN_URL );
44
  $domain = parse_url( home_url(), PHP_URL_HOST ) . parse_url( home_url(), PHP_URL_PATH );
45
 
@@ -100,7 +100,7 @@ abstract class Webhook_Service extends Service {
100
  $ret = wp_remote_post( $webhook_url, $webhook_args );
101
 
102
  if ( is_wp_error( $ret ) ) {
103
- $backup->error( 'Webhook', sprintf( __( 'Error: %s', 'backupwordpress' ), $ret->get_error_message() ) );
104
  }
105
 
106
  }
39
  }
40
 
41
  $webhook_url = $this->get_url();
42
+ $file = $backup->get_backup_filepath();
43
  $download = add_query_arg( 'hmbkp_download', base64_encode( $file ), HMBKP_ADMIN_URL );
44
  $domain = parse_url( home_url(), PHP_URL_HOST ) . parse_url( home_url(), PHP_URL_PATH );
45
 
100
  $ret = wp_remote_post( $webhook_url, $webhook_args );
101
 
102
  if ( is_wp_error( $ret ) ) {
103
+ $backup->warning( 'Webhook', sprintf( __( 'Error: %s', 'backupwordpress' ), $ret->get_error_message() ) );
104
  }
105
 
106
  }
classes/class-wpremote-webhook-service.php CHANGED
@@ -69,4 +69,4 @@ class WPRemote_Webhook_Service extends Webhook_Service {
69
  }
70
 
71
  // Register the service
72
- Services::register( __FILE__, 'HM\BackUpWordPress\WPRemote_Webhook_Service' );
69
  }
70
 
71
  // Register the service
72
+ Services::register( __FILE__, 'HM\BackUpWordPress\WPRemote_Webhook_Service' );
composer.json ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name" : "humanmade/backupwordpress",
3
+ "description": "Simple automated backups of your WordPress powered website.",
4
+ "keywords" : [
5
+ "wordpress",
6
+ "backup"
7
+ ],
8
+ "type" : "wordpress-plugin",
9
+ "homepage" : "https://github.com/humanmade/backupwordpress",
10
+ "license" : "GPL-3.0",
11
+ "authors" : [
12
+ {
13
+ "name" : "Human Made Limited",
14
+ "email" : "backupwordpress@hmn.md",
15
+ "homepage": "https://bwp.hmn.md/"
16
+ }
17
+ ],
18
+ "support" : {
19
+ "issues": "https://github.com/humanmade/backupwordpress/issues"
20
+ },
21
+ "require": {
22
+ "symfony/finder": "~2.6",
23
+ "ifsnop/mysqldump-php":"2.*"
24
+ }
25
+ }
composer.lock ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_readme": [
3
+ "This file locks the dependencies of your project to a known state",
4
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5
+ "This file is @generated automatically"
6
+ ],
7
+ "hash": "a5d481f4e8a2c50cc0c488d199f4c83e",
8
+ "content-hash": "77f3dd81e4007d746a29f10fd5dce8af",
9
+ "packages": [
10
+ {
11
+ "name": "ifsnop/mysqldump-php",
12
+ "version": "v2.1",
13
+ "source": {
14
+ "type": "git",
15
+ "url": "https://github.com/ifsnop/mysqldump-php.git",
16
+ "reference": "701024dd160f15796bed8130c3bdeb26c634785a"
17
+ },
18
+ "dist": {
19
+ "type": "zip",
20
+ "url": "https://api.github.com/repos/ifsnop/mysqldump-php/zipball/701024dd160f15796bed8130c3bdeb26c634785a",
21
+ "reference": "701024dd160f15796bed8130c3bdeb26c634785a",
22
+ "shasum": ""
23
+ },
24
+ "require": {
25
+ "php": ">=5.3.0"
26
+ },
27
+ "require-dev": {
28
+ "phpunit/phpunit": "3.7.*",
29
+ "squizlabs/php_codesniffer": "1.*"
30
+ },
31
+ "type": "library",
32
+ "autoload": {
33
+ "psr-4": {
34
+ "Ifsnop\\": "src/Ifsnop/"
35
+ }
36
+ },
37
+ "notification-url": "https://packagist.org/downloads/",
38
+ "license": [
39
+ "MIT"
40
+ ],
41
+ "authors": [
42
+ {
43
+ "name": "Diego Torres",
44
+ "homepage": "https://github.com/ifsnop",
45
+ "role": "Developer"
46
+ }
47
+ ],
48
+ "description": "This is a php version of linux's mysqldump in terminal \"$ mysqldump -u username -p...\"",
49
+ "homepage": "https://github.com/ifsnop/mysqldump-php",
50
+ "keywords": [
51
+ "backup",
52
+ "database",
53
+ "dump",
54
+ "export",
55
+ "mysql",
56
+ "mysqldump",
57
+ "pdo",
58
+ "sqlite"
59
+ ],
60
+ "time": "2015-10-19 15:58:11"
61
+ },
62
+ {
63
+ "name": "symfony/finder",
64
+ "version": "v2.8.2",
65
+ "source": {
66
+ "type": "git",
67
+ "url": "https://github.com/symfony/finder.git",
68
+ "reference": "c90fabdd97e431ee19b6383999cf35334dff27da"
69
+ },
70
+ "dist": {
71
+ "type": "zip",
72
+ "url": "https://api.github.com/repos/symfony/finder/zipball/c90fabdd97e431ee19b6383999cf35334dff27da",
73
+ "reference": "c90fabdd97e431ee19b6383999cf35334dff27da",
74
+ "shasum": ""
75
+ },
76
+ "require": {
77
+ "php": ">=5.3.9"
78
+ },
79
+ "type": "library",
80
+ "extra": {
81
+ "branch-alias": {
82
+ "dev-master": "2.8-dev"
83
+ }
84
+ },
85
+ "autoload": {
86
+ "psr-4": {
87
+ "Symfony\\Component\\Finder\\": ""
88
+ },
89
+ "exclude-from-classmap": [
90
+ "/Tests/"
91
+ ]
92
+ },
93
+ "notification-url": "https://packagist.org/downloads/",
94
+ "license": [
95
+ "MIT"
96
+ ],
97
+ "authors": [
98
+ {
99
+ "name": "Fabien Potencier",
100
+ "email": "fabien@symfony.com"
101
+ },
102
+ {
103
+ "name": "Symfony Community",
104
+ "homepage": "https://symfony.com/contributors"
105
+ }
106
+ ],
107
+ "description": "Symfony Finder Component",
108
+ "homepage": "https://symfony.com",
109
+ "time": "2016-01-14 08:26:52"
110
+ }
111
+ ],
112
+ "packages-dev": [],
113
+ "aliases": [],
114
+ "minimum-stability": "stable",
115
+ "stability-flags": [],
116
+ "prefer-stable": false,
117
+ "prefer-lowest": false,
118
+ "platform": [],
119
+ "platform-dev": []
120
+ }
functions/core.php CHANGED
@@ -1,20 +1,12 @@
1
  <?php
2
 
3
- /**
4
- * Returns the backup path
5
- *
6
- * @see Path
7
- * @todo remove the need for this
8
- */
9
- function hmbkp_path() {
10
- return HM\BackUpWordPress\Path::get_instance()->get_path();
11
- }
12
 
13
  /**
14
  * Handles anything that needs to be
15
  * done when the plugin is updated
16
  */
17
- function hmbkp_update() {
18
 
19
  // Update from backUpWordPress 0.4.5
20
  if ( get_option( 'bkpwp_max_backups' ) ) {
@@ -63,7 +55,7 @@ function hmbkp_update() {
63
  /**
64
  * Setup a backwards compatible schedule
65
  */
66
- $legacy_schedule = new HM\BackUpWordPress\Scheduled_Backup( 'backup' );
67
 
68
  // Backup type
69
  if ( ( defined( 'HMBKP_FILES_ONLY' ) && HMBKP_FILES_ONLY ) || get_option( 'hmbkp_files_only' ) ) {
@@ -254,7 +246,7 @@ function hmbkp_update() {
254
  // Update from PRIOR_VERSION
255
  if ( get_option( 'hmbkp_plugin_version' ) && version_compare( '3.3.0', get_option( 'hmbkp_plugin_version' ), '>' ) ) {
256
 
257
- $schedules = HM\BackUpWordPress\Schedules::get_instance();
258
 
259
  // Loop through all schedules and re-set the reccurrence to include hmbkp_
260
  foreach ( $schedules->get_schedules() as $schedule ) {
@@ -272,17 +264,17 @@ function hmbkp_update() {
272
  }
273
 
274
  // Every update
275
- if ( get_option( 'hmbkp_plugin_version' ) && version_compare( HM\BackUpWordPress\Plugin::PLUGIN_VERSION, get_option( 'hmbkp_plugin_version' ), '>' ) ) {
276
 
277
- HM\BackUpWordPress\Setup::deactivate();
278
 
279
- HM\BackUpWordPress\Path::get_instance()->protect_path( 'reset' );
280
 
281
  }
282
 
283
  // Update the stored version
284
- if ( get_option( 'hmbkp_plugin_version' ) !== HM\BackUpWordPress\Plugin::PLUGIN_VERSION ) {
285
- update_option( 'hmbkp_plugin_version', HM\BackUpWordPress\Plugin::PLUGIN_VERSION );
286
  }
287
 
288
  }
@@ -290,9 +282,9 @@ function hmbkp_update() {
290
  /**
291
  * Setup the default backup schedules
292
  */
293
- function hmbkp_setup_default_schedules() {
294
 
295
- $schedules = HM\BackUpWordPress\Schedules::get_instance();
296
 
297
  if ( $schedules->get_schedules() ) {
298
  return;
@@ -302,9 +294,9 @@ function hmbkp_setup_default_schedules() {
302
  * Schedule a database backup daily and store backups
303
  * for the last 2 weeks
304
  */
305
- $database_daily = new HM\BackUpWordPress\Scheduled_Backup( (string) time() );
306
  $database_daily->set_type( 'database' );
307
- $database_daily->set_schedule_start_time( hmbkp_determine_start_time( 'daily', array( 'hours' => '23', 'minutes' => '0' ) ) );
308
  $database_daily->set_reoccurrence( 'daily' );
309
  $database_daily->set_max_backups( 7 );
310
  $database_daily->save();
@@ -313,9 +305,9 @@ function hmbkp_setup_default_schedules() {
313
  * Schedule a complete backup to run weekly and store backups for
314
  * the last 3 months
315
  */
316
- $complete_weekly = new HM\BackUpWordPress\Scheduled_Backup( (string) ( time() + 1 ) );
317
  $complete_weekly->set_type( 'complete' );
318
- $complete_weekly->set_schedule_start_time( hmbkp_determine_start_time( 'weekly', array( 'day_of_week' => 'sunday', 'hours' => '3', 'minutes' => '0' ) ) );
319
  $complete_weekly->set_reoccurrence( 'weekly' );
320
  $complete_weekly->set_max_backups( 3 );
321
  $complete_weekly->save();
@@ -328,7 +320,7 @@ function hmbkp_setup_default_schedules() {
328
 
329
  }
330
 
331
- add_action( 'admin_init', 'hmbkp_setup_default_schedules' );
332
 
333
  /**
334
  * Return an array of cron schedules
@@ -336,7 +328,7 @@ add_action( 'admin_init', 'hmbkp_setup_default_schedules' );
336
  * @param $schedules
337
  * @return array $reccurrences
338
  */
339
- function hmbkp_cron_schedules( $schedules = array() ) {
340
 
341
  $schedules += array(
342
  'hourly' => array( 'interval' => HOUR_IN_SECONDS, 'display' => __( 'Once Hourly', 'backupwordpress' ) ),
@@ -350,7 +342,7 @@ function hmbkp_cron_schedules( $schedules = array() ) {
350
  return $schedules;
351
  }
352
 
353
- add_filter( 'cron_schedules', 'hmbkp_cron_schedules' );
354
 
355
  /**
356
  * Recursively delete a directory including
@@ -360,26 +352,31 @@ add_filter( 'cron_schedules', 'hmbkp_cron_schedules' );
360
  * @return bool
361
  * @return bool|WP_Error
362
  */
363
- function hmbkp_rmdirtree( $dir ) {
364
 
365
- if ( false !== strpos( HM\BackUpWordPress\Backup::get_home_path(), $dir ) )
366
  return new WP_Error( 'hmbkp_invalid_action_error', sprintf( __( 'You can only delete directories inside your WordPress installation', 'backupwordpress' ) ) );
 
367
 
368
- if ( is_file( $dir ) )
369
  @unlink( $dir );
 
370
 
371
- if ( ! is_dir( $dir ) || ! is_readable( $dir ) )
372
  return false;
 
373
 
374
- $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ), RecursiveIteratorIterator::CHILD_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD );
375
 
376
  foreach ( $files as $file ) {
377
 
378
- if ( $file->isDir() )
379
  @rmdir( $file->getPathname() );
 
380
 
381
- else
382
  @unlink( $file->getPathname() );
 
383
 
384
  }
385
 
@@ -394,15 +391,13 @@ function hmbkp_rmdirtree( $dir ) {
394
  *
395
  * @return bool
396
  */
397
- function hmbkp_possible() {
398
 
399
- if ( ! wp_is_writable( hmbkp_path() ) || ! is_dir( hmbkp_path() ) ) {
400
  return false;
401
  }
402
 
403
- $test_backup = new HM\BackUpWordPress\Backup();
404
-
405
- if ( ! is_readable( $test_backup->get_root() ) ) {
406
  return false;
407
  }
408
 
@@ -416,7 +411,7 @@ function hmbkp_possible() {
416
  *
417
  * return int the filesize
418
  */
419
- function hmbkp_get_max_attachment_size() {
420
 
421
  $max_size = '10mb';
422
 
@@ -427,10 +422,10 @@ function hmbkp_get_max_attachment_size() {
427
 
428
  }
429
 
430
- function hmbkp_is_path_accessible( $dir ) {
431
 
432
  // Path is inaccessible
433
- if ( strpos( $dir, HM\BackUpWordPress\Backup::get_home_path() ) === false ) {
434
  return false;
435
  }
436
 
@@ -442,8 +437,8 @@ function hmbkp_is_path_accessible( $dir ) {
442
  *
443
  * @return array
444
  */
445
- function hmbkp_get_cron_schedules() {
446
- return hmbkp_cron_schedules();
447
  }
448
 
449
  /**
@@ -464,7 +459,7 @@ function hmbkp_get_cron_schedules() {
464
  * }
465
  * @return int $timestamp Returns the resulting timestamp on success and Int 0 on failure
466
  */
467
- function hmbkp_determine_start_time( $type, $times = array() ) {
468
 
469
  // Default to in 10 minutes
470
  if ( ! empty( $times['now'] ) ) {
@@ -484,7 +479,7 @@ function hmbkp_determine_start_time( $type, $times = array() ) {
484
 
485
  $args = wp_parse_args( $times, $default_times );
486
 
487
- $intervals = HM\BackUpWordPress\Scheduled_Backup::get_cron_schedules();
488
 
489
  // Allow the hours and minutes to be overwritten by a constant
490
  if ( defined( 'HMBKP_SCHEDULE_TIME' ) && HMBKP_SCHEDULE_TIME ) {
@@ -558,9 +553,100 @@ function hmbkp_determine_start_time( $type, $times = array() ) {
558
  *
559
  * @return string
560
  */
561
- function hmbkp_admin_action_url( $action, array $query_args = array() ) {
562
 
563
  $query_args = array_merge( $query_args, array( 'action' => 'hmbkp_' . $action ) );
564
 
565
  return esc_url( wp_nonce_url( add_query_arg( $query_args, admin_url( 'admin-post.php' ) ), 'hmbkp_' . $action, 'hmbkp-' . $action . '_nonce' ) );
566
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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' ) ) {
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' ) ) {
246
  // Update from PRIOR_VERSION
247
  if ( get_option( 'hmbkp_plugin_version' ) && version_compare( '3.3.0', get_option( 'hmbkp_plugin_version' ), '>' ) ) {
248
 
249
+ $schedules = Schedules::get_instance();
250
 
251
  // Loop through all schedules and re-set the reccurrence to include hmbkp_
252
  foreach ( $schedules->get_schedules() as $schedule ) {
264
  }
265
 
266
  // Every update
267
+ if ( get_option( 'hmbkp_plugin_version' ) && version_compare( Plugin::PLUGIN_VERSION, get_option( 'hmbkp_plugin_version' ), '>' ) ) {
268
 
269
+ Setup::deactivate();
270
 
271
+ Path::get_instance()->protect_path( 'reset' );
272
 
273
  }
274
 
275
  // Update the stored version
276
+ if ( get_option( 'hmbkp_plugin_version' ) !== Plugin::PLUGIN_VERSION ) {
277
+ update_option( 'hmbkp_plugin_version', Plugin::PLUGIN_VERSION );
278
  }
279
 
280
  }
282
  /**
283
  * Setup the default backup schedules
284
  */
285
+ function setup_default_schedules() {
286
 
287
+ $schedules = Schedules::get_instance();
288
 
289
  if ( $schedules->get_schedules() ) {
290
  return;
294
  * Schedule a database backup daily and store backups
295
  * for the last 2 weeks
296
  */
297
+ $database_daily = new Scheduled_Backup( (string) time() );
298
  $database_daily->set_type( 'database' );
299
+ $database_daily->set_schedule_start_time( determine_start_time( 'daily', array( 'hours' => '23', 'minutes' => '0' ) ) );
300
  $database_daily->set_reoccurrence( 'daily' );
301
  $database_daily->set_max_backups( 7 );
302
  $database_daily->save();
305
  * Schedule a complete backup to run weekly and store backups for
306
  * the last 3 months
307
  */
308
+ $complete_weekly = new Scheduled_Backup( (string) ( time() + 1 ) );
309
  $complete_weekly->set_type( 'complete' );
310
+ $complete_weekly->set_schedule_start_time( determine_start_time( 'weekly', array( 'day_of_week' => 'sunday', 'hours' => '3', 'minutes' => '0' ) ) );
311
  $complete_weekly->set_reoccurrence( 'weekly' );
312
  $complete_weekly->set_max_backups( 3 );
313
  $complete_weekly->save();
320
 
321
  }
322
 
323
+ add_action( 'admin_init', '\HM\BackUpWordPress\setup_default_schedules' );
324
 
325
  /**
326
  * Return an array of cron schedules
328
  * @param $schedules
329
  * @return array $reccurrences
330
  */
331
+ function cron_schedules( $schedules = array() ) {
332
 
333
  $schedules += array(
334
  'hourly' => array( 'interval' => HOUR_IN_SECONDS, 'display' => __( 'Once Hourly', 'backupwordpress' ) ),
342
  return $schedules;
343
  }
344
 
345
+ add_filter( 'cron_schedules', '\HM\BackUpWordPress\cron_schedules' );
346
 
347
  /**
348
  * Recursively delete a directory including
352
  * @return bool
353
  * @return bool|WP_Error
354
  */
355
+ function rmdirtree( $dir ) {
356
 
357
+ if ( false !== strpos( Path::get_home_path(), $dir ) ) {
358
  return new WP_Error( 'hmbkp_invalid_action_error', sprintf( __( 'You can only delete directories inside your WordPress installation', 'backupwordpress' ) ) );
359
+ }
360
 
361
+ if ( is_file( $dir ) ){
362
  @unlink( $dir );
363
+ }
364
 
365
+ if ( ! is_dir( $dir ) || ! is_readable( $dir ) ){
366
  return false;
367
+ }
368
 
369
+ $files = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $dir, \RecursiveDirectoryIterator::SKIP_DOTS ), \RecursiveIteratorIterator::CHILD_FIRST, \RecursiveIteratorIterator::CATCH_GET_CHILD );
370
 
371
  foreach ( $files as $file ) {
372
 
373
+ if ( $file->isDir() ){
374
  @rmdir( $file->getPathname() );
375
+ }
376
 
377
+ else{
378
  @unlink( $file->getPathname() );
379
+ }
380
 
381
  }
382
 
391
  *
392
  * @return bool
393
  */
394
+ function is_backup_possible() {
395
 
396
+ if ( ! wp_is_writable( Path::get_path() ) || ! is_dir( Path::get_path() ) ) {
397
  return false;
398
  }
399
 
400
+ if ( ! is_readable( Path::get_root() ) ) {
 
 
401
  return false;
402
  }
403
 
411
  *
412
  * return int the filesize
413
  */
414
+ function get_max_attachment_size() {
415
 
416
  $max_size = '10mb';
417
 
422
 
423
  }
424
 
425
+ function is_path_accessible( $dir ) {
426
 
427
  // Path is inaccessible
428
+ if ( strpos( $dir, Path::get_home_path() ) === false ) {
429
  return false;
430
  }
431
 
437
  *
438
  * @return array
439
  */
440
+ function get_cron_schedules() {
441
+ return cron_schedules();
442
  }
443
 
444
  /**
459
  * }
460
  * @return int $timestamp Returns the resulting timestamp on success and Int 0 on failure
461
  */
462
+ function determine_start_time( $type, $times = array() ) {
463
 
464
  // Default to in 10 minutes
465
  if ( ! empty( $times['now'] ) ) {
479
 
480
  $args = wp_parse_args( $times, $default_times );
481
 
482
+ $intervals = get_cron_schedules();
483
 
484
  // Allow the hours and minutes to be overwritten by a constant
485
  if ( defined( 'HMBKP_SCHEDULE_TIME' ) && HMBKP_SCHEDULE_TIME ) {
553
  *
554
  * @return string
555
  */
556
+ function admin_action_url( $action, array $query_args = array() ) {
557
 
558
  $query_args = array_merge( $query_args, array( 'action' => 'hmbkp_' . $action ) );
559
 
560
  return esc_url( wp_nonce_url( add_query_arg( $query_args, admin_url( 'admin-post.php' ) ), 'hmbkp_' . $action, 'hmbkp-' . $action . '_nonce' ) );
561
  }
562
+
563
+ /**
564
+ * OS dependant way to pipe stderr to null
565
+ *
566
+ * @return string The exec argument to pipe stderr to null
567
+ */
568
+ function ignore_stderr() {
569
+
570
+ // If we're on Windows
571
+ if ( DIRECTORY_SEPARATOR == '\\' ) {
572
+ return '2>nul';
573
+ }
574
+
575
+ // Or Unix
576
+ return '2>/dev/null';
577
+
578
+ }
579
+
580
+ /**
581
+ * Return the contents of `$directory` as a single depth list ordered by total filesize.
582
+ *
583
+ * Will schedule background threads to recursively calculate the filesize of subdirectories.
584
+ * The total filesize of each directory and subdirectory is cached in a transient for 1 week.
585
+ *
586
+ * @param string $directory The directory to list
587
+ *
588
+ * @todo doesn't really belong in this class, should just be a function
589
+ * @return array returns an array of files ordered by filesize
590
+ */
591
+ function list_directory_by_total_filesize( $directory ) {
592
+
593
+ $files = $files_with_no_size = $empty_files = $files_with_size = $unreadable_files = array();
594
+
595
+ if ( ! is_dir( $directory ) ) {
596
+ return $files;
597
+ }
598
+
599
+ $finder = new \Symfony\Component\Finder\Finder();
600
+ $finder->followLinks();
601
+ $finder->ignoreDotFiles( false );
602
+ $finder->ignoreUnreadableDirs();
603
+ $finder->depth( '== 0' );
604
+
605
+ $site_size = new Site_Size;
606
+
607
+ $files = $finder->in( $directory );
608
+
609
+ foreach ( $files as $entry ) {
610
+
611
+ // Get the total filesize for each file and directory
612
+ $filesize = $site_size->filesize( $entry );
613
+
614
+ if ( $filesize ) {
615
+
616
+ // 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
617
+ while ( array_key_exists( $filesize, $files_with_size ) ) {
618
+ $filesize ++;
619
+ }
620
+
621
+ $files_with_size[ $filesize ] = $entry;
622
+
623
+ } elseif ( 0 === $filesize ) {
624
+
625
+ $empty_files[] = $entry;
626
+
627
+ } else {
628
+
629
+ $files_with_no_size[] = $entry;
630
+
631
+ }
632
+
633
+ }
634
+
635
+ // Sort files by filesize, largest first
636
+ krsort( $files_with_size );
637
+
638
+ // Add 0 byte files / directories to the bottom
639
+ $files = $files_with_size + array_merge( $empty_files, $unreadable_files );
640
+
641
+ // Add directories that are still calculating to the top
642
+ if ( $files_with_no_size ) {
643
+
644
+ // 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
645
+ foreach ( $files_with_no_size as $entry ) {
646
+ array_unshift( $files, $entry );
647
+ }
648
+ }
649
+
650
+ return $files;
651
+
652
+ }
functions/interface.php CHANGED
@@ -1,12 +1,14 @@
1
  <?php
2
 
 
 
3
  /**
4
  * Displays a row in the manage backups table
5
  *
6
  * @param string $file
7
- * @param HM\BackUpWordPress\Scheduled_Backup $schedule
8
  */
9
- function hmbkp_get_backup_row( $file, HM\BackUpWordPress\Scheduled_Backup $schedule ) {
10
 
11
  $encoded_file = urlencode( base64_encode( $file ) );
12
  $offset = get_option( 'gmt_offset' ) * 3600;
@@ -23,11 +25,11 @@ function hmbkp_get_backup_row( $file, HM\BackUpWordPress\Scheduled_Backup $sched
23
  <?php echo esc_html( size_format( @filesize( $file ) ) ); ?>
24
  </td>
25
 
26
- <td><?php echo esc_html( hmbkp_human_get_type( $file, $schedule ) ); ?></td>
27
 
28
  <td>
29
 
30
- <?php if ( hmbkp_is_path_accessible( hmbkp_path() ) ) : ?>
31
  <a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'hmbkp_backup_archive' => $encoded_file, 'hmbkp_schedule_id' => $schedule->get_id(), 'action' => 'hmbkp_request_download_backup' ), admin_url( 'admin-post.php' ) ), 'hmbkp_download_backup', 'hmbkp_download_backup_nonce' ) ); ?>" class="download-action"><?php _e( 'Download', 'backupwordpress' ); ?></a> |
32
  <?php endif; ?>
33
 
@@ -45,7 +47,7 @@ function hmbkp_get_backup_row( $file, HM\BackUpWordPress\Scheduled_Backup $sched
45
  *
46
  * @return void
47
  */
48
- function hmbkp_admin_notices() {
49
 
50
  $current_screen = get_current_screen();
51
 
@@ -58,7 +60,7 @@ function hmbkp_admin_notices() {
58
  return;
59
  }
60
 
61
- $notices = HM\BackUpWordPress\Notices::get_instance()->get_notices();
62
 
63
  if ( empty( $notices ) ) {
64
  return;
@@ -71,9 +73,6 @@ function hmbkp_admin_notices() {
71
  <div id="hmbkp-warning-backup" class="error notice is-dismissible">
72
  <p>
73
  <strong><?php _e( 'BackUpWordPress detected issues with your last backup.', 'backupwordpress' ); ?></strong>
74
- <a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'hmbkp_dismiss_error' ), admin_url( 'admin-post.php' ) ), 'hmbkp_dismiss_error', 'hmbkp_dismiss_error_nonce' ) ); ?>" style="float: right;" class="button">
75
- <?php _e( 'Dismiss', 'backupwordpress' ); ?>
76
- </a>
77
  </p>
78
 
79
  <ul>
@@ -126,9 +125,10 @@ function hmbkp_admin_notices() {
126
 
127
  <p><?php echo wp_kses_data( $msg ); ?></p>
128
 
129
- <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'backupwordpress' ); ?></span></button>
130
-
131
  <?php endforeach; ?>
 
 
 
132
  </div>
133
 
134
  <?php endif; ?>
@@ -140,16 +140,16 @@ function hmbkp_admin_notices() {
140
  <?php echo ob_get_clean();
141
 
142
  }
143
- add_action( 'admin_notices', 'hmbkp_admin_notices' );
144
- add_action( 'network_admin_notices', 'hmbkp_admin_notices' );
145
 
146
- function hmbkp_set_server_config_notices() {
147
 
148
- $notices = HM\BackUpWordPress\Notices::get_instance();
149
 
150
  $messages = array();
151
 
152
- if ( ! HM\BackUpWordPress\Backup::is_shell_exec_available() ) {
153
  $php_user = '<PHP USER>';
154
  $php_group = '<PHP GROUP>';
155
  } else {
@@ -158,15 +158,15 @@ function hmbkp_set_server_config_notices() {
158
  $php_group = reset( $groups );
159
  }
160
 
161
- if ( ! is_dir( hmbkp_path() ) ) {
162
- $messages[] = sprintf( __( 'The backups directory can\'t be created because your %1$s directory isn\'t writable. Run %2$s or %3$s or create the folder yourself.', 'backupwordpress' ), '<code>' . esc_html( dirname( hmbkp_path() ) ) . '</code>', '<code>chown ' . esc_html( $php_user ) . ':' . esc_html( $php_group ) . ' ' . esc_html( dirname( hmbkp_path() ) ) . '</code>', '<code>chmod 777 ' . esc_html( dirname( hmbkp_path() ) ) . '</code>' );
163
  }
164
 
165
- if ( is_dir( hmbkp_path() ) && ! wp_is_writable( hmbkp_path() ) ) {
166
- $messages[] = sprintf( __( 'Your backups directory isn\'t writable. Run %1$s or %2$s or set the permissions yourself.', 'backupwordpress' ), '<code>chown -R ' . esc_html( $php_user ) . ':' . esc_html( $php_group ) . ' ' . esc_html( hmbkp_path() ) . '</code>', '<code>chmod -R 777 ' . esc_html( hmbkp_path() ) . '</code>' );
167
  }
168
 
169
- if ( HM\BackUpWordPress\Backup::is_safe_mode_active() ) {
170
  $messages[] = sprintf( __( '%1$s is running in %2$s, please contact your host and ask them to disable it. BackUpWordPress may not work correctly whilst %3$s is on.', 'backupwordpress' ), '<code>PHP</code>', sprintf( '<a href="%1$s">%2$s</a>', __( 'http://php.net/manual/en/features.safe-mode.php', 'backupwordpress' ), __( 'Safe Mode', 'backupwordpress' ) ), '<code>' . __( 'Safe Mode', 'backupwordpress' ) . '</code>' );
171
  }
172
 
@@ -177,27 +177,25 @@ function hmbkp_set_server_config_notices() {
177
 
178
  $messages[] = sprintf( __( 'Your custom path does not exist', 'backupwordpress' ) );
179
 
180
- } elseif ( hmbkp_is_restricted_custom_path() ) {
181
 
182
  $messages[] = sprintf( __( 'Your custom path is unreachable due to a restriction set in your PHP configuration (open_basedir)', 'backupwordpress' ) );
183
 
184
  } else {
185
 
186
  if ( ! @is_dir( HMBKP_PATH ) ) {
187
- $messages[] = sprintf( __( 'Your custom backups directory %1$s doesn\'t exist and can\'t be created, your backups will be saved to %2$s instead.', 'backupwordpress' ), '<code>' . esc_html( HMBKP_PATH ) . '</code>', '<code>' . esc_html( hmbkp_path() ) . '</code>' );
188
  }
189
 
190
  if ( @is_dir( HMBKP_PATH ) && ! wp_is_writable( HMBKP_PATH ) ) {
191
- $messages[] = sprintf( __( 'Your custom backups directory %1$s isn\'t writable, new backups will be saved to %2$s instead.', 'backupwordpress' ), '<code>' . esc_html( HMBKP_PATH ) . '</code>', '<code>' . esc_html( hmbkp_path() ) . '</code>' );
192
 
193
  }
194
  }
195
  }
196
 
197
- $test_backup = new HM\BackUpWordPress\Backup();
198
-
199
- if ( ! is_readable( $test_backup->get_root() ) ) {
200
- $messages[] = sprintf( __( 'Your site root path %s isn\'t readable.', 'backupwordpress' ), '<code>' . $test_backup->get_root() . '</code>' );
201
  }
202
 
203
  if ( count( $messages ) > 0 ) {
@@ -205,7 +203,7 @@ function hmbkp_set_server_config_notices() {
205
  }
206
 
207
  }
208
- add_action( 'admin_init', 'hmbkp_set_server_config_notices' );
209
 
210
  /**
211
  * Hook in an change the plugin description when BackUpWordPress is activated
@@ -213,50 +211,29 @@ add_action( 'admin_init', 'hmbkp_set_server_config_notices' );
213
  * @param array $plugins
214
  * @return array $plugins
215
  */
216
- function hmbkp_plugin_row( $plugins ) {
217
 
218
  $menu = is_multisite() ? 'Settings' : 'Tools';
219
 
220
  if ( isset( $plugins[HMBKP_PLUGIN_SLUG . '/backupwordpress.php'] ) ) {
221
- $plugins[HMBKP_PLUGIN_SLUG . '/backupwordpress.php']['Description'] = str_replace( 'Once activated you\'ll find me under <strong>' . $menu . ' &rarr; Backups</strong>', 'Find me under <strong><a href="' . esc_url( hmbkp_get_settings_url() ) . '">' . $menu . ' &rarr; Backups</a></strong>', $plugins[HMBKP_PLUGIN_SLUG . '/backupwordpress.php']['Description'] );
222
  }
223
 
224
  return $plugins;
225
 
226
  }
227
 
228
- add_filter( 'all_plugins', 'hmbkp_plugin_row', 10 );
229
-
230
- /**
231
- * Parse the json string of errors and
232
- * output as a human readable message
233
- *
234
- * @access public
235
- * @return null
236
- */
237
- function hmbkp_backup_errors_message() {
238
-
239
- $message = '';
240
-
241
- foreach ( (array) json_decode( hmbkp_backup_errors() ) as $key => $errors ) {
242
- foreach ( $errors as $error ) {
243
- $message .= '<p><strong>' . esc_html( $key ) . '</strong>: <code>' . implode( ':', array_map( 'esc_html', (array) $error ) ) . '</code></p>';
244
- }
245
- }
246
-
247
- return $message;
248
-
249
- }
250
 
251
  /**
252
  * Get the human readable backup type in.
253
  *
254
  * @access public
255
  * @param string $type
256
- * @param HM\BackUpWordPress\Scheduled_Backup $schedule (default: null)
257
  * @return string
258
  */
259
- function hmbkp_human_get_type( $type, HM\BackUpWordPress\Scheduled_Backup $schedule = null ) {
260
 
261
  if ( strpos( $type, 'complete' ) !== false ) {
262
  return __( 'Database and Files', 'backupwordpress' );
@@ -271,7 +248,7 @@ function hmbkp_human_get_type( $type, HM\BackUpWordPress\Scheduled_Backup $sched
271
  }
272
 
273
  if ( ! is_null( $schedule ) ) {
274
- return hmbkp_human_get_type( $schedule->get_type() );
275
  }
276
 
277
  return __( 'Legacy', 'backupwordpress' );
@@ -282,16 +259,18 @@ function hmbkp_human_get_type( $type, HM\BackUpWordPress\Scheduled_Backup $sched
282
  * Display the row of actions for a schedule
283
  *
284
  * @access public
285
- * @param HM\BackUpWordPress\Scheduled_Backup $schedule
286
  * @return void
287
  */
288
- function hmbkp_schedule_status( HM\BackUpWordPress\Scheduled_Backup $schedule, $echo = true ) {
 
 
289
 
290
  ob_start(); ?>
291
 
292
- <span class="hmbkp-status"<?php if ( $schedule->get_status() ) { ?> title="<?php printf( __( 'Started %s ago', 'backupwordpress' ), human_time_diff( $schedule->get_schedule_running_start_time() ) ); ?>"<?php } ?>>
293
- <?php echo $schedule->get_status() ? wp_kses_data( $schedule->get_status() ) : __( 'Starting Backup', 'backupwordpress' ); ?>
294
- <a href="<?php echo hmbkp_admin_action_url( 'request_cancel_backup', array( 'hmbkp_schedule_id' => $schedule->get_id() ) ); ?>"><?php _e( 'cancel', 'backupwordpress' ); ?></a>
295
  </span>
296
 
297
  <?php $output = ob_get_clean();
@@ -304,37 +283,7 @@ function hmbkp_schedule_status( HM\BackUpWordPress\Scheduled_Backup $schedule, $
304
 
305
  }
306
 
307
- /**
308
- * Load the backup errors file
309
- *
310
- * @return string
311
- */
312
- function hmbkp_backup_errors() {
313
-
314
- if ( ! file_exists( hmbkp_path() . '/.backup_errors' ) ) {
315
- return '';
316
- }
317
-
318
- return file_get_contents( hmbkp_path() . '/.backup_errors' );
319
-
320
- }
321
-
322
- /**
323
- * Load the backup warnings file
324
- *
325
- * @return string
326
- */
327
- function hmbkp_backup_warnings() {
328
-
329
- if ( ! file_exists( hmbkp_path() . '/.backup_warnings' ) ) {
330
- return '';
331
- }
332
-
333
- return file_get_contents( hmbkp_path() . '/.backup_warnings' );
334
-
335
- }
336
-
337
- function hmbkp_backups_number( \HM\BackUpWordPress\Scheduled_Backup $schedule ) {
338
 
339
  $number = count( $schedule->get_backups() );
340
 
@@ -347,7 +296,7 @@ function hmbkp_backups_number( \HM\BackUpWordPress\Scheduled_Backup $schedule )
347
  echo apply_filters( 'hmbkp_backups_number', $output, $number );
348
  }
349
 
350
- function hmbkp_translated_schedule_title( $slug, $title ) {
351
 
352
  $titles = array(
353
  'complete-hourly' => esc_html__( 'Complete Hourly', 'backupwordpress' ),
@@ -381,13 +330,13 @@ function hmbkp_translated_schedule_title( $slug, $title ) {
381
 
382
  }
383
 
384
- function hmbkp_get_settings_url() {
385
 
386
  $url = is_multisite() ? network_admin_url( 'settings.php?page=' . HMBKP_PLUGIN_SLUG ) : admin_url( 'tools.php?page=' . HMBKP_PLUGIN_SLUG );
387
 
388
- HM\BackUpWordPress\schedules::get_instance()->refresh_schedules();
389
 
390
- if ( ! empty( $_REQUEST['hmbkp_schedule_id'] ) && HM\BackUpWordPress\schedules::get_instance()->get_schedule( sanitize_text_field( $_REQUEST['hmbkp_schedule_id'] ) ) ) {
391
  $url = add_query_arg( 'hmbkp_schedule_id', sanitize_text_field( $_REQUEST['hmbkp_schedule_id'] ), $url );
392
  }
393
 
@@ -400,7 +349,7 @@ function hmbkp_get_settings_url() {
400
  *
401
  * @param $error_message
402
  */
403
- function hmbkp_add_settings_error( $error_message ){
404
 
405
  $hmbkp_settings_errors = get_transient( 'hmbkp_settings_errors' );
406
 
@@ -413,12 +362,22 @@ function hmbkp_add_settings_error( $error_message ){
413
 
414
  }
415
 
 
 
 
 
 
 
 
 
 
 
416
  /**
417
  * Fetch the form submission errors for display.
418
  *
419
  * @return mixed
420
  */
421
- function hmbkp_get_settings_errors() {
422
  return get_transient( 'hmbkp_settings_errors' );
423
  }
424
 
@@ -427,11 +386,11 @@ function hmbkp_get_settings_errors() {
427
  *
428
  * @return bool
429
  */
430
- function hmbkp_clear_settings_errors(){
431
  return delete_transient( 'hmbkp_settings_errors' );
432
  }
433
 
434
- function hmbkp_is_restricted_custom_path() {
435
 
436
  $open_basedir = @ini_get( 'open_basedir' );
437
 
1
  <?php
2
 
3
+ namespace HM\BackUpWordPress;
4
+
5
  /**
6
  * Displays a row in the manage backups table
7
  *
8
  * @param string $file
9
+ * @param Scheduled_Backup $schedule
10
  */
11
+ function get_backup_row( $file, Scheduled_Backup $schedule ) {
12
 
13
  $encoded_file = urlencode( base64_encode( $file ) );
14
  $offset = get_option( 'gmt_offset' ) * 3600;
25
  <?php echo esc_html( size_format( @filesize( $file ) ) ); ?>
26
  </td>
27
 
28
+ <td><?php echo esc_html( human_get_type( $file, $schedule ) ); ?></td>
29
 
30
  <td>
31
 
32
+ <?php if ( is_path_accessible( Path::get_path() ) ) : ?>
33
  <a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'hmbkp_backup_archive' => $encoded_file, 'hmbkp_schedule_id' => $schedule->get_id(), 'action' => 'hmbkp_request_download_backup' ), admin_url( 'admin-post.php' ) ), 'hmbkp_download_backup', 'hmbkp_download_backup_nonce' ) ); ?>" class="download-action"><?php _e( 'Download', 'backupwordpress' ); ?></a> |
34
  <?php endif; ?>
35
 
47
  *
48
  * @return void
49
  */
50
+ function admin_notices() {
51
 
52
  $current_screen = get_current_screen();
53
 
60
  return;
61
  }
62
 
63
+ $notices = Notices::get_instance()->get_notices();
64
 
65
  if ( empty( $notices ) ) {
66
  return;
73
  <div id="hmbkp-warning-backup" class="error notice is-dismissible">
74
  <p>
75
  <strong><?php _e( 'BackUpWordPress detected issues with your last backup.', 'backupwordpress' ); ?></strong>
 
 
 
76
  </p>
77
 
78
  <ul>
125
 
126
  <p><?php echo wp_kses_data( $msg ); ?></p>
127
 
 
 
128
  <?php endforeach; ?>
129
+
130
+ <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'backupwordpress' ); ?></span></button>
131
+
132
  </div>
133
 
134
  <?php endif; ?>
140
  <?php echo ob_get_clean();
141
 
142
  }
143
+ add_action( 'admin_notices', 'HM\BackUpWordPress\admin_notices' );
144
+ add_action( 'network_admin_notices', 'HM\BackUpWordPress\admin_notices' );
145
 
146
+ function set_server_config_notices() {
147
 
148
+ $notices = Notices::get_instance();
149
 
150
  $messages = array();
151
 
152
+ if ( ! Backup_Utilities::is_exec_available() ) {
153
  $php_user = '<PHP USER>';
154
  $php_group = '<PHP GROUP>';
155
  } else {
158
  $php_group = reset( $groups );
159
  }
160
 
161
+ if ( ! is_dir( Path::get_path() ) ) {
162
+ $messages[] = sprintf( __( 'The backups directory can\'t be created because your %1$s directory isn\'t writable. Run %2$s or %3$s or create the folder yourself.', 'backupwordpress' ), '<code>' . esc_html( dirname( Path::get_path() ) ) . '</code>', '<code>chown ' . esc_html( $php_user ) . ':' . esc_html( $php_group ) . ' ' . esc_html( dirname( Path::get_path() ) ) . '</code>', '<code>chmod 777 ' . esc_html( dirname( Path::get_path() ) ) . '</code>' );
163
  }
164
 
165
+ if ( is_dir( Path::get_path() ) && ! wp_is_writable( Path::get_path() ) ) {
166
+ $messages[] = sprintf( __( 'Your backups directory isn\'t writable. Run %1$s or %2$s or set the permissions yourself.', 'backupwordpress' ), '<code>chown -R ' . esc_html( $php_user ) . ':' . esc_html( $php_group ) . ' ' . esc_html( Path::get_path() ) . '</code>', '<code>chmod -R 777 ' . esc_html( Path::get_path() ) . '</code>' );
167
  }
168
 
169
+ if ( Backup_Utilities::is_safe_mode_on() ) {
170
  $messages[] = sprintf( __( '%1$s is running in %2$s, please contact your host and ask them to disable it. BackUpWordPress may not work correctly whilst %3$s is on.', 'backupwordpress' ), '<code>PHP</code>', sprintf( '<a href="%1$s">%2$s</a>', __( 'http://php.net/manual/en/features.safe-mode.php', 'backupwordpress' ), __( 'Safe Mode', 'backupwordpress' ) ), '<code>' . __( 'Safe Mode', 'backupwordpress' ) . '</code>' );
171
  }
172
 
177
 
178
  $messages[] = sprintf( __( 'Your custom path does not exist', 'backupwordpress' ) );
179
 
180
+ } elseif ( is_restricted_custom_path() ) {
181
 
182
  $messages[] = sprintf( __( 'Your custom path is unreachable due to a restriction set in your PHP configuration (open_basedir)', 'backupwordpress' ) );
183
 
184
  } else {
185
 
186
  if ( ! @is_dir( HMBKP_PATH ) ) {
187
+ $messages[] = sprintf( __( 'Your custom backups directory %1$s doesn\'t exist and can\'t be created, your backups will be saved to %2$s instead.', 'backupwordpress' ), '<code>' . esc_html( HMBKP_PATH ) . '</code>', '<code>' . esc_html( Path::get_path() ) . '</code>' );
188
  }
189
 
190
  if ( @is_dir( HMBKP_PATH ) && ! wp_is_writable( HMBKP_PATH ) ) {
191
+ $messages[] = sprintf( __( 'Your custom backups directory %1$s isn\'t writable, new backups will be saved to %2$s instead.', 'backupwordpress' ), '<code>' . esc_html( HMBKP_PATH ) . '</code>', '<code>' . esc_html( Path::get_path() ) . '</code>' );
192
 
193
  }
194
  }
195
  }
196
 
197
+ if ( ! is_readable( Path::get_root() ) ) {
198
+ $messages[] = sprintf( __( 'Your site root path %s isn\'t readable.', 'backupwordpress' ), '<code>' . Path::get_root() . '</code>' );
 
 
199
  }
200
 
201
  if ( count( $messages ) > 0 ) {
203
  }
204
 
205
  }
206
+ add_action( 'admin_init', 'HM\BackUpWordPress\set_server_config_notices' );
207
 
208
  /**
209
  * Hook in an change the plugin description when BackUpWordPress is activated
211
  * @param array $plugins
212
  * @return array $plugins
213
  */
214
+ function plugin_row( $plugins ) {
215
 
216
  $menu = is_multisite() ? 'Settings' : 'Tools';
217
 
218
  if ( isset( $plugins[HMBKP_PLUGIN_SLUG . '/backupwordpress.php'] ) ) {
219
+ $plugins[HMBKP_PLUGIN_SLUG . '/backupwordpress.php']['Description'] = str_replace( 'Once activated you\'ll find me under <strong>' . $menu . ' &rarr; Backups</strong>', 'Find me under <strong><a href="' . esc_url( get_settings_url() ) . '">' . $menu . ' &rarr; Backups</a></strong>', $plugins[HMBKP_PLUGIN_SLUG . '/backupwordpress.php']['Description'] );
220
  }
221
 
222
  return $plugins;
223
 
224
  }
225
 
226
+ add_filter( 'all_plugins', 'HM\BackUpWordPress\plugin_row', 10 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  /**
229
  * Get the human readable backup type in.
230
  *
231
  * @access public
232
  * @param string $type
233
+ * @param Scheduled_Backup $schedule (default: null)
234
  * @return string
235
  */
236
+ function human_get_type( $type, Scheduled_Backup $schedule = null ) {
237
 
238
  if ( strpos( $type, 'complete' ) !== false ) {
239
  return __( 'Database and Files', 'backupwordpress' );
248
  }
249
 
250
  if ( ! is_null( $schedule ) ) {
251
+ return human_get_type( $schedule->get_type() );
252
  }
253
 
254
  return __( 'Legacy', 'backupwordpress' );
259
  * Display the row of actions for a schedule
260
  *
261
  * @access public
262
+ * @param Scheduled_Backup $schedule
263
  * @return void
264
  */
265
+ function schedule_status( Scheduled_Backup $schedule, $echo = true ) {
266
+
267
+ $status = new Backup_Status( $schedule->get_id() );
268
 
269
  ob_start(); ?>
270
 
271
+ <span class="hmbkp-status"<?php if ( $status->get_status() ) { ?> title="<?php printf( __( 'Started %s ago', 'backupwordpress' ), human_time_diff( $status->get_start_time() ) ); ?>"<?php } ?>>
272
+ <?php echo $status->get_status() ? wp_kses_data( $status->get_status() ) : __( 'Starting backup...', 'backupwordpress' ); ?>
273
+ <a href="<?php echo admin_action_url( 'request_cancel_backup', array( 'hmbkp_schedule_id' => $schedule->get_id() ) ); ?>"><?php _e( 'cancel', 'backupwordpress' ); ?></a>
274
  </span>
275
 
276
  <?php $output = ob_get_clean();
283
 
284
  }
285
 
286
+ function backups_number( Scheduled_Backup $schedule ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
  $number = count( $schedule->get_backups() );
289
 
296
  echo apply_filters( 'hmbkp_backups_number', $output, $number );
297
  }
298
 
299
+ function translated_schedule_title( $slug, $title ) {
300
 
301
  $titles = array(
302
  'complete-hourly' => esc_html__( 'Complete Hourly', 'backupwordpress' ),
330
 
331
  }
332
 
333
+ function get_settings_url() {
334
 
335
  $url = is_multisite() ? network_admin_url( 'settings.php?page=' . HMBKP_PLUGIN_SLUG ) : admin_url( 'tools.php?page=' . HMBKP_PLUGIN_SLUG );
336
 
337
+ schedules::get_instance()->refresh_schedules();
338
 
339
+ if ( ! empty( $_REQUEST['hmbkp_schedule_id'] ) && schedules::get_instance()->get_schedule( sanitize_text_field( $_REQUEST['hmbkp_schedule_id'] ) ) ) {
340
  $url = add_query_arg( 'hmbkp_schedule_id', sanitize_text_field( $_REQUEST['hmbkp_schedule_id'] ), $url );
341
  }
342
 
349
  *
350
  * @param $error_message
351
  */
352
+ function add_settings_error( $error_message ) {
353
 
354
  $hmbkp_settings_errors = get_transient( 'hmbkp_settings_errors' );
355
 
362
 
363
  }
364
 
365
+ /**
366
+ * Back compat version of add_settings_error
367
+ *
368
+ * @deprecated 3.4 add_settings_error()
369
+ */
370
+ function hmbkp_add_settings_error( $error_message ) {
371
+ _deprecated_function( __FUNCTION__, '3.4', 'add_settings_error()' );
372
+ add_settings_error( $error_message );
373
+ }
374
+
375
  /**
376
  * Fetch the form submission errors for display.
377
  *
378
  * @return mixed
379
  */
380
+ function get_settings_errors() {
381
  return get_transient( 'hmbkp_settings_errors' );
382
  }
383
 
386
  *
387
  * @return bool
388
  */
389
+ function clear_settings_errors(){
390
  return delete_transient( 'hmbkp_settings_errors' );
391
  }
392
 
393
+ function is_restricted_custom_path() {
394
 
395
  $open_basedir = @ini_get( 'open_basedir' );
396
 
languages/backupwordpress.pot CHANGED
@@ -1,78 +1,78 @@
1
- # Copyright (C) 2015 Human Made Limited
2
  # This file is distributed under the GPL-2+.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: BackUpWordPress 3.3.4\n"
6
  "Report-Msgid-Bugs-To: backupwordpress@hmn.md\n"
7
- "POT-Creation-Date: 2015-12-10 13:34:05+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=utf-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2015-MO-DA HO:MI+ZONE\n"
12
  "Last-Translator: Human Made Limited\n"
13
  "Language-Team: Human Made Limited\n"
14
- "X-Generator: grunt-wp-i18n 0.4.9\n"
15
 
16
- #: admin/actions.php:191
17
  msgid "The schedule ID was not provided. Aborting."
18
  msgstr ""
19
 
20
- #: admin/actions.php:241
21
  msgid "Backup type cannot be empty"
22
  msgstr ""
23
 
24
- #: admin/actions.php:245
25
  msgid "Invalid backup type"
26
  msgstr ""
27
 
28
- #: admin/actions.php:259
29
  msgid "Schedule cannot be empty"
30
  msgstr ""
31
 
32
- #: admin/actions.php:263
33
  msgid "Invalid schedule"
34
  msgstr ""
35
 
36
- #: admin/actions.php:277
37
  msgid "Day of the week must be a valid, lowercase day name"
38
  msgstr ""
39
 
40
- #: admin/actions.php:296
41
  msgid "Day of month must be between 1 and 31"
42
  msgstr ""
43
 
44
- #: admin/actions.php:315
45
  msgid "Hours must be between 0 and 23"
46
  msgstr ""
47
 
48
- #: admin/actions.php:334
49
  msgid "Minutes must be between 0 and 59"
50
  msgstr ""
51
 
52
- #: admin/actions.php:348
53
  msgid "Max backups can't be empty"
54
  msgstr ""
55
 
56
- #: admin/actions.php:352
57
  msgid "Max backups must be a number"
58
  msgstr ""
59
 
60
- #: admin/actions.php:356
61
  msgid "Max backups must be greater than 0"
62
  msgstr ""
63
 
64
- #: admin/actions.php:708 admin/actions.php:714
65
  msgid "BackUpWordPress has detected a problem."
66
  msgstr ""
67
 
68
- #: admin/actions.php:708
69
  msgid ""
70
  "%1$s is returning a %2$s response which could mean cron jobs aren't getting "
71
  "fired properly. BackUpWordPress relies on wp-cron to run scheduled backups. "
72
  "See the %3$s for more details."
73
  msgstr ""
74
 
75
- #: admin/actions.php:714
76
  msgid ""
77
  "%1$s is returning a %2$s response which could mean cron jobs aren't getting "
78
  "fired properly. BackUpWordPress relies on wp-cron to run scheduled backups, "
@@ -80,93 +80,93 @@ msgid ""
80
  "for manual backups. See the %3$s for more details."
81
  msgstr ""
82
 
83
- #: admin/backups-table.php:8 admin/schedule-form-excludes.php:102
84
  msgid "Size"
85
  msgstr ""
86
 
87
- #: admin/backups-table.php:9 admin/schedule-form-excludes.php:104
88
  msgid "Type"
89
  msgstr ""
90
 
91
- #: admin/backups-table.php:10
92
  msgid "Actions"
93
  msgstr ""
94
 
95
- #: admin/backups-table.php:35
96
  msgid "This is where your backups will appear once you have some."
97
  msgstr ""
98
 
99
- #: admin/backups.php:22
100
  msgid "add schedule"
101
  msgstr ""
102
 
103
- #: admin/constants.php:3
104
  msgid ""
105
  "You can %1$s any of the following %2$s in your %3$s to control advanced "
106
  "settings. %4$s. Defined %5$s will be highlighted."
107
  msgstr ""
108
 
109
- #: admin/constants.php:3 admin/menu.php:82
110
  msgid "Constants"
111
  msgstr ""
112
 
113
- #: admin/constants.php:3
114
  msgid "The Codex can help"
115
  msgstr ""
116
 
117
- #: admin/constants.php:14 admin/constants.php:30 admin/constants.php:46
118
- #: admin/constants.php:62 admin/constants.php:78 admin/constants.php:94
119
- #: admin/constants.php:110 classes/class-email-service.php:60
120
  msgid "You've set it to: %s"
121
  msgstr ""
122
 
123
- #: admin/constants.php:17
124
  msgid ""
125
  "The path to the folder you would like to store your backup files in, "
126
  "defaults to %s."
127
  msgstr ""
128
 
129
- #: admin/constants.php:17 admin/constants.php:33 admin/constants.php:49
130
- #: admin/constants.php:65 admin/constants.php:81 admin/constants.php:97
131
- #: admin/constants.php:113 classes/class-email-service.php:63
132
  msgid "e.g."
133
  msgstr ""
134
 
135
- #: admin/constants.php:33
136
  msgid ""
137
  "The path to your %1$s executable. Will be used for the %2$s part of the "
138
  "back up if available."
139
  msgstr ""
140
 
141
- #: admin/constants.php:33 admin/constants.php:49
142
  msgid "database"
143
  msgstr ""
144
 
145
- #: admin/constants.php:49
146
  msgid ""
147
  "The path to your %1$s executable. Will be used to zip up your %2$s and %3$s "
148
  "if available."
149
  msgstr ""
150
 
151
- #: admin/constants.php:49
152
  msgid "files"
153
  msgstr ""
154
 
155
- #: admin/constants.php:65
156
  msgid ""
157
  "Comma separated list of files or directories to exclude, the backups "
158
  "directory is automatically excluded."
159
  msgstr ""
160
 
161
- #: admin/constants.php:81
162
  msgid "The capability to use when calling %1$s. Defaults to %2$s."
163
  msgstr ""
164
 
165
- #: admin/constants.php:97
166
  msgid "The root directory that is backed up. Defaults to %s."
167
  msgstr ""
168
 
169
- #: admin/constants.php:113
170
  msgid "The time that your schedules should run. Defaults to %s."
171
  msgstr ""
172
 
@@ -366,315 +366,311 @@ msgid ""
366
  "emailing backupwordpress@hmn.md"
367
  msgstr ""
368
 
369
- #: admin/menu.php:13 admin/menu.php:17
370
  msgid "Manage Backups"
371
  msgstr ""
372
 
373
- #: admin/menu.php:13 admin/menu.php:17 admin/menu.php:46
374
  msgid "Backups"
375
  msgstr ""
376
 
377
- #: admin/menu.php:77
378
  msgid "FAQ"
379
  msgstr ""
380
 
381
- #: admin/menu.php:95
382
  msgid "Server Info"
383
  msgstr ""
384
 
385
- #: admin/menu.php:102
386
  msgid "For more information:"
387
  msgstr ""
388
 
389
- #: admin/menu.php:102
390
  msgid "Support Forums"
391
  msgstr ""
392
 
393
- #: admin/menu.php:102
394
  msgid "Help with translation"
395
  msgstr ""
396
 
397
- #: admin/page.php:7
398
  msgid "Support"
399
  msgstr ""
400
 
401
- #: admin/page.php:10
402
  msgid "Enable Support"
403
  msgstr ""
404
 
405
- #: admin/page.php:19
406
  msgid ""
407
  "If you're finding BackUpWordPress useful, please %1$s rate it on the plugin "
408
  "directory%2$s."
409
  msgstr ""
410
 
411
- #: admin/schedule-form-excludes.php:6
412
  msgid "Currently Excluded"
413
  msgstr ""
414
 
415
- #: admin/schedule-form-excludes.php:9
416
  msgid ""
417
  "We automatically detect and ignore common <abbr title=\"Version Control "
418
  "Systems\">VCS</abbr> folders and other backup plugin folders."
419
  msgstr ""
420
 
421
- #: admin/schedule-form-excludes.php:43
422
  msgid "Default rule"
423
  msgstr ""
424
 
425
- #: admin/schedule-form-excludes.php:47
426
  msgid "Defined in wp-config.php"
427
  msgstr ""
428
 
429
- #: admin/schedule-form-excludes.php:54
430
  msgid "Stop excluding"
431
  msgstr ""
432
 
433
- #: admin/schedule-form-excludes.php:70
434
  msgid "Your Site"
435
  msgstr ""
436
 
437
- #: admin/schedule-form-excludes.php:72
438
  msgid ""
439
  "Here's a directory listing of all files on your site, you can browse "
440
  "through and exclude files or folders that you don't want included in your "
441
  "backup."
442
  msgstr ""
443
 
444
- #: admin/schedule-form-excludes.php:101
445
  msgid "Name"
446
  msgstr ""
447
 
448
- #: admin/schedule-form-excludes.php:103
449
  msgid "Permissions"
450
  msgstr ""
451
 
452
- #: admin/schedule-form-excludes.php:105
453
  msgid "Status"
454
  msgstr ""
455
 
456
- #: admin/schedule-form-excludes.php:165 admin/schedule-form-excludes.php:283
457
  msgid "Refresh"
458
  msgstr ""
459
 
460
- #: admin/schedule-form-excludes.php:182 admin/schedule-form-excludes.php:307
461
  msgid "Symlink"
462
  msgstr ""
463
 
464
- #: admin/schedule-form-excludes.php:186 admin/schedule-form-excludes.php:311
465
  msgid "Folder"
466
  msgstr ""
467
 
468
- #: admin/schedule-form-excludes.php:281
469
  msgid "Recalculate the size of this directory"
470
  msgstr ""
471
 
472
- #: admin/schedule-form-excludes.php:315
473
  msgid "File"
474
  msgstr ""
475
 
476
- #: admin/schedule-form-excludes.php:326
477
  msgid "Unreadable files won't be backed up."
478
  msgstr ""
479
 
480
- #: admin/schedule-form-excludes.php:326
481
  msgid "Unreadable"
482
  msgstr ""
483
 
484
- #: admin/schedule-form-excludes.php:330
485
  msgid "Excluded"
486
  msgstr ""
487
 
488
- #: admin/schedule-form-excludes.php:346
489
  msgid "Exclude &rarr;"
490
  msgstr ""
491
 
492
- #: admin/schedule-form-excludes.php:359
493
  msgid "This folder is empty"
494
  msgstr ""
495
 
496
- #: admin/schedule-form-excludes.php:371 admin/schedule-form.php:195
497
- #: admin/schedule-settings.php:88
498
  msgid "Done"
499
  msgstr ""
500
 
501
- #: admin/schedule-form.php:1 admin/schedule-settings.php:9
502
  msgid "Settings"
503
  msgstr ""
504
 
505
- #: admin/schedule-form.php:36
506
  msgid "Backup"
507
  msgstr ""
508
 
509
- #: admin/schedule-form.php:43
510
  msgid "Both Database &amp; files"
511
  msgstr ""
512
 
513
- #: admin/schedule-form.php:45
514
  msgid "Files only"
515
  msgstr ""
516
 
517
- #: admin/schedule-form.php:47
518
  msgid "Database only"
519
  msgstr ""
520
 
521
- #: admin/schedule-form.php:58
522
  msgid "Schedule"
523
  msgstr ""
524
 
525
- #: admin/schedule-form.php:65
526
  msgid "Manual Only"
527
  msgstr ""
528
 
529
- #: admin/schedule-form.php:92
530
  msgid "Start Day"
531
  msgstr ""
532
 
533
- #: admin/schedule-form.php:100
534
  msgid "Monday"
535
  msgstr ""
536
 
537
- #: admin/schedule-form.php:101
538
  msgid "Tuesday"
539
  msgstr ""
540
 
541
- #: admin/schedule-form.php:102
542
  msgid "Wednesday"
543
  msgstr ""
544
 
545
- #: admin/schedule-form.php:103
546
  msgid "Thursday"
547
  msgstr ""
548
 
549
- #: admin/schedule-form.php:104
550
  msgid "Friday"
551
  msgstr ""
552
 
553
- #: admin/schedule-form.php:105
554
  msgid "Saturday"
555
  msgstr ""
556
 
557
- #: admin/schedule-form.php:106
558
  msgid "Sunday"
559
  msgstr ""
560
 
561
- #: admin/schedule-form.php:124
562
  msgid "Start Day of Month"
563
  msgstr ""
564
 
565
- #: admin/schedule-form.php:136
566
  msgid "Start Time"
567
  msgstr ""
568
 
569
- #: admin/schedule-form.php:145
570
  msgid "Hours"
571
  msgstr ""
572
 
573
- #: admin/schedule-form.php:149
574
  msgid "Minutes"
575
  msgstr ""
576
 
577
- #: admin/schedule-form.php:154
578
  msgid "24-hour format."
579
  msgstr ""
580
 
581
- #: admin/schedule-form.php:155
582
  msgid "The second backup will run 12 hours after the first."
583
  msgstr ""
584
 
585
- #: admin/schedule-form.php:165
586
  msgid "Number of backups to store on this server"
587
  msgstr ""
588
 
589
- #: admin/schedule-form.php:174
590
  msgid "Past this limit older backups will be deleted automatically."
591
  msgstr ""
592
 
593
- #: admin/schedule-form.php:177
594
  msgid "This schedule will store a maximum of %s of backups."
595
  msgstr ""
596
 
597
- #: admin/schedule-sentence.php:11
598
  msgid "The next backup will be on %1$s at %2$s %3$s"
599
  msgstr ""
600
 
601
- #: admin/schedule-sentence.php:18
602
  msgid "hourly on the hour"
603
  msgstr ""
604
 
605
- #: admin/schedule-sentence.php:18
606
  msgid "hourly at %s minutes past the hour"
607
  msgstr ""
608
 
609
- #: admin/schedule-sentence.php:24
610
  msgid "daily at %s"
611
  msgstr ""
612
 
613
- #: admin/schedule-sentence.php:35
614
  msgid "every 12 hours at %1$s &amp; %2$s"
615
  msgstr ""
616
 
617
- #: admin/schedule-sentence.php:41
618
  msgid "weekly on %1$s at %2$s"
619
  msgstr ""
620
 
621
- #: admin/schedule-sentence.php:47
622
  msgid "every two weeks on %1$s at %2$s"
623
  msgstr ""
624
 
625
- #: admin/schedule-sentence.php:53
626
  msgid "on the %1$s of each month at %2$s"
627
  msgstr ""
628
 
629
- #: admin/schedule-sentence.php:59 admin/schedule-sentence.php:65
630
  msgid "manually"
631
  msgstr ""
632
 
633
- #: admin/schedule-sentence.php:70
634
  msgid "this server"
635
  msgstr ""
636
 
637
- #: admin/schedule-sentence.php:78
638
  msgid "store the most recent backup in %s"
639
  msgstr ""
640
 
641
- #: admin/schedule-sentence.php:84
642
  msgid "don't store any backups in on this server"
643
  msgstr ""
644
 
645
- #: admin/schedule-sentence.php:90
646
  msgid "store the last %1$s backups in %2$s"
647
  msgstr ""
648
 
649
- #: admin/schedule-sentence.php:121
650
- msgid "%s. "
651
- msgstr ""
652
-
653
- #: admin/schedule-sentence.php:125
654
  msgid "Send a copy of each backup to %s."
655
  msgstr ""
656
 
657
- #: admin/schedule-sentence.php:156
658
  msgid "Backups will be compressed and should be smaller than this."
659
  msgstr ""
660
 
661
- #: admin/schedule-sentence.php:160
662
  msgid "this shouldn't take long&hellip;"
663
  msgstr ""
664
 
665
- #: admin/schedule-sentence.php:160
666
- msgid "calculating the size of your backup&hellip;"
667
  msgstr ""
668
 
669
- #: admin/schedule-settings.php:7
670
  msgid "Run now"
671
  msgstr ""
672
 
673
- #: admin/schedule-settings.php:15
674
  msgid "Excludes"
675
  msgstr ""
676
 
677
- #: admin/schedule-settings.php:27 functions/interface.php:34
678
  msgid "Delete"
679
  msgstr ""
680
 
@@ -696,79 +692,35 @@ msgstr ""
696
  msgid "BackUpWordPress Error"
697
  msgstr ""
698
 
699
- #: classes/class-backup.php:341
700
- msgid "archive filename must be a non-empty string"
701
  msgstr ""
702
 
703
- #: classes/class-backup.php:345
704
- msgid "invalid file extension for archive filename <code>%s</code>"
705
  msgstr ""
706
 
707
- #: classes/class-backup.php:386
708
- msgid "database dump filename must be a non-empty string"
709
- msgstr ""
710
-
711
- #: classes/class-backup.php:390
712
- msgid "invalid file extension for database dump filename <code>%s</code>"
713
- msgstr ""
714
-
715
- #: classes/class-backup.php:424
716
- msgid "Invalid root path <code>%s</code> must be a valid directory path"
717
- msgstr ""
718
-
719
- #: classes/class-backup.php:450
720
- msgid ""
721
- "Invalid existing archive filepath <code>%s</code> must be a non-empty "
722
- "(string)"
723
- msgstr ""
724
-
725
- #: classes/class-backup.php:505
726
- msgid ""
727
- "Invalid backup type <code>%s</code> must be one of (string) file, database "
728
- "or complete"
729
- msgstr ""
730
-
731
- #: classes/class-backup.php:935
732
- msgid "mysqldump fallback error %s"
733
- msgstr ""
734
-
735
- #: classes/class-backup.php:957
736
- msgid "Zip command is not available."
737
- msgstr ""
738
-
739
- #: classes/class-backup.php:964
740
- msgid "ZipArchive method is not available."
741
- msgstr ""
742
-
743
- #: classes/class-backup.php:968 classes/class-backup.php:980
744
- msgid "No valid archive method found."
745
- msgstr ""
746
-
747
- #: classes/class-backup.php:1659
748
- msgid "Could not connect to mysql"
749
- msgstr ""
750
-
751
- #: classes/class-backupwordpress-wp-cli-command.php:50
752
  msgid "Backup: Dumping database..."
753
  msgstr ""
754
 
755
- #: classes/class-backupwordpress-wp-cli-command.php:54
756
  msgid "Backup: Zipping everything up..."
757
  msgstr ""
758
 
759
- #: classes/class-backupwordpress-wp-cli-command.php:70
760
  msgid "Invalid backup path"
761
  msgstr ""
762
 
763
- #: classes/class-backupwordpress-wp-cli-command.php:75
764
  msgid "Invalid root path"
765
  msgstr ""
766
 
767
- #: classes/class-backupwordpress-wp-cli-command.php:106
768
  msgid "Backup Complete: "
769
  msgstr ""
770
 
771
- #: classes/class-backupwordpress-wp-cli-command.php:108
772
  msgid "Backup Failed"
773
  msgstr ""
774
 
@@ -847,114 +799,79 @@ msgstr ""
847
  msgid "Unfortunately, the backup file was too large to attach to this email."
848
  msgstr ""
849
 
850
- #: classes/class-path.php:249
851
  msgid "This %s file ensures that other people cannot download your backup files."
852
  msgstr ""
853
 
854
- #: classes/class-plugin.php:214
855
  msgid "Update"
856
  msgstr ""
857
 
858
- #: classes/class-plugin.php:215
859
  msgid "Cancel"
860
  msgstr ""
861
 
862
- #: classes/class-plugin.php:216
863
  msgid ""
864
  "Are you sure you want to delete this schedule? All of its backups will also "
865
  "be deleted."
866
  msgstr ""
867
 
868
- #: classes/class-plugin.php:216 classes/class-plugin.php:217
869
- #: classes/class-plugin.php:218 classes/class-plugin.php:219
870
  msgid "'Cancel' to go back, 'OK' to delete."
871
  msgstr ""
872
 
873
- #: classes/class-plugin.php:217
874
  msgid "Are you sure you want to delete this backup?"
875
  msgstr ""
876
 
877
- #: classes/class-plugin.php:218
878
  msgid "Are you sure you want to remove this exclude rule?"
879
  msgstr ""
880
 
881
- #: classes/class-plugin.php:219
882
  msgid ""
883
  "Reducing the number of backups that are stored on this server will cause "
884
  "some of your existing backups to be deleted. Are you sure that's what you "
885
  "want?"
886
  msgstr ""
887
 
888
- #: classes/class-schedule.php:150
889
- msgid "Invalid Option Name"
890
- msgstr ""
891
-
892
- #: classes/class-schedule.php:261
893
- msgid "Argument 1 for %s must be a valid integer"
894
- msgstr ""
895
-
896
- #: classes/class-schedule.php:607
897
- msgid "Argument 1 for %s must be a valid future timestamp"
898
- msgstr ""
899
-
900
- #: classes/class-schedule.php:644
901
  msgid "Argument 1 for %s must be a valid cron recurrence or \"manually\""
902
  msgstr ""
903
 
904
- #: classes/class-schedule.php:763 functions/interface.php:293
905
- msgid "Starting Backup"
906
  msgstr ""
907
 
908
- #: classes/class-schedule.php:850
909
- msgid "Error writing to file. (%s)"
 
910
  msgstr ""
911
 
912
- #: classes/class-schedule.php:901
913
- msgid "Dumping Database %s"
914
- msgstr ""
915
-
916
- #: classes/class-schedule.php:906
917
- msgid "Verifying Database Dump %s"
918
- msgstr ""
919
-
920
- #: classes/class-schedule.php:911
921
- msgid "Creating zip archive %s"
922
- msgstr ""
923
-
924
- #: classes/class-schedule.php:916
925
- msgid "Verifying Zip Archive %s"
926
- msgstr ""
927
-
928
- #: classes/class-schedule.php:921
929
- msgid "Finishing Backup"
930
- msgstr ""
931
-
932
- #: classes/class-schedule.php:972
933
- msgid "An unexpected error occurred"
934
- msgstr ""
935
-
936
- #: classes/class-schedule.php:1035
937
  #. translators: min=minute
938
  msgid "%s min"
939
  msgid_plural "%s mins"
940
  msgstr[0] ""
941
  msgstr[1] ""
942
 
943
- #: classes/class-schedule.php:1045
944
  msgid "%s hour"
945
  msgid_plural "%s hours"
946
  msgstr[0] ""
947
  msgstr[1] ""
948
 
949
- #: classes/class-schedule.php:1107
950
  msgid "Argument 1 for %s must be a non-empty string"
951
  msgstr ""
952
 
953
- #: classes/class-schedule.php:1112
954
  msgid "%s doesn't exist"
955
  msgstr ""
956
 
957
- #: classes/class-schedule.php:1117
958
  msgid "That backup wasn't created by this schedule"
959
  msgstr ""
960
 
@@ -984,58 +901,54 @@ msgstr ""
984
  msgid "Error: %s"
985
  msgstr ""
986
 
987
- #: functions/core.php:326
988
  msgid "BackUpWordPress has set up your default schedules."
989
  msgstr ""
990
 
991
- #: functions/core.php:326
992
  msgid ""
993
  "By default BackUpWordPress performs a daily backup of your database and a "
994
  "weekly backup of your database &amp; files. You can modify these schedules."
995
  msgstr ""
996
 
997
- #: functions/core.php:342
998
  msgid "Once Hourly"
999
  msgstr ""
1000
 
1001
- #: functions/core.php:343
1002
  msgid "Twice Daily"
1003
  msgstr ""
1004
 
1005
- #: functions/core.php:344
1006
  msgid "Once Daily"
1007
  msgstr ""
1008
 
1009
- #: functions/core.php:345
1010
  msgid "Once Weekly"
1011
  msgstr ""
1012
 
1013
- #: functions/core.php:346
1014
  msgid "Once Every Two Weeks"
1015
  msgstr ""
1016
 
1017
- #: functions/core.php:347
1018
  msgid "Once Monthly"
1019
  msgstr ""
1020
 
1021
- #: functions/core.php:366
1022
  msgid "You can only delete directories inside your WordPress installation"
1023
  msgstr ""
1024
 
1025
- #: functions/interface.php:31
1026
  msgid "Download"
1027
  msgstr ""
1028
 
1029
- #: functions/interface.php:73
1030
- msgid "BackUpWordPress detected issues with your last backup."
1031
- msgstr ""
1032
-
1033
  #: functions/interface.php:75
1034
- msgid "Dismiss"
1035
  msgstr ""
1036
 
1037
- #: functions/interface.php:89 functions/interface.php:109
1038
- #: functions/interface.php:129
1039
  msgid "Dismiss this notice."
1040
  msgstr ""
1041
 
@@ -1087,119 +1000,119 @@ msgid ""
1087
  "saved to %2$s instead."
1088
  msgstr ""
1089
 
1090
- #: functions/interface.php:200
1091
  msgid "Your site root path %s isn't readable."
1092
  msgstr ""
1093
 
1094
- #: functions/interface.php:262
1095
  msgid "Database and Files"
1096
  msgstr ""
1097
 
1098
- #: functions/interface.php:266
1099
  msgid "Files"
1100
  msgstr ""
1101
 
1102
- #: functions/interface.php:270
1103
  msgid "Database"
1104
  msgstr ""
1105
 
1106
- #: functions/interface.php:277
1107
  msgid "Legacy"
1108
  msgstr ""
1109
 
1110
- #: functions/interface.php:292
1111
  msgid "Started %s ago"
1112
  msgstr ""
1113
 
1114
- #: functions/interface.php:294
1115
  msgid "cancel"
1116
  msgstr ""
1117
 
1118
- #: functions/interface.php:342
1119
  msgid "No backups completed"
1120
  msgstr ""
1121
 
1122
- #: functions/interface.php:353
1123
  msgid "Complete Hourly"
1124
  msgstr ""
1125
 
1126
- #: functions/interface.php:354
1127
  msgid "File Hourly"
1128
  msgstr ""
1129
 
1130
- #: functions/interface.php:355
1131
  msgid "Database Hourly"
1132
  msgstr ""
1133
 
1134
- #: functions/interface.php:356
1135
  msgid "Complete Twice Daily"
1136
  msgstr ""
1137
 
1138
- #: functions/interface.php:357
1139
  msgid "File Twice Daily"
1140
  msgstr ""
1141
 
1142
- #: functions/interface.php:358
1143
  msgid "Database Twice Daily"
1144
  msgstr ""
1145
 
1146
- #: functions/interface.php:359
1147
  msgid "Complete Daily"
1148
  msgstr ""
1149
 
1150
- #: functions/interface.php:360
1151
  msgid "File Daily"
1152
  msgstr ""
1153
 
1154
- #: functions/interface.php:361
1155
  msgid "Database Daily"
1156
  msgstr ""
1157
 
1158
- #: functions/interface.php:362
1159
  msgid "Complete Weekly"
1160
  msgstr ""
1161
 
1162
- #: functions/interface.php:363
1163
  msgid "File Weekly"
1164
  msgstr ""
1165
 
1166
- #: functions/interface.php:364
1167
  msgid "Database Weekly"
1168
  msgstr ""
1169
 
1170
- #: functions/interface.php:365
1171
  msgid "Complete Every Two Weeks"
1172
  msgstr ""
1173
 
1174
- #: functions/interface.php:366
1175
  msgid "File Every Two Weeks"
1176
  msgstr ""
1177
 
1178
- #: functions/interface.php:367
1179
  msgid "Database Every Two Weeks"
1180
  msgstr ""
1181
 
1182
- #: functions/interface.php:368
1183
  msgid "Complete Monthly"
1184
  msgstr ""
1185
 
1186
- #: functions/interface.php:369
1187
  msgid "File Monthly"
1188
  msgstr ""
1189
 
1190
- #: functions/interface.php:370
1191
  msgid "Database Monthly"
1192
  msgstr ""
1193
 
1194
- #: functions/interface.php:371
1195
  msgid "Complete Manually"
1196
  msgstr ""
1197
 
1198
- #: functions/interface.php:372
1199
  msgid "File Manually"
1200
  msgstr ""
1201
 
1202
- #: functions/interface.php:373
1203
  msgid "Database Manually"
1204
  msgstr ""
1205
 
@@ -1222,14 +1135,14 @@ msgstr ""
1222
  msgid "http://hmn.md/"
1223
  msgstr ""
1224
 
1225
- #: admin/schedule-sentence.php:118
1226
  msgctxt ""
1227
  "1: Backup Type 2: Total size of backup 3: Schedule 4: Number of backups to "
1228
  "store"
1229
  msgid "Backup my %1$s %2$s %3$s, %4$s."
1230
  msgstr ""
1231
 
1232
- #: functions/interface.php:344
1233
  msgctxt "backups count"
1234
  msgid "One backup completed"
1235
  msgid_plural "%1$s backups completed"
1
+ # Copyright (C) 2016 Human Made Limited
2
  # This file is distributed under the GPL-2+.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: BackUpWordPress 3.4.0\n"
6
  "Report-Msgid-Bugs-To: backupwordpress@hmn.md\n"
7
+ "POT-Creation-Date: 2016-01-20 16:44:43+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=utf-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n"
12
  "Last-Translator: Human Made Limited\n"
13
  "Language-Team: Human Made Limited\n"
14
+ "X-Generator: grunt-wp-i18n 0.5.3\n"
15
 
16
+ #: admin/actions.php:180
17
  msgid "The schedule ID was not provided. Aborting."
18
  msgstr ""
19
 
20
+ #: admin/actions.php:236
21
  msgid "Backup type cannot be empty"
22
  msgstr ""
23
 
24
+ #: admin/actions.php:240
25
  msgid "Invalid backup type"
26
  msgstr ""
27
 
28
+ #: admin/actions.php:254
29
  msgid "Schedule cannot be empty"
30
  msgstr ""
31
 
32
+ #: admin/actions.php:258
33
  msgid "Invalid schedule"
34
  msgstr ""
35
 
36
+ #: admin/actions.php:272
37
  msgid "Day of the week must be a valid, lowercase day name"
38
  msgstr ""
39
 
40
+ #: admin/actions.php:291
41
  msgid "Day of month must be between 1 and 31"
42
  msgstr ""
43
 
44
+ #: admin/actions.php:310
45
  msgid "Hours must be between 0 and 23"
46
  msgstr ""
47
 
48
+ #: admin/actions.php:329
49
  msgid "Minutes must be between 0 and 59"
50
  msgstr ""
51
 
52
+ #: admin/actions.php:343
53
  msgid "Max backups can't be empty"
54
  msgstr ""
55
 
56
+ #: admin/actions.php:347
57
  msgid "Max backups must be a number"
58
  msgstr ""
59
 
60
+ #: admin/actions.php:351
61
  msgid "Max backups must be greater than 0"
62
  msgstr ""
63
 
64
+ #: admin/actions.php:695 admin/actions.php:701
65
  msgid "BackUpWordPress has detected a problem."
66
  msgstr ""
67
 
68
+ #: admin/actions.php:695
69
  msgid ""
70
  "%1$s is returning a %2$s response which could mean cron jobs aren't getting "
71
  "fired properly. BackUpWordPress relies on wp-cron to run scheduled backups. "
72
  "See the %3$s for more details."
73
  msgstr ""
74
 
75
+ #: admin/actions.php:701
76
  msgid ""
77
  "%1$s is returning a %2$s response which could mean cron jobs aren't getting "
78
  "fired properly. BackUpWordPress relies on wp-cron to run scheduled backups, "
80
  "for manual backups. See the %3$s for more details."
81
  msgstr ""
82
 
83
+ #: admin/backups-table.php:14 admin/schedule-form-excludes.php:109
84
  msgid "Size"
85
  msgstr ""
86
 
87
+ #: admin/backups-table.php:15 admin/schedule-form-excludes.php:111
88
  msgid "Type"
89
  msgstr ""
90
 
91
+ #: admin/backups-table.php:16
92
  msgid "Actions"
93
  msgstr ""
94
 
95
+ #: admin/backups-table.php:41
96
  msgid "This is where your backups will appear once you have some."
97
  msgstr ""
98
 
99
+ #: admin/backups.php:25
100
  msgid "add schedule"
101
  msgstr ""
102
 
103
+ #: admin/constants.php:9
104
  msgid ""
105
  "You can %1$s any of the following %2$s in your %3$s to control advanced "
106
  "settings. %4$s. Defined %5$s will be highlighted."
107
  msgstr ""
108
 
109
+ #: admin/constants.php:9 admin/menu.php:80
110
  msgid "Constants"
111
  msgstr ""
112
 
113
+ #: admin/constants.php:9
114
  msgid "The Codex can help"
115
  msgstr ""
116
 
117
+ #: admin/constants.php:20 admin/constants.php:36 admin/constants.php:52
118
+ #: admin/constants.php:68 admin/constants.php:84 admin/constants.php:100
119
+ #: admin/constants.php:116 classes/class-email-service.php:60
120
  msgid "You've set it to: %s"
121
  msgstr ""
122
 
123
+ #: admin/constants.php:23
124
  msgid ""
125
  "The path to the folder you would like to store your backup files in, "
126
  "defaults to %s."
127
  msgstr ""
128
 
129
+ #: admin/constants.php:23 admin/constants.php:39 admin/constants.php:55
130
+ #: admin/constants.php:71 admin/constants.php:87 admin/constants.php:103
131
+ #: admin/constants.php:119 classes/class-email-service.php:63
132
  msgid "e.g."
133
  msgstr ""
134
 
135
+ #: admin/constants.php:39
136
  msgid ""
137
  "The path to your %1$s executable. Will be used for the %2$s part of the "
138
  "back up if available."
139
  msgstr ""
140
 
141
+ #: admin/constants.php:39 admin/constants.php:55
142
  msgid "database"
143
  msgstr ""
144
 
145
+ #: admin/constants.php:55
146
  msgid ""
147
  "The path to your %1$s executable. Will be used to zip up your %2$s and %3$s "
148
  "if available."
149
  msgstr ""
150
 
151
+ #: admin/constants.php:55
152
  msgid "files"
153
  msgstr ""
154
 
155
+ #: admin/constants.php:71
156
  msgid ""
157
  "Comma separated list of files or directories to exclude, the backups "
158
  "directory is automatically excluded."
159
  msgstr ""
160
 
161
+ #: admin/constants.php:87
162
  msgid "The capability to use when calling %1$s. Defaults to %2$s."
163
  msgstr ""
164
 
165
+ #: admin/constants.php:103
166
  msgid "The root directory that is backed up. Defaults to %s."
167
  msgstr ""
168
 
169
+ #: admin/constants.php:119
170
  msgid "The time that your schedules should run. Defaults to %s."
171
  msgstr ""
172
 
366
  "emailing backupwordpress@hmn.md"
367
  msgstr ""
368
 
369
+ #: admin/menu.php:14 admin/menu.php:16
370
  msgid "Manage Backups"
371
  msgstr ""
372
 
373
+ #: admin/menu.php:14 admin/menu.php:16 admin/menu.php:44
374
  msgid "Backups"
375
  msgstr ""
376
 
377
+ #: admin/menu.php:75
378
  msgid "FAQ"
379
  msgstr ""
380
 
381
+ #: admin/menu.php:93
382
  msgid "Server Info"
383
  msgstr ""
384
 
385
+ #: admin/menu.php:100
386
  msgid "For more information:"
387
  msgstr ""
388
 
389
+ #: admin/menu.php:100
390
  msgid "Support Forums"
391
  msgstr ""
392
 
393
+ #: admin/menu.php:100
394
  msgid "Help with translation"
395
  msgstr ""
396
 
397
+ #: admin/page.php:13
398
  msgid "Support"
399
  msgstr ""
400
 
401
+ #: admin/page.php:16
402
  msgid "Enable Support"
403
  msgstr ""
404
 
405
+ #: admin/page.php:25
406
  msgid ""
407
  "If you're finding BackUpWordPress useful, please %1$s rate it on the plugin "
408
  "directory%2$s."
409
  msgstr ""
410
 
411
+ #: admin/schedule-form-excludes.php:11
412
  msgid "Currently Excluded"
413
  msgstr ""
414
 
415
+ #: admin/schedule-form-excludes.php:14
416
  msgid ""
417
  "We automatically detect and ignore common <abbr title=\"Version Control "
418
  "Systems\">VCS</abbr> folders and other backup plugin folders."
419
  msgstr ""
420
 
421
+ #: admin/schedule-form-excludes.php:50
422
  msgid "Default rule"
423
  msgstr ""
424
 
425
+ #: admin/schedule-form-excludes.php:54
426
  msgid "Defined in wp-config.php"
427
  msgstr ""
428
 
429
+ #: admin/schedule-form-excludes.php:61
430
  msgid "Stop excluding"
431
  msgstr ""
432
 
433
+ #: admin/schedule-form-excludes.php:75
434
  msgid "Your Site"
435
  msgstr ""
436
 
437
+ #: admin/schedule-form-excludes.php:77
438
  msgid ""
439
  "Here's a directory listing of all files on your site, you can browse "
440
  "through and exclude files or folders that you don't want included in your "
441
  "backup."
442
  msgstr ""
443
 
444
+ #: admin/schedule-form-excludes.php:108
445
  msgid "Name"
446
  msgstr ""
447
 
448
+ #: admin/schedule-form-excludes.php:110
449
  msgid "Permissions"
450
  msgstr ""
451
 
452
+ #: admin/schedule-form-excludes.php:112
453
  msgid "Status"
454
  msgstr ""
455
 
456
+ #: admin/schedule-form-excludes.php:172 admin/schedule-form-excludes.php:290
457
  msgid "Refresh"
458
  msgstr ""
459
 
460
+ #: admin/schedule-form-excludes.php:189 admin/schedule-form-excludes.php:314
461
  msgid "Symlink"
462
  msgstr ""
463
 
464
+ #: admin/schedule-form-excludes.php:193 admin/schedule-form-excludes.php:318
465
  msgid "Folder"
466
  msgstr ""
467
 
468
+ #: admin/schedule-form-excludes.php:288
469
  msgid "Recalculate the size of this directory"
470
  msgstr ""
471
 
472
+ #: admin/schedule-form-excludes.php:322
473
  msgid "File"
474
  msgstr ""
475
 
476
+ #: admin/schedule-form-excludes.php:333
477
  msgid "Unreadable files won't be backed up."
478
  msgstr ""
479
 
480
+ #: admin/schedule-form-excludes.php:333
481
  msgid "Unreadable"
482
  msgstr ""
483
 
484
+ #: admin/schedule-form-excludes.php:337
485
  msgid "Excluded"
486
  msgstr ""
487
 
488
+ #: admin/schedule-form-excludes.php:353
489
  msgid "Exclude &rarr;"
490
  msgstr ""
491
 
492
+ #: admin/schedule-form-excludes.php:366
493
  msgid "This folder is empty"
494
  msgstr ""
495
 
496
+ #: admin/schedule-form-excludes.php:378 admin/schedule-form.php:204
497
+ #: admin/schedule-settings.php:90
498
  msgid "Done"
499
  msgstr ""
500
 
501
+ #: admin/schedule-form.php:7 admin/schedule-settings.php:11
502
  msgid "Settings"
503
  msgstr ""
504
 
505
+ #: admin/schedule-form.php:42
506
  msgid "Backup"
507
  msgstr ""
508
 
509
+ #: admin/schedule-form.php:49
510
  msgid "Both Database &amp; files"
511
  msgstr ""
512
 
513
+ #: admin/schedule-form.php:51
514
  msgid "Files only"
515
  msgstr ""
516
 
517
+ #: admin/schedule-form.php:53
518
  msgid "Database only"
519
  msgstr ""
520
 
521
+ #: admin/schedule-form.php:64
522
  msgid "Schedule"
523
  msgstr ""
524
 
525
+ #: admin/schedule-form.php:71
526
  msgid "Manual Only"
527
  msgstr ""
528
 
529
+ #: admin/schedule-form.php:98
530
  msgid "Start Day"
531
  msgstr ""
532
 
533
+ #: admin/schedule-form.php:106
534
  msgid "Monday"
535
  msgstr ""
536
 
537
+ #: admin/schedule-form.php:107
538
  msgid "Tuesday"
539
  msgstr ""
540
 
541
+ #: admin/schedule-form.php:108
542
  msgid "Wednesday"
543
  msgstr ""
544
 
545
+ #: admin/schedule-form.php:109
546
  msgid "Thursday"
547
  msgstr ""
548
 
549
+ #: admin/schedule-form.php:110
550
  msgid "Friday"
551
  msgstr ""
552
 
553
+ #: admin/schedule-form.php:111
554
  msgid "Saturday"
555
  msgstr ""
556
 
557
+ #: admin/schedule-form.php:112
558
  msgid "Sunday"
559
  msgstr ""
560
 
561
+ #: admin/schedule-form.php:130
562
  msgid "Start Day of Month"
563
  msgstr ""
564
 
565
+ #: admin/schedule-form.php:142
566
  msgid "Start Time"
567
  msgstr ""
568
 
569
+ #: admin/schedule-form.php:151
570
  msgid "Hours"
571
  msgstr ""
572
 
573
+ #: admin/schedule-form.php:155
574
  msgid "Minutes"
575
  msgstr ""
576
 
577
+ #: admin/schedule-form.php:160
578
  msgid "24-hour format."
579
  msgstr ""
580
 
581
+ #: admin/schedule-form.php:161
582
  msgid "The second backup will run 12 hours after the first."
583
  msgstr ""
584
 
585
+ #: admin/schedule-form.php:171
586
  msgid "Number of backups to store on this server"
587
  msgstr ""
588
 
589
+ #: admin/schedule-form.php:180
590
  msgid "Past this limit older backups will be deleted automatically."
591
  msgstr ""
592
 
593
+ #: admin/schedule-form.php:187
594
  msgid "This schedule will store a maximum of %s of backups."
595
  msgstr ""
596
 
597
+ #: admin/schedule-sentence.php:14
598
  msgid "The next backup will be on %1$s at %2$s %3$s"
599
  msgstr ""
600
 
601
+ #: admin/schedule-sentence.php:24
602
  msgid "hourly on the hour"
603
  msgstr ""
604
 
605
+ #: admin/schedule-sentence.php:24
606
  msgid "hourly at %s minutes past the hour"
607
  msgstr ""
608
 
609
+ #: admin/schedule-sentence.php:30
610
  msgid "daily at %s"
611
  msgstr ""
612
 
613
+ #: admin/schedule-sentence.php:41
614
  msgid "every 12 hours at %1$s &amp; %2$s"
615
  msgstr ""
616
 
617
+ #: admin/schedule-sentence.php:47
618
  msgid "weekly on %1$s at %2$s"
619
  msgstr ""
620
 
621
+ #: admin/schedule-sentence.php:53
622
  msgid "every two weeks on %1$s at %2$s"
623
  msgstr ""
624
 
625
+ #: admin/schedule-sentence.php:59
626
  msgid "on the %1$s of each month at %2$s"
627
  msgstr ""
628
 
629
+ #: admin/schedule-sentence.php:65 admin/schedule-sentence.php:71
630
  msgid "manually"
631
  msgstr ""
632
 
633
+ #: admin/schedule-sentence.php:76
634
  msgid "this server"
635
  msgstr ""
636
 
637
+ #: admin/schedule-sentence.php:84
638
  msgid "store the most recent backup in %s"
639
  msgstr ""
640
 
641
+ #: admin/schedule-sentence.php:90
642
  msgid "don't store any backups in on this server"
643
  msgstr ""
644
 
645
+ #: admin/schedule-sentence.php:96
646
  msgid "store the last %1$s backups in %2$s"
647
  msgstr ""
648
 
649
+ #: admin/schedule-sentence.php:131
 
 
 
 
650
  msgid "Send a copy of each backup to %s."
651
  msgstr ""
652
 
653
+ #: admin/schedule-sentence.php:164
654
  msgid "Backups will be compressed and should be smaller than this."
655
  msgstr ""
656
 
657
+ #: admin/schedule-sentence.php:168
658
  msgid "this shouldn't take long&hellip;"
659
  msgstr ""
660
 
661
+ #: admin/schedule-sentence.php:168
662
+ msgid "calculating the size of your site&hellip;"
663
  msgstr ""
664
 
665
+ #: admin/schedule-settings.php:9
666
  msgid "Run now"
667
  msgstr ""
668
 
669
+ #: admin/schedule-settings.php:17
670
  msgid "Excludes"
671
  msgstr ""
672
 
673
+ #: admin/schedule-settings.php:29 functions/interface.php:36
674
  msgid "Delete"
675
  msgstr ""
676
 
692
  msgid "BackUpWordPress Error"
693
  msgstr ""
694
 
695
+ #: classes/backup/class-backup.php:53
696
+ msgid "Backing up database..."
697
  msgstr ""
698
 
699
+ #: classes/backup/class-backup.php:107
700
+ msgid "Backing up files..."
701
  msgstr ""
702
 
703
+ #: classes/class-backupwordpress-wp-cli-command.php:46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
704
  msgid "Backup: Dumping database..."
705
  msgstr ""
706
 
707
+ #: classes/class-backupwordpress-wp-cli-command.php:50
708
  msgid "Backup: Zipping everything up..."
709
  msgstr ""
710
 
711
+ #: classes/class-backupwordpress-wp-cli-command.php:64
712
  msgid "Invalid backup path"
713
  msgstr ""
714
 
715
+ #: classes/class-backupwordpress-wp-cli-command.php:69
716
  msgid "Invalid root path"
717
  msgstr ""
718
 
719
+ #: classes/class-backupwordpress-wp-cli-command.php:96
720
  msgid "Backup Complete: "
721
  msgstr ""
722
 
723
+ #: classes/class-backupwordpress-wp-cli-command.php:98
724
  msgid "Backup Failed"
725
  msgstr ""
726
 
799
  msgid "Unfortunately, the backup file was too large to attach to this email."
800
  msgstr ""
801
 
802
+ #: classes/class-path.php:332
803
  msgid "This %s file ensures that other people cannot download your backup files."
804
  msgstr ""
805
 
806
+ #: classes/class-plugin.php:228
807
  msgid "Update"
808
  msgstr ""
809
 
810
+ #: classes/class-plugin.php:229
811
  msgid "Cancel"
812
  msgstr ""
813
 
814
+ #: classes/class-plugin.php:230
815
  msgid ""
816
  "Are you sure you want to delete this schedule? All of its backups will also "
817
  "be deleted."
818
  msgstr ""
819
 
820
+ #: classes/class-plugin.php:230 classes/class-plugin.php:231
821
+ #: classes/class-plugin.php:232 classes/class-plugin.php:233
822
  msgid "'Cancel' to go back, 'OK' to delete."
823
  msgstr ""
824
 
825
+ #: classes/class-plugin.php:231
826
  msgid "Are you sure you want to delete this backup?"
827
  msgstr ""
828
 
829
+ #: classes/class-plugin.php:232
830
  msgid "Are you sure you want to remove this exclude rule?"
831
  msgstr ""
832
 
833
+ #: classes/class-plugin.php:233
834
  msgid ""
835
  "Reducing the number of backups that are stored on this server will cause "
836
  "some of your existing backups to be deleted. Are you sure that's what you "
837
  "want?"
838
  msgstr ""
839
 
840
+ #: classes/class-scheduled-backup.php:321
 
 
 
 
 
 
 
 
 
 
 
 
841
  msgid "Argument 1 for %s must be a valid cron recurrence or \"manually\""
842
  msgstr ""
843
 
844
+ #: classes/class-scheduled-backup.php:428 functions/interface.php:272
845
+ msgid "Starting backup..."
846
  msgstr ""
847
 
848
+ #: classes/class-scheduled-backup.php:430
849
+ #: classes/class-scheduled-backup.php:445
850
+ msgid "Deleting old backups..."
851
  msgstr ""
852
 
853
+ #: classes/class-scheduled-backup.php:545
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
854
  #. translators: min=minute
855
  msgid "%s min"
856
  msgid_plural "%s mins"
857
  msgstr[0] ""
858
  msgstr[1] ""
859
 
860
+ #: classes/class-scheduled-backup.php:555
861
  msgid "%s hour"
862
  msgid_plural "%s hours"
863
  msgstr[0] ""
864
  msgstr[1] ""
865
 
866
+ #: classes/class-scheduled-backup.php:617
867
  msgid "Argument 1 for %s must be a non-empty string"
868
  msgstr ""
869
 
870
+ #: classes/class-scheduled-backup.php:622
871
  msgid "%s doesn't exist"
872
  msgstr ""
873
 
874
+ #: classes/class-scheduled-backup.php:627
875
  msgid "That backup wasn't created by this schedule"
876
  msgstr ""
877
 
901
  msgid "Error: %s"
902
  msgstr ""
903
 
904
+ #: functions/core.php:318
905
  msgid "BackUpWordPress has set up your default schedules."
906
  msgstr ""
907
 
908
+ #: functions/core.php:318
909
  msgid ""
910
  "By default BackUpWordPress performs a daily backup of your database and a "
911
  "weekly backup of your database &amp; files. You can modify these schedules."
912
  msgstr ""
913
 
914
+ #: functions/core.php:334
915
  msgid "Once Hourly"
916
  msgstr ""
917
 
918
+ #: functions/core.php:335
919
  msgid "Twice Daily"
920
  msgstr ""
921
 
922
+ #: functions/core.php:336
923
  msgid "Once Daily"
924
  msgstr ""
925
 
926
+ #: functions/core.php:337
927
  msgid "Once Weekly"
928
  msgstr ""
929
 
930
+ #: functions/core.php:338
931
  msgid "Once Every Two Weeks"
932
  msgstr ""
933
 
934
+ #: functions/core.php:339
935
  msgid "Once Monthly"
936
  msgstr ""
937
 
938
+ #: functions/core.php:358
939
  msgid "You can only delete directories inside your WordPress installation"
940
  msgstr ""
941
 
942
+ #: functions/interface.php:33
943
  msgid "Download"
944
  msgstr ""
945
 
 
 
 
 
946
  #: functions/interface.php:75
947
+ msgid "BackUpWordPress detected issues with your last backup."
948
  msgstr ""
949
 
950
+ #: functions/interface.php:88 functions/interface.php:108
951
+ #: functions/interface.php:130
952
  msgid "Dismiss this notice."
953
  msgstr ""
954
 
1000
  "saved to %2$s instead."
1001
  msgstr ""
1002
 
1003
+ #: functions/interface.php:198
1004
  msgid "Your site root path %s isn't readable."
1005
  msgstr ""
1006
 
1007
+ #: functions/interface.php:239
1008
  msgid "Database and Files"
1009
  msgstr ""
1010
 
1011
+ #: functions/interface.php:243
1012
  msgid "Files"
1013
  msgstr ""
1014
 
1015
+ #: functions/interface.php:247
1016
  msgid "Database"
1017
  msgstr ""
1018
 
1019
+ #: functions/interface.php:254
1020
  msgid "Legacy"
1021
  msgstr ""
1022
 
1023
+ #: functions/interface.php:271
1024
  msgid "Started %s ago"
1025
  msgstr ""
1026
 
1027
+ #: functions/interface.php:273
1028
  msgid "cancel"
1029
  msgstr ""
1030
 
1031
+ #: functions/interface.php:291
1032
  msgid "No backups completed"
1033
  msgstr ""
1034
 
1035
+ #: functions/interface.php:302
1036
  msgid "Complete Hourly"
1037
  msgstr ""
1038
 
1039
+ #: functions/interface.php:303
1040
  msgid "File Hourly"
1041
  msgstr ""
1042
 
1043
+ #: functions/interface.php:304
1044
  msgid "Database Hourly"
1045
  msgstr ""
1046
 
1047
+ #: functions/interface.php:305
1048
  msgid "Complete Twice Daily"
1049
  msgstr ""
1050
 
1051
+ #: functions/interface.php:306
1052
  msgid "File Twice Daily"
1053
  msgstr ""
1054
 
1055
+ #: functions/interface.php:307
1056
  msgid "Database Twice Daily"
1057
  msgstr ""
1058
 
1059
+ #: functions/interface.php:308
1060
  msgid "Complete Daily"
1061
  msgstr ""
1062
 
1063
+ #: functions/interface.php:309
1064
  msgid "File Daily"
1065
  msgstr ""
1066
 
1067
+ #: functions/interface.php:310
1068
  msgid "Database Daily"
1069
  msgstr ""
1070
 
1071
+ #: functions/interface.php:311
1072
  msgid "Complete Weekly"
1073
  msgstr ""
1074
 
1075
+ #: functions/interface.php:312
1076
  msgid "File Weekly"
1077
  msgstr ""
1078
 
1079
+ #: functions/interface.php:313
1080
  msgid "Database Weekly"
1081
  msgstr ""
1082
 
1083
+ #: functions/interface.php:314
1084
  msgid "Complete Every Two Weeks"
1085
  msgstr ""
1086
 
1087
+ #: functions/interface.php:315
1088
  msgid "File Every Two Weeks"
1089
  msgstr ""
1090
 
1091
+ #: functions/interface.php:316
1092
  msgid "Database Every Two Weeks"
1093
  msgstr ""
1094
 
1095
+ #: functions/interface.php:317
1096
  msgid "Complete Monthly"
1097
  msgstr ""
1098
 
1099
+ #: functions/interface.php:318
1100
  msgid "File Monthly"
1101
  msgstr ""
1102
 
1103
+ #: functions/interface.php:319
1104
  msgid "Database Monthly"
1105
  msgstr ""
1106
 
1107
+ #: functions/interface.php:320
1108
  msgid "Complete Manually"
1109
  msgstr ""
1110
 
1111
+ #: functions/interface.php:321
1112
  msgid "File Manually"
1113
  msgstr ""
1114
 
1115
+ #: functions/interface.php:322
1116
  msgid "Database Manually"
1117
  msgstr ""
1118
 
1135
  msgid "http://hmn.md/"
1136
  msgstr ""
1137
 
1138
+ #: admin/schedule-sentence.php:124
1139
  msgctxt ""
1140
  "1: Backup Type 2: Total size of backup 3: Schedule 4: Number of backups to "
1141
  "store"
1142
  msgid "Backup my %1$s %2$s %3$s, %4$s."
1143
  msgstr ""
1144
 
1145
+ #: functions/interface.php:293
1146
  msgctxt "backups count"
1147
  msgid "One backup completed"
1148
  msgid_plural "%1$s backups completed"
readme.md ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <table width="100%">
2
+ <tr>
3
+ <td align="left" width="70">
4
+ <strong><a href="https://bwp.hmn.md/">BackUpWordPress</a></strong><br />
5
+ Simple automated backups of your WordPress powered website.
6
+ </td>
7
+ <td align="right" width="20%">
8
+ <a href="https://travis-ci.org/humanmade/backupwordpress">
9
+ <img src="https://travis-ci.org/humanmade/backupwordpress.svg?branch=master" alt="Build status">
10
+ </a>
11
+ <a href="https://scrutinizer-ci.com/g/humanmade/backupwordpress/">
12
+ <img src="https://scrutinizer-ci.com/g/humanmade/backupwordpress/badges/coverage.png?b=master" alt="Coverage via Scrutiniser" />
13
+ </a>
14
+ </td>
15
+ </tr>
16
+ <tr>
17
+ <td>
18
+ A <strong><a href="https://hmn.md/">Human Made</a></strong> project. Maintained by @pdewouters.
19
+ </td>
20
+ <td align="center">
21
+ <img src="https://hmn.md/content/themes/hmnmd/assets/images/hm-logo.svg" width="100" />
22
+ </td>
23
+ </tr>
24
+ </table>
25
+
26
+ BackUpWordPress is a well tested, onsite backup plugin for WordPress. It's supports sending backups to cloud services through premium extensions
27
+
28
+ Stop using outdated practices, and start making sense.
29
+
30
+ ## Requirements
31
+ BackUpWordPress requires WordPress 3.9 or newer and requires PHP 5.3+ due to the use of namespaced code.
32
+
33
+ ## Installation
34
+ Just install like any other plugin
35
+
36
+ ## Contributing
37
+ Read our [contributing.md](https://github.com/humanmade/backupwordpress/blob/master/CONTRIBUTING.md) file
38
+
39
+
40
+ ## License
41
+ BackUpWordPress is licensed under the GPLv2 or later.
42
+
43
+ ## Credits
44
+ Created by Human Made for high volume and large-scale sites. BackUpWordPress is used on over 200,000 sites worldwide.
45
+
46
+ Written and maintained by [Tom Willmot](https://github.com/willmot) & [Paul De Wouters](https://github.com/pdwouters). Thanks to all our [contributors](https://github.com/humanmade/backupwordpress/graphs/contributors).
47
+
48
+ BackUpWordPress was originally developed by [wpdprx](http://profiles.wordpress.org/users/wpdprx/) before being taken over by Human Made.
49
+
50
+ ---
51
+
52
+ Interested in joining in on the fun? [Join us, and become human!](https://hmn.md/is/hiring/)
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: humanmade, willmot, pauldewouters, joehoyle, mattheu, tcrsavage, c
3
  Tags: back up, backup, backups, database, zip, db, files, archive, wp-cli, humanmade
4
  Requires at least: 3.9
5
  Tested up to: 4.4
6
- Stable tag: 3.3.4
7
 
8
  Simple automated backups of your WordPress-powered website.
9
 
@@ -118,6 +118,12 @@ You can also tweet <a href="http://twitter.com/humanmadeltd">@humanmadeltd</a> o
118
 
119
  == Upgrade Notice ==
120
 
 
 
 
 
 
 
121
  = 3.3.4 =
122
 
123
  * WordPress 4.4 compatibility.
@@ -152,6 +158,54 @@ You can also tweet <a href="http://twitter.com/humanmadeltd">@humanmadeltd</a> o
152
 
153
  == Changelog ==
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  ### 3.3.4 / 2015-12-10
156
 
157
  * Fixes styling issues with WordPress 4.4
3
  Tags: back up, backup, backups, database, zip, db, files, archive, wp-cli, humanmade
4
  Requires at least: 3.9
5
  Tested up to: 4.4
6
+ Stable tag: 3.4.0
7
 
8
  Simple automated backups of your WordPress-powered website.
9
 
118
 
119
  == Upgrade Notice ==
120
 
121
+ = 3.4 =
122
+
123
+ This version introduces a major refactoring code responsible for the files and database backup. We made sure to write
124
+ unit tests for the new code, and we have tested it on several user's sites. It fixes a lot of old bugs, and Windows
125
+ users should see major improvments to reliablity.
126
+
127
  = 3.3.4 =
128
 
129
  * WordPress 4.4 compatibility.
158
 
159
  == Changelog ==
160
 
161
+ ### 3.4 / 2016/01/20
162
+
163
+ * Updated composer packages
164
+ * Strip empty services from the schedule sentence.
165
+ * Back compat pass
166
+ * Ensure row actions always show
167
+ * don't hardcode byte sizes in unit tests
168
+ * correct namespaced Finder call
169
+ * allow for more variance when comparing filesizes
170
+ * Add unit tests for Site Size
171
+ * fixup the full-backup tests
172
+ * Add tests for the Backup Status class
173
+ * Unit tests now pass on Windows
174
+ * correctly ignore the backup running file when zipping the database.
175
+ * Normalize a bunch of paths to fix issues on windows
176
+ * Don't declare Service::action() as abstract
177
+ * use submit button function
178
+ * Close the service settings page when clicking done as long as there are no errors
179
+ * Remove the old .backup_errors and .backup_warnings files and associated functions as they are no longer used.
180
+ * Fixup the email and webhook services
181
+ * Delete the backups class and fix a recursion issue in mysqldump
182
+ * Re-organise the tests and fixup the failing ones.
183
+ * fix excludes
184
+ * Fixup the backup status.
185
+ * ensure excludes are correctly passed through the backups director
186
+ * Ensure unwritable backup paths are never chosen
187
+ * Return methods directly instead of storing in unneeded variables
188
+ * Fix several bugs in the site size functionality
189
+ * add a test to confirm backups exclude backups, currently failing
190
+ * Hook the new Site_Backup class up the scheduled backups
191
+ * Introduce `Site_Size`, `Excludes` & `Site_Backup`.
192
+ * fix some global state issues in the Path tests
193
+ * Fixup excludes functionality
194
+ * Fix the backup director unit tests
195
+ * More namespacing and renaming.
196
+ * Start using namespaces in a lot more places.
197
+ * start integrating with main plugin
198
+ * Add in tests for unreadable directories and account for how zip uniquely handles them
199
+ * Skip dots in hmbkp_rmdirtree
200
+ * Introduce `Backup_Utilities` and move the static methods from `Backup_Engine` too it.
201
+ * backcompat version of assertNotWPError for when tests run on old versions of WordPress
202
+ * More unit test fixes
203
+ * Update to the latest version of symfony/Finder
204
+ * Fix a fatal error in the tests
205
+ * remove uneeded code copy paste
206
+ * The major backups refactor of 2015
207
+
208
+
209
  ### 3.3.4 / 2015-12-10
210
 
211
  * Fixes styling issues with WordPress 4.4
vendor/composer/installed.json CHANGED
@@ -55,34 +55,37 @@
55
  },
56
  {
57
  "name": "symfony/finder",
58
- "version": "v2.7.6",
59
- "version_normalized": "2.7.6.0",
60
  "source": {
61
  "type": "git",
62
  "url": "https://github.com/symfony/finder.git",
63
- "reference": "2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d"
64
  },
65
  "dist": {
66
  "type": "zip",
67
- "url": "https://api.github.com/repos/symfony/finder/zipball/2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d",
68
- "reference": "2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d",
69
  "shasum": ""
70
  },
71
  "require": {
72
  "php": ">=5.3.9"
73
  },
74
- "time": "2015-10-11 09:39:48",
75
  "type": "library",
76
  "extra": {
77
  "branch-alias": {
78
- "dev-master": "2.7-dev"
79
  }
80
  },
81
  "installation-source": "dist",
82
  "autoload": {
83
  "psr-4": {
84
  "Symfony\\Component\\Finder\\": ""
85
- }
 
 
 
86
  },
87
  "notification-url": "https://packagist.org/downloads/",
88
  "license": [
55
  },
56
  {
57
  "name": "symfony/finder",
58
+ "version": "v2.8.2",
59
+ "version_normalized": "2.8.2.0",
60
  "source": {
61
  "type": "git",
62
  "url": "https://github.com/symfony/finder.git",
63
+ "reference": "c90fabdd97e431ee19b6383999cf35334dff27da"
64
  },
65
  "dist": {
66
  "type": "zip",
67
+ "url": "https://api.github.com/repos/symfony/finder/zipball/c90fabdd97e431ee19b6383999cf35334dff27da",
68
+ "reference": "c90fabdd97e431ee19b6383999cf35334dff27da",
69
  "shasum": ""
70
  },
71
  "require": {
72
  "php": ">=5.3.9"
73
  },
74
+ "time": "2016-01-14 08:26:52",
75
  "type": "library",
76
  "extra": {
77
  "branch-alias": {
78
+ "dev-master": "2.8-dev"
79
  }
80
  },
81
  "installation-source": "dist",
82
  "autoload": {
83
  "psr-4": {
84
  "Symfony\\Component\\Finder\\": ""
85
+ },
86
+ "exclude-from-classmap": [
87
+ "/Tests/"
88
+ ]
89
  },
90
  "notification-url": "https://packagist.org/downloads/",
91
  "license": [
vendor/symfony/finder/Adapter/AbstractAdapter.php CHANGED
@@ -11,10 +11,14 @@
11
 
12
  namespace Symfony\Component\Finder\Adapter;
13
 
 
 
14
  /**
15
  * Interface for finder engine implementations.
16
  *
17
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
18
  */
19
  abstract class AbstractAdapter implements AdapterInterface
20
  {
11
 
12
  namespace Symfony\Component\Finder\Adapter;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\AbstractAdapter class is deprecated since version 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
15
+
16
  /**
17
  * Interface for finder engine implementations.
18
  *
19
  * @author Jean-François Simon <contact@jfsimon.fr>
20
+ *
21
+ * @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
22
  */
23
  abstract class AbstractAdapter implements AdapterInterface
24
  {
vendor/symfony/finder/Adapter/AbstractFindAdapter.php CHANGED
@@ -11,6 +11,8 @@
11
 
12
  namespace Symfony\Component\Finder\Adapter;
13
 
 
 
14
  use Symfony\Component\Finder\Exception\AccessDeniedException;
15
  use Symfony\Component\Finder\Iterator;
16
  use Symfony\Component\Finder\Shell\Shell;
@@ -23,6 +25,8 @@ use Symfony\Component\Finder\Comparator\DateComparator;
23
  * Shell engine implementation using GNU find command.
24
  *
25
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
26
  */
27
  abstract class AbstractFindAdapter extends AbstractAdapter
28
  {
11
 
12
  namespace Symfony\Component\Finder\Adapter;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\AbstractFindAdapter class is deprecated since version 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
15
+
16
  use Symfony\Component\Finder\Exception\AccessDeniedException;
17
  use Symfony\Component\Finder\Iterator;
18
  use Symfony\Component\Finder\Shell\Shell;
25
  * Shell engine implementation using GNU find command.
26
  *
27
  * @author Jean-François Simon <contact@jfsimon.fr>
28
+ *
29
+ * @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
30
  */
31
  abstract class AbstractFindAdapter extends AbstractAdapter
32
  {
vendor/symfony/finder/Adapter/AdapterInterface.php CHANGED
@@ -13,6 +13,8 @@ namespace Symfony\Component\Finder\Adapter;
13
 
14
  /**
15
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
16
  */
17
  interface AdapterInterface
18
  {
13
 
14
  /**
15
  * @author Jean-François Simon <contact@jfsimon.fr>
16
+ *
17
+ * @deprecated since 2.8, to be removed in 3.0.
18
  */
19
  interface AdapterInterface
20
  {
vendor/symfony/finder/Adapter/BsdFindAdapter.php CHANGED
@@ -11,6 +11,8 @@
11
 
12
  namespace Symfony\Component\Finder\Adapter;
13
 
 
 
14
  use Symfony\Component\Finder\Shell\Shell;
15
  use Symfony\Component\Finder\Shell\Command;
16
  use Symfony\Component\Finder\Iterator\SortableIterator;
@@ -20,6 +22,8 @@ use Symfony\Component\Finder\Expression\Expression;
20
  * Shell engine implementation using BSD find command.
21
  *
22
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
23
  */
24
  class BsdFindAdapter extends AbstractFindAdapter
25
  {
11
 
12
  namespace Symfony\Component\Finder\Adapter;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\BsdFindAdapter class is deprecated since version 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
15
+
16
  use Symfony\Component\Finder\Shell\Shell;
17
  use Symfony\Component\Finder\Shell\Command;
18
  use Symfony\Component\Finder\Iterator\SortableIterator;
22
  * Shell engine implementation using BSD find command.
23
  *
24
  * @author Jean-François Simon <contact@jfsimon.fr>
25
+ *
26
+ * @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
27
  */
28
  class BsdFindAdapter extends AbstractFindAdapter
29
  {
vendor/symfony/finder/Adapter/GnuFindAdapter.php CHANGED
@@ -11,6 +11,8 @@
11
 
12
  namespace Symfony\Component\Finder\Adapter;
13
 
 
 
14
  use Symfony\Component\Finder\Shell\Shell;
15
  use Symfony\Component\Finder\Shell\Command;
16
  use Symfony\Component\Finder\Iterator\SortableIterator;
@@ -20,6 +22,8 @@ use Symfony\Component\Finder\Expression\Expression;
20
  * Shell engine implementation using GNU find command.
21
  *
22
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
23
  */
24
  class GnuFindAdapter extends AbstractFindAdapter
25
  {
11
 
12
  namespace Symfony\Component\Finder\Adapter;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\GnuFindAdapter class is deprecated since version 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
15
+
16
  use Symfony\Component\Finder\Shell\Shell;
17
  use Symfony\Component\Finder\Shell\Command;
18
  use Symfony\Component\Finder\Iterator\SortableIterator;
22
  * Shell engine implementation using GNU find command.
23
  *
24
  * @author Jean-François Simon <contact@jfsimon.fr>
25
+ *
26
+ * @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
27
  */
28
  class GnuFindAdapter extends AbstractFindAdapter
29
  {
vendor/symfony/finder/Adapter/PhpAdapter.php CHANGED
@@ -11,12 +11,16 @@
11
 
12
  namespace Symfony\Component\Finder\Adapter;
13
 
 
 
14
  use Symfony\Component\Finder\Iterator;
15
 
16
  /**
17
  * PHP finder engine implementation.
18
  *
19
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
20
  */
21
  class PhpAdapter extends AbstractAdapter
22
  {
11
 
12
  namespace Symfony\Component\Finder\Adapter;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\PhpAdapter class is deprecated since version 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
15
+
16
  use Symfony\Component\Finder\Iterator;
17
 
18
  /**
19
  * PHP finder engine implementation.
20
  *
21
  * @author Jean-François Simon <contact@jfsimon.fr>
22
+ *
23
+ * @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
24
  */
25
  class PhpAdapter extends AbstractAdapter
26
  {
vendor/symfony/finder/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
  CHANGELOG
2
  =========
3
 
 
 
 
 
 
4
  2.5.0
5
  -----
6
  * added support for GLOB_BRACE in the paths passed to Finder::in()
1
  CHANGELOG
2
  =========
3
 
4
+ 2.8.0
5
+ -----
6
+
7
+ * deprecated adapters and related classes
8
+
9
  2.5.0
10
  -----
11
  * added support for GLOB_BRACE in the paths passed to Finder::in()
vendor/symfony/finder/Exception/AdapterFailureException.php CHANGED
@@ -11,12 +11,16 @@
11
 
12
  namespace Symfony\Component\Finder\Exception;
13
 
 
 
14
  use Symfony\Component\Finder\Adapter\AdapterInterface;
15
 
16
  /**
17
  * Base exception for all adapter failures.
18
  *
19
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
20
  */
21
  class AdapterFailureException extends \RuntimeException implements ExceptionInterface
22
  {
11
 
12
  namespace Symfony\Component\Finder\Exception;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\AdapterFailureException class is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
15
+
16
  use Symfony\Component\Finder\Adapter\AdapterInterface;
17
 
18
  /**
19
  * Base exception for all adapter failures.
20
  *
21
  * @author Jean-François Simon <contact@jfsimon.fr>
22
+ *
23
+ * @deprecated since 2.8, to be removed in 3.0.
24
  */
25
  class AdapterFailureException extends \RuntimeException implements ExceptionInterface
26
  {
vendor/symfony/finder/Exception/OperationNotPermitedException.php CHANGED
@@ -11,8 +11,12 @@
11
 
12
  namespace Symfony\Component\Finder\Exception;
13
 
 
 
14
  /**
15
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
16
  */
17
  class OperationNotPermitedException extends AdapterFailureException
18
  {
11
 
12
  namespace Symfony\Component\Finder\Exception;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\OperationNotPermitedException class is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
15
+
16
  /**
17
  * @author Jean-François Simon <contact@jfsimon.fr>
18
+ *
19
+ * @deprecated since 2.8, to be removed in 3.0.
20
  */
21
  class OperationNotPermitedException extends AdapterFailureException
22
  {
vendor/symfony/finder/Exception/ShellCommandFailureException.php CHANGED
@@ -11,11 +11,15 @@
11
 
12
  namespace Symfony\Component\Finder\Exception;
13
 
 
 
14
  use Symfony\Component\Finder\Adapter\AdapterInterface;
15
  use Symfony\Component\Finder\Shell\Command;
16
 
17
  /**
18
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
19
  */
20
  class ShellCommandFailureException extends AdapterFailureException
21
  {
11
 
12
  namespace Symfony\Component\Finder\Exception;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\ShellCommandFailureException class is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
15
+
16
  use Symfony\Component\Finder\Adapter\AdapterInterface;
17
  use Symfony\Component\Finder\Shell\Command;
18
 
19
  /**
20
  * @author Jean-François Simon <contact@jfsimon.fr>
21
+ *
22
+ * @deprecated since 2.8, to be removed in 3.0.
23
  */
24
  class ShellCommandFailureException extends AdapterFailureException
25
  {
vendor/symfony/finder/Expression/Expression.php CHANGED
@@ -11,6 +11,8 @@
11
 
12
  namespace Symfony\Component\Finder\Expression;
13
 
 
 
14
  /**
15
  * @author Jean-François Simon <contact@jfsimon.fr>
16
  */
11
 
12
  namespace Symfony\Component\Finder\Expression;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\Expression class is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
15
+
16
  /**
17
  * @author Jean-François Simon <contact@jfsimon.fr>
18
  */
vendor/symfony/finder/Expression/Glob.php CHANGED
@@ -11,6 +11,8 @@
11
 
12
  namespace Symfony\Component\Finder\Expression;
13
 
 
 
14
  use Symfony\Component\Finder\Glob as FinderGlob;
15
 
16
  /**
11
 
12
  namespace Symfony\Component\Finder\Expression;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\Glob class is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
15
+
16
  use Symfony\Component\Finder\Glob as FinderGlob;
17
 
18
  /**
vendor/symfony/finder/Expression/Regex.php CHANGED
@@ -11,6 +11,8 @@
11
 
12
  namespace Symfony\Component\Finder\Expression;
13
 
 
 
14
  /**
15
  * @author Jean-François Simon <contact@jfsimon.fr>
16
  */
11
 
12
  namespace Symfony\Component\Finder\Expression;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\Regex class is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
15
+
16
  /**
17
  * @author Jean-François Simon <contact@jfsimon.fr>
18
  */
vendor/symfony/finder/Expression/ValueInterface.php CHANGED
@@ -11,6 +11,8 @@
11
 
12
  namespace Symfony\Component\Finder\Expression;
13
 
 
 
14
  /**
15
  * @author Jean-François Simon <contact@jfsimon.fr>
16
  */
11
 
12
  namespace Symfony\Component\Finder\Expression;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\ValueInterface interface is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
15
+
16
  /**
17
  * @author Jean-François Simon <contact@jfsimon.fr>
18
  */
vendor/symfony/finder/Finder.php CHANGED
@@ -60,7 +60,7 @@ class Finder implements \IteratorAggregate, \Countable
60
  private $iterators = array();
61
  private $contains = array();
62
  private $notContains = array();
63
- private $adapters = array();
64
  private $paths = array();
65
  private $notPaths = array();
66
  private $ignoreUnreadableDirs = false;
@@ -73,13 +73,6 @@ class Finder implements \IteratorAggregate, \Countable
73
  public function __construct()
74
  {
75
  $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
76
-
77
- $this
78
- ->addAdapter(new GnuFindAdapter())
79
- ->addAdapter(new BsdFindAdapter())
80
- ->addAdapter(new PhpAdapter(), -50)
81
- ->setAdapter('php')
82
- ;
83
  }
84
 
85
  /**
@@ -99,9 +92,15 @@ class Finder implements \IteratorAggregate, \Countable
99
  * @param int $priority Highest is selected first
100
  *
101
  * @return Finder The current Finder instance
 
 
102
  */
103
  public function addAdapter(AdapterInterface $adapter, $priority = 0)
104
  {
 
 
 
 
105
  $this->adapters[$adapter->getName()] = array(
106
  'adapter' => $adapter,
107
  'priority' => $priority,
@@ -115,9 +114,15 @@ class Finder implements \IteratorAggregate, \Countable
115
  * Sets the selected adapter to the best one according to the current platform the code is run on.
116
  *
117
  * @return Finder The current Finder instance
 
 
118
  */
119
  public function useBestAdapter()
120
  {
 
 
 
 
121
  $this->resetAdapterSelection();
122
 
123
  return $this->sortAdapters();
@@ -131,9 +136,15 @@ class Finder implements \IteratorAggregate, \Countable
131
  * @throws \InvalidArgumentException
132
  *
133
  * @return Finder The current Finder instance
 
 
134
  */
135
  public function setAdapter($name)
136
  {
 
 
 
 
137
  if (!isset($this->adapters[$name])) {
138
  throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name));
139
  }
@@ -148,9 +159,13 @@ class Finder implements \IteratorAggregate, \Countable
148
  * Removes all adapters registered in the finder.
149
  *
150
  * @return Finder The current Finder instance
 
 
151
  */
152
  public function removeAdapters()
153
  {
 
 
154
  $this->adapters = array();
155
 
156
  return $this;
@@ -160,9 +175,15 @@ class Finder implements \IteratorAggregate, \Countable
160
  * Returns registered adapters ordered by priority without extra information.
161
  *
162
  * @return AdapterInterface[]
 
 
163
  */
164
  public function getAdapters()
165
  {
 
 
 
 
166
  return array_values(array_map(function (array $adapter) {
167
  return $adapter['adapter'];
168
  }, $this->adapters));
@@ -732,8 +753,6 @@ class Finder implements \IteratorAggregate, \Countable
732
  * @param $dir
733
  *
734
  * @return \Iterator
735
- *
736
- * @throws \RuntimeException When none of the adapters are supported
737
  */
738
  private function searchInDirectory($dir)
739
  {
@@ -745,18 +764,93 @@ class Finder implements \IteratorAggregate, \Countable
745
  $this->notPaths[] = '#(^|/)\..+(/|$)#';
746
  }
747
 
748
- foreach ($this->adapters as $adapter) {
749
- if ($adapter['adapter']->isSupported()) {
750
- try {
751
- return $this
752
- ->buildAdapter($adapter['adapter'])
753
- ->searchInDirectory($dir);
754
- } catch (ExceptionInterface $e) {
 
 
755
  }
756
  }
757
  }
758
 
759
- throw new \RuntimeException('No supported adapter found.');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
760
  }
761
 
762
  /**
@@ -795,4 +889,17 @@ class Finder implements \IteratorAggregate, \Countable
795
  return $properties;
796
  }, $this->adapters);
797
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
798
  }
60
  private $iterators = array();
61
  private $contains = array();
62
  private $notContains = array();
63
+ private $adapters = null;
64
  private $paths = array();
65
  private $notPaths = array();
66
  private $ignoreUnreadableDirs = false;
73
  public function __construct()
74
  {
75
  $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
 
 
 
 
 
 
 
76
  }
77
 
78
  /**
92
  * @param int $priority Highest is selected first
93
  *
94
  * @return Finder The current Finder instance
95
+ *
96
+ * @deprecated since 2.8, to be removed in 3.0.
97
  */
98
  public function addAdapter(AdapterInterface $adapter, $priority = 0)
99
  {
100
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
101
+
102
+ $this->initDefaultAdapters();
103
+
104
  $this->adapters[$adapter->getName()] = array(
105
  'adapter' => $adapter,
106
  'priority' => $priority,
114
  * Sets the selected adapter to the best one according to the current platform the code is run on.
115
  *
116
  * @return Finder The current Finder instance
117
+ *
118
+ * @deprecated since 2.8, to be removed in 3.0.
119
  */
120
  public function useBestAdapter()
121
  {
122
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
123
+
124
+ $this->initDefaultAdapters();
125
+
126
  $this->resetAdapterSelection();
127
 
128
  return $this->sortAdapters();
136
  * @throws \InvalidArgumentException
137
  *
138
  * @return Finder The current Finder instance
139
+ *
140
+ * @deprecated since 2.8, to be removed in 3.0.
141
  */
142
  public function setAdapter($name)
143
  {
144
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
145
+
146
+ $this->initDefaultAdapters();
147
+
148
  if (!isset($this->adapters[$name])) {
149
  throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name));
150
  }
159
  * Removes all adapters registered in the finder.
160
  *
161
  * @return Finder The current Finder instance
162
+ *
163
+ * @deprecated since 2.8, to be removed in 3.0.
164
  */
165
  public function removeAdapters()
166
  {
167
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
168
+
169
  $this->adapters = array();
170
 
171
  return $this;
175
  * Returns registered adapters ordered by priority without extra information.
176
  *
177
  * @return AdapterInterface[]
178
+ *
179
+ * @deprecated since 2.8, to be removed in 3.0.
180
  */
181
  public function getAdapters()
182
  {
183
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
184
+
185
+ $this->initDefaultAdapters();
186
+
187
  return array_values(array_map(function (array $adapter) {
188
  return $adapter['adapter'];
189
  }, $this->adapters));
753
  * @param $dir
754
  *
755
  * @return \Iterator
 
 
756
  */
757
  private function searchInDirectory($dir)
758
  {
764
  $this->notPaths[] = '#(^|/)\..+(/|$)#';
765
  }
766
 
767
+ if ($this->adapters) {
768
+ foreach ($this->adapters as $adapter) {
769
+ if ($adapter['adapter']->isSupported()) {
770
+ try {
771
+ return $this
772
+ ->buildAdapter($adapter['adapter'])
773
+ ->searchInDirectory($dir);
774
+ } catch (ExceptionInterface $e) {
775
+ }
776
  }
777
  }
778
  }
779
 
780
+ $minDepth = 0;
781
+ $maxDepth = PHP_INT_MAX;
782
+
783
+ foreach ($this->depths as $comparator) {
784
+ switch ($comparator->getOperator()) {
785
+ case '>':
786
+ $minDepth = $comparator->getTarget() + 1;
787
+ break;
788
+ case '>=':
789
+ $minDepth = $comparator->getTarget();
790
+ break;
791
+ case '<':
792
+ $maxDepth = $comparator->getTarget() - 1;
793
+ break;
794
+ case '<=':
795
+ $maxDepth = $comparator->getTarget();
796
+ break;
797
+ default:
798
+ $minDepth = $maxDepth = $comparator->getTarget();
799
+ }
800
+ }
801
+
802
+ $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
803
+
804
+ if ($this->followLinks) {
805
+ $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
806
+ }
807
+
808
+ $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
809
+
810
+ if ($this->exclude) {
811
+ $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
812
+ }
813
+
814
+ $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
815
+
816
+ if ($minDepth > 0 || $maxDepth < PHP_INT_MAX) {
817
+ $iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
818
+ }
819
+
820
+ if ($this->mode) {
821
+ $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
822
+ }
823
+
824
+ if ($this->names || $this->notNames) {
825
+ $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
826
+ }
827
+
828
+ if ($this->contains || $this->notContains) {
829
+ $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
830
+ }
831
+
832
+ if ($this->sizes) {
833
+ $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
834
+ }
835
+
836
+ if ($this->dates) {
837
+ $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
838
+ }
839
+
840
+ if ($this->filters) {
841
+ $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
842
+ }
843
+
844
+ if ($this->paths || $this->notPaths) {
845
+ $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
846
+ }
847
+
848
+ if ($this->sort) {
849
+ $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
850
+ $iterator = $iteratorAggregate->getIterator();
851
+ }
852
+
853
+ return $iterator;
854
  }
855
 
856
  /**
889
  return $properties;
890
  }, $this->adapters);
891
  }
892
+
893
+ private function initDefaultAdapters()
894
+ {
895
+ if (null === $this->adapters) {
896
+ $this->adapters = array();
897
+ $this
898
+ ->addAdapter(new GnuFindAdapter())
899
+ ->addAdapter(new BsdFindAdapter())
900
+ ->addAdapter(new PhpAdapter(), -50)
901
+ ->setAdapter('php')
902
+ ;
903
+ }
904
+ }
905
  }
vendor/symfony/finder/Iterator/FilePathsIterator.php CHANGED
@@ -11,12 +11,16 @@
11
 
12
  namespace Symfony\Component\Finder\Iterator;
13
 
 
 
14
  use Symfony\Component\Finder\SplFileInfo;
15
 
16
  /**
17
  * Iterate over shell command result.
18
  *
19
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
20
  */
21
  class FilePathsIterator extends \ArrayIterator
22
  {
11
 
12
  namespace Symfony\Component\Finder\Iterator;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\FilePathsIterator class is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
15
+
16
  use Symfony\Component\Finder\SplFileInfo;
17
 
18
  /**
19
  * Iterate over shell command result.
20
  *
21
  * @author Jean-François Simon <contact@jfsimon.fr>
22
+ *
23
+ * @deprecated since 2.8, to be removed in 3.0.
24
  */
25
  class FilePathsIterator extends \ArrayIterator
26
  {
vendor/symfony/finder/Iterator/FilecontentFilterIterator.php CHANGED
@@ -41,25 +41,7 @@ class FilecontentFilterIterator extends MultiplePcreFilterIterator
41
  return false;
42
  }
43
 
44
- // should at least not match one rule to exclude
45
- foreach ($this->noMatchRegexps as $regex) {
46
- if (preg_match($regex, $content)) {
47
- return false;
48
- }
49
- }
50
-
51
- // should at least match one rule
52
- $match = true;
53
- if ($this->matchRegexps) {
54
- $match = false;
55
- foreach ($this->matchRegexps as $regex) {
56
- if (preg_match($regex, $content)) {
57
- return true;
58
- }
59
- }
60
- }
61
-
62
- return $match;
63
  }
64
 
65
  /**
41
  return false;
42
  }
43
 
44
+ return $this->isAccepted($content);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
 
47
  /**
vendor/symfony/finder/Iterator/FilenameFilterIterator.php CHANGED
@@ -11,7 +11,7 @@
11
 
12
  namespace Symfony\Component\Finder\Iterator;
13
 
14
- use Symfony\Component\Finder\Expression\Expression;
15
 
16
  /**
17
  * FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string).
@@ -27,27 +27,7 @@ class FilenameFilterIterator extends MultiplePcreFilterIterator
27
  */
28
  public function accept()
29
  {
30
- $filename = $this->current()->getFilename();
31
-
32
- // should at least not match one rule to exclude
33
- foreach ($this->noMatchRegexps as $regex) {
34
- if (preg_match($regex, $filename)) {
35
- return false;
36
- }
37
- }
38
-
39
- // should at least match one rule
40
- $match = true;
41
- if ($this->matchRegexps) {
42
- $match = false;
43
- foreach ($this->matchRegexps as $regex) {
44
- if (preg_match($regex, $filename)) {
45
- return true;
46
- }
47
- }
48
- }
49
-
50
- return $match;
51
  }
52
 
53
  /**
@@ -62,6 +42,6 @@ class FilenameFilterIterator extends MultiplePcreFilterIterator
62
  */
63
  protected function toRegex($str)
64
  {
65
- return Expression::create($str)->getRegex()->render();
66
  }
67
  }
11
 
12
  namespace Symfony\Component\Finder\Iterator;
13
 
14
+ use Symfony\Component\Finder\Glob;
15
 
16
  /**
17
  * FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string).
27
  */
28
  public function accept()
29
  {
30
+ return $this->isAccepted($this->current()->getFilename());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
 
33
  /**
42
  */
43
  protected function toRegex($str)
44
  {
45
+ return $this->isRegex($str) ? $str : Glob::toRegex($str);
46
  }
47
  }
vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php CHANGED
@@ -11,8 +11,6 @@
11
 
12
  namespace Symfony\Component\Finder\Iterator;
13
 
14
- use Symfony\Component\Finder\Expression\Expression;
15
-
16
  /**
17
  * MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings).
18
  *
@@ -43,6 +41,41 @@ abstract class MultiplePcreFilterIterator extends FilterIterator
43
  parent::__construct($iterator);
44
  }
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  /**
47
  * Checks whether the string is a regex.
48
  *
@@ -52,7 +85,22 @@ abstract class MultiplePcreFilterIterator extends FilterIterator
52
  */
53
  protected function isRegex($str)
54
  {
55
- return Expression::create($str)->isRegex();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  }
57
 
58
  /**
11
 
12
  namespace Symfony\Component\Finder\Iterator;
13
 
 
 
14
  /**
15
  * MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings).
16
  *
41
  parent::__construct($iterator);
42
  }
43
 
44
+ /**
45
+ * Checks whether the string is accepted by the regex filters.
46
+ *
47
+ * If there is no regexps defined in the class, this method will accept the string.
48
+ * Such case can be handled by child classes before calling the method if they want to
49
+ * apply a different behavior.
50
+ *
51
+ * @param string $string The string to be matched against filters
52
+ *
53
+ * @return bool
54
+ */
55
+ protected function isAccepted($string)
56
+ {
57
+ // should at least not match one rule to exclude
58
+ foreach ($this->noMatchRegexps as $regex) {
59
+ if (preg_match($regex, $string)) {
60
+ return false;
61
+ }
62
+ }
63
+
64
+ // should at least match one rule
65
+ if ($this->matchRegexps) {
66
+ foreach ($this->matchRegexps as $regex) {
67
+ if (preg_match($regex, $string)) {
68
+ return true;
69
+ }
70
+ }
71
+
72
+ return false;
73
+ }
74
+
75
+ // If there is no match rules, the file is accepted
76
+ return true;
77
+ }
78
+
79
  /**
80
  * Checks whether the string is a regex.
81
  *
85
  */
86
  protected function isRegex($str)
87
  {
88
+ if (preg_match('/^(.{3,}?)[imsxuADU]*$/', $str, $m)) {
89
+ $start = substr($m[1], 0, 1);
90
+ $end = substr($m[1], -1);
91
+
92
+ if ($start === $end) {
93
+ return !preg_match('/[*?[:alnum:] \\\\]/', $start);
94
+ }
95
+
96
+ foreach (array(array('{', '}'), array('(', ')'), array('[', ']'), array('<', '>')) as $delimiters) {
97
+ if ($start === $delimiters[0] && $end === $delimiters[1]) {
98
+ return true;
99
+ }
100
+ }
101
+ }
102
+
103
+ return false;
104
  }
105
 
106
  /**
vendor/symfony/finder/Iterator/PathFilterIterator.php CHANGED
@@ -32,25 +32,7 @@ class PathFilterIterator extends MultiplePcreFilterIterator
32
  $filename = str_replace('\\', '/', $filename);
33
  }
34
 
35
- // should at least not match one rule to exclude
36
- foreach ($this->noMatchRegexps as $regex) {
37
- if (preg_match($regex, $filename)) {
38
- return false;
39
- }
40
- }
41
-
42
- // should at least match one rule
43
- $match = true;
44
- if ($this->matchRegexps) {
45
- $match = false;
46
- foreach ($this->matchRegexps as $regex) {
47
- if (preg_match($regex, $filename)) {
48
- return true;
49
- }
50
- }
51
- }
52
-
53
- return $match;
54
  }
55
 
56
  /**
32
  $filename = str_replace('\\', '/', $filename);
33
  }
34
 
35
+ return $this->isAccepted($filename);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  }
37
 
38
  /**
vendor/symfony/finder/Iterator/SortableIterator.php CHANGED
@@ -55,15 +55,15 @@ class SortableIterator implements \IteratorAggregate
55
  };
56
  } elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
57
  $this->sort = function ($a, $b) {
58
- return ($a->getATime() - $b->getATime());
59
  };
60
  } elseif (self::SORT_BY_CHANGED_TIME === $sort) {
61
  $this->sort = function ($a, $b) {
62
- return ($a->getCTime() - $b->getCTime());
63
  };
64
  } elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
65
  $this->sort = function ($a, $b) {
66
- return ($a->getMTime() - $b->getMTime());
67
  };
68
  } elseif (is_callable($sort)) {
69
  $this->sort = $sort;
55
  };
56
  } elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
57
  $this->sort = function ($a, $b) {
58
+ return $a->getATime() - $b->getATime();
59
  };
60
  } elseif (self::SORT_BY_CHANGED_TIME === $sort) {
61
  $this->sort = function ($a, $b) {
62
+ return $a->getCTime() - $b->getCTime();
63
  };
64
  } elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
65
  $this->sort = function ($a, $b) {
66
+ return $a->getMTime() - $b->getMTime();
67
  };
68
  } elseif (is_callable($sort)) {
69
  $this->sort = $sort;
vendor/symfony/finder/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2015 Fabien Potencier
2
 
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
  of this software and associated documentation files (the "Software"), to deal
1
+ Copyright (c) 2004-2016 Fabien Potencier
2
 
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
  of this software and associated documentation files (the "Software"), to deal
vendor/symfony/finder/Shell/Command.php CHANGED
@@ -11,8 +11,12 @@
11
 
12
  namespace Symfony\Component\Finder\Shell;
13
 
 
 
14
  /**
15
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
16
  */
17
  class Command
18
  {
11
 
12
  namespace Symfony\Component\Finder\Shell;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\Command class is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
15
+
16
  /**
17
  * @author Jean-François Simon <contact@jfsimon.fr>
18
+ *
19
+ * @deprecated since 2.8, to be removed in 3.0.
20
  */
21
  class Command
22
  {
vendor/symfony/finder/Shell/Shell.php CHANGED
@@ -11,8 +11,12 @@
11
 
12
  namespace Symfony\Component\Finder\Shell;
13
 
 
 
14
  /**
15
  * @author Jean-François Simon <contact@jfsimon.fr>
 
 
16
  */
17
  class Shell
18
  {
11
 
12
  namespace Symfony\Component\Finder\Shell;
13
 
14
+ @trigger_error('The '.__NAMESPACE__.'\Shell class is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
15
+
16
  /**
17
  * @author Jean-François Simon <contact@jfsimon.fr>
18
+ *
19
+ * @deprecated since 2.8, to be removed in 3.0.
20
  */
21
  class Shell
22
  {
vendor/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php DELETED
@@ -1,236 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Adapter;
13
-
14
- /**
15
- * Interface for finder engine implementations.
16
- *
17
- * @author Jean-François Simon <contact@jfsimon.fr>
18
- */
19
- abstract class AbstractAdapter implements AdapterInterface
20
- {
21
- protected $followLinks = false;
22
- protected $mode = 0;
23
- protected $minDepth = 0;
24
- protected $maxDepth = PHP_INT_MAX;
25
- protected $exclude = array();
26
- protected $names = array();
27
- protected $notNames = array();
28
- protected $contains = array();
29
- protected $notContains = array();
30
- protected $sizes = array();
31
- protected $dates = array();
32
- protected $filters = array();
33
- protected $sort = false;
34
- protected $paths = array();
35
- protected $notPaths = array();
36
- protected $ignoreUnreadableDirs = false;
37
-
38
- private static $areSupported = array();
39
-
40
- /**
41
- * {@inheritdoc}
42
- */
43
- public function isSupported()
44
- {
45
- $name = $this->getName();
46
-
47
- if (!array_key_exists($name, self::$areSupported)) {
48
- self::$areSupported[$name] = $this->canBeUsed();
49
- }
50
-
51
- return self::$areSupported[$name];
52
- }
53
-
54
- /**
55
- * {@inheritdoc}
56
- */
57
- public function setFollowLinks($followLinks)
58
- {
59
- $this->followLinks = $followLinks;
60
-
61
- return $this;
62
- }
63
-
64
- /**
65
- * {@inheritdoc}
66
- */
67
- public function setMode($mode)
68
- {
69
- $this->mode = $mode;
70
-
71
- return $this;
72
- }
73
-
74
- /**
75
- * {@inheritdoc}
76
- */
77
- public function setDepths(array $depths)
78
- {
79
- $this->minDepth = 0;
80
- $this->maxDepth = PHP_INT_MAX;
81
-
82
- foreach ($depths as $comparator) {
83
- switch ($comparator->getOperator()) {
84
- case '>':
85
- $this->minDepth = $comparator->getTarget() + 1;
86
- break;
87
- case '>=':
88
- $this->minDepth = $comparator->getTarget();
89
- break;
90
- case '<':
91
- $this->maxDepth = $comparator->getTarget() - 1;
92
- break;
93
- case '<=':
94
- $this->maxDepth = $comparator->getTarget();
95
- break;
96
- default:
97
- $this->minDepth = $this->maxDepth = $comparator->getTarget();
98
- }
99
- }
100
-
101
- return $this;
102
- }
103
-
104
- /**
105
- * {@inheritdoc}
106
- */
107
- public function setExclude(array $exclude)
108
- {
109
- $this->exclude = $exclude;
110
-
111
- return $this;
112
- }
113
-
114
- /**
115
- * {@inheritdoc}
116
- */
117
- public function setNames(array $names)
118
- {
119
- $this->names = $names;
120
-
121
- return $this;
122
- }
123
-
124
- /**
125
- * {@inheritdoc}
126
- */
127
- public function setNotNames(array $notNames)
128
- {
129
- $this->notNames = $notNames;
130
-
131
- return $this;
132
- }
133
-
134
- /**
135
- * {@inheritdoc}
136
- */
137
- public function setContains(array $contains)
138
- {
139
- $this->contains = $contains;
140
-
141
- return $this;
142
- }
143
-
144
- /**
145
- * {@inheritdoc}
146
- */
147
- public function setNotContains(array $notContains)
148
- {
149
- $this->notContains = $notContains;
150
-
151
- return $this;
152
- }
153
-
154
- /**
155
- * {@inheritdoc}
156
- */
157
- public function setSizes(array $sizes)
158
- {
159
- $this->sizes = $sizes;
160
-
161
- return $this;
162
- }
163
-
164
- /**
165
- * {@inheritdoc}
166
- */
167
- public function setDates(array $dates)
168
- {
169
- $this->dates = $dates;
170
-
171
- return $this;
172
- }
173
-
174
- /**
175
- * {@inheritdoc}
176
- */
177
- public function setFilters(array $filters)
178
- {
179
- $this->filters = $filters;
180
-
181
- return $this;
182
- }
183
-
184
- /**
185
- * {@inheritdoc}
186
- */
187
- public function setSort($sort)
188
- {
189
- $this->sort = $sort;
190
-
191
- return $this;
192
- }
193
-
194
- /**
195
- * {@inheritdoc}
196
- */
197
- public function setPath(array $paths)
198
- {
199
- $this->paths = $paths;
200
-
201
- return $this;
202
- }
203
-
204
- /**
205
- * {@inheritdoc}
206
- */
207
- public function setNotPath(array $notPaths)
208
- {
209
- $this->notPaths = $notPaths;
210
-
211
- return $this;
212
- }
213
-
214
- /**
215
- * {@inheritdoc}
216
- */
217
- public function ignoreUnreadableDirs($ignore = true)
218
- {
219
- $this->ignoreUnreadableDirs = (bool) $ignore;
220
-
221
- return $this;
222
- }
223
-
224
- /**
225
- * Returns whether the adapter is supported in the current environment.
226
- *
227
- * This method should be implemented in all adapters. Do not implement
228
- * isSupported in the adapters as the generic implementation provides a cache
229
- * layer.
230
- *
231
- * @see isSupported()
232
- *
233
- * @return bool Whether the adapter is supported
234
- */
235
- abstract protected function canBeUsed();
236
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php DELETED
@@ -1,327 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Adapter;
13
-
14
- use Symfony\Component\Finder\Exception\AccessDeniedException;
15
- use Symfony\Component\Finder\Iterator;
16
- use Symfony\Component\Finder\Shell\Shell;
17
- use Symfony\Component\Finder\Expression\Expression;
18
- use Symfony\Component\Finder\Shell\Command;
19
- use Symfony\Component\Finder\Comparator\NumberComparator;
20
- use Symfony\Component\Finder\Comparator\DateComparator;
21
-
22
- /**
23
- * Shell engine implementation using GNU find command.
24
- *
25
- * @author Jean-François Simon <contact@jfsimon.fr>
26
- */
27
- abstract class AbstractFindAdapter extends AbstractAdapter
28
- {
29
- /**
30
- * @var Shell
31
- */
32
- protected $shell;
33
-
34
- /**
35
- * Constructor.
36
- */
37
- public function __construct()
38
- {
39
- $this->shell = new Shell();
40
- }
41
-
42
- /**
43
- * {@inheritdoc}
44
- */
45
- public function searchInDirectory($dir)
46
- {
47
- // having "/../" in path make find fail
48
- $dir = realpath($dir);
49
-
50
- // searching directories containing or not containing strings leads to no result
51
- if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode && ($this->contains || $this->notContains)) {
52
- return new Iterator\FilePathsIterator(array(), $dir);
53
- }
54
-
55
- $command = Command::create();
56
- $find = $this->buildFindCommand($command, $dir);
57
-
58
- if ($this->followLinks) {
59
- $find->add('-follow');
60
- }
61
-
62
- $find->add('-mindepth')->add($this->minDepth + 1);
63
-
64
- if (PHP_INT_MAX !== $this->maxDepth) {
65
- $find->add('-maxdepth')->add($this->maxDepth + 1);
66
- }
67
-
68
- if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode) {
69
- $find->add('-type d');
70
- } elseif (Iterator\FileTypeFilterIterator::ONLY_FILES === $this->mode) {
71
- $find->add('-type f');
72
- }
73
-
74
- $this->buildNamesFiltering($find, $this->names);
75
- $this->buildNamesFiltering($find, $this->notNames, true);
76
- $this->buildPathsFiltering($find, $dir, $this->paths);
77
- $this->buildPathsFiltering($find, $dir, $this->notPaths, true);
78
- $this->buildSizesFiltering($find, $this->sizes);
79
- $this->buildDatesFiltering($find, $this->dates);
80
-
81
- $useGrep = $this->shell->testCommand('grep') && $this->shell->testCommand('xargs');
82
- $useSort = is_int($this->sort) && $this->shell->testCommand('sort') && $this->shell->testCommand('cut');
83
-
84
- if ($useGrep && ($this->contains || $this->notContains)) {
85
- $grep = $command->ins('grep');
86
- $this->buildContentFiltering($grep, $this->contains);
87
- $this->buildContentFiltering($grep, $this->notContains, true);
88
- }
89
-
90
- if ($useSort) {
91
- $this->buildSorting($command, $this->sort);
92
- }
93
-
94
- $command->setErrorHandler(
95
- $this->ignoreUnreadableDirs
96
- // If directory is unreadable and finder is set to ignore it, `stderr` is ignored.
97
- ? function ($stderr) { return; }
98
- : function ($stderr) { throw new AccessDeniedException($stderr); }
99
- );
100
-
101
- $paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute());
102
- $iterator = new Iterator\FilePathsIterator($paths, $dir);
103
-
104
- if ($this->exclude) {
105
- $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
106
- }
107
-
108
- if (!$useGrep && ($this->contains || $this->notContains)) {
109
- $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
110
- }
111
-
112
- if ($this->filters) {
113
- $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
114
- }
115
-
116
- if (!$useSort && $this->sort) {
117
- $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
118
- $iterator = $iteratorAggregate->getIterator();
119
- }
120
-
121
- return $iterator;
122
- }
123
-
124
- /**
125
- * {@inheritdoc}
126
- */
127
- protected function canBeUsed()
128
- {
129
- return $this->shell->testCommand('find');
130
- }
131
-
132
- /**
133
- * @param Command $command
134
- * @param string $dir
135
- *
136
- * @return Command
137
- */
138
- protected function buildFindCommand(Command $command, $dir)
139
- {
140
- return $command
141
- ->ins('find')
142
- ->add('find ')
143
- ->arg($dir)
144
- ->add('-noleaf'); // the -noleaf option is required for filesystems that don't follow the '.' and '..' conventions
145
- }
146
-
147
- /**
148
- * @param Command $command
149
- * @param string[] $names
150
- * @param bool $not
151
- */
152
- private function buildNamesFiltering(Command $command, array $names, $not = false)
153
- {
154
- if (0 === count($names)) {
155
- return;
156
- }
157
-
158
- $command->add($not ? '-not' : null)->cmd('(');
159
-
160
- foreach ($names as $i => $name) {
161
- $expr = Expression::create($name);
162
-
163
- // Find does not support expandable globs ("*.{a,b}" syntax).
164
- if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
165
- $expr = Expression::create($expr->getGlob()->toRegex(false));
166
- }
167
-
168
- // Fixes 'not search' and 'full path matching' regex problems.
169
- // - Jokers '.' are replaced by [^/].
170
- // - We add '[^/]*' before and after regex (if no ^|$ flags are present).
171
- if ($expr->isRegex()) {
172
- $regex = $expr->getRegex();
173
- $regex->prepend($regex->hasStartFlag() ? '/' : '/[^/]*')
174
- ->setStartFlag(false)
175
- ->setStartJoker(true)
176
- ->replaceJokers('[^/]');
177
- if (!$regex->hasEndFlag() || $regex->hasEndJoker()) {
178
- $regex->setEndJoker(false)->append('[^/]*');
179
- }
180
- }
181
-
182
- $command
183
- ->add($i > 0 ? '-or' : null)
184
- ->add($expr->isRegex()
185
- ? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
186
- : ($expr->isCaseSensitive() ? '-name' : '-iname')
187
- )
188
- ->arg($expr->renderPattern());
189
- }
190
-
191
- $command->cmd(')');
192
- }
193
-
194
- /**
195
- * @param Command $command
196
- * @param string $dir
197
- * @param string[] $paths
198
- * @param bool $not
199
- */
200
- private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false)
201
- {
202
- if (0 === count($paths)) {
203
- return;
204
- }
205
-
206
- $command->add($not ? '-not' : null)->cmd('(');
207
-
208
- foreach ($paths as $i => $path) {
209
- $expr = Expression::create($path);
210
-
211
- // Find does not support expandable globs ("*.{a,b}" syntax).
212
- if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
213
- $expr = Expression::create($expr->getGlob()->toRegex(false));
214
- }
215
-
216
- // Fixes 'not search' regex problems.
217
- if ($expr->isRegex()) {
218
- $regex = $expr->getRegex();
219
- $regex->prepend($regex->hasStartFlag() ? preg_quote($dir).DIRECTORY_SEPARATOR : '.*')->setEndJoker(!$regex->hasEndFlag());
220
- } else {
221
- $expr->prepend('*')->append('*');
222
- }
223
-
224
- $command
225
- ->add($i > 0 ? '-or' : null)
226
- ->add($expr->isRegex()
227
- ? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
228
- : ($expr->isCaseSensitive() ? '-path' : '-ipath')
229
- )
230
- ->arg($expr->renderPattern());
231
- }
232
-
233
- $command->cmd(')');
234
- }
235
-
236
- /**
237
- * @param Command $command
238
- * @param NumberComparator[] $sizes
239
- */
240
- private function buildSizesFiltering(Command $command, array $sizes)
241
- {
242
- foreach ($sizes as $i => $size) {
243
- $command->add($i > 0 ? '-and' : null);
244
-
245
- switch ($size->getOperator()) {
246
- case '<=':
247
- $command->add('-size -'.($size->getTarget() + 1).'c');
248
- break;
249
- case '>=':
250
- $command->add('-size +'.($size->getTarget() - 1).'c');
251
- break;
252
- case '>':
253
- $command->add('-size +'.$size->getTarget().'c');
254
- break;
255
- case '!=':
256
- $command->add('-size -'.$size->getTarget().'c');
257
- $command->add('-size +'.$size->getTarget().'c');
258
- break;
259
- case '<':
260
- default:
261
- $command->add('-size -'.$size->getTarget().'c');
262
- }
263
- }
264
- }
265
-
266
- /**
267
- * @param Command $command
268
- * @param DateComparator[] $dates
269
- */
270
- private function buildDatesFiltering(Command $command, array $dates)
271
- {
272
- foreach ($dates as $i => $date) {
273
- $command->add($i > 0 ? '-and' : null);
274
-
275
- $mins = (int) round((time()-$date->getTarget()) / 60);
276
-
277
- if (0 > $mins) {
278
- // mtime is in the future
279
- $command->add(' -mmin -0');
280
- // we will have no result so we don't need to continue
281
- return;
282
- }
283
-
284
- switch ($date->getOperator()) {
285
- case '<=':
286
- $command->add('-mmin +'.($mins - 1));
287
- break;
288
- case '>=':
289
- $command->add('-mmin -'.($mins + 1));
290
- break;
291
- case '>':
292
- $command->add('-mmin -'.$mins);
293
- break;
294
- case '!=':
295
- $command->add('-mmin +'.$mins.' -or -mmin -'.$mins);
296
- break;
297
- case '<':
298
- default:
299
- $command->add('-mmin +'.$mins);
300
- }
301
- }
302
- }
303
-
304
- /**
305
- * @param Command $command
306
- * @param string $sort
307
- *
308
- * @throws \InvalidArgumentException
309
- */
310
- private function buildSorting(Command $command, $sort)
311
- {
312
- $this->buildFormatSorting($command, $sort);
313
- }
314
-
315
- /**
316
- * @param Command $command
317
- * @param string $sort
318
- */
319
- abstract protected function buildFormatSorting(Command $command, $sort);
320
-
321
- /**
322
- * @param Command $command
323
- * @param array $contains
324
- * @param bool $not
325
- */
326
- abstract protected function buildContentFiltering(Command $command, array $contains, $not = false);
327
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php DELETED
@@ -1,144 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Adapter;
13
-
14
- /**
15
- * @author Jean-François Simon <contact@jfsimon.fr>
16
- */
17
- interface AdapterInterface
18
- {
19
- /**
20
- * @param bool $followLinks
21
- *
22
- * @return AdapterInterface Current instance
23
- */
24
- public function setFollowLinks($followLinks);
25
-
26
- /**
27
- * @param int $mode
28
- *
29
- * @return AdapterInterface Current instance
30
- */
31
- public function setMode($mode);
32
-
33
- /**
34
- * @param array $exclude
35
- *
36
- * @return AdapterInterface Current instance
37
- */
38
- public function setExclude(array $exclude);
39
-
40
- /**
41
- * @param array $depths
42
- *
43
- * @return AdapterInterface Current instance
44
- */
45
- public function setDepths(array $depths);
46
-
47
- /**
48
- * @param array $names
49
- *
50
- * @return AdapterInterface Current instance
51
- */
52
- public function setNames(array $names);
53
-
54
- /**
55
- * @param array $notNames
56
- *
57
- * @return AdapterInterface Current instance
58
- */
59
- public function setNotNames(array $notNames);
60
-
61
- /**
62
- * @param array $contains
63
- *
64
- * @return AdapterInterface Current instance
65
- */
66
- public function setContains(array $contains);
67
-
68
- /**
69
- * @param array $notContains
70
- *
71
- * @return AdapterInterface Current instance
72
- */
73
- public function setNotContains(array $notContains);
74
-
75
- /**
76
- * @param array $sizes
77
- *
78
- * @return AdapterInterface Current instance
79
- */
80
- public function setSizes(array $sizes);
81
-
82
- /**
83
- * @param array $dates
84
- *
85
- * @return AdapterInterface Current instance
86
- */
87
- public function setDates(array $dates);
88
-
89
- /**
90
- * @param array $filters
91
- *
92
- * @return AdapterInterface Current instance
93
- */
94
- public function setFilters(array $filters);
95
-
96
- /**
97
- * @param \Closure|int $sort
98
- *
99
- * @return AdapterInterface Current instance
100
- */
101
- public function setSort($sort);
102
-
103
- /**
104
- * @param array $paths
105
- *
106
- * @return AdapterInterface Current instance
107
- */
108
- public function setPath(array $paths);
109
-
110
- /**
111
- * @param array $notPaths
112
- *
113
- * @return AdapterInterface Current instance
114
- */
115
- public function setNotPath(array $notPaths);
116
-
117
- /**
118
- * @param bool $ignore
119
- *
120
- * @return AdapterInterface Current instance
121
- */
122
- public function ignoreUnreadableDirs($ignore = true);
123
-
124
- /**
125
- * @param string $dir
126
- *
127
- * @return \Iterator Result iterator
128
- */
129
- public function searchInDirectory($dir);
130
-
131
- /**
132
- * Tests adapter support for current platform.
133
- *
134
- * @return bool
135
- */
136
- public function isSupported();
137
-
138
- /**
139
- * Returns adapter name.
140
- *
141
- * @return string
142
- */
143
- public function getName();
144
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php DELETED
@@ -1,103 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Adapter;
13
-
14
- use Symfony\Component\Finder\Shell\Shell;
15
- use Symfony\Component\Finder\Shell\Command;
16
- use Symfony\Component\Finder\Iterator\SortableIterator;
17
- use Symfony\Component\Finder\Expression\Expression;
18
-
19
- /**
20
- * Shell engine implementation using BSD find command.
21
- *
22
- * @author Jean-François Simon <contact@jfsimon.fr>
23
- */
24
- class BsdFindAdapter extends AbstractFindAdapter
25
- {
26
- /**
27
- * {@inheritdoc}
28
- */
29
- public function getName()
30
- {
31
- return 'bsd_find';
32
- }
33
-
34
- /**
35
- * {@inheritdoc}
36
- */
37
- protected function canBeUsed()
38
- {
39
- return in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::canBeUsed();
40
- }
41
-
42
- /**
43
- * {@inheritdoc}
44
- */
45
- protected function buildFormatSorting(Command $command, $sort)
46
- {
47
- switch ($sort) {
48
- case SortableIterator::SORT_BY_NAME:
49
- $command->ins('sort')->add('| sort');
50
-
51
- return;
52
- case SortableIterator::SORT_BY_TYPE:
53
- $format = '%HT';
54
- break;
55
- case SortableIterator::SORT_BY_ACCESSED_TIME:
56
- $format = '%a';
57
- break;
58
- case SortableIterator::SORT_BY_CHANGED_TIME:
59
- $format = '%c';
60
- break;
61
- case SortableIterator::SORT_BY_MODIFIED_TIME:
62
- $format = '%m';
63
- break;
64
- default:
65
- throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort));
66
- }
67
-
68
- $command
69
- ->add('-print0 | xargs -0 stat -f')
70
- ->arg($format.'%t%N')
71
- ->add('| sort | cut -f 2');
72
- }
73
-
74
- /**
75
- * {@inheritdoc}
76
- */
77
- protected function buildFindCommand(Command $command, $dir)
78
- {
79
- parent::buildFindCommand($command, $dir)->addAtIndex('-E', 1);
80
-
81
- return $command;
82
- }
83
-
84
- /**
85
- * {@inheritdoc}
86
- */
87
- protected function buildContentFiltering(Command $command, array $contains, $not = false)
88
- {
89
- foreach ($contains as $contain) {
90
- $expr = Expression::create($contain);
91
-
92
- // todo: avoid forking process for each $pattern by using multiple -e options
93
- $command
94
- ->add('| grep -v \'^$\'')
95
- ->add('| xargs -I{} grep -I')
96
- ->add($expr->isCaseSensitive() ? null : '-i')
97
- ->add($not ? '-L' : '-l')
98
- ->add('-Ee')->arg($expr->renderPattern())
99
- ->add('{}')
100
- ;
101
- }
102
- }
103
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php DELETED
@@ -1,104 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Adapter;
13
-
14
- use Symfony\Component\Finder\Shell\Shell;
15
- use Symfony\Component\Finder\Shell\Command;
16
- use Symfony\Component\Finder\Iterator\SortableIterator;
17
- use Symfony\Component\Finder\Expression\Expression;
18
-
19
- /**
20
- * Shell engine implementation using GNU find command.
21
- *
22
- * @author Jean-François Simon <contact@jfsimon.fr>
23
- */
24
- class GnuFindAdapter extends AbstractFindAdapter
25
- {
26
- /**
27
- * {@inheritdoc}
28
- */
29
- public function getName()
30
- {
31
- return 'gnu_find';
32
- }
33
-
34
- /**
35
- * {@inheritdoc}
36
- */
37
- protected function buildFormatSorting(Command $command, $sort)
38
- {
39
- switch ($sort) {
40
- case SortableIterator::SORT_BY_NAME:
41
- $command->ins('sort')->add('| sort');
42
-
43
- return;
44
- case SortableIterator::SORT_BY_TYPE:
45
- $format = '%y';
46
- break;
47
- case SortableIterator::SORT_BY_ACCESSED_TIME:
48
- $format = '%A@';
49
- break;
50
- case SortableIterator::SORT_BY_CHANGED_TIME:
51
- $format = '%C@';
52
- break;
53
- case SortableIterator::SORT_BY_MODIFIED_TIME:
54
- $format = '%T@';
55
- break;
56
- default:
57
- throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort));
58
- }
59
-
60
- $command
61
- ->get('find')
62
- ->add('-printf')
63
- ->arg($format.' %h/%f\\n')
64
- ->add('| sort | cut')
65
- ->arg('-d ')
66
- ->arg('-f2-')
67
- ;
68
- }
69
-
70
- /**
71
- * {@inheritdoc}
72
- */
73
- protected function canBeUsed()
74
- {
75
- return $this->shell->getType() === Shell::TYPE_UNIX && parent::canBeUsed();
76
- }
77
-
78
- /**
79
- * {@inheritdoc}
80
- */
81
- protected function buildFindCommand(Command $command, $dir)
82
- {
83
- return parent::buildFindCommand($command, $dir)->add('-regextype posix-extended');
84
- }
85
-
86
- /**
87
- * {@inheritdoc}
88
- */
89
- protected function buildContentFiltering(Command $command, array $contains, $not = false)
90
- {
91
- foreach ($contains as $contain) {
92
- $expr = Expression::create($contain);
93
-
94
- // todo: avoid forking process for each $pattern by using multiple -e options
95
- $command
96
- ->add('| xargs -I{} -r grep -I')
97
- ->add($expr->isCaseSensitive() ? null : '-i')
98
- ->add($not ? '-L' : '-l')
99
- ->add('-Ee')->arg($expr->renderPattern())
100
- ->add('{}')
101
- ;
102
- }
103
- }
104
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php DELETED
@@ -1,98 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Adapter;
13
-
14
- use Symfony\Component\Finder\Iterator;
15
-
16
- /**
17
- * PHP finder engine implementation.
18
- *
19
- * @author Jean-François Simon <contact@jfsimon.fr>
20
- */
21
- class PhpAdapter extends AbstractAdapter
22
- {
23
- /**
24
- * {@inheritdoc}
25
- */
26
- public function searchInDirectory($dir)
27
- {
28
- $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
29
-
30
- if ($this->followLinks) {
31
- $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
32
- }
33
-
34
- $iterator = new \RecursiveIteratorIterator(
35
- new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs),
36
- \RecursiveIteratorIterator::SELF_FIRST
37
- );
38
-
39
- if ($this->minDepth > 0 || $this->maxDepth < PHP_INT_MAX) {
40
- $iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->minDepth, $this->maxDepth);
41
- }
42
-
43
- if ($this->mode) {
44
- $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
45
- }
46
-
47
- if ($this->exclude) {
48
- $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
49
- }
50
-
51
- if ($this->names || $this->notNames) {
52
- $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
53
- }
54
-
55
- if ($this->contains || $this->notContains) {
56
- $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
57
- }
58
-
59
- if ($this->sizes) {
60
- $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
61
- }
62
-
63
- if ($this->dates) {
64
- $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
65
- }
66
-
67
- if ($this->filters) {
68
- $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
69
- }
70
-
71
- if ($this->sort) {
72
- $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
73
- $iterator = $iteratorAggregate->getIterator();
74
- }
75
-
76
- if ($this->paths || $this->notPaths) {
77
- $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
78
- }
79
-
80
- return $iterator;
81
- }
82
-
83
- /**
84
- * {@inheritdoc}
85
- */
86
- public function getName()
87
- {
88
- return 'php';
89
- }
90
-
91
- /**
92
- * {@inheritdoc}
93
- */
94
- protected function canBeUsed()
95
- {
96
- return true;
97
- }
98
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/CHANGELOG.md DELETED
@@ -1,34 +0,0 @@
1
- CHANGELOG
2
- =========
3
-
4
- 2.5.0
5
- -----
6
- * added support for GLOB_BRACE in the paths passed to Finder::in()
7
-
8
- 2.3.0
9
- -----
10
-
11
- * added a way to ignore unreadable directories (via Finder::ignoreUnreadableDirs())
12
- * unified the way subfolders that are not executable are handled by always throwing an AccessDeniedException exception
13
-
14
- 2.2.0
15
- -----
16
-
17
- * added Finder::path() and Finder::notPath() methods
18
- * added finder adapters to improve performance on specific platforms
19
- * added support for wildcard characters (glob patterns) in the paths passed
20
- to Finder::in()
21
-
22
- 2.1.0
23
- -----
24
-
25
- * added Finder::sortByAccessedTime(), Finder::sortByChangedTime(), and
26
- Finder::sortByModifiedTime()
27
- * added Countable to Finder
28
- * added support for an array of directories as an argument to
29
- Finder::exclude()
30
- * added searching based on the file content via Finder::contains() and
31
- Finder::notContains()
32
- * added support for the != operator in the Comparator
33
- * [BC BREAK] filter expressions (used for file name and content) are no more
34
- considered as regexps but glob patterns when they are enclosed in '*' or '?'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php DELETED
@@ -1,98 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Comparator;
13
-
14
- /**
15
- * Comparator.
16
- *
17
- * @author Fabien Potencier <fabien@symfony.com>
18
- */
19
- class Comparator
20
- {
21
- private $target;
22
- private $operator = '==';
23
-
24
- /**
25
- * Gets the target value.
26
- *
27
- * @return string The target value
28
- */
29
- public function getTarget()
30
- {
31
- return $this->target;
32
- }
33
-
34
- /**
35
- * Sets the target value.
36
- *
37
- * @param string $target The target value
38
- */
39
- public function setTarget($target)
40
- {
41
- $this->target = $target;
42
- }
43
-
44
- /**
45
- * Gets the comparison operator.
46
- *
47
- * @return string The operator
48
- */
49
- public function getOperator()
50
- {
51
- return $this->operator;
52
- }
53
-
54
- /**
55
- * Sets the comparison operator.
56
- *
57
- * @param string $operator A valid operator
58
- *
59
- * @throws \InvalidArgumentException
60
- */
61
- public function setOperator($operator)
62
- {
63
- if (!$operator) {
64
- $operator = '==';
65
- }
66
-
67
- if (!in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) {
68
- throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
69
- }
70
-
71
- $this->operator = $operator;
72
- }
73
-
74
- /**
75
- * Tests against the target.
76
- *
77
- * @param mixed $test A test value
78
- *
79
- * @return bool
80
- */
81
- public function test($test)
82
- {
83
- switch ($this->operator) {
84
- case '>':
85
- return $test > $this->target;
86
- case '>=':
87
- return $test >= $this->target;
88
- case '<':
89
- return $test < $this->target;
90
- case '<=':
91
- return $test <= $this->target;
92
- case '!=':
93
- return $test != $this->target;
94
- }
95
-
96
- return $test == $this->target;
97
- }
98
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php DELETED
@@ -1,53 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Comparator;
13
-
14
- /**
15
- * DateCompare compiles date comparisons.
16
- *
17
- * @author Fabien Potencier <fabien@symfony.com>
18
- */
19
- class DateComparator extends Comparator
20
- {
21
- /**
22
- * Constructor.
23
- *
24
- * @param string $test A comparison string
25
- *
26
- * @throws \InvalidArgumentException If the test is not understood
27
- */
28
- public function __construct($test)
29
- {
30
- if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) {
31
- throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test));
32
- }
33
-
34
- try {
35
- $date = new \DateTime($matches[2]);
36
- $target = $date->format('U');
37
- } catch (\Exception $e) {
38
- throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2]));
39
- }
40
-
41
- $operator = isset($matches[1]) ? $matches[1] : '==';
42
- if ('since' === $operator || 'after' === $operator) {
43
- $operator = '>';
44
- }
45
-
46
- if ('until' === $operator || 'before' === $operator) {
47
- $operator = '<';
48
- }
49
-
50
- $this->setOperator($operator);
51
- $this->setTarget($target);
52
- }
53
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php DELETED
@@ -1,81 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Comparator;
13
-
14
- /**
15
- * NumberComparator compiles a simple comparison to an anonymous
16
- * subroutine, which you can call with a value to be tested again.
17
- *
18
- * Now this would be very pointless, if NumberCompare didn't understand
19
- * magnitudes.
20
- *
21
- * The target value may use magnitudes of kilobytes (k, ki),
22
- * megabytes (m, mi), or gigabytes (g, gi). Those suffixed
23
- * with an i use the appropriate 2**n version in accordance with the
24
- * IEC standard: http://physics.nist.gov/cuu/Units/binary.html
25
- *
26
- * Based on the Perl Number::Compare module.
27
- *
28
- * @author Fabien Potencier <fabien@symfony.com> PHP port
29
- * @author Richard Clamp <richardc@unixbeard.net> Perl version
30
- * @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>
31
- * @copyright 2002 Richard Clamp <richardc@unixbeard.net>
32
- *
33
- * @see http://physics.nist.gov/cuu/Units/binary.html
34
- */
35
- class NumberComparator extends Comparator
36
- {
37
- /**
38
- * Constructor.
39
- *
40
- * @param string $test A comparison string
41
- *
42
- * @throws \InvalidArgumentException If the test is not understood
43
- */
44
- public function __construct($test)
45
- {
46
- if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
47
- throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test));
48
- }
49
-
50
- $target = $matches[2];
51
- if (!is_numeric($target)) {
52
- throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target));
53
- }
54
- if (isset($matches[3])) {
55
- // magnitude
56
- switch (strtolower($matches[3])) {
57
- case 'k':
58
- $target *= 1000;
59
- break;
60
- case 'ki':
61
- $target *= 1024;
62
- break;
63
- case 'm':
64
- $target *= 1000000;
65
- break;
66
- case 'mi':
67
- $target *= 1024*1024;
68
- break;
69
- case 'g':
70
- $target *= 1000000000;
71
- break;
72
- case 'gi':
73
- $target *= 1024*1024*1024;
74
- break;
75
- }
76
- }
77
-
78
- $this->setTarget($target);
79
- $this->setOperator(isset($matches[1]) ? $matches[1] : '==');
80
- }
81
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php DELETED
@@ -1,19 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Exception;
13
-
14
- /**
15
- * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
16
- */
17
- class AccessDeniedException extends \UnexpectedValueException
18
- {
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php DELETED
@@ -1,46 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Exception;
13
-
14
- use Symfony\Component\Finder\Adapter\AdapterInterface;
15
-
16
- /**
17
- * Base exception for all adapter failures.
18
- *
19
- * @author Jean-François Simon <contact@jfsimon.fr>
20
- */
21
- class AdapterFailureException extends \RuntimeException implements ExceptionInterface
22
- {
23
- /**
24
- * @var \Symfony\Component\Finder\Adapter\AdapterInterface
25
- */
26
- private $adapter;
27
-
28
- /**
29
- * @param AdapterInterface $adapter
30
- * @param string|null $message
31
- * @param \Exception|null $previous
32
- */
33
- public function __construct(AdapterInterface $adapter, $message = null, \Exception $previous = null)
34
- {
35
- $this->adapter = $adapter;
36
- parent::__construct($message ?: 'Search failed with "'.$adapter->getName().'" adapter.', $previous);
37
- }
38
-
39
- /**
40
- * {@inheritdoc}
41
- */
42
- public function getAdapter()
43
- {
44
- return $this->adapter;
45
- }
46
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php DELETED
@@ -1,23 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Exception;
13
-
14
- /**
15
- * @author Jean-François Simon <contact@jfsimon.fr>
16
- */
17
- interface ExceptionInterface
18
- {
19
- /**
20
- * @return \Symfony\Component\Finder\Adapter\AdapterInterface
21
- */
22
- public function getAdapter();
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php DELETED
@@ -1,19 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Exception;
13
-
14
- /**
15
- * @author Jean-François Simon <contact@jfsimon.fr>
16
- */
17
- class OperationNotPermitedException extends AdapterFailureException
18
- {
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php DELETED
@@ -1,45 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Exception;
13
-
14
- use Symfony\Component\Finder\Adapter\AdapterInterface;
15
- use Symfony\Component\Finder\Shell\Command;
16
-
17
- /**
18
- * @author Jean-François Simon <contact@jfsimon.fr>
19
- */
20
- class ShellCommandFailureException extends AdapterFailureException
21
- {
22
- /**
23
- * @var Command
24
- */
25
- private $command;
26
-
27
- /**
28
- * @param AdapterInterface $adapter
29
- * @param Command $command
30
- * @param \Exception|null $previous
31
- */
32
- public function __construct(AdapterInterface $adapter, Command $command, \Exception $previous = null)
33
- {
34
- $this->command = $command;
35
- parent::__construct($adapter, 'Shell command failed: "'.$command->join().'".', $previous);
36
- }
37
-
38
- /**
39
- * @return Command
40
- */
41
- public function getCommand()
42
- {
43
- return $this->command;
44
- }
45
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Expression/Expression.php DELETED
@@ -1,146 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Expression;
13
-
14
- /**
15
- * @author Jean-François Simon <contact@jfsimon.fr>
16
- */
17
- class Expression implements ValueInterface
18
- {
19
- const TYPE_REGEX = 1;
20
- const TYPE_GLOB = 2;
21
-
22
- /**
23
- * @var ValueInterface
24
- */
25
- private $value;
26
-
27
- /**
28
- * @param string $expr
29
- *
30
- * @return Expression
31
- */
32
- public static function create($expr)
33
- {
34
- return new self($expr);
35
- }
36
-
37
- /**
38
- * @param string $expr
39
- */
40
- public function __construct($expr)
41
- {
42
- try {
43
- $this->value = Regex::create($expr);
44
- } catch (\InvalidArgumentException $e) {
45
- $this->value = new Glob($expr);
46
- }
47
- }
48
-
49
- /**
50
- * @return string
51
- */
52
- public function __toString()
53
- {
54
- return $this->render();
55
- }
56
-
57
- /**
58
- * {@inheritdoc}
59
- */
60
- public function render()
61
- {
62
- return $this->value->render();
63
- }
64
-
65
- /**
66
- * {@inheritdoc}
67
- */
68
- public function renderPattern()
69
- {
70
- return $this->value->renderPattern();
71
- }
72
-
73
- /**
74
- * @return bool
75
- */
76
- public function isCaseSensitive()
77
- {
78
- return $this->value->isCaseSensitive();
79
- }
80
-
81
- /**
82
- * @return int
83
- */
84
- public function getType()
85
- {
86
- return $this->value->getType();
87
- }
88
-
89
- /**
90
- * {@inheritdoc}
91
- */
92
- public function prepend($expr)
93
- {
94
- $this->value->prepend($expr);
95
-
96
- return $this;
97
- }
98
-
99
- /**
100
- * {@inheritdoc}
101
- */
102
- public function append($expr)
103
- {
104
- $this->value->append($expr);
105
-
106
- return $this;
107
- }
108
-
109
- /**
110
- * @return bool
111
- */
112
- public function isRegex()
113
- {
114
- return self::TYPE_REGEX === $this->value->getType();
115
- }
116
-
117
- /**
118
- * @return bool
119
- */
120
- public function isGlob()
121
- {
122
- return self::TYPE_GLOB === $this->value->getType();
123
- }
124
-
125
- /**
126
- * @throws \LogicException
127
- *
128
- * @return Glob
129
- */
130
- public function getGlob()
131
- {
132
- if (self::TYPE_GLOB !== $this->value->getType()) {
133
- throw new \LogicException('Regex can\'t be transformed to glob.');
134
- }
135
-
136
- return $this->value;
137
- }
138
-
139
- /**
140
- * @return Regex
141
- */
142
- public function getRegex()
143
- {
144
- return self::TYPE_REGEX === $this->value->getType() ? $this->value : $this->value->toRegex();
145
- }
146
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Expression/Glob.php DELETED
@@ -1,157 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Expression;
13
-
14
- /**
15
- * @author Jean-François Simon <contact@jfsimon.fr>
16
- */
17
- class Glob implements ValueInterface
18
- {
19
- /**
20
- * @var string
21
- */
22
- private $pattern;
23
-
24
- /**
25
- * @param string $pattern
26
- */
27
- public function __construct($pattern)
28
- {
29
- $this->pattern = $pattern;
30
- }
31
-
32
- /**
33
- * {@inheritdoc}
34
- */
35
- public function render()
36
- {
37
- return $this->pattern;
38
- }
39
-
40
- /**
41
- * {@inheritdoc}
42
- */
43
- public function renderPattern()
44
- {
45
- return $this->pattern;
46
- }
47
-
48
- /**
49
- * {@inheritdoc}
50
- */
51
- public function getType()
52
- {
53
- return Expression::TYPE_GLOB;
54
- }
55
-
56
- /**
57
- * {@inheritdoc}
58
- */
59
- public function isCaseSensitive()
60
- {
61
- return true;
62
- }
63
-
64
- /**
65
- * {@inheritdoc}
66
- */
67
- public function prepend($expr)
68
- {
69
- $this->pattern = $expr.$this->pattern;
70
-
71
- return $this;
72
- }
73
-
74
- /**
75
- * {@inheritdoc}
76
- */
77
- public function append($expr)
78
- {
79
- $this->pattern .= $expr;
80
-
81
- return $this;
82
- }
83
-
84
- /**
85
- * Tests if glob is expandable ("*.{a,b}" syntax).
86
- *
87
- * @return bool
88
- */
89
- public function isExpandable()
90
- {
91
- return false !== strpos($this->pattern, '{')
92
- && false !== strpos($this->pattern, '}');
93
- }
94
-
95
- /**
96
- * @param bool $strictLeadingDot
97
- * @param bool $strictWildcardSlash
98
- *
99
- * @return Regex
100
- */
101
- public function toRegex($strictLeadingDot = true, $strictWildcardSlash = true)
102
- {
103
- $firstByte = true;
104
- $escaping = false;
105
- $inCurlies = 0;
106
- $regex = '';
107
- $sizeGlob = strlen($this->pattern);
108
- for ($i = 0; $i < $sizeGlob; $i++) {
109
- $car = $this->pattern[$i];
110
- if ($firstByte) {
111
- if ($strictLeadingDot && '.' !== $car) {
112
- $regex .= '(?=[^\.])';
113
- }
114
-
115
- $firstByte = false;
116
- }
117
-
118
- if ('/' === $car) {
119
- $firstByte = true;
120
- }
121
-
122
- if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
123
- $regex .= "\\$car";
124
- } elseif ('*' === $car) {
125
- $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
126
- } elseif ('?' === $car) {
127
- $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
128
- } elseif ('{' === $car) {
129
- $regex .= $escaping ? '\\{' : '(';
130
- if (!$escaping) {
131
- ++$inCurlies;
132
- }
133
- } elseif ('}' === $car && $inCurlies) {
134
- $regex .= $escaping ? '}' : ')';
135
- if (!$escaping) {
136
- --$inCurlies;
137
- }
138
- } elseif (',' === $car && $inCurlies) {
139
- $regex .= $escaping ? ',' : '|';
140
- } elseif ('\\' === $car) {
141
- if ($escaping) {
142
- $regex .= '\\\\';
143
- $escaping = false;
144
- } else {
145
- $escaping = true;
146
- }
147
-
148
- continue;
149
- } else {
150
- $regex .= $car;
151
- }
152
- $escaping = false;
153
- }
154
-
155
- return new Regex('^'.$regex.'$');
156
- }
157
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Expression/Regex.php DELETED
@@ -1,321 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Expression;
13
-
14
- /**
15
- * @author Jean-François Simon <contact@jfsimon.fr>
16
- */
17
- class Regex implements ValueInterface
18
- {
19
- const START_FLAG = '^';
20
- const END_FLAG = '$';
21
- const BOUNDARY = '~';
22
- const JOKER = '.*';
23
- const ESCAPING = '\\';
24
-
25
- /**
26
- * @var string
27
- */
28
- private $pattern;
29
-
30
- /**
31
- * @var array
32
- */
33
- private $options;
34
-
35
- /**
36
- * @var bool
37
- */
38
- private $startFlag;
39
-
40
- /**
41
- * @var bool
42
- */
43
- private $endFlag;
44
-
45
- /**
46
- * @var bool
47
- */
48
- private $startJoker;
49
-
50
- /**
51
- * @var bool
52
- */
53
- private $endJoker;
54
-
55
- /**
56
- * @param string $expr
57
- *
58
- * @return Regex
59
- *
60
- * @throws \InvalidArgumentException
61
- */
62
- public static function create($expr)
63
- {
64
- if (preg_match('/^(.{3,}?)([imsxuADU]*)$/', $expr, $m)) {
65
- $start = substr($m[1], 0, 1);
66
- $end = substr($m[1], -1);
67
-
68
- if (
69
- ($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start))
70
- || ($start === '{' && $end === '}')
71
- || ($start === '(' && $end === ')')
72
- ) {
73
- return new self(substr($m[1], 1, -1), $m[2], $end);
74
- }
75
- }
76
-
77
- throw new \InvalidArgumentException('Given expression is not a regex.');
78
- }
79
-
80
- /**
81
- * @param string $pattern
82
- * @param string $options
83
- * @param string $delimiter
84
- */
85
- public function __construct($pattern, $options = '', $delimiter = null)
86
- {
87
- if (null !== $delimiter) {
88
- // removes delimiter escaping
89
- $pattern = str_replace('\\'.$delimiter, $delimiter, $pattern);
90
- }
91
-
92
- $this->parsePattern($pattern);
93
- $this->options = $options;
94
- }
95
-
96
- /**
97
- * @return string
98
- */
99
- public function __toString()
100
- {
101
- return $this->render();
102
- }
103
-
104
- /**
105
- * {@inheritdoc}
106
- */
107
- public function render()
108
- {
109
- return self::BOUNDARY
110
- .$this->renderPattern()
111
- .self::BOUNDARY
112
- .$this->options;
113
- }
114
-
115
- /**
116
- * {@inheritdoc}
117
- */
118
- public function renderPattern()
119
- {
120
- return ($this->startFlag ? self::START_FLAG : '')
121
- .($this->startJoker ? self::JOKER : '')
122
- .str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern)
123
- .($this->endJoker ? self::JOKER : '')
124
- .($this->endFlag ? self::END_FLAG : '');
125
- }
126
-
127
- /**
128
- * {@inheritdoc}
129
- */
130
- public function isCaseSensitive()
131
- {
132
- return !$this->hasOption('i');
133
- }
134
-
135
- /**
136
- * {@inheritdoc}
137
- */
138
- public function getType()
139
- {
140
- return Expression::TYPE_REGEX;
141
- }
142
-
143
- /**
144
- * {@inheritdoc}
145
- */
146
- public function prepend($expr)
147
- {
148
- $this->pattern = $expr.$this->pattern;
149
-
150
- return $this;
151
- }
152
-
153
- /**
154
- * {@inheritdoc}
155
- */
156
- public function append($expr)
157
- {
158
- $this->pattern .= $expr;
159
-
160
- return $this;
161
- }
162
-
163
- /**
164
- * @param string $option
165
- *
166
- * @return bool
167
- */
168
- public function hasOption($option)
169
- {
170
- return false !== strpos($this->options, $option);
171
- }
172
-
173
- /**
174
- * @param string $option
175
- *
176
- * @return Regex
177
- */
178
- public function addOption($option)
179
- {
180
- if (!$this->hasOption($option)) {
181
- $this->options .= $option;
182
- }
183
-
184
- return $this;
185
- }
186
-
187
- /**
188
- * @param string $option
189
- *
190
- * @return Regex
191
- */
192
- public function removeOption($option)
193
- {
194
- $this->options = str_replace($option, '', $this->options);
195
-
196
- return $this;
197
- }
198
-
199
- /**
200
- * @param bool $startFlag
201
- *
202
- * @return Regex
203
- */
204
- public function setStartFlag($startFlag)
205
- {
206
- $this->startFlag = $startFlag;
207
-
208
- return $this;
209
- }
210
-
211
- /**
212
- * @return bool
213
- */
214
- public function hasStartFlag()
215
- {
216
- return $this->startFlag;
217
- }
218
-
219
- /**
220
- * @param bool $endFlag
221
- *
222
- * @return Regex
223
- */
224
- public function setEndFlag($endFlag)
225
- {
226
- $this->endFlag = (bool) $endFlag;
227
-
228
- return $this;
229
- }
230
-
231
- /**
232
- * @return bool
233
- */
234
- public function hasEndFlag()
235
- {
236
- return $this->endFlag;
237
- }
238
-
239
- /**
240
- * @param bool $startJoker
241
- *
242
- * @return Regex
243
- */
244
- public function setStartJoker($startJoker)
245
- {
246
- $this->startJoker = $startJoker;
247
-
248
- return $this;
249
- }
250
-
251
- /**
252
- * @return bool
253
- */
254
- public function hasStartJoker()
255
- {
256
- return $this->startJoker;
257
- }
258
-
259
- /**
260
- * @param bool $endJoker
261
- *
262
- * @return Regex
263
- */
264
- public function setEndJoker($endJoker)
265
- {
266
- $this->endJoker = (bool) $endJoker;
267
-
268
- return $this;
269
- }
270
-
271
- /**
272
- * @return bool
273
- */
274
- public function hasEndJoker()
275
- {
276
- return $this->endJoker;
277
- }
278
-
279
- /**
280
- * @param array $replacement
281
- *
282
- * @return Regex
283
- */
284
- public function replaceJokers($replacement)
285
- {
286
- $replace = function ($subject) use ($replacement) {
287
- $subject = $subject[0];
288
- $replace = 0 === substr_count($subject, '\\') % 2;
289
-
290
- return $replace ? str_replace('.', $replacement, $subject) : $subject;
291
- };
292
-
293
- $this->pattern = preg_replace_callback('~[\\\\]*\\.~', $replace, $this->pattern);
294
-
295
- return $this;
296
- }
297
-
298
- /**
299
- * @param string $pattern
300
- */
301
- private function parsePattern($pattern)
302
- {
303
- if ($this->startFlag = self::START_FLAG === substr($pattern, 0, 1)) {
304
- $pattern = substr($pattern, 1);
305
- }
306
-
307
- if ($this->startJoker = self::JOKER === substr($pattern, 0, 2)) {
308
- $pattern = substr($pattern, 2);
309
- }
310
-
311
- if ($this->endFlag = (self::END_FLAG === substr($pattern, -1) && self::ESCAPING !== substr($pattern, -2, -1))) {
312
- $pattern = substr($pattern, 0, -1);
313
- }
314
-
315
- if ($this->endJoker = (self::JOKER === substr($pattern, -2) && self::ESCAPING !== substr($pattern, -3, -2))) {
316
- $pattern = substr($pattern, 0, -2);
317
- }
318
-
319
- $this->pattern = $pattern;
320
- }
321
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php DELETED
@@ -1,60 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Expression;
13
-
14
- /**
15
- * @author Jean-François Simon <contact@jfsimon.fr>
16
- */
17
- interface ValueInterface
18
- {
19
- /**
20
- * Renders string representation of expression.
21
- *
22
- * @return string
23
- */
24
- public function render();
25
-
26
- /**
27
- * Renders string representation of pattern.
28
- *
29
- * @return string
30
- */
31
- public function renderPattern();
32
-
33
- /**
34
- * Returns value case sensitivity.
35
- *
36
- * @return bool
37
- */
38
- public function isCaseSensitive();
39
-
40
- /**
41
- * Returns expression type.
42
- *
43
- * @return int
44
- */
45
- public function getType();
46
-
47
- /**
48
- * @param string $expr
49
- *
50
- * @return ValueInterface
51
- */
52
- public function prepend($expr);
53
-
54
- /**
55
- * @param string $expr
56
- *
57
- * @return ValueInterface
58
- */
59
- public function append($expr);
60
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Finder.php DELETED
@@ -1,840 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder;
13
-
14
- use Symfony\Component\Finder\Adapter\AdapterInterface;
15
- use Symfony\Component\Finder\Adapter\GnuFindAdapter;
16
- use Symfony\Component\Finder\Adapter\BsdFindAdapter;
17
- use Symfony\Component\Finder\Adapter\PhpAdapter;
18
- use Symfony\Component\Finder\Comparator\DateComparator;
19
- use Symfony\Component\Finder\Comparator\NumberComparator;
20
- use Symfony\Component\Finder\Exception\ExceptionInterface;
21
- use Symfony\Component\Finder\Iterator\CustomFilterIterator;
22
- use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
23
- use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
24
- use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
25
- use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
26
- use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
27
- use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
28
- use Symfony\Component\Finder\Iterator\SortableIterator;
29
-
30
- /**
31
- * Finder allows to build rules to find files and directories.
32
- *
33
- * It is a thin wrapper around several specialized iterator classes.
34
- *
35
- * All rules may be invoked several times.
36
- *
37
- * All methods return the current Finder object to allow easy chaining:
38
- *
39
- * $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
40
- *
41
- * @author Fabien Potencier <fabien@symfony.com>
42
- *
43
- * @api
44
- */
45
- class Finder implements \IteratorAggregate, \Countable
46
- {
47
- const IGNORE_VCS_FILES = 1;
48
- const IGNORE_DOT_FILES = 2;
49
-
50
- private $mode = 0;
51
- private $names = array();
52
- private $notNames = array();
53
- private $exclude = array();
54
- private $filters = array();
55
- private $depths = array();
56
- private $sizes = array();
57
- private $followLinks = false;
58
- private $sort = false;
59
- private $ignore = 0;
60
- private $dirs = array();
61
- private $dates = array();
62
- private $iterators = array();
63
- private $contains = array();
64
- private $notContains = array();
65
- private $adapters = array();
66
- private $paths = array();
67
- private $notPaths = array();
68
- private $ignoreUnreadableDirs = false;
69
-
70
- private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
71
-
72
- /**
73
- * Constructor.
74
- */
75
- public function __construct()
76
- {
77
- $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
78
-
79
- $this
80
- ->addAdapter(new GnuFindAdapter())
81
- ->addAdapter(new BsdFindAdapter())
82
- ->addAdapter(new PhpAdapter(), -50)
83
- ->setAdapter('php')
84
- ;
85
- }
86
-
87
- /**
88
- * Creates a new Finder.
89
- *
90
- * @return Finder A new Finder instance
91
- *
92
- * @api
93
- */
94
- public static function create()
95
- {
96
- return new static();
97
- }
98
-
99
- /**
100
- * Registers a finder engine implementation.
101
- *
102
- * @param AdapterInterface $adapter An adapter instance
103
- * @param int $priority Highest is selected first
104
- *
105
- * @return Finder The current Finder instance
106
- */
107
- public function addAdapter(AdapterInterface $adapter, $priority = 0)
108
- {
109
- $this->adapters[$adapter->getName()] = array(
110
- 'adapter' => $adapter,
111
- 'priority' => $priority,
112
- 'selected' => false,
113
- );
114
-
115
- return $this->sortAdapters();
116
- }
117
-
118
- /**
119
- * Sets the selected adapter to the best one according to the current platform the code is run on.
120
- *
121
- * @return Finder The current Finder instance
122
- */
123
- public function useBestAdapter()
124
- {
125
- $this->resetAdapterSelection();
126
-
127
- return $this->sortAdapters();
128
- }
129
-
130
- /**
131
- * Selects the adapter to use.
132
- *
133
- * @param string $name
134
- *
135
- * @throws \InvalidArgumentException
136
- *
137
- * @return Finder The current Finder instance
138
- */
139
- public function setAdapter($name)
140
- {
141
- if (!isset($this->adapters[$name])) {
142
- throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name));
143
- }
144
-
145
- $this->resetAdapterSelection();
146
- $this->adapters[$name]['selected'] = true;
147
-
148
- return $this->sortAdapters();
149
- }
150
-
151
- /**
152
- * Removes all adapters registered in the finder.
153
- *
154
- * @return Finder The current Finder instance
155
- */
156
- public function removeAdapters()
157
- {
158
- $this->adapters = array();
159
-
160
- return $this;
161
- }
162
-
163
- /**
164
- * Returns registered adapters ordered by priority without extra information.
165
- *
166
- * @return AdapterInterface[]
167
- */
168
- public function getAdapters()
169
- {
170
- return array_values(array_map(function (array $adapter) {
171
- return $adapter['adapter'];
172
- }, $this->adapters));
173
- }
174
-
175
- /**
176
- * Restricts the matching to directories only.
177
- *
178
- * @return Finder The current Finder instance
179
- *
180
- * @api
181
- */
182
- public function directories()
183
- {
184
- $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
185
-
186
- return $this;
187
- }
188
-
189
- /**
190
- * Restricts the matching to files only.
191
- *
192
- * @return Finder The current Finder instance
193
- *
194
- * @api
195
- */
196
- public function files()
197
- {
198
- $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
199
-
200
- return $this;
201
- }
202
-
203
- /**
204
- * Adds tests for the directory depth.
205
- *
206
- * Usage:
207
- *
208
- * $finder->depth('> 1') // the Finder will start matching at level 1.
209
- * $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
210
- *
211
- * @param int $level The depth level expression
212
- *
213
- * @return Finder The current Finder instance
214
- *
215
- * @see DepthRangeFilterIterator
216
- * @see NumberComparator
217
- *
218
- * @api
219
- */
220
- public function depth($level)
221
- {
222
- $this->depths[] = new Comparator\NumberComparator($level);
223
-
224
- return $this;
225
- }
226
-
227
- /**
228
- * Adds tests for file dates (last modified).
229
- *
230
- * The date must be something that strtotime() is able to parse:
231
- *
232
- * $finder->date('since yesterday');
233
- * $finder->date('until 2 days ago');
234
- * $finder->date('> now - 2 hours');
235
- * $finder->date('>= 2005-10-15');
236
- *
237
- * @param string $date A date rage string
238
- *
239
- * @return Finder The current Finder instance
240
- *
241
- * @see strtotime
242
- * @see DateRangeFilterIterator
243
- * @see DateComparator
244
- *
245
- * @api
246
- */
247
- public function date($date)
248
- {
249
- $this->dates[] = new Comparator\DateComparator($date);
250
-
251
- return $this;
252
- }
253
-
254
- /**
255
- * Adds rules that files must match.
256
- *
257
- * You can use patterns (delimited with / sign), globs or simple strings.
258
- *
259
- * $finder->name('*.php')
260
- * $finder->name('/\.php$/') // same as above
261
- * $finder->name('test.php')
262
- *
263
- * @param string $pattern A pattern (a regexp, a glob, or a string)
264
- *
265
- * @return Finder The current Finder instance
266
- *
267
- * @see FilenameFilterIterator
268
- *
269
- * @api
270
- */
271
- public function name($pattern)
272
- {
273
- $this->names[] = $pattern;
274
-
275
- return $this;
276
- }
277
-
278
- /**
279
- * Adds rules that files must not match.
280
- *
281
- * @param string $pattern A pattern (a regexp, a glob, or a string)
282
- *
283
- * @return Finder The current Finder instance
284
- *
285
- * @see FilenameFilterIterator
286
- *
287
- * @api
288
- */
289
- public function notName($pattern)
290
- {
291
- $this->notNames[] = $pattern;
292
-
293
- return $this;
294
- }
295
-
296
- /**
297
- * Adds tests that file contents must match.
298
- *
299
- * Strings or PCRE patterns can be used:
300
- *
301
- * $finder->contains('Lorem ipsum')
302
- * $finder->contains('/Lorem ipsum/i')
303
- *
304
- * @param string $pattern A pattern (string or regexp)
305
- *
306
- * @return Finder The current Finder instance
307
- *
308
- * @see FilecontentFilterIterator
309
- */
310
- public function contains($pattern)
311
- {
312
- $this->contains[] = $pattern;
313
-
314
- return $this;
315
- }
316
-
317
- /**
318
- * Adds tests that file contents must not match.
319
- *
320
- * Strings or PCRE patterns can be used:
321
- *
322
- * $finder->notContains('Lorem ipsum')
323
- * $finder->notContains('/Lorem ipsum/i')
324
- *
325
- * @param string $pattern A pattern (string or regexp)
326
- *
327
- * @return Finder The current Finder instance
328
- *
329
- * @see FilecontentFilterIterator
330
- */
331
- public function notContains($pattern)
332
- {
333
- $this->notContains[] = $pattern;
334
-
335
- return $this;
336
- }
337
-
338
- /**
339
- * Adds rules that filenames must match.
340
- *
341
- * You can use patterns (delimited with / sign) or simple strings.
342
- *
343
- * $finder->path('some/special/dir')
344
- * $finder->path('/some\/special\/dir/') // same as above
345
- *
346
- * Use only / as dirname separator.
347
- *
348
- * @param string $pattern A pattern (a regexp or a string)
349
- *
350
- * @return Finder The current Finder instance
351
- *
352
- * @see FilenameFilterIterator
353
- */
354
- public function path($pattern)
355
- {
356
- $this->paths[] = $pattern;
357
-
358
- return $this;
359
- }
360
-
361
- /**
362
- * Adds rules that filenames must not match.
363
- *
364
- * You can use patterns (delimited with / sign) or simple strings.
365
- *
366
- * $finder->notPath('some/special/dir')
367
- * $finder->notPath('/some\/special\/dir/') // same as above
368
- *
369
- * Use only / as dirname separator.
370
- *
371
- * @param string $pattern A pattern (a regexp or a string)
372
- *
373
- * @return Finder The current Finder instance
374
- *
375
- * @see FilenameFilterIterator
376
- */
377
- public function notPath($pattern)
378
- {
379
- $this->notPaths[] = $pattern;
380
-
381
- return $this;
382
- }
383
-
384
- /**
385
- * Adds tests for file sizes.
386
- *
387
- * $finder->size('> 10K');
388
- * $finder->size('<= 1Ki');
389
- * $finder->size(4);
390
- *
391
- * @param string $size A size range string
392
- *
393
- * @return Finder The current Finder instance
394
- *
395
- * @see SizeRangeFilterIterator
396
- * @see NumberComparator
397
- *
398
- * @api
399
- */
400
- public function size($size)
401
- {
402
- $this->sizes[] = new Comparator\NumberComparator($size);
403
-
404
- return $this;
405
- }
406
-
407
- /**
408
- * Excludes directories.
409
- *
410
- * @param string|array $dirs A directory path or an array of directories
411
- *
412
- * @return Finder The current Finder instance
413
- *
414
- * @see ExcludeDirectoryFilterIterator
415
- *
416
- * @api
417
- */
418
- public function exclude($dirs)
419
- {
420
- $this->exclude = array_merge($this->exclude, (array) $dirs);
421
-
422
- return $this;
423
- }
424
-
425
- /**
426
- * Excludes "hidden" directories and files (starting with a dot).
427
- *
428
- * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
429
- *
430
- * @return Finder The current Finder instance
431
- *
432
- * @see ExcludeDirectoryFilterIterator
433
- *
434
- * @api
435
- */
436
- public function ignoreDotFiles($ignoreDotFiles)
437
- {
438
- if ($ignoreDotFiles) {
439
- $this->ignore = $this->ignore | static::IGNORE_DOT_FILES;
440
- } else {
441
- $this->ignore = $this->ignore & ~static::IGNORE_DOT_FILES;
442
- }
443
-
444
- return $this;
445
- }
446
-
447
- /**
448
- * Forces the finder to ignore version control directories.
449
- *
450
- * @param bool $ignoreVCS Whether to exclude VCS files or not
451
- *
452
- * @return Finder The current Finder instance
453
- *
454
- * @see ExcludeDirectoryFilterIterator
455
- *
456
- * @api
457
- */
458
- public function ignoreVCS($ignoreVCS)
459
- {
460
- if ($ignoreVCS) {
461
- $this->ignore = $this->ignore | static::IGNORE_VCS_FILES;
462
- } else {
463
- $this->ignore = $this->ignore & ~static::IGNORE_VCS_FILES;
464
- }
465
-
466
- return $this;
467
- }
468
-
469
- /**
470
- * Adds VCS patterns.
471
- *
472
- * @see ignoreVCS()
473
- *
474
- * @param string|string[] $pattern VCS patterns to ignore
475
- */
476
- public static function addVCSPattern($pattern)
477
- {
478
- foreach ((array) $pattern as $p) {
479
- self::$vcsPatterns[] = $p;
480
- }
481
-
482
- self::$vcsPatterns = array_unique(self::$vcsPatterns);
483
- }
484
-
485
- /**
486
- * Sorts files and directories by an anonymous function.
487
- *
488
- * The anonymous function receives two \SplFileInfo instances to compare.
489
- *
490
- * This can be slow as all the matching files and directories must be retrieved for comparison.
491
- *
492
- * @param \Closure $closure An anonymous function
493
- *
494
- * @return Finder The current Finder instance
495
- *
496
- * @see SortableIterator
497
- *
498
- * @api
499
- */
500
- public function sort(\Closure $closure)
501
- {
502
- $this->sort = $closure;
503
-
504
- return $this;
505
- }
506
-
507
- /**
508
- * Sorts files and directories by name.
509
- *
510
- * This can be slow as all the matching files and directories must be retrieved for comparison.
511
- *
512
- * @return Finder The current Finder instance
513
- *
514
- * @see SortableIterator
515
- *
516
- * @api
517
- */
518
- public function sortByName()
519
- {
520
- $this->sort = Iterator\SortableIterator::SORT_BY_NAME;
521
-
522
- return $this;
523
- }
524
-
525
- /**
526
- * Sorts files and directories by type (directories before files), then by name.
527
- *
528
- * This can be slow as all the matching files and directories must be retrieved for comparison.
529
- *
530
- * @return Finder The current Finder instance
531
- *
532
- * @see SortableIterator
533
- *
534
- * @api
535
- */
536
- public function sortByType()
537
- {
538
- $this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
539
-
540
- return $this;
541
- }
542
-
543
- /**
544
- * Sorts files and directories by the last accessed time.
545
- *
546
- * This is the time that the file was last accessed, read or written to.
547
- *
548
- * This can be slow as all the matching files and directories must be retrieved for comparison.
549
- *
550
- * @return Finder The current Finder instance
551
- *
552
- * @see SortableIterator
553
- *
554
- * @api
555
- */
556
- public function sortByAccessedTime()
557
- {
558
- $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
559
-
560
- return $this;
561
- }
562
-
563
- /**
564
- * Sorts files and directories by the last inode changed time.
565
- *
566
- * This is the time that the inode information was last modified (permissions, owner, group or other metadata).
567
- *
568
- * On Windows, since inode is not available, changed time is actually the file creation time.
569
- *
570
- * This can be slow as all the matching files and directories must be retrieved for comparison.
571
- *
572
- * @return Finder The current Finder instance
573
- *
574
- * @see SortableIterator
575
- *
576
- * @api
577
- */
578
- public function sortByChangedTime()
579
- {
580
- $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
581
-
582
- return $this;
583
- }
584
-
585
- /**
586
- * Sorts files and directories by the last modified time.
587
- *
588
- * This is the last time the actual contents of the file were last modified.
589
- *
590
- * This can be slow as all the matching files and directories must be retrieved for comparison.
591
- *
592
- * @return Finder The current Finder instance
593
- *
594
- * @see SortableIterator
595
- *
596
- * @api
597
- */
598
- public function sortByModifiedTime()
599
- {
600
- $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
601
-
602
- return $this;
603
- }
604
-
605
- /**
606
- * Filters the iterator with an anonymous function.
607
- *
608
- * The anonymous function receives a \SplFileInfo and must return false
609
- * to remove files.
610
- *
611
- * @param \Closure $closure An anonymous function
612
- *
613
- * @return Finder The current Finder instance
614
- *
615
- * @see CustomFilterIterator
616
- *
617
- * @api
618
- */
619
- public function filter(\Closure $closure)
620
- {
621
- $this->filters[] = $closure;
622
-
623
- return $this;
624
- }
625
-
626
- /**
627
- * Forces the following of symlinks.
628
- *
629
- * @return Finder The current Finder instance
630
- *
631
- * @api
632
- */
633
- public function followLinks()
634
- {
635
- $this->followLinks = true;
636
-
637
- return $this;
638
- }
639
-
640
- /**
641
- * Tells finder to ignore unreadable directories.
642
- *
643
- * By default, scanning unreadable directories content throws an AccessDeniedException.
644
- *
645
- * @param bool $ignore
646
- *
647
- * @return Finder The current Finder instance
648
- */
649
- public function ignoreUnreadableDirs($ignore = true)
650
- {
651
- $this->ignoreUnreadableDirs = (bool) $ignore;
652
-
653
- return $this;
654
- }
655
-
656
- /**
657
- * Searches files and directories which match defined rules.
658
- *
659
- * @param string|array $dirs A directory path or an array of directories
660
- *
661
- * @return Finder The current Finder instance
662
- *
663
- * @throws \InvalidArgumentException if one of the directories does not exist
664
- *
665
- * @api
666
- */
667
- public function in($dirs)
668
- {
669
- $resolvedDirs = array();
670
-
671
- foreach ((array) $dirs as $dir) {
672
- if (is_dir($dir)) {
673
- $resolvedDirs[] = $dir;
674
- } elseif ($glob = glob($dir, GLOB_BRACE | GLOB_ONLYDIR)) {
675
- $resolvedDirs = array_merge($resolvedDirs, $glob);
676
- } else {
677
- throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
678
- }
679
- }
680
-
681
- $this->dirs = array_merge($this->dirs, $resolvedDirs);
682
-
683
- return $this;
684
- }
685
-
686
- /**
687
- * Returns an Iterator for the current Finder configuration.
688
- *
689
- * This method implements the IteratorAggregate interface.
690
- *
691
- * @return \Iterator An iterator
692
- *
693
- * @throws \LogicException if the in() method has not been called
694
- */
695
- public function getIterator()
696
- {
697
- if (0 === count($this->dirs) && 0 === count($this->iterators)) {
698
- throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
699
- }
700
-
701
- if (1 === count($this->dirs) && 0 === count($this->iterators)) {
702
- return $this->searchInDirectory($this->dirs[0]);
703
- }
704
-
705
- $iterator = new \AppendIterator();
706
- foreach ($this->dirs as $dir) {
707
- $iterator->append($this->searchInDirectory($dir));
708
- }
709
-
710
- foreach ($this->iterators as $it) {
711
- $iterator->append($it);
712
- }
713
-
714
- return $iterator;
715
- }
716
-
717
- /**
718
- * Appends an existing set of files/directories to the finder.
719
- *
720
- * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
721
- *
722
- * @param mixed $iterator
723
- *
724
- * @return Finder The finder
725
- *
726
- * @throws \InvalidArgumentException When the given argument is not iterable.
727
- */
728
- public function append($iterator)
729
- {
730
- if ($iterator instanceof \IteratorAggregate) {
731
- $this->iterators[] = $iterator->getIterator();
732
- } elseif ($iterator instanceof \Iterator) {
733
- $this->iterators[] = $iterator;
734
- } elseif ($iterator instanceof \Traversable || is_array($iterator)) {
735
- $it = new \ArrayIterator();
736
- foreach ($iterator as $file) {
737
- $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
738
- }
739
- $this->iterators[] = $it;
740
- } else {
741
- throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
742
- }
743
-
744
- return $this;
745
- }
746
-
747
- /**
748
- * Counts all the results collected by the iterators.
749
- *
750
- * @return int
751
- */
752
- public function count()
753
- {
754
- return iterator_count($this->getIterator());
755
- }
756
-
757
- /**
758
- * @return Finder The current Finder instance
759
- */
760
- private function sortAdapters()
761
- {
762
- uasort($this->adapters, function (array $a, array $b) {
763
- if ($a['selected'] || $b['selected']) {
764
- return $a['selected'] ? -1 : 1;
765
- }
766
-
767
- return $a['priority'] > $b['priority'] ? -1 : 1;
768
- });
769
-
770
- return $this;
771
- }
772
-
773
- /**
774
- * @param $dir
775
- *
776
- * @return \Iterator
777
- *
778
- * @throws \RuntimeException When none of the adapters are supported
779
- */
780
- private function searchInDirectory($dir)
781
- {
782
- if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
783
- $this->exclude = array_merge($this->exclude, self::$vcsPatterns);
784
- }
785
-
786
- if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
787
- $this->notPaths[] = '#(^|/)\..+(/|$)#';
788
- }
789
-
790
- foreach ($this->adapters as $adapter) {
791
- if ($adapter['adapter']->isSupported()) {
792
- try {
793
- return $this
794
- ->buildAdapter($adapter['adapter'])
795
- ->searchInDirectory($dir);
796
- } catch (ExceptionInterface $e) {
797
- }
798
- }
799
- }
800
-
801
- throw new \RuntimeException('No supported adapter found.');
802
- }
803
-
804
- /**
805
- * @param AdapterInterface $adapter
806
- *
807
- * @return AdapterInterface
808
- */
809
- private function buildAdapter(AdapterInterface $adapter)
810
- {
811
- return $adapter
812
- ->setFollowLinks($this->followLinks)
813
- ->setDepths($this->depths)
814
- ->setMode($this->mode)
815
- ->setExclude($this->exclude)
816
- ->setNames($this->names)
817
- ->setNotNames($this->notNames)
818
- ->setContains($this->contains)
819
- ->setNotContains($this->notContains)
820
- ->setSizes($this->sizes)
821
- ->setDates($this->dates)
822
- ->setFilters($this->filters)
823
- ->setSort($this->sort)
824
- ->setPath($this->paths)
825
- ->setNotPath($this->notPaths)
826
- ->ignoreUnreadableDirs($this->ignoreUnreadableDirs);
827
- }
828
-
829
- /**
830
- * Unselects all adapters.
831
- */
832
- private function resetAdapterSelection()
833
- {
834
- $this->adapters = array_map(function (array $properties) {
835
- $properties['selected'] = false;
836
-
837
- return $properties;
838
- }, $this->adapters);
839
- }
840
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Glob.php DELETED
@@ -1,103 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder;
13
-
14
- /**
15
- * Glob matches globbing patterns against text.
16
- *
17
- * if match_glob("foo.*", "foo.bar") echo "matched\n";
18
- *
19
- * // prints foo.bar and foo.baz
20
- * $regex = glob_to_regex("foo.*");
21
- * for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t)
22
- * {
23
- * if (/$regex/) echo "matched: $car\n";
24
- * }
25
- *
26
- * Glob implements glob(3) style matching that can be used to match
27
- * against text, rather than fetching names from a filesystem.
28
- *
29
- * Based on the Perl Text::Glob module.
30
- *
31
- * @author Fabien Potencier <fabien@symfony.com> PHP port
32
- * @author Richard Clamp <richardc@unixbeard.net> Perl version
33
- * @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>
34
- * @copyright 2002 Richard Clamp <richardc@unixbeard.net>
35
- */
36
- class Glob
37
- {
38
- /**
39
- * Returns a regexp which is the equivalent of the glob pattern.
40
- *
41
- * @param string $glob The glob pattern
42
- * @param bool $strictLeadingDot
43
- * @param bool $strictWildcardSlash
44
- *
45
- * @return string regex The regexp
46
- */
47
- public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true)
48
- {
49
- $firstByte = true;
50
- $escaping = false;
51
- $inCurlies = 0;
52
- $regex = '';
53
- $sizeGlob = strlen($glob);
54
- for ($i = 0; $i < $sizeGlob; $i++) {
55
- $car = $glob[$i];
56
- if ($firstByte) {
57
- if ($strictLeadingDot && '.' !== $car) {
58
- $regex .= '(?=[^\.])';
59
- }
60
-
61
- $firstByte = false;
62
- }
63
-
64
- if ('/' === $car) {
65
- $firstByte = true;
66
- }
67
-
68
- if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
69
- $regex .= "\\$car";
70
- } elseif ('*' === $car) {
71
- $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
72
- } elseif ('?' === $car) {
73
- $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
74
- } elseif ('{' === $car) {
75
- $regex .= $escaping ? '\\{' : '(';
76
- if (!$escaping) {
77
- ++$inCurlies;
78
- }
79
- } elseif ('}' === $car && $inCurlies) {
80
- $regex .= $escaping ? '}' : ')';
81
- if (!$escaping) {
82
- --$inCurlies;
83
- }
84
- } elseif (',' === $car && $inCurlies) {
85
- $regex .= $escaping ? ',' : '|';
86
- } elseif ('\\' === $car) {
87
- if ($escaping) {
88
- $regex .= '\\\\';
89
- $escaping = false;
90
- } else {
91
- $escaping = true;
92
- }
93
-
94
- continue;
95
- } else {
96
- $regex .= $car;
97
- }
98
- $escaping = false;
99
- }
100
-
101
- return '#^'.$regex.'$#';
102
- }
103
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php DELETED
@@ -1,63 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- /**
15
- * CustomFilterIterator filters files by applying anonymous functions.
16
- *
17
- * The anonymous function receives a \SplFileInfo and must return false
18
- * to remove files.
19
- *
20
- * @author Fabien Potencier <fabien@symfony.com>
21
- */
22
- class CustomFilterIterator extends FilterIterator
23
- {
24
- private $filters = array();
25
-
26
- /**
27
- * Constructor.
28
- *
29
- * @param \Iterator $iterator The Iterator to filter
30
- * @param array $filters An array of PHP callbacks
31
- *
32
- * @throws \InvalidArgumentException
33
- */
34
- public function __construct(\Iterator $iterator, array $filters)
35
- {
36
- foreach ($filters as $filter) {
37
- if (!is_callable($filter)) {
38
- throw new \InvalidArgumentException('Invalid PHP callback.');
39
- }
40
- }
41
- $this->filters = $filters;
42
-
43
- parent::__construct($iterator);
44
- }
45
-
46
- /**
47
- * Filters the iterator values.
48
- *
49
- * @return bool true if the value should be kept, false otherwise
50
- */
51
- public function accept()
52
- {
53
- $fileinfo = $this->current();
54
-
55
- foreach ($this->filters as $filter) {
56
- if (false === call_user_func($filter, $fileinfo)) {
57
- return false;
58
- }
59
- }
60
-
61
- return true;
62
- }
63
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php DELETED
@@ -1,60 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- use Symfony\Component\Finder\Comparator\DateComparator;
15
-
16
- /**
17
- * DateRangeFilterIterator filters out files that are not in the given date range (last modified dates).
18
- *
19
- * @author Fabien Potencier <fabien@symfony.com>
20
- */
21
- class DateRangeFilterIterator extends FilterIterator
22
- {
23
- private $comparators = array();
24
-
25
- /**
26
- * Constructor.
27
- *
28
- * @param \Iterator $iterator The Iterator to filter
29
- * @param DateComparator[] $comparators An array of DateComparator instances
30
- */
31
- public function __construct(\Iterator $iterator, array $comparators)
32
- {
33
- $this->comparators = $comparators;
34
-
35
- parent::__construct($iterator);
36
- }
37
-
38
- /**
39
- * Filters the iterator values.
40
- *
41
- * @return bool true if the value should be kept, false otherwise
42
- */
43
- public function accept()
44
- {
45
- $fileinfo = $this->current();
46
-
47
- if (!file_exists($fileinfo->getRealPath())) {
48
- return false;
49
- }
50
-
51
- $filedate = $fileinfo->getMTime();
52
- foreach ($this->comparators as $compare) {
53
- if (!$compare->test($filedate)) {
54
- return false;
55
- }
56
- }
57
-
58
- return true;
59
- }
60
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php DELETED
@@ -1,47 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- /**
15
- * DepthRangeFilterIterator limits the directory depth.
16
- *
17
- * @author Fabien Potencier <fabien@symfony.com>
18
- */
19
- class DepthRangeFilterIterator extends FilterIterator
20
- {
21
- private $minDepth = 0;
22
-
23
- /**
24
- * Constructor.
25
- *
26
- * @param \RecursiveIteratorIterator $iterator The Iterator to filter
27
- * @param int $minDepth The min depth
28
- * @param int $maxDepth The max depth
29
- */
30
- public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX)
31
- {
32
- $this->minDepth = $minDepth;
33
- $iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);
34
-
35
- parent::__construct($iterator);
36
- }
37
-
38
- /**
39
- * Filters the iterator values.
40
- *
41
- * @return bool true if the value should be kept, false otherwise
42
- */
43
- public function accept()
44
- {
45
- return $this->getInnerIterator()->getDepth() >= $this->minDepth;
46
- }
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php DELETED
@@ -1,55 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- /**
15
- * ExcludeDirectoryFilterIterator filters out directories.
16
- *
17
- * @author Fabien Potencier <fabien@symfony.com>
18
- */
19
- class ExcludeDirectoryFilterIterator extends FilterIterator
20
- {
21
- private $patterns = array();
22
-
23
- /**
24
- * Constructor.
25
- *
26
- * @param \Iterator $iterator The Iterator to filter
27
- * @param array $directories An array of directories to exclude
28
- */
29
- public function __construct(\Iterator $iterator, array $directories)
30
- {
31
- foreach ($directories as $directory) {
32
- $this->patterns[] = '#(^|/)'.preg_quote($directory, '#').'(/|$)#';
33
- }
34
-
35
- parent::__construct($iterator);
36
- }
37
-
38
- /**
39
- * Filters the iterator values.
40
- *
41
- * @return bool true if the value should be kept, false otherwise
42
- */
43
- public function accept()
44
- {
45
- $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
46
- $path = strtr($path, '\\', '/');
47
- foreach ($this->patterns as $pattern) {
48
- if (preg_match($pattern, $path)) {
49
- return false;
50
- }
51
- }
52
-
53
- return true;
54
- }
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php DELETED
@@ -1,131 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- use Symfony\Component\Finder\SplFileInfo;
15
-
16
- /**
17
- * Iterate over shell command result.
18
- *
19
- * @author Jean-François Simon <contact@jfsimon.fr>
20
- */
21
- class FilePathsIterator extends \ArrayIterator
22
- {
23
- /**
24
- * @var string
25
- */
26
- private $baseDir;
27
-
28
- /**
29
- * @var int
30
- */
31
- private $baseDirLength;
32
-
33
- /**
34
- * @var string
35
- */
36
- private $subPath;
37
-
38
- /**
39
- * @var string
40
- */
41
- private $subPathname;
42
-
43
- /**
44
- * @var SplFileInfo
45
- */
46
- private $current;
47
-
48
- /**
49
- * @param array $paths List of paths returned by shell command
50
- * @param string $baseDir Base dir for relative path building
51
- */
52
- public function __construct(array $paths, $baseDir)
53
- {
54
- $this->baseDir = $baseDir;
55
- $this->baseDirLength = strlen($baseDir);
56
-
57
- parent::__construct($paths);
58
- }
59
-
60
- /**
61
- * @param string $name
62
- * @param array $arguments
63
- *
64
- * @return mixed
65
- */
66
- public function __call($name, array $arguments)
67
- {
68
- return call_user_func_array(array($this->current(), $name), $arguments);
69
- }
70
-
71
- /**
72
- * Return an instance of SplFileInfo with support for relative paths.
73
- *
74
- * @return SplFileInfo File information
75
- */
76
- public function current()
77
- {
78
- return $this->current;
79
- }
80
-
81
- /**
82
- * @return string
83
- */
84
- public function key()
85
- {
86
- return $this->current->getPathname();
87
- }
88
-
89
- public function next()
90
- {
91
- parent::next();
92
- $this->buildProperties();
93
- }
94
-
95
- public function rewind()
96
- {
97
- parent::rewind();
98
- $this->buildProperties();
99
- }
100
-
101
- /**
102
- * @return string
103
- */
104
- public function getSubPath()
105
- {
106
- return $this->subPath;
107
- }
108
-
109
- /**
110
- * @return string
111
- */
112
- public function getSubPathname()
113
- {
114
- return $this->subPathname;
115
- }
116
-
117
- private function buildProperties()
118
- {
119
- $absolutePath = parent::current();
120
-
121
- if ($this->baseDir === substr($absolutePath, 0, $this->baseDirLength)) {
122
- $this->subPathname = ltrim(substr($absolutePath, $this->baseDirLength), '/\\');
123
- $dir = dirname($this->subPathname);
124
- $this->subPath = '.' === $dir ? '' : $dir;
125
- } else {
126
- $this->subPath = $this->subPathname = '';
127
- }
128
-
129
- $this->current = new SplFileInfo(parent::current(), $this->subPath, $this->subPathname);
130
- }
131
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php DELETED
@@ -1,55 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- /**
15
- * FileTypeFilterIterator only keeps files, directories, or both.
16
- *
17
- * @author Fabien Potencier <fabien@symfony.com>
18
- */
19
- class FileTypeFilterIterator extends FilterIterator
20
- {
21
- const ONLY_FILES = 1;
22
- const ONLY_DIRECTORIES = 2;
23
-
24
- private $mode;
25
-
26
- /**
27
- * Constructor.
28
- *
29
- * @param \Iterator $iterator The Iterator to filter
30
- * @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES)
31
- */
32
- public function __construct(\Iterator $iterator, $mode)
33
- {
34
- $this->mode = $mode;
35
-
36
- parent::__construct($iterator);
37
- }
38
-
39
- /**
40
- * Filters the iterator values.
41
- *
42
- * @return bool true if the value should be kept, false otherwise
43
- */
44
- public function accept()
45
- {
46
- $fileinfo = $this->current();
47
- if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {
48
- return false;
49
- } elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) {
50
- return false;
51
- }
52
-
53
- return true;
54
- }
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php DELETED
@@ -1,76 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- /**
15
- * FilecontentFilterIterator filters files by their contents using patterns (regexps or strings).
16
- *
17
- * @author Fabien Potencier <fabien@symfony.com>
18
- * @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
19
- */
20
- class FilecontentFilterIterator extends MultiplePcreFilterIterator
21
- {
22
- /**
23
- * Filters the iterator values.
24
- *
25
- * @return bool true if the value should be kept, false otherwise
26
- */
27
- public function accept()
28
- {
29
- if (!$this->matchRegexps && !$this->noMatchRegexps) {
30
- return true;
31
- }
32
-
33
- $fileinfo = $this->current();
34
-
35
- if ($fileinfo->isDir() || !$fileinfo->isReadable()) {
36
- return false;
37
- }
38
-
39
- $content = $fileinfo->getContents();
40
- if (!$content) {
41
- return false;
42
- }
43
-
44
- // should at least not match one rule to exclude
45
- foreach ($this->noMatchRegexps as $regex) {
46
- if (preg_match($regex, $content)) {
47
- return false;
48
- }
49
- }
50
-
51
- // should at least match one rule
52
- $match = true;
53
- if ($this->matchRegexps) {
54
- $match = false;
55
- foreach ($this->matchRegexps as $regex) {
56
- if (preg_match($regex, $content)) {
57
- return true;
58
- }
59
- }
60
- }
61
-
62
- return $match;
63
- }
64
-
65
- /**
66
- * Converts string to regexp if necessary.
67
- *
68
- * @param string $str Pattern: string or regexp
69
- *
70
- * @return string regexp corresponding to a given string or regexp
71
- */
72
- protected function toRegex($str)
73
- {
74
- return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
75
- }
76
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php DELETED
@@ -1,67 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- use Symfony\Component\Finder\Expression\Expression;
15
-
16
- /**
17
- * FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string).
18
- *
19
- * @author Fabien Potencier <fabien@symfony.com>
20
- */
21
- class FilenameFilterIterator extends MultiplePcreFilterIterator
22
- {
23
- /**
24
- * Filters the iterator values.
25
- *
26
- * @return bool true if the value should be kept, false otherwise
27
- */
28
- public function accept()
29
- {
30
- $filename = $this->current()->getFilename();
31
-
32
- // should at least not match one rule to exclude
33
- foreach ($this->noMatchRegexps as $regex) {
34
- if (preg_match($regex, $filename)) {
35
- return false;
36
- }
37
- }
38
-
39
- // should at least match one rule
40
- $match = true;
41
- if ($this->matchRegexps) {
42
- $match = false;
43
- foreach ($this->matchRegexps as $regex) {
44
- if (preg_match($regex, $filename)) {
45
- return true;
46
- }
47
- }
48
- }
49
-
50
- return $match;
51
- }
52
-
53
- /**
54
- * Converts glob to regexp.
55
- *
56
- * PCRE patterns are left unchanged.
57
- * Glob strings are transformed with Glob::toRegex().
58
- *
59
- * @param string $str Pattern: glob or regexp
60
- *
61
- * @return string regexp corresponding to a given glob or regexp
62
- */
63
- protected function toRegex($str)
64
- {
65
- return Expression::create($str)->getRegex()->render();
66
- }
67
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php DELETED
@@ -1,49 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- /**
15
- * This iterator just overrides the rewind method in order to correct a PHP bug.
16
- *
17
- * @see https://bugs.php.net/bug.php?id=49104
18
- *
19
- * @author Alex Bogomazov
20
- */
21
- abstract class FilterIterator extends \FilterIterator
22
- {
23
- /**
24
- * This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after
25
- * rewind in some cases.
26
- *
27
- * @see FilterIterator::rewind()
28
- */
29
- public function rewind()
30
- {
31
- $iterator = $this;
32
- while ($iterator instanceof \OuterIterator) {
33
- $innerIterator = $iterator->getInnerIterator();
34
-
35
- if ($innerIterator instanceof RecursiveDirectoryIterator) {
36
- if ($innerIterator->isRewindable()) {
37
- $innerIterator->next();
38
- $innerIterator->rewind();
39
- }
40
- } elseif ($iterator->getInnerIterator() instanceof \FilesystemIterator) {
41
- $iterator->getInnerIterator()->next();
42
- $iterator->getInnerIterator()->rewind();
43
- }
44
- $iterator = $iterator->getInnerIterator();
45
- }
46
-
47
- parent::rewind();
48
- }
49
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php DELETED
@@ -1,66 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- use Symfony\Component\Finder\Expression\Expression;
15
-
16
- /**
17
- * MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings).
18
- *
19
- * @author Fabien Potencier <fabien@symfony.com>
20
- */
21
- abstract class MultiplePcreFilterIterator extends FilterIterator
22
- {
23
- protected $matchRegexps = array();
24
- protected $noMatchRegexps = array();
25
-
26
- /**
27
- * Constructor.
28
- *
29
- * @param \Iterator $iterator The Iterator to filter
30
- * @param array $matchPatterns An array of patterns that need to match
31
- * @param array $noMatchPatterns An array of patterns that need to not match
32
- */
33
- public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
34
- {
35
- foreach ($matchPatterns as $pattern) {
36
- $this->matchRegexps[] = $this->toRegex($pattern);
37
- }
38
-
39
- foreach ($noMatchPatterns as $pattern) {
40
- $this->noMatchRegexps[] = $this->toRegex($pattern);
41
- }
42
-
43
- parent::__construct($iterator);
44
- }
45
-
46
- /**
47
- * Checks whether the string is a regex.
48
- *
49
- * @param string $str
50
- *
51
- * @return bool Whether the given string is a regex
52
- */
53
- protected function isRegex($str)
54
- {
55
- return Expression::create($str)->isRegex();
56
- }
57
-
58
- /**
59
- * Converts string into regexp.
60
- *
61
- * @param string $str Pattern
62
- *
63
- * @return string regexp corresponding to a given string
64
- */
65
- abstract protected function toRegex($str);
66
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php DELETED
@@ -1,74 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- /**
15
- * PathFilterIterator filters files by path patterns (e.g. some/special/dir).
16
- *
17
- * @author Fabien Potencier <fabien@symfony.com>
18
- * @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
19
- */
20
- class PathFilterIterator extends MultiplePcreFilterIterator
21
- {
22
- /**
23
- * Filters the iterator values.
24
- *
25
- * @return bool true if the value should be kept, false otherwise
26
- */
27
- public function accept()
28
- {
29
- $filename = $this->current()->getRelativePathname();
30
-
31
- if ('\\' === DIRECTORY_SEPARATOR) {
32
- $filename = strtr($filename, '\\', '/');
33
- }
34
-
35
- // should at least not match one rule to exclude
36
- foreach ($this->noMatchRegexps as $regex) {
37
- if (preg_match($regex, $filename)) {
38
- return false;
39
- }
40
- }
41
-
42
- // should at least match one rule
43
- $match = true;
44
- if ($this->matchRegexps) {
45
- $match = false;
46
- foreach ($this->matchRegexps as $regex) {
47
- if (preg_match($regex, $filename)) {
48
- return true;
49
- }
50
- }
51
- }
52
-
53
- return $match;
54
- }
55
-
56
- /**
57
- * Converts strings to regexp.
58
- *
59
- * PCRE patterns are left unchanged.
60
- *
61
- * Default conversion:
62
- * 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/'
63
- *
64
- * Use only / as directory separator (on Windows also).
65
- *
66
- * @param string $str Pattern: regexp or dirname.
67
- *
68
- * @return string regexp corresponding to a given string or regexp
69
- */
70
- protected function toRegex($str)
71
- {
72
- return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
73
- }
74
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php DELETED
@@ -1,126 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- use Symfony\Component\Finder\Exception\AccessDeniedException;
15
- use Symfony\Component\Finder\SplFileInfo;
16
-
17
- /**
18
- * Extends the \RecursiveDirectoryIterator to support relative paths.
19
- *
20
- * @author Victor Berchet <victor@suumit.com>
21
- */
22
- class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
23
- {
24
- /**
25
- * @var bool
26
- */
27
- private $ignoreUnreadableDirs;
28
-
29
- /**
30
- * @var bool
31
- */
32
- private $rewindable;
33
-
34
- /**
35
- * Constructor.
36
- *
37
- * @param string $path
38
- * @param int $flags
39
- * @param bool $ignoreUnreadableDirs
40
- *
41
- * @throws \RuntimeException
42
- */
43
- public function __construct($path, $flags, $ignoreUnreadableDirs = false)
44
- {
45
- if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
46
- throw new \RuntimeException('This iterator only support returning current as fileinfo.');
47
- }
48
-
49
- parent::__construct($path, $flags);
50
- $this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
51
- }
52
-
53
- /**
54
- * Return an instance of SplFileInfo with support for relative paths.
55
- *
56
- * @return SplFileInfo File information
57
- */
58
- public function current()
59
- {
60
- return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname());
61
- }
62
-
63
- /**
64
- * @return \RecursiveIterator
65
- *
66
- * @throws AccessDeniedException
67
- */
68
- public function getChildren()
69
- {
70
- try {
71
- $children = parent::getChildren();
72
-
73
- if ($children instanceof self) {
74
- // parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore
75
- $children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
76
- }
77
-
78
- return $children;
79
- } catch (\UnexpectedValueException $e) {
80
- if ($this->ignoreUnreadableDirs) {
81
- // If directory is unreadable and finder is set to ignore it, a fake empty content is returned.
82
- return new \RecursiveArrayIterator(array());
83
- } else {
84
- throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
85
- }
86
- }
87
- }
88
-
89
- /**
90
- * Do nothing for non rewindable stream.
91
- */
92
- public function rewind()
93
- {
94
- if (false === $this->isRewindable()) {
95
- return;
96
- }
97
-
98
- // @see https://bugs.php.net/bug.php?id=49104
99
- parent::next();
100
-
101
- parent::rewind();
102
- }
103
-
104
- /**
105
- * Checks if the stream is rewindable.
106
- *
107
- * @return bool true when the stream is rewindable, false otherwise
108
- */
109
- public function isRewindable()
110
- {
111
- if (null !== $this->rewindable) {
112
- return $this->rewindable;
113
- }
114
-
115
- if (false !== $stream = @opendir($this->getPath())) {
116
- $infos = stream_get_meta_data($stream);
117
- closedir($stream);
118
-
119
- if ($infos['seekable']) {
120
- return $this->rewindable = true;
121
- }
122
- }
123
-
124
- return $this->rewindable = false;
125
- }
126
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php DELETED
@@ -1,59 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- use Symfony\Component\Finder\Comparator\NumberComparator;
15
-
16
- /**
17
- * SizeRangeFilterIterator filters out files that are not in the given size range.
18
- *
19
- * @author Fabien Potencier <fabien@symfony.com>
20
- */
21
- class SizeRangeFilterIterator extends FilterIterator
22
- {
23
- private $comparators = array();
24
-
25
- /**
26
- * Constructor.
27
- *
28
- * @param \Iterator $iterator The Iterator to filter
29
- * @param NumberComparator[] $comparators An array of NumberComparator instances
30
- */
31
- public function __construct(\Iterator $iterator, array $comparators)
32
- {
33
- $this->comparators = $comparators;
34
-
35
- parent::__construct($iterator);
36
- }
37
-
38
- /**
39
- * Filters the iterator values.
40
- *
41
- * @return bool true if the value should be kept, false otherwise
42
- */
43
- public function accept()
44
- {
45
- $fileinfo = $this->current();
46
- if (!$fileinfo->isFile()) {
47
- return true;
48
- }
49
-
50
- $filesize = $fileinfo->getSize();
51
- foreach ($this->comparators as $compare) {
52
- if (!$compare->test($filesize)) {
53
- return false;
54
- }
55
- }
56
-
57
- return true;
58
- }
59
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php DELETED
@@ -1,82 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Iterator;
13
-
14
- /**
15
- * SortableIterator applies a sort on a given Iterator.
16
- *
17
- * @author Fabien Potencier <fabien@symfony.com>
18
- */
19
- class SortableIterator implements \IteratorAggregate
20
- {
21
- const SORT_BY_NAME = 1;
22
- const SORT_BY_TYPE = 2;
23
- const SORT_BY_ACCESSED_TIME = 3;
24
- const SORT_BY_CHANGED_TIME = 4;
25
- const SORT_BY_MODIFIED_TIME = 5;
26
-
27
- private $iterator;
28
- private $sort;
29
-
30
- /**
31
- * Constructor.
32
- *
33
- * @param \Traversable $iterator The Iterator to filter
34
- * @param int|callable $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback)
35
- *
36
- * @throws \InvalidArgumentException
37
- */
38
- public function __construct(\Traversable $iterator, $sort)
39
- {
40
- $this->iterator = $iterator;
41
-
42
- if (self::SORT_BY_NAME === $sort) {
43
- $this->sort = function ($a, $b) {
44
- return strcmp($a->getRealpath(), $b->getRealpath());
45
- };
46
- } elseif (self::SORT_BY_TYPE === $sort) {
47
- $this->sort = function ($a, $b) {
48
- if ($a->isDir() && $b->isFile()) {
49
- return -1;
50
- } elseif ($a->isFile() && $b->isDir()) {
51
- return 1;
52
- }
53
-
54
- return strcmp($a->getRealpath(), $b->getRealpath());
55
- };
56
- } elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
57
- $this->sort = function ($a, $b) {
58
- return ($a->getATime() - $b->getATime());
59
- };
60
- } elseif (self::SORT_BY_CHANGED_TIME === $sort) {
61
- $this->sort = function ($a, $b) {
62
- return ($a->getCTime() - $b->getCTime());
63
- };
64
- } elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
65
- $this->sort = function ($a, $b) {
66
- return ($a->getMTime() - $b->getMTime());
67
- };
68
- } elseif (is_callable($sort)) {
69
- $this->sort = $sort;
70
- } else {
71
- throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');
72
- }
73
- }
74
-
75
- public function getIterator()
76
- {
77
- $array = iterator_to_array($this->iterator, true);
78
- uasort($array, $this->sort);
79
-
80
- return new \ArrayIterator($array);
81
- }
82
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/LICENSE DELETED
@@ -1,19 +0,0 @@
1
- Copyright (c) 2004-2015 Fabien Potencier
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining a copy
4
- of this software and associated documentation files (the "Software"), to deal
5
- in the Software without restriction, including without limitation the rights
6
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- copies of the Software, and to permit persons to whom the Software is furnished
8
- to do so, subject to the following conditions:
9
-
10
- The above copyright notice and this permission notice shall be included in all
11
- copies or substantial portions of the Software.
12
-
13
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- THE SOFTWARE.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/README.md DELETED
@@ -1,53 +0,0 @@
1
- Finder Component
2
- ================
3
-
4
- Finder finds files and directories via an intuitive fluent interface.
5
-
6
- ```php
7
- use Symfony\Component\Finder\Finder;
8
-
9
- $finder = new Finder();
10
-
11
- $iterator = $finder
12
- ->files()
13
- ->name('*.php')
14
- ->depth(0)
15
- ->size('>= 1K')
16
- ->in(__DIR__);
17
-
18
- foreach ($iterator as $file) {
19
- print $file->getRealpath()."\n";
20
- }
21
- ```
22
-
23
- The iterator returns instances of [Symfony\Component\Finder\SplFileInfo\SplFileInfo][1].
24
- Besides the build-in methods inherited from [\SplFileInfo][2] (`getPerms()`, `getSize()`, ...),
25
- you can also use `getRelativePath()` and `getRelativePathname()`. Read the
26
- [official documentation][3] for more information.
27
-
28
- But you can also use it to find files stored remotely like in this example where
29
- we are looking for files on Amazon S3:
30
-
31
- ```php
32
- $s3 = new \Zend_Service_Amazon_S3($key, $secret);
33
- $s3->registerStreamWrapper("s3");
34
-
35
- $finder = new Finder();
36
- $finder->name('photos*')->size('< 100K')->date('since 1 hour ago');
37
- foreach ($finder->in('s3://bucket-name') as $file) {
38
- print $file->getFilename()."\n";
39
- }
40
- ```
41
-
42
- Resources
43
- ---------
44
-
45
- You can run the unit tests with the following command:
46
-
47
- $ cd path/to/Symfony/Component/Finder/
48
- $ composer.phar install
49
- $ phpunit
50
-
51
- [1]: http://api.symfony.com/2.5/Symfony/Component/Finder/SplFileInfo.html
52
- [2]: http://php.net/splfileinfo
53
- [3]: http://symfony.com/doc/current/components/finder.html#usage
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Shell/Command.php DELETED
@@ -1,294 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Shell;
13
-
14
- /**
15
- * @author Jean-François Simon <contact@jfsimon.fr>
16
- */
17
- class Command
18
- {
19
- /**
20
- * @var Command|null
21
- */
22
- private $parent;
23
-
24
- /**
25
- * @var array
26
- */
27
- private $bits = array();
28
-
29
- /**
30
- * @var array
31
- */
32
- private $labels = array();
33
-
34
- /**
35
- * @var \Closure|null
36
- */
37
- private $errorHandler;
38
-
39
- /**
40
- * Constructor.
41
- *
42
- * @param Command|null $parent Parent command
43
- */
44
- public function __construct(Command $parent = null)
45
- {
46
- $this->parent = $parent;
47
- }
48
-
49
- /**
50
- * Returns command as string.
51
- *
52
- * @return string
53
- */
54
- public function __toString()
55
- {
56
- return $this->join();
57
- }
58
-
59
- /**
60
- * Creates a new Command instance.
61
- *
62
- * @param Command|null $parent Parent command
63
- *
64
- * @return Command New Command instance
65
- */
66
- public static function create(Command $parent = null)
67
- {
68
- return new self($parent);
69
- }
70
-
71
- /**
72
- * Escapes special chars from input.
73
- *
74
- * @param string $input A string to escape
75
- *
76
- * @return string The escaped string
77
- */
78
- public static function escape($input)
79
- {
80
- return escapeshellcmd($input);
81
- }
82
-
83
- /**
84
- * Quotes input.
85
- *
86
- * @param string $input An argument string
87
- *
88
- * @return string The quoted string
89
- */
90
- public static function quote($input)
91
- {
92
- return escapeshellarg($input);
93
- }
94
-
95
- /**
96
- * Appends a string or a Command instance.
97
- *
98
- * @param string|Command $bit
99
- *
100
- * @return Command The current Command instance
101
- */
102
- public function add($bit)
103
- {
104
- $this->bits[] = $bit;
105
-
106
- return $this;
107
- }
108
-
109
- /**
110
- * Prepends a string or a command instance.
111
- *
112
- * @param string|Command $bit
113
- *
114
- * @return Command The current Command instance
115
- */
116
- public function top($bit)
117
- {
118
- array_unshift($this->bits, $bit);
119
-
120
- foreach ($this->labels as $label => $index) {
121
- $this->labels[$label] += 1;
122
- }
123
-
124
- return $this;
125
- }
126
-
127
- /**
128
- * Appends an argument, will be quoted.
129
- *
130
- * @param string $arg
131
- *
132
- * @return Command The current Command instance
133
- */
134
- public function arg($arg)
135
- {
136
- $this->bits[] = self::quote($arg);
137
-
138
- return $this;
139
- }
140
-
141
- /**
142
- * Appends escaped special command chars.
143
- *
144
- * @param string $esc
145
- *
146
- * @return Command The current Command instance
147
- */
148
- public function cmd($esc)
149
- {
150
- $this->bits[] = self::escape($esc);
151
-
152
- return $this;
153
- }
154
-
155
- /**
156
- * Inserts a labeled command to feed later.
157
- *
158
- * @param string $label The unique label
159
- *
160
- * @return Command The current Command instance
161
- *
162
- * @throws \RuntimeException If label already exists
163
- */
164
- public function ins($label)
165
- {
166
- if (isset($this->labels[$label])) {
167
- throw new \RuntimeException(sprintf('Label "%s" already exists.', $label));
168
- }
169
-
170
- $this->bits[] = self::create($this);
171
- $this->labels[$label] = count($this->bits)-1;
172
-
173
- return $this->bits[$this->labels[$label]];
174
- }
175
-
176
- /**
177
- * Retrieves a previously labeled command.
178
- *
179
- * @param string $label
180
- *
181
- * @return Command The labeled command
182
- *
183
- * @throws \RuntimeException
184
- */
185
- public function get($label)
186
- {
187
- if (!isset($this->labels[$label])) {
188
- throw new \RuntimeException(sprintf('Label "%s" does not exist.', $label));
189
- }
190
-
191
- return $this->bits[$this->labels[$label]];
192
- }
193
-
194
- /**
195
- * Returns parent command (if any).
196
- *
197
- * @return Command Parent command
198
- *
199
- * @throws \RuntimeException If command has no parent
200
- */
201
- public function end()
202
- {
203
- if (null === $this->parent) {
204
- throw new \RuntimeException('Calling end on root command doesn\'t make sense.');
205
- }
206
-
207
- return $this->parent;
208
- }
209
-
210
- /**
211
- * Counts bits stored in command.
212
- *
213
- * @return int The bits count
214
- */
215
- public function length()
216
- {
217
- return count($this->bits);
218
- }
219
-
220
- /**
221
- * @param \Closure $errorHandler
222
- *
223
- * @return Command
224
- */
225
- public function setErrorHandler(\Closure $errorHandler)
226
- {
227
- $this->errorHandler = $errorHandler;
228
-
229
- return $this;
230
- }
231
-
232
- /**
233
- * @return \Closure|null
234
- */
235
- public function getErrorHandler()
236
- {
237
- return $this->errorHandler;
238
- }
239
-
240
- /**
241
- * Executes current command.
242
- *
243
- * @return array The command result
244
- *
245
- * @throws \RuntimeException
246
- */
247
- public function execute()
248
- {
249
- if (null === $errorHandler = $this->errorHandler) {
250
- exec($this->join(), $output);
251
- } else {
252
- $process = proc_open($this->join(), array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes);
253
- $output = preg_split('~(\r\n|\r|\n)~', stream_get_contents($pipes[1]), -1, PREG_SPLIT_NO_EMPTY);
254
-
255
- if ($error = stream_get_contents($pipes[2])) {
256
- $errorHandler($error);
257
- }
258
-
259
- proc_close($process);
260
- }
261
-
262
- return $output ?: array();
263
- }
264
-
265
- /**
266
- * Joins bits.
267
- *
268
- * @return string
269
- */
270
- public function join()
271
- {
272
- return implode(' ', array_filter(
273
- array_map(function ($bit) {
274
- return $bit instanceof Command ? $bit->join() : ($bit ?: null);
275
- }, $this->bits),
276
- function ($bit) { return null !== $bit; }
277
- ));
278
- }
279
-
280
- /**
281
- * Insert a string or a Command instance before the bit at given position $index (index starts from 0).
282
- *
283
- * @param string|Command $bit
284
- * @param int $index
285
- *
286
- * @return Command The current Command instance
287
- */
288
- public function addAtIndex($bit, $index)
289
- {
290
- array_splice($this->bits, $index, 0, $bit);
291
-
292
- return $this;
293
- }
294
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/Shell/Shell.php DELETED
@@ -1,97 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder\Shell;
13
-
14
- /**
15
- * @author Jean-François Simon <contact@jfsimon.fr>
16
- */
17
- class Shell
18
- {
19
- const TYPE_UNIX = 1;
20
- const TYPE_DARWIN = 2;
21
- const TYPE_CYGWIN = 3;
22
- const TYPE_WINDOWS = 4;
23
- const TYPE_BSD = 5;
24
-
25
- /**
26
- * @var string|null
27
- */
28
- private $type;
29
-
30
- /**
31
- * Returns guessed OS type.
32
- *
33
- * @return int
34
- */
35
- public function getType()
36
- {
37
- if (null === $this->type) {
38
- $this->type = $this->guessType();
39
- }
40
-
41
- return $this->type;
42
- }
43
-
44
- /**
45
- * Tests if a command is available.
46
- *
47
- * @param string $command
48
- *
49
- * @return bool
50
- */
51
- public function testCommand($command)
52
- {
53
- if (!function_exists('exec')) {
54
- return false;
55
- }
56
-
57
- // todo: find a better way (command could not be available)
58
- $testCommand = 'which ';
59
- if (self::TYPE_WINDOWS === $this->type) {
60
- $testCommand = 'where ';
61
- }
62
-
63
- $command = escapeshellcmd($command);
64
-
65
- exec($testCommand.$command, $output, $code);
66
-
67
- return 0 === $code && count($output) > 0;
68
- }
69
-
70
- /**
71
- * Guesses OS type.
72
- *
73
- * @return int
74
- */
75
- private function guessType()
76
- {
77
- $os = strtolower(PHP_OS);
78
-
79
- if (false !== strpos($os, 'cygwin')) {
80
- return self::TYPE_CYGWIN;
81
- }
82
-
83
- if (false !== strpos($os, 'darwin')) {
84
- return self::TYPE_DARWIN;
85
- }
86
-
87
- if (false !== strpos($os, 'bsd')) {
88
- return self::TYPE_BSD;
89
- }
90
-
91
- if (0 === strpos($os, 'win')) {
92
- return self::TYPE_WINDOWS;
93
- }
94
-
95
- return self::TYPE_UNIX;
96
- }
97
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/SplFileInfo.php DELETED
@@ -1,77 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\Finder;
13
-
14
- /**
15
- * Extends \SplFileInfo to support relative paths.
16
- *
17
- * @author Fabien Potencier <fabien@symfony.com>
18
- */
19
- class SplFileInfo extends \SplFileInfo
20
- {
21
- private $relativePath;
22
- private $relativePathname;
23
-
24
- /**
25
- * Constructor.
26
- *
27
- * @param string $file The file name
28
- * @param string $relativePath The relative path
29
- * @param string $relativePathname The relative path name
30
- */
31
- public function __construct($file, $relativePath, $relativePathname)
32
- {
33
- parent::__construct($file);
34
- $this->relativePath = $relativePath;
35
- $this->relativePathname = $relativePathname;
36
- }
37
-
38
- /**
39
- * Returns the relative path.
40
- *
41
- * @return string the relative path
42
- */
43
- public function getRelativePath()
44
- {
45
- return $this->relativePath;
46
- }
47
-
48
- /**
49
- * Returns the relative path name.
50
- *
51
- * @return string the relative path name
52
- */
53
- public function getRelativePathname()
54
- {
55
- return $this->relativePathname;
56
- }
57
-
58
- /**
59
- * Returns the contents of the file.
60
- *
61
- * @return string the contents of the file
62
- *
63
- * @throws \RuntimeException
64
- */
65
- public function getContents()
66
- {
67
- $level = error_reporting(0);
68
- $content = file_get_contents($this->getPathname());
69
- error_reporting($level);
70
- if (false === $content) {
71
- $error = error_get_last();
72
- throw new \RuntimeException($error['message']);
73
- }
74
-
75
- return $content;
76
- }
77
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/composer.json DELETED
@@ -1,31 +0,0 @@
1
- {
2
- "name": "symfony/finder",
3
- "type": "library",
4
- "description": "Symfony Finder Component",
5
- "keywords": [],
6
- "homepage": "http://symfony.com",
7
- "license": "MIT",
8
- "authors": [
9
- {
10
- "name": "Fabien Potencier",
11
- "email": "fabien@symfony.com"
12
- },
13
- {
14
- "name": "Symfony Community",
15
- "homepage": "http://symfony.com/contributors"
16
- }
17
- ],
18
- "require": {
19
- "php": ">=5.3.3"
20
- },
21
- "autoload": {
22
- "psr-0": { "Symfony\\Component\\Finder\\": "" }
23
- },
24
- "target-dir": "Symfony/Component/Finder",
25
- "minimum-stability": "dev",
26
- "extra": {
27
- "branch-alias": {
28
- "dev-master": "2.6-dev"
29
- }
30
- }
31
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/Symfony/Component/Finder/phpunit.xml.dist DELETED
@@ -1,28 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
-
3
- <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
- xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
5
- backupGlobals="false"
6
- colors="true"
7
- bootstrap="vendor/autoload.php"
8
- >
9
- <php>
10
- <!-- Silence E_USER_DEPRECATED (-16385 == -1 & ~E_USER_DEPRECATED) -->
11
- <ini name="error_reporting" value="-16385"/>
12
- </php>
13
- <testsuites>
14
- <testsuite name="Symfony Finder Component Test Suite">
15
- <directory>./Tests/</directory>
16
- </testsuite>
17
- </testsuites>
18
-
19
- <filter>
20
- <whitelist>
21
- <directory>./</directory>
22
- <exclude>
23
- <directory>./Tests</directory>
24
- <directory>./vendor</directory>
25
- </exclude>
26
- </whitelist>
27
- </filter>
28
- </phpunit>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/symfony/finder/composer.json CHANGED
@@ -19,12 +19,15 @@
19
  "php": ">=5.3.9"
20
  },
21
  "autoload": {
22
- "psr-4": { "Symfony\\Component\\Finder\\": "" }
 
 
 
23
  },
24
  "minimum-stability": "dev",
25
  "extra": {
26
  "branch-alias": {
27
- "dev-master": "2.7-dev"
28
  }
29
  }
30
  }
19
  "php": ">=5.3.9"
20
  },
21
  "autoload": {
22
+ "psr-4": { "Symfony\\Component\\Finder\\": "" },
23
+ "exclude-from-classmap": [
24
+ "/Tests/"
25
+ ]
26
  },
27
  "minimum-stability": "dev",
28
  "extra": {
29
  "branch-alias": {
30
+ "dev-master": "2.8-dev"
31
  }
32
  }
33
  }