Оригинальная заметка содержала одно выражение для каталога с двумя символьными сегментами.

Исходный вариант

preg_match(
  '~^/catalog/([A-Za-z0-9_]+)/([A-Za-z0-9_]+)~',
  $_SERVER['REQUEST_URI']
);

Выражение проверяет начало пути /catalog/ и два сегмента из латинских букв, цифр и подчёркивания. Но оно не фиксирует конец URL, не учитывает дефис и работает с URI вместе с параметрами запроса.

Более надёжная проверка пути

$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

$isDetail = preg_match(
  '~^/catalog/([a-z0-9_-]+)/([a-z0-9_-]+)/?$~i',
  $path,
  $matches
) === 1;

parse_url() отделяет путь от query string, /?$ допускает завершающий слэш, а якорь $ не позволяет принять более глубокий URL за детальную страницу.

Не используйте совпадение как проверку существования товара.Регулярное выражение подтверждает только форму URL. Элемент всё равно нужно получить из инфоблока с проверкой активности, прав и привязки к сайту.

Когда лучше ParseComponentPath

Если компонент работает в режиме ЧПУ, надёжнее использовать шаблоны компонента и CComponentEngine::ParseComponentPath(). Тогда логика маршрута не дублируется отдельной регуляркой и остаётся согласованной с настройками раздела и элемента.

preg_match остаётся уместным для небольшой проверки, аналитического события или выбора оформления, если формат URL стабилен и покрыт тестами.