DKAlgorithm::isFollowPattern()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
namespace LeKoala\Tin\Algo;
4
5
use LeKoala\Tin\Util\DateUtil;
6
use LeKoala\Tin\Util\StringUtil;
7
8
/**
9
 * Denmark
10
 */
11
class DKAlgorithm extends TINAlgorithm
12
{
13
    const LENGTH = 10;
14
    const PATTERN = "[0-3]\\d[0-1]\\d{3}\\d{4}";
15
16
    public function validate(string $tin)
17
    {
18
        $whithoutHyphen = str_replace("-", "", $tin);
19
        if (!$this->isFollowLength($whithoutHyphen)) {
20
            return StatusCode::INVALID_LENGTH;
21
        }
22
        if (!$this->isFollowPattern($whithoutHyphen) || !$this->isValidDate($whithoutHyphen)) {
23
            return StatusCode::INVALID_PATTERN;
24
        }
25
        if (!$this->isFollowRules($whithoutHyphen)) {
26
            return StatusCode::INVALID_SYNTAX;
27
        }
28
        return StatusCode::VALID;
29
    }
30
31
    public function isFollowLength(string $tin)
32
    {
33
        return StringUtil::isFollowLength($tin, self::LENGTH);
34
    }
35
36
    public function isFollowPattern(string $tin)
37
    {
38
        return StringUtil::isFollowPattern($tin, self::PATTERN);
39
    }
40
41
    public function isFollowRules(string $tin)
42
    {
43
        return $this->isFollowDenmarkRule($tin);
44
    }
45
46
    /**
47
     * @link https://cpr.dk/cpr-systemet/personnumre-uden-kontrolciffer-modulus-11-kontrol/
48
     *
49
     * The CPR office has since 2007 given out social security numbers without the so called modulus 11 control.
50
     * The social security numbers without modulus 11 are completely valid
51
     * and are given out, as some birth years no longer have the capacity to provide them with modulus 11 control.
52
     *
53
     * We should not check modulus 11 control for the following birthdays:
54
     *
55
     * 1st of januar 1960
56
     * 1st of januar 1964
57
     * 1st of januar 1965
58
     * 1st of januar 1966
59
     * 1st of januar 1969
60
     * 1st of januar 1970
61
     * 1st of januar 1974
62
     * 1st of januar 1980
63
     * 1st of januar 1982
64
     * 1st of januar 1984
65
     * 1st of januar 1985
66
     * 1st of januar 1986
67
     * 1st of januar 1987
68
     * 1st of januar 1988
69
     * 1st of januar 1989
70
     * 1st of januar 1990
71
     * 1st of januar 1991
72
     * 1st of januar 1992
73
     *
74
     * @param string $tin
75
     * @return boolean
76
     */
77
    public function isFollowDenmarkRule(string $tin)
78
    {
79
        $serialNumber = intval(StringUtil::substring($tin, 6, 10));
80
        $dayOfBirth = intval(StringUtil::substring($tin, 0, 2));
81
        $monthOfBirth = intval(StringUtil::substring($tin, 2, 4));
82
        $yearOfBirth = intval(StringUtil::substring($tin, 4, 6));
83
        if ($yearOfBirth >= 37 && $yearOfBirth <= 57 && $serialNumber >= 5000 && $serialNumber <= 8999) {
84
            return false;
85
        }
86
87
        $excludedYears = [60, 64, 65, 66, 69, 70, 74, 80, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92];
88
        if ($dayOfBirth == 1 && $monthOfBirth == 1 && in_array($yearOfBirth, $excludedYears)) {
89
            return true;
90
        }
91
92
        $c1 = StringUtil::digitAt($tin, 0);
93
        $c2 = StringUtil::digitAt($tin, 1);
94
        $c3 = StringUtil::digitAt($tin, 2);
95
        $c4 = StringUtil::digitAt($tin, 3);
96
        $c5 = StringUtil::digitAt($tin, 4);
97
        $c6 = StringUtil::digitAt($tin, 5);
98
        $c7 = StringUtil::digitAt($tin, 6);
99
        $c8 = StringUtil::digitAt($tin, 7);
100
        $c9 = StringUtil::digitAt($tin, 8);
101
        $c10 = StringUtil::digitAt($tin, 9);
102
        $sum = $c1 * 4 + $c2 * 3 + $c3 * 2 + $c4 * 7 + $c5 * 6 + $c6 * 5 + $c7 * 4 + $c8 * 3 + $c9 * 2;
103
        $remainderBy11 = $sum % 11;
104
        if ($remainderBy11 == 1) {
105
            return false;
106
        }
107
        if ($remainderBy11 == 0) {
108
            return $c10 == 0;
109
        }
110
        return $c10 == 11 - $remainderBy11;
111
    }
112
113
    private function isValidDate(string $tin)
114
    {
115
        $day = intval(StringUtil::substring($tin, 0, 2));
116
        $month = intval(StringUtil::substring($tin, 2, 4));
117
        $year = intval(StringUtil::substring($tin, 4, 6));
118
        return DateUtil::validate(1900 + $year, $month, $day) || DateUtil::validate(2000 + $year, $month, $day);
119
    }
120
}
121