Helper::getShoppingcartData()   B
last analyzed

Complexity

Conditions 6
Paths 2

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 8.8177
c 0
b 0
f 0
cc 6
nc 2
nop 0
1
<?php
2
3
/*
4
    HCSF - A multilingual CMS and Shopsystem
5
    Copyright (C) 2014  Marcus Haase - [email protected]
6
7
    This program is free software: you can redistribute it and/or modify
8
    it under the terms of the GNU General Public License as published by
9
    the Free Software Foundation, either version 3 of the License, or
10
    (at your option) any later version.
11
12
    This program is distributed in the hope that it will be useful,
13
    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
    GNU General Public License for more details.
16
17
    You should have received a copy of the GNU General Public License
18
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
namespace HaaseIT\HCSF\Shop;
22
23
24
use Zend\ServiceManager\ServiceManager;
25
26
/**
27
 * Class Helper
28
 * @package HaaseIT\HCSF\Shop
29
 */
30
class Helper
31
{
32
    /**
33
     * @var ServiceManager
34
     */
35
    protected $serviceManager;
36
37
    /**
38
     * @var \HaaseIT\HCSF\HelperConfig
39
     */
40
    protected $config;
41
42
    /**
43
     * @var array
44
     */
45
    protected $shop = [];
46
47
    /**
48
     * @var \HaaseIT\HCSF\Customer\Helper
49
     */
50
    protected $helperCustomer;
51
52
    public function __construct(ServiceManager $serviceManager)
53
    {
54
        $this->serviceManager = $serviceManager;
55
        $this->config = $serviceManager->get('config');
56
        $this->shop = $this->config->getShop();
57
        $this->helperCustomer = $serviceManager->get('helpercustomer');
58
    }
59
60
    /**
61
     * @param \HaaseIT\Toolbox\Textcat $textcats
62
     * @param string $sStatusShort
63
     * @return bool|string
64
     */
65
    public function showOrderStatusText(\HaaseIT\Toolbox\Textcat $textcats, $sStatusShort)
66
    {
67
        $mapping = [
68
            'y' => 'order_status_completed',
69
            'n' => 'order_status_open',
70
            'i' => 'order_status_inwork',
71
            's' => 'order_status_canceled',
72
            'd' => 'order_status_deleted',
73
        ];
74
75
        if (!empty($mapping[$sStatusShort])) {
76
            return $textcats->T($mapping[$sStatusShort]);
77
        }
78
79
        return '';
80
    }
81
82
    /**
83
     * @param array $aOrder
84
     * @return float
85
     */
86
    public function calculateTotalFromDB($aOrder)
87
    {
88
        $fGesamtnetto = $aOrder['o_sumnettoall'];
89
        $fVoll = $aOrder['o_sumvoll'];
90
        $fSteuererm = $aOrder['o_taxerm'];
91
92
        if ($aOrder['o_mindermenge'] > 0) {
93
            $fVoll += $aOrder['o_mindermenge'];
94
            $fGesamtnetto += $aOrder['o_mindermenge'];
95
        }
96
        if ($aOrder['o_shippingcost'] > 0) {
97
            $fVoll += $aOrder['o_shippingcost'];
98
            $fGesamtnetto += $aOrder['o_shippingcost'];
99
        }
100
101
        $fSteuervoll = ($fVoll * $aOrder['o_vatfull'] / 100);
102
103
        return $fGesamtnetto + $fSteuervoll + $fSteuererm;
104
    }
105
106
    /**
107
     * @param array $aSumme
108
     * @param int $iVATfull
109
     * @param int $iVATreduced
110
     * @return array
111
     */
112
    public function addAdditionalCostsToItems($aSumme, $iVATfull, $iVATreduced)
113
    {
114
        $fGesamtnetto = $aSumme['sumvoll'] + $aSumme['sumerm'];
115
        $fSteuervoll = $aSumme['sumvoll'] * $iVATfull / 100;
116
        $fSteuererm = $aSumme['sumerm'] * $iVATreduced / 100;
117
        $fGesamtbrutto = $fGesamtnetto + $fSteuervoll + $fSteuererm;
118
119
        $aOrder = [
120
            'sumvoll' => $aSumme['sumvoll'],
121
            'sumerm' => $aSumme['sumerm'],
122
            'sumnettoall' => $fGesamtnetto,
123
            'taxvoll' => $fSteuervoll,
124
            'taxerm' => $fSteuererm,
125
            'sumbruttoall' => $fGesamtbrutto,
126
        ];
127
128
        $fGesamtnettoitems = $aOrder['sumnettoall'];
129
        $aOrder['fVoll'] = $aOrder['sumvoll'];
130
        $aOrder['fErm'] = $aOrder['sumerm'];
131
        $aOrder['fGesamtnetto'] = $aOrder['sumnettoall'];
132
        $aOrder['fSteuervoll'] = $aOrder['taxvoll'];
133
        $aOrder['fSteuererm'] = $aOrder['taxerm'];
134
        $aOrder['fGesamtbrutto'] = $aOrder['sumbruttoall'];
135
136
        $aOrder['bMindesterreicht'] = true;
137
        $aOrder['fMindergebuehr'] = 0;
138
        $aOrder['iMindergebuehr_id'] = 0;
139
        if ($fGesamtnettoitems < $this->shop['minimumorderamountnet']) {
140
            $aOrder['bMindesterreicht'] = false;
141
            $aOrder['iMindergebuehr_id'] = 0;
142
        } elseif ($fGesamtnettoitems < $this->shop['reducedorderamountnet1']) {
143
            $aOrder['iMindergebuehr_id'] = 1;
144
145
        } elseif ($fGesamtnettoitems < $this->shop['reducedorderamountnet2']) {
146
            $aOrder['iMindergebuehr_id'] = 2;
147
        }
148
149
        if ($aOrder['iMindergebuehr_id'] > 0) {
150
            $aOrder['fVoll'] += $this->shop['reducedorderamountfee'.$aOrder['iMindergebuehr_id']];
151
            $aOrder['fGesamtnetto'] += $this->shop['reducedorderamountfee'.$aOrder['iMindergebuehr_id']];
152
            $aOrder['fSteuervoll'] = $aOrder['fVoll'] * $iVATfull / 100;
153
            $aOrder['fGesamtbrutto'] = $aOrder['fGesamtnetto'] + $aOrder['fSteuervoll'] + $aOrder['fSteuererm'];
154
            $aOrder['fMindergebuehr'] = $this->shop['reducedorderamountfee'.$aOrder['iMindergebuehr_id']];
155
        }
156
157
        $aOrder['fVersandkosten'] = 0;
158
        if (
159
            isset($this->shop['shippingcoststandardrate'])
160
            && $this->shop['shippingcoststandardrate'] != 0
161
            &&
162
            (
163
                (
164
                    !isset($this->shop['mindestbetragversandfrei'])
165
                    || !$this->shop['mindestbetragversandfrei']
166
                )
167
                || $fGesamtnettoitems < $this->shop['mindestbetragversandfrei']
168
            )
169
        ) {
170
            $aOrder['fVersandkostennetto'] = self::getShippingcost();
171
            $aOrder['fVersandkostenvat'] = $aOrder['fVersandkostennetto'] * $iVATfull / 100;
172
            $aOrder['fVersandkostenbrutto'] = $aOrder['fVersandkostennetto'] + $aOrder['fVersandkostenvat'];
173
174
            $aOrder['fSteuervoll'] = ($aOrder['fVoll'] * $iVATfull / 100) + $aOrder['fVersandkostenvat'];
175
            $aOrder['fVoll'] += $aOrder['fVersandkostennetto'];
176
            $aOrder['fGesamtnetto'] += $aOrder['fVersandkostennetto'];
177
            $aOrder['fGesamtbrutto'] = $aOrder['fGesamtnetto'] + $aOrder['fSteuervoll'] + $aOrder['fSteuererm'];
178
        }
179
180
        return $aOrder;
181
    }
182
183
    /**
184
     * @return mixed
185
     */
186
    public function getShippingcost()
187
    {
188
        $fShippingcost = $this->shop['shippingcoststandardrate'];
189
190
        $sCountry = $this->helperCustomer->getDefaultCountryByConfig($this->config->getLang());
191
        $postdocheckout = filter_input(INPUT_POST, 'doCheckout');
192
        if (isset($_SESSION['user']['cust_country'])) {
193
            $sCountry = $_SESSION['user']['cust_country'];
194
        } elseif (filter_input(INPUT_POST, 'country') !== null && $postdocheckout === 'yes') {
195
            $sCountry = trim(\HaaseIT\Toolbox\Tools::getFormfield('country'));
196
        } elseif (isset($_SESSION['formsave_addrform']['country'])) {
197
            $sCountry = $_SESSION['formsave_addrform']['country'];
198
        }
199
200
        foreach ($this->shop['shippingcosts'] as $aValue) {
201
            if (isset($aValue['countries'][$sCountry])) {
202
                $fShippingcost = $aValue['cost'];
203
                break;
204
            }
205
        }
206
207
        return $fShippingcost;
208
    }
209
210
    /**
211
     * @param array $aCart
212
     * @return array
213
     */
214
    public function calculateCartItems($aCart)
215
    {
216
        $fErm = 0;
217
        $fVoll = 0;
218
        $fTaxErm = 0;
219
        $fTaxVoll = 0;
220
        foreach ($aCart as $aValue) {
221
            // Hmmmkay, so, if vat is not disabled and there is no vat id or none as vat id set to this item, then
222
            // use the full vat as default. Only use reduced if it is set. Gotta use something as default or item
223
            // will not add up to total price
224
            if ($aValue['vat'] !== 'reduced') {
225
                $fVoll += ($aValue['amount'] * $aValue['price']['netto_use']);
226
                $fTaxVoll += ($aValue['amount'] * $aValue['price']['netto_use'] * ($this->shop['vat']['full'] / 100));
227
                continue;
228
            }
229
230
            $fErm += ($aValue['amount'] * $aValue['price']['netto_use']);
231
            $fTaxErm += ($aValue['amount'] * $aValue['price']['netto_use'] * ($this->shop['vat']['reduced'] / 100));
232
        }
233
234
        return [
235
            'sumvoll' => $fVoll,
236
            'sumerm' => $fErm,
237
            'taxvoll' => $fTaxVoll,
238
            'taxerm' => $fTaxErm
239
        ];
240
    }
241
242
    /**
243
     * @param ServiceManager $serviceManager
244
     */
245
    public function refreshCartItems(ServiceManager $serviceManager) // bei login/logout ändern sich ggf die preise, shoppingcart neu berechnen
246
    {
247
        if (isset($_SESSION['cart']) && is_array($_SESSION['cart'])) {
248
            foreach ($_SESSION['cart'] as $sKey => $aValue) {
249
                $sItemkey = $sKey;
250
                if (!empty($this->shop['custom_order_fields'])) {
251
                    $TMP = explode('|', $sKey);
252
                    $sItemkey = $TMP[0];
253
                    unset($TMP);
254
                }
255
                $aData = $serviceManager->get('oItem')->sortItems('', $sItemkey);
256
                $_SESSION['cart'][$sKey]['price'] = $aData['item'][$sItemkey]['pricedata'];
257
            }
258
        }
259
    }
260
261
    /**
262
     * @param array $aCart
263
     * @param bool $bReadonly
264
     * @param string $sCustomergroup
265
     * @param array $aErr
266
     * @param string $iVATfull
267
     * @param string $iVATreduced
268
     * @return mixed
269
     */
270
    public function buildShoppingCartTable(array $aCart, $bReadonly = false, $sCustomergroup = '', $aErr = [], $iVATfull = '', $iVATreduced = '')
271
    {
272
        if ($iVATfull === '' && $iVATreduced === '') {
273
            $iVATfull = $this->shop['vat']['full'];
274
            $iVATreduced = $this->shop['vat']['reduced'];
275
        }
276
        $aSumme = self::calculateCartItems($aCart);
277
        $aData['shoppingcart'] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
$aData was never initialized. Although not strictly required by PHP, it is generally a good practice to add $aData = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
278
            'readonly' => $bReadonly,
279
            'customergroup' => $sCustomergroup,
280
            'cart' => $aCart,
281
            'rebategroups' => $this->shop['rebate_groups'],
282
            'additionalcoststoitems' => self::addAdditionalCostsToItems($aSumme, $iVATfull, $iVATreduced),
283
            'minimumorderamountnet' => $this->shop['minimumorderamountnet'],
284
            'reducedorderamountnet1' => $this->shop['reducedorderamountnet1'],
285
            'reducedorderamountnet2' => $this->shop['reducedorderamountnet2'],
286
            'reducedorderamountfee1' => $this->shop['reducedorderamountfee1'],
287
            'reducedorderamountfee2' => $this->shop['reducedorderamountfee2'],
288
            'minimumamountforfreeshipping' => $this->shop['minimumamountforfreeshipping'],
289
        ];
290
291
        if (!$bReadonly) {
292
            $aCartpricesums = $aData['shoppingcart']['additionalcoststoitems'];
293
            $_SESSION['cartpricesums'] = $aCartpricesums;
294
        }
295
296
        if (!$bReadonly && $aData['shoppingcart']['additionalcoststoitems']['bMindesterreicht']) {
297
            $aData['customerform'] = $this->helperCustomer->buildCustomerForm($this->config->getLang(), 'shoppingcart', $aErr);
298
        }
299
300
        return $aData;
301
    }
