PluginProbe
Plugin Memory Usage / 1.2.8
Plugin Memory Usage v1.2.8
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.2.8, at plugin-memory-usage.php

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