| @@ -78,8 +78,14 @@ | ||
| 78 | 78 | /** advanced-cache.php is there and cannot be read. */ |
| 79 | 79 | public const BLOCKER_UNREADABLE_DROPIN = 'unreadable_dropin'; |
| 80 | 80 | /** A page-cache-capable plugin is active with no drop-in evidence. */ |
| 81 | 81 | public const BLOCKER_ACTIVE_PAGE_CACHE = 'active_page_cache'; |
| 82 | + /** | |
| 83 | + * An active plugin's own settings say its page cache is on. Unlike | |
| 84 | + * `active_page_cache`, which only says it CAN cache pages, this is | |
| 85 | + * proof that it does, so xSpeed's would be stacked on top of it. (#653) | |
| 86 | + */ | |
| 87 | + public const BLOCKER_PAGE_CACHE_SWITCHED_ON = 'page_cache_switched_on'; | |
| 82 | 88 | /** More than one plugin owns, or could own, the page cache. */ |
| 83 | 89 | public const BLOCKER_MULTIPLE_PAGE_CACHES = 'multiple_page_caches'; |
| 84 | 90 | /** WP_CACHE is true with no drop-in to explain it. */ |
| 85 | 91 | public const BLOCKER_WP_CACHE_ORPHANED = 'wp_cache_orphaned'; |
| @@ -182,8 +188,10 @@ | ||
| 182 | 188 | if ( ! $active && empty( $signals ) ) { |
| 183 | 189 | continue; |
| 184 | 190 | } |
| 185 | 191 | |
| 192 | + $page_cache = in_array( Cache_Plugin_Catalog::CAP_PAGE_CACHE, $entry['capabilities'], true ); | |
| 193 | + | |
| 186 | 194 | $out[] = array( |
| 187 | 195 | 'plugin' => $file, |
| 188 | 196 | 'label' => $entry['label'], |
| 189 | 197 | 'active' => $active, |
| @@ -191,9 +199,12 @@ | ||
| 191 | 199 | 'site_active' => $site_active, |
| 192 | 200 | 'network_active' => $network_active, |
| 193 | 201 | 'activation_scope' => $site_active && $network_active ? 'site-and-network' : ( $network_active ? 'network' : ( $site_active ? 'site' : 'inactive' ) ), |
| 194 | 202 | 'capabilities' => $entry['capabilities'], |
| 195 | - 'page_cache' => in_array( Cache_Plugin_Catalog::CAP_PAGE_CACHE, $entry['capabilities'], true ), | |
| 203 | + 'page_cache' => $page_cache, | |
| 204 | + // The plugin's own page-cache switch: true on, false off, | |
| 205 | + // null unknown. Only asked of an active page cache. | |
| 206 | + 'page_cache_on' => ( $page_cache && $active ) ? Cache_Plugin_Catalog::page_cache_state( $file ) : null, | |
| 196 | 207 | 'signals' => $signals, |
| 197 | 208 | ); |
| 198 | 209 | } |
| 199 | 210 | return $out; |
| @@ -446,9 +457,9 @@ | ||
| 446 | 457 | foreach ( self::inspect()['plugins'] as $plugin ) { |
| 447 | 458 | if ( self::SELF_PLUGIN_FILE === (string) $plugin['plugin'] ) { |
| 448 | 459 | continue; |
| 449 | 460 | } |
| 450 | - if ( ! empty( $plugin['page_cache'] ) && ! empty( $plugin['active'] ) ) { | |
| 461 | + if ( self::may_be_caching_pages( $plugin ) ) { | |
| 451 | 462 | return true; |
| 452 | 463 | } |
| 453 | 464 | } |
| 454 | 465 | return false; |
| @@ -453,8 +464,26 @@ | ||
| 453 | 464 | } |
| 454 | 465 | return false; |
| 455 | 466 | } |
| 456 | 467 | |
| 468 | + /** | |
| 469 | + * An active page-cache plugin, unless its own settings prove its page | |
| 470 | + * cache is off. | |
| 471 | + * | |
| 472 | + * A plugin kept active for its other features, with its page cache | |
| 473 | + * switched off, owns nothing here. Counting it anyway is what made | |
| 474 | + * Health report LiteSpeed Cache as a conflict on GridPane's | |
| 475 | + * OpenLiteSpeed servers with its page cache off. When the setting | |
| 476 | + * cannot be read we still assume it may be caching. (#653) | |
| 477 | + * | |
| 478 | + * @param array $plugin One row from inspect_plugins(). | |
| 479 | + */ | |
| 480 | + private static function may_be_caching_pages( array $plugin ): bool { | |
| 481 | + return ! empty( $plugin['page_cache'] ) | |
| 482 | + && ! empty( $plugin['active'] ) | |
| 483 | + && false !== ( $plugin['page_cache_on'] ?? null ); | |
| 484 | + } | |
| 485 | + | |
| 457 | 486 | public static function is_field_clear(): bool { |
| 458 | 487 | $verdict = self::classify(); |
| 459 | 488 | |
| 460 | 489 | if ( ! empty( $verdict['blockers'] ) ) { |
| @@ -478,9 +507,9 @@ | ||
| 478 | 507 | */ |
| 479 | 508 | public static function active_page_caches(): array { |
| 480 | 509 | $out = array(); |
| 481 | 510 | foreach ( self::inspect()['plugins'] as $plugin ) { |
| 482 | - if ( $plugin['page_cache'] && $plugin['active'] ) { | |
| 511 | + if ( self::may_be_caching_pages( $plugin ) ) { | |
| 483 | 512 | $out[] = (string) $plugin['label']; |
| 484 | 513 | } |
| 485 | 514 | } |
| 486 | 515 | return $out; |
| @@ -589,11 +618,26 @@ | ||
| 589 | 618 | */ |
| 590 | 619 | $owners = array(); |
| 591 | 620 | $active = array(); |
| 592 | 621 | foreach ( $report['plugins'] as $plugin ) { |
| 593 | - if ( $plugin['page_cache'] && $plugin['active'] ) { | |
| 622 | + if ( self::may_be_caching_pages( $plugin ) ) { | |
| 594 | 623 | $active[ (string) $plugin['plugin'] ] = $plugin; |
| 595 | 624 | $owners[ (string) $plugin['plugin'] ] = (string) $plugin['label']; |
| 625 | + } | |
| 626 | + } | |
| 627 | + | |
| 628 | + /* | |
| 629 | + * Proven live, not just possible. Raised before the state ladder so | |
| 630 | + * it rides along with whichever state wins: two page caches serving | |
| 631 | + * the same pages is the problem whoever holds the drop-in. (#653) | |
| 632 | + */ | |
| 633 | + foreach ( $active as $file => $plugin ) { | |
| 634 | + if ( self::SELF_PLUGIN_FILE !== $file && true === ( $plugin['page_cache_on'] ?? null ) ) { | |
| 635 | + $blockers[] = array( | |
| 636 | + 'code' => self::BLOCKER_PAGE_CACHE_SWITCHED_ON, | |
| 637 | + 'plugin' => $plugin['plugin'], | |
| 638 | + 'label' => $plugin['label'], | |
| 639 | + ); | |
| 596 | 640 | } |
| 597 | 641 | } |
| 598 | 642 | if ( self::OWNER_FOREIGN === $dropin['owner'] ) { |
| 599 | 643 | $key = (string) ( $dropin['plugin'] ?? $dropin['label'] ); |