Completed
Push — master ( cc5380...7c0124 )
by Andreas
02:30
created

CreditCard::detectBrand()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
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
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'      => null,
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
        if (false === $this->brand = $this->detectBrand()) {
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->detectBrand() can also be of type integer or false. However, the property $brand is declared as type string. 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...
120
            $this->brand = $brand;
121
        }
122
123
        if ($token) {
124
            $token instanceof Token
125
                ? $this->token = $token
126
                : $this->token = new Token($token);
127
        }
128
    }
129
130
    private function detectBrand()
131
    {
132
        $detector = new CreditCardDetector();
133
134
        return $detector->detect($this->number);
135
    }
136
137
    private function with($prop, $value)
138
    {
139
        $card = clone $this;
140
141
        $card->$prop = $value;
142
143
        return $card;
144
    }
145
146
    /**
147
     * Gets the number of card.
148
     *
149
     * @return string
150
     */
151
    public function getNumber()
152
    {
153
        return $this->number;
154
    }
155
156
    /**
157
     * Sets card number.
158
     *
159
     * @param  string $number
160
     * @return CreditCard
161
     */
162
    public function withNumber($number)
163
    {
164
        $card = $this->with('number', $number);
165
        $card->brand = $card->detectBrand();
0 ignored issues
show
Documentation Bug introduced by
It seems like $card->detectBrand() can also be of type integer or false. However, the property $brand is declared as type string. 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...
166
        $card->token = null;
167
168
        return $card;
169
    }
170
171
    /**
172
     * Gets card holder name.
173
     *
174
     * @return string
175
     */
176
    public function getHolderName()
177
    {
178
        return $this->holderName;
179
    }
180
181
    /**
182
     * Sets card holder name.
183
     *
184
     * @param  string $holderName
185
     * @return CreditCard
186
     */
187
    public function withHolderName($holderName)
188
    {
189
        $holderName = strtoupper($holderName);
190
        return $this->with('holderName', $holderName);
191
    }
192
193
    /**
194
     * Gets expiry date card.
195
     *
196
     * @return ExpireDate
197
     */
198
    public function getExpiryDate()
199
    {
200
        return $this->expiryDate;
201
    }
202
203
    /**
204
     * Sets expiry month of card.
205
     *
206
     * @param  ExpiryDate $expiryDate
207
     * @return CreditCard
208
     */
209
    public function withExpiryDate(ExpiryDate $expiryDate)
210
    {
211
        return $this->with('expiryDate', $expiryDate);
212
    }
213
214
    /**
215
     * Gets the brand of card.
216
     *
217
     * @return string
218
     */
219
    public function getBrand()
220
    {
221
        return $this->brand;
222
    }
223
224
    /**
225
     * Sets the brand of card.
226
     *
227
     * @param  string $brand
228
     * @return CreditCard
229
     */
230
    public function withBrand($brand)
231
    {
232
        return $this->with('brand', $brand);
233
    }
234
235
    /**
236
     * Gets card verification value (cvv).
237
     *
238
     * @return integer
239
     */
240
    public function getCvv()
241
    {
242
        return $this->cvv;
243
    }
244
245
    /**
246
     * Sets card verification value.
247
     *
248
     * @param  integer $cvv
249
     * @return CreditCard
250
     */
251
    public function withCvv($cvv)
252
    {
253
        return $this->with('cvv', $cvv);
254
    }
255
256
    /**
257
     * Check if cvv is required for credit card validation.
258
     *
259
     * @return boolean
260
     */
261
    public function isRequireCvv()
262
    {
263
        return $this->requireCvv;
264
    }
265
266
    /**
267
     * Gets referenece token of a credit card.
268
     *
269
     * @return Token
270
     */
271
    public function getToken()
272
    {
273
        return $this->token;
274
    }
275
276
    /**
277
     * Sets token value.
278
     *
279
     * @param  Token $token
280
     * @return CreditCard
281
     */
282
    public function withToken(Token $token)
283
    {
284
        $card = $this->with('token', $token);
285
286
        if (null !== $card->number) {
287
            $lastDigits = strlen($card->number) <= 4
288
                ? $card->number :
289
                substr($card->number, -4);
290
            $card->number = "XXXX-XXXX-XXXX-" . $lastDigits;
291
        }
292
293
        $card->cvv = null;
294
295
        return $card;
296
    }
297
298
    /**
299
     * Checks whether credit card has stored a Token reference or not.
300
     *
301
     * @return boolean
302
     */
303
    public function hasToken()
304
    {
305
        return null !== $this->token;
306
    }
307
308
    public function __clone()
309
    {
310
        if ($this->expiryDate) {
311
            $this->expiryDate = clone $this->expiryDate;
312
        }
313
    }
314
}
315