<?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>Pseudo Random Bytes</title>
	<atom:link href="http://arr.gr/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://arr.gr/blog</link>
	<description>Shahar writes about the Web, programming and other stuff</description>
	<lastBuildDate>Wed, 08 Feb 2012 09:29:13 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Say Hi to Shoppimon &#8211; Magento Monitoring for &#8220;Normal&#8221; People</title>
		<link>http://arr.gr/blog/2012/02/say-hi-to-shoppimon-e-commerce-monitoring-for-normal-people/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=say-hi-to-shoppimon-e-commerce-monitoring-for-normal-people</link>
		<comments>http://arr.gr/blog/2012/02/say-hi-to-shoppimon-e-commerce-monitoring-for-normal-people/#comments</comments>
		<pubDate>Tue, 07 Feb 2012 10:49:52 +0000</pubDate>
		<dc:creator>shahar</dc:creator>
				<category><![CDATA[Thoughts & Possibilities]]></category>
		<category><![CDATA[PHP & Web Technologies]]></category>
		<category><![CDATA[shoppimon]]></category>
		<category><![CDATA[zend framework]]></category>
		<category><![CDATA[zf2]]></category>

		<guid isPermaLink="false">http://arr.gr/blog/?p=213</guid>
		<description><![CDATA[For a while now I have been telling people I am &#8220;working on a small project&#8221; &#8211; and now is the time to unveil the mystery and introduce Shoppimon &#8211; a new start-up which I founded together with a small group of friends, and am currently spending most of my time around. The idea of [...]]]></description>
			<content:encoded><![CDATA[<p>For a while now I have been telling people I am &#8220;working on a small project&#8221; &#8211; and now is the time to unveil the mystery and introduce <a href="http://www.shoppimon.com/">Shoppimon</a> &#8211; a new start-up which I founded together with a small group of friends, and am currently spending most of my time around.</p>
<p>The idea of Shoppimon is simple &#8211; we want to provide Web monitoring and availability analysis which will be useable by, and useful to &#8220;normal&#8221; people &#8211; not only the tech guy, the programmer or the IT specialist, but the site owner, the business owner or even the marketing guy &#8211; in other words the real stake holder.</p>
<p><span id="more-213"></span></p>
<p>Shoppimon focuses on synthetic monitoring &#8211; it simulates real users going through the store and logs their &#8220;experience&#8221; &#8211; any errors encountered, overall time to complete certain actions, etc. &#8211; as such it complements real-user monitoring that products like Zend Server provide). In comparison to existing synthetic Web monitoring services, we want Shoppimon to be a snatch to get started with, an inexpensive solution that would be useful to even the smallest commercial site owners, and most important using it should not require one to be a tech savvy person.</p>
<p>Shoppimon is focusing on <a href="http://www.magentocommerce.com/">Magento</a> &#8211; a popular PHP / Zend Framework based eCommerce solution. Magento has proved to be a popular eCommerce solution and has grown a rich ecosystem of developers and service providers around it, and has a rich community of users. But as most PHP programmers know, managing Magento&#8217;s availability and performance isn&#8217;t easy. It&#8217;s a heavy application, one of the heaviest PHP apps ever built, and the codebase is somewhat complex. Our goal is to help Magento store owners find (and fix) problems in their stores, and show them how their store compares to others.</p>
<p>This is why we decided to focus Shoppimon on Magento and provide objective scientific data (which we present in an easy-to-digest way) that would help Magento store owners (and in turn their developers and hosting providers) to enjoy smooth sailing.</p>
<p>Technically, Shoppimon is pretty interesting (hey, otherwise I wouldn&#8217;t have done it) &#8211; it has parts written in different programming languages. It is entirely based on Cloud technologies. The front-end runs on <a href="http://framework.zend.com/">Zend Framework 2.0</a> (it is possibly the first commercial app to be out there running on ZF 2.0) and on <a href="http://www.zend.com/en/products/server/">Zend Server</a>. The backend parts mix PHP, Java and Python (a long story&#8230;) and uses some very interesting technology to simulate real shoppers browsing around on the Magento sites we test.</p>
<p>In the next few weeks I&#8217;ll probably be posting more about how stuff work at Shoppimon under the hood. Stay tuned!</p>
]]></content:encoded>
			<wfw:commentRss>http://arr.gr/blog/2012/02/say-hi-to-shoppimon-e-commerce-monitoring-for-normal-people/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Storing Passwords the Right Way</title>
		<link>http://arr.gr/blog/2012/01/storing-passwords-the-right-way/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=storing-passwords-the-right-way</link>
		<comments>http://arr.gr/blog/2012/01/storing-passwords-the-right-way/#comments</comments>
		<pubDate>Tue, 31 Jan 2012 06:04:47 +0000</pubDate>
		<dc:creator>shahar</dc:creator>
				<category><![CDATA[PHP & Web Technologies]]></category>
		<category><![CDATA[101]]></category>
		<category><![CDATA[hashing]]></category>
		<category><![CDATA[md5]]></category>
		<category><![CDATA[passwords]]></category>
		<category><![CDATA[salt]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[sha1]]></category>

		<guid isPermaLink="false">http://arr.gr/blog/?p=188</guid>
		<description><![CDATA[I consider this post a bit of an experiment in writing about what I consider &#8220;beginner&#8221; material. Not that it is necessarily simple or easy stuff anyone should know, but simply because this is not a &#8220;new discovery&#8221; as far as I am concerned. Also, I usually try not to write about security related material, [...]]]></description>
			<content:encoded><![CDATA[<p>I consider this post a bit of an experiment in writing about what I consider &#8220;beginner&#8221; material. Not that it is necessarily simple or easy stuff anyone should know, but simply because this is not a &#8220;new discovery&#8221; as far as I am concerned. Also, I usually try not to write about security related material, as I do not consider myself a security expert. However, since I&#8217;m starting to teach a &#8220;PHP 101&#8243; course soon (maybe I&#8217;ll post more about it in the next few weeks), and since I was asked a few times about this topic recently, I&#8217;ve decided to write up my experience on this topic and test the reactions.</p>
<p>So, the topic in question is &#8220;what is the right way to store user passwords in my DB&#8221;. To be clear, I am talking specifically about the passwords users will use to log in to your application, not some 3rd party password you need to store for whatever reason. This is something almost any application out there requires &#8211; unless you interface with some external authentication mechanism (OAuth, openId, your office LDAP or Kerberos server), there&#8217;s a very high chance you&#8217;ll need to authenticate users against a self-stored user name and password.</p>
<p><span id="more-188"></span></p>
<p>In order to figure out what is the best solution, let&#8217;s start by going over the problems we might face if we simply take the naive approach and store passwords in their original, clear text form:</p>
<ul>
<li>If our database gets hacked (for example if we are exposed to an SQL injection attack through some 3rd party app we have installed on our server), passwords could get stolen. Clear-text passwords could easily be used to hijack our users&#8217; accounts. In many cases, even if we ignore the risk of a hacked database &#8211; a clear-text password can be stolen by a disgruntled worker with access to the DB.</li>
<li>Moreover, users tend to use the same passwords for all sorts of different services. If I know someone&#8217;s password to one service, chances are I can use the same password or a similar one to impersonate that user in other sites as well. Most users do not consider the fact (but you should!) that when they type in a password for your silly site with funny kitten pictures, there&#8217;s a good chance they are entrusting the password to their bank or PayPal account in your hands.</li>
</ul>
<p>Given the points above, it is our responsibility as web developers to ensure that nobody, not even us, could get a clear-text version of the user&#8217;s password. So what&#8217;s the right way to do that?</p>
<h2>Best solution: avoid the problem all together</h2>
<p>The best way is, of course, not to store passwords at all. If functional and business terms allow, you should consider using some other authentication mechanism like OAuth or OpenID &#8211; basically let someone else worry about storing sensitive data. Many users have Google or Facebook profiles, and you can find several examples of pretty big websites that simply let users authenticate through these providers instead of through their own identity management service. This is an elegant solution, but clearly it does not work in all cases. If you still need to store passwords, read on.</p>
<h2>Password Hashing</h2>
<p>One aspect of passwords is that really, you have no use for their actual value. A password can be anything, and as long as the user repeats the same initial password when asked to sign in, you do not care what the password&#8217;s textual value is. This is important, because it means we don&#8217;t need to store the original value of the password: it is safe to store a product of the password, and as long as we know how to reproduce this product from a typed-in password, we can compare the products and not the password itself.</p>
<p>Sounds confusing? Here is a simple example: assume that instead of passwords, our site uses numbers to authenticate users. Each user has to provide a user name and a random number as a password. Before we store this &#8220;password&#8221; in our DB, we pass it through this simple mathematical function:</p>
<pre>    f(x) = x * 5</pre>
<p>So if a user types in &#8220;4709&#8243; as a password, we multiply that number by 5 and store the value &#8220;23545&#8243; in our DB. When the user attempts to sign in again, we pass the value typed in as password through the same function. If we get &#8220;23545&#8243; as the product of the typed in password passed through our function, we know the user typed in the right password.</p>
<p>This is good, because now the value stored in our DB is not the actual password, but an obscure value (well, sort of). If someone steals our DB, they can type in &#8220;23545&#8243; into the sign-in form all day &#8211; but that&#8217;s not the right password (remember, we multiply whatever is typed in by 5 before comparing it!).</p>
<p>Unfortunately, things are not that easy. First, it&#8217;s enough for someone to know that we multiply numbers by 5 to obfuscate them, and they can easily reverse engineer passwords by dividing the data stored in the DB by 5 &#8211; our function is <strong>reversible</strong>. Second, a smart enough hacker looking at our list of stored password values would probably notice that all of them are multiples of 5 &#8211; so even without knowing what we do in advance, reverse engineering our &#8220;security&#8221; method is quite easy.</p>
<p>As it turns out, the approach is right but the function we are using is too simple. What we need is a function that:</p>
<ul>
<li>Is irreversible, or at least is impossible to reverse in the real world. In other words, even if one knows both the result and the function used, computing the input value will be impossible or impractical.</li>
<li>Is indeed a function in the mathematical sense &#8211; that is given the same input value, only a single outcome is possible, and the same outcome is always produced for the same input. For example, a computer function which multiplies the input value by a random number is no good for us.</li>
<li>Produces a distinct, single value for each input value &#8211; or at least provides a very low risk of producing the same result for two input values. This is important because we want to make sure that <strong>only</strong> one typed-in password matches the obscured value stored in the DB.</li>
</ul>
<p>Of course, most of us are not mathematicians &#8211; so you&#8217;d be happy to know that  such functions exist, and are usually referred to as &#8220;hash functions&#8221;. Hash functions use used extensively by programmers for all sorts of uses, and cryptography is most definitely one of them. A few popular and useful cryptographic-grade hash functions are <a href="http://en.wikipedia.org/wiki/MD5">MD5</a>, <a href="http://en.wikipedia.org/wiki/SHA-1">SHA-1</a> and <a href="http://en.wikipedia.org/wiki/SHA-2">SHA-2</a>. We will not go into the mathematical definitions of these functions (I have very little knowledge of how these functions actually work!) &#8211; it&#8217;s enough to say pretty much every popular programming language  out there has at least one implementation of these functions.</p>
<p>As an example, the MD5 function produces a 128-bit &#8220;hash value&#8221; for an input value provided to it. The hash is always 128 bit long, regardless of the input size. It will always produce the same hash value for the same input. The most successful known collision attack (that is an attack producing the same hash value for a modified input value) on MD5 took about 2 million execution attempts which makes it quite bad for validating SSL certificates (which it used to be used for), but still sort of Ok (although not great) for password hashing.</p>
<p>To compute an MD5 hash value in PHP, you can do the following:</p>
<pre>    php &gt; echo md5("my name is Inigo Montoya");
    d9937edae7d26a399d41dda16f137e42</pre>
<p>As you can see the MD5 value of the string &#8220;my name is Inigo Montoya&#8221; is &#8220;d9937edae7d26a399d41dda16f137e42&#8243; (this is in fact a hexadecimal representation of the MD5 value, which is a 128 bit number &#8211; this is the standard way to present hash values of various functions). On the other hand:</p>
<pre>    php &gt; echo md5("my name is In<strong>d</strong>igo Montoya");
    ae7cd5e68c73f9f44df66030cc9d1c06</pre>
<p>Even a slight change in the input text produces a completely different MD5 hash.</p>
<h2>It&#8217;s time to stop using MD5 for cryptographic purposes</h2>
<p>While MD5 was the de-facto standard for storing hashed passwords for some time, it is now becoming clear that it may not be suitable for cryptographic purposes (it is definitely suitable for other things). In 2009 it was shown that producing collisions for MD5 can be done within seconds or minutes on commodity hardware &#8211; this does not mean it is easy to reverse engineer password values stored in your DB as MD5 hash values, but it does mean that if a highly skilled hacker wants to specifically target your site, they have a better chance of succeeding in doing so. In addition, it is safe to assume additional vulnerabilities will be detected in the future.</p>
<p>Unless you are somehow limited (not if you&#8217;re a PHP developer!), switching to stronger hash functions such as SHA-1 or SHA-256 is highly recommended.</p>
<p>Throughout the rest of this article I will use SHA-1 in examples. SHA-1 is not collisions free, but so far the best known <strong>theoretical</strong> collision attack on SHA-1 took 2 to the power of 51 attempts to perform (that&#8217;s a number with 16 digits!), and until now nobody has been able to show an actual successful attempt to do so. SHA-1 produces a 160-bit digest values, and can be computed in PHP like so:</p>
<pre>    php &gt; echo sha1("my name is Inigo Montoya");
    b208946a9c3c4b26a4d6bb87c3f630f996146ee</pre>
<h2>So, I should just store the password hash in the DB?</h2>
<p>Well, yes and no.</p>
<p>Yes &#8211; because that&#8217;s the first step. By storing a SHA-1 hashed version of the password in your DB you ensure nobody can <strong>compute</strong> the password by simply stealing your users DB table data. When a user types in their password, you compare the stored hash to the SHA-1 hash of the typed in string, and if they match, you grant access. Simple and effective.</p>
<p>But wait&#8230; that&#8217;s still not good enough.</p>
<h2>Stupid but Effective: Dictionary Attacks</h2>
<p>All hash functions are vulnerable to a type of attack sometimes referred to as <strong>rainbow attacks </strong>or <strong>dictionary attacks</strong>.</p>
<p>These attacks take advantage of the fact that in most cases, humans are humans &#8211; and the passwords they use are of limited size (how many people can you think of that use 12 or even 10 character long passwords?) and are composed of a limited set of characters (remember that even power users that use punctuation, numbers and mixed-case characters in their passwords are still confined to the ~75 characters or so on their keyboards).</p>
<p>Dictionary attacks are stupid but effective: the idea is to create a dictionary (basically a big key -&gt; value table) of predictable passwords (dictionary words, expected combinations of key strokes, all permutations of what&#8217;s on your keyboard up to 8 characters long) and their MD5 or SHA-1 values. Once such a table exists (creating it may take several hours on commodity hardware, but this is a one-time effort), you can search for an original password using it&#8217;s hash value.</p>
<p>A dictionary attack allows me to reverse-engineer the original password from it&#8217;s hash value not by smart computation (which, given a good hash function, is impossible or impractical), but through a simple query to a ready-made &#8220;dictionary&#8221; mapping hash values to original strings.</p>
<p>But it&#8217;s even easier than that: nowdays there are <a href="http://md5.rednoize.com/">services</a> that offer such dictionary lookup in their existing databases. It&#8217;s not even required to do the work of building the dictionary.</p>
<p>One good solution to dictionary attacks is forcing your users to mix punctuation, upper and lower-case characters and numbers in their at-least 12 character long passwords. However, we all know that in many cases this means expecting too much from your users.</p>
<p>The practical solution to dictionary attacks is quite simple, and is called <strong>salting</strong>.</p>
<h2>Just Add Salt</h2>
<p>Salting is a simple yet effective method to improve the security of stored passwords and prevent dictionary attacks. The idea is that instead of expecting a long, random password from the user, you take whatever password the user provides and add additional random noise (referred to as &#8220;salt&#8221;) to it yourself. You store that random noise next to the password, and use it to compute the hash when checking passwords.</p>
<p>Once a long enough and random enough salt is added, comparing hash values stored in your DB to a dictionary becomes very hard: an attacker will need to build an entire database of hash values for each different salt + password combination, effectively requiring the creation of a table with hundreds of billions of records to crack a single password.</p>
<p>Make sure a different random salt is added to each password: otherwise a single DB of salted hash values can be created for your application &#8211; it won&#8217;t be useful for other apps, but if someone wants to target your app they can definitely achieve their goals.</p>
<p>As an example, let&#8217;s assume a user who&#8217;s password is &#8216;inigo2001&#8242;. Here is how this user&#8217;s password will be stored in the DB without salting:</p>
<pre> +-------------------+------------------------------------------+
 | user              | password_hash                            |
 +-------------------+------------------------------------------+
 | inigo@montoya.com | e40900c950cc6011297b2b392b42c29688b33ac7 |
 +-------------------+------------------------------------------+</pre>
<p>An attacker with a good dictionary can figure out that the password_hash value is in fact the SHA-1 digest of &#8220;inigo2001&#8243;. However, if we add salt:</p>
<pre> +-------------------+------------------------------------------+------------------------------+
 | user              | password_hash                            | password_salt                |
 +-------------------+------------------------------------------+------------------------------+
 | inigo@montoya.com | fe66f3eb9c0afc8c935dc9f3f26dbea68d48ccc1 | 9ljYI+xMaVOSloDwt9ahzTpqMHA= |
 +-------------------+------------------------------------------+------------------------------+</pre>
<p>Guessing that password_hash is the SHA-1 digest of &#8220;indigo20019ljYI+xMaVOSloDwt9ahzTpqMHA=&#8221; is quite hard &#8211; one would need to build a huge dictionary just to figure out this one password, assuming they also have insight into our code and have figured out that we have concatenated the password_salt value after the original password value and passed that through SHA-1.</p>
<p>Note that the password_salt value in this case is a base-64 encoded string of 20 random bytes &#8211; using a random enough and long enough salt is important, otherwise there&#8217;s a good chance your password + salt value happens to already exist in the attacker&#8217;s DB.</p>
<h2>Example Time</h2>
<p>To summarize things, here is an actual example of a few PHP functions that store user information in the DB in a secure manner and verify passwords against that stored information.</p>
<p>Our users table in the database is assumed to look something like:</p>
<pre>mysql&gt; DESCRIBE users;
+---------------+---------------------+------+-----+---------+----------------+
| Field         | Type                | Null | Key | Default | Extra          |
+---------------+---------------------+------+-----+---------+----------------+
| id            | int(10) unsigned    | NO   | PRI | NULL    | auto_increment |
| email         | varchar(50)         | NO   | UNI | NULL    |                |
| password      | char(40)            | NO   |     | NULL    |                |
| password_salt | binary(16)          | YES  |     | NULL    |                |
+---------------+---------------------+------+-----+---------+----------------+</pre>
<p>The password field is a 40 byte long CHAR (SHA-1 hashes in hexadecimal representation are always 40 byte long). The password_salt field is a 16 byte BINARY field &#8211; it will contain some random bytes with no particular encoding so it shouldn&#8217;t be a CHAR or VARCHAR field.</p>
<p>As new users register, the following functions are used to set the user&#8217;s password in the DB:</p>
<pre class="brush: php; title: ; notranslate">
class User
{
  /**
   * This will contain a hashed version of the user's password
   *
   * @var string
   */
  protected $password = null;

  /**
   * This will contain the salt value used to add noise to the password hash
   *
   * @var string
   */
  protected $password_salt = null;

  /**
   * Set the user's password
   *
   * @param string $password
   */
  public function setPassword($password)
  {
    // Test that password is at least 6 characters mixing letters and digits
    if (! preg_match('/^.*(?=.{6,})(?=.*[a-z])(?=.*[A-Z])(?=.*\d).*$/')) {
        throw new \ErrorException(&quot;Password is not strong enough&quot;);
    }

    $this-&gt;password_salt = $this-&gt;generateRandomSalt();
    $this-&gt;password = sha1($password . $this-&gt;password_salt);
  }

  /**
   * Generate a random salt value, 16 bytes long
   *
   * This relies on OpenSSL being available. If it is not available, any
   * cryptographic-grade random string generation function would work. On
   * UNIX machines, you can just read 16 bytes from /dev/urandom.
   *
   * @return string
   */
  protected function generateRandomSalt()
  {
    return openssl_random_pseudo_bytes(16);
  }
}
</pre>
<p>To check a given password, we add the following function to the same class (assume that the protected values are populated from values fetched from the DB):</p>
<pre class="brush: php; title: ; notranslate">
  /**
   * Check if password is correct
   *
   * @param  string $password
   * @return boolean
   */
  public function checkPassword($password)
  {
    $hashed = sha1($password . $this-&gt;password_salt);
    return ($hashed === $this-&gt;password);
  }
</pre>
<p>As you can see, this class (assuming a working database access layer) will do the work of properly salting and hashing passwords before saving them, and of comparing given clear-text passwords to a salted, hashed value stored in the DB. This practically ensures stealing passwords from you is near impossible.</p>
<h2>What&#8217;s next?</h2>
<p>I hope that this article pointed out some good practices in storing passwords in the DB. It is important to remember that while your site may not be very interesting to hack into, hacking into your users&#8217; accounts could be a first step towards identity theft or the hijacking of accounts on another site holding much more sensitive data. Implementing the measures described here would mean you are at least treating your users&#8217; password with the right care.</p>
<p>There are additional aspects to password security which you should look into: using proper security on the transport channel when asking for passwords (HTTPS with a valid certificate), requiring strong enough passwords from your users, avoiding session fixation attacks (<a href="http://php.net/session_regenerate_id">session_regenerate_id</a> at login) and more. I also did not touch procedures of replacing lost passwords, which are also a common weak point vulnerable for phishing attacks. There is quite a lot of material to read out there on these topics, and if I see an interest is raised I might cover some of them myself in the future.</p>
]]></content:encoded>
			<wfw:commentRss>http://arr.gr/blog/2012/01/storing-passwords-the-right-way/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>My PHP Streams API article was published by php&#124;architect</title>
		<link>http://arr.gr/blog/2011/12/my-php-streams-api-article-was-published-by-phparchitect/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=my-php-streams-api-article-was-published-by-phparchitect</link>
		<comments>http://arr.gr/blog/2011/12/my-php-streams-api-article-was-published-by-phparchitect/#comments</comments>
		<pubDate>Sat, 31 Dec 2011 10:30:14 +0000</pubDate>
		<dc:creator>shahar</dc:creator>
				<category><![CDATA[PHP & Web Technologies]]></category>
		<category><![CDATA[article]]></category>
		<category><![CDATA[magazine]]></category>
		<category><![CDATA[php|architect]]></category>

		<guid isPermaLink="false">http://arr.gr/blog/?p=184</guid>
		<description><![CDATA[php&#124;architect, one of the most prominent professional PHP magazines in the world, has published an article I wrote about PHP&#8217;s user-space Streams API in its December 2011 issue: Go with the Flow: PHP’s Userspace Streams API Almost every PHP application out there needs to read data from files or write data to files – or [...]]]></description>
			<content:encoded><![CDATA[<p>php|architect, one of the most prominent professional PHP magazines in the world, has published an article I wrote about PHP&#8217;s user-space Streams API in its <a href="http://www.phparch.com/magazine/2011-2/december/">December 2011</a> issue:</p>
<blockquote>
<h2>Go with the Flow: PHP’s Userspace Streams API</h2>
<div>Almost every PHP application out there needs to read data from files or write data to files – or things that look like files but are not quite files – these unstructured blobs of data are commonly referred to as “streams”. Stream functions allow a scalable, portable and memory efficient way to handle data, and pretty much any PHP developer out there knows how to read data from or write data to a steam. The best part is that you don’t have to be an extension author in order to provide access to any data source as if it was just a regular file. PHP’s userspace streams API allows you do to exactly that, and this article will show you how.</div>
</blockquote>
<div>If you&#8217;re a subscriber, feel free to read the article and send me your feedback. If not, go ahead an buy the issue <img src='http://arr.gr/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </div>
]]></content:encoded>
			<wfw:commentRss>http://arr.gr/blog/2011/12/my-php-streams-api-article-was-published-by-phparchitect/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Replacing a lost SSH key on an Amazon EC2 machine</title>
		<link>http://arr.gr/blog/2011/11/replacing-a-lost-ssh-key-on-an-amazon-ec2-machine/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=replacing-a-lost-ssh-key-on-an-amazon-ec2-machine</link>
		<comments>http://arr.gr/blog/2011/11/replacing-a-lost-ssh-key-on-an-amazon-ec2-machine/#comments</comments>
		<pubDate>Wed, 23 Nov 2011 18:18:11 +0000</pubDate>
		<dc:creator>shahar</dc:creator>
				<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[Linux & FOSS]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[ec2]]></category>
		<category><![CDATA[keypair]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[oops]]></category>
		<category><![CDATA[ssh]]></category>
		<category><![CDATA[tip]]></category>

		<guid isPermaLink="false">http://arr.gr/blog/?p=180</guid>
		<description><![CDATA[Due to an unfortunate shmelting accident (read: poor backup practices), I lost the SSH private key granting me the only way to access one of my EC2 hosted servers. Being unable to access the server, and unable to easily set a new public key through Amazon&#8217;s interfaces, I panicked for a few seconds. Then I [...]]]></description>
			<content:encoded><![CDATA[<p>Due to <a href="http://www.youtube.com/watch?v=sr0gNJ090JA" target="_blank">an unfortunate shmelting accident</a> (read: poor backup practices), I lost the SSH private key granting me the only way to access one of my EC2 hosted servers. Being unable to access the server, and unable to easily set a new public key through Amazon&#8217;s interfaces, I panicked for a few seconds. Then I started trying to hack my way in, and eventually found a way to set a new public key to my user. Here is what I did.</p>
<p>First, know that I was lucky: for this method to properly work, you need a few things:</p>
<ul>
<li>The machine must be EBS based</li>
<li>You need to be able to afford a couple of minutes of downtime</li>
<li>You need to be able to withstand the effects of restarting the machine &#8211; for example, if you do not have an Elastic IP address associated with the machine, its public address will change. In some situations this is not acceptable.</li>
</ul>
<p>After trying some different approaches, what worked for me was to do the following:</p>
<ol>
<li>Generate a new keypair for yourself, and import the public key to your EC2 account</li>
<li>Start a new, clean, cheap machine (this will only be needed to do very simple things, so I recommend using a <em>tiny</em> machine) in the same availability zone as the affected machine</li>
<li>Stop the affected machine (do not terminate, STOP it &#8211; this is only possible with EBS machines)</li>
<li>Detach the root device from the affected machine (by default attached as /dev/sda1)</li>
<li>Attach the detached device to the new clean machine</li>
<li>SSH into the clean machine and mount the affected machine&#8217;s root filesystem somewhere (e.g. in /mnt/fs)</li>
<li>Now you can edit /mnt/fs/root/.ssh/authorized_keys (or on official Ubuntu machines /home/ubuntu/.ssh/authorized_keys) and add your new public key to it</li>
<li>Unmount the volume and terminate the clean machine &#8211; you no longer need it</li>
<li>Re-attach the root device to the affected machine (which should be stopped) &#8211; ensure to attach it as the same device it was before (e.g. /dev/sda1)</li>
<li>Re-start your old machine &#8211; you should now be able to use your new key!</li>
</ol>
<p>Another approach which could work but I gave up on after a couple of attempts (I think it really depends on the init scripts in the machine you are using), is to stop the machine and change the User Data of it to a shell script that sets a new public key in the right place, then start it again.</p>
<p>And really, you should backup your keys!</p>
]]></content:encoded>
			<wfw:commentRss>http://arr.gr/blog/2011/11/replacing-a-lost-ssh-key-on-an-amazon-ec2-machine/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Why I don&#8217;t like the term &#8220;NoSQL&#8221;</title>
		<link>http://arr.gr/blog/2011/10/why-i-dont-like-the-term-nosql/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=why-i-dont-like-the-term-nosql</link>
		<comments>http://arr.gr/blog/2011/10/why-i-dont-like-the-term-nosql/#comments</comments>
		<pubDate>Mon, 31 Oct 2011 10:22:05 +0000</pubDate>
		<dc:creator>shahar</dc:creator>
				<category><![CDATA[Linux & FOSS]]></category>
		<category><![CDATA[PHP & Web Technologies]]></category>
		<category><![CDATA[Thoughts & Possibilities]]></category>
		<category><![CDATA[couchdb]]></category>
		<category><![CDATA[mongodb]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[rant]]></category>
		<category><![CDATA[rdmbs]]></category>
		<category><![CDATA[terminology]]></category>

		<guid isPermaLink="false">http://arr.gr/blog/?p=171</guid>
		<description><![CDATA[This is a rant post, but just to clarify things, it&#8217;s not a rant against the use of non-relational databases. I think that the shift in recent years from a world in which relational databases are used almost exclusively regardless of what the need is, to today&#8217;s situation where it is possible and even considered [...]]]></description>
			<content:encoded><![CDATA[<p>This is a rant post, but just to clarify things, it&#8217;s not a rant against the use of non-relational databases. I think that the shift in recent years from a world in which relational databases are used almost exclusively regardless of what the need is, to today&#8217;s situation where it is possible and even considered a good idea to choose the best fitting solution from any number of data storage paradigms, is a truly blessed change. I am a big fan of some non-relational database solutions, and to be honest as a programmer I enjoy using some of them more than I enjoy MySQL or any other relational database.</p>
<p>This is a rant against the too-common term &#8220;NoSQL&#8221;. In my opinion, &#8220;NoSQL&#8221; is an example of layman terminology which does not properly describe the concepts which in most cases it aims to describe, and should not be used by professionals which are technical enough to understand the true meaning of these concepts.</p>
<p>&#8220;NoSQL&#8221; databases are all about the data model &#8211; in most cases, the term is used to describe any kind of storage engine (or database) in which data is stored in non-relational manner: object storage, document storage, key-value storage etc. Indeed, the term is more about what the database is not that about what it is.</p>
<p>Relational data is data that can be described as a table &#8211; in contrast to what some think, the term &#8220;relational database&#8221; has nothing to do with the ability to define and enforce relationships between data in different tables. If this was the case, MySQL using the MyISAM storage engine would not be a relational database. The term <a href="http://en.wikipedia.org/wiki/Relational_algebra">&#8220;relation&#8221; is a mathematical term</a>, which existed before the creation of relational databases and is used to describe a relationship between two finite data sets, which can be described in a tabular manner (and I am not a mathematician, not even close &#8211; so I apologize in advance for this likely inaccurate description).</p>
<p>But, SQL has nothing to do with this &#8211; SQL is the language used to send commands to the database, and nothing more. It is true that there is an almost 1-to-1 correlation between database engines that store data in a relational manner and database engines that use SQL as a query language, but saying that relational databases are SQL databases is like saying that  (and assume it&#8217;s 1984 again) the Russian language should be abolished when in fact we want to say that communism is an unfitting economic system. It&#8217;s a poor way to describe your intentions, and it makes you sound like an ignorant moron.</p>
<p>There are many client libraries and wrappers that allow you to query a relational database such as MySQL and Oracle without writing any SQL code yourself. This doesn&#8217;t make them NoSQL databases. Some popular non-relational databases, such as <a href="http://aws.amazon.com/articles/1231">Amazon SimpleDB</a> and the <a href="http://code.google.com/appengine/docs/python/datastore/gqlreference.html">Google App Engine Data Store</a> provide query languages that are quite similar to SQL. This doesn&#8217;t make them SQL databases.SQL is just a language, and it is a good one for what it&#8217;s supposed to do (putting aside all sorts of discrepancies between vendor-specific SQL implementations). SQL is not what NoSQL databases are NOT about.</p>
<p>So, next time when you want to use a term that describes all databases that do not store data in a tabular manner, use the term &#8220;non-relational&#8221; or if you really like acronyms, &#8220;NonRDBMS&#8221;, and not &#8220;NoSQL&#8221;. Or even better &#8211; use a term that describes what your preferred solution <strong>is</strong>, not what it is not. After all, when you say &#8220;non-relational storage engine&#8221;, you are probably not referring to your file system, right?</p>
]]></content:encoded>
			<wfw:commentRss>http://arr.gr/blog/2011/10/why-i-dont-like-the-term-nosql/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Bitbucket: Converting Hg repositories to Git</title>
		<link>http://arr.gr/blog/2011/10/bitbucket-converting-hg-repositories-to-git/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=bitbucket-converting-hg-repositories-to-git</link>
		<comments>http://arr.gr/blog/2011/10/bitbucket-converting-hg-repositories-to-git/#comments</comments>
		<pubDate>Tue, 04 Oct 2011 23:05:03 +0000</pubDate>
		<dc:creator>shahar</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[bitbucket]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[hg]]></category>
		<category><![CDATA[mercurial]]></category>
		<category><![CDATA[scm]]></category>
		<category><![CDATA[tip]]></category>

		<guid isPermaLink="false">http://arr.gr/blog/?p=163</guid>
		<description><![CDATA[Recently I started using Bitbucket for private repository hosting for a project I&#8217;m working on. While I had no experience with Mercurial, I figured it can&#8217;t be that tricky &#8211; and Bitbucket offers free private hosting which is what this project needed (couldn&#8217;t go public, couldn&#8217;t pay, didn&#8217;t have the time to set up self-hosted [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I started using <a href="https://bitbucket.org/">Bitbucket</a> for private repository hosting for a project I&#8217;m working on. While I had no experience with Mercurial, I figured it can&#8217;t be that tricky &#8211; and Bitbucket offers free private hosting which is what this project needed (couldn&#8217;t go public, couldn&#8217;t pay, didn&#8217;t have the time to set up self-hosted SCM hosting).</p>
<p>All in all I like Bitbucket (although I have to admit on most aspects they seem to fall behind GitHub), but not so much using Mercurial &#8211; for all sorts of reasons it felt quirky and less polished than Git, which honestly I have much more experience with.</p>
<p>So following <a href="http://blog.bitbucket.org/2011/10/03/bitbucket-now-rocks-git/">Bitbucket&#8217;s big announcement</a> on Git support, I&#8217;ve decided to migrate my repositories from Hg to Git, while keeping them on Bitbucket and maintaining repository history. I&#8217;m happy to say it was relatively a piece of cake to successfully achieve. Here is what I did:</p>
<h3>Step 1: Set up your repositories</h3>
<p>First, I renamed my old Hg repository through Mercurial&#8217;s web interface, to something like &#8220;MyProject&#8221; to &#8220;MyProject Hg&#8221;. This changes the repository URL, but since I wasn&#8217;t planning on using it anymore that doesn&#8217;t really matters &#8211; plus you can always rename back if things go bad.</p>
<p>Then, I created a new Git repository with the name of the previous repository, e.g. &#8220;MyProject&#8221;. Again, that can be easily done from Bitbucket&#8217;s Web interface.</p>
<h3>Step 2: Install the Mercurial hggit plugin</h3>
<p>The <em>hggit</em> plugin allows your Mercurial command line tool <em>hg</em> to talk to git repositories &#8211; that is push and pull from Git. Installing it is easy, as it is probably available from your package manager. On Mac, if you use Macports, you can run:</p>
<pre>  $ sudo port install py26-hggit</pre>
<p>While on Ubuntu, run:</p>
<pre>  $ sudo aptitude install mercurial-git</pre>
<p>Then, make sure to load the plugin by adding the following lines to your <em>~/.hgrc </em>file:</p>
<pre>  [extensions]
  hggit=</pre>
<p>Congratulations: Your <em>hg</em> command now speaks Git!</p>
<h3>Step 3: Push your code into your Git repository</h3>
<p>To push your code into your new Git repository, you basically need to run two commands:</p>
<p>First, create a Mercurial bookmark that references <em>master</em> to your <em>default </em>branch. This will help Git create the right refs later on:</p>
<pre>  $ cd ~/myproject-hg-repo/
  $ hg bookmark -r default master</pre>
<p>Next, simply push your code into the newly created Git repository:</p>
<pre>  $ hg push git+ssh://git@bitbucket.org:shaharevron/myproject.git</pre>
<p>Of course, make sure to change the repository URL to the URL of your new Git repository. To make sure <em>hg</em> understands you&#8217;re referring to a Git repository, if using SSH add the <em>git+ssh://</em> prefix to the URL. This should push your entire repository to the new Git repository, and within a few seconds up to a few minutes (depending on how big your repository is), you should be able to see all your old commits in the new Git repo.</p>
<h3>Step 4: Switch your local repository to use Git</h3>
<p>Now that your new Git repo is up at Bitbucket, you&#8217;ll need to switch to using Git locally. There are two paths you can take here: the safe one, is to simply <em>git clone </em>your code to a new working directory and work from there. It&#8217;s safe, and will work well. However, if you&#8217;re a cowboy like me, and are too lazy to create a new IDE project on a different directory, you can in fact simply switch to working with Git on the same directory (but I still seriously recommend you ensure Bitbucket really does have your code as backup&#8230;).</p>
<p>Here is how to do it. From the local repository directory, run:</p>
<pre>  $ git init
  $ git remote add origin git@bitbucket.org:shaharevron/myproject.git
  $ git pull origin master
  $ git reset --hard HEAD</pre>
<p>Again, replace the repository URL with your own. This will &#8220;merge&#8221; everything in your Git repo into the local working directory. You will need to create a new <em>.gitignore </em>file if needed &#8211; and can now simply delete the <em>.hg</em> directory, as it is no longer needed. You can now happily use Git with your Bitbucket code.</p>
<p>While there shouldn&#8217;t be any problems, I also recommend keeping your old Hg repository around on Bitbucket for a few days, just to make sure nothing blows up &#8211; and delete it from Bitbucket&#8217;s web interface once you&#8217;re sure everything works well.</p>
]]></content:encoded>
			<wfw:commentRss>http://arr.gr/blog/2011/10/bitbucket-converting-hg-repositories-to-git/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Goodbye, Zend</title>
		<link>http://arr.gr/blog/2011/09/goodbye-zend/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=goodbye-zend</link>
		<comments>http://arr.gr/blog/2011/09/goodbye-zend/#comments</comments>
		<pubDate>Sat, 17 Sep 2011 12:28:09 +0000</pubDate>
		<dc:creator>shahar</dc:creator>
				<category><![CDATA[Thoughts & Possibilities]]></category>
		<category><![CDATA[life]]></category>
		<category><![CDATA[work]]></category>
		<category><![CDATA[zend]]></category>

		<guid isPermaLink="false">http://arr.gr/blog/?p=160</guid>
		<description><![CDATA[Ok, the title kind of says it all &#8211; this has been known to some for a few months now, but for the sake of clarifying things up, I&#8217;m leaving Zend &#8211; or to be technically accurate, has already left. This was not an easy decision for me, as Zend has been for more than [...]]]></description>
			<content:encoded><![CDATA[<p>Ok, the title kind of says it all &#8211; this has been known to some for a few months now, but for the sake of clarifying things up, I&#8217;m leaving <a href="http://zend.com">Zend</a> &#8211; or to be technically accurate, has already left.</p>
<p>This was not an easy decision for me, as Zend has been for more than 6 years not only my employer but also my school, my workshop and a little bit of home as well. This sounds like bullshit &#8211; but since I started there as a first-level support engineer and am leaving as a co-Product Manager for the company&#8217;s flagship product, I think it&#8217;s fair to say I gained as much as I contributed.</p>
<p>However, it&#8217;s time to move on for me. I&#8217;m looking into doing my own things at my own time, and being my own boss. I want more free time to experiment, play and pursue my <a href="http://www.youtube.com/user/shaharevronnn#p/a/u/0/rjcCwyUk3nM">hobbies</a> and <a href="https://plus.google.com/u/0/110444114091063742013/posts/Vk4KsufgNwe">silly ideas</a>.</p>
<p>In recent years the company took some directions I was not 100% happy with, and being responsible for realizing some of these ideas, it was hard for me to stay at my role. I started thinking what I want to do next &#8211; and was offered a couple of very tempting roles within Zend &#8211; but then I realized that really, I want to make a bigger change.</p>
<p>And here I am.</p>
<p>For the last couple of months I have reduced my role at Zend to a 2-day consulting position, and will continue to consult Zend on a part-time basis for at least a few weeks.In the rest of my time, I plan to think, <a title="that's what I'm reading now" href="http://www.amazon.com/Zen-Motorcycle-Maintenance-Robert-Pirsig/dp/B001I8ETFA/ref=tmm_hrd_title_0">read</a>, paddle, rest, blog more and work on some ideas I have. I plan to keep contributing to the PHP community, and specifically to <a href="http://github.com/shevron/zf2">Zend Framework 2.0</a>.</p>
<p>Will I chicken out in 3 months and decide to get a real job again? Maybe&#8230; but I plan to make the most of my time until then.</p>
]]></content:encoded>
			<wfw:commentRss>http://arr.gr/blog/2011/09/goodbye-zend/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Blog Moved</title>
		<link>http://arr.gr/blog/2011/07/blog-moved/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=blog-moved</link>
		<comments>http://arr.gr/blog/2011/07/blog-moved/#comments</comments>
		<pubDate>Sun, 31 Jul 2011 13:07:15 +0000</pubDate>
		<dc:creator>shahar</dc:creator>
				<category><![CDATA[Meta]]></category>
		<category><![CDATA[meta]]></category>

		<guid isPermaLink="false">http://arr.gr/blog/?p=148</guid>
		<description><![CDATA[I&#8217;ve just moved my blog to a new address (as you can hopefully see). It&#8217;s much shorter and I hope that now that I don&#8217;t have to type that very long address every time I want to post something, I might blog a little more (say once every 4 months and not 6). Once I&#8217;ll [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just moved my blog to a new address (as you can hopefully see). It&#8217;s much shorter and I hope that now that I don&#8217;t have to type that very long address every time I want to post something, I might blog a little more (say once every 4 months and not 6).</p>
<p>Once I&#8217;ll make sure everything works well in the new home, I&#8217;ll post some news &#8211; so stay tuned!</p>
]]></content:encoded>
			<wfw:commentRss>http://arr.gr/blog/2011/07/blog-moved/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTML 5 Canvas Game of Life</title>
		<link>http://arr.gr/blog/2011/01/html-5-canvas-game-of-life/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=html-5-canvas-game-of-life</link>
		<comments>http://arr.gr/blog/2011/01/html-5-canvas-game-of-life/#comments</comments>
		<pubDate>Tue, 25 Jan 2011 09:20:02 +0000</pubDate>
		<dc:creator>shahar</dc:creator>
				<category><![CDATA[Linux & FOSS]]></category>
		<category><![CDATA[PHP & Web Technologies]]></category>
		<category><![CDATA[Thoughts & Possibilities]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[experiment]]></category>
		<category><![CDATA[game-of-life]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://prematureoptimization.org/blog/?p=134</guid>
		<description><![CDATA[I recently started looking into different HTML 5.0 related technologies, one of the most exciting ones being the new Canvas tag and API. As a little test, I&#8217;ve implemented a little Game of Life thing using HTML 5 Canvas, which you can see in action here: http://arr.gr/playground/life/ (view source to see the code behind it). [...]]]></description>
			<content:encoded><![CDATA[<p>I recently started looking into different <a href="http://en.wikipedia.org/wiki/HTML5">HTML 5.0</a> related technologies, one of the most exciting ones being the new <a href="https://developer.mozilla.org/en/HTML/canvas">Canvas tag and API</a>.</p>
<p>As a little test, I&#8217;ve implemented a little <a href="http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">Game of Life</a> thing using HTML 5 Canvas, which you can see in action here: <a href="http://arr.gr/playground/life/">http://arr.gr/playground/life/</a> (view source to see the code behind it).</p>
<p style="text-align: center;"><a href="http://arr.gr/playground/life/"><img class="aligncenter size-medium wp-image-135" title="Game of Life in HTML5 Canvas" src="http://arr.gr/blog/wp-content/uploads/2011/01/Screen-shot-2011-01-25-at-11.08.57-AM-300x214.png" alt="Game of Life in HTML5 Canvas" /></a></p>
<p>The algorithm is not very smart so it&#8217;s kind of slow and CPU intensive, but still fun to watch. It works nicely on Firefox 4.0, and latest Chrome and Safari versions, and a bit slow on Firefox 3.6. I did not test with any IE version but I do not expect it to work in IE 6 or 7, maybe 8 and probably 9.</p>
<p>I think Game of Life by itself is worth at least an entire post regardless of this HTML5 implementation, especially because I&#8217;m a big fan of things that bring CS and philosophy together, so I may write about it at a later point, but for now I suggest you let it run for a while (a few hundreds of generations) and see what you get <img src='http://arr.gr/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://arr.gr/blog/2011/01/html-5-canvas-game-of-life/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ZendCon 10 talk: Amazon Services in Zend Framework</title>
		<link>http://arr.gr/blog/2010/11/zendcon-10-talk-amazon-services-in-zend-framework/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=zendcon-10-talk-amazon-services-in-zend-framework</link>
		<comments>http://arr.gr/blog/2010/11/zendcon-10-talk-amazon-services-in-zend-framework/#comments</comments>
		<pubDate>Wed, 03 Nov 2010 23:00:07 +0000</pubDate>
		<dc:creator>shahar</dc:creator>
				<category><![CDATA[Linux & FOSS]]></category>
		<category><![CDATA[PHP & Web Technologies]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[ebs]]></category>
		<category><![CDATA[ec2]]></category>
		<category><![CDATA[s3]]></category>
		<category><![CDATA[slb]]></category>
		<category><![CDATA[zend framework]]></category>
		<category><![CDATA[zendcon]]></category>
		<category><![CDATA[zendcon10]]></category>
		<category><![CDATA[zf]]></category>

		<guid isPermaLink="false">http://prematureoptimization.org/blog/?p=131</guid>
		<description><![CDATA[Wow I haven&#8217;t posted in a while&#8230; I&#8217;m still at ZendCon in Santa Clara, and have just finished my last talk, which was about the different Zend Framework components that can be used to work with the Amazon Cloud Services of S3 and EC2. Presentation was pretty good, although I had to hurry up in [...]]]></description>
			<content:encoded><![CDATA[<p>Wow I haven&#8217;t posted in a while&#8230; I&#8217;m still at ZendCon in Santa Clara, and have just finished my last talk, which was about the different Zend Framework components that can be used to work with the Amazon Cloud Services of S3 and EC2. </p>
<p>Presentation was pretty good, although I had to hurry up in the end and skip some of the last slides. </p>
<div style="width:425px" id="__ss_5660031"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/shahar/amazon-cloud-services-and-zend-framework" title="Amazon Cloud Services and Zend Framework">Amazon Cloud Services and Zend Framework</a></strong><object id="__sse5660031" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=amazoncloudandzendframework-101103171033-phpapp01&#038;stripped_title=amazon-cloud-services-and-zend-framework&#038;userName=shahar" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse5660031" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=amazoncloudandzendframework-101103171033-phpapp01&#038;stripped_title=amazon-cloud-services-and-zend-framework&#038;userName=shahar" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object>
<div style="padding:5px 0 12px">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/shahar">Shahar Evron</a>.</div>
</div>
<p>The slides are now up in <a href="http://www.slideshare.net/shahar/amazon-cloud-services-and-zend-framework">Slideshare</a>, and can be downloaded or viewed on-line. </p>
]]></content:encoded>
			<wfw:commentRss>http://arr.gr/blog/2010/11/zendcon-10-talk-amazon-services-in-zend-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

