<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.seld.be/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
	<channel>
		<title>PHP - Blog - Jordi Boggiano</title>
		<link>http://seld.be/</link>
		<description>Notes on random stuff, thoughts, tutorials, code, ideas, anything that can be put down to letters.</description>
		<pubDate>Sun, 19 May 2013 20:33:09 +0200</pubDate>
		<generator>PHP RSSGen 1.0.0 - Seld.be</generator>
		<docs>http://www.rssboard.org/rss-2-0-10</docs>
		<ttl>60</ttl>
		<language>en</language>
		<lastBuildDate>Mon, 04 Mar 2013 10:00:00 +0100</lastBuildDate>
		<category>PHP</category>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.seld.be/php-blog-seld" /><feedburner:info uri="php-blog-seld" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><creativeCommons:license>http://creativecommons.org/licenses/by-nc-sa/3.0/</creativeCommons:license><image><link>http://creativecommons.org/licenses/by-nc-sa/3.0/</link><url>http://creativecommons.org/images/public/somerights20.gif</url><title>Some Rights Reserved</title></image><item>
			<title>Composer: an update on require-dev</title>
			<description>&lt;p&gt;Using &lt;a href="http://getcomposer.org/doc/04-schema.md#require-dev"&gt;require-dev&lt;/a&gt; in &lt;a href="http://getcomposer.org/"&gt;Composer&lt;/a&gt; you can declare the dependencies you need for development/testing. It works in most simple cases, but when the dev dependencies overlap with the regular ones, it can get tricky to handle. In too many cases it also tends to just fail at resolving dependencies with quite strange error messages.&lt;/p&gt;

&lt;p&gt;Since this was quite unreliable, I set out to rework the whole feature this week-end. The &lt;a href="https://github.com/composer/composer/pull/1644"&gt;patch&lt;/a&gt; has been merged, and it fixes six open issues which is great. The short story there is that it now does things in one pass instead of two before, so it should be faster and a lot more reliable. Also dev dependencies can now impact the non-dev ones without problems since it's all resolved at once.&lt;/p&gt;

Workflow changes

&lt;p&gt;I took the chance to change another thing while I was at it. The update command now installs dev requirements by default. This makes sense since you should only run it on dev environments. No more update --dev, the dev flag is now implicit and if you really don't want these packages installed you can use update --no-dev instead.&lt;/p&gt;

&lt;p&gt;The install command on the hand remains the same. It does not install dev dependencies by default, and it will actually remove them if they were previously installed and you run it without --dev. Again this makes sense since in production you should only run install to get the last verified state (stored in composer.lock) of your dependencies installed.&lt;/p&gt;

&lt;p&gt;I think this minor change in workflow will simplify things for most people, and I really hope it doesn't break any assumptions that were made in third party tools.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/Dkc9BRWi52w" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/Dkc9BRWi52w/composer-an-update-on-require-dev</link>
			<pubDate>Mon, 04 Mar 2013 10:00:00 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/composer-an-update-on-require-dev</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/composer-an-update-on-require-dev</feedburner:origLink></item>
		<item>
			<title>One logger to rule them all</title>
			<description>&lt;p&gt;I &lt;a href="https://groups.google.com/forum/?fromgroups=#!topic/php-fig/d0yPC7jWPAE"&gt;called the vote&lt;/a&gt; on the Logger Interface proposal last week. When the vote ends next week it will become PSR-3 (since it already collected a majority). The fourth recommendation from the &lt;a href="http://www.php-fig.org/"&gt;PHP-FIG group&lt;/a&gt;, and the first one actually including interfaces/code, which is a great milestone.&lt;/p&gt;

&lt;p&gt;You can read the &lt;a href="https://github.com/php-fig/fig-standards/pull/60"&gt;proposal&lt;/a&gt; if you have not done so yet, but I wanted to discuss the goal and long term hopes I have in more details here.&lt;/p&gt;

Where we come from

&lt;p&gt;Most PHP frameworks and larger applications have in the past implemented their own logging solutions and this makes sense since I think everyone recognizes the usefulness of logs. Traditionally most of those did not have many external dependencies, established libraries were few and far between. Having no logging capability in those was not such a hindrance.&lt;/p&gt;

Libraries deserve logs too

&lt;p&gt;Yet in the last couple years, thanks to GitHub allowing easier sharing, composer allowing more reusability, and mentalities shifting slowly to a less-NIH approach, we are seeing more and more libraries used in applications and even by frameworks themselves. This is great, but as soon as you call a library you enter a black box and if you want anything to show up in your logs you have to log yourself.&lt;/p&gt;

&lt;p&gt;The availability of the PSR-3 interface means that libraries can optionally accept a Psr\Log\LoggerInterface instance, and if it is given to them they can log to it. That opens up a whole lot of possibilities for tighter integration of libraries with the framework/application loggers. I really hope library developers will jump on this and start logging more things so that when things go south it is easier to identify problems by looking at your application logs.&lt;/p&gt;

Take a deep breath

&lt;p&gt;I am sure people will have questions or complaints regarding details of the interface itself, but I hope this helped you see the broader benefits it brings.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/HeruxtWP7Nk" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/HeruxtWP7Nk/one-logger-to-rule-them-all</link>
			<pubDate>Thu, 20 Dec 2012 19:10:06 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/one-logger-to-rule-them-all</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/one-logger-to-rule-them-all</feedburner:origLink></item>
		<item>
			<title>Encouraging contributions with the Easy Pick label</title>
			<description>&lt;p&gt;One of the barriers to convert users into contributors in an open-source projects is that many people have no idea where to start. They are usually scared to take on large tasks because they are not comfortable enough with the code-base. Yet I think there are ways you can help them as a project maintainer.&lt;/p&gt;

&lt;p&gt;One good way that I found to fix this is to tag specific issues that are a good starting point for new contributors. However I think the practice would be even more effective if more projects did the same way, so that people know to look for it.&lt;/p&gt;

&lt;p&gt;The way I do it is using a custom Easy Pick label to indicate issues on GitHub that are just that. Easy to pick up tasks, either because they are small in scope, or just don't involve much in-depth knowledge of the project.&lt;/p&gt;

&lt;p&gt;The result of this is a much clearer view of issues. On the Composer project for example if you go in the issues tab and then &lt;a href="https://github.com/composer/composer/issues?labels=Easy+Pick&amp;page=1&amp;state=open"&gt;filter by "Easy Pick"&lt;/a&gt;, you end up with 14 issues listed instead of 170. A much more manageable amount to look at and pick from, and you are empowered by the knowledge that those should all be reasonably easy to work out.&lt;/p&gt;

&lt;p&gt;I have also created this label in the &lt;a href="https://github.com/symfony/symfony/issues?labels=Easy+Pick&amp;page=1&amp;state=open"&gt;Symfony2&lt;/a&gt; issues a while back. As you see both use the same wording and the same yellow that's one of the default colors on GitHub.&lt;/p&gt;

&lt;p&gt;I would love to see this spread because I have already seen it bring in a few new contributors. So if you feel like encouraging people to join in on your project give it a try. And if you feel like giving back this week-end, browse the issues of the projects you use and enjoy. See if you find anything you can help them with.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/6KJ9CmOIobQ" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/6KJ9CmOIobQ/encouraging-contributions-with-the-easy-pick-label</link>
			<pubDate>Fri, 16 Nov 2012 14:00:45 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/encouraging-contributions-with-the-easy-pick-label</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/encouraging-contributions-with-the-easy-pick-label</feedburner:origLink></item>
		<item>
			<title>I&amp;#039;m going nomad - introducing Nelmio</title>
			<description>&lt;p&gt;After almost three years working at &lt;a href="http://liip.ch/"&gt;Liip&lt;/a&gt;, I have finally decided to take the plunge and start my own business. Together with &lt;a href="https://twitter.com/shvi"&gt;Pierre Spring&lt;/a&gt;, in early May we will start building up &lt;a href="http://nelm.io/"&gt;Nelmio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why? To keep it short, Liip is a great company to be employed at - and &lt;a href="http://liip.ch/jobs"&gt;they're hiring&lt;/a&gt; - but both Pierre and I have had the urge to be our own bosses for a while, and that is something that's hard to suppress. Eventually we had to give in.&lt;/p&gt;

&lt;p&gt;What next? We're both web devs, with lots of experience across the board. Pierre is probably more into JavaScript and the frontend side - along with web performance optimization. I'm a big &lt;a href="http://symfony.com"&gt;Symfony2&lt;/a&gt; contributor and more into the PHP/backend side of things, but we are both quite knowledgeable in all those technologies, they're merely complementary tools after all. Anyway, we'd love to share some of that knowledge, and will be available for some consulting/code review/coaching fun. We're based in Zürich, Switzerland, &lt;a href="http://nelm.io/contact"&gt;let us know&lt;/a&gt; if you need us.&lt;/p&gt;

&lt;p&gt;You can find Nelmio's site at &lt;a href="http://nelm.io/"&gt;nelm.io&lt;/a&gt; - it's not complete yet but it should give you a good overview already. And in any case you should of course follow &lt;a href="https://twitter.com/nelm_io"&gt;@nelm_io&lt;/a&gt; on Twitter to get more news soon :)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/BQLmZAr0mS4" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/BQLmZAr0mS4/i-m-going-nomad-introducing-nelmio</link>
			<pubDate>Sat, 23 Apr 2011 19:05:12 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/i-m-going-nomad-introducing-nelmio</guid>
			<category>News</category>
			<category>PHP</category>
			<category>JavaScript</category>
		<feedburner:origLink>http://seld.be/notes/i-m-going-nomad-introducing-nelmio</feedburner:origLink></item>
		<item>
			<title>Terminal (Bash) arguments tricks</title>
			<description>&lt;p&gt;Reading David DeSandro's last post on how to &lt;a href="http://dropshado.ws/post/4554069627/strings-in-terminal"&gt;store strings in variables&lt;/a&gt; in terminal, or any bash-y shell (I'd say any unix shell but I'm sure there is a weird one out there that does things differently) for that matter, it struck me that many web developers seem to have a big disconnect with the shell.&lt;/p&gt;

&lt;p&gt;Now I'm no expert, but I know that the use case he describes can be solved much more efficiently, so I felt like writing a little follow-up, and hopefully teach you, dear reader, a thing or two. The short story is that you sometimes want to do many operations on the same file. Now the neat trick to do that is to use &lt;a href="http://www.gnu.org/software/bash/manual/bashref.html#History-Interaction"&gt;history expansion&lt;/a&gt;, which allows you to reference one of the parameters from the previous commands you typed.&lt;/p&gt;

&lt;p&gt;As always with unix stuff, it has simple useful basics, and then it can get really hairy. Here are a few examples, from most commonly useful to those things you just won't remember in five minutes.&lt;/p&gt;

# First, the example from DeSandro's post
# !$ references the last argument of the previous command.

mate _posts/2011/2011-04-12-terminal-strings.mdown
git add !$
tumblr !$

# Now more complex, let's copy the second argument
# !! references the last command, and :2 the second arg. 

echo foo bar baz
echo !!:2 # outputs &amp;quot;bar&amp;quot;

# Batshit crazy
# !?baz? references the last command containing baz, :0-1 grabs the two first args

echo !?baz?:0-1 # should output &amp;quot;echo foo&amp;quot;

&lt;p&gt;Now if you've been paying attention, the second example had !! in it that referenced the last command. This one is really useful for all those times you forgot to sudo something. Just type sudo !! like you really mean it, and it will copy your last command after sudo. It does not work if you add cursing to it though.&lt;/p&gt;

&lt;p&gt;So read up those history expansion docs, it's really worth if only to know your options, and if you know other related tricks, please do share in the comments.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/S-RBeFbSE0I" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/S-RBeFbSE0I/terminal-arguments-tricks</link>
			<pubDate>Wed, 13 Apr 2011 19:50:25 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/terminal-arguments-tricks</guid>
			<category>PHP</category>
			<category>Web</category>
			<category>JavaScript</category>
		<feedburner:origLink>http://seld.be/notes/terminal-arguments-tricks</feedburner:origLink></item>
		<item>
			<title>Speaking at ConFoo 2011</title>
			<description>&lt;p&gt;I recently had the pleasure to hear that I would be speaking at the &lt;a href="http://confoo.ca/"&gt;ConFoo&lt;/a&gt; conference. This is a great opportunity for me as I'll finally be able to meet a few US-based guys from the PHP community that I have only ever met virtually.&lt;/p&gt;

&lt;p&gt;Besides that the conference itself also looks great, and covers an insane amount of topics with almost &lt;a href="http://confoo.ca/en/2011/session"&gt;150 sessions&lt;/a&gt; in 3 days, and boatloads of &lt;a href="http://confoo.ca/en/2011/speaker"&gt;speakers&lt;/a&gt;. I will give two talks, one about JavaScript Scopes, Events and other complications to try and illustrate why people really ought to learn JavaScript better. The second will be about frontend web performance, i.e. how to perform well in the browser. It will include a short overview of old classics and then a couple new topics such as the Web Performance APIs and some more advanced optimizations that are hopefully not too widespread like CSS selectors and DOM reflow issues.&lt;/p&gt;

&lt;p&gt;So I hope to see you all in Montreal for what promises to be a huge conference.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/I7yQdTmJH1A" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/I7yQdTmJH1A/speaking-at-confoo-ca</link>
			<pubDate>Fri, 31 Dec 2010 00:20:00 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/speaking-at-confoo-ca</guid>
			<category>PHP</category>
			<category>JavaScript</category>
		<feedburner:origLink>http://seld.be/notes/speaking-at-confoo-ca</feedburner:origLink></item>
		<item>
			<title>Speaking at Symfony Live 2011</title>
			<description>&lt;p&gt;I have the pleasure to announce that I will be speaking at the upcoming &lt;a href="http://www.symfony-live.com/paris"&gt;Symfony Live&lt;/a&gt; conference (Paris edition).&lt;/p&gt;

