我使用 filter_var($url, FILTER_VALIDATE_URL)
发现的问题是它在$url = "http://x";
时返回 true
不需要顶级域名。我如何解决这个问题,所以需要一个顶级域名?
对于 TLD 验证,您需要使用公共后缀列表运行的库。这里有两个不同的解决方案给你。
首先是TLDDatabase,从技术上讲,它只是真正的TLD数据库。
$store = new LayerShifter'TLDDatabase'Store();
$store->isICCAN('com'); // returns true
$store->isICCAN('co.uk'); // returns true
$store->isICCAN('example'); // returns false
如果您需要更智能的解决方案,我推荐TLDExtract。它是可以用作验证器的域解析器。
$extract = new LayerShifter'TLDExtract'Extract();
$extract->setExtractionMode(Extract::MODE_ALLOW_ICCAN);
# For domain 'shop.github.com'
$result = $extract->parse('shop.github.com');
$result->getRegistrableDomain(); // will return 'github.com'
$result->getSuffix(); // will return 'com'
# For domain 'shop.github.co.uk'
$result = $extract->parse('http://shop.github.co.uk');
$result->getRegistrableDomain(); // will return 'github.co.uk'
$result->getSuffix(); // will return 'co.uk'
# For domain 'example.example'
$result = $extract->parse('https://example.example');
$result->getRegistrableDomain(); // will return NULL
$result->getSuffix(); // will return NULL
# For domain 'localhost'
$result = $extract->parse('localhost');
$result->getRegistrableDomain(); // will return NULL
$result->getSuffix(); // will return NULL
任何
以方案开头的 URI,如 http://
,之后包含有效的 URI 字符,根据 RFC 3986 中的官方 URI 规范是有效的:
每个 URI 都以方案名称开头,如第 3.1 节中所定义,该名称是指在该方案中分配标识符的规范。 因此,URI 语法是一个联合且可扩展的命名系统,其中每个方案的规范可能会进一步限制使用该方案的标识符的语法和语义。
FILTER_VALIDATE_URL所做的是正确的。
http://localhost
或http://x
是完全有效的 URI。
如果您确实想要要求和验证 TLD,则必须使用包含所有有效 TLD 的白名单。因为每个 TLD 在子域、二级域等的计数上都不同。有顶级域、二级域和子域。从技术上讲,除 TLD 之外的所有内容都是子域。
您可以在此处找到维护的 TLD 列表:
- https://publicsuffix.org/list/effective_tld_names.dat
- https://publicsuffix.org/
对于 PHP 实现(列表解析器(:
- http://toby.ink/blog/2007/07/19/php-domain-class/
- https://stackoverflow.com/a/9917859/1163786
从我的角度来看,这个问题不能通过"正则表达式"或"主机名扫描中的点数"来解决。一个例外:如果验证器的使用范围仅限于几个已知的 URL,那么您可以使用这些策略来解决此问题。
有趣的是这里建议的MX记录检查:https://stackoverflow.com/a/14688913/1163786
引用
- 为什么Symfony2 URL验证器跳过TLD?
- 从网址获取子域名