<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link rel="hub" href="http://tumblr.superfeedr.com/" xmlns:atom="http://www.w3.org/2005/Atom"/><description>Symfony Guru at opensky.com.
Discussing web development, Symfony and fatherhood.</description><title>Kris Wallsmith</title><generator>Tumblr (3.0; @kriswallsmith)</generator><link>http://kriswallsmith.net/</link><item><title>Our Montessori preschool’s annual fundraiser.</title><description>&lt;iframe src="http://player.vimeo.com/video/33388107" width="400" height="225" frameborder="0"&gt;&lt;/iframe&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Our Montessori preschool’s annual fundraiser.&lt;/p&gt;</description><link>http://kriswallsmith.net/post/22794125107</link><guid>http://kriswallsmith.net/post/22794125107</guid><pubDate>Thu, 10 May 2012 13:16:00 -0700</pubDate><category>montessori</category></item><item><title>Symfony2 ESI error: File name too long</title><description>&lt;p&gt;We recently came across a strange, intermittent error at OpenSky:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;(36)File name too long: Cannot map GET /_internal&amp;#8230; HTTP/1.1 to file&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We use Varnish to cache large sections of our pages that include
follow/unfollow buttons. If the user is following a certain curator we show
the unfollow button, otherwise we show the follow button.&lt;/p&gt;

&lt;p&gt;Our render tag looked like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{% render 'AppBundle:Curator:list' with
    { following: app.user.sortedFollowingSlugs }
    { standalone: true } %}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When the current user is following just a few curators this works fine. The
app renders the curator list with follow/unfollow buttons accurate for the
current user and Varnish caches it for us. However, when a user was following
all or nearly all of our curators we see the &amp;#8220;file name too long&amp;#8221; error and
Apache would render its 403 error template (no idea why it chooses 403 in this
case).&lt;/p&gt;

&lt;h2&gt;Unpack the WTF&lt;/h2&gt;

&lt;p&gt;To understand why this happens we need to dig into how Symfony2 implements
ESI. When ESI is enabled and your app sits behind a reverse proxy that
supports ESI, any call to Twig&amp;#8217;s &lt;code&gt;{% render %}&lt;/code&gt; tag with the &lt;code&gt;standalone&lt;/code&gt;
option set to &lt;code&gt;true&lt;/code&gt; will render an &lt;code&gt;&amp;lt;esi:include /&amp;gt;&lt;/code&gt; tag instead of rendering
the action inline. Included in the &lt;code&gt;src&lt;/code&gt; attribute of that tag is an URL with
all the values you passed to the &lt;code&gt;{% render %}&lt;/code&gt; tag encoded in.&lt;/p&gt;

&lt;p&gt;The routing pattern used for the ESI URL looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/{controller}/{path}.{_format}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the case above the &lt;code&gt;{controller}&lt;/code&gt; value would be &lt;code&gt;AppBundle:Curator:list&lt;/code&gt;
and the &lt;code&gt;{path}&lt;/code&gt; value would be equivalent to the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;php &amp;gt; echo http_build_query(array('following' =&amp;gt; array('bar', 'foo')));
following%5B0%5D=bar&amp;amp;following%5B1%5D=foo
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now imagine what the &lt;code&gt;{path}&lt;/code&gt; value will look like when a user is following
100+ curators. Big, right? Turns out it would be too big for Apache to handle
as a filename, which cannot exceed 255 characters, hence the &amp;#8220;file name too
long&amp;#8221; error.&lt;/p&gt;

&lt;h2&gt;Query String to the Rescue!&lt;/h2&gt;

&lt;p&gt;Fortunately the fix is easy and does not require any hacking on Symfony. By
moving the potentially large parameter to the ESI URL&amp;#8217;s query string our
character limit jumps from 255 up to 8,190 — lots more room!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{% render 'AppBundle:Curator:list' {
    standalone: true,
    query: { following: app.user.sortedFollowingSlugs }
} %}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Of course you would also need to update &lt;code&gt;CuratorController::listAction()&lt;/code&gt; to
look for the &lt;code&gt;following&lt;/code&gt; parameter in the query string rather than in the
action arguments, but that&amp;#8217;s an easy change.&lt;/p&gt;

&lt;h2&gt;Stay Curious&lt;/h2&gt;

