<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <title>Blog entries tagged microphp :: mwop.net</title>
    <description>Blog entries tagged microphp :: mwop.net</description>
    <pubDate>Fri, 17 Aug 2012 11:00:00 -0500</pubDate>
    <generator>Laminas_Feed_Writer 2 (https://getlaminas.org)</generator>
    <link>https://mwop.net/blog/tag/microphp</link>
    <atom:link rel="self" type="application/rss+xml" href="https://mwop.net/blog/tag/microphp/rss.xml"/>
    <item>
      <title>On Microframeworks</title>
      <pubDate>Fri, 17 Aug 2012 11:00:00 -0500</pubDate>
      <link>https://mwop.net/blog/2012-08-17-on-microframeworks.html</link>
      <guid>https://mwop.net/blog/2012-08-17-on-microframeworks.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>A number of months ago, <a href="http://funkatron.com/">Ed Finkler</a> started a
discussion in the PHP community about <a href="http://microphp.org/">&quot;MicroPHP&quot;</a>; to
summarize, the movement is about:</p>
<ul>
<li>Building small, single-purpose libraries.</li>
<li>Using small things that work together to solve larger problems.</li>
</ul>
<p>I think there are some really good ideas that have come out of this, and also a
number of questionable practices<sup id="t1"><a href="#f1">1</a></sup>.</p>
<p>One piece in particular I've focussed on is the concept of so-called
“microframeworks”.</p>


<h2>What is a microframework?</h2>
<p>PHP has had microframeworks for quite some time<sup id="t2"><a href="#f2">2</a></sup>, though I
only really first saw the term being used around 3 years ago. The “grand-daddy”
of modern-day microframeworks can actually be traced to Ruby, however, and
specifically <a href="http://www.sinatrarb.com">Sinatra</a>.</p>
<p>Sinatra is not so much a framework as it is a domain-specific language (DSL).
The language and structure it created, however, have been re-created in the
vast majority of microframeworks you see currently in the PHP arena.
Specifically, it describes how to map HTTP request methods and paths to the
code that will handle them. It borrowed route matching ideas from
<a href="http://rubyonrails.org/">Ruby on Rails</a>, and relied on the fact that Ruby uses
the last value of a block as the return value.</p>
<p>As some simple examples:</p>
<pre><code class="language-ruby hljs ruby" data-lang="ruby">get <span class="hljs-string">'/hello/:name'</span> <span class="hljs-keyword">do</span> <span class="hljs-params">|n|</span>
    <span class="hljs-string">"Hello <span class="hljs-subst">#{n}</span>!"</span>
<span class="hljs-keyword">end</span>

post <span class="hljs-string">'/address'</span>
    <span class="hljs-comment"># create address</span>
<span class="hljs-keyword">end</span>

put <span class="hljs-string">'/address/:id'</span> <span class="hljs-params">|i|</span>
    <span class="hljs-comment"># update address</span>
<span class="hljs-keyword">end</span>

get <span class="hljs-string">'/feed.?:format?'</span>, <span class="hljs-symbol">:provides</span> =&gt; [<span class="hljs-string">'rss'</span>, <span class="hljs-string">'atom'</span>, <span class="hljs-string">'xml'</span>] <span class="hljs-keyword">do</span>
    builder <span class="hljs-symbol">:feed</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>The language is expressive, and allows the developer to focus on two things:</p>
<ul>
<li>What are the specific entry points (URIs) for the application?</li>
<li>What needs to be done for each specific entry point?</li>
</ul>
<p>I'd argue that the above two points are the defining characteristics of modern
microframeworks. Typically, the entry points are given the term &quot;routing&quot;, and
the second corresponds to &quot;controllers&quot;.</p>
<h2>PHP implementations</h2>
<p>I'd argue one of the earliest microframework implementations, though it wasn't
termed as such, was <a href="http://dev.horde.org/routes/">Horde Routes</a><sup id="t3"><a href="#f3">3</a></sup>
(which was itself inspired by <a href="http://routes.readthedocs.org/en/latest/index.html">Python Routes</a>,
in turn inspired by the Rails routing system, like Sinatra). It follows the two
principles I outlined above: it allows defining routes (entry points), and
mapping them to controllers. Controllers for Routes are simply classes, and a
route must provide both a controller and an action in the match, with the
latter corresponding to a method on the controller class.</p>
<p>Since around 2009, I've seen an increasing number of new PHP
microframeworks<sup id="t4"><a href="#f4">4</a></sup> that follow in the steps of Sinatra and
Horde. In the various implementations I've looked at, instead of using a DSL,
the authors have all opted for either a procedural or OOP interface. Starting
with PHP 5.3, most authors have also primarily targetted any PHP callable as a
controller, favoring callbacks specifically. The fundamental ideas remain the
same as Sinatra, however:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-comment">/* Procedural */</span>
get(<span class="hljs-string">'/hello/:name'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($n)</span> </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello {$n}!"</span>;
});

