-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[HttpFoundation] Refactor session handling and flash messages #2714
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
$memcacheOptions['serverpool'] = array('host' => '127.0.0.1', 'port' => 11211, 'timeout' => 1, 'persistent' => false, 'weight' => 1); | ||
} | ||
$memcacheOptions['expiretime'] = isset($memcacheOptions['expiretime']) ? (int)$memcacheOptions['expiretime'] : 86400; | ||
$this->memcached->setOption(\Memcached::OPT_PREFIX_KEY, isset($memcacheOptions['prefix']) ? $memcachedOption['prefix'] : 'sf2s'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You seem to have got the Memcache/d drivers confused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Your change will break the functionnal test client. As the different requests are done by the same PHP process (launched by PHPUnit) so you will not save the session. This is one of the reason why there was different sort of session, using the native session lifecycle or a session stuff not based on |
@stof You have to separate the idea of session management (things like how we deal with attributes and flash messages) with the save handlers to store and retrieve the session data (which PHP requires), that's what I've been trying to explain in the other ticket and if you read the docblocks in |
Please see #2607 (comment) in particular regarding the session lifecycle. Please note the functional tests pass. It would appear there is no need to actually use |
Suggestion for a simple public interface for flash messages for the |
"The clearing of flash messages is now done when they are used as opposed to automatically." I think we discussed this a while ago and back then I suggested doing exactly that. I don't remember why we didn't do it in the end. One "issue" is of course that there is now the risk for messages to stick around causing the session to grow or to eventually be shown out of the context. |
@@ -43,14 +43,14 @@ class TestSessionListenerTest extends \PHPUnit_Framework_TestCase | |||
|
|||
public function testShouldSaveMasterRequestSession() | |||
{ | |||
$this->sessionMustBeSaved(); | |||
// $this->sessionMustBeSaved(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason to keep the commented out code around?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I will clean that stuff up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments were removed.
It's actually quite the opposite. With the old system you could potentially lose messages if an unexpected error occurs and you have to reload the page. This way, messages will only be cleared after they have been displayed. In any case, the old system is unworkable because it was relying on trying to fake php session handling. There are a number of bugs listed currently on the tracker that show the old session management is buggy and I've tried to explain this as much as possible in the #2607 ticket and in comments in the new code. For example, if you are using PHP's native save handlers in the old system then chances are you will either lose attributes / flashes entirely. The correct way to handle flash messages is to clear them once they have actually been displayed and there is no other practical way to do this reliably when adhering to PHP's session save_handler workflow. In aeab19eb7fb0cf546956e6f38432cba1516e3553 [which is not merged into this PR at the moment], I added a public interface to some of the flash messages API for common tasks. You can see that |
Its not really the "opposite" your argument doesn't invalidate the issues I explained, but like I said I also propose your solution a while back because I also think that otherwise its really tricky to not loose flash message accidentally. |
It also brings another advantage. I don't know where the whole idea of validating a form submission and immediately redirecting to a response page came from, but IMO it's a very very wasteful workflow because basically you require your application to process two pageloads: process and display. There is another workflow where the controller validates the form submission and immediately displays the relevant view. The old flash system pretty much wants us to redirect, but the new system allows us the flexibility to do either according to our own preferences. |
There are two reasons:
|
@@ -33,6 +33,10 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c | |||
* [BC BREAK] assets_base_urls and base_urls merging strategy has changed | |||
* changed the default profiler storage to use the filesystem instead of SQLite | |||
* added support for placeholders in route defaults and requirements (replaced by the value set in the service container) | |||
* [BC BREAK] changed session.xml session.storage.native to session.storage.native_file | |||
* added new session storage drivers to session.xml: | |||
session.storage.native_ememcache, session.storage.native_memcached, session.storage.native_sqlite, session.storage.null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo here. It's native_memcache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Session fixation solution [1] popped out on php-internals. Does anybody else share the notion of it would be nice to have this in this rewrite? [1] - https://wiki.php.net/rfc/strict_sessions#current_solution |
…sistent. Changed session.storage.native to session.storage.nativefile to be consistent with naming standards. Changed SecurityBundle/Tests/Functional/app/config/framework.yml to use session.storage.nativefile by default (works perfectly for function testing and makes session.storage.filesystem redundant). [HttpFoundation] Fixed FilesystemSessionStorage sessionWrite() not saving data. [HttpFoundation] Fixed mixed up prefix implementation between memcache and memcached.
…not required for functional session tests. Use NativeFileSessionStorage instead.
Adds flashGet(), flashAdd() which is enough for every day use.
…n interface. Refactored code for interface changes. Refactored SessionTests into more unit-like tests. Doc-blocks and private properties.
…ge more straightforward. Making use of dependencies required by the Session object using setter injection.
…equired. Required if Response object is not being used because there will be no HeaderBag
…ag injection back to the storage constructors. removed *bag from method names.
…sion testing with persistence to the file system. This commit introduces save() in a way that does not interfere with the PHP session workflow. Under normal circumstances this method doesn't need to be invoked at all and if it is, it will force the session to write using session_write_close(). When under test, the mock session storage driver can persist the session data as and if required.
In order to make the PR more readable I've now squashed the commits and rebased to remove the various iterations and make a coherent story. When I tried to adjust for the new branch, github opened a new PR number #2853, so I'm closing this one. |
Commits ------- cb6fdb1 [HttpFoundation] removed Session::close() c59d880 Docblocks. b8df162 Correct instanceof condition. 8a01dd5 renamed getFlashes() to getFlashBag() to avoid clashes 282d3ae updated CHANGELOG for 2.1 0f6c50a [HttpFoundation] added some method for a better BC 146a502 [FrameworkBundle] added some service aliases to avoid some BC breaks 93d81a1 [HttpFoundation] removed configuration for session storages in session.xml as we cannot provide a way to configure them (like before this PR anyway) 74ccf70 reverted 5b7ef11 (Simplify session storage class names now we have a separate namespace for sessions) 91f4f8a [HttpFoundation] changed default flash bag to auto-expires to keep BC 0494250 removed unused use statements 7878a0a [HttpFoundation] renamed pop() to all() and getAll() to all() 0d2745f [HttpFoundation] Remove constants from FlashBagInterface dad60ef [HttpFoundation] Add back get defaults and small clean-up. 5b7ef11 [HttpFoundation] Simplify session storage class names now we have a separate namespace for sessions. 27530cb [HttpFoundation] Moved session related classes to own sub-namespace. 4683915 [HttpFoundation] Free bags from session storage and move classes to their own namespaces. d64939a [DoctrineBridge] Refactored driver for changed interface. f9951a3 Fixed formatting. 398acc9 [HttpFoundation] Reworked flashes to maintain same behaviour as in Symfony 2.0 f98f9ae [HttpFoundation] Refactor for DRY code. 9dd4dbe Documentation, changelogs and coding standards. 1ed6ee3 [DoctribeBridge][SecurityBundle][WebProfiler] Refactor code for HttpFoundation changes. 7aaf024 [FrameworkBundle] Refactored code for changes to HttpFoundation component. 669bc96 [HttpFoundation] Added pure Memcache, Memcached and Null storage drivers. e185c8d [HttpFoundation] Refactored component for session workflow. 85b5c43 [HttpFoundation] Added drivers for PHP native session save handlers, files, sqlite, memcache and memcached. 57ef984 [HttpFoundation] Added unit and functional testing session storage objects. 3a263dc [HttpFoundation] Introduced session storage base class and interfaces. c969423 [HttpFoundation] Added FlashBagInterface and concrete implementation. 39288bc [HttpFoundation] Added AttributesInterface and AttributesBagInterface and concrete implementations. Discussion ---------- [2.1][HttpFoundation] Refactor session handling and flash messages Bug fix: yes Feature addition: yes Backwards compatibility break: yes Symfony2 tests pass: yes Fixes the following tickets: #2607, #2591, #2717, #2773 References the following tickets: #2592, #2543, #2541, #2510, #2714, #2684 Todo: - __Introduction__ This extensive PR is a refactor with minimal BC breaks of the `[HttpFoundation]` component's session management which fixes several issues in the current implementation. This PR includes all necessary changes to other bundles and components is documented in the `CHANGELOG-2.1` and `UPGRADING-2.1`. __Summary of Changes__ __Session:__ - Session object now implements `SessionInterface` __Attributes:__ - Attributes now handled by `AttributeBagInterface` - Added two AttributeBag implementations: `AttributeBag` replicates the current Symfony2 attributes behaviour, and the second, `NamespacedAttributeBag` introduces structured namespaced representation using '/' in the key. Both are BC. `FrameworkBundle` defaults to the old behaviour. __Flash messages:__ - Flash messages now handled by `FlashBagInterface` - Introduced `FlashBag` which changes the way flash messages expire, they now expire on use rather than automatically, useful for ESI. - Introduced `AutoExpireFlashBag` (default) which replicates the old automatic expiry behaviour of flash messages. __Session Storage:__ - Introduced a base object, `AbstractSessionStorage` for session storage drivers - Introduced a `SessionSaveHandlerInterface` when using custom session save handlers - Introduced a `NullSessionStorage` driver which allows for unsaved sessions - Introduced new session storage drivers for Memcache and Memcached - Introduced new session storage drivers for PHP's native SQLite, Memcache and Memcached support __General:__ - Fixed bugs where session attributes are not saved and all cases where flash messages would get lost - Wrote new tests, refactored related existing tests and increased test coverage extensively. __Rationale/Details__ I'll explain more detail in the following sections. __Unit Tests__ All unit and functional tests pass. __Note on Functional Testing__ I've introduced `MockFileSessionStorage` which replaces `FilesystemSessionStorage` to emulate a PHP session for functional testing. Essentially the same methodology of functional testing has been maintained but without interrupting the other session storage drivers interaction with real PHP sessions. The service is now called `session.storage.mock_file`. __Session Workflow__ PHP sessions follow a specific workflow which is not being followed by the current session management implementation and is responsible for some unpredictable bugs and behaviours. Basically, PHP session workflow is as follows: `open`, `read`, `write`, `close`. In between these can occur, `destroy` and `garbage collection`. These actions are handled by `session save handlers` and one is always registered in all cases. By default, the `files` save handler (internally to PHP) is registered by PHP at the start of code execution. PHP offers the possibility to change the save handler to another internal type, for example one provided by a PHP extension (think SQLite, Memcache etc), or you can register a user type and set your own handlers. However __in all cases__ PHP requires the handlers. The handlers are called when the following things occur: - `open` and `read` when `session_start()` or the session autostarts when PHP outputs some display - `destroy` when `session_regenerate_id(true)` is called - `write` and `close` when PHP shuts down or when `session_write_close()` is called - `garbage collection` is called randomly according to configurable probability The next very important aspect of this PR is that `$_SESSION` plays an important part in this workflow because the contents of the $_SESSION is populated __after__ the `read` handler returns the previously saved serialised session data. The `write` handler is sent the serialised `$_SESSION` contents for save. Please note the serialisation is a different format to `serialize()`. For this reason, any session implementation cannot get rid of using `$_SESSION`. I wrote more details on this issue [here](#2607 (comment)) In order to make writing session storage drivers simple, I created a light base class `AbstractSessionStorage` and the `SessionSaveHandlerInterface` which allows you to quickly write native and custom save handler drivers. __Flash Messages [BC BREAK]__ Flash messages currently allow representation of a single message per `$name`. Fabien designed the original system so that `$name` was equivalent to flash message type. The current PR changes the fact that Flash messages expire explicitly when retrieved for display to the user as opposed to immediately on the next page load. The last issue fixes potential cases when flash messages are lost due to an unexpected intervening page-load (an error for example). The API `get()` has a flag which allows you to override the `clear()` action. __Flash message translation__ This PR does not cover translation of flash messages because messages should be translated before calling the flash message API. This is because flash messages are used to present messages to the user after a specific action, and in any case, immediately on the next page load. Since we know the locale of the request in every case we can translate the message before storing. Secondly, translation is simply a string manipulation. Translation API calls should always have the raw untranslated string present because it allows for extraction of translation catalogs. For a complete answer see my answer [here](#2543 (comment)) __Session attribute and structured namespacing__ __This has been implemented without changing the current default behaviour__ but details are below for the alternative: Attributes are currently stored in a flat array which limits the potential of session attributes: Here are some examples to see why this 'structured namespace' methodology is extremely convenient over using a flat system. Let's look at an example with csrf tokens. Let's say we have multiple csrftokens stored by form ID (allowing multiple forms on the page and tabbed browsing). If we're using a flat system, you might have 'tokens' => array('a' => 'a6c1e0b6', 'b' => 'f4a7b1f3') With a flat system when you get the key `tokens`, you will get back an array, so now you have to analyse the array. So if you simply want to add another token, you have to follow three steps: get the session attribute `tokens`, have to add to the array, and lastly set the entire array back to the session. $tokens = $session->get('tokens'); $tokens['c'] = $value; $session->set('tokens', $tokens); Doable, but you can see it's pretty long winded. With structured namespacing you can simply do: $session->set('c', $value, '/tokens'); There are several ways to implement this, either with an additional `$namespace` argument, or by treating a character in the `$key` as a namespacer. `NamespacedAttributeBag` treats `/` as a namespacer so you can represent `user.tokens/a` for example. The namespace character is configurable in `NamespacedAttributeBag`. --------------------------------------------------------------------------- by marijn at 2011-12-18T15:43:17Z I haven't read the code yet but the description from this PR and your line of thought seem very well structured. Seems like a big +1 for me. --------------------------------------------------------------------------- by lsmith77 at 2011-12-19T16:01:19Z @deviantintegral could you look over this to see if it really addresses everything you wanted with PR #2510 ? --------------------------------------------------------------------------- by deviantintegral at 2011-12-24T20:12:03Z I've read through the documentation and upgrade notes, and I can't see anything that's obviously missing from #2510. Being able to support multiple flashes per type is the most important, and the API looks reasonable to me. Drupal does support supressing repeat messages, but that can easily be implemented in our code unless there's a compelling case for it to be a part of Symfony. I wonder if PHP memcache support is required in Symfony given the availability of memcached. I'm not familiar with how other parts of Symfony handle it, but there is often quite a bit of confusion between the two PHP extensions. It could be simpler to remove one, or add a bit of info describing or linking to why there are two nearly identical classes. Is it possible to make one class inherit from the other (memcached is a child of memcache)? --------------------------------------------------------------------------- by Fristi at 2011-12-24T20:29:46Z Interesting, maybe add: session events as I did with the current impl: https://github.com/Fristi/SessionBundle --------------------------------------------------------------------------- by drak at 2011-12-25T00:50:03Z @deviantintegral - I agree about the confusion between memcache and memcached but actually, it is necessary to support both because `memcached` is not available everywhere. For example on Debian Lenny and RHEL/CentOS 5, only memcache is available by default. This would preclude a massive amount of shared hosting environments. Also, it is not possible to inherit one from the other, they are completely different drivers. @Fristi - I also thought about the events, but they do not belong as part of the standalone component as this would create a coupling to the event dispatcher. The way you have done it, ie, in a bundle is the right way to achieve it. --------------------------------------------------------------------------- by matheo at 2011-12-25T01:12:00Z Impressive work, looks like a big improvement and deserves a big +1 --------------------------------------------------------------------------- by datiecher at 2011-12-26T11:57:12Z Took some time to grok all the changes in this PR but all in all it is a keeper. Specially the new flash message API, it's really nicer to work with it then the previous one. Nicely done @Drak! --------------------------------------------------------------------------- by lsmith77 at 2012-01-02T15:00:00Z @fabpot did you have time to review this yet? with all the work @Drak has done its important that he gets some feedback soon. its clear this PR breaks BC in ways we never wanted to allow. but i think this PR also clearly explains why its necessary none the less. --------------------------------------------------------------------------- by drak at 2012-01-02T15:41:53Z @fabpot - I have removed the WIP status from this PR now and rebased against the current master branch. --------------------------------------------------------------------------- by Tobion at 2012-01-07T07:13:38Z From what I read from the IRC chat logs, the main concern of @fabpot is whether we really need multiple flash messages per type. I'm in favor of this PR and just want to add one point to this discussion. At the moment you can add multiple flash messages of different type/category/identifier. For example you can specify one error message and one info message after an operation. I think most agree that this can be usefull. But then it makes semantically no sense that you currently cannot add 2 info messages. This approach feels a bit half-done. So I think this PR eliminates this paradox. --------------------------------------------------------------------------- by drak at 2012-01-07T09:11:07Z For reference there is also a discussion started by @lsmith77 on the mailing list at https://groups.google.com/forum/#!topic/symfony-devs/cy4wokD0mQI --------------------------------------------------------------------------- by dlsniper at 2012-01-07T16:02:15Z @Drak I could also add the next scenario that I currently have to live with, in addition to @lsmith77 ones. I had this issue while working on our shopping cart implementation for a customer where the customer wanted to show the unavailability of the items as different lines in the 'flash-error' section of the cart. We had to set an array as the 'flash message' in order to display that information. So in this case for example having the flash messages types as array would actually make more sense that sending an array to the flasher. Plus the the other issue we had was that we also wanted to add another error in the message but we had to do a check to see if the flash message is an array already or we need to make it an array. I think it's better not to impose a limit of this sort and let the users be able to handle every scenario, even if some are rare, rather that forcing users to overcome limitations such as these. I really hope this PR gets approved faster and thanks everyone for their hard work :) --------------------------------------------------------------------------- by Tobion at 2012-01-07T21:01:07Z @dlsniper I think you misinterpreted my point. --------------------------------------------------------------------------- by dlsniper at 2012-01-07T21:04:04Z @Tobion I'm sorry I did that, I'll edit the message asap. Seems no sleep in 26 hours can cause brain not to function as intended :) --------------------------------------------------------------------------- by lsmith77 at 2012-02-01T14:38:52Z FYI the drupal guys are liking this PR (including the flash changes): http://drupal.org/node/335411 --------------------------------------------------------------------------- by drak at 2012-02-01T14:51:33Z @lsmith77 Fabien asked me to remove the changes to the flash messages so that they are as before - i.e. only one flash per name/type /cc @fabpot --------------------------------------------------------------------------- by fabpot at 2012-02-01T14:58:23Z To be clear, I've asked to split this PR in two parts: * one about the session refactoring (which is non-controversial and should be merged ASAP) * this one with only the flash refactoring --------------------------------------------------------------------------- by drak at 2012-02-02T11:29:26Z @fabpot this is ready to be merged now. I will open a separate PR later today for the flash messages as a bucket. --------------------------------------------------------------------------- by fabpot at 2012-02-02T11:34:39Z I must have missed something, but I still see a lot of changes related to the flash messages. --------------------------------------------------------------------------- by drak at 2012-02-02T11:39:10Z When I spoke to you you said you wanted to make the commit with flash messages with one message per name/type rather than multiple. The old flash messages behaviour is 100% maintained in `AutoExpireFlashBag` which can be the default in the framework if you wish. The `FlashBag` implementation makes Symfony2 ESI compatible. --------------------------------------------------------------------------- by stof at 2012-02-02T11:47:38Z @Drak splitting into 2 PRs means you should not refactor the flash messages in this one but in the dedicated one. --------------------------------------------------------------------------- by drak at 2012-02-02T12:29:43Z @stof Yes. I discussed with Fabien over chat there are basically no changes to flashes in `FlashBag` and `AutoExpireFlashBag` maintains the exact behaviour as before. The FlashBag just introduces ESI compatible flashes. There is no way to refactor the sessions without moving the flash messages to their own bag. The next PR will propose the changes to flashes that allow multiple messages per name/type. I can size the PR down a little more removing the new storage drivers and so on to make the PR smaller but that's really as far as I can go. To be clear, while the API has changed a little for flashes, the behaviour is the same.
Commits ------- 5ae76f1 [HttpFoundation] Update documentation. 910b5c7 [HttpFoudation] CS, more tests and some optimization. b0466e8 [HttpFoundation] Refactored BC Session class methods. 84c2e3c [HttpFoundation] Allow flash messages to have multiple messages per type. Discussion ---------- [2.1][HttpFoundation] Multiple session flash messages Bug fix: no Feature addition: yes Backwards compatibility break: yes, but this already happened in #2583. BC `Session` methods remain unbroken. Symfony2 tests pass: yes Fixes the following tickets: #1863 References the following tickets: #2714, #2753, #2510, #2543, #2853 Todo: - This PR alters flash messages so that it is possible to store more than one message per flash type using the `add()` method or by passing an array of messages to `set()`. __NOTES ABOUT BC__ This PR maintains BC behaviour with the `Session` class in that the old Symfony 2.0 methods will continue to work as before. --------------------------------------------------------------------------- by drak at 2012-02-13T06:28:33Z I think this is ready for review @fabpot @lsmith77 --------------------------------------------------------------------------- by lsmith77 at 2012-02-14T19:30:39Z the FlashBag vs. AutoExpireFlashBag behavior and setup difference should probably also be explained in the upgrading log --------------------------------------------------------------------------- by drak at 2012-02-15T04:43:14Z @lsmith77 Those differences are explained already in the changelog * Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`. This makes the implementation ESI compatible. * Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire behaviour of messages auto expiring after one page page load. Messages must be retrived by `get()` or `all()`. --------------------------------------------------------------------------- by Crell at 2012-02-19T17:35:34Z Drak asked me to weigh in here with use cases. Drupal currently has a similar session-stored-messaging system in place that I'd like to be able to replace with Flash messages. We frequently have multiple messages within a single request, however, so this change is critical to our being able to do so. For instance, when saving an article in Drupal there is, by default, a "yay, you saved an article!" type message that gets displayed. If you also have the site configured to send email when a post is updated, you may see a "email notifications sent" message (depending on your access level). If you have a Solr server setup for search, and you're in debug mode, there will also be a "record ID X added to Solr, it should update in 2 minutes" message. And if there's a bug somewhere, you'll also get, as an error message rather than notice message, a "Oops, E_NOTICE on line 54" message. Form validation is another case. If you have multiple errors in a single form, we prefer to list all of them. So if you screw up 4 times on a form, you may get 4 different error messages showing what you screwed up so you can fix it in one go instead of several. Now sure, one could emulate that by building a multi-message layer on top of single-layer messages, but, really, why? "One is a special case of many", and there are many many cases where you'll want to post multiple messages. Like, most of Drupal. :-) --------------------------------------------------------------------------- by lsmith77 at 2012-03-06T20:55:51Z @fabpot is there any information you still need before merging this? do you want more discussion in which case you might want to take this to the mailing list .. --------------------------------------------------------------------------- by drak at 2012-03-08T18:54:13Z Another plus for this PR is that it requires no extra lines of code in templates etc to display the flashes, see https://github.com/symfony/symfony/pull/3267/files#diff-1 --------------------------------------------------------------------------- by drak at 2012-03-15T06:38:21Z Rebased against current `master`, should be mergeable again.. --------------------------------------------------------------------------- by evillemez at 2012-03-17T03:08:41Z +1 to this, I have an extended version of HttpFoundation just for this... would love to get rid of it.
Bug fix: yes
Feature addition: yes
Backwards compatibility break: yes
Symfony2 tests pass: yes
Fixes the following tickets: #2607, #2591, #2717, #1863, #2773
References the following tickets: #2592, #2543, #2541, #2510
Todo: -
Introduction
This extensive PR is a refactor with minimal BC breaks of the
[HttpFoundation]
component's session management which fixes several issues in the current implementation. This PR includes all necessary changes to other bundles and components is documented in theCHANGELOG-2.1
andUPGRADING-2.1
.Unit Tests
All unit and functional tests pass.
Summary of Changes
SessionInterface
AttributeBagInterface
AttributeBag
replicates the current Symfony2 attributes behaviour, and the second,NamespacedAttributeBag
introduces structured namespaced representation using '/' in the key. Both are BC.FrameworkBundle
defaults to the old behaviour at the momentFlashBagInterface
INFO
,NOTICE
,WARNING
,ERROR
and can be extended as required.AbstractSessionStorage
for session storage driversSessionSaveHandlerInterface
when using custom session save handlersNullSessionStorage
driver which allows for unsaved sessionsRationale/Details
I'll explain more detail in the following sections.
Functional Testing
I've introduced
FunctionalTestFileSessionStorage
which replacesFilesystemSessionStorage
to emulate a PHP session for functional testing. Essentially the same methodology of functional testing has been maintained but without interrupting the other session storage drivers interaction with real PHP sessions.Session Workflow
PHP sessions follow a specific workflow which is not being followed by the current session management implementation and is responsible for some unpredictable bugs and behaviours.
Basically, PHP session workflow is as follows:
open
,read
,write
,close
. In between these can occur,destroy
andgarbage collection
. These actions are handled bysession save handlers
and one is always registered in all cases. By default, thefiles
save handler (internally to PHP) is registered by PHP at the start of code execution.PHP offers the possibility to change the save handler to another internal type, for example one provided by a PHP extension (think SQLite, Memcache etc), or you can register a user type and set your own handlers. However in all cases PHP requires the handlers.
The handlers are called when the following things occur:
open
andread
whensession_start()
or the session autostarts when PHP outputs some displaydestroy
whensession_regenerate_id(true)
is calledwrite
andclose
when PHP shuts down or whensession_write_close()
is calledgarbage collection
is called randomly according to configurable probabilityThe next VERY IMPORTANT aspect of this PR is that
$_SESSION
plays an important part in this workflow because the contents of the $_SESSION is populated after theread
handler returns the previously saved serialised session data. Thewrite
handler is sent the serialised$_SESSION
contents for save. Please note the serialisation is a different format toserialize()
.For this reason, any session implementation cannot get rid of using
$_SESSION
.I wrote more details on this issue here
In order to make writing session storage drivers simple, I created a light base class
AbstractSessionStorage
and theSessionSaveHandlerInterface
which allows you to quickly write native and custom save handler drivers.Flash Messages [BC BREAK]
Flash messages currently allow representation of a single message per
$name
. Fabien designed the original system so that$name
was equivalent to flash message type. The current PR changes three things relating to flash messages.INFO
,NOTICE
,WARNING
,ERROR
and defaults toNOTICE
The last issue fixes potential cases when flash messages are lost due to an unexpected intervening page-load (an error for example). The API
get()
has a flag which allows you to override theclear()
action.Flash message translation
This PR does not cover translation of flash messages because messages should be translated before calling the flash message API. This is because flash messages are used to present messages to the user after a specific action, and in any case, immediately on the next page load. Since we know the locale of the request in every case we can translate the message before storing. Secondly, translation is simply a string manipulation. Translation API calls should always have the raw untranslated string present because it allows for extraction of translation catalogs. For a complete answer see my answer here
Session attribute and structured namespacing
This has been implemented without changing the current default behaviour but details are below:
Attributes are currently stored in a flat array which limits the potential of session attributes:
Here are some examples to see why this 'structured namespace' methodology is extremely convenient over using a flat system. Let's look at an example with csrf tokens. Let's say we have multiple csrftokens stored by form ID (allowing multiple forms on the page and tabbed browsing).
If we're using a flat system, you might have
With a flat system when you get the key
tokens
, you will get back an array, so now you have to analyse the array. So if you simply want to add another token, you have to follow three steps: get the session attributetokens
, have to add to the array, and lastly set the entire array back to the session.Doable, but you can see it's pretty long winded.
With structured namespacing you can simply do:
There are several ways to implement this, either with an additional
$namespace
argument, or by treating a character in the$key
as a namespacer.NamespacedAttributeBag
currently treats/
as a namespacer so you can representuser.tokens/a
for example. The namespace character is configurable inNamespacedAttributeBag
.