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 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 |
||
29 | class Shoppingcart extends Base |
||
30 | { |
||
31 | /** |
||
32 | * @var \HaaseIT\Toolbox\Textcat |
||
33 | */ |
||
34 | private $textcats; |
||
35 | |||
36 | /** |
||
37 | * Shoppingcart constructor. |
||
38 | * @param \Zend\ServiceManager\ServiceManager $serviceManager |
||
39 | */ |
||
40 | public function __construct(\Zend\ServiceManager\ServiceManager $serviceManager) |
||
41 | { |
||
42 | parent::__construct($serviceManager); |
||
43 | $this->textcats = $this->serviceManager->get('textcats'); |
||
44 | } |
||
45 | |||
46 | /** |
||
47 | * |
||
48 | */ |
||
49 | public function preparePage() |
||
|
|||
50 | { |
||
51 | $this->P = new \HaaseIT\HCSF\CorePage($this->serviceManager); |
||
52 | $this->P->cb_pagetype = 'contentnosubnav'; |
||
53 | |||
54 | if (HelperConfig::$shop['show_pricesonlytologgedin'] && !CHelper::getUserData()) { |
||
55 | $this->P->oPayload->cl_html = $this->textcats->T('denied_notloggedin'); |
||
56 | } else { |
||
57 | $this->P->cb_customcontenttemplate = 'shop/shoppingcart'; |
||
58 | |||
59 | // Check if there is a message to display above the shoppingcart |
||
60 | $this->P->oPayload->cl_html = $this->getNotification(); |
||
61 | |||
62 | // Display the shoppingcart |
||
63 | if (isset($_SESSION['cart']) && count($_SESSION['cart']) >= 1) { |
||
64 | $aErr = []; |
||
65 | if (filter_input(INPUT_POST, 'doCheckout') === 'yes') { |
||
66 | $aErr = $this->validateCheckout($aErr); |
||
67 | if (count($aErr) === 0) { |
||
68 | $this->doCheckout(); |
||
69 | } |
||
70 | } |
||
71 | |||
72 | $aShoppingcart = SHelper::buildShoppingCartTable($_SESSION['cart'], false, '', $aErr); |
||
73 | |||
74 | $this->P->cb_customdata = $aShoppingcart; |
||
75 | } else { |
||
76 | $this->P->oPayload->cl_html .= $this->textcats->T('shoppingcart_empty'); |
||
77 | } |
||
78 | } |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * @param array $aErr |
||
83 | * @return array |
||
84 | */ |
||
85 | private function validateCheckout($aErr = []) |
||
86 | { |
||
87 | $aErr = CHelper::validateCustomerForm(HelperConfig::$lang, $aErr, true); |
||
88 | View Code Duplication | if (!CHelper::getUserData() && filter_input(INPUT_POST, 'tos') !== 'y') { |
|
89 | $aErr['tos'] = true; |
||
90 | } |
||
91 | View Code Duplication | if (!CHelper::getUserData() && filter_input(INPUT_POST, 'cancellationdisclaimer') !== 'y') { |
|
92 | $aErr['cancellationdisclaimer'] = true; |
||
93 | } |
||
94 | $postpaymentmethod = filter_input(INPUT_POST, 'paymentmethod'); |
||
95 | if ( |
||
96 | $postpaymentmethod === null |
||
97 | || in_array($postpaymentmethod, HelperConfig::$shop['paymentmethods'], true) === false |
||
98 | ) { |
||
99 | $aErr['paymentmethod'] = true; |
||
100 | } |
||
101 | |||
102 | return $aErr; |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * @param $aV |
||
107 | * @return array |
||
108 | */ |
||
109 | private function getItemImage($aV) |
||
110 | { |
||
111 | // base64 encode img and prepare for db |
||
112 | // image/png image/jpeg image/gif |
||
113 | // data:{mimetype};base64,XXXX |
||
1 ignored issue
–
show
|
|||
114 | |||
115 | $aImagesToSend = []; |
||
116 | $base64Img = false; |
||
117 | $binImg = false; |
||
118 | |||
119 | if (HelperConfig::$shop['email_orderconfirmation_embed_itemimages_method'] === 'glide') { |
||
120 | $sPathToImage = '/'.HelperConfig::$core['directory_images'].'/'.HelperConfig::$shop['directory_images_items'].'/'; |
||
121 | $sImageroot = PATH_BASEDIR . HelperConfig::$core['directory_glide_master']; |
||
122 | |||
123 | if ( |
||
124 | is_file($sImageroot.substr($sPathToImage.$aV['img'], strlen(HelperConfig::$core['directory_images']) + 1)) |
||
125 | && $aImgInfo = getimagesize($sImageroot.substr($sPathToImage.$aV['img'], strlen(HelperConfig::$core['directory_images']) + 1)) |
||
126 | ) { |
||
127 | $glideserver = \League\Glide\ServerFactory::create([ |
||
128 | 'source' => $sImageroot, |
||
129 | 'cache' => PATH_GLIDECACHE, |
||
130 | 'max_image_size' => HelperConfig::$core['glide_max_imagesize'], |
||
131 | ]); |
||
132 | $glideserver->setBaseUrl('/' . HelperConfig::$core['directory_images'] . '/'); |
||
133 | $base64Img = $glideserver->getImageAsBase64($sPathToImage.$aV['img'], HelperConfig::$shop['email_orderconfirmation_embed_itemimages_glideparams']); |
||
134 | $TMP = explode(',', $base64Img); |
||
135 | $binImg = base64_decode($TMP[1]); |
||
136 | unset($TMP); |
||
137 | } |
||
138 | } else { |
||
139 | $sPathToImage = |
||
140 | PATH_DOCROOT.HelperConfig::$core['directory_images'].'/' |
||
141 | .HelperConfig::$shop['directory_images_items'].'/' |
||
142 | .HelperConfig::$shop['directory_images_items_email'].'/'; |
||
143 | if ($aImgInfo = getimagesize($sPathToImage.$aV['img'])) { |
||
144 | $binImg = file_get_contents($sPathToImage.$aV['img']); |
||
145 | $base64Img = 'data:' . $aImgInfo['mime'] . ';base64,'; |
||
146 | $base64Img .= base64_encode($binImg); |
||
147 | } |
||
148 | } |
||
149 | if (HelperConfig::$shop['email_orderconfirmation_embed_itemimages']) { |
||
150 | $aImagesToSend['binimg'] = $binImg; |
||
151 | } |
||
152 | if ($base64Img) { |
||
153 | $aImagesToSend['base64img'] = $base64Img; |
||
154 | } |
||
155 | return $aImagesToSend; |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * @return array |
||
160 | */ |
||
161 | private function prepareDataOrder() |
||
162 | { |
||
163 | $cartpricesums = $_SESSION['cartpricesums']; |
||
164 | return [ |
||
165 | 'o_custno' => filter_var(trim(Tools::getFormfield('custno')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
166 | 'o_email' => filter_var(trim(Tools::getFormfield('email')), FILTER_SANITIZE_EMAIL), |
||
167 | 'o_corpname' => filter_var(trim(Tools::getFormfield('corpname')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
168 | 'o_name' => filter_var(trim(Tools::getFormfield('name')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
169 | 'o_street' => filter_var(trim(Tools::getFormfield('street')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
170 | 'o_zip' => filter_var(trim(Tools::getFormfield('zip')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
171 | 'o_town' => filter_var(trim(Tools::getFormfield('town')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
172 | 'o_phone' => filter_var(trim(Tools::getFormfield('phone')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
173 | 'o_cellphone' => filter_var(trim(Tools::getFormfield('cellphone')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
174 | 'o_fax' => filter_var(trim(Tools::getFormfield('fax')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
175 | 'o_country' => filter_var(trim(Tools::getFormfield('country')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
176 | 'o_group' => trim(CHelper::getUserData('cust_group')), |
||
177 | 'o_remarks' => filter_var(trim(Tools::getFormfield('remarks')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
178 | 'o_tos' => (filter_input(INPUT_POST, 'tos') === 'y' || CHelper::getUserData()) ? 'y' : 'n', |
||
179 | 'o_cancellationdisclaimer' => (filter_input(INPUT_POST, 'cancellationdisclaimer') === 'y' || CHelper::getUserData()) ? 'y' : 'n', |
||
180 | 'o_paymentmethod' => filter_var(trim(Tools::getFormfield('paymentmethod')), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
181 | 'o_sumvoll' => $cartpricesums['sumvoll'], |
||
182 | 'o_sumerm' => $cartpricesums['sumerm'], |
||
183 | 'o_sumnettoall' => $cartpricesums['sumnettoall'], |
||
184 | 'o_taxvoll' => $cartpricesums['taxvoll'], |
||
185 | 'o_taxerm' => $cartpricesums['taxerm'], |
||
186 | 'o_sumbruttoall' => $cartpricesums['sumbruttoall'], |
||
187 | 'o_mindermenge' => isset($cartpricesums['mindergebuehr']) ? $cartpricesums['mindergebuehr'] : '', |
||
188 | 'o_shippingcost' => SHelper::getShippingcost(), |
||
189 | 'o_orderdate' => date('Y-m-d'), |
||
190 | 'o_ordertimestamp' => time(), |
||
191 | 'o_authed' => CHelper::getUserData() ? 'y' : 'n', |
||
192 | 'o_sessiondata' => serialize($_SESSION), |
||
193 | 'o_postdata' => serialize($_POST), |
||
194 | 'o_remote_address' => filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
195 | 'o_ordercompleted' => 'n', |
||
196 | 'o_paymentcompleted' => 'n', |
||
197 | 'o_srv_hostname' => filter_input(INPUT_SERVER, 'SERVER_NAME', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW), |
||
198 | 'o_vatfull' => HelperConfig::$shop['vat']['full'], |
||
199 | 'o_vatreduced' => HelperConfig::$shop['vat']['reduced'], |
||
200 | ]; |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * @param int $orderid |
||
205 | * @param string $cartkey |
||
206 | * @param array $values |
||
207 | * @param string $image |
||
208 | * @return array |
||
209 | */ |
||
210 | private function buildOrderItemRow($orderid, $cartkey, array $values, $image) |
||
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' => HelperConfig::$shop['vat'][$values['vat']], |
||
222 | 'oi_rg' => $values['rg'], |
||
223 | 'oi_rg_rebate' => isset( |
||
224 | HelperConfig::$shop['rebate_groups'][$values['rg']][trim(CHelper::getUserData('cust_group'))] |
||
225 | ) |
||
226 | ? HelperConfig::$shop['rebate_groups'][$values['rg']][trim(CHelper::getUserData('cust_group'))] |
||
227 | : '', |
||
228 | 'oi_itemname' => $values['name'], |
||
229 | 'oi_img' => $image, |
||
230 | ]; |
||
231 | } |
||
232 | |||
233 | private function doCheckout() |
||
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 = \HaaseIT\HCSF\Helper::autoInsert($dbal, 'orders', $aDataOrder); |
||
244 | |||
245 | $aImagesToSend = []; |
||
246 | foreach ($_SESSION['cart'] as $sK => $aV) { |
||
247 | $aImagesToSend[$aV['img']] = $this->getItemImage($aV); |
||
248 | |||
249 | \HaaseIT\HCSF\Helper::autoInsert( |
||
250 | $dbal, |
||
251 | 'orders_items', |
||
252 | $this->buildOrderItemRow($iInsertID, $sK, $aV, $aImagesToSend[$aV['img']]['base64img']) |
||
253 | ); |
||
254 | } |
||
255 | $dbal->commit(); |
||
256 | } catch (\Exception $e) { |
||
257 | // If something raised an exception in our transaction block of statements, |
||
258 | // roll back any work performed in the transaction |
||
259 | print '<p>Unable to complete transaction!</p>'; |
||
260 | error_log($e); |
||
261 | $dbal->rollBack(); |
||
262 | } |
||
263 | $sMailbody_us = $this->buildOrderMailBody(false, $iInsertID); |
||
264 | $sMailbody_they = $this->buildOrderMailBody(true, $iInsertID); |
||
265 | |||
266 | // write to file |
||
267 | $this->writeCheckoutToFile($sMailbody_us); |
||
268 | |||
269 | // Send Mails |
||
270 | $this->sendCheckoutMails($iInsertID, $sMailbody_us, $sMailbody_they, $aImagesToSend); |
||
271 | |||
272 | unset($_SESSION['cart'], $_SESSION['cartpricesums'], $_SESSION['sondercart']); |
||
273 | |||
274 | $postpaymentmethod = filter_input(INPUT_POST, 'paymentmethod'); |
||
275 | if ( |
||
276 | $postpaymentmethod !== null |
||
277 | && isset(HelperConfig::$shop['paypal_interactive']) |
||
278 | && $postpaymentmethod === 'paypal' |
||
279 | && in_array('paypal', HelperConfig::$shop['paymentmethods'], true) !== false |
||
280 | && HelperConfig::$shop['paypal_interactive'] |
||
281 | ) { |
||
282 | $redirectto = '/_misc/paypal.html?id=' . $iInsertID; |
||
283 | } elseif ( |
||
284 | $postpaymentmethod !== null |
||
285 | && $postpaymentmethod === 'sofortueberweisung' |
||
286 | && in_array('sofortueberweisung', HelperConfig::$shop['paymentmethods'], true) !== false |
||
287 | ) { |
||
288 | $redirectto = '/_misc/sofortueberweisung.html?id=' . $iInsertID; |
||
289 | } else { |
||
290 | $redirectto = '/_misc/checkedout.html?id=' . $iInsertID; |
||
291 | } |
||
292 | \HaaseIT\HCSF\Helper::redirectToPage($redirectto); |
||
293 | } |
||
294 | |||
295 | /** |
||
296 | * @param int $iInsertID |
||
297 | * @param string $sMailbody_us |
||
298 | * @param string $sMailbody_they |
||
299 | * @param array $aImagesToSend |
||
300 | */ |
||
301 | private function sendCheckoutMails($iInsertID, $sMailbody_us, $sMailbody_they, $aImagesToSend) |
||
332 | |||
333 | /** |
||
334 | * @param string $sMailbody_us |
||
335 | */ |
||
336 | private function writeCheckoutToFile($sMailbody_us) |
||
343 | |||
344 | /** |
||
345 | * @param string $field |
||
346 | * @return string |
||
347 | */ |
||
348 | private function getPostValue($field) |
||
353 | |||
354 | /** |
||
355 | * @param bool $bCust |
||
356 | * @param int $iId |
||
357 | * @return mixed |
||
358 | */ |
||
359 | private function buildOrderMailBody($bCust = true, $iId) |
||
411 | |||
412 | /** |
||
413 | * @return string |
||
414 | */ |
||
415 | private function getNotification() |
||
452 | } |
||
453 |
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: