Deprecated: Assigning the return value of new by reference is deprecated in /home/bigfish/blogs/adam/wp-includes/cache.php on line 36

Deprecated: Assigning the return value of new by reference is deprecated in /home/bigfish/blogs/adam/wp-includes/query.php on line 21

Deprecated: Assigning the return value of new by reference is deprecated in /home/bigfish/blogs/adam/wp-includes/theme.php on line 540
CakePHP 1.2 Sessions and SWFUpload : Adam Royle

CakePHP 1.2 Sessions and SWFUpload

Have you ever used SWFUpload to upload more than one file at once without refreshing the page? Of course you have! If you haven't, then I suggest you go read up about it here and here before continuing with this tutorial.

One issue many people run into when using SWFUpload with CakePHP is that sessions are often lost in the upload process. Fortunately there is a way to prevent these incessant session-less side-effects.

DISCLAIMER: I am assuming that you've already got SWFUpload working with your CakePHP 1.2 application. I won't be covering the basics in this tutorial because you can find this information elsewhere.

Why doesn't it just work as is?

The problem lies in the built-in security of the Session class. It tries to detect session hi-jacking by checking the User Agent before resuming a session. Normally this is good practice, however some browsers send a "Flash Player" User Agent when using SWFUpload, which results in lots of blood and mayhem as you could imagine.

Show me the special sauce!

Of course you don't want to make the rest of your application insecure by messing about with global security settings, so the solution is to start the session manually for your upload action.

In your view where you initialise SWFUpload (adapt as necessary)

<script type="text/javascript">
var upload_url = '<?php echo $html->url('/uploads/upload/'.$session->id()) ?>';
</script>
 

The above code includes the current session id as part of the URL and so the beforeFilter callback can revive your session information.

/app/controllers/uploads_controller.php

<?php
class UploadsController extends AppController {
    
    var $name = 'Uploads';
    var $components = array('SwfUpload');
    var $helpers = array('Html', 'Javascript');
    
    function beforeFilter() {
        
        if ($this->action == 'upload') {
            $this->Session->id($this->params['pass'][0]);
            $this->Session->start();
        }
        
        parent::beforeFilter();
        
    }
    
    function upload() {
        
        if (isset($this->params['form']['Filedata'])) {
            // process your upload in here
            // and you can read from or write to the session
            // as you would normally
        }
        
    }

}
?>
 

It's still not working!

If you've implemented the above but it's still not working right, it's possible some rogue AJAX requests are re-generating the session id behind your back. Change the Security.level to medium to prevent CakePHP from automatically doing this.

/app/config/core.php

Configure::write('Security.level', 'medium');
 

Conclusion

People may argue that the above method is a bit of a hack, and truthfully it is, however for most people the above should work flawlessly.

Comments

