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