I am trying to develop a small web shop with CodeIgniter. To store the items, I use the Cart library. Most of the time, everything works great. However, sometimes the content of the cart gets lost after a redirect.
I found a couple of fixes on the web, but none of them works in my case. Here is my setup:
I use the DB for storing sessions
I don't use AJAX
There is no underscore in the name of the session cookie
Here is an example of a refresh:
public function add_item() {
$item_id = $this->input->post('item');
// Query database
$item = $this->model->find_item($item_id);
// Rewrite model info
...
$data = array(
'id' => 'item-' . $item['id'] . '-size-' . $item['sizes'][$i]['id'],
'qty' => $qty,
'price' => $item['sizes'][$i]['price'],
'name' => $item['name'],
'options' => array('short_name' => $item['short_name'])
);
$this->cart->insert($data);
usleep(10000);
redirect('shop');
}
I finally found an answer to my problem, thanks to this question: CodeIgniter Cart and Session lost when refresh page
The problem was that the data stored in the session got too big. CodeIgniter stores all the data in a cookie, which is limited to 4kB. My mistake was to think that, if I used the DB to store my sessions, I could avoid that limit. Apparently, CodeIgniter saves sessions in the database "only" for security reasons. There is still a lot of data in the cookie.
Now, I use a library called Native session. I found it here: https://github.com/EllisLab/CodeIgniter/wiki/Native-session
I just put the file in 'application/libraries', renamed the first function to '__construct()', added it to autoimport and replaced all the 'session' tags with 'native_session' in my code. I also had to change the Cart class because it was using CodeIgniter's original session.
Related
I know this question is frequently asked on this forum, but none of the proposed solutions has worked for me, basically, I am a WordPress plugin developer and developing a plugin whose session starts in the init file but close when the client closes his/her browser, and it is necessary to keep the session open. In this case, how can I get rid of this warning? Maybe I should use something other than the session or what you suggest.
Till now there are different proposed solutions like Use
if ( !session_id() ) {
session_start( [
'read_and_close' => true,
] );
}
but with this technique, I can never be able to use sessions in my plugin, and the whole functionality goes on the ground.
Any help in this regard would be much appreciated.
thanks
WordPress plugin or theme developers cannot use php's session subsystem as you have discovered.
If you need to save some kind of global context for your plugin to work, use WordPress's add_option() function to save it at the end of each operation. You can retrieve your context using get_option().
If you need per-user context, use add_user_meta() to save it and get_user_meta() to retrieve it. Like this:
$id = get_current_user_id();
$myContext = get_user_meta( $id, 'my_plugins_meta_tag_name' );
...
add_user_meta( $id, 'my_plugins_meta_tag_name', $myContext);
WordPress uses a database table to store these sorts of options. Some sites put an object cache in front of the table. But these APIs are how you do what you want to do.
In your plugin's deactivation hook, please consider calling delete_option() to clean up that storage. And, if you use per-user context, please consider using delete_user_meta() to remove your user_meta item from all of them.
$users = get_users( array( 'fields' => array( 'ID' ) ) );
foreach($users as $user){
delete_user_meta ( $user->ID, 'my_plugins_meta_tag_name' );
}
As for sensitive information, WordPress offers no other place to put it. And, if you were using sessions, php would need to put it somewhere as well. By default, php puts session data in the filesystem of the server. That's why WordPress hashes user passwords before storing them.
Simply trying to store and retrieve unregistered customer data from page to page in a custom checkout template. I've stripped it down to the bare bones to try to pinpoint the issue.
This
<?php
$customer = WC()->customer;
$customer->set_billing_first_name("WORKING!!!");
$customer->save();
var_dump($customer->get_billing());
?>
Outputs this
array (size=2)
'country' => string 'US' (length=2)
'first_name' => string 'WORKING!!!' (length=10)
But then this
<?php
$customer = WC()->customer;
//$customer->set_billing_first_name("WORKING!!!");
//$customer->save();
var_dump($customer->get_billing());
?>
Outputs this
array (size=1)
'country' => string 'US' (length=2)
Even though I should still be firmly within the same session, and therefore should get the data stored before the comments. All I did was refresh the page after commenting out those two lines.
Am I completely wrong about these methods?
Checked
Environment is configured entirely correctly. Even had someone else double-check it for me. URLs, caches, etc.
It does appear to work when logged in, but the vast majority of users never do so that's not very helpful.
Have tried this on two different servers (one local, one remote) and have the same issue.
Started fresh with a new WP+WC install, created a blank theme, functions.php that does the above on init code. Same exact issue.
If $customer->save() doesn't persist the changes you made to the customer's data (e.g. $customer->set_billing_first_name('Test')), then it's likely because the customer is not registered to the site or not logged-in, where $customer->get_id() is 0.
And that is normal because the user's ID or the session's ID is required in order to properly save the changes and make it persistent across different pages.
So when the user is not registered/logged-in, WooCommerce doesn't start its session until the user logs in, or that he/she added a product into the cart.
But you can manually start the session, like so: (add the code to the active theme's functions.php file)
add_action( 'woocommerce_init', function(){
if ( ! WC()->session->has_session() ) {
WC()->session->set_customer_session_cookie( true );
}
} );
And then, changes to the customer's data would then be carried on to other pages so long as cookies are enabled on the browser, because just like WordPress, WooCommerce stores its session ID (the user ID or an auto-generated ID/hash) in the cookies — and the session ID is used to set/retrieve the session data in the database — the table name is woocommerce_sessions if without any table prefix.
Try this after starting the WooCommerce session:
$customer = WC()->customer;
// Change 'Test' if necessary - e.g. 'Something unique 123'
if ( 'Test' !== $customer->get_billing_first_name() ) {
$customer->set_billing_first_name( 'Test' );
echo 'First name added to session<br>';
} else {
echo 'First name read from session<br>';
}
And this one — you should see a new date on each page load: (well, the one you've set previously)
echo WC()->session->get( 'test', 'None set' ) . '<br>';
WC()->session->set( 'test', current_time( 'mysql' ) );
echo WC()->session->get( 'test', 'It can\'t be empty' );
Are you using the Url set in Wordpress? It seems that it stores the cookie based on the url configured in the app rather than the actual one.
Hope this helps.
Source: https://en.blogpascher.com/wordpress-tutorial/How-correct-the-loss-of-session-on-wordpress
Is that possible instead of using <input name="...."/> I get my value from div or span etc. ?
currently I get my data directly from database, base on product id, but as my product might have discount that will show different price with what is saved in my database and I cannot show it as input so i need to pass it through div or span etc.
code:
here is my code now:
public function addingItem(Request $request, $id)
{
$product = Product::findOrFail($id);
Cart::add(array(
'id' => $product->id,
'name' => $product->title,
'price' => $product->price, // this comes directly from products table
));
}
with my code i always will get 45.325 but I need to get 35.325 during discount time.
That's why i need to pass it through div and cannot use input here.
any idea?
As far as your PHP code is concerned, data doesn't come from any particular part of a page, it comes from the HTTP request sent by the browser. An HTML form is just the simplest way to get the browser to add some data to that request. This may seem like nitpicking, but it has important consequences.
First, it means that what you are asking for is absolutely possible. You just need to write some JavaScript to run in the browser and tell the browser to add that value to the request. A simple way would be to have a hidden input field on the form and set the value in the JavaScript, but you can also create a completely custom request and send it to the server (AJAX).
Second, though, it means that any user can submit any data to your application by telling their browser to submit their choice of value not yours. Consequently, you have to be very careful of what data you trust, and trusting the browser to send you a price sounds like a really bad idea. What's to stop someone giving themselves a 100% discount by editing the value on the page?
Somewhere, you know what discounts you're offering. That discount is a core part of your application, so however the view knows what discount to show, the rest of the application should be able to know the same way. This probably means moving some code out of your view into a new function, which can be used by various parts of the application; that makes each use more readable, and means you don't have to change it in lots of places if the requirements get more complex.
SOLVED
well I decided to bring my codes to controller instead of using ajax or other ways, here is how I've done it:
$discounts = Discount::all();
$mytime = Carbon::now();
//get discounted price or normal price of product
$price = $product->discounts;
if($price->count() > 0 ) {
foreach($discounts as $disc){
if($disc->value_to >= $mytime) {
$price = $product->price - $disc->amount;
}
}
}else{
$price = $product->price;
}
Hope it help others.
I have adopted a CakePhp 2 project. We get to the project from another project, linking to the CakePhp project with a "token" and a conference ID as a parameter in the URL. Using that token, we authorize the user, and using the conference ID get the information from the database. The session value "auth" is set to true.
We have it running on 2 "platforms" locally on my system using a vagrant machine, and on a production server. Locally the session value dies really quick and at random times. On the production server not as often, but the issues we have where Ajax calls don't seem to do what are expected, we believe are being caused by a similar issue. We have many different projects, all Laravel, with zero issues where the session values clear. This issue is strictly with the CakePhp project.
All the authentication magic happens in the beforeFilter method. The code:
public function beforeFilter() {
$session = new CakeSession();
/**
*
* We will check if the current user is authorized here!
*
*/
// If the visitor is coming for the first time, there should be a parameter in
// the URL that is the auth code to check against the database.
if ( ( isset($_GET['conf']) && is_numeric($_GET['conf']) ) && isset($_GET['token']) ) {
$getConference = ClassRegistry::init('Conference')->find('first', ["conditions" => ["conference_id"=>$_GET['conf'] ]]);
$checkToken = ClassRegistry::init('User')->find('first', ["conditions" => ["remember_token"=>$_GET['token'] ]]);
if ($getConference && $checkToken) {
$checkToken['User']['remember_token'] = $this->generateToken();
if ( ClassRegistry::init('User')->save( $checkToken ) ) {
$session->write('auth', true);
$session->write('conferenceId', $_GET['conf']);
$this->redirect('/');
}
}
else {
$session->write('auth', false);
$session->write('conferenceId', null);
}
}
if (! $session->read('auth') || $session->read('conferenceId') == null ) {
echo "No permission!";
exit;
}
}
At the top of the controller:
App::uses('CakeSession', 'Model/Datasource');
When the URL parameters are present, it traps them, does the work, and redirects to the home route without the parameters.
$this->generateToken();
Creates a new token, and overwrites the old one in the database.
There are 2 main controllers. The controller with this code is the main projects controller. The only time it is really hit is the first time you go to the project, and we hit the index method. From there everything else is AJAX calls to the other controller. There is one link, a "home" type link that will hit that index method.
Sometimes these Ajax calls stop working, and clicking that home link will output "No Permission" instead of the expected html in the container the Ajax call outputs too.
Steps to troubleshoot led me to putting this beforeFilter method on the top of the second controller. Now, randomly I'll get no permission. Sometimes, when I'm on the main project that links to this CakePhp project, I click that link, I get no permission right off the bat.
I found this page: cakephp takes me to login page on multiple request and have tried to set the session details like this:
Configure::write('Session', array(
'defaults' => 'php',
'timeout' => '300' // <- added this element
));
And I have tried:
Configure::write('Session.timeout', '300');
Additionally, I have tried cookieTimeout in both of those cases.
I've also tried
Configure::write('Security.level', 'low');
and included
Configure::write('Session.autoRegenerate', true);
In any order, any of these cause the session to bomb out immediately. I get "No permission on page load, and never get anywhere.
The code for this project is honestly crap. The developer who wrote it had mistakes and errors all over the place. On top of that, we are a Laravel shop. We are just trying to keep the project limping along until sometime in the future when we can nuke it from orbit. So we just need to get this working. Any thoughts on what could be causing this? Any other details I am forgetting to include that would help troubleshoot this issue?
Thanks
Reading & writing session data
You can read values from the session using Set::classicExtract() compatible syntax:
CakeSession::read('Config.language');
$key should be the dot separated path you wish to write $value to:
CakeSession::write('Config.language', 'eng');
When you need to delete data from the session, you can use delete:
CakeSession::delete('Config.language');
You should also see the documentation on Sessions and SessionHelper for how to access Session data in the controller and view.
I have an odd situation. consider the following scenario:
form on page posts to another domain
that domain processes data, and posts back to controller A
controller A processes returned data, and sets flash session vars
controller A redirects to controller B
session values set in controller A are lost.
Now, I have noticed that if I eliminate step 4; the session values are not lost. It is only happening after calling Response::redirect().
Following is the relevant code in controller A's post handler (in this case, $vars is set to Input::post(); and the renew() method does a soap call to another API, and returns response data.):
$success_object = $this->renew( $vars );
$persist_through_redirect = array(
'accountId' => $success_object->account->accountId,
'expireDate' => $success_object->account->expireDate,
'createDate' => $success_object->account->createDate,
'due_amount' => $success_object->account->lastPaymentAmt,
'offer_name' => $offer_name,
'member_name' => $vars['first_name'] . ' ' . $vars['last_name']
);
return $this->redirect('success/', $persist_through_redirect);
And this is the code behind method redirect():
private function redirect($redirect_endpoint, $redirect_values = null) {
Session::set_flash('flash_redirect', $redirect_values);
Response::redirect($redirect_endpoint);
}
Finally, here is the code in controller B that accesses the session data:
$information = Session::get_flash('flash_redirect');
if(isset($information)) {
View::set_global('membership', $information);
}
// set the locale based on fuel's setting
View::set_global('locale', str_replace( '_', '-', Fuel::$locale ));
return View::forge('successes/success_card');
It gets weirder though. Not all session data is lost. Any session data I had set in the before() method is staying put.
I'm very stumped on this one. Any ideas why my session data is lost?
As it turned out, the core issue was that my session cookie was exceeding 4kb.
What was happening is that when I made a call to Response::redirect(), the script was ending at that point, but also causing a redirect that was hiding the exception being thrown. I added a call to Session::write() just before the redirect, which allowed the exception to not be "hidden" due to page reload.
Once I saw this, it was just a matter of tracking down code that was pushing all of this extra data I did not need, and removing it.