Passed
Branch main (274970)
by Antonio Oertel
03:05
created

AbstractAccessKey::generateKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 9
nc 1
nop 8
dl 0
loc 23
ccs 10
cts 10
cp 1
crap 1
rs 9.9666
c 1
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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 130
    public function __construct(string $accessKey)
82
    {
83 130
        $accessKey = preg_replace('/\D/', '', $accessKey);
84 130
        parent::__construct($accessKey, static::LENGTH, static::NUMBER_OF_DIGITS, static::LABEL);
85 30
        $this->validateModel($accessKey);
86 29
        $this->loadFromKey($accessKey);
87
    }
88
89 62
    public static function createFromString(string $number)
90
    {
91 62
        return parent::tryCreateFromString(static::class, $number, self::LENGTH, self::NUMBER_OF_DIGITS, self::LABEL);
92
    }
93
94
    /**
95
     * @return Model
96
     */
97
    abstract protected function defaultModel() : Model;
98
99 30
    protected function validateModel(string $accessKey)
100
    {
101 30
        $model = new Model(substr($accessKey, 20, 2));
0 ignored issues
show
Bug introduced by
substr($accessKey, 20, 2) of type string is incompatible with the type integer expected by parameter $model of Brazanation\Documents\Sped\Model::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
        $model = new Model(/** @scrutinizer ignore-type */ substr($accessKey, 20, 2));
Loading history...
102 30
        if (!$this->defaultModel()->equalsTo($model)) {
103 1
            throw DocumentModel::invalid($accessKey, static::LABEL);
104
        }
105
    }
106
107 29
    private function loadFromKey(string $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');
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));
0 ignored issues
show
Bug introduced by
substr($accessKey, $startPosition, 2) of type string is incompatible with the type integer expected by parameter $model of Brazanation\Documents\Sped\Model::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
        $this->model = new Model(/** @scrutinizer ignore-type */ substr($accessKey, $startPosition, 2));
Loading history...
120
121 29
        $startPosition += 2;
122 29
        $this->sequence = substr($accessKey, $startPosition, 3);
0 ignored issues
show
Documentation Bug introduced by
The property $sequence was declared of type integer, but substr($accessKey, $startPosition, 3) 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...
123
124 29
        $startPosition += 3;
125 29
        $this->invoiceNumber = substr($accessKey, $startPosition, 9);
0 ignored issues
show
Documentation Bug introduced by
The property $invoiceNumber was declared of type integer, but substr($accessKey, $startPosition, 9) 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...
126
127 29
        $startPosition += 9;
128 29
        $this->emissionType = new EmissionType(substr($accessKey, $startPosition, 1));
0 ignored issues
show
Bug introduced by
substr($accessKey, $startPosition, 1) of type string is incompatible with the type integer expected by parameter $type of Brazanation\Documents\Sp...sionType::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

128
        $this->emissionType = new EmissionType(/** @scrutinizer ignore-type */ substr($accessKey, $startPosition, 1));
Loading history...
129
130 29
        $startPosition += 1;
131 29
        $this->controlNumber = substr($accessKey, $startPosition, 8);
0 ignored issues
show
Documentation Bug introduced by
The property $controlNumber was declared of type integer, but substr($accessKey, $startPosition, 8) 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...
132
133 29
        $startPosition += 8;
134 29
        $this->digit = substr($accessKey, $startPosition, 1);
135
    }
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
        int $state,
153
        \DateTime $generatedAt,
154
        Cnpj $cnpj,
155
        Model $model,
156
        int $sequence,
157
        int $invoiceNumber,
158
        EmissionType $emissionType,
159
        int $controlNumber
160
    ) : AbstractAccessKey {
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() : string
180
    {
181 10
        return trim(preg_replace(self::REGEX, self::MASK, "{$this}"));
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 120
    public function calculateDigit(string $baseNumber) : string
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(string $baseNumber) : string
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