Issues (19)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/LaraCart.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace LukePOLO\LaraCart;
4
5
use Illuminate\Auth\AuthManager;
6
use Illuminate\Contracts\Events\Dispatcher;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Session\SessionManager;
9
use LukePOLO\LaraCart\Contracts\CouponContract;
10
use LukePOLO\LaraCart\Contracts\LaraCartContract;
11
use LukePOLO\LaraCart\Exceptions\ModelNotFound;
12
13
/**
14
 * Class LaraCart.
15
 */
16
class LaraCart implements LaraCartContract
17
{
18
    const SERVICE = 'laracart';
19
    const HASH = 'generateCartHash';
20
    const RANHASH = 'generateRandomCartItemHash';
21
22
    protected $events;
23
    protected $session;
24
    protected $authManager;
25
26
    public $cart;
27
    public $prefix;
28
    public $itemModel;
29
    public $itemModelRelations;
30
31
    /**
32
     * LaraCart constructor.
33
     *
34
     * @param SessionManager $session
35
     * @param Dispatcher     $events
36
     * @param AuthManager    $authManager
37
     */
38
    public function __construct(SessionManager $session, Dispatcher $events, AuthManager $authManager)
39
    {
40
        $this->session = $session;
41
        $this->events = $events;
42
        $this->authManager = $authManager;
43
        $this->prefix = config('laracart.cache_prefix', 'laracart');
44
        $this->itemModel = config('laracart.item_model', null);
45
        $this->itemModelRelations = config('laracart.item_model_relations', []);
46
47
        $this->setInstance($this->session->get($this->prefix.'.instance', 'default'));
48
    }
49
50
    /**
51
     * Gets all current instances inside the session.
52
     *
53
     * @return mixed
54
     */
55
    public function getInstances()
56
    {
57
        return $this->session->get($this->prefix.'.instances', []);
58
    }
59
60
    /**
61
     * Sets and Gets the instance of the cart in the session we should be using.
62
     *
63
     * @param string $instance
64
     *
65
     * @return LaraCart
66
     */
67
    public function setInstance($instance = 'default')
68
    {
69
        $this->get($instance);
70
71
        $this->session->set($this->prefix.'.instance', $instance);
72
73
        if (!in_array($instance, $this->getInstances())) {
74
            $this->session->push($this->prefix.'.instances', $instance);
75
        }
76
        $this->events->fire('laracart.new');
77
78
        return $this;
79
    }
80
81
    /**
82
     * Gets the instance in the session.
83
     *
84
     * @param string $instance
85
     *
86
     * @return $this cart instance
87
     */
88
    public function get($instance = 'default')
89
    {
90
        if (config('laracart.cross_devices', false) && $this->authManager->check()) {
91
            if (!empty($cartSessionID = $this->authManager->user()->cart_session_id)) {
92
                $this->session->setId($cartSessionID);
93
                $this->session->start();
94
            }
95
        }
96
97
        if (empty($this->cart = $this->session->get($this->prefix.'.'.$instance))) {
98
            $this->cart = new Cart($instance);
99
        }
100
101
        return $this;
102
    }
103
104
    /**
105
     * Gets an an attribute from the cart.
106
     *
107
     * @param $attribute
108
     * @param $defaultValue
109
     *
110
     * @return mixed
111
     */
112
    public function getAttribute($attribute, $defaultValue = null)
113
    {
114
        return array_get($this->cart->attributes, $attribute, $defaultValue);
115
    }
116
117
    /**
118
     * Gets all the carts attributes.
119
     *
120
     * @return mixed
121
     */
122
    public function getAttributes()
123
    {
124
        return $this->cart->attributes;
125
    }
126
127
    /**
128
     * Adds an Attribute to the cart.
129
     *
130
     * @param $attribute
131
     * @param $value
132
     */
133
    public function setAttribute($attribute, $value)
134
    {
135
        array_set($this->cart->attributes, $attribute, $value);
136
137
        $this->update();
138
    }
139
140
    /**
141
     * Updates cart session.
142
     */
143
    public function update()
144
    {
145
        $this->session->set($this->prefix.'.'.$this->cart->instance, $this->cart);
146
147
        if (config('laracart.cross_devices', false) && $this->authManager->check()) {
148
            $this->authManager->user()->cart_session_id = $this->session->getId();
149
            $this->authManager->user()->save();
150
        }
151
152
        $this->session->save();
153
154
        $this->events->fire('laracart.update', $this->cart);
155
    }
156
157
    /**
158
     * Removes an attribute from the cart.
159
     *
160
     * @param $attribute
161
     */
162
    public function removeAttribute($attribute)
163
    {
164
        array_forget($this->cart->attributes, $attribute);
165
166
        $this->update();
167
    }
168
169
    /**
170
     * Creates a CartItem and then adds it to cart.
171
     *
172
     * @param string|int $itemID
173
     * @param null       $name
174
     * @param int        $qty
175
     * @param string     $price
176
     * @param array      $options
177
     * @param bool|true  $taxable
178
     *
179
     * @return CartItem
180
     */
181
    public function addLine($itemID, $name = null, $qty = 1, $price = '0.00', $options = [], $taxable = true)
182
    {
183
        return $this->add($itemID, $name, $qty, $price, $options, $taxable, true);
184
    }
185
186
    /**
187
     * Creates a CartItem and then adds it to cart.
188
     *
189
     * @param $itemID
190
     * @param null       $name
191
     * @param int        $qty
192
     * @param string     $price
193
     * @param array      $options
194
     * @param bool|false $taxable
195
     * @param bool|false $lineItem
196
     *
197
     * @throws ModelNotFound
198
     *
199
     * @return CartItem
200
     */
201
    public function add(
202
        $itemID,
203
        $name = null,
204
        $qty = 1,
205
        $price = '0.00',
206
        $options = [],
207
        $taxable = true,
208
        $lineItem = false
209
    ) {
210
        if (!empty(config('laracart.item_model'))) {
211
            $itemModel = $itemID;
212
213
            if (!$this->isItemModel($itemModel)) {
214
                $itemModel = (new $this->itemModel())->with($this->itemModelRelations)->find($itemID);
215
            }
216
217
            if (empty($itemModel)) {
218
                throw new ModelNotFound('Could not find the item '.$itemID);
219
            }
220
221
            $bindings = config('laracart.item_model_bindings');
222
223
            $itemID = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_ID]];
224
            $name = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_NAME]];
