PHP Compatibility Checker - Version 1.1.0

Version Description

  • Test results now persist page reloads.
  • Failed tests will show an overview of the results.
  • The scan timeout is now configurable using a filter. See the FAQ for more details.
Download this release

Release Info

Developer wpengine
Plugin Icon 128x128 PHP Compatibility Checker
Version 1.1.0
Comparing to
See all releases

Code changes from version 1.0.3 to 1.1.0

Files changed (5) hide show
  1. readme.txt +23 -7
  2. src/css/style.css +25 -20
  3. src/js/run.js +55 -13
  4. src/wpephpcompat.php +52 -32
  5. wpengine-phpcompat.php +37 -14
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: wpengine, octalmage, stevenkword, taylor4484
3
  Tags: php 7, php 5.5, php, version, compatibility, checker, wp engine, wpe, wpengine
4
  Requires at least: 3.0.1
5
  Tested up to: 4.5
6
- Stable tag: 1.0.3
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -41,7 +41,7 @@ To manually install:
41
  1. Upload `phpcompat` to the `/wpengine-wp-content/plugins/` directory
42
  2. Activate the plugin through the 'Plugins' menu in WordPress
43
 
44
- You will find the plugin options in the WP Admin `Tools => PHP Compatibility` menu. Once you click `run` it will take a few minutes to conduct the test. While the test is running, you cannot navigate away from the page.
45
 
