Passed
Push — master ( de3770...e18c28 )
by Petr
07:43
created

AIP::compare()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace kalanis\kw_bans\Bans;
4
5
6
use kalanis\kw_bans\Interfaces\IKBTranslations;
7
use kalanis\kw_bans\Ip;
8
use kalanis\kw_bans\Sources\ASources;
9
use kalanis\kw_bans\Traits;
10
11
12
abstract class AIP extends ABan
13
{
14
    use Traits\TExpandIp;
15
    use Traits\TLang;
16
    use Traits\TIp;
17
18
    /** @var Ip[] */
19
    protected $knownIps = [];
20
    /** @var Ip */
21
    protected $searchIp = null;
22
    /** @var int */
23
    protected $bitsInBlock = 4;
24
25 21
    public function __construct(ASources $source, ?IKBTranslations $lang = null)
26
    {
27 21
        $this->setIKbLang($lang);
28 21
        $this->setBasicIp(new Ip());
29 21
        $this->knownIps = array_map(function ($row) {
30 19
            return $this->expandIP($row);
31 21
        }, $source->getRecords());
32 21
    }
33
34 16
    public function setLookedFor(string $lookedFor): void
35
    {
36 16
        $this->searchIp = $this->expandIP($lookedFor);
37 16
    }
38
39 16
    protected function matched(): array
40
    {
41 16
        return array_filter($this->knownIps, [$this, 'comparedByAddress']);
42
    }
43
44 16
    public function comparedByAddress(Ip $ipAddress): bool
45
    {
46
        // compare only parts unaffected by mask, then special bitwise compare for partially affected, cut the rest
47 16
        $knownToCompare = $this->cutPositions($ipAddress->getAddress(), $ipAddress->getAffectedBits());
48 16
        $searchToCompare = $this->cutPositions($this->searchIp->getAddress(), $ipAddress->getAffectedBits());
49
50
//print_r(['cutted', $knownToCompare, $searchToCompare, $leftKnownToBitwiseCompare, $leftSearchToBitwiseCompare, $bitsInAffectedPart]);
51
        // now compare only relevant portions
52 16
        foreach ($knownToCompare->getAddress() as $position => $segment) {
53 16
            $forCompare = strval($searchToCompare->getAddress()[$position]);
54 16
            if ($segment == $forCompare) {
55 13
                continue;
56
            }
57 10
            if ($this->compareAsString($segment, $forCompare)) {
58 4
                continue;
59
            }
60 10
            return false;
61
        }
62
63
//print_r(['cmpaddr', $bitsInAffectedPart, $leftKnownToBitwiseCompare, $leftSearchToBitwiseCompare]);
64 9
        if (!empty($searchToCompare->getBitsInAffectedPart())) {
65
            // compare bitwise the last segment
66 4
            if (!$this->compareAsBinary($knownToCompare, $searchToCompare)) {
67 2
                return false;
68
            }
69
        }
70
71 7
        return true;
72
    }
73
74
    /**
75
     * @param array<int, string> $address
76
     * @param int $affectedBits
77
     * @return Cut
78
     */
79 16
    protected function cutPositions(array $address, int $affectedBits): Cut
80
    {
81 16
        $bitsInAffectedPart = $affectedBits % $this->bitsInBlock;
82 16
        $affectedBlocks = intval(ceil($affectedBits / $this->bitsInBlock));
83
84 16
        $useAddress = array_slice($address, 0, $this->blocks - $affectedBlocks);
85 16
        $bitwiseBlock = $bitsInAffectedPart ? $address[count($useAddress)] : '';
86
87
//print_r(['cut', $address, $useAddress, $affectedBits, $bitwiseBlock, $bitsInAffectedPart]);
88 16
        return (new Cut())->setData($useAddress, $bitwiseBlock, $bitsInAffectedPart);
89
    }
90
91 10
    protected function compareAsString(string $known, string $tested): bool
92
    {
93
        // direct for *
94 10
        if ('*' == $known[0]) {
95 4
            return true;
96
        }
97 10
        if (strlen($known) != strlen($tested)) {
98 5
            return false;
99
        }
100
        // through for ?
101 9
        for ($i = 0; $i < strlen($known); $i++) {
102 9
            $pos = strval($known[$i]);
103 9
            if (('?' != $pos) && ('*' != $pos) && (strval($tested[$i]) != $pos)) {
104 8
                return false;
105
            }
106
        }
107 2
        return true;
108
    }
109
110 4
    protected function compareAsBinary(Cut $knownToCompare, Cut $searchToCompare): bool
111
    {
112 4
        $testingBinary = str_pad(decbin($this->toNumber($searchToCompare->getBitwiseBlock())), $this->bitsInBlock, '0', STR_PAD_LEFT);
113 4
        $knownBinary = str_pad(decbin($this->toNumber($knownToCompare->getBitwiseBlock())), $this->bitsInBlock, '0', STR_PAD_LEFT);
114
//print_r(['cmpbin', $testingBinary, $knownBinary, $cutBits]);
115 4
        if (0 < $searchToCompare->getBitsInAffectedPart()) {
116 4
            $testingBinary = substr($testingBinary, 0, ((-1) * $searchToCompare->getBitsInAffectedPart()));
117 4
            $knownBinary = substr($knownBinary, 0, ((-1) * $searchToCompare->getBitsInAffectedPart()));
118
        }
119
//print_r(['cmpbincut', $testingBinary, $knownBinary, $cutBits]);
120 4
        return $testingBinary == $knownBinary;
121
    }
122
123
    abstract protected function toNumber(string $value): int;
124
}
125