225
226
            if (empty($qty = $name) || !is_int($name)) {
227
                $qty = 1;
228
            }
229
230
            $price = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_PRICE]];
231
232
            $options = array_merge($options,
233
                $this->getItemModelOptions($itemModel, $bindings[\LukePOLO\LaraCart\CartItem::ITEM_OPTIONS]));
234
235
            $taxable = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_TAXABLE]] ? true : false;
236
        }
237
238
        $item = $this->addItem(new CartItem(
239
            $itemID,
240
            $name,
241
            $qty,
242
            $price,
243
            $options,
244
            $taxable,
245
            $lineItem
246
        ));
247
248
        $this->update();
249
250
        return $this->getItem($item->getHash());
251
    }
252
253
    /**
254
     * Adds the cartItem into the cart session.
255
     *
256
     * @param CartItem $cartItem
257
     *
258
     * @return CartItem
259
     */
260
    public function addItem(CartItem $cartItem)
261
    {
262
        $itemHash = $cartItem->generateHash();
263
264
        if ($this->getItem($itemHash)) {
265
            $this->getItem($itemHash)->qty += $cartItem->qty;
266
        } else {
267
            $this->cart->items[] = $cartItem;
268
        }
269
270
        $this->events->fire('laracart.addItem', $cartItem);
271
272
        return $cartItem;
273
    }
274
275
    /**
276
     * Increment the quantity of a cartItem based on the itemHash.
277
     *
278
     * @param $itemHash
279
     *
280
     * @return CartItem | null
281
     */
282
    public function increment($itemHash)
283
    {
284
        $item = $this->getItem($itemHash);
285
        $item->qty++;
286
        $this->update();
287
288
        return $item;
289
    }
290
291
    /**
292
     * Decrement the quantity of a cartItem based on the itemHash.
293
     *
294
     * @param $itemHash
295
     *
296
     * @return CartItem | null
297
     */
298
    public function decrement($itemHash)
