Ruby on Rails
PhpSession

Our primary use for rails is in conversion of legacy applications to the rails framework. As we do this in an iterative manner, we’d like to be able to replace only portions of applications with rails versions of the functionality. In order to accomplish this seamlessly, we needed some glue to allow rails to be aware of things such as already authorized users, peristent variables, etc. To accomplish this we came up with a patch to php_serialize.rb

Here’s the patch:


--- php_serialize.rb    2004-09-17 13:46:30.000000000 -0500
+++ PHP/php_serialize.rb    2005-03-09 15:19:20.000000000 -0600
@@ -22,9 +22,13 @@
 # }}}

-# PHP serialize() and unserialize() workalikes
+# PHP serialize() and unserialize() workalikes # {{{ Docs
 #  First Released: 2003-06-02 (1.0.0)
 #  Prev Release: 2003-06-16 (1.0.1), by Thomas Hurst <<a href="mailto:tom@hur.st">tom@hur.st</a>>
-#  This Release: 2004-09-17 (1.0.2), by Thomas Hurst <<a href="mailto:tom@hur.st">tom@hur.st</a>>
+#  Prev Release: 2004-09-17 (1.0.2), by Thomas Hurst <<a href="mailto:tom@hur.st">tom@hur.st</a>>
 #                Switch all {}'s to explicit Hash.new's.
+#  This Release (patch): 2005-03-09 (1.0.2.1), by The Rubyists <<a href="mailto:rubyists@rubyists.com">rubyists@rubyists.com</a>>
+#                       added handling for php session files with PHP.parse_session
+#                       as well as a hook in PHP.unserialize to recognize strings which
+#                       are sessions.  Disregards objects stored in sessions.
 #
 #  These two methods should, for the most part, be functionally identical
@@ -78,7 +82,7 @@
 #   classmap.
 #
-# Note: StringIO is required for unserialize(); it's loaded as needed
+# Note: StringIO is required for unserialize(); it's loaded as needed # }}}

-module PHP
+module PHP # {{{
    def PHP.serialize(var, assoc = false) # {{{
        s = ''
@@ -147,4 +151,7 @@
    def PHP.unserialize(string, classmap = nil, assoc = false) # {{{
        require 'stringio'
+       if pipe = string[/^[^:]*:/].index("|")
+           return PHP.parse_session(string)
+       end
        string = StringIO.new(string)
        def string.read_until(char)
@@ -159,7 +166,8 @@

        do_unserialize(string, classmap, assoc)
-   end
+   end # }}}

-   def PHP.do_unserialize(string, classmap, assoc)
+private
+   def PHP.do_unserialize(string, classmap, assoc) # {{{
        val = nil
        # determine a type
@@ -257,8 +265,20 @@
        val
    end # }}}
-end
+   
+   def PHP.parse_session(sessionstring) # {{{
+       $; = /\w+\|/
+       names   = sessionstring.scan($;)
+       objects = sessionstring.split[1,sessionstring.split.length]
+       count   = -1
+       structs = names.map do |name| 
+           count += 1
+           { name.sub(/\|$/,"") => PHP.unserialize(objects[count]) } if objects[count][0,1] != 'O' 
+       end.select { |f| f != "" }
+       structs
+   end # }}}

+end # }}}

-if $0  __FILE__
+if $0  FILE # {{{
    require ‘tempfile’

@ -306,4 +326,4 @ tmp.close! end
-end
+end # }}}

Usage



sessPath = "/var/lib/php4" 
sessFile = sessPath + "/sess_" + @cookies['PHPSESSID'][0]
phpVars = PHP.unserialize(File.read(sessFile))

This will return everything except for php object into the phpVars instance. Next step is to add dynamic creation of ruby objects from the stored php objects, if possible. Any help would be greatly appreciated. The full module source with patch applied is available:
php_serialize-1.0.2.1.rb

For a similar project (and we would be willing to collaberate) see UsingRailsToGraduallyReplaceARunningPHPNukeInstallation

Easy serialization for sessions/cookies

Storing a hash in a cookie without marshaling:

# set cookie
user = {:name => "Alex", :age => 25}
cookies[:user] = user.inspect
# get cookie
user = eval(cookies[:user])
user[:name] # => Alex

Wow, so you’re basically advocating eval’ing the value of a cookie? That’s a massive security hole, you realise the user can easily change the value of a cookie to anything they like? I do not recommend anyone uses the above “easy serialization” method for cookies

I faced an exception with the current patch when
one of the values in the session is NULL. It looks like ‘atribute_name|N;’ in the sessionstring which causes an nil Exception in line: 153 because the regexp will not match anything. I modified line: 275 to look like this:

            { name.sub(/\|$/,"") => PHP.unserialize(objects[count]) } if objects[count][0,1] != 'O' && objects[count][0,1] != 'N'

Just in case someone else is facing this problem…

Sharing Sessions between rails and Php with memcache

Heres a secure way to do it and its fast!

Firstly make sure you have memcached set up on your server
and rails configured to use memcached for sessions instead of active record or pstore

# environment.rb
config.action_controller.session_store = :mem_cache_store

rails will store the session data in memcache as a key which will look something like this session:c6005a94d097e4010f6a8758ce4e7c31

in rails you can find out the session key by accessing


session.session_id

So for example if we wanted to flick over to php and make use of the session data we could find the current users session id in rails as above and redirect users the the url of the php page like

www.example.com/uploadimages.php?key=c6005a94d097e4010f6a8758ce4e7c31

make sure your php is compiled with memcached and the extension enabled in php.ini. if you dont do this correctly then the following code will give a php error


$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ("Could not connect");

$key = $_GET['key'];

$get_result = $memcache->get("session:".$key);

var_dump($get_result);

you should get a dump of the session data from memcache with the variables you have stored from rails!

i have not yet tested how this works with rails 2 cookie sessions