post(<span class="hljs-string">'/address'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// create address</span>
});

put(<span class="hljs-string">'/address/:id'</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($i)</span> </span>{
    <span class="hljs-comment">// update address</span>
});

get(<span class="hljs-string">'/feed.?:format?'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">($feed, $format)</span> </span>{
    <span class="hljs-keyword">return</span> builder($feed, $format);
});

<span class="hljs-comment">/* OOP */</span>
$app-&gt;get(<span class="hljs-string">'/hello/:name'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($n)</span> </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello {$n}!"</span>;
});

$app-&gt;post(<span class="hljs-string">'/address'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// create address</span>
});
end

$app-&gt;put(<span class="hljs-string">'/address/:id'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($i)</span> </span>{
    <span class="hljs-comment">// update address</span>
});

$app-&gt;get(<span class="hljs-string">'/feed.?:format?'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($feed, $format)</span> <span class="hljs-title">use</span> <span class="hljs-params">($app)</span> </span>{
    <span class="hljs-keyword">return</span> $app-&gt;builder($feed, $format);
})-&gt;constraints([<span class="hljs-string">'format'</span> =&gt; <span class="hljs-string">'/^(rss|atom|xml)$/'</span>]);
</code></pre>
<p>One key difference I've witnessed in the implementations is surrounding how
route matches are passed to the callback. In the examples above, they are
passed as individual arguments to the handler. Some, however, opt for an
approach more like Sinatra, which passes a single &quot;params&quot; argument into the
scope of the handler. This approach tends to be more expedient both from an
implementation standpoint as well as a performance standpoint, as it does not
require reflection to determine name and position of arguments, and makes
handling wildcard arguments simpler. I've seen this latter approach handled
several ways:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-comment">// Pass in route match parameters as an argument.</span>
$app-&gt;get(<span class="hljs-string">'/feed.:format'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($params)</span> </span>{
    $format = $params[<span class="hljs-string">'format'</span>];
});

<span class="hljs-comment">// Pass in the $app instance, and retrieve route </span>
<span class="hljs-comment">// match parameters from it.</span>
$app-&gt;get(<span class="hljs-string">'/feed.:format'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($app)</span> </span>{
    $format = $app-&gt;params(<span class="hljs-string">'format'</span>);
});

<span class="hljs-comment">// Curry in the $app instance when desired, and </span>
<span class="hljs-comment">// retrieve route match parameters from it.</span>
$app-&gt;get(<span class="hljs-string">'/feed.:format'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> <span class="hljs-title">use</span> <span class="hljs-params">($app)</span> </span>{
    $format = $app-&gt;params(<span class="hljs-string">'format'</span>);
});
</code></pre>
<p>Another difference I've seen is in how route constraints, defaults, and names
are handled. The most elegant solutions usually allow chaining method calls in
order to alter this data:</p>
<pre><code class="language-php hljs php" data-lang="php">$app-&gt;get(<span class="hljs-string">'/feed.:format'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($app)</span> </span>{
    <span class="hljs-comment">// ...</span>
})-&gt;constraints([<span class="hljs-string">'format'</span> =&gt; <span class="hljs-string">'/^(atom|xml|json)$/'</span>])
  -&gt;name(<span class="hljs-string">'feed'</span>);
