robotstxt-mediaaudit/includes/External/ResultsConsolidator.php
2026-06-03 06:24:03 +00:00

99 lines
3.2 KiB
PHP

<?php
/**
* Cross-provider result consolidation.
*
* @package MediaRightsAudit\External
*/
namespace MediaRightsAudit\External;
/**
* Aggregates external scan results across multiple providers to surface
* domains that appear in more than one provider's findings (consensus signals).
*
* Consensus detection provides stronger copyright-risk signals: a domain
* independently found by both Google Vision and TinEye is more likely to be
* a genuine unauthorised copy than one found by a single provider.
*/
class ResultsConsolidator {
/**
* Returns domains confirmed by results from at least $min_providers distinct providers.
*
* Reads top_domains JSON stored in mra_external_results for the given attachment
* and counts how many providers reported each domain. Only domains meeting the
* threshold are returned, sorted descending by provider-agreement count.
*
* @param int $attachment_id WordPress attachment ID.
* @param int $min_providers Minimum number of providers that must agree (default: 2).
*
* @return array<string, int> Domain → agreeing-provider count, sorted descending.
*/
public static function get_consensus_domains( int $attachment_id, int $min_providers = 2 ): array {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$rows = $wpdb->get_results(
$wpdb->prepare(
"SELECT provider, top_domains FROM {$wpdb->prefix}mra_external_results WHERE attachment_id = %d",
$attachment_id
),
ARRAY_A
);
if ( ! $rows ) {
return array();
}
return self::aggregate_consensus( $rows, $min_providers );
}
/**
* Computes consensus domains from an array of provider result rows.
*
* Each row must contain 'provider' (string) and 'top_domains' (JSON string
* mapping domain → count). Designed to be called directly in unit tests
* by passing mock row data.
*
* @param array<int, array<string, mixed>> $rows Provider result rows.
* @param int $min_providers Minimum provider agreement.
*
* @return array<string, int> Domain → agreeing-provider count, sorted descending.
*/
public static function aggregate_consensus( array $rows, int $min_providers = 2 ): array {
$domain_to_providers = array();
foreach ( $rows as $row ) {
$provider_val = $row['provider'] ?? null;
$provider = is_string( $provider_val ) ? $provider_val : '';
if ( '' === $provider ) {
continue;
}
$domains_val = $row['top_domains'] ?? null;
$domains_raw = is_string( $domains_val ) ? json_decode( $domains_val, true ) : null;
$domains = is_array( $domains_raw ) ? $domains_raw : array();
foreach ( $domains as $domain => $count ) {
if ( ! is_string( $domain ) ) {
continue;
}
if ( ! isset( $domain_to_providers[ $domain ] ) ) {
$domain_to_providers[ $domain ] = array();
}
$domain_to_providers[ $domain ][ $provider ] = true;
}
}
$consensus = array();
foreach ( $domain_to_providers as $domain => $providers_map ) {
$provider_count = count( $providers_map );
if ( $provider_count >= $min_providers ) {
$consensus[ $domain ] = $provider_count;
}
}
arsort( $consensus );
return $consensus;
}
}