Completed
Push — master ( 61756c...9c4d64 )
by Antony
02:56
created

SilvershopJsonResponse::getCurrentShoppingCart()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 20
rs 9.2
cc 4
eloc 11
nc 5
nop 0
1
<?php
2
3
/**
4
 * ShopJsonResponse
5
 *
6
 * Json Response for shopping cart of Silverstripe Shop
7
 * @package shop
8
 */
9
class SilvershopJsonResponse extends Extension
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
10
{
11
    /**
12
     * Allow get action to obtain a copy of the shopping cart
13
     */
14
    private static $allowed_actions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $allowed_actions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
15
        'get'
16
    );
17
18
    /**
19
     * get the shopping cart
20
     *
21
     * @param SS_HTTPRequest $request
22
     * @return SS_HTTPResponse $response with JSON body
23
     */
24
    public function get(SS_HTTPRequest $request)
25
    {
26
        if (!$request->isAjax()) {
27
            return $this->owner->httpError(404, _t("ShoppingCart.GETCARTAJAXONLY", "Ajax request only Bo"));
28
        }
29
        $response = $this->owner->getResponse();
30
        $response->removeHeader('Content-Type');
31
        $response->addHeader('Content-Type', 'application/json; charset=utf-8');
32
33
        $data = $this->getCurrentShoppingCart();
34
35
        $this->owner->extend('updateGet', $data, $request, $response);
36
        return $response->setBody(json_encode($data));
37
    }
38
39
    /**
40
     * Add one of an item to a cart (Category Page)
41
     *
42
     * @see 'add' function of ShoppingCart_Controller ($this->owner)
43
     * @param SS_HTTPRequest $request
44
     * @param AjaxHTTPResponse $response
45
     * @param Buyable $product [optional]
46
     * @param int $quantity [optional]
47
     */
48
    public function updateAddResponse(&$request, &$response, $product = null, $quantity = 1)
49
    {
50
        if ($request->isAjax()) {
51
            if (!$response) {
52
                $response = $this->owner->getResponse();
53
            }
54
            $response->removeHeader('Content-Type');
55
            $response->addHeader('Content-Type', 'application/json; charset=utf-8');
56
            $shoppingcart = ShoppingCart::curr();
57
            $shoppingcart->calculate(); // recalculate the shopping cart
58
59
            $data = array(
60
                'id' => (string) $product->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
61
                'internalItemID' => $product->InternalItemID,
0 ignored issues
show
Bug introduced by
Accessing InternalItemID on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
62
                'title' => $product->Title,
0 ignored issues
show
Bug introduced by
Accessing Title on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
63
                'url' => $product->URLSegment,
0 ignored issues
show
Bug introduced by
Accessing URLSegment on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
64
                'categories' => $product->getCategories()->column('Title'),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method getCategories() does only exist in the following implementations of said interface: Product, ProductVariation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
65
                'message' => array(
66
                    'content' => $this->owner->cart->getMessage(),
67
                    'type' => $this->owner->cart->getMessageType(),
68
                ),
69
            );
70
            $this->owner->cart->clearMessage();
71
72
            // add separately as these are absent with variations
73
            if (method_exists($product, "getPrice")) {
74
                $data['unitPrice'] = $product->getPrice();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method getPrice() does only exist in the following implementations of said interface: Product.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
75
            }
76
            if (method_exists($product, "addLink")) {
77
                $data['addLink'] = $product->addLink();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method addLink() does only exist in the following implementations of said interface: Product, ProductVariation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
78
            }
79
            if (method_exists($product, "removeLink")) {
80
                $data['removeLink'] = $product->removeLink();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method removeLink() does only exist in the following implementations of said interface: Product.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
81
            }
82
            if (method_exists($product, "removeallLink")) {
83
                $data['removeallLink'] = $product->removeallLink();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method removeallLink() does only exist in the following implementations of said interface: Product.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
84
            }
85
            if (method_exists($product->Item(), "setquantityLink")) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method Item() does only exist in the following implementations of said interface: Product, ProductVariation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
86
                $data['setquantityLink'] = $product->Item()->setquantityLink();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method Item() does only exist in the following implementations of said interface: Product, ProductVariation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
87
            }
88
89
            if ($shoppingcart) {
90
                $data['subTotal'] = $shoppingcart->SubTotal();
91
                $data['grandTotal'] = $shoppingcart->GrandTotal();
92
            }
93
94
            $this->owner->extend('updateAddResponseShopJsonResponse', $data, $request, $response, $product, $quantity);
95
            $response->setBody(json_encode($data));
96
        }
97
    }
98
99
    /**
100
     * Remove one of an item from a cart (Cart Page)
101
     *
102
     * @see 'remove' function of ShoppingCart_Controller ($this->owner)
103
     * @param SS_HTTPRequest $request
104
     * @param AjaxHTTPResponse $response
105
     * @param Buyable $product [optional]
106
     * @param int $quantity [optional]
107
     */