42 Responses to “CakePHP 1.2 Sessions and SWFUpload”

  1. rdeman on April 4th, 2008 1:16 am

    Thanks for writing this tutorial -- helped me a lot

  2. irfan ahmed on April 9th, 2008 5:36 am

    for my application, this piece of code in your article was a really saved my time.
    $this->Session->id();

    thanks.

  3. Sam S on April 11th, 2008 10:19 am

    Truly Insightful. Though I am switching to medium security in the controllers beforeFilter.

    thanks - S

  4. Adam on April 11th, 2008 11:39 am

    @Sam S: Be aware that your technique won't protect your other controllers from rogue ajax requests (or simultaneous non-ajax requests via tabs)

  5. Juan Basso on April 13th, 2008 4:21 am

    This help me too.
    I have suggest changes:
    - In beforeFilter, don't need set Session->id(...);
    - Add 'Session' in helpers of controller;
    - In instance of SWFUpload (javascript, not component) add the follow line.
    - post_params: {"CAKESESSION" : "<?php echo $session->id(); ?>"},

    It's solve the problem too.

  6. Adam on April 13th, 2008 8:20 am

    @Juan Basso: Thanks for pointing this out. I have an older modded version of swfupload that doesn't have post_params, but that is a cleaner way to go. However I failed to see how the CAKESESSION param would automatically be picked up on the server side. I'll have to test this out later.

  7. Yuriy K. on May 21st, 2008 10:54 pm

    Many thanks for the article! It helped me so much.
    In my case session was lost in IEs only, but all worked well in Firefox and Opera.

  8. zheka on May 31st, 2008 6:43 am

    Hello,

    can this be done w/ cake 1.1?

  9. Adam on May 31st, 2008 8:24 am

    @Zheka: I've never used 1.1 so I can't verify this, but the above code prob won't work as is.

  10. Anton on June 19th, 2008 2:57 pm

    Thank you very much! It helps me a lot :)

  11. Tyler on July 31st, 2008 4:13 am

    This is great, thanks. Any chance we could see the SwfUpload component you're using?

  12. Luke on August 13th, 2008 12:46 am

    when you say you can get the info elsewhere on 1.2 and SWFUpload - do you mean the Revillini Bakery article on Multipurpose SWFUpload? I thought that was only wokring with 1.1 cake? thanks for your info anyway.

    I saw another fix by PoLK - who suggests just deleting the Session var Config at the vital moment and it stops the session dropping. not tried it though obviously!

  13. Tyler on August 13th, 2008 1:04 am

    Luke:

    I was able to get that component working on 1.2 with minimal changes. If I remember correctly, you really only need to make sure you change the controller / model named 'File' to something else. 'File' causes conflict with 1.2

  14. Adam on August 13th, 2008 1:18 am

    @Tyler: The cake SwfUpload component I use is the one in the bakery. The swf & js files I use are based on some modifications I found on the swfupload forums over a year ago that I can't find anymore. I'm happy to share privately but I won't post on this blog as it is not very generic and a few things are hardcoded.

    @Luke: Yes, that article on the bakery is still valid for 1.2 afaik. Also, the PoLK hack never worked for me, but I haven't had upload issues since I implemented the above.

  15. Luke on August 13th, 2008 6:59 pm

    hi thanks for the valuable and prompt information peeps! I shall give it a whirl - sounds good!

  16. Luke on August 19th, 2008 1:41 am

    hi Adam - I Was wondering if you would let me see the swf/js code you have used? I have followed the tutorial very carefully and adjusted certain things as I saw fit. But I get a problem with the SWFUpload side of things.

    My file inputs are swapped out for links (the flash loads for sure as I checked in Firefox debug) and also the javascript has no errors and inits OK, *but* after choosing a file, when I click on the submit button - nothing happens! It is trying to load /correct_form_action/#

    but it doesnt really do anything it seems: and neither is there a JS error!

    I'd just like someones view code to crib from - it could be the 1.0.2 src .zip I got from the SWFUpload site I suppose though.

    thanks in advance if you can give me any help

    Luke

  17. briddo on September 19th, 2008 5:32 pm

    thanks for this, works perfectly now!

  18. bhimrao on December 31st, 2008 5:54 pm

    nice

  19. Justin Thomas on January 8th, 2009 8:02 am

    Are there any security issues with going this route? You can also set your Security.level setting in your core.php file to 'medium' and that seems to work. I'm wondering which method is the safer more secure way to go. Or does it matter? Any ideas?

  20. Adam on January 8th, 2009 1:46 pm

    @Justin: I haven't any real experience with session hi-jacking so maybe someone else could give some insight as to the main risks.

    However, someone can hi-jack your session if they know your session id, which is saved in your cookies. So the only real situation where I can see it is a problem is if someone can get at your url, but can't get at your cookies too.

  21. Senthil Raj on February 20th, 2009 5:35 pm

    Thanks for writing this tutorial :-)

  22. Joe on March 18th, 2009 2:37 am

    Thank your! Good article.

    I have suggest changes:
    - Keep config 'Security.level' is high
    Configure::write('Security.level', 'high');

    - modify function beforeFilter()
    function beforeFilter() {

    if ($this->action == 'upload') {
    Configure::write('Security.level', 'medium');
    $this->Session->id($this->params['pass'][0]);
    $this->Session->start();
    }

    parent::beforeFilter();

    }

    It work in my project.

  23. Adam on March 18th, 2009 7:04 am

    @Joe: Thanks for your comment. On all of my projects now I set my security level to medium. Changing the session id on every request has given me some weird results when using concurrent ajax requests / page loads, and those problems outweigh the security benefits for my projects IMHO.

  24. Muhammad Rizwan on April 21st, 2009 6:23 pm

    hello guys.
    i am trying to implement swfupload in 1.2 following the article at cake bakery.
    But its not working for me.
    If any one can help me by putting up some working demo or can email me the url at rizwan.ciit@yahoo.com.

    thanks

  25. Emanuele on June 25th, 2009 10:28 am

    thanks! this solution saved me a lot of time!

  26. Pixelastic on July 22nd, 2009 12:06 pm

    I had to alter a little this code to suit my needs (I needed access to my session in a component), but I'm faced with an other problem.

    When starting my brand new session, everything works well and I can access my session vars from a flash request in my component.
    But it does alter the session and change the userAgent hash to set the one of the Flash Player instead.
    The consequence is that after I upload a file, I'm loggued out of my admin panel if I go to a new page.

    Am I the only one getting this problem ?

  27. Adam on July 22nd, 2009 12:19 pm

    @Pixelastic: The problem you describe is exactly what this article intends to workaround. The above code uses beforeFilter() to start a new session so possibly your component code is running before this occurs?

  28. Pixelastic on July 23rd, 2009 8:00 pm

    It was kind of late when I wrote this comment and I may not have been very clear. I was over-complicating things talking about component. Sorry about that.

    I'm talking about a different problem, in fact, a side-effect of your solution. The solution you provide allow us to use the original session instead of the one created for the flash player.

    But it does alter the session file by changing the Config.userAgent to the Flash one.
    Add a debug($this->Session->read()) statement after the $this->Session->start() or directly check your session file on your server.

    It means that a subsequent request (in the browser) will try to load the session file, check against the userAgent and destroy the whole session thinking it's an highjack attempt.

    I've used your method for a few months now, and I just discovered this issue recently. I have a fallback mechanism using cookie to automatically regenerate lost sessions and I think that's why I haven't spotted it earlier.
    Could you confirm that you experience the same behavior ?

  29. Adam on July 24th, 2009 8:57 am

    @Pixelastic: Thanks for your detailed explanation. To be honest I haven't tested this technique for some time so something may have changed in the cake core since it was written. One possible solution is to use Configure::write('Session.checkAgent', false); either in your entire site, or just for the upload action (possibly like this).


    function constructClasses(){
    if ($this->action == 'upload') {
    Configure::write('Session.checkAgent', false);
    }
    parent::constructClasses();
    }

  30. Pixelastic on July 24th, 2009 9:10 am

    The solution I've finally found is the following :

    First, I have to pass a second parameter (one can use the same technique you used for passing the sessionId, but personnally I use the post_params key of SWFUpload).
    The var to pass is the userAgent hash (found in $session->read('Config.userAgent').

    And then in your controller beforeFilter (or component startup(), provided your vars are named $sessionId and $userAgent, you have to do the following :

    // Destroying the session created by flash
    $this->Session->destroy();
    // Using instead the session specified
    $this->Session->id($sessionId);
    $this->controller->Session->start();
    // We revert to the original userAgent because starting a new session modified it
    $this->Session->write('Config.userAgent', $userAgent);
    // We delete the flash cookie, forcing it to restart this whole process on each request
    setcookie(Configure::read('Session.cookie'), '', time() - 42000, $this->Session->path);

    And this solved the issue. I haven't tested your solution of disabling the Session.checkAgent, but finding mine took me so much hours and works so I haven't strength enough to try an other one :) Thanks anyway, hope it would help someone.

  31. Pixelastic on July 24th, 2009 9:12 am

    There is a typo in my previous comment : it's $this->Session->start() instead of $this->controller->Session->start();

  32. atz on October 28th, 2009 4:35 am

    After this solution did work for a few weeks on my old server - it suddenly stopped. I had some more problems recreating the session. The id alone wouldn't help.

    I added the userAgent value from the session to the upload-Url as second parameter:

    '/uploads/upload/'.$session->id().'/'.$session->read('Config.userAgent')) ?>'

    ...and rewrite it to the session in the beforeFilter:

    $this->Session->write('Config.userAgent', $this->params['pass'][1] );

    Also I had a problem with Suhosin installed on my servers. Suhosin - by default - keeps encrypting all sessions, which didn't help while recreating the session in the beforeFilter. I had to shut it off in /etc/php5/conf.d/suhosin.ini with:

    suhosin.session.encrypt = off

    After these changes I could recreate my session and the upload did work :)

    Hope that helps :)

  33. Sergio on December 25th, 2009 4:37 pm

    One word: Tnx! ;)

  34. Bjorn on February 5th, 2010 7:13 am

    This works fine, but I don't understand how to not always create a new session. When I upload one image I store it in the session. When uploading a second one it is starting the session again and all session variables are gone. Am I missing something here or is this expected behaviour?

  35. Freshman on February 26th, 2010 9:19 pm

    @Pixelastic: you are my hero!! I was getting logged out with every upload attempt - this solved the problem. Thanks.

  36. ashish on September 7th, 2010 10:40 pm

    It works properly but it destroys all the sessions in my controller. So it effects other functionality in my site

  37. Benjamin on October 26th, 2010 3:24 pm

    Made it work combining both Pixelastic and atz solutions:
    function beforeFilter() {
    if ($this->action == 'upload') {
    $this->Session->id($_POST['cake_session_id']);
    $this->Session->start();
    $this->Session->write('Config.userAgent', $_POST['cake_user_agent']);
    }
    parent::beforeFilter();
    }

  38. tekomp on November 24th, 2010 8:32 pm

    Couldn't get the 302 bug to stop for a while. Turned out it was conflicting with DebugKit plugin. Once I disabled that, it worked with this in the controller:


    function beforeFilter() {
    if ($this->action == 'upload') {
    $this->Session->id($this->params['form'][Configure::read('Session.cookie')]);
    $this->Session->start();

    }
    parent::beforeFilter();
    }

    And passing this in post_params:


    Configure::read('Session.cookie') : $session->id(),

  39. Swathi on June 27th, 2011 8:26 pm

    Hi ,
    I have s:file upload tag in my jsp.and one sx:autocompleter tag.
    if i change the element in sx:autocompleter the ajax to work.
    But the s:file tag is preventing not to work ajax properly.
    Can you give me some idea.Pls…..
    Im trying this for past one week…..
    Thanks in advance.

  40. Rakesh Tembhurne on March 20th, 2012 6:26 pm

    Currently I am working on a CakePHP 1.3 application where I am using uploadify.js for uploading images. the beforeFilter code worked really well with Uploadify.js.

Leave a Reply