feat(classifier): count_placeholder_fields() helper for drop-logic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vladimir Bryzgalov
2026-04-21 16:06:32 +05:00
parent 1168a733a4
commit 249f195257
2 changed files with 75 additions and 0 deletions
+36
View File
@@ -46,6 +46,14 @@ final class Classifier
'rambler.ru', 'gmail.com',
];
/** Service field keys excluded from placeholder count. */
private const SERVICE_FIELD_KEYS = [
'acceptance-data',
'_wpcf7', '_wpcf7_version', '_wpcf7_locale', '_wpcf7_unit_tag',
'_wpcf7_container_post', '_wpcf7_posted_data_hash',
'g-recaptcha-response',
];
/**
* @param array<string,mixed> $fields CF7 posted_data (field => value or array of values)
* @param array{reason?:string} $meta Context: reason = Akismet / Honeypot / CF7 rules / Неизвестно
@@ -266,4 +274,32 @@ final class Classifier
if ($name === '') return false;
return (bool)preg_match('/^[A-Za-z0-9]{1,3}$/', $name);
}
/**
* Counts non-empty fields whose value is exactly 1-2 ASCII alphanumeric chars
* (the typical "AB", "70" placeholder pattern bots fill custom-keyed fields with).
*
* Skips service keys (acceptance-data, _wpcf7*, g-recaptcha-response) and any key
* starting with `_wpcf7`. Array values are joined without separators before length
* check (so `['A','B']` collapses to "AB" — still counted).
*
* @param array<string,string|string[]|null> $fields CF7 posted_data
* @return int Number of fields matching the placeholder pattern
*/
public static function count_placeholder_fields(array $fields): int
{
$count = 0;
foreach ($fields as $k => $v) {
if (is_string($k)) {
if (in_array($k, self::SERVICE_FIELD_KEYS, true)) continue;
if (strpos($k, '_wpcf7') === 0) continue;
}
if (is_array($v)) $v = implode('', array_map('strval', $v));
$v = trim((string)$v);
if ($v === '') continue;
if (!preg_match('/^[A-Za-z0-9]{1,2}$/', $v)) continue;
$count++;
}
return $count;
}
}