Skip to content
This repository has been archived by the owner on Jan 31, 2020. It is now read-only.

Don’t pass Traversable as ArrayUtils::merge parameter #93

Merged
merged 5 commits into from
Nov 28, 2017
Merged

Don’t pass Traversable as ArrayUtils::merge parameter #93

merged 5 commits into from
Nov 28, 2017

Conversation

gszy
Copy link
Contributor

@gszy gszy commented Nov 28, 2017

SessionManager::start() retrieves data from $_SESSION and passes it to ArrayUtils::merge(). The problem is that the retrieved data can be either:

  • plain array — that’s not a problem 🙂;
  • or Traversable (specifically, it should be instance of Zend\Session\Storage\StorageInterface), but such type is not acceptable as ArrayUtils::merge() parameter 😞.

The point of this PR is to convert such Traversable to a plain array, by calling toArray() (defined in the mentioned StorageInterface) on it. I’ve created also an alternative PR in zend-stdlib (zendframework/zend-stdlib#86) that’d let ArrayUtils::merge() accept iterable parameters.

If session data retrieved from $_SESSION (in SessionManager::start) was
instance of Traversable, it would be incorrectly passed to
ArrayUtils::merge, as that method accepts only plain arrays.
@Ocramius
Copy link
Member

Needs tests

@gszy
Copy link
Contributor Author

gszy commented Nov 28, 2017

Would gscscnd/zend-session@9c5a1e75237a9997375cc9e9b821fe2ca8fe5c55 be enough or more tests are needed?

$oldSessionData = $oldSessionData->toArray();
}

if (! empty($oldSessionData) && is_array($oldSessionData)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can have here just if ($oldSessionData && is_array($oldSessionData)) {

@@ -130,9 +130,13 @@ public function start($preserveStorage = false)

session_start();

if ($oldSessionData instanceof \Traversable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this condition is removed. What then in case when $oldSessionData instanceof \Traversable (=== true) ?

Copy link
Contributor Author

@gszy gszy Nov 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I ask what should be done in such case? Is there a PHP (or ZF) function that converts Traversable to array or should I write something like:

if ($oldSessionData instanceof \Traversable) {
    $oldSessionDataArray = [];

    foreach ($oldSessionData as $oldSessionDataKey => $oldSessionDataValue) {
        $oldSessionDataArray[$oldSessionDataKey] = $oldSessionDataValue;
    }

    $oldSessionData = $oldSessionDataArray;
}

Update: iterator_to_array would probably do the job.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I’ve updated the patch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing to note: iterator_to_array is not recursive

@gscscnd Can you provide an example when $_SESSION is not an array?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is, before being passed to ArrayUtils::merge? I’ve encountered #10 when implementing “remember me” functionality — I had to retrieve SessionManager and I wasn’t able to do it until I’ve provided the following (minimal) configuration:

return [
    'session_config' => [],
    'session_storage' => ['type' => Zend\Session\SessionStorage::class],
];

What happened is probably the same as described in #10. Yet another workaround: 8trust/zend-session@c5e523a

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gscscnd thanks, now I see the issue, basically it's not possible to use other session storage than (session)array(storage), right?

Copy link
Contributor Author

@gszy gszy Nov 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven’t tried all storage variants, but it seems SessionManager is not fully compatible with SessionStorage (and perhaps not only this), so either of them should be somehow fixed 😕

Update: SessionArrayStorage seems to work for me. But I don’t think it’s an acceptable solution — while the docs say that SessionStorage may be incompatible with 3rd party libraries, it should be compatible with zend-stdlib, shouldn’t it?

if ($oldSessionData instanceof \Traversable
|| (! empty($oldSessionData) && is_array($oldSessionData))
) {
if ($oldSessionData instanceof Storage\StorageInterface) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I don't understand something....

interface StorageInterface extends Traversable, ArrayAccess, Serializable, Countable

Shouldn't we just have here: if ($oldSessionData instanceof \Traversable) { instead?

Copy link
Contributor Author

@gszy gszy Nov 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toArray is defined in StorageInterface and not in the other interfaces.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, see my above comments #93 (comment)

@gszy
Copy link
Contributor Author

gszy commented Nov 28, 2017

Sorry, I didn’t notice this PR is aiming to do the same as #57

gszy added 2 commits November 28, 2017 11:35
$_SESSION is populated with StorageInterface (and then Traversable).
After start(), $_SESSION is checked whether it is an array and if still
contains initial data.
$oldSessionData = iterator_to_array($oldSessionData);
}

if (! empty($oldSessionData) && is_array($oldSessionData)) {
$_SESSION = ArrayUtils::merge($oldSessionData, $_SESSION, true);
Copy link
Member

@michalbundyra michalbundyra Nov 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How it's going to work? As I can see merge has type hint on param 1 and 2 (array).
https://github.com/zendframework/zend-stdlib/blob/43d209995f054fa62557652192c4943625b4145a/src/ArrayUtils.php#L269
Your fix is in case when $_SESSION is NOT an array but above you pass (not an array) as the 2nd param?

Copy link
Contributor Author

@gszy gszy Nov 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand the code correctly, the new $_SESSION is passed to merge() after session_start() was called. Have a look at the original code — it’s assumed (I guess so, because there’s no validation) that the new $_SESSION is something acceptable by merge. session_start() somehow modifies the $_SESSION superglobal.

I should move that session_start() call below my new code to make it easier to understand, shouldn’t I?

Update: moved.

Moved code that conditionally converts data fetched from $_SESSION to
plain array a few lines up for better readability.
@weierophinney weierophinney merged commit d33a0c9 into zendframework:master Nov 28, 2017
weierophinney added a commit that referenced this pull request Nov 28, 2017
…bility

Don’t pass Traversable as ArrayUtils::merge parameter

Conflicts:
	src/SessionManager.php
weierophinney added a commit that referenced this pull request Nov 28, 2017
weierophinney added a commit that referenced this pull request Nov 28, 2017
weierophinney added a commit that referenced this pull request Nov 28, 2017
@weierophinney
Copy link
Member

Thanks, @gscscnd; I have merged your changes along with #57 to provide a comprehensive fix.

@gszy
Copy link
Contributor Author

gszy commented Nov 28, 2017

@weierophinney, I’m not entirely sure if https://github.com/zendframework/zend-session/blob/master/src/SessionManager.php#L142-L144 is completely correct:

Update: #96 apparently aims to solve these problems.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants