chore: monorepo - plugin, backend und hilfsdaten in einem repo
- Eltern-Ordner ist jetzt EIN Git-Repo (statt getrennter Repos). - root .gitignore haelt Secrets (.env), node_modules, DB und Build-Artefakte raus. - release.ps1: manueller Release (ZIP bauen + ans Backend laden). - root README mit Struktur und Release-Ablauf. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
109
gdpr-content-blocker/includes/class-autodetect.php
Normal file
109
gdpr-content-blocker/includes/class-autodetect.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Auto-detects iframes in the rendered HTML and replaces them with consent
|
||||
* placeholders based on configured match patterns.
|
||||
*
|
||||
* Strategy (safe by design):
|
||||
* 1. A regex LOCATES each <iframe>…</iframe> block (it does NOT parse attributes).
|
||||
* 2. Each block is parsed individually with DOMDocument to read the src reliably.
|
||||
* 3. Only the matched iframe block is replaced in the original HTML via callback.
|
||||
*
|
||||
* The rest of the page HTML is never re-serialized, so themes, page builders,
|
||||
* inline JS and JSON-LD stay byte-for-byte intact.
|
||||
*
|
||||
* Known limitation: Only iframes present in the initial server-rendered HTML are
|
||||
* covered here. For iframes injected later by JavaScript, use the manual shortcode
|
||||
* [content_blocker id="…"] around the embed.
|
||||
*/
|
||||
class CB_Autodetect {
|
||||
|
||||
public static function init(): void {
|
||||
add_action( 'template_redirect', [ __CLASS__, 'start_buffer' ] );
|
||||
}
|
||||
|
||||
public static function start_buffer(): void {
|
||||
if ( is_admin() || wp_doing_ajax() || wp_doing_cron() ) {
|
||||
return;
|
||||
}
|
||||
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
|
||||
return;
|
||||
}
|
||||
if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$services = CB_Settings::get_services();
|
||||
$active = array_values( array_filter(
|
||||
$services,
|
||||
fn( $s ) => ! empty( $s['match_pattern'] ) && ( $s['enabled'] ?? true )
|
||||
) );
|
||||
if ( empty( $active ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start( fn( string $html ) => self::process( $html, $active ) );
|
||||
}
|
||||
|
||||
public static function process( string $html, array $services ): string {
|
||||
if ( $html === '' || stripos( $html, '<iframe' ) === false ) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
// Locate iframe blocks only. Attribute parsing happens via DOMDocument below.
|
||||
return (string) preg_replace_callback(
|
||||
'#<iframe\b[^>]*>.*?</iframe>#is',
|
||||
function ( array $m ) use ( $services ): string {
|
||||
return self::maybe_replace_iframe( $m[0], $services );
|
||||
},
|
||||
$html
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single iframe block with DOMDocument, read its src, and return either
|
||||
* the consent placeholder (on match) or the unchanged original block.
|
||||
*/
|
||||
private static function maybe_replace_iframe( string $iframe_html, array $services ): string {
|
||||
$src = self::get_src( $iframe_html );
|
||||
if ( $src === '' ) {
|
||||
return $iframe_html;
|
||||
}
|
||||
|
||||
foreach ( $services as $svc ) {
|
||||
$pattern = $svc['match_pattern'] ?? '';
|
||||
if ( $pattern !== '' && str_contains( $src, $pattern ) ) {
|
||||
$attrs = CB_Renderer::extract_iframe_attrs( $iframe_html );
|
||||
$dims = [ 'width' => $attrs['width'], 'height' => $attrs['height'] ];
|
||||
return CB_Renderer::render_placeholder( $svc, $src, $dims );
|
||||
}
|
||||
}
|
||||
|
||||
return $iframe_html;
|
||||
}
|
||||
|
||||
/** Reliably extract the src attribute of a single iframe via DOMDocument. */
|
||||
private static function get_src( string $iframe_html ): string {
|
||||
$dom = new DOMDocument();
|
||||
libxml_use_internal_errors( true );
|
||||
// Wrap so loadHTML has a clean context; force UTF-8 so umlauts survive.
|
||||
$dom->loadHTML(
|
||||
'<?xml encoding="utf-8"?><div>' . $iframe_html . '</div>',
|
||||
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
|
||||
);
|
||||
libxml_clear_errors();
|
||||
|
||||
$node = $dom->getElementsByTagName( 'iframe' )->item( 0 );
|
||||
if ( ! $node instanceof DOMElement ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$src = trim( $node->getAttribute( 'src' ) );
|
||||
|
||||
// Normalise protocol-relative and HTML-entity-encoded ampersands.
|
||||
$src = str_replace( '&', '&', $src );
|
||||
|
||||
return $src;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user