ShoppingCart_Controller::debug()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
nc 2
nop 0
1
<?php
2
3
4
/**
5
 * ShoppingCart_Controller.
6
 *
7
 * Handles the modification of a shopping cart via http requests.
8
 * Provides links for making these modifications.
9
 *
10
 *@author: Jeremy Shipman, Nicolaas Francken
11
 *@package: ecommerce
12
 *
13
 *@todo supply links for adding, removing, and clearing cart items
14
 *@todo link for removing modifier(s)
15
 */
16
class ShoppingCart_Controller extends Controller
17
{
18
19
    /**
20
     * Default URL handlers - (Action)/(ID)/(OtherID).
21
     */
22
    private static $url_handlers = 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...
23
        '$Action//$ID/$OtherID/$Version' => 'handleAction',
24
    );
25
26
    /**
27
     * We need to only use the Security ID on a few
28
     * actions, these are listed here.
29
     *
30
     * @var array
31
     */
32
    protected $methodsRequiringSecurityID = array(
33
        'additem',
34
        'removeitem',
35
        'removeallitem',
36
        'removeallitemandedit',
37
        'removemodifier',
38
        'addmodifier',
39
        'copyorder',
40
        'deleteorder',
41
        'save',
42
    );
43
44
    /**
45
     * @var ShoppingCart
46
     */
47
    protected $cart = null;
48
49
    public function init()
50
    {
51
        parent::init();
52
        $action = $this->request->param('Action');
53
        if (!isset($_GET['cached'])) {
54
            if ($action && (in_array($action, $this->methodsRequiringSecurityID))) {
55
                $savedSecurityID = Session::get('SecurityID');
56
                if ($savedSecurityID) {
57
                    if (!isset($_GET['SecurityID'])) {
58
                        $_GET['SecurityID'] = '';
59
                    }
60
                    if ($savedSecurityID) {
61
                        if ($_GET['SecurityID'] != $savedSecurityID) {
62
                            $this->httpError(400, "Security token doesn't match, possible CSRF attack.");
63
                        } else {
64
                            //all OK!
65
                        }
66
                    }
67
                }
68
            }
69
        }
70
        $this->cart = ShoppingCart::singleton();
71
    }
72
73
    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...
74
        'json',
75
        'index',
76
        'additem',
77
        'removeitem',
78
        'removeallitem',
79
        'removeallitemandedit',
80
        'removemodifier',
81
        'addmodifier',
82
        'setcountry',
83
        'setregion',
84
        'setcurrency',
85
        'removefromsale',
86
        'setquantityitem',
87
        'clear',
88
        'clearandlogout',
89
        'save',
90
        'deleteorder',
91
        'numberofitemsincart',
92
        'showcart',
93
        'loadorder',
94
        'copyorder',
95
        'removeaddress',
96
        'submittedbuyable',
97
        'placeorderformember',
98
        'loginas',// no need to set to  => 'ADMIN',
99
        'debug', // no need to set to  => 'ADMIN',
100
        'ajaxtest', // no need to set to  => 'ADMIN',
101
    );
102
103
    public function index()
104
    {
105
        if ($this->cart) {
106
            $this->redirect($this->cart->Link());
107
108
            return;
109
        }
110
        user_error(_t('Order.NOCARTINITIALISED', 'no cart initialised'), E_USER_NOTICE);
111
        return $this->goToErrorPage();
112
        user_error(_t('Order.NOCARTINITIALISED', 'no 404 page available'), E_USER_ERROR);
0 ignored issues
show
Unused Code introduced by
user_error(_t('Order.NOC...lable'), E_USER_ERROR); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
113
    }
114
115
    /*******************************************************
116
    * CONTROLLER LINKS
117
    *******************************************************/
118
119
    /**
120
     * @param string $action
0 ignored issues
show
Documentation introduced by
Should the type for parameter $action not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
121
     *
122
     * @return string (Link)
123
     */