302
303
    /**
304
     * @return array
305
     */
306
    public function getShoppingcartData()
307
    {
308
        $aCartinfo = [
309
            'numberofitems' => 0,
310
            'cartsums' => [],
311
            'cartsumnetto' => 0,
312
            'cartsumbrutto' => 0,
313
        ];
314
        if (isset($_SESSION['cart']) && (!$this->shop['show_pricesonlytologgedin'] || $this->helperCustomer->getUserData()) && count($_SESSION['cart'])) {
315
            $aCartsums = $this->calculateCartItems($_SESSION['cart']);
316
            $aCartinfo = [
317
                'numberofitems' => count($_SESSION['cart']),
318
                'cartsums' => $aCartsums,
319
                'cartsumnetto' => $aCartsums['sumvoll'] + $aCartsums['sumerm'],
320
                'cartsumbrutto' => $aCartsums['sumvoll'] + $aCartsums['sumerm'] + $aCartsums['taxerm'] + $aCartsums['taxvoll'],
321
            ];
322
            unset($aCartsums);
323
            foreach ($_SESSION['cart'] as $sKey => $aValue) {
324
                $aCartinfo['cartitems'][$sKey] = [
325
                    'cartkey' => $sKey,
326
                    'name' => $aValue['name'],
327
                    'amount' => $aValue['amount'],
328
                    'img' => $aValue['img'],
329
                    'price' => $aValue['price'],
330
                ];
331
            }
332
        }
333
334
        return $aCartinfo;
335
    }
