Completed
Push — master ( 7c0a06...367a2f )
by Lawrence
02:13
created

Prestashop::onOpen()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 7
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace ABM\Wasabi;
4
5
use Ratchet\MessageComponentInterface;
6
use Ratchet\ConnectionInterface;
7
use Analog\Analog;
8
9
class Prestashop implements MessageComponentInterface
10
{
11
    protected $clients;
12
    protected $dbConn;
13
14
    public function __construct()
15
    {
16
        $this->clients = new \SplObjectStorage();
17
        // Now connect to PS DB using Simplon on Composer
18
        $this->dbConn = new \Simplon\Mysql\Mysql(
19
    '127.0.0.1',
20
    _DB_USER_,
21
    _DB_PASSWD_,
22
    _DB_NAME_
23
        );
24
        $log_file = 'wasabi.log';
25
        Analog::handler(\Analog\Handler\File::init($log_file));
0 ignored issues
show
Documentation introduced by
\Analog\Handler\File::init($log_file) is of type object<Closure>, but the function expects a boolean.

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...
26
    }
27
28
    public function onOpen(ConnectionInterface $conn)
29
    {
30
        // Store the new connection to send messages to later
31
        $this->clients->attach($conn);
32
33
        Analog::log("New connection: $conn->resourceId");
0 ignored issues
show
Bug introduced by
Accessing resourceId on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
34
    }
35
36
    public function onMessage(ConnectionInterface $from, $msg)
37
    {
38
        $result = null;
39
        foreach ($this->clients as $client) {
40
            if ($from == $client) {
41
                $type = strtolower(substr($msg, 0, strpos($msg, '|')));
42
                $data = substr($msg, strpos($msg, '|') + 1);
43
                switch ($type) {
44
                    case 'cart': $result = $this->getCartData($data);
45
                                    break;
46
                    case 'prod': $result = $this->getProductData($data);
47
                                    break;
48
                    case 'comb': $result = $this->getCombinationData($data);
49
                                    break;
50
                    default:        break;
51
                }
52
                if (!empty($result)) {
53
                $client->send(json_encode($result));
54
                }
55
            }
56
        }
57
    }
58
59
    /**
60
     * @param string $data
61
     */
62
    private function getCombinationData($data) {
63
        $product = substr($data, 0, strpos($data, ','));
64
        $combinations = array();
65
                $vars = explode(',', $data);
66
                Analog::log("Product variables: $data");
67
                $choices = array_slice($vars, 1);
68
                $id_product_attribute = $this->getAttribute($product, $choices);
69
                Analog::log("Product combination: $id_product_attribute");
70
                $combo_groups = $this->getCombination($id_product_attribute);
71
                if (!empty($combo_groups) && is_array($combo_groups) && $combo_groups) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $combo_groups of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
72
                    foreach ($combo_groups as $k => $row) {
73
                        $combinations = $this->buildAttributes($combinations, $id_product_attribute, $row);
74
                    }
75
                }
76
        return $combinations;
77
    }
78
79
    /**
80
     * @param string $data
81
     */
82
    private function getProductData($data) {
83
                $category = (int) substr($data, 0, strpos($data, ','));
84
                if ($category != 0) {
85
                    $products = $this->getProducts($category);
86
                } else {
87
                    $product = substr($data, strpos($data, ',') + 1);
88
                    $products = $this->getProducts($product);
89
                }
90
                Analog::log("Product variables: $data");
91
        return $products;
92
    }
93
94
    /**
95
     * @param string $data
96
     */
97
    private function getCartData($data) {
98
        $cart = substr($data, 0, strpos($data, ','));
99
        $cust = substr($data, strpos($data, ',') + 1);
100
101
        Analog::log("Cart & customer variables: $data");
102
        $otherCarts = $this->processFindCarts($cart, $cust);
103
        return $otherCarts;
104
    }
105
106
107
/**
108
 * @param string $cart
109
 * @param string $cust
110
 */
111
    private function processFindCarts($cart, $cust)
112
    {
113
        $sql = 'SELECT DISTINCT pc.id_cart as id, DATE_FORMAT(pc.date_upd,"%a %D %b %Y, %l:%i %p") as timer from '._DB_PREFIX_.'cart as pc
114
                LEFT JOIN  '._DB_PREFIX_.'cart_product as pcp on pcp.id_cart = pc.id_cart
115
                WHERE pc.id_cart NOT IN (SELECT po.id_cart FROM  '._DB_PREFIX_.'orders as po)
116
                AND pcp.id_product IS NOT NULL
117
                AND pc.id_customer = '.(int) $cust.'
118
                AND pc.id_cart != '.(int) $cart.'
119
                ORDER BY pc.date_upd DESC
120
                LIMIT 10';
121
        if ($results = $this->dbConn->fetchRowMany($sql)) {
122
            foreach ($results as &$row) {
123
                $row['token'] = md5(_COOKIE_KEY_.'recover_cart_'.$row['id']);
124
            }
125
126
            return $results;
127
        } 
128
    }
129
130
131
    /**
132
     * @param string $product
133
     */
134
    private function getAttribute($product, $choices)
135
    {
136
        $sql = 'SELECT pac.id_product_attribute from '._DB_PREFIX_.'product_attribute_combination as pac
137
                LEFT JOIN  '._DB_PREFIX_.'product_attribute as ppa on ppa.id_product_attribute = pac.id_product_attribute
138
                LEFT JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute = pac.id_attribute)
139
                WHERE ppa.id_product = '.$product;
140
        foreach ($choices as $value) {
141
            $sql .= ' AND pac.id_product_attribute IN
142
                    ( SELECT pac.id_product_attribute from '._DB_PREFIX_.'product_attribute_combination as pac
143
                    WHERE pac.id_attribute = '.(int) $value.')  ';
144
        }
145
        $sql .= ' GROUP BY pac.id_product_attribute';
146
        $id_product_attribute = $this->dbConn->fetchColumn($sql);
147
148
        return $id_product_attribute;
149
    }
150
151
    /**
152
     * @param null|string $id_product_attribute
153
     */
154
    private function buildAttributes($combinations, $id_product_attribute, $row)
155
    {
156
        $combinationSet = array();
157
        $specific_price = null;
158
159
        $combinations['name'] = $row['product_name'];
160
        $typcheck = array("id_product", "price", "base_price", "price", "ecotax", "weight", "quantity", "unit_impact", "minimal_quantity");
161
162
        foreach ($typcheck as $key=>$value) 
163
        { 
164
            ((strpos($value, 'price') !== false) || (strpos($value, 'weight') !== false) || (strpos($value, 'ecotax') !== false) || (strpos($value, 'impact') !== false)) ? $combinations[$value] = (float) $row[$value] : $combinations[$value] = (int) $row[$value];
165
            
166
        }
167
        $combinations['attributes_values'][$row['id_attribute_group']] = $row['attribute_name'];
168
        $combinations['attributes'][] = (int) $row['id_attribute'];
169
170
        list ($combinationSet[(int) $row['id_product_attribute']], $combinations['specific_price']) = $this->getCombinationSpecificPrice($combinationSet, $row, $id_product_attribute);
171
172
        $combinations['reference'] = $row['reference'];
173
        $combinations['available_date'] = $this->getAvailableDate($row);
174
        $combinations['image'] = $this->getComboImage($id_product_attribute);
175
        $combinations['final_price'] = $this->getFinalPrice($row, $specific_price);
176
177
        return $combinations;
178
    }
179
180
    private function getFinalPrice($row, $specific_price)
181
    {
182
        $specific_price['price'] != 0 ? $final_price = (((float) $row['base_price'] + (float) $row['price']) * (((int) 100 - $specific_price['reduction_percent']) / 100)) : $final_price = (float) $row['base_price'] + (float) $row['price'];
183
        return $final_price;
184
    }
185
186
187
    private function getAvailableDate($row) {
188
        ($row['available_date'] != '0000-00-00') ? $dater = $row['available_date'] : $dater = '';
189
        return $dater;
190
   }
191
192
    /**
193
     * @param null|string $id_product_attribute
194
     */
195
    private function getCombinationSpecificPrice($combinationSet, $row, $id_product_attribute) {
196
            // Call getSpecificPrice in order to set $combination_specific_price
197
                if (!isset($combinationSet[(int) $row['id_product_attribute']])) {
198
                    $specific_price = $this->getSpecificPrice($id_product_attribute, $row['id_product']);
199
                    $combinationSet[(int) $row['id_product_attribute']] = true;
200
                    return array($combinationSet, $specific_price);
201
                } else {
202
                    return array(false, null);
203
                }
204
    }
205
206
207
208
    /**
209
     * @param null|string $id_product_attribute
210
     */
211
    private function getCombination($id_product_attribute)
212
    {
213
       $sql = 'SELECT ag.`id_attribute_group`, ag.`is_color_group`, agl.`name` AS group_name, agl.`public_name` AS public_group_name,
214
                    a.`id_attribute`, al.`name` AS attribute_name, a.`color` AS attribute_color, pas.`id_product_attribute`,
215
                    IFNULL(stock.quantity, 0) as quantity, pc.price as base_price, pc.id_product, pas.`price`, pas.`ecotax`, pas.`weight`,
216
                    pas.`default_on`, pa.`reference`, pas.`unit_price_impact`, pl.name as product_name,
217
                    pas.`minimal_quantity`, pas.`available_date`, ag.`group_type`
218
                FROM `'._DB_PREFIX_.'product_attribute` pa
219
                 INNER JOIN '._DB_PREFIX_.'product_attribute_shop pas
220
                    ON (pas.id_product_attribute = pa.id_product_attribute AND pas.id_shop = 1)
221
                 LEFT JOIN '._DB_PREFIX_.'stock_available stock
222
                        ON (stock.id_product = pa.id_product AND stock.id_product_attribute = IFNULL(`pa`.id_product_attribute, 0) AND stock.id_shop = 1  )
223
                LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (pac.`id_product_attribute` = pa.`id_product_attribute`)
224
                LEFT JOIN `'._DB_PREFIX_.'product` pc ON (pa.`id_product` = pc.`id_product`)
225
                LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (pc.`id_product` = pl.`id_product`)
226
                LEFT JOIN `'._DB_PREFIX_.'attribute` a ON (a.`id_attribute` = pac.`id_attribute`)
227
                LEFT JOIN `'._DB_PREFIX_.'attribute_group` ag ON (ag.`id_attribute_group` = a.`id_attribute_group`)
228
                LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute`)
229
                LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group`)
230
                 INNER JOIN '._DB_PREFIX_.'attribute_shop attribute_shop
231
                    ON (attribute_shop.id_attribute = a.id_attribute AND attribute_shop.id_shop = 1)
232
                WHERE pas.`id_product_attribute`= '.(int)$id_product_attribute.'
233
                GROUP BY id_attribute_group, id_product_attribute
234
                ORDER BY ag.`position` ASC, a.`position` ASC, agl.`name` ASC';
235
236
        $combo_groups = $this->dbConn->fetchRowMany($sql);
237
238
        return $combo_groups;
239
    }
240
241
    public function getAttributeBase($attribute) {
242
       $sql = 'SELECT al.*
243
            FROM '._DB_PREFIX_.'product_attribute_combination pac
244
            JOIN '._DB_PREFIX_.'attribute_lang al ON (pac.id_attribute = al.id_attribute AND al.id_lang=1)
245
            WHERE pac.id_product_attribute='.(int) $attribute;
246
        $result = $this->dbConn->fetchRowMany($sql);
247
248
        return $result;
249
    }
250
251
    /**
252
     * @param null|string $id_product_attribute
253
     * @param string $id_product
254
     */
255
    private function getSpecificPrice($id_product_attribute, $id_product)
256
    {
257
        $specific_price = array();
258
        if ($this->getNumberSpecificPrice($id_product_attribute, $id_product) > 0) {
259
            $result = $this->getSpecificPriceData($id_product_attribute, $id_product, date('Y-m-d H:i:s'));
260
            $specific_price['price'] = $result['price'];
261
            $specific_price['id_product_attribute'] = $result['id_product_attribute'];
262
            $specific_price['reduction_percent'] = (int) 100 * $result['reduction'];
263
            $specific_price['reduction_price'] = 0;
264
            $specific_price['reduction_type'] = $result['reduction_type'];
265
266
            return $specific_price;
267
        }
268
    }
269
270
271
    /**
272
     * @param string $now
273
     * @param null|string $id_product_attribute
274
     * @param string $id_product
275
     */
276
    private function getSpecificPriceData($id_product_attribute, $id_product, $now)
