PHP函数获取URL的子域


107

PHP中是否有一个函数来获取子域的名称?

在下面的示例中,我想获取URL的“ en”部分:

en.example.com

6
您是否将URL作为字符串存储在变量中,或者该URL来自何处?上下文是什么?请详细说明。
Felix Kling

您不能使用执行类似操作的正则表达式来(^|://)(.*)\.捕获.*吗?我宁愿同时吸取php和regex,但这是想到的。
corsiKa 2011年

你应该得到它的en.foo.bar.example.com还是en.example.co.uk
阿尔瓦罗·冈萨雷斯

parse_url也可以提供帮助
Swapnil 2014年

Answers:


132

这是一线解决方案:

array_shift((explode('.', $_SERVER['HTTP_HOST'])));

或使用您的示例:

array_shift((explode('.', 'en.example.com')));

编辑:通过添加双括号修复了“仅变量应通过引用传递”。


编辑2:从PHP 5.4开始,您可以简单地执行以下操作:

explode('.', 'en.example.com')[0];

17
仅变量应通过引用传递。
陶巴氏

8
explode(...)[0]这些天,您难道不能只做轮班吗?没有PHPing几年..
托尔Valamo

错误:Strict Standards: Only variables should be passed by reference.
贾斯汀

1
非常确定您可以(explode(...))[0],但是应该在返回数组上进行操作,而不要在函数
paranthesis

3
如果有人输入该解决方案将不起作用www.en.example.com,因此将www作为子域返回。
lolbas

65

使用parse_url函数。

$url = 'http://en.example.com';

$parsedUrl = parse_url($url);

$host = explode('.', $parsedUrl['host']);

$subdomain = $host[0];
echo $subdomain;

对于多个子域

$url = 'http://usa.en.example.com';

$parsedUrl = parse_url($url);

$host = explode('.', $parsedUrl['host']);

$subdomains = array_slice($host, 0, count($host) - 2 );
print_r($subdomains);

@Mike Lewis-这样是否可以解决多个子域(例如usa.en.example.com)的问题?只是想知道(我自己的答案没有,顺便说一句)。
Jared Farrish

@Jared,刚刚添加了一个解决方案来检测多个子域。
Mike Lewis

1
@Mike-可以在tx.usa.en.example.com上使用吗?(或science.news.bbc.co.uk)?(顺便说一句,尽管news.bbc.co.uk确实有效,但这不是一个有效的链接,只是一个例子)
Jared Farrish

4
这适用于只有单个“单词” TLD的所有内容,例如net,com,biz等。但是,例如,在与co.uk打交道时,它就没有作用。如此处所示这实际上是一个更难解决的问题。
Mike Lewis

2
如果根本没有子域,这也将失败。
raveren'2

32

您可以先获取域名(例如sub.example.com => example.co.uk),然后使用strstr来获取子域。

$testArray = array(
    'sub1.sub2.example.co.uk',
    'sub1.example.com',
    'example.com',
    'sub1.sub2.sub3.example.co.uk',
    'sub1.sub2.sub3.example.com',
    'sub1.sub2.example.com'
);

foreach($testArray as $k => $v)
{
    echo $k." => ".extract_subdomains($v)."\n";
}

function extract_domain($domain)
{
    if(preg_match("/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i", $domain, $matches))
    {
        return $matches['domain'];
    } else {
        return $domain;
    }
}

function extract_subdomains($domain)
{
    $subdomains = $domain;
    $domain = extract_domain($subdomains);

    $subdomains = rtrim(strstr($subdomains, $domain, true), '.');

    return $subdomains;
}

输出:

0 => sub1.sub2
1 => sub1
2 =>
3 => sub1.sub2.sub3
4 => sub1.sub2.sub3
5 => sub1.sub2

2
这似乎是最好的解决方案,因为它还允许不带子域的域,而不是因为子域是第一个点之前的部分而重新引用域名。对于检查子域的存在非常有用。
卡尔·兆瓦

我需要获取“基本”域(不包含子域),并且通过分解主机并使用for循环获取数组的最后一个元素来制定自己的解决方案,但是我必须检查它们的长度(以检测它们的长度)是域名的一部分,例如“ co.uk”)。实际上,您的解决方案比我所做的要简单得多。正则表达式可以挽救生命,谢谢!
Yoone 2013年

