'; public const END = ''; private const CONSENT_TOOLS = array( 'cookiebot' => 'Cookiebot', 'onetrust' => 'OneTrust', 'borlabs' => 'Borlabs Cookie', 'complianz' => 'Complianz', 'cookieyes' => 'CookieYes', 'iubenda' => 'iubenda', 'usercentrics' => 'Usercentrics', 'cookie-notice' => 'Cookie Notice', 'real-cookie-banner' => 'Real Cookie Banner', ); /** * What the page contains. * * @param string $html Page HTML. * @return array{ours: array, other: array, consent_tools: array} */ public static function inspect( string $html ): array { $ours = ''; // Scripts printed by WordPress for this app carry an id that starts with "fxd-google-". $other = (string) preg_replace_callback( '#]*\bid=["\']fxd-google-[^"\']*["\'][^>]*>.*?#is', static function ( array $found ) use ( &$ours ): string { $ours .= $found[0]; return ''; }, $html ); // The verification line sits between two comment markers. $start = strpos( $other, self::START ); $end = strpos( $other, self::END ); if ( false !== $start && false !== $end && $end > $start ) { $ours .= substr( $other, $start, $end - $start ); $other = substr( $other, 0, $start ) . substr( $other, $end + strlen( self::END ) ); } return array( 'ours' => self::codes( $ours ), 'other' => self::codes( $other ), 'consent_tools' => self::consent_tools( $other ), ); } /** * The Google codes in a piece of HTML, with how often each is loaded. * * @param string $html HTML. * @return array{verification: array, ga4: array, gtm: array, universal: array} */ private static function codes( string $html ): array { $out = array( 'verification' => array(), 'ga4' => array(), 'gtm' => array(), 'universal' => array(), ); if ( '' === $html ) { return $out; } if ( preg_match_all( '/]+name\s*=\s*["\']google-site-verification["\'][^>]*>/i', $html, $tags ) ) { foreach ( $tags[0] as $tag ) { if ( preg_match( '/content\s*=\s*["\']([^"\']+)["\']/i', $tag, $content ) ) { $out['verification'][] = $content[1]; } } } // A gtag.js loader counts once per script tag. if ( preg_match_all( '#googletagmanager\.com/gtag/js\?id=([A-Za-z0-9-]+)#', $html, $loaders ) ) { foreach ( $loaders[1] as $id ) { $kind = str_starts_with( strtoupper( $id ), 'UA-' ) ? 'universal' : 'ga4'; if ( 'ga4' === $kind && ! str_starts_with( strtoupper( $id ), 'G-' ) ) { continue; } $out[ $kind ][ strtoupper( $id ) ] = ( $out[ $kind ][ strtoupper( $id ) ] ?? 0 ) + 1; } } // Universal Analytics through analytics.js. if ( preg_match_all( '/ga\(\s*[\'"]create[\'"]\s*,\s*[\'"](UA-[0-9]+-[0-9]+)[\'"]/', $html, $classic ) ) { foreach ( $classic[1] as $id ) { $out['universal'][ $id ] = ( $out['universal'][ $id ] ?? 0 ) + 1; } } // Tag Manager: the script loader, once per container. if ( preg_match_all( '#googletagmanager\.com/gtm\.js\?id=(GTM-[A-Z0-9]+)#', $html, $containers ) ) { foreach ( $containers[1] as $id ) { $out['gtm'][ $id ] = ( $out['gtm'][ $id ] ?? 0 ) + 1; } } // The standard inline snippet builds the address in script: ...'dataLayer','GTM-XXXX'. if ( preg_match_all( '/[\'"]dataLayer[\'"]\s*,\s*[\'"](GTM-[A-Z0-9]+)[\'"]/', $html, $inline ) ) { foreach ( $inline[1] as $id ) { $out['gtm'][ $id ] = ( $out['gtm'][ $id ] ?? 0 ) + 1; } } return $out; } /** * Names of consent tools whose files appear in the page. * * @param string $html HTML. * @return array */ private static function consent_tools( string $html ): array { $found = array(); $lower = strtolower( $html ); foreach ( self::CONSENT_TOOLS as $needle => $name ) { if ( str_contains( $lower, $needle ) ) { $found[] = $name; } } return $found; } }