[eluser]DeaD SouL[/eluser]
Hi there,
I posted a topic about this bug in: [bug] set_flashdata() duplicates the sql queries -> http://ellislab.com/forums/viewthread/189739/
And since there was no help. I checked it myself, and tried to fix it..
and it WORKED
So, I just wanted to share it with you guys
copy the following code, and save it in application/libraries/MY_Session.php
This is OLD, go to Reply #1
Session doesn't work with redirect() ? see: Reply #9
Code: <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Fixing the duplication of updates
*
* Please note, that the $this->db->query_count won't count the session db
* update query. It is because of the way of the __destruct() method.
*
* If you want to show the update query in profiler, first change the name of
* the __destruct() to anything else to avoid the autocall from php, then use
* the hooks and tweak it to do the job automatically. Otherwise, you can call
* it manually in the end of your each controller's method (before showing the
* views).
*
* @package CodeIgniter
* @author Mubarak Alrashidi (DeaDSouL)
* @copyright Copyright (c) 2011, Mubarak Alrashidi.
* @license http://ellislab.com/codeigniter/user-guide/license.html
* @link http://codeigniter.com
* @since Version 2.0.2
* @filesource
*/
// -------------------------------------------------------------------------
class MY_Session extends CI_Session {
private
$old_userdata = array();
// -------------------------------------------------------------------------
public function __construct()
{
parent::__construct();
// getting a copy of the userdata
$this->old_userdata = $this->userdata;
}
// -------------------------------------------------------------------------
/**
* Write the session data
*
* @access public
* @return void
*/
public function __destruct()
{
// Are we saving custom data to the DB? If not, all we do is update the cookie
if ($this->sess_use_database === FALSE)
{
$this->_set_cookie();
return;
}
// set the custom userdata, the session data we will set in a second
$custom_userdata = $this->userdata;
$cookie_userdata = array();
// Before continuing, we need to determine if there is any custom data to deal with.
// Let's determine this by removing the default indexes to see if there's anything left in the array
// and set the session data while we're at it
foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
{
unset($custom_userdata[$val]);
$cookie_userdata[$val] = $this->userdata[$val];
// remove $val from $this->old_userdata too
unset($this->old_userdata[$val]);
}
// counting the custom_userdata & old_userdata
$c_custom_userdata = count( $custom_userdata );
$c_old_userdata = count( $this->old_userdata );
// comparing the custom_userdata with the old_userdata
$need_update = FALSE; // default value
if( $c_old_userdata <> $c_custom_userdata )
{
$need_update = TRUE;
}
if( $need_update === FALSE
AND $c_custom_userdata > 0 )
{
foreach( $custom_userdata as $k => $v )
{
if( ! isset( $this->old_userdata[$k] )
OR $this->old_userdata[$k] <> $v )
{
$need_update = TRUE;
break;
}
}
}
// Did we find any custom data? If not, we turn the empty array into a string
// since there's no reason to serialize and store an empty array in the DB
if ( $c_custom_userdata === 0 )
{
$custom_userdata = '';
}
else
{
// Serialize the custom data array so we can store it
$custom_userdata = $this->_serialize($custom_userdata);
}
// Do we need to update the database
if( $need_update === TRUE )
{
// Run the update query
$this->CI->db->where('session_id', $this->userdata['session_id']);
$this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata));
}
// Write the cookie. Notice that we manually pass the cookie data array to the
// _set_cookie() function. Normally that function will store $this->userdata, but
// in this case that array contains custom data, which we do not want in the cookie.
$this->_set_cookie($cookie_userdata);
}
// -------------------------------------------------------------------------
/**
* ignore $this->session->sess_write() if its being called
*
* @access public
* @return void
*/
public function sess_write()
{
return;
}
}
// END MY_Session Class
/* End of file MY_Session.php */
/* Location: ./application/libraries/MY_Session.php */
By the way, I tested it on CI 2.0.2
I hope anyone tries it, tell us whether it worked for him or not, and what was his CI version
Regards !
[eluser]DeaD SouL[/eluser]
Hi again,
I strongly recommend to forget about the first code that was mentioned in my previous post, as it will conflict with MY_ libs & MY_ cores specially with system/core/Exceptions.php if you are using the __autoload() function.
the new application/libraries/MY_Session.php
Code: <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Fixing the duplication of updates
*
* Install:
* 1) save this code in application/libraries/MY_Session.php
*
* 2) setup the hook
* 1) update "application/config/config.php"
* $config['enable_hooks'] = TRUE; // FALSE;
*
* 2) update "application/config/hooks.php"
* $hook['post_controller'] = array(
* 'class' => '',
* 'function' => 'update_session_db',
* 'filename' => 'sess_update_fix.php',
* 'filepath' => 'hooks',
* 'params' => array()
* );
*
* 3) create "application/hooks/sess_update_fix.php"
* function update_session_db()
* {
* $CI =& get_instance();
* $CI->session->sess_write( TRUE );
* }
*
* If you don't wish to use the hooks, then you'll have to save the session
* manually like this: $this->session->sess_write(TRUE);
*
* @package CodeIgniter
* @author Mubarak Alrashidi (DeaDSouL)
* @copyright Copyright (c) 2011, Mubarak Alrashidi.
* @license http://ellislab.com/codeigniter/user-guide/license.html
* @link http://codeigniter.com
* @since Version 2.0.2
* @filesource
*/
// -------------------------------------------------------------------------
class MY_Session extends CI_Session {
private
$old_userdata = array();
// -------------------------------------------------------------------------
public function __construct()
{
parent::__construct();
// getting a copy of the userdata
$this->old_userdata = $this->userdata;
}
// -------------------------------------------------------------------------
/**
* Write the session data
*
* ignore $this->session->sess_write() if its being called with any
* parameter that doesn't equal to TRUE.
*
* @access public
* @param bool (default: FALSE)
* @return void
*/
function sess_write( $real_write = FALSE )
{
if( $real_write !== TRUE )
{
return;
}
// Are we saving custom data to the DB? If not, all we do is update the cookie
if ($this->sess_use_database === FALSE)
{
$this->_set_cookie();
return;
}
// set the custom userdata, the session data we will set in a second
$custom_userdata = $this->userdata;
$cookie_userdata = array();
// Before continuing, we need to determine if there is any custom data to deal with.
// Let's determine this by removing the default indexes to see if there's anything left in the array
// and set the session data while we're at it
foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
{
unset($custom_userdata[$val]);
$cookie_userdata[$val] = $this->userdata[$val];
// remove $val from $this->old_userdata too
unset($this->old_userdata[$val]);
}
// counting the custom_userdata & old_userdata
$c_custom_userdata = count( $custom_userdata );
$c_old_userdata = count( $this->old_userdata );
// comparing the custom_userdata with the old_userdata
$need_update = FALSE; // default value
if( $c_old_userdata <> $c_custom_userdata )
{
$need_update = TRUE;
}
if( $need_update === FALSE
AND $c_custom_userdata > 0 )
{
foreach( $custom_userdata as $k => $v )
{
if( ! isset( $this->old_userdata[$k] )
OR $this->old_userdata[$k] <> $v )
{
$need_update = TRUE;
break;
}
}
}
// Did we find any custom data? If not, we turn the empty array into a string
// since there's no reason to serialize and store an empty array in the DB
if ( $c_custom_userdata === 0 )
{
$custom_userdata = '';
}
else
{
// Serialize the custom data array so we can store it
$custom_userdata = $this->_serialize($custom_userdata);
}
// Do we need to update the database
if( $need_update === TRUE )
{
// Run the update query
$this->CI->db->where('session_id', $this->userdata['session_id']);
$this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata));
}
// Write the cookie. Notice that we manually pass the cookie data array to the
// _set_cookie() function. Normally that function will store $this->userdata, but
// in this case that array contains custom data, which we do not want in the cookie.
$this->_set_cookie($cookie_userdata);
}
}
// END MY_Session Class
/* End of file MY_Session.php */
/* Location: ./application/libraries/MY_Session.php */
Enjoy
[eluser]DeaD SouL[/eluser]
If you want to use the Hooks, continue reading please:
1) update "application/config/config.php"
Code: $config['enable_hooks'] = TRUE; // FALSE;
2) update "application/config/hooks.php"
Code: $hook['post_controller'] = array(
'class' => '',
'function' => 'update_session_db',
'filename' => 'sess_update_fix.php',
'filepath' => 'hooks',
'params' => array()
);
3) create "application/hooks/sess_update_fix.php"
Code: function update_session_db()
{
$CI =& get_instance();
$CI->session->sess_write( TRUE );
}
[eluser]DeaD SouL[/eluser]
The word: "Thanks" <-- would make me feel happy, as if I did something :p
And the discussing would exchange our experiences .. !
[eluser]Drew J[/eluser]
Hi DeaD SouL,
Thanks for debugging and working on this! I would recommend filing a bug report over on BitBucket. You'll likely get a lot more feedback and testing that way.
Also, a little more information on what the offending code is that's causing the double database call would be very helpful. Personally (for me), I won't use code that is posted as "this solves the problem" without explaining what it's changing or what it fixed.
[eluser]DeaD SouL[/eluser]
Hi Drew J,
Right, I didn't mention that
aw, if you call any of these methods:
Code: $this->session->set_userdata();
$this->session->unset_userdata();
$this->session->set_flashdata();
$this->session->keep_flashdata();
The session library will update the database, because and will call Code: $this->session->sess_write()
. while the and will call which will call Code: $this->session->sess_write()
So, it means that the library will update the database every time you call any of the previous methods.
What I did: overwriting the by adding a new parameter $real_write with a default value as (bool) FALSE. So, when ever its being called by the CI_Session library, it does nothing. unless that parameter is (bool) TRUE. then, it would compare the old userdata which is already in the database with the new one, and if its matched, then we don't need to update the database, otherwise, we do update it only one time.
Regards ^^
[eluser]arvid.janson[/eluser]
Ok, so this is exactly what I asked for a couple of hours ago, so first off - thanks!
I did have a couple of problems though: did not get the session to save when using hooks (using the manual call DID work), and flashdata did not remove itself after reloading the controller. But, that was just my first impressions, so I will have to do some more testing. But a great start!
[eluser]WanWizard[/eluser]
I find the copyright notice in this code interesting, I'm quite sure I saw this exact solution mentioned before in these forums...
[eluser]DeaD SouL[/eluser]
WanWizard, I'm not that stupid to copy someone's code and claim its mine, specially if it was posted in the same website !! I'd rather just use it. Without sharing it
Regards
[eluser]DeaD SouL[/eluser]
If you're going to use redirect() the session won't be saved. to fix that:
Solution #1:
You'll have to call
Code: $this->session->sess_write( TRUE );
manually before calling redirect();
Solution #2:
Overwriting the redirect() function:
Create a new helper and name it "overwrite_helper.php" (or any name)
Code: application/helpers/overwrite_helper.php
then add this
Code: if ( ! function_exists('redirect'))
{
function redirect($uri = '', $method = 'location', $http_response_code = 302)
{
// forcing session to be saved
$CI =& get_instance();
$CI->session->sess_write( TRUE );
// codeigniter code
if ( ! preg_match('#^https?://#i', $uri))
{
$uri = site_url($uri);
}
switch($method)
{
case 'refresh' : header("Refresh:0;url=".$uri);
break;
default : header("Location: ".$uri, TRUE, $http_response_code);
break;
}
exit;
}
}
finally, you'll have to load the "overwrite_helper" before the "url_helper". in order to overwrite the original redirect() function in url_helper.php
Regards
|