Jump to content

PHP shopping cart session not updating after custom session handler incorporated into site


NoWar11

Recommended Posts

i am putting together a basic site with a basic shopping cart, we plan to store sensitive information in the sessions in the future so getting this sorted now is a smart choice.

 

Everything works fine without the custom session handler, for some context the session handling code is below:

 

    function decrypt($edata, $password) {
    $data = base64_decode($edata);
    $salt = substr($data, 0, 16);
    $ct = substr($data, 16);
 
    $rounds = 3; // depends on key length
    $data00 = $password.$salt;
    $hash = array();
    $hash[0] = hash('sha256', $data00, true);
    $result = $hash[0];
    for ($i = 1; $i < $rounds; $i++) {
        $hash[$i] = hash('sha256', $hash[$i - 1].$data00, true);
        $result .= $hash[$i];
     }
        $key = substr($result, 0, 32);
        $iv  = substr($result, 32,16);
 
        return openssl_decrypt($ct, 'AES-256-CBC', $key, true, $iv); 
 
       } 
 
       function encrypt($data, $password) {
 
    // Set a random salt
    $salt = openssl_random_pseudo_bytes(16);
    $salted = '';
    $dx = '';
    // Salt the key(32) and iv(16) = 48
    while (strlen($salted) < 48) {
      $dx = hash('sha256', $dx.$password.$salt, true);
      $salted .= $dx;
    }
 
    $key = substr($salted, 0, 32);
    $iv  = substr($salted, 32,16);
 
    $encrypted_data = openssl_encrypt($data, 'AES-256-CBC', $key, true, $iv);
    return base64_encode($salt . $encrypted_data);
 
     }
 
     class SecureSessionHandler extends SessionHandler {
 
    protected $name, $cookie;
private  $key;
 
    public function __construct($key, $name = 'MY_SESSION', $cookie = [])
           { 
        $this->key = $key;
        $this->name = $name;
        $this->cookie = $cookie;
        $this->cookie += [
            'lifetime' => 0,
            'path'     => ini_get('session.cookie_path'),
            'domain'   => ini_get('session.cookie_domain'),
            'secure'   => isset($_SERVER['HTTPS']),
            'httponly' => true
        ];
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256- 
         cbc'));
        $this->setup();
    }
    private function setup()
    {
        ini_set('session.use_cookies', 1);
        ini_set('session.use_only_cookies', 1);
        session_name($this->name);
        session_set_cookie_params(
            $this->cookie['lifetime'],
            $this->cookie['path'],
            $this->cookie['domain'],
            $this->cookie['secure'],
            $this->cookie['httponly']
        );
 
    }
 
    public function start()
    {
        if (session_id() === '') {
            if (session_start()) {
                return mt_rand(45, 99) === 55 ? $this->refresh() : true; // 1/5
            }
        }
        return false;
    }
 
    public function forget()
    {
        if (session_id() === '') {
            return false;
        }
        $_SESSION = [];
        setcookie(
            $this->name,
            '',
            time() - 42000,
            $this->cookie['path'],
            $this->cookie['domain'],
            $this->cookie['secure'],
            $this->cookie['httponly']
        );
        return session_destroy();
    }
 
    public function refresh()
    {
        return session_regenerate_id(false);
    }
 
public function write($id, $data)
    {
$data = encrypt($data, $this->key);
        return parent::write($id, $data);
 
    }
 
    public function read($id)
    {
$data = parent::read($id);
 
        if (!$data) {
            return "";
        } else {
            return decrypt($data, $this->key);
        }
    }
 
    public function isExpired($ttl = 30)
    {
        $last = isset($_SESSION['_last_activity'])
            ? $_SESSION['_last_activity']
            : false;
        if ($last !== false && time() - $last > $ttl * 60) {
            return true;
        }
        $_SESSION['_last_activity'] = time();
        return false;
    }
 
    public function isFingerprint()
    {
        $hash = md5(
            $_SERVER['HTTP_USER_AGENT'] .
            (ip2long($_SERVER['REMOTE_ADDR']) & ip2long('255.255.0.0'))
        );
        if (isset($_SESSION['_fingerprint'])) {
            return $_SESSION['_fingerprint'] === $hash;
        }
        $_SESSION['_fingerprint'] = $hash;
        return true;
    }
 
    public function isValid()
    {
        return ! $this->isExpired() && $this->isFingerprint();
    }
    public function get($name)
    {
        $parsed = explode('.', $name);
        $result = $_SESSION;
        while ($parsed) {
            $next = array_shift($parsed);
            if (isset($result[$next])) {
                $result = $result[$next];
            } else {
                return null;
            }
        }
        return $result;
    }
    public function put($name, $value)
    {
        $parsed = explode('.', $name);
        $session =& $_SESSION;
        while (count($parsed) > 1) {
            $next = array_shift($parsed);
            if ( ! isset($session[$next]) || ! is_array($session[$next])) {
                $session[$next] = [];
            }
            $session =& $session[$next];
        }
        $session[array_shift($parsed)] = $value;
        }
        }
 
        $key = file_get_contents('./key.pem', FILE_USE_INCLUDE_PATH);
         $session = new SecureSessionHandler($key);
 
        ini_set('session.save_handler', 'files');
        session_set_save_handler($session, true);
        session_save_path(__DIR__ . '/sessions');
 
       $session->start('cheese');
       if ( ! $session->isValid(5)) {
       $session->destroy('cheese');
        }
 