336
337
    /**
338
     * @param Items $oItem
339
     * @param array $aPossibleSuggestions
340
     * @param string $sSetSuggestions
341
     * @param string $sCurrentitem
342
     * @param string|array $mItemindex
343
     * @param array $itemindexpathtree
344
     * @return array
345
     */
346
    public function getItemSuggestions(
347
        Items $oItem,
348
        $aPossibleSuggestions,
349
        $sSetSuggestions,
350
        $sCurrentitem,
351
        $mItemindex,
352
        $itemindexpathtree
353
    )
354
    {
355
        //$aPossibleSuggestions = $aP["items"]["item"]; // put all possible suggestions that are already loaded into this array
356
        unset($aPossibleSuggestions[$sCurrentitem]); // remove the currently shown item from this list, we do not want to show it as a suggestion
357
358
        $suggestions = static::prepareSuggestions($sSetSuggestions, $aPossibleSuggestions, $oItem);
359
360
        $suggestions = static::fillSuggestions($suggestions);
361
362
        foreach ($suggestions as $aSuggestionsKey => $aSuggestionsValue) { // build the paths to the suggested items
363
            if (mb_strpos($aSuggestionsValue['itm_index'], '|') !== false) { // check if the suggestions itemindex contains multiple indexes, if so explode an array
364
                $aSuggestionIndexes = explode('|', $aSuggestionsValue['itm_index']);
365
366
                // iterate through these indexes
367
                foreach ($aSuggestionIndexes as $suggestionindexval) {
368
                    // check if there is an index configured on this page
369
                    if (isset($mItemindex)) {
370
                        // check if it is an array and if the suggestions index is in that array, set path to empty string
371
                        if (is_array($mItemindex) && in_array($suggestionindexval, $mItemindex)) {
372
                            $suggestions[$aSuggestionsKey]['path'] = '';
373
                            // path to suggestion set, continue with next suggestion
374
                            continue 2;
375
                        // if the suggestion index is on this page, set path to empty string
376
                        } elseif ($mItemindex == $suggestionindexval) {
377
                            $suggestions[$aSuggestionsKey]['path'] = '';
378
                            continue 2; // path to suggestion set, continue with next suggestion
379
                        }
380
                    }
381
                    if (isset($itemindexpathtree[$suggestionindexval])) {
382
                        $suggestions[$aSuggestionsKey]['path'] = $itemindexpathtree[$suggestionindexval];
383
                        continue 2;
384
                    }
385
                }
386
            } elseif (isset($itemindexpathtree[$aSuggestionsValue['itm_index']])) {
387
                $suggestions[$aSuggestionsKey]['path'] = $itemindexpathtree[$aSuggestionsValue['itm_index']];
388
            }
389
        }
390
        if ($this->shop['itemdetail_suggestions_shuffle']) {
391
            shuffle($suggestions);
392
        }
393
394
        return $suggestions;
395
    }