277
    {
278
        $sql = 'SELECT * FROM '._DB_PREFIX_.'specific_price
279
                            WWHERE id_product = '.(int) $id_product.'
280
                            AND id_product_attribute IN (0, '.(int) $id_product_attribute.')
281
                            AND (
282
                                   (from = \'0000-00-00 00:00:00\' OR \''.$now.'\' >= from)
283
                            AND
284
                                     (to = \'0000-00-00 00:00:00\' OR \''.$now.'\' <= to)
285
                            ) ';
286
287
        $sql .= ' ORDER BY id_product_attribute DESC, from_quantity DESC, id_specific_price_rule ASC';
288
289
        $result = $this->dbConn->fetchRow($sql);
290
291
        return $result;
292
    }
293
294
    /**
295
     * @param null|string $id_product_attribute
296
     * @param string $id_product
297
     */
298
    private function getNumberSpecificPrice($id_product_attribute, $id_product)
299
    {
300
        $sql = 'SELECT COUNT(*) FROM '._DB_PREFIX_.'specific_price WHERE id_product = '.(int) $id_product.' AND id_product_attribute = 0';
301
        $spec = $this->dbConn->fetchColumn($sql);
302
        if ($spec == 0) {
303
            $sql = 'SELECT COUNT(*) FROM '._DB_PREFIX_.'specific_price WHERE id_product_attribute = '.(int) $id_product_attribute;
304
            $spec = $this->dbConn->fetchColumn($sql);
305
        }
306
307
        return $spec;
308
    }
309
310
    /**
311
     * @param null|string $id_product_attribute
312
     */
313
    private function getComboImage($id_product_attribute)
314
    {
315
        $sql = 'SELECT pai.id_imageas image
316
                        FROM '._DB_PREFIX_.'product_attribute_image pai
317
                        LEFT JOIN '._DB_PREFIX_.'image_lang il ON (il.id_image = pai.id_image)
318
                        LEFT JOIN '._DB_PREFIX_.'image i ON (i.id_image = pai.id_image)
319
                        WHERE pai.id_product_attribute = '.(int) $id_product_attribute.' ORDER by i.position';
320
        $image = $this->dbConn->fetchColumn($sql);
321
        ($image !== false) ? $imager = (int) $image : $imager = -1;
322
        return $imager;
323
    }
324
325
    /**
326
     * @param string $category
327
     */
328
    private function getProducts($category)
329
    {
330
        $product_ids = $this->getProductIDs($category);
331
        $products = $this->getProduct($product_ids);
332
333
        return $products;
334
    }
335
336
    /**
337
     * @param string $category
338
     */
339
    private function getProductIDs($category)
340
    {
341
        $sql = 'SELECT DISTINCT p.id_product
342
                from '._DB_PREFIX_.'product as p
343
                LEFT JOIN '._DB_PREFIX_.'image AS i ON i.id_product = p.id_product 
344
                LEFT JOIN '._DB_PREFIX_.'product_lang as pl ON pl.id_product = p.id_product
345
                WHERE p.active = 1
346
                AND p.id_category_default = '.(int) $category.'
347
                GROUP BY p.id_product';
348
        $pcats = $this->dbConn->fetchRowMany($sql);
349
        $ids = '';
350
        if (is_array($pcats) && (!empty($pcats))) {
351
        foreach ($pcats as $row) {
352
            $ids .= $row['id_product'].',';
353
            }
354
        }
355
356
        $ids = rtrim($ids, ',');
357
358
        return $ids;
359
    }
360
361
    /**
362
     * @param string $ids
363
     */
364
    private function getProduct($ids)
365
    {
366
        $sql = 'SELECT p.id_product, p.id_supplier, p.ean13, p.upc, p.price, p.wholesale_price, p.on_sale, p.quantity, p.id_category_default, p.id_category_default AS cat_id,
367
                    p.show_price, p.available_for_order, p.minimal_quantity, p.customizable,
368
                    p.out_of_stock, pl.link_rewrite, pl.name, i.id_image, il.legend
369
                    FROM '._DB_PREFIX_.'product as p                 
370
                    LEFT JOIN '._DB_PREFIX_.'image AS i ON i.id_product = p.id_product 
371
                    LEFT JOIN '._DB_PREFIX_.'image_lang as il ON i.id_image = il.id_image
372
                    WHERE p.id_product IN ('.$ids.')
373
                    AND i.cover = 1
374
                    AND p.active = 1
375
                    GROUP BY p.id_product
376
                    ORDER BY p.price ASC';
377
378
        $result = $this->dbConn->fetchRowMany($sql);
379
380
        if (is_array($result))
381
        {
382
         foreach($result as $key => $value)
383
        {
384
            $result['orderprice'] = $this->getOrderPrice($product);
0 ignored issues
show
Bug introduced by
The variable $product does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
385
            $result['category_default'] = $this->getProductCat($value['cat_id']);
386
        }
387
        return $result;
388
        }
389
    }
390
391
    private function getOrderPrice($product) {
392
        $sql = 'SELECT ps.price from '._DB_PREFIX_.'product_shop as ps WHERE ps.id_product = '.(int) $product;
393
        $result = $this->dbConn->fetchValue($sql);
0 ignored issues
show
Bug introduced by
The method fetchValue() does not seem to exist on object<Simplon\Mysql\Mysql>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
394
        return $result;
395
    }
396
397
    private function getProductCat($category) {
398
        $sql = 'SELECT cl.name from '._DB_PREFIX_.'category_lang as cl WHERE cl.id_category = '.(int) $category;
399
        $result = $this->dbConn->fetchValue($sql);
0 ignored issues
show
Bug introduced by
The method fetchValue() does not seem to exist on object<Simplon\Mysql\Mysql>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
400
        return $result;
401
    }
402
403
404
    public function onClose(ConnectionInterface $conn)
405
    {
406
        // The connection is closed, remove it, as we can no longer send it messages
407
        $this->clients->detach($conn);
408
        Analog::log('Connection '.$conn->resourceId.' has disconnected');
0 ignored issues
show
Bug introduced by
Accessing resourceId on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
409
    }
410
411
    public function onError(ConnectionInterface $conn, \Exception $e)
412
    {
413
        echo "An error has occurred: {$e->getMessage()}\n";
414
        Analog::log('Error: '.$e->getMessage().'');
415
        $conn->close();
416
    }
417
}
418