3c8886c97f
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
106 lines
4.1 KiB
PHP
106 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Autopodbor;
|
|
|
|
use App\Support\PhoneNormalizer;
|
|
|
|
/**
|
|
* Нормализует домены и телефоны для дедупликации конкурентов и источников.
|
|
*/
|
|
final class AutopodborNormalizer
|
|
{
|
|
/**
|
|
* Возвращает «голову» домена: без схемы, www, пути, порта, нижний регистр.
|
|
* Примеры:
|
|
* https://www.Okna-Komfort.RU/contacts → okna-komfort.ru
|
|
* http://site.ru:8080/path?x=1 → site.ru
|
|
*/
|
|
public function domainHead(string $raw): string
|
|
{
|
|
$s = trim(mb_strtolower($raw));
|
|
// Убираем схему (http://, https://, ftp:// и т.п.)
|
|
$s = preg_replace('#^[a-z]+://#', '', $s);
|
|
// Убираем www.
|
|
$s = preg_replace('#^www\.#', '', $s);
|
|
// Берём только host часть (до первого /)
|
|
$s = explode('/', $s)[0];
|
|
// Убираем query string если вдруг осталась
|
|
$s = explode('?', $s)[0];
|
|
// Убираем порт
|
|
$s = explode(':', $s)[0];
|
|
|
|
return $s;
|
|
}
|
|
|
|
/**
|
|
* Нормализует телефон к виду 7xxxxxxxxxx (11 цифр, без плюса).
|
|
* Использует существующий PhoneNormalizer::normalize, который возвращает +7XXXXXXXXXX.
|
|
*/
|
|
public function phone(string $raw): string
|
|
{
|
|
$normalized = PhoneNormalizer::normalize($raw);
|
|
|
|
if ($normalized === null) {
|
|
// Fallback: оставить только цифры и привести к 7xxxxxxxxxx
|
|
$digits = preg_replace('/\D+/', '', $raw) ?? '';
|
|
if (strlen($digits) === 11 && ($digits[0] === '8' || $digits[0] === '7')) {
|
|
return '7' . substr($digits, 1);
|
|
}
|
|
if (strlen($digits) === 10) {
|
|
return '7' . $digits;
|
|
}
|
|
return $digits;
|
|
}
|
|
|
|
// PhoneNormalizer возвращает +7XXXXXXXXXX — срезаем ведущий '+'
|
|
return ltrim($normalized, '+');
|
|
}
|
|
|
|
/**
|
|
* Строит dedup-ключ для источника (сайт или звонок).
|
|
* Формат: «type:нормализованный_идентификатор»
|
|
*/
|
|
public function sourceKey(string $type, string $identifier): string
|
|
{
|
|
$id = $type === 'call'
|
|
? $this->phone($identifier)
|
|
: $this->domainHead($identifier);
|
|
|
|
return $type . ':' . $id;
|
|
}
|
|
|
|
/**
|
|
* Срезает хвостовой значок (✓ или 🎭) вместе с пробелами перед ним.
|
|
* Если значка нет — строка возвращается без изменений.
|
|
* Примеры:
|
|
* 'Окна Комфорт ✓' → 'Окна Комфорт'
|
|
* 'Окна Комфорт 🎭' → 'Окна Комфорт'
|
|
* 'Окна Комфорт' → 'Окна Комфорт'
|
|
* 'Балкон-Сервис 16' → 'Балкон-Сервис 16'
|
|
*/
|
|
public function stripBadge(string $name): string
|
|
{
|
|
// Срезаем ровно один хвостовой значок (✓ или 🎭) вместе с пробелами перед ним.
|
|
// Используем mb-безопасный regex с флагом u (эмодзи 🎭 — 4-байтный).
|
|
return preg_replace('/\s*(?:\x{2713}|\x{1F3AD})\s*$/u', '', $name) ?? $name;
|
|
}
|
|
|
|
/**
|
|
* Строит dedup-ключ для конкурента.
|
|
* Если есть сайт — «site:домен», иначе «name:имя_в_нижнем_регистре».
|
|
*/
|
|
public function competitorKey(string $name, ?string $siteUrl): string
|
|
{
|
|
if ($siteUrl !== null) {
|
|
return 'site:' . $this->domainHead($siteUrl);
|
|
}
|
|
|
|
// Нижний регистр + схлопываем пробелы
|
|
$normalized = preg_replace('#\s+#u', ' ', trim(mb_strtolower($name)));
|
|
|
|
return 'name:' . $normalized;
|
|
}
|
|
}
|