396
397
    /**
398
     * @param string $sSetSuggestions - Items defined as Suggestions in Item config
399
     * @param array $aPossibleSuggestions - Items from the same category
400
     * @param Items $oItem
401
     * @return array
402
     */
403
    public function prepareSuggestions($sSetSuggestions, array $aPossibleSuggestions, Items $oItem)
404
    {
405
        // prepare defined suggestions
406
        $sSetSuggestions = trim($sSetSuggestions);
407
        $aDefinedSuggestions = [];
408
        if (!empty($sSetSuggestions)) {
409
            $aDefinedSuggestions = explode('|', $sSetSuggestions);
410
        }
411
412
        // see, which suggestions are not loaded through the current item category yet
413
        $aSuggestionsToLoad = [];
414
        // iterate all defined suggestions and put those not loaded yet into array
415
        foreach ($aDefinedSuggestions as $defisugsval) {
416
            if (!isset($aPossibleSuggestions[$defisugsval])) {
417
                $aSuggestionsToLoad[] = $defisugsval;
418
            }
419
        }
420
421
        // if there are not yet loaded suggestions, load them
422
        if (isset($aSuggestionsToLoad)) {
423
            $aItemsNotInCategory = $oItem->sortItems('', $aSuggestionsToLoad, false);
0 ignored issues
show
Documentation introduced by
$aSuggestionsToLoad is of type array, but the function expects a string.

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...
424
425
            // merge loaded and newly loaded items
426
            if (!empty($aItemsNotInCategory)) {
427
                $aPossibleSuggestions = array_merge($aPossibleSuggestions, $aItemsNotInCategory['item']);
428
            }
429
        }
430
431
        // default = configured suggestions, additional = possible suggestions to fill up to configured maximum
432
        $suggestions = [
433
            'default' => [],
434
            'additional' => [],
435
        ];
436
        // iterate through all possible suggestions
437
        foreach ($aPossibleSuggestions as $posssugskey => $posssugsval) {
438
            if (in_array($posssugskey, $aDefinedSuggestions)) { // if this suggestion is a defined one, put into this array
439
                $suggestions['default'][$posssugskey] = $posssugsval;
440
                continue;
441
            }
442
            // if not, put into this one
443
            $suggestions['additional'][$posssugskey] = $posssugsval;
444
        }
445
446
        // now we see, that the configured suggestions are ordered as configured
447
        $aDefinedSuggestions = array_reverse($aDefinedSuggestions, true);
448
        foreach ($aDefinedSuggestions as $aDefinedSuggestion) {
449
            if (isset($suggestions['default'][$aDefinedSuggestion])) {
450
                $tmp = $suggestions['default'][$aDefinedSuggestion];
451
                unset($suggestions['default'][$aDefinedSuggestion]);
452
                $suggestions['default'] = [$aDefinedSuggestion => $tmp] + $suggestions['default'];
453
            }
454
        }
455
456
        return $suggestions;
457
    }
458
459
    /**
460
     * @param array $suggestions
461
     * @return array
462
     */
463
    public function fillSuggestions($suggestions)
464
    {
465
        $iNumberOfSuggestions = count($suggestions['default']);
466
        if ($iNumberOfSuggestions > $this->shop['itemdetail_suggestions']) { // if there are more suggestions than should be displayed, randomly pick as many as to be shown
467
            $aKeysSuggestions = array_rand($suggestions['default'], $this->shop['itemdetail_suggestions']); // get the array keys that will stay
468
            foreach ($suggestions['default'] as $aSuggestionsKey => $aSuggestionsValue) { // iterate suggestions and remove those that which will not be kept
469
                if (!in_array($aSuggestionsKey, $aKeysSuggestions)) {
470
                    unset($suggestions['default'][$aSuggestionsKey]);
471
                }
472
            }
473
474
            return $suggestions['default'];
475
        }
476
477
        // if less or equal continue here
478
        $numAdditionalSuggs = count($suggestions['additional']);
479
        if (
480
            $numAdditionalSuggs > 0
481
            && $iNumberOfSuggestions < $this->shop['itemdetail_suggestions']
482
        ) { // if there are less suggestions than should be displayed and there are additional available
483
            // how many more are needed?
484
            $addSuggsRequired = $this->shop['itemdetail_suggestions'] - $iNumberOfSuggestions;
485
            // see if there are more available than required, if so, pick as many as needed
486
            if ($numAdditionalSuggs > $addSuggsRequired) {
487
                // since array_rand returns a string and no array if there is only one row picked, we have to do this awkward dance
488
                $keysAddSuggsTMP = array_rand($suggestions['additional'], $addSuggsRequired);
489
                if (is_string($keysAddSuggsTMP) || is_int($keysAddSuggsTMP)) {
490
                    $keysAddSuggsTMP = [$keysAddSuggsTMP];
491
                }
492
                // because array_rand will change numerical (string) values to integer, we have to do this awkward dance
493
                foreach ($keysAddSuggsTMP as $key => $item) {
0 ignored issues
show
Bug introduced by
The expression $keysAddSuggsTMP of type object|double|null|array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
494
                    $keysAddSuggs[] = (string)$item;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$keysAddSuggs was never initialized. Although not strictly required by PHP, it is generally a good practice to add $keysAddSuggs = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
495
                }
496
497
                // iterate suggestions and remove those that which will not be kept
498
                foreach ($suggestions['additional'] as $addSuggsKey => $addSuggsVal) {
499
                    if (!in_array((string)$addSuggsKey, $keysAddSuggs, true)) {
0 ignored issues
show
Bug introduced by
The variable $keysAddSuggs does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
500
                        unset($suggestions['additional'][$addSuggsKey]);
501
                    }
502
                }
503
            }
504
            return array_merge($suggestions['default'], $suggestions['additional']); // merge
505
        }
506
507
        // if the number of default suggestions is not larger than configured and also not smaller, then it equals the
508
        // configured amount, so lets return this then.
509
        return $suggestions['default'];
510
    }
