Passed
Pull Request — master (#42)
by Antonio Oertel
02:24
created

AbstractDocument::__toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Brazanation\Documents;
4
5
use Brazanation\Documents\Exception\InvalidDocument as InvalidDocumentException;
6
use Brazanation\Documents\Exception\InvalidDocument;
7
use Brazanation\Documents\Exception\Readonly;
8
9
/**
10
 * Class AbstractDocument
11
 *
12
 * @package Brazanation\Documents
13
 *
14
 * @property string $number
15
 * @property string $digit
16
 * @property int $length
17
 * @property int $numberOfDigits
18
 * @property string $type
19
 */
20
abstract class AbstractDocument implements DigitCalculable, Formattable
21
{
22
    /**
23
     * @var string
24
     */
25
    protected $number;
26
27
    /**
28
     * @var string
29
     */
30
    protected $digit;
31
32
    /**
33
     * @var int
34
     */
35
    protected $length;
36
37
    /**
38
     * @var int
39
     */
40
    protected $numberOfDigits;
41
42
    /**
43
     * @var string
44
     */
45
    protected $type;
46
47
    /**
48
     * AbstractDocument constructor.
49
     *
50
     * @param string $number Numeric section with checker digit.
51
     * @param int $length Max length of document.
52
     * @param int $numberOfDigits Max length of checker digits.
53
     * @param string $type Document name/type.
54
     */
55 1306
    public function __construct($number, $length, $numberOfDigits, $type)
56
    {
57 1306
        $this->type = (string) $type;
58 1306
        $this->numberOfDigits = (int) $numberOfDigits;
59 1306
        $this->length = (int) $length;
60 1306
        $this->digit = $this->extractCheckerDigit($number);
61 1306
        $this->assert($number);
62 888
        $this->number = $number;
63 888
    }
64
65 5
    public function __get($name)
66
    {
67 5
        return $this->$name;
68
    }
69
70
    public function __set($name, $value)
71
    {
72
        throw Readonly::notAllowed(static::class, $name);
73
    }
74
75
    /**
76
     * Create a Document object from given number.
77
     *
78
     * @param string $number Numeric section with checker digit.
79
     *
80
     * @return AbstractDocument|boolean Returns a new Document instance or FALSE on failure.
81
     */
82
    abstract public static function createFromString($number);
83
84
    /**
85
     * Try to create a Document object from given number.
86
     *
87
     * @param string $number Numeric section with checker digit.
88
     * @param int $length Max length of document.
89
     * @param int $numberOfDigits Max length of checker digits.
90
     * @param string $type Document name/type.
91
     *
92
     * @return AbstractDocument|boolean Returns a new Document instance or FALSE on failure.
93
     */
94 167
    protected static function tryCreateFromString($class, $number, $length, $numberOfDigits, $type)
95
    {
96
        try {
97 167
            return new $class($number, $length, $numberOfDigits, $type);
98 116
        } catch (InvalidDocument $exception) {
99 116
            return false;
100
        }
101
    }
102
103
    /**
104
     * Handle number to string.
105
     *
106
     * @return string
107
     */
108 883
    public function __toString()
109
    {
110 883
        return "{$this->number}";
111
    }
112
113
    /**
114
     * Check if document number is valid.
115
     *
116
     * @param string $number Numeric section with checker digit.
117
     *
118
     * @throws InvalidDocumentException when number is empty
119
     * @throws InvalidDocumentException when number is not valid
120
     */
121 1306
    protected function assert($number)
122
    {
123 1306
        if (empty($number)) {
124 154
            throw InvalidDocumentException::notEmpty($this->type);
125
        }
126 1152
        if (!$this->isValid($number)) {
127 264
            throw InvalidDocumentException::isNotValid($this->type, $number);
128
        }
129 888
    }
130
131
    /**
132
     * Validates number is a valid.
133
     *
134
     * @param string $number Numeric section with checker digit.
135
     *
136
     * @return bool Returns true if it is a valid number, otherwise false.
137
     */
138 1152
    protected function isValid($number)
139
    {
140 1152
        $baseNumber = $this->extractBaseNumber($number);
141
142 1152
        if (!$baseNumber) {
143 34
            return false;
144
        }
145
146 1118
        $isRepeated = preg_match("/^[{$baseNumber[0]}]+$/", $baseNumber);
147
148 1118
        if ($isRepeated) {
149 54
            return false;
150
        }
151
152 1064
        $digit = $this->calculateDigit($baseNumber);
153
154 1064
        return "$digit" === "{$this->digit}";
155
    }
156
157
    /**
158
     * Extracts the base number document.
159
     *
160
     * @param string $number Number of document.
161
     *
162
     * @return string Returns only base number without checker digit.
163
     */
164 248
    protected function extractBaseNumber($number)
165
    {
166 248
        return substr($number, 0, -($this->numberOfDigits));
167
    }
168
169
    /**
170
     * Extracts the checker digit from document number.
171
     *
172
     * @param string $number Number of document.
173
     *
174
     * @return string Returns only checker digit.
175
     */
176 318
    protected function extractCheckerDigit($number)
177
    {
178 318
        return substr($number, -($this->numberOfDigits));
179
    }
180
}
181