108 View Code Duplication
    public function updateRemoveResponse(&$request, &$response, $product = null, $quantity = 1)
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...
109
    {
110
        if ($request->isAjax()) {
111
            if (!$response) {
112
                $response = $this->owner->getResponse();
113
            }
114
            $response->removeHeader('Content-Type');
115
            $response->addHeader('Content-Type', 'application/json; charset=utf-8');
116
            $shoppingcart = ShoppingCart::curr();
117
            $shoppingcart->calculate(); // recalculate the shopping cart
118
119
            $data = array(
120
                'id' => (string) $product->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
121
                'message' => array(
122
                    'content' => $this->owner->cart->getMessage(),
123
                    'type' => $this->owner->cart->getMessageType(),
124
                ),
125
            );
126
            $this->owner->cart->clearMessage();
127
128
            if ($shoppingcart) {
129
                $data['subTotal'] = $shoppingcart->SubTotal();
130
                $data['grandTotal'] = $shoppingcart->GrandTotal();
131
            }
132
133
            $this->owner->extend('updateRemoveResponseShopJsonResponse', $data, $request, $response, $product, $quantity);
134
            $response->setBody(json_encode($data));
135
        }
136
    }
137
138
    /**
139
     * Remove all of an item from a cart (Cart Page)
140
     * Quantity is NIL
141
     *
142
     * @see 'removeall' function of ShoppingCart_Controller ($this->owner)
143
     * @param SS_HTTPRequest $request
144
     * @param AjaxHTTPResponse $response
145
     * @param Buyable $product [optional]
146
     */
147 View Code Duplication
    public function updateRemoveAllResponse(&$request, &$response, $product = null)
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...
148
    {
149
        if ($request->isAjax()) {
150
            if (!$response) {
151
                $response = $this->owner->getResponse();
152
            }
153
            $response->removeHeader('Content-Type');
154
            $response->addHeader('Content-Type', 'application/json; charset=utf-8');
155
            $shoppingcart = ShoppingCart::curr();
156
            $shoppingcart->calculate(); // recalculate the shopping cart
157
158
            $data = array(
159
                'id' => (string) $product->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
160
                'message' => array(
161
                    'content' => $this->owner->cart->getMessage(),
162
                    'type' => $this->owner->cart->getMessageType(),
163
                ),
164
            );
165
            $this->owner->cart->clearMessage();
166
167
            if ($shoppingcart) {
168
                $data['subTotal'] = $shoppingcart->SubTotal();
169
                $data['grandTotal'] = $shoppingcart->GrandTotal();
170
            }
171
172
            $this->owner->extend('updateRemoveAllResponseShopJsonResponse', $data, $request, $response, $product);
173
            $response->setBody(json_encode($data));
174
        }
175
    }
176
177
    /**
178
     * Update the quantity of an item in a cart (Cart Page)
179
     *
180
     * @see 'setquantity' function of ShoppingCart_Controller ($this->owner)
181
     * @param SS_HTTPRequest $request
182
     * @param AjaxHTTPResponse $response
183
     * @param Buyable $product [optional]
184
     * @param int $quantity [optional]
185
     */
186
    public function updateSetQuantityResponse(&$request, &$response, $product = null, $quantity = 1)
0 ignored issues
show
Unused Code introduced by
The parameter $quantity is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
187
    {
188
        if ($request->isAjax()) {
189
            if (!$response) {
190
                $response = $this->owner->getResponse();
191
            }
192
            $response->removeHeader('Content-Type');
193
            $response->addHeader('Content-Type', 'application/json; charset=utf-8');
194
            $shoppingcart = ShoppingCart::curr();
195
            $shoppingcart->calculate(); // recalculate the shopping cart
196
197
            $currentquantity = (int) $product->Item()->Quantity; // quantity of the order item left now in the cart
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method Item() does only exist in the following implementations of said interface: Product, ProductVariation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
198
199
            $data = array(
200
                'id' => (string) $product->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
201
                'quantity' => $currentquantity,
202
                'message' => array(
203
                    'content' => $this->owner->cart->getMessage(),
204
                    'type' => $this->owner->cart->getMessageType(),
205
                ),
206
            );
207
            $this->owner->cart->clearMessage();
208
209
            // include totals if required
210
            if ($shoppingcart) {
211
                $data['subTotal'] = $shoppingcart->SubTotal();
212
                $data['grandTotal'] = $shoppingcart->GrandTotal();
213
            }
214
215
            $this->owner->extend('updateSetQuantityResponseShopJsonResponse', $data, $request, $response, $product, $currentquantity);
216
            $response->setBody(json_encode($data));
217
        }
218
    }
