Проверка / регулярное выражение PHP для URL-адреса

Я искал простое регулярное выражение для URL-адресов, у кого-нибудь есть что-то удобное, что хорошо работает? Я не нашел его с классами проверки структуры zend и видел несколько реализаций.

благодаря

Я использовал это в нескольких проектах, я не верю, что у меня возникли проблемы, но я уверен, что это не является исчерпывающим:

$text = preg_replace( '#((https?|ftp)://(\S*?\.\S*?))([\s)\[\]{},;"\':<]|\.\s|$)#i', "'<a href=\"$1\" target=\"_blank\">$3</a>$4'", $text ); 

Большая часть случайного мусора в конце – это дело с такими ситуациями, как http://domain.com. в предложении (во избежание соответствия периода ожидания). Я уверен, что он может быть очищен, но так как это сработало. Я более или менее просто скопировал его из проекта в проект.

Используйте filter_var() чтобы проверить, является ли строка URL-адресом или нет:

 var_dump(filter_var('example.com', FILTER_VALIDATE_URL)); 

Плохая практика – использовать регулярные выражения, когда это не необходимо.

EDIT : будьте осторожны, это решение не является безопасным для Unicode, а не безопасным для XSS. Если вам нужна сложная проверка, возможно, лучше посмотреть где-то еще.

Согласно руководству PHP – parse_url не должен использоваться для проверки URL-адреса.

К сожалению, похоже, что filter_var('example.com', FILTER_VALIDATE_URL) не работает лучше.

И parse_url() и filter_var() будут передавать неверные URL-адреса, такие как http://...

Поэтому в этом случае – регулярное выражение лучший метод.

На всякий случай вы хотите узнать, действительно ли URL-адрес:

 function url_exist($url){//se passar a URL existe $c=curl_init(); curl_setopt($c,CURLOPT_URL,$url); curl_setopt($c,CURLOPT_HEADER,1);//get the header curl_setopt($c,CURLOPT_NOBODY,1);//and *only* get the header curl_setopt($c,CURLOPT_RETURNTRANSFER,1);//get the response as a string from curl_exec(), rather than echoing it curl_setopt($c,CURLOPT_FRESH_CONNECT,1);//don't use a cached version of the url if(!curl_exec($c)){ //echo $url.' inexists'; return false; }else{ //echo $url.' exists'; return true; } //$httpcode=curl_getinfo($c,CURLINFO_HTTP_CODE); //return ($httpcode<400); } 

По словам Джона Грубера (Daring Fireball):

Regex:

 (?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][az]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”''])) 

используя в preg_match ():

 preg_match("/(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][az]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”'']))/", $url) 

