CHttpSession implements the ArrayAccess interface,
so when you do $session[‘foo’] = $value this function is actually called:
public function offsetSet($offset,$item) {
$_SESSION[$offset]=$item;
}
When you do $value = $session[‘foo’] then this funciton is called:
public function offsetGet($offset) {
return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null;
}
So I think when you do $session[‘foo’][‘bar’] = $value then first offsetGet is called and $_SESSION[‘foo’] is returned as a copy (not a reference) and then [‘bar’] is set on that copied array.
This is how I understood the bug reports on php.net. There where suggestions to have offsetGet return a reference. Older versions of PHP did not seem to behave like this and I am not sure how the newest PHP version behaves.
Unfortunately, this is a limit by PHP. We can’t think of a good way to solve it. Your proposal of using ‘foo.bar2’ is not very sounding because it is very likely someone may use ‘foo.bar2’ as a 1D key rather than 2D.
Having several Yii apps sharing the same session, I REALLY needed a way of dividing the variables as several developers are working on the project and it would be simply impossible to make sure one wasn’t overwriting data of another.
I decided to use objects instead of arrays though as this will allow greater flexibility in the future. Additionally, it would be possible to use an object implementing ArrayAccess if array style access becomes important.
Now, for the code. Here is my modified session class :
class MySession extends CHttpSession
{
public function setInstance($sessionName, $className='stdClass')
{
if (!isset($_SESSION[$sessionName])) {
$_SESSION[$sessionName] = new $className();
}
}
}
When I need to add a division, I declare it like so :
class MyWebUser extends CWebUser
{
public function afterLogin($fromCookie)
{
Yii::app()->session->setInstance('secretData');
Yii::app()->session['secretData']->secret = 'superSecretThing';
[....]
}
}
… though of course this could be anywhere. The important thing is that it only needs to be done once.
Once the object has been initialized by the ‘setInstance’ method, it can be accessed and modified from any other parts of the code without having to set its instance again.
class UserController extends CController
{
public function actionTest1()
{
Yii::app()->session['secretData']->secret = 'ohNoesYouFoundMySecret';
}
public function actionTest2()
{
var_dump(Yii::app()->session['secretData']->secret);
}
}
I had to fiddle a lot with this issue… my problem was that i had to handle multidimensional arrays in various depths.
The solution was quite simple at last… i put all session data in a variable as an array, did all the manipulations on that array and then just wrote back the changed part of the array to the session via "add()".
$session = Yii::app()->session->toArray();
// the data to put into the session
foreach($data as $key => $value) {
$session['dimension1']['dimension2']['dimension3'][$key] = $value;
}
// some more data on a different level
$session['dimension1']['dimension2']['foo'] = 'bar';
Yii::app()->session->add('dimension1', $session['dimension1']);