PluginProbe
Performance Lab / 2.2.0
Performance Lab v2.2.0
trunk 1.0.0 1.0.0-beta.1 1.0.0-beta.2 1.0.0-beta.3 1.0.0-rc.1 1.1.0 1.2.0 1.3.0 1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 1.9.0 2.0.0 2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.7.0 2.8.0 All 44 releases
performance-lab / load.php

load.php in Performance Lab 2.2.0, at load.php

527 lines 15.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Performance Lab
4 * Plugin URI: https://github.com/WordPress/performance
5 * Description: Performance plugin from the WordPress Performance Team, which is a collection of standalone performance modules.
6 * Requires at least: 6.1
7 * Requires PHP: 5.6
8 * Version: 2.2.0
9 * Author: WordPress Performance Team
10 * Author URI: https://make.wordpress.org/performance/
11 * License: GPLv2 or later
12 * License URI: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
13 * Text Domain: performance-lab
14 *
15 * @package performance-lab
16 */
17
18 define( 'PERFLAB_VERSION', '2.2.0' );
19 define( 'PERFLAB_MAIN_FILE', __FILE__ );
20 define( 'PERFLAB_PLUGIN_DIR_PATH', plugin_dir_path( PERFLAB_MAIN_FILE ) );
21 define( 'PERFLAB_MODULES_SETTING', 'perflab_modules_settings' );
22 define( 'PERFLAB_MODULES_SCREEN', 'perflab-modules' );
23
24 // If the constant isn't defined yet, it means the Performance Lab object cache file is not loaded.
25 if ( ! defined( 'PERFLAB_OBJECT_CACHE_DROPIN_VERSION' ) ) {
26 define( 'PERFLAB_OBJECT_CACHE_DROPIN_VERSION', false );
27 }
28
29 require_once PERFLAB_PLUGIN_DIR_PATH . 'server-timing/class-perflab-server-timing-metric.php';
30 require_once PERFLAB_PLUGIN_DIR_PATH . 'server-timing/class-perflab-server-timing.php';
31 require_once PERFLAB_PLUGIN_DIR_PATH . 'server-timing/load.php';
32 require_once PERFLAB_PLUGIN_DIR_PATH . 'server-timing/defaults.php';
33
34 /**
35 * Registers the performance modules setting.
36 *
37 * @since 1.0.0
38 */
39 function perflab_register_modules_setting() {
40 register_setting(
41 PERFLAB_MODULES_SCREEN,
42 PERFLAB_MODULES_SETTING,
43 array(
44 'type' => 'object',
45 'sanitize_callback' => 'perflab_sanitize_modules_setting',
46 'default' => perflab_get_modules_setting_default(),
47 )
48 );
49 }
50 add_action( 'init', 'perflab_register_modules_setting' );
51
52 /**
53 * Gets the default value for the performance modules setting.
54 *
55 * @since 1.0.0
56 *
57 * @return array Associative array of module settings keyed by module slug.
58 */
59 function perflab_get_modules_setting_default() {
60 // Since the default relies on some minimal logic that includes requiring an additional file,
61 // the result is "cached" in a static variable.
62 static $default_option = null;
63
64 if ( null === $default_option ) {
65 // To set the default value for which modules are enabled, rely on this generated file.
66 $default_enabled_modules = require PERFLAB_PLUGIN_DIR_PATH . 'default-enabled-modules.php';
67 $default_option = array_reduce(
68 $default_enabled_modules,
69 function( $module_settings, $module_dir ) {
70 $module_settings[ $module_dir ] = array( 'enabled' => true );
71 return $module_settings;
72 },
73 array()
74 );
75 }
76
77 return $default_option;
78 }
79
80 /**
81 * Sanitizes the performance modules setting.
82 *
83 * @since 1.0.0
84 *
85 * @param mixed $value Modules setting value.
86 * @return array Sanitized modules setting value.
87 */
88 function perflab_sanitize_modules_setting( $value ) {
89 if ( ! is_array( $value ) ) {
90 return array();
91 }
92
93 // Ensure that every element is an array with an 'enabled' key.
94 return array_filter(
95 array_map(
96 function( $module_settings ) {
97 if ( ! is_array( $module_settings ) ) {
98 return array();
99 }
100 return array_merge(
101 array( 'enabled' => false ),
102 $module_settings
103 );
104 },
105 $value
106 )
107 );
108 }
109
110 /**
111 * Gets the performance module settings.
112 *
113 * @since 1.0.0
114 *
115 * @return array Associative array of module settings keyed by module slug.
116 */
117 function perflab_get_module_settings() {
118 // Even though a default value is registered for this setting, the default must be explicitly
119 // passed here, to support scenarios where this function is called before the 'init' action,
120 // for example when loading the active modules.
121 $module_settings = (array) get_option( PERFLAB_MODULES_SETTING, perflab_get_modules_setting_default() );
122
123 $legacy_module_slugs = array(
124 'site-health/audit-autoloaded-options' => 'database/audit-autoloaded-options',
125 'site-health/audit-enqueued-assets' => 'js-and-css/audit-enqueued-assets',
126 'site-health/webp-support' => 'images/webp-support',
127 'images/dominant-color' => 'images/dominant-color-images',
128 );
129
130 foreach ( $legacy_module_slugs as $legacy_slug => $current_slug ) {
131 if ( isset( $module_settings[ $legacy_slug ] ) ) {
132 $module_settings[ $current_slug ] = $module_settings[ $legacy_slug ];
133 unset( $module_settings[ $legacy_slug ] );
134 }
135 }
136
137 return $module_settings;
138 }
139
140 /**
141 * Gets the active performance modules.
142 *
143 * @since 1.0.0
144 *
145 * @return array List of active module slugs.
146 */
147 function perflab_get_active_modules() {
148 $modules = array_keys(
149 array_filter(
150 perflab_get_module_settings(),
151 function( $module_settings ) {
152 return isset( $module_settings['enabled'] ) && $module_settings['enabled'];
153 }
154 )
155 );
156
157 /**
158 * Filters active modules to allow programmatically control which modules are active.
159 *
160 * @since 1.0.0
161 *
162 * @param array $modules An array of the currently active modules.
163 */
164 $modules = apply_filters( 'perflab_active_modules', $modules );
165
166 return $modules;
167 }
168
169 /**
170 * Gets the active and valid performance modules.
171 *
172 * @since 1.3.0
173 * @since 2.2.0 Adds an additional check for standalone plugins.
174 *
175 * @param string $module Slug of the module.
176 * @return bool True if the module is active and valid, otherwise false.
177 */
178 function perflab_is_valid_module( $module ) {
179
180 if ( empty( $module ) ) {
181 return false;
182 }
183
184 // Do not load the module if it can be loaded by a separate plugin.
185 if ( perflab_is_standalone_plugin_loaded( $module ) ) {
186 return false;
187 }
188
189 // Do not load module if no longer exists.
190 $module_file = PERFLAB_PLUGIN_DIR_PATH . 'modules/' . $module . '/load.php';
191 if ( ! file_exists( $module_file ) ) {
192 return false;
193 }
194
195 // Do not load module if it cannot be loaded, e.g. if it was already merged and is available in WordPress core.
196 return perflab_can_load_module( $module );
197 }
198
199 /**
200 * Gets the content attribute for the generator tag for the Performance Lab plugin.
201 *
202 * This attribute is then used in {@see perflab_render_generator()}.
203 *
204 * @since 1.1.0
205 */
206 function perflab_get_generator_content() {
207 $active_and_valid_modules = array_filter( perflab_get_active_modules(), 'perflab_is_valid_module' );
208
209 return sprintf(
210 'Performance Lab %1$s; modules: %2$s',
211 PERFLAB_VERSION,
212 implode( ', ', $active_and_valid_modules )
213 );
214 }
215
216 /**
217 * Displays the HTML generator tag for the Performance Lab plugin.
218 *
219 * See {@see 'wp_head'}.
220 *
221 * @since 1.1.0
222 */
223 function perflab_render_generator() {
224 $content = perflab_get_generator_content();
225
226 echo '<meta name="generator" content="' . esc_attr( $content ) . '">' . "\n";
227 }
228 add_action( 'wp_head', 'perflab_render_generator' );
229
230 /**
231 * Checks whether the given module can be loaded in the current environment.
232 *
233 * @since 1.3.0
234 *
235 * @param string $module Slug of the module.
236 * @return bool Whether the module can be loaded or not.
237 */
238 function perflab_can_load_module( $module ) {
239 $module_load_file = PERFLAB_PLUGIN_DIR_PATH . 'modules/' . $module . '/can-load.php';
240
241 // If the `can-load.php` file does not exist, assume the module can be loaded.
242 if ( ! file_exists( $module_load_file ) ) {
243 return true;
244 }
245
246 // Require the file to get the closure for whether the module can load.
247 $module = require $module_load_file;
248
249 // If the `can-load.php` file is invalid and does not return a closure, assume the module can be loaded.
250 if ( ! is_callable( $module ) ) {
251 return true;
252 }
253
254 // Call the closure to determine whether the module can be loaded.
255 return (bool) $module();
256 }
257
258 /**
259 * Checks whether the given module has already been loaded by a separate plugin.
260 *
261 * @since 2.2.0
262 *
263 * @param string $module Slug of the module.
264 * @return bool Whether the module has already been loaded by a separate plugin.
265 */
266 function perflab_is_standalone_plugin_loaded( $module ) {
267 $standalone_plugins_constants = perflab_get_standalone_plugins_constants();
268 if (
269 isset( $standalone_plugins_constants[ $module ] ) &&
270 defined( $standalone_plugins_constants[ $module ] ) &&
271 ! str_starts_with( constant( $standalone_plugins_constants[ $module ] ), 'Performance Lab ' )
272 ) {
273 return true;
274 }
275 return false;
276 }
277
278 /**
279 * Gets the standalone plugin constants used for each module / plugin.
280 *
281 * @since 2.2.0
282 *
283 * @return array Map of module path to version constant used.
284 */
285 function perflab_get_standalone_plugins_constants() {
286 return array(
287 'images/webp-uploads' => 'WEBP_UPLOADS_VERSION',
288 );
289 }
290
291 /**
292 * Loads the active and valid performance modules.
293 *
294 * @since 1.0.0
295 * @since 1.3.0 Renamed to perflab_load_active_and_valid_modules().
296 */
297 function perflab_load_active_and_valid_modules() {
298 $active_and_valid_modules = array_filter( perflab_get_active_modules(), 'perflab_is_valid_module' );
299
300 foreach ( $active_and_valid_modules as $module ) {
301
302 require_once PERFLAB_PLUGIN_DIR_PATH . 'modules/' . $module . '/load.php';
303 }
304 }
305 add_action( 'plugins_loaded', 'perflab_load_active_and_valid_modules' );
306
307 /**
308 * Places the Performance Lab's object cache drop-in in the drop-ins folder.
309 *
310 * This only runs in WP Admin to not have any potential performance impact on
311 * the frontend.
312 *
313 * This function will short-circuit if the constant
314 * 'PERFLAB_DISABLE_OBJECT_CACHE_DROPIN' is set as true.
315 *
316 * @since 1.8.0
317 * @since 2.1.0 No longer attempts to use two of the drop-ins together.
318 *
319 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
320 */
321 function perflab_maybe_set_object_cache_dropin() {
322 global $wp_filesystem;
323
324 // Bail if disabled via constant.
325 if ( defined( 'PERFLAB_DISABLE_OBJECT_CACHE_DROPIN' ) && PERFLAB_DISABLE_OBJECT_CACHE_DROPIN ) {
326 return;
327 }
328
329 // Bail if already placed.
330 if ( PERFLAB_OBJECT_CACHE_DROPIN_VERSION ) {
331 return;
332 }
333
334 /**
335 * Filters whether the Perflab server timing drop-in should be set.
336 *
337 * @since 2.0.0
338 *
339 * @param bool Whether the server timing drop-in should be set.
340 */
341 if ( apply_filters( 'perflab_disable_object_cache_dropin', false ) ) {
342 return;
343 }
344
345 // Bail if already attempted before timeout has been completed.
346 // This is present in case placing the file fails for some reason, to avoid
347 // excessively retrying to place it on every request.
348 $timeout = get_transient( 'perflab_set_object_cache_dropin' );
349 if ( false !== $timeout ) {
350 return;
351 }
352
353 if ( $wp_filesystem || WP_Filesystem() ) {
354 $dropin_path = WP_CONTENT_DIR . '/object-cache.php';
355
356 /**
357 * If there is an actual object-cache.php file, do not replace it.
358 * Previous versions of the Performance Lab plugin were renaming the
359 * original object-cache.php file and then loading both. However, due
360 * to other plugins eagerly checking file headers, this caused too many
361 * problems across sites so it was decided to remove this layer.
362 * Only placing the drop-in file if no other one exists yet is the
363 * safest solution.
364 */
365 if ( $wp_filesystem->exists( $dropin_path ) ) {
366 // Set timeout of 1 day before retrying again (only in case the file already exists).
367 set_transient( 'perflab_set_object_cache_dropin', true, DAY_IN_SECONDS );
368 return;
369 }
370
371 $wp_filesystem->copy( PERFLAB_PLUGIN_DIR_PATH . 'server-timing/object-cache.copy.php', $dropin_path );
372 }
373
374 // Set timeout of 1 hour before retrying again (only relevant in case the above failed).
375 set_transient( 'perflab_set_object_cache_dropin', true, HOUR_IN_SECONDS );
376 }
377 add_action( 'admin_init', 'perflab_maybe_set_object_cache_dropin' );
378
379 /**
380 * Removes the Performance Lab's object cache drop-in from the drop-ins folder.
381 *
382 * This function should be run on plugin deactivation. For backward compatibility with
383 * an earlier implementation of `perflab_maybe_set_object_cache_dropin()`, this function
384 * checks whether there is an object-cache-plst-orig.php file, and if so restores it.
385 *
386 * This function will short-circuit if the constant
387 * 'PERFLAB_DISABLE_OBJECT_CACHE_DROPIN' is set as true.
388 *
389 * @since 1.8.0
390 *
391 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
392 */
393 function perflab_maybe_remove_object_cache_dropin() {
394 global $wp_filesystem;
395
396 // Bail if disabled via constant.
397 if ( defined( 'PERFLAB_DISABLE_OBJECT_CACHE_DROPIN' ) && PERFLAB_DISABLE_OBJECT_CACHE_DROPIN ) {
398 return;
399 }
400
401 // Bail if custom drop-in not present anyway.
402 if ( ! PERFLAB_OBJECT_CACHE_DROPIN_VERSION ) {
403 return;
404 }
405
406 if ( $wp_filesystem || WP_Filesystem() ) {
407 $dropin_path = WP_CONTENT_DIR . '/object-cache.php';
408 $dropin_backup_path = WP_CONTENT_DIR . '/object-cache-plst-orig.php';
409
410 /**
411 * If there is an object-cache-plst-orig.php file, restore it and
412 * override the Performance Lab file. This is only relevant for
413 * backward-compatibility with previous Performance Lab versions
414 * which were backing up the file and then loading both.
415 * Otherwise just delete the Performance Lab file.
416 */
417 if ( $wp_filesystem->exists( $dropin_backup_path ) ) {
418 $wp_filesystem->move( $dropin_backup_path, $dropin_path, true );
419 } else {
420 $wp_filesystem->delete( $dropin_path );
421 }
422 }
423
424 // Delete transient for drop-in check in case the plugin is reactivated shortly after.
425 delete_transient( 'perflab_set_object_cache_dropin' );
426 }
427 register_deactivation_hook( __FILE__, 'perflab_maybe_remove_object_cache_dropin' );
428
429 // Only load admin integration when in admin.
430 if ( is_admin() ) {
431 require_once PERFLAB_PLUGIN_DIR_PATH . 'admin/load.php';
432 }
433
434 /**
435 * Trigger actions when a module gets activated or deactivated.
436 *
437 * @since 1.8.0
438 *
439 * @param mixed $old_value Old value of the option.
440 * @param mixed $value New value of the option.
441 */
442 function perflab_run_module_activation_deactivation( $old_value, $value ) {
443 $old_value = (array) $old_value;
444 $value = (array) $value;
445
446 // Get the list of modules that were activated, and load the activate.php files if they exist.
447 if ( ! empty( $value ) ) {
448 foreach ( $value as $module => $module_settings ) {
449 if ( ! empty( $module_settings['enabled'] ) && ( empty( $old_value[ $module ] ) || empty( $old_value[ $module ]['enabled'] ) ) ) {
450 perflab_activate_module( PERFLAB_PLUGIN_DIR_PATH . 'modules/' . $module );
451 }
452 }
453 }
454
455 // Get the list of modules that were deactivated, and load the deactivate.php files if they exist.
456 if ( ! empty( $old_value ) ) {
457 foreach ( $old_value as $module => $module_settings ) {
458 if ( ! empty( $module_settings['enabled'] ) && ( empty( $value[ $module ] ) || empty( $value[ $module ]['enabled'] ) ) ) {
459 perflab_deactivate_module( PERFLAB_PLUGIN_DIR_PATH . 'modules/' . $module );
460 }
461 }
462 }
463
464 return $value;
465 }
466
467 /**
468 * Activate a module.
469 *
470 * Runs the activate.php file if it exists.
471 *
472 * @since 1.8.0
473 *
474 * @param string $module_dir_path The module's directory path.
475 */
476 function perflab_activate_module( $module_dir_path ) {
477 $module_activation_file = $module_dir_path . '/activate.php';
478 if ( ! file_exists( $module_activation_file ) ) {
479 return;
480 }
481 $module = require $module_activation_file;
482 if ( ! is_callable( $module ) ) {
483 return;
484 }
485 $module();
486 }
487
488 /**
489 * Deactivate a module.
490 *
491 * Runs the deactivate.php file if it exists.
492 *
493 * @since 1.8.0
494 *
495 * @param string $module_dir_path The module's directory path.
496 */
497 function perflab_deactivate_module( $module_dir_path ) {
498 $module_deactivation_file = $module_dir_path . '/deactivate.php';
499 if ( ! file_exists( $module_deactivation_file ) ) {
500 return;
501 }
502 $module = require $module_deactivation_file;
503 if ( ! is_callable( $module ) ) {
504 return;
505 }
506 $module();
507 }
508
509 // Run the module activation & deactivation actions when the option is updated.
510 add_action( 'update_option_' . PERFLAB_MODULES_SETTING, 'perflab_run_module_activation_deactivation', 10, 2 );
511
512 // Run the module activation & deactivation actions when the option is added.
513 add_action(
514 'add_option_' . PERFLAB_MODULES_SETTING,
515 /**
516 * Fires after the option has been added.
517 *
518 * @param string $option Name of the option to add.
519 * @param mixed $value Value of the option.
520 */
521 function( $option, $value ) {
522 perflab_run_module_activation_deactivation( perflab_get_modules_setting_default(), $value );
523 },
524 10,
525 2
526 );
527