multi dimensional arrays in session

Hi everyone,

I’ve got a problem with sessions and I’m not sure if this just won’t work or I’m doing something wrong

I have the following code


		$session = Yii::app()->session['foo']['bar'] = 3;




which gives the error

Indirect modification of overloaded element of CHttpSession has no eff

when I try




		$session = Yii::app()->session;

		$session['foo']['bar'] = 3;



I get the same error

is there any way to make this work?

from the docs:

session property read-only

public CHttpSession getSession()

the session component

i think the correct aproach is


$session=new CHttpSession;

  $session->open();

  $session['foo']['bar']=$value;

if I’m not mistaken this approach is exactly the same as




                $session = Yii::app()->session;

                $session['foo']['bar'] = 3;



I had the same issue.

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.

Currently my solution is this:




$session = Yii::app()->session;

$temp = $session['foo'];

$temp['bar'] = 3;

$session['foo'] = $temp;



This is not nice and I hope somebody can tell me a better solution.

Of course you could always access the $_SESSION variable directly

@sluderitz

what you suggested is what I did for now. Of course this is not very satisfiying but I guess there is no other way atm

This workaround seems very inevitable, but




Yii::app()->session['foo'] = array('bar' => 3);



might look prettier for you.

It does but it doesn’t really solve anything

the problem does not appear when initially setting the foo property. but when you wanna change one something

suppose something like this




Yii::app()->session['foo'] = array('bar1' => 3, 'bar2'=>4);



now I wanna change bar2




$data = Yii::app()->session['foo'];

$data['bar2'] = 'new value';

Yii::app()->session['foo'] = $data;



this is a bit inconvenient.

I’m wondering if it would make sense to extend the session component with a method that takes care of this

for example




Yii::app()->session->set('foo.bar2','value');



what do you think pestaa?

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.

For some reason, using $_SESSION instead of $session seems to work:




$session = Yii::app()->session;


// Set session var

$_SESSION['foo']['bar'] = 'result';


// Change session var

$_SESSION['foo']['bar'] = 'result 2';



I am using CDbHttpSession and it holds the session in the database.


		Yii::app()->session->add('test',array('bar1' => 3, 'bar2'=>4));

		print_r(Yii::app()->session->get('test'));

Results are:


Array

(

    [bar1] => 3

    [bar2] => 4

)



This doesn’t address the problem though. We want to be able to access and set variable in one line of code, and


Yii::app()->session->add('test')['bar1']

will not work, neither will


Yii::app()->session->get('test')['bar1']

.

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);

	}

}



hi,

ill do something like that




//init session var

if(!isset(Yii::app()->session['myvar'])){

  Yii::app()->session['myvar'] = serialize(array());

}

//add new item to session myvar

$myvarArray = unserialize(Yii::app()->session['myvar']);

$myvarArray[] = 'new value';

Yii::app()->session['myvar'] = serialize($myvarArray);


//get session myvar as array

$myvarArray = unserialize(Yii::app()->session['myvar']);



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']);



Not really elegant, but works quite well for me.

I had some similar issues that led me here.

Not only can you not use multidimensional arrays, but since everything is converted to string, you can’t use boolean or integer either.

I ended up having to check the session var I thought was a bool as a string instead. This tripped me up for a while.


<?php echo (Yii::app()->session['myVar']=='true')?'Foo':'Bar'; ?>

(note the quotes around ‘true’)

serialize sounds like a good workaround to the array issue.