feat(classifier): count_placeholder_fields() helper for drop-logic
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,14 @@ final class Classifier
|
|||||||
'rambler.ru', 'gmail.com',
|
'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<string,mixed> $fields CF7 posted_data (field => value or array of values)
|
||||||
* @param array{reason?:string} $meta Context: reason = Akismet / Honeypot / CF7 rules / Неизвестно
|
* @param array{reason?:string} $meta Context: reason = Akismet / Honeypot / CF7 rules / Неизвестно
|
||||||
@@ -266,4 +274,32 @@ final class Classifier
|
|||||||
if ($name === '') return false;
|
if ($name === '') return false;
|
||||||
return (bool)preg_match('/^[A-Za-z0-9]{1,3}$/', $name);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,4 +102,43 @@ final class ClassifierDropTest extends Cf7stgTestCase
|
|||||||
$fields = ['your-name' => ['OB', 'MU']];
|
$fields = ['your-name' => ['OB', 'MU']];
|
||||||
self::assertFalse(Classifier::has_short_latin_name($fields));
|
self::assertFalse(Classifier::has_short_latin_name($fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_count_placeholder_fields_screenshot_1(): void
|
||||||
|
{
|
||||||
|
$fields = require __DIR__ . '/fixtures/spam-screenshot-1.php';
|
||||||
|
// text-785: OB(2), text-787: 70(2), text-786: BP(2), text-788: ZZ(2), text-789: QV(2)
|
||||||
|
// text-790: 10 цифр — не placeholder. acceptance-data: служебное → исключено.
|
||||||
|
self::assertGreaterThanOrEqual(3, Classifier::count_placeholder_fields($fields));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_count_placeholder_fields_ignores_empty(): void
|
||||||
|
{
|
||||||
|
$fields = ['a' => 'AB', 'b' => '', 'c' => 'XY', 'd' => null, 'e' => 'CD'];
|
||||||
|
self::assertSame(3, Classifier::count_placeholder_fields($fields));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_count_placeholder_fields_ignores_long_values(): void
|
||||||
|
{
|
||||||
|
$fields = ['a' => 'AB', 'b' => 'Hello world', 'c' => 'CD'];
|
||||||
|
self::assertSame(2, Classifier::count_placeholder_fields($fields));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_count_placeholder_fields_ignores_service_keys(): void
|
||||||
|
{
|
||||||
|
$fields = [
|
||||||
|
'acceptance-data' => '1',
|
||||||
|
'_wpcf7' => 'AB',
|
||||||
|
'_wpcf7_unit_tag' => 'XY',
|
||||||
|
'g-recaptcha-response' => 'CD',
|
||||||
|
'real' => 'EF',
|
||||||
|
];
|
||||||
|
self::assertSame(1, Classifier::count_placeholder_fields($fields));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_count_placeholder_fields_ignores_non_alnum(): void
|
||||||
|
{
|
||||||
|
// Кириллица или с пробелами не считается placeholder
|
||||||
|
$fields = ['a' => 'Ия', 'b' => 'a b', 'c' => 'XY'];
|
||||||
|
self::assertSame(1, Classifier::count_placeholder_fields($fields));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user