511
512
    /**
513
     * @param ServiceManager $serviceManager
514
     * @param \HaaseIT\HCSF\Page $P
515
     * @param array $aP
516
     * @return mixed
517
     */
518
    public function handleItemPage(ServiceManager $serviceManager, \HaaseIT\HCSF\Page $P, array $aP)
519
    {
520
        $mItemIndex = '';
521
        if (isset($P->cb_pageconfig->itemindex)) {
522
            $mItemIndex = $P->cb_pageconfig->itemindex;
523
        }
524
525
        /** @var \HaaseIT\HCSF\Shop\Items $oItem */
526
        $oItem = $serviceManager->get('oItem');
527
528
        $aP['items'] = $oItem->sortItems($mItemIndex, '', ($aP['pagetype'] === 'itemoverviewgrpd'));
529
530
        // this is a way to link directly to an itemdetailpage
531
        if (
532
            count($aP['items']['item']) === 1
533
            && filter_input(INPUT_GET, 'artnoexact') !== null
534
            && filter_input(INPUT_GET, 'itemdetail') !== null
535
        ) {
536
            $aP['pagetype'] = 'itemdetail';
537
            if (!is_object($P->cb_pageconfig)) {
538
                $P->cb_pageconfig = new \stdClass();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \stdClass() of type object<stdClass> is incompatible with the declared type array|string of property $cb_pageconfig.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
539
            }
540
            $P->cb_pageconfig->itemno = current($aP['items']['item'])['itm_no'];
541
        }
542
543
        if ($aP['pagetype'] === 'itemdetail') {
544
545
            $aP['itemindexpathtreeforsuggestions'] = $oItem->getItemPathTree();
546
547
            if (isset($aP['pageconfig']->itemindex)) {
548
                $aP['itemindexpathtreeforsuggestions'][$aP['pageconfig']->itemindex] = '';
549
                if (is_array($aP['pageconfig']->itemindex)) {
550
                    foreach ($aP['pageconfig']->itemindex as $sItemIndexValue) {
551
                        $aP['itemindexpathtreeforsuggestions'][$sItemIndexValue] = '';
552
                    }
553
                }
554
            }
555
            $aP = $this->seekItem($P, $aP, $oItem);
556
        }
557
558
        return $aP;
559
    }
