AIP::compareAsString()   B
last analyzed

Complexity

Conditions 7
Paths 5

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 7

Importance

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