299
    {
300
        $item = $this->getItem($itemHash);
301
        if ($item->qty > 1) {
302
            $item->qty--;
303
            $this->update();
304
305
            return $item;
306
        }
307
        $this->removeItem($itemHash);
308
        $this->update();
309
    }
310
311
    /**
312
     * Find items in the cart matching a data set.
313
     *
314
     *
315
     * param $data
316
     *
317
     * @return array
318
     */
319
    public function find($data)
320
    {
321
        $matches = [];
322
323
        foreach ($this->getItems() as $item) {
324
            if ($item->find($data)) {
325
                $matches[] = $item;
326
            }
327
        }
328
329
        return $matches;
330
    }
331
332
    /**
333
     * Finds a cartItem based on the itemHash.
334
     *
335
     * @param $itemHash
336
     *
337
     * @return CartItem | null
338
     */
339
    public function getItem($itemHash)
340
    {
341
        return array_get($this->getItems(), $itemHash);
342
    }
343
344
    /**
345
     * Gets all the items within the cart.
346
     *
347
     * @return array
348
     */
349
    public function getItems()
350
    {
351
        $items = [];
352
        if (isset($this->cart->items) === true) {
353
            foreach ($this->cart->items as $item) {
354
                $items[$item->getHash()] = $item;
355
            }
356
        }
357
358
        return $items;
359
    }
360
361
    /**
362
     * Updates an items attributes.
363
     *
364
     * @param $itemHash
365
     * @param $key
366
     * @param $value
367
     *
368
     * @throws Exceptions\InvalidPrice
369
     * @throws Exceptions\InvalidQuantity
370
     *
371
     * @return CartItem
372
     */
373
    public function updateItem($itemHash, $key, $value)
374
    {
375
        if (empty($item = $this->getItem($itemHash)) === false) {
376
            $item->$key = $value;
377
        }
378
379
        $item->generateHash();
380
381
        $this->update();
382
383
        return $item;
384
    }
385
386
    /**
387
     * Removes a CartItem based on the itemHash.
388
     *
389
     * @param $itemHash
390
     */
391
    public function removeItem($itemHash)
392
    {
393
        foreach ($this->cart->items as $itemKey => $item) {
394
            if ($item->getHash() == $itemHash) {
395
                unset($this->cart->items[$itemKey]);
396
                break;
397
            }
398
        }
399
400
        $this->events->fire('laracart.removeItem', $item);
0 ignored issues
show
The variable $item seems to be defined by a foreach iteration on line 393. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
401
402
        $this->update();
403
    }
404
405
    /**
406
     * Empties the carts items.
407
     */
408
    public function emptyCart()
409
    {
410
        unset($this->cart->items);
411
412
        $this->update();
413
414
        $this->events->fire('laracart.empty', $this->cart->instance);
415
    }
416
417
    /**
418
     * Completely destroys cart and anything associated with it.
419
     */
420
    public function destroyCart()
421
    {
422
        $instance = $this->cart->instance;
423
424
        $this->session->forget($this->prefix.'.'.$instance);
425
426
        $this->setInstance('default');
427
428
        $this->events->fire('laracart.destroy', $instance);
429
430
        $this->update();
431
    }
432
433
    /**
434
     * Gets the coupons for the current cart.
435
     *
436
     * @return array
437
     */
438
    public function getCoupons()
439
    {
440
        return $this->cart->coupons;
441
    }
442
443
    /**
444
     * Finds a specific coupon in the cart.
445
     *
446
     * @param $code
447
     *
448
     * @return mixed
449
     */
450
    public function findCoupon($code)
451
    {
452
        return array_get($this->cart->coupons, $code);
453
    }
454
455
    /**
456
     * Applies a coupon to the cart.
457
     *
458
     * @param CouponContract $coupon
459
     */
460
    public function addCoupon(CouponContract $coupon)
461
    {
462
        if (!$this->cart->multipleCoupons) {
463
            $this->cart->coupons = [];
464
        }
465
466
        $this->cart->coupons[$coupon->code] = $coupon;
467
468
        $this->update();
469
    }
470
471
    /**
472
     * Removes a coupon in the cart.
473
     *
474
     * @param $code
475
     */
476
    public function removeCoupon($code)
