· 5 years ago · Mar 24, 2020, 04:10 PM
1<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.1.6 or newer
6 *
7 * @package CodeIgniter
8 * @author EllisLab Dev Team
9 * @copyright Copyright (c) 2006 - 2014, EllisLab, Inc.
10 * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
11 * @license http://codeigniter.com/user_guide/license.html
12 * @link http://codeigniter.com
13 * @since Version 1.0
14 * @filesource
15 */
16
17// ------------------------------------------------------------------------
18
19/**
20 * Shopping Cart Class
21 *
22 * @package CodeIgniter
23 * @subpackage Libraries
24 * @category Shopping Cart
25 * @author EllisLab Dev Team
26 * @link http://codeigniter.com/user_guide/libraries/cart.html
27 */
28class CI_Cart {
29
30 // These are the regular expression rules that we use to validate the product ID and product name
31 var $product_id_rules = '\.a-z0-9_-'; // alpha-numeric, dashes, underscores, or periods
32 var $product_name_rules = '\.\:\-_ a-z0-9'; // alpha-numeric, dashes, underscores, colons or periods
33
34 // Private variables. Do not change!
35 var $CI;
36 var $_cart_contents = array();
37
38
39 /**
40 * Shopping Class Constructor
41 *
42 * The constructor loads the Session class, used to store the shopping cart contents.
43 */
44 public function __construct($params = array())
45 {
46 // Set the super object to a local variable for use later
47 $this->CI =& get_instance();
48
49 // Are any config settings being passed manually? If so, set them
50 $config = array();
51 if (count($params) > 0)
52 {
53 foreach ($params as $key => $val)
54 {
55 $config[$key] = $val;
56 }
57 }
58
59 // Load the Sessions class
60 $this->CI->load->library('session', $config);
61
62 // Grab the shopping cart array from the session table, if it exists
63 if ($this->CI->session->userdata('cart_contents') !== FALSE)
64 {
65 $this->_cart_contents = $this->CI->session->userdata('cart_contents');
66 }
67 else
68 {
69 // No cart exists so we'll set some base values
70 $this->_cart_contents['cart_total'] = 0;
71 $this->_cart_contents['total_items'] = 0;
72 }
73
74 log_message('debug', "Cart Class Initialized");
75 }
76
77 // --------------------------------------------------------------------
78
79 /**
80 * Insert items into the cart and save it to the session table
81 *
82 * @access public
83 * @param array
84 * @return bool
85 */
86 function insert($items = array())
87 {
88 // Was any cart data passed? No? Bah...
89 if ( ! is_array($items) OR count($items) == 0)
90 {
91 log_message('error', 'The insert method must be passed an array containing data.');
92 return FALSE;
93 }
94
95 // You can either insert a single product using a one-dimensional array,
96 // or multiple products using a multi-dimensional one. The way we
97 // determine the array type is by looking for a required array key named "id"
98 // at the top level. If it's not found, we will assume it's a multi-dimensional array.
99
100 $save_cart = FALSE;
101 if (isset($items['id']))
102 {
103 if (($rowid = $this->_insert($items)))
104 {
105 $save_cart = TRUE;
106 }
107 }
108 else
109 {
110 foreach ($items as $val)
111 {
112 if (is_array($val) AND isset($val['id']))
113 {
114 if ($this->_insert($val))
115 {
116 $save_cart = TRUE;
117 }
118 }
119 }
120 }
121
122 // Save the cart data if the insert was successful
123 if ($save_cart == TRUE)
124 {
125 $this->_save_cart();
126 return isset($rowid) ? $rowid : TRUE;
127 }
128
129 return FALSE;
130 }
131
132 // --------------------------------------------------------------------
133
134 /**
135 * Insert
136 *
137 * @access private
138 * @param array
139 * @return bool
140 */
141 function _insert($items = array())
142 {
143 // Was any cart data passed? No? Bah...
144 if ( ! is_array($items) OR count($items) == 0)
145 {
146 log_message('error', 'The insert method must be passed an array containing data.');
147 return FALSE;
148 }
149
150 // --------------------------------------------------------------------
151
152 // Does the $items array contain an id, quantity, price, and name? These are required
153 //if ( ! isset($items['id']) OR ! isset($items['qty']) OR ! isset($items['price']) OR ! isset($items['name']))
154
155 if ( ! isset($items['id']) OR ! isset($items['qty']) OR ! isset($items['price']) OR ! isset($items['name']) OR ! isset($items['tax']) OR ! isset($items['disc'])
156 )
157
158
159
160 {
161 log_message('error', 'The cart array must contain a product ID, quantity, price, and name.');
162 return FALSE;
163 }
164
165 // --------------------------------------------------------------------
166
167 // Prep the quantity. It can only be a number. Duh...
168 $items['qty'] = trim(preg_replace('/([^0-9])/i', '', $items['qty']));
169 // Trim any leading zeros
170 $items['qty'] = trim(preg_replace('/(^[0]+)/i', '', $items['qty']));
171
172
173 /////////////////////////////////////
174 $items['tax'] = trim(preg_replace('/([^0-9])/i', '', $items['tax']));
175 // Trim any leading zeros
176 $items['tax'] = trim(preg_replace('/(^[0]+)/i', '', $items['tax']));
177
178 $items['disc'] = trim(preg_replace('/([^0-9])/i', '', $items['disc']));
179 // Trim any leading zeros
180 $items['disc'] = trim(preg_replace('/(^[0]+)/i', '', $items['disc']));
181
182 ////////////////////////////////////
183
184 // If the quantity is zero or blank there's nothing for us to do
185 if ( ! is_numeric($items['qty']) OR $items['qty'] == 0 or
186 ! is_numeric($items['tax']) OR $items['tax'] == 0 or
187 ! is_numeric($items['disc']) OR $items['disc'] == 0
188 )
189 {
190 return FALSE;
191 }
192
193
194
195 // --------------------------------------------------------------------
196
197 // Validate the product ID. It can only be alpha-numeric, dashes, underscores or periods
198 // Not totally sure we should impose this rule, but it seems prudent to standardize IDs.
199 // Note: These can be user-specified by setting the $this->product_id_rules variable.
200 if ( ! preg_match("/^[".$this->product_id_rules."]+$/i", $items['id']))
201 {
202 log_message('error', 'Invalid product ID. The product ID can only contain alpha-numeric characters, dashes, and underscores');
203 return FALSE;
204 }
205
206 // --------------------------------------------------------------------
207
208 // Validate the product name. It can only be alpha-numeric, dashes, underscores, colons or periods.
209 // Note: These can be user-specified by setting the $this->product_name_rules variable.
210 if ( ! preg_match("/^[".$this->product_name_rules."]+$/i", $items['name']))
211 {
212 log_message('error', 'An invalid name was submitted as the product name: '.$items['name'].' The name can only contain alpha-numeric characters, dashes, underscores, colons, and spaces');
213 return FALSE;
214 }
215
216 // --------------------------------------------------------------------
217
218 // Prep the price. Remove anything that isn't a number or decimal point.
219 $items['price'] = trim(preg_replace('/([^0-9\.])/i', '', $items['price']));
220 // Trim any leading zeros
221 $items['price'] = trim(preg_replace('/(^[0]+)/i', '', $items['price']));
222
223 // Is the price a valid number?
224 if ( ! is_numeric($items['price']))
225 {
226 log_message('error', 'An invalid price was submitted for product ID: '.$items['id']);
227 return FALSE;
228 }
229
230 // --------------------------------------------------------------------
231
232 // We now need to create a unique identifier for the item being inserted into the cart.
233 // Every time something is added to the cart it is stored in the master cart array.
234 // Each row in the cart array, however, must have a unique index that identifies not only
235 // a particular product, but makes it possible to store identical products with different options.
236 // For example, what if someone buys two identical t-shirts (same product ID), but in
237 // different sizes? The product ID (and other attributes, like the name) will be identical for
238 // both sizes because it's the same shirt. The only difference will be the size.
239 // Internally, we need to treat identical submissions, but with different options, as a unique product.
240 // Our solution is to convert the options array to a string and MD5 it along with the product ID.
241 // This becomes the unique "row ID"
242 if (isset($items['options']) AND count($items['options']) > 0)
243 {
244 $rowid = md5($items['id'].implode('', $items['options']));
245 }
246 else
247 {
248 // No options were submitted so we simply MD5 the product ID.
249 // Technically, we don't need to MD5 the ID in this case, but it makes
250 // sense to standardize the format of array indexes for both conditions
251 $rowid = md5($items['id']);
252 }
253
254 // --------------------------------------------------------------------
255
256 // Now that we have our unique "row ID", we'll add our cart items to the master array
257
258 // let's unset this first, just to make sure our index contains only the data from this submission
259 unset($this->_cart_contents[$rowid]);
260
261 // Create a new index with our new row ID
262 $this->_cart_contents[$rowid]['rowid'] = $rowid;
263
264 // And add the new items to the cart array
265 foreach ($items as $key => $val)
266 {
267 $this->_cart_contents[$rowid][$key] = $val;
268 }
269
270 // Woot!
271 return $rowid;
272 }
273
274 // --------------------------------------------------------------------
275
276 /**
277 * Update the cart
278 *
279 * This function permits the quantity of a given item to be changed.
280 * Typically it is called from the "view cart" page if a user makes
281 * changes to the quantity before checkout. That array must contain the
282 * product ID and quantity for each item.
283 *
284 * @access public
285 * @param array
286 * @param string
287 * @return bool
288 */
289 function update($items = array())
290 {
291 // Was any cart data passed?
292 if ( ! is_array($items) OR count($items) == 0)
293 {
294 return FALSE;
295 }
296
297 // You can either update a single product using a one-dimensional array,
298 // or multiple products using a multi-dimensional one. The way we
299 // determine the array type is by looking for a required array key named "id".
300 // If it's not found we assume it's a multi-dimensional array
301 $save_cart = FALSE;
302 if (isset($items['rowid']) AND isset($items['qty']) AND isset($items['tax']) AND isset($items['disc']) )
303 {
304 if ($this->_update($items) == TRUE)
305 {
306 $save_cart = TRUE;
307 }
308 }
309 else
310 {
311 foreach ($items as $val)
312 {
313 if (is_array($val) AND isset($val['rowid']) AND isset($val['qty']) and isset($items['tax']) and isset($items['disc']))
314 {
315 if ($this->_update($val) == TRUE)
316 {
317 $save_cart = TRUE;
318 }
319 }
320 }
321 }
322
323 // Save the cart data if the insert was successful
324 if ($save_cart == TRUE)
325 {
326 $this->_save_cart();
327 return TRUE;
328 }
329
330 return FALSE;
331 }
332
333 // --------------------------------------------------------------------
334
335 /**
336 * Update the cart
337 *
338 * This function permits the quantity of a given item to be changed.
339 * Typically it is called from the "view cart" page if a user makes
340 * changes to the quantity before checkout. That array must contain the
341 * product ID and quantity for each item.
342 *
343 * @access private
344 * @param array
345 * @return bool
346 */
347 function _update($items = array())
348 {
349 // Without these array indexes there is nothing we can do
350
351 if ( ! isset($items['qty']) OR ! isset($items['tax']) OR ! isset($items['disc']) OR !isset($items['rowid']) )
352 {
353 return FALSE;
354 }
355
356 // Prep the quantity
357 $items['qty'] = preg_replace('/([^0-9])/i', '', $items['qty']);
358 $items['tax'] = preg_replace('/([^0-9])/i', '', $items['tax']);
359 $items['disc'] = preg_replace('/([^0-9])/i', '', $items['disc']);
360
361
362 // Is the quantity a number?
363 if ( ! is_numeric($items['qty']) or ! is_numeric($items['tax']) or ! is_numeric($items['disc']) )
364 {
365 return FALSE;
366 }
367
368 // Is the new quantity different than what is already saved in the cart?
369 // If it's the same there's nothing to do
370 if ($this->_cart_contents[$items['rowid']]['qty'] == $items['qty']or !
371 $this->_cart_contents[$items['rowid']]['tax'] == $items['tax'] or !
372 $this->_cart_contents[$items['rowid']]['disc'] == $items['disc']
373
374
375 )
376 {
377 return FALSE;
378 }
379
380 // Is the quantity zero? If so we will remove the item from the cart.
381 // If the quantity is greater than zero we are updating
382 if ($items['qty'] == 0 or! $items['tax'] == 0 or !$items['disc'] == 0 )
383 {
384 unset($this->_cart_contents[$items['rowid']]);
385 }
386 else
387 {
388 $this->_cart_contents[$items['rowid']]['qty'] = $items['qty'];
389 $this->_cart_contents[$items['rowid']]['tax'] = $items['tax'] ;
390 $this->_cart_contents[$items['rowid']]['disc'] = $items['disc'];
391 }
392
393
394
395 return TRUE;
396 }
397
398 // --------------------------------------------------------------------
399
400 /**
401 * Save the cart array to the session DB
402 *
403 * @access private
404 * @return bool
405 */
406 function _save_cart()
407 {
408 // Unset these so our total can be calculated correctly below
409 unset($this->_cart_contents['total_items']);
410 unset($this->_cart_contents['cart_total']);
411
412 // Lets add up the individual prices and set the cart sub-total
413 $total = 0;
414 $items = 0;
415
416 foreach ($this->_cart_contents as $key => $val)
417 {
418 // We make sure the array contains the proper indexes
419 if ( ! is_array($val) OR ! isset($val['price']) OR ! isset($val['qty']) OR ! isset($val['tax']) OR ! isset($val['disc']) )
420 {
421 continue;
422 }
423
424 $total += ($val['price'] * $val['qty']+$val['tax'] -$val['disc']);
425 $items += $val['qty'] + $val['tax'] +$val['disc'] ;
426 //$items += $val['tax'] ;
427 //$items += $val['disc'] ;
428
429 // Set the subtotal
430 $this->_cart_contents[$key]['subtotal'] = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty'] +$this->_cart_contents[$key]['tax'] -$this->_cart_contents[$key]['disc']);
431 }
432
433 // Set the cart total and total items.
434 $this->_cart_contents['total_items'] = $items;
435 $this->_cart_contents['cart_total'] = $total;
436
437 // Is our cart empty? If so we delete it from the session
438 if (count($this->_cart_contents) <= 2)
439 {
440 $this->CI->session->unset_userdata('cart_contents');
441
442 // Nothing more to do... coffee time!
443 return FALSE;
444 }
445
446 // If we made it this far it means that our cart has data.
447 // Let's pass it to the Session class so it can be stored
448 $this->CI->session->set_userdata(array('cart_contents' => $this->_cart_contents));
449
450 // Woot!
451 return TRUE;
452 }
453
454 // --------------------------------------------------------------------
455
456 /**
457 * Cart Total
458 *
459 * @access public
460 * @return integer
461 */
462 function total()
463 {
464 return $this->_cart_contents['cart_total'];
465 }
466
467 // --------------------------------------------------------------------
468
469 /**
470 * Total Items
471 *
472 * Returns the total item count
473 *
474 * @access public
475 * @return integer
476 */
477 function total_items()
478 {
479 return $this->_cart_contents['total_items'];
480 }
481
482 // --------------------------------------------------------------------
483
484 /**
485 * Cart Contents
486 *
487 * Returns the entire cart array
488 *
489 * @access public
490 * @return array
491 */
492 function contents()
493 {
494 $cart = $this->_cart_contents;
495
496 // Remove these so they don't create a problem when showing the cart table
497 unset($cart['total_items']);
498 unset($cart['cart_total']);
499
500 return $cart;
501 }
502
503 // --------------------------------------------------------------------
504
505 /**
506 * Has options
507 *
508 * Returns TRUE if the rowid passed to this function correlates to an item
509 * that has options associated with it.
510 *
511 * @access public
512 * @return array
513 */
514 function has_options($rowid = '')
515 {
516 if ( ! isset($this->_cart_contents[$rowid]['options']) OR count($this->_cart_contents[$rowid]['options']) === 0)
517 {
518 return FALSE;
519 }
520
521 return TRUE;
522 }
523
524 // --------------------------------------------------------------------
525
526 /**
527 * Product options
528 *
529 * Returns the an array of options, for a particular product row ID
530 *
531 * @access public
532 * @return array
533 */
534 function product_options($rowid = '')
535 {
536 if ( ! isset($this->_cart_contents[$rowid]['options']))
537 {
538 return array();
539 }
540
541 return $this->_cart_contents[$rowid]['options'];
542 }
543
544 // --------------------------------------------------------------------
545
546 /**
547 * Format Number
548 *
549 * Returns the supplied number with commas and a decimal point.
550 *
551 * @access public
552 * @return integer
553 */
554 function format_number($n = '')
555 {
556 if ($n == '')
557 {
558 return '';
559 }
560
561 // Remove anything that isn't a number or decimal point.
562 $n = trim(preg_replace('/([^0-9\.])/i', '', $n));
563
564 return number_format($n, 2, '.', ',');
565 }
566
567 // --------------------------------------------------------------------
568
569 /**
570 * Destroy the cart
571 *
572 * Empties the cart and kills the session
573 *
574 * @access public
575 * @return null
576 */
577 function destroy()
578 {
579 unset($this->_cart_contents);
580
581 $this->_cart_contents['cart_total'] = 0;
582 $this->_cart_contents['total_items'] = 0;
583
584 $this->CI->session->unset_userdata('cart_contents');
585 }
586
587
588}
589
590/* End of file Cart.php */
591/* Location: ./system/libraries/Cart.php */