Вот расширенный шаблон регулярного выражения (с комментариями):

 (?xi) \b ( # Capture 1: entire matched URL (?: https?:// # http or https protocol | # or www\d{0,3}[.] # "www.", "www1.", "www2." … "www999." | # or [a-z0-9.\-]+[.][az]{2,4}/ # looks like domain name followed by a slash ) (?: # One or more: [^\s()<>]+ # Run of non-space, non-()<> | # or \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels )+ (?: # End with: \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels | # or [^\s`!()\[\]{};:'".,<>?«»“”''] # not a space or one of these punct chars ) ) 

Для получения дополнительной информации см.: http://daringfireball.net/2010/07/improved_regex_for_matching_urls

Я не думаю, что использование регулярных выражений – это разумная вещь в этом случае. Невозможно сопоставить все возможности, и даже если вы это сделали, есть еще шанс, что URL просто не существует.

Вот очень простой способ проверить, действительно ли url существует и доступен для чтения:

 if (preg_match("#^https?://.+#", $link) and @fopen($link,"r")) echo "OK"; 

(если нет preg_match тогда это также подтвердит все имена файлов на вашем сервере)

  function validateURL($URL) { $pattern_1 = "/^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i"; $pattern_2 = "/^(www)((\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i"; if(preg_match($pattern_1, $URL) || preg_match($pattern_2, $URL)){ return true; } else{ return false; } } 

Я использовал этот с хорошим успехом – я не помню, откуда я его получил

 $pattern = "/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i"; 

И есть ваш ответ =) Постарайтесь сломать его, вы не сможете !!!

 function link_validate_url($text) { $LINK_DOMAINS = 'aero|arpa|asia|biz|com|cat|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|mobi|local'; $LINK_ICHARS_DOMAIN = (string) html_entity_decode(implode("", array( // @TODO completing letters ... "&#x00E6;", // æ "&#x00C6;", // Æ "&#x00C0;", // À "&#x00E0;", // à "&#x00C1;", // Á "&#x00E1;", // á "&#x00C2;", //  "&#x00E2;", // â "&#x00E5;", // å "&#x00C5;", // Å "&#x00E4;", // ä "&#x00C4;", // Ä "&#x00C7;", // Ç "&#x00E7;", // ç "&#x00D0;", // Ð "&#x00F0;", // ð "&#x00C8;", // È "&#x00E8;", // è "&#x00C9;", // É "&#x00E9;", // é "&#x00CA;", // Ê "&#x00EA;", // ê "&#x00CB;", // Ë "&#x00EB;", // ë "&#x00CE;", // Î "&#x00EE;", // î "&#x00CF;", // Ï "&#x00EF;", // ï "&#x00F8;", // ø "&#x00D8;", // Ø "&#x00F6;", // ö "&#x00D6;", // Ö "&#x00D4;", // Ô "&#x00F4;", // ô "&#x00D5;", // Õ "&#x00F5;", // õ "&#x0152;", // Œ "&#x0153;", // œ "&#x00FC;", // ü "&#x00DC;", // Ü "&#x00D9;", // Ù "&#x00F9;", // ù "&#x00DB;", // Û "&#x00FB;", // û "&#x0178;", // Ÿ "&#x00FF;", // ÿ "&#x00D1;", // Ñ "&#x00F1;", // ñ "&#x00FE;", // þ "&#x00DE;", // Þ "&#x00FD;", // ý "&#x00DD;", // Ý "&#x00BF;", // ¿ )), ENT_QUOTES, 'UTF-8'); $LINK_ICHARS = $LINK_ICHARS_DOMAIN . (string) html_entity_decode(implode("", array( "&#x00DF;", // ß )), ENT_QUOTES, 'UTF-8'); $allowed_protocols = array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal'); // Starting a parenthesis group with (?: means that it is grouped, but is not captured $protocol = '((?:'. implode("|", $allowed_protocols) .'):\/\/)'; $authentication = "(?:(?:(?:[\w\.\-\+!$&'\(\)*\+,;=" . $LINK_ICHARS . "]|%[0-9a-f]{2})+(?::(?:[\w". $LINK_ICHARS ."\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})*)?)?@)"; $domain = '(?:(?:[a-z0-9' . $LINK_ICHARS_DOMAIN . ']([a-z0-9'. $LINK_ICHARS_DOMAIN . '\-_\[\]])*)(\.(([a-z0-9' . $LINK_ICHARS_DOMAIN . '\-_\[\]])+\.)*('. $LINK_DOMAINS .'|[az]{2}))?)'; $ipv4 = '(?:[0-9]{1,3}(\.[0-9]{1,3}){3})'; $ipv6 = '(?:[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7})'; $port = '(?::([0-9]{1,5}))'; // Pattern specific to external links. $external_pattern = '/^'. $protocol .'?'. $authentication .'?('. $domain .'|'. $ipv4 .'|'. $ipv6 .' |localhost)'. $port .'?'; // Pattern specific to internal links. $internal_pattern = "/^(?:[a-z0-9". $LINK_ICHARS ."_\-+\[\]]+)"; $internal_pattern_file = "/^(?:[a-z0-9". $LINK_ICHARS ."_\-+\[\]\.]+)$/i"; $directories = "(?:\/[a-z0-9". $LINK_ICHARS ."_\-\.~+%=&,$'#!():;*@\[\]]*)*"; // Yes, four backslashes == a single backslash. $query = "(?:\/?\?([?a-z0-9". $LINK_ICHARS ."+_|\-\.~\/\\\\%=&,$'():;*@\[\]{} ]*))"; $anchor = "(?:#[a-z0-9". $LINK_ICHARS ."_\-\.~+%=&,$'():;*@\[\]\/\?]*)"; // The rest of the path for a standard URL. $end = $directories .'?'. $query .'?'. $anchor .'?'.'$/i'; $message_id = '[^@].*@'. $domain; $newsgroup_name = '(?:[0-9a-z+-]*\.)*[0-9a-z+-]*'; $news_pattern = '/^news:('. $newsgroup_name .'|'. $message_id .')$/i'; $user = '[a-zA-Z0-9'. $LINK_ICHARS .'_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\'\[\]]+'; $email_pattern = '/^mailto:'. $user .'@'.'(?:'. $domain .'|'. $ipv4 .'|'. $ipv6 .'|localhost)'. $query .'?$/'; if (strpos($text, '<front>') === 0) { return false; } if (in_array('mailto', $allowed_protocols) && preg_match($email_pattern, $text)) { return false; } if (in_array('news', $allowed_protocols) && preg_match($news_pattern, $text)) { return false; } if (preg_match($internal_pattern . $end, $text)) { return false; } if (preg_match($external_pattern . $end, $text)) { return false; } if (preg_match($internal_pattern_file, $text)) { return false; } return true; } 

Редактировать:
Как отмечалось выше, этот код был ОТКЛЮЧЕН с выпуском PHP 5.3.0 (2009-06-30) и должен быть соответствующим образом использован.


Только мои два цента, но я разработал эту функцию и использовал ее на некоторое время с успехом. Он хорошо документирован и разделен, поэтому вы можете легко его изменить.

 // Checks if string is a URL // @param string $url // @return bool function isURL($url = NULL) { if($url==NULL) return false; $protocol = '(http://|https://)'; $allowed = '([a-z0-9]([-a-z0-9]*[a-z0-9]+)?)'; $regex = "^". $protocol . // must include the protocol '(' . $allowed . '{1,63}\.)+'. // 1 or several sub domains with a max of 63 chars '[az]' . '{2,6}'; // followed by a TLD if(eregi($regex, $url)==true) return true; else return false; } 
 function is_valid_url ($url="") { if ($url=="") { $url=$this->url; } $url = @parse_url($url); if ( ! $url) { return false; } $url = array_map('trim', $url); $url['port'] = (!isset($url['port'])) ? 80 : (int)$url['port']; $path = (isset($url['path'])) ? $url['path'] : ''; if ($path == '') { $path = '/'; } $path .= ( isset ( $url['query'] ) ) ? "?$url[query]" : ''; if ( isset ( $url['host'] ) AND $url['host'] != gethostbyname ( $url['host'] ) ) { if ( PHP_VERSION >= 5 ) { $headers = get_headers("$url[scheme]://$url[host]:$url[port]$path"); } else { $fp = fsockopen($url['host'], $url['port'], $errno, $errstr, 30); if ( ! $fp ) { return false; } fputs($fp, "HEAD $path HTTP/1.1\r\nHost: $url[host]\r\n\r\n"); $headers = fread ( $fp, 128 ); fclose ( $fp ); } $headers = ( is_array ( $headers ) ) ? implode ( "\n", $headers ) : $headers; return ( bool ) preg_match ( '#^HTTP/.*\s+[(200|301|302)]+\s#i', $headers ); } return false; } 

Режик Питера не подходит мне по многим причинам. Он позволяет использовать все виды специальных символов в имени домена и не испытывает многого.

Функция Фрэнки выглядит хорошо для меня, и вы можете создать хорошее регулярное выражение из компонентов, если вы не хотите функции, например:

 ^(http://|https://)(([a-z0-9]([-a-z0-9]*[a-z0-9]+)?){1,63}\.)+[az]{2,6} 

Непроверенный, но я думаю, что это должно сработать.

Кроме того, ответ Оуэн не выглядит на 100%. Я взял доменную часть регулярного выражения и протестировал его на инструменте тестера Regex http://erik.eae.net/playground/regexp/regexp.html

Я поставил следующую строку:

 (\S*?\.\S*?) 

в разделе «regexp» и следующей строке:

-hello.com

в разделе «образец текста».

Результат разрешил минус-символ. Потому что \ S означает любой непространственный символ.

Обратите внимание, что регулярное выражение от Frankie обрабатывает минус, потому что у него есть эта часть для первого символа:

 [a-z0-9] 

Это не допустит минус или какой-либо другой особый характер.

Вот как я это сделал. Но я хочу упомянуть, что я не так уверен в регулярном выражении. Но это должно сработать ты 🙂

 $pattern = "#((http|https)://(\S*?\.\S*?))(\s|\;|\)|\]|\[|\{|\}|,|”|\"|'|:|\<|$|\.\s)#i"; $text = preg_replace_callback($pattern,function($m){ return "<a href=\"$m[1]\" target=\"_blank\">$m[1]</a>$m[4]"; }, $text); 

Таким образом вам не понадобится маркер eval на вашем шаблоне.

Надеюсь, поможет 🙂

Хорошо, так что это немного сложнее, чем простое регулярное выражение, но оно позволяет использовать разные типы URL-адресов.

Примеры:

Все, что должно быть отмечено как действительное.

 function is_valid_url($url) { // First check: is the url just a domain name? (allow a slash at the end) $_domain_regex = "|^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,})/?$|"; if (preg_match($_domain_regex, $url)) { return true; } // Second: Check if it's a url with a scheme and all $_regex = '#^([az][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][az]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))$#'; if (preg_match($_regex, $url, $matches)) { // pull out the domain name, and make sure that the domain is valid. $_parts = parse_url($url); if (!in_array($_parts['scheme'], array( 'http', 'https' ))) return false; // Check the domain using the regex, stops domains like "-example.com" passing through if (!preg_match($_domain_regex, $_parts['host'])) return false; // This domain looks pretty valid. Only way to check it now is to download it! return true; } return false; } 

Обратите внимание, что есть проверка in_array для протоколов, которые вы хотите разрешить (в настоящий момент только http и https находятся в этом списке).

 var_dump(is_valid_url('google.com')); // true var_dump(is_valid_url('google.com/')); // true var_dump(is_valid_url('http://google.com')); // true var_dump(is_valid_url('http://google.com/')); // true var_dump(is_valid_url('https://google.com')); // true 

Вдохновленный в этом вопросе .NET StackOverflow, и в этой ссылочной статье из этого вопроса есть этот валидатор URI (URI означает, что он проверяет как URL, так и URN).

 if( ! preg_match( "/^([az][a-z0-9+.-]*):(?:\\/\\/((?:(?=((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*))(\\3)@)?(?=(\\[[0-9A-F:.]{2,}\\]|(?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*))\\5(?::(?=(\\d*))\\6)?)(\\/(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/]|%[0-9A-F]{2})*))\\8)?|(\\/?(?!\\/)(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/]|%[0-9A-F]{2})*))\\10)?)(?:\\?(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9A-F]{2})*))\\11)?(?:#(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9A-F]{2})*))\\12)?$/i", $uri ) ) { throw new \RuntimeException( "URI has not a valid format." ); } 

Я успешно проверил эту функцию внутри объекта ValueObject, который я назвал Uri и тестировал UriTest .

UriTest.php (содержит допустимые и недействительные случаи как для URL-адресов, так и для URN)

 <?php declare( strict_types = 1 ); namespace XaviMontero\ThrasherPortage\Tests\Tour; use XaviMontero\ThrasherPortage\Tour\Uri; class UriTest extends \PHPUnit_Framework_TestCase { private $sut; public function testCreationIsOfProperClassWhenUriIsValid() { $sut = new Uri( 'http://example.com' ); $this->assertInstanceOf( 'XaviMontero\\ThrasherPortage\\Tour\\Uri', $sut ); } /** * @dataProvider urlIsValidProvider * @dataProvider urnIsValidProvider */ public function testGetUriAsStringWhenUriIsValid( string $uri ) { $sut = new Uri( $uri ); $actual = $sut->getUriAsString(); $this->assertInternalType( 'string', $actual ); $this->assertEquals( $uri, $actual ); } public function urlIsValidProvider() { return [ [ 'http://example-server' ], [ 'http://example.com' ], [ 'http://example.com/' ], [ 'http://subdomain.example.com/path/?parameter1=value1&parameter2=value2' ], [ 'random-protocol://example.com' ], [ 'http://example.com:80' ], [ 'http://example.com?no-path-separator' ], [ 'http://example.com/pa%20th/' ], [ 'ftp://example.org/resource.txt' ], [ 'file://../../../relative/path/needs/protocol/resource.txt' ], [ 'http://example.com/#one-fragment' ], [ 'http://example.edu:8080#one-fragment' ], ]; } public function urnIsValidProvider() { return [ [ 'urn:isbn:0-486-27557-4' ], [ 'urn:example:mammal:monotreme:echidna' ], [ 'urn:mpeg:mpeg7:schema:2001' ], [ 'urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66' ], [ 'rare-urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66' ], [ 'urn:FOO:a123,456' ] ]; } /** * @dataProvider urlIsNotValidProvider * @dataProvider urnIsNotValidProvider */ public function testCreationThrowsExceptionWhenUriIsNotValid( string $uri ) { $this->expectException( 'RuntimeException' ); $this->sut = new Uri( $uri ); } public function urlIsNotValidProvider() { return [ [ 'only-text' ], [ 'http//missing.colon.example.com/path/?parameter1=value1&parameter2=value2' ], [ 'missing.protocol.example.com/path/' ], [ 'http://example.com\\bad-separator' ], [ 'http://example.com|bad-separator' ], [ 'ht tp://example.com' ], [ 'http://exampl e.com' ], [ 'http://example.com/pa th/' ], [ '../../../relative/path/needs/protocol/resource.txt' ], [ 'http://example.com/#two-fragments#not-allowed' ], [ 'http://example.edu:portMustBeANumber#one-fragment' ], ]; } public function urnIsNotValidProvider() { return [ [ 'urn:mpeg:mpeg7:sch ema:2001' ], [ 'urn|mpeg:mpeg7:schema:2001' ], [ 'urn?mpeg:mpeg7:schema:2001' ], [ 'urn%mpeg:mpeg7:schema:2001' ], [ 'urn#mpeg:mpeg7:schema:2001' ], ]; } } 

Uri.php (объект Value)

 <?php declare( strict_types = 1 ); namespace XaviMontero\ThrasherPortage\Tour; class Uri { /** @var string */ private $uri; public function __construct( string $uri ) { $this->assertUriIsCorrect( $uri ); $this->uri = $uri; } public function getUriAsString() { return $this->uri; } private function assertUriIsCorrect( string $uri ) { // https://stackoverflow.com/questions/30847/regex-to-validate-uris // http://snipplr.com/view/6889/regular-expressions-for-uri-validationparsing/ if( ! preg_match( "/^([az][a-z0-9+.-]*):(?:\\/\\/((?:(?=((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*))(\\3)@)?(?=(\\[[0-9A-F:.]{2,}\\]|(?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*))\\5(?::(?=(\\d*))\\6)?)(\\/(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/]|%[0-9A-F]{2})*))\\8)?|(\\/?(?!\\/)(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/]|%[0-9A-F]{2})*))\\10)?)(?:\\?(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9A-F]{2})*))\\11)?(?:#(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9A-F]{2})*))\\12)?$/i", $uri ) ) { throw new \RuntimeException( "URI has not a valid format." ); } } } 

Запуск UnitTests

В 46 тестах содержится 65 утверждений. Предупреждение: есть 2 поставщика данных для действительных и еще 2 для недопустимых выражений. Один для URL, а другой для URN. Если вы используете версию PhpUnit версии v5.6 * или ранее, вам необходимо присоединиться к двум поставщикам данных в один.

 xavi@bromo:~/custom_www/hello-trip/mutant-migrant$ vendor/bin/phpunit PHPUnit 5.7.3 by Sebastian Bergmann and contributors. .............................................. 46 / 46 (100%) Time: 82 ms, Memory: 4.00MB OK (46 tests, 65 assertions) 

Кодовый охват

В этом примере URI-проверки есть 100% кода.

Вот простой класс для проверки URL-адресов с помощью RegEx, а затем перекрестные ссылки на домен с помощью популярных RBL (Realtime Blackhole Lists) серверов:

Установка:

 require 'URLValidation.php'; 

Применение:

 require 'URLValidation.php'; $urlVal = new UrlValidation(); //Create Object Instance 

Добавьте URL-адрес в качестве параметра метода domain() и проверьте возврат.

 $urlArray = ['http://www.bokranzr.com/test.php?test=foo&test=dfdf', 'https://en-gb.facebook.com', 'https://www.google.com']; foreach ($urlArray as $k=>$v) { echo var_dump($urlVal->domain($v)) . ' URL: ' . $v . '<br>'; } 

Вывод:

 bool(false) URL: http://www.bokranzr.com/test.php?test=foo&test=dfdf bool(true) URL: https://en-gb.facebook.com bool(true) URL: https://www.google.com 

Как вы можете видеть выше, http://www.bokranzr.com указан как вредоносный веб-сайт через RBL, поэтому домен был возвращен как ложный.

Я нашел, что это наиболее полезно для сопоставления URL-адреса.

 ^(https?:\/\/)?([\da-z\.-]+)\.([az\.]{2,6})([\/\w \.-]*)*\/?$ 

Для этого есть встроенная функция PHP:

 $url = 'http://www.yoururl.co.uk/sub1/sub2/?param=1&param2/'; if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { // Wrong } else { // Valid } 

Возвращает отфильтрованные данные или FALSE, если фильтр не работает.

Проверьте это здесь