server_rules = \Closure::fromCallable( $server_rules ); } /** * Applies settings. * * @param Settings $settings Settings. * @param bool $replace_foreign Take over a foreign advanced-cache.php. * @return list */ public function apply( Settings $settings, bool $replace_foreign = false ): array { if ( ! $settings->get( 'page_cache_enabled' ) ) { return $this->remove(); } try { $this->dropin->install( $this->dropin_source, $replace_foreign ); } catch ( ForeignDropinException ) { return [ new Problem( 'advanced_cache_foreign', 'Another plugin owns wp-content/advanced-cache.php. Page caching stays off until you replace it.' ) ]; } catch ( \Throwable ) { return [ new Problem( 'advanced_cache_unwritable', 'wp-content/advanced-cache.php could not be written. Check file permissions.' ) ]; } return array_values( array_filter( [ $this->enableWpCache(), $settings->get( 'server_rules_enabled' ) ? $this->applyServerRules( $settings ) : $this->removeServerRules(), ] ) ); } /** * Removes everything this plugin added. * * @return list */ public function remove(): array { $problems = []; try { $this->dropin->uninstall(); } catch ( \Throwable ) { $problems[] = new Problem( 'advanced_cache_unwritable', 'wp-content/advanced-cache.php could not be removed.' ); } return array_values( array_filter( array_merge( $problems, [ $this->locked() ? null : $this->editWpConfig( static fn ( string $config ) => WpConfigEditor::disableWpCache( $config ) ), $this->removeServerRules(), ] ) ) ); } /** * Turns WP_CACHE on, unless the owner turned it off or the file must not be written. */ private function enableWpCache(): ?Problem { $config = $this->wp_config->read(); if ( null !== $config && WpConfigEditor::turnedOff( $config ) ) { return new Problem( 'wp_cache_disabled', "wp-config.php sets WP_CACHE to false, so WordPress does not load the page cache. Change it to define( 'WP_CACHE', true );." ); } if ( $this->locked() ) { return null !== $config && WpConfigEditor::enableWpCache( $config ) === $config ? null : new Problem( 'wp_config_unwritable', "Add define( 'WP_CACHE', true ); to wp-config.php manually, or make it writable." ); } return $this->editWpConfig( static fn ( string $edited ) => WpConfigEditor::enableWpCache( $edited ) ); } /** * Whether wp-config.php must not be written (filter akantio_cache_wpconfig_readonly). */ private function locked(): bool { return null !== $this->wp_config_locked && ( $this->wp_config_locked )(); } /** * Edits wp-config.php when it is readable and writable. * * @param callable(string): string $edit Transformation. */ private function editWpConfig( callable $edit ): ?Problem { $config = $this->wp_config->read(); try { if ( null === $config ) { throw new \RuntimeException( 'unreadable' ); } $updated = $edit( $config ); if ( $updated !== $config ) { $this->wp_config->write( $updated ); } } catch ( \Throwable ) { return new Problem( 'wp_config_unwritable', "Add define( 'WP_CACHE', true ); to wp-config.php manually, or make it writable." ); } return null; } /** * Writes the server rules block. * * @param Settings $settings Settings. */ private function applyServerRules( Settings $settings ): ?Problem { return $this->editHtaccess( fn ( string $contents ) => MarkedBlock::apply( $contents, self::BLOCK, ( $this->server_rules )( $settings ) ) ); } /** * Removes the server rules block. */ private function removeServerRules(): ?Problem { return $this->editHtaccess( static fn ( string $contents ) => MarkedBlock::remove( $contents, self::BLOCK ) ); } /** * Edits .htaccess when it exists. * * @param callable(string): string $edit Transformation. */ private function editHtaccess( callable $edit ): ?Problem { $contents = $this->htaccess?->read(); if ( null === $contents ) { return null; } try { $updated = $edit( $contents ); if ( $updated !== $contents ) { $this->htaccess->write( $updated ); } } catch ( \Throwable ) { return new Problem( 'htaccess_unwritable', '.htaccess could not be updated; pages are still cached and served by PHP.' ); } return null; } }