Parser::getGender()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
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 0
dl 0
loc 2
rs 10
1
<?php
2
declare(strict_types = 1);
3
4
namespace SLWDC\NICParser;
5
6
use DateInterval;
7
use DateTime;
8
use SLWDC\NICParser\Exception\InvalidArgumentException;
9
10
use function strlen;
11
12
class Parser {
13
  private $data_components = [];
14
15
  const ID_FORMAT_PRE_2016 = 1;
16
  const ID_FORMAT_2016 = 2;
17
18
  public function __construct(string $id_number) {
19
    $this->parse($id_number);
20
  }
21
22
  public function getBirthday(): DateTime {
23
    return $this->data_components['date'];
24
  }
25
26
  public function getSerialNumber(): int {
27
    return $this->data_components['serial'];
28
  }
29
30
  public function getFormat(): int {
31
    return $this->data_components['format'];
32
  }
33
34
  public function getGender(): string {
35
    return $this->data_components['gender'];
36
  }
37
38
  private function parse(string $id_number) {
39
    $id_number = $this->checkLength($id_number);
40
    $this->checkBirthDate($id_number);
41
    $this->detectFormat($id_number);
42
  }
43
44
  private function checkLength(string $id_number): int {
45
    $id_number = strtoupper($id_number);
46
    $strlen = strlen($id_number);
47
48
    if ($strlen === 10) {
49
      if ($id_number[9] !== 'V') {
50
        throw new InvalidArgumentException('Ending character is invalid.', 103);
51
      }
52
      $id_number = substr($id_number, 0, 9);
53
    }
54
55
    if (!ctype_digit($id_number)) {
56
      throw new InvalidArgumentException('Provided number is not all-numeric', 102);
57
    }
58
    return (int) $id_number;
59
  }
60
61
  private function checkBirthDate(int $id_number) {
62
    $full_number = strlen((string) $id_number) === 9
63
      ? '19' . $id_number
64
      : (string) $id_number;
65
66
    $year = (int) substr($full_number, 0, 4);
67
    $this->data_components['year'] = $year;
68
69
    $this->checkBirthYear($year);
70
    $this->buildBirthDateObject($full_number, $year);
71
    $this->data_components['serial'] = (int) substr($full_number, 7);
72
  }
73
74
  private function checkBirthYear(int $year) {
75
    if ($year < 1900 || $year > 2100) {
76
      throw new InvalidArgumentException('Birth year is out ff 1900-2100 range', 200);
77
    }
78
  }
79
80
  private function buildBirthDateObject(string $full_number, int $year) {
81
    $birthday = new DateTime();
82
    $birthday->setDate($year, 1, 1)->setTime(0, 0);
83
    $birth_days_since = (int) substr($full_number, 4, 3);
84
85
    if ($birth_days_since > 500) {
86
      $birth_days_since -= 500;
87
      $this->data_components['gender'] = 'F';
88
    }
89
    else {
90
      $this->data_components['gender'] = 'M';
91
    }
92
93
    if (date('L', mktime(0, 0, 0, 1, 1, $year)) === "1") {
94
      --$birth_days_since;
95
    } else {
96
      $birth_days_since -= 2;
97
    }
98
99
    $birthday->add(new DateInterval('P' . $birth_days_since . 'D'));
100
    $this->data_components['date'] = $birthday;
101
    if ($birthday->format('Y') !== (string) $year) {
102
      throw new InvalidArgumentException('Birthday indicator is invalid.', 201);
103
    }
104
  }
105
106
  private function detectFormat(int $id_number) {
107
    $strlen = strlen((string) $id_number);
108
    if ($strlen === 12) {
109
      $this->data_components['format'] = static::ID_FORMAT_2016;
110
    }
111
    else {
112
      $this->data_components['format'] = static::ID_FORMAT_PRE_2016;
113
    }
114
  }
115
}
116