477
    {
478
        $this->removeCouponFromItems($code);
479
        array_forget($this->cart->coupons, $code);
480
        $this->update();
481
    }
482
483
    /**
484
     * Removes all coupons from the cart.
485
     */
486
    public function removeCoupons()
487
    {
488
        $this->removeCouponFromItems();
489
        $this->cart->coupons = [];
490
        $this->update();
491
    }
492
493
    /**
494
     * Gets a specific fee from the fees array.
495
     *
496
     * @param $name
497
     *
498
     * @return mixed
499
     */
500
    public function getFee($name)
501
    {
502
        return array_get($this->cart->fees, $name, new CartFee(null, false));
503
    }
504
505
    /**
506
     * Allows to charge for additional fees that may or may not be taxable
507
     * ex - service fee , delivery fee, tips.
508
     *
509
     * @param $name
510
     * @param $amount
511
     * @param bool|false $taxable
512
     * @param array      $options
513
     */
514
    public function addFee($name, $amount, $taxable = false, array $options = [])
515
    {
516
        array_set($this->cart->fees, $name, new CartFee($amount, $taxable, $options));
517
518
        $this->update();
519
    }
520
521
    /**
522
     * Removes a fee from the fee array.
523
     *
524
     * @param $name
525
     */
526
    public function removeFee($name)
527
    {
528
        array_forget($this->cart->fees, $name);
529
530
        $this->update();
531
    }
532
533
    /**
534
     * Removes all the fees set in the cart.
535
     */
536
    public function removeFees()
537
    {
538
        $this->cart->fees = [];
539
540
        $this->update();
541
    }
542
543
    /**
544
     * Gets the total tax for the cart.
545
     *
546
     * @param bool|true $format
547
     *
548
     * @return string
549
     */
550
    public function taxTotal($format = true)
551
    {
552
        $totalTax = 0;
553
        $discounted = 0;
554
        $totalDiscount = $this->totalDiscount(false);
555
556
        if ($this->count() != 0) {
557
            foreach ($this->getItems() as $item) {
558
                if ($discounted >= $totalDiscount) {
559
                    $totalTax += $item->tax();
560
                } else {
561
                    $itemPrice = $item->subTotal(false);
562
563
                    if (($discounted + $itemPrice) > $totalDiscount) {
564
                        $totalTax += $item->tax($totalDiscount - $discounted);
565
                    }
566
567
                    $discounted += $itemPrice;
568
                }
569
            }
570
        }
571
572
        foreach ($this->getFees() as $fee) {
573
            if ($fee->taxable) {
574
                $totalTax += $fee->amount * $fee->tax;
575
            }
576
        }
577
578
        return $this->formatMoney($totalTax, null, null, $format);
579
    }
580
581
    /**
582
     * Gets the total of the cart with or without tax.
583
     *
584
     * @param bool $format
585
     * @param bool $withDiscount
586
     *
587
     * @return string
588
     */
589
    public function total($format = true, $withDiscount = true, $withTax = true)
590
    {
591
        $total = $this->subTotal(false) + $this->feeTotals(false);
592
593
        if ($withDiscount) {
594
            $total -= $this->totalDiscount(false);
595
        }
596
597
        if ($withTax) {
598
            $total += $this->taxTotal(false);
599
        }
600
601
        return $this->formatMoney($total, null, null, $format);
602
    }
603
604
    /**
605
     * Gets the subtotal of the cart with or without tax.
606
     *
607
     * @param bool $format
608
     * @param bool $withDiscount
609
     *
610
     * @return string
611
     */
612
    public function subTotal($format = true, $withDiscount = true)
613
    {
614
        $total = 0;
615
616
        if ($this->count() != 0) {
617
            foreach ($this->getItems() as $item) {
618
                $total += $item->subTotal(false, $withDiscount);
619
            }
620
        }
621
622
        return $this->formatMoney($total, null, null, $format);
623
    }
624
625
    /**
626
     * Get the count based on qty, or number of unique items.
627
     *
628
     * @param bool $withItemQty
629
     *
630
     * @return int
631
     */
632
    public function count($withItemQty = true)
633
    {
634
        $count = 0;
635
636
        foreach ($this->getItems() as $item) {
637
            if ($withItemQty) {
638
                $count += $item->qty;
639
            } else {
640
                $count++;
641
            }
642
        }
643
644
        return $count;
645
    }
