QrPaymentReferenceGenerator   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 94
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 14
eloc 41
c 2
b 0
f 0
dl 0
loc 94
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getReferenceNumber() 0 3 1
A getCustomerIdentificationNumber() 0 3 1
A loadValidatorMetadata() 0 23 1
A validateFullReference() 0 7 3
A doGenerate() 0 15 3
A generate() 0 5 1
A __construct() 0 6 2
A modulo10() 0 10 2
1
<?php declare(strict_types=1);
2
3
namespace Sprain\SwissQrBill\Reference;
4
5
use Sprain\SwissQrBill\String\StringModifier;
6
use Sprain\SwissQrBill\Validator\Exception\InvalidQrPaymentReferenceException;
7
use Sprain\SwissQrBill\Validator\SelfValidatableInterface;
8
use Sprain\SwissQrBill\Validator\SelfValidatableTrait;
9
use Symfony\Component\Validator\Constraints as Assert;
10
use Symfony\Component\Validator\Context\ExecutionContextInterface;
11
use Symfony\Component\Validator\Mapping\ClassMetadata;
12
13
final class QrPaymentReferenceGenerator implements SelfValidatableInterface
14
{
15
    use SelfValidatableTrait;
16
17
    private ?string $customerIdentificationNumber = null;
18
    private string $referenceNumber;
19
20
    public static function generate(?string $customerIdentificationNumber, string $referenceNumber): string
21
    {
22
        $qrPaymentReferenceGenerator = new self($customerIdentificationNumber, $referenceNumber);
23
24
        return $qrPaymentReferenceGenerator->doGenerate();
25
    }
26
27
    public function __construct(?string $customerIdentificationNumber, string $referenceNumber)
28
    {
29
        if (null !== $customerIdentificationNumber) {
30
            $this->customerIdentificationNumber = StringModifier::stripWhitespace($customerIdentificationNumber);
31
        }
32
        $this->referenceNumber = StringModifier::stripWhitespace($referenceNumber);
33
    }
34
35
    public function getCustomerIdentificationNumber(): ?string
36
    {
37
        return $this->customerIdentificationNumber;
38
    }
39
40
    public function getReferenceNumber(): ?string
41
    {
42
        return $this->referenceNumber;
43
    }
44
45
    public function doGenerate(): string
46
    {
47
        if (!$this->isValid()) {
48
            throw new InvalidQrPaymentReferenceException(
49
                'The provided data is not valid to generate a qr payment reference number.'
50
            );
51
        }
52
53
        $completeReferenceNumber  = $this->getCustomerIdentificationNumber();
54
55
        $strlen = $completeReferenceNumber ? strlen($completeReferenceNumber) : 0;
56
        $completeReferenceNumber .= str_pad($this->getReferenceNumber(), 26 - $strlen, '0', STR_PAD_LEFT);
57
        $completeReferenceNumber .= $this->modulo10($completeReferenceNumber);
58
59
        return $completeReferenceNumber;
60
    }
61
62
    public static function loadValidatorMetadata(ClassMetadata $metadata): void
63
    {
64
        $metadata->addPropertyConstraints('customerIdentificationNumber', [
65
            // Only numbers are allowed (including leading zeros)
66
            new Assert\Regex([
67
                'pattern' => '/^\d*$/',
68
                'match' => true
69
            ]),
70
            new Assert\Length([
71
                'max' => 11
72
            ]),
73
        ]);
74
75
        $metadata->addPropertyConstraints('referenceNumber', [
76
            // Only numbers are allowed (including leading zeros)
77
            new Assert\Regex([
78
                'pattern' => '/^\d*$/',
79
                'match' => true
80
            ]),
81
            new Assert\NotBlank()
82
        ]);
83
84
        $metadata->addConstraint(new Assert\Callback('validateFullReference'));
85
    }
86
87
    public function validateFullReference(ExecutionContextInterface $context): void
88
    {
89
        $strlenCustomerIdentificationNumber = $this->customerIdentificationNumber ? strlen($this->customerIdentificationNumber) : 0;
90
91
        if ($strlenCustomerIdentificationNumber + strlen($this->referenceNumber) > 26) {
92
            $context->buildViolation('The length of customer identification number + reference number may not exceed 26 characters in total.')
93
                ->addViolation();
94
        }
95
    }
96
97
    private function modulo10(string $number): int
98
    {
99
        $table = [0, 9, 4, 6, 8, 2, 7, 1, 3, 5];
100
        $next = 0;
101
        $strlen = strlen($number);
102
        for ($i = 0; $i < $strlen; $i++) {
103
            $next =  $table[($next + (int)$number[$i]) % 10];
104
        }
105
106
        return (10 - $next) % 10;
107
    }
108
}
109