如何正确的使用正则表达式验证网址链接

  • 内容
  • 评论
  • 相关

之前折腾了一个短网址程序,过程挺顺利的,唯一就是在验证网址这一步卡了壳,花费了整个过程的一大半时间,最终经过一番搜索、折腾和测试,才算找到一个完美的解决方案。

在短网址程序中,验证网址无疑是很重要的。且不说各种安全问题,就是一些「浑水摸鱼」的网址占据大量的短网址都非常让人头疼。

提到验证网址,我相信大多数人第一时间反应出来的都是正则表达式,的确,这很科学,但何奈自己正则太渣,平时想要匹配一段复杂的 HTML 都要反复调试半天,更何况我连网址的结构都不能完全梳理清楚,所以还是绕过它,咱能不用就别自己找不自在。

esc_url_raw()

想了想,平时的开发中,验证网址都是直接调的 WordPress 核心给的函数,所以考虑直接把 WordPress 的 esc_url_raw() 函数搬过来,省时又省力。

事实证明我实在是太天真了。复制的过程中我发现它牵连了太多其它的模块,一个模块勾搭一点,想要完全搬过来实在是太过庞大,到最后我都分不清楚哪是哪了,你到底跟几个模块有关系啊?照这样搞下去,还不如直接基于 WordPress 算了。

再想想,其实这个函数的本意是「过滤」网址,尽可能的把不合法的网站变成合法的:没有协议?咱给它添上协议;不合法字符?直接干掉它,别脏了眼;浏览器或服务器根本没法解析?我管你那个?

它只是在把网址变得不出现根本错误就没事,别让你这一个小小的网址把我整个程序都给搞乱了就行,虽然在 WordPress 的环境中,大多数情况这样就够了,但在我们的短网址程序中,这显然不够严格

filter_var()

自己的想法还没能运行一次,就已经胎死腹中了,没办法,只好去求助万能的搜索引擎大人了。找了半天,看着 Stack Overflow 上的各路大神的一个个长篇大论(其实我现在也好像在长篇大论……),最终决定选择大多数人推荐的 filter_var() 函数试试,其实我以前也听说过这个东西,不过一直没什么机会实际使用下,也可以趁现在好好研究研究。

filter_var() 是 PHP 自带的一个函数,顾名思义,用来验证(过滤)变量,印象中应该不止可以验证 URL,什么邮箱之类的也都玩得转。

如果要用它验证网址,只需要这样:

if ( filter_var( $url, FILTER_VALIDATE_URL ) !== false )
	echo '可以,这很网址。'; // 验证成功
else
	echo '这是什么鬼?'; // 验证失败,返回 FALSE。

非常的简单,不过要注意,失败时返回 FALSE 并不意味着你可以直接用 if ( !filter_var( ... ) ) 这样的形式来 草率的判定网址不合法,因为它成功时会返回过滤的内容,而不是 TRUE,如果内容是 '0' 那你怎么办?虽然我们验证网址不可能出现这种情况,但这种习惯还是不要养成的为好,我年轻的时候就曾自作聪明的修改例子代码,在 strpos() 函数上栽跟头喽(其实我现在还经常自作聪明,并且为其付出代价,然后继续自作聪明)。

OK,经过一番测试,感觉上是没什么大问题了,所以把代码交了出去。过一会,别人小小测试一下,发现类似百度和谷歌的搜索结果的网址无法验证通过,又是一波紧急研究。

结论是 filter_var() 会认定所有带有中文的网址不合法,虽然可以通过转义中文部分的方法强行通过验证,但我之前已经为了避免重复所以取消了所有 URL 的转义啊,这想想感觉会弄得好混乱。而且过会又发现,即使是在没有中文的情况下,它对锚点(就是网址中「#」后边的那一堆)链接的支持还有些迷之问题,所以果断抛弃之。

Happy End

好,重头戏来了。

无奈,只好继续去搜索解法,偶然间看到了一个网址,进去之后感觉进入了天堂一样,相见恨晚,里边简直有我现在所急需的一切。

如何正确的使用正则表达式验证网址链接

这个页面的作者搜集了很多不同的正则表达式,并用它们对一系列网址进行测试,有的网址需要验证通过,也有恰恰相反的。

最终,只有一位叫 Diego Perini 的选手通过了比赛,完美的达到了所有要求,他的正则表达式足足有 502 个字符,打眼一瞧非常的吓人,反正我是完全看不懂……

为了方便使用,我把它的正则表达式封装成了一个 PHP 函数,想要使用的话,直接调用就行了:

/**
 * 检测网址是否合法
 *
 * @link https://www.bgbk.org/regex-url/ 
 * @link https://gist.github.com/dperini/729294
 *
 * @param string $url 需要检测的网址。
 * @return bool 是否为一个合法的网址。
 */
function is_url( $url ) {
	if ( !trim( $url ) )
		return false;

	if ( strlen( $url ) < 10 )
		return false;

	$pattern = '_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s]*)?$_iuS';

	$result = preg_match( $pattern, $url );
	$result = (bool) $result;

	return $result;
}

就像这样:

if ( is_url( $url ) )
	echo '可以,这很网址。'; // 验证成功
else
	echo '这是什么鬼?'; // 验证失败

虽然我对正则表达式理解不多,但它看长度就显然是巨耗费性能的,所以我先对网址进行了一些简单的预判断,希望可以减少服务器的压力。

另外,如果你想了解它的原理,或者想在 JavaScript 中使用这个正则表达式的话,可以参考作者在 GitHub 上的一个页面,里边有原版的 JavaScript 版本,并且附有大量注释,方便你学习和使用!

除此之外,你可以点击下载一份所有测试的 URLs 列表,可以亲自尝试各种正则表达式,或者验证你的方法,如果找到了更好的办法,一定要在评论中分享给大家哦!

附赠资料

最后附赠一个也是偶然发现的页面,里边用非常清晰的方式详细的介绍了网址的构成规则,感兴趣的同学可以点击此处去参观参观。

如何正确的使用正则表达式验证网址链接

在 javaScript 中,用 window.location. 加上演示模型里提到的属性名,就可以获取到当前页面网址的那一部分啦,比如 window.location.protocol,一般就会返回「http:」或「https:」。

当然,上边的网址只是展示了网址基本的构成元素,如果你想了解的更多,比如网址每个部分不能出现的字符啊、需要转义的文本啊,以及各种约定俗称的规则和注意事项,都可以在这里找到,非常完整哦!

评论

16条评论
  1. Gravatar 头像

    五卡 回复

    厉害,一点也看不懂

发表评论

电子邮件地址不会被公开。 必填项已用*标注