<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Web Developer / Blog</title>
	
	<link>http://www.jaisenmathai.com/blog</link>
	<description>A blog about killer code</description>
	<pubDate>Fri, 06 Nov 2009 09:25:17 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/jaisenmathai" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
		<title>Writing your own URL shortener in 25 lines of PHP</title>
		<link>http://feedproxy.google.com/~r/jaisenmathai/~3/cKdHhwRM2zw/</link>
		<comments>http://www.jaisenmathai.com/blog/2009/06/05/writing-your-own-url-shortener-in-25-lines-of-php/#comments</comments>
		<pubDate>Fri, 05 Jun 2009 08:25:37 +0000</pubDate>
		<dc:creator>jaisen</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=145</guid>
		<description><![CDATA[So you want to write a URL shortener?  Let&#8217;s make it short and painless.  You&#8217;ll need two pieces: a key generator and redirector.  The key generator is used to represent a shortened url with a series of characters, often a-zA-Z0-9.  This is what you see at the end of tinyurl and [...]]]></description>
			<content:encoded><![CDATA[<p>So you want to write a URL shortener?  Let&#8217;s make it short and painless.  You&#8217;ll need two pieces: a key generator and redirector.  The key generator is used to represent a shortened url with a series of characters, often a-zA-Z0-9.  This is what you see at the end of tinyurl and bit.ly links.  The redirector needs to link the key to a url and perform a HTTP redirect.  Shall we?<span id="more-145"></span></p>
<p><strong>First things first</strong><br />
The <a href="http://github.com/jmathai/playground/tree/master" target="_blank">source code</a> that I based this post on is available on <a href="http://github.com/jmathai" target="_blank">GitHub</a>.</p>
<p><strong>Generating URL shortener keys</strong><br />
Understanding how to properly generate a key is the most important part of a url shortener.  If you get this wrong, you&#8217;ll find out that your urls aren&#8217;t very short and you can&#8217;t go back and fix it.  The sequence can be thought of a base 62 sequence (using a-zA-Z0-9).  Following this logic, the 1,000,000th sequence would be <em>emjb</em>.  Expanding that out would look like the following equation.</p>
<pre name="code" class="php">
// e=4
// m=12
// j=9
// b=1
// 1*62^0 + 9*62^1 + 12*62^2 + 4*62^3 = 999,999</pre>
<p>I deviated from this logic when writing the <em>getNextKey</em> function.  Nonetheless, it&#8217;s good to understand the compactness of the keys.  The <em>getNextKey</em> takes a key and calculates the next one.  If a key isn&#8217;t passed in then it defaults to <em>a</em>.</p>
<pre name="code" class="php">// don't use globals, put these in a class
$range = array_merge(range('a','z'), range('A','Z'), range(0,9));
$trans = array_flip($range);

function getLastKey()
{
  // fetch last key and return
  return 'a';
}

function getNextKey($key = null, $tail = '')
{
  global $range, $trans; // bad
  if($key === null)
    $key = getLastKey();
  if(empty($key))
    return "a{$tail}";

  $keylen = strlen($key);
  $lastChar = $key[$keylen-1];

  if($lastChar != '9')
    return substr($key, 0, ($keylen-1)) . $range[$trans[$lastChar]+1] . $tail;
  else
    return getNextKey(substr($key, 0, -1), "a{$tail}");
}</pre>
<p><strong>Redirecting shortened requests</strong><br />
When a request comes in you need to yank the key from the url, look up its long url and perform the redirect.  A sample database schema for storing URLs would look like this:</p>
<pre name="code" class="sh">CREATE TABLE `url_redirect` (
  `ur_id` int(10) unsigned NOT NULL auto_increment,
  `ur_key` varbinary(255) NOT NULL,
  `ur_url` varchar(255) NOT NULL,
  `ur_dateCreated` int(10) unsigned NOT NULL,
  PRIMARY KEY  (`ur_id`),
  UNIQUE KEY `ur_key` (`ur_key`)
);</pre>
<p>The next piece is to set Apache up to handle all requests.  Place the following, which uses mod_rewrite, into your VirtualHost definition.</p>
<pre name="code" class="php">RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*) /index.php?__key__=$1 [L,QSA]</pre>
<p>This makes sure all requests are handled by index.php.</p>
<pre name="code" class="php">$key = addslashes(substr($_GET['__key__'], 1));
$dbh = mysql_connect('localhost', 'root', ''); // bad
$redirect = mysql_fetch_assoc(mysql_query("SELECT ur_id, ur_url FROM url_redirect WHERE ur_key='{$key}'"));

if(!$redirect)
  throw new Exception("Did not find redirect key {$_SERVER['REQUEST_URI']} in database");

header('HTTP/1.1 301 Moved Permanently');
header("Location: {$redirect['ur_url']}");</pre>
<p><strong>Wrapping it up</strong><br />
These are the basics of setting up a URL shortener.  Their value is a topic of much debate.  I personally feel that they do more harm than good.  Regardless, they&#8217;re here to stay.</p>
<p>You can download these samples on GitHub (<a href="http://github.com/jmathai/playground/tree/master" target="_blank">http://github.com/jmathai/playground/tree/master</a>).</p>
<img src="http://feeds.feedburner.com/~r/jaisenmathai/~4/cKdHhwRM2zw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jaisenmathai.com/blog/2009/06/05/writing-your-own-url-shortener-in-25-lines-of-php/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jaisenmathai.com/blog/2009/06/05/writing-your-own-url-shortener-in-25-lines-of-php/</feedburner:origLink></item>
		<item>
		<title>Letting your users “Sign In With Twitter” with OAuth</title>
		<link>http://feedproxy.google.com/~r/jaisenmathai/~3/ZwfkvWMzE7s/</link>
		<comments>http://www.jaisenmathai.com/blog/2009/04/30/letting-your-users-sign-in-with-twitter-with-oauth/#comments</comments>
		<pubDate>Fri, 01 May 2009 07:36:56 +0000</pubDate>
		<dc:creator>jaisen</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=123</guid>
		<description><![CDATA[Twitter recently added a feature to their API that let&#8217;s you allow users to sign into your site with their twitter username and password.  I recently wrote a blog post on how to use Twitter&#8217;s OAuth API.  This feature is a natural progression in allowing Twitter users to securely sign into your site.
Experience [...]]]></description>
			<content:encoded><![CDATA[<p>Twitter recently added a feature to their API that let&#8217;s you allow users to sign into your site with their twitter username and password.  I recently wrote a blog post on <a href="http://www.jaisenmathai.com/blog/2009/03/31/how-to-quickly-integrate-with-twitters-oauth-api-using-php/" target="_blank">how to use Twitter&#8217;s OAuth API</a>.  This feature is a natural progression in allowing Twitter users to securely sign into your site.<span id="more-123"></span></p>
<p><strong>Experience how it works</strong><br />
I&#8217;ve set up a basic example on this site.  <a href="/sign_in_with_twitter/start.php">Try it out here</a>.  The files used in this example are <a href="/files/sign_in_with_twitter.zip">available for download</a> and should work on your server if you follow the steps.</p>
<p><strong>Getting up to date</strong><br />
If you experience any problems then make sure you are using the latest version of the code.  <a href="https://github.com/jmathai/twitter-async/tree" target="_blank">It&#8217;s available on Github</a>.</p>
<p><strong>Setting up your application on Twitter</strong><br />
The first step is to set up your application on Twitter.  You can add an application on <a href="http://twitter.com/oauth_clients/" target="_blank">Twitters OAuth clients page</a>.  Click on <em>register a new application</em> and fill out all the fields.  The two important fields are:<br />
   * Callback URL: The location of confirm.php on your server (http://yourdomain.com/confirm.php)<br />
   * User Twitter for login: Check this!<br />
Once you&#8217;ve created your application you will need to copy and paste your consumer key and secret into secret.php.</p>
<p><strong>Seeing it all in action</strong><br />
That&#8217;s it.  Browse to start.php on your server and follow the flow.  You&#8217;ll be asked to log in to twitter if you&#8217;re not already.  Once you log in, Twitter will confirm that you want to allow your application access to your account.  Clicking confirm will take you back to your callback url along with a request token.  You&#8217;ll exchange your request token for an access token which you can use to authenticate as yourself (or whoever is going through flow).  For the sake of this exercise we are saving the access token to a cookie.  We use the access token to retrieve your profile and place a link to another page.  This other page will use the access token from your cookie and get a listing of all your friends.</p>
<p>The beauty of this is that once a user allows your application access to their account they don&#8217;t have to do it again.  This means that the next time they visit your site and click on the link to sign in with twitter, it will immediately redirect them to your callback page with the request token.  It&#8217;s all very seamless because from the user&#8217;s perspective if they are logged into Twitter they are logged into your site.  Very cool.</p>
<p><strong>A look at the code</strong><br />
Every file loads in the dependencies.  These are the includes at the top of each page.</p>
<pre name="code" class="php">include 'EpiCurl.php';
include 'EpiOAuth.php';
include 'EpiTwitter.php';
include 'secret.php';</pre>
<p>Anytime you want to make requests to Twitter you need to instantiate the EpiTwitter class.  The constructor takes a minimum of two parameters: your consumer key and consumer secret.</p>
<pre name="code" class="php">$twitterObj = new EpiTwitter($consumer_key, $consumer_secret);</pre>
<p>To obtain the authenticate url which starts the flow you simply call the correct function.</p>
<pre name="code" class="php">$authenticateUrl = $twitterObj->getAuthenticateUrl();</pre>
<p>Once the user authenticates with Twitter they are redirected back to your callback url along with a request token.  We need to exchange the request token for an access token.  For simplicity we will save the access token to a cookie so we can make subsequent calls.  On this page we will use the setToken method to set the access token and secret.  As we will see on the next page, these can be passed in as parameters to the constructor as well.</p>
<pre name="code" class="php">$twitterObj->setToken($_GET['oauth_token']);
$token = $twitterObj->getAccessToken();
$twitterObj->setToken($token->oauth_token, $token->oauth_token_secret);
setcookie('oauth_token', $token->oauth_token);
setcookie('oauth_token_secret', $token->oauth_token_secret);</pre>
<p>Since we saved the access token in a cookie we can make calls to twitter on behalf of the user for as long as the cookies are valid.</p>
<pre name="code" class="php">$twitterObj = new EpiTwitter($consumer_key, $consumer_secret,
    $_COOKIE['oauth_token'], $_COOKIE['oauth_token_secret']);

$twitterInfo= $twitterObj->get_statusesFriends();
try{
  foreach($twitterInfo as $friend) {
    echo $friend->screen_name;
  }
}catch(EpiTwitterException $e){
  echo $e->getMessage();
}</pre>
<p><em>Update: If you don&#8217;t access any of the response variables then there is no guarantee that the call completes.  This should also be done in a try/catch block since a non-200 response will throw an exception.</em></p>
<p>That&#8217;s all!  If you&#8217;re still interested in learning more about OAuth then the following links are invaluable.</p>
<ul>
<li><a href="http://wiki.github.com/jmathai/twitter-async" target="_blank">http://wiki.github.com/jmathai/twitter-async</a></li>
<li><a href="http://apiwiki.twitter.com/Twitter-API-Documentation" target="_blank">http://apiwiki.twitter.com/Twitter-API-Documentation</a></li>
<li><a href="http://oauth.net/core/1.0/" target="_blank">http://oauth.net/core/1.0/</a></li>
<li><a href="http://www.hueniverse.com/hueniverse/2008/10/beginners-gui-1.html" target="_blank">http://www.hueniverse.com/hueniverse/2008/10/beginners-gui-1.html</a></li>
</ul>
<img src="http://feeds.feedburner.com/~r/jaisenmathai/~4/ZwfkvWMzE7s" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jaisenmathai.com/blog/2009/04/30/letting-your-users-sign-in-with-twitter-with-oauth/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jaisenmathai.com/blog/2009/04/30/letting-your-users-sign-in-with-twitter-with-oauth/</feedburner:origLink></item>
		<item>
		<title>How to quickly integrate with Twitter’s OAuth API using PHP</title>
		<link>http://feedproxy.google.com/~r/jaisenmathai/~3/1yApzQw_NeY/</link>
		<comments>http://www.jaisenmathai.com/blog/2009/03/31/how-to-quickly-integrate-with-twitters-oauth-api-using-php/#comments</comments>
		<pubDate>Tue, 31 Mar 2009 09:45:03 +0000</pubDate>
		<dc:creator>jaisen</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=109</guid>
		<description><![CDATA[UPDATE: Usage documentation is now hosted on GitHub (http://wiki.github.com/jmathai/twitter-async)
A new project I&#8217;m working on integrates closely with Twitter.  As a result, I become a lot more familiar with OAuth than I care to.  Many developers want to be able to speak the OAuth protocol without having to know the fine details.  I [...]]]></description>
			<content:encoded><![CDATA[<p>UPDATE: Usage documentation is now hosted on GitHub (<a href="http://wiki.github.com/jmathai/twitter-async" target="_blank">http://wiki.github.com/jmathai/twitter-async</a>)</p>
<p>A new project I&#8217;m working on integrates closely with Twitter.  As a result, I become a lot more familiar with OAuth than I care to.  Many developers want to be able to speak the OAuth protocol without having to know the fine details.  I am deviating from the normal order by starting with an example and explaining it afterwards.  If you are interested in using &#8220;<a href="http://www.jaisenmathai.com/blog/2009/04/30/letting-your-users-sign-in-with-twitter-with-oauth/">Sign in with Twitter</a>&#8221; then read my other blog post.<span id="more-109"></span></p>
<p><strong>A simple example</strong><br />
To follow the example, you&#8217;ll need to download <a href="/files/jaisenmathai_com_oauth_test.zip">these files</a> and have a web server set up.  I <a href="http://www.jaisenmathai.com/blog/2008/12/09/developing-web-applications-locally-on-your-mac-osx-using-macports/" target="_blank">use my laptop</a> but you can also use a webhost if you&#8217;d like.  Head over to Twitter and <a href="http://twitter.com/oauth">set up an application</a>.  The last two fields are important.  The callback url will need to redirect to your web server at a location where you will be uploading some files.  Assuming you&#8217;re going to upload into your web root, the callback url would be something like <em>http://yourdomain.com/confirm.php</em>.  You&#8217;ll also need to make sure you select &#8220;Read &#038; Write&#8221; access.</p>
<p>Once you click save, you will be presented with some information.  Download the <a href="/files/jaisenmathai_com_oauth_test.zip">php files for this example</a> and open secret.php.  Enter your consumer key and consumer secret in the appropriate areas.  Save and upload to your webserver.</p>
<p>Open a browser to your webserver&#8217;s start.php file you just uploaded.  You&#8217;ll see a link to authorize with Twitter.  Clicking on that link will take you to Twitter&#8217;s site and ask you to log in if you&#8217;re not already.</p>
<p><img src="/images/screenshots/twitter_access.png" alt="Oauth Flow" width="570" height="342" /></p>
<p>Once you click <em>Allow</em> you&#8217;ll be redirected back to your site with a custom greeting pulled straight from Twitter.  It doesn&#8217;t get much easier than that.  From here on out you can use the token and secret provided by Twitter to access the API on behalf of the user who authorized you.  Now, the details.</p>
<p><strong>Why use OAuth&#8230;the benefits</strong><br />
OAuth is &#8220;<em>an open protocol to allow secure API authorization in a simple and standard method from desktop and web applications</em>.&#8221;  What&#8217;s that mean?  In short, it means that a user of your service can provide you limited access to a third party account of theirs.  OAuth is often described as a valet key that your users can give you to access their accounts on other services.  For example, a user using Flickr (<em>the service provider</em>) would provide Snapfish (<em>the consumer</em>) with read only access to their Flickr account.  This lets Snapfish access photos in the user&#8217;s Flickr account so they can order prints.</p>
<p><strong>It&#8217;s all in the tokens</strong><br />
How does this happen without asking the user to give up their Flickr password?  The flow would start by Snapfish obtaining a <em>consumer key and secret</em> and using them to generate an authorization link to Flickr.  Once the user follows the authorization link, they are asked to log in on Flickr&#8217;s site.  Once logged in they can choose to grant Snapfish access to their Flickr account.  Flickr then marks the <em>request token</em> as having been authorized by the user.  Snapfish uses the <em>request token</em> to obtain an <em>access token</em> which can be used by to make requests to Flickr on behalf of the user.  This diagram may help visualize it easier.  <em>C = Consumer, SP = Service Provider</em></p>
<p><img src="/images/screenshots/oauth_flow.png" alt="Oauth Flow" width="479" height="256" /></p>
<p><strong>Let&#8217;s dive in</strong><br />
I had several goals in writing a PHP client for OAuth and Twitter.</p>
<ul>
<li>The two should be decoupled, making it easier to add other OAuth services</li>
<li>Reuse the <a href="http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/" target="_blank">asynchronous/non-blocking curl library</a></li>
<li>Expose methods which would enable OAuth requests in less than 4 lines of code</li>
</ul>
<p><strong>Generating a valid OAuth request</strong><br />
It turns out that generating an OAuth request is very simple but debugging it is a pain.  Every OAuth request contains certain parameters.  These include:</p>
<ul>
<li>oauth_consumer_key</li>
<li>oauth_token</li>
<li>oauth_nonce</li>
<li>oauth_timestamp</li>
<li>oauth_signature method</li>
<li>oauth_version</li>
<li>oauth_signature</li>
</ul>
<p>These can be passed in as GET or POST parameters or in the <em>Authorization</em> header.  You&#8217;ll most likely be passing in other additional parameters based on the API you&#8217;re accessing.  I won&#8217;t get into the details of what these parameters represent in this blog post but it&#8217;s good information to know.</p>
<p><strong>A look inside EpiOAuth</strong><br />
EpiOAuth initializes an instance of the EpiCurl object in its constructor.  Since EpiCurl is not included, you&#8217;ll want to make sure you&#8217;ve made it available ahead of time.  There are several methods which you&#8217;ll need to know about.</p>
<ul>
<li>getAccessToken</li>
<li>getAuthorizationLink</li>
<li>getRequestToken</li>
</ul>
<p>If you reference the diagram above, then these should be self explanatory.  There are several other helper methods which act more behind the scenes but play an important role.</p>
<ul>
<li>generateSignature</li>
<li>generateNonce</li>
<li>httpRequest</li>
<li>prepareParameters</li>
<li>signString</li>
</ul>
<p>These do all the magic to turn an otherwise normal request into a valid OAuth one.</p>
<p><strong>How EpiTwitter extends EpiOAuth</strong><br />
Per my original goal, the Twitter class should contain a very minimal set of functions which call specific endpoints.  These methods are self explanatory if you look at them as well.  I did, however, want to make it easy to use the class in an asynchronous manner since the underlying http library I was using supports it.  This led me to create an EpiTwitterJson.  When you make a request you&#8217;ll receive an instance of the EpiTwitterJson object without blocking for a response from the Twitter API.  You can at any point access individual elements in the response as a member variable.  Accessing the member variable with return the value and block if needed.</p>
<p><strong>Wrapping it up</strong><br />
This should help get you up and running.  The code provided is very much under construction.  I would greatly appreciate contributions to make it better.  You can <a href="http://github.com/jmathai" target="_blank">watch me on GitHub</a> or <a href="https://github.com/jmathai/twitter-async/tree" target="_blank">fork the project</a>.</p>
<p>If you&#8217;re interested in learning more about OAuth then there&#8217;s a great set of articles located at <a href="http://oauth.net/documentation/getting-started">http://oauth.net/documentation/getting-started</a>.</p>
<p>Comments are closed on this post.  If you find a bug, please open an issue on GitHub.</p>
<img src="http://feeds.feedburner.com/~r/jaisenmathai/~4/1yApzQw_NeY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jaisenmathai.com/blog/2009/03/31/how-to-quickly-integrate-with-twitters-oauth-api-using-php/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jaisenmathai.com/blog/2009/03/31/how-to-quickly-integrate-with-twitters-oauth-api-using-php/</feedburner:origLink></item>
		<item>
		<title>How to host the php.net manual on your laptop for offline use</title>
		<link>http://feedproxy.google.com/~r/jaisenmathai/~3/LQDDDIcIh48/</link>
		<comments>http://www.jaisenmathai.com/blog/2009/03/03/how-to-host-the-phpnet-manual-on-your-laptop-for-offline-use/#comments</comments>
		<pubDate>Wed, 04 Mar 2009 07:00:51 +0000</pubDate>
		<dc:creator>jaisen</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=89</guid>
		<description><![CDATA[In a previous article I wrote about how to develop web applications locally on your laptop.  In addition to being faster to develop, it lets you work without needing to be connected to the Internet.  But what about the tools you use while developing?  If you&#8217;re a PHP developer then the manual [...]]]></description>
			<content:encoded><![CDATA[<p>In a previous article I wrote about <a href="http://www.jaisenmathai.com/blog/2008/12/09/developing-web-applications-locally-on-your-mac-osx-using-macports/" target="_blank">how to develop web applications locally on your laptop</a>.  In addition to being faster to develop, it lets you work without needing to be connected to the Internet.  But what about the tools you use while developing?  If you&#8217;re a PHP developer then the manual at php.net is an invaluable tool.  It only make sense to have it available for when you&#8217;re not online.<br />
<span id="more-89"></span><br />
<strong>The general concept</strong><br />
The best way to get the full functionality of the php.net website is to create a mirror of it.  This will give you the manual as well as additional functionality.  The additional functionality isn&#8217;t as useful and you can remove any unwanted parts as you wish.  We&#8217;ll be following the <a href="http://www.php.net/mirroring.php" target="_blank">official mirroring documentation</a> from the php.net website.  This is for people that want to provide a mirror, but it turns out that it&#8217;s just as useful if you want to run a local copy of the site.</p>
<p><strong>Setting up the rsync</strong><br />
The rsync command is fairly straightforward but we&#8217;ll be doing some slight modifications to it.  We&#8217;ll create a shell script so that it&#8217;s easy to update the files as needed.</p>
<pre name="code" class="sh">user# echo '#!/bin/sh

rsync -avzC --timeout=600 --delete --delete-after \
      --include="manual/en/" --include="manual/en/**" --exclude="manual/**" --exclude="distributions/manual/**" \
      --exclude="distributions/**" \
      rsync.php.net::phpweb /path/to/document/root' > php-net-mirror.sh
user# chmod u+x php-net-mirror.sh
user# sudo su
root# echo 'php' >> /etc/hosts
root# exit
user# ./php-net-mirror.sh</pre>
<p>You&#8217;ll notice that we added an exclusion list for <em>distributions/**</em>.  This is so you won&#8217;t be sitting and waiting for 10+ distributions of PHP source to download to your laptop.  You&#8217;ll also need to replace <em>/path/to/document/root</em> to any location that you have write permission to.  We&#8217;ll later use this in our Apache vhost definition.  I put it something inside of my home directory in a subdirectory named php.net.  Don&#8217;t forget to set the permissions on the shell script so that you can execute it.</p>
<p><em>NOTE: If you&#8217;re using windows then you can use <a href="http://sourceforge.net/project/showfiles.php?group_id=69227&#038;package_id=68081" target="_blank">cwRsync available on sourceforge</a>.  Thanks to Ronnie and rui for the tips in the comments.</em></p>
<p>In order to access it from your browser you&#8217;ll need to decide what hostname to use.  I chose <em>php</em> so that I can quickly browse to <em>http://php/mysql_connect</em> to view the documentation for that function.  You can choose whatever you want, just substitute that when echo&#8217;ing into the hosts file.</p>
<p>The final step is to run the script.  You&#8217;ll see the full list of files as they download.  Updates should be much faster since you&#8217;ll only be pulling down files which have been modified.</p>
<p><strong>Setting up Apache</strong><br />
Create a new Apache virtual host using the following definition.</p>
<pre name="code" class="sh">&lt;VirtualHost *&gt;
     &lt;Directory "/path/to/document/root"&gt;
          # Do not display directory listings if index is not present,
          # and do not try to match filenames if extension is omitted
          Options -Indexes -MultiViews
     &lt;/Directory&gt;

     ServerName php
     ServerAdmin yourname@example.com

     # Webroot of PHP mirror site
     DocumentRoot /path/to/document/root

     # Log server activity
     ErrorLog logs/error_log
     TransferLog logs/access_log

     # Set directory index
     DirectoryIndex index.php index.html

     # Handle errors with local error handler script
     ErrorDocument 401 /error.php
     ErrorDocument 403 /error.php
     ErrorDocument 404 /error.php

     # Add types not specified by Apache by default
     AddType application/octet-stream .chm .bz2 .tgz .msi
     AddType application/x-pilot .prc .pdb 

     # Set mirror's preferred language here
     SetEnv MIRROR_LANGUAGE "en"

     # Turn spelling support off (which would break URL shortcuts)
     &lt;IfModule mod_speling.c&gt;
       CheckSpelling Off
     &lt;/IfModule&gt;
&lt;/VirtualHost&gt;</pre>
<p>You&#8217;ll need to update the path to your document root on lines 2 and 12.  This should match the path you specified in the shell script above.  Line 8 also needs to be updated with the hostname you specified earlier to access the site with.</p>
<p><strong>Fire up the site</strong><br />
That&#8217;s all!  Execute the command you use to restart Apache.  If you&#8217;re using OSX with MacPorts then you can execute the following commands.</p>
<pre name="code" class="sh">user# sudo /opt/local/apache2/bin/apachectl restart
user# open -a Safari "http://php/mysql_connect"</pre>
<p>Viola!</p>
<img src="http://feeds.feedburner.com/~r/jaisenmathai/~4/LQDDDIcIh48" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jaisenmathai.com/blog/2009/03/03/how-to-host-the-phpnet-manual-on-your-laptop-for-offline-use/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jaisenmathai.com/blog/2009/03/03/how-to-host-the-phpnet-manual-on-your-laptop-for-offline-use/</feedburner:origLink></item>
		<item>
		<title>Caching memcached, what are you waiting for?</title>
		<link>http://feedproxy.google.com/~r/jaisenmathai/~3/GVeBuwYzPpY/</link>
		<comments>http://www.jaisenmathai.com/blog/2009/02/03/caching-memcached-what-are-you-waiting-for/#comments</comments>
		<pubDate>Wed, 04 Feb 2009 07:29:25 +0000</pubDate>
		<dc:creator>jaisen</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=75</guid>
		<description><![CDATA[Caching is often looked to as a first step in making something faster.  Let&#8217;s face it, complex or frequent data access is expensive.  As a result, everyone and their mother manager turns to caching for a magical shot in the arm.  However, an often overlooked fact is that accessing a cache isn&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>Caching is often looked to as a first step in making something faster.  Let&#8217;s face it, complex or frequent data access is expensive.  As a result, everyone and their <del datetime="2009-02-04T06:40:54+00:00">mother</del> manager turns to caching for a magical shot in the arm.  However, an often overlooked fact is that accessing a cache isn&#8217;t a <a href="http://en.wikipedia.org/wiki/NOP#NOP_code" target="_blank">NOOP</a>.<span id="more-75"></span></p>
<p><strong>Memcached: It&#8217;s Comcastic!</strong><br />
There&#8217;s no doubt that memcached is wicked fast.  It&#8217;s used heavily here at Yahoo! and Facebook <a href="http://www.facebook.com/note.php?note_id=39391378919&#038;id=9445547199&#038;index=0" target="_blank">uses</a> <a href="http://www.datacenterknowledge.com/archives/2008/12/15/facebook-pushes-limits-for-memcached/" target="_blank">it</a>  <a href="http://highscalability.com/strategy-facebook-tweaks-handle-6-time-many-memcached-requests" target="_blank">extensively</a> as well.  An important feature which makes memcached both scalable yet at the same time more expensive (when compared to local memory) is that it&#8217;s on the network.  This ads a bit of overhead which can generally be avoided with some simple caching of memcached.</p>
<p><strong>A quick example</strong><br />
Every scenario is different, but let&#8217;s assume that you use memcached for your session store (it may have MySql behind it but that&#8217;s irrelevant).  It&#8217;s so easy to get a common piece of information like a username.</p>
<pre class="php" name="code">$memcachedObj->get("username-{$_COOKIE['userId']}");</pre>
<p>Let&#8217;s say you do that 5 times on a page.  Without realizing it, you&#8217;ve made 5 network calls to get the username from your ultra speedy cache.  That doesn&#8217;t sound like an efficient cache store to me.</p>
<p><strong>YAC (Yet Another Cache)?</strong><br />
I&#8217;m not suggesting you get data from memcached and then store it in a persistent local memory store.  You <em>could</em> do that but a simple alternative is to just store it for the current process and dispose of it along with all the other data that goes along with it.</p>
<pre class="php" name="code">function connectToCache(){
  static $memcachedObj;
  if(!$memcachedObj)
    $memcachedObj = memcache_connect('remote_host', 11211);

  return $memcachedObj;
}

function getFromCache($key){
  static $cache;
  if(!$cache[$key]){
    $memcachedObj = connectToCache();
    $cache[$key] = $memcachedObj->get($key);
  }

  return $cache[$key];
}

echo getFromCache("username-{$_COOKIE['userId']}");</pre>
<p><strong>Wrapping it up</strong><br />
While the above code probably works (I didn&#8217;t actually test it), you really want to wrap this up in a nice and tiny caching class.  Fortunately, I&#8217;ve gone ahead and done that for you.  As part of the <a href="http://github.com/jmathai/epicode/tree/master" target="_blank">EpiCode PHP framework</a> I&#8217;ve written a generic caching component with plugins for both APC and memcached.</p>
<pre class="php" name="code">require_once 'EpiCache.php';
$cache = EpiCache::getInstance(EpiCache::MEMCACHED);
echo $cache->get("username-{$_COOKIE['userId']}");
// or a one liner
// EpiCache::getInstance(EpiCache::MEMCACHED)->get("username-{$_COOKIE['userId']}");</pre>
<p><strong>Feedback</strong><br />
If you&#8217;ve used memcached in clever ways then post in the comments.  I would like to continually improve the performance of various caches and this is just one of many methods.</p>
<img src="http://feeds.feedburner.com/~r/jaisenmathai/~4/GVeBuwYzPpY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jaisenmathai.com/blog/2009/02/03/caching-memcached-what-are-you-waiting-for/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jaisenmathai.com/blog/2009/02/03/caching-memcached-what-are-you-waiting-for/</feedburner:origLink></item>
		<item>
		<title>['yahoo', 'google', 0, 'amazon', 'ebay'].search(’facebook’) === true … in PHP</title>
		<link>http://feedproxy.google.com/~r/jaisenmathai/~3/IQVqm6OCMzw/</link>
		<comments>http://www.jaisenmathai.com/blog/2008/12/18/yahoo_0_google_search_facebook_true_in_php/#comments</comments>
		<pubDate>Fri, 19 Dec 2008 05:03:18 +0000</pubDate>
		<dc:creator>jaisen</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=60</guid>
		<description><![CDATA[A coworker of mine found some peculiar behavior in some code we had.  We had an array with several values like you see in the title.  We checked values in that array using PHP&#8217;s array_search function.  While the string being searched for wasn&#8217;t present, the function was returning true.  Initially perplexed [...]]]></description>
			<content:encoded><![CDATA[<p>A coworker of mine found some peculiar behavior in some code we had.  We had an array with several values like you see in the title.  We checked values in that array using PHP&#8217;s array_search function.  While the string being searched for wasn&#8217;t present, the function was returning true.  Initially perplexed by this we realized quickly after looking at the source of PHP&#8217;s array searching function we noticed the problem.  There&#8217;s a second parameter to the array searching functions which tell PHP to do strict comparisons.  This is in their documentation at http://php.net/array_search.  It makes complete sense since PHP is loosely typed.  But it&#8217;s not at all intuitive at first glance.<span id="more-60"></span></p>
<p><strong>So what&#8217;s the problem?</strong><br />
No problem.  PHP does implicit type conversion when comparing two values.  The string <em>facebook</em> when converted to an integer becomes <em>0</em>.  So when PHP does the comparison it&#8217;s true.</p>
<pre name="code" class="php">var_dump((0 == 'facebook')); // true
var_dump((1=='1 is the loneliest number')); // true</pre>
<p>Generally speaking you&#8217;ll probably want to do a strict comparison when using PHP&#8217;s array search functions.  Unfortunately, the default behavior is to do loose comparisons.  Make sure you pass in the extra argument in order to force a strict check.</p>
<p><strong>May the source be with you</strong><br />
The C source for the function that winds up being called helped us location the problem.  Note the last argument <em>behavior</em>.</p>
<pre name="code" class="php">static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
{
        zval *value,                            /* value to check for */
                 *array,                                /* array to check in */
                 **entry,                               /* pointer to array entry */
                  res;                                  /* comparison result */
        HashPosition pos;                       /* hash iterator */
        zend_bool strict = 0;           /* strict comparison or not */
        ulong num_key;
        uint str_key_len;
        char *string_key;
        int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|b", &#038;value, &#038;array, &#038;strict) == FAILURE) {
                return;
        }

        if (strict) {
                is_equal_func = is_identical_function;
        }

        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &#038;pos);
        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&#038;entry, &#038;pos) == SUCCESS) {
                is_equal_func(&#038;res, value, *entry TSRMLS_CC);
                if (Z_LVAL(res)) {
                        if (behavior == 0) {
                                RETURN_TRUE;
                        } else {
                                /* Return current key */
                                switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &#038;string_key, &#038;str_key_len, &#038;num_key, 0, &#038;pos)) {
                                        case HASH_KEY_IS_STRING:
                                                RETURN_STRINGL(string_key, str_key_len - 1, 1);
                                                break;
                                        case HASH_KEY_IS_LONG:
                                                RETURN_LONG(num_key);
                                                break;
                                }
                        }
                }
                zend_hash_move_forward_ex(Z_ARRVAL_P(array), &#038;pos);
        }

        RETURN_FALSE;
}</pre>
<img src="http://feeds.feedburner.com/~r/jaisenmathai/~4/IQVqm6OCMzw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jaisenmathai.com/blog/2008/12/18/yahoo_0_google_search_facebook_true_in_php/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jaisenmathai.com/blog/2008/12/18/yahoo_0_google_search_facebook_true_in_php/</feedburner:origLink></item>
		<item>
		<title>Developing web applications locally on your Mac OSX using MacPorts</title>
		<link>http://feedproxy.google.com/~r/jaisenmathai/~3/5DmlLV8KAbE/</link>
		<comments>http://www.jaisenmathai.com/blog/2008/12/09/developing-web-applications-locally-on-your-mac-osx-using-macports/#comments</comments>
		<pubDate>Tue, 09 Dec 2008 09:00:55 +0000</pubDate>
		<dc:creator>jaisen</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=57</guid>
		<description><![CDATA[I&#8217;ve long been a proponent of developing web applications locally on my laptop.  It&#8217;s amazing how much time you can save when you don&#8217;t have to transfer files to a development server or wait for a file to save over the network.  You can also do development when you&#8217;re not at the office, [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve long been a proponent of developing web applications locally on my laptop.  It&#8217;s amazing how much time you can save when you don&#8217;t have to transfer files to a development server or wait for a file to save over the network.  You can also do development when you&#8217;re not at the office, vpn&#8217;ed or even have an internet connection.  However, the most compelling reason to do it is because it&#8217;s relatively trivial.<span id="more-57"></span></p>
<p>Currently, I use a Mac so I&#8217;ll go over how to set everything up using MacPorts.  However, there are many packaged applications that let you get up and running with a web server, scripting language and database.</p>
<p><strong>Installing MacPorts</strong><br />
MacPorts is great.  It reminds me a lot of Gentoo&#8217;s portage which I love.  To get started you&#8217;ll need to download the appropriate version of MacPorts <a href="http://www.macports.org/install.php">here</a>.  Make sure you have XCode installed, else follow the instructions to install it.  You can tell if you have XCode installed by doing the following in your terminal.  You should se a similar output if XCode is installed.</p>
<pre name="code" class="sh">jmathai@[~]: ls /Developer
About Xcode Tools.pdf Documentation         Extras                Icon?                 Makefiles             SDKs                  usr
Applications          Examples              Headers               Library               Private               Tools</pre>
<p>Once you get MacPorts and XCode installed you can begin getting familiar with the port command.  MacPorts has a subsystem you can use to quickly familiarize yourself with the options available to you.  To enter the subsystem enter the following in your terminal.</p>
<p><strong>Getting started with MacPorts</strong><br />
You&#8217;ll want to modify your executable path so you can easily access binaries installed by MacPorts.  You&#8217;ll want to add <span class="code">export PATH=/opt/local/bin:/opt/local/sbin:$PATH</span> somewhere in <span class="code">~/.bashrc</span> or <span class="code">~/.bash_profile</span>.  Otherwise you&#8217;ll receive errors in the form <span class="code">-bash: port: command not found</span>.</p>
<pre name="code" class="sh">jmathai@[~]: which port
/opt/local/bin/port
jmathai@[~]: sudo port
MacPorts 1.600
Entering interactive mode... ("help" for help, "quit" to quit)
[Users/jmathai] &gt;</pre>
<p>Now you&#8217;re in the MacPort subsystem and can begin issuing commands.</p>
<pre name="code" class="sh">[Users/jmathai] &gt; help
...
Supported commands
------------------
activate, archive, build, cat, cd, checksum, clean, compact, configure,
contents, deactivate, dependents, deps, destroot, dir, distcheck, dmg,
dpkg, echo, ed, edit, exit, extract, fetch, file, gohome, help, info,
install, installed, lint, list, livecheck, location, mdmg, mirror, mpkg,
outdated, patch, pkg, provides, quit, rpm, search, selfupdate, srpm,
submit, sync, test, trace, unarchive, uncompact, uninstall, upgrade,
url, usage, variants, version, work
...</pre>
<p>One of the first things you&#8217;ll want to do is to make sure MacPorts is up to date.  Once you do that then go ahead and search for some common packages that you might want to install.  For this example we&#8217;ll go with the traditional LAMP stack including Apache, PHP, MySql&#8230;excluding Linux of Course.  We can search for apache, php and mysql.</p>
<pre name="code" class="sh">[Users/jmathai] &gt; selfupdate
MacPorts base version 1.600 installed
Downloaded MacPorts base version 1.600
The MacPorts installation is not outdated and so was not updated
selfupdate done!
[Users/jmathai] &gt; search php5
...
php5                           www/php5       5.2.6        PHP: Hypertext Preprocessor
...
[Users/jmathai] &gt; search apache2
...
apache2                        www/apache2    2.2.10       The extremely popular second version of the Apache http server
...
[Users/jmathai] &gt; search mysql5
...
mysql5                         databases/mysql5 5.0.67       Multithreaded SQL database server
...
[Users/jmathai] &gt;</pre>
<p><strong>Start installing packages with MacPorts</strong><br />
Ports have the notion of variants which is beyond the scope of this post but we&#8217;ll touch on the topic.  To install php you can specify that you want it built with apache and myself.  You can additionally specify that you want pear installed as well.  We&#8217;ll install php5 with support for apache2 and mysql5.  MacPorts will take care of the dependencies and install all the required packages.</p>
<pre name="code" class="sh">[Users/jmathai] &gt; install php5 +apache2 +mysql5</pre>
<p>MacPorts downloads source tarballs and compiles them.  This can take a long time, sometimes REALLY long.  Be patient.</p>
<p><strong>Understanding where MacPorts installs packages</strong><br />
MacPorts installs packages into /opt/local.  For example, you&#8217;ll find your php.ini in <span class="code">/opt/local/etc/php.ini</span> and your apache2 files in <span class="code">/opt/local/apache</span>.  Let&#8217;s verify that everything was installed successfully.  Get back to your normal terminal prompt.  </p>
<pre name="code" class="sh">[Users/jmathai] &gt; exit
Goodbye
jmathai@[~]: sudo /opt/local/apache2/bin/apachectl restart
jmathai@[~]: echo '&lt;?php phpinfo(); ?&gt;' | sudo tee /opt/local/apache2/htdocs/phpinfo.php
&lt;?php phpinfo(); ?&gt;
jmathai@[~]: open -a Safari "http://localhost/phpinfo.php"</pre>
<p><strong>Viola!</strong><br />
There&#8217;s plenty of information available on configuring apache and mysql.  If you have any specific questions then post them in the comments and I&#8217;ll do my best to answer them.</p>
<p><strong>Additional reading</strong></p>
<ul>
<li><a href="http://garrickvanburen.com/archive/how-to-install-macports-apache2-rails-mysql-mongrel-and-subversion-on-an-intel-mac">http://garrickvanburen.com/archive/how-to-install-macports-apache2-rails-mysql-mongrel-and-subversion-on-an-intel-mac</a></li>
<li><a href="http://badpopcorn.com/blog/2008/09/19/installing-mysql-with-macports-for-rails-on-leopard/">http://badpopcorn.com/blog/2008/09/19/installing-mysql-with-macports-for-rails-on-leopard/</a></li>
<li><a href="http://www.virtualchaos.co.uk/blog/2008/08/23/installing-php5-apache2-using-macports-on-leopard/">http://www.virtualchaos.co.uk/blog/2008/08/23/installing-php5-apache2-using-macports-on-leopard/</a></li>
<li><a href="http://iparrizar.mnstate.edu/~juan/urania/2008/08/14/fixing-my-php-woes-with-macports/">http://iparrizar.mnstate.edu/~juan/urania/2008/08/14/fixing-my-php-woes-with-macports/</a></li>
</ul>
<img src="http://feeds.feedburner.com/~r/jaisenmathai/~4/5DmlLV8KAbE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jaisenmathai.com/blog/2008/12/09/developing-web-applications-locally-on-your-mac-osx-using-macports/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jaisenmathai.com/blog/2008/12/09/developing-web-applications-locally-on-your-mac-osx-using-macports/</feedburner:origLink></item>
		<item>
		<title>Interview questions from Google, Yahoo!, O’Reilly and a few startups</title>
		<link>http://feedproxy.google.com/~r/jaisenmathai/~3/eZF525LZs-8/</link>
		<comments>http://www.jaisenmathai.com/blog/2008/12/04/interview-questions-from-google-yahoo-oreilly-and-a-few-startups/#comments</comments>
		<pubDate>Fri, 05 Dec 2008 07:48:07 +0000</pubDate>
		<dc:creator>jaisen</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=53</guid>
		<description><![CDATA[Just over a year ago my wife and I decided that we&#8217;d consider moving from Cincinnati to Silicon Valley.  I&#8217;ve always found it difficult to find work that I enjoyed in Cincinnati.  That resulted in me being involved in two startups, neither being successful :).  As part of the process my friends who were also [...]]]></description>
			<content:encoded><![CDATA[<p>Just over a year ago my wife and I decided that we&#8217;d consider moving from Cincinnati to Silicon Valley.  I&#8217;ve always found it difficult to find work that I enjoyed in Cincinnati.  That resulted in me being involved in two startups, neither being successful :).  As part of the process my friends who were also considering moving and I documented the questions we were asked during interviews.<br />
<span id="more-53"></span><br />
<strong>O&#8217;Reilly (in person):</strong></p>
<ul>
<li>Create a Perl dictionary in Python.</li>
</ul>
<p><strong>Google (phone):</strong></p>
<ul>
<li>What is a sticky bit?</li>
<li>How do you display all available information for an inode?</li>
<li>What is the default switch for kill?</li>
</ul>
<p><strong>Gaia Online (in person):</strong></p>
<ul>
<li>Write pseudocode which determines if a move is legal in reversi.</li>
<li>Write a regular expression to convert this UBB code into valid HTML: <em>The dog [b]ran over [i]the street[/b] and said: [blockquote]i am a dog[/blockquote].</em></li>
<li>Questions regarding what I did to help scale software at Photagious and ClearChannel.</li>
</ul>
<p><strong>Rearden Commerce (in person):</strong></p>
<ul>
<li>What is the architecture where you currently work and what are some of the bottlenecks?</li>
<li>How do you go about solving a performance problem?</li>
<li>Can a computer process two things at the same time?</li>
<li>How would you implement support for multiple languages in a web application?</li>
<li>How would you remove all of the sequential characters from the following string: <em>AADBDDDABDDEAAD</em>?</li>
<li>How would you speed up the performance of Javascript?</li>
<li>How would you speed up the performance of CSS?</li>
</ul>
<p><strong>Yahoo! (in person):</strong><br />
Group 1</p>
<ul>
<li>What does the gnu top program do?</li>
<li>What does the gnu find program do?</li>
<li>What is the difference between a thread and a process?</li>
<li>What version control software have you used?</li>
<li>Why would you use git over subversion?</li>
<li>What is a hash datatype?</li>
<li>What is a super class or parent class?</li>
<li>Explain prototype in Javascript.</li>
<li>What character encoding does Javascript use?</li>
<li>What methods are supported by HTTP?</li>
<li>When wouldn&#8217;t you use an MD5 hash?</li>
<li>How would you approach writing an similar hashing algorithm to MD5?</li>
<li>What is a CSS sprite?</li>
</ul>
<p>Group 2</p>
<ul>
<li>Given a 2 dimensional array write pseudocode which sorts the array by the 2nd dimension.</li>
<li>How would you replicate the PHP extract function?</li>
</ul>
<p>Group 3</p>
<ul>
<li>How would you efficiently pull out links to MP from an HTML file?</li>
<li>Can you avoid cross site scripting (XSS) with ajax calls?  If so, how?</li>
</ul>
<p>Group 4</p>
<ul>
<li>Given n points in the form (<em>x1, y1, z1</em>)&#8230;..(<em>xn, yn, zn</em>), find the k closest points to the origin.</li>
<li>Given the same points as above, find the K closest points to each other.</li>
<li>Given a url such as <em>http://yahoo.com/foo/bar/../baz/./abc/foo/def/../ghi</em>, determine the final directory being requested by parsing the string.</li>
<li>Given the following set of numbers: <em>10, 7, 5, 8</em> find all pairs which add up to <em>15</em>.</li>
</ul>
<p>Once it was all said and done, we all ended up out here in sunny California.</p>
<img src="http://feeds.feedburner.com/~r/jaisenmathai/~4/eZF525LZs-8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jaisenmathai.com/blog/2008/12/04/interview-questions-from-google-yahoo-oreilly-and-a-few-startups/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jaisenmathai.com/blog/2008/12/04/interview-questions-from-google-yahoo-oreilly-and-a-few-startups/</feedburner:origLink></item>
		<item>
		<title>Using the Facebook API to upload photos</title>
		<link>http://feedproxy.google.com/~r/jaisenmathai/~3/z0Ko_znMmDI/</link>
		<comments>http://www.jaisenmathai.com/blog/2008/11/27/using-the-facebook-api-to-upload-photos/#comments</comments>
		<pubDate>Thu, 27 Nov 2008 10:59:25 +0000</pubDate>
		<dc:creator>jaisen</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=46</guid>
		<description><![CDATA[Facebook&#8217;s photos and albums are no way to truly store or organize your photos.  It is however one of the best ways to share them.  This created quite a conundrum for me as I had no interest in uploading photos to two different websites.  I don&#8217;t use Flickr or Smugmug or any other photo sharing [...]]]></description>
			<content:encoded><![CDATA[<p>Facebook&#8217;s photos and albums are no way to truly store or organize your photos.  It is however one of the best ways to share them.  This created quite a conundrum for me as I had no interest in uploading photos to two different websites.  I don&#8217;t use Flickr or Smugmug or any other photo sharing service that you&#8217;ve heard of.  Since I was a co-founder at Photagious it&#8217;s a natural choice as my photo management site.<span id="more-46"></span></p>
<p><strong>Defining the goal</strong><br />
I wanted to be able to selectively publish photos from my Photagious account to Facebook.  I didn&#8217;t need any of the fancy Facebook application junk&#8230;I just wanted to make it easy to post photos from my Photagious account into my Facebook photos.  Turns out that it&#8217;s dead simple once you do a little bit of research.</p>
<p><strong>Step number one</strong><br />
First, you need to get an application key and secret.  One way to do this by creating a new application.  Most of the information you&#8217;ll be asked for will be unused, so fill them in with whatever you&#8217;d like.  Just make sure to enable developers working on the application to install it.</p>
<p><strong>Step number two</strong><br />
Second, you need to install the application.</p>
<p><strong>Step number three (final)</strong><br />
Now, the code.  Basically, you generate the request with the required fields, generate the signature, append the file and send off the request.  For the sake of brevity, I&#8217;m pasting in the entire file here.</p>
<pre name="code" class="php">$key = 'your_key';
$sec = 'your_secret';
$ver = '1.0';
$cid = microtime(true);
$uid = 'BIGINT';
$file= '/path/to/your/file.jpg';

$args = array(
  'method' =&gt; 'photos.upload',
  'v' =&gt; $ver,
  'api_key' =&gt; $key,
  'uid' =&gt; $uid,
  'call_id' =&gt; $cid,
  'format' =&gt; 'XML'
);
signRequest($args, $sec);
$args[basename($file)] = '@' . realpath($file);

$ch = curl_init();
$url = 'http://api.facebook.com/restserver.php?method=photos.upload';
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $args);
$data = curl_exec($ch);

echo $data;

function signRequest(&amp;$args, $secret){
  ksort($args);
  $sig = '';
  foreach($args as $k =&gt; $v){
    $sig .= $k . '=' . $v;
  }
  $sig .= $secret;
  $args['sig'] = md5($sig);
}</pre>
<p>Seriously, that&#8217;s it.  Here&#8217;s the output I received from the command line.</p>
<pre name="code" class="sh">jmathai@[~/Y/photos]: php fb.php
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;photos_upload_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd"&gt;
&lt;pid&gt;XXXXXXXXXXXXXXXXXX&lt;/pid&gt;
&lt;aid&gt;XXXXXXXXXXXXXXXXXX&lt;/aid&gt;
&lt;owner&gt;XXXXXXXXXXX&lt;/owner&gt;
&lt;src&gt;http://photos-d.ak.fbcdn.net/photos-ak-sf2p/v645/2/72/500273081/s500273081_1648227_6794.jpg&lt;/src&gt;
&lt;src_big&gt;http://photos-d.ak.fbcdn.net/photos-ak-sf2p/v645/2/72/500273081/n500273081_1648227_6794.jpg&lt;/src_big&gt;
&lt;src_small&gt;http://photos-d.ak.fbcdn.net/photos-ak-sf2p/v645/2/72/500273081/t500273081_1648227_6794.jpg&lt;/src_small&gt;
&lt;link&gt;http://www.facebook.com/photo.php?pid=1648227&amp;amp;id=500273081&lt;/link&gt;
&lt;caption/&gt;
&lt;created&gt;1227781696&lt;/created&gt;
&lt;story_fbid&gt;0&lt;/story_fbid&gt;
&lt;/photos_upload_response&gt;</pre>
<p><strong>Additional reading</strong><br />
You&#8217;ll want to get more familiar with Facebook&#8217;s API and some of the nuances when posting photos.  Here are some links to help get you on your way.</p>
<ul>
<li><a href="http://wiki.developers.facebook.com/index.php/Photos.upload">http://wiki.developers.facebook.com/index.php/Photos.upload</a></li>
<li><a href="http://wiki.developers.facebook.com/index.php/How_Facebook_Authenticates_Your_Application">http://wiki.developers.facebook.com/index.php/How_Facebook_Authenticates_Your_Application</a></li>
<li><a href="http://wiki.developers.facebook.com/index.php/Extended_permission">http://wiki.developers.facebook.com/index.php/Extended_permission</a></li>
<li><a href="http://code.google.com/p/servicesfacebook/">http://code.google.com/p/servicesfacebook/</a></li>
</ul>
<img src="http://feeds.feedburner.com/~r/jaisenmathai/~4/z0Ko_znMmDI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jaisenmathai.com/blog/2008/11/27/using-the-facebook-api-to-upload-photos/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jaisenmathai.com/blog/2008/11/27/using-the-facebook-api-to-upload-photos/</feedburner:origLink></item>
		<item>
		<title>OSX + Quicksilver + Firefox + Delicious = Bookmark Heaven</title>
		<link>http://feedproxy.google.com/~r/jaisenmathai/~3/fByJcJbfFDo/</link>
		<comments>http://www.jaisenmathai.com/blog/2008/11/26/osx-quicksilver-firefox-delicious-bookmark-heaven/#comments</comments>
		<pubDate>Wed, 26 Nov 2008 17:28:07 +0000</pubDate>
		<dc:creator>jaisen</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=38</guid>
		<description><![CDATA[Quicksilver is the only OSX application that I couldn&#8217;t live without.  It&#8217;s one of those pieces of software that changes how you accomplish your most frequent tasks.  Here&#8217;s short list of the things you can do much faster than you could otherwise: open applications, send emails and more.  But one of my most frequent actions [...]]]></description>
			<content:encoded><![CDATA[<p>Quicksilver is the only OSX application that I couldn&#8217;t live without.  It&#8217;s one of those pieces of software that changes how you accomplish your most frequent tasks.  Here&#8217;s short list of the things you can do much faster than you could otherwise: open applications, send emails and more.  But one of my most frequent actions is to pull up specific web pages that.  Normally, I would bookmark these but we can do much better than &#8220;normally&#8221;.<span id="more-38"></span></p>
<p>If you don&#8217;t already have Quicksilver installed then <a title="Download Quicksilver" href="http://www.blacktree.com/" target="_blank">go get to know it</a> (<a title="Quicksilver video on YouTube" href="http://www.youtube.com/watch?v=EBvFUhTqKK4" target="_blank">here&#8217;s an indepth video tutorial</a>).  Ok, now that you&#8217;re familiar with the basics of using Quicksilver let&#8217;s do the second most important thing you can for your computer: install the <a title="Delicious addon for Firefox" href="https://addons.mozilla.org/en-US/firefox/addon/3615" target="_blank">Delicious addon for Firefox</a>.</p>
<p><strong>Take Quicksilver on a test drive</strong><br />
Now that we&#8217;ve got everything we need let&#8217;s hook it all up.  First, open up Quicksilver and go to the preferences pane.  You can do this by pressing command <em>+ ,</em>.  Once in the preferences pane, select plugins and make sure that Social Bookmarks is checked.  Then go to catalog and select custom from the left menu and add Social Bookmarks to the list if it&#8217;s not already there.</p>
<p>You can enter your Delicious username and password by clicking the i icon in the lower right of the preferences pane.  It&#8217;s as easy as that.</p>
<p><strong>See it all in action</strong><br />
Now, open Quicksilver and start typing something in the title of one of the pages you&#8217;ve bookmarked.  You&#8217;ll wonder how you ever lived without Quicksilver, Firefox and Delicious.  Below is what I get when type <em>ronpaul</em>:</p>
<div id="attachment_40" class="wp-caption aligncenter" style="width: 387px"><a href="http://www.jaisenmathai.com/blog/wp-content/uploads/2008/11/quicksilver_delicious.png"><img class="size-full wp-image-40" title="Quicksilver using delicious" src="http://www.jaisenmathai.com/blog/wp-content/uploads/2008/11/quicksilver_delicious.png" alt="OSX + Quicksilver + Firefox + Delicious = Bookmark Heaven" width="377" height="456" /></a><p class="wp-caption-text">OSX + Quicksilver + Firefox + Delicious = Bookmark Heaven</p></div>
<img src="http://feeds.feedburner.com/~r/jaisenmathai/~4/fByJcJbfFDo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jaisenmathai.com/blog/2008/11/26/osx-quicksilver-firefox-delicious-bookmark-heaven/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jaisenmathai.com/blog/2008/11/26/osx-quicksilver-firefox-delicious-bookmark-heaven/</feedburner:origLink></item>
	</channel>
</rss>