&lt;p&gt;I've been working with and on Symfony2 for a few months already, both in my spare time and at the office - thanks to &lt;a href="http://liip.ch/"&gt;Liip&lt;/a&gt;, my employer, I can work on Symfony2 patches during office hours :) - and I must say it's really nice to work with already, so if you don't have time to check it out yourself you should come, and if you do have time, then you should still come to share your experiences with other users or newcomers, I'm sure we'll have a great time.&lt;/p&gt;

&lt;p&gt;It should be relevant to anyone with any sort of interest in PHP frameworks by the way, since the brand new Symfony2 framework should be released in its final version during the conference and several sessions will be focused on it. It's a rewrite from scratch so no symfony1 knowledge is required, but if you use symfony1 already you should really come to learn about the framework's future.&lt;/p&gt;

&lt;p&gt;Now with all that said, my talk will not actually be about frameworks, Symfony or PHP! It'll be about JavaScript and why you should learn it, which I'll try to demonstrate by talking about Events, Scopes and other little things that most PHP devs like to ignore but should not. JavaScript code is present in most sites, and most PHP developers will have to write some sooner or later, so you might as well learn how to do it right, and avoid creating an unmaintainable mess in the frontend, now that we've gotten out of the PHP-spaghetti era.&lt;/p&gt;

&lt;p&gt;You can find the full schedule on the &lt;a href="http://www.symfony-live.com/paris/schedule"&gt;conference website&lt;/a&gt;, and don't forget about the last day (hack day), I'm sure that will be an excellent opportunity to talk to people that have been using Sf2 for months, or just ask more questions to speakers you missed during the tighter schedule of the conference days.&lt;/p&gt;

&lt;p&gt;See you in Paris!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/iZF2nE4IGkU" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/iZF2nE4IGkU/speaking-at-symfony-live-2011</link>
			<pubDate>Wed, 15 Dec 2010 03:02:22 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/speaking-at-symfony-live-2011</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/speaking-at-symfony-live-2011</feedburner:origLink></item>
		<item>
			<title>ESI - Full page caching with Symfony2</title>
			<description>&lt;p&gt;Launched about a month ago, &lt;a href="http://techup.ch"&gt;techup.ch&lt;/a&gt; runs on the &lt;a href="http://symfony-reloaded.org"&gt;Symfony2&lt;/a&gt; PHP framework, which is still undergoing heavy development but is already a great framework.&lt;/p&gt;

Full page caching basics

&lt;p&gt;Don't get me wrong, the framework is fast, pages are rendered by our fairly modest server in 40-50ms on average, so it hardly needs optimization. However I still wanted to try and squeeze more speed out of it, and also get a chance to play with cool stuff, so I decided to implement full page caching with ESI into the application.&lt;/p&gt;

&lt;p&gt;The way this works is that you typically install some reverse proxy like &lt;a href="http://www.varnish-cache.org/"&gt;Varnish&lt;/a&gt;, which will sit between the web and your http server. More complex setups might include another http server in front of varnish to gzip output but I won't go into details on that in this post. The purpose of the reverse proxy is that it will cache the output of your application, for as long as you specify in your Cache-Control header. Once a page is cached, it will just return the output to the clients straight, without ever hitting your http server, php, or your application. Needless to say this is ideal for performance. Symfony2 is a great match for this type of cache because it's supported natively as I'll show, and it also implements a reverse proxy layer in php, that can be used for development or on hostings where you can't have access to Varnish. It acts just the same and is automatically turned off if an ESI-capable proxy is added in front of php.&lt;/p&gt;

ESI awesomeness

&lt;p&gt;Of course the issue with caching the entire output is that most sites have areas with dynamic content, especially when users are logged in. This is where ESI comes into play. ESI stands for &lt;a href="http://www.akamai.com/html/support/esi.html"&gt;Edge Side Includes&lt;/a&gt;, and is a &lt;a href="http://www.w3.org/TR/esi-lang"&gt;standard&lt;/a&gt; that defines a way to tell reverse proxies how to assemble pages out of smaller bits, that can be cached for various amounts of time, or fully dynamic.&lt;/p&gt;

&lt;p&gt;So if you take for example an &lt;a href="http://techup.ch/18"&gt;event page&lt;/a&gt; on techup, you have two user-dependent areas, the "login with twitter" button, which turns into "@username" once you're logged in, and the "attend" button is also showing attend or unattend depending on the user viewing the page. Those two areas are ESI includes. What this means for the reverse proxy is that it will first try and fetch the main page content out of its cache, and if found, it will then process the &amp;lt;esi:include src="http://..." /&amp;gt; tags that it finds. Those tags contain the url to a sub-component of the page. So one url will point to an action in one of my controllers that only outputs an attend button, green or red depending on the user viewing it. The rest of the page is still taken out of the cache.&lt;/p&gt;

&lt;p&gt;Each of those sub-components have their own Cache-Control header, which means that you can composite a page with various components that expire after various durations.&lt;/p&gt;

&lt;p&gt;The way this is done in Symfony2 is pretty straightforward. Your controller actions must always return a response object, and all you need for the reverse proxy cache length is to set the shared max age of the response - beware, max age will apply to the entire page, so you really want to use the shared variant. It's as simple as calling $response-&gt;setSharedMaxAge(3600);, 3600 being the length in seconds.&lt;/p&gt;

&lt;p&gt;In your templates, if you use &lt;a href="http://twig-project.org"&gt;Twig&lt;/a&gt;, and you really should with Symfony2, it is also quite easy to define an &amp;lt;esi:include /&amp;gt; tag. You call out the controller/action that you want to execute, give it some parameters, and specify it as being standalone which means it's an ESI include, for example {% render 'FooBundle:Default:attendButton' with ['event_id': event.id], ['standalone': true] %}. For more info on how to set that up feel free to go read the &lt;a href="http://docs.symfony-reloaded.org/guides/cache/http.html"&gt;Symfony2 docs on the topic&lt;/a&gt;.&lt;/p&gt;

Invalidation woes

&lt;p&gt;The tricky part, which is also a slightly controversial topic, is invalidation. In theory if you say that a page or sub-component is cache-able for X seconds, you should just live with it and let it be cached, even if the data changed. Now this is an acceptable downside on really high traffic sites, or in cases where only admins publish content and it doesn't really matter if it takes a few seconds/minutes to appear to the end users. But I like to give our users feedback when they add or change data, and I think they should see it straight away, so I decided to invalidate the cached pages in the proxy whenever the data is modified.&lt;/p&gt;

&lt;p&gt;I will refer you to &lt;a href="http://docs.symfony-reloaded.org/guides/cache/http.html#invalidation"&gt;the docs&lt;/a&gt; as to how to actually setup support for purging (invalidating) caches in your proxy of choice, no point in repeating it all here, but what I want to share is the approach I took on actually managing invalidation. As you &lt;a href="http://martinfowler.com/bliki/TwoHardThings.html"&gt;may know&lt;/a&gt;, invalidation can quickly get very tricky to handle. So what I did is just built centralized methods that contain all the invalidation logic for one domain model. When that model changes, it's passed to the matching method and all the urls that will render it are purged. This at least allows you to keep a good overview of the pages that are affected, and gives you a single point of entry to make adjustments to those invalidation rules.&lt;/p&gt;