124
    public function Link($action = null)
125
    {
126
        return self::create_link($action);
127
    }
128
129
    /**
130
     * returns ABSOLUTE link to the shopping cart controller.
131
     * @param null | array | string $actionAndOtherLinkVariables
132
     * @return string
133
     */
134
    protected static function create_link($actionAndOtherLinkVariables = null)
135
    {
136
        return Controller::join_links(
137
            Director::baseURL(),
138
            Config::inst()->get('ShoppingCart_Controller', 'url_segment'),
139
            $actionAndOtherLinkVariables
140
        );
141
    }
142
143
    /**
144
     * @param int    $buyableID
145
     * @param string $classNameForBuyable
146
     * @param array  $parameters
147
     *
148
     * @return string
149
     */
150
    public static function add_item_link($buyableID, $classNameForBuyable = 'Product', array $parameters = array())
151
    {
152
        return self::create_link('additem/'.$buyableID.'/'.$classNameForBuyable.'/'.self::params_to_get_string($parameters));
153
    }
154
155
    /**
156
     * @param int    $buyableID
157
     * @param string $classNameForBuyable
158
     * @param array  $parameters
159
     *
160
     * @return string
161
     */
162
    public static function remove_item_link($buyableID, $classNameForBuyable = 'Product', array $parameters = array())
163
    {
164
        return self::create_link('removeitem/'.$buyableID.'/'.$classNameForBuyable.'/'.self::params_to_get_string($parameters));
165
    }
166
167
    /**
168
     * @param int    $buyableID
169
     * @param string $classNameForBuyable
170
     * @param array  $parameters
171
     *
172
     * @return string
173
     */
174
    public static function remove_all_item_link($buyableID, $classNameForBuyable = 'Product', array $parameters = array())
175
    {
176
        return self::create_link('removeallitem/'.$buyableID.'/'.$classNameForBuyable.'/'.self::params_to_get_string($parameters));
177
    }
178
179
    /**
180
     * @param int    $buyableID
181
     * @param string $classNameForBuyable
182
     * @param array  $parameters
183
     *
184
     * @return string
185
     */
186
    public static function remove_all_item_and_edit_link($buyableID, $classNameForBuyable = 'Product', array $parameters = array())
187
    {
188
        return self::create_link('removeallitemandedit/'.$buyableID.'/'.$classNameForBuyable.'/'.self::params_to_get_string($parameters));
189
    }
190
191
    /**
192
     * @param int    $buyableID
193
     * @param string $classNameForBuyable
194
     * @param array  $parameters
195
     *
196
     * @return string
197
     */
198
    public static function set_quantity_item_link($buyableID, $classNameForBuyable = 'Product', array $parameters = array())
199
    {
200
        return self::create_link('setquantityitem/'.$buyableID.'/'.$classNameForBuyable.'/'.self::params_to_get_string($parameters));
201
    }
202
203
    /**
204
     * @param int   $modifierID
205
     * @param array $parameters
206
     *
207
     * @return string
208
     */
209
    public static function remove_modifier_link($modifierID, array $parameters = array())
210
    {
211
        return self::create_link('removemodifier/'.$modifierID.'/'.self::params_to_get_string($parameters));
212
    }
213
214
    /**
215
     * @param int   $modifierID
216
     * @param array $parameters
217
     *
218
     * @return string
219
     */
220
    public static function add_modifier_link($modifierID, array $parameters = array())
221
    {
222
        return self::create_link('addmodifier/'.$modifierID.'/'.self::params_to_get_string($parameters));
223
    }
224
225
    /**
226
     * @param int    $addressID
227
     * @param string $addressClassName
228
     * @param array  $parameters
229
     *
230
     * @return string
231
     */
232
    public static function remove_address_link($addressID, $addressClassName, array $parameters = array())
233
    {
234
        return self::create_link('removeaddress/'.$addressID.'/'.$addressClassName.'/'.self::params_to_get_string($parameters));
235
    }
