PHP - 从子网数组创建分层列表

时间:2015-12-28 14:49:52

标签: php multidimensional-array ip

我有一个由IP地址和子网掩码组成的多维数组:

array(4) {
  [0]=>
  array(3) {
    ["ip"]=>
    string(12) "192.168.0.0"
    ["mask"]=>
    int(22)
  }
  [1]=>
  array(3) {
    ["ip"]=>
    string(12) "192.168.0.0"
    ["mask"]=>
    int(30)
  }
  [2]=>
  array(3) {
    ["ip"]=>
    string(12) "192.168.0.4"
    ["mask"]=>
    int(31)
  }
  [3]=>
  array(3) {
    ["ip"]=>
    string(12) "192.168.0.4"
    ["mask"]=>
    int(32)
  }
}

阵列已按正确顺序排列。我需要做的是将此数组转换为分层列表,所需的输出应该是这样的:

192.168.0.0/22
- 192.168.0.0/30
- 192.168.0.4/31
-- 192.168.0.4/32

每次子网属于更大的子网时,缩进(' - ')必须增加1,如上面的输出所示。我没有使用预定义的父ID,因为子网列表可能随时更改。这种转换必须在运行中完成。

我希望我会尝试任何东西,但我不知道从哪里开始。我唯一能想到的是在每次迭代中通过数组比较2个子网。首先比较子网1和2,然后是2和3,然后是3和4,依此类推......

2 个答案:

答案 0 :(得分:1)

我写了一个小类来处理CIDR逻辑(简化嵌套过程)。

<?php
class Cidr {

    /**
     * @var int
     */
    private $subnet;

    /**
     * @var int
     */
    private $mask;

    /**
     * @var int
     */
    private $upperBound;

    /**
     * @var self[]
     */
    private $children = [];

    /**
     * Cidr constructor
     *
     * @param string $subnet
     * @param int    $mask
     */
    public function __construct($subnet, $mask) {
        $this->subnet     = ip2long($subnet);
        $this->mask       = (int) $mask;
        $this->upperBound = $this->subnet + pow(2, 32 - $this->mask) - 1;
    }

    /**
     * @param string $rangeStr
     *
     * @return Cidr
     */
    public static function fromString($rangeStr) {
        list($subnet, $mask) = explode('/', $rangeStr);
        return new self($subnet, $mask);
    }

    /**
     * @return string
     */
    public function __toString() {
        return "{$this->minIp()}/{$this->mask}";
    }

    /**
     * @param Cidr $cidr
     *
     * @return bool
     */
    public function addChild(self $cidr) {
        if ($cidr === $this) {
            return false;
        }

        if ($this->isRangeInRange($cidr)) {
            foreach ($this->children as $child) {
                if ($child->addChild($cidr)) {
                    return true;
                }
            }

            $this->children[] = $cidr;
            return true;
        }

        return false;
    }

    /**
     * @return Cidr[]
     */
    public function getChildren() {
        return $this->children;
    }

    /**
     * @return string
     */
    public function minIp() {
        return long2ip($this->subnet);
    }

    /**
     * @return string
     */
    public function maxIp() {
        return long2ip($this->upperBound);
    }

    /**
     * Check if an IP falls within this range
     *
     * @param string $ip
     *
     * @return bool
     */
    public function isIpInRange($ip) {
        $mask = -1 << (32 - $this->mask);
        return (ip2long($ip) & $mask) === ($this->subnet & $mask);
    }

    /**
     * @param Cidr $cidr
     *
     * @return bool
     */
    public function isRangeInRange(self $cidr) {
        return $cidr->subnet >= $this->subnet && $cidr->upperBound <= $this->upperBound;
    }

    /**
     * @param self[] $cidrs
     */
    public static function nestCidrs(array &$cidrs) {
        foreach ($cidrs as $a) {
            foreach ($cidrs as $k => $b) {
                if ($a !== $b && $a->addChild($b)) {
                    unset($cidrs[$k]);
                }
            }
        }
    }

    /**
     * @param self[] $cidrs
     * @param int    $depth
     */
    public static function displayCidrs(array $cidrs, $depth = 0) {
        foreach ($cidrs as $cidr) {
            echo str_repeat('-', $depth) . "{$cidr}\n";
            self::displayCidrs($cidr->getChildren(), $depth + 1);
        }
    }
}

使用子网数组(如您在示例中所示):

$subnets = [
    [
        'ip'   => '192.168.0.0',
        'mask' => 22,
    ],
    [
        'ip'   => '192.168.0.0',
        'mask' => 30,
    ],
    [
        'ip'   => '192.168.0.4',
        'mask' => 31,
    ],
    [
        'ip'   => '192.168.0.4',
        'mask' => 32,
    ],
];

创建一个Cidr个对象数组:

$cidrs = [];
foreach ($subnets as $subnet) {
    $cidr = new Cidr($subnet['ip'], $subnet['mask']);
    $cidrs[(string) $cidr] = $cidr;
}

运行嵌套功能(根据需要递归添加每个子网范围:

Cidr::nestCidrs($cidrs);

然后显示结果:

Cidr::displayCidrs($cidrs);

结果:

192.168.0.0/22
-192.168.0.0/30
-192.168.0.4/31
--192.168.0.4/32

答案 1 :(得分:-1)

您想要的示例示例

        $h = array(
        array("ip_bin"=>"192.168.0.0","mask"=>22),
        array("ip_bin"=>"192.168.1.0","mask"=>30),
        array("ip_bin"=>"192.168.4.0","mask"=>31),);

        $dash = "";

        foreach($h as $s){
          $dash .="-";
          echo  $dash. $s['ip_bin']."/".$s['mask']."<br>";
            }

输出

     -192.168.0.0
     --192.168.1.0
     ---192.168.4.0