46
  There are WP-CLI commands available see the [Other Notes](https://wordpress.org/plugins/php-compatibility-checker/other_notes/) tab for details.
47
 
@@ -85,9 +85,19 @@ Example: `wp phpcompat 5.5 --scan=active`
85
 
86
  5. Why was my plugin/theme skipped?
87
 
88
- Some servers have timeouts to prevent long running queries, this is commonly 60 seconds. This can prevent the checker from being able to process large themes or plugins. You should check with your host to see if this timeout can be temporarily removed. The best way around this timeout issues is to run this plugin on a [local copy](https://make.wordpress.org/core/handbook/tutorials/installing-a-local-server/) of your site.
 
 
 
 
 
 
89
 
90
- 6. I found a bug, or have a suggestion, can I contribute back?
 
 
 
 
91
 
92
  Yes! WP Engine has a public GitHub repo where you can contribute back to this plugin. Please open an issue on the [Plugin GitHub](https://github.com/wpengine/phpcompat). We actively develop this plugin, and are always happy to receive pull requests.
93
 
@@ -102,6 +112,11 @@ To disclose security issues for this plugin please email WordPress@wpengine.com
102
 
103
  == Changelog ==
104
 
 
 
 
 
 
105
  = 1.0.3 =
106
  - Fixed a bug in the WP-CLI command
107
  - Added a handful of PHP 7 compatible plugins to the whitelist
@@ -128,6 +143,7 @@ To disclose security issues for this plugin please email WordPress@wpengine.com
128
 
129
  == Upgrade Notice ==
130
 
131
- = 1.0.3 =
132
- - Fixed a bug in the WP-CLI command
133
- - Added a handful of PHP 7 compatible plugins to the whitelist
 
3
  Tags: php 7, php 5.5, php, version, compatibility, checker, wp engine, wpe, wpengine
4
  Requires at least: 3.0.1
5
  Tested up to: 4.5
6
+ Stable tag: 1.1.0
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
41
  1. Upload `phpcompat` to the `/wpengine-wp-content/plugins/` directory
42
  2. Activate the plugin through the 'Plugins' menu in WordPress
43
 
44
+ You will find the plugin options in the WP Admin `Tools => PHP Compatibility` menu. Once you click `run` it will take a few minutes to conduct the test. Feel free to navigate away from the page and check back later.
45
 
46
  There are WP-CLI commands available see the [Other Notes](https://wordpress.org/plugins/php-compatibility-checker/other_notes/) tab for details.
47
 
85
 
86
  5. Why was my plugin/theme skipped?
87
 
88
+ Some servers have timeouts to prevent long running queries, this is commonly 60 seconds. This can prevent the checker from being able to process large themes or plugins. You should check with your host to see if this timeout can be temporarily removed. The best way around this timeout issue is to run this plugin on a [local copy](https://make.wordpress.org/core/handbook/tutorials/installing-a-local-server/) of your site, or you can use the WP-CLI command.
89
+
90
+ You can use the filter `wpephpcompat_scan_timeout` to customize the scan timeout. See [this](https://gist.github.com/octalmage/07f26e0d1f25cea9a8ca92ebc67a3a14) for an example.
91
+
92
+ Setting the timeout to 0 disables the cron/timeout.
93
+
94
+ 6. The scan is stuck, what can I do?
95
 
96
+ The PHP Compatibility Checker relies on WP-Cron to process plugins/themes in batches, this is necessary to avoid server timeouts. The scan will get stuck if your site's WP-Cron isn't functioning. You can look into this using [WP Crontrol](https://wordpress.org/plugins/wp-crontrol/). The cron is called `wpephpcompat_start_test_cron`.
97
+
98
+ You can also use the [WP-CLI command](https://wordpress.org/plugins/php-compatibility-checker/other_notes/) or disable the timeout to avoid using WP-Cron.
99
+
100
+ 7. I found a bug, or have a suggestion, can I contribute back?
101
 
102
  Yes! WP Engine has a public GitHub repo where you can contribute back to this plugin. Please open an issue on the [Plugin GitHub](https://github.com/wpengine/phpcompat). We actively develop this plugin, and are always happy to receive pull requests.
103
 
112
 
113
  == Changelog ==
114
 
115
+ = 1.1.0 =
116
+ - Test results now persist page reloads.
117
+ - Failed tests will show an overview of the results.
118
+ - The scan timeout is now configurable using a filter. See the FAQ for more details.
119
+
120
  = 1.0.3 =
121
  - Fixed a bug in the WP-CLI command
122
  - Added a handful of PHP 7 compatible plugins to the whitelist
143
 
144
  == Upgrade Notice ==
145
 
146
+ = 1.1.0 =
147
+ - Test results now persist page reloads.
148
+ - Failed tests will show an overview of the results.
149
+ - The scan timeout is now configurable using a filter. See the FAQ for more details.
src/css/style.css CHANGED
@@ -2,23 +2,23 @@
2
  .wpe-results-card
3
  {
4
  background-color: #fff;
5
- border-bottom-color: rgb(221, 221, 221);
6
- border-bottom-style: solid;
7
- border-bottom-width: 1px;
8
- border-left-style: solid;
9
- border-left-width: 6px;
10
- width: 812px;
11
- padding-bottom: 14px;
12
- padding-left: 16px;
13
- padding-right: 16px;
14
- padding-top: 14px;
15
  margin-bottom: 10px;
16
  /*box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.12);*/
17
  }
18
 
19
  .wpe-results-card .inner-left
20
  {
21
- display: inline-block;
22
  letter-spacing: normal;
23
  text-rendering: auto;
24
  vertical-align: top;
@@ -29,7 +29,7 @@
29
  {
30
  color: rgb(51, 51, 51);
31
  display: inline-block;
32
- font-family: sans-serif;
33
  letter-spacing: normal;
34
  text-rendering: auto;
35
  vertical-align: top;
@@ -44,24 +44,24 @@
44
 
45
  .wpe-results-card textarea
46
  {
47
- white-space: nowrap;
48
- width: 100%;
49
  height: 400px;
50
  }
51
 
52
  .wpe-results-card .view-details
53
  {
54
- font-size: 12px;
55
  color: #c0c0c0;
56
  cursor: pointer;
57
  }
58
 
59
  .wpe-results-card .badge
60
  {
61
- display: inline-block;
62
- color: #fff;
63
- text-align: center;
64
- padding: 2px 10px;
65
  }
66
 
67
  .wpe-update a:link, .wpe-update a:visited, .wpe-update a:hover, .wpe-update a:active { color:#ffffff; cursor: pointer; text-decoration: none; }
@@ -91,4 +91,9 @@
91
  #progressbar .ui-progressbar-value
92
  {
93
  background: #0085ba;
94
- }
 
 
 
 
 
2
  .wpe-results-card
3
  {
4
  background-color: #fff;
5
+ border-bottom-color: rgb(221, 221, 221);
6
+ border-bottom-style: solid;
7
+ border-bottom-width: 1px;
8
+ border-left-style: solid;
9
+ border-left-width: 6px;
10
+ width: 812px;
11
+ padding-bottom: 14px;
12
+ padding-left: 16px;
13
+ padding-right: 16px;
14
+ padding-top: 14px;
15
  margin-bottom: 10px;
16
  /*box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.12);*/
17
  }
18
 
19
  .wpe-results-card .inner-left
20
  {
21
+ display: inline-block;
22
  letter-spacing: normal;
23
  text-rendering: auto;
24
  vertical-align: top;
29
  {
30
  color: rgb(51, 51, 51);
31
  display: inline-block;
32
+ font-family: sans-serif;
33
  letter-spacing: normal;
34
  text-rendering: auto;
35
  vertical-align: top;
44
 
45
  .wpe-results-card textarea
46
  {
47
+ white-space: nowrap;
48
+ width: 100%;
49
  height: 400px;
50
  }
51
 
52
  .wpe-results-card .view-details
53
  {
54
+ font-size: 12px;
55
  color: #c0c0c0;
56
  cursor: pointer;
57
  }
58
 
59
  .wpe-results-card .badge
60
  {
61
+ display: inline-block;
62
+ color: #fff;
63
+ text-align: center;
64
+ padding: 2px 10px;
65
  }
66
 
67
  .wpe-update a:link, .wpe-update a:visited, .wpe-update a:hover, .wpe-update a:active { color:#ffffff; cursor: pointer; text-decoration: none; }
91
  #progressbar .ui-progressbar-value
92
  {
93
  background: #0085ba;
94
+ }
95
+
96
+ #wpe-progress-count, #wpe-progress-active
97
+ {
98
+ display: inline;
99
+ }
src/js/run.js CHANGED
@@ -3,6 +3,9 @@ var test_version, only_active, timer;
3
 
4
  jQuery( document ).ready(function($) {
5
 
 
 
 
6
  $( '#developermode' ).change(function() {
7
  if ( $(this).is( ':checked' ) ) {
8
  $( '#developerMode' ).show();
@@ -72,19 +75,47 @@ function checkStatus() {
72
  obj = JSON.parse( response );
73
  } catch(e) {
74
  // If response wasn't JSON something is wrong.
75
- alert(e);
76
  return;
77
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  if ( '0' !== obj.results ) {
79
- displayReport( obj.results );
 
 
 
80
  jQuery( '#wpe-progress' ).hide();
81
  } else {
82
  jQuery( '#progressbar' ).progressbar({
83
  value: obj.progress
84
  });
 
85
 
86
  // Display the current plugin count.
87
- jQuery( '#wpe-progress-count' ).text( ( obj.total - obj.count ) + '/' + obj.total );
 
 
 
 
88
  // Requeue the checkStatus call.
89
  timer = setTimeout(function() {
90
  checkStatus();
@@ -102,6 +133,7 @@ function resetDisplay() {
102
  jQuery( '#testResults' ).text('');
103
  jQuery( '#standardMode' ).html('');
104
  jQuery( '#wpe-progress-count' ).text('');
 
105
  }
106
  /**
107
  * Loop through a string and count the total matches.
@@ -131,22 +163,29 @@ function displayReport( response ) {
131
  resetDisplay();
132
  var $ = jQuery;
133
  var compatible = 1;
 
 
 
134
  var errorsRegex = /(\d*) ERRORS?/g;
135
  var warningRegex = /(\d*) WARNINGS?/g;
136
  var updateVersionRegex = /e: (.*?);/g;
137
  var currentVersionRegex = /n: (.*?);/g;
138
- $( '#runButton' ).removeClass( 'button-primary-disabled' );
139
- $( '.spinner' ).hide();
 
 
 
140
  $( '#testResults' ).text( response );
141
  $( '#footer' ).show();
142
- $( '#runButton' ).val( 'Re-run' );
143
  // Separate plugins/themes.
144
  var plugins = response.replace( /^\s+|\s+$/g, '' ).split( 'Name: ' );
 
 
 
 
145
  // Loop through them.
146
  for ( var x in plugins ) {
147
- if ( '' === plugins[x].trim() ) {
148
- continue;
149
- }
150
  var updateVersion;
151
  var updateAvailable = 0;
152
  var passed = 1;
@@ -166,16 +205,15 @@ function displayReport( response ) {
166
  if ( parseInt( errors ) > 0 ) {
167
  compatible = 0;
168
  passed = 0;
 
169
  }
170
  // Trim whitespace and newlines from report.
171
  log = log.replace( /^\s+|\s+$/g, '' );
172
-
173
  if ( log.search('skipped') !== -1 ) {
174
  skipped = 1;
175
  }
176
  // Use handlebars to build our template.
177
- var source = $( '#result-template' ).html();
178
- var template = Handlebars.compile( source );
179
  var context = {
180
  plugin_name: name,
181
  warnings: warnings,
@@ -189,10 +227,14 @@ function displayReport( response ) {
189
  var html = template( context );
190
  $('#standardMode').append( html );
191
  }
 
192
  // Display global compatibility status.
193
  if ( compatible ) {
194
  $( '#standardMode' ).prepend( '<h3>Your WordPress install is PHP ' + test_version + ' compatible.</h3>' );
195
  } else {
 
 
 
196
  $( '#standardMode' ).prepend( '<h3>Your WordPress install is not PHP ' + test_version + ' compatible.</h3>' );
197
  }
198
- }
3
 
4
  jQuery( document ).ready(function($) {
5
 
6
+ // Check the status immediately to reflect if tests are running.
7
+ checkStatus();
8
+
9
  $( '#developermode' ).change(function() {
10
  if ( $(this).is( ':checked' ) ) {
11
  $( '#developerMode' ).show();
75
  obj = JSON.parse( response );
76
  } catch(e) {
77
  // If response wasn't JSON something is wrong.
78
+ alert( "Error: " + e + "\nResponse: " + response );
79
  return;
80
  }
81
+
82
+ /*
83
+ * Status false: the test is not running and has not been run yet
84
+ * Status 1: the test is currently running
85
+ * Status 0: the test as completed but is not currently running
86
+ */
87
+ if ( false === obj.results ) {
88
+ jQuery( '#runButton' ).val( 'Run' );
89
+ } else {
90
+ jQuery( '#runButton' ).val( 'Re-run' );
91
+ }
92
+
93
+ if ( '1' === obj.status ) {
94
+ jQuery( '#runButton' ).addClass( 'button-primary-disabled' );
95
+ jQuery( '.spinner' ).show();
96
+ } else {
97
+ jQuery( '#runButton' ).removeClass( 'button-primary-disabled' );
98
+ jQuery( '.spinner' ).hide();
99
+ }
100
+
101
  if ( '0' !== obj.results ) {
102
+ if( false !== obj.results ) {
103
+ test_version = obj.version;
104
+ displayReport( obj.results );
105
+ }
106
  jQuery( '#wpe-progress' ).hide();
107
  } else {
108
  jQuery( '#progressbar' ).progressbar({
109
  value: obj.progress
110
  });
111
+ jQuery( '#wpe-progress' ).show();
112
 
113
  // Display the current plugin count.
114
+ jQuery( '#wpe-progress-count' ).text( ( obj.total - obj.count + 1 ) + '/' + obj.total );
115
+
116
+ // Display the object being scanned.
117
+ jQuery( '#wpe-progress-active' ).text( obj.activeJob );
118
+
119
  // Requeue the checkStatus call.
120
  timer = setTimeout(function() {
121
  checkStatus();
133
  jQuery( '#testResults' ).text('');
134
  jQuery( '#standardMode' ).html('');
135
  jQuery( '#wpe-progress-count' ).text('');
136
+ jQuery( '#wpe-progress-active' ).text('');
137
  }
138
  /**
139
  * Loop through a string and count the total matches.
163
  resetDisplay();
164
  var $ = jQuery;
165
  var compatible = 1;
166
+
167
+ // Keep track of the number of failed plugins/themes.
168
+ var failedCount = 0;
169
  var errorsRegex = /(\d*) ERRORS?/g;
170
  var warningRegex = /(\d*) WARNINGS?/g;
171
  var updateVersionRegex = /e: (.*?);/g;
172
  var currentVersionRegex = /n: (.*?);/g;
173
+
174
+ // Grab and compile our template.
175
+ var source = $( '#result-template' ).html();
176
+ var template = Handlebars.compile( source );
177
+
178
  $( '#testResults' ).text( response );
179
  $( '#footer' ).show();
180
+
181
  // Separate plugins/themes.
182
  var plugins = response.replace( /^\s+|\s+$/g, '' ).split( 'Name: ' );
183
+
184
+ // Remove the first item, it's empty.
185
+ plugins.shift();
186
+
187
  // Loop through them.
188
  for ( var x in plugins ) {
 
 
 
189
  var updateVersion;
190
  var updateAvailable = 0;
191
  var passed = 1;
205
  if ( parseInt( errors ) > 0 ) {
206
  compatible = 0;
207
  passed = 0;
208
+ failedCount++;
209
  }
210
  // Trim whitespace and newlines from report.
211
  log = log.replace( /^\s+|\s+$/g, '' );
212
+
213
  if ( log.search('skipped') !== -1 ) {
214
  skipped = 1;
215
  }
216
  // Use handlebars to build our template.
 
 
217
  var context = {
218
  plugin_name: name,
219
  warnings: warnings,
227
  var html = template( context );
228
  $('#standardMode').append( html );
229
  }
230
+
231
  // Display global compatibility status.
232
  if ( compatible ) {
233
  $( '#standardMode' ).prepend( '<h3>Your WordPress install is PHP ' + test_version + ' compatible.</h3>' );
234
  } else {
235
+ // Display scan stats.
236
+ $( '#standardMode' ).prepend( '<p>' + failedCount + ' out of ' + plugins.length + ' plugins/themes are not compatible.</p>' );
237
+
238
  $( '#standardMode' ).prepend( '<h3>Your WordPress install is not PHP ' + test_version + ' compatible.</h3>' );
239
  }
240
+ }
src/wpephpcompat.php CHANGED
@@ -69,6 +69,7 @@ class WPEPHPCompat {
69
  '*/wordfence/*' => '7.0', // https://github.com/wpengine/phpcompat/wiki/Results#wordfence-security
70
  '*/woocommerce/*' => '7.0', // https://github.com/wpengine/phpcompat/wiki/Results#woocommerce
71
  '*/wp-migrate-db/*' => '7.0', // https://github.com/wpengine/phpcompat/wiki/Results#wp-migrate-db
 
72
  );
73
 
74
  /**
@@ -88,33 +89,54 @@ class WPEPHPCompat {
88
  public function start_test() {
89
 
90
  $this->debug_log( 'startScan: ' . isset( $_POST['startScan'] ) );
91
- // Try to lock.
92
- $lock_result = add_option( 'wpephpcompat.lock', time(), '', 'no' );
93
 
94
- $this->debug_log( 'lock: ' . $lock_result );
95
-
96
- if ( ! $lock_result ) {
97
- $lock_result = get_option( 'wpephpcompat.lock' );
98
-
99
- // Bail if we were unable to create a lock, or if the existing lock is still valid.
100
- if ( ! $lock_result || ( $lock_result > ( time() - MINUTE_IN_SECONDS ) ) ) {
101
- $this->debug_log( 'Process already running (locked), returning.' );
102
-
103
- $timestamp = wp_next_scheduled( 'wpephpcompat_start_test_cron' );
104
-
105
- if ( $timestamp == false ) {
106
- wp_schedule_single_event( time() + ( MINUTE_IN_SECONDS ), 'wpephpcompat_start_test_cron' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  }
108
- return;
109
  }
 
110
  }
111
- update_option( 'wpephpcompat.lock', time(), false );
112
 
113
  // Check to see if scan has already started.
114
  $scan_status = get_option( 'wpephpcompat.status' );
115
  $this->debug_log( 'scan status: ' . $scan_status );
116
  if ( ! $scan_status ) {
117
 
 
 
 
118
  update_option( 'wpephpcompat.status', '1', false );
119
  update_option( 'wpephpcompat.test_version', $this->test_version, false );
120
  update_option( 'wpephpcompat.only_active', $this->only_active, false );
@@ -145,15 +167,16 @@ class WPEPHPCompat {
145
 
146
  return;
147
  }
148
-
149
- wp_schedule_single_event( time() + ( MINUTE_IN_SECONDS ), 'wpephpcompat_start_test_cron' );
 
150
 
151
  if ( ! $this->is_command_line() ) {
152
  // Close the connection to the browser.
153
  $this->close_connection("started");
154
 
155
- // Kill cron after a minute.
156
- set_time_limit( 55 );
157
  }
158
 
159
  $scan_results = get_option( 'wpephpcompat.scan_results' );
@@ -163,7 +186,7 @@ class WPEPHPCompat {
163
 
164
  // Add the plugin/theme name to the results.
165
  $scan_results .= 'Name: ' . $directory->post_title . "\n\n";
166
-
167
  // Keep track of the number of times we've attempted to scan the plugin.
168
  $count = get_post_meta( $directory->ID, 'count', true ) ?: 1;
169
  $this->debug_log( 'Attempted scan count: ' . $count );
@@ -177,7 +200,7 @@ class WPEPHPCompat {
177
  continue;
178
  }
179
 
180
- // Increment and save the count.
181
  $count++;
182
  update_post_meta( $directory->ID, 'count', $count );
183
 
@@ -237,7 +260,7 @@ class WPEPHPCompat {
237
 
238
  return $this->clean_report( $report );
239
  }
240
-
241
  /**
242
  * Generate a list of ignored files and directories.
243
  *
@@ -251,14 +274,14 @@ class WPEPHPCompat {
251
  '*/node_modules/*', // Commonly used for development but not in production.
252
  '*/tmp/*', // Temporary files.
253
  );
254
-
255
  foreach ( $this->whitelist as $plugin => $version ) {
256
  // Check to see if the plugin is compatible with the tested version.
257
  if ( version_compare( $this->test_version, $version, '<=' ) ) {
258
  array_push( $ignored, $plugin );
259
  }
260
  }
261
-
262
  return $ignored;
263
  }
264
 
@@ -337,13 +360,13 @@ class WPEPHPCompat {
337
 
338
  $this->add_directory( $all_themes[$k]->Name, $theme_path );
339
  }
340
-
341
  // Add parent theme if the current theme is a child theme.
342
  if ( 'yes' === $this->only_active && is_child_theme() ) {
343
  $parent_theme_path = get_template_directory();
344
  $theme_data = wp_get_theme();
345
  $parent_theme_name = $theme_data->parent()->Name;
346
-
347
  $this->add_directory( $parent_theme_name, $parent_theme_path );
348
  }
349
  }
@@ -354,7 +377,7 @@ class WPEPHPCompat {
354
  * @param string $report The full report.
355
  * @return string The cleaned report.
356
  */
357
- private function clean_report( $report ) {
358
  // Remove unnecessary overview.
359
  $report = preg_replace ( '/Time:.+\n/si', '', $report );
360
 
@@ -374,9 +397,6 @@ class WPEPHPCompat {
374
  // Delete options created during the scan.
375
  delete_option( 'wpephpcompat.lock' );
376
  delete_option( 'wpephpcompat.status' );
377
- delete_option( 'wpephpcompat.scan_results' );
378
- delete_option( 'wpephpcompat.test_version' );
379
- delete_option( 'wpephpcompat.only_active' );
380
  delete_option( 'wpephpcompat.numdirs' );
381
 
382
  // Clear scheduled cron.
69
  '*/wordfence/*' => '7.0', // https://github.com/wpengine/phpcompat/wiki/Results#wordfence-security
70
  '*/woocommerce/*' => '7.0', // https://github.com/wpengine/phpcompat/wiki/Results#woocommerce
71
  '*/wp-migrate-db/*' => '7.0', // https://github.com/wpengine/phpcompat/wiki/Results#wp-migrate-db
72
+ '*/easy-digital-downloads/*' => '7.0', // https://github.com/wpengine/phpcompat/wiki/Results#easy-digital-downloads
73
  );
74
 
75
  /**
89
  public function start_test() {
90
 
91
  $this->debug_log( 'startScan: ' . isset( $_POST['startScan'] ) );
 
 
92
 
93
+ /**
94
+ * Filters the scan timeout.
95
+ *
96
+ * Lets you change the timeout of the scan. The value is how long the scan
97
+ * runs before dying and picking back up on a cron. You can set $timeout to
98
+ * 0 to disable the timeout and the cron.
99
+ *
100
+ * @since 1.0.4
101
+ *
102
+ * @param int $timeout The timeout in seconds.
103
+ */
104
+ $timeout = apply_filters( 'wpephpcompat_scan_timeout', MINUTE_IN_SECONDS );
105
+ $this->debug_log( 'timeout: ' . $timeout );
106
+
107
+ // No reason to lock if there's no timeout.
108
+ if ( 0 !== $timeout ) {
109
+ // Try to lock.
110
+ $lock_result = add_option( 'wpephpcompat.lock', time(), '', 'no' );
111
+
112
+ $this->debug_log( 'lock: ' . $lock_result );
113
+
114
+ if ( ! $lock_result ) {
115
+ $lock_result = get_option( 'wpephpcompat.lock' );
116
+
117
+ // Bail if we were unable to create a lock, or if the existing lock is still valid.
118
+ if ( ! $lock_result || ( $lock_result > ( time() - $timeout ) ) ) {
119
+ $this->debug_log( 'Process already running (locked), returning.' );
120
+
121
+ $timestamp = wp_next_scheduled( 'wpephpcompat_start_test_cron' );
122
+
123
+ if ( $timestamp == false ) {
124
+ wp_schedule_single_event( time() + $timeout, 'wpephpcompat_start_test_cron' );
125
+ }
126
+ return;
127
  }
 
128
  }
129
+ update_option( 'wpephpcompat.lock', time(), false );
130
  }
 
131
 
132
  // Check to see if scan has already started.
133
  $scan_status = get_option( 'wpephpcompat.status' );
134
  $this->debug_log( 'scan status: ' . $scan_status );
135
  if ( ! $scan_status ) {
136
 
137
+ // Clear the previous results.
138
+ delete_option( 'wpephpcompat.scan_results' );
139
+
140
  update_option( 'wpephpcompat.status', '1', false );
141
  update_option( 'wpephpcompat.test_version', $this->test_version, false );
142
  update_option( 'wpephpcompat.only_active', $this->only_active, false );
167
 
168
  return;
169
  }
170
+ if ( 0 !== $timeout ) {
171
+ wp_schedule_single_event( time() + $timeout, 'wpephpcompat_start_test_cron' );
172
+ }
173
 
174
  if ( ! $this->is_command_line() ) {
175
  // Close the connection to the browser.
176
  $this->close_connection("started");
177
 
178
+ // Kill cron after a configurable timeout.
179
+ set_time_limit( ( $timeout > 5 ? $timeout - 5 : $timeout ) );
180
  }
181
 
182
  $scan_results = get_option( 'wpephpcompat.scan_results' );
186
 
187
  // Add the plugin/theme name to the results.
188
  $scan_results .= 'Name: ' . $directory->post_title . "\n\n";
189
+
190
  // Keep track of the number of times we've attempted to scan the plugin.
191
  $count = get_post_meta( $directory->ID, 'count', true ) ?: 1;
192
  $this->debug_log( 'Attempted scan count: ' . $count );
200
  continue;
201
  }
202
 
203
+ // Increment and save the count.
204
  $count++;
205
  update_post_meta( $directory->ID, 'count', $count );
206
 
260
 
261
  return $this->clean_report( $report );
262
  }
263
+
264
  /**
265
  * Generate a list of ignored files and directories.
266
  *
274
  '*/node_modules/*', // Commonly used for development but not in production.
275
  '*/tmp/*', // Temporary files.
276
  );
277
+
278
  foreach ( $this->whitelist as $plugin => $version ) {
279
  // Check to see if the plugin is compatible with the tested version.
280
  if ( version_compare( $this->test_version, $version, '<=' ) ) {
281
  array_push( $ignored, $plugin );
282
  }
283
  }
284
+
285
  return $ignored;
286
  }
287
 
360
 
361
  $this->add_directory( $all_themes[$k]->Name, $theme_path );
362
  }
363
+
364
  // Add parent theme if the current theme is a child theme.
365
  if ( 'yes' === $this->only_active && is_child_theme() ) {
366
  $parent_theme_path = get_template_directory();
367
  $theme_data = wp_get_theme();
368
  $parent_theme_name = $theme_data->parent()->Name;
369
+
370
  $this->add_directory( $parent_theme_name, $parent_theme_path );
371
  }
372
  }
377
  * @param string $report The full report.
378
  * @return string The cleaned report.
379
  */
380
+ public function clean_report( $report ) {
381
  // Remove unnecessary overview.
382
  $report = preg_replace ( '/Time:.+\n/si', '', $report );
383
 
397
  // Delete options created during the scan.
398
  delete_option( 'wpephpcompat.lock' );
399
  delete_option( 'wpephpcompat.status' );
 
 
 
400
  delete_option( 'wpephpcompat.numdirs' );
401
 
402
  // Clear scheduled cron.
wpengine-phpcompat.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: PHP Compatibility Checker
4
  Plugin URI: https://wpengine.com
5
  Description: Make sure your plugins and themes are compatible with newer PHP versions.
6
  Author: WP Engine
7
- Version: 1.0.3
8
  Author URI: https://wpengine.com
9
  */
10
 
@@ -35,7 +35,7 @@ class WPEngine_PHPCompat {
35
  * @return self An instance of this class.
36
  */
37
  public static function instance() {
38
- if( ! self::$instance ) {
39
  self::$instance = new self;
40
  self::$instance->init();
41
  }
@@ -105,17 +105,33 @@ class WPEngine_PHPCompat {
105
  $scan_status = get_option( 'wpephpcompat.status' );
106
  $count_jobs = wp_count_posts( 'wpephpcompat_jobs' );
107
  $total_jobs = get_option( 'wpephpcompat.numdirs' );
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
  $to_encode = array(
110
- 'status' => $scan_status,
111
- 'count' => $count_jobs->publish,
112
- 'total' => $total_jobs,
113
- 'progress' => 100 - ( ( $count_jobs->publish / $total_jobs ) * 100 )
 
 
114
  );
115
 
116
  // If the scan is still running.
117
  if ( $scan_status ) {
118
  $to_encode['results'] = '0';
 
 
119
  } else {
120
  // Else return the results and clean up!
121
  $scan_results = get_option( 'wpephpcompat.scan_results' );
@@ -160,7 +176,7 @@ class WPEngine_PHPCompat {
160
  function admin_enqueue( $hook ) {
161
 
162
  // Only enqueue these assets on the settings page.
163
- if( $this->page !== $hook ) {
164
  return;
165
  }
166
 
@@ -197,6 +213,13 @@ class WPEngine_PHPCompat {
197
  * @return null
198
  */
199
  function settings_page() {
 
 
 
 
 
 
 
200
  ?>
201
  <div class="wrap">
202
  <div style="float: left;">
@@ -212,16 +235,16 @@ class WPEngine_PHPCompat {
212
  <tr>
213
  <th scope="row"><label for="phptest_version">PHP Version</label></th>
214
  <td>
215
- <label><input type="radio" name="phptest_version" value="7.0" checked="checked"> PHP 7.0</label><br>
216
- <label><input type="radio" name="phptest_version" value="5.5"> PHP 5.5</label><br>
217
- <label><input type="radio" name="phptest_version" value="5.4"> PHP 5.4</label><br>
218
- <label><input type="radio" name="phptest_version" value="5.3"> PHP 5.3</label>
219
  </td>
220
  </tr>
221
  <tr>
222
  <th scope="row"><label for="active_plugins">Only Active</label></th>
223
- <td><label><input type="radio" name="active_plugins" value="yes" checked="checked"> Only scan active plugins and themes</label><br>
224
- <label><input type="radio" name="active_plugins" value="no"> Scan all plugins and themes</label>
225
  </td>
226
  </tr>
227
  </tbody>
@@ -231,7 +254,7 @@ class WPEngine_PHPCompat {
231
  <label for="">Progress</label>
232
  <div id="progressbar"></div>
233
  <div id="wpe-progress-count"></div>
234
- <b>Please don't leave this page during the test.</b>
235
  </div>
236
 
237
  <!-- Area for pretty results. -->
4
  Plugin URI: https://wpengine.com
5
  Description: Make sure your plugins and themes are compatible with newer PHP versions.
6
  Author: WP Engine
7
+ Version: 1.1.0
8
  Author URI: https://wpengine.com
9
  */
10
 
35
  * @return self An instance of this class.
36
  */
37
  public static function instance() {
38
+ if ( ! self::$instance ) {
39
  self::$instance = new self;
40
  self::$instance->init();
41
  }
105
  $scan_status = get_option( 'wpephpcompat.status' );
106
  $count_jobs = wp_count_posts( 'wpephpcompat_jobs' );
107
  $total_jobs = get_option( 'wpephpcompat.numdirs' );
108
+ $test_version = get_option( 'wpephpcompat.test_version' );
109
+ $only_active = get_option( 'wpephpcompat.only_active' );
110
+
111
+ $active_job = false;
112
+ $jobs = get_posts( array(
113
+ 'posts_per_page' => -1,
114
+ 'post_type' => 'wpephpcompat_jobs',
115
+ ) );
116
+
117
+ if ( 0 < count( $jobs ) ) {
118
+ $active_job = $jobs[ 0 ]->post_title;
119
+ }
120
 
121
  $to_encode = array(
122
+ 'status' => $scan_status,
123
+ 'count' => $count_jobs->publish,
124
+ 'total' => $total_jobs,
125
+ 'activeJob' => $active_job,
126
+ 'version' => $test_version,
127
+ 'onlyActive' => $only_active,
128
  );
129
 
130
  // If the scan is still running.
131
  if ( $scan_status ) {
132
  $to_encode['results'] = '0';
133
+ // Adding one because we don't remove an item until it's done processing.
134
+ $to_encode['progress'] = 100 - ( ( ( $count_jobs->publish + 1 ) / $total_jobs ) * 100 );
135
  } else {
136
  // Else return the results and clean up!
137
  $scan_results = get_option( 'wpephpcompat.scan_results' );
176
  function admin_enqueue( $hook ) {
177
 
178
  // Only enqueue these assets on the settings page.
179
+ if ( $this->page !== $hook ) {
180
  return;
181
  }
182
 
213
  * @return null
214
  */
215
  function settings_page() {
216
+ // Discovers last options used.
217
+ $test_version = get_option( 'wpephpcompat.test_version' );
218
+ $only_active = get_option( 'wpephpcompat.only_active' );
219
+
220
+ // Assigns defaults for the scan if none are found in the database.
221
+ $test_version = ( false !== $test_version ) ? $test_version : '7.0';
222
+ $only_active = ( false !== $only_active ) ? $only_active : 'yes';
223
  ?>
224
  <div class="wrap">
225
  <div style="float: left;">
235
  <tr>
236
  <th scope="row"><label for="phptest_version">PHP Version</label></th>
237
  <td>
238
+ <label><input type="radio" name="phptest_version" value="7.0" <?php checked( $test_version, '7.0', true ); ?>> PHP 7.0</label><br>
239
+ <label><input type="radio" name="phptest_version" value="5.5" <?php checked( $test_version, '5.5', true ); ?>> PHP 5.5</label><br>
240
+ <label><input type="radio" name="phptest_version" value="5.4" <?php checked( $test_version, '5.4', true ); ?>> PHP 5.4</label><br>
241
+ <label><input type="radio" name="phptest_version" value="5.3" <?php checked( $test_version, '5.3', true ); ?>> PHP 5.3</label>
242
  </td>
243
  </tr>
244
  <tr>
245
  <th scope="row"><label for="active_plugins">Only Active</label></th>
246
+ <td><label><input type="radio" name="active_plugins" value="yes" <?php checked( $only_active, 'yes', true ); ?>> Only scan active plugins and themes</label><br>
247
+ <label><input type="radio" name="active_plugins" value="no" <?php checked( $only_active, 'no', true ); ?>> Scan all plugins and themes</label>
248
  </td>
249
  </tr>
250
  </tbody>
254
  <label for="">Progress</label>
255
  <div id="progressbar"></div>
256
  <div id="wpe-progress-count"></div>
257
+ <div id="wpe-progress-active"></div>
258
  </div>
259
 
260
  <!-- Area for pretty results. -->