99 lines
3.2 KiB
PHP
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;
|
|
}
|
|
}
|