&lt;p&gt;Symfony makes a lot of cool things very easy for you, but don&amp;#8217;t become lazy.
Open up &lt;a href="https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/HttpKernel.php#L194-223" target="_blank"&gt;the source code&lt;/a&gt; and figure out how it works, what technologies
you&amp;#8217;re using under the hood, and what constraints may be lurking in a dark
corner somewhere, waiting to pounce.&lt;/p&gt;</description><link>http://kriswallsmith.net/post/22720626123</link><guid>http://kriswallsmith.net/post/22720626123</guid><pubDate>Wed, 09 May 2012 09:42:34 -0700</pubDate><category>symfony2</category><category>ESI</category></item><item><title>The HTML5 placeholder attribute is not a substitute for the label element</title><description>&lt;a href="http://www.456bereastreet.com/archive/201204/the_html5_placeholder_attribute_is_not_a_substitute_for_the_label_element/"&gt;The HTML5 placeholder attribute is not a substitute for the label element&lt;/a&gt;: &lt;p&gt;A good reminder from Roger Johansson. I admit I’ve been guilty of this in the past…&lt;/p&gt;</description><link>http://kriswallsmith.net/post/21779040956</link><guid>http://kriswallsmith.net/post/21779040956</guid><pubDate>Wed, 25 Apr 2012 05:15:21 -0700</pubDate><category>html5</category></item><item><title>Type Matters</title><description>&lt;p&gt;&lt;pre&gt;&lt;code&gt;json_encode(array()) --&amp;gt; []
json_encode((object) array()) --&amp;gt; {}
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;</description><link>http://kriswallsmith.net/post/20915021107</link><guid>http://kriswallsmith.net/post/20915021107</guid><pubDate>Wed, 11 Apr 2012 12:13:00 -0700</pubDate><category>php</category></item><item><title>Hello Spork! (aka "Forking PHP...")</title><description>&lt;p&gt;A few months ago I was tasked with speeding up the upload of assets to the OpenSky CDN, which was taking a few minutes each deploy. I ended up dividing the upload into multiple processes using &lt;code&gt;pcntl_fork()&lt;/code&gt; and bringing the total time of the upload down to a matter of seconds.&lt;/p&gt;

&lt;p&gt;Since then I&amp;#8217;ve been working on wrapping some of the complexities of working with a parent and child processes into an OO library and am happy to announce this experimental library: &lt;a href="https://github.com/kriswallsmith/spork" target="_blank"&gt;Spork&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Its usage is pretty straight forward.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

use Spork\ProcessManager;
use Spork\Deferred\DeferredFactory;

