<?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>Straylight Run</title>
	<atom:link href="https://blog.straylightrun.net/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.straylightrun.net</link>
	<description>Software, Technology, PHP</description>
	<lastBuildDate>Mon, 18 Mar 2013 20:18:53 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2.9</generator>
	<item>
		<title>This Is A Reminder For Me</title>
		<link>https://blog.straylightrun.net/2013/03/18/this-is-a-reminder-for-me/</link>
		
		<dc:creator><![CDATA[gerard]]></dc:creator>
		<pubDate>Mon, 18 Mar 2013 20:18:53 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<guid isPermaLink="false">http://blog.straylightrun.net/?p=402</guid>

					<description><![CDATA[@p9k nope: `bundle install &#8211;path vendor &#8211;binstubs` and add ./bin to PATH. Gemsets are a terrible idea. &#8212; Chris Moore (@cdmwebs) November 12, 2012]]></description>
										<content:encoded><![CDATA[<blockquote class="twitter-tweet" data-conversation="none">
<p>@<a href="https://twitter.com/p9k">p9k</a> nope: `bundle install &#8211;path vendor &#8211;binstubs` and add ./bin to PATH. Gemsets are a terrible idea.</p>
<p>&mdash; Chris Moore (@cdmwebs) <a href="https://twitter.com/cdmwebs/status/268006540659798016">November 12, 2012</a></p></blockquote>
<p><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>So You Want To Use jQuery In Your Javascript Widget?</title>
		<link>https://blog.straylightrun.net/2012/10/23/so-you-want-to-use-jquery-in-your-javascriptwidget/</link>
					<comments>https://blog.straylightrun.net/2012/10/23/so-you-want-to-use-jquery-in-your-javascriptwidget/#comments</comments>
		
		<dc:creator><![CDATA[gerard]]></dc:creator>
		<pubDate>Tue, 23 Oct 2012 20:45:28 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[bookmarklet]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[module]]></category>
		<category><![CDATA[widget]]></category>
		<category><![CDATA[zepto]]></category>
		<guid isPermaLink="false">http://blog.straylightrun.net/?p=370</guid>

					<description><![CDATA[Recently, I tweeted: Going to start inlining Zepto into all my bookmarklet and widget Javascript. All the benefits of jquery without the big download. — gerard sychay (@hellogerard) September 29, 2012 &#160; which prompted: @hellogerard share a sample, please. — Richard Bronosky (@RichardBronosky) October 12, 2012 &#160; Unfortunately, the code I was referring to was [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Recently, I tweeted:</p>
<blockquote class="twitter-tweet"><p>Going to start inlining Zepto into all my bookmarklet and widget Javascript. All the benefits of jquery without the big download.</p>
<p>— gerard sychay (@hellogerard) <a href="https://twitter.com/hellogerard/status/252068860046487552" data-datetime="2012-09-29T15:34:34+00:00">September 29, 2012</a></p></blockquote>
<p>&nbsp;</p>
<p>which prompted:</p>
<blockquote class="twitter-tweet" data-in-reply-to="252068860046487552"><p>@<a href="https://twitter.com/hellogerard">hellogerard</a> share a sample, please.</p>
<p>— Richard Bronosky (@RichardBronosky) <a href="https://twitter.com/RichardBronosky/status/256771460008407042" data-datetime="2012-10-12T15:01:01+00:00">October 12, 2012</a></p></blockquote>
<p>&nbsp;</p>
<p>Unfortunately, the code I was referring to was client code, but how about a blog post?</p>
<p><span style="text-decoration: underline;"><strong>The Widget Problem</strong></span></p>
<p>For whatever reason, I have been writing lots of Javascript widgets and bookmarklets.  This type of 3rd-party code is usually copied and pasted into a website or initiated via a boorkmark (both work in similar fashion). The tricky party about widgets is that the code must be lean and fast, and must be robust to hostile environments.  By hostile environments, I mean that you can&#8217;t make any assumptions about a particular website these days.  If you&#8217;ve ever tried to scrape arbitrary webpages, you know what I&#8217;m talking about.</p>
<p>The problem with jQuery is that sometimes it is neither lean nor fast, and you can&#8217;t assume that the website hosting your widget has jQuery already, or has a particular version of jQuery.  But we need jQuery.  We need its selectors, and Ajax helpers, and CSS helpers.  The solution is to load our own jQuery.</p>
<p><span style="text-decoration: underline;"><strong>I Got Mine (jQuery)</strong></span></p>
<p>Start with the typical <a title="Preparing Yourself for Modern JavaScript Development " href="http://www.codethinked.com/preparing-yourself-for-modern-javascript-development">function expression module</a> that isolates your code from any other Javascript.</p>
<pre lang="javascript">(function(document, undefined) {

  // ... your isolated code

})(document);</pre>
<p>Load jQuery asynchronously, and prepare a callback that runs when jQuery is loaded.</p>
<pre lang="javascript">(function(document, undefined) {

  // Load our own jQuery
  s = document.createElement('script');
  s.src='//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js';
  s.onload = function() {
    // ... 
  });

  d.getElementsByTagName('head')[0].appendChild(s);

})(document);</pre>
<p>When jQuery loads, it will assign itself to the $ variable, overwriting whatever was using it beforehand, such as the host site&#8217;s copy of jQuery.  Fortunately, jQuery keeps a copy of the original value, and running <a title="jQuery.noConflict()" href="http://api.jquery.com/jQuery.noConflict/">jQuery.noConflict()</a> restores the original value of $ and returns the latest value.  Run <code>$.noConflict()</code> to get our own, local copy of jQuery in the $ variable.</p>
<pre lang="javascript">(function(document, undefined) {

  // Load our own jQuery
  s = document.createElement('script');
  s.src='//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js';
  s.onload = function() {
    var $ = jQuery.noConflict();
    // ... $ now safe to use
  });

  d.getElementsByTagName('head')[0].appendChild(s);

})(document);</pre>
<p><span style="text-decoration: underline;"><strong>Can We Do Better?</strong></span></p>
<p>This works well. However, if the thought of a whole network request (even if cached) just to run your widget bothers you, there&#8217;s a surprisingly simple alternative: inline jQuery.  That is, copy and paste the entire jQuery library into your widget code.  This may seem unusual, but despite it&#8217;s size, it&#8217;s still cheaper than a network request.</p>
<pre lang="javascript">(function(document, undefined) {

  // Load our own jQuery
  /*! jQuery v@1.8.1 jquery.com | jquery.org/license */
  (function(a,b){function G(a){var b=F[a]={};return ...

  var $ = jQuery.noConflict();
  // ... $ now safe to use

})(document);</pre>
<p><span style="text-decoration: underline;"><strong>Can We (Still) Do Better?</strong></span></p>
<p>If inlining all 91k (as of 1.8.2) of jQuery also bothers you: consider <a title="Zepto.js" href="http://zeptojs.com/">Zepto</a> (23k for 1.0RC1). Zepto is billed as <strong>&#8220;</strong>a minimalist <strong>JavaScript library for modern browsers</strong> with a largely <strong>jQuery-compatible API</strong>.&#8221;</p>
<pre lang="javascript" escaped="true">(function(document, undefined) {

  // Load our own Zepto
  /* Zepto v1.0rc1 - polyfill zepto event detect fx ajax form touch - zeptojs.com/license */
  (function(a){String.prototype.trim===a&amp;&amp;(String.prototype ...

  var $ = window.Zepto;
  // ... $ now safe to use

})(document);</pre>
<p>Zepto, unlike jQuery, will not assign itself to $ if it is already defined, so no issue there.</p>
<p>Now, don&#8217;t make the mistake I did in thinking Zepto is a drop-in replacement as a &#8220;lightweight&#8221; jQuery because it certainly is not, especially in CSS manipulations.  Support for CSS effects in IE is sketchy, and its Ajax helpers are a bit different.  <em>However</em>, Zepto gives you all the selectors and DOM manipulators you&#8217;re used to, has all the Ajax helpers you need, and offers useful utilities such as <a title="$.proxy()" href="http://zeptojs.com/#$.proxy">$.proxy()</a>.</p>
<p>So if you need visual effects in your widget, Zepto will probably not do. But if all you need is the ability to add/remove/select elements, then Zepto works great as a jQuery replacement.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.straylightrun.net/2012/10/23/so-you-want-to-use-jquery-in-your-javascriptwidget/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Introducing Jobby, Your PHP Cron Job Manager</title>
		<link>https://blog.straylightrun.net/2012/10/23/introducing-jobby-your-php-cron-job-manager/</link>
					<comments>https://blog.straylightrun.net/2012/10/23/introducing-jobby-your-php-cron-job-manager/#comments</comments>
		
		<dc:creator><![CDATA[gerard]]></dc:creator>
		<pubDate>Tue, 23 Oct 2012 14:33:43 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[batch]]></category>
		<category><![CDATA[cron]]></category>
		<category><![CDATA[crontab]]></category>
		<category><![CDATA[jobby]]></category>
		<category><![CDATA[offline]]></category>
		<category><![CDATA[shell]]></category>
		<guid isPermaLink="false">http://blog.straylightrun.net/?p=372</guid>

					<description><![CDATA[If you&#8217;re like me, you have a number of offline jobs running in your application.  These jobs process images, send emails, warm up caches, and sync up data with 3rd-party APIs. Also, if you&#8217;re like me, you run into the same problems over and over: Manually maintaining crontab files is a burden. Frequent jobs that stall [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>If you&#8217;re like me, you have a number of offline jobs running in your application.  These jobs process images, send emails, warm up caches, and sync up data with 3rd-party APIs.</p>
<p>Also, if you&#8217;re like me, you run into the same problems over and over:</p>
<ul>
<li>Manually maintaining crontab files is a burden.</li>
<li>Frequent jobs that stall or delay may start running multiple copies.</li>
<li>Knowing when a job fails automatically is tough because honestly who has time to look at log files?</li>
<li>If you have a webfarm, you have to decide which jobs run on which servers, or all of them.</li>
</ul>
<p>Enter <a title="hellogerard/jobby" href="https://github.com/hellogerard/jobby">jobby, the PHP cron job manager</a>.</p>
<p>Install a single crontab entry on each of your servers, and never edit crontab again.  The master cron job runs a PHP file which contains all of your real jobs and executes them according to their individual schedules (using crontab syntax) along with other configs. The nice thing is that since <code>jobby</code> is just PHP, you can decided what jobs to run and when programmatically. Now, I know there are more expansive server configuration tools out there, but those felt too heavy for this kind of use.</p>
<p>More features: <code>jobby</code> will ensure only 1 copy of a job runs at a time, can email one or more recipients if a job fails, and can run as different users on select hosts.</p>
<p>I developed a version of <code>jobby</code> years ago, and have brought it along with me on various projects, slowly tweaking it.  It&#8217;s helped me immensely and if you&#8217;re interested, check out the <a title="hellogerard/jobby" href="https://github.com/hellogerard/jobby">README</a> and <code>composer install</code> away!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.straylightrun.net/2012/10/23/introducing-jobby-your-php-cron-job-manager/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>Give Read-Only Access to Your Git Repo via SSH</title>
		<link>https://blog.straylightrun.net/2012/04/25/give-read-only-access-to-your-git-repo-via-ssh/</link>
					<comments>https://blog.straylightrun.net/2012/04/25/give-read-only-access-to-your-git-repo-via-ssh/#comments</comments>
		
		<dc:creator><![CDATA[gerard]]></dc:creator>
		<pubDate>Wed, 25 Apr 2012 16:10:02 +0000</pubDate>
				<category><![CDATA[Sysadmin]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[git-shell]]></category>
		<category><![CDATA[git-shell-commands]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[gitolite]]></category>
		<category><![CDATA[gitosis]]></category>
		<category><![CDATA[ssh]]></category>
		<guid isPermaLink="false">http://blog.straylightrun.net/?p=366</guid>

					<description><![CDATA[So we took on some vendors to help us out. I wanted to provide these individuals authenticated, read-only access to our git repos so they could stay current with the project, but not commit code directly (they’ll have their own repo). Google yielded these excellent results pages. 8 ways to share your git repository [SO] [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>So we took on some vendors to help us out. I wanted to provide these individuals <em>authenticated</em>, read-only access to our git repos so they could stay current with the project, but not commit code directly (they’ll have their own repo). Google yielded these excellent results pages.</p>
<ul>
<li><a href="http://www.jedi.be/blog/2009/05/06/8-ways-to-share-your-git-repository/">8 ways to share your git repository</a> </li>
<li><a href="http://stackoverflow.com/questions/1662205/how-to-make-a-git-repository-read-only/1663441#1663441">[SO] How to make a git repository read-only?</a> </li>
</ul>
<p>When read altogether, I had a few options.</p>
<ol>
<li>Run the <a href="http://schacon.github.com/git/git-daemon.html">git-daemon</a>. The repo would be publicy available over the internet via the git (<code>git://</code>) protocol.&#160; This provides read-only access, but it provides it to the <em>whole world</em>.&#160; Not a solution. </li>
<li>Run a <a href="http://github.com">GitHub</a>-like server, like <a href="http://swik.net/gitosis">Gitosis</a> or <a href="http://github.com/sitaramc/gitolite">Gitolite</a>.&#160; This was the sledgehammer solution.&#160; Fine-grained access control, plus all the other bells and whistles. But I was not interested in installing and understanding a whole git-hosting server application. </li>
<li>SSH via <a href="http://schacon.github.com/git/git-shell.html"><code>git-shell</code></a>.&#160; Each user gets a regular SSH account on the server, but each user gets a <code>git-shell</code> instead of a regular login shell like <code>bash</code>. The <code>git-shell</code> restricts the user to git operations.&#160; Write permissions are restricted using standard Linux permissions on the repo itself. </li>
</ol>
<p>So #3 was what I needed. I created an SSH login for each user that needed access to the repo. I set the login shell for each user to <code>/usr/bin/git-shell</code>. I put each user in a group that had read-only file system permissions to the repo.&#160; Testing it out worked well.&#160; Users could git clone and pull, but pushing failed and attempting to SSH directly failed.</p>
<p>One last note: as the man page mentions, you can create a directory called <code>git-shell-commands</code> in home directories of <code>git-shell users</code>.&#160; <code>git-shell</code> users will be able to run any command in this directory.&#160; If there is a help program in the directory, it is run when a <code>git-shell</code> user logs in.&#160; More details on <a href="http://serverfault.com/questions/285324/git-shell-not-enabled/325484#325484"><code>git-shell-commands</code> here</a>, including the location of sample <code>git-shell</code> commands on your server.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.straylightrun.net/2012/04/25/give-read-only-access-to-your-git-repo-via-ssh/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Making Blueprint CSS Grid Mobile-Friendly</title>
		<link>https://blog.straylightrun.net/2011/11/07/making-blueprint-css-grid-mobile-friendly/</link>
					<comments>https://blog.straylightrun.net/2011/11/07/making-blueprint-css-grid-mobile-friendly/#comments</comments>
		
		<dc:creator><![CDATA[gerard]]></dc:creator>
		<pubDate>Mon, 07 Nov 2011 19:19:40 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[front-end]]></category>
		<category><![CDATA[layout]]></category>
		<guid isPermaLink="false">http://blog.straylightrun.net/2011/11/07/making-blueprint-css-grid-mobile-friendly/</guid>

					<description><![CDATA[I had an existing site I created using Blueprint CSS a while ago. I wanted to make it mobile-friendly. There are responsive CSS grid frameworks out there now, but I didn’t feel like replacing Blueprint, and I figured all I had to do was, using media queries, make every column span a fixed width. That [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I had an existing site I created using <a href="http://blueprintcss.org/">Blueprint CSS</a> a while ago. I wanted to make it mobile-friendly. There are <a href="http://framelessgrid.com/">responsive CSS</a> <a href="http://getskeleton.com/">grid frameworks</a> out there now, but I didn’t feel like replacing Blueprint, and I figured all I had to do was, using media queries, make every column span a fixed width. That was mostly correct. In the end, I used 100% widths, plus a few other alterations.  Here is how I ended up doing it.</p>
<p>Normally, you will link to your Blueprint <code>screen.css</code> file like so:</p>
<pre lang="html">	<link href="/css/screen.css" rel="stylesheet" media="screen, projection" type="text/css" /></pre>
<p>The following lines tell the browser to link first to a mobile-friendly CSS file. Then, if and when the width is <code>600px</code>, link to the normal, desktop CSS file. The idea is that bandwidth is scarcer on mobile, so we don&#8217;t mind downloading two files on a desktop machine if we have to.</p>
<pre lang="html"><!-- For mobile displays -->
	<link href="/css/mobile.css" rel="stylesheet" media="screen, projection" type="text/css" />
	<link href="/css/print.css" rel="stylesheet" media="print" type="text/css" /> 

<!-- For desktop displays -->
	<link href="/css/screen.css" rel="stylesheet" media="only screen and (min-width: 600px)" /></pre>
<p>To create my <code>mobile.css</code>, I copied <code>screen.css</code>, and made the following changes.</p>
<ol>
<li>Change table elements padding to <code>2px</code>&nbsp;
<pre lang="css">th, td, caption {padding: 2px;}</pre>
</li>
<li>Resize all image widths to fit on screen&nbsp;
<pre lang="css">a img {border:none;max-width:90%}</pre>
</li>
<li>Change <code>input</code> text fields to 67% width.&nbsp;
<pre lang="css">input.text, input.title {width: 67%; padding: 5px;}</pre>
</li>
<li>Change <code>textarea</code> inputs to 80% width, and reduce <code>height</code> to <code>150px</code>.&nbsp;
<pre lang="css">textarea {width: 80%; height: 150px; padding: 5px;}</pre>
</li>
<li>Change the overall <code>container</code> to 90% width.&nbsp;
<pre lang="css">.container {width: 90%; margin: 0 auto;}</pre>
</li>
<li>Change all <code>span</code> grid classes to 100% width.&nbsp;
<pre lang="css">.span-1 {width: 100%;}
...
.span-24 {width: 100%; margin-right: 0;}</pre>
</li>
<li>For some reason, I ended up deleting all <code>input.span</code> and <code>textarea.span</code> classes, though I&#8217;m not sure why now.</li>
<li>Change all <code>append</code> grid classes to <code>0px</code> right padding.&nbsp;
<pre lang="css">.append-1 {padding-right: 0;}
...
.append-24 {padding-right: 0;}</pre>
</li>
<li>Change all <code>prepend</code> grid classes to <code>0px</code> left padding.&nbsp;
<pre lang="css">.prepend-1 {padding-left: 0;}
...
.prepend-24 {padding-left: 0;}</pre>
</li>
<li>Change all <code>pull</code> grid classes to <code>0px</code> left margin.&nbsp;
<pre lang="css">.pull-1 {margin-left: 0;}
...
.pull-24 {margin-left: 0;}</pre>
</li>
<li>Change all <code>push</code> grid classes to <code>0px</code> margin.&nbsp;
<pre lang="css">.push-1 {margin: 0;}
...
.push-24 {margin: 0;}</pre>
</li>
</ol>
<p>Not pretty, but it seems to work well.  Happy CSS!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.straylightrun.net/2011/11/07/making-blueprint-css-grid-mobile-friendly/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>My Facebook Authentication Best Practices</title>
		<link>https://blog.straylightrun.net/2011/09/19/my-facebook-authentication-best-practices/</link>
					<comments>https://blog.straylightrun.net/2011/09/19/my-facebook-authentication-best-practices/#comments</comments>
		
		<dc:creator><![CDATA[gerard]]></dc:creator>
		<pubDate>Mon, 19 Sep 2011 22:17:09 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[authentication]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[oauth]]></category>
		<category><![CDATA[oauth2]]></category>
		<category><![CDATA[sdk]]></category>
		<guid isPermaLink="false">http://blog.straylightrun.net/2011/09/19/my-facebook-authentication-best-practices/</guid>

					<description><![CDATA[NOTE: I had a version of this blog post in draft mode for months addressing the old (PHP SDK v2.1.2) Facebook PHP library.&#160; In a fit of momentum, I am publishing this post now, updated to use the new library (PHP SDK v3.1.1).&#160; Since I am not as familiar with the new one, there may [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><em><strong>NOTE: </strong>I had a version of this blog post in draft mode for months addressing the old (PHP SDK v2.1.2) Facebook PHP library.&#160; In a fit of momentum, I am publishing this post now, updated to use the new library (PHP SDK v3.1.1).&#160; Since I am not as familiar with the new one, there may be better ways to do the following, although the code below works.</em></p>
<p>Facebook authentication, much like the rest of the platform, can be maddening.&#160; It appears easy enough at 1st glance, until you realize the <a href="https://developers.facebook.com/docs/guides/web/#login">contrived XFBML examples in the docs</a> will not get you very far.&#160; So then you might take a look at the <a href="https://developers.facebook.com/docs/authentication/">advanced document</a>. And then realize that the advance document, that giant, complicated, very detailed and thorough explanation of OAuth 2.0 as implemented by Facebook, <em>does not actually</em> help you code your website.</p>
<p>This blog post is more for my benefit than yours, and serves to summarize my current Facebook authentication strategy, so I don’t have to figure it out over and over. At least, until Facebook changes it.</p>
<p><strong><u>My Requirements</u></strong></p>
<p>First off, realize that there are 3 ways to do any particular thing on the Facebook platform: XFBML, JS, and PHP (server-side). </p>
<p>That said, I do not use the XFBML <code>&lt;fb:login/&gt;</code> button.&#160; XFBML is great for non-developers who can copy and paste the documentation into a website and impress their friends.&#160; But XFBML is slower, ties your user’s application session to your user’s Facebook session, and you can’t make general API calls, so you may as well learn how to use the JS or PHP SDKs anyway.</p>
<ol>
<li>Authentication flow must work with and without JavaScript, as we are targeting older smart phones, such as Blackberry Curves. </li>
<li>I do not want to use Facebook to <em>persist</em> my application sessions.&#160; I merely want to use Facebook for <em>authentication</em> and then use standard PHP sessions to maintain a user login.&#160; Using the Facebook session <em>as your </em>application session (as in the case of XFBML above) is sucky.&#160; You will noticed sites that do this because when you log out of the site, you will log out of Facebook, and vice-versa.&#160; Logging out of your site <em>should not </em>log you out of Facebook.&#160; Facebook sessions and your application’s session should be separate.&#160; I want to use Facebook only to <em>authenticate</em> the user, that is, to confirm the user is who they say they are.&#160; <a href="http://foursquare.com">Foursquare</a> does a great job of this.</li>
</ol>
<p><strong><u>Implementation</u></strong></p>
<p>First, I need a login login link that non-JS browsers can use.</p>
<pre lang="html" line="0" escaped="true">&lt;a id=&quot;fb-login&quot; href=&quot;&lt;?= $loginUrl ?&gt;&quot;&gt;Login&lt;/a&gt; </pre>
<p>That <code>$loginUrl</code> comes from:</p>
<pre lang="php" line="0" escaped="true">$loginUrl = $facebook-&gt;getLoginUrl(array('scope' =&gt; 'offline_access')); </pre>
<p>The login link will ask the user for <code>offline_access </code>permission, as an example.</p>
<p>Then we need some the FB JS SDK to handle the link for those of us with modern browsers.</p>
<pre lang="javascript" line="1" escaped="true">            
FB.init( { appId: 'XXXXXX', status: true, cookie: false, oauth: true } );

$('#fb-login').click(function(e) {
  e.preventDefault();

  FB.getLoginStatus(function(response) {

    // maintain application anchor/query string, if any
    q = window.location.search.substring(1);
    if (window.location.hash) {
      q = window.location.hash.split('?')[1];
    }

    // if already logged in, redirect
    if (response.authResponse) {
      window.location.href = '/?signed_request=' + response.authResponse.signedRequest + '&amp;' + q;

    } else {

      // else present user with FB auth popup
      FB.login(function(response) {

        // if user logs in successfully, redirect
        if (response.authResponse) {
          window.location.href = '/?signed_request=' + response.authResponse.signedRequest + '&amp;' + q;
        }
      }, { scope:'offline_access' } );
    }
  });
});</pre>
<p>Couple interesting things to point out:</p>
<ol>
<li>In the call to <a href="https://developers.facebook.com/docs/reference/javascript/FB.init/"><code>FB.init()</code></a>, the <code>cookie </code>flag is set to <code>false</code>.&#160; In most FB examples, this flag is set to <code>true</code>.&#160; If this flag is <code>true</code>, the JS will set a cookie (that begins with ‘<code>fbsr_</code>’) that will keep the user connected to your Facebook app during a browser session.&#160; This can confuse your app, because if you do not clear this cookie when your user logs out, your app may automatically re-authenticate your user.&#160; If you <em>do </em>intend to use your user’s Facebook session as your website’s session, set this flag to <code>true</code>. </li>
<li>On lines 16 and 25, as in most FB examples, a simple <code>window.location.reload()</code> might appear.&#160; This only works if the JS sets the Facebook cookie, as this cookie will tell the server that the user is authenticated after the reload.&#160; Since we can’t use the cookie, we need another way to tell the server if this login attempt was successful. </li>
</ol>
<p><strong><u>Server side</u></strong></p>
<p>On the server-side, Facebook will tell us if this user is who he says he is with the <a href="https://developers.facebook.com/docs/reference/php/facebook-getUser/"><code>getUser()</code></a> method.&#160; If this method returns a non-zero user ID, then Facebook has authenticated this user, and we can create a normal PHP login session, however you usually do that.</p>
<pre lang="php" line="0" escaped="true">$id = $facebook-&gt;getUser();

if ($id)
{
    // login successful
    $_SESSION['user'] = $id;
}

// redirect to user dashboard or something
header('HTTP/1.1 302 Found');
header('Location: /');
exit;</pre>
<p><code>getUser()</code> tries to authenticate a few different ways.&#160; You can read the code in the <a href="https://github.com/facebook/php-sdk/blob/v3.1.1/src/base_facebook.php#L408">PHP SDK</a> if you’re really curious. One way is from the ‘<code>fbsr_</code>’ cookie.&#160; Another is by checking for the <code>signed_request </code>in the query string.&#160; Remember when we passed that in earlier via JS?&#160;&#160; The cool thing is, that even in the non-JS case, Facebook will send that <code>signed_request </code>in the query string, so this code will function the same in both cases.</p>
<p>Keeping the user logged in as he makes requests is the same as usual.&#160; I simply check for the flag (or, in the example above, a user ID) to verify authenticated status. </p>
<p>To logout, I can simply clear the PHP session like normal. </p>
<pre lang="php" line="0" escaped="true">// logout
session_unset();
session_destroy();

// redirect to external home page
header('HTTP/1.1 302 Found');
header('Location: /');
exit;</pre>
<p>Again, this does not log me out of Facebook</p>
<p><u><strong>Conclusion</strong></u></p>
<p>When might you want to use your user’s Facebook session for your user’s website session?&#160; I think the only case you would do that is if your web app is a Facebook-centric application.&#160; That is, if every feature of your app involves Facebook somehow. In that case, maybe it makes sense because your user cannot use your app without being connected to Facebook.</p>
<p>You can find a fully working example of this flow on my <a href="https://github.com/hellogerard/facebook-auth">GitHub</a>.&#160; This really belongs in an MVC-type framework, but for simplicity I put everything in a giant <code>if/else </code>statement.&#160; Throw the files in a webroot, add your app’s ID and secret to the PHP file, and try it out. Try it with JavaScript enabled and disabled.&#160; After logging into the example, try logging out of Facebook, and then logging out of the example, and vice-versa. And let me know if you spot anything fishy.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.straylightrun.net/2011/09/19/my-facebook-authentication-best-practices/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Facebook: Upgrading to OAuth 2.0</title>
		<link>https://blog.straylightrun.net/2011/09/19/facebook-upgrading-to-oauth-2-0/</link>
					<comments>https://blog.straylightrun.net/2011/09/19/facebook-upgrading-to-oauth-2-0/#comments</comments>
		
		<dc:creator><![CDATA[gerard]]></dc:creator>
		<pubDate>Mon, 19 Sep 2011 18:01:44 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[authentication]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[oauth]]></category>
		<category><![CDATA[oauth2]]></category>
		<category><![CDATA[sdk]]></category>
		<category><![CDATA[upgrading]]></category>
		<guid isPermaLink="false">http://blog.straylightrun.net/2011/09/19/facebook-upgrading-to-oauth-2-0/</guid>

					<description><![CDATA[As Facebook has beaten into me via my RSS reader over the last few months: All apps must migrate to OAuth 2.0 for authentication and expect an encrypted access token. The old SDKs, including the old JS SDK and old iOS SDK will no longer work. &#60;digression&#62; In the spirit of typical Facebook developer documentation, [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>As Facebook has beaten into me via my RSS reader over the last few months:</p>
<blockquote>
<p>All apps must <a href="https://developers.facebook.com/docs/oauth2-https-migration/">migrate to OAuth 2.0 for authentication</a> and expect an encrypted access token. The old SDKs, including the old JS SDK and old iOS SDK will no longer work.</p>
</blockquote>
<p>&lt;digression&gt; In the spirit of typical Facebook developer documentation, even this ostensibly unambiguous statement raises questions.&#160; See, I have been using the “new” Graph API to authenticate for many months now, but not with the <code>oauth </code>flag set to <code>true</code>.&#160; (In other words, I am on Oauth 1.0?)&#160; Does this mean my code will break on Oct. 1st?&#160; Or when they say that legacy authentication will break on Oct. 1st, do they mean that only the <em><a href="https://developers.facebook.com/docs/authentication/connect_auth/">really old legacy auth</a></em> will break?&#160; In either case, I may as well upgrade to the latest and greatest now. &lt;/digression&gt;</p>
<p>Here are the changes I had to make to get my stuff working with the <code>oauth </code>flag set to <code>true</code>.&#160; They are mostly field and param name changes.&#160; There may be other changes important to you, but this is what I needed to modify to upgrade.</p>
<h6><strong><u>1. Add &#8216;<code>oauth: true</code>&#8216; to </u><a href="https://developers.facebook.com/docs/reference/javascript/FB.init/"><code>FB.init()</code></a></strong></h6>
<p>So change this:</p>
<pre lang="javascript" escaped="true">FB.init({ appId : XXXXXX });</pre>
<p>to this:</p>
<pre lang="javascript" escaped="true">FB.init({ appId : XXXXXX, oauth : true });</pre>
<p>I infer from Facebook (which is usually dangerous) that this flag will be enabled for everyone on Oct. 1st.</p>
<p><strong><u>2. Change &#8216;<code>perms</code>&#8216; to &#8216;<code>scope</code>&#8216; </u></strong></p>
<p>Anywhere you request permissions for your app upon Facebook login, you must change the ‘<code>perms</code>’ field to ‘<code>scope</code>’. So for a typical XFBML implementation, change this:</p>
<pre lang="html" escaped="true">&lt;fb:login-button perms=”offline_access”&gt;&lt;/fb:login-button&gt;</pre>
<p>to this:</p>
<pre lang="html" escaped="true">&lt;fb:login-button scope=”offline_access”&gt;&lt;/fb:login-button&gt;</pre>
<p>For a typical JS implementation, change this:</p>
<pre lang="javascript" escaped="true">FB.login(function(response) { }, { perms : ‘offline_access’ });</pre>
<p>to this:</p>
<pre lang="javascript" escaped="true">FB.login(function(response) { }, { scope : ‘offline_access’ });</pre>
<p>For a typical non-JS, server-side (PHP) implementation, change this:</p>
<pre lang="php" escaped="true">&lt;a href=”&lt;?php echo $facebook-&gt;getLoginUrl(array(‘req_perms’ =&gt; ‘offline_acces's’)); ?&gt;”&gt;Connect with Facebook&lt;/a&gt;</pre>
<p>to this:</p>
<pre lang="php" escaped="true">&lt;a href=”&lt;?php echo $facebook-&gt;getLoginUrl(array(‘scope’ =&gt; ‘offline_acces's’)); ?&gt;”&gt;Connect with Facebook&lt;/a&gt;</pre>
<p><strong><u>3. Update JavaScript response object</u></strong></p>
<p>Functions like <a href="https://developers.facebook.com/docs/reference/javascript/FB.login/"><code>FB.login()</code></a> and <a href="https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/"><code>FB.getLoginStatus()</code></a> accept a callback that gets passed a response object from Facebook after an authentication attempt.&#160; The previous response looked like this:</p>
<pre lang="javascript">response = { perms : ‘offline_access’, 
             session : { 
               access_token : ‘XXXXXX’, 
               base_domain : ‘mydomain.com’, 
               expires : ‘0’, 
               secret : ‘XXXXXX’, 
               session_key : ‘XXXXXX’, 
               sig : ‘XXXXXX’, 
               uid : ‘XXXXXX’ }, 
             status : ‘connected’ };</pre>
<p>The new response looks like this:</p>
<pre lang="javascript">response = { authResponse : { 
               accessToken : ‘XXXXXX’, 
               expiresIn : 0, 
               signedRequest : ‘XXXXXX’, 
               userID : ‘XXXXXX’ }, 
             status : ‘connected’ };</pre>
<p>You must update your JS authentication logic accordingly.</p>
<p><strong><u>4. Change server-side PHP call getSession() to </u><a href="https://developers.facebook.com/docs/reference/php/facebook-getUser/">getUser()</a></strong></p>
<p>Versions of the PHP SDK prior to 3.0 had both a <code>getSession()</code> and a <code>getUser()</code> method. After 3.0, there is only the <code>getUser()</code> call.&#160; If you were using <code>getSession() </code>to check for authentication previously, you must use <code>getUser() </code>now.&#160; If you don’t care about the internals of the new SDK, then <code>getUser() </code>functions the same as before and returns the Facebook ID of the currently logged in user, or 0 if there is none.</p>
<p>If you do care about the new SDK, then the reason for this is because Facebook has done away with the session concept and embraced the OAuth2 <a href="https://developers.facebook.com/docs/authentication/signed_request/"><code>signed_request</code></a> concept instead.&#160; For example, the JSON-ified session array is no longer passed back to your callback URL in the query string upon authentication.&#160; Now, an encoded signed_request is passed back in the query string, which contains the same info (user ID, access token, expiration time, etc.)</p>
<p><strong><u>5. Change server-side PHP Facebook class constructor</u></strong></p>
<p>The new PHP Facebook class no longer accepts a <code>cookie </code>flag in the constructor.&#160; If you don’t care about the internals of the new SDK, then remove the cookie flag and you’re done.</p>
<p>If you do care about the new SDK, then the reason for this – well, I’m not sure what the reason is.&#160; Perhaps a commenter could enlighten me.&#160; Best as I can tell, they have removed overlapping features of the the JS SDK and PHP SDK. Prior to OAuth2, both SDKs had cookie flags, and both could set and read the session cookie, which could be confusing, especially if you were persisting your own authentication cookie.&#160; Now, the JS can still set and read the <code>signed_request </code>cookie, but the PHP library has gotten out of the cookie game (although it can still read a <code>signed_request </code>cookie set by JS, and now it stores the <code>signed_request </code>data in the PHP session).</p>
<p>One more note about cookie: the new Facebook cookie begins with ‘<code>fbsr_</code>’ and not ‘<code>fbs_</code>’.</p>
<p><u><strong>Conclusion</strong></u></p>
<p>That’s it. As I said, these worked for me, but YMMV. Happy upgrading!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.straylightrun.net/2011/09/19/facebook-upgrading-to-oauth-2-0/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Slides from tek11</title>
		<link>https://blog.straylightrun.net/2011/05/28/slides-from-tek11/</link>
		
		<dc:creator><![CDATA[gerard]]></dc:creator>
		<pubDate>Sat, 28 May 2011 17:34:48 +0000</pubDate>
				<category><![CDATA[Career]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[conferences]]></category>
		<category><![CDATA[cookies]]></category>
		<category><![CDATA[sessions]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[talks]]></category>
		<category><![CDATA[tek]]></category>
		<category><![CDATA[tek11]]></category>
		<guid isPermaLink="false">http://blog.straylightrun.net/2011/05/28/slides-from-tek11/</guid>

					<description><![CDATA[Here are my slides from my two talks at PHP 2011 tek conference in Chicago, IL.&#160; Slide PDFs and any demo code are on GitHub.&#160; Recap to follow! Of (PHP) Sessions, Cookies, and Authentication Do you know the difference between the PHP config directives session.gc_maxlifetime and session.cookie_lifetime? Have you wrestled with implementing a “Remember Me” [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Here are my slides from my two talks at PHP 2011 tek conference in Chicago, IL.&#160; <a href="https://github.com/hellogerard/tek11">Slide PDFs and any demo code are on GitHub</a>.&#160; Recap to follow!</p>
<blockquote>
<p><u>Of (PHP) Sessions, Cookies, and Authentication</u></p>
<p>Do you know the difference between the PHP config directives <code>session.gc_maxlifetime</code> and <code>session.cookie_lifetime</code>? Have you wrestled with implementing a “Remember Me” button on your login page? Learn how popular sites, such as Twitter and Facebook, keep you logged in (apparently) forever and the security risks of such methods.</p>
</blockquote>
<div style="width: 425px" id="__ss_8116440"><strong style="margin: 12px 0px 4px; display: block"><a title="PHP Cookies, Sessions and Authentication" href="http://www.slideshare.net/gerrys0/php-cookies-sessions-and-authentication">PHP Cookies, Sessions and Authentication</a></strong> <iframe loading="lazy" height="355" marginheight="0" src="http://www.slideshare.net/slideshow/embed_code/8116440" frameborder="0" width="425" marginwidth="0" scrolling="no"></iframe>    </p>
<div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/gerrys0">Gerard Sychay</a> </div>
</p></div>
<blockquote>
<p><u>Who&#8217;s Using Your Software?</u></p>
<p>Software is only successful if someone can use it. Good developers need to do more than just follow specifications, they need to visualize the people who will use it and understand what they need. Get to know your users and the questions you need to ask to make your implementation a success on all fronts.</p>
</blockquote>
<div style="width: 425px" id="__ss_8129301"><strong style="margin: 12px 0px 4px; display: block"><a title="Who&#39;s Using Your Software" href="http://www.slideshare.net/gerrys0/whos-using-your-software">Who&#8217;s Using Your Software</a></strong> <iframe loading="lazy" height="355" marginheight="0" src="http://www.slideshare.net/slideshow/embed_code/8129301" frameborder="0" width="425" marginwidth="0" scrolling="no"></iframe>    </p>
<div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/gerrys0">Gerard Sychay</a> </div>
</p></div>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Ideas of March (or, It&#8217;s Never Too Late) #ideasofmarch</title>
		<link>https://blog.straylightrun.net/2011/04/13/ideas-of-march-or-its-never-too-late-ideasofmarch/</link>
					<comments>https://blog.straylightrun.net/2011/04/13/ideas-of-march-or-its-never-too-late-ideasofmarch/#comments</comments>
		
		<dc:creator><![CDATA[gerard]]></dc:creator>
		<pubDate>Thu, 14 Apr 2011 04:39:20 +0000</pubDate>
				<category><![CDATA[News]]></category>
		<category><![CDATA[blogging]]></category>
		<category><![CDATA[Career]]></category>
		<category><![CDATA[ideasofmarch]]></category>
		<category><![CDATA[twitter]]></category>
		<category><![CDATA[writing]]></category>
		<guid isPermaLink="false">http://blog.straylightrun.net/2011/04/13/ideas-of-march-or-its-never-too-late-ideasofmarch/</guid>

					<description><![CDATA[I am nothing if not timely.&#160; Last month, Chris Shiflett proposed an excellent blog revival idea, wherein I am to write a blog in the month of March about why I like blogs.&#160; Of the ones I managed to read, I liked this “Ideas of March” the best. As for me, well, we are halfway [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I am nothing if not timely.&#160; </p>
<p>Last month, <a href="http://shiflett.org/">Chris Shiflett</a> proposed an excellent <a href="http://shiflett.org/blog/2011/mar/ideas-of-march">blog revival</a> idea, wherein I am to write a blog in the month of March about why I like blogs.&#160; Of the ones I managed to read, I liked this “<a href="http://www.eschrade.com/page/ideas-of-march/">Ideas of March</a>” the best. As for me, well, we are halfway through April – but I started this draft in March?&#160; Here were the rules:</p>
<ol>
<li>Write a post called <em>Ideas of March</em>. </li>
<li>List some of the reasons you like blogs. </li>
<li>Pledge to blog more the rest of the month. </li>
<li>Share your thoughts on Twitter with the <a href="http://search.twitter.com/search?q=%23ideasofmarch">#ideasofmarch</a> hashtag. </li>
</ol>
<p>So here goes…</p>
<p>Way before I had a technology blog, I had a blog about local issues.&#160; Local politics, sports, development, you name it.&#160; I started that blog because I had a lot of opinions about development that was happening around me and issues that were balloted, and I couldn’t rob the world of my enlightened positions by keeping them to myself.&#160;&#160; Blogs allow anyone with something to say to say it.</p>
<p>Blogs are ultimately social.&#160; Before there were tweetups, there were blogger meetups. And it is very easy to have something to talk about when introducing yourself to someone (“Hey, I really liked your blog about…”).&#160; Twitter has taken that “breaking the ice” concept to new levels, but it I’d much rather have someone come up to me and say that they’ve read my blog rather than say that they follow me.</p>
<p>Blogs are written, and writing is a good skill to practice.&#160; If I have to write to explain something, then I have to know it well.&#160; And with practice, the explanation gets better and clearer. Then other thoughts get better and clearer.&#160; Then the world just gets better and clearer.</p>
<p>Oh, and blogs contain a crapload of useful information that solve all sorts of problems, or just spread interesting news.</p>
<p>In summary, I like blogs because:</p>
<ol>
<li>They give everyone a voice. </li>
<li>They help people be more social. </li>
<li>They help me improve my writing. </li>
<li>They contain a crapload of useful information and news. </li>
</ol>
<p>To round off the requirements, I pledge to blog more in this month of March, er – how about I just pledge to blog more?&#160; And hopefully my RSS tweeter should get out the hashtag in the title. </p>
<p><img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.straylightrun.net/2011/04/13/ideas-of-march-or-its-never-too-late-ideasofmarch/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Zend Framework Bootstrap.php (Or, They Hung Him Up By His Bootstraps)</title>
		<link>https://blog.straylightrun.net/2011/04/13/zend-framework-bootstrap-php-or-they-hung-him-up-by-his-bootstraps/</link>
					<comments>https://blog.straylightrun.net/2011/04/13/zend-framework-bootstrap-php-or-they-hung-him-up-by-his-bootstraps/#comments</comments>
		
		<dc:creator><![CDATA[gerard]]></dc:creator>
		<pubDate>Thu, 14 Apr 2011 03:56:11 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[bootstrap]]></category>
		<category><![CDATA[frameworks]]></category>
		<category><![CDATA[resources]]></category>
		<category><![CDATA[zend framework]]></category>
		<guid isPermaLink="false">http://blog.straylightrun.net/2011/04/13/zend-framework-bootstrap-php-or-they-hung-him-up-by-his-bootstraps/</guid>

					<description><![CDATA[I recently came up a very steep learning curve using the Zend Framework (version 1.11.3). I had used components of ZF in the past with success, but this was the first time I had used Zend_Application for my core MVC.&#160; I am no stranger to MVC frameworks in general, but I struggled with Zend. My [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I recently came up a very steep learning curve using the <a href="http://framework.zend.com">Zend Framework</a> (version 1.11.3). I had used components of ZF in the past with success, but this was the first time I had used <a href="http://framework.zend.com/manual/en/zend.application.html"><code>Zend_Application</code></a> for my core MVC.&#160; I am no stranger to MVC frameworks in general, but I struggled with Zend.</p>
<p>My biggest source of confusion was the bootstrap process.&#160; Namely, the autoloading of resources.&#160; Yes, I followed the <a href="http://framework.zend.com/manual/en/learning.quickstart.html">Zend Framework Quick Start</a> and got my app up and running. Unfortunately, I didn’t actually know <em>what was happening</em>.&#160; Finally, I came across this great question on <a href="http://stackoverflow.com">Stackoverflow</a> about <a href="http://stackoverflow.com/questions/1074139/zend-framework-layout">bootstrapping the layout resource</a> that pointed me in the right direction:</p>
<blockquote>
<p>The line from <code>application.ini</code></p>
<pre><code>resources.layout[] = <br /></code></pre>
<p>is equivalent to:</p>
<pre><code>_initLayout() {}<br /></code></pre>
<p>in <code>Bootstrap.php</code></p>
</blockquote>
<p>Aha!&#160; So now it was clear. There are two ways to initialize a resource in Zend: </p>
<ol>
<li>By adding one or more mysterious lines to your application.ini, or </li>
<li>By constructing them with good old fashion code in a method that begins with “_init” in <code>Bootstrap.php</code>. </li>
</ol>
<p>Option #1 bothers me.&#160; Yes, it’s really cool that you can bootstrap a whole MVC application with no code and only a config file, but IMO that option should <em>not</em> be the recommended way for beginners and that options should most definitely <em>not</em> be used in any sort of “Quick Start” guide.&#160; That sort of magic should be reserved for advanced users.&#160; With a little more exploring, I found the code that initialized these resources in the non-trivial <a href="http://framework.zend.com/code/filedetails.php?repname=Zend+Framework&amp;path=%2Ftrunk%2Flibrary%2FZend%2FApplication%2FBootstrap%2FBootstrapAbstract.php"><code>Zend_Application_Bootstrap_BootstrapAbstract</code></a> class.&#160; And I found all the built-in Zend resources in <a href="http://framework.zend.com/code/listing.php?repname=Zend+Framework&amp;path=%2Ftrunk%2Flibrary%2FZend%2FApplication%2FResource%2F&amp;#ae42a1368351649c6b01f1d2aadf9e3d3"><code>Zend/Application/Resource</code></a>, which let me see which resources had which options and how to bootstrap them.&#160; </p>
<p>On to option #2, my preferred way. If I want to initialize the Front Controller, I’ll just put this in the bootstrap file:</p>
<pre lang="php" escaped="true">    
protected function _initFrontController()
{
    $front = Zend_Controller_Front::getInstance();
    $front-&gt;setControllerDirectory(APPLICATION_PATH . '/controllers');
    return $front;
}</pre>
<p>If I want to enable and initialize the layout:</p>
<pre lang="php" escaped="true">    
protected function _initLayout()
{
    $layout = Zend_Layout::startMvc();
    $layout-&gt;setLayoutPath(APPLICATION_PATH . '/layouts/scripts/');
    return $layout;
}</pre>
<p>I really don’t mind carrying this around to every project.&#160; Then I can make changes in the PHP, and not in an INI file.</p>
<p>Clearly I am not the only one struggling with this. I googled problems heavily and the top results usually contained 3 or 4 of the same question asked on Stackoverflow, each of which I opened in a tab.&#160; I think Zend would benefit with a re-organization of the documentation from the ground up, at least of the starter docs and <code>Zend_Application</code>. They should also post a big, flashing red link to that Stackoverflow question about bootstrapping layout.&#160; </p>
<p>Eventually, other components became clear: Resources, Plugins, Action Helpers, and View Helpers, Modules.&#160; I see real power in using the Zend Framework now, particularly with Plugins and Modules, but only after much pain.</p>
<p>I have created a <a href="https://github.com/hellogerard/zend-starter">starter project for Zend projects</a> that contains <a href="https://github.com/hellogerard/zend-starter/blob/modules-app/application/Bootstrap.php"><code>Bootstrap.php</code></a> the way I like it, and some other custom classes for logging and error handling.&#160; There is a branch for a starter project <a href="https://github.com/hellogerard/zend-starter">with modules</a> and <a href="https://github.com/hellogerard/zend-starter/tree/single-app">without modules</a>.&#160; (Modules is a different blog post altogether.)</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.straylightrun.net/2011/04/13/zend-framework-bootstrap-php-or-they-hung-him-up-by-his-bootstraps/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>