1
太好了..这对所有域类型和子域都很好用..很好。
2013年

2
而这种解决方案是非常整齐,并可能工作,几乎所有的情况下,请注意域的名称可能有超过6个字符,像pvt.k12.ma.ushealth.vn甚至k12.ak.us。另外,域名可能使用中文或俄语字符集,因此正则表达式部分[a-z\.]{2,6}将与它们不匹配。在这里
pomeh

12

http://php.net/parse_url

<?php
  $url = 'http://user:password@sub.hostname.tld/path?argument=value#anchor';
  $array=parse_url($url);
  $array['host']=explode('.', $array['host']);

  echo $array['host'][0]; // returns 'en'
?>

7

作为域后缀的唯一可靠来源是域注册商,因此在没有子域名的情况下您无法找到子域。在https://publicsuffix.org上有一个包含所有域后缀的列表。该站点还链接到PHP库:https : //github.com/jeremykendall/php-domain-parser

请在下面找到一个例子。我还添加了en.test.co.uk的示例,该示例是一个具有多个后缀(co.uk)的域。

<?php

require_once 'vendor/autoload.php';

$pslManager = new Pdp\PublicSuffixListManager();
$parser = new Pdp\Parser($pslManager->getList());
$host = 'http://en.example.com';
$url = $parser->parseUrl($host);

echo $url->host->subdomain;


$host = 'http://en.test.co.uk';
$url = $parser->parseUrl($host);

echo $url->host->subdomain;

5

最简单,最快的解决方案。

$sSubDomain = str_replace('.example.com','',$_SERVER['HTTP_HOST']);

4

只是...

    preg_match('/(?:http[s]*\:\/\/)*(.*?)\.(?=[^\/]*\..{2,5})/i', $url, $match);

只需阅读$ match [1]

工作实例

该网址列表非常适合

$url = array(
    'http://www.domain.com', // www
    'http://domain.com', // --nothing--
    'https://domain.com', // --nothing--
    'www.domain.com', // www
    'domain.com', // --nothing--
    'www.domain.com/some/path', // www
    'http://sub.domain.com/domain.com', // sub
    'опубликованному.значения.ua', // опубликованному ;)
    'значения.ua', // --nothing--
    'http://sub-domain.domain.net/domain.net', // sub-domain
    'sub-domain.third-Level_DomaIN.domain.uk.co/domain.net' // sub-domain
);

foreach ($url as $u) {
    preg_match('/(?:http[s]*\:\/\/)*(.*?)\.(?=[^\/]*\..{2,5})/i', $u, $match);
    var_dump($match);
}

2
PS-我不知道俄语文字写的是什么。刚刚从ru.wikipedia.org上听了一些随便的话;)
Kamafeather

不是乌克兰语吗?.ua是乌克兰的国家代码。
2014年

不。只是混合信息。但我不确定,我还不足以区分它们;)
Kamafeather 2014年

3
关于俄语,谷歌将俄语翻译为英语返回为“已发布的价值”(以防有人像我一样好奇)
杰里米·哈里斯

@Kamafeather,这看起来很防弹。有什么办法可以得到$match[1]一部分吗?$match[0]似乎没有必要。
Andres SK

3
$REFERRER = $_SERVER['HTTP_REFERER']; // Or other method to get a URL for decomposition

$domain = substr($REFERRER, strpos($REFERRER, '://')+3);
$domain = substr($domain, 0, strpos($domain, '/'));
// This line will return 'en' of 'en.example.com'
$subdomain = substr($domain, 0, strpos($domain, '.')); 

1
$_SERVER['HTTP_HOST']假设这就是答案背后的基本思想,那么有更好的方法可以自动检测当前主机(例如),然后依靠可欺骗的引荐来源标头。
马修

是的,我使用的是旧代码。但是,该示例仍然有效。那不是问题的根源。
Jared Farrish

仅在上面添加这些注释时,依靠$ _SERVER ['HTTP_HOST']可能没有效果,因为可能没有设置它。
gmslzr

2

PHP 7.0:使用爆炸功能并创建所有结果的列表。

list($subdomain,$host) = explode('.', $_SERVER["SERVER_NAME"]);

示例:sub.domain.com

echo $subdomain; 

结果:子

echo $host;

结果:域


