<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Perplexed Labs &#187; session</title>
	<atom:link href="http://blog.perplexedlabs.com/tag/session/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.perplexedlabs.com</link>
	<description>web development war stories from the frontlines to the backend</description>
	<lastBuildDate>Mon, 16 May 2011 14:19:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>PHP Custom MySQL Session Handler</title>
		<link>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/</link>
		<comments>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 14:00:30 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[memcache]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[session handler]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=302</guid>
		<description><![CDATA[The Problem I'm sure many have used PHP's default session handling capabilities. By default, PHP uses the filesystem to store session data naming files with their session id # and putting them in /tmp. This is done for the sake of simplicity. On a single-server, low load website, this particular setup works fine. It's when [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/' rel='bookmark' title='Permanent Link: Mutex with PHP and mySQL'>Mutex with PHP and mySQL</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/12/17/php-parallel-web-scraper/' rel='bookmark' title='Permanent Link: PHP Parallel Web Scraper'>PHP Parallel Web Scraper</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/php-simple-profiling-class/' rel='bookmark' title='Permanent Link: PHP Simple Profiling Class'>PHP Simple Profiling Class</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>The Problem</h3>
<p>I'm sure many have used PHP's default session handling capabilities.  By default, PHP uses the filesystem to store session data naming files with their session id # and putting them in /tmp.</p>
<p>This is done for the sake of simplicity.  On a single-server, low load website, this particular setup works fine.  It's when you start having multiple simultaneous requests from a single client (identified by a session) that the problems begin to show their ugly heads.  Utilizing AJAX multiple simultaneous requests might be the norm, even for a low load website.</p>
<p>Essentially, in order to prevent <a href="http://en.wikipedia.org/wiki/Race_condition">race conditions</a> PHP internally uses a lock to maintain exclusive access to the file containing the session data for the client connection.  This means that as soon as a single request acquires exclusive access to that session file, no other request can access the file until the original request completes.  What happens when that second request asks to start the session?  It waits.</p>
<h3>The Solution</h3>
<p>Fortunately there's a solution to all this.  Implementing your own custom session handler and moving your session storage backend to another technology (such as a MySQL database or memcache) affords you the ability to handle simultaneous requests in a thread-safe manner.  Remember, it's a good thing that PHP prevents race conditions by locking the session file.  What we're looking to do is increase the granularity of the lock to the level of individual session data key =&gt; value pairs.</p>
<p>For this post we're going to stick to storing sessions in the database with our own custom session save handler.  Perhaps in another post I'll talk about doing the same in memcache.  It's the theory we're concerned about, not necessarily the exact storage mechanism implementation.</p>
<p>Implementing your own custom session handler is simply a matter of calling session_set_save_handler() with the appropriate callback methods for handling the following scenarios:</p>
<ul>
<li><strong>open</strong><br />
Open function, this works like a constructor in classes and is executed when the session is being opened. The open function expects two parameters, where the first is the save path and the second is the session name.</li>
<li><strong>close</strong><br />
Close function, this works like a destructor in classes and is executed when the session operation is done.</li>
<li><strong>read</strong><br />
Read function must return string value always to make save handler work as expected. Return empty string if there is no data to read. Return values from other handlers are converted to boolean expression. TRUE for success, FALSE for failure.</li>
<li><strong>write</strong><br />
The "write" handler is not executed until after the output stream is closed.</li>
<li><strong>destroy</strong><br />
The destroy handler, this is executed when a session is destroyed with session_destroy() and takes the session id as its only parameter.</li>
<li><strong>gc</strong><br />
The garbage collector, this is executed when the session garbage collector is executed and takes the max session lifetime as its only parameter.</li>
</ul>
<p>There's a well known trick for situations like this that allow you to pass a class method (static or instance) for callbacks.  Let's take a look at a simple example.  This correctly implements the required methods but obviously doesn't do much:</p>
<pre class="brush: php; title: ;">
class MySession
{
	public function __construct()
	{
		session_set_save_handler(
						array('MySession', 'sess_open'),
						array('MySession', 'sess_close'),
						array('MySession', 'sess_read'),
						array('MySession', 'sess_write'),
						array('MySession', 'sess_destroy'),
						array('MySession', 'sess_gc')
						);

		ini_set('session.auto_start',					0);
		ini_set('session.gc_probability',				1);
		ini_set('session.gc_divisor',					100);
		ini_set('session.gc_maxlifetime',				604800);
		ini_set('session.referer_check',				'');
		ini_set('session.entropy_file',					'/dev/urandom');
		ini_set('session.entropy_length',				16);
		ini_set('session.use_cookies',					1);
		ini_set('session.use_only_cookies',				1);
		ini_set('session.use_trans_sid',				0);
		ini_set('session.hash_function',				1);
		ini_seT('session.hash_bits_per_character',		5);

		session_cache_limiter('nocache');
		session_set_cookie_params(0, '/', '.mydomainname.com');
		session_name('mySessionName');
		session_start();
	}

	public static function sess_open($save_path, $session_name)
	{
		return true;
	}

	public static function sess_close()
	{
		return true;
	}

	public static function sess_read($id)
	{
		return '';
	}

	public static function sess_write($id, $sess_data)
	{
		return true;
	}

	public static function sess_destroy($id)
	{
		return true;
	}

	public static function sess_gc($maxlifetime)
	{
		return true;
	}
}
</pre>
<h3>SPL and Storing Session Data In MySQL</h3>
<p>Adding support for MySQL to this class is fairly trivial.  Let's start off by creating a table to store our session data:</p>
<pre class="brush: sql; title: ;">
CREATE TABLE `sessions` (
  `sesskey` char(32) NOT NULL,
  `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `varkey` varchar(128) NOT NULL,
  `varval` longtext NOT NULL,
  PRIMARY KEY  (`sesskey`,`varkey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
</pre>
<p><a href="http://us3.php.net/spl">PHP's SPL</a> provides the ability to create objects that behave as though they were arrays.  You can make objects that iterate, respond to accessing them via array notation ($object['key']), and lots of other interesting things.</p>
<p>We're going to enhance the MySession class with a couple SPL interfaces that will allow the object, when instantiated, to behave like an array.   We can then override the $_SESSION superglobal with an instance of our new MySession class.  It will provide an identical interface to access session data while internally storing session data to the database.  Also, internally it will use methods for row-level locking via MySQL's advisory locks (GET_LOCK() and RELEASE_LOCK()).</p>
<p>The next step is to implement the required methods for the interfaces we're using.  These methods allow the object to behave as though it's an array.</p>
<pre class="brush: php; title: ;">
class MySession implements Countable, ArrayAccess, Iterator
{
	private $index;
	private $curElement;
	private $locks = array();
	private $sessionName = 'sessionId';
	private $serialize = 'serialize';
	private $unserialize = 'unserialize';
	private $session_id = null;

	public function __construct()
	{
		session_set_save_handler(
						array('MySession', 'sess_open'),
						array('MySession', 'sess_close'),
						array('MySession', 'sess_read'),
						array('MySession', 'sess_write'),
						array('MySession', 'sess_destroy'),
						array('MySession', 'sess_gc')
						);

		ini_set('session.auto_start',					0);
		ini_set('session.gc_probability',				1);
		ini_set('session.gc_divisor',					100);
		ini_set('session.gc_maxlifetime',				604800);
		ini_set('session.referer_check',				'');
		ini_set('session.entropy_file',					'/dev/urandom');
		ini_set('session.entropy_length',				16);
		ini_set('session.use_cookies',					1);
		ini_set('session.use_only_cookies',				1);
		ini_set('session.use_trans_sid',				0);
		ini_set('session.hash_function',				1);
		ini_seT('session.hash_bits_per_character',		5);

		session_cache_limiter('nocache');
		session_set_cookie_params(0, '/', '.mydomainname.com');
		session_name('mySessionName');
		session_start();
	}

	public function destroy()
	{
		$sessionName = session_name();
		$cookieInfo = session_get_cookie_params();
		$cookieExpires = time() - 3600;
		if((empty($cookieInfo['domain'])) &amp;&amp; (empty($cookieInfo['secure']))) {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path']);
		} elseif(empty($cookieInfo['secure'])) {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path'], $cookieInfo['domain']);
		} else {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path'], $cookieInfo['domain'], $cookieInfo['secure']);
		}
		unset($_COOKIE[$sessionName]);

		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;'&quot;;
		$dbo-&gt;query($q);

		session_destroy();
	}

	private function lockName($k)
	{
		return 'sesslock'.$this-&gt;session_id.$k;
	}

	public function locked($k)
	{
		$k = $this-&gt;lockName($k);

		return isset($this-&gt;locks[$k]);
	}

	public function acquire($k, $timeout = 0)
	{
		$k = $this-&gt;lockName($k);

		if(!isset($this-&gt;locks[$k])) {
			$dbo = DBO::getInstance();
			$q = &quot;SELECT GET_LOCK('&quot;.$k.&quot;', &quot;.$timeout.&quot;)&quot;;
			$rs = $dbo-&gt;query($q);
			$this-&gt;locks[$k] = $dbo-&gt;result($rs, 0);
			$dbo-&gt;fr($rs);

			return $this-&gt;locks[$k];
		}

		return false;
	}

	public function release($k)
	{
		$k = $this-&gt;lockName($k);

		unset($this-&gt;locks[$k]);

		$dbo = DBO::getInstance();
		$q = &quot;SELECT RELEASE_LOCK('&quot;.$k.&quot;')&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;fetch($rs);
		$dbo-&gt;fr($rs);

		return true;
	}

	public function count()
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT COUNT(*) FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;result($rs, 0);
		$dbo-&gt;fr($rs);

		return $ret;
	}

	public function rewind()
	{
		$this-&gt;index = 0;
		$this-&gt;getCurElement();
	}

	private function getCurElement()
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varkey`, `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' LIMIT &quot;.$this-&gt;index.&quot;,1&quot;;
		$rs = $dbo-&gt;query($q);
		$row = $dbo-&gt;fetch($rs);
		$dbo-&gt;fr($rs);
		if(is_array($row) &amp;&amp; (count($row) == 2)) {
			$this-&gt;curElement = $row;
		} else {
			$this-&gt;curElement = array(null, null);
		}
	}

	public function key()
	{
		return $this-&gt;curElement[0];
	}

	public function current()
	{
		return call_user_func($this-&gt;unserialize, $this-&gt;curElement[1]);
	}

	public function next()
	{
		$this-&gt;index++;
		$this-&gt;getCurElement();
	}

	public function valid()
	{
		return ($this-&gt;curElement[0] !== null);
	}

	public function offsetSet($k, $v)
	{
		$dbo = DBO::getInstance();
		$q = &quot;REPLACE INTO `sessions` (`sesskey`, `varkey`, `varval`) VALUES ('&quot;.$this-&gt;session_id.&quot;', '&quot;.$k.&quot;', '&quot;.$dbo-&gt;sanitize(call_user_func($this-&gt;serialize, $v)).&quot;')&quot;;
		$dbo-&gt;query($q);
	}

	public function offsetGet($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		if($ret = $dbo-&gt;result($rs, 0)) {
			$ret = call_user_func($this-&gt;unserialize, $ret);
		}
		$dbo-&gt;fr($rs);

		return $ret;
	}

	public function offsetUnset($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$dbo-&gt;query($q);
	}

	public function offsetExists($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;result($rs, 0);
		$dbo-&gt;fr($rs);

		return (bool)$ret;
	}

	public static function sess_open($save_path, $session_name)
	{
		return true;
	}

	public static function sess_close()
	{
		return true;
	}

	public static function sess_read($id)
	{
		return '';
	}

	public static function sess_write($id, $sess_data)
	{
		return true;
	}

	public static function sess_destroy($id)
	{
		return true;
	}

	public static function sess_gc($maxlifetime)
	{
		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `timestamp` &lt; '&quot;.date('Y-m-d H:i:s', time() - $maxlifetime).&quot;'&quot;;
		$dbo-&gt;query($q);

		return $dbo-&gt;query($q);
	}
}
</pre>
<p>The $dbo object is just an example of an interface to the database through the use of a singleton.  Replace the $dbo object with your preferred mysql database interface and you'll be set to go!</p>
<p>Starting your session is now as simple as:</p>
<pre class="brush: php; title: ;">
$_SESSION = new MySession;
</pre>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/' rel='bookmark' title='Permanent Link: Mutex with PHP and mySQL'>Mutex with PHP and mySQL</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/12/17/php-parallel-web-scraper/' rel='bookmark' title='Permanent Link: PHP Parallel Web Scraper'>PHP Parallel Web Scraper</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/php-simple-profiling-class/' rel='bookmark' title='Permanent Link: PHP Simple Profiling Class'>PHP Simple Profiling Class</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Adventures in Django and Python &#8211; Part III</title>
		<link>http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/</link>
		<comments>http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/#comments</comments>
		<pubDate>Thu, 13 Aug 2009 14:00:38 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[ternary]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=307</guid>
		<description><![CDATA[Read my previous two posts on Django and Python - Part I and Part II I've been working on a project management tool suite in Django. It's been a great side project to really experiment with Django in real-world scenarios. Forms At times I feel like I fight with newforms. In particular, it lacks the [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/04/22/django-url-parameter-passing-and-python-strings/' rel='bookmark' title='Permanent Link: Django URL Parameter Passing and Python Strings'>Django URL Parameter Passing and Python Strings</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><em>Read my previous two posts on Django and Python - <a href="http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/">Part I</a> and <a href="http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/">Part II</a></em></p>
<p>I've been working on a project management tool suite in Django.  It's been a great side project to really experiment with Django in real-world scenarios.</p>
<h3>Forms</h3>
<p>At times I feel like I fight with newforms.  In particular, it lacks the ability to specify basic class or style attributes for a given form field from within the template.  I'd like to be able to more finely tune the display of the field, directly within the template, with a style attribute or a class.  Is it suggested you write your own custom form field widget for a single element?  I've been getting around this by doing the following:</p>
<pre class="brush: python; title: ;">
&lt;input type=&quot;text&quot; name=&quot;{{ todo_form.item.name }}&quot; style=&quot;width: 720px;&quot;/&gt;
</pre>
<p>This gets more complicated if you want to set a style attribute for a form field that's a select box (for a ForeignKey model field, for example).  </p>
<pre class="brush: python; title: ;">
&lt;label for=&quot;category&quot;&gt;Category:&lt;/label&gt; &lt;select name=&quot;{{ category_form.project.name }}&quot; style=&quot;width: 221px;&quot;&gt;
{% for choice_val, choice_label in category_form.project.field.choices %}
	&lt;option value=&quot;{{ choice_val }}&quot;&gt;{{ choice_label }}&lt;/option&gt;
{% endfor %}
</pre>
<p>Is this a good use case for template tags?  I feel like I'm missing something here.</p>
<p>On the positive side, it was an absolute pleasure to work with multiple forms on a single page submitted to and processed by a single view.  This is primarily thanks to prefixes.  Excellent, that's how easy it should be.</p>
<h3>Ternary Operator</h3>
<p><strong>Update:</strong> <em>It's been pointed out in comments (thanks!) that Python 2.5 introduced a ternary operator.  It's syntax is as follows:</em></p>
<pre class="brush: python; title: ;">
label = &quot;true&quot; if booleanVariable else &quot;false&quot;
</pre>
<p>I also ran into a minor Python syntax issue.  I love the ternary operator in languages that offer it.  It's a concise, one-line, syntax for an if-else clause.  Consider the following PHP code:</p>
<pre class="brush: php; title: ;">
$label = $booleanVariable ? 'true' : 'false';

// the above is identical to the following block:
if($booleanVariable) {
   $label = 'true';
} else {
   $label = 'false';
}
</pre>
<p>Python unfortunately lacks this syntactic sugar.  Fortunately, however, you can effectively accomplish the same thing by doing this:</p>
<pre class="brush: python; title: ;">
label = (booleanVariable and 'true' or 'false')

# the above is equivalent to the following block:
if booleanVariable:
   label = 'true'
else:
   label = 'false'
</pre>
<h3>Sessions</h3>
<p>Django has built in support for sessions.  By default, sessions last longer than the lifecycle of the user's browser.  I personally think it should be the other way around.  It's easily changed though (in your settings.py):</p>
<pre class="brush: python; title: ;">
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
</pre>
<h3>Views</h3>
<p>In one of my views I wanted to test whether a filtered result set was empty or not.  I was curious whether this was the "pythonic" way to accomplish this:</p>
<pre class="brush: python; title: ;">
account = get_object_or_404(Account, pk=account_id)
if account.useraccount_set.filter(user__exact=request.user) != []:
</pre>
<p>Also, with respect to views and passing context to the response, sometimes it's an efficient shortcut to use <strong>locals()</strong> instead of explicitly typing out all the variables you'd like to expose.  locals() returns a dictionary of all variables defined within the local scope.</p>
<pre class="brush: python; title: ;">
def myview(request, id):
    account = get_object_or_404(Account, pk=id)
    new_account_form = NewAccountForm()

    return render_to_response('myview.html', locals())
</pre>
<p>More soon!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/04/22/django-url-parameter-passing-and-python-strings/' rel='bookmark' title='Permanent Link: Django URL Parameter Passing and Python Strings'>Django URL Parameter Passing and Python Strings</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>PHP fast, large (megabyte), data transfer between sessions</title>
		<link>http://blog.perplexedlabs.com/2008/02/12/php-fast-large-megabyte-data-transfer-between-sessions/</link>
		<comments>http://blog.perplexedlabs.com/2008/02/12/php-fast-large-megabyte-data-transfer-between-sessions/#comments</comments>
		<pubDate>Tue, 12 Feb 2008 17:24:04 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[serialize]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[unserialize]]></category>
		<category><![CDATA[var_export]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/2008/02/12/php-fast-large-megabyte-data-transfer-between-sessions/</guid>
		<description><![CDATA[The ability to transfer data from one executing script to the next as components of a web application is of the utmost importance. Sometimes the objects in transit can grow in size to 10's of megabytes of data. Nested classes, arrays of smaller classes, arrays of data, indexes, etc. I've been on a pretty long [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/13/php-error-log-viewer/' rel='bookmark' title='Permanent Link: PHP Error Log Viewer'>PHP Error Log Viewer</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/04/09/php-daisy-chain-class-method-calls/' rel='bookmark' title='Permanent Link: PHP Daisy Chain Class Method Calls'>PHP Daisy Chain Class Method Calls</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/' rel='bookmark' title='Permanent Link: PHP Custom MySQL Session Handler'>PHP Custom MySQL Session Handler</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>The ability to transfer data from one executing script to the next as components of a web application is of the utmost importance.  Sometimes the objects in transit can grow in size to 10's of megabytes of data.  Nested classes, arrays of smaller classes, arrays of data, indexes, etc.</p>
<p>I've been on a pretty long optimization kick for a lot of the production applications that are used on a daily basis.  Most applications use an "extended database listing" class that extracts raw data from a mySQL table and provides interfaces to add columns using that base data and sort, format, and highlight the data.  The process works in two phases - the second being an AJAX request to actually deliver the HTML output to the browser.  In choosing to go this route (the originating page now finishes execution faster and displays faster) it's necessary to pass the list object from the originating script to the AJAX display helper script.  It is this object that, depending on the size and data in the list, can become enormous.</p>
<p>Initially I had been using the tried and true serialize() / unserialize() on the object and storing it in the $_SESSION superglobal.  As soon as the object becomes anywhere "unusually" large, the unserialize call will take an exponentially increasing # of seconds to complete.  Not satisfied with this situation I searched and searched for a solution.</p>
<p>One solution I've come across that cuts execution time by 50% is the use of var_export() instead.  var_export() will, by default, output a php parsable representation of the variable passed to it.  In my case, the object being exported was a class.  PHP &gt; 5.1.0 changed the way var_export worked on class objects.  You now have to implement the magic function __set_state() in your class in order to correctly handle a class exported with var_export().  On the other end of the process, where you would normally use unserialize(), now all you do is include the file you exported to.</p>
<p>Here is my implementation of the __set_state() magic function:</p>
<pre class="brush: php; title: ;">
public static function __set_state($array)
{
    $obj = new cListExtDB;
    foreach($array as $field =&gt; $val) {
        $obj-&gt;$field = $array[$field];
    }
    return $obj;
}
</pre>
<p>The following example illustrates the exporting side of this procedure:</p>
<pre class="brush: php; title: ;">
&lt;?php
$_SESSION['_list_hash'] = $hash;
file_put_contents('_list'.$hash.'.dat', '&lt;?php $__data = '.var_export($object, true).'; ?&gt;');
?&gt;
</pre>
<p>And in the AJAX helper script, the following example illustrates the importing side of this procedure:</p>
<pre class="brush: php; title: ;">
&lt;?php
$fname = '_list'.$_SESSION['_list_hash'].'.dat';
if(file_exists($fname)) {
    include($fname);
    unlink($fname);
    $list = $__data;
}
// proceed to operate with $list as it existed when exported in originating script
?&gt;
</pre>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/13/php-error-log-viewer/' rel='bookmark' title='Permanent Link: PHP Error Log Viewer'>PHP Error Log Viewer</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/04/09/php-daisy-chain-class-method-calls/' rel='bookmark' title='Permanent Link: PHP Daisy Chain Class Method Calls'>PHP Daisy Chain Class Method Calls</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/' rel='bookmark' title='Permanent Link: PHP Custom MySQL Session Handler'>PHP Custom MySQL Session Handler</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2008/02/12/php-fast-large-megabyte-data-transfer-between-sessions/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

