Files
Malin f3ff7b7186 feat: initial ACRIB WordPress deployment
- WordPress 6.9.4 (es_ES) with Kadence theme
- Homepage: Hero, La Asociación, Pilares, Beneficios, Eventos, Miembros, Hazte Miembro, Contacto
- Brand identity: #13294b navy, #a12932 burgundy, #c69c48 gold
- Fonts: Raleway (headings) + Source Sans 3 (body) + Lato (UI)
- Plugins: Kadence Blocks, Polylang, Contact Form 7
- Custom CSS with full brand styling and responsive layout
- HTTPS enforced via wp-config.php proxy detection
2026-05-19 19:25:59 +02:00

259 lines
6.9 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php declare( strict_types=1 );
namespace KadenceWP\KadenceBlocks\Optimizer\Hash;
use InvalidArgumentException;
use KadenceWP\KadenceBlocks\Optimizer\Enums\Viewport;
use KadenceWP\KadenceBlocks\Optimizer\Path\Path_Factory;
use KadenceWP\KadenceBlocks\Optimizer\Request\Request;
use KadenceWP\KadenceBlocks\Optimizer\Skip_Rules\Rule_Collection;
use KadenceWP\KadenceBlocks\Optimizer\Store\Contracts\Store;
use KadenceWP\KadenceBlocks\Psr\Log\LoggerInterface;
use KadenceWP\KadenceBlocks\StellarWP\SuperGlobals\SuperGlobals as SG;
use KadenceWP\KadenceBlocks\Traits\Viewport_Trait;
use Throwable;
/**
* Handles outputbuffer hashing during the WordPress shutdown phase to detect
* whether the rendered HTML for the current request has changed since the last
* optimization pass and invalidates the current optimization data if it's outdated.
*/
final class Hash_Handler {
use Viewport_Trait;
/**
* Captures the final HTML before output buffering is
* flushed.
*
* @var string
*/
private string $html = '';
private Hash_Builder $hasher;
private Store $store;
private Rule_Collection $rules;
private Background_Processor $background_processor;
private Hash_Store $hash_store;
private Path_Factory $path_factory;
private LoggerInterface $logger;
/**
* @param Hash_Builder $hasher
* @param Store $store
* @param Rule_Collection $rules
* @param Background_Processor $background_processor
* @param Hash_Store $hash_store
* @param Path_Factory $path_factory
* @param LoggerInterface $logger
*/
public function __construct(
Hash_Builder $hasher,
Store $store,
Rule_Collection $rules,
Background_Processor $background_processor,
Hash_Store $hash_store,
Path_Factory $path_factory,
LoggerInterface $logger
) {
$this->hasher = $hasher;
$this->store = $store;
$this->rules = $rules;
$this->background_processor = $background_processor;
$this->hash_store = $hash_store;
$this->path_factory = $path_factory;
$this->logger = $logger;
}
/**
* Begin buffering the request.
*
* @action template_redirect
*
* @return void
*/
public function start_buffering(): void {
ob_start( [ $this, 'end_buffering' ] );
}
/**
* Manage the current HTML hash state for the request.
*
* Compares the freshly generated hash of the final output buffer
* against the previously stored hash. If the hash differs, this indicates
* that the page content has changed since the last optimization pass.
*
* This method is intended to be called AS LATE AS POSSIBLE in the `shutdown` phase, after
* output buffering has captured the full HTML content.
*
* If running under fastcgi or litespeed, the request will be returned immediately and the logic
* processed in the background.
*
* @action shutdown
*
* @return void
*/
public function check_hash(): void {
if ( SG::get_get_var( Request::QUERY_OPTIMIZER_PREVIEW ) ) {
$this->logger->debug( 'Skipping hash check due to optimizer preview query variable.' );
return;
}
if ( ! $this->html ) {
if ( ! is_admin() ) {
$this->logger->debug(
'Bypassing Optimizer: No HTML found to check',
[
'request_uri' => SG::get_server_var( 'REQUEST_URI', 'unknown' ),
]
);
}
return;
}
// Don't check hashes on 404 requests.
if ( is_404() ) {
$this->logger->debug(
'Bypassing Optimizer: 404 not found',
[
'request_uri' => SG::get_server_var( 'REQUEST_URI', 'unknown' ),
]
);
return;
}
// Return request early, if possible, so we can process this in the background.
$this->background_processor->try_finish();
$viewport = Viewport::current( $this->is_mobile() );
// Process skip rules and bail if required.
foreach ( $this->rules->all() as $rule ) {
if ( $rule->should_skip() ) {
$this->logger->debug(
'Bypassing Optimizer: skip rule',
[
'rule' => get_class( $rule ),
'viewport' => $viewport->value(),
]
);
return;
}
}
try {
$path = $this->path_factory->make();
} catch ( InvalidArgumentException $e ) {
$this->logger->error(
'Hash handler unable to determine the path',
[
'viewport' => $viewport->value(),
'exception' => $e,
]
);
return;
}
// Generate a hash based on the final HTML markup, note this differs for mobile vs desktop.
$hash = $this->hasher->build_hash( $this->html );
$stored_hash = $this->hash_store->get( $path, $viewport );
// The frontend script will pass this get var as a hash set request.
$maybe_set_hash = (bool) SG::get_get_var( 'kadence_set_optimizer_hash', false );
if ( $maybe_set_hash ) {
$this->logger->debug(
'Attempting to store new optimizer hash',
[
'path' => $path->path(),
'viewport' => $viewport->value(),
'hash' => $hash,
]
);
// Store the hash for the current viewport.
$this->hash_store->set( $path, $viewport, $hash );
do_action( 'kadence_blocks_optimizer_set_hash', $hash, $path, $viewport );
return;
}
// The HTML has been changed somehow, invalidate the optimization data, so that the next request will not have the data.
if ( $stored_hash && $stored_hash !== $hash ) {
$changes = $this->hasher->get_changed_components( $stored_hash, $hash );
$this->logger->debug(
'Optimizer hash does not match...deleting',
[
'path' => $path->path(),
'viewport' => $viewport->value(),
'changes' => $changes,
]
);
// Delete the viewport hash.
$this->hash_store->delete( $path, $viewport );
$analysis = $this->store->get( $path );
// This page isn't optimized or the data is already invalidated.
if ( ! $analysis ) {
return;
}
// Set data to stale to force invalidate data for all viewports.
try {
$this->logger->debug(
'Marking optimizer path as stale to remove on next page load',
[
'path' => $path->path(),
]
);
$analysis->isStale = true;
$this->store->set( $path, $analysis );
do_action( 'kadence_blocks_optimizer_data_invalidated', $analysis->isStale, $path );
} catch ( Throwable $e ) {
// Our DateTimeImmutable should never throw an exception, but this is here just in case.
return;
}
}
$this->html = '';
do_action( 'kadence_blocks_hash_check_complete' );
}
/**
* Get the HTML, which will differ as the request proceeds.
*
* @return string
*/
public function html(): string {
return $this->html;
}
/**
* Callback that receives the buffer's contents. Captures the full page HTML
* in our property for use when we manage the hash state down the line.
*
* @param string $html The final HTML.
* @param int $phase The bitmask of the PHP_OUTPUT_HANDLER_* constants.
*
* @return string
*/
private function end_buffering( string $html, int $phase ): string {
if ( $phase & PHP_OUTPUT_HANDLER_FINAL ) {
$this->html = $html;
}
return $html;
}
}