J'ai une URL qui peut être l'un des formats suivants:
http://example.com
https://example.com
http://example.com/foo
http://example.com/foo/bar
www.example.com
example.com
foo.example.com
www.foo.example.com
foo.bar.example.com
http://foo.bar.example.com/foo/bar
example.net/foo/bar
Essentiellement, je dois pouvoir faire correspondre n'importe quelle URL normale. Comment puis-je extraire example.com
(ou .net, quel que soit le tld qui se trouve. J'ai besoin de cela pour fonctionner avec n'importe quel TLD.) De tous ceux-ci via une seule expression régulière?
Eh bien, vous pouvez utiliser parse_url
pour obtenir l'hôte:
$info = parse_url($url);
$Host = $info['Host'];
Ensuite, vous pouvez faire des choses fantaisistes pour n’obtenir que le TLD et l’hôte.
$Host_names = explode(".", $Host);
$bottom_Host_name = $Host_names[count($Host_names)-2] . "." . $Host_names[count($Host_names)-1];
Pas très élégant, mais devrait fonctionner.
Si vous voulez une explication, la voici:
Tout d’abord, nous saisissons tout entre le schéma (http://
, etc.), en utilisant les capacités de parse_url
pour ... ainsi .... analyser les URL. :)
Ensuite, nous prenons le nom d'hôte et le séparons dans un tableau en fonction de l'endroit où les périodes se situent. test.world.hello.myname
deviendrait:
array("test", "world", "hello", "myname");
Après cela, nous prenons le nombre d'éléments dans le tableau (4).
Ensuite, on lui soustrait 2 pour obtenir l'avant-dernière chaîne (le nom d'hôte ou example
dans votre exemple)
Ensuite, on lui soustrait 1 pour obtenir la dernière chaîne (car les clés du tableau commencent à 0), également appelé TLD
Ensuite, nous combinons ces deux parties avec un point et vous avez votre nom d’hôte de base.
Ma solution dans https://Gist.github.com/pocesar/5366899
et les tests sont ici http://codepad.viper-7.com/GAh1tP
Cela fonctionne avec n'importe quel TLD et avec des modèles de sous-domaines hideux (jusqu'à 3 sous-domaines).
Un test est inclus avec de nombreux noms de domaine.
Ne collera pas la fonction ici à cause de l'indentation bizarre du code dans StackOverflow (aurait pu isoler des blocs de code comme github)
Il n'est pas possible d'obtenir le nom de domaine sans utiliser une liste de TLD avec laquelle comparer, car ils existent dans de nombreux cas avec complètement la même structure et la même longueur:
La liste des suffixes publics de Mozilla devrait être la meilleure option car elle est utilisée par tous les navigateurs major :
https://publicsuffix.org/list/public_suffix_list.dat
N'hésitez pas à utiliser ma fonction:
function tld_list($cache_dir=null) {
// we use "/tmp" if $cache_dir is not set
$cache_dir = isset($cache_dir) ? $cache_dir : sys_get_temp_dir();
$lock_dir = $cache_dir . '/public_suffix_list_lock/';
$list_dir = $cache_dir . '/public_suffix_list/';
// refresh list all 30 days
if (file_exists($list_dir) && @filemtime($list_dir) + 2592000 > time()) {
return $list_dir;
}
// use exclusive lock to avoid race conditions
if (!file_exists($lock_dir) && @mkdir($lock_dir)) {
// read from source
$list = @fopen('https://publicsuffix.org/list/public_suffix_list.dat', 'r');
if ($list) {
// the list is older than 30 days so delete everything first
if (file_exists($list_dir)) {
foreach (glob($list_dir . '*') as $filename) {
unlink($filename);
}
rmdir($list_dir);
}
// now set list directory with new timestamp
mkdir($list_dir);
// read line-by-line to avoid high memory usage
while ($line = fgets($list)) {
// skip comments and empty lines
if ($line[0] == '/' || !$line) {
continue;
}
// remove wildcard
if ($line[0] . $line[1] == '*.') {
$line = substr($line, 2);
}
// remove exclamation mark
if ($line[0] == '!') {
$line = substr($line, 1);
}
// reverse TLD and remove linebreak
$line = implode('.', array_reverse(explode('.', (trim($line)))));
// we split the TLD list to reduce memory usage
touch($list_dir . $line);
}
fclose($list);
}
@rmdir($lock_dir);
}
// repair locks (should never happen)
if (file_exists($lock_dir) && mt_Rand(0, 100) == 0 && @filemtime($lock_dir) + 86400 < time()) {
@rmdir($lock_dir);
}
return $list_dir;
}
function get_domain($url=null) {
// obtain location of public suffix list
$tld_dir = tld_list();
// no url = our own Host
$url = isset($url) ? $url : $_SERVER['SERVER_NAME'];
// add missing scheme ftp:// http:// ftps:// https://
$url = !isset($url[5]) || ($url[3] != ':' && $url[4] != ':' && $url[5] != ':') ? 'http://' . $url : $url;
// remove "/path/file.html", "/:80", etc.
$url = parse_url($url, PHP_URL_Host);
// replace absolute domain name by relative (http://www.dns-sd.org/TrailingDotsInDomainNames.html)
$url = trim($url, '.');
// check if TLD exists
$url = explode('.', $url);
$parts = array_reverse($url);
foreach ($parts as $key => $part) {
$tld = implode('.', $parts);
if (file_exists($tld_dir . $tld)) {
return !$key ? '' : implode('.', array_slice($url, $key - 1));
}
// remove last part
array_pop($parts);
}
return '';
}
Qu'est-ce que cela rend spécial:
get_domain()
n'a besoin de vérifier que file_exists()
s'il existe, il n'a donc pas besoin d'inclure une énorme base de données dans chaque requête comme le fait TLDExtract .Tester:
$urls = array(
'http://www.example.com',// example.com
'http://subdomain.example.com',// example.com
'http://www.example.uk.com',// example.uk.com
'http://www.example.co.uk',// example.co.uk
'http://www.example.com.ac',// example.com.ac
'http://example.com.ac',// example.com.ac
'http://www.example.accident-prevention.aero',// example.accident-prevention.aero
'http://www.example.sub.ar',// sub.ar
'http://www.congresodelalengua3.ar',// congresodelalengua3.ar
'http://congresodelalengua3.ar',// congresodelalengua3.ar
'http://www.example.pvt.k12.ma.us',// example.pvt.k12.ma.us
'http://www.example.lib.wy.us',// example.lib.wy.us
'com',// empty
'.com',// empty
'http://big.uk.com',// big.uk.com
'uk.com',// empty
'www.uk.com',// www.uk.com
'.uk.com',// empty
'stackoverflow.com',// stackoverflow.com
'.foobarfoo',// empty
'',// empty
false,// empty
' ',// empty
1,// empty
'a',// empty
);
Version récente avec explications (allemand):
http://www.programmierer-forum.de/domainnamen-ermitteln-t244185.htm
$onlyHostName = implode('.', array_slice(explode('.', parse_url($link, PHP_URL_Host)), -2));
Je pense que la meilleure façon de gérer ce problème est:
$second_level_domains_regex = '/\.asn\.au$|\.com\.au$|\.net\.au$|\.id\.au$|\.org\.au$|\.edu\.au$|\.gov\.au$|\.csiro\.au$|\.act\.au$|\.nsw\.au$|\.nt\.au$|\.qld\.au$|\.sa\.au$|\.tas\.au$|\.vic\.au$|\.wa\.au$|\.co\.at$|\.or\.at$|\.priv\.at$|\.ac\.at$|\.avocat\.fr$|\.aeroport\.fr$|\.veterinaire\.fr$|\.co\.hu$|\.film\.hu$|\.lakas\.hu$|\.ingatlan\.hu$|\.sport\.hu$|\.hotel\.hu$|\.ac\.nz$|\.co\.nz$|\.geek\.nz$|\.gen\.nz$|\.kiwi\.nz$|\.maori\.nz$|\.net\.nz$|\.org\.nz$|\.school\.nz$|\.cri\.nz$|\.govt\.nz$|\.health\.nz$|\.iwi\.nz$|\.mil\.nz$|\.parliament\.nz$|\.ac\.za$|\.gov\.za$|\.law\.za$|\.mil\.za$|\.nom\.za$|\.school\.za$|\.net\.za$|\.co\.uk$|\.org\.uk$|\.me\.uk$|\.ltd\.uk$|\.plc\.uk$|\.net\.uk$|\.sch\.uk$|\.ac\.uk$|\.gov\.uk$|\.mod\.uk$|\.mil\.uk$|\.nhs\.uk$|\.police\.uk$/';
$domain = $_SERVER['HTTP_Host'];
$domain = explode('.', $domain);
$domain = array_reverse($domain);
if (preg_match($second_level_domains_regex, $_SERVER['HTTP_Host']) {
$domain = "$domain[2].$domain[1].$domain[0]";
} else {
$domain = "$domain[1].$domain[0]";
}
Je recommande d'utiliser TLDExtract library pour toutes les opérations avec un nom de domaine.
Il existe deux manières d'extraire un sous-domaine d'un hôte:
La première méthode la plus précise consiste à utiliser une base de données de tld (comme public_suffix_list.dat ) et à faire correspondre le domaine à. C'est un peu lourd dans certains cas. Il existe certaines classes PHP pour l'utiliser comme php-domain-parser et TLDExtract .
La seconde façon n’est pas aussi précise que la première, mais elle est très rapide et peut donner la bonne réponse dans de nombreux cas, j’ai écrit cette fonction pour cela:
function get_domaininfo($url) {
// regex can be replaced with parse_url
preg_match("/^(https|http|ftp):\/\/(.*?)\//", "$url/" , $matches);
$parts = explode(".", $matches[2]);
$tld = array_pop($parts);
$Host = array_pop($parts);
if ( strlen($tld) == 2 && strlen($Host) <= 3 ) {
$tld = "$Host.$tld";
$Host = array_pop($parts);
}
return array(
'protocol' => $matches[1],
'subdomain' => implode(".", $parts),
'domain' => "$Host.$tld",
'Host'=>$Host,'tld'=>$tld
);
}
Exemple:
print_r(get_domaininfo('http://mysubdomain.domain.co.uk/index.php'));
Résultats:
Array
(
[protocol] => https
[subdomain] => mysubdomain
[domain] => domain.co.uk
[Host] => domain
[tld] => co.uk
)
Voici une fonction que j'ai écrite pour récupérer le domaine sans sous-domaine (s), que le domaine utilise un ccTLD ou un nouveau style long TLD, etc. . Il peut être beaucoup plus court en utilisant l'opérateur ternaire et l'imbrication, mais je l'ai développé pour la lisibilité.
// Per Wikipedia: "All ASCII ccTLD identifiers are two letters long,
// and all two-letter top-level domains are ccTLDs."
function topDomainFromURL($url) {
$url_parts = parse_url($url);
$domain_parts = explode('.', $url_parts['Host']);
if (strlen(end($domain_parts)) == 2 ) {
// ccTLD here, get last three parts
$top_domain_parts = array_slice($domain_parts, -3);
} else {
$top_domain_parts = array_slice($domain_parts, -2);
}
$top_domain = implode('.', $top_domain_parts);
return $top_domain;
}
En voici un qui fonctionne pour tous les domaines, y compris ceux avec des domaines de second niveau comme "co.uk"
function strip_subdomains($url){
# credits to gavingmiller for maintaining this list
$second_level_domains = file_get_contents("https://raw.githubusercontent.com/gavingmiller/second-level-domains/master/SLDs.csv");
# presume sld first ...
$possible_sld = implode('.', array_slice(explode('.', $url), -2));
# and then verify it
if (strpos($second_level_domains, $possible_sld)){
return implode('.', array_slice(explode('.', $url), -3));
} else {
return implode('.', array_slice(explode('.', $url), -2));
}
}
On dirait qu'il y a une question en double ici: delete-subdomain-from-url-string-if-subdomain-is-found
J'ai eu des problèmes avec la solution fournie par pocesar . Lorsque j'utiliserais par exemple subdomain.domain.nl, il ne renverrait pas domain.nl. Au lieu de cela, il renverrait subdomain.domain.nl Un autre problème était que domain.com.br renverrait com.br
Je ne suis pas sûr mais j'ai corrigé ces problèmes avec le code suivant (j'espère que cela aidera quelqu'un, si c'est le cas, je suis un homme heureux):
function get_domain($domain, $debug = false){
$original = $domain = strtolower($domain);
if (filter_var($domain, FILTER_VALIDATE_IP)) {
return $domain;
}
$debug ? print('<strong style="color:green">»</strong> Parsing: '.$original) : false;
$arr = array_slice(array_filter(explode('.', $domain, 4), function($value){
return $value !== 'www';
}), 0); //rebuild array indexes
if (count($arr) > 2){
$count = count($arr);
$_sub = explode('.', $count === 4 ? $arr[3] : $arr[2]);
$debug ? print(" (parts count: {$count})") : false;
if (count($_sub) === 2){ // two level TLD
$removed = array_shift($arr);
if ($count === 4){ // got a subdomain acting as a domain
$removed = array_shift($arr);
}
$debug ? print("<br>\n" . '[*] Two level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false;
}elseif (count($_sub) === 1){ // one level TLD
$removed = array_shift($arr); //remove the subdomain
if (strlen($arr[0]) === 2 && $count === 3){ // TLD domain must be 2 letters
array_unshift($arr, $removed);
}elseif(strlen($arr[0]) === 3 && $count === 3){
array_unshift($arr, $removed);
}else{
// non country TLD according to IANA
$tlds = array(
'aero',
'arpa',
'asia',
'biz',
'cat',
'com',
'coop',
'edu',
'gov',
'info',
'jobs',
'mil',
'mobi',
'museum',
'name',
'net',
'org',
'post',
'pro',
'tel',
'travel',
'xxx',
);
if (count($arr) > 2 && in_array($_sub[0], $tlds) !== false){ //special TLD don't have a country
array_shift($arr);
}
}
$debug ? print("<br>\n" .'[*] One level TLD: <strong>'.join('.', $_sub).'</strong> ') : false;
}else{ // more than 3 levels, something is wrong
for ($i = count($_sub); $i > 1; $i--){
$removed = array_shift($arr);
}
$debug ? print("<br>\n" . '[*] Three level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false;
}
}elseif (count($arr) === 2){
$arr0 = array_shift($arr);
if (strpos(join('.', $arr), '.') === false && in_array($arr[0], array('localhost','test','invalid')) === false){ // not a reserved domain
$debug ? print("<br>\n" .'Seems invalid domain: <strong>'.join('.', $arr).'</strong> re-adding: <strong>'.$arr0.'</strong> ') : false;
// seems invalid domain, restore it
array_unshift($arr, $arr0);
}
}
$debug ? print("<br>\n".'<strong style="color:gray">«</strong> Done parsing: <span style="color:red">' . $original . '</span> as <span style="color:blue">'. join('.', $arr) ."</span><br>\n") : false;
return join('.', $arr);
}
Essayez simplement ceci:
preg_match('/(www.)?([^.]+\.[^.]+)$/', $yourHost, $matches);
echo "domain name is: {$matches[0]}\n";
cela fonctionne pour la majorité des domaines.