您会忘记顶级域名(TLD)之类的内容.co.uk-您的代码段无法使用这些顶级域名(TLD)
Adrian Preuss

1

我发现最好的短期解决方案是

array_shift(explode(".",$_SERVER['HTTP_HOST']));

会导致严格错误。explode的输出不能直接传递到array_shift。
YAAK 2013年

1

对于那些“错误:严格的标准:仅变量应通过引用传递”的人。像这样使用:

$env = (explode(".",$_SERVER['HTTP_HOST'])); $env = array_shift($env);


这不是问题,但感谢您的投入。
FazoM 2013年


1

并没有真正的100%动态解决方案-我也一直在尝试解决这个问题,由于域扩展名(DTL)不同,如果不实际解析所有这些扩展名并每次都对其进行检查,则此任务将非常困难:

.com vs .co.uk vs org.uk

最可靠的选择是定义一个常数(或数据库条目等),该常数存储实际的域名并将其从$_SERVER['SERVER_NAME']使用中删除substr()

defined("DOMAIN")
    || define("DOMAIN", 'mymaindomain.co.uk');



function getSubDomain() {

    if (empty($_SERVER['SERVER_NAME'])) {

        return null;

    }

    $subDomain = substr($_SERVER['SERVER_NAME'], 0, -(strlen(DOMAIN)));

    if (empty($subDomain)) {

        return null;

    }

    return rtrim($subDomain, '.');

}

现在,如果您正在使用此功能,http://test.mymaindomain.co.uk则该功能将为您提供帮助;test或者,如果您具有多个子域级别,则将http://another.test.mymaindomain.co.uk获得another.test-除非您当然更新DOMAIN

我希望这有帮助。



1

使用正则表达式,字符串函数,parse_url()或其组合不是真正的解决方案。只需使用域测试任何建议的解决方案test.en.example.co.uk,就不会有任何正确的结果。

正确的解决方案是使用带有Public Suffix List解析域的软件包。我推荐TLDExtract,这是示例代码:

$extract = new LayerShifter\TLDExtract\Extract();

$result = $extract->parse('test.en.example.co.uk');
$result->getSubdomain(); // will return (string) 'test.en'
$result->getSubdomains(); // will return (array) ['test', 'en']
$result->getHostname(); // will return (string) 'example'
$result->getSuffix(); // will return (string) 'co.uk'


0
// For www.abc.en.example.com 
$host_Array = explode(".",$_SERVER['HTTP_HOST']); // Get HOST as array www, abc, en, example, com
array_pop($host_Array); array_pop($host_Array);   // Remove com and exmaple
array_shift($host_Array);                         // Remove www (Optional)
echo implode($host_Array, ".");                   // Combine array abc.en

0

我知道我真的很迟到,但是可以。

我所做的就是获取HTTP_HOST服务器变量($_SERVER['HTTP_HOST'])和域中的字母数(因此example.com它将是11)。

然后,我使用该substr函数来获取子域。我做了

$numberOfLettersInSubdomain = strlen($_SERVER['HTTP_HOST'])-12
$subdomain = substr($_SERVER['HTTP_HOST'], $numberOfLettersInSubdomain);

我将子字符串从12而不是11截断,因为第二个参数的子字符串从1开始。所以,现在如果输入test.example.com,价值$subdomaintest

这比使用更好,explode因为如果子域中有一个.,则不会切断它。


您的答案中缺少起始位置“ 0”。$ subdomain = substr($ _ SERVER ['HTTP_HOST'],0,$ numberOfLettersInSubdomain);
杰米

0

如果您使用的是drupal 7

这将帮助您:

global $base_path;
global $base_root;  
$fulldomain = parse_url($base_root);    
$splitdomain = explode(".", $fulldomain['host']);
$subdomain = $splitdomain[0];

0
$host = $_SERVER['HTTP_HOST'];
preg_match("/[^\.\/]+\.[^\.\/]+$/", $host, $matches);
$domain = $matches[0];
$url = explode($domain, $host);
$subdomain = str_replace('.', '', $url[0]);

echo 'subdomain: '.$subdomain.'<br />';
echo 'domain: '.$domain.'<br />';

0

从PHP 5.3开始,您可以将strstr()true参数一起使用

echo strstr($_SERVER["HTTP_HOST"], '.', true); //prints en

