<?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>Andrew's Tech Musings</title>
	<atom:link href="http://andrewcurioso.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://andrewcurioso.com</link>
	<description>Tech, Social Media, PHP, Opinions</description>
	<lastBuildDate>Mon, 04 Jun 2012 18:02:42 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>The high tech scene in New Hampshire</title>
		<link>http://andrewcurioso.com/2012/06/the-high-tech-scene-in-new-hampshire/</link>
		<comments>http://andrewcurioso.com/2012/06/the-high-tech-scene-in-new-hampshire/#comments</comments>
		<pubDate>Sun, 03 Jun 2012 05:35:57 +0000</pubDate>
		<dc:creator>Andrew Curioso</dc:creator>
				<category><![CDATA[Community]]></category>
		<category><![CDATA[Boston]]></category>
		<category><![CDATA[community]]></category>
		<category><![CDATA[New Hampshire]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://andrewcurioso.com/?p=757</guid>
		<description><![CDATA[As many of you know, I moved to New Hampshire a couple years ago from Boston. It&#8217;s not as big a move as it sounds. I can still get to Boston in 45 minutes if traffic permits me. Plus I have all the fresh air, hiking, biking, and great restaurants I want. But it is [...]]]></description>
			<content:encoded><![CDATA[<p>As many of you know, I moved to New Hampshire a couple years ago from Boston.</p>
<p>It&#8217;s not as big a move as it sounds. I can still get to Boston in 45 minutes if traffic permits me. Plus I have all the fresh air, hiking, biking, and great restaurants I want.</p>
<p>But it is a huge move in terms of developer community. I find myself routinely driving to Boston for the always great <a href="http://www.bostonphp.org/">Boston PHP</a> meetup but most other meetups I dropped entirely because I can&#8217;t justify the commute.<span id="more-757"></span></p>
<p>I have yet to meet a single PHP developer in New Hampshire. I know they exist, they are just very difficult to find! Developers need to network and mingle with like-minded people and share ideas too. The business and marketing people can&#8217;t have all the fun.</p>
<p>There is a growing start-up scene in New Hampshire, in no small part thanks to organizations like <a href="http://abihub.org/">abi Innovation Hub</a> in Manchester &#8212; where my company currently has it&#8217;s office &#8212; and New Hampshire based companies like <a href="http://dyn.com/">Dyn</a> (yes, that Dyn).</p>
<p>In fact <a href="http://www.s.co/press-release/startup-new-hampshire-joins-ranks-startup-america-regions">Startup New Hampshire just joined forces with Startup America</a>. But the infrastructure of developer meetups and community that you get in established tech hubs isn&#8217;t there yet. Unless I missed it or it is hiding in the bushes waiting to pounce. A search of <a href="http://www.meetup.com">Meetup.com</a> finds a bunch of business, marketing, and recreational meetups but only a handful of tech meetups. Some of which are ghost towns.</p>
<p><img class="alignright" src="data:image/png;base64,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" alt="" width="180" height="162" /></p>
<p>That has to change. And I might as well be the one to try to change it. I&#8217;m not sure if this will work but I&#8217;ve taken some of my own money and started a Meetup group. That&#8217;s right, I upgraded to an organizer account on Meetup.com, registered a domain name, and drew a fun logo that looks strangely like the PHP elePHPant only <strong>it is a moose</strong> (because&#8230; you know&#8230; New Hampshire). This is serious stuff.</p>
<p>And I encourage other people to do the same with their own favorite technologies. Well, except the blue moose logo part, it only makes sense for PHP anyway since the PHP mascot is an elephant.</p>
<p>Now all I need is members. Lots of them so that I can attract great guest speakers.</p>
<p>If you or someone you know is in New Hampshire and is a PHP developer (or even just a general web developer) let them know about the new meetup group at <a href="http://www.NewHampshirePHP.org">www.NewHampshirePHP.org</a>.</p>
<p>Once I get 10 members I will start finding a venue and sponsors for the first event. I&#8217;ll do my best to make it a fun and worthwhile for everyone involved.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewcurioso.com/2012/06/the-high-tech-scene-in-new-hampshire/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Counting to 1000 in PHP without loops or conditionals</title>
		<link>http://andrewcurioso.com/2011/03/counting-to-1000-in-php-without-loops-or-conditionals/</link>
		<comments>http://andrewcurioso.com/2011/03/counting-to-1000-in-php-without-loops-or-conditionals/#comments</comments>
		<pubDate>Tue, 15 Mar 2011 00:54:45 +0000</pubDate>
		<dc:creator>Andrew Curioso</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[hacking]]></category>

		<guid isPermaLink="false">http://andrewcurioso.com/?p=690</guid>
		<description><![CDATA[Update: added a benchmark for performance comparison and updated the code to no longer rely on a fatal error to exit. It started with one question: Print numbers from 1 to 1000 without using any loop or conditional statements. Don&#8217;t just write the printf() or cout statement 1000 times. How would you do that using [...]]]></description>
			<content:encoded><![CDATA[<p class="update"><strong>Update:</strong> added a benchmark for performance comparison and updated the code to no longer rely on a fatal error to exit.</p>
<p>It started with one question:</p>
<blockquote><p>
Print numbers from 1 to 1000 without using any loop or conditional statements. Don&#8217;t just write the printf() or cout statement 1000 times.</p>
<p>How would you do that using C or C++?<br />
<a class="citation" href="http://stackoverflow.com/questions/4568645/printing-1-to-1000-without-loop-or-conditionals/4583502">Source: Stack Overflow</a>
</p></blockquote>
<p>My favorite answer was an amazing example of obscure C. Using math to iterate between two function pointers (printf and exit). And while it brought back memories of my C days, I kind of left it at that.</p>
<p>Later in the day, one of my co-workers messaged me with his version of a 1-1000 iterator in PHP. So naturally, I had to write my own version.</p>
<p><span id="more-690"></span></p>
<p>Remember the rules: no control loops and no conditionals.</p>
<p>I hammered out a quick example using an array to simulate the pointer technique used in my favorite C-language solution. It was 10 lines of code. I managed to break it down to one function call:</p>
<pre class="brush: php; title: ; notranslate">
call_user_func( $x = function( $f, $i=1 ) {
  echo &quot;$i\n&quot;, $f[floor($i/1000)]($f, ++$i);
}, array( $x, function(){} ));
</pre>
<p>Try it. Assuming that you have PHP 5.3 for the lambda functions, it will work. Now to break down why it works:</p>
<p>The call_user_function call takes a callback as the first parameter and passes all subsequent parameters directly to the callback. A callback in PHP can be a string, array, or lambda function. I&#8217;m using the later here.</p>
<p>My lambda function takes two parameters. An array and a counter. If the counter is not specified it will default to one. The trickier part is the array. In this case it is an array of functions. My function is recursive so the array needs to reference it. By taking advantage of the left-to-right nature of PHP, I am assigning the function to the variable $x prior to the array being constructed.</p>
<p>Now, inside my function I am echoing the value of $i and the return value of the recursive function (which is undefined&#8230; it&#8217;s just a little cheat to keep it on one line).</p>
<p>Now to break down the second parameter to the echo.</p>
<p>It looks up a function in the array that is passed in. Remember, the array contains only <del>one value (the function itself)</del> two values (the function itself and an empty &#8220;exit&#8221; function). It uses the floor of $i/1000 as the array index. Which means that the value will be 0 until $i is equal to 1000. Then it passes in the array and the counter. Remember that if the &#8220;++&#8221; is before the variable then the increment will happen prior to the enclosing function being called.</p>
<p>When the index does hit 1, <del>there will be an error. I suppressed that using the &#8220;@&#8221; operator</del> it will call the empty function. At this point the recursion ends.</p>
<p>And we&#8217;re done.</p>
<h3>Benchmarks (added March 15th)</h3>
<p>This is obviously meant to be a thought exercise and not a practical thing to do in a real-world application but I thought it might be fun to run some tests against a normal loop. I upped the loop to 1,000,000 to make things more interesting. Here is my test code:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

ini_set('memory_limit', '1000M');
const COUNT = 1000000;

$t1 = 0;
$t2 = 0;

$s = microtime(true);

call_user_func( $x = function( $f, $i=1 ) {
  echo &quot;$i\n&quot;,  $f[floor($i/COUNT)]($f, ++$i);
}, array( $x, function(){} ));

$t1 = microtime(true)-$s;

$s = microtime(true);

for ( $i=0; $i&lt;COUNT; $i++ ) {
  echo &quot;$i\n&quot;;
}

$t2 = microtime(true)-$s;

echo 'Recursive: '.number_format($t1,2).&quot;s\n&quot;;
echo 'Loop: '.number_format($t2,2).&quot;s\n&quot;;
?&gt;
</pre>
<p>The results:</p>
<pre class="brush: plain; title: ; notranslate">
Recursive: 6.38s
Loop: 5.28s
</pre>
<p>Not to mention that every recursive call needs to add the current state to the stack. Which is why the memory limit is increased the the beginning of the script. So clearly it is impractical. However, I&#8217;m actually a little surprised that the results are as close at they are.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewcurioso.com/2011/03/counting-to-1000-in-php-without-loops-or-conditionals/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>RIA: Desktop Notifications in Google Chrome</title>
		<link>http://andrewcurioso.com/2011/01/ria-desktop-notifications-in-google-chrome/</link>
		<comments>http://andrewcurioso.com/2011/01/ria-desktop-notifications-in-google-chrome/#comments</comments>
		<pubDate>Thu, 27 Jan 2011 17:37:36 +0000</pubDate>
		<dc:creator>Andrew Curioso</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[early adopters]]></category>
		<category><![CDATA[Tips]]></category>

		<guid isPermaLink="false">http://andrewcurioso.com/?p=596</guid>
		<description><![CDATA[I&#8217;ve seen a number of blog posts floating around today about GMail Desktop Notifications (here, here, here, here and here &#8212; did I miss anyone?). I tried them out myself and they are very useful. Being a rich web applications developer I, of course, wanted to figure out how it works and how I could [...]]]></description>
			<content:encoded><![CDATA[<pre style="display:none"><script type="text/javascript">
<!--
function demoCheck() {
  var c = ( window.webkitNotifications !== undefined );
  if ( !c ) alert("Your browser does not support desktop notifications using this method. Try out the demos in Google Chrome!");
  return c;
}

function demoCheckPermission() {
  if ( !demoCheck() ) return;
  switch ( webkitNotifications.checkPermission() )
  {
    case 0: // PERMISSION_ALLOWED
      alert( "Permission: allowed" );
      break;
    case 1: // PERMISSION_NOT_ALLOWED
      alert( "Permission: not allowed" );
      break;
    case 2: // PERMISSION_DENIED
      alert( "Permission: denied" );
      break;
  }
}

function demoRequestPermission() {
  if ( !demoCheck() ) return;
  webkitNotifications.requestPermission();
}

function demoNotify() {
  if ( !demoCheck() ) return;
  if ( webkitNotifications.checkPermission() == 0 )
  {
    var iconImageUrl = "http://0.gravatar.com/avatar/25c07794aa15e4173b0f8b5c3f66c66b?s=64&#038;d=http://0.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536%3Fs%3D32&#038;r=G";
    var title = "Andrew sent you a message";
    var subTitle = "Hello world.";

    var notification = webkitNotifications.createNotification( iconImageUrl, title, subTitle );
    notification.show();
  }
  else
  {
    alert( "Please request permissions first." );
  }
}

function demoHtmlNotify() {
  if ( !demoCheck() ) return;
  if ( webkitNotifications.checkPermission() == 0 )
  {
    var url = "http://andrewcurioso.com/wp-content/custom/2011/notification.html";
    var notification = webkitNotifications.createHTMLNotification( url );
    notification.show();
  }
  else
  {
    alert( "Please request permissions first." );
  }
}

function demoTimerNotify() {
  if ( !demoCheck() ) return;
  if ( webkitNotifications.checkPermission() == 0 )
  {
    var iconImageUrl = "http://0.gravatar.com/avatar/25c07794aa15e4173b0f8b5c3f66c66b?s=64&#038;d=http://0.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536%3Fs%3D32&#038;r=G";
    var title = "Andrew sent you another message";
    var subTitle = "This notification will disappear in 10 seconds.";

    var notification = webkitNotifications.createNotification( iconImageUrl, title, subTitle );
    notification.show();
    setTimeout( function() { notification.cancel() }, 10000 );
  }
  else
  {
    alert( "Please request permissions first." );
  }
}
//--></script></pre>
<p>I&#8217;ve seen a number of blog posts floating around today about GMail Desktop Notifications (<a href="http://downloadsquad.switched.com/2011/01/26/gmail-enables-html5-powered-desktop-notifications/?utm_source=feedburner&#038;utm_medium=feed&#038;utm_campaign=Feed%3A+weblogsinc%2Fdownloadsquad+%28Download+Squad%29&#038;utm_content=Google+Reader">here</a>, <a href="http://lifehacker.com/5744356/gmail-now-has-desktop-notifications-baked-in-for-chrome">here</a>, <a href="http://mashable.com/2011/01/27/gmail-desktop-notifications/">here</a>, <a href="http://www.engadget.com/2011/01/27/google-adds-html5-gmail-and-gtalk-notifications-for-the-desktop/">here</a> and <a href="http://googlesystem.blogspot.com/2011/01/gmail-desktop-notifications.html">here</a> &#8212; did I miss anyone?). I tried them out myself and they are very useful. Being a rich web applications developer I, of course, wanted to figure out how it works and how I could use it for my own apps. Here&#8217;s a quick overview of what I found.<br />
<span id="more-596"></span></p>
<p>There is a proposed web notification standard over at the W3C that Google submitted earlier this month (you can find it on the <a href="http://dev.w3.org/2006/webapi/WebNotifications/publish/Notifications.html">W3C site</a>) but from what I can tell, that draft isn&#8217;t implemented in any browsers (including Google Chrome!). But  you can get desktop notifications today without the need for any third-party browser extensions, if you&#8217;re using Google Chrome. You can find the documentation <a href="http://www.chromium.org/developers/design-documents/desktop-notifications/api-specification">over on the Chromium developer site</a>. The rest of this post is a collection of quick examples I put together for using notifications:</p>
<h3>Checking for permission</h3>
<p>It is pretty easy to check to see if the user has allowed notifications for your website. The first example displays an alert depending on the permission level.</p>
<pre class="brush: jscript; title: ; notranslate">
switch ( webkitNotifications.checkPermission() )
{
  case 0: // PERMISSION_ALLOWED
    alert( &quot;Permission: allowed&quot; );
    break;
  case 1: // PERMISSION_NOT_ALLOWED
    alert( &quot;Permission: not allowed&quot; );
    break;
  case 2: // PERMISSION_DENIED
    alert( &quot;Permission: denied&quot; );
    break;
}
</pre>
<p>Try it: <a href="javascript: demoCheckPermission()">Check Permission</a></p>
<p>Unless you skipped ahead and tried the next bit of code, the answer is &#8220;No&#8221; my site doesn&#8217;t have permission to display notifications.</p>
<h3>Requesting Permission</h3>
<p>So before we do anything we need to ask for permission.</p>
<p>This method will only work when responding to a user gesture. Which means you can call it as a direct response to an action taken by the user; but you can&#8217;t call it (for example) on the response to an Ajax function. So if you wanted to call it when your Ajax call returns or when a timer fires, you&#8217;re out of luck. You need to request permission before then.</p>
<pre class="brush: jscript; title: ; notranslate">
webkitNotifications.requestPermission();
</pre>
<p>Try it: <a href="javascript: demoRequestPermission()">Request Permission</a></p>
<h3>Creating and Showing Notification</h3>
<p>Notably, showing a standard browser alert dialog when permission is denied is a terrible user experience. Don&#8217;t do that! This is just for demo purposes.</p>
<pre class="brush: jscript; title: ; notranslate">
if ( webkitNotifications.checkPermission() == 0 )
{
  var iconImageUrl = &quot;http://www.example.com/foo.png&quot;;
  var title = &quot;Hello World&quot;;
  var subTitle = &quot;This is a sample desktop notification.&quot;

  var notification = webkitNotifications.createNotification( iconImageUrl, title, subTitle );
  notification.show();
}
else
{
  alert( &quot;Please request permissions first.&quot; );
}
</pre>
<p>Try it: <a href="javascript: demoNotify()">Show Notification</a></p>
<p>The new notification looks something like this (for those of you not using Chrome and want to see what I am talking about):</p>
<div id="attachment_620" class="wp-caption alignnone" style="width: 318px"><a href="http://andrewcurioso.com/wp-content/uploads/2011/01/Screen-shot-2011-01-27-at-11.30.06-AM.png"><img src="http://andrewcurioso.com/wp-content/uploads/2011/01/Screen-shot-2011-01-27-at-11.30.06-AM.png" alt="Andrew sent you a message" title="Example Google Chrome Notification" width="308" height="80" class="size-full wp-image-620" /></a><p class="wp-caption-text">Example Notification (Google Chrome on OS X)</p></div>
<h3>Creating and Showing an HTML Notification</h3>
<p>You can also create a notification by passing in a URL.</p>
<pre class="brush: jscript; title: ; notranslate">
if ( webkitNotifications.checkPermission() == 0 )
{
  var url = &quot;http://www.example.com/notification.html&quot;;
  var notification = webkitNotifications.createHTMLNotification( url );
  notification.show();
}
else
{
  alert( &quot;Please request permissions first.&quot; );
}
</pre>
<p>Try it: <a href="javascript: demoHtmlNotify()">Show Notification</a></p>
<p>At first this seems like a terrible idea. It could be yet another way to have annoying advertisements pop up on a site. But it&#8217;s not as bad as it sounds since the user has to explicitly allow the notifications and they can be turned off at any time.</p>
<h3>One Final Example</h3>
<p>Now, you probably don&#8217;t want the notification to pop up and stay there forever. So, you can auto-hide it using a timer.</p>
<pre class="brush: jscript; title: ; notranslate">
if ( webkitNotifications.checkPermission() == 0 )
{
  var iconImageUrl = &quot;http://www.example.com/foo.png&quot;;
  var title = &quot;Hello World&quot;;
  var subTitle = &quot;This is a sample desktop notification.&quot;

  var notification = webkitNotifications.createNotification( iconImageUrl, title, subTitle );
  notification.show();
  setTimeout( function() { notification.cancel() }, 10000 );
}
else
{
  alert( &quot;Please request permissions first.&quot; );
}
</pre>
<p>Try it: <a href="javascript: demoTimerNotify()">Show Notification</a></p>
<h3>Wrapping it Up</h3>
<p>I hope someone found this useful. If you do anything cool with this library please let me know in the comments.</p>
<p class="pitfall">Note: this is an experimental API that only works in Google Chrome right now. The API may change at any time and may be different when other browsers decide to implement similar functionality. Use with caution. Always check to make sure <code>window.webkitNotifications !== undefined</code>.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewcurioso.com/2011/01/ria-desktop-notifications-in-google-chrome/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>My 2011 New Year&#8217;s resolutions</title>
		<link>http://andrewcurioso.com/2011/01/my-2011-new-years-resolutions/</link>
		<comments>http://andrewcurioso.com/2011/01/my-2011-new-years-resolutions/#comments</comments>
		<pubDate>Mon, 17 Jan 2011 15:47:24 +0000</pubDate>
		<dc:creator>Andrew Curioso</dc:creator>
				<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://andrewcurioso.com/?p=586</guid>
		<description><![CDATA[I haven&#8217;t written a blog posts in a while so it figured a good start might be to make some resolutions for myself in the new year (yes, I know that January is almost over). Here are few things I want to improve: First, and I&#8217;ve been saying this for a while, I need to [...]]]></description>
			<content:encoded><![CDATA[<p>I haven&#8217;t written a blog posts in a while so it figured a good start might be to make some resolutions for myself in the new year (yes, I know that January is almost over).</p>
<p>Here are few things I want to improve:<br />
<span id="more-586"></span><br />
First, and I&#8217;ve been saying this for a while, I need to <strong>start blogging more</strong>. This post is a okay start but I want to finish my initiative that I start back in December. Which is to to break the site up into code in business and to post in each of those categories at least once a month. Once I get there, I can see where it goes.</p>
<p>Second, I&#8217;m going to <strong>start replying to more e-mails</strong>. This may seem like a no-brainer but I haven&#8217;t been in the habit of replying to e-mails that aren&#8217;t urgent or work related. So a lot of e-mails and the going unanswered. I want to change that.</p>
<p>Third, I want to <strong>participate more in the open source world</strong>. I work for start up so it&#8217;s difficult to find any time to work on open-source projects, but I imagine I can find a little time here and there and I want to all get a bit more involved with the community. I believe open source is fundamental to innovation and I think I can make some valuable contributions.</p>
<p>Fourth, and this is really my only non-tech related goal, I want to <strong>be more active in my life</strong>. Being a software engineer it&#8217;s sometimes easy to get into a rut where you&#8217;re just sitting at a desk 12+ hours a day staring at a computer screen. It&#8217;s clearly not the most healthy thing in the world. So my final resolution is to get out and be more active on both in the community and also in a more traditional sense (exercising, eating right, etc.).</p>
<p>Usually don&#8217;t like to post about my personal life but I want to get this out there for everybody to see. Maybe it will provide a bit of motivation. I know that some of you out there will start nagging me if I fall behind (thank you for that). And finally I wish you all the absolute best in this new year and with everything you do.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewcurioso.com/2011/01/my-2011-new-years-resolutions/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Error handling stack in PHP 5.3+</title>
		<link>http://andrewcurioso.com/2010/10/error-handeling-stack-in-php-5-3/</link>
		<comments>http://andrewcurioso.com/2010/10/error-handeling-stack-in-php-5-3/#comments</comments>
		<pubDate>Wed, 13 Oct 2010 15:49:49 +0000</pubDate>
		<dc:creator>Andrew Curioso</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://andrewcurioso.com/?p=518</guid>
		<description><![CDATA[Update: this article is mentioned in a few places as a practical example of using closures. Some languages pass variables into a closure automatically. In PHP it needs to be done explicitly using the use keyword. See line #6 of the code example. I was inspired by a question that I was asked on Twitter [...]]]></description>
			<content:encoded><![CDATA[<p class="update"><strong>Update:</strong> this article is mentioned in a few places as a practical example of using <strong>closures</strong>.<br />
Some languages pass variables into a closure automatically. In PHP it needs to be done explicitly using the <strong>use</strong> keyword. See line #6 of the code example.</p>
<p>I was inspired by a question that I was asked on Twitter to write a quick code snippet.</p>
<p>As you may know, set_error_handler can be used to set a custom error handler in PHP. It will catch any errors that happen in the script (with a few notable exceptions). If the function returns false then error handling resumes as normal; otherwise it is assumed that the custom handler took care of things. The problem is that you can only have one error handler active at one time. The purpose of this code is to provide a error handeling stack for PHP.</p>
<p>Using this code you can have more than one error handler while taking advantage of the set_error_handler function.<br />
<span id="more-518"></span><br />
Because this example uses closures, it will only work in PHP 5.3 or newer.</p>
<pre class="brush: php; title: ; notranslate">
function push_error_handler( $error_handler, $error_types = 32767 )
{
  $old_callback = null;

  $callback = function( $errno, $errstr, $errfile, $errline, $errcontext )
  use ( &amp;$old_callback, $error_handler )
  {
    $result = call_user_func($error_handler, $errno , $errstr, $errfile, $errline, $errcontext);

    if ( $result === false &amp;&amp; $old_callback != null )
      return call_user_func( $old_callback, $errno , $errstr, $errfile, $errline, $errcontext );

    return $result;
  };

  $old_callback = set_error_handler($callback, $error_types);
}
</pre>
<p>Let&#8217;s see it in action:</p>
<pre class="brush: php; title: ; notranslate">
function test1( $a, $b, $c, $d, $e ) { echo &quot;Test 1 -- &quot;; return false; }
function test2( $a, $b, $c, $d, $e ) { echo &quot;Test 2 -- &quot;; return false; }
function test3( $a, $b, $c, $d, $e ) { echo &quot;Test 3 -- &quot;; return false; }
function test4( $a, $b, $c, $d, $e ) { echo &quot;Test 4 -- &quot;; return false; }

push_error_handler( 'test1' );
push_error_handler( 'test2' );
push_error_handler( 'test3' );
push_error_handler( 'test4' );

trigger_error('testing');
</pre>
<p>The output is:</p>
<pre class="brush: plain; title: ; notranslate">
Test 4 -- Test 3 -- Test 2 -- Test 1 --
Notice: testing in /Users/andrew/recursive_error_handler.php on line 32
</pre>
<p>It is worth a closer look. First we initialize a variable that will be needed by the closure later on. Then we define the closure (which will be our actual error handling function) and register it as the error handler. The function set_error_handler returns the old handler which we then assign to the variable that we created earlier. We also return the old handler to keep compatibility with the normal set_error_handler.</p>
<p>The closure itself calls the new error handler. If the handler returned false (resume error handeling) then we call the previous error handler (if there was one). We also make sure to pass the error code and the old callback function into the closure. It is important that the old callback is passed by reference (note the &#8220;&#038;&#8221;).</p>
<p>If you want to learn some more, here are some handy links:<br />
PHP Manual for <a href="http://us2.php.net/manual/en/function.set-error-handler.php">set_error_handler</a> and <a href="http://php.net/manual/en/functions.anonymous.php">anonymous functions and closures</a>.<br />
My book, <a href="http://www.amazon.com/gp/product/0470563125?ie=UTF8&#038;tag=365note-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0470563125">Expert PHP and MySQL</a><img src="http://www.assoc-amazon.com/e/ir?t=365note-20&#038;l=as2&#038;o=1&#038;a=0470563125" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />, also talks about closures in depth starting on page 78.</p>
<p class="pitfall">Note: this method only works if almost all the error handlers in the application use this or a similar method. Otherwise the chain will be broken. I say &#8220;almost&#8221; because the first registered error handler doesn&#8217;t have to worry about calling the previous handler since there isn&#8217;t any at that point.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewcurioso.com/2010/10/error-handeling-stack-in-php-5-3/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Welcome CakeFest 2010 Attendees</title>
		<link>http://andrewcurioso.com/2010/09/welcome-cakefest-2010-attendees/</link>
		<comments>http://andrewcurioso.com/2010/09/welcome-cakefest-2010-attendees/#comments</comments>
		<pubDate>Sat, 04 Sep 2010 12:37:31 +0000</pubDate>
		<dc:creator>Andrew Curioso</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://andrewcurioso.com/?p=461</guid>
		<description><![CDATA[I noticed a spike in my traffic which can only mean one thing: welcome, CakeFest attendees, to my home on the web! When conference day two roles around you&#8217;ll be able to find all my presentation notes here and I will be uploading the slides as well. As you may know, I will be talking [...]]]></description>
			<content:encoded><![CDATA[<p>I noticed a spike in my traffic which can only mean one thing: welcome, CakeFest attendees, to my home on the web!</p>
<p>When conference day two roles around you&#8217;ll be able to find all my presentation notes <a href="http://andrewcurioso.com/2010/06/cakefest-2010/">here</a> and I will be uploading the slides as well.</p>
<p>As you may know, I will be talking on API Development with CakePHP. It is a complex topic so my presentation won&#8217;t be the end of it, I will be adding additional information over time. So I hope that you <a href="http://andrewcurioso.com/feed/">subscribe to me via RSS</a>.</p>
<p>Happy coding!</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewcurioso.com/2010/09/welcome-cakefest-2010-attendees/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Authentication vs. Authorization</title>
		<link>http://andrewcurioso.com/2010/07/authentication-vs-authorization/</link>
		<comments>http://andrewcurioso.com/2010/07/authentication-vs-authorization/#comments</comments>
		<pubDate>Wed, 14 Jul 2010 19:03:40 +0000</pubDate>
		<dc:creator>Andrew Curioso</dc:creator>
				<category><![CDATA[Security]]></category>
		<category><![CDATA[curl]]></category>
		<category><![CDATA[hacking]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://andrewcurioso.com/?p=402</guid>
		<description><![CDATA[This seems like a no-brain-er but I have seen it more times than I can count and I have seen it happen to some very experienced developers. Put simply: authentication is not enough; you need to make sure that the authenticated user is actually authorized to perform an action. It is one thing to know [...]]]></description>
			<content:encoded><![CDATA[<p>This seems like a no-brain-er but I have seen it more times than I can count and I have seen it happen to some very experienced developers. Put simply: <strong>authentication</strong> is not enough; you need to make sure that the authenticated user is actually <strong>authorized</strong> to perform an action. It is one thing to know who a user is and an entirely different &#8212; though equally important &#8212; thing to know what a user is allowed to do.</p>
<p>This article covers the concepts of authentication and authorization.</p>
<p><span id="more-402"></span></p>
<p>Authentication is knowing who the logged in user is. Authorization is knowing what the user can and can&#8217;t do and can be as easy as checking that the user ID in the database matches the user ID of the authenticated user. It can also be as complex  as Access Control Lists, social graphs, and multi-moderator systems. Either way it needs to be taken care of. Very few things are more devastating than when a malicious user finds that all you have to do to edit another user&#8217;s data is to change the value of a POST variable or all you need to do to access someone&#8217;s private photos is to change a URL. Do either of those two cases sound familiar?</p>
<h2>Thinking like a hacker</h2>
<blockquote><p>
If I were to try to exploit this feature: how would I go about it?<br />
<span class="citation">you, every time you write code (hopefully)</span>
</p></blockquote>
<p>Let&#8217;s start by looking at a imaginary site that makes an Ajax request to delete a resource via a RESTful API. The HTTP request probably looks a bit like this:</p>
<pre class="brush: plain; title: ; notranslate">
POST /resources/1234.json HTTP/1.1
Host: www.example.com
Content-Length: 14

_method=DELETE
</pre>
<p>Now, make the assumption that you can find the ID of any resource on the system. This could be because it is exposed in a &#8220;view&#8221; URL or it could be that it is returned in an API call for search. Either way, assume that if it exists a hacker can find it either by design or through flaws in the code. Never assume primary keys are a secret. The next step is to find an ID of a resource not owned by the hacker (in this example: 5678) and to go the command line (assuming cURL is installed on your system):</p>
<pre class="brush: plain; title: ; notranslate">
curl -d &quot;_method=delete&quot; http://www.example.com/resources/5678.json
</pre>
<p>Curl doesn&#8217;t share cookies with the web browser so if that call succeeded and record 5678 was actually deleted then the application is not checking to see if the user is <strong>authenticated</strong>. There is no need to go any further, you&#8217;ve already found a devastating exploit. If this is your application (I hope that you aren&#8217;t using this article to try to hack other people&#8217;s apps!) it is time to go back to the code, add a check to make sure that the user is logged in. Then come back here to read on.</p>
<p>If the call didn&#8217;t work then it is time to try to delete the resource as an authenticated (but hypothetically not authorized) user:</p>
<pre class="brush: plain; title: ; notranslate">
curl -d &quot;username=you&amp;password=abcdefg&quot; -c &quot;cookies.txt&quot; http://www.example.com/login/
curl -d &quot;_method=delete&quot; -b &quot;cookies.txt&quot; http://www.example.com/resources/5678.json
</pre>
<p>The first line authenticates the user and stores the cookies. The second line tries the delete method again with the new cookies. Make sure to replace all the appropriate variables and URLs in all of these examples. If all went <del>well</del> bad the resource 5678 should now be deleted. If that happened then the application needs to check for <strong>authorization</strong> as well as authentication.</p>
<p>The same concepts can be applied to viewing, editing, and creating resources. This article uses cURL but there are numerous other ways of spoofing Ajax and API requests, including injecting Javascript into the browser and writing a PHP / Perl / Ruby / Python / etc. script to do it.</p>
<p class="pitfall">One thing to watch out for is any request that takes the user ID as a parameter. It should raise a red flag. Whenever possible, get the user ID from the currently authenticated user. I once saw a password vault web application that returned the entire password list from a SOAP call given only the user ID. Just so you are sufficiently mortified, I&#8217;ll rephrase it: I could enter in any user&#8217;s ID and get back a list of passwords for other sites on the Internet (including Google). Don&#8217;t let that happen to you!</p>
<h2>Taking care of business</h2>
<p>Addressing the problem takes as much thought and planning then actually technical know-how. Imagine a user for each role (resource owner, administrator, moderator, customer support, friend of the user &#8212; if you are a social network &#8212; etc.) then ask three questions:</p>
<ol>
<li>User X can/can&#8217;t view resource Y because&#8230;</li>
<li>User X can/can&#8217;t edit resource Y because&#8230;</li>
<li>User X can/can&#8217;t delete resource Y because&#8230;</li>
</ol>
<p>Then for each of those, check to make sure the code reinforces that statement. It is also a good idea to give these stories to the testers and have them try to break your code. And remember: the authentication system tells you WHO the user is and authorization system tells you WHAT actions the user can perform.</p>
<p>How complex a system you need for authorization depends on your application. It can range from one-off code to full-featured generic systems that can be used for any type of resource imaginable. Social networks are the most complicated of the bunch because authorization often depends on a personal relationship with the user requesting access to the resource. The simplest form of authentication is:</p>
<pre class="brush: php; title: ; notranslate">
if ( $user-&gt;id != $resource-&gt;owner_id )
  throw new Exception(&quot;Access denied&quot;);
</pre>
<p>Or if your application is a social network and you give friend&#8217;s access to resources:</p>
<pre class="brush: php; title: ; notranslate">
if ( $user-&gt;id != $resource-&gt;owner_id &amp;&amp; !$user-&gt;isFriendsWith($resource-&gt;owner_id) )
  throw new Exception(&quot;Access denied&quot;);
</pre>
<p>For a more robust system you&#8217;ll probably want to implement an Access Control List (ACL). An ACL at its core is just a mapping of users to resources. For example: Joe has view, edit, and delete access to resource 1234.</p>
<p>More advanced access control lists also have groups (called &#8220;roles&#8221;) and they can cascade. Roles introduce some ambiguity, and multiple entries in the list may govern the same action. If that happens, the most specific one is taken. For example, editing a resource may be governed by the rules:</p>
<ul>
<li>&#8220;Joe&#8221; is in the group &#8220;Basic Users&#8221; and &#8220;Basic Users&#8221; explicitly can NOT edit resources of type &#8220;forum post&#8221;
	</li>
<li>&#8220;Joe&#8221; CAN edit resources of type &#8220;forum post&#8221; with ID &#8220;1234&#8243;
</li>
</ul>
<p>Since Joe has edit rights to the forum post with an ID of 1234 it doesn&#8217;t matter that the role Joe plays (a &#8220;Basic User&#8221;) cannot edit any forum posts.  There are numerous articles on implementing an ACL in a PHP application and many frameworks have built-in classes for ACL.</p>
<h2>Summary</h2>
<p>When developing web applications (or any application for that matter): always be cognizant of authentication and authorization. Remember, authentication answers the question of WHO and authorization answers the question of WHAT. The application must always know the answer to both of those questions and be able to deny or allow certain actions based on those answers. It might be useful for newer developers to to actually put themselves in the shoes of a hacker and attempt to find exploits for their own website. Eventually, it will become second nature.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewcurioso.com/2010/07/authentication-vs-authorization/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Detecting file size overflow in PHP</title>
		<link>http://andrewcurioso.com/2010/06/detecting-file-size-overflow-in-php/</link>
		<comments>http://andrewcurioso.com/2010/06/detecting-file-size-overflow-in-php/#comments</comments>
		<pubDate>Thu, 10 Jun 2010 03:55:06 +0000</pubDate>
		<dc:creator>Andrew Curioso</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tips]]></category>

		<guid isPermaLink="false">http://andrewcurioso.com/?p=232</guid>
		<description><![CDATA[One of the things that separates a good web application from a great one is how gracefully they handle failures. One of the often overlooked cases is when a user attempts to upload a file that exceeds the set PHP upload file size. This article shows how to detect when the user tries to upload [...]]]></description>
			<content:encoded><![CDATA[<p>One of the things that separates a good web application from a great one is how gracefully they handle failures. One of the often overlooked cases is when a user attempts to upload a file that exceeds the set PHP upload file size. This article shows how to detect when the user tries to upload a file that is too large and display an appropriate message.<br />
<span id="more-232"></span><br />
This article assumes that you have already set upload_max_filesize, post_max_size, and memory_limit in your php.ini file to appropriate values. It also assumes that you already have a working file upload form. There are plenty of tutorials out there already to get you started. </p>
<p>If you can, you may want to set post_max_size to a low value (say &#8220;1M&#8221;) to make testing easier. </p>
<p>First test to see how your script behaves. Try uploading a file that is larger than post_max_size. If you do you will get a message like this in your error log:</p>
<pre class="brush: plain; title: ; notranslate">
[09-Jun-2010 19:28:01] PHP Warning:  POST Content-Length of 30980857 bytes exceeds the limit of 2097152 bytes in Unknown on line 0
</pre>
<p>If you&#8217;re not careful this can lead to unexpected behavior in your application. The end result can range from silent failure all the way to lost customers.</p>
<h2>Solving the problem</h2>
<p>The PHP documentation provides a hack to solve this problem:</p>
<blockquote><p>If the size of post data is greater than post_max_size, the $_POST and $_FILES  superglobals  are empty. This can be tracked in various ways, e.g. by passing the $_GET variable to the script processing the data, i.e. &lt;form action=&#8221;edit.php?processed=1&#8243;&gt;, and then checking if $_GET['processed'] is set.<br />
<a href="http://php.net/manual/en/ini.core.php" class="citation">Source: PHP manual</a></p></blockquote>
<p>To be clear, it is suggesting that you pass a value in the query string along with your form. If the value is in the $_GET superglobal and both $_FILE and $_POST are empty then the maximum upload size is exceeded. There are two problems with this approach: it adds extra complexity on the front-end and it can potential give a false positive.</p>
<p>Extra complexity on the front-end means extra documentation and more room for mistakes. And if there is a mistake it may not be caught for a long time (does your QA team routinely upload large files?).  In this case we already have all the data that we need to determine if the maximum file size was exceeded without adding extra complexity and headache for developers.</p>
<p>We know what type of request is being processed, we have the $_POST and $_FILES arrays, and we have the content length as it was passed to the HTTP server from the client.  From that we get this code:</p>
<pre class="brush: php; title: ; notranslate">
if ( $_SERVER['REQUEST_METHOD'] == 'POST' &amp;&amp; empty($_POST) &amp;&amp;
     empty($_FILES) &amp;&amp; $_SERVER['CONTENT_LENGTH'] &gt; 0 )
{
  $displayMaxSize = ini_get('post_max_size');

  switch ( substr($displayMaxSize,-1) )
  {
    case 'G':
      $displayMaxSize = $displayMaxSize * 1024;
    case 'M':
      $displayMaxSize = $displayMaxSize * 1024;
    case 'K':
       $displayMaxSize = $displayMaxSize * 1024;
  }

  $error = 'Posted data is too large. '.
           $_SERVER[CONTENT_LENGTH].
           ' bytes exceeds the maximum size of '.
           $displayMaxSize.' bytes.&quot;;
}
</pre>
<p>The important thing to notice is the &#8220;if&#8221; statement on lines one and two. The example code just sets an error string. Production code might display a message to the user, execute some Javascript (for asynchronous uploads), or pass back a XML or Json object for Flash clients.</p>
<p>I&#8217;ve tested this code with Apache as both a module and as CGI. As far as I know it should work fine with IIS as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewcurioso.com/2010/06/detecting-file-size-overflow-in-php/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>A method called &#8216;delete&#8217; in Flex and AS3</title>
		<link>http://andrewcurioso.com/2009/07/a-method-called-delete-in-flex-and-as3/</link>
		<comments>http://andrewcurioso.com/2009/07/a-method-called-delete-in-flex-and-as3/#comments</comments>
		<pubDate>Thu, 02 Jul 2009 05:26:30 +0000</pubDate>
		<dc:creator>Andrew Curioso</dc:creator>
				<category><![CDATA[Flex and AS3]]></category>
		<category><![CDATA[AS3]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Tips]]></category>

		<guid isPermaLink="false">http://andrewcurioso.com/?p=137</guid>
		<description><![CDATA[You are in for a headache if you have try to call a method or create a member variable with the name of a reserved word in Actionscript. It can lead to such fun situations as having variables called: insert; update; deleteSomething. Because calling the third variable &#8220;delete&#8221; may be logical but it is a [...]]]></description>
			<content:encoded><![CDATA[<p>You are in for a headache if you have try to call a method or create a member variable with the name of a reserved word in Actionscript. It can lead to such fun situations as having variables called: insert; update; deleteSomething. Because calling the third variable &#8220;delete&#8221; may be logical but it is a reserved word so that is out of the question. It gets hairier when you don&#8217;t necessarily have control over the object format (such is often the case with remote calls). I ran into this today when trying to call the &#8220;node_delete&#8221; (or &#8220;node.delete&#8221;) method in Drupal via Services and AMFPHP. This is frustrating so I&#8217;m going to show two situations where you could run into this problem and how I fixed them.<br />
<span id="more-137"></span></p>
<h3>Situation #1: A variable named &#8220;new&#8221; in a dynamic class</h3>
<p>You are creating a dynamic object and you need to use a reserved word as a member variable name but you can&#8217;t.<br />
<span class="syntaxhighlighterContainer">
<pre class="brush: jscript; title: ; notranslate">
var x:Object = new Object;
x.new = &quot;this doesn't work&quot;;
x['new'] = &quot;this works&quot;;
</pre>
<p></span><br />
The first method is a nice way to get <em>1084: Syntax error: expecting identifier before new</em> when you try to compile. Remove that line and use just the second one and you are all set. It is OK to mix and match access methods, as long as you never use dot notation for reserved words.</p>
<h3>Situation #2: A RPC method named delete</h3>
<p>You are making a remote call via the RemoteObject class (such as I was doing with Drupal) and you need to call a method named &#8220;delete&#8221; (or &#8220;new&#8221; for that matter). You naturally try this:<br />
<span class="syntaxhighlighterContainer">
<pre class="brush: jscript; title: ; notranslate">
var ro:RemoteObject = new RemoteObject;
ro.endpoint = &quot;http://www.example.com/amfphp&quot;;
ro.delete( 1234 );
</pre>
<p></span><br />
You will be promptly greeted by the now familiar 1084 error when you try to compile and using a different notation won&#8217;t work. I&#8217;ll break the solution into multiple parts although it could just as easily be chained together:<br />
<span class="syntaxhighlighterContainer">
<pre class="brush: jscript; title: ; notranslate">
var op:AbstractOperation = ro.getOperation('delete');
op.send( 12345 );
</pre>
<p></span><br />
Incidentally the result of the &#8220;send&#8221; method is a AsyncToken object (the same object that &#8220;ro.delete()&#8221; would return if delete were not reserved) which can then be used to add responders.</p>
<p>There you have it. Two quick and easy ways to get around methods and properties with reserved words for names.</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewcurioso.com/2009/07/a-method-called-delete-in-flex-and-as3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>5 things about PHP 5.3 that make me smile</title>
		<link>http://andrewcurioso.com/2009/06/5-things-about-php-53-that-make-me-smile/</link>
		<comments>http://andrewcurioso.com/2009/06/5-things-about-php-53-that-make-me-smile/#comments</comments>
		<pubDate>Wed, 01 Jul 2009 03:41:08 +0000</pubDate>
		<dc:creator>Andrew Curioso</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[early adopters]]></category>

		<guid isPermaLink="false">http://andrewcurioso.com/?p=89</guid>
		<description><![CDATA[Rest assured. Soon I will be writing &#8220;Things about PHP 5.3 that make me cringe&#8221; but for now I sing the praises of the latest release of PHP that that came out today. I&#8217;ve been playing with the new release for months and there are indeed many good things about it and many of them [...]]]></description>
			<content:encoded><![CDATA[<p>Rest assured. Soon I will be writing &#8220;Things about PHP 5.3 that make me cringe&#8221; but for now I sing the praises of the latest release of PHP that that came out today. I&#8217;ve been playing with the new release for months and there are indeed many good things about it and many of them have been a long time coming.</p>
<p>The other day I was reading the release notes and I couldn&#8217;t help but smile.<br />
<span id="more-89"></span></p>
<h3>1. New native MySQL driver</p>
<p>I&#8217;m saving the best for last. So bear with me. Lets get through the small grins before we get to the big toothy ones (or you can read ahead&#8230; you&#8217;re choice). PHP 5.3 ships with a new MySQL driver called mysqlnd. A database driver is responsible for making the actually connection from PHP to MySQL. The previous MySQL driver had some flaws. For one, it was license in a way that was not compatible with the PHP license. The new <em>MySQL Native Driver</em> has a more amicable license (which is big in the open source world). It also adds some experimental functionality including improved persistent connections. There is also a down side but that is for another post.</p>
</h3>
<h3>2. Host specific PHP INI configurations</h3>
<p>I previously worked on a hosted CMS and web publishing tool that had dozens of virtual hosts but only one php.ini. The new functionality allows to section off your PHP configuration to have a different configuration for every host or file path. I haven&#8217;t tried this one yet so I&#8217;m not sure how well it works. Try it out for yourself. There is a comment on <a href="http://www.php.net/">PHP.net</a> right now saying that it only works for CGI PHP and not for the CLI implementation.<br />
<span class="syntaxhighlighterContainer">
<pre class="brush: plain; title: ; notranslate">
[HOST=example.com]
error_reporting = E_ALL
display_errors = On
</pre>
<p></span><br />
Example from the PHP documentation.</p>
<h3>3. Shortcut ternary operator</h3>
<p>I had never considered this before. However, this saves a lot of time for rather repetitive code. Consider these three identical code snipits.<br />
<span class="syntaxhighlighterContainer">
<pre class="brush: php; title: ; notranslate">
&lt;?php
if ( $foo ) $x = $foo;
else $x = $bar;

$x = ( $foo ? $foo : $bar );
$x = ( $foo ?: $bar );
?&gt;
</pre>
<p></span></p>
<p>The third method is the new shortcut. It reads simply: &#8220;if foo than foo else bar.&#8221; I am still waiting for the first time for this to be useful. The biggest issue I see is that in the above example $foo cannot legitimately be anything that evaluates to false. As a result it is best used for variables that should be non-empty strings or non-zero numbers.</p>
<h3>4. Date math</h3>
<p>The DateTime class now has several new methods in it for dealing with date arithmetic. It puts an end to manually converting to timestamps and back to dates again. It works very simply:<br />
<span class="syntaxhighlighterContainer">
<pre class="brush: php; title: ; notranslate">
&lt;?php
$date = new DateTime('2009-06-30 09:00:00');
$date-&gt;sub('P5D'); // Subtract five days
echo $data-&gt;diff( new DateTime() )-&gt;format('%d').' days ago';
?&gt;
</pre>
<p></span><br />
The new DateTime methods and the new DateInterval class (returned from and passed to math functions) aren&#8217;t very well documented because they are so new.<br />
It is worth noting that the format methods are different in the two classes. Intervals require a percentage (%) in front of placeholders. Watch out for that.</p>
<h3>5. Closures</h3>
<p>Closures are one of the best parts of PHP 5.3. At first I wasn&#8217;t very excited about them. I use closures constantly in Javascript but in a stateless HTTP request situation they appear less useful. But then I got into it. They are improved methods of dealing with lambda-functions. In other words, they are functions that are nameless and can be assigned to variables. In actuality they are classes.<br />
<span class="syntaxhighlighterContainer">
<pre class="brush: php; title: ; notranslate">
&lt;?phpi
$y = 10;
$x = function($number) use ( &amp;$y ) {
  return $number * $y;
};
$y = 100;
echo $x(8); // Output: 800
?&gt;
</pre>
<p></span><br />
This is the point at which a lot of PHP programmers would pause. Did I say they are classes? Since when can you call a class like it was a function? Since PHP 5.3 you can! . You do it by defining the &#8220;_invoke&#8221; magic method. Like so:<br />
<span class="syntaxhighlighterContainer">
<pre class="brush: php; title: ; notranslate">
&lt;?php
class testInvoke {
  public function __invoke( $x ) { echo &quot;Hello $x&quot;; }
};
$x = new TestInvoke();
echo $x('world'); // outputs &quot;Hello World&quot;
?&gt;
</pre>
<p></span><br />
This is by far one of the coolest new features in PHP 5.3. It opens a whole new world of possibilities for clean / manageable code.</p>
<h3>Bonus Things</h3>
<h4>5.1. New magic method for matching calls to static methods</h4>
<p>For a while now we have been able to define the magic method &#8220;__call&#8221; in our classes that will be executed if you try to call a method in a class instance that does not exist. Now the &#8220;__callStatic&#8221; method does the same thing only for methods of static classes.</p>
<h4>5.2. Late static binding</h4>
<p>Late static binding is a long time coming. In fact, this has tripped me up in several projects. In simplest terms late binding is waiting to determine what object a method or member variable belongs to until it is called. Late static binding in PHP, as its name indicates, applies this concept to static methods and members variables in PHP. The PHP.net website bests describes in on the <a href="http://us.php.net/lsb">manual page for late static binding</a>.</p>
<h4>5.3. E_DEPRECATED</h4>
<p>Here is a tip for everyone: if you are developing open source PHP software you should develop it in E_STRICT mode. This new E_DEPRECATED flag is actually part of E_ALL which sends a strong message that you shouldn&#8217;t be using these depreciated functions. I am a huge fan of anything that helps us write better code.</p>
<p>I hope everyone got through this post just fine. It is a long one. Leave comments (the comment section is OpenID enabled).</p>
]]></content:encoded>
			<wfw:commentRss>http://andrewcurioso.com/2009/06/5-things-about-php-53-that-make-me-smile/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
