Passed
Push — master ( 9040d9...ba8838 )
by Ayesh
01:28
created

Parser::detectFormat()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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