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 ... "æ", // æ "Æ", // Æ "À", // À "à", // à "Á", // Á "á", // á "Â", //  "â", // â "å", // å "Å", // Å "ä", // ä "Ä", // Ä "Ç", // Ç "ç", // ç "Ð", // Ð "ð", // ð "È", // È "è", // è "É", // É "é", // é "Ê", // Ê "ê", // ê "Ë", // Ë "ë", // ë "Î", // Î "î", // î "Ï", // Ï "ï", // ï "ø", // ø "Ø", // Ø "ö", // ö "Ö", // Ö "Ô", // Ô "ô", // ô "Õ", // Õ "õ", // õ "Œ", // Œ "œ", // œ "ü", // ü "Ü", // Ü "Ù", // Ù "ù", // ù "Û", // Û "û", // û "Ÿ", // Ÿ "ÿ", // ÿ "Ñ", // Ñ "ñ", // ñ "þ", // þ "Þ", // Þ "ý", // ý "Ý", // Ý "¿", // ¿ )), ENT_QUOTES, 'UTF-8'); $LINK_ICHARS = $LINK_ICHARS_DOMAIN . (string) html_entity_decode(implode("", array( "ß", // ß )), 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。
例子:
- google.com
- http://www.microsoft.com/
- http://www.yahoo.com/
- https://www.bandcamp.com/artist/#!someone-special !
所有应该被标记为有效的。
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¶meter2=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¶meter2=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¶m2/'; if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { // Wrong } else { // Valid }
返回过滤的数据,如果filter失败,则返回FALSE。
在这里检查