Completed
Push — master ( d6a1be...0d1f44 )
by Barry vd.
12s queued 10s
created

AbstractRequest   F

Complexity

Total Complexity 71

Size/Duplication

Total Lines 639
Duplicated Lines 3.6 %

Coupling/Cohesion

Components 2
Dependencies 13

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 71
lcom 2
cbo 13
dl 23
loc 639
ccs 150
cts 150
cp 1
rs 2.681
c 0
b 0
f 0

45 Methods

Rating   Name   Duplication   Size   Complexity  
A setAmountInteger() 0 4 1
A setMoney() 0 8 1
A getCurrencies() 0 8 2
B getMoney() 0 40 11
A __construct() 0 6 1
A initialize() 0 12 2
A setParameter() 0 8 2
A getTestMode() 0 4 1
A setTestMode() 0 4 1
A getCard() 0 4 1
A setCard() 0 8 3
A getToken() 0 4 1
A setToken() 0 4 1
A getCardReference() 0 4 1
A setCardReference() 0 4 1
A getAmount() 0 10 2
A setAmount() 0 4 2
A getAmountInteger() 0 8 2
A getCurrency() 0 4 1
A setCurrency() 0 7 2
A getCurrencyNumeric() 12 12 3
A getCurrencyDecimalPlaces() 11 11 3
A formatCurrency() 0 7 1
A getDescription() 0 4 1
A setDescription() 0 4 1
A getTransactionId() 0 4 1
A setTransactionId() 0 4 1
A getTransactionReference() 0 4 1
A setTransactionReference() 0 4 1
A getItems() 0 4 1
A setItems() 0 8 3
A getClientIp() 0 4 1
A setClientIp() 0 4 1
A getReturnUrl() 0 4 1
A setReturnUrl() 0 4 1
A getCancelUrl() 0 4 1
A setCancelUrl() 0 4 1
A getNotifyUrl() 0 4 1
A setNotifyUrl() 0 4 1
A getIssuer() 0 4 1
A setIssuer() 0 4 1
A getPaymentMethod() 0 4 1
A setPaymentMethod() 0 4 1
A send() 0 6 1
A getResponse() 0 8 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AbstractRequest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractRequest, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Abstract Request
4
 */
5
6
namespace Omnipay\Common\Message;
7
8
use Money\Currencies\ISOCurrencies;
9
use Money\Currency;
10
use Money\Formatter\DecimalMoneyFormatter;
11
use Money\Money;
12
use Money\Number;
13
use Money\Parser\DecimalMoneyParser;
14
use Omnipay\Common\CreditCard;
15
use Omnipay\Common\Exception\InvalidRequestException;
16
use Omnipay\Common\Exception\RuntimeException;
17
use Omnipay\Common\Helper;
18
use Omnipay\Common\Http\Client;
19
use Omnipay\Common\Http\ClientInterface;
20
use Omnipay\Common\ItemBag;
21
use Omnipay\Common\ParametersTrait;
22
use Symfony\Component\HttpFoundation\ParameterBag;
23
use Symfony\Component\HttpFoundation\Request as HttpRequest;
24
25
/**
26
 * Abstract Request
27
 *
28
 * This abstract class implements RequestInterface and defines a basic
29
 * set of functions that all Omnipay Requests are intended to include.
30
 *
31
 * Requests of this class are usually created using the createRequest
32
 * function of the gateway and then actioned using methods within this
33
 * class or a class that extends this class.
34
 *
35
 * Example -- creating a request:
36
 *
37
 * <code>
38
 *   class MyRequest extends \Omnipay\Common\Message\AbstractRequest {};
39
 *
40
 *   class MyGateway extends \Omnipay\Common\AbstractGateway {
41
 *     function myRequest($parameters) {
42
 *       $this->createRequest('MyRequest', $parameters);
43
 *     }
44
 *   }
45
 *
46
 *   // Create the gateway object
47
 *   $gw = Omnipay::create('MyGateway');
48
 *
49
 *   // Create the request object
50
 *   $myRequest = $gw->myRequest($someParameters);
51
 * </code>
52
 *
53
 * Example -- validating and sending a request:
54
 *
55
 * <code>
56
 *   try {
57
 *     $myRequest->validate();
58
 *     $myResponse = $myRequest->send();
59
 *   } catch (InvalidRequestException $e) {
60
 *     print "Something went wrong: " . $e->getMessage() . "\n";
61
 *   }
62
 *   // now do something with the $myResponse object, test for success, etc.
63
 * </code>
64
 *
65
 */
66
abstract class AbstractRequest implements RequestInterface
67
{
68
    use ParametersTrait {
69
        setParameter as traitSetParameter;
70
    }
71
72
    /**
73
     * The request client.
74
     *
75
     * @var ClientInterface
76
     */
77
    protected $httpClient;
78
79
    /**
80
     * The HTTP request object.
81
     *
82
     * @var \Symfony\Component\HttpFoundation\Request
83
     */
84
    protected $httpRequest;
85
86
    /**
87
     * An associated ResponseInterface.
88
     *
89
     * @var ResponseInterface
90
     */
91
    protected $response;
92
93
    /**
94
     * @var ISOCurrencies
95
     */
96
    protected $currencies;
97
98
    /**
99
     * @var bool
100
     */
101
    protected $zeroAmountAllowed = true;
102
103
    /**
104
     * @var bool
105
     */
106
    protected $negativeAmountAllowed = false;
107
108
    /**
109
     * Create a new Request
110
     *
111
     * @param ClientInterface $httpClient  A HTTP client to make API calls with
112
     * @param HttpRequest     $httpRequest A Symfony HTTP request object
113
     */
114 177
    public function __construct(ClientInterface $httpClient, HttpRequest $httpRequest)
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $httpRequest. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
115
    {
116 177
        $this->httpClient = $httpClient;
117 177
        $this->httpRequest = $httpRequest;
118 177
        $this->initialize();
119 177
    }
120
121
    /**
122
     * Initialize the object with parameters.
123
     *
124
     * If any unknown parameters passed, they will be ignored.
125
     *
126
     * @param array $parameters An associative array of parameters
127
     *
128
     * @return $this
129
     * @throws RuntimeException
130
     */
131 177
    public function initialize(array $parameters = array())
132
    {
133 177
        if (null !== $this->response) {
134 3
            throw new RuntimeException('Request cannot be modified after it has been sent!');
135
        }
136
137 177
        $this->parameters = new ParameterBag;
138
139 177
        Helper::initialize($this, $parameters);
140
141 177
        return $this;
142
    }
143
144
    /**
145
     * Set a single parameter
146
     *
147
     * @param string $key The parameter key
148
     * @param mixed $value The value to set
149
     * @return $this
150
     * @throws RuntimeException if a request parameter is modified after the request has been sent.
151
     */
152 153
    protected function setParameter($key, $value)
153
    {
154 153
        if (null !== $this->response) {
155 3
            throw new RuntimeException('Request cannot be modified after it has been sent!');
156
        }
157
158 150
        return $this->traitSetParameter($key, $value);
159
    }
160
161
    /**
162
     * Gets the test mode of the request from the gateway.
163
     *
164
     * @return boolean
165
     */
166 3
    public function getTestMode()
167
    {
168 3
        return $this->getParameter('testMode');
169
    }
170
171
    /**
172
     * Sets the test mode of the request.
173
     *
174
     * @param boolean $value True for test mode on.
175
     * @return $this
176
     */
177 12
    public function setTestMode($value)
178
    {
179 12
        return $this->setParameter('testMode', $value);
180
    }
181
182
    /**
183
     * Get the card.
184
     *
185
     * @return CreditCard
186
     */
187 6
    public function getCard()
188
    {
189 6
        return $this->getParameter('card');
190
    }
191
192
    /**
193
     * Sets the card.
194
     *
195
     * @param CreditCard $value
196
     * @return $this
197
     */
198 6
    public function setCard($value)
199
    {
200 6
        if ($value && !$value instanceof CreditCard) {
201 3
            $value = new CreditCard($value);
202
        }
203
204 6
        return $this->setParameter('card', $value);
205
    }
206
207
    /**
208
     * Get the card token.
209
     *
210
     * @return string
211
     */
212 3
    public function getToken()
213
    {
214 3
        return $this->getParameter('token');
215
    }
216
217
    /**
218
     * Sets the card token.
219
     *
220
     * @param string $value
221
     * @return $this
222
     */
223 9
    public function setToken($value)
224
    {
225 9
        return $this->setParameter('token', $value);
226
    }
227
228
    /**
229
     * Get the card reference.
230
     *
231
     * @return string
232
     */
233 3
    public function getCardReference()
234
    {
235 3
        return $this->getParameter('cardReference');
236
    }
237
238
    /**
239
     * Sets the card reference.
240
     *
241
     * @param string $value
242
     * @return $this
243
     */
244 3
    public function setCardReference($value)
245
    {
246 3
        return $this->setParameter('cardReference', $value);
247
    }
248
249
    /**
250
     * @return ISOCurrencies
251
     */
252 72
    protected function getCurrencies()
253
    {
254 72
        if ($this->currencies === null) {
255 72
            $this->currencies = new ISOCurrencies();
256
        }
257
258 72
        return $this->currencies;
259
    }
260
261
    /**
262
     * @param  string|int|null $amount
263
     * @return null|Money
264
     * @throws InvalidRequestException
265
     */
266 69
    private function getMoney($amount = null)
267
    {
268 69
        $currencyCode = $this->getCurrency() ?: 'USD';
269 69
        $currency = new Currency($currencyCode);
270
271 69
        $amount = $amount !== null ? $amount : $this->getParameter('amount');
272
273 69
        if ($amount === null) {
274 6
            return null;
275 63
        } elseif ($amount instanceof Money) {
276 3
            $money = $amount;
277 60
        } elseif (is_integer($amount)) {
278 3
            $money = new Money($amount, $currency);
279
        } else {
280 57
            $moneyParser = new DecimalMoneyParser($this->getCurrencies());
281
282 57
            $number = Number::fromString($amount);
283
284
            // Check for rounding that may occur if too many significant decimal digits are supplied.
285 51
            $decimal_count = strlen($number->getFractionalPart());
286 51
            $subunit = $this->getCurrencies()->subunitFor($currency);
287 51
            if ($decimal_count > $subunit) {
288 6
                throw new InvalidRequestException('Amount precision is too high for currency.');
289
            }
290
291 45
            $money = $moneyParser->parse((string) $number, $currency);
292
        }
293
294
        // Check for a negative amount.
295 51
        if (!$this->negativeAmountAllowed && $money->isNegative()) {
296 6
            throw new InvalidRequestException('A negative amount is not allowed.');
297
        }
298
299
        // Check for a zero amount.
300 45
        if (!$this->zeroAmountAllowed && $money->isZero()) {
301 3
            throw new InvalidRequestException('A zero amount is not allowed.');
302
        }
303
304 42
        return $money;
305
    }
306
307
    /**
308
     * Validates and returns the formatted amount.
309
     *
310
     * @throws InvalidRequestException on any validation failure.
311
     * @return string The amount formatted to the correct number of decimal places for the selected currency.
312
     */
313 54
    public function getAmount()
314
    {
315 54
        $money = $this->getMoney();
316
317 33
        if ($money !== null) {
318 30
            $moneyFormatter = new DecimalMoneyFormatter($this->getCurrencies());
319
320 30
            return $moneyFormatter->format($money);
321
        }
322 3
    }
323
324
    /**
325
     * Sets the payment amount.
326
     *
327
     * @param string|null $value
328
     * @return $this
329
     */
330 57
    public function setAmount($value)
331
    {
332 57
        return $this->setParameter('amount', $value !== null ? (string) $value : null);
333
    }
334
335
    /**
336
     * Get the payment amount as an integer.
337
     *
338
     * @return integer
339
     */
340 15
    public function getAmountInteger()
341
    {
342 15
        $money = $this->getMoney();
343
344 15
        if ($money !== null) {
345 12
            return (int) $money->getAmount();
346
        }
347 3
    }
348
349
    /**
350
     * Sets the payment amount as integer.
351
     *
352
     * @param int $value
353
     * @return $this
354
     */
355 6
    public function setAmountInteger($value)
356
    {
357 6
        return $this->setParameter('amount', (int) $value);
358
    }
359
360
    /**
361
     * Sets the payment amount as integer.
362
     *
363
     * @param Money $value
364
     * @return $this
365
     */
366 3
    public function setMoney(Money $value)
367
    {
368 3
        $currency = $value->getCurrency()->getCode();
369
370 3
        $this->setCurrency($currency);
371
372 3
        return $this->setParameter('amount', $value);
373
    }
374
375
    /**
376
     * Get the payment currency code.
377
     *
378
     * @return string
379
     */
380 96
    public function getCurrency()
381
    {
382 96
        return $this->getParameter('currency');
383
    }
384
385
    /**
386
     * Sets the payment currency code.
387
     *
388
     * @param string $value
389
     * @return $this
390
     */
391 45
    public function setCurrency($value)
392
    {
393 45
        if ($value !== null) {
394 39
            $value = strtoupper($value);
395
        }
396 45
        return $this->setParameter('currency', $value);
397
    }
398
399
    /**
400
     * Get the payment currency number.
401
     *
402
     * @return string|null
403
     */
404 12 View Code Duplication
    public function getCurrencyNumeric()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
405
    {
406 12
        if (! $this->getCurrency()) {
407 6
            return null;
408
        }
409
410 6
        $currency = new Currency($this->getCurrency());
411
412 6
        if ($this->getCurrencies()->contains($currency)) {
413 3
            return (string) $this->getCurrencies()->numericCodeFor($currency);
414
        }
415 3
    }
416
417
    /**
418
     * Get the number of decimal places in the payment currency.
419
     *
420
     * @return integer
421
     */
422 6 View Code Duplication
    public function getCurrencyDecimalPlaces()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
423
    {
424 6
        if ($this->getCurrency()) {
425 3
            $currency = new Currency($this->getCurrency());
426 3
            if ($this->getCurrencies()->contains($currency)) {
427 3
                return $this->getCurrencies()->subunitFor($currency);
428
            }
429
        }
430
431 3
        return 2;
432
    }
433
434
    /**
435
     * Format an amount for the payment currency.
436
     *
437
     * @param string $amount
438
     * @return string
439
     */
440 6
    public function formatCurrency($amount)
441
    {
442 6
        $money = $this->getMoney((string) $amount);
443 6
        $formatter = new DecimalMoneyFormatter($this->getCurrencies());
444
445 6
        return $formatter->format($money);
0 ignored issues
show
Bug introduced by
It seems like $money defined by $this->getMoney((string) $amount) on line 442 can be null; however, Money\Formatter\DecimalMoneyFormatter::format() 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...
446
    }
447
448
    /**
449
     * Get the request description.
450
     *
451
     * @return string
452
     */
453 3
    public function getDescription()
454
    {
455 3
        return $this->getParameter('description');
456
    }
457
458
    /**
459
     * Sets the request description.
460
     *
461
     * @param string $value
462
     * @return $this
463
     */
464 3
    public function setDescription($value)
465
    {
466 3
        return $this->setParameter('description', $value);
467
    }
468
469
    /**
470
     * Get the transaction ID.
471
     *
472
     * The transaction ID is the identifier generated by the merchant website.
473
     *
474
     * @return string
475
     */
476 3
    public function getTransactionId()
477
    {
478 3
        return $this->getParameter('transactionId');
479
    }
480
481
    /**
482
     * Sets the transaction ID.
483
     *
484
     * @param string $value
485
     * @return $this
486
     */
487 3
    public function setTransactionId($value)
488
    {
489 3
        return $this->setParameter('transactionId', $value);
490
    }
491
492
    /**
493
     * Get the transaction reference.
494
     *
495
     * The transaction reference is the identifier generated by the remote
496
     * payment gateway.
497
     *
498
     * @return string
499
     */
500 3
    public function getTransactionReference()
501
    {
502 3
        return $this->getParameter('transactionReference');
503
    }
504
505
    /**
506
     * Sets the transaction reference.
507
     *
508
     * @param string $value
509
     * @return $this
510
     */
511 3
    public function setTransactionReference($value)
512
    {
513 3
        return $this->setParameter('transactionReference', $value);
514
    }
515
516
    /**
517
     * A list of items in this order
518
     *
519
     * @return ItemBag|null A bag containing items in this order
520
     */
521 6
    public function getItems()
522
    {
523 6
        return $this->getParameter('items');
524
    }
525
526
    /**
527
     * Set the items in this order
528
     *
529
     * @param ItemBag|array $items An array of items in this order
530
     * @return $this
531
     */
532 6
    public function setItems($items)
533
    {
534 6
        if ($items && !$items instanceof ItemBag) {
535 3
            $items = new ItemBag($items);
536
        }
537
538 6
        return $this->setParameter('items', $items);
539
    }
540
541
    /**
542
     * Get the client IP address.
543
     *
544
     * @return string
545
     */
546 3
    public function getClientIp()
547
    {
548 3
        return $this->getParameter('clientIp');
549
    }
550
551
    /**
552
     * Sets the client IP address.
553
     *
554
     * @param string $value
555
     * @return $this
556
     */
557 3
    public function setClientIp($value)
558
    {
559 3
        return $this->setParameter('clientIp', $value);
560
    }
561
562
    /**
563
     * Get the request return URL.
564
     *
565
     * @return string
566
     */
567 3
    public function getReturnUrl()
568
    {
569 3
        return $this->getParameter('returnUrl');
570
    }
571
572
    /**
573
     * Sets the request return URL.
574
     *
575
     * @param string $value
576
     * @return $this
577
     */
578 3
    public function setReturnUrl($value)
579
    {
580 3
        return $this->setParameter('returnUrl', $value);
581
    }
582
583
    /**
584
     * Get the request cancel URL.
585
     *
586
     * @return string
587
     */
588 3
    public function getCancelUrl()
589
    {
590 3
        return $this->getParameter('cancelUrl');
591
    }
592
593
    /**
594
     * Sets the request cancel URL.
595
     *
596
     * @param string $value
597
     * @return $this
598
     */
599 3
    public function setCancelUrl($value)
600
    {
601 3
        return $this->setParameter('cancelUrl', $value);
602
    }
603
604
    /**
605
     * Get the request notify URL.
606
     *
607
     * @return string
608
     */
609 3
    public function getNotifyUrl()
610
    {
611 3
        return $this->getParameter('notifyUrl');
612
    }
613
614
    /**
615
     * Sets the request notify URL.
616
     *
617
     * @param string $value
618
     * @return $this
619
     */
620 3
    public function setNotifyUrl($value)
621
    {
622 3
        return $this->setParameter('notifyUrl', $value);
623
    }
624
625
    /**
626
     * Get the payment issuer.
627
     *
628
     * This field is used by some European gateways, and normally represents
629
     * the bank where an account is held (separate from the card brand).
630
     *
631
     * @return string
632
     */
633 3
    public function getIssuer()
634
    {
635 3
        return $this->getParameter('issuer');
636
    }
637
638
    /**
639
     * Set the payment issuer.
640
     *
641
     * This field is used by some European gateways, and normally represents
642
     * the bank where an account is held (separate from the card brand).
643
     *
644
     * @param string $value
645
     * @return $this
646
     */
647 3
    public function setIssuer($value)
648
    {
649 3
        return $this->setParameter('issuer', $value);
650
    }
651
652
    /**
653
     * Get the payment issuer.
654
     *
655
     * This field is used by some European gateways, which support
656
     * multiple payment providers with a single API.
657
     *
658
     * @return string
659
     */
660 3
    public function getPaymentMethod()
661
    {
662 3
        return $this->getParameter('paymentMethod');
663
    }
664
665
    /**
666
     * Set the payment method.
667
     *
668
     * This field is used by some European gateways, which support
669
     * multiple payment providers with a single API.
670
     *
671
     * @param string $value
672
     * @return $this
673
     */
674 3
    public function setPaymentMethod($value)
675
    {
676 3
        return $this->setParameter('paymentMethod', $value);
677
    }
678
679
    /**
680
     * Send the request
681
     *
682
     * @return ResponseInterface
683
     */
684 12
    public function send()
685
    {
686 12
        $data = $this->getData();
687
688 12
        return $this->sendData($data);
689
    }
690
691
    /**
692
     * Get the associated Response.
693
     *
694
     * @return ResponseInterface
695
     */
696 6
    public function getResponse()
697
    {
698 6
        if (null === $this->response) {
699 3
            throw new RuntimeException('You must call send() before accessing the Response!');
700
        }
701
702 3
        return $this->response;
703
    }
704
}
705