I've found a lot of information on session serialization from PHP 5.4 and below, but not much for PHP 7. A couple years ago I was advised to use $_SESSION['var'] = serialize($object); when storing a class variable (class object { public $value; function __construct() { $this->value = new object2(); }}).
Today, I transitioned my code from one development environment to another. The new one seems to dislike = serialize($object); but appears to be fine with = $object.
A few weeks ago I ran across a few posts that mentioned PHP always serializes variables before putting them into sessions, and since this event (During today's search), I've found many of those same posts.
In PHP's documentation, I ran across a mention of session_register(), which apparently was supposed to be used to store objects in session variables... but it was apparently discontinued in 5.3.
Previous testing taught me that assigning session variables with class variables without serializing them first doesn't seem to keep track of class variables well, in particular situations like the above, where a variable is another class variable (object2).
The problems I had seemed hit-and-miss, and was in fact the reason I went looking for an answer and found serialize to begin with, and once I added serialize/unserialize (And no other changes), everything worked as intended.
So, here's my question: What's the 'real' way to handle object serialization for PHP sessions?
$_SESSION['var'] = serialize($object); causes a 500 error. $_SESSION['var'] = $object doesn't have an error, but due to previous issues, I'm not sure how well it works.
While on the topic, what's the best approach for unserializing? $object = unserialize($_SESSION['var']); is my current approach. It doesn't result in an error, but for the sake of possible minor optimization and future knowledge, might as well cover the base.
If my approach (Using serialize() and unserialize()) is correct, what setting(s) would cause serialize() to fail?
If there are any typos in any 'code' piece here, I wrote them inline in the post, so ignore them.
You can save objects in sessions like any other value. At the end of the script the session got serialized and saved anyway:
When PHP shuts down, it will automatically take the contents of the $_SESSION superglobal, serialize it, and send it for storage using the session save handler.
The "main" problem is that for loading the session again the class definition for the object you try to load must be available/loaded before using the session_start(); call. Other than that everything should work just fine.
For your 500 server error, you have to check the error message and/or error log what the problem is.
Related
I am trying to track down the source of a very serious bug in my application. I use a session variable to track the current logged in user which is derived from a database call (via PEAR, configured to return an associative array).
The variable is set up like this:
$_SESSION['u'] =& $db->getRow("SELECT * FROM user WHERE blah");
$u =& $_SESSION['u'];
global $u;
I then use $u in my code throughout to access this data as a shortcut.
This code has been running for years without issue but it seems recently that there have been occasions where users have found themselves logged in as other people. This seems to happen some time after they log in, and they migrate to become someone else. Clearly this is awful.
I was trying to debug but without a repeatable sequence of events this is pretty difficult.
My question is, do you think the setting of the session variable by reference to the database-derived array could be causing it? Could on a further page load, the memory location to which the session variable points be replaced with another user's data from a different session? Or could it be replaced with data from another database call that I am running in this user's session? I understand from reading up subsequently that this code should not be operated like this and have since removed the first & but I would like to have some idea if this could have actually been the problem. I've read that the memory location could get GC'd and turned to nothing, but this doesn't seem to have happened, it only seems to have been corrupted credibly with another user's details.
All help welcome.
I'm facing a problem which I don't know how to solve. Let's start explaining:
I've got the following class:
class MyClass{
function MyClass($mysql_dbcon){
$this->mysql_dbcon = $mysql_dbcon;
}
function execute(){
include("myscript.php");
}
}
myscript.php is an HTML template which uses Ajax to comunicate with other PHP scripts. Since these other PHP scripts aren't included directly in the class I'm not able to get the variable mysql_dbcon and use a different connection for each instance. Let's guess I've got the following instances:
$i1 = new Myclass($dbcon1);
$i1->execute();
$i2 = new Myclass($dbcon2);
$i2->execute();
Both instances will execute an Ajax function which call other processing scripts but these scripts can't access the instance's mysql_dbcon variable due to they're independent script executions.
How can I solve this? Is it correct to store the MySQL connection in $_SESSION so all the scripts can access it? Maybe this:
$_SESSION['instance_identifier'] = $this->mysql_dbcon;
What I want to achieve
I want to create a class in which the developer can set a MySQL connection and each instance can access to different databases so each instance will show data from different databases. The problem is the dynamic loading of that data.
Thanks in advance
You cannot solve it.
but this script can't access the instance's mysql_dbcon variable due to it's an independent script execution
Exactly. Period. You're starting an entirely different PHP script which shares absolutely nothing with the previous script. In fact, by the time the AJAX request is initiated by the browser, the "existing" MySQL connection will already be torn down and closed since the entire PHP script has already stopped.
"myscript.php" will simply have to open a new connection. You cannot serialise a connection into a session; it's not data which can be represented in a serialised form. This is exactly the same mechanism as any other two independent PHP files, the fact that include and AJAX are involved changes nothing.
Also see https://stackoverflow.com/a/13840431/476
Is it correct to store the MySQL connection in $_SESSION so all the scripts can access it?
No.
I'm facing a problem
You probably aren't, or at least it's not the problem you think it is. It's likely there is no reason for you to need to try to "share" the same database connection across the scripts, even if you could. If you provide more context about why you want to access the same connection, then further advice can be given. There is probably a better way to achieve what you want.
Perhaps you are trying to access one query's result set in different places, in which case the answer would not be trying to share a connection but probably to pass the result set itself.
Additional note about your syntax
class MyClass {
function MyClass($mysql_dbcon) {
Looks like you're using outdated PHP 4 syntax for your constructors. See the manual entry on constructors in PHP 5 for the modern standard.
It's also good practice to define the visibility of your class members using the public/protected/private keywords. See the manual entry on visibility.
Is there a way in PHP to use "out of session" variables, which would not be loaded/unloaded at every connexion, like in a Java server ?
Please excuse me for the lack of accuracy, I don't figure out how to write it in a proper way.
The main idea would be to have something like this :
<?php
...
// $variablesAlreadyLoaded is kind of "static" and shared between all PHP threads
// No need to initialize/load/instantiate it.
$myVar = $variablesAlreadyLoaded['aConstantValueForEveryone'];
...
?>
I already did things like this using shmop and other weird things, but if there is a "clean" way to do this in "pure PHP" without using caching systems (I think about APC, Redis...), nor database.
EDIT 1 :
Since people (thanks to them having spent time for me) are answering me the same way with sessions, I add a constraint I missed to write : no sessions please.
EDIT 2 :
It seems the only PHP native methods to do such a thing are shared memory (shmop) and named pipes. I would use a managed manner to access shared objects, with no mind of memory management (shared memory block size) nor system problems (pipes).
Then, I browsed the net for a PHP module/library which provides functions/methods to do that : I found nothing.
EDIT 3 :
After a few researches on the way pointed out by #KFO, it appears that the putenv / setenv are not made to deal with objects (and I would avoid serialization). Thus, it resolves the problem for short "things" such as strings or numbers but not for more large/comples objects.
Using the "env way" AND another method to deal with bigger objects would be uncoherent and add complexity to the code and maintenability.
EDIT 4 :
Found this : DBus (GREE Lab DBus), but I'm not having tools to test it at work. Has somebody tested it yet ?
I'm open to every suggestion.
Thanks
EDIT 5 ("ANSWER"):
Since DBus is not exactly what I'm looking for (needs to install a third-party module, with no "serious" application evidence), I'm now using Memcache which has already proven its reliability (following #PeterM comment, see below).
// First page
session_id('same_session_id_for_all');
session_start();
$_SESSION['aConstantValueForEveryone'] = 'My Content';
// Second page
session_id('same_session_id_for_all');
session_start();
echo $_SESSION['aConstantValueForEveryone'];
This works out of the box in PHP. Using the same session id (instead of an random user-uniqe string) to initialize the session for all visitors leads to a session which is the same for all users.
Is it really necessary to use session to achieve the goal or wouldn't it better to use constants?
There is no pure PHP way of sharing information across different
threads in PHP! Except for an "external"
file/database/servervariable/sessionfile solution.
Since some commentators pointed out, that there is serialize/unserialize functionality for Session data which might break data on the transport, there is a solution: In PHP the serialize and unserialize functionality serialize_handler can be configured as needed. See https://www.php.net/manual/session.configuration.php#ini.session.serialize-handler It might be also interesting to have a look at the magic class methods __sleep() and __wakeup() they define how a object behaves on a serialize or unserialize request. https://www.php.net/manual/language.oop5.magic.php#object.sleep ... Since PHP 5.1 there is also a predefined Serializable interface available: https://www.php.net/manual/class.serializable.php
You can declare a Variable in your .htaccess. For Example SetEnv APPLICATION_ENVIRONMENT production and access it in your application with the function getenv('APPLICATION_ENVIRONMENT')
Another solution is to wrap your variable in a "persistent data" class that will automatically restore its data content every time the php script is run.
Your class needs to to the following:
store content of variable into file in __destructor
load content of variable from file in __constructor
I prefer storing the file in JSON format so the content can be easily examined for debugging, but that is optional.
Be aware that some webservers will change the current working directory in the destructor, so you need to work with an absolute path.
I think you can use $_SESSION['aConstantValueForEveryone'] that you can read it on every page on same domain.
Consider to refer to it's manual.
QUESTIONS
Is the intermittent loss of session data likely to be due to a race condition?If no, what is likely to be the problem?
How can I prevent race conditions when writing/ reading joomla
session variables from an external php script?
DETAILS
I'm using
Joomla 2.5
PHP 5.4.3
apache 2.2.22
mysql 5.5.24
wampserver 2 on localhost.
My script is external to Joomla.
(cometchat version 3.0.1)
The script is using asynchronous ajax requests to get and set joomla variables multiple times. The data I'm storing in session is an array. Intermittently some of the array data goes missing. It seems to happen much more consistently once the user logs in and uses the script.
To be honest, I'm not really sure what the problem is, but I'm starting to think that my code is suffering from a race condition. I think Joomla might be trying to read the session information before it has finished being written or it just isn't getting set. The information that goes missing seems to be chosen at random and the loss of data occurs intermittently.
SCRIPT1
Script 1 is using an asynchronous ajax request to get/set Joomla session variables. This will be called multiple times. Due to design, script 1 cannot be called again until the ajax response is successful.
$.ajax(
{ cache:false,
url: 'script2.php',
data: { 'change': change},
dataType: 'json',
success: function(data)
{
//do something
}
});
This is a rough idea of the code I'm using in script 2 to access Joomla and get/set session data.
SCRIPT2
<?php
//some code omitted for brevity
$app = &JFactory::getApplication('site');/
$app->initialise();
$nottimedout=false;
$session = JFactory::getSession();
$jquizstart = date( 'Y-m-d H:i:s', time() ); //<<-- time of access
$nottimedout = $session->has('jtimedout');
if ($nottimedout==true)
{
$jqid = $session->get('jqid'); //<<-- get array from session
if (isset($_GET['change']))
{
$qnumber=$_GET['change'];
$firephp->log($qnumber, 'qnumber');
$jqid[$qnumber][3]=$jquizstart; //<<-- add time of access to array
$firephp->log($jqid[$qnumber][3], '$jqid[$qnumber][3]');
$session->set('jqid', $jqid); //<<-- store array in Joomla with updated data
}
else
{
$firephp->log('CHANGE NOT SET');
}
echo json_encode(
array("nottimedout" => $nottimedout)
);
}
else
{
//Do something
}
?>
TEST FOR RACE CONDITIONS
I thought the data might be getting overwritten, so I did a quick test using the code below. Each time I update the session array I create a new session variable with just the updated data.
$qnum[$qnumber]=$jquizstart;
$session->set('var'.$qnumber, $qnum);
In another script when the website completed updating I checked each of the individual sessions to see if they had been set.
//Test for race condition in Joomla session
for ($counter=0; $counter<=$totalnumber-1; $counter++)
{
$racecondition=$session->get('var'.$counter);
$firephp->log($racecondition, 'var'.$counter.'=');
}
RESULTS OF TEST
The array information missing from jqid was also missing from the corresponding individual session (the session with just the updated data), so it seems that it isn't a problem of the data being overwritten. I'm not sure if this disproves a race condition though.
Any suggestions as to what you think might be going on and how to fix this would be most welcome.
EDIT
Even generalized answers on how to prevent race conditions in Joomla are welcome. Thanks
EDIT2
I'm starting to wonder if it isn't a problem with php5.4 and Joomla. I've heard that they don't play well together and I can't remember having this problem before I updated from php5.3. I could be wrong though.
EDIT3
I'm at wits end. I installed the website on a different server with php 5.3.10. I tried it ten or more times as an unlogged-in user. There was no loss of data. I then logged into Joomla and data was lost nearly every time I accessed the page. If only I didn't have to use Joomla sessions! GRRRrrrrr
EDIT4
Getting desperate now and just trying anything. JRequest didn't work, although I should be using it anyway.
Since the problem occurs more frequently when logged in, I figured it must be because there is a lot more content stored in the session than when the user is a guest. Jqid is a large array, so rather than updating it all the time, I tried making several smaller arrays and updating each when appropriate. It had absolutely no effect. Again, I probably should be doing this anyway.
EDIT5B
While trying to find a makeshift solution, I tried to test whether the session had successfully updated or not (this was done in the same script that updated the session).
Here is the code I used to check jstart.
//jstart updated
$session->close('jstart');
$try_again_session = JFactory::getSession();
$newjstart=$try_again_session->get('jstart');
$firephp->log($newjstart[$qnumber], 'confirm_jstart_set=');
The interesting thing I found was that, jstart contained the updated information during the check, but at completion it was missing.
I'm not really sure what that means, but I guess if we treated JFactory::getSession() as a variable then the variable was updated only for this script (kind of like a local variable?), the database value for JFactory::getSession() for whatever reason was not written to the database. Thus later, when this script fired again it retrieved the old value of JFactory::getSession() that was saved in the database.
Still have no idea what is causing the session not to be written to the database.
While I haven't found a solution to the problem, I have found a work around. It's not a good one, but it works. Whenever the session variable is updated store the information in a cookie as a backup. Later in the script, check whether there is missing information and update from the cookie as necessary.
I'd rather not do it this way, but it doesn't look like I have a choice.
Finally it looks like I've found a solution!
I happened to check the error logs in apache_error.log (wamp/logs/apache_error.log) There were a tonne of session related errors such as
PHP Warning: session_start(): Cannot send session cache limiter - headers already sent (output started at Z:\\libraries\\joomla\\session\\session.php:96) in Z:\\libraries\\joomla\\session\\session.php on line 532, referer: http://localhost/cq
PHP Warning: Cannot modify header information - headers already sent by (output started at Z:\\libraries\\joomla\\session\\session.php:96) in Z:\\cometchat\\cometchatcss.php on line 68, referer: http://localhost/cq
PHP Warning: session_destroy(): Session object destruction failed in Z:\\libraries\\joomla\\session\\session.php on line 96, referer: http://localhost/cq
After turning off cometchat and rebooting the server I found that the intermittent session data loss seemed to stop. After the change there were no more errors appearing under apache_error.log
Since the problem is intermittent I’m not 100% sure that it's solved, but I’m confident enough that I’ve written this up as the solution.
I was using
cometchat version 3.0.1
I'm gonna keep testing it. If the solution holds up I will try an updated version of cometchat and post the results.
UPDATE: It does seem to be related to cometchat. I've installed the latest version 4.6.0, but the session loss is still occurring. I can work around this by excluding cometchat on the pages containing my script.
USEFUL INFO:
Just in case someone else finds themselves struggling with Joomla session-I found this website quite useful http://tutsforu.com/joomla-module-tutorial/8-joomla-tutorial/75-using-session-in-joomla.html
Specifically
$session->getId();
$session->get('session.counter');
$session->isNew();
$session->getName();
$session->getState();
$session->getExpire();
I'm working on application built years ago, that has recently stopped working correctly. Old programmer said, that he might be accessing $_GET or $_POST variables without reading them from $_GET[] array, but through the register_globals
I want to ask: What are different ways to access $_GET variables without using $_GET[] array (e.g. direct ways?) and, if known, how can I check if this application uses any of them?
Thank you in advance
EDIT: The other ways I remembered was register_globals, not magic_quotes. Also, I do not wish to use it, but rather detect if it was used and in latest server update deprecated (what could explain why app stopped working correctly)
EDIT: My english is horrible today. As I explained in one of answers: I need to check, whether original programmer used some obscure and/or deprecated method of getting variables from query string to PHP, so the values application now works with are wrong/not initialized
IMPORTANT EDIT: import_request_variables is off the table, it isn't used. All $_ arrays are off the table too, because latest update wouldn't broke them (=>they still work). How can I detect what variables are initialized with register_globals?
YET ANOTHER EDIT: I found this:
foreach ($_POST as $k => $v) {
eval("\$".$k." = '".$v."';");
}
foreach ($_GET as $k => $v) {
eval("\$".$k." = '".$v."';");
}
Could it have been broken by one of latest updates (max. 1 week ago)?
You mean through Register Globals and not Magic Quotes... BTW Register Globals is pure evil never use them (and they are deprecated as of PHP 5.3.0)!
Edit: If you want to check if the application used Register Globals, try to search for $_GET values as variables. For example for index.php?id=123 try to look for $id in the PHP code. If you find it this does not mean that the script uses Register Globals but if $id comes from nowhere and is never initialized/setted it's a good (bad!) sign that the app uses Register Globals...
$_SERVER["QUERY_STRING"] will give you the raw GET string.
That said, this sounds like a problem that should be fixed at its root, not by using a different variable.
If magic quotes are an issue, do a check for whether they are enabled, and deal with the incoming data accordingly.
The Disabling Magic Quotes page in the PHP manual shows a way to "fix" the incoming data depending on whether the functionality is activated or not. It's not very efficient for huge amounts of data, but should do in an everyday task.
You also have $_REQUEST, magic_quotes (deprecated) would only influence the content of the variables, not the means of capturing them.
Also see import_request_variables, the original coder may have used it to grab the contents of a GET variable and insert it into another variable which is then subsequently being referenced.
Register Globals is a horrible feature that older PHP programs often rely on.
With register globals switched on, PHP will look at the GET and POST variables and "promote" them to normal variable names - eg $_GET['myvar'] would be accessible in the code as $myvar.
Among other issues, this makes the program very easy for hackers to break simply by guessing what other variable names the programmer may have used. The register globals feature has therefore been turned off by default for a long time, is now officially deprecated, and will be removed entirely in a future version.
Because the variables used this way are referenced in a way that is indistinguishable from normal variables, it means that trying to update old code that uses register globals can be very difficult. It does depend a lot on how well written the code is.
The problem is probably PHP's register_globals. This option makes $_GET['some_var'] or their equivalent $_POST version available as $some_var automatically. This is deprecated and you should definitely not use it, but the other programmer might have used them in that application.