Completed
Push — wip ( 681ec1 )
by z38
02:37
created

BankCreditTransfer::fromQRCode()   D

Complexity

Conditions 9
Paths 35

Size

Total Lines 33
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 9.732

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 19
cts 24
cp 0.7917
rs 4.909
c 0
b 0
f 0
cc 9
eloc 21
nc 35
nop 4
crap 9.732
1
<?php
2
3
namespace Z38\SwissPayment\TransactionInformation;
4
5
use DOMDocument;
6
use InvalidArgumentException;
7
use Z38\SwissPayment\BIC;
8
use Z38\SwissPayment\FinancialInstitutionInterface;
9
use Z38\SwissPayment\IBAN;
10
use Z38\SwissPayment\IID;
11
use Z38\SwissPayment\Money;
12
use Z38\SwissPayment\PaymentInformation\PaymentInformation;
13
use Z38\SwissPayment\PostalAddressInterface;
14
use Z38\SwissPayment\QRCode;
15
use Z38\SwissPayment\QRReference;
16
use Z38\SwissPayment\RemittanceInformation\CreditorReferenceInformation;
17
use Z38\SwissPayment\RemittanceInformation\QRReferenceInformation;
18
use Z38\SwissPayment\RemittanceInformation\UnstructuredInformation;
19
20
/**
21
 * BankCreditTransfer contains all the information about a type 3 transaction.
22
 */