// src/Application/FooBundle/Controller/FooController.php
protected function invalidateEvent($event)
{
    $args = array('event' =&amp;gt; $event-&amp;gt;getId(), 'title' =&amp;gt; $event-&amp;gt;getSlug());
    $this-&amp;gt;invalidate('viewEvent', $args);
    $this-&amp;gt;invalidate('home');
}

protected function invalidate($route, $parameters = array())
{
    $url = $this-&amp;gt;router-&amp;gt;generate($route, $parameters, true);

    $context = stream_context_create(array('http'=&amp;gt;array('method'=&amp;gt;'PURGE')));
    $stream = fopen($url, 'r', false, $context);
    fclose($stream);
}

&lt;p&gt;This example implementation will do a PURGE request to the site URL. This only scales if you have one single Varnish instance though. I assume you must do a PURGE request on each if you have a redundant setup, but in this case it might become cleaner to use an external job queue like Gearman to execute those outside of php.&lt;/p&gt;

&lt;p&gt;There are a few gotchas you should consider, especially if you use the Symfony2 reverse proxy and not Varnish. First of all one thing that is fairly obvious is that you must prevent anyone from purging stuff, otherwise attackers could DDoS you with PURGE requests and make your load skyrocket. The second issue is that if you return a 404 code for "Not purged" a.k.a the page wasn't cached, fopen() will throw a php warning, which is really not that nice. For this reason, and since I don't want to care whether the purge happened or not for now, I chose to just respond always with a 200. It could be handled nicer with curl though, if you really need to have a proper response code to your PURGE requests.&lt;/p&gt;

// app/AppCache.php
protected function invalidate(Request $request)
{
    if ($_SERVER['SERVER_ADDR'] !== $request-&amp;gt;getClientIp() || 'PURGE' !== $request-&amp;gt;getMethod()) {
        return parent::invalidate($request);
    }

    $response = new Response();
    if (!$this-&amp;gt;store-&amp;gt;purge($request-&amp;gt;getUri())) {
        $response-&amp;gt;setStatusCode(200, 'Not purged');
    } else {
        $response-&amp;gt;setStatusCode(200, 'Purged');
    }

    return $response;
}

The results

&lt;p&gt;It sounds nice and all, but is it actually working?&lt;/p&gt;

&lt;p&gt;I used &lt;a href="http://jakarta.apache.org/jmeter/"&gt;JMeter&lt;/a&gt; to benchmark the site with and without reverse proxy. Note that I used the integrated Symfony cache layer and not Varnish, so the results would be even better with Varnish since it's written in C and doesn't have to to hit apache and php on every request.&lt;/p&gt;

Before:

/ =&amp;gt; 63req/sec
/86/rails-hock =&amp;gt; 100req/sec
/api/events/upcoming.json =&amp;gt; 70req/sec
/api/event/10.json =&amp;gt; 120req/sec

After:

/ =&amp;gt; 200req/sec *
/86/rails-hock =&amp;gt; 230req/sec
/api/events/upcoming.json =&amp;gt; 100req/sec *
/api/event/10.json =&amp;gt; 800req/sec

* my 20mbps internet line was the bottleneck for those because they have too large response bodies

&lt;p&gt;In short: Holy crap. Now for the two first pages tested, the improvement is "modest" because they include sub-components which are not cacheable, so they always require some full framework cycles. But the last one which is from the API is just amazing, with 8 times more requests processed per second.&lt;/p&gt;

&lt;p&gt;All I can say to conclude is that this is worth playing with, and that Symfony2 really doesn't disappoint with regard to speed. If you have any experience with that kind of setup and want to add anything feel free to do so in the comments, questions are also welcome.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/Mvd3KGxhu3I" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/Mvd3KGxhu3I/esi-full-page-caching-with-symfony2</link>
			<pubDate>Thu, 09 Dec 2010 15:30:00 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/esi-full-page-caching-with-symfony2</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/esi-full-page-caching-with-symfony2</feedburner:origLink></item>
		<item>
			<title>Speaking at the IPC and WebTechCon</title>
			<description>&lt;p&gt;Next week the &lt;a href="http://phpconference.com/"&gt;International PHP Conference&lt;/a&gt; and the &lt;a href="http://webtechcon.de/"&gt;WebTechCon&lt;/a&gt; will happen both in Mainz, Germany. I will speak at both events over the three days and the good news is that the combined 100 sessions are available for attendees of both conferences.&lt;/p&gt;

&lt;p&gt;My only talk as part of the IPC is entitled Of knowledge sharing and the developer quality lifecycle, it's non-technical and will hopefully be more a seeded discussion than a plain presentation. We will talk about the ways to share knowledge within a company in the Gutenberg III room, monday at 11.45.&lt;/p&gt;

&lt;p&gt;My second and third talks will be part of the WebTechCon schedule, but I think they are very good fits for PHP devs nonetheless. On tuesday at 10.15 as part of the JavaScript Day I will talk about JS Events and Scopes. Every web developer should understand those concepts so I would highly recommend you attend if you don't know how the this variable is bound in event listeners, or have never heard of variable hoisting.&lt;/p&gt;

&lt;p&gt;The final talk will be part of the Web Security Day, and touching on the essentials of web security, the things you just can't afford to ignore. The talk is on wednesday at 9.00am however, so plan ahead and avoid getting too drunk if you want to attend :)&lt;/p&gt;

&lt;p&gt;And finally, if anyone wants me to do some informal Symfony2 presentation, I got slides ready and would be very happy to do that, so just come and ask.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/5Fnli7XAYMc" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/5Fnli7XAYMc/speaking-at-the-ipc-and-webtechcon</link>
			<pubDate>Thu, 07 Oct 2010 20:11:38 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/speaking-at-the-ipc-and-webtechcon</guid>
			<category>PHP</category>
			<category>Web</category>
			<category>JavaScript</category>
		<feedburner:origLink>http://seld.be/notes/speaking-at-the-ipc-and-webtechcon</feedburner:origLink></item>
		<item>
			<title>Symfony2 Presentation</title>
			<description>&lt;p&gt;Last weekend at the &lt;a href="http://www.php-unconference.de/"&gt;PHP Unconference&lt;/a&gt; in Hamburg I gave a talk showing an overview of the &lt;a href="http://symfony-reloaded.org/"&gt;Symfony2 framework&lt;/a&gt;. The allowed 45min were way too short to say everything I wanted to say and I didn't really have time to prepare slides so it was a bit messy, apologies for that.&lt;/p&gt;

&lt;p&gt;Yesterday I did an internal training at &lt;a href="http://liip.ch/"&gt;Liip&lt;/a&gt; for a team that will start a Symfony2 project soon, and spent some time building slides. This time I had two hours for the presentation so it went way better I think. And then in the afternoon everyone built a small ultra-basic blog application to get their hands dirty a bit.&lt;/p&gt;