</code></pre>
<p>One common feature I've seen is the ability to generate URLs based on the
defined routes. Most commonly, this is a function or method <code>urlTo()</code>, which
takes a route name, and an associative array of replacements.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">echo</span> $app-&gt;urlTo(<span class="hljs-string">'feed'</span>, [<span class="hljs-string">'format'</span> =&gt; <span class="hljs-string">'atom'</span>]);
</code></pre>
<p>That's it in a nutshell: the ability to match HTTP request methods and path
information, and map it to controllers/handlers, and to generate URLs based on
those present in the application.</p>
<h2>What are they good for?</h2>
<p>In my research and experience, microframeworks have three typical use cases:</p>
<ol>
<li><strong>Prototyping.</strong> Because of their simplicity, microframeworks are fantastic
for prototyping a basic website. Very often, in the early stages of a site, you
have a limited number of pages, and most often simply need to render a template
with limited variable substitutions. Microframeworks are a perfect fit for
this.</li>
<li><strong>APIs</strong>. API needs are usually quite well-defined, and often involve a
small, finite number of URLs. The logic required is usually already
encapsulated in business objects, so the application layer is simply for
filtering and returning a representation. Microframeworks again offer a nice
fit.</li>
<li><strong>Small, mostly static sites</strong>. Similar to the first point, if you know the
site will be relatively small and mostly static, then the minimal overhead of a
microframework is often a good fit.</li>
</ol>
<h2>Where do microframeworks fail?</h2>
<p>Because of the rather declarative nature of microframeworks, and the typically
1:1 mapping of a route to a controller, microframeworks do not tend to promote
code re-use. Additionally, this extends to how microframework applications are
organized: usually, there are no clear guidelines on how to organize routes and
controllers, much less separate them into multiple files. This can lead to
maintenance issues as the application grows, as well as logistical issues
whenever you need to add new routes and controllers (do they go at the top, or
bottom? are there other routes that could potentially match as well? etc.).</p>
<p>Additionally, though many frameworks offer ways to alter the workflow of the
application either via hooks, events, or “middleware”<sup id="t5"><a href="#f5">5</a></sup>, most
of these are limited in scope, often non-reusable, and often non-stackable. As
such, comprehensive manipulation of the application workflow is out of reach.</p>
<p>One other area that is overlooked, however, is one I find curious, particularly
in light of the MicroPHP movement: so much of the underlying plumbing is
basically the same, yet every microframework re-implements it. Specifically:</p>
<ul>
<li>Routing is basically the same across most implementations, following the same
basic specifications outlined in Rails. There are very few differences in the
public APIs.</li>
<li>Request and Response object abstraction is largely the same as well,
providing access to query/post/cookie/session/etc. parameters through roughly
equivalent APIs.</li>
<li>Many implement their own view layers.<sup id="t6"><a href="#f6">6</a></sup></li>
</ul>
<p>Most of this code should be considered commodity code at this point. There are
several outstanding view layers and templating engines available (Smarty, Twig,
Savant, <code>Zend\View</code>). Standalone routing libraries exist such as Horde Routes,
and even those bundled with frameworks are often available separately via
Composer or Pyrus; the same is true with Request and Response object
abstraction. It seems to me that a few microframework authors should be working
on abstracting these concerns, and then focussing their efforts on
differentiators in their own microframeworks.</p>
<h2>An experiment</h2>
<p>Building on my last point, I looked at the APIs of
<a href="http://limonade-php.github.com/">Limonade</a> and
<a href="http://www.slimframework.com/">Slim Framework</a>, and built up a specification
for a microframework. I then matched as many pieces of it as possible to
existing components in <a href="http://packages.zendframework.com/">ZF2</a>, and started
building.</p>
<p>In a matter of a few hours, I had written up a complete test
suite<sup id="t7"><a href="#f7">7</a></sup> and all code for a microframework, featuring the
following (this is basically the testdox output from the unit test suite):</p>
<ul>
<li>Lazy loads request</li>
<li>Lazy loads response</li>
<li>Request is injectible</li>
<li>Response is injectible</li>
<li>Halt should raise halt exception</li>
<li>Response should contain status provided to halt</li>
<li>Response should contain message provided to halt</li>
<li>Stop should raise halt exception</li>
<li>Response should remain unaltered after stop</li>
<li>Redirect should raise halt exception</li>
<li>Redirect should set 302 response status by default</li>
<li>Redirect should set response status based on provided status code</li>
<li>Redirect should set location header</li>
<li>Map creates a segment route when provided with a string route</li>
<li>Map can receive a route object</li>
<li>Passing invalid route raises exception</li>
<li>Map can receive a callable</li>
<li>Passing invalid controller to route does not immediately raise exception</li>
<li>Accessing invalid controller raises exception</li>
<li>Passing invalid method to route via method raises exception</li>
<li>Can set methods route responds to singly</li>
<li>Can set methods route responds to as array</li>
<li>Can set methods route responds to as multiple arguments</li>
<li>Can specify additional method types to respond to</li>
<li>Can specify route name</li>
<li>Adding route using method type creates route that responds to that method type</li>
<li>Running with no matching routes raises page not found exception</li>
<li>Routing sets list of named routes</li>
<li>Routing sets lists of routes by method</li>
<li>Successful routing dispatches controller</li>
<li>Unsuccessful routing triggers 404 event</li>
<li>Calling halt triggers halt event</li>
<li>Invalid controller triggers 501 event</li>
<li>Exception raised in controller triggers 500 event</li>
<li>Can pass to next matching route</li>
<li>Url for helper assembles url based on name provided</li>
<li>Url for helper assembles url based on name and params provided</li>
<li>Url for helper assembles url based on current route match when no name provided</li>
<li>Composes logger instance by default</li>
<li>Can inject specific logger instance</li>
<li>Mustache view is used by default</li>
<li>Can inject alternate view instance</li>
<li>Render renders a template to the response</li>
<li>View model returns mustache view model by default</li>
<li>Subsequent calls to view model return separate instances</li>
<li>Can provide view model prototype</li>
</ul>
<p>I utilized ZF2's routing library from its MVC component, the request and
response objects from its HTTP component, its Log component, and the Session
component. These had a few other dependencies, but nothing terribly onerous.</p>
<p>For the view, I used my own <a href="http://weierophinney.github.com/phly_mustache">phly_mustache</a>,
and provided a basic &quot;view model&quot; implementation that receives the application
instance, thus allowing the ability to call application helpers (such as url
generation).</p>
<p>To make installation simple, I used <a href="http://getcomposer.org">Composer</a> to
manage my dependencies on specific ZF2 components and for <code>phly_mustache</code>. The
microframework contains only the code it needs to get its work done, leveraging
the work of others whenever possible.</p>
<p>This post is not meant as a way to announce a new microframework,
however.<sup id="t8"><a href="#f8">8</a></sup> The point of the experiment was to prove something:
microframeworks are trivially easy to write, <em>particularly if you follow the
principals of MicroPHP, and re-use existing code</em>. Just because code comes from
a framework or a third-party library does not make it suspect or inferior; in
fact, whenever possible, you should leverage such code so you can focus on
<em>writing awesome applications</em>.</p>
<h2>Lessons learned</h2>
<p>I really like microframeworks for specific problems: prototyping, APIs, and
small, simple sites. I think they are ideally suited for these tasks. That
said, I'd love to see some solid libraries targetting the fundamental, shared
aspects of these efforts: routing, request and response abstraction, etc. With
dependency management tools such as Composer and Pyrus, having required
dependencies is not a big deal anymore, and re-use should be encouraged.</p>
<p>Also, writing a microframework is an excellent coding exercise. It helps a
developer appreciate the complexities of abstraction while limiting the number
of moving parts. I highly recommend it as an exercise — but do it using
available components, and be prepared to throw it away and instead collaborate
with others, or adopt something which better solves both the problems you have
and the problems you anticipate.</p>
<p>In sum: <em>Use the right tool for the job</em>. If you foresee expanding requirements
in your project's future, you may want to evaluate a full-stack
framework,<sup id="t9"><a href="#f9">9</a></sup> or consider building something robust that suits
your specific project's needs. Use microframeworks where and when they make
sense.</p>
<h4>Afterword</h4>
<p>I'm well aware that Fabien Potencier has written <a href="http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1">a comprehensive series of posts on creating a microframework using Symfony 2 components</a>.
I deliberately chose not to read them until (a) ZF2 was almost ready to
release, and (b) I'd had a chance to formulate my own opinions on
microframeworks. They're an excellent read, however, and show a nice
progression of development from flat PHP to a fully functional microframework;
click the link and see for yourself.</p>
<h4>Footnotes</h4>
<ul>
<li><sup id="f1"><a href="#t1">1</a></sup> In particular, I feel that the movement (a) disparages
components from larger libraries simply because they originate from a larger
library, and (b) distrust any code that has additional dependencies. This
latter I find truly puzzling, as I'd think it fits the idea of “use small
things that work together to solve larger problems.” If the code solves a
particular problem and allows you to focus on a larger problem, where it
originates and the number of dependencies should not be an issue.</li>
<li><sup id="f2"><a href="#t2">2</a></sup> In fact, my first foray into MVC in PHP was writing a
clone of Perl's <a href="http://cgi-app.org/">CGI::Application</a>, which in many ways
is also a microframework.</li>
<li><sup id="f3"><a href="#t3">3</a></sup> Trivia: Both authors of Horde Routes worked at Zend when
I first started at the company, and Mike Naberezny wrote the very first lines
of code for Zend Framework.</li>
<li><sup id="f4"><a href="#t4">4</a></sup> I swear, you see new ones on Github daily, and on
<a href="http://phpdeveloper.org/">PHP Developer</a> at least once a week.</li>
<li><sup id="f5"><a href="#t5">5</a></sup> <a href="http://www.slimframework.com">Slim</a> has this concept.
Basically, any callables placed between the route string and the last
callable when defining a route — i.e., the “middle” arguments, and thus
middleware — will be executed in order prior to attempting to execute the
controller.</li>
<li><sup id="f6"><a href="#t6">6</a></sup> <a href="http://www.slimframework.com">Slim</a> is an outlier here,
as it utilizes <a href="http://twig.sensiolabs.org/">Twig</a> by default.</li>
<li><sup id="f7"><a href="#t7">7</a></sup> I'm sure that my TDD experiment will warm the soul of
<a href="http://www.littlehart.net/atthekeyboard/">the Grumpy Programmer</a>.</li>
<li><sup id="f8"><a href="#t8">8</a></sup> That said, if you want to look at the results, you can
<a href="http://github.com/weierophinney/phlyty">find Phlyty on Github</a>.</li>
<li><sup id="f9"><a href="#t9">9</a></sup> As you may guess, I'm biased towards <a href="http://framework.zend.com/">Zend Framework</a>.
However, you should always carefully evaluate a framework against your
project's needs.</li>
</ul>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/2012-08-17-on-microframeworks.html">On Microframeworks</a> was originally
    published <time class="dt-published" datetime="2012-08-17T11:00:00-05:00">17 August 2012</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
  </channel>
</rss>
