v1.6.0
This commit is contained in:
parent
83d629568a
commit
98b1cf0f3b
17 changed files with 2968 additions and 1313 deletions
186
includes/External/HostnameFilter.php
vendored
Normal file
186
includes/External/HostnameFilter.php
vendored
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
<?php
|
||||
/**
|
||||
* Utility for classifying hostnames against the alert/ignored filter lists.
|
||||
*
|
||||
* @package MediaRightsAudit\External
|
||||
*/
|
||||
|
||||
namespace MediaRightsAudit\External;
|
||||
|
||||
/**
|
||||
* Classifies hostnames against the site's configured alert (include) and
|
||||
* ignored (exclude) lists stored in the robotstxt_mediaaudit_settings option.
|
||||
*
|
||||
* All methods are static; the loaded lists are cached in a class property so
|
||||
* that the option is read at most once per request.
|
||||
*/
|
||||
class HostnameFilter {
|
||||
|
||||
/**
|
||||
* Cached filter lists, populated lazily on first use.
|
||||
*
|
||||
* @var array{include: list<string>, exclude: list<string>}|null
|
||||
*/
|
||||
private static ?array $lists = null;
|
||||
|
||||
/**
|
||||
* Loads and returns the alert / ignored hostname lists from the plugin option.
|
||||
*
|
||||
* Result is cached in self::$lists for the duration of the request.
|
||||
*
|
||||
* @return array{include: list<string>, exclude: list<string>}
|
||||
*/
|
||||
private static function get_lists(): array {
|
||||
if ( null !== self::$lists ) {
|
||||
return self::$lists;
|
||||
}
|
||||
|
||||
$raw = get_option( 'robotstxt_mediaaudit_settings' );
|
||||
$opts = is_array( $raw ) ? $raw : array();
|
||||
|
||||
$raw_include = $opts['filter_include'] ?? array();
|
||||
$raw_exclude = $opts['filter_exclude'] ?? array();
|
||||
|
||||
$include = array();
|
||||
if ( is_array( $raw_include ) ) {
|
||||
foreach ( $raw_include as $item ) {
|
||||
if ( is_string( $item ) && '' !== $item ) {
|
||||
$include[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$exclude = array();
|
||||
if ( is_array( $raw_exclude ) ) {
|
||||
foreach ( $raw_exclude as $item ) {
|
||||
if ( is_string( $item ) && '' !== $item ) {
|
||||
$exclude[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$lists = array(
|
||||
'include' => $include,
|
||||
'exclude' => $exclude,
|
||||
);
|
||||
|
||||
return self::$lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when $domain matches a filter list entry.
|
||||
*
|
||||
* The $entry may carry a `*.` wildcard prefix; its base is derived by
|
||||
* stripping that prefix. A match occurs when:
|
||||
* - $domain equals the base exactly, OR
|
||||
* - $domain ends with a dot followed by the base (subdomain match).
|
||||
*
|
||||
* @param string $domain Lowercase hostname to test.
|
||||
* @param string $entry A list entry, possibly prefixed with `*.`.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function matches( string $domain, string $entry ): bool {
|
||||
$base = str_starts_with( $entry, '*.' ) ? substr( $entry, 2 ) : $entry;
|
||||
|
||||
if ( $domain === $base ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( str_ends_with( $domain, '.' . $base ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the given domain matches any entry in the alert list.
|
||||
*
|
||||
* @param string $domain Hostname to classify (will be lower-cased internally).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_alert( string $domain ): bool {
|
||||
$domain = strtolower( $domain );
|
||||
$lists = self::get_lists();
|
||||
|
||||
foreach ( $lists['include'] as $entry ) {
|
||||
if ( self::matches( $domain, $entry ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the given domain matches any entry in the ignored list.
|
||||
*
|
||||
* @param string $domain Hostname to classify (will be lower-cased internally).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_ignored( string $domain ): bool {
|
||||
$domain = strtolower( $domain );
|
||||
$lists = self::get_lists();
|
||||
|
||||
foreach ( $lists['exclude'] as $entry ) {
|
||||
if ( self::matches( $domain, $entry ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classifies a domain and returns 'alert', 'ignored', or 'other'.
|
||||
*
|
||||
* Alert takes precedence: a domain that is in both lists is classified as
|
||||
* 'alert'.
|
||||
*
|
||||
* @param string $domain Hostname to classify.
|
||||
*
|
||||
* @return string 'alert'|'ignored'|'other'
|
||||
*/
|
||||
public static function classify( string $domain ): string {
|
||||
if ( self::is_alert( $domain ) ) {
|
||||
return 'alert';
|
||||
}
|
||||
|
||||
if ( self::is_ignored( $domain ) ) {
|
||||
return 'ignored';
|
||||
}
|
||||
|
||||
return 'other';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when at least one key in $domains is classified as alert.
|
||||
*
|
||||
* @param array<string, int> $domains Map of domain => occurrence count.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_alert_domains( array $domains ): bool {
|
||||
foreach ( array_keys( $domains ) as $domain ) {
|
||||
if ( self::is_alert( $domain ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the internal cache so the option is re-read on the next call.
|
||||
*
|
||||
* Useful in tests or after the settings option has been updated.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function reset_cache(): void {
|
||||
self::$lists = null;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue