Shoppingcart   F
last analyzed

Complexity

Total Complexity 70

Size/Duplication

Total Lines 426
Duplicated Lines 1.41 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 0
Metric Value
wmc 70
lcom 1
cbo 13
dl 6
loc 426
rs 2.8
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B preparePage() 0 31 7
B validateCheckout() 6 19 7
B getItemImage() 0 48 7
B prepareDataOrder() 0 41 7
A buildOrderItemRow() 0 22 4
A writeCheckoutToDB() 0 32 3
A doCheckout() 0 29 5
A sendCheckoutMails() 0 30 3
A writeCheckoutToFile() 0 7 1
A getPostValue() 0 5 2
F buildOrderMailBody() 0 51 11
C getNotification() 0 37 12

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Shoppingcart often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Shoppingcart, and based on these observations, apply Extract Interface, too.

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\Controller\Shop;
22
23
use HaaseIT\Toolbox\Tools;
24
25
class Shoppingcart extends Base
26
{
27
    /**
28
     * @var \HaaseIT\Toolbox\Textcat
29
     */
30
    private $textcats;
31
32
    /**
33
     * @var array
34
     */
35
    private $imagestosend = [];
36
37
    /**
38
     * Shoppingcart constructor.
39
     * @param \Zend\ServiceManager\ServiceManager $serviceManager
40
     */
41
    public function __construct(\Zend\ServiceManager\ServiceManager $serviceManager)
42
    {
43
        parent::__construct($serviceManager);
44
        $this->textcats = $this->serviceManager->get('textcats');
45
    }
46
47
    /**
48
     *
49
     */
50
    public function preparePage()
51
    {
52
        $this->P = new \HaaseIT\HCSF\CorePage($this->serviceManager);
53
        $this->P->cb_pagetype = 'contentnosubnav';
54
55
        if ($this->config->getShop('show_pricesonlytologgedin') && !$this->helperCustomer->getUserData()) {
56
            $this->P->oPayload->cl_html = $this->textcats->T('denied_notloggedin');
57
        } else {
58
            $this->P->cb_customcontenttemplate = 'shop/shoppingcart';
59
60
            // Check if there is a message to display above the shoppingcart
61
            $this->P->oPayload->cl_html = $this->getNotification();
62
63
            // Display the shoppingcart
64
            if (isset($_SESSION['cart']) && count($_SESSION['cart']) >= 1) {
65
                $aErr = [];
66
                if (filter_input(INPUT_POST, 'doCheckout') === 'yes') {
67
                    $aErr = $this->validateCheckout($aErr);
68
                    if (count($aErr) === 0) {
69
                        $this->helper->redirectToPage($this->doCheckout());
70
                    }
71
                }
72
73
                $aShoppingcart = $this->helperShop->buildShoppingCartTable($_SESSION['cart'], false, '', $aErr);
74
75
                $this->P->cb_customdata = $aShoppingcart;
76
            } else {
77
                $this->P->oPayload->cl_html .= $this->textcats->T('shoppingcart_empty');
78
            }
79
        }
80
    }
81
82
    /**
83
     * @param array $aErr
84
     * @return array
85
     */
86
    private function validateCheckout($aErr = [])
87
    {
88
        $aErr = $this->helperCustomer->validateCustomerForm($this->config->getLang(), $aErr, true);
89 View Code Duplication
        if (!$this->helperCustomer->getUserData() && filter_input(INPUT_POST, 'tos') !== 'y') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
90
            $aErr['tos'] = true;
91
        }
92 View Code Duplication
        if (!$this->helperCustomer->getUserData() && filter_input(INPUT_POST, 'cancellationdisclaimer') !== 'y') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
93
            $aErr['cancellationdisclaimer'] = true;
94
        }
95
        $postpaymentmethod = filter_input(INPUT_POST, 'paymentmethod');
96
        if (
97
            $postpaymentmethod === null
98
            || in_array($postpaymentmethod, $this->config->getShop('paymentmethods'), true) === false
99
        ) {
100
            $aErr['paymentmethod'] = true;
101
        }
102
103
        return $aErr;
104
    }
105
106
    /**
107
     * @param $aV
108
     * @return array
109
     */
110
    private function getItemImage($aV)
111
    {
112
        // base64 encode img and prepare for db
113
        // image/png image/jpeg image/gif
114
        // data:{mimetype};base64,XXXX
115
116
        $aImagesToSend = [];
117
        $base64Img = false;
118
        $binImg = false;
119
120
        if ($this->config->getShop('email_orderconfirmation_embed_itemimages_method') === 'glide') {
121
            $sPathToImage = '/'.$this->config->getCore('directory_images').'/'.$this->config->getShop('directory_images_items').'/';
122
            $sImageroot = PATH_BASEDIR.$this->config->getCore('directory_glide_master');
123
124
            if (
125
                is_file($sImageroot.substr($sPathToImage.$aV['img'], strlen($this->config->getCore('directory_images')) + 1))
126
                && $aImgInfo = getimagesize($sImageroot.substr($sPathToImage.$aV['img'], strlen($this->config->getCore('directory_images')) + 1))
127
            ) {
128
                $glideserver = \League\Glide\ServerFactory::create([
129
                    'source' => $sImageroot,
130
                    'cache' => PATH_GLIDECACHE,
131
                    'max_image_size' => $this->config->getCore('glide_max_imagesize'),
132
                ]);
133
                $glideserver->setBaseUrl('/'.$this->config->getCore('directory_images').'/');
134
                $base64Img = $glideserver->getImageAsBase64($sPathToImage.$aV['img'], $this->config->getShop('email_orderconfirmation_embed_itemimages_glideparams'));
0 ignored issues
show
Documentation introduced by
$this->config->getShop('...temimages_glideparams') is of type string, but the function expects a array.

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...
135
                $TMP = explode(',', $base64Img);
136
                $binImg = base64_decode($TMP[1]);
137
                unset($TMP);
138
            }
139
        } else {
140
            $sPathToImage =
141
                PATH_DOCROOT.$this->config->getCore('directory_images').'/'
142
                .$this->config->getShop('directory_images_items').'/'
143
                .$this->config->getShop('directory_images_items_email').'/';
144
            if ($aImgInfo = getimagesize($sPathToImage.$aV['img'])) {
145
                $binImg = file_get_contents($sPathToImage.$aV['img']);
146
                $base64Img = 'data:'.$aImgInfo['mime'].';base64,';
147
                $base64Img .= base64_encode($binImg);
148
            }
149
        }
150
        if ($this->config->getShop('email_orderconfirmation_embed_itemimages')) {
151
            $aImagesToSend['binimg'] = $binImg;
152
        }
153
        if (!empty($base64Img)) {
154
            $aImagesToSend['base64img'] = $base64Img;
155
        }
156
        return $aImagesToSend;
157
    }
158
159
    /**
160
     * @return array
161
     */
162
    private function prepareDataOrder()
