I've created a little class to generate thumbnails with an external program (gm from ImageMagick, in case you're interested). The class issues an exec() call to launch gm.exe but I've hit a PHP bug that makes httpd.exe freeze when there are concurrent executions and there is an open PHP session. There is a workaround that looks like this:
session_write_close();
exec('gm convert -resize 100x100 source.jpg target.jpg');
session_start();
So far so good. Now I want to reuse the thumbnail class in other present and future projects. If I want to be able to just drop the code and call it without changes, I need to omit the session workaround when there aren't sessions and I need to do it programmatically. Otherwise, my thumbnailer will have the nasty side effect of starting a session by itself.
How can I properly detect whether there is an active session? Or, in other words, whether the PHP script that is calling my function has issued a call to session_id() and haven't closed or destroyed the session in any fashion so the operating system is probably holding an open handle that is locking the session file so it cannot be accessed by other running processes which can possibly freeze httpd.exe.
I've tried this:
if( session_id() ){
session_write_close();
exec('gm convert -resize 100x100 source.jpg target.jpg');
session_start();
}
But I've noticed that session_id() returns a value if the script ever used sessions, even if they're not active at this point. If the script has closed the session with session_write_close()... my thumbnailer will open it again!
You cant really. It doesn't look like there are any functions to do that. http://www.php.net/manual/en/book.session.php
You could maybe do some analysis of the files in the directory returned by session_save_path(). But you're going to have issues doing that reliably with different platforms and non-standard session handlers.
The only way I found was to use session_regenerate_id(), which will return false if there is no active session. But this has the obvious disadvantage that it might break any scripts coming after that rely on the old session id.
There isn't any method within PHP to do this. I ended up having to do a couple simple wrapper functions that check for a global variable (bleah) which my session-start wrapper function set once the session was started the first time.
Related
We've recently upgraded an old Codeigniter app from 2.1.0 to 3.1.9, and everything has gone smoothly. Except, that the new session locking is causing issues and I'm wondering the proper way to fix it.
The app uses AJAX heavily, however most of the AJAX calls don't write to the session and don't seem to break it.
Here is an example of the issue: there is a GUI with checkboxes, and when the input is changed (a checkbox is checked or unchecked) an AJAX call was made. On the other end of that AJAX call which boxes were checked were written to session so that they would be remembered from visit to visit. However, if you checked/unchecked multiple boxes causing multiple AJAX calls to go out, you would end up getting logged out. Similar behavior has been discovered around the app, all where session writes are happening.
I've tried implementing session_write_close() as suggested by the Codeigniter documentation but that only half worked in some spots, and caused more issues in area where there were no issues before. The app has a few endpoints that do all the work and all work flows share, so fixing the endpoint where the session writes are happening with session_write_close() breaks other script calls when they continue to need the session.
The short term solution I've come up with is to debounce the AJAX calls (which helps but doesn't solve the problem by itself) and to disable inputs until the AJAX call has finished.
Is there a better long term solution? Ultimately this app is being phased out, so spending a long time rewriting it isn't feasible.
The only long-term solution is to properly use session_write_close().
As you undoubtedly understand, session data is locked so only one script at any time can write to the session's persistent datastore. Session locking prevents hard to troubleshoot concurrency bugs and is more secure.
Without seeing your implementation it's really hard, er... impossible to offer any precise advice. Here are some things to consider that might help sort out the mess.
Either do ALL or NONE of the session writes in the AJAX response functions. (By "AJAX response function" I mean the PHP controller/method value of the AJAX url.)
With the ALL approach call session_write_close() in the "main" script before making any AJAX requests. Keep in mind that $_SESSION is not affected by session_write_close(). All $_SESSION items in the main script will remain accessible so you can reliably read the values. However, changes made to $_SESSION will not be written because, as far as PHP is concerned, the session is closed. But that's only true for the script that calls session_write_close().
With the NONE approach you may still need to read session data. In that case it would be wise to have the AJAX response functions call session_write_close as soon as possible to minimize the time concurrent requests are blocked. The call is more important for functions that require significant time to execute. If the script execution time is short then the explicit call to session_write_close() is not needed. If at all possible, i.e. no need to read session data, then not loading the session class might result in cleaner code. It would definitely eliminate any chance of concurrent request blocking.
Don't try to test session behavior by using multiple tabs to the same app on the same browser.
Consider using $config['sess_time_to_update'] = 0; and then explicitly call $this->sess_regenerate((bool) config_item('sess_regenerate_destroy')); when and where it makes sense that the session id needs to be changed, i.e. right after login; right after a redirect to a "sensitive" page; etc.
What follows next is offered with a large amount of trepidation. I've tested this using the "files" driver, but not extensively. So, buyer beware.
I found that it is possible to "re-start" a session by calling the PHP function session_start() after session_write_close() has been used. CodeIgniter will open and read the session datastore and rebuild the $_SESSION superglobal. It's now possible to change session data and it will be written when script execution ends - or with another call to session_write_close().
This makes sense because session_write_close() does not "do" anything to the CodeIgniter session object. The class is still instantiated and configured. CodeIgniter's custom SessionHandlerInterface is used to open, read, and write session data after session_start() is called.
Maybe this apparent functionality can be used to solve your problems. In case I wasn't clear earlier - use at your own risk!
I'm building a login class in php that will use sessions. I wanted to know:
If an instance of this very class will be saved until it is destroyed, even if I move to another page(without destroying it)?
Will I have to rebuild another instance of the class every time (with some saved data in the sessions)?
What is the life of a php class?
PHP variables persist while the program is running... just like any other programming language.
Now, how long does the program actually run? We have to distinguish between:
The PHP interpreter itself (written in C)
Your script (written in PHP)
The PHP interpreter is not designed to share variables between scripts so it doesn't really matter how long it runs.
Your script will run, well, until it's finished: it reaches the last line of finds an exit, die() or return statement. This simple fact should already answer your question. But it's also worth taking into account that there's no point in keeping a PHP script running continuously like a desktop application when it's only used to serve an HTTP request. Unlike other protocols (namely FTP or IRC), HTTP is stateless: each user request starts and closes the connection*.
(*) That's not entirely true (connections can and are normally reused) but doesn't affect the design implications.
Yes, the php class will be destroyed. You can serialize the object and store it in the session, if you choose : Documentation
A PHP object (class instance) acts just like a variable and will be destroyed at the end of the page request. Moreover you cannot store an object in a PHP session. The best you can try is to serialize() it and unserialize() it afterwards to keep your data.
You should see this per request. If you make a new request when going t another page then yes it needs to be rebuild.
What you could also do is Inversion of control or some sort of register setup. This way you can define it once and return an instance.
Example:
$ioc->translate(function() {
# Do stuff load configs. Get data from previous request
return new \Danoon\Link\Translate('../etc/lang/en_US.php');
});
I have ocassionally detected a strange problem with PHP sessions.
When I am running two PHP scripts using SAME session ID, second script is stuck until first one is completed.
I guess it is because trying to open same session storage file twice. But possible I am not right.
You will never catch this effect in normal site work, because user usually didn't open two or more pages simultaneously.
However, if you try to get content of a page of the same site using file_get_contents(), you will catch this issue.
Additionally, I have copying my cookies through context, so file_get_contents() trying to re-open same session as already opened in calling script.
As result, I have stucked long-running script (about 5-10 mins) which also disables me to open any new page of same site using same sessionid / login.
How I can to resolve this issue? Did you ever see any beautiful solution for it?
Yes, this is called "session locking" and is normal in PHP.
One solution is not not use sessions, just set cookies for your required persistent information.
Another solution is to implement your own session handler:
http://php.net/manual/en/session.customhandler.php
A detailed walkthrough about custom MySQL session handlers is here:
http://phpmaster.com/writing-custom-session-handlers/
I also found quite simple solution for this problem. We can use session_write_close(); to unlock session file in script 1, then we can make any file_get_contents(), curl_exec() etc without any worries and after these operations turn session back by session_start(). Checked by myself, works as charm!
I'm curious to know exactly when the session text file is created? An easy and surface level answer to that would probably be,.. well, when you use session_start(). But is it?
Could it be all the way at the end when the php script ends?
What makes me think that it may be at the end is that I know PHP would not write to the session file everytime you make a change to a session variables while the execution of the page goes on. For example, if you got something like $_SESSION['x'] = $_SESSION['x'] + 1; in a loop, the session file does NOT get modified at each and every iteration of the loop. Only when the PHP scripts is done, then the PHP engine makes a change into the file, to store the final value of the $_SESSION['x'].
With the same taken of logic, I see no reason for PHP engine to create a file as soon as you call the session_start. It could just delay the process.
What that entails is this;
I can start a session with session_start(), set a few session variables, toss them around within functions, using them as globals, and at the end of script, I unset them, and destroy the session and as a result, I assume NO SESSION FILE IS CREATED, thus no overhead associated with creating the session text files is experienced.
I would like to hear your insights in this.
My purpose is to explore the possibilities of using session variables strictly as a temporary place holder to pass global variables left and right - without dealing with file I/O. In fact, I do not need those variables in the next page at all.
In this case, could sessions prove faster than using globals within functions?
Tested and it gets created immediately on session_start. However, session_destroy removes it too. Tested with:
mkdir sess && cd sess
vim main.php
session_save_path(dirname(__FILE__));
session_start();
sleep(5);
session_destroy();
php main.php &
ls # main.php sess_rm4bcun6ear943mf61mdads190
fg # wait for script to end
ls # main.php
There's the answer to your question. Your idea of using _SESSION for global variable purposes is not a good one .. you might as well juse use $GLOBALS. No file IO there.
I have a Symfony2 project, where at the beginning of each session, I create a folder on the server where the user can manipulate and place his files.
I want to be able to delete the user's folder, when he closes his browser
(or any other related event, maybe check for a session timeout?).
How can I achieve this?
PS: I have read somewhere that java has a sessionHandler where you can code your function.
Is there anything similar in php (Symfony2 specifically)?
First of all, you cannot recongnize if a browser is closed by HTML and PHP. You would need ajax and constant polling or some kind of thing to know the browser is still there. Possible, but a bt complicated, mainly because you might run into troubles if a browser is still there (session is valid) but has no internet connection for a few minutes (laptop, crappy wlan, whatever).
You cannot have a sessionHandler which does this for you in PHP because PHP is executed when a script is retrieved from your server. After the last line is executed, it stops. If no one ever retrieves the script again, how should it do something? There is no magic that restarts the script to check if the session is still there.
So, what to do? First of all you want to make the session visible by using database session storage or something like that. Then you need a cronjob starting a script, looking up all sessions and deciding which one is invalid by now and then does something with it (like deleting the folder). Symfony can help as it allows you to configure session management in a way that it stores sessions in the database (see here) as well as creating a task which can be executed via crontab (see here).
The logical part, which contains deciding which session is invalid and what to do with this sessions) is your part. But it shouldn't be very hard as you got the session time and value in the database.