Completed
Push — master ( d20358...f1cd37 )
by Andreas
02:06
created

CreditCard::token()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5
/*
6
 * This file is part of the Larium CreditCard package.
7
 *
8
 * (c) Andreas Kollaros <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Larium\CreditCard;
15
16
/**
17
 * CreditCard class acts as value object.
18
 *
19
 * @author  Andreas Kollaros <[email protected]>
20
 */
21
final class CreditCard
22
{
23
    const VISA               = 'visa';
24
    const MASTER             = 'master';
25
    const DISCOVER           = 'discover';
26
    const AMEX               = 'american_express';
27
    const DINERS_CLUB        = 'diners_club';
28
    const JCB                = 'jcb';
29
    const SWITCH_BRAND       = 'switch';
30
    const SOLO               = 'solo';
31
    const DANKORT            = 'dankort';
32
    const MAESTRO            = 'maestro';
33
    const FORBRUGSFORENINGEN = 'forbrugsforeningen';
34
    const LASER              = 'laser';
35
    const UNIONPAY           = 'unionpay';
36
37
    /**
38
     * Card holder name.
39
     * Should be in upper case.
40
     *
41
     * @var string
42
     */
43
    private $holderName;
44
45
    /**
46
     * Expire date of card as value object
47
     *
48
     * @var ExpiryDate
49
     */
50
    private $expiryDate;
51
52
    /**
53
     * The brand of card.
54
     *
55
     * @var string|false
56
     */
57
    private $brand;
58
59
    /**
60
     * The number of card.
61
     *
62
     * @var string
63
     */
64
    private $number;
65
66
    /**
67
     * The verification value of card (cvv).
68
     * 3 or 4 digits.
69
     *
70
     * @var integer
71
     */
72
    private $cvv;
73
74
    /**
75
     * Whether card is require verification value to be present.
76
     *
77
     * @var boolean
78
     */
79
    private $requireCvv = true;
80
81
    /**
82
     * Token stored from a real credit card and can be used for purchases.
83
     *
84
     * @var Token
85
     */
86
    private $token;
87
88
    public function __construct(array $options = array())
89
    {
90
        $default = array(
91
            'holderName' => null,
92
            'month'      => 1,
93
            'year'       => 1970,
94
            'brand'      => '',
95
            'number'     => null,
96
            'cvv'        => null,
97
            'requireCvv' => true,
98
            'token'      => null
99
        );
100
101
        $options = array_intersect_key($options, $default);
102
103
        $options = array_replace($default, $options);
104
105
        $month = $options['month'];
106
        $year  = $options['year'];
107
        $brand = $options['brand'];
108
        $token = $options['token'];
109
110
        unset($options['month'], $options['year'], $options['brand'], $options['token']);
111
112
        $expiryDate = new ExpiryDate($month, $year);
113
        $this->expiryDate = $expiryDate;
114
115
        foreach ($options as $prop => $value) {
116
            $this->$prop = $value;
117
        }
118
119
        $this->detectBrand($brand);
120
121
        $this->token($token);
122
    }
123
124
    private function token($token)
125
    {
126
        if ($token instanceof Token) {
127
            $this->token = $token;
128
129
            return;
130
        }
131
132
        $this->token = new Token($token);
133
    }
134
135
    /**
136
     * @param string $brand
137
     * @return void
138
     */
139
    private function detectBrand($brand)
140
    {
141
        $detector = new CreditCardDetector();
142
143
        if (false === $this->brand = $detector->detect($this->number)) {
0 ignored issues
show
Documentation Bug introduced by
It seems like $detector->detect($this->number) can also be of type integer. However, the property $brand is declared as type string|false. 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...
144
            $this->brand = $brand;
145
        };
146
    }
147
148
    /**
149
     * @param string $prop
150
     * @param mixed $value
151
     * @return CreditCard
152
     */
153
    private function with($prop, $value)
154
    {
155
        $card = clone $this;
156
157
        $card->$prop = $value;
158
159
        return $card;
160
    }
161
162
    /**
163
     * Gets the number of card.
164
     *
165
     * @return string
166
     */
167
    public function getNumber()
168
    {
169
        return $this->number;
170
    }
171
172
    /**
173
     * Sets card number.
174
     *
175
     * @param  string $number
176
     * @return CreditCard
177
     */
178
    public function withNumber($number)
179
    {
180
        $card = $this->with('number', $number);
181
        $card->brand = $card->detectBrand();
0 ignored issues
show
Bug introduced by
The call to detectBrand() misses a required argument $brand.

This check looks for function calls that miss required arguments.

Loading history...
Bug introduced by
Are you sure the assignment to $card->brand is correct as $card->detectBrand() (which targets Larium\CreditCard\CreditCard::detectBrand()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
182
        $card->token = null;
183
184
        return $card;
185
    }
186
187
    /**
188
     * Gets card holder name.
189
     *
190
     * @return string
191
     */
192
    public function getHolderName()
193
    {
194
        return $this->holderName;
195
    }
196
197
    /**
198
     * Sets card holder name.
199
     *
200
     * @param  string $holderName
201
     * @return CreditCard
202
     */
203
    public function withHolderName($holderName)
204
    {
205
        $holderName = strtoupper($holderName);
206
        return $this->with('holderName', $holderName);
207
    }
208
209
    /**
210
     * Gets expiry date card.
211
     *
212
     * @return ExpiryDate
213
     */
214
    public function getExpiryDate()
215
    {
216
        return $this->expiryDate;
217
    }
218
219
    /**
220
     * Sets expiry month of card.
221
     *
222
     * @param  ExpiryDate $expiryDate
223
     * @return CreditCard
224
     */
225
    public function withExpiryDate(ExpiryDate $expiryDate)
226
    {
227
        return $this->with('expiryDate', $expiryDate);
228
    }
229
230
    /**
231
     * Gets the brand of card.
232
     *
233
     * @return string
234
     */
235
    public function getBrand()
236
    {
237
        return $this->brand;
238
    }
239
240
    /**
241
     * Sets the brand of card.
242
     *
243
     * @param  string $brand
244
     * @return CreditCard
245
     */
246
    public function withBrand($brand)
247
    {
248
        return $this->with('brand', $brand);
249
    }
250
251
    /**
252
     * Gets card verification value (cvv).
253
     *
254
     * @return integer
255
     */
256
    public function getCvv()
257
    {
258
        return $this->cvv;
259
    }
260
261
    /**
262
     * Sets card verification value.
263
     *
264
     * @param  integer $cvv
265
     * @return CreditCard
266
     */
267
    public function withCvv($cvv)
268
    {
269
        return $this->with('cvv', $cvv);
270
    }
271
272
    /**
273
     * Check if cvv is required for credit card validation.
274
     *
275
     * @return boolean
276
     */
277
    public function isRequireCvv()
278
    {
279
        return $this->requireCvv;
280
    }
281
282
    /**
283
     * Gets referenece token of a credit card.
284
     *
285
     * @return Token
286
     */
287
    public function getToken()
288
    {
289
        return $this->token;
290
    }
291
292
    /**
293
     * Sets token value.
294
     *
295
     * @param  Token $token
296
     * @return CreditCard
297
     */
298
    public function withToken(Token $token)
299
    {
300
        $card = $this->with('token', $token);
301
302
        if (null !== $card->number) {
303
            $lastDigits = strlen($card->number) <= 4
304
                ? $card->number :
305
                substr($card->number, -4);
306
            $card->number = "XXXX-XXXX-XXXX-" . $lastDigits;
307
        }
308
309
        $card->cvv = null;
310
311
        return $card;
312
    }
313
314
    /**
315
     * Checks whether credit card has stored a Token reference or not.
316
     *
317
     * @return boolean
318
     */
319
    public function hasToken()
320
    {
321
        return null !== $this->token;
322
    }
323
324
    public function __clone()
325
    {
326
        if ($this->expiryDate) {
327
            $this->expiryDate = clone $this->expiryDate;
328
        }
329
    }
330
}
331