236
237
    /**
238
     * @param array $parameters
239
     *
240
     * @return string
241
     */
242
    public static function clear_cart_link($parameters = array())
243
    {
244
        return self::create_link('clear/'.self::params_to_get_string($parameters));
245
    }
246
247
    /**
248
     * @param array $parameters
249
     *
250
     * @return string
251
     */
252
    public static function save_cart_link(array $parameters = array())
253
    {
254
        return self::create_link('save/'.self::params_to_get_string($parameters));
255
    }
256
257
    /**
258
     * @param array $parameters
259
     *
260
     * @return string
261
     */
262
    public static function clear_cart_and_logout_link(array $parameters = array())
263
    {
264
        return self::create_link('clearandlogout/'.self::params_to_get_string($parameters));
265
    }
266
267
    /**
268
     * @param array $parameters
269
     *
270
     * @return string
271
     */
272
    public static function delete_order_link($orderID, array $parameters = array())
273
    {
274
        return self::create_link('deleteorder/'.$orderID.'/'.self::params_to_get_string($parameters));
275
    }
276
277
    /**
278
     *
279
     * @return null | string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
280
     */
281
    public static function copy_order_link($orderID, $parameters = array())
282
    {
283
        $order = Order::get()->byID($orderID);
284
        if ($order && $order->IsSubmitted()) {
285
            return self::create_link('copyorder/'.$orderID.'/'.self::params_to_get_string($parameters));
286
        }
287
    }
288
289
    /**
290
     * returns a link that allows you to set a currency...
291
     * dont be fooled by the set_ part...
292
     *
293
     * @param string $code
294
     *
295
     * @return string
296
     */
297
    public static function set_currency_link($code, array $parameters = array())
298
    {
299
        return self::create_link('setcurrency/'.$code.'/'.self::params_to_get_string($parameters));
300
    }
301
302
    /**
303
     *
304
     *
305
     * @param  int    $id
306
     * @param  string $className
307
     * @return string
308
     */
309
    public static function remove_from_sale_link($id, $className)
310
    {
311
        return self::create_link('removefromsale/'.$className.'/'.$id .'/');
312
    }
313
314
    /**
315
     * return json for cart... no further actions.
316
     *
317
     * @param SS_HTTPRequest
318
     *
319
     * @return JSON
320
     */