219
220
    /**
221
     * Clear all items from the cart (Cart Page)
222
     *
223
     * @see 'clear' function of ShoppingCart_Controller ($this->owner)
224
     * @param SS_HTTPRequest $request
225
     * @param AjaxHTTPResponse $response
226
     */
227
    public function updateClearResponse(&$request, &$response)
228
    {
229
        if ($request->isAjax()) {
230
            if (!$response) {
231
                $response = $this->owner->getResponse();
232
            }
233
            $response->removeHeader('Content-Type');
234
            $response->addHeader('Content-Type', 'application/json; charset=utf-8');
235
236
            $data = array(
237
                'message' => array(
238
                    'content' => $this->owner->cart->getMessage(),
239
                    'type' => $this->owner->cart->getMessageType(),
240
                ),
241
            );
242
            $this->owner->cart->clearMessage();
243
244
            $this->owner->extend('updateClearResponseShopJsonResponse', $data, $request, $response);
245
            $response->setBody(json_encode($data));
246
        }
247
    }
248
249
    /**
250
     * Update the variations of a product (Cart Page)
251
     *
252
     * @see 'addtocart' function of VariationForm ($this->owner)
253
     * @param SS_HTTPRequest $request
254
     * @param AjaxHTTPResponse $response
255
     * @param Buyable $variation [optional]
256
     * @param int $quantity [optional]
257
     * @param VariationForm $form [optional]
258
     */
259
    public function updateVariationFormResponse(&$request, &$response, $variation = null, $quantity = 1, $form = null)
260
    {
261
        if ($request->isAjax()) {
262
            if (!$response) {
263
                $response = $this->owner->getResponse();
264
            }
265
            $response->removeHeader('Content-Type');
266
            $response->addHeader('Content-Type', 'application/json; charset=utf-8');
267
            $shoppingcart = ShoppingCart::curr();
268
            $shoppingcart->calculate(); // recalculate the shopping cart
269
270
            $data = array(
271
                'id' => (string) $variation->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
272
                'message' => array(
273
                    'content' => $form->Message(),
0 ignored issues
show
Bug introduced by
It seems like $form is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
274
                    'type' => $form->MessageType(),
275
                ),
276
            );
277
            $form->clearMessage();
278
279
            // include totals if required
280
            if ($shoppingcart) {
281
                $data['subTotal'] = $shoppingcart->SubTotal();
282
                $data['grandTotal'] = $shoppingcart->GrandTotal();
283
            }
284
285
            $this->owner->extend('updateVariationFormResponseShopJsonResponse', $data, $request, $response, $variation, $quantity, $form);
286
            $response->setBody(json_encode($data));
287
        }
288
    }
289
290
    /**
291
     * Add one of an item to a cart (Product Page)
292
     *
293
     * @see the addtocart function within AddProductForm class
294
     * @param SS_HTTPRequest $request
295
     * @param AjaxHTTPResponse $response
296
     * @param Buyable $buyable [optional]
297
     * @param int $quantity [optional]
298
     * @param AddProductForm $form [optional]
299
     */
300
    public function updateAddProductFormResponse(&$request, &$response, $buyable, $quantity, $form)
