<?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:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Jonathon Hill</title>
	
	<link>http://jonathonhill.net</link>
	<description>Notes, projects, and opinions on web application development and other related topics.</description>
	<lastBuildDate>Fri, 19 Apr 2013 22:21:43 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/JonathonHill" /><feedburner:info uri="jonathonhill" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Send mail() using PHP on Mac OS X</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/8RIGWxTzhrk/</link>
		<comments>http://jonathonhill.net/2013-04-19/send-mail-using-php-on-mac-os-x/#comments</comments>
		<pubDate>Fri, 19 Apr 2013 22:21:43 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[mail]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[sendmail]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=410</guid>
		<description><![CDATA[Thanks to Benjamin Rojas, Andy Stratton, and a tip from Jasper, I was able to successfully send email from my home-brewed MAMP environment. Here&#8217;s the summary. Add the following to your /etc/postfix/sasl_passwd file: smtp.gmail.com:587 username@gmail.com:password (Of course, you don&#8217;t have to use GMail or port 587, but you get the idea.) Configure postfix: sudo postmap [...]]]></description>
				<content:encoded><![CDATA[<p>Thanks to <a href="http://benjaminrojas.net/configuring-postfix-to-send-mail-from-mac-os-x-mountain-lion/" target="_blank">Benjamin Rojas</a>, <a href="http://theandystratton.com/2009/fix-phps-mail-function-after-latest-os-x-leopard-update" target="_blank">Andy Stratton</a>, and a <a href="http://jspr.tndy.me/php-mail-and-osx-leopard/" target="_blank">tip from Jasper</a>, I was able to successfully send email from my home-brewed MAMP environment. Here&#8217;s the summary.</p>
<ol>
<li>Add the following to your <code>/etc/postfix/sasl_passwd</code> file:
<pre><code>smtp.gmail.com:587 username@gmail.com:password</code></pre>
<p>(Of course, you don&#8217;t have to use GMail or port 587, but you get the idea.)</li>
<li>Configure postfix:
<pre><code>sudo postmap /etc/postfix/sasl_passwd</code></pre>
</li>
<li>Backup and edit your postfix configuration:
<pre><code>sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.orig
sudo vim /etc/postfix/main.cf</code></pre>
<p>If you use TLS, then you will need to add the TLS settings but the other settings should already be there as a result of running the <code>postmap</code> command. You should have these options set in <code>/etc/postfix/main.cf</code>:</p>
<pre><code>mydomain_fallback = localhost
mail_owner = _postfix
setgid_group = _postdrop
relayhost=smtp.gmail.com:587
smtp_sasl_auth_enable=yes
smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options=
smtp_use_tls=yes
smtp_tls_security_level=encrypt
tls_random_source=dev:/dev/urandom</code></pre>
</li>
<li>Start postfix:
<pre><code>sudo postfix start</code></pre>
<p>If there are errors, you may need to edit your <code>/etc/postfix/main.cf</code> and restart postfix:</p>
<pre><code>sudo postfix reload</code></pre>
</li>
<li>Send a test message:
<pre><code>date | mail -s test youremailaddress@yourdomain.com</code></pre>
</li>
<li>Make postfix start automatically on boot by opening your <code>/System/Library/LaunchDaemons/org.postfix.master.plist</code> file and adding:
<pre><code>&lt;key&gt;RunAtLoad&lt;/key&gt;
&lt;true/&gt;</code></pre>
<p>Add this at the bottom just before the closing <code>&lt;/dict&gt; tag.</code></li>
<li>Edit your <code>/etc/php.ini</code> file and configure the <code>sendmail_path</code> option:
<pre><code>sendmail_path = "sendmail -t -i"</code></pre>
</li>
</ol>
<p>You should now be able to send email using PHP&#8217;s <a href="http://php.net/mail" target="_blank"><code>mail()</code></a> function. If you continue to have issues, watch the contents of your postfix mail log:</p>
<pre><code>tail -f /var/log/mail.log</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2013-04-19/send-mail-using-php-on-mac-os-x/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2013-04-19/send-mail-using-php-on-mac-os-x/</feedburner:origLink></item>
		<item>
		<title>Write tests, you might learn something</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/8cBYKEXUrSI/</link>
		<comments>http://jonathonhill.net/2013-04-05/write-tests-you-might-learn-somethin/#comments</comments>
		<pubDate>Fri, 05 Apr 2013 19:43:28 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[aes-256]]></category>
		<category><![CDATA[mcrypt]]></category>
		<category><![CDATA[phpunit]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=405</guid>
		<description><![CDATA[Writing unit tests for your code is widely regarded as a best practice. There are many excuses for not writing tests: time, cost, and the fun factor. Excuses aside, there are some Good Reasons to write tests for your code. I just discovered one today. Write tests, you might learn something&#8230;faster I&#8217;m working on an API [...]]]></description>
				<content:encoded><![CDATA[<p>Writing unit tests for your code is widely regarded as a best practice. There are many excuses for <em>not</em> writing tests: time, cost, and the fun factor. Excuses aside, there are some <a href="http://twitpic.com/7c4k9m" target="_blank">Good Reasons</a> to write tests for your code. I just discovered one today.</p>
<h2>Write tests, you might learn something&#8230;faster</h2>
<p>I&#8217;m working on an API integration that requires a bit of AES-256 encryption. Getting that worked out in PHP took some surprising turns, so just to be sure that I was getting all the details right (such as initialization vector size) I decided to write a unit test for my mcrypt wrapper library.</p>
<p>Since this library had worked well in other projects, imagine my surprise when the first AES-256 encrypt-decrypt test failed:</p>
<pre>$ phpunit *
PHPUnit 3.7.8 by Sebastian Bergmann.

.F..

Time: 0 seconds, Memory: 2.50Mb

There was 1 failure:

1) EncryptionAes256Test::testEncryptDecrypt with data set #0 ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz')
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz'
+Binary String: 0x4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656668696a6b6c6d6e6f707172737475767778797a00000000000000000000000000

/Users/jhill/Sites/mrtz_010/Source/tests/EncryptionTest.php:53

FAILURES!
Tests: 4, Assertions: 7, Failures: 1.</pre>
<p>Echoing out the decrypted string proved just as bizarre:</p>
<pre>string(51) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz"
string(64) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz"</pre>
<p>It seems that <a href="http://www.php.net/manual/en/function.mcrypt-decrypt.php#54734" target="_blank">mcrypt pads the string with enough null characters (<code>\0</code>) to match the block size</a>, and unlike strings in C, PHP strings do not necessarily end with the first null character.</p>
<p>The fix was obvious:</p>
<pre>$decrypted = rtrim($decrypted, "\0");</pre>
<p>Guess what? By investing time in a simple unit test, I unexpectedly learned something new about PHP, AND saved myself quite a bit of time debugging an API call that would not have worked.</p>
<p>I&#8217;d say it was worth it.</p>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2013-04-05/write-tests-you-might-learn-somethin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2013-04-05/write-tests-you-might-learn-somethin/</feedburner:origLink></item>
		<item>
		<title>What Employers are Looking For</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/CLmhDYU3My0/</link>
		<comments>http://jonathonhill.net/2013-01-03/what-employers-are-looking-for/#comments</comments>
		<pubDate>Fri, 04 Jan 2013 01:40:37 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[Hiring]]></category>
		<category><![CDATA[IT]]></category>
		<category><![CDATA[Recruiting]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=401</guid>
		<description><![CDATA[Employers looking for high-quality software developers are plentiful. At the January meeting of the Atlanta PHP user group Ari Waller shared some useful notes on what employers are looking for in 2013 (universally, not just in Atlanta). Genuine software engineers This is extremely important to companies looking to hire talent. Ability to engineer software and [...]]]></description>
				<content:encoded><![CDATA[<p>Employers looking for high-quality software developers are plentiful. At the January meeting of the Atlanta PHP user group Ari Waller shared some useful notes on what employers are looking for in 2013 (universally, not just in Atlanta).<span id="more-401"></span></p>
<h2>Genuine software engineers</h2>
<p>This is extremely important to companies looking to hire talent. Ability to engineer software and build solutions that solve problems in a maintainable and scalable way are in high demand.</p>
<ul>
<li>Problem-solving ability</li>
<li>Clean code</li>
<li>Design patterns</li>
</ul>
<h2>Broader skill sets</h2>
<p>While core specialties such as PHP are still as important as ever, employers these days are looking for candidates with more diversity than they have in the past.</p>
<ul>
<li>Higher demand for Javascript and Python</li>
<li>NoSQL &#8211; has surpassed old-school relational database technologies such as Oracle</li>
<li>Mobile development experience &#8211; less allowance for lack of actual experience in mobile development</li>
<li>DevOps is a huge field</li>
<li>Functional programming</li>
</ul>
<h2>Where do you want to go today?</h2>
<p>Ari shared the &#8220;TLC3&#8243; formula for evaluating both potential employers, and potential employees. There is no right or wrong answer, but these are important questions to ask:</p>
<ul>
<li>Technology &#8211; what tools/technologies are or will be used?</li>
<li>Leadership &#8211; don&#8217;t like, or don&#8217;t trust current leadership? Do you want to lead, or would you rather follow?</li>
<li>Compensation &#8211; is this firm, or are you willing to make trade-offs in exchange for other types of benefits (such as telecommute)?</li>
<li>Career Path &#8211; what is your dream job? Will this help you get there?</li>
<li>Culture &#8211; what goals, management styles, and ideals appeal to you? Is this a good fit?</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2013-01-03/what-employers-are-looking-for/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2013-01-03/what-employers-are-looking-for/</feedburner:origLink></item>
		<item>
		<title>Video: Multi-tasking in PHP</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/6iHkwC86_Rs/</link>
		<comments>http://jonathonhill.net/2012-12-19/video-multi-tasking-in-php/#comments</comments>
		<pubDate>Thu, 20 Dec 2012 05:26:21 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[ATLPHP]]></category>
		<category><![CDATA[Multitasking]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Presentations]]></category>
		<category><![CDATA[Semaphores]]></category>
		<category><![CDATA[Video]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=397</guid>
		<description><![CDATA[My Multi-tasking in PHP talk at the Atlanta PHP User Group December 2012 meetup. Hope you can join us sometime! Presentation link bundle (includes slides): http://bitly.com/atlphpm]]></description>
				<content:encoded><![CDATA[<p><iframe src="http://player.vimeo.com/video/55950992?badge=0" height="281" width="500" allowfullscreen="" frameborder="0"></iframe></p>
<p>My <a href="http://vimeo.com/55950992">Multi-tasking in PHP</a> talk at the <a href="http://vimeo.com/atlantaphp">Atlanta PHP User Group</a> December 2012 meetup. Hope you can join us sometime!</p>
<p>Presentation link bundle (includes slides): <a href="http://bitly.com/atlphpm" target="_blank">http://bitly.com/atlphpm</a></p>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-12-19/video-multi-tasking-in-php/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-12-19/video-multi-tasking-in-php/</feedburner:origLink></item>
		<item>
		<title>Scaling Your Website: Three Stages</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/1t_GQhDw83E/</link>
		<comments>http://jonathonhill.net/2012-12-16/scaling-your-website-three-stages/#comments</comments>
		<pubDate>Mon, 17 Dec 2012 05:55:26 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[Scaling]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=395</guid>
		<description><![CDATA[So, you have A Big Idea that will Revolutionize The World. Naturally, people will flock to your site and engage with it. Naturally, you are concerned about whether your infrastructure can handle success. You wish to plan for success, it would be foolish not to, eh? Don&#8217;t let enthusiasm (or ignorance) lead you to premature over-optimization. [...]]]></description>
				<content:encoded><![CDATA[<p><span style="line-height: 1.714285714; font-size: 1rem;">So, you have A Big Idea that will Revolutionize The World. Naturally, people will flock to your site and engage with it. Naturally, you are concerned about whether your infrastructure can handle success. You wish to plan for success, it would be foolish not to, eh? </span><span style="line-height: 1.714285714; font-size: 1rem;">Don&#8217;t let enthusiasm (or ignorance) lead you to premature over-optimization. After all, your Big Idea has not yet passed the test of time.</span></p>
<p>Here I will describe the three early stages commonly encountered when scaling a new website or startup application. Each one builds off the previous ones, starting with the lowest-hanging fruit and working up.<span id="more-395"></span></p>
<p><span style="line-height: 1.714285714; font-size: 1rem;">Achieving each level requires significant amounts of work, testing, and benchmarking. Consider the economics and do not scale more than is necessary. A</span><span style="line-height: 1.714285714; font-size: 1rem;">s you grow, you will, if successful, have the revenue to scale with the demand.</span></p>
<p><span style="line-height: 1.714285714; font-size: 1rem;">At each stage you should benchmark to test the capacity you can handle, and monitor your load with respect to this benchmark so that you can scale proactively, before your site breaks rather than after.</span></p>
<h2>Stage 1: (1) application server, (1) database server</h2>
<ol>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Use an opcode cache such as </span><a style="line-height: 1.714285714; font-size: 1rem;" href="http://php.net/manual/en/book.apc.php" target="_blank">APC</a><span style="line-height: 1.714285714; font-size: 1rem;"> to speed up PHP execution.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Move the database onto its own server.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Run </span><a style="line-height: 1.714285714; font-size: 1rem;" href="http://www.percona.com/software/percona-server" target="_blank">Percona Server 5.5</a><span style="line-height: 1.714285714; font-size: 1rem;"> instead of MySQL 5.5.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Tune your database server configuration (my.ini).</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Swap out </span><a style="line-height: 1.714285714; font-size: 1rem;" href="http://arstechnica.com/business/2011/11/a-faster-web-server-ripping-out-apache-for-nginx/" target="_blank">Apache for Nginx</a><span style="line-height: 1.714285714; font-size: 1rem;">, it is a lot more efficient.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Store your </span><a style="line-height: 1.714285714; font-size: 1rem;" href="http://shiflett.org/articles/storing-sessions-in-a-database" target="_blank">user session data in the database</a><span style="line-height: 1.714285714; font-size: 1rem;"> (but watch out for </span><a style="line-height: 1.714285714; font-size: 1rem;" href="http://www.mysqlperformanceblog.com/2007/03/27/php-sessions-files-vs-database-based/" target="_blank">this problem</a><span style="line-height: 1.714285714; font-size: 1rem;">).</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Store static assets in the cloud and server them using a CDN (I recommend <a href="http://aws.amazon.com/s3/" target="_blank">Amazon S3</a> + <a href="http://aws.amazon.com/cloudfront/" target="_blank">Amazon CloudFront</a>, but there are others). This will take a large portion of the load off your application server.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Store file uploads in the cloud (Amazon S3).</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Audit the database queries and table structures for inefficiencies (missing indexes, poorly optimized joins, excessive queries).</span></li>
</ol>
<p>Both servers should be running at least 512MB to 1GB of RAM. Properly configured, this simple setup will handle quite a beating. With fewer moving parts, you will have more time to develop and enhance your offering. As traffic grows, add RAM until it is more economical to move to Stage 2.</p>
<h2>Stage 2: (1-2) <a href="http://www.ha-cc.org/high_availability/components/application_availability/cluster/load_balancing_cluster/" target="_blank">load balancers</a>, (2-5) application servers, (1) database server</h2>
<ol>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Add 1 or 2 dedicated servers with load-balancing software running.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Add 2 or more identical copies of your application server.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Scale up your database server to at least 2GB RAM and re-tune your database server configuration.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Change your domain DNS to point to the IP address of the load balancer.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Use a deployment method that supports server clusters, such as </span><a style="line-height: 1.714285714; font-size: 1rem;" href="http://capistranorb.com/" target="_blank">Capistrano</a>.</li>
</ol>
<h3>Cloud-hosted load balancing</h3>
<p><a href="http://www.rackspace.com/cloud/public/loadbalancers/" target="_blank">Rackspace</a> and <a href="http://aws.amazon.com/elasticloadbalancing/" target="_blank">Amazon</a> provide load balancing as a service. You get redundancy, and you can use smaller web servers (256-512MB RAM) for cost savings. Amazon provides more datacenters and more flexibility. The cost of rolling your own load balancer is unlikely to be justified at this stage, and I highly recommend using one of these unless you have skilled system administrators on staff and have done a cost analysis that favors self-hosting.</p>
<p>When combined with Amazon&#8217;s <a href="http://aws.amazon.com/route53/" target="_blank">Route 53 DNS service</a>, you can configure multiple load balancers in multiple regions to provide an even greater level of redundancy.</p>
<h3>Rolling your own load balancer</h3>
<p>There are several open-source load balancing applications available, evaluate and choose the best one for your needs:</p>
<ul>
<li>Squid Cache, <a href="http://wiki.squid-cache.org/ConfigExamples#Reverse_Proxy_.28Acceleration.29" target="_blank">configured as a reverse proxy</a>. Note that Squid can handle SSL (https) if needed, allowing your application servers to communicate with the load balancer more quickly over normal HTTP.</li>
<li><a href="http://haproxy.1wt.eu/" target="_blank">HAProxy</a></li>
<li>Nginx, <a href="http://tumblr.intranation.com/post/766288369/using-nginx-reverse-proxy" target="_blank">configured as a reverse proxy</a></li>
<li><a href="http://www.apsis.ch/pound" target="_blank">Pound</a></li>
</ul>
<p>If you are running more than one load balancer, use <a href="http://www.keepalived.org/" target="_blank">keepalived</a> to handle failover.</p>
<h3>Code deployments</h3>
<p>When multiple application servers are involved, you will need an automated way to push out code deployments. There are several ways to do this:</p>
<ul>
<li>Create a tool using the <a href="http://capistranorb.com/" target="_blank">Capistrano</a> Ruby gem</li>
<li>Roll your own tool using your preferred scripting language and rsync</li>
<li>Configure a protected git server with your code, and set up a cronjob to perform a `git pull`</li>
<li>Use a hosted code repository service like <a href="http://beanstalkapp.com/" target="_blank">Beanstalk</a> which supports code deployment via SFTP</li>
</ul>
<h2>Stage 3: (2+) load balancers, (5-10) application servers, (1) master database server, (2+) slave database servers</h2>
<ol>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Scale your primary (master) database server to a sufficiently large size. Use </span><a style="line-height: 1.714285714; font-size: 1rem;" href="http://aws.amazon.com/rds/" target="_blank">Amazon RDS</a><span style="line-height: 1.714285714; font-size: 1rem;"> for a cloud-based solution, or get a high-performance dedicated server with 4-8 CPU cores, at least 3 SSD hard drives in RAID 5, and 8-32GB RAM (use at least as much RAM as your database size). All database inserts, updates, and deletes will be done on this server.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Add two or more dedicated database servers to function as read replicas. The server specifications will need to match that of your master database server, despite the fact they will be doing nothing but handling database read requests. Configure MySQL master-slave replication or </span><a style="line-height: 1.714285714; font-size: 1rem;" href="http://www.drbd.org/" target="_blank">DRBD</a><span style="line-height: 1.714285714; font-size: 1rem;"> to keep your read replicas in sync with the master database.</span></li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Update your application code to connect to the master database for writes and to the replicas for reads. Using a random or round robin scheme for choosing a read replica to connect to, or some other method based on your needs.</span></li>
</ol>
<h2>Beyond Stage 3</h2>
<p>Beyond this point, scaling your site and keeping it running smoothly will require a dedicated <em>team</em> of system administrators, architects, developers, and testers. This is the situation that large-scale websites like Facebook, Twitter, Pinterest, and Etsy currently have and it requires lots of innovation, time, and cash.</p>
<h2>Specialized Tools/Techniques</h2>
<p>These could be selectively intermingled anywhere along the way, as needed.</p>
<ul>
<li>Search &#8211; use a dedicated search server, such as <a href="http://sphinxsearch.com/" target="_blank">Sphinx</a>.</li>
<li>Use <a href="http://memcached.org/" target="_blank">memcached</a> on dedicated servers (or <a href="http://aws.amazon.com/elasticache/" target="_blank">Amazon ElastiCache</a>) to cache parts of your application, and/or your user session data.</li>
<li>Migrate part or all of your database to a non-relational database, such as MongoDB (or <a href="http://aws.amazon.com/dynamodb/" target="_blank">Amazon DynamoDB</a>).</li>
<li>Partition or shard very large databases to get less-frequently-accessed data out of the way (and out of your database cache).</li>
<li>Implement <a href="http://vimeo.com/53746372#at=0" target="_blank">load balanced work queues</a> using <a href="http://www.gearman.org/gearman" target="_blank">Gearman</a> or similar to do background processing in an asynchronous fashion.</li>
<li>Run reporting database queries on a separate database. Either run your reports on a dedicated read replica, or use one of the specialized data warehouse solutions that are on the market (such as <a href="http://www.infobright.com/" target="_blank">Infobright</a>).</li>
</ul>
<h2>Conclusion</h2>
<p>Evaluate how much you really need to scale. The decision needs to be based on the data, of how much traffic you actually have now, how much you can afford to spend on scaling, and how much traffic you reasonably anticipate in the short term future&#8211;not on technological fads or overly optimistic dreams of grandeur. Remember that <a href="http://c2.com/cgi/wiki?PrematureOptimization" target="_blank">premature optimization will get you in trouble</a>.</p>
<p>Should you do this yourself, or hire consultants? That depends on how fast you anticipate growing and what you can afford to pay. In light of the cost of hiring and retaining really competent developers and system administrators, you may find <a href="http://www.mysqlperformanceblog.com/2008/03/13/economics-of-performance-optimization/" target="_blank">using consultants in the early stages to be more cost effective</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-12-16/scaling-your-website-three-stages/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-12-16/scaling-your-website-three-stages/</feedburner:origLink></item>
		<item>
		<title>What Is Wrong With PHP’s Semaphore Extension</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/Ny6rvQvbrzI/</link>
		<comments>http://jonathonhill.net/2012-12-08/what-is-wrong-with-phps-semaphore-extension/#comments</comments>
		<pubDate>Sun, 09 Dec 2012 05:27:20 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Semaphore]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=392</guid>
		<description><![CDATA[Lack of a true Semaphore sem_release() releases the semaphore if it is currently acquired by the calling process, otherwise a warning is generated. http://php.net/manual/en/function.sem-release.php By far the worst, this limitation makes a whole class of problems much more difficult to solve, and in fact means that PHP&#8217;s Semaphore extension, despite the name, is really a [...]]]></description>
				<content:encoded><![CDATA[<h2>Lack of a true Semaphore</h2>
<blockquote><p><strong>sem_release()</strong> releases the semaphore if it is currently acquired by the calling process, otherwise a warning is generated.<br />
<cite><a href="http://php.net/manual/en/function.sem-release.php" target="_blank">http://php.net/manual/en/function.sem-release.php</a></cite></p></blockquote>
<p>By far the worst, this limitation makes a whole class of problems much more difficult to solve, and in fact means that PHP&#8217;s Semaphore extension, despite the name, is really a <a title="Semaphore vs Mutex" href="http://en.wikipedia.org/wiki/Semaphore_(programming)#Semaphore_vs._mutex" target="_blank">mutex</a> (a semaphore with ownership, i.e., only the acquirer may release). <span style="line-height: 1.714285714; font-size: 1rem;">Furthermore, it is inconsistent with the behavior of equivalent functions in other languages such as C and Python.</span></p>
<p>Please see <a href="https://github.com/compwright/phm/blob/master/src/phm/Lock/Semaphore.php" target="_blank">phm\Lock\Semaphore</a> for a kludgy workaround using mutexes, shared memory, and a message queue.<span id="more-392"></span></p>
<h2>Undefined error handling</h2>
<p>What happens if you <a href="http://php.net/manual/en/function.sem-get.php" target="_blank">sem_get()</a> using a key that is already in use by a different type of resource, say, a System V shared memory segment? This is an issue because <a href="http://php.net/manual/en/function.ftok.php" target="_blank">ftok()</a> (the normal way of generating IPC keys for this stuff) is collision-prone, given a large enough filesystem.</p>
<h2>Undefined behavior of sem_get()</h2>
<blockquote><p>[T]he documentation does not stipulate what will happen if the max_acquire paramter is varied upon successive invocations of the sem_get method. [S]o setting it to 100, then to 1 on 2 successive calls will have an undefined behavior.<br />
<cite><a href="http://php.net/manual/en/function.sem-get.php#76793" target="_blank">http://php.net/manual/en/function.sem-get.php#76793</a></cite></p></blockquote>
<p>This is avoidable, but it would be nice if this were fixed.</p>
<h2>Cannot disable semaphore auto-releasing</h2>
<blockquote><p>If I write a program with a sem_get and auto_release=0, in case of shutdown, an other program should not be available to take the semaphore.<br />
<cite><a href="https://bugs.php.net/bug.php?id=52701" target="_blank">Bug #52701</a></cite></p></blockquote>
<h2>A semaphore may be deleted when other processes are waiting to acquire it</h2>
<blockquote><p>sem_remove() should take the SYSVSEM_USAGE (see sysvsem.c) count into consideration when it is called. Only if this count is == 1 should the semaphore be removed. This will allow the last process that is using the semaphore to remove it from the system.<br />
<cite><a href="https://bugs.php.net/bug.php?id=44109" target="_blank">Bug #44109</a></cite></p></blockquote>
<h2>Conclusion</h2>
<p>The mantra among the PHP community seems to be &#8220;less bitchin&#8217;, more fixin&#8217;&#8221;, which is admirable. I do not wish to criticize or complain, but rather to document the need and hopefully encourage someone who has the background in C development to fix this (alas, I do not have this).</p>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-12-08/what-is-wrong-with-phps-semaphore-extension/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-12-08/what-is-wrong-with-phps-semaphore-extension/</feedburner:origLink></item>
		<item>
		<title>Multi-tasking in PHP</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/2qenUknSUQ4/</link>
		<comments>http://jonathonhill.net/2012-12-06/multi-tasking-in-php/#comments</comments>
		<pubDate>Fri, 07 Dec 2012 04:00:14 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[ATLPHP]]></category>
		<category><![CDATA[Multiprocessing]]></category>
		<category><![CDATA[Multitasking]]></category>
		<category><![CDATA[Mutexes]]></category>
		<category><![CDATA[PCNTL]]></category>
		<category><![CDATA[phm]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[POSIX]]></category>
		<category><![CDATA[Presentations]]></category>
		<category><![CDATA[Semaphores]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=381</guid>
		<description><![CDATA[I was honored to deliver this presentation to the Atlanta PHP User Group&#8216;s December 2012 meetup. Enjoy! Presentation link bundle: http://bitly.com/atlphpm Multi-tasking in PHP from Jonathon Hill]]></description>
				<content:encoded><![CDATA[<p>I was honored to deliver this presentation to the <a href="http://atlantaphp.org/" target="_blank">Atlanta PHP User Group</a>&#8216;s December 2012 meetup. Enjoy!</p>
<p>Presentation link bundle: <a href="http://bitly.com/atlphpm" target="_blank">http://bitly.com/atlphpm</a></p>
<p><iframe style="border: 1px solid #CCC; border-width: 1px 1px 0; margin-bottom: 5px;" src="http://www.slideshare.net/slideshow/embed_code/15510096" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="427" height="356"></iframe></p>
<div style="margin-bottom: 5px;"><strong> <a title="Multi-tasking in PHP" href="http://www.slideshare.net/compwright/multiprocessing" target="_blank">Multi-tasking in PHP</a> </strong> from <strong><a href="http://www.slideshare.net/compwright" target="_blank">Jonathon Hill</a></strong></div>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-12-06/multi-tasking-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-12-06/multi-tasking-in-php/</feedburner:origLink></item>
		<item>
		<title>Announcing ShareMyUrl.co</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/0z-nsnMAmzk/</link>
		<comments>http://jonathonhill.net/2012-12-06/announcing-sharemyurl-co/#comments</comments>
		<pubDate>Thu, 06 Dec 2012 17:34:03 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[Facebook]]></category>
		<category><![CDATA[Pinterest]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Share]]></category>
		<category><![CDATA[Twitter]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=388</guid>
		<description><![CDATA[Any task that needs to be done more than once is worth automating. The Grumpy Programmer Enjoy!]]></description>
				<content:encoded><![CDATA[<blockquote><p>Any task that needs to be done more than once is worth automating.<br />
<cite><a href="http://twitter.com/grmpyprogrammer" target="_blank">The Grumpy Programmer</a></cite></p></blockquote>
<p>Enjoy!</p>
<p><a href="http://sharemyurl.co"><img class="alignnone" title="ShareMyUrl.co" src="http://i.imgur.com/lT9Ea.png" alt="" width="795" height="978" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-12-06/announcing-sharemyurl-co/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-12-06/announcing-sharemyurl-co/</feedburner:origLink></item>
		<item>
		<title>Roll your own MAMP development environment</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/z58_sZFPE1E/</link>
		<comments>http://jonathonhill.net/2012-11-29/roll-your-own-mamp-development-environment/#comments</comments>
		<pubDate>Thu, 29 Nov 2012 17:54:24 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[MAMP]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=370</guid>
		<description><![CDATA[Pre-packaged MAMP, LAMP, and WAMP stacks have been common on developer&#8217;s computers for years. Such packages are convenient because they provide a single-step install process, with all components in the server stack preconfigured to work together, and off you go. Except when they don&#8217;t. I&#8217;ve learned from experience that these packages have ways of making [...]]]></description>
				<content:encoded><![CDATA[<p>Pre-packaged MAMP, LAMP, and WAMP stacks have been common on developer&#8217;s computers for years. Such packages are convenient because they provide a single-step install process, with all components in the server stack preconfigured to work together, and off you go.</p>
<p><a title="Cannot run a binary executable from PHP and MAMP?" href="/2012-06-22/cannot-run-a-binary-executable-from-php-and-mamp/">Except when they don&#8217;t</a>.</p>
<p>I&#8217;ve learned from experience that these packages have ways of making you pay for the convenience you enjoyed up front. If you have ever needed to:</p>
<ul>
<li>Install a PHP extension that wasn&#8217;t already provided in your stack</li>
<li>Run a specific version of PHP or MySQL</li>
<li>Install PEAR packages</li>
<li>Install SSL certificates</li>
<li>Run command-line PHP scripts</li>
</ul>
<p>&#8230;you may have encountered some ugly, time-wasting surprises along the way.</p>
<p>It pays to know your environment inside and out. Today, it is quite easy to roll your own Apache-MySQL-PHP stack on <a title="Windows Apache PHP FastCGI Setup" href="http://www.farinspace.com/windows-apache-php-fastcgi/" target="_blank">Windows</a>, <a title="Installing Apache, MySQL, and PHP on Ubuntu Linux" href="https://help.ubuntu.com/community/ApacheMySQLPHP" target="_blank">Linux</a>, or even Mac OS X.<span id="more-370"></span></p>
<h2>Getting started</h2>
<p>This tutorial will show you how to roll your own MAMP stack with Mac OS X 10.7+ (Lion), Apache 2, MySQL 5.5, and PHP 5.4. If you wish to use different versions, a similar procedure should work but the specifics will probably vary.</p>
<p>For reference, I have provided a reference script which was the basis for this blog post: <a href="https://gist.github.com/4170773" target="_blank"><strong>build-mamp-osx.sh</strong></a>.</p>
<p>We will perform the following steps:</p>
<ol>
<li>Install dependencies needed for PHP</li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Compile PHP from scratch</span></li>
<li>Install MySQL</li>
<li>Configure PHP, MySQL, and Apache to run virtual hosts</li>
</ol>
<div><em><span style="line-height: 24px;">Note: OS X Lion and higher already have Apache installed.</span></em></div>
<h2>1. Install PHP dependencies</h2>
<ol>
<li>Verify that you have the latest <a href="https://developer.apple.com/xcode/" target="_blank">Apple XCode</a> from the App Store. If you do not already have XCode, you may need to upgrade to the latest OS X.</li>
<li>Download and install XQuartz from <a href="http://xquartz.macosforge.org/" target="_blank">http://xquartz.macosforge.org/</a>. <span style="line-height: 1.714285714; font-size: 1rem;">After installing XQuartz, run the following:</span>
<pre>sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer
ln -s /opt/X11 /usr/X11</pre>
</li>
<li><a href="http://mxcl.github.com/homebrew/" target="_blank">Install Homebrew</a> and run <code>brew doctor</code> to verify that it is working correctly.</li>
<li>Using Homebrew, install the following libraries:
<pre>brew install libpng
brew install libjpeg
brew install gd
brew install pcre
brew install libxml2
brew install mcrypt
brew install icu4c
brew install wget</pre>
</li>
<li>Install the IMAP-2007f library:
<pre>wget ftp://ftp.cac.washington.edu/imap/imap-2007f.tar.gz
tar zxvf imap-2007f.tar.gz
cd imap-2007f
make osx EXTRACFLAGS="-arch i386 -arch x86_64 -g -Os -pipe -no-cpp-precomp"
sudo cp c-client/*.h /usr/local/include/
sudo cp c-client/*.c /usr/local/lib
sudo cp c-client/c-client.a /usr/local/lib/libc-client.a
cd ..</pre>
</li>
</ol>
<h2>2. Compile PHP</h2>
<ol>
<li>Download and extract the latest PHP 5.4 source tarball from <a href="http://php.net/downloads.php" target="_blank">http://php.net/downloads.php</a>:
<pre>wget http://us3.php.net/get/php-5.4.9.tar.gz/from/this/mirror
tar xzvf php-5.4.9.tar.gz
cd php-5.4.9</pre>
</li>
<li>Configure the makefile:
<pre>./configure \
--prefix=/usr \
--mandir=/usr/share/man \
--infodir=/usr/share/info \
--sysconfdir=/private/etc \
--with-apxs2=/usr/sbin/apxs \
--enable-cli \
--with-config-file-path=/etc \
--with-libxml-dir=/usr \
--with-openssl=/usr \
--with-kerberos=/usr \
--with-zlib=/usr \
--enable-bcmath \
--with-bz2=/usr \
--enable-calendar \
--with-curl=/usr \
--enable-dba \
--enable-exif \
--enable-ftp \
--with-gd \
--enable-gd-native-ttf \
--with-icu-dir=/usr/local \
--with-iodbc=/usr \
--with-ldap=/usr \
--with-ldap-sasl=/usr \
--with-libedit=/usr \
--enable-mbstring \
--enable-mbregex \
--with-mysql=mysqlnd \
--with-mysqli=mysqlnd \
--without-pear \
--with-pdo-mysql=mysqlnd \
--with-mysql-sock=/var/mysql/mysql.sock \
--with-readline=/usr \
--enable-shmop \
--with-snmp=/usr \
--enable-soap \
--enable-pcntl \
--enable-sockets \
--enable-sysvmsg \
--enable-sysvsem \
--enable-sysvshm \
--with-tidy \
--enable-wddx \
--with-xmlrpc \
--with-iconv-dir=/usr \
--with-xsl=/usr \
--enable-zip \
--with-imap=/usr/local/imap-2007 \
--with-kerberos \
--with-imap-ssl \
--enable-intl \
--with-pcre-regex \
--with-pgsql=/usr \
--with-pdo-pgsql=/usr \
--with-freetype-dir=/usr/X11 \
--with-jpeg-dir=/usr \
--with-png-dir=/usr/X11</pre>
<p>Verify that this completed without errors before proceeding. If you have installed the dependencies listed in the previous step, there should be none.</li>
<li>Compile and install the PHP binary:
<pre>make
sudo make install</pre>
</li>
</ol>
<h2>3. Install MySQL</h2>
<ol>
<li>Download and install the latest <a href="http://www.mysql.com/downloads/mysql/" target="_blank">MySQL 5.5 Community Server</a> binary (DMG package). Within the DMG archive, be sure to install all three components: the MySQL package, the MySQL.prefPane, and the MySQLStartupItem package.</li>
<li>Create your MySQL configuration file:
<pre>sudo cp /usr/local/mysql/support-files/my-small.cnf /etc/my.cnf</pre>
</li>
<li><span style="line-height: 1.714285714; font-size: 1rem;">Edit your </span><code style="line-height: 1.714285714;">./profile</code><span style="line-height: 1.714285714; font-size: 1rem;"> and add the </span><code style="line-height: 1.714285714;">/usr/local/mysql/bin</code><span style="line-height: 1.714285714; font-size: 1rem;"> directory to the path.</span><img style="line-height: 1.714285714; font-size: 1rem;" src="http://grab.by/hT3M" alt="export PATH=/usr/local/bin:/usr/bin:/usr/local/mysql/bin:/usr/local/pear/bin:$PATH" /><span style="line-height: 1.714285714; font-size: 1rem;">You will need to restart Terminal for this to take effect.</span></li>
<li>Restart MySQL using the System Preferences MySQL pane.</li>
<li>Verify that MySQL is running on localhost:
<pre>mysql -u root</pre>
<p>It is recommended that you <a href="http://www.cyberciti.biz/faq/mysql-change-root-password/" target="_blank">set a root password</a> at this point.</li>
</ol>
<h2>4. Configure PHP, MySQL, and Apache to run virtual hosts</h2>
<ol>
<li>Enable mod_php and virtual hosts in your Apache configuration by adding the following lines to your /etc/apache2/httpd.conf file:</li>
<li>Configure the MySQL socket settings in your /etc/php.ini file:</li>
<li>Restart apache:</li>
</ol>
<h2>Adding Virtual Hosts</h2>
<p>Once your MAMP stack in set up, here is how to create virtual hosts:</p>
<ol>
<li>Add your virtual host domain name to the <code>/etc/hosts</code> file (requires Administrator or <code>sudo</code> access):
<pre>127.0.0.1 helloworld.dev</pre>
</li>
<li>Add a virtual host block to your <code>/private/etc/apache2/extra/httpd-vhosts.conf</code> file (requires Administrator or <code>sudo</code> access):
<pre>&lt;VirtualHost *:80&gt;
	ServerName helloworld.dev
	DocumentRoot "/Users/jhill/Sites/helloworld.dev"
	&lt;Directory "/Users/jhill/Sites/helloworld.dev"&gt;
		Options Includes FollowSymLinks  
		AllowOverride All
		Order allow,deny
 		Allow from all
	&lt;/Directory&gt;
&lt;/VirtualHost&gt;</pre>
</li>
<li>Restart Apache:
<pre>sudo apachectl restart</pre>
</li>
<li>Add an <code>index.php</code> file in your virtual host document root, <code>~/Sites/helloworld.dev/</code>:
<pre>&lt;?php echo 'Hello, world!';</pre>
</li>
<li>Browse to <strong>http://helloworld.dev/</strong> in your web browser.</li>
</ol>
<h2>Conclusion</h2>
<p>Building your own MAMP stack with Mac OS X 10.7+, MySQL 5.5, and PHP 5.4 is easier than you think, and offers the advantages of total flexibility, less &#8220;magic&#8221;, better reliability, and a greater understanding of where things are and how to fix them if they break.</p>
<p>Did you try it? How did it go?</p>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-11-29/roll-your-own-mamp-development-environment/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-11-29/roll-your-own-mamp-development-environment/</feedburner:origLink></item>
		<item>
		<title>Box drawing in PHP</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/D4v3_IlOK1w/</link>
		<comments>http://jonathonhill.net/2012-11-26/box-drawing-in-php/#comments</comments>
		<pubDate>Tue, 27 Nov 2012 00:20:08 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[ANSI]]></category>
		<category><![CDATA[ASCII]]></category>
		<category><![CDATA[CLI]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[UTF-8]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=364</guid>
		<description><![CDATA[Back in the &#8220;good old days&#8221; of MS-DOS, you could draw lines, boxes, filled areas (think progress bars), and more using the extended ASCII character set (AKA code page 437). While writing a simple command-line utility in PHP I wanted to use the full block (█) and light shade (░) characters to create a simple [...]]]></description>
				<content:encoded><![CDATA[<p><img class="alignright" title="Code Page 437" src="http://upload.wikimedia.org/wikipedia/commons/f/f8/Codepage-437.png" alt="" width="304" height="144" /> Back in the &#8220;good old days&#8221; of MS-DOS, you could draw lines, boxes, filled areas (think progress bars), and more using the extended ASCII character set (AKA <a href="http://en.wikipedia.org/wiki/Code_page_437" target="_blank">code page 437</a>).</p>
<p>While writing a simple command-line utility in PHP I wanted to use the full block (<code>█</code>) and light shade (<code>░</code>) characters to create a simple progress bar that is a bit nicer than the typical <code>=========...................</code>.</p>
<p>Like this:</p>
<pre>█████████████████░░░░░░░░░░░░░░░░░░░░░░░ 42.5%</pre>
<p>Instinctively, I turned to PHP&#8217;s <a href="http://php.net/chr" target="_blank"><code>chr()</code></a> function and <a title="ASCII table" href="http://www.asciitable.com/" target="_blank">looked up the extended ASCII codes</a> for the characters I needed. Boy, was I disappointed when my progress bar was nothing but a series of un-useful question marks. Surely PHP can render simple ASCII characters, I thought.</p>
<p>It might have gone differently if I had been using a PC, but I do 100% of my development these days on a Macbook Pro. It so happens that the bash shell in UNIX, Linux, and Mac OS X all use UTF-8 encoding by default, not CP437. (Of course, your terminal font will need to support UTF-8 characters for this to work.)</p>
<p>So, I just needed to find the UTF-8 codes for the characters I needed and use those instead of the old familiar CP437 ones. However, the results weren&#8217;t much better.</p>
<p>After an hour or more of Googling and experimentation, I finally realized that <code>chr()</code> doesn&#8217;t do UTF-8. I <a title="Stack Overflow: PHP construct a Unicode string?" href="http://stackoverflow.com/a/3704588" target="_blank">found various suggestions</a> on how to produce the desired characters using custom functions and the like, but the best way turned out to just use a good &#8216;ol HTML entity and run it through <code>html_entity_decode</code>:</p>
<pre>$block = html_entity_decode('&amp;#x2588;', ENT_NOQUOTES, 'UTF-8'); // full block
$shade = html_entity_decode('&amp;#x2591;', ENT_NOQUOTES, 'UTF-8'); // light shade</pre>
<p>Now, a progress bar isn&#8217;t exactly a box, so let&#8217;s demonstrate:</p>
<pre>$tl = html_entity_decode('&amp;#x2554;', ENT_NOQUOTES, 'UTF-8'); // top left corner
$tr = html_entity_decode('&amp;#x2557;', ENT_NOQUOTES, 'UTF-8'); // top right corner
$bl = html_entity_decode('&amp;#x255a;', ENT_NOQUOTES, 'UTF-8'); // bottom left corner
$br = html_entity_decode('&amp;#x255d;', ENT_NOQUOTES, 'UTF-8'); // bottom right corner
$v = html_entity_decode('&amp;#x2551;', ENT_NOQUOTES, 'UTF-8');  // vertical wall
$h = html_entity_decode('&amp;#x2550;', ENT_NOQUOTES, 'UTF-8');  // horizontal wall

echo $tl . str_repeat($h, 15)  . $tr . "\n" .
     $v  . ' Hello, World! '   . $v  . "\n" .
     $bl . str_repeat($h, 15)  . $br . "\n";</pre>
<p>Voilà!</p>
<p><img class="alignnone" src="http://grab.by/hOiq" alt="" width="291" height="85" /></p>
<p>UTF-8, once you get used to it, actually <a title="Unicode Characters as Named and Numeric HTML Entities" href="http://la.remifa.so/unicode/unicode.php" target="_blank">provides many more possibilities</a> than CP437 did back in the MS-DOS days. Enjoy!</p>
<p><img class="aligncenter" title="Code Page 437" src="http://upload.wikimedia.org/wikipedia/commons/8/85/Unicode_Box_Drawings_%282500_-_27FF%29.svg" alt="" width="420" /></p>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-11-26/box-drawing-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-11-26/box-drawing-in-php/</feedburner:origLink></item>
		<item>
		<title>Just-in-time $GLOBALS</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/bzsmFuCGmuQ/</link>
		<comments>http://jonathonhill.net/2012-07-10/jit-globals/#comments</comments>
		<pubDate>Wed, 11 Jul 2012 05:22:00 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[globals]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=354</guid>
		<description><![CDATA[Update—Turns out, this isn&#8217;t actually a bug. Rasmus Lerdorf has already explained this behavior in response to a bug report: This is not a bug. It is a documented optimization feature. See http://php.net/manual/en/ini.core.php and look for the section on auto_globals_jit with the big pink warning which says, &#8220;Usage of SERVER and ENV variables is checked [...]]]></description>
				<content:encoded><![CDATA[<p><strong>Update</strong>—Turns out, this isn&#8217;t actually a bug. Rasmus Lerdorf has already explained this behavior in response to <a href="https://bugs.php.net/bug.php?id=54131#1299042912" target="_blank">a bug report</a>:</p>
<blockquote><p>This is not a bug. It is a documented optimization feature. See <a href="http://php.net/manual/en/ini.core.php">http://php.net/manual/en/ini.core.php</a> and look for the section on <a href="http://www.php.net/manual/en/ini.core.php#ini.auto-globals-jit" target="_blank"><code>auto_globals_jit</code></a> with the big pink warning which says, &#8220;Usage of SERVER and ENV variables is checked during compile time so using them through e.g. variable variables will not cause their initialization.&#8221;</p></blockquote>
<hr />
<p>I encountered an odd bug in PHP 5.3.10 while attempting to loop through all the major superglobals (<code>$_SERVER</code>, <code>$_GET</code>, <code>$_POST</code>, etc). Here is what I found, if you can shed any light on why this happens, or if you can confirm that it isn&#8217;t just me, I would greatly appreciate it!</p>
<h2>Test Case</h2>
<pre>&lt;?php
var_dump($GLOBALS['_SERVER']);
// These behave the same way:
// $var = '_SERVER'; var_dump($$var);
// var_dump(${$var});</pre>
<h2>Expected Behavior</h2>
<pre>$GLOBALS["_SERVER"]: array(27) {
...
}</pre>
<h2>Actual Behavior</h2>
<pre>NULL</pre>
<h2>Workaround</h2>
<p>What makes this so strange is that if you access <code>$_SERVER</code> (or <code>${'_SERVER'}</code>) directly in the code anywhere—even if it doesn&#8217;t actually get executed—this bug does not manifest itself! In other words, this works just fine:</p>
<pre>&lt;?php
var_dump($GLOBALS['_SERVER']);
exit;
$var = $_SERVER;</pre>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-07-10/jit-globals/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-07-10/jit-globals/</feedburner:origLink></item>
		<item>
		<title>Lone Star PHP takeaways</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/wq5SYRIHCT0/</link>
		<comments>http://jonathonhill.net/2012-06-30/lone-star-php-takeaways/#comments</comments>
		<pubDate>Sun, 01 Jul 2012 01:19:32 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[GD]]></category>
		<category><![CDATA[ImageMagick]]></category>
		<category><![CDATA[IMagick]]></category>
		<category><![CDATA[LoneStarPHP]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[PECL]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[phpunit]]></category>
		<category><![CDATA[Refactoring]]></category>
		<category><![CDATA[SOLID]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=343</guid>
		<description><![CDATA[Thanks to the guys at Engine Yard, I won a free pass to the Lone Star PHP community conference in Dallas, TX this year. Due to the choice of subject matter and presenters, I think I enjoyed this conference even more than last year&#8217;s PHPCon in Nashville. Here are some of the highlights. Day 1 [...]]]></description>
				<content:encoded><![CDATA[<p>Thanks to the guys at <a href="http://www.engineyard.com/" target="_blank">Engine Yard</a>, I won a free pass to the <a href="http://lonestarphp.com" target="_blank">Lone Star PHP</a> <a href="http://eventifier.co/event/lsp12" target="_blank">community conference</a> in Dallas, TX this year. Due to the choice of subject matter and presenters, I think I enjoyed this conference even more than <a href="http://jonathonhill.net/2011-04-21/phpcon-day-1/">last year&#8217;s PHPCon</a> in Nashville. Here are some of the highlights.</p>
<h2>Day 1</h2>
<h3>!Normal===Awesome!</h3>
<p><a href="http://calevans.com/" target="_blank">Cal Evans</a> inspired us all to go out and build awesome stuff, and that we owe a debt to the giants who came before us and gave, to give to those coming after.</p>
<ul>
<li>&#8220;This stuff is hard. If it looks easy, it&#8217;s because I&#8217;m good at it.&#8221;</li>
<li>Get involved somewhere.</li>
<li>Be a builder, a speaker, a teacher.</li>
</ul>
<h3>Building Testable PHP Applications</h3>
<p><a href="http://grumpy-testing.com/" target="_blank">Chris Hartjes</a> delivered a really helpful talk on testing applications. One of the big obstacles of unit testing for me was mock objects. Chris did a good job explaining how they are used in the context of PHPUnit. Key takeaways for me:</p>
<ul>
<li><span style="text-decoration: underline;">Automate test execution.</span></li>
<li>Following the <a href="http://en.wikipedia.org/wiki/Law_of_Demeter" target="_blank">law of demeter</a> will make your applications easier to test.</li>
<li>Code in a framework-agnostic and environment-agnostic way.</li>
<li>PHPUnit provides methods for setting up mock objects, so you don&#8217;t have to actually define those classes.</li>
<li>Test private/protected methods and properties by using a <a href="http://www.gpug.ca/2012/06/02/testing-protected-methods-with-phpunit/" target="_blank">Reflection hack</a>. <a href="https://github.com/etsy/phpunit-extensions" target="_blank">Etsy&#8217;s PHPUnit extensions</a> can automate this.</li>
<li>Don&#8217;t access the database in Unit tests, use mock objects instead. Filesystem access can be <a href="http://tech.vg.no/2011/03/09/mocking-the-file-system-using-phpunit-and-vfsstream/" target="_blank">mocked using vfsStream</a>.</li>
<li>Some applications resist testing, no matter what. If you cannot refactor, you can still automate tests with <a href="http://behat.org/" target="_blank">Behat</a>.</li>
<li>If you don&#8217;t test, Chris will cut you!</li>
</ul>
<p><script type="text/javascript" src="//speakerdeck.com/assets/embed.js" data-id="4fe51eaf4252c80021019472" data-ratio="1.3333333333333333"></script><a href="https://speakerdeck.com/u/grumpycanuck/p/building-testable-php-applications" target="_blank">See his slides</a> at SpeakerDeck.com.</p>
<h3>PHP Extensions for Dummies</h3>
<p><a href="http://emsmith.net/" target="_blank">Elizabeth Smith</a> laid bare the innards of PHP in her intro to PHP extension development. Much of it was unintelligible to me since I don&#8217;t (yet) know C. However, now I know who to go to if I need help compiling an extension! If I can navigate the dependencies, I&#8217;d like to try compiling a non-thread-safe Windows build of php_wkhtmltox for a future blog post.</p>
<div id="__ss_13500035" style="width: 425px;"><strong style="display: block; margin: 12px 0 4px;"><a title="Php Extensions for Dummies" href="http://www.slideshare.net/auroraeosrose/php-extensions-elizabeths-mac-book-air" target="_blank">Php Extensions for Dummies</a></strong> <iframe style="border: 1px solid #CCC; border-width: 1px 1px 0;" src="http://www.slideshare.net/slideshow/embed_code/13500035" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="425" height="355"></iframe></p>
<div style="padding: 5px 0 12px;">View more <a href="http://www.slideshare.net/thecroaker/death-by-powerpoint" target="_blank">PowerPoint</a> from <a href="http://www.slideshare.net/auroraeosrose" target="_blank">Elizabeth Smith</a></div>
</div>
<h3>Pixel Punching with PHP</h3>
<p><a href="http://catch404.net/" target="_blank">Bob Majdak</a>&#8216;s presentation compared the usage and performance of GD2 and IMagick across a number of common image manipulation tasks. IMagick is better in almost every way.<script type="text/javascript" src="//speakerdeck.com/assets/embed.js" data-id="4fee9958268087001f000a35" data-ratio="1.3333333333333333"></script></p>
<p><a href="https://speakerdeck.com/u/bobmajdakjr/p/pixel-punching-with-php" target="_blank">See his slides</a> at SpeakerDeck.com.</p>
<h2>Day 2</h2>
<h3>It Was Like That When I Got Here: Steps Toward Modernizing a Legacy Codebase</h3>
<p><a href="http://paul-m-jones.com" target="_blank">Paul Jones</a>&#8216; keynote did not disappoint. With insight and humor, Paul dismantled the problems with legacy codebases and presented a logical methodology to improving your life when you are forced to inherit a big stinkin&#8217; pile of spagetti code.</p>
<ul>
<li>No matter how bad you want to, resist the urge to throw it out and rewrite.</li>
<li>You&#8217;ve nothing to lose. Either bear the pain of bad code, or bear the pain of refactoring bad code. May as well put the suffering to some use.</li>
<li>Move class files to a common base directory and autoload, removing require/include calls as you go.</li>
<li>Change class names as you go, if needed.</li>
<li>Wrap procedural functions in a class, so that it can be autoloaded.</li>
<li>Replace globals incrementally, moving them first to the constructor, then replacing them with dependency injection</li>
</ul>
<div style="width:425px" id="__ss_13505608"> <strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/pmjones88/it-was-like-that-when-i-got-here" title="It Was Like That When I Got Here" target="_blank">It Was Like That When I Got Here</a></strong> <iframe src="http://www.slideshare.net/slideshow/embed_code/13505608" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC;border-width:1px 1px 0" allowfullscreen></iframe>
<div style="padding:5px 0 12px"> View more <a href="http://www.slideshare.net/" target="_blank">presentations</a> from <a href="http://www.slideshare.net/pmjones88" target="_blank">pmjones88</a> </div>
</p></div>
<h3>SOLID &#8211; Not Just a State Of Matter, It&#8217;s Principles for OO Propriety</h3>
<p><a href="http://www.chrisweldon.net/" target="_blank">Chris Weldon</a> laid out five principles to get the most out of object oriented development.</p>
<ul>
<li><span style="text-decoration: underline;"><strong>S</strong></span>ingle responsibility: each class should do one and only one thing.</li>
<li><span style="text-decoration: underline;"><strong>O</strong></span>pen/Closed principle: each class is open to extension, but closed for modification. Don&#8217;t break stuff.</li>
<li><span style="text-decoration: underline;"><strong>L</strong></span>iskov Substitution principle: replace if&#8230;else constructs and prevent exceptions by using typed collections.</li>
<li><span style="text-decoration: underline;"><strong>I</strong></span>nterface segregation: each interface should be as focused as possible, so that it can be used by as many classes as need it. Similar to the Open/Closed principle, applied to interfaces.</li>
<li><span style="text-decoration: underline;"><strong>D</strong></span>ependency Inversion: injection dependencies from the outside, don&#8217;t couple on the inside.</li>
</ul>
<h3>Fast, Not Furious: Performance Tuning that Works</h3>
<p><a href="http://daveyshafik.com/" target="_blank">Davey Shafik</a> demonstrated how to use <a href="http://pecl.php.net/package/xhprof" target="_blank">xhprof</a>/<a href="https://github.com/preinheimer/xhprof" target="_blank">xh-gui</a> and <a href="http://memcached.org/" target="_blank">Memcache</a> to optimize your PHP application.</p>
<ul>
<li>1. Benchmark. 2. Profile. 3. Change.</li>
<li>Profiling affects performance, similar to quantum observation.</li>
<li>Benchmarking tests actual runtime, from start to end.</li>
<li>Namespacing Memcache keys can help overcome the 1MB size limit</li>
<li>Create new namespaces, don&#8217;t clear the cache. Garbage collection will clean up the old unused namespaces.</li>
<li>&#8220;Hardware is cheap, programmers are expensive&#8221; only holds when you take care of your hardware.</li>
<li>Don&#8217;t make assumptions, test everything sequentially.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-06-30/lone-star-php-takeaways/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-06-30/lone-star-php-takeaways/</feedburner:origLink></item>
		<item>
		<title>How to install an SSL certificate for Apache, from start to finish</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/HzJw-tcoik8/</link>
		<comments>http://jonathonhill.net/2012-06-22/how-to-install-an-ssl-certificate-for-apache-from-start-to-finish/#comments</comments>
		<pubDate>Fri, 22 Jun 2012 21:45:09 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[SSL]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=338</guid>
		<description><![CDATA[Create an SSL key to use to generate the certificate signing request (Save this, you&#8217;ll need it to install the certificate). To generate the keys for the Certificate Signing Request (CSR) run the following command from a terminal prompt: openssl genrsa -des3 -out server.key 1024 Generating RSA private key, 1024 bit long modulus .....................++++++ .................++++++ [...]]]></description>
				<content:encoded><![CDATA[<ol>
<li>
<h2>Create an SSL key to use to generate the certificate signing request</h2>
<p>(Save this, you&#8217;ll need it to install the certificate). To generate the keys for the Certificate Signing Request (CSR) run the following command from a terminal prompt:</p>
<pre>openssl genrsa -des3 -out server.key 1024
Generating RSA private key, 1024 bit long modulus
.....................++++++
.................++++++
unable to write 'random state'
e is 65537 (0x10001)
Enter pass phrase for server.key:</pre>
<p>Enter a passphrase.</p>
<p>Now we&#8217;ll remove the passphrase from the key, so that you don&#8217;t have to enter this passphrase whenever you restart Apache:</p>
<pre>openssl rsa -in server.key -out server.key.insecure
mv server.key server.key.secure
mv server.key.insecure server.key</pre>
</li>
<li>
<h2>Generate a certificate signing request</h2>
<pre>openssl req -new -key server.key -out server.csr</pre>
<p>It will prompt you to enter Company Name, Site Name, Email Id, etc. Once you enter all these details, your CSR will be created and it will be stored in the server.csr file.</p>
<p>You can now submit this CSR file to a Certificate Authority (CA) for processing. The CA will use this CSR file and issue the certificate.</li>
<li>
<h2>Purchase an SSL certificate</h2>
<p>You will be asked to supply the CSR that you generated in #2.</li>
<li>
<h2>Install the SSL key from #1, the SSL certificate from #3, and the SSL issuer root certificates (aka &#8220;bundle&#8221; or &#8220;chain&#8221;).</h2>
<p>On an Ubuntu server, I usually upload the files here:</p>
<pre>/etc/apache2/ssl/domain.com.key
/etc/apache2/ssl/domain.com.crt
/etc/apache2/ssl/domain.com.bundle</pre>
</li>
<li>
<h2>Modify your Apache vhost</h2>
<p>Note: Apache only supports one SSL vhost per IP address.</p>
<p>Replace {ip_address} with the public IP address of the server:</p>
<pre>&lt;VirtualHost {ip_address}:443&gt;
    DocumentRoot /var/www/vhosts/domain.com

    SSLEngine on
    SSLVerifyClient none
    SSLCertificateFile /etc/apache2/ssl/domain.com.crt
    SSLCertificateKeyFile /etc/apache2/ssl/domain.com.key
    SSLCertificateChainFile /etc/apache2/ssl/domain.com.bundle

    &lt;Directory /var/www/vhosts/domain.com&gt;
        AllowOverride All
        order allow,deny
        allow from all
        Options -Includes -ExecCGI
        AddOutputFilterByType DEFLATE text/html text/plain text/css text/xml application/x-javascript
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;</pre>
</li>
<li>
<h2>Restart Apache</h2>
<pre>/etc/init.d/apache2 restart</pre>
</li>
</ol>
<p>That&#8217;s all!</p>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-06-22/how-to-install-an-ssl-certificate-for-apache-from-start-to-finish/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-06-22/how-to-install-an-ssl-certificate-for-apache-from-start-to-finish/</feedburner:origLink></item>
		<item>
		<title>Cannot run a binary executable from PHP and MAMP?</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/lSwjOj8syZE/</link>
		<comments>http://jonathonhill.net/2012-06-22/cannot-run-a-binary-executable-from-php-and-mamp/#comments</comments>
		<pubDate>Fri, 22 Jun 2012 21:33:48 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=334</guid>
		<description><![CDATA[I recently encountered this obscure error when trying to run an external program using PHP (which was bundled with MAMP Pro) on Mac OS X: dyld: Symbol not found: __cg_jpeg_resync_to_restart Referenced from: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib Expected in: /Applications/MAMP/Library/lib/libJPEG.dylib in /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib Oddly, other commands execute fine from PHP using shell_exec(). Even more strangely, the external program ran fine [...]]]></description>
				<content:encoded><![CDATA[<p>I <a href="http://jonathonhill.net/2012-05-18/unshorten-urls-with-php-and-curl/" target="_blank">recently</a> encountered this obscure error when trying to run an external program using PHP (which was bundled with <a href="http://www.mamp.info/en/mamp-pro/" target="_blank">MAMP Pro</a>) on Mac OS X:</p>
<blockquote>
<pre>dyld: Symbol not found: __cg_jpeg_resync_to_restart
Referenced from: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib
Expected in: /Applications/MAMP/Library/lib/libJPEG.dylib
in /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib</pre>
</blockquote>
<p>Oddly, other commands execute fine from PHP using <code>shell_exec()</code>. Even more strangely, the external program ran fine when executed from a PHP command-line script, just not from MAMP&#8217;s bundled version of Apache!</p>
<p>Thanks to this tidbit from the <a href="http://www.princexml.com/bb/viewtopic.php?f=4&amp;t=3204" target="_blank">PrinceXML forums</a>, the solution was easy:</p>
<blockquote><p>MAMP changes the environment variable <code>$DYLD_LIBRARY_PATH</code>. Check the file:</p>
<pre>/Applications/MAMP/Library/bin/envvars</pre>
<p>and try out if commenting out the two uncommented lines will work for you.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-06-22/cannot-run-a-binary-executable-from-php-and-mamp/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-06-22/cannot-run-a-binary-executable-from-php-and-mamp/</feedburner:origLink></item>
		<item>
		<title>Unshorten URLs with PHP and cURL</title>
		<link>http://feedproxy.google.com/~r/JonathonHill/~3/CdLHwxnB0Js/</link>
		<comments>http://jonathonhill.net/2012-05-18/unshorten-urls-with-php-and-curl/#comments</comments>
		<pubDate>Sat, 19 May 2012 00:12:01 +0000</pubDate>
		<dc:creator>Jonathon</dc:creator>
				<category><![CDATA[Notebook]]></category>
		<category><![CDATA[bitly]]></category>
		<category><![CDATA[CURL]]></category>
		<category><![CDATA[PhantomJS]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://jonathonhill.net/?p=331</guid>
		<description><![CDATA[While working on a site that provides previews of URLs embedded in tweets using the awesome PhantomJS scriptable WebKit browser, and encountered difficulties when an URL shortener such as Bit.ly was used (as is almost always done when tweeting out a link to an interesting article or photo). After a little experimenting, I discovered that [...]]]></description>
				<content:encoded><![CDATA[<p>While working on a site that provides previews of URLs embedded in tweets using the awesome <a href="http://phantomjs.org/" target="_blank">PhantomJS</a> scriptable WebKit browser, and encountered difficulties when an URL shortener such as <a href="http://bit.ly" target="_blank">Bit.ly</a> was used (as is almost always done when tweeting out a link to an interesting article or photo).</p>
<p>After a little experimenting, I discovered that cURL makes it super easy to un-shorten a URL that has been shortened, without depending on a <a href="http://www.unshorten.com/" target="_blank">third</a>-<a href="http://unshort.me/" target="_blank">party</a> <a href="http://unshorten.it/" target="_blank">service</a>.</p>
<p>Improving slightly on <a href="http://codeaid.net/php/get-the-last-effective-url-from-a-series-of-redirects-for-the-given-url" target="_blank">this function</a>, here&#8217;s how I did it in PHP:</p>
<pre>function unshorten_url($url) {
    $ch = curl_init($url);
    curl_setopt_array($ch, array(
        CURLOPT_FOLLOWLOCATION =&gt; TRUE,  // the magic sauce
        CURLOPT_RETURNTRANSFER =&gt; TRUE,
        CURLOPT_SSL_VERIFYHOST =&gt; FALSE, // suppress certain SSL errors
        CURLOPT_SSL_VERIFYPEER =&gt; FALSE, 
    ));
    curl_exec($ch);
    $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
    curl_close($ch);
    return $url;
}</pre>
<p>That&#8217;s all you have to do. This works even if multiple URL shorteners are used in sequence on the same link, and in the event of an error, you&#8217;ll just get the same link back that you started with.</p>
<p>This seems to be a bit more robust than some of the other solutions floating around the &#8216;net, since it doesn&#8217;t try to hack the <a href="http://stackoverflow.com/questions/7746852/how-can-i-unwrap-t-co-links-via-an-api-to-display-the-unshortened-url" target="_blank">response headers</a> or body (which could change without notice), and it will recurse as many times as it needs to up to the value of the <a href="http://us3.php.net/curl_setopt" target="_blank"><code>CURLOPT_MAXREDIRS</code></a> setting.</p>
<p>Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://jonathonhill.net/2012-05-18/unshorten-urls-with-php-and-curl/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://jonathonhill.net/2012-05-18/unshorten-urls-with-php-and-curl/</feedburner:origLink></item>
	</channel>
</rss>