163
    {
164
        $cartpricesums = $_SESSION['cartpricesums'];
165
        return [
166
            'o_custno' => filter_var(trim(Tools::getFormfield('custno')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
167
            'o_email' => filter_var(trim(Tools::getFormfield('email')), FILTER_SANITIZE_EMAIL),
168
            'o_corpname' => filter_var(trim(Tools::getFormfield('corpname')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
169
            'o_name' => filter_var(trim(Tools::getFormfield('name')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
170
            'o_street' => filter_var(trim(Tools::getFormfield('street')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
171
            'o_zip' => filter_var(trim(Tools::getFormfield('zip')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
172
            'o_town' => filter_var(trim(Tools::getFormfield('town')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
173
            'o_phone' => filter_var(trim(Tools::getFormfield('phone')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
174
            'o_cellphone' => filter_var(trim(Tools::getFormfield('cellphone')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
175
            'o_fax' => filter_var(trim(Tools::getFormfield('fax')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
176
            'o_country' => filter_var(trim(Tools::getFormfield('country')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
177
            'o_group' => trim($this->helperCustomer->getUserData('cust_group')),
178
            'o_remarks' => filter_var(trim(Tools::getFormfield('remarks')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
179
            'o_tos' => (filter_input(INPUT_POST, 'tos') === 'y' || $this->helperCustomer->getUserData()) ? 'y' : 'n',
180
            'o_cancellationdisclaimer' => (filter_input(INPUT_POST, 'cancellationdisclaimer') === 'y' || $this->helperCustomer->getUserData()) ? 'y' : 'n',
181
            'o_paymentmethod' => filter_var(trim(Tools::getFormfield('paymentmethod')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
182
            'o_sumvoll' => $cartpricesums['sumvoll'],
183
            'o_sumerm' => $cartpricesums['sumerm'],
184
            'o_sumnettoall' => $cartpricesums['sumnettoall'],
185
            'o_taxvoll' => $cartpricesums['taxvoll'],
186
            'o_taxerm' => $cartpricesums['taxerm'],
187
            'o_sumbruttoall' => $cartpricesums['sumbruttoall'],
188
            'o_mindermenge' => isset($cartpricesums['mindergebuehr']) ? $cartpricesums['mindergebuehr'] : '',
189
            'o_shippingcost' => $this->helperShop->getShippingcost(),
190
            'o_orderdate' => date('Y-m-d'),
191
            'o_ordertimestamp' => time(),
192
            'o_authed' => $this->helperCustomer->getUserData() ? 'y' : 'n',
193
            'o_sessiondata' => serialize($_SESSION),
194
            'o_postdata' => serialize($_POST),
195
            'o_remote_address' => filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
196
            'o_ordercompleted' => 'n',
197
            'o_paymentcompleted' => 'n',
198
            'o_srv_hostname' => filter_input(INPUT_SERVER, 'SERVER_NAME', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW),
199
            'o_vatfull' => $this->config->getShop('vat')['full'],
200
            'o_vatreduced' => $this->config->getShop('vat')['reduced'],
201
        ];
202
    }
203
204
    /**
205
     * @param int $orderid
206
     * @param string $cartkey
207
     * @param array $values
208
     * @return array
209
     */
210
    private function buildOrderItemRow($orderid, $cartkey, array $values)
211
    {
212
        return [
213
            'oi_o_id' => $orderid,
214
            'oi_cartkey' => $cartkey,
215
            'oi_amount' => $values['amount'],
216
            'oi_price_netto_list' => $values['price']['netto_list'],
217
            'oi_price_netto_use' => $values['price']['netto_use'],
218
            'oi_price_brutto_use' => $values['price']['brutto_use'],
219
            'oi_price_netto_sale' => isset($values['price']['netto_sale']) ? $values['price']['netto_sale'] : '',
220
            'oi_price_netto_rebated' => isset($values['price']['netto_rebated']) ? $values['price']['netto_rebated'] : '',
221
            'oi_vat' => $this->config->getShop('vat')[$values['vat']],
222
            'oi_rg' => $values['rg'],
223
            'oi_rg_rebate' => isset(
224
                $this->config->getShop('rebate_groups')[$values['rg']][trim($this->helperCustomer->getUserData('cust_group'))]
225
            )
226
                ? $this->config->getShop('rebate_groups')[$values['rg']][trim($this->helperCustomer->getUserData('cust_group'))]
227
                : '',
228
            'oi_itemname' => $values['name'],
229
            'oi_img' => $this->imagestosend[$values['img']]['base64img'],
230
        ];
231
    }
232
233
    private function writeCheckoutToDB()
234
    {
235
        /** @var \Doctrine\DBAL\Connection $dbal */
236
        $dbal = $this->serviceManager->get('dbal');
237
238
        try {
239
            $dbal->beginTransaction();
240
241
            $aDataOrder = $this->prepareDataOrder();
242
243
            $iInsertID = $this->helper->autoInsert($dbal, 'orders', $aDataOrder);
244
245
            foreach ($_SESSION['cart'] as $sK => $aV) {
246
                $this->imagestosend[$aV['img']] = $this->getItemImage($aV);
247
248
                $this->helper->autoInsert(
249
                    $dbal,
250
                    'orders_items',
251
                    $this->buildOrderItemRow($iInsertID, $sK, $aV)
252
                );
253
            }
254
            $dbal->commit();
255
256
            return $iInsertID;
257
        } catch (\Exception $e) {
258
            // If something raised an exception in our transaction block of statements,
259
            // roll back any work performed in the transaction
260
            print '<p>Unable to complete transaction!</p>';
261
            error_log($e);
262
            $dbal->rollBack();
263
        }
264
    }
265
266
    private function doCheckout()
267
    {
268
        $iInsertID = $this->writeCheckoutToDB();
269
270
        $sMailbody_us = $this->buildOrderMailBody(false, $iInsertID);
271
        $sMailbody_they = $this->buildOrderMailBody(true, $iInsertID);
272
273
        // write to file
274
        $this->writeCheckoutToFile($sMailbody_us);
275
276
        // Send Mails
277
        $this->sendCheckoutMails($iInsertID, $sMailbody_us, $sMailbody_they);
278
279
        unset($_SESSION['cart'], $_SESSION['cartpricesums'], $_SESSION['sondercart']);
280
281
        $postpaymentmethod = filter_input(INPUT_POST, 'paymentmethod');
282
        if ($postpaymentmethod !== null) {
283
            if (
284
                $postpaymentmethod === 'paypal'
285
                && $this->config->getShop('paypal_interactive')
286
            ) {
287
                return '/_misc/paypal.html?id='.$iInsertID;
288
            } elseif ($postpaymentmethod === 'sofortueberweisung') {
289
                return '/_misc/sofortueberweisung.html?id='.$iInsertID;
290
            }
291
        }
292
293
        return '/_misc/checkedout.html?id='.$iInsertID;
294
    }
295
296
    /**
297
     * @param int $iInsertID
298
     * @param string $sMailbody_us
299
     * @param string $sMailbody_they
300
     */
301
    private function sendCheckoutMails($iInsertID, $sMailbody_us, $sMailbody_they)
302
    {
303
        $aFilesToSend = [];
304
        if (
305
            !empty($this->config->getShop('email_orderconfirmation_attachment_cancellationform_'.$this->config->getLang()))
306
            && file_exists(
307
                PATH_DOCROOT.$this->config->getCore('directory_emailattachments')
308
                .'/'.$this->config->getShop('email_orderconfirmation_attachment_cancellationform_'
309
                .$this->config->getLang())
310
            )
311
        ) {
312
            $aFilesToSend[] =
313
                PATH_DOCROOT.$this->config->getCore('directory_emailattachments').'/'
314
                .$this->config->getShop('email_orderconfirmation_attachment_cancellationform_'.$this->config->getLang());
315
        }
316
317
        $this->helper->mailWrapper(
318
            filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL),
319
            $this->textcats->T('shoppingcart_mail_subject').' '.$iInsertID,
320
            $sMailbody_they,
321
            $this->imagestosend,
322
            $aFilesToSend
323
        );
324
        $this->helper->mailWrapper(
325
            $this->config->getCore('email_sender'),
326
            'Bestellung im Webshop Nr: '.$iInsertID,
327
            $sMailbody_us,
328
            $this->imagestosend
329
        );
330
    }
331
332
    /**
333
     * @param string $sMailbody_us
334
     */
335
    private function writeCheckoutToFile($sMailbody_us)
336
    {
337
        $fp = fopen(PATH_LOGS.'shoplog_'.date('Y-m-d').'.html', 'a');
338
        // Write $somecontent to our opened file.
339
        fwrite($fp, $sMailbody_us."\n\n-------------------------------------------------------------------------\n\n");
340
        fclose($fp);
341
    }
342
343
    /**
344
     * @param string $field
345
     * @return string
346
     */
347
    private function getPostValue($field)
348
    {
349
        $postvalue = filter_input(INPUT_POST, $field);
350
        return (!empty($postvalue) ? $postvalue : '');
351
    }
352
353
    /**
354
     * @param bool $bCust
355
     * @param int $iId
356
     * @return mixed
357
     */
358
    private function buildOrderMailBody($bCust = true, $iId)
359
    {
360
        $aM = [
361
            'customdata' => $this->helperShop->buildShoppingCartTable($_SESSION['cart'], true),
362
            'currency' => $this->config->getShop('waehrungssymbol'),
363
        ];
364
        if (!empty($this->config->getShop('custom_order_fields'))) {
365
            $aM['custom_order_fields'] = $this->config->getShop('custom_order_fields');
366
        }
367
368
        $postcustno = trim(filter_input(INPUT_POST, 'custno', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW));
369
        $postcountry = trim(filter_input(INPUT_POST, 'country', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW));
370
        $postpaymentmethod = filter_input(INPUT_POST, 'paymentmethod', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
371
        $serverservername = filter_input(INPUT_SERVER, 'SERVER_NAME', FILTER_SANITIZE_URL);
372
        $aData = [
373
            'customerversion' => $bCust,
374
            'datetime' => date('d.m.Y - H:i'),
375
            'custno' => $postcustno !== null && strlen($postcustno) >= $this->config->getCustomer('minimum_length_custno') ? $postcustno : '',
376
            'corpname' => $this->getPostValue('corpname'),
377
            'name' => $this->getPostValue('name'),
378
            'street' => $this->getPostValue('street'),
379
            'zip' => $this->getPostValue('zip'),
380
            'town' => $this->getPostValue('town'),
381
            'phone' => $this->getPostValue('phone'),
382
            'cellphone' => $this->getPostValue('cellphone'),
383
            'fax' => $this->getPostValue('fax'),
384
            'email' => $this->getPostValue('email'),
385
            'country' => !empty($postcountry) ?
386
            (
387
                isset(
388
                    $this->config->getCountries('countries_'.$this->config->getLang())[$postcountry]
389
                )
390
                    ? $this->config->getCountries('countries_'.$this->config->getLang())[$postcountry]
391
                    : $postcountry)
392
            : '',
393
            'remarks' => $this->getPostValue('remarks'),
394
            'tos' => $this->getPostValue('tos'),
395
            'cancellationdisclaimer' => $this->getPostValue('cancellationdisclaimer'),
396
            'paymentmethod' => $this->getPostValue('paymentmethod'),
397
            'shippingcost' => empty($_SESSION['shippingcost']) ? false : $_SESSION['shippingcost'],
398
            'paypallink' => $postpaymentmethod === 'paypal' ? $serverservername.'/_misc/paypal.html?id='.$iId : '',
399
            'sofortueberweisunglink' => $postpaymentmethod === 'sofortueberweisung' ?  $serverservername.'/_misc/sofortueberweisung.html?id='.$iId : '',
400
            'SESSION' => !$bCust ? Tools::debug($_SESSION, '$_SESSION', true, true) : '',
401
            'POST' => !$bCust ? Tools::debug($_POST, '$_POST', true, true) : '',
402
            'orderid' => $iId,
403
        ];
404
405
        $aM['customdata']['mail'] = $aData;
406
407
        return $this->serviceManager->get('twig')->render('shop/mail-order-html.twig', $aM);
408
    }
409
410
    /**
411
     * @return string
412
     */
413
    private function getNotification()
414
    {
415
        $return = '';
416
        $getmsg = filter_input(INPUT_GET, 'msg');
417
        $getcartkey = filter_input(INPUT_GET, 'cartkey', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
418
        $getamount = filter_input(INPUT_GET, 'amount', FILTER_SANITIZE_NUMBER_INT);
419
        if (!empty($getmsg)) {
420
            if (
421
                ($getmsg === 'updated' && !empty($getcartkey) && !empty($getamount))
422
                || ($getmsg === 'removed' && !empty($getcartkey))
423
            ) {
424
                $return .= $this->textcats->T('shoppingcart_msg_'.$getmsg.'_1').' ';
425
                if (!empty($this->config->getShop('custom_order_fields')) && mb_strpos($getcartkey, '|') !== false) {
426
                    $mCartkeys = explode('|', $getcartkey);
427
                    foreach ($mCartkeys as $sKey => $sValue) {
428
                        if ($sKey == 0) {
429
                            $return .= $sValue.', ';
430
                        } else {
431
                            $TMP = explode(':', $sValue);
432
                            $return .= $this->textcats->T('shoppingcart_item_'.$TMP[0]).' '.$TMP[1].', ';
433
                            unset($TMP);
434
                        }
435
                    }
436
                    $return = Tools::cutStringend($return, 2);
437
                } else {
438
                    $return .= $getcartkey;
439
                }
440
                $return.= ' '.$this->textcats->T('shoppingcart_msg_'.$getmsg.'_2');
441
                if ($getmsg === 'updated') {
442
                    $return .= ' '.$getamount;
443
                }
444
                $return .= '<br><br>';
445
            }
446
        }
447
448
        return $return;
449
    }
450
}
451