301
    {
302
        if ($request->isAjax()) {
303
            if (!$response) {
304
                $response = $this->owner->getController()->getResponse();
305
            }
306
            $response->removeHeader('Content-Type');
307
            $response->addHeader('Content-Type', 'application/json; charset=utf-8');
308
            $shoppingcart = ShoppingCart::curr();
309
            $shoppingcart->calculate(); // recalculate the shopping cart
310
311
            $data = array(
312
                'id' => (string) $buyable->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
313
                'internalItemID' => $buyable->InternalItemID,
0 ignored issues
show
Bug introduced by
Accessing InternalItemID on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
314
                'title' => $buyable->Title,
0 ignored issues
show
Bug introduced by
Accessing Title on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
315
                'url' => $buyable->URLSegment,
0 ignored issues
show
Bug introduced by
Accessing URLSegment on the interface Buyable suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
316
                'categories' => $buyable->getCategories()->column('Title'),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method getCategories() does only exist in the following implementations of said interface: Product, ProductVariation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
317
                'addLink' => $buyable->addLink(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method addLink() does only exist in the following implementations of said interface: Product, ProductVariation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
318
                'removeLink' => $buyable->removeLink(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method removeLink() does only exist in the following implementations of said interface: Product.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
319
                'removeallLink' => $buyable->removeallLink(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method removeallLink() does only exist in the following implementations of said interface: Product.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
320
                'setquantityLink' => $buyable->Item()->setquantityLink(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Buyable as the method Item() does only exist in the following implementations of said interface: Product, ProductVariation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
321
                'message' => array(
322
                    'content' => $form->Message(),
323
                    'type' => $form->MessageType(),
324
                ),
325
            );
326
            $form->clearMessage();
327
328
            // include totals if required
329
            if ($shoppingcart) {
330
                $data['subTotal'] = $shoppingcart->SubTotal();
331
                $data['grandTotal'] = $shoppingcart->GrandTotal();
332
            }
333
334
            $this->owner->extend('updateAddProductFormResponseShopJsonResponse', $data, $request, $response, $buyable, $quantity, $form);
335
            $response->setBody(json_encode($data));
336
        }
337
    }
338
339
    /**
340
     * Provide a copy of the current order in the required format
341
     * Note the id is the cart's id
342
     * @return array of product id, subTotal, grandTotal, and items & modifiers
343
     */
344
    public function getCurrentShoppingCart()
345
    {
346
        $result = [];
347
348
        if ($shoppingcart = ShoppingCart::curr()) {
349
            $result['id'] = (string) $shoppingcart->getReference();
350
351
            if ($items = $this->getCurrentShoppingCartItems()) {
352
                $result['items'] = $items;
353
            }
354
355
            if ($modifiers = $this->getCurrentShoppingCartModifiers()) {
356
                $result['modifiers'] = $modifiers;
357
            }
358
359
            $result['subTotal'] = $shoppingcart->SubTotal();
360
            $result['grandTotal'] = $shoppingcart->GrandTotal();
361
        }
362
        return $result;
363
    }
364
365
    /**
366
     * Provide a copy of the current order's items, including image details and variations
367
     * @todo  what about subTitles?  i.e the variation choosen (I think)
368
     * @return array
369
     */
370
    protected function getCurrentShoppingCartItems()
371
    {
372
        $result = array();
373
        $items = ShoppingCart::curr()->Items();
374
375
        if ($items->exists()) {
376
            foreach ($items->getIterator() as $item) {
377
378
                // Definitions
379
                $data = array();
380
                $product = $item->Product();
381
382
                $data["id"] = (string) $item->ProductID;
383
                $data["internalItemID"] = $product->InternalItemID;
384
                $data["title"] = $product->Title;
385
                $data["quantity"] = (int) $item->Quantity;
386
                $data["unitPrice"] = $product->getPrice();
387
                $data["href"] = $item->Link();
388
                $data['categories'] = $product->getCategories()->column('Title');
389
                $data["addLink"] = $item->addLink();
390
                $data["removeLink"] = $item->removeLink();
391
                $data["removeallLink"] = $item->removeallLink();
392
                $data["setquantityLink"] = $item->setquantityLink();
393
394
                // Image
395
                if ($image = $item->Image()->ScaleWidth((int) Product_Image::config()->cart_image_width)) {
396
                    $data["image"] = array(
397
                        'alt' => $image->Title,
398
                        'src' => $image->Filename,
399
                        'width' => $image->Width,
400
                        'height' => $image->Height,
401
                    );
402
                }
403
404
                // Variations
405
                if ($product->has_many("Variations")) {
406
                    $variations = $product->Variations();
407
                    if ($variations->exists()) {
408
                        $data['variations'] = array();
409
                        foreach ($variations as $variation) {
410
                            $data['variations'][] = array(
411
                                'id' => (string) $variation->ID,
412
                                'title' => $variation->Title,
413
                            );
414
                        }
415
                    }
416
                }
417
                $result[] = $data;
418
            }
419
        }
420
        return $result;
421
    }
422
423
    /**
424
     * Provide a copy of the current order's modifiers
425
     * @todo Only FlatTaxModifier tested
426
     * @return array of modifiers (note: this excludes subtotal and grandtotal)
427
     */
428
    protected function getCurrentShoppingCartModifiers()
429
    {
430
        $result = array();
431
        $modifiers = ShoppingCart::curr()->Modifiers();
432
433
        if ($modifiers->exists()) {
434
            foreach ($modifiers->sort('Sort')->getIterator() as $modifier) {
435
                if ($modifier->ShowInTable()) {
436
                    $data = array(
437
                        'id' => (string) $modifier->ID,
438
                        'tableTitle' => $modifier->TableTitle(),
439
                        'tableValue' => (float) $modifier->TableValue(),
440
                    );
441
442
                    if (method_exists($modifier, 'Link')) {
443
                        // add if there is a link
444
                        $data["href"] = $modifier->Link();
445
                    }
446
447
                    if (method_exists($modifier, 'removeLink')) {
448
                        // add if there is a canRemove method
449
                        $data["removeLink"] = $modifier->removeLink();
450
                    }
451
452
                    $result[] = $data;
453
                }
454
            }
455
        }
456
        $this->owner->extend('updateGetCurrentShoppingCartModifiers', $result);
457
        return $result;
458
    }
459
}
460