Passed
Pull Request — master (#42)
by Antonio Oertel
02:45 queued 27s
created

AbstractAccessKey::createFromString()   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
nc 1
cc 1
nop 1
crap 1
1
<?php
2
3
namespace Brazanation\Documents\Sped;
4
5
use Brazanation\Documents\AbstractDocument;
6
use Brazanation\Documents\Cnpj;
7
use Brazanation\Documents\DigitCalculator;
8
use Brazanation\Documents\Sped\Exception\DocumentModel;
9
10
/**
11
 * Class SpedAccessKey
12
 *
13
 * @package Brazanation\Documents
14
 *
15
 * @property int          $state
16
 * @property \DateTime    $generatedAt
17
 * @property Cnpj         $cnpj
18
 * @property Model        $model
19
 * @property int          $sequence
20
 * @property int          $invoiceNumber
21
 * @property EmissionType $emissionType
22
 * @property int          $controlNumber
23
 */
24
abstract class AbstractAccessKey extends AbstractDocument
25
{
26
    const NUMBER_OF_DIGITS = 1;
27
28
    const LABEL = 'SpedAccessKey';
29
30
    const LENGTH = 44;
31
32
    const MASK = '$1 ';
33
34
    const REGEX = '/([\d]{4})/';
35
36
    /**
37
     * @var int
38
     */
39
    protected $state;
40
41
    /**
42
     * @var \DateTime
43
     */
44
    protected $generatedAt;
45
46
    /**
47
     * @var Cnpj
48
     */
49
    protected $cnpj;
50
51
    /**
52
     * @var Model
53
     */
54
    protected $model;
55
56
    /**
57
     * @var string
58
     */
59
    protected $sequence;
60
61
    /**
62
     * @var string
63
     */
64
    protected $invoiceNumber;
65
66
    /**
67
     * @var EmissionType
68
     */
69
    protected $emissionType;
70
71
    /**
72
     * @var string
73
     */
74
    protected $controlNumber;
75
76
    /**
77
     * SpedAccessKey constructor.
78
     *
79
     * @param $accessKey
80
     */
81 160
    public function __construct($accessKey)
82
    {
83 160
        $accessKey = preg_replace('/\D/', '', $accessKey);
84 160
        parent::__construct($accessKey, static::LENGTH, static::NUMBER_OF_DIGITS, static::LABEL);
85 30
        $this->validateModel($accessKey);
86 29
        $this->loadFromKey($accessKey);
87 29
    }
88
89 77
    public static function createFromString($number)
90
    {
91 77
        return parent::tryCreateFromString(static::class, $number, self::LENGTH, self::NUMBER_OF_DIGITS, self::LABEL);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (tryCreateFromString() instead of createFromString()). Are you sure this is correct? If so, you might want to change this to $this->tryCreateFromString().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
92
    }
93
94
    /**
95
     * @return Model
96
     */
97
    abstract protected function defaultModel();
98
99 30
    protected function validateModel($accessKey)
100
    {
101 30
        $model = new Model(substr($accessKey, 20, 2));
102 30
        if (!$this->defaultModel()->equalsTo($model)) {
103 1
            throw DocumentModel::invalid($accessKey, static::LABEL);
104
        }
105 29
    }
106
107 29
    private function loadFromKey($accessKey)
108
    {
109 29
        $startPosition = 0;
110 29
        $this->state = substr($accessKey, $startPosition, 2);
0 ignored issues
show
Documentation Bug introduced by
The property $state was declared of type integer, but substr($accessKey, $startPosition, 2) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
111
112 29
        $startPosition += 2;
113 29
        $this->generatedAt = \DateTime::createFromFormat('ymd H:i:s', substr($accessKey, $startPosition, 4) . '01 00:00:00');
0 ignored issues
show
Documentation Bug introduced by
It seems like \DateTime::createFromFor...on, 4) . '01 00:00:00') can also be of type false. However, the property $generatedAt is declared as type object<DateTime>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
114
115 29
        $startPosition += 4;
116 29
        $this->cnpj = new Cnpj(substr($accessKey, $startPosition, 14));
117
118 29
        $startPosition += 14;
119 29
        $this->model = new Model(substr($accessKey, $startPosition, 2));
120
121 29
        $startPosition += 2;
122 29
        $this->sequence = substr($accessKey, $startPosition, 3);
123
124 29
        $startPosition += 3;
125 29
        $this->invoiceNumber = substr($accessKey, $startPosition, 9);
126
127 29
        $startPosition += 9;
128 29
        $this->emissionType = new EmissionType(substr($accessKey, $startPosition, 1));
129
130 29
        $startPosition += 1;
131 29
        $this->controlNumber = substr($accessKey, $startPosition, 8);
132
133 29
        $startPosition += 8;
134 29
        $this->digit = substr($accessKey, $startPosition, 1);
135 29
    }
136
137
    /**
138
     * Generates a valid Sped Access Key.
139
     *
140
     * @param int          $state         IBGE state code.
141
     * @param \DateTime    $generatedAt   Year and month when invoice was created.
142
     * @param Cnpj         $cnpj          Cnpj from issuer.
143
     * @param Model        $model         Document model.
144
     * @param int          $sequence      Invoice sequence.
145
     * @param int          $invoiceNumber Invoice number.
146
     * @param EmissionType $emissionType  Emission Type.
147
     * @param int          $controlNumber Control number.
148
     *
149
     * @return AbstractAccessKey
150
     */
151 5
    protected static function generateKey(
152
        $state,
153
        \DateTime $generatedAt,
154
        Cnpj $cnpj,
155
        Model $model,
156
        $sequence,
157
        $invoiceNumber,
158
        EmissionType $emissionType,
159
        $controlNumber
160
    ) {
161 5
        $yearMonth = $generatedAt->format('ym');
162 5
        $sequence = str_pad($sequence, 3, 0, STR_PAD_LEFT);
163 5
        $invoiceNumber = str_pad($invoiceNumber, 9, 0, STR_PAD_LEFT);
164 5
        $controlNumber = str_pad($controlNumber, 8, 0, STR_PAD_LEFT);
165
166 5
        $baseNumber = "{$state}{$yearMonth}{$cnpj}{$model}{$sequence}{$invoiceNumber}{$emissionType}{$controlNumber}";
167
168 5
        $digit = self::calculateDigitFrom($baseNumber);
169
170 5
        $instance = new static("{$baseNumber}{$digit}");
171 5
        $instance->generatedAt = $generatedAt;
172
173 5
        return $instance;
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 10
    public function format()
180
    {
181 10
        return trim(preg_replace(self::REGEX, self::MASK, "{$this}"));
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 120
    public function calculateDigit($baseNumber)
188
    {
189 120
        return self::calculateDigitFrom($baseNumber);
190
    }
191
192
    /**
193
     * Calculate check digit from base number.
194
     *
195
     * It is static because is used from generate static method.
196
     *
197
     * @param string $baseNumber Base numeric section to be calculate your digit.
198
     *
199
     * @return string
200
     */
201 120
    public static function calculateDigitFrom($baseNumber)
202
    {
203 120
        $calculator = new DigitCalculator($baseNumber);
204 120
        $calculator->useComplementaryInsteadOfModule();
205 120
        $calculator->withModule(DigitCalculator::MODULE_11);
206 120
        $calculator->replaceWhen('0', 10, 11);
207 120
        $digit = $calculator->calculate();
208
209 120
        return "{$digit}";
210
    }
211
}
212