560
561
    /**
562
     * @param \HaaseIT\HCSF\Page $P
563
     * @param array $aP
564
     * @param Items $oItem
565
     * @return mixed
566
     */
567
    public function seekItem(\HaaseIT\HCSF\Page $P, array $aP, Items $oItem)
568
    {
569
        // Change pagetype to itemoverview, will be changed back to itemdetail once the item is found
570
        // if it is not found, we will show the overview
571
        $aP['pagetype'] = 'itemoverview';
572
        if (count($aP['items']['item'])) {
573
            foreach ($aP['items']['item'] as $sKey => $aValue) {
574
                if ($aValue['itm_no'] != $P->cb_pageconfig->itemno) {
575
                    continue;
576
                }
577
578
                $aP['pagetype'] = 'itemdetail';
579
                $aP['item']['data'] = $aValue;
580
                $aP['item']['key'] = $sKey;
581
582
                if ($aP['items']['totalitems'] > 1) {
583
                    $iPositionInItems = array_search($sKey, $aP['items']['itemkeys']);
584
                    $aP['item']['currentitem'] = $iPositionInItems + 1;
585
586
                    if ($iPositionInItems === 0) {
587
                        $aP['item']['previtem'] = $aP['items']['itemkeys'][$aP['items']['totalitems'] - 1];
588
                    } else {
589
                        $aP['item']['previtem'] = $aP['items']['itemkeys'][$iPositionInItems - 1];
590
                    }
591
592
                    $aP['item']['nextitem'] = $aP['items']['itemkeys'][$iPositionInItems + 1];
593
                    if ($iPositionInItems == $aP['items']['totalitems'] - 1) {
594
                        $aP['item']['nextitem'] = $aP['items']['itemkeys'][0];
595
                    }
596
                } else {
597
                    $aP['item']['currentitem'] = 1;
598
                    $aP['item']['previtem'] = 1;
599
                    $aP['item']['nextitem'] = 1;
600
                }
601
602
                // build item suggestions if needed
603
                if ($this->shop['itemdetail_suggestions'] > 0) {
604
                    $aP['item']['suggestions'] = self::getItemSuggestions(
605
                        $oItem,
606
                        $aP['items']['item'],
607
                        (!empty($aValue['itm_data']['suggestions']) ? $aValue['itm_data']['suggestions'] : ''),
608
                        $sKey,
609
                        (!empty($aP['pageconfig']->itemindex) ? $aP['pageconfig']->itemindex : ''),
610
                        (!empty($aP['itemindexpathtreeforsuggestions']) ? $aP['itemindexpathtreeforsuggestions'] : [])
0 ignored issues
show
Bug introduced by
It seems like !empty($aP['itemindexpat...suggestions'] : array() can also be of type string; however, HaaseIT\HCSF\Shop\Helper::getItemSuggestions() does only seem to accept array, 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...
611
                    );
612
                }
613
                // Wenn der Artikel gefunden wurde können wir das Ausführen der Suche beenden.
614
                break;
615
            }
616
        }
617
618
        return $aP;
619
    }
620
621
    /**
622
     * @param string $itemindex
623
     * @return string
624
     */
625
    public function renderItemStatusIcon($itemindex)
626
    {
627
        if (trim($itemindex) === '') {
628
            return '0';
629
        } elseif (mb_substr($itemindex, 0, 1) === '!') {
630
            return 'X';
631
        }
632
        return 'I';
633
    }
634
635
    // todo: when we use twig 2.x, move this to macro
636
637
    /**
638
     * @param $id
639
     * @return string
640
     */
641
    public function shopadminMakeCheckbox($id)
642
    {
643
        return '<input type="checkbox" name="id[]" value="'.$id.'">';
644
    }
645
}
646