23
class BankCreditTransfer extends CreditTransfer
24
{
25
    /**
26
     * @var IBAN
27
     */
28
    protected $creditorIBAN;
29
30
    /**
31
     * @var FinancialInstitutionInterface
32
     */
33
    protected $creditorAgent;
34
35
    /**
36
     * @var string|null
37
     */
38
    protected $ultimateCreditorName;
39
40
    /**
41
     * @var PostalAddressInterface|null
42
     */
43
    protected $ultimateCreditorAddress;
44
45
    /**
46
     * {@inheritdoc}
47
     *
48
     * @param IBAN         $creditorIBAN  IBAN of the creditor
49
     * @param BIC|IID|null $creditorAgent BIC or IID of the creditor's financial institution
50
     *
51
     * @throws \InvalidArgumentException When the amount is not in EUR or CHF or when the creditor agent is not BIC or IID.
52
     */
53 4
    public function __construct($instructionId, $endToEndId, Money\Money $amount, $creditorName, PostalAddressInterface $creditorAddress, IBAN $creditorIBAN, FinancialInstitutionInterface $creditorAgent = null)
54
    {
55 4
        if (!$amount instanceof Money\EUR && !$amount instanceof Money\CHF) {
56 1
            throw new InvalidArgumentException(sprintf(
57 1
                'The amount must be an instance of Z38\SwissPayment\Money\EUR or Z38\SwissPayment\Money\CHF (instance of %s given).',
58 1
                get_class($amount)
59 1
            ));
60
        }
61
62 3
        if ($creditorAgent !== null && !$creditorAgent instanceof BIC && !$creditorAgent instanceof IID) {
63 1
            throw new InvalidArgumentException('The creditor agent must be an instance of BIC or IID.');
64
        }
65
66 2
        parent::__construct($instructionId, $endToEndId, $amount, $creditorName, $creditorAddress);
67
68 2
        $this->creditorIBAN = $creditorIBAN;
69 2
        $this->creditorAgent = $creditorAgent;
70 2
    }
71
72
    /**
73
     * Constructs an instruction from a QR code.
74
     *
75
     * @param string $instructionId Identifier of the instruction (should be unique within the message)
76
     * @param string $endToEndId    End-To-End Identifier of the instruction (passed unchanged along the complete processing chain)
77
     * @param QRCode $code          QR Code
78
     * @params Money $amount Amount if it is not part of the code, null otherwise.
79
     *
80
     * @returns PaymentInformation
81
     *
82
     * @throws InvalidArgumentException When the amount is invalid
83
     */
84 2
    public static function fromQRCode($instructionId, $endToEndId, QRCode $code, Money\Money $amount = null)
85
    {
86 2
        if (is_null($code->getAmount()) === is_null($amount)) {
87
            throw new InvalidArgumentException('Amount needs to be passed when it is not part of the code.');
88
        }
89 2
        $amount = $amount ?: $code->getAmount();
90 2
        if ($amount->getCurrency() !== $code->getCurrency()) {
91
            throw new InvalidArgumentException(sprintf('Amount needs to be in %s.', $code->getCurrency()));
92
        }
93
94 2
        $transfer = new self($instructionId, $endToEndId, $amount, $code->getCreditorName(), $code->getCreditorAddress(), $code->getCreditorAccount());
0 ignored issues
show
Bug introduced by
It seems like $amount defined by $amount ?: $code->getAmount() on line 89 can be null; however, Z38\SwissPayment\Transac...Transfer::__construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
95
96 2
        if ($code->getUltimateCreditorName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $code->getUltimateCreditorName() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
97 2
            $transfer->setUltimateCreditorName($code->getUltimateCreditorName());
98 2
            $transfer->setUltimateCreditorAddress($code->getUltimateCreditorAddress());
0 ignored issues
show
Bug introduced by
It seems like $code->getUltimateCreditorAddress() can be null; however, setUltimateCreditorAddress() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
99 2
        }
100
101 2
        if ($code->getUltimateDebtorName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $code->getUltimateDebtorName() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
102 2
            $transfer->setUltimateDebtorName($code->getUltimateDebtorName());
103 2
            $transfer->setUltimateDebtorAddress($code->getUltimateDebtorAddress());
0 ignored issues
show
Bug introduced by
It seems like $code->getUltimateDebtorAddress() can be null; however, setUltimateDebtorAddress() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
104 2
        }
105
106 2
        $reference = $code->getReference();
107 2
        if ($reference instanceof QRReference) {
108 2
            $transfer->setRemittanceInformation(new QRReferenceInformation($reference, $code->getUnstructuredMessage()));
109 2
        } elseif ($reference instanceof CreditorReference) {
0 ignored issues
show
Bug introduced by
The class Z38\SwissPayment\Transac...ation\CreditorReference does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
110
            $transfer->setRemittanceInformation(new CreditorReferenceInformation($reference));
111 2
        } elseif ($code->getUnstructuredMessage() !== null) {
112
            $transfer->setRemittanceInformation(new UnstructuredInformation($code->getUnstructuredMessage()));
113
        }
114
115 2
        return $transfer;
116
    }
117
118
    /**
119
     * Sets the name of the ultimate creditor
120
     *
121
     * @param string|null $name
122
     *
123
     * @return self
124
     */
125 2
    public function setUltimateCreditorName($name)
126
    {
127 2
        $this->ultimateCreditorName = $name;
128
129 2
        return $this;
130
    }
131
132
    /**
133
     * Sets the address of the ultimate creditor
134
     *
135
     * @param PostalAddressInterface|null $address
136
     *
137
     * @return self
138
     */
139 2
    public function setUltimateCreditorAddress(PostalAddressInterface $address)
140
    {
141 2
        $this->ultimateCreditorAddress = $address;
142
143 2
        return $this;
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149 2
    public function asDom(DOMDocument $doc, PaymentInformation $paymentInformation)
150
    {
151 2
        $root = $this->buildHeader($doc, $paymentInformation);
152
153 2
        if ($this->creditorAgent !== null) {
154 2
            $creditorAgent = $doc->createElement('CdtrAgt');
155 2
            $creditorAgent->appendChild($this->creditorAgent->asDom($doc));
156 2
            $root->appendChild($creditorAgent);
157 2
        }
158
159 2
        $root->appendChild($this->buildCreditor($doc));
160
161 2
        $creditorAccount = $doc->createElement('CdtrAcct');
162 2
        $creditorAccount->appendChild($this->creditorIBAN->asDom($doc));
163 2
        $root->appendChild($creditorAccount);
164
165 2
        if (strlen($this->ultimateCreditorName) || $this->ultimateCreditorAddress !== null) {
166 2
            $ultimateCreditor = $doc->createElement('UltmtCdtr');
167 2
            if (strlen($this->ultimateCreditorName)) {
168 2
                $ultimateCreditor->appendChild($doc->createElement('Nm', $this->ultimateCreditorName));
169 2
            }
170 2
            if ($this->ultimateCreditorAddress !== null) {
171 2
                $ultimateCreditor->appendChild($this->ultimateCreditorAddress->asDom($doc));
172 2
            }
173 2
            $root->appendChild($ultimateCreditor);
174 2
        }
175
176 2
        $this->appendPurpose($doc, $root);
177
178 2
        $this->appendRemittanceInformation($doc, $root);
179
180 2
        return $root;
181
    }
182
}
183