Obtener el nombre de dominio(no el subdominio) en php
regex domain-name (11)
Aquí hay una función que escribí para obtener el dominio sin subdominio (s), independientemente de si el dominio usa un ccTLD o un nuevo estilo de TLD largo, etc. No hay búsqueda ni una gran variedad de TLD conocidos, y no hay expresiones regulares. . Puede ser mucho más corto usando el operador ternario y el anidamiento, pero lo amplié para facilitar la lectura.
// 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;
}
Tengo una URL que puede ser cualquiera de los siguientes formatos:
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
Esencialmente, necesito poder hacer coincidir cualquier URL normal. ¿Cómo puedo extraer example.com
(o .net, sea lo que sea el tld. Necesito que funcione con cualquier TLD) de todos estos a través de una sola expresión regular?
Aquí hay uno que funciona para todos los dominios, incluidos aquellos con dominios de segundo nivel como "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));
}
}
Parece que hay una pregunta duplicada aquí: delete-subdomain-from-url-string-if-subdomain-is-found
Bueno, puedes usar parse_url
para obtener el host:
$info = parse_url($url);
$host = $info[''host''];
Entonces, puedes hacer algunas cosas elegantes para obtener solo el TLD y el Anfitrión
$host_names = explode(".", $host);
$bottom_host_name = $host_names[count($host_names)-2] . "." . $host_names[count($host_names)-1];
No es muy elegante, pero debería funcionar.
Si quieres una explicación, aquí va:
Primero tomamos todo entre el esquema ( http://
, etc), usando las capacidades de parse_url
para ... bueno ... analizar las URL. :)
Luego tomamos el nombre de host y lo separamos en una matriz en función de dónde caen los períodos, por lo que test.world.hello.myname
se convertiría en:
array("test", "world", "hello", "myname");
Después de eso, tomamos la cantidad de elementos en la matriz (4).
Luego, restamos 2 de él para obtener la penúltima cadena (el nombre de host, o example
, en su ejemplo)
Luego, restamos 1 de él para obtener la última cadena (porque las teclas de matriz comienzan en 0), también conocido como TLD
Luego combinamos esas dos partes con un punto, y usted tiene su nombre de host base.
Creo que la mejor manera de manejar este problema es:
$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]";
}
Hay dos formas de extraer el subdominio de un host:
El primer método que es más preciso es usar una base de datos de tlds (como https://publicsuffix.org/list/public_suffix_list.dat ) y hacer coincidir el dominio con ella. Esto es un poco pesado en algunos casos. Hay algunas clases de PHP para usarlo como php-domain-parser y TLDExtract .
La segunda forma no es tan precisa como la primera, pero es muy rápida y puede dar la respuesta correcta en muchos casos, escribí esta función para ello:
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 ); }
Ejemplo:
print_r(get_domaininfo(''http://mysubdomain.domain.co.uk/index.php''));
Devoluciones:
Array ( [protocol] => https [subdomain] => mysubdomain [domain] => domain.co.uk [host] => domain [tld] => co.uk )
Mi solución en https://gist.github.com/pocesar/5366899
y las pruebas están aquí http://codepad.viper-7.com/GAh1tP
Funciona con cualquier TLD y patrones de subdominio horribles (hasta 3 subdominios).
Hay una prueba incluida con muchos nombres de dominio.
No pegará la función aquí debido a la sangría extraña para el código en (podría tener bloques de código cercados como github)
No es posible obtener el nombre de dominio sin utilizar una lista de TLD para comparar, ya que existen muchos casos con la misma estructura y longitud:
- www.db.de (Subdominio) versus bbc.co.uk (Dominio)
- big.uk.com (SLD) versus www.uk.com (TLD)
La lista de sufijos públicos de Mozilla debería ser la mejor opción, ya que es utilizada por todos los principales navegadores :
https://publicsuffix.org/list/public_suffix_list.dat
Siéntase libre de usar mi función:
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 '''';
}
Lo que hace especial:
- acepta todas las entradas como URL, nombres de host o dominios con o sin esquema
- la lista se descarga fila por fila para evitar el alto uso de memoria
- crea un nuevo archivo por TLD en una carpeta de caché, por lo que
get_domain()
solo necesita verificar a través defile_exists()
si existe, por lo que no necesita incluir una gran base de datos en cada solicitud como TLDExtract hace. - la lista se actualizará automáticamente cada 30 días
Prueba:
$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
''.com'',// .com
''.foobarfoo'',// empty
'''',// empty
false,// empty
'' '',// empty
1,// empty
''a'',// empty
);
Versión reciente con explicaciones (alemán):
http://www.programmierer-forum.de/domainnamen-ermitteln-t244185.htm
Recomiendo usar la biblioteca TLDExtract para todas las operaciones con nombre de dominio.
Simplemente prueba esto:
<?php
$host = $_SERVER[''HTTP_HOST''];
preg_match("/[^/.//]+/.[^/.//]+$/", $host, $matches);
echo "domain name is: {$matches[0]}/n";
?>
Tuve problemas con la solución proporcionada por pocesar. Cuando usaría, por ejemplo, subdomain.domain.nl, no devolvería domain.nl. En cambio, devolvería subdomain.domain.nl Otro problema era que domain.com.br devolvería com.br
No estoy seguro pero solucioné estos problemas con el siguiente código (espero que ayude a alguien, si es así soy un hombre feliz):
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);
}
$onlyHostName = implode(''.'', array_slice(explode(''.'', parse_url($link, PHP_URL_HOST)), -2));