PluginProbe
Plugin Memory Usage / 1.3.0
Plugin Memory Usage v1.3.0
trunk 1.0 1.0.2 1.1.0 1.1.2 1.1.3 1.2.0 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0
plugin-memory-usage / plugin-memory-usage.php

plugin-memory-usage.php in Plugin Memory Usage 1.3.0, at plugin-memory-usage.php

1,289 lines 41.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Plugin Memory Usage
4 Plugin URI: https://nett.pro/en/plugins/
5 Description: Display different plugins memory usage and impact on Wordpress.
6 Version: 1.3.0
7 Author: NETT.PRO
8 Author URI: https://nett.pro/en/
9 License: GPLv3
10 */
11
12 // Prevent direct access to this file
13 if (!defined('ABSPATH')) {
14 exit;
15 }
16
17 define('PLUGINMEMORYUSAGE_VERSION', '1.3.0');
18
19
20 function pmusage_memory_usage_admin_menu() {
21 add_menu_page(
22 'Plugin Memory Usage',
23 'Memory Usage',
24 'manage_options',
25 'wp_plugin_memory_usage',
26 'pmusage_memory_usage_admin_page',
27 'dashicons-performance',
28 100
29 );
30 }
31 add_action('admin_menu', 'pmusage_memory_usage_admin_menu');
32
33
34
35
36 // Function to register the dashboard widget
37 function pmusage_memory_usage_widget() {
38
39 wp_add_dashboard_widget(
40 'wp_memory_usage',
41 'Plugin Memory Usage <span class="wpmem-version">'.PLUGINMEMORYUSAGE_VERSION.'</span>',
42 'pmusage_display_memory_usage_dashboard'
43 );
44 }
45
46 // Hook to add the widget to the dashboard
47 add_action('wp_dashboard_setup', 'pmusage_memory_usage_widget', 1);
48
49
50
51
52
53
54
55 // Get supported PHP versions with caching
56 function pmusage_get_supported_php_versions() {
57 $supported_versions = get_transient('pmusage_supported_php_versions');
58
59 if (false === $supported_versions) {
60 $response = wp_remote_get('https://www.php.net/supported-versions.php', array(
61 'sslverify' => false,
62 'timeout' => 5
63 ));
64
65 if (!is_wp_error($response) && 200 === wp_remote_retrieve_response_code($response)) {
66 $html = wp_remote_retrieve_body($response);
67 $supported_versions = pmusage_parse_supported_versions($html);
68 set_transient('pmusage_supported_php_versions', $supported_versions, WEEK_IN_SECONDS);
69 }
70 }
71
72 return is_array($supported_versions) ? $supported_versions : [];
73 }
74
75
76
77 // Parse HTML response from PHP.net
78 function pmusage_parse_supported_versions($html) {
79 $versions = [];
80 $dom = new DOMDocument();
81 @$dom->loadHTML($html);
82 $tables = $dom->getElementsByTagName('table');
83
84 foreach ($tables as $table) {
85 $rows = $table->getElementsByTagName('tr');
86 foreach ($rows as $row) {
87 $cells = $row->getElementsByTagName('td');
88 if ($cells->length >= 5) {
89 $version = trim($cells->item(0)->textContent);
90 $eol_date = DateTime::createFromFormat('d M Y', trim($cells->item(3)->textContent));
91
92 if ($eol_date) {
93 $versions[$version] = [
94 'eol' => $eol_date->format('Y-m-d'),
95 'status' => (new DateTime() < $eol_date) ? 'supported' : 'eol'
96 ];
97 }
98 }
99 }
100 }
101
102 return $versions;
103 }
104
105
106
107
108
109
110 function pmusage_get_php_status($current_version) {
111 $supported_versions = pmusage_get_supported_php_versions();
112 $current_branch = preg_replace('/^(\d+\.\d+).*$/', '$1', $current_version);
113
114 if ( isset($supported_versions[$current_branch]) && $supported_versions[$current_branch]['status'] === 'supported' ) {
115 return 'supported';
116 }
117 return 'eol';
118 }
119
120
121
122
123
124 /**
125 * Get latest WordPress version from WordPress.org API with extensive debugging
126 */
127 function pmusage_get_latest_wp_version() {
128
129 // Try to get from cache first
130 $latest_version = get_transient('pmusage_latest_wp_version');
131
132 if (false !== $latest_version) {
133 return $latest_version;
134 }
135
136
137 // Use version 1.7 which returns JSON format
138 $url = 'https://api.wordpress.org/core/version-check/1.7/';
139
140
141 $response = wp_remote_get($url, array(
142 'timeout' => 10,
143 'sslverify' => true,
144 'user-agent' => 'WordPress/' . get_bloginfo('version') . '; ' . get_bloginfo('url')
145 ));
146
147 // Check for errors
148 if (is_wp_error($response)) {
149 return 'Unknown';
150 }
151
152 $response_code = wp_remote_retrieve_response_code($response);
153
154 if (200 !== $response_code) {
155 return 'Unknown';
156 }
157
158 $body = wp_remote_retrieve_body($response);
159
160 if (empty($body)) {
161 return 'Unknown';
162 }
163
164 $data = json_decode($body, true);
165
166 if (json_last_error() !== JSON_ERROR_NONE) {
167 return 'Unknown';
168 }
169
170
171 if (isset($data['offers']) && is_array($data['offers']) && !empty($data['offers'])) {
172 // Get the first offer which is the latest stable version
173 $latest_version = $data['offers'][0]['version'] ?? 'Unknown';
174 } else {
175 return 'Unknown';
176 }
177
178 // Only cache if we got a valid version
179 if ($latest_version !== 'Unknown' && preg_match('/^\d+\.\d+/', $latest_version)) {
180 set_transient('pmusage_latest_wp_version', $latest_version, 12 * HOUR_IN_SECONDS);
181 }
182
183 return $latest_version;
184 }
185
186
187
188
189
190
191
192
193 function pmusage_get_cpu_usage() {
194 if ( ! function_exists('sys_getloadavg') ) {
195 return null;
196 }
197 $load = sys_getloadavg();
198 $cores = 1;
199 if ( is_readable('/proc/cpuinfo') ) {
200 $cpuinfo = file_get_contents('/proc/cpuinfo');
201 preg_match_all('/^processor/m', $cpuinfo, $matches);
202 $cores = max(1, count($matches[0]));
203 }
204 return round(($load[0] / $cores) * 100, 1);
205 }
206
207 function pmusage_get_disk_usage() {
208
209 $path = defined('ABSPATH') ? ABSPATH : '/';
210 $total = @disk_total_space($path);
211 $free = @disk_free_space($path);
212 if ( ! $total || ! $free ) {
213 return null;
214 }
215 $used = $total - $free;
216 $percent = round(($used / $total) * 100, 1);
217 return [
218 'used' => $used,
219 'total' => $total,
220 'free' => $free,
221 'percent' => $percent,
222 ];
223
224 }
225
226
227
228
229
230
231
232
233
234
235
236 // to be shown in several places
237 function pmusage_render_system_info() {
238 global $wpdb;
239
240 // Get PHP version and status
241 $php_version = phpversion();
242 $php_status = pmusage_get_php_status($php_version);
243
244 // Dashicons for PHP version status
245 $icons = [
246 'supported' => '<span class="dashicons dashicons-yes-alt" style="color:#46b450; margin-left:5px;" title="PHP version is supported"></span>',
247 'eol' => '<span class="dashicons dashicons-warning" style="color:#dc3232; margin-left:5px;" title="PHP version is end-of-life"></span>'
248 ];
249
250
251
252 // Fetch latest PHP version with error handling
253 $latest_php_version = get_transient('pmusage_latest_php_version');
254 if (false === $latest_php_version) {
255 $response = wp_remote_get('https://www.php.net/releases/?json', array(
256 'sslverify' => false,
257 'timeout' => 5
258 ));
259
260 if (!is_wp_error($response) && 200 === wp_remote_retrieve_response_code($response)) {
261 $data = json_decode(wp_remote_retrieve_body($response), true);
262 if (is_array($data) && !empty($data)) {
263 // Extract versions
264 $versions = array();
265 foreach ($data as $major_version => $release_info) {
266 if (isset($release_info['version'])) {
267 $versions[] = $release_info['version'];
268 }
269 }
270
271 usort($versions, 'version_compare');
272 $latest_php_version = end($versions);
273 set_transient('pmusage_latest_php_version', $latest_php_version, 12 * HOUR_IN_SECONDS);
274 }
275 }
276 }
277
278 // Validation
279 if (empty($latest_php_version) || !preg_match('/^\d+\.\d+(\.\d+)?$/', $latest_php_version)) {
280 $latest_php_version = 'Unknown';
281 }
282
283
284 $is_latest = ($latest_php_version !== 'Unknown') ? version_compare($php_version, $latest_php_version, '>=') : false;
285
286
287
288 // Get MySQL version and status
289 $mysql_version = wp_cache_get('pmusage_mysql_version');
290 if (false === $mysql_version) {
291 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Getting MySQL version requires direct query, no WordPress alternative available
292 $mysql_version = $wpdb->get_var("SELECT VERSION()");
293 wp_cache_set('pmusage_mysql_version', $mysql_version, '', 12 * HOUR_IN_SECONDS);
294 }
295
296 $mysql_status = pmusage_get_mysql_status($mysql_version);
297 $latest_mysql_version = pmusage_get_mysql_latest_version($mysql_version);
298
299 // Enhanced icons for MySQL status with tooltips
300 $mysql_icons = [
301 'supported' => '<span class="dashicons dashicons-yes-alt" style="color: #46b450;" title="MySQL/MariaDB version is currently supported"></span>',
302 'eol' => '<span class="dashicons dashicons-warning" style="color: #dc3232;" title="MySQL/MariaDB version has reached end-of-life - upgrade recommended"></span>',
303 'innovation' => '<span class="dashicons dashicons-lightbulb" style="color: #ffb900;" title="MySQL Innovation Release - shorter support cycle"></span>',
304 ' unknown' => '<span class="dashicons dashicons-editor-help" style="color: #666;" title="Unable to determine MySQL/MariaDB version support status"></span>'
305 ];
306
307 // Check if current version is latest
308 $current_version_number = '';
309 preg_match('/(\d+\.\d+\.\d+)/', $mysql_version, $matches);
310 if (!empty($matches[1])) {
311 $current_version_number = $matches[1];
312 }
313 $is_latest_mysql = ($latest_mysql_version !== 'Unknown' && !empty($current_version_number)) ?
314 version_compare($current_version_number, $latest_mysql_version, '>=') : false;
315
316
317
318
319 // Get max upload size
320 $max_upload = ini_get('upload_max_filesize');
321 $max_post = ini_get('post_max_size');
322 $memory_limit = ini_get('memory_limit');
323 $upload_mb = min(
324 wp_convert_hr_to_bytes($max_upload),
325 wp_convert_hr_to_bytes($max_post),
326 wp_convert_hr_to_bytes($memory_limit)
327 );
328
329 // Get WP and PHP memory limits
330 $wp_memory_limit = wp_convert_hr_to_bytes(WP_MEMORY_LIMIT);
331 $php_memory_limit = ini_get('memory_limit');
332
333
334
335
336
337
338 // Get current and latest WordPress version
339 $wp_version = get_bloginfo('version');
340
341
342 $latest_wp_version = pmusage_get_latest_wp_version();
343
344
345 // Check if current version is latest
346 $is_wp_latest = ($latest_wp_version !== 'Unknown') ? version_compare($wp_version, $latest_wp_version, '>=') : null;
347
348
349 // Display WordPress version with status
350 echo '<p class="pluginmemoryusage-system-info-line"><strong>WordPress Version:</strong> ' . esc_html($wp_version);
351
352 // Add badge if version is up to date
353 if ($is_wp_latest === true) {
354 echo ' <span class="dashicons dashicons-yes-alt" style="color: #46b450; margin-left: 5px;" title="WordPress is up to date"></span>';
355 } elseif ($is_wp_latest === false) {
356 echo ' <span class="dashicons dashicons-warning" style="color: #dc3232; margin-left: 5px;" title="WordPress update available"></span>';
357 }
358
359 // Show latest version info
360 if ('Unknown' !== $latest_wp_version) {
361 $label = $is_wp_latest ? 'Latest' : 'Latest: ' . esc_html($latest_wp_version);
362 $class = $is_wp_latest ? 'pluginmemoryusage_version-latest' : 'pluginmemoryusage_version-outdated';
363 echo ' <span class="' . esc_attr($class) . '">' . esc_html($label) . '</span>';
364 } else {
365 echo ' <span class="pluginmemoryusage_version-unknown">Version check failed</span>';
366 }
367
368 echo '</p>';
369
370
371
372
373
374
375
376
377 echo '<p class="pluginmemoryusage-system-info-line"><strong>PHP Version:</strong> ' . esc_html($php_version);
378 echo wp_kses_post($icons[$php_status]);
379 if ('Unknown' !== $latest_php_version) {
380 $label = $is_latest ? 'Latest' : 'Latest: ' . esc_html($latest_php_version);
381 $class = $is_latest ? 'pluginmemoryusage_version-latest' : 'pluginmemoryusage_version-outdated';
382 echo ' <span class="' . esc_attr($class) . '">' . esc_html($label) . '</span>';
383 } else {
384 echo ' <span class="php-version-outdated">(Version check failed)</span>';
385 }
386 echo '</p>';
387
388 // Display MySQL version with status
389 echo '<strong>MySQL Version:</strong> ' . esc_html($mysql_version) . ' ';
390 echo wp_kses_post($mysql_icons[$mysql_status]);
391
392 // Show latest version info (similar to PHP version display)
393 if ('Unknown' !== $latest_mysql_version) {
394 $label = $is_latest_mysql ? 'Latest' : 'Latest: ' . esc_html($latest_mysql_version);
395 $class = $is_latest_mysql ? 'pluginmemoryusage_version-latest' : 'pluginmemoryusage_version-outdated';
396 echo ' <span class="' . esc_attr($class) . '">' . esc_html($label) . '</span>';
397
398 } else {
399 echo ' <span class="pluginmemoryusage_version-unknown">(Version check failed)</span>';
400 }
401
402 echo '</p>';
403
404
405 echo '<p class="pluginmemoryusage-system-info-line"><strong>Max Upload Size:</strong> ' . esc_html(size_format($upload_mb)) . '</p>';
406 echo '<p class="pluginmemoryusage-system-info-line"><strong>WordPress Memory Limit:</strong> ' . esc_html(size_format($wp_memory_limit)) . '</p>';
407 echo '<p class="pluginmemoryusage-system-info-line"><strong>PHP Memory Limit:</strong> ' . esc_html($php_memory_limit) . '</p>';
408
409
410 // CPU Usage
411 $cpu_usage = pmusage_get_cpu_usage();
412 if ( $cpu_usage !== null ) {
413 if ( $cpu_usage >= 90 ) $cpu_class = 'pmusage-bar-critical';
414 elseif ( $cpu_usage >= 75 ) $cpu_class = 'pmusage-bar-warning';
415 elseif ( $cpu_usage >= 50 ) $cpu_class = 'pmusage-bar-moderate';
416 else $cpu_class = 'pmusage-bar-good';
417
418 echo '<p class="pluginmemoryusage-system-info-line"><strong>CPU Load (1 min avg):</strong> ' . esc_html($cpu_usage) . '%</p>';
419 echo '<div class="pmusage-mini-bar"><div class="pmusage-mini-bar-fill ' . esc_attr($cpu_class) . '" style="width:' . esc_attr(min($cpu_usage, 100)) . '%"></div></div>';
420 } else {
421 echo '<p class="pluginmemoryusage-system-info-line"><strong>CPU Load:</strong> <span class="pluginmemoryusage_version-unknown">Unavailable</span></p>';
422 }
423
424 // Disk Usage
425 $disk = pmusage_get_disk_usage();
426 if ( $disk !== null ) {
427 if ( $disk['percent'] >= 90 ) $disk_class = 'pmusage-bar-critical';
428 elseif ( $disk['percent'] >= 75 ) $disk_class = 'pmusage-bar-warning';
429 elseif ( $disk['percent'] >= 50 ) $disk_class = 'pmusage-bar-moderate';
430 else $disk_class = 'pmusage-bar-good';
431
432 echo '<p class="pluginmemoryusage-system-info-line"><strong>Disk Usage:</strong> ' . esc_html(size_format($disk['used'])) . ' of ' . esc_html(size_format($disk['total'])) . ' (' . esc_html($disk['percent']) . '%)</p>';
433 echo '<div class="pmusage-mini-bar"><div class="pmusage-mini-bar-fill ' . esc_attr($disk_class) . '" style="width:' . esc_attr($disk['percent']) . '%"></div></div>';
434 } else {
435 echo '<p class="pluginmemoryusage-system-info-line"><strong>Disk Usage:</strong> <span class="pluginmemoryusage_version-unknown">Unavailable</span></p>';
436 }
437
438
439
440
441
442 }
443
444
445
446
447
448
449
450
451
452 // Function to display content in the dashboard widget
453 function pmusage_display_memory_usage_dashboard() {
454 global $wpdb;
455
456
457 // Get WordPress memory limit
458 $wp_memory_limit = wp_convert_hr_to_bytes(WP_MEMORY_LIMIT);
459
460 // Get current memory usage
461 $current_memory_usage = memory_get_usage(true);
462
463 // Calculate percentage used
464 $percentage_used = ($current_memory_usage / $wp_memory_limit) * 100;
465
466
467
468 // Display the data - In seperate function for reusability
469 pmusage_render_system_info();
470
471 // Display memory usage
472 echo '<p>Current Memory Usage: ';
473 echo esc_html(size_format($current_memory_usage)) . ' of ' . esc_html(size_format($wp_memory_limit)) . '</p>';
474 echo '<div style="width: 100%; background: #e0e0e0; height: 24px; border-radius: 10px; overflow: hidden;">
475 <div style="width: ' . esc_html(round($percentage_used, 1)) . '%; background: #76c7c0; height: 100%; text-align: center; line-height: 24px;" class="memory-bar-fill">' . esc_html(round($percentage_used, 1)) . '%</div>
476 </div>';
477
478 // Message to increase memory limit if usage is high
479 if ($current_memory_usage > $wp_memory_limit * 0.8) {
480 echo 'You are using more than 80% of allocated memory. Please go to Memory Control Panel to try to increase memory limit ';
481 echo '<p id="memory-increase-result"></p>';
482 }
483
484
485 // Add button to Plugin Memory Control Panel
486 echo '<p><a href="' . esc_html(admin_url('admin.php?page=wp_plugin_memory_usage')) . '" class="button">Plugin Memory Control Panel</a></p>';
487 }
488
489
490
491
492
493
494
495
496
497
498 function pmusage_memory_usage_admin_page() {
499 ?>
500 <div class="wrap">
501 <h1>Plugin Memory Usage - Control Panel</h1>
502
503 <div class="card-container">
504 <div class="card-row">
505 <div class="card card-system-info">
506 <h2>System Information</h2>
507 <?php pmusage_render_system_info(); ?>
508 </div>
509
510 <div class="card card-memory-usage">
511 <h2>Current Memory Usage</h2>
512 <?php
513 $wp_memory_limit = wp_convert_hr_to_bytes(WP_MEMORY_LIMIT);
514 $current_memory_usage = memory_get_usage(true);
515 $percentage_used = ($current_memory_usage / $wp_memory_limit) * 100;
516 ?>
517 <p>Using <span id="current-memory"><?php echo esc_html(size_format($current_memory_usage)); ?></span> of <?php echo esc_html(size_format($wp_memory_limit)); ?></p>
518 <div class="memory-bar">
519 <div id="memory-bar-fill" class="memory-bar-fill" style="width: <?php echo esc_html(round($percentage_used, 1)); ?>%;">
520 <span id="memory-percentage"><?php echo esc_html(round($percentage_used, 1)); ?>%</span>
521 </div>
522 </div>
523
524 <br><button id="refresh-memory" class="button">Refresh Memory Usage</button>
525 <br><br>Memory fluctuate. Please press button once after entering panel.
526
527 <?php
528 if ($current_memory_usage > $wp_memory_limit * 0.8) {
529 echo '<p style="color:red;">You are using more than 80% of allocated memory. Please click button below to try to increase memory limit.</p> ';
530 echo '<p><button id="increase-memory-limit" class="button">Increase Memory Limit</button></p>';
531 echo '<p id="memory-increase-result"></p>';
532 } ?>
533 </div>
534
535 <div class="card card-memory-history">
536 <h2>Memory Usage History</h2>
537
538 <ul id="memory-history-list">
539
540
541 <hr>
542 <p>This plugin measures changes in WordPress memory usage as you activate and deactivate plugins. Here's how it works:</p>
543 <ol>
544 <li>It records the current memory usage of WordPress.</li>
545 <li>When you activate or deactivate a plugin, it measures the memory usage again.</li>
546 <li>The difference between these measurements gives an estimate of the plugin's memory impact.</li>
547 <li>This process is repeated each time you toggle a plugin.</li>
548 </ol>
549 <p>Note: These measurements are estimates and may vary. Factors like caching, other active plugins, and WordPress itself can influence memory usage. For the most accurate results, consider testing in a controlled environment.</p>
550 <p>To begin, try activating or deactivating plugins using the buttons provided. The memory usage history will appear here.</p>|
551
552
553
554 </ul>
555 </div>
556 </div>
557
558 <div class="card card-plugins">
559 <h2>Plugins</h2>
560 <?php
561 $all_plugins = get_plugins();
562 $active_plugins = get_option('active_plugins');
563
564
565
566
567
568 echo '<ul class="plugin-list">';
569 foreach ($all_plugins as $plugin_path => $plugin_data) {
570 $is_active = in_array($plugin_path, $active_plugins);
571 $plugin_id = sanitize_title($plugin_data['Name']);
572 $is_memory_usage_plugin = (strpos($plugin_path, 'plugin-memory-usage') !== false);
573 $li_class = $is_active ? 'plugin-active' : 'plugin-inactive';
574 if ($is_memory_usage_plugin) {
575 $li_class .= ' memory-usage-plugin';
576 }
577 $avg_memory = pmusage_get_average_memory_usage($plugin_path);
578
579 echo '<li class="' . esc_attr($li_class) . '">';
580 echo '<div class="plugin-info">';
581 echo '<span class="plugin-name">' . esc_html($plugin_data['Name']) . ' <span class="avg-memory" title="The average memory usage represents the typical amount of memory consumed by this plugin over time.">(' . esc_html($avg_memory) . ' MB)</span></span>';
582 echo '<div class="plugin-memory-bar" title="Absolute value of last memory change"><div class="plugin-memory-fill"><span class="plugin-memory-text"></span></div></div>';
583 echo '</div>';
584 echo '<div class="plugin-actions">';
585 if ($is_memory_usage_plugin) {
586 echo '<button class="button" disabled>Active</button>';
587 } else {
588 echo '<button class="button toggle-plugin" data-plugin="' . esc_attr($plugin_path) . '" data-action="' . ($is_active ? 'deactivate' : 'activate') . '">';
589 echo $is_active ? 'Deactivate' : 'Activate';
590 echo '</button>';
591 }
592 echo '</div>';
593 echo '</li>';
594 }
595
596 echo '</ul>';
597
598
599
600
601
602 ?>
603 </div>
604 </div>
605 </div>
606
607
608 <?php
609 }
610
611
612
613
614
615 function pmusage_toggle_plugin() {
616 check_ajax_referer('wp_memory_usage_nonce', 'nonce');
617 if (!current_user_can('activate_plugins')) {
618 wp_send_json_error('Insufficient permissions');
619 }
620
621
622 $plugin = isset($_POST['plugin']) ? sanitize_text_field(wp_unslash($_POST['plugin'])) : '';
623 $action = isset($_POST['toggle_action']) ? sanitize_text_field(wp_unslash($_POST['toggle_action'])) : '';
624
625 if (empty($plugin) || empty($action)) {
626 wp_send_json_error('Invalid plugin or action');
627 return;
628 }
629
630
631
632 if ($action === 'activate') {
633 $result = activate_plugin($plugin);
634 } else {
635 $result = deactivate_plugins($plugin);
636 }
637
638 if (is_wp_error($result)) {
639 wp_send_json_error($result->get_error_message());
640 } else {
641 wp_send_json_success();
642 }
643 }
644 add_action('wp_ajax_pmusage_toggle_plugin', 'pmusage_toggle_plugin');
645
646
647
648
649
650
651 function pmusage_table_exists($table_name) {
652 global $wpdb;
653 $full_table_name = $wpdb->prefix . $table_name;
654
655 // Create a unique cache key
656 $cache_key = 'pmusage_table_exists_' . md5($full_table_name);
657
658 // Try to get the result from cache
659 $table_exists = wp_cache_get($cache_key);
660
661 if (false === $table_exists) {
662 // Cache miss, perform the database query
663 // There isn't a reliable way to check if a table exists in WordPress without making a direct database call
664 $result = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
665 $wpdb->prepare(
666 "SHOW TABLES LIKE %s",
667 $full_table_name
668 )
669 );
670
671 $table_exists = ($result === $full_table_name);
672
673 // Cache the result for future use (cache for 1 hour)
674 wp_cache_set($cache_key, $table_exists, '', 3600);
675 }
676
677 return $table_exists;
678 }
679
680
681
682
683
684 function pmusage_refresh_memory_usage() {
685 check_ajax_referer('wp_memory_usage_nonce', 'nonce');
686
687 global $wpdb;
688 $table_name = $wpdb->prefix . 'wpmem_plugin_history';
689
690 // Check if the table exists
691 if (!pmusage_table_exists('wpmem_plugin_history')) {
692 pmusage_create_history_table();
693 }
694
695
696 $current_memory = memory_get_usage(true);
697 $formatted_memory = size_format($current_memory, 2);
698 $percentage = round(($current_memory / wp_convert_hr_to_bytes(WP_MEMORY_LIMIT)) * 100, 2);
699
700 // Save the measurement if a plugin was toggled
701 if (isset($_POST['plugin']) && isset($_POST['toggle_action'])) {
702 $plugin = sanitize_text_field(wp_unslash($_POST['plugin']));
703 $previous_memory = isset($_POST['previous_memory']) ? intval($_POST['previous_memory']) : 0;
704 $memory_change = $current_memory - $previous_memory;
705 pmusage_save_plugin_measurement($plugin, $memory_change);
706 }
707
708 wp_send_json_success(array(
709 'current_memory' => $formatted_memory,
710 'current_memory_bytes' => $current_memory,
711 'percentage' => $percentage,
712 ));
713 }
714 add_action('wp_ajax_pmusage_refresh_memory_usage', 'pmusage_refresh_memory_usage');
715
716
717
718
719 add_action('admin_enqueue_scripts', 'pmusage_enqueue_styles');
720
721 function pmusage_enqueue_styles($hook) {
722 // Load on all admin pages where the dashboard widget appears
723 wp_enqueue_style('pmusage-styles',
724 plugins_url('plugin-memory-usage.css', __FILE__),
725 array(),
726 filemtime(plugin_dir_path(__FILE__) . 'plugin-memory-usage.css')
727 );
728 }
729
730
731
732
733 function pmusage_enqueue_scripts($hook) {
734 if ($hook != 'toplevel_page_wp_plugin_memory_usage') {
735 return;
736 }
737 wp_enqueue_script('pmusage-script', plugins_url('plugin-memory-usage.js', __FILE__), array(), '1.0', true);
738 $nonce = wp_create_nonce('wp_memory_usage_nonce');
739 wp_localize_script('pmusage-script', 'pmusageData', array(
740 'pmusage_ajaxurl' => admin_url('admin-ajax.php'),
741 'nonce' => $nonce,
742 'initialMemoryUsage' => memory_get_usage(true),
743 'pluginHistory' => pmusage_get_all_plugin_history()
744 ));
745 }
746 add_action('admin_enqueue_scripts', 'pmusage_enqueue_scripts');
747
748
749
750
751
752 function pmusage_create_history_table() {
753 global $wpdb;
754 $table_name = $wpdb->prefix . 'wpmem_plugin_history';
755 $charset_collate = $wpdb->get_charset_collate();
756
757 $sql = "CREATE TABLE $table_name (
758 id mediumint(9) NOT NULL AUTO_INCREMENT,
759 plugin_path varchar(255) NOT NULL,
760 memory_change bigint(20) NOT NULL,
761 zero_count int(11) NOT NULL DEFAULT 0,
762 timestamp datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
763 PRIMARY KEY (id)
764 ) $charset_collate;";
765
766 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
767 $result = dbDelta($sql);
768
769 if (!empty($wpdb->last_error)) {
770 return false;
771 }
772 return true;
773 }
774
775
776 register_activation_hook(__FILE__, 'pmusage_create_history_table');
777
778
779
780 function pmusage_save_plugin_measurement($plugin_path, $memory_change) {
781 global $wpdb;
782 $table_name = $wpdb->prefix . 'wpmem_plugin_history';
783
784 // Convert memory_change to its absolute value
785 $memory_change = abs($memory_change);
786
787 if ($memory_change == 0) {
788 // Increment zero count for the most recent entry
789 $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
790 $wpdb->prepare(
791 "UPDATE `" . esc_sql($table_name) . "` SET zero_count = zero_count + 1
792 WHERE plugin_path = %s
793 ORDER BY timestamp DESC
794 LIMIT 1",
795 $plugin_path
796 )
797 );
798 } else {
799 // Insert new non-zero measurement
800 $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
801 $table_name,
802 array(
803 'plugin_path' => $plugin_path,
804 'memory_change' => $memory_change,
805 'zero_count' => 0,
806 )
807 );
808 }
809
810 // Clear caches
811 pmusage_clear_plugin_history_cache($plugin_path);
812 pmusage_clear_all_plugin_history_cache();
813 pmusage_clear_average_memory_cache($plugin_path);
814 }
815
816
817
818 function pmusage_get_plugin_history($plugin_path) {
819 global $wpdb;
820 $table_name = $wpdb->prefix . 'wpmem_plugin_history';
821
822 // Create a unique cache key for this query
823 $cache_key = 'wpmem_plugin_history_' . md5($plugin_path);
824
825 // Try to get the results from cache
826 $results = wp_cache_get($cache_key);
827
828 // If the results are not in cache, query the database
829 if (false === $results) {
830 $results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
831 $wpdb->prepare(
832 "SELECT memory_change, timestamp FROM `" . esc_sql($table_name) . "` WHERE plugin_path = %s ORDER BY timestamp DESC LIMIT 5",
833 $plugin_path
834 )
835 );
836
837 // Cache the results for future use
838 wp_cache_set($cache_key, $results, '', 3600); // Cache for 1 hour
839 }
840
841 return $results;
842 }
843
844
845
846 function pmusage_clear_plugin_history_cache($plugin_path) {
847 $cache_key = 'wpmem_plugin_history_' . md5($plugin_path);
848 wp_cache_delete($cache_key);
849 }
850
851
852
853 add_action('wp_ajax_pmusage_get_plugin_history', 'pmusage_get_plugin_history_ajax');
854 function pmusage_get_plugin_history_ajax() {
855 check_ajax_referer('wp_memory_usage_nonce', 'nonce');
856 if (!isset($_POST['plugin'])) {
857 wp_send_json_error('Plugin not specified');
858 }
859
860 $plugin = sanitize_text_field(wp_unslash($_POST['plugin']));
861 $history = pmusage_get_plugin_history($plugin);
862
863 wp_send_json_success($history);
864 }
865
866
867
868
869
870
871
872
873 function pmusage_get_all_plugin_history() {
874 global $wpdb;
875 $table_name = $wpdb->prefix . 'wpmem_plugin_history';
876
877 // Create a unique cache key for this query
878 $cache_key = 'pmusage_all_plugin_history';
879
880 // Try to get the results from cache
881 $results = wp_cache_get($cache_key);
882
883 // If the results are not in cache, query the database
884 if (false === $results) {
885 $results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
886 $wpdb->prepare(
887 "SELECT plugin_path, memory_change, timestamp
888 FROM `" . esc_sql($table_name) . "`
889 WHERE plugin_path IN (SELECT DISTINCT plugin_path FROM `" . esc_sql($table_name) . "`)
890 ORDER BY timestamp DESC
891 -- %d",
892 1
893 )
894 );
895
896 // Cache the results for future use
897 wp_cache_set($cache_key, $results, '', 3600); // Cache for 1 hour
898 }
899
900 // Process results...
901 $history = array();
902 foreach ($results as $row) {
903 if (!isset($history[$row->plugin_path])) {
904 $history[$row->plugin_path] = array();
905 }
906 if (count($history[$row->plugin_path]) < 5) {
907 $history[$row->plugin_path][] = array(
908 'memory_change' => $row->memory_change,
909 'timestamp' => $row->timestamp
910 );
911 }
912 }
913
914 return $history;
915 }
916
917
918
919
920 function pmusage_clear_all_plugin_history_cache() {
921 wp_cache_delete('pmusage_all_plugin_history');
922 }
923
924
925
926 function pmusage_clear_table_exists_cache() {
927 global $wpdb;
928 $table_name = $wpdb->prefix . 'wpmem_plugin_history';
929 $cache_key = 'pmusage_table_exists_' . $table_name;
930 wp_cache_delete($cache_key);
931 }
932
933
934 // Reset table verification cache when changing plugin state
935 register_activation_hook(__FILE__, 'pmusage_clear_table_exists_cache'); // Pre-activation check
936 register_deactivation_hook(__FILE__, 'pmusage_clear_table_exists_cache'); // Post-deactivation cleanup
937
938
939
940
941
942 function pmusage_get_average_memory_usage($plugin_path) {
943 global $wpdb;
944 $table_name = $wpdb->prefix . 'wpmem_plugin_history';
945
946 // Create a unique cache key
947 $cache_key = 'pmusage_avg_memory_' . md5($plugin_path);
948
949 // Try to get the result from cache
950 $avg_memory = wp_cache_get($cache_key);
951
952 if (false === $avg_memory) {
953 // Cache miss, perform the database query
954 $result = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
955 $wpdb->prepare(
956 "SELECT SUM(memory_change) as total_change,
957 COUNT(*) as non_zero_count,
958 SUM(zero_count) as zero_count
959 FROM `" . esc_sql($table_name) . "`
960 WHERE plugin_path = %s",
961 $plugin_path
962 )
963 );
964
965 if ($result) {
966 $total_count = $result->non_zero_count + $result->zero_count;
967 $avg_memory = $total_count > 0 ? ($result->total_change / $total_count) / (1024 * 1024) : 0;
968 $avg_memory = round($avg_memory, 2); // Round to 2 decimal places
969 } else {
970 $avg_memory = 0;
971 }
972
973 // Cache the result for future use (cache for 5 minutes)
974 wp_cache_set($cache_key, $avg_memory, '', 300);
975 }
976
977 return $avg_memory;
978 }
979
980
981
982 function pmusage_clear_average_memory_cache($plugin_path) {
983 $cache_key = 'pmusage_avg_memory_' . md5($plugin_path);
984 wp_cache_delete($cache_key);
985 }
986
987
988
989 add_action('wp_ajax_pmusage_update_average_memory', 'pmusage_update_average_memory');
990 add_action('wp_ajax_nopriv_update_average_memory', 'pmusage_update_average_memory');
991
992 function pmusage_update_average_memory() {
993 // Check nonce for security
994 if (!isset($_POST['nonce'])) {
995 wp_send_json_error('Security token not provided');
996 }
997
998 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
999 if (!wp_verify_nonce($nonce, 'wp_memory_usage_nonce')) {
1000 wp_send_json_error('Invalid security token');
1001 }
1002
1003 if (!isset($_POST['plugin_path'])) {
1004 wp_send_json_error('Plugin path not provided');
1005 }
1006
1007 $plugin_path = sanitize_text_field(wp_unslash($_POST['plugin_path']));
1008 $avg_memory = pmusage_get_average_memory_usage($plugin_path);
1009
1010 wp_send_json_success(array('avg_memory' => $avg_memory));
1011 }
1012
1013
1014
1015
1016
1017
1018 function pmusage_add_increase_memory_button($content) {
1019 $wp_memory_limit = wp_convert_hr_to_bytes(WP_MEMORY_LIMIT);
1020 $current_memory_usage = memory_get_usage(true);
1021
1022 if ($current_memory_usage > $wp_memory_limit) {
1023 $content .= '<button id="increase-memory-limit" class="button">Increase Memory Limit</button>';
1024 $content .= '<span id="memory-increase-result"></span>';
1025 }
1026
1027 return $content;
1028 }
1029 add_filter('pmusage_memory_usage_content', 'pmusage_add_increase_memory_button');
1030
1031
1032
1033
1034 function pmusage_increase_memory_limit() {
1035 check_ajax_referer('wp_memory_usage_nonce', 'nonce');
1036
1037 if (!current_user_can('manage_options')) {
1038 wp_send_json_error(array('message' => 'Insufficient permissions'));
1039 }
1040
1041 $current_limit = wp_convert_hr_to_bytes(WP_MEMORY_LIMIT);
1042
1043 // If current limit is 160MB or less, double it. Otherwise, increase by 25%.
1044 if ($current_limit < 160 * 1024 * 1024) { // 160 MB in bytes
1045 $new_limit = $current_limit * 2;
1046 } else {
1047 $new_limit = (int)($current_limit * 1.25); // Increase by 25%
1048 }
1049
1050 // Optional: Cap the maximum limit to avoid runaway values (e.g., 512MB)
1051 $max_limit = 512 * 1024 * 1024; // 512 MB in bytes
1052 if ($new_limit > $max_limit) {
1053 $new_limit = $max_limit;
1054 }
1055
1056 // Attempt to increase the limit
1057 if (pmusage_set_memory_limit($new_limit)) {
1058 wp_send_json_success(array('new_limit' => size_format($new_limit)));
1059 } else {
1060 wp_send_json_error(array('message' => 'Unable to increase memory limit. You may need to contact your hosting provider.'));
1061 }
1062 }
1063 add_action('wp_ajax_pmusage_increase_memory_limit', 'pmusage_increase_memory_limit');
1064
1065
1066
1067 function pmusage_set_memory_limit($new_limit) {
1068 global $wp_filesystem;
1069
1070 // Initialize the WP filesystem
1071 if (empty($wp_filesystem)) {
1072 require_once (ABSPATH . '/wp-admin/includes/file.php');
1073 WP_Filesystem();
1074 }
1075
1076 $wp_config_file = ABSPATH . 'wp-config.php';
1077 $config_content = $wp_filesystem->get_contents($wp_config_file);
1078
1079 if ($config_content === false) {
1080 return false;
1081 }
1082
1083 $new_limit_formatted = size_format($new_limit);
1084
1085 if (preg_match("/define\(\s*'WP_MEMORY_LIMIT',\s*'.*?'\s*\);/", $config_content)) {
1086 // WP_MEMORY_LIMIT is already defined, so update it
1087 $new_content = preg_replace(
1088 "/define\(\s*'WP_MEMORY_LIMIT',\s*'.*?'\s*\);/",
1089 "define('WP_MEMORY_LIMIT', '$new_limit_formatted');",
1090 $config_content
1091 );
1092 } else {
1093 // WP_MEMORY_LIMIT is not defined, so add it
1094 $new_content = preg_replace(
1095 "/<\?php/",
1096 "<?php\ndefine('WP_MEMORY_LIMIT', '$new_limit_formatted');",
1097 $config_content,
1098 1
1099 );
1100 }
1101
1102 if ($wp_filesystem->put_contents($wp_config_file, $new_content) === false) {
1103 return false;
1104 }
1105
1106 return true;
1107 }
1108
1109
1110
1111
1112
1113
1114 function pmusage_add_settings_link($links) {
1115 $settings_link = '<a href="' . admin_url('admin.php?page=wp_plugin_memory_usage') . '">Settings</a>';
1116 array_unshift($links, $settings_link);
1117 return $links;
1118 }
1119
1120 $plugin = plugin_basename(__FILE__);
1121 add_filter("plugin_action_links_$plugin", 'pmusage_add_settings_link');
1122
1123
1124
1125
1126
1127 function pmusage_add_memory_to_admin_bar($wp_admin_bar) {
1128 $wp_memory_limit = wp_convert_hr_to_bytes(WP_MEMORY_LIMIT);
1129 $current_memory_usage = memory_get_usage(true);
1130 $percentage_used = round(($current_memory_usage / $wp_memory_limit) * 100, 1);
1131
1132
1133 // Determine color class based on memory usage
1134 if ($percentage_used >= 90) {
1135 $color_class = 'wpmem-critical'; // Red
1136 } elseif ($percentage_used >= 75) {
1137 $color_class = 'wpmem-warning'; // Orange
1138 } elseif ($percentage_used >= 50) {
1139 $color_class = 'wpmem-moderate'; // Yellow
1140 } else {
1141 $color_class = 'wpmem-good'; // Green
1142 }
1143
1144 $wp_admin_bar->add_node(array(
1145 'id' => 'pmusage_memory_usage',
1146 'title' => '<span class="' . esc_attr($color_class) . '">MEM: ' . $percentage_used . '%</span>',
1147 'href' => admin_url('admin.php?page=wp_plugin_memory_usage'),
1148 'meta' => array(
1149 'title' => 'Current memory usage: ' . size_format($current_memory_usage) . ' of ' . size_format($wp_memory_limit),
1150 ),
1151 ));
1152 }
1153 add_action('admin_bar_menu', 'pmusage_add_memory_to_admin_bar', 100);
1154
1155
1156
1157
1158
1159 function pmusage_get_mysql_versions_from_api() {
1160 $mysql_versions = get_transient('pmusage_mysql_eol_data');
1161 if (false === $mysql_versions) {
1162 // Get MySQL data
1163 $mysql_response = wp_remote_get('https://endoflife.date/api/mysql.json', array(
1164 'sslverify' => false,
1165 'timeout' => 5
1166 ));
1167
1168 // Get MariaDB data
1169 $mariadb_response = wp_remote_get('https://endoflife.date/api/mariadb.json', array(
1170 'sslverify' => false,
1171 'timeout' => 5
1172 ));
1173
1174 $versions_data = ['mysql' => [], 'mariadb' => []];
1175
1176 if (!is_wp_error($mysql_response) && 200 === wp_remote_retrieve_response_code($mysql_response)) {
1177 $mysql_data = json_decode(wp_remote_retrieve_body($mysql_response), true);
1178 if (is_array($mysql_data)) {
1179 foreach ($mysql_data as $version_info) {
1180 $cycle = $version_info['cycle'];
1181 $eol_date = $version_info['eol'] ?? null;
1182
1183 if ($eol_date && $eol_date !== false) {
1184 $versions_data['mysql'][$cycle] = [
1185 'eol_date' => $eol_date,
1186 'latest' => $version_info['latest'] ?? '',
1187 'support_end' => $version_info['support'] ?? $eol_date
1188 ];
1189 }
1190 }
1191 }
1192 }
1193
1194 if (!is_wp_error($mariadb_response) && 200 === wp_remote_retrieve_response_code($mariadb_response)) {
1195 $mariadb_data = json_decode(wp_remote_retrieve_body($mariadb_response), true);
1196 if (is_array($mariadb_data)) {
1197 foreach ($mariadb_data as $version_info) {
1198 $cycle = $version_info['cycle'];
1199 $eol_date = $version_info['eol'] ?? null;
1200
1201 if ($eol_date && $eol_date !== false) {
1202 $versions_data['mariadb'][$cycle] = [
1203 'eol_date' => $eol_date,
1204 'latest' => $version_info['latest'] ?? '', // This is the key addition
1205 'lts' => $version_info['lts'] ?? false
1206 ];
1207 }
1208 }
1209 }
1210 }
1211
1212 // Cache for one week
1213 set_transient('pmusage_mysql_eol_data', $versions_data, WEEK_IN_SECONDS);
1214 $mysql_versions = $versions_data;
1215 }
1216
1217 return $mysql_versions;
1218 }
1219
1220
1221 function pmusage_get_mysql_latest_version($current_version) {
1222 $version_data = pmusage_get_mysql_versions_from_api();
1223 $is_mariadb = stripos($current_version, 'mariadb') !== false;
1224
1225 // Extract version number
1226 preg_match('/(\d+\.\d+)/', $current_version, $matches);
1227 $version_number = $matches[1] ?? '';
1228
1229 if (empty($version_number)) {
1230 return 'Unknown';
1231 }
1232
1233 $database_type = $is_mariadb ? 'mariadb' : 'mysql';
1234 $versions = $version_data[$database_type] ?? [];
1235
1236 if (isset($versions[$version_number]) && !empty($versions[$version_number]['latest'])) {
1237 return $versions[$version_number]['latest'];
1238 }
1239
1240 return 'Unknown';
1241 }
1242
1243
1244
1245
1246
1247 function pmusage_get_mysql_status($current_version) {
1248 $version_data = pmusage_get_mysql_versions_from_api();
1249 $is_mariadb = stripos($current_version, 'mariadb') !== false;
1250
1251 // Extract version number
1252 preg_match('/(\d+\.\d+)/', $current_version, $matches);
1253 $version_number = $matches[1] ?? '';
1254
1255 if (empty($version_number)) {
1256 return 'unknown';
1257 }
1258
1259 $database_type = $is_mariadb ? 'mariadb' : 'mysql';
1260 $versions = $version_data[$database_type] ?? [];
1261
1262 if (isset($versions[$version_number])) {
1263 $version_info = $versions[$version_number];
1264 $eol_date_str = $version_info['eol_date'];
1265
1266 // Handle different date formats from API
1267 if ($eol_date_str === true || $eol_date_str === false) {
1268 return $eol_date_str === false ? 'supported' : 'eol';
1269 }
1270
1271 try {
1272 $eol_date = new DateTime($eol_date_str);
1273 $now = new DateTime();
1274
1275 return ($now < $eol_date) ? 'supported' : 'eol';
1276 } catch (Exception $e) {
1277 return 'unknown';
1278 }
1279 }
1280
1281 return 'unknown';
1282 }
1283
1284
1285
1286
1287
1288
1289 ?>