Completed
Push — master ( 9a3c41...108cc9 )
by Carsten
02:42
created

Basket::destroy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
 * This file is part of Lenius Basket, a PHP package to handle
5
 * your shopping basket.
6
 *
7
 * Copyright (c) 2013 Lenius.
8
 * http://github.com/lenius/basket
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 *
13
 * @author Carsten Jonstrup<[email protected]>
14
 * @copyright 2013 Lenius.
15
 *
16
 * @version dev
17
 *
18
 * @link http://github.com/lenius/basket
19
 */
20
namespace Lenius\Basket;
21
22
use InvalidArgumentException;
23
24
class Basket
25
{
26
    protected $id;
27
28
    protected $identifier;
29
    protected $store;
30
31
    protected $currency;
32
33
    protected $requiredParams = [
34
        'id',
35
        'name',
36
        'quantity',
37
        'price',
38
        'weight',
39
    ];
40
41
    /**
42
     * Basket constructor.
43
     *
44
     * @param StorageInterface    $store      The interface for storing the cart data
45
     * @param IdentifierInterface $identifier The interface for storing the identifier
46
     */
47 14
    public function __construct(StorageInterface $store, IdentifierInterface $identifier)
48 1
    {
49 14
        $this->store = $store;
50 14
        $this->identifier = $identifier;
51
52
        // Generate/retrieve identifier
53 14
        $this->id = $this->identifier->get();
54
55
        // Restore the cart from a saved version
56 14
        if (method_exists($this->store, 'restore')) {
57
            $this->store->restore();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Lenius\Basket\StorageInterface as the method restore() does only exist in the following implementations of said interface: Lenius\Basket\Storage\Session.

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...
58
        }
59
60
        // Let our storage class know which cart we're talking about
61 14
        $this->store->setIdentifier($this->id);
62 14
    }
63
64
    /**
65
     * Retrieve the basket contents.
66
     *
67
     * @param bool $asArray
68
     *
69
     * @return array An array of Item objects
70
     */
71 9
    public function &contents($asArray = false)
72
    {
73 9
        return $this->store->data($asArray);
74
    }
75
76
    /**
77
     * Insert an item into the basket.
78
     *
79
     * @param array $item An array of item data
80
     *
81
     * @return string A unique item identifier
82
     */
83 14
    public function insert(array $item)
84
    {
85 14
        $this->checkArgs($item);
86
87 14
        $itemIdentifier = $this->createItemIdentifier($item);
88
89 14
        if ($this->has($itemIdentifier)) {
90 1
            $item['quantity'] = $this->item($itemIdentifier)->quantity + $item['quantity'];
0 ignored issues
show
Documentation introduced by
The property quantity does not exist on object<Lenius\Basket\Item>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
91 6
            $this->update($itemIdentifier, $item);
92
93 1
            return $itemIdentifier;
94
        }
95
96 14
        $item = new Item($itemIdentifier, $item, $this->store);
97
98 14
        $this->store->insertUpdate($item);
99
100 14
        return $itemIdentifier;
101
    }
102
103
    /**
104
     * Update an item.
105
     *
106
     * @param string           $itemIdentifier The unique item identifier
107
     * @param string|int|array $key            The key to update, or an array of key-value pairs
108
     * @param mixed            $value          The value to set $key to
109
     *
110
     * @return void
111
     */
112 2
    public function update($itemIdentifier, $key, $value = null)
113
    {
114 2
        foreach ($this->contents() as $item) {
115 2
            if ($item->identifier == $itemIdentifier) {
116 2
                $item->update($key, $value);
117 2
                break;
118
            }
119 2
        }
120 2
    }
121
122
    /**
123
     * Remove an item from the basket.
124
     *
125
     * @param string $identifier Unique item identifier
126
     *
127
     * @return void
128
     */
129 1
    public function remove($identifier)
130
    {
131 1
        $this->store->remove($identifier);
132 1
    }
133
134
    /**
135
     * Destroy/empty the basket.
136
     *
137
     * @return void
138
     */
139 14
    public function destroy()
140
    {
141 14
        $this->store->destroy();
142 14
    }
143
144
    /**
145
     * Check if the basket has a specific item.
146
     *
147
     * @param string $itemIdentifier The unique item identifier
148
     *
149
     * @return bool Yes or no?
150
     */
151 14
    public function has($itemIdentifier)
152
    {
153 14
        return $this->store->has($itemIdentifier);
154
    }
155
156
    /**
157
     * Return a specific item object by identifier.
158
     *
159
     * @param string $itemIdentifier The unique item identifier
160
     *
161
     * @return Item Item object
162
     */
163 6
    public function item($itemIdentifier)
164
    {
165 6
        return $this->store->item($itemIdentifier);
166
    }
167
168
    /**
169
     * Returns the first occurance of an item with a given id.
170
     *
171
     * @param string $id The item id
172
     *
173
     * @return Item Item object
174
     */
175 1
    public function find($id)
176
    {
177 1
        return $this->store->find($id);
178
    }
179
180
    /**
181
     * The total tax value for the basket.
182
     *
183
     * @return float The total tax value
184
     */
185
    public function tax()
186
    {
187
        $total = 0;
188
189
        foreach ($this->contents() as $item) {
190
            $total += (float) $item->tax();
191
        }
192
193
        return $total;
194
    }
195
196
    /**
197
     * The total weight value for the basket.
198
     *
199
     * @return float The total weight value
200
     */
201
    public function weight()
202
    {
203
        $weight = 0;
204
205
        foreach ($this->contents() as $item) {
206
            $weight += (float) $item->weight();
207
        }
208
209
        return $weight;
210
    }
211
212
    /**
213
     * The total value of the basket.
214
     *
215
     * @param bool $includeTax Include tax on the total?
216
     *
217
     * @return float The total basket value
218
     */
219 3
    public function total($includeTax = true)
220
    {
221 3
        $total = 0;
222
223 3
        foreach ($this->contents() as $item) {
224 3
            $total += (float) $item->total($includeTax);
225 3
        }
226
227 3
        return (float) $total;
228
    }
229
230
    /**
231
     * The total number of items in the basket.
232
     *
233
     * @param bool $unique Just return unique items?
234
     *
235
     * @return int Total number of items
236
     */
237 1
    public function totalItems($unique = false)
238
    {
239 1
        $total = 0;
240
241 1
        foreach ($this->contents() as $item) {
242 1
            $total += $unique ? 1 : $item->quantity;
243 1
        }
244
245 1
        return $total;
246
    }
247
248
    /**
249
     * Set the basket identifier, useful if restoring a saved basket.
250
     *
251
     * @param  mixed The identifier
252
     *
253
     * @return void
254
     */
255
    public function setIdentifier($identifier)
256
    {
257
        $this->store->setIdentifier($identifier);
258
    }
259
260
    /**
261
     * Create a unique item identifier.
262
     *
263
     * @param array $item An array of item data
264
     *
265
     * @return string An md5 hash of item
266
     */
267 14
    protected function createItemIdentifier(array $item)
268
    {
269 14
        if (!array_key_exists('options', $item)) {
270 13
            $item['options'] = [];
271 13
        }
272
273 14
        ksort($item['options']);
274
275 14
        return md5($item['id'].serialize($item['options']));
276
    }
277
278
    /**
279
     * Check if a basket item has the required parameters.
280
     *
281
     * @param array $item An array of item data
282
     *
283
     * @return void
284
     */
285 14
    protected function checkArgs(array $item)
286
    {
287 14
        foreach ($this->requiredParams as $param) {
288 14
            if (!array_key_exists($param, $item)) {
289
                throw new InvalidArgumentException("The '{$param}' field is required");
290
            }
291 14
        }
292 14
    }
293
}
294