<?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>ybits</title>
	<atom:link href="http://ybits.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://ybits.net</link>
	<description>(why bits? because.)</description>
	<lastBuildDate>Thu, 12 Aug 2010 01:57:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>TextMate Bundles and Regular Expressions</title>
		<link>http://ybits.net/2009/11/textmate-bundles-and-regular-expressions/</link>
		<comments>http://ybits.net/2009/11/textmate-bundles-and-regular-expressions/#comments</comments>
		<pubDate>Fri, 27 Nov 2009 22:27:24 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Newness]]></category>
		<category><![CDATA[Regular Expressions]]></category>
		<category><![CDATA[TextMate]]></category>

		<guid isPermaLink="false">http://ybits.net/?p=482</guid>
		<description><![CDATA[One of the greatest things about TextMate is its powerful scripting engine, which includes native (non-string) support for regular expressions as well as support for shell interpolations. The ability to understand and use these mechanisms in snippets and commands makes the TextMate experience second to none. In this post, I concentrate on regular expressions. In [...]]]></description>
			<content:encoded><![CDATA[<p><img class="logo" src="/img/textmate.png" alt="TextMate Logo">One of the greatest things about <a href="http://macromates.com">TextMate</a> is its powerful scripting engine, which includes native (non-string) support for regular expressions as well as support for shell interpolations. The ability to understand and use these mechanisms in snippets and commands makes the TextMate experience second to none. </p>
<p>In this post, I concentrate on regular expressions. In particular: captured groups, backreferences and conditionals &#8212; what they are, how to use them, and why. I also assume you have a basic understanding of what regex is and what the syntax looks like. If you don&#8217;t, <a href="http://manual.macromates.com/en/regular_expressions">read up</a> first.</p>
<h4>What are captured groups, backreferences and conditionals?</h4>
<ul>
<li>
<p>A <span class="keyword">captured group</span> is a subpattern in an expression that can be evaluated and referenced later in the same expression. It is created by placing the pattern inside of parentheses.</p>
<p>
Captured groups are either implicitly enumerated or explicitly named. The enumeration is very simple; every opening paren begins a new enumeration. To figure out the number of a nested group, start from 1 and count each opening paren from left to right until you reach the group you&#8217;re looking for. That&#8217;s its number.</p>
<p> I deal only with enumerated groups in the following examples.
</p>
</li>
<li>
<p>
A <span class="keyword">backreference</span> is a section of an expression that references a preceding captured group.
</p>
</li>
<li>
<p>
A <span class="keyword">conditional</span> is a section of an expression that evaluates a pattern, and based on a match (or not) executes another portion of the expression.
</p>
</li>
</ul>
<h4>How do they work?</h4>
<p>The best way to get through this is by getting right into an actual example. The following is taken directly from the model snippet in the <a href="http://ybits.net/projects/zend-framework-textmate-bundle/">Zend Framework TextMate Bundle</a>.</p>
<pre class="brush:html">
class ${1:${TM_FILENAME/\.[^.]+$//}} extends ${2:Zend_Db_Table_Abstract}
{
	${3:protected \$_name = '${4:${1/^([A-Z])|(([a-z])([A-Z]))/(?1:\l$1)(?2:$3_\l$4)/g}}';}
	$0
}
</pre>
<p>The regex on line 1 isn&#8217;t really relevant here. What that does is take the file name and remove the extension. What <em>is</em> relevant is on line 3. The code on line 3 uses captured groups, backreferences and conditionals to convert the camel-cased value of $1 on line 1 to underscore.</p>
<p>Specifically, this expression:<br />
=&gt; <code class="example">/^([A-Z])|(([a-z])([A-Z]))/(?1:\l$1)(?2:$3_\l$4)/</code></p>
<p>There are two rules to convert a string from camel case to underscore:</p>
<ol>
<li>
If the first letter is capital, change it to lowercase.
</li>
<li>
For every occurrence of a lowercase letter followed by a capital letter,  put an underscore between them and convert the capital to lowercase.
</li>
</ol>
<p>The example regex implements these rules, and converted to english, looks like this:</p>
<ul>
<li>Match if the first letter is capital. Store the expression as  group 1. <br />
=&gt; <code class="example">^([A-Z])</code></li>
<li>Match each combination of a lowercase letter followed by capital letter. Store the entire expression as  group 2, the lowercase expression as  group 3 and the capital expression as group 4. <br />
=&gt; <code class="example">(([a-z])([A-Z]))</code></li>
<li>If group 1 is a match, convert what was matched to lowercase. <br />
=&gt; <code class="example">(?1:\l$1)</code></li>
<li>If group 2 is a match, convert what was matched for group 4 to lowercase, and add an underscore between group 3 and group 4.<br />
=&gt; <code class="example">(?2:$3_\l$4)</code></li>
</ul>
<p>Referencing a captured group subpattern in a conditional for replacement is done using dollar sign-notation:<br />
=&gt; <code class="example">(?1:\l$1)</code></p>
<p>This says &#8220;if the first group matches, replace it with the lowercase value of the first group.&#8221;</p>
<p>Hopefully this is a gentle-enough introduction to these concepts, and demonstrates how they can be applied to TextMate scripting. There are hundreds of more complicated examples that live in your TextMate bundles. Look at them. Play with them. Learn from them.</p>
]]></content:encoded>
			<wfw:commentRss>http://ybits.net/2009/11/textmate-bundles-and-regular-expressions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Later Turns to Never</title>
		<link>http://ybits.net/2009/11/later-turns-to-never/</link>
		<comments>http://ybits.net/2009/11/later-turns-to-never/#comments</comments>
		<pubDate>Wed, 18 Nov 2009 21:14:59 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Newness]]></category>
		<category><![CDATA[Life]]></category>
		<category><![CDATA[Me]]></category>

		<guid isPermaLink="false">http://ybits.net/?p=453</guid>
		<description><![CDATA[In a little bit, later, tomorrow, next weekend, next summer, someday. I say these things a lot. I say them so much that I hear my two-year-old repeating them to me. And you know what? Sometimes the actions these put-offs describe never even happen. They just fall by the wayside and disappear from thought. Sometimes, [...]]]></description>
			<content:encoded><![CDATA[<p><img class="logo" alt="TextMate Logo" src="/img/me_av.png"/>In a little bit, later, tomorrow, next weekend, next summer, someday. I say these things a lot. I say them so much that I hear my two-year-old repeating them to me. And you know what? Sometimes the actions these put-offs describe never even happen. They just fall by the wayside and disappear from thought. Sometimes, later turns to never.</p>
<p>I want to stop this put-off routine completely, because I realize this: Life is unidirectional. Every moment that passes is gone. Forever.</p>
<p>You already knew that, I know. The thing is, so did I, long before I understood what it meant. Maybe I heard the words so much that they became cliché and lost their meaning. That&#8217;s a shame, because every time I heard them they were no less true than the time before. In fact, they were even more relevant.</p>
<p>Occasionally I look back at college, high school &#8212; hell, even grade school &#8212; and think about what I could have done differently. I think about the chances I had but didn&#8217;t take. Know what I&#8217;ve realized? That I <em>still</em> have the chance to do things differently. I&#8217;ve realized that through my actions today, I can change the past by changing the future. That someday, ten years from <em>now</em>, I&#8217;ll be looking back at <em>this</em> moment, and thinking about what could have been.</p>
<p>So screw that. Do I have opportunities that I&#8217;m afraid to take today? Absolutely. Is it possible that things will turn out badly? Yep. Ten years down the road am I gonna wonder what would have been if I&#8217;d just had the balls to go for it? NO. Because I am. Not in a little bit, not later, tomorrow, next weekend or next summer. Not someday.</p>
<p>Now.</p>
]]></content:encoded>
			<wfw:commentRss>http://ybits.net/2009/11/later-turns-to-never/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Zend Framework Bundle for TextMate</title>
		<link>http://ybits.net/2009/10/zend-framework-bundle-for-textmate/</link>
		<comments>http://ybits.net/2009/10/zend-framework-bundle-for-textmate/#comments</comments>
		<pubDate>Wed, 21 Oct 2009 02:36:03 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Newness]]></category>
		<category><![CDATA[Bundle]]></category>
		<category><![CDATA[TextMate]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://ybits.net/?p=201</guid>
		<description><![CDATA[I decided to start a Zend Framework bundle for TextMate. Then I decided to make a screencast of it. These are both firsts. Find out more about it.]]></description>
			<content:encoded><![CDATA[<p><img class="logo" src="/img/textmate.png" alt="TextMate Logo">I decided to start a Zend Framework bundle for TextMate. Then I decided to make a screencast of it. These are both firsts.</p>
<p><a href="http://ybits.net/zend-framework-textmate-bundle/">Find out more about it</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://ybits.net/2009/10/zend-framework-bundle-for-textmate/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Making jQuery Tablesorter Persistent</title>
		<link>http://ybits.net/2009/10/making-jquery-tablesorter-persistent/</link>
		<comments>http://ybits.net/2009/10/making-jquery-tablesorter-persistent/#comments</comments>
		<pubDate>Fri, 09 Oct 2009 18:45:30 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Newness]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Persistent Sort]]></category>
		<category><![CDATA[Tablesorter]]></category>

		<guid isPermaLink="false">http://ybits.net/?p=136</guid>
		<description><![CDATA[Every day I become more of a jQuery fan, and every day I find some other jQuery plugin that does something I need to do. Today, that something is table sorting. There is, of course, a jQuery plugin for this: Tablesorter. It&#8217;s simple. I like simple. Most Javascript-based table sorting solutions lack persistence, as they [...]]]></description>
			<content:encoded><![CDATA[<p><img class="logo" src="/img/jquery.png" alt="jQuery Logo">Every day I become more of a <a href="http://jquery.com">jQuery</a> fan, and every day I find some other <a href="http://plugins.jquery.com/">jQuery plugin</a> that does something I need to do. Today, that something is table sorting. There is, of course, a jQuery plugin for this: <a href="http://tablesorter.com/docs/">Tablesorter</a>. It&#8217;s simple. I like simple.</p>
<p>Most Javascript-based table sorting solutions lack persistence, as they probably should, and Tablesorter is no exception. There&#8217;s a common belief that all initial sorting should be done on the back-end, and that using Javascript to do it is bad practice. To those who harbor this belief, I say: Whatever! What I want is a drop-in solution to use with Tablesorter, which is itself a drop-in solution. I&#8217;m all about dropping things in, and don&#8217;t steal my thunder with your &#8220;ideals.&#8221;</p>
<p>I can hear you say: &#8220;Why not just use Ajax or pass some query parameters to your script that modify the &#8216;order by&#8217; section of your SQL statement?&#8221; Good question. Or is it? (Hint: no)</p>
<p>I won&#8217;t provide a long, convoluted explanation, but the simplest way to look at it is this: Data can be built in different places and in different ways. Maybe you can quickly write something that handles the first part of that, but how about the second? What if the data isn&#8217;t being built directly from a SQL statement. What if the columns you see (in the HTML table) don&#8217;t map to columns in a database? And so on. Work work work.</p>
<p>This is getting complicated. Let&#8217;s simplify. What did I say I wanted to do? Ah yes, persistently sort HTML tables. And that&#8217;s it. I don&#8217;t care where the data is coming from, I don&#8217;t care what it&#8217;s for, or what scripting language is used to create it. I just want to sort it, and have the sort stick.</p>
<p>And I&#8217;m willing to use cookies to do it. Let&#8217;s have a look at how, together.</p>
<p>Tablesorter has a widget interface and a parser interface. &#8220;Persistent sorting&#8221; doesn&#8217;t really sound like a widget <em>or</em> a parser, but it sounds <em>less</em> like a parser so let&#8217;s make a widget. To do this, we&#8217;ll also need the <a href="http://code.google.com/p/jquery-json/">jQuery JSON plugin</a> and the <a href="http://plugins.jquery.com/project/cookie">jQuery Cookie plugin</a>.</p>
<p>Once we have all that, we can make a widget that creates a cookie to keep a map of the last sort done to a table, using the form: {tableId: sortList}. The idea is to set the cookie sortList whenever a sort is done on a table, and check the cookie for an existing sortList every time a page with the table loads. If a sortList exists for the table, sort with it, which will also then save it to the cookie, and so on.</p>
<p>Lucky for us, Tablesorter&#8217;s widget interface fires off the function <code>format(table)</code> every time the sorter is initialized, and once after every sort, which is exactly what we need. The code:</p>
<pre class="brush:javascript" >
$(document).ready(function() {
  $.tablesorter.addWidget({
    // give the widget an id
    id: "sortPersist",
    // format is called when the on init and when a sorting has finished
    format: function(table) {

      // Cookie info
      var cookieName = 'MY_SORT_COOKIE';
      var cookie = $.cookie(cookieName);
      var options = {path: '/'};

      var data = {};
      var sortList = table.config.sortList;
      var tableId = $(table).attr('id');
      var cookieExists = (typeof(cookie) != "undefined" &#038;&#038; cookie != null);

      // If the existing sortList isn't empty, set it into the cookie and get out
      if (sortList.length > 0) {
        if (cookieExists) {
          data = $.evalJSON(cookie);
        }
        data[tableId] = sortList;
        $.cookie(cookieName, $.toJSON(data), options);
      }

      // Otherwise...
      else {
        if (cookieExists) { 

          // Get the cookie data
          var data = $.evalJSON($.cookie(cookieName));

          // If it exists
          if (typeof(data[tableId]) != "undefined" &#038;&#038; data[tableId] != null) {

            // Get the list
            sortList = data[tableId];

            // And finally, if the list is NOT empty, trigger the sort with the new list
            if (sortList.length > 0) {
              $(table).trigger("sorton", [sortList]);
            }
          }
        }
      }
    }
  });
  $("#maintable").tablesorter({widgets: ['sortPersist']});
});
</pre>
<p>And there you have it, a Tablesorter widget that implements a persistable sort using cookies. Here&#8217;s an example of it <a href="/scripts/persist">in action</a>. Sort on a column, then refresh to page to see that the column is still being sorted.</p>
]]></content:encoded>
			<wfw:commentRss>http://ybits.net/2009/10/making-jquery-tablesorter-persistent/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Extending Zend_Db_Table_Row</title>
		<link>http://ybits.net/2009/09/extending-zend_db_table_row/</link>
		<comments>http://ybits.net/2009/09/extending-zend_db_table_row/#comments</comments>
		<pubDate>Thu, 24 Sep 2009 01:01:18 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Newness]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend_Db_Table]]></category>

		<guid isPermaLink="false">http://ybits.net/?p=64</guid>
		<description><![CDATA[Zend_Db_Table and Zend_Db_Table_Row provide a great base for the &#8220;model&#8221; portion of the MVC paradigm implemented by the Zend Framework. Zend_Db_Table abstracts an underlying database table, and Zend_Db_Table_Row abstracts a row. They provide normalized functionality, are database agnostic, and offer other types of slickness that won&#8217;t be covered here. If you haven&#8217;t experimented with Zend_Db [...]]]></description>
			<content:encoded><![CDATA[<p><img class="logo" src="/img/zf.png" alt="Zend Framework Logo"><a href="http://framework.zend.com/manual/en/zend.db.table.html">Zend_Db_Table</a> and <a href="http://framework.zend.com/manual/en/zend.db.table.row.html">Zend_Db_Table_Row</a> provide a great base for the &#8220;model&#8221; portion of the MVC paradigm implemented by the Zend Framework. Zend_Db_Table abstracts an underlying database table, and Zend_Db_Table_Row abstracts a row. They provide normalized functionality, are database agnostic, and offer other types of slickness that won&#8217;t be covered here. </p>
<p>If you haven&#8217;t experimented with Zend_Db yet, here&#8217;s a simple example of the basic abstractions:</p>
<p>Insert a record:</p>
<pre class="brush:php">
if ($this->_request->isPost()) {
    $usersModel = new Users();
    $user = $usersModel->createRow();
    $user->name = $this->_request->getParam('name');
    $user->age = $this->_request->getParam('age');
    $user->save();
}
</pre>
<p>Update a record:</p>
<pre class="brush:php">
$id = (int)$this->_request->getParam('id');
$usersModel = new Users();
$user = $usersModel->getRow('id = ?', $id);
if (!empty($user)) {
    // Just set the name to uppercase..
    $user->name = strtoupper($user->name);
    $user->save();
}
</pre>
<p>Notice that when $row->save() is called, the appropriate insert/update is made depending upon whether the row is new or not.</p>
<p>The update method is even smart enough to only update fields that have been marked as modified.</p>
<p>But there <em>is</em> a small problem. Let&#8217;s have a look at <code>Zend_Db_Table_Row_Abstract::_set()</code> to see what happens when a value is set into a row:</p>
<pre class="brush:php">
public function __set($columnName, $value)
{
    $columnName = $this->_transformColumn($columnName);
    if (!array_key_exists($columnName, $this->_data)) {
        require_once 'Zend/Db/Table/Row/Exception.php';
	throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
    }
    $this->_data[$columnName] = $value;
    $this->_modifiedFields[$columnName] = true;
}
</pre>
<p>Observe at the marked lines that the column is marked modified <em>regardless of the value being set</em>. If the value passed in is already set for that column, it&#8217;s still marked modified even though technically it hasn&#8217;t been.</p>
<p>This may or may not be desirable given the context, but consider this: Data is always posted in a form, whether or not it changes on the client side. It doesn&#8217;t make sense to always compare each field with the column in the row to determine if the data has changed. Surely this can be done better.</p>
<p>What makes sense is for the row to do this check automatically. What makes even more sense is to make the modified columns and their values available, so that controller specific logic can be written to handle special cases when certain data has changed.</p>
<p>It all starts with that <code>Zend_Db_Table_Row_Abstract::_set()</code> function.</p>
<p>What we want is to make the <code>_set</code> function smarter, so that setting a value into a column that already has that value doesn&#8217;t also mark it modified. </p>
<p>Zend_Db_Table_Row_Abstract can be extended to add the functionality, then Zend_Db_Table_Abstract can be extended to use the new extended row class. When the model is defined, it will extend My_Db_Table_Abstract and inherit the new functionality.</p>
<p>An overloaded Zend_Db_Table_Row_Abstract:</p>
<pre class="brush:php">
class My_Db_Table_Row extends Zend_Db_Table_Row_Abstract
{
    // Return modifiedFields
    public function getModifiedFields()
    {
    	return $this->_modifiedFields;
    }

    // Return a hash of only modified field => value pairs
    public function getModifiedValues()
    {
    	$modifiedValues = array();
    	foreach ($this->_modifiedFields as $field => $value)
    	{
    	    $modifiedValues[$field] = $this->_data[$field];
    	}
    	return $modifiedValues;
    }

    //Override, don't allow unmodified fields to be marked...
    public function __set($columnName, $value)
    {
        $columnName = $this->_transformColumn($columnName);
        if (!array_key_exists($columnName, $this->_data)) {
            require_once 'Zend/Db/Table/Row/Exception.php';
            throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
        }

        $origData = $this->_data[$columnName];
        if ($origData != $value) {
            $this->_data[$columnName] = $value;
            $this->_modifiedFields[$columnName] = true;
        }

    }
}
</pre>
<p>An overloaded Zend_Db_Table_Abstract:</p>
<pre class="brush:php">
class My_Db_Table_Abstract extends Zend_Db_Table_Abstract {

    protected $_rowClass = 'My_Db_Table_Row';

    public function columnExists($column)
    {
        return in_array($column, $this->_cols);
    }
}
</pre>
<p>Portion of a controller script utilizing the new row:</p>
<pre class="brush:php">
$form = new MyForm();
$form->populate($this->_request()->getPost();
$id = (int)$this->_request->getParam('id');
$usersModel = new Users();
$user = $usersModel->getRow('id = ?', $id);
if (!empty($user)) {
    $user->name = $form->getElement['name']->getValue();
    $user->age = $form->getElement['age']->getValue();
    $mods = user->getModifiedValues();
    $user->save();

    foreach($mods as $mod) {
	// do something with the modified values;
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ybits.net/2009/09/extending-zend_db_table_row/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hybrid Zend Form</title>
		<link>http://ybits.net/2009/09/hybrid-zend-form/</link>
		<comments>http://ybits.net/2009/09/hybrid-zend-form/#comments</comments>
		<pubDate>Wed, 23 Sep 2009 15:25:20 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Newness]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend_Form]]></category>

		<guid isPermaLink="false">http://ybits.net/?p=29</guid>
		<description><![CDATA[A Zend_Form is (or at least, can be) an unfortunate mish-mash of logical and visual rules. Instead of designing how the form will render visually using HTML in a .phtml file, like everything else that&#8217;s rendered, there are a bunch of programmatic hoops to jump through to set up the visual properties in code using [...]]]></description>
			<content:encoded><![CDATA[<p><img class="logo" src="/img/zf.png" alt="Zend Framework Logo">A <a href="http://framework.zend.com/manual/en/zend.form.html">Zend_Form</a> is (or at least, can be) an unfortunate mish-mash of logical and visual rules. Instead of designing how the form will render visually using HTML in a <code>.phtml</code> file, like everything else that&#8217;s rendered, there are a bunch of programmatic hoops to jump through to set up the visual properties in code using decorators. </p>
<p>The hoops aren&#8217;t so bad if the layout of the form is simple, for example a basic layout with all the labels to the left and all the elements on the right. But to create a complex form layout using tables (yes, we still use tables for tabular data), the table layout must be created in HTML anyway, and then back-fitted into code by breaking the structure apart and assigning different pieces to individual elements of the form.</p>
<p>This process is tedious, and twice the amount of work to get the same end result. Plus, to change something visually, either the template has to be modified until it&#8217;s right and then back-fitted again, or the visual properties have to be tweaked directly in code without the benefit of the template. The latter being the &#8220;hope for the best&#8221; method.</p>
<p>Mashing this visual structure into the form object itself:</p>
<ul>
<li>Encroaches on the role of the view.</li>
<li>Breaks separation of concerns.</li>
<li>Is harder than it should be.</li>
</ul>
<p>The hybrid approach to Zend_Form is to use all of its functionality <em>except</em> the decorators for visual layout. Instead, the form is laid out normally in a view script, and each element of the form is rendered individually where it needs to be.</p>
<p>This process provides the best of both worlds. The form object is still used to define the elements and do validations, but the actual layout is done (once) in HTML, which is easier to create, modify and maintain.</p>
<p>Here&#8217;s an example, with bits of a form object, controller, and view script:</p>
<p>MyForm.php:</p>
<pre class="brush:php">
class MyForm extends Zend_Form
{
    public function __construct($options = null)
    {
        parent::__construct($options);
        $this->setName('my_form');

        $textFilters = array(
            'StripTags',
            'StringTrim'
        );

        $options = array(
            'name' => 'name',
            'required' => true,
            'validators' => array('NotEmpty'),
            'filters' => $textFilters,
            'class' => 'form',
            'size' => 65,
            'required' => true
        ); 

        $input = new Zend_Form_Element_Text($options);
        $this->addElement($input);

        $options = array(
            'name' => 'age',
            'required' => false,
            'validators' => array('NotEmpty'),
            'filters' => $textFilters,
            'class' => 'form',
            'size' => 5,
            'required' => true
        ); 

        $input = new Zend_Form_Element_Text($options);
        $this->addElement($input);

        $element = new Zend_Form_Element_Submit('submitbtn');
        $element->setAttrib('id', 'submitbtn')
                      ->setAttrib('class', 'button')
                      ->setLabel('Save');
        $this->addElement($element);

        $this->setElementDecorators(array(
            'ViewHelper',
            'Errors'
         ));
    }
}
</pre>
<p>MyController.php:</p>
<pre class="brush:php">
public function showAction()
{
    $form = new MyForm();
    $form->populate($this->_request->getPost());
    $this->view->form = $form;
}
</pre>
<p>show.phtml:</p>
<pre class="brush:html">
&lt;form
name="&lt;?php echo $this-&gt;form-&gt;getName() ?&gt;"
id="&lt;?php echo $this-&gt;form-&gt;getName() ?&gt;"
method="post" enctype="application/x-www-form-urlencoded"
action=""
&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;&lt;?php echo $this-&gt;form-&gt;getElement('name')-&gt;getLabel() ?&gt;&lt;/td&gt;
&lt;td&gt;&lt;?php echo $this-&gt;form-&gt;getElement('name')?&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;?php echo $this-&gt;form-&gt;getElement('age')-&gt;getLabel() ?&gt;&lt;/td&gt;
&lt;td&gt;&lt;?php echo $this-&gt;form-&gt;getElement('age')?&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;?php echo $this-&gt;form-&gt;getElement('submitbtn')?&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
&lt;/form&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ybits.net/2009/09/hybrid-zend-form/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Fresh Start</title>
		<link>http://ybits.net/2009/09/a-fresh-start/</link>
		<comments>http://ybits.net/2009/09/a-fresh-start/#comments</comments>
		<pubDate>Tue, 22 Sep 2009 22:00:18 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Newness]]></category>

		<guid isPermaLink="false">http://ybits.net/?p=1</guid>
		<description><![CDATA[Sometimes it&#8217;s a good idea to delete everything you have and start over. This is one of those times, and indeed it feels good. Newness.]]></description>
			<content:encoded><![CDATA[<p><img class="logo" src="/img/tree.png" alt="Bonsai">Sometimes it&#8217;s a good idea to delete everything you have and start over. This is one of those times, and indeed it feels good.</p>
<p>Newness.</p>
]]></content:encoded>
			<wfw:commentRss>http://ybits.net/2009/09/a-fresh-start/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
