• Записи 162
  • Теги 66
  • Комментарии 330

Компьютерное

Немного о юзабилити обычных ссылок

Казалось бы, что может быть проще, чем вывести ссылку в HTML? Однако если создавать сайт, наполнением которого будут заниматься сами пользователи, обнаруживается множество тонкостей в плане юзабилити, которые нужно учитывать. Во-первых, ссылки, вставленные пользователями, могут быть слишком длинными из-за большого «хвоста» параметров (типичный пример — ссылка на страницы Интернет-магазинов с настроенными фильтрами) и переноситься на несколько строк, и в таких случаях их нужно визуально сокращать. Во-вторых, если в ссылке есть кириллица, она при копировании через буфер обмена преобразуется либо в punycode, либо в url encoding и становится нечитаемой. Пример подобного — ссылки на Wikipedia, глядя на которые, нельзя сказать до перехода, на какую статью они ведут. В-третьих, если ссылка ведёт на корневую страницу сайта, желательно показывать только доменное имя, без http или https в начале и / в конце.
Возникает необходимость сделать обработчик для ссылок без описаний (т.е. вида <a href="URL">URL</a>), который справлялся бы с перечисленными ситуациями. Я для себя написал следующую функцию:
<?php
function link_processor($matches) {
    
$url_parts parse_url($matches[3]);

        
// Blocks unallowed schemes
    
if (!empty($url_parts['scheme']) && !in_array(strtolower($url_parts['scheme']),array('http','https','ftp','tg','magnet'))) return '*** Invalid or malicious link! ***';
    
$scheme = empty($url_parts['scheme']) ? '' $url_parts['scheme'];
    
    if (
$matches[3]!==$matches[5]) return $matches[0]; // skipping links with user's description

        // processing URL parts
    
$host = (!empty($url_parts['host'])) ? mb_strtoupper(mb_substr($url_parts['host'],0,1)).mb_substr($url_parts['host'],1) : '';
    
$path = (empty($url_parts['path']) || $url_parts['path']==='/') ? '' $path urldecode($url_parts['path']);
        
$query = (!empty($url_parts['query'])) ? $url_parts['query'] : '';    
    
$fragment = (!empty($url_parts['fragment'])) ? '#'.urldecode($url_parts['fragment']) : '';    
    
    
// Wikipedia
    
if (strtolower(substr($host,-14))==='.wikipedia.org' && mb_substr($path,0,6)==='/wiki/'$new_link 'Wikipedia: '.str_replace('_',' ',mb_substr($path,6));
    
// Google
    
elseif (strtolower(substr($host,-10))==='google.com' && $path==='/search'  && substr($query,0,2)==='q=') {
       
$pos strpos($query,'&');
       if (
$pos!==false$query=substr($query,0,$pos);
       
$new_link "Google: ".urldecode(substr($query,2));
    }
    
// Yandex
    
elseif ((strtolower(substr($host,-5))==='ya.ru' || strtolower(substr($host,-9))==='yandex.ru') && $path==='/search/'  && substr($query,0,5)==='text=') {
       
$pos strpos($query,'&');
       if (
$pos!==false$query=substr($query,0,$pos);
       
$new_link "Yandex: ".urldecode(substr($query,5));
    }
    
// Telegram
    
elseif (strtolower(substr($host,-4))==='t.me' || $scheme=='tg'$new_link 'Telegram: '.mb_substr($url_parts['path'],1);
    
// YouTube
    
elseif (strtolower(substr($host,-11))==='youtube.com' || strtolower(substr($host,-8))==='youtu.be') {
        
$new_link 'YouTube: ';        
        if (
$path==='/watchv') {
            
$pos strpos($query,'&');
           if (
$pos!==false$new_link.=substr($query,0,$pos);
           else 
$new_link.=$query;
        }
        if (
$path==='/watch') {
          if (
preg_match('|v=([\w\-]+)|',$query,$match)) $new_link.=$match[1];
          else 
$new_link $matches[5]; // if v parameter not found, fallbacks to initial link description
        
}
        else 
$new_link.=mb_substr($path,1);
        if (
preg_match('|t=(\d+)|',$query,$match)) { // if time offset specified
            
$time intval($match[1]);
            
$min floor($time/60);
            
$sec $time 60;
            
$new_link.=" ($min:$sec)";
        }
    }
    else {
        if (
$query && strpos($path,'search')===false && !preg_match('/search=|text=|query=|filter=/i',$query)) $query='?...';
        elseif (
strlen($query)>32$query=substr($query,0,32).'…';
        
        
$host idn_to_utf8($host);
        if (empty(
$path)) $host=mb_strtoupper(mb_substr($host,0,1)).mb_substr($host,1);        
        if (
mb_strtolower($host)==='vk.com' || mb_strtolower($host)==='m.vk.com'$host='VK.com';

                 if (
mb_strlen($path)>48$path mb_substr($path,0,6).'…'.mb_substr($path,-6);
            if (
mb_strlen($fragment)>32$path mb_substr($path,0,32).'…';

        
        
$new_link $host.$path.$query.$fragment;
    }

    return 
str_replace('>'.$matches[5].'<','>'.$new_link.'<',$matches[0]);
}


Эта функция сокращает пути длиннее 48 символов, строки параметров и anchors длиннее 32, скрывает префиксы и делает первую букву имени домена заглавной. Для её корректной работы необходим модуль mbstring.
Кроме перечисленного выше, эта функция умеет также распознавать поисковые ссылки на Яндекс и Google, и ссылки на YouTube, выделяя из них идентификатор ролика и время, с которого тот начнёт воспроизводиться, если оно указано.

Вызывать её нужно с помощью preg_replace_callback (предположим, что текст для обработки лежит в переменной $text):
<?php
    $text 
preg_replace_callback('|<a\s+([^>]*?)href=(["\']?)(\S+)\2([^>]*)>(.*?)</a\s*>|isu','link_processor',$text);


Посмотреть код в действии с тестовыми примерами можно на onlinephp.io/c/edbb8.

Написать комментарий:

Написать комментарий


Задать вопрос