<?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>saltybeagle.com</title>
	<atom:link href="http://saltybeagle.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://saltybeagle.com</link>
	<description>All of Brett&#039;s junk.</description>
	<lastBuildDate>Thu, 22 Jul 2010 14:00:11 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.5</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>SVN Switch and Relocate Everything</title>
		<link>http://saltybeagle.com/2010/07/svn-switch-and-relocate-everything/</link>
		<comments>http://saltybeagle.com/2010/07/svn-switch-and-relocate-everything/#comments</comments>
		<pubDate>Thu, 22 Jul 2010 14:00:11 +0000</pubDate>
		<dc:creator>bbieber</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://saltybeagle.com/?p=233</guid>
		<description><![CDATA[We recently had a server DNS name change that affected our main SVN repository server, and required every checkout to be svn switch &#8211;relocate &#8216;ed.
In Eclipse I have every project checked out in ~/workspace/ so every directory with the matching server root would need to be switched. This shell script handled all the svn switch [...]]]></description>
			<content:encoded><![CDATA[<p>We recently had a server DNS name change that affected our main SVN repository server, and required every checkout to be svn switch &#8211;relocate &#8216;ed.</p>
<p>In Eclipse I have every project checked out in ~/workspace/ so every directory with the matching server root would need to be switched. This shell script handled all the svn switch &#8211;relocate statements for me. Replace oldservername and newservername.</p>
<p><code>for dir in `ls -1 */.svn/entries | xargs grep -H -l oldservername | grep  -E -o "^[^\/]+"`; do<br />
	svn switch --relocate `svn info $dir | grep ^Repository\ Root | cut -f 3 -d ' '` `svn info $dir | grep ^Repository\ Root | cut -f 3 -d ' ' | sed 's/oldservername/newservername/'` $dir<br />
done</code> </p>
<p>It may be best to make a backup of your project directories, especially if you have local uncommitted changes. This script works on bash-like command lines, such as Mac OS X.</p>
]]></content:encoded>
			<wfw:commentRss>http://saltybeagle.com/2010/07/svn-switch-and-relocate-everything/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>On Experience</title>
		<link>http://saltybeagle.com/2010/02/on-experience/</link>
		<comments>http://saltybeagle.com/2010/02/on-experience/#comments</comments>
		<pubDate>Mon, 15 Feb 2010 17:50:09 +0000</pubDate>
		<dc:creator>bbieber</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://saltybeagle.com/?p=227</guid>
		<description><![CDATA[I came across an interesting quote on experience which made me think about those that disclose how long they&#8217;ve worked in a field (myself included of course). The context in which I&#8217;m thinking about this is a bit different than the author which was &#8216;the trades&#8217; and apprenticeship, but, food for thought nonetheless.  
&#8220;When [...]]]></description>
			<content:encoded><![CDATA[<p>I came across an interesting quote on experience which made me think about those that disclose how long they&#8217;ve worked in a field (myself included of course). The context in which I&#8217;m thinking about this is a bit different than the author which was &#8216;the trades&#8217; and apprenticeship, but, food for thought nonetheless. <img src='http://saltybeagle.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<blockquote><p>&#8220;When the Union Delegates called upon me to insist that none but men who had seven years&#8217; apprenticeship should be employed in the works, I told them that I preferred employing a man who had acquired the requisite mechanical skill in two years rather than another who was so stupid as to require seven years&#8217; teaching. The delegates regarded this statement as preposterous and heretical. In fact, it was high treason. But in the long run we carried our point.&#8221;</p></blockquote>
<p><a href="http://www.gutenberg.org/cache/epub/476/pg476.html">James Nasmyth: Engineer; an autobiography by James Nasmyth</a></p>
]]></content:encoded>
			<wfw:commentRss>http://saltybeagle.com/2010/02/on-experience/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A New PEAR Channel Frontend</title>
		<link>http://saltybeagle.com/2009/12/a-new-pear-channel-frontend/</link>
		<comments>http://saltybeagle.com/2009/12/a-new-pear-channel-frontend/#comments</comments>
		<pubDate>Tue, 22 Dec 2009 03:48:34 +0000</pubDate>
		<dc:creator>bbieber</dc:creator>
				<category><![CDATA[PEAR]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[pear2]]></category>

		<guid isPermaLink="false">http://saltybeagle.com/?p=182</guid>
		<description><![CDATA[It seems like PEAR2 has been brewing forever. I mean, we&#8217;ve been talking about PEAR2, namespace usage, and a brand new installer for a long time. But, now that PHP 5.3.0 is a reality and 5.3.1 is so close out the door &#8211; things are actually taking shape and getting to the point where not [...]]]></description>
			<content:encoded><![CDATA[<p>It seems like PEAR2 has been brewing forever. I mean, we&#8217;ve been talking about PEAR2, namespace usage, and a brand new installer for a long time. But, now that PHP 5.3.0 is a reality and 5.3.1 is <del datetime="2009-12-22T03:35:44+00:00">so close</del> out the door &#8211; things are actually taking shape and getting to the point where not only can we show you what we&#8217;ve been working on, but you can actually try things out for yourself.</p>
<p>What I&#8217;ve been working on the past couple of <del datetime="2009-12-22T03:35:44+00:00">days</del> months is a new PEAR channel frontend; the basis for the PEAR2 website. I wanted a simple PEAR channel frontend, that just looks at the local filesystem for all the channel info, and would display the basics: what packages are available, categories, releases etc. Basically, just a simple web view that would not need a database backend, or any complex installation.</p>
<p>It would be simple to create a standalone library that just parsed xml files and read out the data, but having each underlying piece a reusable component would be very beneficial.</p>
<ul>
<li>a simpler XML library for parsing files</li>
<li>an HTTP request library</li>
<li>a library to talk to PEAR channels</li>
<li>a simple templating system with php as the template language</li>
<li>html/css templates to present the content in</li>
</ul>
<p>So when you consider the all the pieces that are required, this is quite an undertaking.</p>
<p>Implementation:</p>
<p>Ordinarily the pear installer, or pyrus, communicates to a PEAR channel through the xml files that describe the packages available. What we&#8217;ve done, is used the installer as the backbone for channel discovery and information retrieval, and we&#8217;ve told it to look at the local filesystem instead of at a remote channel. This is done using a &#8220;filesystem&#8221; adapter for the <a href="http://pear2.php.net/PEAR2_HTTP_Request">PEAR2_HTTP_Request</a> package, which turns your local filesystem into a fake Internet. This is a fairly simple concept, that makes for a very simple PEAR channel frontend.</p>
<p>So, if you couple that model for data retrieval with a completely <a href="http://pear2.php.net/PEAR2_Templates_Savant">new PHP 5.3+ template system</a> with roots in Savant, we&#8217;ve got a really nifty base for building pear channel websites.</p>
<p>So! The package is in the pear2 sandbox and is titled <a href="http://pear2.php.net/PEAR2_SimpleChannelFrontend">PEAR2_SimpleChannelFrontend</a> as a companion to the <a href="http://pear2.php.net/PEAR2_SimpleChannelServer">PEAR2_SimpleChannelServer</a> which makes channel creation (with categories!) a breeze.</p>
<p>I&#8217;ve added it to my <a href="http://pear.saltybeagle.com/">local PEAR channel</a> by simply dropping the .phar file in the web root as index.php, add a small .htaccess file and that&#8217;s all that is needed. Check it out at <a href="http://pear.saltybeagle.com/">http://pear.saltybeagle.com/</a></p>
<p>As of tonight you can try it out for yourself by grabbing the <a href="http://pear2.php.net/get/PEAR2_SimpleChannelFrontend-0.1.0.phar">PEAR2_SimpleChannelFrontend-0.1.0.phar off of the PEAR2 website</a> and saving it along side your channel.xml and browsing to the phar file. I named it <var>index.php</var> for simplicity, and placed the following <var>.htaccess</var> file in place:</p>
<p><code>&lt;IfModule mod_rewrite.c&gt;<br />
RewriteEngine On<br />
RewriteBase /<br />
RewriteCond %{REQUEST_FILENAME} !-f<br />
RewriteCond %{REQUEST_FILENAME} !-d<br />
RewriteRule . index.php [L]<br />
&lt;/IfModule&gt;</code></p>
<p>Also, my thanks to <a href="http://gluegadget.com/blog/">Amir</a> for the coding help with <a href="http://pear2.php.net/PEAR2_SimpleChannelFrontend">SimpleChannelFrontend</a>, and the <a href="http://pear2.php.net/">pear2 site</a>. Things are moving along quite nicely.</p>
]]></content:encoded>
			<wfw:commentRss>http://saltybeagle.com/2009/12/a-new-pear-channel-frontend/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP Template Wars &#8211; A New Hope</title>
		<link>http://saltybeagle.com/2009/11/php-template-wars/</link>
		<comments>http://saltybeagle.com/2009/11/php-template-wars/#comments</comments>
		<pubDate>Fri, 06 Nov 2009 18:07:51 +0000</pubDate>
		<dc:creator>bbieber</dc:creator>
				<category><![CDATA[PEAR]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://saltybeagle.com/?p=186</guid>
		<description><![CDATA[Templating has been a recent issue up for debate in the PHP community, which is why I think it&#8217;s time to share what we&#8217;ve been up to with PEAR2 and Savant.
Savant is the foundation of the top PHP-based templating systems. In particular Solar View and Zend Framework&#8217;s View have roots in Savant and the great [...]]]></description>
			<content:encoded><![CDATA[<p>Templating has been a recent issue up for debate in the PHP community, which is why I think it&#8217;s time to share what we&#8217;ve been up to with PEAR2 and Savant.</p>
<p>Savant is the foundation of the top PHP-based templating systems. In particular Solar View and Zend Framework&#8217;s View have roots in Savant and the great work of Paul M. Jones. Each of these are very successful templating systems, and they make for a proven foundation for templating and ease of use. So now that PEAR2 is gathering energy, and needs a strong templating system, Savant makes a good fit. But, with PEAR2, we have a lot of new features available with PHP 5.3, which means we can re-think some aspects of templating.</p>
<p>First, I want to talk about template/view annoyances, then about how Savant has solved these issues in <strong>PEAR2_Templates_Savant</strong>.</p>
<h2><strong>Annoyances:</strong></h2>
<p><strong>1.) assigning data to the view</strong></p>
<p>This is a problematic issue. Either you manually assign all of the data to the view, or choose individual pieces of data. Assigning individual pieces takes too much time and can lead to missing data in the view if new fields are added to a model and not assigned, or mismatched variable names. Data assignment is also difficult when the model uses magic methods or iterators for data retrieval.</p>
<p><strong>2.) view selection</strong></p>
<p>If you&#8217;re a sane person you probably have a pretty strong map between your models and views. Meaning, for the BaseballPlayer model, you&#8217;re probably going to be using a BaseballPlayer[.tpl.php|.phtml] file. This model to view mapping should be logical and happen automatically.</p>
<p><strong>3.) isolation/sandboxing view data and template system<br />
</strong></p>
<p>The data used within the view should be isolated from the templating system. The contextual data should not interfere with the templating system and vice-versa. Protected/private methods in the template system should not be callable within the template.</p>
<p><strong>4.) view system setup</strong></p>
<p>You should only have to set up your view system once per application, not per template.</p>
<p><strong>5.) controller and flow logic in templates</strong></p>
<p>It&#8217;s easier to maintain one template file for the site rather than separate sandwich templates pieces for the header and footer. The sandwich template method can lead to out of sync HTML tags, but it is difficult to use one template for the site without introducing controller logic into this view.</p>
<p><strong>6.) data escaping</strong></p>
<p>Sometimes this is necessary, sometimes it isn&#8217;t. Should the developer handle this or the designer? Does it matter? Flexibility should be allowed for either, and if we want data to be escaped it should happen automatically while still allowing access to un-escaped data if I know what I&#8217;m doing.</p>
<h2><strong>PEAR2_Templates_Savant &#8211; A New Hope</strong></h2>
<p>First, I want to talk about the concept of partial templates. If you&#8217;ve used the partial helper within Zend View or Solar View, you know how beneficial this can be. Partials are a simple way to break up large templates into smaller templates. This makes for a very object-oriented template usage, which matches many MVC applications, and also aids in data isolation and limiting variable scope. <strong>This is the default method of rendering in PEAR2_Templates_Savant. </strong></p>
<p>Now the pitch.</p>
<p><strong>How PEAR2_Templates_Savant fixes the annoyances above</strong></p>
<p><strong>1.) assigning data to the view</strong></p>
<p>Because partials are the default method of rendering, there is no more assignment of data to the view. The data is made available to the template as the $context variable. This means instead of $view-&gt;name = $baseballplayer-&gt;name; or using $view-&gt;assign($baseballplayer);, you&#8217;ll simply use $view-&gt;render($baseballplayer, [tpl]);</p>
<p>This means that there is never any confusion about what data was assigned, or how to access the data from within the template. It also means that views have access to the public methods of the object rendered, which helps for objects which use getters and setters, or magic methods for data retrieval.</p>
<p>For those of you used to the old way, you can still pass a stdClass object with your own custom assignments.</p>
<p><strong>2.) view selection</strong></p>
<p>The render method accepts two parameters, contextual data and the template you wish to use. BOTH are optional arguments, but the magic happens when the template name is null.</p>
<p>Back to our &#8217;sane&#8217; developers who know that a BaseballPlayer object should always be rendered using the BaseballPlayer.tpl.php template. They have taken the time to build a good structure for the models in their applications, and by doing so, their life is made easier by eliminating what template to select. PEAR2_Templates_Savant uses a mapper which will map objects to templates (configurable of course). The default mapper simply maps class names to template files.</p>
<p>BaseballPlayer =&gt; BaseballPlayer.tpl.php<br />
BaseballCoach =&gt; BaseballCoach.tpl.php<br />
BaseballTeam_Phillies =&gt; BaseballTeam/Phillies.tpl.php<br />
BaseballTeam\Phillies =&gt; BaseballTeam/Phillies.tpl.php</p>
<p>You can assign simple replacements to apply to the class name and the file extension is also customizable for the .phtml fans out there. Of course you can provide your own template mapper which implements the MapperInterface.</p>
<p><a href="http://svn.php.net/viewvc/pear2/Templates_Savant/trunk/src/Templates/Savant/ClassToTemplateMapper.php?view=markup">ClassToTemplateMapper.php</a></p>
<p><strong>3.) isolation/sandboxing view data and template system</strong></p>
<p>This is where PHP 5.3 shines. Closures allow us to create little sandboxes for data to play in. Feel free to pass in a __config value to the view, knowing that the data won&#8217;t interfere with the view object (who would do this?) &#8211; without using nasty global variables. Also, your template system can have private/protected methods which aren&#8217;t available inside the view which makes the class easy to extend if you need to customize. Side effect, we don&#8217;t use $this-&gt; to get to the data, we use the $context to access data given to the view, and $savant to render further partial templates. Win for data and template separation!</p>
<p><strong>4.) view system setup</strong></p>
<p>Because partials are used as the default rendering, the view setup happens once and is used through the entire application.</p>
<p><strong>5.) controller and flow logic in templates</strong></p>
<p>When used in an application which maps objects to templates, this makes it very easy to pass off data to other templates without knowing the type of data. This means there&#8217;s no more controller-ish logic within a template such as:<br />
<code><br />
&lt;html&gt;<br />
…<br />
&lt;body&gt;<br />
&lt;?php<br />
if ($this-&gt;mydata instanceof BaseballPlayer) echo $this-&gt;partial('baseballplayer.tpl.php', array('name', $this-&gt;mydata-&gt;name));<br />
elseif ($this-&gt;mydata instanceof BaseballTeam) echo $this-&gt;partial('baseballteam.tpl.php', array('name', $this-&gt;mydata-&gt;name));<br />
?&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
</code><br />
Now, you can create one outer page template, and hand off data to another template without having to know what type of data is:<br />
<code><br />
&lt;html&gt;<br />
…<br />
&lt;body&gt;<br />
&lt;?php echo $savant-&gt;render($context-&gt;mydata); ?&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
</code><br />
Objects are mapped to templates, arrays are looped over, strings, ints, doubles etc are returned after they&#8217;ve passed through any escape methods you&#8217;ve assigned.</p>
<p><strong>6.) data escaping</strong></p>
<p>If the developer assigns methods to escape data, this happens automatically. This is handled through an ObjectProxy which handles all requests to the inner object. If you need direct/raw access to unescaped data, you can use $context-&gt;__raw(&#8217;fieldName&#8217;);</p>
<p>This makes escaping easy, automatic, and avoidable if you know what you&#8217;re doing.</p>
<h2><strong>Teh End.</strong></h2>
<p>There are a few other nuances that I can share in another post, but those are the basics. Also, I want to really thank Greg Beaver who contributed many of the ideas for the latest version of Savant – including the title of this post, so thanks Greg!</p>
<p>So feel free to try it out and give some feedback. And so far things are shaping up nicely in the PEAR2 universe &#8211; definitely something to keep an eye on in my <em>biased</em> opinion.</p>
<p><a href="http://svn.php.net/viewvc/pear2/Templates_Savant/trunk/examples/baseball/">A full example is available in the repository.</a></p>
<p>The development version of PEAR2_Templates_Savant is available through the svn repository, or installable with pyrus.</p>
<p>SVN:<br />
svn checkout http://svn.php.net/repository/pear2/Templates_Savant/trunk</p>
<p>Browse online:<a href="http://svn.php.net/viewvc/pear2/Templates_Savant/trunk/src/Templates/Savant/"><br />
http://svn.php.net/viewvc/pear2/Templates_Savant/trunk/src/Templates/Savant/</a></p>
<p>On the (under construction) PEAR2 website:<br />
<a href="http://pear2.php.net/?view=package&amp;package=PEAR2_Templates_Savant">PEAR2_Templates_Savant</a></p>
<p>Install with pyrus<br />
php pyrus.phar /putithere install PEAR2_Templates_Savant-0.1.0</p>
]]></content:encoded>
			<wfw:commentRss>http://saltybeagle.com/2009/11/php-template-wars/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Autoloader That Installs PEAR Packages</title>
		<link>http://saltybeagle.com/2009/11/autoloader-that-installs-pear-packages/</link>
		<comments>http://saltybeagle.com/2009/11/autoloader-that-installs-pear-packages/#comments</comments>
		<pubDate>Wed, 04 Nov 2009 12:23:08 +0000</pubDate>
		<dc:creator>bbieber</dc:creator>
				<category><![CDATA[Fun]]></category>
		<category><![CDATA[PEAR]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[pear2]]></category>
		<category><![CDATA[pyrus]]></category>

		<guid isPermaLink="false">http://saltybeagle.com/?p=191</guid>
		<description><![CDATA[I had a thought the other night about an autoloader that could install pear packages automatically for you.
Security concerns aside, I thought this would be a fun/juvenile coding experiment, and something that could showcase how easy the pyrus api is for installing PEAR packages. So here it is, pushed out on github &#8211; http://github.com/saltybeagle/AutoloadPackage/blob/master/AutoloadPackage.php
Only 56 [...]]]></description>
			<content:encoded><![CDATA[<p>I had a thought the other night about an autoloader that could <strong>install pear packages automatically</strong> for you.</p>
<p>Security concerns aside, I thought this would be a fun/juvenile coding experiment, and something that could showcase <strong>how easy the pyrus api is</strong> for installing PEAR packages. So here it is, pushed out on github &#8211; <a href="http://github.com/saltybeagle/AutoloadPackage/blob/master/AutoloadPackage.php">http://github.com/saltybeagle/AutoloadPackage/blob/master/AutoloadPackage.php</a></p>
<p>Only <strong>56 lines</strong> of code to have an autoloader that will install a PEAR2 package. Now if that&#8217;s not fun, I don&#8217;t know what is.</p>
<p> <img src='http://saltybeagle.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Oh, and you should really check out <strong>Pyrus, the new PEAR installer</strong>. Get it over at the <a href="http://pear2.php.net/">pear2 website</a> (under construction).</p>
]]></content:encoded>
			<wfw:commentRss>http://saltybeagle.com/2009/11/autoloader-that-installs-pear-packages/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Blog updated.</title>
		<link>http://saltybeagle.com/2009/10/blog-updated/</link>
		<comments>http://saltybeagle.com/2009/10/blog-updated/#comments</comments>
		<pubDate>Mon, 26 Oct 2009 00:57:19 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://saltybeagle.com/?p=180</guid>
		<description><![CDATA[Just converted the old blog to Wordpress, and now you can call me a hypocrite.
]]></description>
			<content:encoded><![CDATA[<p>Just converted the old blog to Wordpress, and now you can call me <a href="http://saltybeagle.com/2006/05/i-refuse-to-be-a-wp-sellout/">a hypocrite.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://saltybeagle.com/2009/10/blog-updated/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Cross-Origin Resource Sharing Demo</title>
		<link>http://saltybeagle.com/2009/09/cross-origin-resource-sharing-demo/</link>
		<comments>http://saltybeagle.com/2009/09/cross-origin-resource-sharing-demo/#comments</comments>
		<pubDate>Fri, 11 Sep 2009 12:36:18 +0000</pubDate>
		<dc:creator>bbieber</dc:creator>
				<category><![CDATA[Fun]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[The Problem
Many times I&#8217;ve been frustrated with the same-origin policy for AJAX requests. I have legitimate content on another domain that I would like to retrieve and display on another page using AJAX.
As an example, department.unl.edu would like to display a list of upcoming events from events.unl.edu/department/.
Historically you would have to serve the content out [...]]]></description>
			<content:encoded><![CDATA[<h3>The Problem</h3>
<p>Many times I&#8217;ve been frustrated with the same-origin policy for AJAX requests. I have legitimate content on another domain that I would like to retrieve and display on another page using AJAX.</p>
<p>As an example, department.unl.edu would like to display a list of upcoming events from events.unl.edu/department/.</p>
<p>Historically you would have to serve the content out as JSON, or use a proxy to convert the content to json, and reference the resource using a script tag which gets around the same-origin policy &#8211; and for years, this has worked well.</p>
<p>Unfortunately this requires you to either send content out in multiple formats (html + json for these requests), or use html and send all requests for cross-site resources through a proxy script, which adds a translation delay.</p>
<p>Serving the content out in two formats requires more work to maintain, and if the content output is cached, requires more resources to generate. If you use a proxy to translate the content, you have an open proxy unless you lock down what resources the proxy script can retrieve and translate.</p>
<h3>CORS is the Answer</h3>
<p>The answer is in the <a href="http://www.w3.org/TR/access-control/">Cross-Origin Resource Sharing specification,</a> which is supported in Firefox 3.5+, Safari 4+ and albeit using agent specific methods, supported in Internet Explorer 8+.</p>
<p>The specification allows resources on other domains to be retrieved through AJAX methods, if the resource announces support as a Cross-Origin Resource. This is done through special headers which identify to the user-agent which alternate origin domains can request the resource and what methods are supported.</p>
<h3>Demo</h3>
<p>For the demo, the html and js is served off of this domain, and the CORS resource is located on ucommbieber.unl.edu.</p>
<p><a href="http://saltybeagle.com/cors/">Click here to go to the demo.</a></p>
<p>This page demonstrates both GET and POST methods, with custom callbacks, using an api just like the jQuery <a href="http://docs.jquery.com/Ajax/jQuery.get">get</a> and <a href="http://docs.jquery.com/Ajax/jQuery.post">post</a> methods.</p>
<p>The HTML:</p>
<pre><code>&lt;html&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;cors.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;

function testGet()
{
    getCORS('http://ucommbieber.unl.edu/CORS/cors.php', null, function(data){alert(data);});
}
function testPost(form)
{
    postCORS('http://ucommbieber.unl.edu/CORS/cors.php', {name: form.name.value}, function(data){alert(data);});
}
&lt;/script&gt;
&lt;body&gt;
&lt;h1&gt;CORS Examples&lt;/h1&gt;

&lt;p&gt;Test GET
    This page retrieves content from another server, using CORS&lt;br /&gt;
    &lt;a href=&quot;#&quot; onclick=&quot;testGet(); return false;&quot;&gt;Get content from another server&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Test POST:
    &lt;form action=&quot;#&quot; onsubmit=&quot;testPost(this); return false&quot;&gt;

        Name: &lt;input type=&quot;text&quot; name=&quot;name&quot; /&gt;
        &lt;input type=&quot;submit&quot; /&gt;
    &lt;/form&gt;

&lt;/p&gt;
&lt;a href=&quot;http://saltybeagle.com/2009/09/cross-origin-resource-sharing-demo/&quot;&gt;More info&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>The javascript:</p>
<pre><code>/**
 * This is for Cross-site Origin Resource Sharing (CORS) requests.
 *
 * Additionally the script will fail-over to a proxy if you have one set up.
 *
 * @param string   url      the url to retrieve
 * @param mixed    data     data to send along with the get request [optional]
 * @param function callback function to call on successful result [optional]
 * @param string   type     the type of data to be returned [optional]
 */
function getCORS(url, data, callback, type) {
    try {
        // Try using jQuery to get data
        jQuery.get(url, data, callback, type);
    } catch(e) {
        // jQuery get() failed, try IE8 CORS, or use the proxy
        if (jQuery.browser.msie &amp;&amp; window.XDomainRequest) {
            // Use Microsoft XDR
            var xdr = new XDomainRequest();
            xdr.open("get", url);
            xdr.onload = function() {
                callback(this.responseText, 'success');
            };
            xdr.send();
        } else {
            try {
                // Ancient browser, use our proxy
                var mycallback = function() {
                    var textstatus = 'error';
                    var data = 'error';
                    if ((this.readyState == 4)
                        &amp;&amp; (this.status == '200')) {
                        textstatus = 'success';
                        data = this.responseText;
                    }
                    callback(data, textstatus);
                };
                // proxy_xmlhttp is a separate script you'll have to set up
                request = new proxy_xmlhttp();
                request.open('GET', url, true);
                request.onreadystatechange = mycallback;
                request.send();
            } catch(e) {
                // Could not fetch using the proxy
            }
        }
    }
}

/**
 * This method is for Cross-site Origin Resource Sharing (CORS) POSTs
 *
 * @param string   url      the url to post to
 * @param mixed    data     additional data to send [optional]
 * @param function callback a function to call on success [optional]
 * @param string   type     the type of data to be returned [optional]
 */
function postCORS(url, data, callback, type)
{
    try {
        // Try using jQuery to POST
        jQuery.post(url, data, callback, type);
    } catch(e) {
        // jQuery POST failed
        var params = '';
        for (key in data) {
            params = params+'&amp;'+key+'='+data[key];
        }
        // Try XDR, or use the proxy
        if (jQuery.browser.msie &amp;&amp; window.XDomainRequest) {
            // Use XDR
            var xdr = new XDomainRequest();
            xdr.open("post", url);
            xdr.send(params);
            xdr.onload = function() {
                callback(xdr.responseText, 'success');
            };
        } else {
            try {
                // Use the proxy to post the data.
                request = new proxy_xmlhttp();
                request.open('POST', url, true);
                request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                request.send(params);
            } catch(e) {
                // could not post using the proxy
            }
        }
    }
}</code></pre>
<p>The PHP code responding from the server looks like this:<br />
<code><span style="color: #000000"><br />
<span style="color: #0000BB">&lt;?php</p>
<p></span><span style="color: #FF8000">//&nbsp;Specify&nbsp;domains&nbsp;from&nbsp;which&nbsp;requests&nbsp;are&nbsp;allowed<br /></span><span style="color: #0000BB">header</span><span style="color: #007700">(</span><span style="color: #DD0000">'Access-Control-Allow-Origin:&nbsp;*'</span><span style="color: #007700">);</p>
<p></span><span style="color: #FF8000">//&nbsp;Specify&nbsp;which&nbsp;request&nbsp;methods&nbsp;are&nbsp;allowed<br /></span><span style="color: #0000BB">header</span><span style="color: #007700">(</span><span style="color: #DD0000">'Access-Control-Allow-Methods:&nbsp;GET,&nbsp;POST,&nbsp;OPTIONS'</span><span style="color: #007700">);</p>
<p></span><span style="color: #FF8000">//&nbsp;Additional&nbsp;headers&nbsp;which&nbsp;may&nbsp;be&nbsp;sent&nbsp;along&nbsp;with&nbsp;the&nbsp;CORS&nbsp;request<br />//&nbsp;The&nbsp;X-Requested-With&nbsp;header&nbsp;allows&nbsp;jQuery&nbsp;requests&nbsp;to&nbsp;go&nbsp;through<br /></span><span style="color: #0000BB">header</span><span style="color: #007700">(</span><span style="color: #DD0000">'Access-Control-Allow-Headers:&nbsp;X-Requested-With'</span><span style="color: #007700">);</p>
<p></span><span style="color: #FF8000">//&nbsp;Set&nbsp;the&nbsp;age&nbsp;to&nbsp;1&nbsp;day&nbsp;to&nbsp;improve&nbsp;speed/caching.<br /></span><span style="color: #0000BB">header</span><span style="color: #007700">(</span><span style="color: #DD0000">'Access-Control-Max-Age:&nbsp;86400'</span><span style="color: #007700">);</p>
<p></span><span style="color: #FF8000">//&nbsp;Exit&nbsp;early&nbsp;so&nbsp;the&nbsp;page&nbsp;isn't&nbsp;fully&nbsp;loaded&nbsp;for&nbsp;options&nbsp;requests<br /></span><span style="color: #007700">if&nbsp;(</span><span style="color: #0000BB">strtolower</span><span style="color: #007700">(</span><span style="color: #0000BB">$_SERVER</span><span style="color: #007700">[</span><span style="color: #DD0000">'REQUEST_METHOD'</span><span style="color: #007700">])&nbsp;==&nbsp;</span><span style="color: #DD0000">'options'</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;exit();<br />}</p>
<p></span><span style="color: #FF8000">//&nbsp;If&nbsp;raw&nbsp;post&nbsp;data,&nbsp;this&nbsp;could&nbsp;be&nbsp;from&nbsp;IE8&nbsp;XDomainRequest<br />//&nbsp;Only&nbsp;use&nbsp;this&nbsp;if&nbsp;you&nbsp;want&nbsp;to&nbsp;populate&nbsp;$_POST&nbsp;in&nbsp;all&nbsp;instances<br /></span><span style="color: #007700">if&nbsp;(isset(</span><span style="color: #0000BB">$HTTP_RAW_POST_DATA</span><span style="color: #007700">))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">'&amp;'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$HTTP_RAW_POST_DATA</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;foreach&nbsp;(</span><span style="color: #0000BB">$data&nbsp;</span><span style="color: #007700">as&nbsp;</span><span style="color: #0000BB">$val</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!empty(</span><span style="color: #0000BB">$val</span><span style="color: #007700">))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list(</span><span style="color: #0000BB">$key</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$value</span><span style="color: #007700">)&nbsp;=&nbsp;</span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">'='</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$val</span><span style="color: #007700">);&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$_POST</span><span style="color: #007700">[</span><span style="color: #0000BB">$key</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">urldecode</span><span style="color: #007700">(</span><span style="color: #0000BB">$value</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</p>
<p>echo&nbsp;</span><span style="color: #DD0000">'Hello&nbsp;CORS,&nbsp;this&nbsp;is&nbsp;'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #007700">.&nbsp;</span><span style="color: #0000BB">$_SERVER</span><span style="color: #007700">[</span><span style="color: #DD0000">'SERVER_NAME'</span><span style="color: #007700">]&nbsp;.&nbsp;</span><span style="color: #0000BB">PHP_EOL<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #007700">.</span><span style="color: #DD0000">'You&nbsp;sent&nbsp;a&nbsp;'</span><span style="color: #007700">.</span><span style="color: #0000BB">$_SERVER</span><span style="color: #007700">[</span><span style="color: #DD0000">'REQUEST_METHOD'</span><span style="color: #007700">]&nbsp;.&nbsp;</span><span style="color: #DD0000">'&nbsp;request.'&nbsp;</span><span style="color: #007700">.&nbsp;</span><span style="color: #0000BB">PHP_EOL</span><span style="color: #007700">;</p>
<p>if&nbsp;(</span><span style="color: #0000BB">$_SERVER</span><span style="color: #007700">[</span><span style="color: #DD0000">'REQUEST_METHOD'</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">'POST'</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span style="color: #DD0000">'Your&nbsp;name&nbsp;is&nbsp;'&nbsp;</span><span style="color: #007700">.&nbsp;</span><span style="color: #0000BB">htmlentities</span><span style="color: #007700">(</span><span style="color: #0000BB">$_POST</span><span style="color: #007700">[</span><span style="color: #DD0000">'name'</span><span style="color: #007700">]);<br />}</span></p>
<p></span><br />
</code></p>
<p>Note the special headers that have to be returned to allow the request to complete:<br />
Access-Control-Allow-Origin: *<br />
Access-Control-Allow-Methods: GET, POST, OPTIONS<br />
Access-Control-Allow-Headers: X-Requested-With</p>
<p>The X-Requested-With allows us to use jQuery&#8217;s internal GET method which adds in the additional header.</p>
<p><a href="http://github.com/saltybeagle/cors">View all the code on GitHub.</a></p>
<p>Support for proxy fallback for older browsers is left up to you to code. The proxy requires you to set up a script which translates a page into JSON and handles the response. There are many examples of how to do this.</p>
]]></content:encoded>
			<wfw:commentRss>http://saltybeagle.com/2009/09/cross-origin-resource-sharing-demo/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Apache Virtual Hosts On Mac OS X</title>
		<link>http://saltybeagle.com/2009/08/apache-virtual-hosts-on-mac-os-x/</link>
		<comments>http://saltybeagle.com/2009/08/apache-virtual-hosts-on-mac-os-x/#comments</comments>
		<pubDate>Sat, 01 Aug 2009 13:38:40 +0000</pubDate>
		<dc:creator>bbieber</dc:creator>
				<category><![CDATA[Web]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[I&#8217;ve said, if it takes you more than 10 minutes to find an answer &#8211; it&#8217;s your responsibility to document it, and I&#8217;ve been slacking.
Today&#8217;s issue was enabling virtual hosts on Apache &#38; Mac OS X. Normally not a big deal, but I was stumped for a while by a simple error. Apache would not [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve said, if it takes you more than 10 minutes to find an answer &#8211; it&#8217;s your responsibility to document it, and I&#8217;ve been slacking.</p>
<p>Today&#8217;s issue was enabling virtual hosts on Apache &amp; Mac OS X. Normally not a big deal, but I was stumped for a while by a simple error. Apache would not start, and apachectl was giving me no error.</p>
<p>The workaround to find out what the heck was going on was just to execute httpd directly, which showed me what the problem was:</p>
<pre>Syntax error on line 37 of /private/etc/apache2/extra/httpd-vhosts.conf:
CustomLog takes two or three arguments, a file name, a custom log format string or format name, and an optional "env=" clause (see docs)</pre>
<p>Well the issue is, the default vhosts file on Mac OS X has a misplaced double quote:</p>
<pre>CustomLog "/private/var/log/apache2/dummy-host2.example.com-access_log common"</pre>
<p>Which should be:</p>
<pre>CustomLog "/private/var/log/apache2/dummy-host2.example.com-access_log" common</pre>
<p>of course.</p>
<p>So, the simple instructions:<br />
edit /etc/apache2/httpd.conf and uncomment the line that says:<br />
Include /private/etc/apache2/extra/httpd-vhosts.conf</p>
<p>Now add your virtual hosts by editing the<br />
/etc/apache2/extra/httpd-vhosts.conf file, make sure you place that double quote in the correct place if you&#8217;re defining custom log files.</p>
<p>Then run sudo apachectl graceful</p>
]]></content:encoded>
			<wfw:commentRss>http://saltybeagle.com/2009/08/apache-virtual-hosts-on-mac-os-x/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Remote PEAR Package Releases</title>
		<link>http://saltybeagle.com/2009/06/remote-pear-package-releases/</link>
		<comments>http://saltybeagle.com/2009/06/remote-pear-package-releases/#comments</comments>
		<pubDate>Thu, 18 Jun 2009 03:51:43 +0000</pubDate>
		<dc:creator>bbieber</dc:creator>
				<category><![CDATA[PEAR]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Sune Jensen sent me a note about a couple minor additions to the remote PEAR package releaser script, which would allow it to automatically create the package and maintainers if it did not exist on the PEAR channel managed with Chiara_PEAR_Server. This is now possible since Christian Weiske added the options to the Chiara_PEAR_Server admin [...]]]></description>
			<content:encoded><![CDATA[<p>Sune Jensen sent me a note about a couple minor additions to the remote PEAR package releaser script, which would allow it to automatically create the package and maintainers if it did not exist on the PEAR channel managed with Chiara_PEAR_Server. This is now possible since Christian Weiske added the options to the Chiara_PEAR_Server admin interface a while ago.</p>
<p>I&#8217;ve added in the patch from Sune, and released it as Salty_PEAR_Server_RemoteReleaseDeployer-0.1.0.</p>
<p>You can grab <a href="http://pear.saltybeagle.com/index.php?package=Salty_PEAR_Server_RemoteReleaseDeployer&amp;release=0.1.0&amp;downloads">the release here,</a> or install it with</p>
<pre>pear channel-discover pear.saltybeagle.com
pear install salty/Salty_PEAR_Server_RemoteReleaseDeployer-0.1.0</pre>
<p>Thanks Sune!</p>
]]></content:encoded>
			<wfw:commentRss>http://saltybeagle.com/2009/06/remote-pear-package-releases/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lincoln, NE PHP Meetup</title>
		<link>http://saltybeagle.com/2009/01/lincoln-ne-php-meetup/</link>
		<comments>http://saltybeagle.com/2009/01/lincoln-ne-php-meetup/#comments</comments>
		<pubDate>Tue, 06 Jan 2009 18:06:53 +0000</pubDate>
		<dc:creator>bbieber</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Tonight, Tuesday January 6th 2009 at 6:30 PM in Yia Yia&#8217;s. I&#8217;ll try and wear my bright GREEN PHP Appalachia shirt so I should be easily recognizable.
Meetup if you&#8217;re interested.
]]></description>
			<content:encoded><![CDATA[<p>Tonight, Tuesday January 6th 2009 at 6:30 PM in Yia Yia&#8217;s. I&#8217;ll try and wear my bright GREEN PHP Appalachia shirt so I should be easily recognizable.</p>
<p>Meetup if you&#8217;re interested.</p>
]]></content:encoded>
			<wfw:commentRss>http://saltybeagle.com/2009/01/lincoln-ne-php-meetup/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
