PHPvalidation/正则expression式的URL

我一直在寻找一个简单的URL的正则expression式,有没有人有一个方便,运作良好? 我没有find一个与Zend框架validation类,并已经看到了几个实现。

谢谢

我在一些项目上使用了这个,我不相信我遇到了问题,但是我确定它不是详尽的:

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

大多数随机垃圾最后是处理像http://domain.com.这样的情况http://domain.com. 在一个句子中(为了避免跟踪期间匹配)。 我确信它可以被清理,但因为它的工作。 我已经或多或less地从项目复制到项目。

使用filter_var()函数来validationstring是否为URL:

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

不必要的时候使用正则expression式是不好的做法。

编辑 :小心,这个解决scheme不是unicode安全的,而不是XSS安全的。 如果你需要一个复杂的validation,也许最好去别的地方看看。

根据PHP手册 – parse_url 应该用来validation一个URL。

不幸的是, filter_var('example.com', FILTER_VALIDATE_URL)似乎没有更好的performance。

parse_url()filter_var()都会传递格式不正确的URL,比如http://...

因此在这种情况下 – 正则expression式更好的方法。

以防万一你想知道该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):

正则expression式:

 (?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) 

这里是扩展的正则expression式模式(带注释):

 (?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

我不认为在这种情况下使用正则expression式是一件聪明的事情。 不可能匹配所有的可能性,即使你做了,仍然有一个URL根本不存在的机会。

这里是一个非常简单的方法来testing如果url实际存在并且是可读的:

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

(如果没有preg_match那么这也将validation您的服务器上的所有文件名)

  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年6月30日)的发布被拒绝,应该相应地使用。


只是我的两分钱,但我已经开发了这个function,并成功地使用了一段时间。 它有很好的logging和分离,所以你可以很容易地改变它。

 // 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; } 

彼得的正则expression式由于很多原因并不适合我。 它允许在域名中的各种特殊字符,并没有太多的testing。

弗兰基的function看起来不错,你可以从组件中build立一个良好的正则expression式,如果你不想要一个函数,像这样:

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

未经testing,但我认为应该工作。

另外,欧文的答案也不是100%。 我采取了正则expression式的领域的一部分,并在正则expression式testing工具http://erik.eae.net/playground/regexp/regexp.html

我把下面一行:

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

在“正则expression式”部分和以下行中:

-hello.com

在“示例文本”部分下。

结果允许通过减号字符。 因为\ S意味着任何非空格字符。

请注意,Frankie的正则expression式处理减号,因为它包含第一个字符的这个部分:

 [a-z0-9] 

这将不允许减号或任何其他特殊字符。

这是我做到的。 但是我想指导一下,我对这个正则expression式并没有那么舒服。 但它应该工作你:)

 $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); 

这样你就不需要你的模式上的评价标记。

希望它有助于:)

好吧,所以这是一个简单的正则expression式更复杂一点,但它允许不同types的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问题 , 在这个引用的文章中,这个URIvalidation器(URI意味着它validationURL和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." ); } 

我已经成功地在一个名为Uri的ValueObject中对这个函数进行了unit testing,并由UriTest进行了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 Object)

 <?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个testing中有65个断言。 警告:有2个有效的数据提供者和2个无效的expression式。 一个是URL,另一个是URN。 如果您使用的是v5.6 *或更早版本的PhpUnit版本,则需要将两个数据提供者合并为一个。

 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%的代码覆盖率。

这里有一个使用RegEx进行URLvalidation的简单类,然后将这个域与stream行的RBL(实时黑名单)服务器进行交叉引用:

安装:

 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 

正如您在上面看到的,www.bokranzr.com被RBL列为恶意网站,所以域被返回为false。

我发现这是匹配url最有用的..

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

有一个PHP的本地function:

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

返回过滤的数据,如果filter失败,则返回FALSE。

在这里检查