PHP IPアドレスが指定した範囲内にあるかどうか判別する関数を適当に書いた

## 概要

googlebotなどのクローラーがアクセスしてきた時に、特定の処理を除外するために書いた。例えば広告を表示しないなど。

参考:[IPアドレスが指定した範囲内にあるかどうか判別する](https://qiita.com/ran/items/039706c93a8ff85a011a)

## 範囲の形式

* 完全一致 192.168.0.1
* マスク 192.168.0.0/24
* マスク 192.168.0.100/24
* from/to 192.168.0.100/192.168.0.200

## PHPUnitテスト
```PHP
public function test_accept_ip() {

    // 完全一致
    $this->assertTrue(accept_ip('192.168.0.1', '192.168.0.1'));
    $this->assertFalse(accept_ip('192.168.0.1', '192.168.0.22'));

    // マスク
    $this->assertTrue(accept_ip('192.168.0.1', '192.168.0.0/24'));
    $this->assertFalse(accept_ip('192.168.0.1', '192.168.0.22/24'));
    $this->assertTrue(accept_ip('192.168.0.23', '192.168.0.22/24'));
    $this->assertTrue(accept_ip('66.249.71.129', '66.249.71.128/27'));
    $this->assertFalse(accept_ip('66.249.71.127', '66.249.71.128/27'));
    $this->assertFalse(accept_ip('66.249.64.191', '66.249.64.192/27'));
    $this->assertTrue(accept_ip('66.249.64.192', '66.249.64.192/27'));
    $this->assertTrue(accept_ip('66.249.64.223', '66.249.64.192/27'));
    $this->assertFalse(accept_ip('66.249.64.224', '66.249.64.192/27'));

    // from/to
    $this->assertTrue(accept_ip('148.64.56.64', '148.64.56.64/148.64.56.80'));
    $this->assertFalse(accept_ip('148.64.56.63', '148.64.56.64/148.64.56.80'));
    $this->assertTrue(accept_ip('148.64.56.80', '148.64.56.64/148.64.56.80'));
    $this->assertFalse(accept_ip('148.64.56.81', '148.64.56.64/148.64.56.80'));

    // 引数が不正
    set_error_handler(function(){});
    $this->assertFalse(accept_ip('', null));
    $this->assertFalse(accept_ip(null, null));
    $this->assertFalse(accept_ip('ABCDEFG', null));
    $this->assertFalse(accept_ip('148.64.56.81', ''));
    $this->assertFalse(accept_ip('148.64.56.81', null));
    $this->assertFalse(accept_ip('148.64.56.81', 'ABCDEFG'));
    $this->assertFalse(accept_ip('148.64.56.81', 'ABCDEFG/24'));
    $this->assertFalse(accept_ip('148.64.56.81', '148.64.56.64/ABCDEFG'));
    $this->assertFalse(accept_ip('148.64.56.81', 'ABCDEFG/148.64.56.64'));
}
```

## PHP

```PHP
/**
 * $remote_ipが$accept_ipの定義に当てはまるか
 * @see 参考 https://qiita.com/ran/items/039706c93a8ff85a011a
 *
 * @param string $remote_ip
 * @param string $accept_ip
 * @return bool
 *
 */
function accept_ip($remote_ip, $accept_ip) {

    if (! valid_ip($remote_ip, 'ipv4')) {
        trigger_error(__FUNCTION__.'(): Argument #1 ($remote_ip) must be in the form of an IP address.', E_USER_WARNING);
        return false;
    }

    if (strpos($accept_ip, '/')) {
        $tmp = explode('/', $accept_ip);
        $accept_ip = $tmp[0];
        $remote_long = ip2long($remote_ip);

        // サブネットマスク のパターン
        if (ctype_digit($tmp[1])) {
            if (! valid_ip($accept_ip, 'ipv4')) {
                trigger_error(__FUNCTION__.'(): Argument #2 ($accept_ip) must be in the form of an IP address.', E_USER_WARNING);
                return false;
            }
            $mask = $tmp[1];
            $accept_long = ip2long($accept_ip);
            $remote_long_prefix = $remote_long >> (32 - $mask);
            $accept_long_prefix = $accept_long >> (32 - $mask);
            return ($remote_long_prefix === $accept_long_prefix && $remote_long >= $accept_long);
        }

        // 範囲下/範囲上 のパターン
        else if (valid_ip($tmp[1], 'ipv4')) {
            $accept_long_gte = ip2long($accept_ip);
            $accept_long_lte = ip2long($tmp[1]);
            return ($accept_long_gte <= $remote_long && $remote_long <= $accept_long_lte);
        }

        trigger_error(__FUNCTION__.'(): Argument #2 ($accept_ip) must be in the form of an IP address.', E_USER_WARNING);
        return false;
    }

    if (! valid_ip($accept_ip, 'ipv4')) {
        trigger_error(__FUNCTION__.'(): Argument #2 ($accept_ip) must be in the form of an IP address.', E_USER_WARNING);
        return false;
    }

    if ($accept_ip === $remote_ip) {
        return true;
    }
    return false;
}

/**
 * Validate IP Address
 *
 * @param string $ip
 * @param string $options 'ipv4' or 'ipv6' to validate a specific IP format
 * @return bool
 */
function valid_ip($ip, $options = null) {
    if (! is_null($options)) {
        $which = strtolower($options);
        if ('ipv4' === $which) {
            $options = 1048576;
        }
        else if ('ipv6' === $which) {
            $options = 2097152;
        }
    }
    return (bool)filter_var($ip, 275, $options);
}
```