仅当www字符串开头没有时,这才起作用。过于琐碎的方法。
FooBar 2014年

这可以简化团队中其他开发人员的工作,我宁愿使用它,也不愿使用一些高级的reg exp。如果要修剪www,请使用trim($ s,'www'); 或只是根据您的业务逻辑进行调整...
tasmaniski 2014年

1
为了完整起见,www 实际上是一个子域。由于历史原因,它通常只是域名的别名。
Levi Morrison 2014年

0

试试这个...

$domain = 'en.example.com';
$tmp = explode('.', $domain);
$subdomain = current($tmp);
echo($subdomain);     // echo "en"

当您为自己的意图添加一些说明时,我认为这对于OP和其他访问者会更有用。
记者

0
function get_subdomain($url=""){
    if($url==""){
        $url = $_SERVER['HTTP_HOST'];
    }
    $parsedUrl = parse_url($url);
    $host = explode('.', $parsedUrl['path']);
    $subdomains = array_slice($host, 0, count($host) - 2 );
    return implode(".", $subdomains);
}

1
7号线应该是$host = explode('.', isset($parsedUrl['path']) ? $parsedUrl['path'] : $parsedUrl['host']);
Kal

0

你也可以用这个

echo substr($_SERVER['HTTP_HOST'], 0, strrpos($_SERVER['HTTP_HOST'], '.', -5));

0

我正在做这样的事情

$url = https://en.example.com

$splitedBySlash = explode('/', $url);
$splitedByDot = explode('.', $splitedBySlash[2]);

$subdomain = $splitedByDot[0];

0

我们使用此功能来处理多个子域,多个tld也可以处理ip和localhost

function analyse_host($_host)
    {
        $my_host   = explode('.', $_host);
        $my_result = ['subdomain' => null, 'root' => null, 'tld' => null];

        // if host is ip, only set as root
        if(filter_var($_host, FILTER_VALIDATE_IP))
        {
            // something like 127.0.0.5
            $my_result['root'] = $_host;
        }
        elseif(count($my_host) === 1)
        {
            // something like localhost
            $my_result['root'] = $_host;
        }
        elseif(count($my_host) === 2)
        {
            // like jibres.com
            $my_result['root'] = $my_host[0];
            $my_result['tld']  = $my_host[1];
        }
        elseif(count($my_host) >= 3)
        {
            // some conditons like
            // ermile.ac.ir
            // ermile.jibres.com
            // ermile.jibres.ac.ir
            // a.ermile.jibres.ac.ir

            // get last one as tld
            $my_result['tld']  = end($my_host);
            array_pop($my_host);

            // check last one after remove is probably tld or not
            $known_tld    = ['com', 'org', 'net', 'gov', 'co', 'ac', 'id', 'sch', 'biz'];
            $probably_tld = end($my_host);
            if(in_array($probably_tld, $known_tld))
            {
                $my_result['tld'] = $probably_tld. '.'. $my_result['tld'];
                array_pop($my_host);
            }

            $my_result['root'] = end($my_host);
            array_pop($my_host);

            // all remain is subdomain
            if(count($my_host) > 0)
            {
                $my_result['subdomain'] = implode('.', $my_host);
            }
        }

        return $my_result;
    }

0

假设当前网址= sub.example.com

    $ host = array_reverse(explode('。',$ _SERVER ['SERVER_NAME'])));

    如果(count($ host)> = 3){
       echo“主域为=”。$ host [1]。“。”。$ host [0]。“&子域为=”。$ host [2];
       //主域名为= example.com,子域名为=子域名
    }其他{
       echo“主域为=”。$ host [1]。“。”。$ host [0]。“&子域未找到”;
       //“主域名为= example.com&未找到子域名”;
    }


-3

如果您只想要第一期之前的内容:

list($sub) = explode('.', 'en.example.com', 2);

如果开头有协议处理程序,例如http://,https://,ftp://等,该怎么办?;)
Jared Farrish

@Jared,他要解析的字符串中没有协议...但是,如果有的话,我会用它parse_url()来提取主机。
马修,

因此,我们提供了两种适用于不同环境的方法。
Jared Farrish

主要是,我很高兴有人还没有发布正则表达式答案。更不用说我回答的最后一行也可以完成您的操作。
Jared Farrish

如果主机名是en.example.co.uk?
Marc B
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.