&lt;p&gt;So for those that attended the talk in Hamburg and want to see a bit more, or just anyone interested in the current state of Symfony2, I put the &lt;a href="http://slides.seld.be/?file=2010-09-28+Symfony2.html"&gt;slides&lt;/a&gt; online (use the arrows to move). Note that they will most likely not be up to date anymore in a few weeks/months given that the framework is still ongoing heavy development.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/SGdFe-QpTu8" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/SGdFe-QpTu8/symfony2-presentation</link>
			<pubDate>Wed, 29 Sep 2010 12:35:11 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/symfony2-presentation</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/symfony2-presentation</feedburner:origLink></item>
		<item>
			<title>PHP Console in Your Browser</title>
			<description>&lt;p&gt;So-called interactive modes for scripting languages are commonly used in the command line, and they are great for quick tests, but most of the time when I try something it tends to grow and quickly becomes painful to handle in a CLI one-liner.&lt;/p&gt;

&lt;p&gt;Since I spend most of my days programming PHP I tend to need that a lot and a few years back I wrote a small script that would let me type php code in my browser and execute it. Nothing fancy, but quite useful.&lt;/p&gt;

&lt;p&gt;Over the years a few people got interest seeing me use it and asked for the sources, so instead of repackaging it every time, I thought I'd clean it up, polish a bit, add some features, and put it on github.&lt;/p&gt;

&lt;p&gt;I can't really let you try it on my website for the obvious security implications, but you can look at the screenshot below or to get your hands on it more directly head to &lt;a href="http://github.com/seldaek/php-console"&gt;github (seldaek/php-console)&lt;/a&gt; or grab the &lt;a href="http://github.com/Seldaek/php-console/zipball/master"&gt;zip archive&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Setup is easy, just put it somewhere, and run it in a browser. It only works from localhost, so it's as secure as your machine is, and I can't be held responsible for anything.&lt;/p&gt;

&lt;p&gt;It fetches the execution result with javascript so you can even die() in the script with no problem, and expands tabs to spaces. Press ctrl-enter to submit from the textarea.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://seld.be/_misc/php-console.png" /&gt;&lt;/p&gt;

&lt;p&gt;What do you think?&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/XTJP5Q63BHs" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/XTJP5Q63BHs/php-console-in-your-browser</link>
			<pubDate>Thu, 23 Sep 2010 18:05:55 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/php-console-in-your-browser</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/php-console-in-your-browser</feedburner:origLink></item>
		<item>
			<title>Switching on my WLAN with my phone</title>
			<description>&lt;p&gt;It all started when my router began to crash every few days. All my connections would drop, and misery ensued. I disabled WLAN/Wi-Fi and it stopped crashing, so I was happy. But then my laptop's range became limited, and my phone sucked up my precious data plan, which wasn't ideal either.&lt;/p&gt;

&lt;p&gt;Now the router I got is quite specific, it's remote controlled by the ISP and some settings are accessible only via their web interface, which means that enabling/disabling WLAN takes 3 minutes of waiting and is very annoying. I recently got fed up and decided to try and script the whole process.&lt;/p&gt;

&lt;p&gt;I wrote a php script that does the job with curl, and now I can call it from my phone's browser to enable or disable the WLAN at any time, from anywhere.&lt;/p&gt;

&lt;p&gt;So if you are interested, although it will only work as-is for Swisscom routers in Switzerland, I attached the full script I used below. You can read on for hints though since I guess it could be applicable to any other router configured via a web interface, and if it's only accessible from the local network you could have the script run on a home server that is accessible from the outside and would basically be a proxy.&lt;/p&gt;

&lt;p&gt;A few parts are noteworthy though, the whole CURL configuration was a bit tricky, especially since I failed to RTFM correctly on the whole COOKIE stuff, until &lt;a href="http://matthewturland.com/"&gt;Elazar&lt;/a&gt; (you can buy his book on web scraping for more details on this whole topic) pointed me to the right setting, CURLOPT_COOKIEJAR.&lt;/p&gt;

&lt;p&gt;An interesting thing is that you can pass 'php://memory' to the COOKIEJAR option, which is a &lt;a href="http://php.net/manual/en/wrappers.php.php"&gt;php stream&lt;/a&gt; creating a virtual file somewhere in memory. It's good for throw-away stuff if you don't want to mess with the filesystem. Also CURLOPT_SSL_VERIFYPEER is a very good thing to have if you want to be lazy about SSL setup, it basically skips the entire certificate verification process.&lt;/p&gt;

&lt;p&gt;And for people trying to implementing this kind of stuff with other routers, most of them use HTTP Authentication, so you most likely will need the CURLOPT_USERPWD option, providing it your user/pass couple as: "username:password".&lt;/p&gt;

&amp;lt;?php

$user = 'foo';
$pass = 'bar';
$state = isset($_GET['state']) ? $_GET['state'] : false;

if ($state !== 'on' &amp;amp;&amp;amp; $state !== 'off') {
    die('Unknown or missing state '.$state);
}

function execRequest($ch)
{
    $res = curl_exec($ch);
    if (!$res) {
        var_dump(curl_error($ch));
        die('ABORTED');
    }
    return $res;
}

$ch = curl_init();
curl_setopt_array($ch, array(
    // base curl config
    CURLOPT_AUTOREFERER =&amp;gt; true,
    CURLOPT_FOLLOWLOCATION =&amp;gt; true,
    CURLOPT_RETURNTRANSFER =&amp;gt; true,
    CURLOPT_SSL_VERIFYPEER =&amp;gt; false,
    CURLOPT_UNRESTRICTED_AUTH =&amp;gt; true,
    CURLOPT_COOKIEJAR =&amp;gt; 'php://memory',
    // login request
    CURLOPT_URL =&amp;gt; 'https://sam.sso.bluewin.ch/my/data/MyData?lang=en',
    CURLOPT_POST =&amp;gt; true,
    CURLOPT_POSTFIELDS =&amp;gt; http_build_query(array('username' =&amp;gt; $user, 'password' =&amp;gt; $pass)),
));

execRequest($ch);

curl_setopt_array($ch, array(
    CURLOPT_URL =&amp;gt; 'https://sam.sso.bluewin.ch/my/data/ModemMgmtService?lang=en&amp;amp;mode=overview',
    CURLOPT_POSTFIELDS =&amp;gt; null,
    CURLOPT_HTTPGET =&amp;gt; true,
));

execRequest($ch);
sleep(3);

curl_setopt($ch, CURLOPT_URL, 'https://sam.sso.bluewin.ch/my/data/ModemMgmtService?lang=en&amp;amp;mode=changewlanstatus&amp;amp;WLAN_STATE='.$state);

$res = execRequest($ch);

