From the perspective of GallerySession, a normal G2 request goes like this:
if new session { if persistent session is necessary { get new sessionId and save session (total 1 db query) } } else { return; /* don't need a session */ } } send a cookie
if session is new { if persistent session is newly created { replace temp session id placeholder that is in the generated HTML with the real, new sessionId } else { remove the g2_GALLERYSID= temp session id placeholder from all URLs in the generated HTML } }
In G2 standalone, we can differentiate three basic types of requests in main.php (from the session's point of view):
We need to check if need to create a new persistent session before redirecting and if there's a sessionId in the redirect URL, replace it with the real, new one
We need to check if we need to create a new persistent session after fetching the rendered HTML and we need to replace the pseudo temp sessionId with a real one for new persistent session / remove it if no persistent session was created. If we had a real sessionId at the beginning of the request, do nothing.
Just before giving the control to the progress-bar view / immediate view, create a new session if necessary. Then switch to not use a pseudo temp session id anymore (getId() will return an empty string or the real sessionId) since we can't do additional session stuff during the immediate / progressbar view.
If we determine in $session->init() that the session is new / no persistent session exists yet, then we have a problem. On the one hand, we need a new sessionId as soon as possible since we need to append the new sessionId to all generated URLs since we don't yet know whether the user-agent (browser) accepts cookies or not. Without cookies, stateful browsing is only possible with a sessionId in all URLs that a user may click to request the next page.
On the other hand, we can't acquire a new, unique/collision-free sessionId without querying the database to check if the new, randomly chosen sessionId is not already in use. The chance for a md5 collision is very small (around 1 : 2^128), but we want to have guarantee that. And querying the database for a new sessionId costs 1 db query. And in most cases, we don't need a session for users that don't have any session yet. And if we find out that we don't need a session after all, we should delete the acquired sessionId at the end of the request, that makes 2 queries and for most guest requests we don't need a session anyway.
For new sessions, we use a temporary pseudo session ID during the whole request. All generated URLs have this 'g2_GALLERYSID=' . SESSION_TEMP_ID, and other modules get the the SESSION_TEMP_ID too if they call $session->getId().
Once we reached the latest point in the request before we output HTML or redirect to another page, we evaluate the session data to find out whether a persistent session is required. If so, a persistent session is created.
One could argue that we don't need a SESSION_TEMP_ID and should set sessionId for new sessions to a randomly chosen new sessionId. And once we call $session->start() to check if we really need the session, we can find out whether the id that we used throughout the request in URLs etc. is a new, unique id or not. Then in replaceTempSessionIdIfNecessary() we wouldn't have to replace the sessionId if a new persistent session was created. Only in the unlikely case of 1 : 2^128 (if a collision occurred) we would have to replace the used sessionId with the newly, truly unique sessionId.
The reason why we choose a SESSION_TEMP_ID which doesn't have the format of our normal sessionIds is that it's easy to find out if some code uses an unreplaced sessionId or not. Also, the step to replace the SESSION_TEMP_ID is super cheap, so we wouldn't win that much.
If you want to create a new session and 'login' a specific user, do (omitting the obligatory error checks):
Alteratively, use Gallery, but then you're interacting with the one and only $session object:
If you want to create a new session and ensure that you've got a valid sessionId, do:
Before outputting, we call in main.php:
If the HTML is outputted directly to the browser, we can't replace / remove the temp session id and therefore have to check if need a session before we start outputting. That is the case for immediate views and the progressbar (as an exception among the non-immediate-views).
We don't create sessions / sessionId for anonymous / guest users and all search engines fall into this category in G2, since they don't explicitely log in into G2. That means that all generated URLs have no sessionId for search engines. This is particularely important, since search engines could fall into a loop of following the same links again and again just because the sessionId in the URL has changed, which is not the case in G2.
We also need to detect search engines since some pages in G2 could add some data to the session. Guests don't have a session in G2 as long as their session data has nothing important in it. As soon as a view / controller adds some special data to the session, we create a persistent session for this guest user. If we didn't detect search engines, then we'd create a persistent session for them, which could be quite expensive if the search engines fetch hundreds or thousands of pages from G2. Also, in embedded G2, we'd add the sessionId to the core.DownloadItem URLs and the search engine would record ugly URLs with sessionIds in them. That's why we still need to detect search engines and then we set that the user agent is using cookies and we call $this->doNotUseTempId() and we remember to not create a persistent session too.
As of G2.2, the following search engines are detected by G2:
Search Engine Identifier in G2 | User Agent Must Contain |
---|---|
gsa-crawler OR Google | |
yahoo | Yahoo |
askjeeves | Ask Jeeves |
microsoft | msnbot |
yandex | Yandex |
stackrambler | StackRambler |
convera | ConveraCrawler |
You can edit this list in modules/core/classes/GalleryUtilities.class function identifySearchEngine().
Since we don't create persistent sessions for guest users unless they browse around a lot / pick a preferred language and such things, the problem of a time-consuming expiration operation to expire / remove old sessions is eased.
Performance is not really an issue.
Guests without a session don't get a navId / back links since we can't store the navigation data in between requests. But as mindless said guests usually don't have access to pages that would make use of the navigation data.
Check if we still get a valid back link when browsing as guest and adding something to the cart.
If we wanted to keep the lastViewed data in the session, we'd have to create a session for all guests. Instead, we don't create a session just for the lastViewed data. But that means that we'd increment the view count again and again if a guest browses back and forth or views multiple resizes of a specific image.
I've added a check for the HTTP If-modified-since header in incrementViewCount() if we don't have lastViewed data. I'm not checking the date of If-modified-sine at all, just checkinf for its presence. Probably it won't work that well, since the request URLs for different resizes and the fullsize are different.
Alternatively, one could send another cookie, not a GALLERYSID cookie, just to keep data about things like navId and lastViewed.
Data that is set in the controller and passed on to the session or to the view could contain the SESSION_TEMP_ID. I'll also have to replace / remove the SESSION_TEMP_ID from the $form and the session data I guess.
During immediate views / progress-bar views, you're no longer allowed to manipulate the session. I don't think that's a problem, but it might be one. At least you can still manipulate the session if the session already exists. In such a case, it won't attempt to send a cookie and doesn't have to replace a pseudo id or something like that.
Maybe split GallerySession.class into a GallerySession and a GallerySessionHelper_simple.class since in most cases, we only need to load the session data and nothing else.