There is the full session handler and encryption/decryption method, everything works fine overall but the problem is when going through my shopping cart code and changing all the $_SESSION variables into the $session->get objects, i managed to replace every $_SESSION then the last one i need to change throws a fatal error:

 

changing " $_SESSION['cart_contents'] = $this->cart_contents;"

to  "$session->get('cart_contents') = $this->cart_contents;"

 

results in:

 

Fatal error: Can't use method return value in write context in C:\xampp\htdocs\teck\Cart.php on line 375

 

 

The rest of the important code is mentioned below, i made sure nothing was left out, its better to have a long post with all details than getting flamed for a small post with a question missing all details lol.

       

 

The page that you can adjust the quantity on is viewcart.php, the code thats important from this page is below:

 

 

<input type="number" class="form-control text-center" value="<?php echo 

     $item["qty"]; ?>" onchange="updateCartItem(this, '<?php echo 

     $item["rowid"]; ?>')">

 

 

and the script on the same page (viewcart.php) that throws the alert is below:

 

      <script>
      function updateCartItem(obj,id){
        $.get("cartAction.php", {action:"updateCartItem", id:id, qty:obj.value}, 
      function(data){
            if(data == 'ok'){
                location.reload();
            }else{
                alert('Cart update failed, please try again.');
            }
        });
    }
    </script>
 

 

 

Now finally cartaction.php, the code for the action "updatecartitem" is below:

     }elseif($_REQUEST['action'] == 'updateCartItem' && !empty($_REQUEST['id']))
     {
        $itemData = array(
            'rowid' => $_REQUEST['id'],
            'qty' => $_REQUEST['qty']
        );
       $updateItem = $cart->update($itemData);
        echo $updateItem?'ok':'err';die;
 
    }
 

The update function thats in cart.php:

 

 
      public function update($item = array()){
if (!is_array($item) OR count($item) === 0){
return FALSE;
}else{
if (!isset($item['rowid'], $this->cart_contents[$item['rowid']])){
return FALSE;
}else{
// prep the quantity
if(isset($item['qty'])){
$item['qty'] = filter_var($item['qty'], 
          FILTER_VALIDATE_INT);
// remove the item from the cart, if quantity is zero
if ($item['qty'] == 0){
unset($this->cart_contents[$item['rowid']]);
return TRUE;
}
}
 
// find updatable keys
$keys = array_intersect(array_keys($this-
           >cart_contents[$item['rowid']]), array_keys($item));
// prep the price
if(isset($item['price'])){
$item['price'] = filter_var($item['price'], 
        FILTER_VALIDATE_FLOAT);
}
// product id & name shouldn't be changed
foreach(array_diff($keys, array('id', 'name')) as $key){
$this->cart_contents[$item['rowid']][$key] = $item[$key];
}
// save cart data
$this->save_cart();
 
 
return TRUE;
}
}
 }
 

And last but not least the save_cart function in cart.php also:

      protected function save_cart(){
$this->cart_contents['total_items'] = $this->cart_contents['cart_total'] 
        = 0;
foreach ($this->cart_contents as $key => $val){
// make sure the array contains the proper indexes
if(!is_array($val) OR !isset($val['price'], $val['qty'])){
continue;
}
 
$this->cart_contents['cart_total'] += ($val['price'] * $val['qty']);
$this->cart_contents['total_items'] += $val['qty'];
$this->cart_contents[$key]['subtotal'] = ($this->cart_contents[$key]
            ['price'] * $this->cart_contents[$key]['qty']);
}
global $key;
global $session;
 
// if cart empty, delete it from the session
if(count($this->cart_contents) <= 2){
$session->forget();
return FALSE;
}else{
 
 
 
// original code below
$_SESSION['cart_contents'] = $this->cart_contents;
 
 
 
 
 
          // code below is what i thought would work if swapped with the line above but it throws the 
          //  fatal error as stated at the start of the post.
$session->get('cart_contents') = $this->cart_contents;
 
 
return TRUE;
 
 
 
 
}
        }
      

 

The cart code above is in the same file as the session handler, 

 

A bit more of an explanation, with " $_SESSION['cart_contents'] = $this->cart_contents;" (the original code),  

 

i press the button +1 to add extra item into cart, the text field increases 1, and the error alert pops up saying "Cart update failed, please try again", i click ok and the alert goes away and nothing happens, the item qty in the text box stays updated but the actual real cart qty doesnt update until i refresh the page or click a link.

 

Without the custom session handler in place and just the original "$_SESSION" in place of "$session->" this doesnt happen, everything works perfectly (and of course all the other session variables changed back to "$_SESSION" aswel).

 

Is it a must that i change this $_SESSION['cart_contents'] variable to $session->get('cart_contents')?

 

I should be able to change that alert script to update the cart properly without throwing the alert but will my custom session handler work flawlessly if i leave that one session variable and dont replace it with the session handler class? 

 

Im very sorry for the long post but i wanted to make sure everything is as clear as possible for other people to also understand if they are having similar problems.

 

The code does seem quite messy from this point of view but if you were to go over the entire site everything would make much more sense.

 

I have been on php for about 2 months max now trying my best to pick up as much as possible so please bear with me, obviously most of the code is not as clean as it should be, but this is production.

 

I have spent the past 3-4 days on this session handler, i really dont want to scrap it and just go back to the standard _session.

 

All & any advice is welcome, constructive criticism will be taken lightly!

 

I really appreciate the time you have taken to go through this, Thanks for your effort!

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.