646
647
    /**
648
     * Formats the number into a money format based on the locale and international formats.
649
     *
650
     * @param $number
651
     * @param $locale
652
     * @param $internationalFormat
653
     * @param $format
654
     *
655
     * @return string
656
     */
657
    public static function formatMoney($number, $locale = null, $internationalFormat = null, $format = true)
658
    {
659
        $number = number_format($number, 2, '.', '');
660
661
        if ($format) {
662
            setlocale(LC_MONETARY, null);
663
            setlocale(LC_MONETARY, empty($locale) ? config('laracart.locale', 'en_US.UTF-8') : $locale);
664
665
            if (empty($internationalFormat) === true) {
666
                $internationalFormat = config('laracart.international_format', false);
667
            }
668
669
            $number = money_format($internationalFormat ? '%i' : '%n', $number);
670
        }
671
672
        return $number;
673
    }
674
675
    /**
676
     * Gets all the fee totals.
677
     *
678
     * @param bool $format
679
     *
680
     * @return string
681
     */
682
    public function feeTotals($format = true, $withTax = false)
683
    {
684
        $feeTotal = 0;
685
686
        foreach ($this->getFees() as $fee) {
687
            $feeTotal += $fee->amount;
688
689
            if ($withTax && $fee->taxable && $fee->tax > 0) {
690
                $feeTotal += $fee->amount * $fee->tax;
691
            }
692
        }
693
694
        return $this->formatMoney($feeTotal, null, null, $format);
695
    }
696
697
    /**
698
     * Gets all the fees on the cart object.
699
     *
700
     * @return mixed
701
     */
702
    public function getFees()
703
    {
704
        return $this->cart->fees;
705
    }
706
707
    /**
708
     * Gets the total amount discounted.
709
     *
710
     * @param bool $format
711
     *
712
     * @return string
713
     */
714
    public function totalDiscount($format = true)
715
    {
716
        $total = 0;
717
718
        foreach ($this->cart->coupons as $coupon) {
719
            if ($coupon->appliedToCart) {
720
                $total += $coupon->discount();
721
            }
722
        }
723
724
        return $this->formatMoney($total, null, null, $format);
725
    }
726
727
    /**
728
     * Checks to see if its an item model.
729
     *
730
     * @param $itemModel
731
     *
732
     * @return bool
733
     */
734
    private function isItemModel($itemModel)
735
    {
736
        if (is_object($itemModel) && get_class($itemModel) == config('laracart.item_model')) {
737
            return true;
738
        }
739
740
        return false;
741
    }
742
743
    /**
744
     * Gets the item models options based the config.
745
     *
746
     * @param Model $itemModel
747
     * @param array $options
748
     *
749
     * @return array
750
     */
751
    private function getItemModelOptions(Model $itemModel, $options = [])
752
    {
753
        $itemOptions = [];
754
        foreach ($options as $option) {
755
            $itemOptions[$option] = $this->getFromModel($itemModel, $option);
756
        }
757
758
        return array_filter($itemOptions, function ($value) {
759
            if ($value !== false && empty($value)) {
760
                return false;
761
            }
762
763
            return true;
764
        });
765
    }
766
767
    /**
768
     * Gets a option from the model.
769
     *
770
     * @param Model $itemModel
771
     * @param $attr
772
     * @param null $defaultValue
773
     *
774
     * @return Model|null
775
     */
776
    private function getFromModel(Model $itemModel, $attr, $defaultValue = null)
777
    {
778
        $variable = $itemModel;
779
780
        if (!empty($attr)) {
781
            foreach (explode('.', $attr) as $attr) {
782
                $variable = array_get($variable, $attr, $defaultValue);
783
            }
784
        }
785
786
        return $variable;
787
    }
788
789
    /**
790
     * Removes a coupon from the item.
791
     *
792
     * @param null $code
793
     */
794
    private function removeCouponFromItems($code = null)
795
    {
796
        foreach ($this->getItems() as $item) {
797
            if (isset($item->code) && (empty($code) || $item->code == $code)) {
798
                $item->code = null;
799
                $item->discount = null;
800
                $item->couponInfo = [];
801
            }
802
        }
803
    }
804
}
805