echo strstr($res, 'Confirmation: WLAN data have been changed.') ? 'Done ('.$state.')' : 'FAILED';&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/zMI7zeFH-7g" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/zMI7zeFH-7g/switching-on-my-wlan-with-my-phone</link>
			<pubDate>Wed, 22 Sep 2010 20:04:48 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/switching-on-my-wlan-with-my-phone</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/switching-on-my-wlan-with-my-phone</feedburner:origLink></item>
		<item>
			<title>Introducing Slippy - HTML Presentations</title>
			<description>&lt;p&gt;Slippy is a HTML Presentation library written with jQuery, it takes a html file in and plays it in any browser.&lt;/p&gt;

&lt;p&gt;It is optimal for programming-related talks since it includes a syntax highlighter and is very easy to use since it's just standard html markup with a few classes to enable specific functions.&lt;/p&gt;

&lt;p&gt;If you are making a talk about Javascript, it can even execute your code samples live and displays alert() boxes nicely instead of using the ugly browser dialog, which -I tried it today- works quite well to prove your point interactively.&lt;/p&gt;

&lt;p&gt;You can find the sources on &lt;a href="http://github.com/Seldaek/slippy"&gt;github&lt;/a&gt;, view the &lt;a href="http://slides.seld.be/?file=2010-05-30+Example.html"&gt;example slide deck&lt;/a&gt; which includes some documentation or view the slides for the small talk I gave today about Javascript Events on my Slippy &lt;a href="http://slides.seld.be/"&gt;slide repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Obviously feedback is very much welcome and even though it's not perfect yet, I hope it'll be useful to others. More docs and styling fixes (the dark grey background wasn't too visible on a projector, my bad) should come soon as I have more talks planned, and the slide repository page will receive some love as well when I have time.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/Qd4OgS2Rg6U" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/Qd4OgS2Rg6U/introducing-slippy-html-presentations</link>
			<pubDate>Thu, 03 Jun 2010 17:54:34 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/introducing-slippy-html-presentations</guid>
			<category>News</category>
			<category>PHP</category>
			<category>JavaScript</category>
		<feedburner:origLink>http://seld.be/notes/introducing-slippy-html-presentations</feedburner:origLink></item>
		<item>
			<title>Unpredictable hashes for humans</title>
			<description>&lt;p&gt;It is not uncommon for web developers to have to generate random ids or hashes, for instance large scale project or frameworks may want to implement their own PHP session handlers either completely abstracted in their API, or overloading PHP's internal API using &lt;a href="http://php.net/session_set_save_handler"&gt;session_set_save_handler()&lt;/a&gt;. If you do so, unless you want to entrust PHP's core to do it, one thing you will have to take care of is generating unique session ids to send as a cookie to your users, allowing the session to persist. Other common use cases for such unique hashes is to generate CSRF tokens to insert in forms or URLs, and finally authentication tokens for email validation or such.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://seld.be/notes/unpredictable-hashes-for-humans"&gt;Proceed to the article&lt;/a&gt; to learn more about it in a -hopefully- easy to grasp way, this wasn't written for security experts but rather any PHP coder out there that is remotely interested in security, and you really should.&lt;/p&gt; &lt;a href="http://seld.be/notes/unpredictable-hashes-for-humans"&gt;Read more...&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/Zo6NR0A1j-w" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/Zo6NR0A1j-w/unpredictable-hashes-for-humans</link>
			<pubDate>Mon, 10 May 2010 08:24:32 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/unpredictable-hashes-for-humans</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/unpredictable-hashes-for-humans</feedburner:origLink></item>
		<item>
			<title>Project management in PHP with Arbit</title>
			<description>&lt;p&gt;I would like to attract everyone's attention on the 0.3-alpha release of &lt;a href="http://arbitracker.org/"&gt;Arbit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For those that do not know Arbit yet, it is a project management and issue tracker build in PHP. It uses CouchDB as a storage backend by default but work to support RDBMS via PDO is in progress.&lt;/p&gt;

&lt;p&gt;Interestingly, it also provides experimental support for continuous integration, also fully PHP-based, unlike other popular solutions. This is not enabled by default in this release since it isn't fully ready but feel free to stop by the irc channel (#arbit on freenode) to know more.&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://arbitracker.org/news/0010_released_0_3-alpha.html"&gt;full announcement&lt;/a&gt; contains details about what we fixed and implemented in the 0.3.&lt;/p&gt;

Get involved!

&lt;p&gt;As all open source projects, Arbit needs your help, I joined the project early this year and we have had a few contributions from other people since then, but we can always use more help. Therefore if you are interested and wish to take part by developing new features, fixing bugs or at least &lt;a href="http://tracker.arbitracker.org/arbit"&gt;reporting them&lt;/a&gt;, please don't hesitate and get in touch! And as &lt;a href="http://naramore.net/blog/why-people-don-t-contribute-to-os-projects-and-what-we-can-do-about-it"&gt;Elizabeth Naramore&lt;/a&gt;'s article recently pointed out most people are afraid to contribute, I would like to say that no matter how skilled you are, contributions are welcomed. We will provide assistance if needed.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/BnTKeQk-mmE" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/BnTKeQk-mmE/project-management-in-php-with-arbit</link>
			<pubDate>Mon, 05 Apr 2010 13:21:03 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/project-management-in-php-with-arbit</guid>
			<category>News</category>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/project-management-in-php-with-arbit</feedburner:origLink></item>
		<item>
			<title>Including open source in the hiring process</title>
			<description>&lt;p&gt;We were discussing the difficulty of the hiring process from a company point of view last week at the &lt;a href="http://github.com/"&gt;github&lt;/a&gt; &lt;a href="http://www.symfony-live.com/github-meetup"&gt;meetup&lt;/a&gt; in Paris, and more specifically how hard it is to get quality people without relying on test assignments, which most agree are total bullshit, or on a couple of interviews, which can also be very misleading since it depends a lot on the person's social skills, or lack thereof.&lt;/p&gt;
&lt;p&gt;One big thing that is overlooked in my opinion is participation in open source projects, be it a single patch or long term commitment. As an employer you can see that the guy has enough interest in programming in general that he has taken the extra step to contribute something, and also that his work was accepted by a peer as valid. It is obviously not the full story and we all know some open source projects' code is utter crap (disclaimer, this also applies to closed source software, you just don't get to see it), but I still believe it gives you a better metric than just some code the guy did (or didn't) code and is presenting to you during an interview.&lt;/p&gt;
&lt;p&gt;You can use &lt;a href="http://www.ohloh.net/"&gt;ohloh&lt;/a&gt; to track your &lt;a href="http://www.ohloh.net/accounts/Seldaek"&gt;open-source-CV of sorts&lt;/a&gt;, and I would very much like it if more companies would push the open source involvement forward in their job ads, probably not as a requirement but at least as a big plus. It would benefit both companies that are trying to hire good people, and good people to be recognized. Of course it would also benefit the open source community at large if the work you do there gets you more recognition, pushing more people to take the leap to contribute. It is definitely helping already, if only for the contacts you get, which are always good when looking for a job, but increasing the perceived benefit of contributing to the open source world would be great, so I would very much like if all you HR people would give it a thought, and other readers please mention it to HR in your company, or your friends looking for work, your little brother starting to study, anyone can contribute.&lt;/p&gt;
&lt;p&gt;Any other ideas on how to find great developers? Is your company using open source as a criteria? Did it help?&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/fdDg5htdepU" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/fdDg5htdepU/including-open-source-in-the-hiring-process</link>
			<pubDate>Mon, 22 Feb 2010 20:21:24 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/including-open-source-in-the-hiring-process</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/including-open-source-in-the-hiring-process</feedburner:origLink></item>
		<item>
			<title>Symfony Live 2010 - Symfony2, speaking and stuff</title>
			<description>&lt;p&gt;Overall the &lt;a href="http://www.symfony-live.com/"&gt;conference&lt;/a&gt; was pretty interesting since I don't have a lot of experience with &lt;a href="http://www.symfony-project.org/"&gt;symfony&lt;/a&gt; I learned quite a bunch of things about it's usage. I also met a lot of nice people, and ended the trip yesterday evening at the &lt;a href="http://github.com/"&gt;github&lt;/a&gt; &lt;a href="http://www.symfony-live.com/github-meetup"&gt;meetup&lt;/a&gt;, after going for food with a couple &lt;a href="http://phpbb.com"&gt;phpBB&lt;/a&gt; guys who are really much nicer than the forum software they stand for. They were also very open to us bashing phpBB and seem to be headed towards a &lt;a href="http://area51.phpbb.com/"&gt;brighter future&lt;/a&gt; for the next version, which I'm sure nobody will complain about.&lt;/p&gt;
&lt;p&gt;I also had my first session at a conference, accompanying &lt;a href="http://pooteeweet.org/"&gt;Lukas&lt;/a&gt; though so I wasn't really flying by myself yet but it was still a nice and interesting (and stressful) experience that I will try to renew. We didn't get all that much &lt;a href="http://joind.in/talk/view/1410/"&gt;feedback&lt;/a&gt; by the way so feel free to do so (also here if you are too lazy to register on joind.in), the organizers need it and obviously I wonder how the talk was received as well.&lt;/p&gt;
&lt;p&gt;As for &lt;a href="http://www.symfony-reloaded.org/"&gt;Symfony 2&lt;/a&gt; (which now comes with a capital S please), I kind of saw the flexibility coming since we already implemented the dependency injection container in our &lt;a href="http://okapi.liip.ch/"&gt;Okapi framework&lt;/a&gt; at &lt;a href="http://liip.ch/"&gt;Liip&lt;/a&gt;, but I was still impressed by the jump away from symfony (1) Fabien conceded, many people would have tried to keep more BC at the cost of going forward, and I'm really glad he didn't, I think it will pay in the long run. The new version of the framework will basically be able to be totally ripped apart to fit your needs better if you have high performance requirements, which was the major pain point of symfony 1 as far as I'm concerned, and one of our reasons to keep working on Okapi which is pretty much a baseline micro-framework you can build upon. We will have to see if adopting Symfony in its place will make sense, but it sounds promising and it would offload some maintenance away from us which is always good.&lt;/p&gt;
&lt;p&gt;Obviously Symfony 2 isn't going to be stable for a while, and there are some rough edges that still need to be discussed and improved, mostly in the way bundles are handled imo, but it looks very good already and I'll definitely give it a try asap. I would also encourage everyone to do so, especially framework developers, because the dependency injection is a pretty awesome thing to have, both for the testability of code and flexibility of the development process. Although if it's your only interest in it, checking out the Okapi 2 &lt;a href="http://svn.liip.ch/repos/public/okapi2"&gt;core&lt;/a&gt; (or the &lt;a href="http://liip.to"&gt;liip.to&lt;/a&gt; app &lt;a href="http://svn.liip.ch/repos/public/misc/liipto/branches/okapi2/"&gt;ported to use it&lt;/a&gt;) is probably easier as there is less code to read, and we didn't add any of the abstraction to the dependency injection layer that Symfony 2 has.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/mLNc01a02Qw" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/mLNc01a02Qw/symfony-live-2010-symfony2-speaking-and-stuff</link>
			<pubDate>Thu, 18 Feb 2010 19:48:42 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/symfony-live-2010-symfony2-speaking-and-stuff</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/symfony-live-2010-symfony2-speaking-and-stuff</feedburner:origLink></item>
		<item>
			<title>Dwoo is better than Twig</title>
			<description>&lt;p&gt;It's lame catchy title day, a more appropriate one would be &amp;quot;Think for yourself&amp;quot;, but I want to get my point across.&lt;/p&gt;
&lt;p&gt;This is a general purpose idea of course, I don't think there is any case in your life where you shouldn't think for yourself, but this particular post is about programming.&lt;/p&gt;
&lt;p&gt;I just read someone (and I won't name names, it's not relevant) that was pondering using &lt;a href="http://dwoo.org"&gt;Dwoo&lt;/a&gt; or &lt;a href="http://www.twig-project.org"&gt;Twig&lt;/a&gt; in his CMS, who ended up picking Twig because, and I quote: &amp;quot;but twig says they're better than dwoo so ...&amp;quot;. Now I sincerely couldn't care less if someone decides to use something else over Dwoo - which I'm working on in case you wouldn't know. It's your own choice, and even I wouldn't say Dwoo is the best choice for every damned purpose out there.&lt;/p&gt;
&lt;p&gt;What bothers me though, is that obviously the guy read Fabien Potencier's article about php &lt;a href="http://fabien.potencier.org/article/34/templating-engines-in-php"&gt;template engines&lt;/a&gt;, which was obviously not so much of an objective post, but that has already been discussed so let's not go to deep into it. Anyway, the guy most likely read it, and all it says about Dwoo to dismiss it is &amp;quot;Unfortunately, Dwoo has no sandbox feature and its core is not flexible enough&amp;quot;. So.. out of this most enlightening comment, you decide to trust Fabien and just assume Twig is better? I just don't get it.&lt;/p&gt;
&lt;p&gt;So again, please, just think for yourself.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/uJiQSIBWKSo" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/uJiQSIBWKSo/dwoo-is-better-than-twig</link>
			<pubDate>Tue, 08 Dec 2009 09:58:14 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/dwoo-is-better-than-twig</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/dwoo-is-better-than-twig</feedburner:origLink></item>
		<item>
			<title>Major glob() fail</title>
			<description>&lt;p&gt;I just had the pleasure of discovering another of PHP's little quirks and since it's been almost a year since my last post, I thought it would be a good occasion.&lt;/p&gt;
&lt;p&gt;Working on some personal project that lists a bunch of stuff on my hard drive, I found out that directories that contain square brackets (those []) don't return any results for the simple reason that glob reads [stuff] as a character class, just like in regular expressions. When you know it it makes perfect sense, but when you don't, the &lt;a href="http://docs.php.net/manual/en/function.glob.php"&gt;documentation&lt;/a&gt; is really not so helpful. Of course it mentions libc's glob() and unix shells, but not everyone knows what that implies at first glance.&lt;/p&gt;
&lt;p&gt;My first reaction when I noticed that those directories were missing was to try and escape them with backslashes, which works on unix systems, but not on windows since the backslash is the directory separator. The cross platform solution is to enclose them in brackets (i.e. [[]), which effectively creates a character class with only the opening bracket in it, so it matches correctly.&lt;/p&gt;
&lt;p&gt;I then wrote this glob_quote function which, just like preg_quote, escapes the meta characters that glob uses.&lt;/p&gt;
function glob_quote($str) { 
    $from = array( '[', '*', '?'); 
    $to = array('[[]', '[*]', '[?]'); 
    return str_replace($from, $to, $str); 
}
&lt;p&gt;Another detail worth noting while I'm at it is that this problem also occurs when you do glob('*.txt') if your &lt;a href="http://docs.php.net/manual/en/function.getcwd.php"&gt;cwd&lt;/a&gt; contains brackets, since in this case the cwd is pre-pended to the pattern, the solution being to escape it as well as such:&lt;br /&gt;glob(glob_quote(getcwd()).DIRECTORY_SEPARATOR.'*.txt');&lt;/p&gt;
&lt;p&gt;That's it for today, so until next year..&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/wyS__yhVoqM" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/wyS__yhVoqM/major-glob-fail</link>
			<pubDate>Wed, 02 Dec 2009 20:15:40 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/major-glob-fail</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/major-glob-fail</feedburner:origLink></item>
		<item>
			<title>Multiton base class</title>
			<description>&lt;p&gt;While I like the &lt;a href="http://en.wikipedia.org/wiki/Singleton_pattern"&gt;Singleton&lt;/a&gt; pattern every now and then, I prefer the flexibility that the &lt;a href="http://en.wikipedia.org/wiki/Multiton"&gt;Multiton&lt;/a&gt; potentially offers, and well it's just an extended version of the Singleton, so it's "compatible" with the Singleton model.&lt;/p&gt;
&lt;p&gt;Anyway, to the point, PHP5.3 is coming, and with Late Static Binding you can do a base Multiton (or Singleton if you insist), which wasn't possible before. Now I like this very much because you can simply extend it rather than rewriting those (few, I know, but still) lines each time.  &lt;/p&gt; &lt;a href="http://seld.be/notes/multiton-base-class"&gt;Read more...&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/lKxaFkzCTIQ" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/lKxaFkzCTIQ/multiton-base-class</link>
			<pubDate>Tue, 23 Dec 2008 01:40:00 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/multiton-base-class</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/multiton-base-class</feedburner:origLink></item>
		<item>
			<title>Dwoo v1.0 is out</title>
			<description>&lt;p&gt;Now that Dwoo's user base has grown a bit and that I've received enough feedback and fixed quite a bunch of bugs and design flaws, I feel confident it's time to go stable, so here comes &lt;a href="http://dwoo.org/download"&gt;Dwoo 1.0.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For those that missed it, Dwoo is a template engine compatible with Smarty templates, with a lot of new features and syntax sugar and a new PHP5 codebase, if you want to read more I suggest you have a look at &lt;a href="http://seld.be/notes/who-let-the-dwoo-out"&gt;my earlier post&lt;/a&gt;, and its &lt;a href="http://dwoo.org"&gt;website&lt;/a&gt;.&lt;/p&gt; &lt;a href="http://seld.be/notes/dwoo-v1-0-is-out"&gt;Read more...&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/UZwNHgGePA0" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/UZwNHgGePA0/dwoo-v1-0-is-out</link>
			<pubDate>Fri, 24 Oct 2008 23:00:00 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/dwoo-v1-0-is-out</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/dwoo-v1-0-is-out</feedburner:origLink></item>
		<item>
			<title>Who let the Dwoo out ?</title>
			<description>&lt;p&gt;Four months have passed since I started on this project and I finally feel that it is stable enough to make an announcement and have more people trying it.&lt;/p&gt;&lt;p&gt;So what is it ? &lt;a href="http://dwoo.org/"&gt;Dwoo&lt;/a&gt; is a PHP5 template engine. Another one you might think, indeed but with every new project comes a new vision, and hopefully you will like mine.&lt;/p&gt; &lt;a href="http://seld.be/notes/who-let-the-dwoo-out"&gt;Read more...&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/YToeTQBw_XM" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/YToeTQBw_XM/who-let-the-dwoo-out</link>
			<pubDate>Tue, 13 May 2008 00:47:41 +0200</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/who-let-the-dwoo-out</guid>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/who-let-the-dwoo-out</feedburner:origLink></item>
		<item>
			<title>PHP RSS Generator</title>
			<description>&lt;p&gt;As part of my work on my CMS (and website as this site is running on it), I decided to code a RSS feed builder. This could easily be made as a standalone class so I thought I would &lt;a href="http://seld.be/code/rss-generator/downloads"&gt;share it&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The package is made of two files, one being the feed and the other one representing an item (i.e. an article). It works nicely as you can see from all the feeds I set up in here, and it supports the RSS2.0 standard entirely. &lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/_o1JMpyq3u4" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/_o1JMpyq3u4/php-rss-generator</link>
			<pubDate>Tue, 18 Dec 2007 20:44:43 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/php-rss-generator</guid>
			<category>News</category>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/php-rss-generator</feedburner:origLink></item>
		<item>
			<title>ActionScript 3 Language file for GeSHi</title>
			<description>&lt;p&gt;As I wanted to post AS3 code with syntax coloring, I researched it a bit and found &lt;a href="http://qbnz.com/highlighter/"&gt;GeSHi&lt;/a&gt;, a PHP syntax highlighter, for which you can create language files quite easily. There is currently no AS3 file available in GeSHi though, so I decided to build one.&lt;/p&gt;&lt;p&gt;For now it's available here, but I hope it will make it into GeSHi's next releases. It's following the FlexBuilder2 colors for the default style, but it is stylable with CSS.&lt;/p&gt;&lt;p&gt;Should you see any missing keyword or anything, please contact me so I can update it.&lt;/p&gt;&lt;p&gt;Edit : I updated this file to v1.0.1, I was so focused on scraping data that I forgot the &amp;quot;this&amp;quot; keyword in the process, that's now fixed. &lt;/p&gt;&lt;p&gt;Download : &lt;a href="http://seld.be/code/as3-for-geshi/downloads"&gt;AS3 Language file&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/php-blog-seld/~4/BaiKOpaLCho" height="1" width="1"/&gt;</description>
			<link>http://feeds.seld.be/~r/php-blog-seld/~3/BaiKOpaLCho/actionscript-3-language-file-for-geshi</link>
			<pubDate>Fri, 30 Nov 2007 17:36:28 +0100</pubDate>
			<guid isPermaLink="false">http://seld.be/notes/actionscript-3-language-file-for-geshi</guid>
			<category>ActionScript</category>
			<category>PHP</category>
		<feedburner:origLink>http://seld.be/notes/actionscript-3-language-file-for-geshi</feedburner:origLink></item>
	</channel>
</rss>