$pm = new ProcessManager(new DeferredFactory());
$pm-&amp;gt;fork(function() {
    // do something in a child process...
    echo posix_getpid();
})-&amp;gt;then(function($output) {
    // do something in the parent process...
    printf('Parent %d forked child %d!', posix_getpid(), $output);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can pass any callable into the process manager&amp;#8217;s &lt;code&gt;fork()&lt;/code&gt; method. In return you get a nice little deferred object which you can use to queue more callables to run after the child process exits. Just like the jQuery object &lt;a href="http://kriswallsmith.net/post/19404235746/jquery-event-delegation" target="_blank"&gt;I ♥ so much&lt;/a&gt;, there are three basic methods: &lt;code&gt;always()&lt;/code&gt;, &lt;code&gt;done()&lt;/code&gt;, and &lt;code&gt;fail()&lt;/code&gt;, each of which accept a callable as an argument. There is also a &lt;code&gt;then()&lt;/code&gt; method, which is a convenience method for adding a &lt;code&gt;done&lt;/code&gt; and a &lt;code&gt;fail&lt;/code&gt; callback in one method call.&lt;/p&gt;

&lt;h2&gt;Case Studies&lt;/h2&gt;

&lt;p&gt;If you end up using Spork I would like to hear about it. Please post your own blog and I&amp;#8217;ll link to it, or send me a little description of what you&amp;#8217;re doing and I&amp;#8217;ll post it here.&lt;/p&gt;</description><link>http://kriswallsmith.net/post/20844920640</link><guid>http://kriswallsmith.net/post/20844920640</guid><pubDate>Tue, 10 Apr 2012 08:42:03 -0700</pubDate><category>spork</category><category>opensky</category></item><item><title>jQuery Events: Stop (Mis)Using Return False</title><description>&lt;a href="http://fuelyourcoding.com/jquery-events-stop-misusing-return-false/"&gt;jQuery Events: Stop (Mis)Using Return False&lt;/a&gt;: &lt;p&gt;If you’re like me, this kind of rule doesn’t stick until there’s some meat behind it. Here is that meat.&lt;/p&gt;

&lt;p&gt;Thanks for the tip, NiKo!&lt;/p&gt;</description><link>http://kriswallsmith.net/post/19453529129</link><guid>http://kriswallsmith.net/post/19453529129</guid><pubDate>Sat, 17 Mar 2012 08:13:00 -0700</pubDate><category>jquery</category></item><item><title>I ♥ Event Delegation</title><description>&lt;p&gt;jQuery event delegation is &lt;s&gt;one of the coolest things since&lt;/s&gt; way cooler than sliced bread.&lt;/p&gt;

&lt;p&gt;This new hotness allows you to listen for an event farther up the DOM than where that event is triggered. If you understand event bubbling, you should be able to grok this fairly quickly.&lt;/p&gt;

&lt;p&gt;It starts getting interesting when you look at the implications of adding this tool to your toolkit. Let&amp;#8217;s look at a concrete example.&lt;/p&gt;

&lt;h2&gt;Ye Ole List&lt;/h2&gt;

&lt;p&gt;What application would be complete without a list? We all know the deal: a list of things, each with a &amp;#8220;remove&amp;#8221; button and then an &amp;#8220;add&amp;#8221; button at the bottom. Without event delegation we would wire up all these remove buttons something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var $list = $("#list");
$list.find("a.remove").click(function(e) {
    e.preventDefault();
    $(this).closest("#list &amp;gt; li").remove();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This looks nice — but looks can be deceiving. The &lt;code&gt;.click()&lt;/code&gt; function loops over the list of elements inside the current jQuery object and applies a listener to each one. So if there are &lt;em&gt;n&lt;/em&gt; elements, you will have added &lt;em&gt;n&lt;/em&gt; listeners. That bloat starts to show when you get into adding more rows to the list:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var template = $list.data("template");
$("#add_item").click(function(e) {
    e.preventDefault();
    $(template.replace(/__name__/, $list.children().length))
        .appendTo($list)
        .find("a.remove")
        .click(function(e) {
            e.preventDefault();
            $(this).closest("#list &amp;gt; li").remove();
        });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The remove listener is now defined in two places: first in the initial setup and again in the setup of each new row. We could move this listener to its own variable and reuse it, but that would only address the symptom of more code.&lt;/p&gt;

&lt;p&gt;The more important fix is to reduce the number of listeners from &lt;em&gt;n&lt;/em&gt; to 1. This is possible with event delegation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var $list = $("#list");
$list.on("click", "a.remove", function(e) {
    e.preventDefault();
    $(this).closest("#list &amp;gt; li").remove();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This little beauty tells the &lt;code&gt;#list&lt;/code&gt; element to listen for &lt;code&gt;click&lt;/code&gt; events that bubble up from any of its &lt;code&gt;a.remove&lt;/code&gt; progeny and run those events through a single listener. Since the listener lives on a common ancestor now, there is no need to add this listener when creating a new row:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var template = $list.data("template");
$("#add_item").click(function(e) {
    e.preventDefault();
    $(template.replace(/__name__/, $list.children().length)).appendTo($list);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There you have it! Go forth and profit with less, more performant code!&lt;/p&gt;</description><link>http://kriswallsmith.net/post/19404235746</link><guid>http://kriswallsmith.net/post/19404235746</guid><pubDate>Fri, 16 Mar 2012 11:05:00 -0700</pubDate><category>jquery</category></item><item><title>If you need to be able to tell others in the business when they can expect cached pages to...</title><description>&lt;p&gt;If you need to be able to tell others in the business when they can expect cached pages to refresh:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$response-&amp;gt;setPublic();
$response-&amp;gt;setExpires(new \DateTime(sprintf('+%d seconds', 300 - time() % 300)));
&lt;/code&gt;&lt;/pre&gt;</description><link>http://kriswallsmith.net/post/19351352090</link><guid>http://kriswallsmith.net/post/19351352090</guid><pubDate>Thu, 15 Mar 2012 11:17:27 -0700</pubDate><category>Symfony2</category><category>HttpFoundation</category></item><item><title>$response-&gt;isNotModified($request)</title><description>&lt;p&gt;This is one of my favorite methods in all of Symfony2 and it&amp;#8217;s buried in the docs. Take a moment to glory in its simplicity:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function someHeavyAction(Widget $widget, Request $request)
{
    $response = new Response();
    $response-&amp;gt;setLastModified($widget-&amp;gt;getUpdatedAt());

    if ($response-&amp;gt;isNotModified($request)) {
        return $response;
    }

    // do some heavy lifting
    $response-&amp;gt;setContent($this-&amp;gt;renderView(/* ... */));

    return $response;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, this method makes implementing cache validation a snap. Once you&amp;#8217;ve determined what you are going to use for validation — a last-modified timestamp and/or an e-tag — set that value on a new response object and this method will take care of the rest.&lt;/p&gt;

&lt;p&gt;It accepts the current request as an argument and compares the cache headers coming in on the request with those you have set on the response. If the method returns &lt;code&gt;true&lt;/code&gt; simply return the 304 response. If the method returns &lt;code&gt;false&lt;/code&gt;, generate a fresh response.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s this sort of method that makes the &lt;a href="http://symfony.com/doc/current/components/http_foundation.html" target="_blank"&gt;Symfony2 HttpFoundation&lt;/a&gt; component the clear choice for anyone working with PHP and HTTP.&lt;/p&gt;</description><link>http://kriswallsmith.net/post/19147669810</link><guid>http://kriswallsmith.net/post/19147669810</guid><pubDate>Sun, 11 Mar 2012 16:22:00 -0700</pubDate><category>Symfony2</category><category>HttpFoundation</category></item><item><title>Faster PHPUnit</title><description>&lt;p&gt;I made a simple optimization to the test suite at &lt;a href="https://opensky.com" target="_blank"&gt;OpenSky&lt;/a&gt; over the weekend and we are reaping big benefits. The premise is pretty straight forward. We use the &lt;code&gt;setUp()&lt;/code&gt; method to create a lot of mock objects through our test suite. If these are allowed to accumulate they end up wasting a lot of space and slowing down your suite.&lt;/p&gt;

&lt;p&gt;Luckily there is also a &lt;code&gt;tearDown()&lt;/code&gt; method you can use to cleanup, and you can do it automatically if you use this base class:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

abstract class BaseTestCase extends PHPUnit_Framework_TestCase
{
    protected function tearDown()
    {
        $refl = new ReflectionObject($this);
        foreach ($refl-&amp;gt;getProperties() as $prop) {
            if (!$prop-&amp;gt;isStatic() &amp;amp;&amp;amp; 0 !== strpos($prop-&amp;gt;getDeclaringClass()-&amp;gt;getName(), 'PHPUnit_')) {
                $prop-&amp;gt;setAccessible(true);
                $prop-&amp;gt;setValue($this, null);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Our buddy Jenkins is much happier now — builds are approximately 20% faster.&lt;/p&gt;</description><link>http://kriswallsmith.net/post/18029585104</link><guid>http://kriswallsmith.net/post/18029585104</guid><pubDate>Tue, 21 Feb 2012 13:56:00 -0800</pubDate><category>phpunit</category><category>opensky</category></item><item><title>Symfony2 Security Voters</title><description>&lt;p&gt;I answered &lt;a href="http://stackoverflow.com/questions/8879221/dynamically-adding-roles-to-a-user/8883269" target="_blank"&gt;this question&lt;/a&gt; on StackOverflow today that is probably worth repeating
here. The poster was asking how to implement subscription-based authorization
logic in Symfony2. I imagine he models his problem something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Subscription
{
    const SECURED_AREA_FOO = 'FOO';
    const SECURED_AREA_BAR = 'BAR';

    // ...

    /** @ManyToOne(targetEntity="User", inversedBy="subscriptions") */
    public $user;

    /** @Column */
    public $securedArea;

    /** @Column(type="date") */
    public $start;

    /** @Column(type="date") */
    public $end;

    public function isActive()
    {
        $now = new DateTime();

        return $now &amp;gt;= $this-&amp;gt;start &amp;amp;&amp;amp; $now &amp;lt; $this-&amp;gt;end;
    }

    // ...
}

class User implements UserInterface
{
    // ...

    public function getRoles()
    {
        $roles = $this-&amp;gt;roles;

        foreach ($this-&amp;gt;subscriptions as $subscription) {
            if ($subscription-&amp;gt;isActive()) {
                $roles[] = 'ROLE_SUBSCRIPTION_'.$subscription-&amp;gt;securedArea;
            }
        }

        return $roles;
    }

    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;He may then configure access control in his &lt;code&gt;security.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;access_control:
    - { path: "^/sections/foo", roles: [ ROLE_SUBSCRIPTION_FOO ] }
    - { path: "^/sections/bar", roles: [ ROLE_SUBSCRIPTION_BAR ] }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This solution is nice enough, but we can do better. As the poster pointed out
when someone suggested this solution, it doesn&amp;#8217;t make sense to hydrate all of
a user&amp;#8217;s subscription objects (some of which may have expired &lt;em&gt;years&lt;/em&gt; ago)
just to fetch a string value. On top of that, fetching all of these objects
every request is a waste of resources because the system will only be checking
for the existence of one of them in any given request.&lt;/p&gt;

&lt;p&gt;This authorization logic can be implemented much more lightly by encapsulating
it in a custom security voter.&lt;/p&gt;

&lt;h2&gt;Voters&lt;/h2&gt;

&lt;p&gt;When questions about authorization are asked in Symfony2, the answer is
arrived at by a process of voting. For example, when someone requests an URL
that matches a configured &lt;code&gt;access_control&lt;/code&gt; rule, a vote is held to decide
whether to allow or deny access to that resource.&lt;/p&gt;

&lt;p&gt;This voting process is similar in some ways to a US appeals court. There are a
certain numbers of judges presiding over any given proceeding and in the end
a decision is rendered. Occasionally judges recuse themselves from a case for
this or that reason.&lt;/p&gt;

&lt;p&gt;The authorization portion of the Symfony2 security component also includes a
panel of judges called voters. These voters each have a say whenever questions
of authorization come up in your application. Not every voter will have an
opinion on every decision; some will abstain.&lt;/p&gt;

&lt;p&gt;There are two basic types of votes: whether a user is granted a certain
security attribute (i.e. the &lt;code&gt;ROLE_USER&lt;/code&gt; attribute) and whether a user is
granted a certain security attribute for a certain object (i.e. the &lt;code&gt;EDIT&lt;/code&gt;
attribute on blog post X). Each voter can be tuned to only chime in when
certain votes are held. For example, you could write a voter that only
participates when considering users with Gmail addresses.&lt;/p&gt;

&lt;p&gt;In this particular case, we want to create a voter that only chimes in when
questions regarding access to areas secured by subscriptions are raised. We
can signify this by creating a new set of attributes that start with the
string &lt;code&gt;SUBSCRIPTION_&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;access_control:
    - { path: "^/sections/foo", roles: [ SUBSCRIPTION_FOO ] }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This configuration proposes that only users with access to the security
attribute &lt;code&gt;SUBSCRIPTION_FOO&lt;/code&gt; should be granted access to any URL that starts
with &lt;code&gt;/sections/foo&lt;/code&gt;. Since there are no voters configured to evaluate this
particular set of attributes, access with always be denied. But that is solved
easy enough by creating a custom security voter.&lt;/p&gt;

&lt;p&gt;This voter will look something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class SubscriptionVoter implements VoterInterface
{
    private $em;

    public function __construct(EntityManager $em)
    {
        $this-&amp;gt;em = $em;
    }

    public function supportsAttribute($attribute)
    {
        return 0 === strpos($attribute, 'SUBSCRIPTION_');
    }

    public function supportsClass($class)
    {
        return true;
    }

    public function vote(TokenInterface $token, $object, array $attributes)
    {
        $result = VoterInterface::ACCESS_ABSTAIN;
        $user = $token-&amp;gt;getUser();

        foreach ($attributes as $attribute) {
            if (!$this-&amp;gt;supportsAttribute($attribute)) {
                continue;
            }

            $securedArea = substr($attribute, strlen('SUBSCRIPTION_'));

            // use the entity manager to query for active
            // subscriptions that connect the current user to the
            // requested secured area
            // $success = ...

            if ($success) {
                return VoterInterface::ACCESS_GRANTED;
            }

            $result = VoterInterface::ACCESS_DENIED;
        }

        return $result;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And be configured in the service container something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;services:
    subscription_voter:
        class: SubscriptionVoter
        public: false
        arguments:
            - @doctrine.orm.entity_manager
        tags:
            - { name: security.voter }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that&amp;#8217;s all there is to it. You have encapsulated your custom authorization
logic in one clean class and added it to the Symfony2 security layer.&lt;/p&gt;

&lt;h2&gt;Other Applications&lt;/h2&gt;

&lt;p&gt;This is an example of one specific application of security voters, but there
are many more. If you are struggling with how to implement some special access
control logic that doesn&amp;#8217;t fit nicely into either security roles or the
security component&amp;#8217;s ACL, consider creating a custom voter.&lt;/p&gt;</description><link>http://kriswallsmith.net/post/15994931191</link><guid>http://kriswallsmith.net/post/15994931191</guid><pubDate>Mon, 16 Jan 2012 21:14:00 -0800</pubDate><category>Symfony2</category><category>security</category></item><item><title>Twig Node Visitors (Part 2)</title><description>&lt;blockquote&gt;
  &lt;p&gt;This is the second in a series of articles on Twig node visitors. Please read
  &lt;a href="http://kriswallsmith.net/post/13551737951/getting-twiggy-with-it-node-visitors" target="_blank"&gt;part one&lt;/a&gt; first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Node visitors can be used for any number of things. The
&lt;code&gt;Twig_NodeVisitorInterface&lt;/code&gt; interface itself is just three methods:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;interface Twig_NodeVisitorInterface
{
    /**
     * @return Twig_NodeInterface The modified node
     */
    function enterNode(Twig_NodeInterface $node, Twig_Environment $env);

    /**
     * @return Twig_NodeInterface The modified node
     */
    function leaveNode(Twig_NodeInterface $node, Twig_Environment $env);

    /**
     * @return integer The priority level
     */
    function getPriority();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The interface is simple and powerful. It provides a mechanism for manipulating
nodes before a template is compiled down to a PHP class. It puts no
constraints on what it can be used for. In the Twig core there are node
visitors for escaping and optimization, both of which bear no semblance to
what we are going to do here.&lt;/p&gt;

&lt;p&gt;Our use case at OpenSky has to do with querying blocks of CMS content from the database
eagerly, based on what blocks have been included in the template using the
following function:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{{ cms_block('header') }}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can accomplish this eager loading by using a node visitor to statically
analyze each template and stashing the CMS blocks it calls for. The first
method of the interface, &lt;code&gt;enterNode()&lt;/code&gt;, can be used to look at every node in
each template to see if it represents a call to this function.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
{
    if ($cmsBlock = $this-&amp;gt;getCmsBlockKey($node)) {
        $this-&amp;gt;cmsBlocks[] = $cmsBlock;
    }

    return $node;
}

// ...

private function getCmsBlockKey(Twig_NodeInterface $node)
{
    if ($node instanceof Twig_Node_Expression_Function
        &amp;amp;&amp;amp; 'cms_block' == $node-&amp;gt;getAttribute('name')) {
        return $node-&amp;gt;getNode('arguments')-&amp;gt;getNode(0)-&amp;gt;getAttribute('value');
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This code looks at each node to see if it represents a call to the &lt;code&gt;cms_block&lt;/code&gt;
function and pushes the first argument (the name of the CMS block) to an
internal array for use later.&lt;/p&gt;

&lt;p&gt;After running this and debugging what was being stacked onto that array we
found a few issues. First, the visitor was not smart enough to crawl included
or imported templates and look for CMS blocks there. Second, the node visitor
was not being reset for each template so by the end of cache warmup all CMS
blocks called across the entire application were stacked on that internal
array.&lt;/p&gt;

&lt;p&gt;Solving the first issue meant adding a way to recursively crawl the graph of
each template&amp;#8217;s children &amp;#8212; a child being a call to either &lt;code&gt;{% include %}&lt;/code&gt; or
&lt;code&gt;{% import %}&lt;/code&gt; in our case. We did this by adding another internal stack:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
{
    if ($cmsBlock = $this-&amp;gt;getCmsBlockKey($node)) {
        $this-&amp;gt;cmsBlocks[] = $cmsBlock;
    } elseif ($templateName = $this-&amp;gt;getIncludedTemplateName($node)) {
        $this-&amp;gt;includes[] = $templateName;
    }

    return $node;
}

// ...

private function getIncludedTemplateName(Twig_NodeInterface $node)
{
    if ($node instanceof Twig_Node_Include || $node instanceof Twig_Node_Import) {
        return $node-&amp;gt;getNode('expr')-&amp;gt;getAttribute('value');
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We solved the second issue by listening for the root node, an instance of
&lt;code&gt;Twig_Node_Module&lt;/code&gt;, and clearing the internal stacks when we leave that node:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
{
    if ($node instanceof Twig_Node_Module) {
        // todo: make these stacks available at runtime

        // reset
        $this-&amp;gt;cmsBlocks = array();
        $this-&amp;gt;includes  = array();
    }

    return $node;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We&amp;#8217;re in pretty good shape at this point. We have built a node visitor that
collects the information necessary for eagerly querying our CMS for the blocks
that each template calls for. Now we just need to make this information
available at runtime, when the eager query needs to be executed.&lt;/p&gt;

&lt;p&gt;We&amp;#8217;ll do this
in the next post by wrapping the &lt;code&gt;Twig_Node_Module&lt;/code&gt; in our own module node
that compiles down the a PHP class with the necessary public methods. Stay tuned!&lt;/p&gt;</description><link>http://kriswallsmith.net/post/15421960092</link><guid>http://kriswallsmith.net/post/15421960092</guid><pubDate>Fri, 06 Jan 2012 16:06:00 -0800</pubDate><category>twig</category></item><item><title>Getting Twiggy With It: Node Visitors</title><description>&lt;p&gt;I am going to write about node visitors: one of the more obscure but powerful
concepts in Twig. To help make sense of it I will be using a simple, real
world example.&lt;/p&gt;

&lt;p&gt;At OpenSky we recently added a basic CMS to our site that allows us to make
edits to text without going through the hassle of editing a template and
redeploying the entire codebase. We added a module to our admin that manages
these CMS &amp;#8220;blocks&amp;#8221; as documents in MongoDB. Each document represents a block
and includes a unique, descriptive key and the text content. In our templates
we render these blocks using a simple Twig function. For example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{{ cms_block('welcome') }}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When rendering a template, Twig would come across this function and issue a
query to MongoDB for the &lt;code&gt;welcome&lt;/code&gt; document in the collection of CMS blocks.
Easy enough, right?&lt;/p&gt;

&lt;p&gt;Not quite. We would like to speckle these blocks all over pages across the
site: a paragraph here, a header there, an image over there, meta tags,
Facebook tags&amp;#8230; We could be looking at adding a dozen or more queries to a
page for our little CMS, which is unacceptable.&lt;/p&gt;

&lt;h2&gt;Twig&amp;#8217;s own Flux Capacitor&lt;/h2&gt;

&lt;p&gt;Imagine being able to look into the future to see what CMS blocks a template
was going to use and issue a single query to prefetch them all from the
database. That is exactly the sort of thing you can do by taking advantage of
the static compilation phase, when Twig converts your templates into optimized
PHP classes.&lt;/p&gt;

&lt;h2&gt;A Quick Primer on Twig Internals&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s take a step back and review some of the guts of Twig. I promise I&amp;#8217;ll
return to &lt;em&gt;Back to the Future&lt;/em&gt; references later.&lt;/p&gt;

&lt;p&gt;Compilation of a template into a PHP class is a four step process:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Load&lt;/li&gt;
&lt;li&gt;Tokenize&lt;/li&gt;
&lt;li&gt;Parse&lt;/li&gt;
&lt;li&gt;Compile&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;The first step involves an implementation of &lt;a href="https://github.com/fabpot/Twig/blob/master/lib/Twig/LoaderInterface.php" target="_blank"&gt;&lt;code&gt;Twig_LoaderInterface&lt;/code&gt;&lt;/a&gt;, of
which only one method is pertinent to us: &lt;code&gt;getSource()&lt;/code&gt;. This method accepts a
template name and returns the raw content of that template. In the case of the
default &lt;a href="https://github.com/fabpot/Twig/blob/master/lib/Twig/Loader/Filesystem.php" target="_blank"&gt;&lt;code&gt;Twig_Loader_Filesystem&lt;/code&gt;&lt;/a&gt; implementation, this boils down to a
simple call to &lt;code&gt;file_get_contents()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The second step, tokenizing the loaded source, is handled by an implementation
of &lt;a href="https://github.com/fabpot/Twig/blob/master/lib/Twig/LexerInterface.php" target="_blank"&gt;&lt;code&gt;Twig_LexerInterface&lt;/code&gt;&lt;/a&gt; which consists of one method, &lt;code&gt;tokenize()&lt;/code&gt;. If
you are having a hard time sleeping at night you can read up on &lt;a href="http://en.wikipedia.org/wiki/Lexical_analysis" target="_blank"&gt;lexical
analysis&lt;/a&gt; on Wikipedia. For the purpose of this article, you only need to
understand that the lexer converts what you&amp;#8217;ve written in your template using
Twig&amp;#8217;s grammar into a stream of simple PHP objects called tokens.&lt;/p&gt;

&lt;p&gt;In the third step the stream of tokens created by the lexer is parsed into a
multi-dimensional tree of nodes. This work is done by the &lt;a href="https://github.com/fabpot/Twig/blob/master/lib/Twig/Parser.php" target="_blank"&gt;parser&lt;/a&gt; in
cooperation with a collection of &lt;a href="https://github.com/fabpot/Twig/blob/master/lib/Twig/TokenParserInterface.php" target="_blank"&gt;token parsers&lt;/a&gt;. This is the extension
point you would hook into if you wanted to create a new &lt;code&gt;{% foo %}&lt;/code&gt; tag, a
topic outside the scope of this article.&lt;/p&gt;

&lt;p&gt;In the final step the node tree created by the parser is compiled into runtime
PHP code. Each node in the tree implements &lt;a href="https://github.com/fabpot/Twig/blob/master/lib/Twig/NodeInterface.php" target="_blank"&gt;&lt;code&gt;Twig_NodeInterface&lt;/code&gt;&lt;/a&gt;, which
includes a &lt;code&gt;compile()&lt;/code&gt; method that allows it to write arbitrary code to the
resulting template class.&lt;/p&gt;

&lt;h2&gt;Great Scott!&lt;/h2&gt;

&lt;p&gt;That description of the Twig engine was criminally brief, but it should give
you enough knowledge to understand where node visitors come in. After the
parser creates the node tree but before the tree is compiled into PHP code,
the parser recursively iterates over the tree and filters each node through
its registered node visitors. Each visitor has a chance to inspect every
single node in the tree, make changes, replace it with another, or even remove
it altogether. It&amp;#8217;s using this tool that we are able to inspect each template
and magically anticipate what CMS blocks each template will need, before that
template is rendered.&lt;/p&gt;

&lt;h2&gt;Coming Soon&amp;#8230;&lt;/h2&gt;

&lt;p&gt;Dive into code with a working example of a Twig node visitor.&lt;/p&gt;</description><link>http://kriswallsmith.net/post/13551737951</link><guid>http://kriswallsmith.net/post/13551737951</guid><pubDate>Wed, 30 Nov 2011 10:11:00 -0800</pubDate><category>twig</category></item><item><title>Cancer</title><description>&lt;p&gt;I&amp;#8217;ve been sitting on this for awhile and think I should share now.&lt;/p&gt;

&lt;p&gt;My wife of 7 years, partner of 10 and mother of my three young children was diagnosed with stage 4 breast cancer a few months ago. It&amp;#8217;s been an extremely difficult fact to come to terms with. The diagnosis is serious, but we are very hopeful. She has just finished her third round of chemotherapy and the scans say all the marshmallows are shrinking. That news and the outpouring of support from our family, friends and colleagues adds up to a lot to be grateful for.&lt;/p&gt;

&lt;p&gt;If you pray, please pray for the complete eradication of cancer from Franya&amp;#8217;s body. If you don&amp;#8217;t pray, please send some happy thoughts our way.&lt;/p&gt;

&lt;p&gt;Anyway&amp;#8230; this is why I&amp;#8217;ve been relatively quiet lately, will only be at ZendCon for less than 24 hours and won&amp;#8217;t be coming to Cologne for Symfony Day at all. I&amp;#8217;m not leaving anything, I just have less time and energy to spread around.&lt;/p&gt;

&lt;p&gt;Blessings and protection,&lt;br/&gt;
Kris&lt;/p&gt;</description><link>http://kriswallsmith.net/post/11589956631</link><guid>http://kriswallsmith.net/post/11589956631</guid><pubDate>Mon, 17 Oct 2011 16:32:00 -0700</pubDate><category>cancer</category></item><item><title>My first passport...</title><description>&lt;p&gt;&amp;#8230;has expired! I have stamps for these countries:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;The Bahamas&lt;/li&gt;
&lt;li&gt;France&lt;/li&gt;
&lt;li&gt;The Netherlands&lt;/li&gt;
&lt;li&gt;The British Virgin Islands&lt;/li&gt;
&lt;li&gt;Japan&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;What will the next 10 years will bring?&lt;/p&gt;</description><link>http://kriswallsmith.net/post/2977713267</link><guid>http://kriswallsmith.net/post/2977713267</guid><pubDate>Fri, 28 Jan 2011 09:32:25 -0800</pubDate><category>travel</category></item><item><title>Old Glory</title><description>&lt;img src="http://24.media.tumblr.com/tumblr_lcv1maaVAD1qzyw1go1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Old Glory&lt;/p&gt;</description><link>http://kriswallsmith.net/post/2083045296</link><guid>http://kriswallsmith.net/post/2083045296</guid><pubDate>Fri, 03 Dec 2010 08:25:00 -0800</pubDate></item><item><title>Unit Tests, Mocking, and PHPUnit 3.5's new Mock Builder</title><description>&lt;a href="http://engineering.shopopensky.com/post/unit-tests-mocking-and-phpunit-3-5s-new-mock-builder"&gt;Unit Tests, Mocking, and PHPUnit 3.5's new Mock Builder&lt;/a&gt;: &lt;p&gt;My latest tidbit of testing goodness, this time on the OpenSky engineering blog.&lt;/p&gt;</description><link>http://kriswallsmith.net/post/1432753430</link><guid>http://kriswallsmith.net/post/1432753430</guid><pubDate>Fri, 29 Oct 2010 10:25:33 -0700</pubDate><category>PHPUnit</category></item><item><title>Look Behind the "Feature Veil"</title><description>&lt;p&gt;I&amp;#8217;ve been thinking about a decision Apple made awhile ago to allow free iOS
apps to offer in-app purchases. My recollection of their argument against
doing this is that users would be frustrated by downloading a free app only to
have to purchase something in-app to get it to work.&lt;/p&gt;

&lt;p&gt;Around the same time Apple decided to allow free apps to offer in-app
purchases, the App Store also began promoting each application&amp;#8217;s &amp;#8220;Top In-App
Purchases.&amp;#8221; This feature is pretty silly (who cares?), but it&amp;#8217;s positioned
prominently at the top of each app&amp;#8217;s page.&lt;/p&gt;

&lt;p&gt;It seems clear that Apple&amp;#8217;s intention here is to denote which apps ask you to
buy more stuff. This is a valid objective, but they&amp;#8217;ve accomplished it in a
strange way. The feature, when taken at face value, appears to do something
unrelated.&lt;/p&gt;

&lt;p&gt;This particular &amp;#8220;feature veil&amp;#8221; is pretty easy to see through, but I&amp;#8217;m guessing
the technique has been used before, by Apple and others, in less obvious
instances. Can you think of any?&lt;/p&gt;</description><link>http://kriswallsmith.net/post/1416097470</link><guid>http://kriswallsmith.net/post/1416097470</guid><pubDate>Wed, 27 Oct 2010 09:59:47 -0700</pubDate><category>Apple</category></item><item><title>Who can find the jet lagged American?</title><description>&lt;img src="http://25.media.tumblr.com/tumblr_lawcmwOjlU1qzyw1go1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Who can find the jet lagged American?&lt;/p&gt;</description><link>http://kriswallsmith.net/post/1406270197</link><guid>http://kriswallsmith.net/post/1406270197</guid><pubDate>Tue, 26 Oct 2010 05:13:44 -0700</pubDate><category>sfdaycgn</category></item><item><title>Keep a trim autoloader</title><description>&lt;p&gt;The symfony 1.4 autoloader works by scanning your PHP class files and
remembering where each class and interface is defined, so it can magically
load it when needed. This class-to-filename mapping is stored as an array in
your application&amp;#8217;s cache directory.&lt;/p&gt;

&lt;p&gt;You can (and should) look at this array by opening this cache file in your
text editor from time to time:
&lt;code&gt;cache/%SF_APP%/%SF_ENVIRONMENT%/config/config_autoload.yml.php&lt;/code&gt;. Inside
you&amp;#8217;ll see a big array with &lt;code&gt;// comment&lt;/code&gt; headers.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php
// auto-generated by sfAutoloadConfigHandler
// date: 2010/10/24 05:29:41
return array(

  // sfDoctrineGuardPlugin_lib
  'pluginsfguardgroupformfilter' =&amp;gt; '/path/to/plugins/sfDoctrineGuardPlugin/lib/filter/doctrine/PluginsfGuardGroupFormFilter.class.php',
  'pluginsfguardpermissionformfilter' =&amp;gt; '/path/to/plugins/sfDoctrineGuardPlugin/lib/filter/doctrine/PluginsfGuardPermissionFormFilter.class.php',
  'pluginsfguarduserformfilter' =&amp;gt; '/path/to/plugins/sfDoctrineGuardPlugin/lib/filter/doctrine/PluginsfGuardUserFormFilter.class.php',

  // ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A few things to look for in this file:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;You should &lt;strong&gt;not&lt;/strong&gt; see any reference to libraries that have their own
autoloader. This includes the symfony core classes, Swiftmailer, Zend
Framework, etc. If you see references to classes from one of these
libraries, your autoloader is not configured correctly.&lt;/li&gt;
&lt;li&gt;You should &lt;strong&gt;not&lt;/strong&gt; see any non-runtime classes.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;You&amp;#8217;ll probably need to add some configuration to accomplish that second
point. For example, your tasks and Doctrine migration classes will never be
involved in a runtime request, so they should not be included in the
autoloader&amp;#8217;s cache.&lt;/p&gt;

&lt;h2&gt;Configure the autoloader&lt;/h2&gt;

&lt;p&gt;Fortunately, excluding these files is easy enough using &lt;code&gt;autoload.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add this file to your project…&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# config/autoload.yml
autoload:
  # extend the "project" configuration block defined in the symfony core
  project:
    exclude: [vendor, symfony, model, migration, task]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Clear your cache…&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ php symfony cache:clear
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Visit your site in the browser to prime the cache, and re-inspect the cached
&lt;code&gt;config_autoload.yml.php&lt;/code&gt; file. Your task and Doctrine migration classes
should no longer be represented there.&lt;/p&gt;

&lt;h2&gt;Add this to your installer&lt;/h2&gt;

&lt;p&gt;The new installer mechanism in symfony 1.4 is the perfect place to add this
sort of thing since you&amp;#8217;re going to want to do it on every project. For an
example of how this is done, check out commit &lt;a href="http://github.com/kriswallsmith/symfony-installer/commit/dede92eefc5e54d6c913e354486c045e3b6b03cc" target="_blank"&gt;dede92e&lt;/a&gt; on my GitHub
&lt;a href="http://github.com/kriswallsmith/symfony-installer" target="_blank"&gt;symfony-installer&lt;/a&gt; project.&lt;/p&gt;

&lt;h2&gt;Read more&lt;/h2&gt;

&lt;p&gt;If you want to learn more about configuring the symfony 1.4 autoloader, please
read through &lt;a href="http://www.symfony-project.org/reference/1_4/en/" target="_blank"&gt;The symfony Reference Guide&lt;/a&gt;, specifically the section on
&lt;a href="http://www.symfony-project.org/reference/1_4/en/14-Other-Configuration-Files#chapter_14_autoload_yml" target="_blank"&gt;autoload.yml&lt;/a&gt;.&lt;/p&gt;</description><link>http://kriswallsmith.net/post/1389089654</link><guid>http://kriswallsmith.net/post/1389089654</guid><pubDate>Sun, 24 Oct 2010 05:46:00 -0700</pubDate><category>symfony</category></item></channel></rss>