321
    public function json(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
322
    {
323
        return $this->cart->setMessageAndReturn();
324
    }
325
326
    /**
327
     * Adds item to cart via controller action; one by default.
328
     *
329
     * @param HTTPRequest
330
     *
331
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
332
     *               If it is not AJAX it redirects back to requesting page.
333
     */
334
    public function additem(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
335
    {
336
        $buyable = $this->buyable();
337
        if ($buyable) {
338
            $this->cart->addBuyable($buyable, $this->quantity(), $this->parameters());
0 ignored issues
show
Documentation introduced by
$buyable is of type object<DataObject>, but the function expects a object<BuyableModel>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
339
            return $this->cart->setMessageAndReturn();
340
        } else {
341
            return $this->goToErrorPage();
342
        }
343
    }
344
345
    /**
346
     * Sets the exact passed quantity.
347
     * Note: If no ?quantity=x is specified in URL, then quantity will be set to 1.
348
     *
349
     * @param HTTPRequest
350
     *
351
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
352
     *               If it is not AJAX it redirects back to requesting page.
353
     */
354
    public function setquantityitem(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
355
    {
356
        $buyable = $this->buyable();
357
        if ($buyable) {
358
            $this->cart->setQuantity($buyable, $this->quantity(), $this->parameters());
0 ignored issues
show
Documentation introduced by
$buyable is of type object<DataObject>, but the function expects a object<BuyableModel>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
359
360
            return $this->cart->setMessageAndReturn();
361
        } else {
362
            return $this->goToErrorPage();
363
        }
364
    }
365
366
    /**
367
     * Removes item from cart via controller action; one by default.
368
     *
369
     * @param HTTPRequest
370
     *
371
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
372
     *               If it is not AJAX it redirects back to requesting page.
373
     */
374
    public function removeitem(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
375
    {
376
        $buyable = $this->buyable();
377
        if ($buyable) {
378
            $this->cart->decrementBuyable($buyable, $this->quantity(), $this->parameters());
0 ignored issues
show
Documentation introduced by
$buyable is of type object<DataObject>, but the function expects a object<BuyableModel>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
379
380
            return $this->cart->setMessageAndReturn();
381
        } else {
382
            return $this->goToErrorPage();
383
        }
384
    }
385
386
    /**
387
     * Removes all of a specific item.
388
     *
389
     * @param HTTPRequest
390
     *
391
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
392
     *               If it is not AJAX it redirects back to requesting page.
393
     */
394
    public function removeallitem(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
395
    {
396
        $buyable = $this->buyable();
397
        if ($buyable) {
398
            $this->cart->deleteBuyable($buyable, $this->parameters());
0 ignored issues
show
Documentation introduced by
$buyable is of type object<DataObject>, but the function expects a object<BuyableModel>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
399
            //added this because cart was not updating correctly
400
            $order = $this->cart->CurrentOrder();
401
            $order->calculateOrderAttributes($force = true);
402
403
            return $this->cart->setMessageAndReturn();
404
        } else {
405
            return $this->goToErrorPage();
406
        }
407
    }
408
409
    /**
410
     * Removes all of a specific item AND return back.
411
     *
412
     * @param HTTPRequest
413
     *
414
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
415
     *               If it is not AJAX it redirects back to requesting page.
416
     */
417
    public function removeallitemandedit(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
418
    {
419
        $buyable = $this->buyable();
420
        if ($buyable) {
421
            $link = $buyable->Link();
422
            $this->cart->deleteBuyable($buyable, $this->parameters());
0 ignored issues
show
Documentation introduced by
$buyable is of type object<DataObject>, but the function expects a object<BuyableModel>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
423
            $this->redirect($link);
424
        } else {
425
            $this->redirectBack();
426
        }
427
    }
428
429
    /**
430
     * Removes a specified modifier from the cart;.
431
     *
432
     * @param HTTPRequest
433
     *
434
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
435
     *               If it is not AJAX it redirects back to requesting page.
436
     */
437
    public function removemodifier(SS_HTTPRequest $request)
438
    {
439
        $modifierID = intval($request->param('ID'));
440
        $this->cart->removeModifier($modifierID);
0 ignored issues
show
Documentation introduced by
$modifierID is of type integer, but the function expects a object<OrderModifier>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
441
        $order = $this->cart->CurrentOrder();
442
        $order->calculateOrderAttributes($force = true);
443
444
        return $this->cart->setMessageAndReturn();
445
    }
446
447
    /**
448
     * Adds a specified modifier to the cart;.
449
     *
450
     * @param HTTPRequest
451
     *
452
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
453
     *               If it is not AJAX it redirects back to requesting page.
454
     */
455
    public function addmodifier(SS_HTTPRequest $request)
456
    {
457
        $modifierID = intval($request->param('ID'));
458
        $this->cart->addModifier($modifierID);
459
        $order = $this->cart->CurrentOrder();
460
        $order->calculateOrderAttributes($force = true);
461
462
        return $this->cart->setMessageAndReturn();
463
    }
464
465
    /**
466
     * sets the country.
467
     *
468
     * @param SS_HTTPRequest
469
     *
470
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
471
     *               If it is not AJAX it redirects back to requesting page.
472
     **/
473
    public function setcountry(SS_HTTPRequest $request)
474
    {
475
        $countryCode = Convert::raw2sql($request->param('ID'));
476
        //set_country will check if the country code is actually allowed....
477
        $this->cart->setCountry($countryCode);
478
479
        return $this->cart->setMessageAndReturn();
480
    }
481
482
    /**
483
     * @param SS_HTTPRequest
484
     *
485
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
486
     *               If it is not AJAX it redirects back to requesting page.
487
     **/
488
    public function setregion(SS_HTTPRequest $request)
489
    {
490
        $regionID = intval($request->param('ID'));
491
        $this->cart->setRegion($regionID);
492
493
        return $this->cart->setMessageAndReturn();
494
    }
495
496
    /**
497
     * @param SS_HTTPRequest
498
     *
499
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
500
     *               If it is not AJAX it redirects back to requesting page.
501
     **/
502
    public function setcurrency(SS_HTTPRequest $request)
503
    {
504
        $currencyCode = Convert::raw2sql($request->param('ID'));
505
        $this->cart->setCurrency($currencyCode);
0 ignored issues
show
Bug introduced by
It seems like $currencyCode defined by \Convert::raw2sql($request->param('ID')) on line 504 can also be of type array; however, ShoppingCart::setCurrency() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
506
507
        return $this->cart->setMessageAndReturn();
508
    }
509
510
    /**
511
     * @param SS_HTTPRequest
512
     *
513
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
514
     *               If it is not AJAX it redirects back to requesting page.
515
     **/
516
    public function removefromsale(SS_HTTPRequest $request)
517
    {
518
        if (EcommerceRole::current_member_is_shop_assistant()) {
519
            $className = Convert::raw2sql($request->param('ID'));
520
            $id = intval($request->param('OtherID'));
521
            if (class_exists($className)) {
522
                $obj = $className::get()->byID($id);
523
                $obj->AllowPurchase = 0;
524
                if ($obj instanceof SiteTree) {
525
                    $obj->writeToStage('Stage');
526
                    $obj->doPublish();
527
                } else {
528
                    $obj->write();
529
                }
530
            }
531
532
            return $this->cart->setMessageAndReturn();
533
        } else {
534
            return Security::permissionFailure($this);
535
        }
536
    }
537
538
    /**
539
     * @param SS_HTTPRequest
540
     *
541
     * @return mixed - if the request is AJAX, it returns JSON - CartResponse::ReturnCartData();
542
     *               If it is not AJAX it redirects back to requesting page.
543
     **/
544
    public function save(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
545
    {
546
        $order = $this->cart->save();
0 ignored issues
show
Unused Code introduced by
$order is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
547
548
        return $this->cart->setMessageAndReturn();
549
    }
550
551
    /**
552
     * @param SS_HTTPRequest
553
     *
554
     * @return REDIRECT
0 ignored issues
show
Documentation introduced by
Should the return type not be array?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
555
     **/
556
    public function clear(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
557
    {
558
        $this->cart->clear();
559
        $this->redirect(Director::baseURL());
560
561
        return array();
562
    }
563
564
    /**
565
     * @param SS_HTTPRequest
566
     *
567
     * @return REDIRECT
0 ignored issues
show
Documentation introduced by
Should the return type not be array?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
568
     **/
569
    public function clearandlogout(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
570
    {
571
        $this->cart->clear();
572
        if ($member = Member::currentUser()) {
573
            $member->logout();
574
        }
575
        $this->redirect(Director::baseURL());
576
577
        return array();
578
    }
579
580
    /**
581
     * @param SS_HTTPRequest
582
     *
583
     * @return REDIRECT
0 ignored issues
show
Documentation introduced by
Should the return type not be REDIRECT|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
584
     **/
585
    public function deleteorder(SS_HTTPRequest $request)
586
    {
587
        $orderID = intval($request->param('ID'));
588
        if ($order = Order::get_by_id_if_can_view($orderID)) {
589
            if ($order->canDelete()) {
590
                $order->delete();
591
            }
592
        }
593
        $this->redirectBack();
594
    }
595
596
    public function copyorder($request)
597
    {
598
        $orderID = intval($request->param('ID'));
599
        if ($order = Order::get_by_id_if_can_view($orderID)) {
600
            $this->cart->copyOrder($order);
601
        }
602
        $link = CheckoutPage::find_link();
603
        return $this->redirect($link);
604
    }
605
606
    /**
607
     * return number of items in cart.
608
     *
609
     * @param SS_HTTPRequest
610
     *
611
     * @return int
612
     **/
613
    public function numberofitemsincart(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
614
    {
615
        $order = $this->cart->CurrentOrder();
616
617
        return $order->TotalItems($recalculate = true);
618
    }
619
620
    /**
621
     * return cart for ajax call.
622
     *
623
     * @param SS_HTTPRequest
624
     *
625
     * @return HTML
0 ignored issues
show
Documentation introduced by
Should the return type not be HTMLText?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
626
     */
627
    public function showcart(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
628
    {
629
        return $this->customise($this->cart->CurrentOrder())->renderWith('AjaxCart');
630
    }
631
632
    /**
633
     * loads an order.
634
     *
635
     * @param SS_HTTPRequest
636
     *
637
     * @return REDIRECT
0 ignored issues
show
Documentation introduced by
Should the return type not be SS_HTTPResponse|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
638
     */
639
    public function loadorder(SS_HTTPRequest $request)
640
    {
641
        $this->cart->loadOrder(intval($request->param('ID')));
642
        $cartPageLink = CartPage::find_link();
643
        if ($cartPageLink) {
644
            return $this->redirect($cartPageLink);
645
        } else {
646
            return $this->redirect(Director::baseURL());
647
        }
648
    }
649
650
    /**
651
     * remove address from list of available addresses in checkout.
652
     *
653
     * @param SS_HTTPRequest
654
     *
655
     * @return string | REDIRECT
0 ignored issues
show
Documentation introduced by
Should the return type not be string|array?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
656
     * @TODO: add non-ajax version of this request.
657
     */
658
    public function removeaddress(SS_HTTPRequest $request)
659
    {
660
        $id = intval($request->param('ID'));
661
        $className = Convert::raw2sql($request->param('OtherID'));
662
        if (class_exists($className)) {
663
            $address = $className::get()->byID($id);
664
            if ($address && $address->canView()) {
665
                $member = Member::currentUser();
666
                if ($member) {
667
                    $address->MakeObsolete($member);
668
                    if ($request->isAjax()) {
669
                        return _t('Order.ADDRESSREMOVED', 'Address removed.');
670
                    } else {
671
                        $this->redirectBack();
672
                    }
673
                }
674
            }
675
        }
676
        if ($request->isAjax()) {
677
            return _t('Order.ADDRESSNOTREMOVED', 'Address could not be removed.');
678
        } else {
679
            $this->redirectBack();
680
        }
681
682
        return array();
683
    }
684
685
    /**
686
     * allows us to view out-dated buyables that have been deleted
687
     * where only old versions exist.
688
     * this method should redirect.
689
     *
690
     * @param SS_HTTPRequest
691
     *
692
     * @return REDIRECT
0 ignored issues
show
Documentation introduced by
Should the return type not be array|SS_HTTPResponse|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
693
     */
694
    public function submittedbuyable(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
695
    {
696
        $buyableClassName = Convert::raw2sql($this->getRequest()->param('ID'));
697
        $buyableID = intval($this->getRequest()->param('OtherID'));
698
        $version = intval($this->getRequest()->param('Version'));
699
        if ($buyableClassName && $buyableID) {
700
            if (EcommerceDBConfig::is_buyable($buyableClassName)) {
0 ignored issues
show
Bug introduced by
It seems like $buyableClassName defined by \Convert::raw2sql($this-...Request()->param('ID')) on line 696 can also be of type array; however, EcommerceDBConfig::is_buyable() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
701
                $bestBuyable = $buyableClassName::get()->byID($buyableID);
702
                if ($bestBuyable instanceof ProductVariation) {
0 ignored issues
show
Bug introduced by
The class ProductVariation does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
703
                    $link = $bestBuyable->Link('filterforvariations/'.$buyableID.'/?version='.$version.'/');
704
                    $this->redirect($link);
705
706
                    return array();
707
                }
708
                if ($bestBuyable) {
709
                    //show singleton with old version
710
                    $link = $bestBuyable->Link('viewversion/'.$version.'/');
711
                    $this->redirect($link);
712
713
                    return array();
714
                }
715
            }
716
        }
717
        $errorPage404 = DataObject::get_one(
718
            'ErrorPage',
719
            array('ErrorCode' => '404')
720
        );
721
        if ($errorPage404) {
722
            return $this->redirect($errorPage404->Link());
723
        }
724
725
        return;
726
    }
727
728
    /**
729
     * This can be used by admins to log in as customers
730
     * to place orders on their behalf...
731
     *
732
     * @param SS_HTTPRequest
733
     *
734
     * @return REDIRECT
0 ignored issues
show
Documentation introduced by
Should the return type not be SS_HTTPResponse|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
735
     */
736
    public function placeorderformember(SS_HTTPRequest $request)
737
    {
738
        if (EcommerceRole::current_member_is_shop_admin()) {
739
            $member = Member::get()->byID(intval($request->param('ID')));
740
            if ($member) {
741
                $newOrder = Order::create();
742
                //copying fields.
743
                $newOrder->MemberID = $member->ID;
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
744
                //load the order
745
                $newOrder->write();
746
                $this->cart->loadOrder($newOrder);
747
748
                return $this->redirect($newOrder->Link());
749
            } else {
750
                user_error('Can not find this member.');
751
            }
752
        } else {
753
            //echo "please <a href=\"Security/login/?BackURL=".urlencode($this->config()->get("url_segment")."/placeorderformember/".$request->param("ID")."/")."\">log in</a> first.";
754
            return Security::permissionFailure($this);
755
        }
756
    }
757
758
    /**
759
     * This can be used by admins to log in as customers
760
     * to place orders on their behalf...
761
     *
762
     * @param SS_HTTPRequest
763
     *
764
     * @return REDIRECT
0 ignored issues
show
Documentation introduced by
Should the return type not be SS_HTTPResponse|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
765
     */
766
    public function loginas(SS_HTTPRequest $request)
767
    {
768
        if (Permission::check('ADMIN')) {
769
            $newMember = Member::get()->byID(intval($request->param('ID')));
770
            if ($newMember) {
771
                //$memberToTest->logout();
772
                $newMember->logIn();
773
                if ($accountPage = DataObject::get_one('AccountPage')) {
774
                    return $this->redirect($accountPage->Link());
775
                } else {
776
                    return $this->redirect(Director::baseURL());
777
                }
778
            } else {
779
                user_error('Can not find this member.');
780
            }
781
        } else {
782
            return Security::permissionFailure($this);
783
        }
784
    }
785
786
    /**
787
     * Helper function used by link functions
788
     * Creates the appropriate url-encoded string parameters for links from array.
789
     *
790
     * Produces string such as: MyParam%3D11%26OtherParam%3D1
791
     *     ...which decodes to: MyParam=11&OtherParam=1
792
     *
793
     * you will need to decode the url with javascript before using it.
794
     *
795
     * @todo: check that comment description actually matches what it does
796
     *
797
     * @return string (URLSegment)
798
     */
799
    protected static function params_to_get_string(array $array)
800
    {
801
        $token = SecurityToken::inst();
802
        if (!isset($array['SecurityID'])) {
803
            $array['SecurityID'] = $token->getValue();
804
        }
805
806
        return '?'.http_build_query($array);
807
    }
808
809
    /**
810
     * Gets a buyable object based on URL actions.
811
     *
812
     * @return DataObject | Null - returns buyable
813
     */
814
    protected function buyable()
815
    {
816
        $buyableClassName = Convert::raw2sql($this->getRequest()->param('OtherID'));
817
        $buyableID = intval($this->getRequest()->param('ID'));
818
        if ($buyableClassName && $buyableID) {
819
            if (EcommerceDBConfig::is_buyable($buyableClassName)) {
0 ignored issues
show
Bug introduced by
It seems like $buyableClassName defined by \Convert::raw2sql($this-...st()->param('OtherID')) on line 816 can also be of type array; however, EcommerceDBConfig::is_buyable() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
820
                $obj = $buyableClassName::get()->byID(intval($buyableID));
821
                if ($obj) {
822
                    if ($obj->ClassName == $buyableClassName) {
823
                        return $obj;
824
                    }
825
                }
826
            } else {
827
                if (strpos($buyableClassName, 'OrderItem')) {
828
                    user_error('ClassName in URL should be buyable and not an orderitem', E_USER_NOTICE);
829
                }
830
            }
831
        }
832
833
        return;
834
    }
835
836
    /**
837
     * Gets the requested quantity.
838
     *
839
     * @return float
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
840
     */
841
    protected function quantity()
842
    {
843
        $quantity = $this->getRequest()->getVar('quantity');
844
        if (is_numeric($quantity)) {
845
            return $quantity;
846
        }
847
848
        return 1;
849
    }
850
851
    /**
852
     * Gets the request parameters.
853
     *
854
     * @param $getpost - choose between obtaining the chosen parameters from GET or POST
855
     *
856
     * @return array
857
     */
858
    protected function parameters($getpost = 'GET')
859
    {
860
        return ($getpost == 'GET') ? $this->getRequest()->getVars() : $_POST;
861
    }
862
863
    protected function goToErrorPage()
864
    {
865
        $errorPage404 = DataObject::get_one(
866
            'ErrorPage',
867
            array('ErrorCode' => '404')
868
        );
869
        if ($errorPage404) {
870
            return $this->redirect($errorPage404->Link());
871
        }
872
        return $this->redirect('page-not-found');
873
    }
874
875
    /**
876
     * Handy debugging action visit.
877
     * Log in as an administrator and visit mysite/shoppingcart/debug.
878
     */
879
    public function debug()
880
    {
881
        if (Director::isDev() || EcommerceRole::current_member_is_shop_admin()) {
882
            return $this->cart->debug();
883
        } else {
884
            return Security::permissionFailure($this);
885
            //echo "please <a href=\"Security/login/?BackURL=".urlencode($this->config()->get("url_segment")."/debug/")."\">log in</a> first.";
886
        }
887
    }
888
889
    /**
890
     * test the ajax response
891
     * for developers only.
892
     *
893
     * @return output to buffer
0 ignored issues
show
Documentation introduced by
Should the return type not be output|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
894
     */
895
    public function ajaxtest(SS_HTTPRequest $request)
896
    {
897
        if (Director::isDev() || Permission::check('ADMIN')) {
898
            header('Content-Type', 'text/plain');
899
            echo '<pre>';
900
            $_REQUEST['ajax'] = 1;
901
            $v = $this->cart->setMessageAndReturn('test only');
902
            $v = str_replace(',', ",\r\n\t\t", $v);
903
            $v = str_replace('}', "\r\n\t}", $v);
904
            $v = str_replace('{', "\t{\r\n\t\t", $v);
905
            $v = str_replace(']', "\r\n]", $v);
906
            echo $v;
907
            echo '</pre>';
908
        } else {
909
            echo 'please <a href="Security/login/?BackURL='.urlencode($this->config()->get('url_segment').'/ajaxtest/').'">log in</a> first.';
910
        }
911
        if (!$request->isAjax()) {
912
            die('---- make sure to add ?ajax=1 to the URL ---');
913
        }
914
    }
915
}
916