<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>P'unk Avenue Window</title>
	<atom:link href="http://window.punkave.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://window.punkave.com</link>
	<description></description>
	<lastBuildDate>Thu, 11 Mar 2010 18:07:33 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Beware of Geeks Bearing Gifts</title>
		<link>http://window.punkave.com/2010/03/11/beware-of-geeks-bearing-gifts/</link>
		<comments>http://window.punkave.com/2010/03/11/beware-of-geeks-bearing-gifts/#comments</comments>
		<pubDate>Thu, 11 Mar 2010 18:06:38 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[history]]></category>
		<category><![CDATA[social networking]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://window.punkave.com/2010/03/11/beware-of-geeks-bearing-gifts/</guid>
		<description><![CDATA[@odysseus is now mayor of Shiny Wooden Horse Disco Everybody Come Check It Out! on #foursquare!
@agamemnon just checked in from Shiny Wooden Horse Disco Everybody Come Check It Out! via gowalla
@cassandra IT&#8217;S A TRAP!
@priam @cassandra SHUT UP ADMIRAL AKBAR THIS HORSE IS AWESOME
@agamemnon is now mayor of Sacked Ruin of Troy BOOYEAH! on #foursquare!
]]></description>
			<content:encoded><![CDATA[<p>@odysseus is now mayor of Shiny Wooden Horse Disco Everybody Come Check It Out! on #foursquare!</p>
<p>@agamemnon just checked in from Shiny Wooden Horse Disco Everybody Come Check It Out! via gowalla</p>
<p>@cassandra IT&#8217;S A TRAP!</p>
<p>@priam @cassandra SHUT UP ADMIRAL AKBAR THIS HORSE IS AWESOME</p>
<p>@agamemnon is now mayor of Sacked Ruin of Troy BOOYEAH! on #foursquare!</p>
]]></content:encoded>
			<wfw:commentRss>http://window.punkave.com/2010/03/11/beware-of-geeks-bearing-gifts/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Faster, PHP! Kill! Kill!</title>
		<link>http://window.punkave.com/2010/03/08/faster-php-kill-kill/</link>
		<comments>http://window.punkave.com/2010/03/08/faster-php-kill-kill/#comments</comments>
		<pubDate>Tue, 09 Mar 2010 03:39:41 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Apostrophe]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[apc]]></category>
		<category><![CDATA[fastcgi]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://window.punkave.com/?p=1029</guid>
		<description><![CDATA[
PHP is easy&#8230; as programming languages go, that is. You can build sites in a real hurry. 
With frameworks like Symfony, you can build them faster still, and follow modern programming practices at the same time. 
And Apostrophe strips away yet another layer of effort if your site calls for a content management system.
Yes, Java [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://window.punkave.com/wp-content/uploads/2010/03/Screen-shot-2010-03-08-at-11.04.17-PM.png" alt="" title="Screen shot 2010-03-08 at 11.04.17 PM" width="216" height="284" class="alignnone size-full wp-image-1036" /><br />
PHP is easy&#8230; as programming languages go, that is. You can build sites in a real hurry. </p>
<p>With frameworks like <a href="http://www.symfony-project.org/">Symfony</a>, you can build them faster still, and follow modern programming practices at the same time. </p>
<p>And <a href="http://www.apostrophenow.com/">Apostrophe</a> strips away yet another layer of effort if your site calls for a content management system.</p>
<p>Yes, Java has more raw speed, all else being equal (which it never is). But as the LISP programmers used to say, &#8220;a moment of regret, a lifetime of convenience.&#8221;</p>
<p>Still, sooner or later success catches up with you and you want your site to cope with Serious Traffic&#8230; or cope with moderate traffic on a cheap virtual machine&#8230; or at the very least, not be dog-slow with just a handful of users on the system.</p>
<p>There&#8217;s a lot of advice out there about optimizing PHP code, some of it well worth your while. And there&#8217;s excitement about HipHop, Facebook&#8217;s new native code compiler for PHP. But these are drastic steps that require you to rewrite your code or adopt less proven and more awkward ways of delivering your code. </p>
<p>Justified? Sure, sometimes, on the biggest projects in the world (like Facebook) (*). But as Donald Knuth says, &#8220;premature optimization is the root of all evil.&#8221; That&#8217;s because tweaking your code for speed&#8217;s sake usually makes it harder to maintain and less adaptable to new requirements.</p>
<p>What most developers don&#8217;t realize is that there are three major factors that typically slow down PHP projects based on frameworks (like Symfony or, <em>sigh</em>, Drupal) so much that code profiling and database query redesign don&#8217;t even have a chance to become relevant factors. Fix these things first before you worry about other issues:</p>
<p><strong>1. Compiling code over and over and over.</strong> Would you wait for your Mac to recompile MacOS X from source code every time you boot it up? Of course not. How about every time you fill out a dialog box? That&#8217;s pretty much what you&#8217;re doing every time you access a PHP-driven website that doesn&#8217;t use a bytecode cache.</p>
<p><strong>2. Waiting and waiting and waiting for web browsers to make another request,</strong> pinning down web server processes that your other users need. By default Apache usually lets browsers hold on to a connection for up to 15 seconds just in case they ask for more. This is a good thing in many ways, but 15 seconds is far too long. Which leads us to #3:</p>
<p><strong>3. Tying up a &#8220;fat&#8221; web server process with PHP on board for every request,</strong> even requests for the zillions of little static PNGs that probably make up your page design. (**)  A typical Apache web server configuration with <tt>mod_php</tt> suffers from this flaw, fatally limiting the number of simultaneous users you can handle. </p>
<p>So what can we do about these problems? Quite a bit as it turns out. I&#8217;ll start with the low-hanging fruit and move on to the tougher stuff. The fascinating common thread with all of these suggestions: no changes at all to your PHP code.</p>
<p><span id="more-1029"></span></p>
<h2>Stop Recompiling Your World: APC</h2>
<p>APC, aka the Alternative PHP Cache, is a bytecode cache. What does that mean and what good does it do?</p>
<p>Today, when users visit your website, your web server struggles to translate all of your PHP code into a more manageable &#8220;bytecode.&#8221; And only then does it actually run the bytecode and output the page.</p>
<p>Bytecode isn&#8217;t true native machine code and doesn&#8217;t run as fast as compiled Java, and certainly not as fast as compiled C. That&#8217;s why Facebook&#8217;s HipHop project seeks to convert PHP code to C++, and then compile that to native code. But that&#8217;s a drastic step with far-reaching consequences for your project. Before you think about HipHop, try a simpler fix: stop throwing away the bytecode and starting over from zero on every new request that comes to the site! </p>
<p>In our experience, enabling APC reduces both memory usage and the time required to respond to a request by vast amounts. Even a simple action in Symfony or Drupal can implicitly require that quite a bit of PHP code be compiled. With APC, all of that code <i>stays</i> compiled, and that simple action actually runs quickly and simply. </p>
<p>APC is simply indispensable for serious PHP programming. Turn it on. Make sure it&#8217;s really working. And enjoy dramatic improvements in your social life, hair and skin.</p>
<h3>Installing APC</h3>
<p>Depending on your system, APC may already be installed or even, if you are very lucky, activated correctly. Virtual machine hosting from <a href="http://www.servergrove.com/">ServerGrove</a> comes with APC &#8220;out of the box.&#8221; Most shared hosting probably won&#8217;t because it increases the amount of memory pinned down for each user on the shared server, but if you care about your website&#8217;s speed, you&#8217;re not using shared hosting. Get a small, cheap virtual machine from ServerGrove instead (or if you must, do it the hard way and set up a Slicehost or linode virtual machine; you&#8217;ll learn a lot about system administration).</p>
<p>On our slicehost VMs, we found it necessary to install APC with pecl, the PHP package manager for extensions (like PEAR, but different in that they are not written in plain PHP):</p>
<pre>
pecl install apc
</pre>
<p>We then enable it in php.ini (you won&#8217;t be able to do this in a .htaccess file):</p>
<pre>
; Must enable APC otherwise we compile all of Symfony on every hit
extension=apc.so
; Give APC enough shared memory to handle Symfony, etc.
apc.shm_size = 48
apc.include_once_override = 1
apc.mmap_file_mask = /tmp/apc.XXXXXX
</pre>
<p>Then restart Apache:</p>
<pre>
apachectl restart
</pre>
<p>And you&#8217;re off to the races. The first page access will be as slow as ever, but subsequent accesses will be much, much faster. You can also check whether you&#8217;re saving memory and time using Symfony&#8217;s debugging toolbar. </p>
<p>Your system also likely has an <a href="http://www.electrictoolbox.com/apc-php-cache-information/">APC status page</a> that you can copy to your website to check out the current status of APC. It&#8217;s fun to play with.</p>
<h2>Dialing Back Keepalive: Too Much of a Good Thing</h2>
<p>In the beginning there was NCSA Mosaic, and it was pretty cool, all things considered. But it wasn&#8217;t fast.</p>
<p>Mosaic made only one request of a web server at a time. Trying to do more probably seemed rude. Besides, lots of web servers couldn&#8217;t cope with more than one request a time <i>at all</i>. But that would only happen if two users came along at once, and that was never gonna happen, right? </p>
<p>Ah, those were the (sad, lame, pathetic) days. Scroll to the present. Typical web browsers first request the page, say &#8220;hang on a minute,&#8221; open perhaps five <em>more</em> connections besides and then make note of all the images, JavaScript files, CSS files and other embedded bric-a-brac and start requesting all of those things. Consecutively and in parallel. Really a lot.</p>
<p>The Apache web server is actually very good at handling this. But by default, it is very generous: it lets the browser leave it hangin&#8217; for fifteen seconds. That means an Apache process and all of the memory it requires is tied up for all of that time, just doin&#8217; nothin&#8217;. And that&#8217;s bad news.</p>
<p>Fortunately it&#8217;s very easy to fix this in your <code>httpd.conf</code> file (actually, in our setups, this is usually in <code>apache2.conf</code>):</p>
<pre>
KeepAliveTimeout 2
</pre>
<p>Restart Apache:</p>
<pre>
apachectl restart
</pre>
<p>And you&#8217;ll find that your site can suddenly cope with higher loads. That&#8217;s because your&#8217;re not tying up valuable resources waiting around for more requests from browsers that are <i>already</i> using and reusing five other connections to your server. The browser is welcome to promptly request more files and finish its business with you, but not to stand around twiddling its thumbs while you wonder if you have a date or not.</p>
<p>You can turn keepalive off entirely, but doing so forces the browser to &#8220;call back&#8221; for each and every little image file on your page, which is a terrible idea. So use it&#8230; just don&#8217;t let it abuse you.</p>
<h2>MaxClients: Does This <code>mod_php</code> Make My Butt Look Big?</h2>
<p><code>mod_php</code> is fast. Unlike the old-fashioned &#8220;PHP as CGI&#8221; approach, <code>mod_php</code> keeps the PHP interpreter itself loaded in memory and ready to rock at all times, and that&#8217;s good.</p>
<p>But <code>mod_php</code> is also a pig. For a big-deal web application that wrangles lots of database objects, or makes thumbnails of uploaded images, or does pretty much anything cool, it&#8217;s not uncommon for a PHP process to use 50MB of memory or more. More miserly code can still hit 30MB without much difficulty.</p>
<p>We first ran into this problem when we wanted to move smaller client sites onto small VMs, so that they could have well-secured, highly-reliable web hosting at an affordable price. They tended to bog down and become unusable quickly.</p>
<p>We discovered that dialing back keepalive helped tremendously with the problem. And we also learned to do the math and figure out how many reasonably fat PHP Apache processes would really fit in the memory available to the slice, and configure Apache to run no more than that. In <code>httpd.conf</code>:</p>
<pre>
MaxClients          20
</pre>
<p>This may seem drastic (OMG! I can&#8217;t serve more than 20 simultaneous users!) but here&#8217;s the thing: you already can&#8217;t. If you try, if you leave this set to its much higher default, the result will be a VM crushed by the need to swap out all of these Apache processes to virtual memory while they are still trying to do work. As the operating systems geeks say, the &#8220;resident set size&#8221; of your Apache and PHP processes is bigger than your actual memory. And when that happens you&#8217;re SOL.</p>
<p>When you set MaxClients to a realistic number, what happens instead is this:</p>
<p><em>You can handle up to 20 connections at once gracefully.<br />
That 21st connection queues up and is dealt with soon after.<br />
If the average load doesn&#8217;t exceed 20 connections, you&#8217;re fine.</em></p>
<p>If too many connections queue up, some people will not get through. But <em>you</em> will still be able to get through to other services on the VM&#8230; like the ssh connection you use to administer it and change these settings. Without that, you&#8217;re stuck in a seesaw battle, shutting down your website, tweaking settings, firing it up again and watching it gradually become overwhelmed.</p>
<h3>What Is The Right MaxClients For Me?</h3>
<p>Take the amount of RAM in your server (for instance, a 256MB slicehost VM). Subtract a bit of overhead (at least 50MB, ideally more, bigger VMs are better that way). Now divide by the largest PHP process you typically see when running &#8220;top&#8221; or &#8220;ps auxw | grep php&#8221;. For instance, if your processes top off around 30MB, you can set MaxClients to (256-30) divided by 30. Round down and you have a MaxClients setting of 7.</p>
<p>One snag: some operating systems show shared memory (like the memory used by APC) as memory in use by every one of the Apache processes, even though it is really only allocated once and shared by all of them. If your APC cache is set to 40MB and all of your PHP processes seem 40MB overweight, you can increase MaxClients somewhat to allow for this.</p>
<p>Yes, you can use swap space (virtual memory) to go higher, but this just forces the machine to swap, using disk space in place of memory to do hard things it needs to do quickly and freezing you out of your own machine. So don&#8217;t do that.</p>
<h2>PHP, I Love You, But Could You Sleep Downstairs?</h2>
<p>Dialing back the number of Apache processes is a helpful step that allows your server to say no with grace and style when there is too much traffic. But the underlying problem remains: we can&#8217;t deal with a lot of requests at once. And the reason we can&#8217;t do that is PHP. So we&#8217;re stuck. Or are we?</p>
<p>Our Apache processes are &#8220;fat&#8221; in terms of memory use because of the memory taken up by PHP. But most of the requests we&#8217;re handling are for little PNG files and not-so-little JPEG files and CSS files and JavaScript files&#8230; all of which Apache can serve up handily with about three bytes of memory and a piece of string. </p>
<p>The obvious solution is to serve PHP requests and static files separately. And many sites do this explicitly, using absolute URLs pointing at a different subdomain for static files. For example:</p>
<p>&lt;img src=&#8221;http://static.example.com/foo.png&#8221; /&gt;</p>
<p>Now, this is a fine thing and I have nothing against it. When you design things this way you can use entirely separate machines to serve these requests if you wish, and that&#8217;s pretty exciting from a performance perspective. Many bigger sites find this well worth their while.</p>
<p>But you are also complicating things. And you are forced to change working code, which introduces the potential for new and exciting bugs.</p>
<p>There&#8217;s another approach (well, one of many): FastCGI. FastCGI is an enhanced version of the CGI protocol that appeared quite early in the evolution of the web but caught on only with those most concerned about performance. I must admit I am a very recent convert, ironic since FastCGI is a bit out of vogue today. But there are solid reasons to consider using it.</p>
<p>FastCGI has three big advantages:</p>
<p>1. FastCGI runs PHP processes separately from Apache. That means Apache can stay lean and mean, not only running much smaller processes (and therefore perhaps many more of them) but even running in a separate &#8220;worker thread&#8221; that outperforms the separate-processes mode by a long mile. This is a big, big win.</p>
<p>2. It can reuse a single process for new PHP requests (***), avoiding the need to restart the PHP interpreter. That&#8217;s nice, but mod_php can do the same trick, and mod_php is capable of sharing APC cache memory between all of its processes. And FastCGI can&#8217;t do that&#8230; or it wouldn&#8217;t be able to without a little help from PHP:</p>
<p>3. PHP comes with a special FastCGI mode in which it manages several child processes of its own, and <strong>allows all of those processes to share a single APC cache.</strong> </p>
<p>Put these advantages together and you have the holy grail of PHP hosting: PHP running separately from Apache, but still maintaining all of the performance advantages of the APC cache. And we can do it without changing a line of PHP.</p>
<h3>Installing FastCGI</h3>
<p>There&#8217;s one catch: Apache 2.0 ships with <code>mod_cgid</code>, a rewritten version of FastCGI that complies with the official Apache license, instead of the oh-so-fractionally-different FastCGI license (****). And it&#8217;s nice and all, except that it completely lacks the third advantage of FastCGI I mentioned above. If you want the benefits I&#8217;m describing here, you have to use the real <code>mod_fastcgi</code>. Accept no substitutes.</p>
<p>These directions work for slicehost Ubuntu slices and other Ubuntu-based Linux. They will not work exactly as-is for servergrove which is centos-based:</p>
<p>Edit /etc/apt/sources.list<br />
Add multiverse, which includes software with insufficiently &#8220;open&#8221; licenses, such as mod_fastcgi. Change intrepid to the ubuntu version on your box, it&#8217;ll be mentioned all over sources.list:</p>
<pre>
# tom@punkave.com: we want multiverse support so we can have real mod_fastcgi
deb http://us.archive.ubuntu.com/ubuntu/ intrepid multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ intrepid multiverse
deb http://us.archive.ubuntu.com/ubuntu/ intrepid-updates multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ intrepid-updates multiverse
</pre>
<p>Now do:</p>
<pre>
apt-get update
apt-get install libapache2-mod-fastcgi
</pre>
<p>Now edit /etc/apache2/mods-available/fastcgi.conf to read:</p>
<pre>
&lt;IfModule mod_fastcgi.c&gt;
  # One shared PHP-managed fastcgi for all sites
  Alias /fcgi /var/local/fcgi
  # IMPORTANT: without this we get more than one instance
  # of our wrapper, which itself spawns 20 PHP processes, so
  # that would be Bad (tm)
  FastCgiConfig -idle-timeout 20 -maxClassProcesses 1
  &lt;Directory /var/local/fcgi&gt;
    # Use the + so we don't clobber other options that
    # may be needed. You might want FollowSymLinks here
    Options +ExecCGI
  &lt;/Directory&gt;
  AddType application/x-httpd-php5 .php
  AddHandler fastcgi-script .fcgi
  Action application/x-httpd-php5 /fcgi/php-cgi-wrapper.fcgi
&lt;/IfModule&gt;
</pre>
<p>Now set up /var/local/fcgi/php-cgi-wrapper.fcgi:</p>
<pre>
mkdir -p /var/local/fcgi/
vi /var/local/fcgi/php-cgi-wrapper.fcgi
</pre>
<p>Paste in:</p>
<pre>
#!/bin/sh

# We like to use the same settings we formerly used for apache mod_php.
# You don't want this if your php.ini is in /etc
PHPRC="/etc/php5/apache2"
export PHPRC
# We can accommodate about 20 50mb processes on a 1GB slice. More than that
# will swap, making people wait and locking us out of our own box.
# Better idea: just make people wait to begin with
PHP_FCGI_CHILDREN=20
export PHP_FCGI_CHILDREN
exec /usr/bin/php-cgi
</pre>
<p>Note the figure 20, this can be adjusted for the box in question if it has more or less memory to offer. Also note that we use the same php.ini we used with mod_php (/etc/php5/apache2). You can change that if you think it appropriate but don&#8217;t forget to bring settings over.</p>
<p>Set permissions:</p>
<pre>
chmod -R 755 /var/local/fcgi
</pre>
<p>IMPORTANT: comment out any php_value lines in .htaccess files for the sites on this box, and move those directives to php.ini. Otherwise you will get errors form those sites and they won&#8217;t work. </p>
<p>If two sites on the same slice really must have separate php.ini settings, consider separate slices for the sites. It&#8217;s possible to set up more than one fastcgi pool, but that halves the resources available and so on.</p>
<pre>
# Commented out; moved to /etc/php/apache2/php.ini
# php_value arg_separator.output &#038;
</pre>
<p>Disable mod_php5, enable mod_fastcgi in its place, enable mod_actions which is required by the above, and restart Apache:</p>
<pre>
a2dismod php5; a2enmod fastcgi; a2enmod actions; apache2ctl restart
</pre>
<p>Now test the site. Also check /var/log/error.log and/or error.logs for individual sites to make sure you see mentions of fastcgi and not mod_php.</p>
<p>If you get an error and you see this in the log:</p>
<pre>
[Thu Mar 04 02:51:33 2010] [alert] [client 69.141.215.7] (2)No such file or directory: FastCGI: failed to connect to (dynamic) server "/var/local/fcgi/php-cgi-wrapper.fcgi": something is seriously wrong, any chance the socket/named_pipe directory was removed?, see the FastCgiIpcDir directive
</pre>
<p>There&#8217;s a chance it&#8217;s just a transitory problem; first-time fastcgi startups seem to be a bit temperamental, but we&#8217;ve had no problems in the long haul. Restart apache again:</p>
<pre>
apache2ctl restart
</pre>
<p>If it still doesn&#8217;t work reenable mod_php5:</p>
<pre>
a2dismod fastcgi; a2enmod php5; apache2ctl restart
</pre>
<p>And do some more reading.</p>
<h3>Installing the Worker Thread MPM</h3>
<p>Once you reach this point successfully, you are ready to switch Apache from &#8220;prefork&#8221; mode (which mod_php requires) to &#8220;worker thread&#8221; mode. Worker thread mode is blindingly, ri<em>dun</em>culously fast at serving static stuff, and uses mod_fastcgi to take care of PHP. You can&#8217;t do this with mod_php enabled, so you must succeed in setting up fastcgi first (see above):</p>
<pre>
apt-get install apache2-mpm-worker
</pre>
<p>That will restart Apache by itself. But if not you can do so manually.</p>
<p>Now test the site again. If it doesn&#8217;t work, you can easily undo this:</p>
<pre>
apt-get install apache2-mpm-prefork
</pre>
<p>To verify success:</p>
<pre>
ps auxw | grep apache
</pre>
<p>Should show fewer processes (unless your operating system shows threads as processes). The processes you do have are running many threads (lightweight subprocesses that are exposed to each other&#8217;s memory etc. and generally less expensive).</p>
<p>ps auxw | grep php</p>
<p>Should show roughly 20 processes, plus a process to manage the others. Significantly more than 20 (say, 40+) indicates you goofed and didn&#8217;t set maxClassProcesses properly and you&#8217;re going to have a lot of PHP processes on your hands in a hurry, so fix that. (I&#8217;m assuming you chose 20 processes as your limit for PHP above.)</p>
<p>Now beat up on the site a bit. Remember to check any other sites on the same box, you just changed how all of your PHP sites on this box are run. </p>
<p>Now copy apc.php to the web folder of one of your sites and visit that URL to see a page giving stats about how well the cache is working. You should see a healthy percentage of cache hits if you have revisited some of the same pages or at least reused Symfony and Doctrine. If not check whether you ever enabled APC for this site. You can find apc.php in /usr/share/php/apc.php on some systems. Its absence doesn&#8217;t mean APC is not available.</p>
<p>You need to set up both mpm-worker and fastcgi to get the most benefit here. The worker MPM can serve zillions of static files in a hurry with minimal memory usage each. However, after setting up fastcgi, you can certainly crank up MaxClients and stick with the prefork MPM if you prefer.</p>
<h2>Alternatives</h2>
<p><strong>APC alternatives:</strong> there are alternatives to the APC cache. These include eAccelerator and Zend Optimizer. I&#8217;m not an expert on the others, but APC will be standard in PHP 6.0, so I recommend standardizing on it now. For extreme cases (i.e. &#8220;we&#8217;re getting as big as Facebook and we have too much code to start thinking about improving the code itself in any serious way&#8221;), by all means consider <a href="http://wiki.github.com/facebook/hiphop-php/">HipHop</a>. You can think of it as a distant relative of APC that provides a roughly 2x performance boost at a large cost in complexity and maintenance.</p>
<p><strong>FastCGI alternatives:</strong> some will disagree with my recommendation of FastCGI. mod_cgid supposedly has better child process management&#8230; with the glaring exception that APC is not shared, ballooning your memory requirements in a seriously ugly way. Better alternatives include running the <a href="http://nginx.org/en/">nginx</a> (pronounced &#8220;engine X&#8221;) web server as a lightweight &#8220;front end&#8221; that serves static files directly and proxies other requests through to an Apache server running mod_php. One can also run Apache (without the fat mod_php module) as a front end for another instance of Apache. In these setups, the back-end Apache doesn&#8217;t accept connections except from the front-end one, and Apache&#8217;s reverse proxy features come into play. Personally I like the simplicity of Apache communicating directly with a master PHP process via the FastCGI protocol.</p>
<h2>Conclusion</h2>
<p>ERRRRRRRRRRRROOOOOMMMMMMM</p>
<p>Beep beep</p>
<p>ERRRRRRRRRRRRRRRRRROOMMMMMMMM</p>
<p>[HONK]</p>
<p>Zoom!</p>
<p>(*) Actually, one of Facebook&#8217;s stated goals for HipHop is to avoid rewriting and presumably hand-optimizing their existing PHP code. They have billyuns and billyuns of lines of PHP code, not all of it written by hardcore software jocks. So if a handful of smart folks can make mediocre PHP run 50% faster, that&#8217;s a big deal for Facebook. For most of us it makes more sense to pick the low-hanging fruit first (see all of the above), and then top making separate SQL queries for every single darn object on the page, and <em>then</em> maybe think about HipHop. I&#8217;m not knocking HipHop here at all, it&#8217;s an impressive technical accomplishment. But if you&#8217;re starting with HipHop as your<br />
first optimization, you&#8217;re not paying attention.</p>
<p>(**) Yes, you could use CSS sprites to speed these up, and given time you probably should, but in the short term let&#8217;s talk about ways to deliver them quickly without getting bogged down in hard-to-maintain optimizations. With HTTP keepalive working for us instead of against us, the performance difference between delivering 20 PNGs and one PNG is mostly on the browser side&mdash; still good to avoid, but not a brutal impact on your web server. One step at a time, okay?</p>
<p>(***) Yes, FastCGI is capable of running lots of things other than PHP, but this is a PHP article. So: neener.</p>
<p>(****) There&#8217;s nothing wrong with the FastCGI license. It&#8217;s <a href="http://www.fastcgi.com/devkit/LICENSE.TERMS">a very boring MIT-style license</a> that shouldn&#8217;t interfere with your commercial use.</p>
]]></content:encoded>
			<wfw:commentRss>http://window.punkave.com/2010/03/08/faster-php-kill-kill/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Choice is a good thing</title>
		<link>http://window.punkave.com/2010/02/24/choice-is-a-good-thing/</link>
		<comments>http://window.punkave.com/2010/02/24/choice-is-a-good-thing/#comments</comments>
		<pubDate>Wed, 24 Feb 2010 22:55:15 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Apostrophe]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://window.punkave.com/?p=1027</guid>
		<description><![CDATA[Just a quick note in response to Lukas Kahwe Smith&#8217;s recent comments about the &#8220;problem&#8221; of multiple CMSes for Symfony. This note began as a comment of mine on Lukas&#8217; blog.
We developed Apostrophe to scratch our own itch: we needed a CMS that our clients could use to add content to their sites in a [...]]]></description>
			<content:encoded><![CDATA[<p><i>Just a quick note in response to <a href="http://pooteeweet.org/blog/1674">Lukas Kahwe Smith&#8217;s recent comments about the &#8220;problem&#8221; of multiple CMSes for Symfony</a>. This note began as a comment of mine on Lukas&#8217; blog.</i></p>
<p>We developed <a href="http://www.apostrophenow.com/">Apostrophe</a> to scratch our own itch: we needed a CMS that our clients could use to add content to their sites in a variety of ways without inadvertently breaking the site design. So our CMS concentrates on the admin&#8217;s user experience, extending metaphors already present in the page. It&#8217;s about design even more than it&#8217;s about software architecture.</p>
<p>We also wanted to make sure it supported Symfony coding practices we were already having good success with. Therefore slots and engines implemented as Symfony modules, not new abstractions, and a clear lineage back to ideas from sfSimpleCMS.</p>
<p>We open sourced Apostrophe because we saw a win-win situation: other developers are in the same boat (their clients are also frustrated with Drupal administration, and they need a good solution today).</p>
<p>More eyes on our code, more bug reports, more suggestions equals a better system for everyone in the Apostrophe community.</p>
<p>Right now this is working for us in a big way, and it&#8217;s running in production on a number of client sites. Others on the <a href="http://groups.google.com/group/apostrophenow">apostrophenow google group</a> are also happy with it (although they have valid criticisms that we are addressing), and they are building client sites that work. There&#8217;s no business case for us to screech to a stop and start doing something radically different.</p>
<p>Right now we have a team of seven people doing great work with Apostrophe and no interest in stopping that. Evolving it to be better, eventually rewriting it extensively in the Symfony 2.0 timeframe&#8230; sure! But unless the marketplace tells us otherwise, we&#8217;re going to keep doing good business, writing good code and sharing our work with those who like our approach.</p>
<p>Joomla, Wordpress and Drupal are all thought of as CMSes for PHP, but no one is telling them that they must merge. Why? People pick the project that works best for their needs. That&#8217;s as it should be. Software architecture isn&#8217;t the only differentiating factor between content management systems. </p>
<p>If you like Diem or Sympal&#8217;s approach better, heck, go ahead and use &#8216;em. A &#8220;there can be only one&#8221; approach leads to design by committee, and that is not always a good thing.</p>
]]></content:encoded>
			<wfw:commentRss>http://window.punkave.com/2010/02/24/choice-is-a-good-thing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>#slive2010 Liveblogging: Fabien Potencier: Symfony 2.0</title>
		<link>http://window.punkave.com/2010/02/17/slive2010-liveblogging-fabien-potencier-symfony-2-0/</link>
		<comments>http://window.punkave.com/2010/02/17/slive2010-liveblogging-fabien-potencier-symfony-2-0/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 17:16:32 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Conferences]]></category>
		<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://window.punkave.com/?p=991</guid>
		<description><![CDATA[I am liveblogging Fabien Potencier&#8217;s Symfony 2.0 presentation. Bear with me and my editorial errors, simplifications and bare-faced libels.
My own opinions are in parentheses.
They are setting up now.
&#8220;Symfony 2.0 Reloaded&#8221; Preview Release
&#8220;I&#8217;ve decided to do the session in French&#8221; heh
In 10 minutes you will discover the shiny new version of Symfony, Symfony 2.0. Symfony 2 [...]]]></description>
			<content:encoded><![CDATA[<p>I am liveblogging Fabien Potencier&#8217;s Symfony 2.0 presentation. Bear with me and my editorial errors, simplifications and bare-faced libels.</p>
<p>My own opinions are in parentheses.</p>
<p>They are setting up now.</p>
<p>&#8220;Symfony 2.0 Reloaded&#8221; Preview Release</p>
<p>&#8220;I&#8217;ve decided to do the session in French&#8221; heh</p>
<p>In 10 minutes you will discover the shiny new version of Symfony, Symfony 2.0. Symfony 2 is just awesome. But first a bit of history.</p>
<p>Symfony 1.0 released in January 2007. Started as glue between existing libraries: Mojavi, Propel, Pardo i18n, &#8230;</p>
<p>&#8220;How many of you are still using 1.0?&#8221; [Some hands go up] &#8220;Ohhhhh!&#8221; [Hey, I raised my hand too, I have to support existing projects after all, heh. Our new stuff is all 1.4]</p>
<p>With Symfony 1.2 in November 2008, decoupled but cohesive components wre rolled out: forms, routing cache, YAML, ORMs&#8230; but still the same core.</p>
<p>Symfony 1.4 &#8211; November 2009. Added polish on existing features. Removed the support for deprecated features. Current LTS released, maintained until late 2012.</p>
<p>What is Symfony 2.0? The next version of course&#8230; except Symfony now takes an &#8216;S&#8217; instead of an &#8217;s&#8217;. That&#8217;s the big difference!</p>
<p>Talk about Symfony 2 with a capital S please! Symfony 1 (uppercase) does not make any sense, symfony 2 does not make sense! It&#8217;s all about Symfony 2. &#8220;I&#8217;m quite picky.&#8221;</p>
<p>&#8220;Same philosophy, just better.&#8221; We&#8217;ve learned a lot from our mistakes. We&#8217;ve learned a lot&#8230; we have problems with Symfony 1, but not all are fixable. It requires breaking backwards compatibility. So we need to go forward.</p>
<p>Symfony 2 is still an MVC framework.</p>
<p>It&#8217;s probably more of a Fabien-style framework than anything else (his words not mine (: ).</p>
<p>MTV: Model, Template and View?</p>
<p>Highly configurable and extensible. Same Symfony Components (DI, events, request, etc), same great developer tools, full featured. </p>
<p>Everything is upgraded to PHP 5.3. (Great, I hope they debug PHP 5.3 by the time Symfony 2.0 is complete!)</p>
<p>OK, but why a major version # change then?<br />
<span id="more-991"></span><br />
Because it has a brand new low-level architecture to fix some of the deeper issues with Symfony 1.x. </p>
<p>New best practices and concepts, built with speed and flexibility in mind. Based on dependency injection container, a new plugin system. </p>
<p>Namespaces and anonymous functions will be used.</p>
<p>A Quick Tour</p>
<pre>
namespace Application\HelloBundle\Controller;
use Symfony\Framework\WeBundle\Controller;

class HelloControlelr extends Controller
{
  public function indexAction($name)
  {
    return $this->render('HelloBundle:Hello:index', array('name' => $name));
  }
}

&#038;lt!-- No magic for layouts this is an explicit layout extension in the template -->
&lt;?php $view->extend('HelloBundle::layout') ?>

Hello &lt;?php echo $name ?>!

Layout:

&lt;html>
&lt;head>....</head>
&lt;body>
&lt;?php $view->slots->output('_content') ?>
&lt;/body>
&lt;/html>
</pre>
<p>Helpers are objects</p>
<pre>
hello:
  pattern: /hello/:name
  defaults:
    _bundle: HelloBundle
    _controller: Hello
   _action: index
</pre>
<p>:name becomes the first argument to the action.  If you have more in the route, they become additional arguments to the action. No $request parameter.</p>
<p>Extremely Configurable</p>
<p>Why? Because it is all managed by a Dependency Injection Container. &#8220;I won&#8217;t talk about the DI container today&#8221; (aw, I wish someone had explained it this week; it was taken as a given by an earlier presentation today; I&#8217;ve read about it but many haven&#8217;t had a chance yet I&#8217;m sure).</p>
<p>The DI Container replaces &#8220;a lot of symfony 1 things:&#8221;</p>
<p>sfConfig<br />
All config handlers<br />
sfProjectConfiguration<br />
sfContext (no singleton anymore!)<br />
The configuration cache system (!!)<br />
And some more</p>
<p>In one easy-to-master, unified, and cohesive package.</p>
<p>databases.yml can be renamed. You can create just one big config file if you want, or have several small ones. </p>
<p>Name your configuration files any way you want. Use PHP, XML, YAML or even INI format.</p>
<p>You can do raw PHP rather well:</p>
<pre>
$configuration = new BuilderConfiguration();
$configuration->addResource(new FileResource(__FILE__));

$configuration->mergeExtension('web.user', array('default_culture' => 'fr', 'session' => array('name => 'SYMFONY')...); etc
</pre>
<p>Same thing shown in YAML and XML. XML syntax is rather consistent, not much more verbose than YAML anymore.</p>
<pre>
$configuration->mergeExtension('swift.mailer', array('transport' => 'gmail', 'username' -=> 'fabien.potencier', 'password' => 'xxxxxx', ... ));
</pre>
<p>XMLNS schema definitions for lots of things</p>
<p>Autocompletion based on schemas demonstrated in NetBeans IDE</p>
<p>Inherit configuration as much as you want</p>
<p>You can have a master configuration and then configurations for each environment for instance. Symfony 1 had several defined levels. Framework, project, application, module. </p>
<p>In Symfony 2 it&#8217;s up to you. You can have as many as you want, and mix and match config files written in different formats, which is useful when using third-party plugins. </p>
<pre>
&lt;imports>
&lt;import resource="parent.xml" />
&lt;import resource="config.yml" />
&lt;import resource="parameters.ini" />
&lt;/imports>

&lt;zend:logger
priority="debug"
/>

&lt;web:debug
toolbar="%kernel.debug"
ide="textmate"
/>
</pre>
<p>Which format to use? Internally they only use XML because it has autocomplete, validation, help etc. etc. due to schemas. Fabien endorses XML.</p>
<p>Store Sensitive Settings Outside Of Your Project (database passwords&#8230; clever rsync_exclude tricks go away?)</p>
<p>Old way (in 2 syntax):</p>
<pre>
&lt;doctrine:dbal
dbname="sfweb"
username="root"
password="HeyThisIsTotallyPublic"
/>
</pre>
<p>New way:</p>
<p>password=%SYMFONY_DOCTRINE_DBAL_PASSWORD%</p>
<p>And set:</p>
<p>SetEnv SYMFONY_DOCTRINE_DBAL_PASSWORD &#8220;foobar&#8221;</p>
<p>(is that literally a shell thing?)</p>
<p>Creating DIC Extensions Is Insanely Simple</p>
<p>Very fast thanks to a smart caching mechanism &#8211; always knows when to flush the cache (!)</p>
<p>Whenever you do something that requires creating the cache, Symfony knows it must flush and recreate the cache.</p>
<p>The code in the cache is PHPDoc, seriously readable code</p>
<p>The code to fetch something from the DIC is as fast as the code you&#8217;d write yourself (he showed us; there are no unnecessary layers of cruft)</p>
<p>The DIC can manage any PHP object: POPO (?)</p>
<p>&#8220;Plugins&#8230;&#8221;</p>
<p>Good in 1.x, better in 2.0</p>
<p>&#8220;So much better we need a new name.&#8221; They are now called Bundles.</p>
<p>Why? Because they are first class citizens in Symfony 2.0. In Symfony 1, they added the plugin system late in the game. They had applications and plugins and there was a question as to what should be what. In 2.0 everything is a bundle. Core features, third-party code, application code. The framework itself is made up of bundles. Doctrine is a bundle, etc. </p>
<p>Default directory structure:</p>
<p>app/<br />
src/<br />
web/</p>
<p>You don&#8217;t have to adhere to it.</p>
<pre>
app/
  AppKernel.php
  cache/
  config/
  console
  logs/

src/
  autoload.php
  Application/
  Bundle/
  vendor/
    doctrine/
    swiftmailer/
    symfony/
    zend/
</pre>
<p>Note that Zend is on the shortlist, but in &#8220;vendor&#8221; (and so is symfony).</p>
<pre>
web/
  index.php
  index_dev.php
</pre>
<p>Controllers.</p>
<p>A bundle dir looks like this:</p>
<pre>
.../
  SomeBundle/
  Bundle.php
  Controller/
  Model/
  Resources/
    config/
    views/
</pre>
<p>Templates, data, views, configuration, js, etc. live in Resources.</p>
<pre>
public function registerBundleDirs()
{
  return array(
    'Application' => __DIR__ . '/../src/Application',
    same for bundle
    similar for Symfony Framework pointing to its bundle dir, giving it a prettier name on the left, using namespaces
}

$this->render('SomeBundle:Hello:index', $params)
</pre>
<p>SomeBundle is the bundle&#8217;s declared name, not something based on position in the filesystem. Looks for a matching bundle (much as it now looks for modules in plugins and the app).</p>
<p>Could be in:</p>
<p>Applciation\SomeBundle<br />
Bundle\SomeBundle<br />
Symfony\Framework\SomeBundle</p>
<p>Extend things by making one that appears earlier on that list.</p>
<p>In Symfony 1 to extend a plugin you must muck about with a BaseaActions class to make aActions extensible. In Symfony 2 it is natural. Just create another bundle. (? How does a bundle extend a bundle exactly? Is it about the order you register them in? Are you extending classes or is this a DI trick?)</p>
<p>&#8220;Less concepts&#8230; but more powerful ones&#8221;</p>
<p>Symfony 1 View Layer</p>
<p>A layout is just another tempalte with __content as a special slot</p>
<p>A partial is just at emplate you embed in another one</p>
<p>A component is just another action</p>
<pre>
&lt;?php $view->output('BlogBundle:Post:list', array('posts' => $posts)) ?>

&lt;?php $view->actions->output('BlogBundle:Post:list', array('limit' => 2)) ?>
</pre>
<p>So back to the layout&#8230; _content is just a slot now, not something special.</p>
<p>Big and small  improvements.</p>
<p>Multiple levels of layouts. You can extend the template, but you can also extend the layout as much as you want&#8230; partials can be decorated too. Better logging. </p>
<p>INFO: Matched route &#8220;blog_home&#8221; (parameters: array(&#8216;_bundle&#8217; => &#8216;BlogBundle&#8217; &#8230;.))</p>
<p>Errors are called out better with an ERR prefix (there were supposed to be multiple levels in 1.x right?)</p>
<pre>
&lt;zend:logger priority="debug" />
</pre>
<p>Great exception error pages</p>
<p>They integrate with your editor if you have the symfony firebug bar, very nice</p>
<p>&#8220;Content of the Output&#8221; available in the event of an error! Big improvement over 1.x. You can see the var_dumps before the error etc.</p>
<p>&#8220;An Even Better Web Debug Toolbar&#8221; (oops it was &#8220;Event Better&#8221; I was very puzzled (: )</p>
<p>The web debug toolbar is at the bottom of the page. You can look at it when you want to, otherwise out of your way.</p>
<p>&#8220;Everythign you need is at the bottom of the screen.&#8221;</p>
<p>symfony 2.0.0-DEV php 5.3.1/xdebug/accel blog/dev/debug default/html/200/text/html 6ms 1280kb [db] 0</p>
<p>&#8220;default&#8221; is the route that matched.</p>
<hr />
<a href="http://www.apostrophenow.com">Check out Apostrophe, our Symfony-based Content Management System!</a></p>
<p>2.x is web designer &#8220;friendly.&#8221; No more apps/frontend/modules/foobar/templates before you can find what you are working on. You can have one templates folder.</p>
<pre>
app/
  views/
    BlogBundle/
    Post/
      index.php
 And then other bundles with their subfolders
</pre>
<p>(That looks familiar actually&#8230;? But he&#8217;s saying there is one templates dir&#8230;? Ah because a bundle is equivalent to a plugin not just a module. All templates for a plugin-scale thing are now in one folder)</p>
<p>Symfony 2 Is A Lazy Framework</p>
<pre>
use Symfony\Foundation\UniveralClassLoader;

$loader = new UniversalClassLoader();
$loader->registerNamespaces(Symfony, Application, Bundle, Doctrine with their root paths in a hash));
$loader->registerPrefixes(array('Swift_' ... ));
</pre>
<p>Lazy-loading of services</p>
<p>In 1.x factories are created by default. No choice. In 2.x, if you don&#8217;t use a user object, you won&#8217;t get one. If you don&#8217;t need request, you don&#8217;t get it. </p>
<p>Lazy-loading of listeners</p>
<p>A bunch of events, a lot of different listeners. In Symfony 2 we only create listeners if an event is notified. You can register as many listeners as you want, the overhead is small. </p>
<p>Lazy-loading of helpers</p>
<pre>
&lt;?php echo $view->router->generate('blog_post', array('id' => $post->getId())) ?>
</pre>
<p>The router helper doesn&#8217;t get created unless I use it.</p>
<p>(Note that $view is a new variable present in view layer code like partials and templates. Helpers are objects, and view itself is an object. No more pretending you don&#8217;t need -> in templates (we all used it anyway with the model).)</p>
<p>Symfony 2 routing system has similar features but the implementation is very different. Compiles down to plain PHP code.</p>
<p>You can use Apache for Routing matching for extreme speed (!).</p>
<p>A Very Fast Development Environment</p>
<p>Again, no more clear cache, it&#8217;s automatic in dev environment. A metafile references the files that are relevant to the cache, and when they get touched Symfony can check very quickly whether it must clear the cache.</p>
<p>You can also stash routes in a db, and Symfony also knows if it needs to rebuild based on that.</p>
<p>And that&#8217;s Symfony 2, &#8220;we have barely scratched the face&#8221; of Symfony 2.0. </p>
<p>Easy to learn<br />
Easy to use<br />
Extensible at will</p>
<p>&#8220;People will tell you that Symfony 1 is slow, hard to learn, complex. I think they are wrong. But I think Symfony 2 is easy to learn, easy to use, and extensible at will. I want to lower the barrier to entry of Symfony 2.0. I want it to be usable for very small websites and very big ones. I want people to be able to grasp Symfony 2.0 in a matter of minutes. Probably 20 minutes. I want people to be able to understand and be fluent in a matter of hours. Perhaps a day, not more. Everything is more explicit. Less what-the-fuck effects&#8221; (borrowing Jon Wage&#8217;s line from yesterday heh). &#8220;You can change everything in Symfony 2.0. If something is not tasty enough for you, you can swap the component and use something else. You can change the whole directory structure if you want, create a whole application in one file if you want. You should be able to guess new concepts without reading the documentation, guess the thing you don&#8217;t know. That&#8217;s possible because we have fewer concepts. Done with two or three concepts, easier to guess how it works and how it would work for your specific case. </p>
<p>Of course we have all of the great features of Symfony 1&#8230;</p>
<p>But Symfony 2 should be slow right? No. It is</p>
<h1>Fast as hell</h1>
<p>This is the fastest framework ever created. It can be seven times faster than any other framework for a simple hello world application but I don&#8217;t care. The last time a customer asked for a hello world application was in the early nineties so that benchmark does not make sense. So I created a benchmark with a more typical application using all the layers of a typical framework. Models, views, controller, routing. I did benchmarks on a simple application. It&#8217;s a hello &#8216;name&#8217; application, so there is a route, /hello/someone. There is a layout with 15 links and a menu with 15 routes&#8230; there are partials.</p>
<p>So we use templates and partials (setTemplate()), links for the routing, layout, but no session or database in this case. We are trying to measure the speed of the framework itself.</p>
<p>If you do this, Symfony 2.0 is 2x faster than Solar 1.0.0, the fastest framework for simple applications. So that&#8217;s great. </p>
<p>Symfony 2.0 is 2x faster than Symfony 1.2, 2.5x faster than Zend, 4x faster than Lithium, 6x faster than CakePHP 1.2.6. 60x faster than Flow3 (and by the way notice that this means Symfony 1.x is pretty fast relative to most of these).</p>
<p>Symfony 2.0 uses half the memory needed by both Symfony 1 and Zend Framework. Scales better than any other framework.</p>
<p>Stuff still missing:</p>
<p>Controller&#8217;s nice default pages. Cache via ZF &#8211; DI extension coming soon. Form/Validation/Widget: can use 1.4 version for now. Admin generator, ditto. Doctrine 2.0 needs its upper layers but the DBAL layer is already impressive. There is no handy bundle installer yet. </p>
<p>Not for production projects yet, quite stable but he needs to be able to change his mind and make big changes for now. Final release date 2010. </p>
<p>For bleeding edge news (ha!) follow @fabpot, and also on Github, github.com/fabpot.</p>
<p>He&#8217;s going to release the code (pull a copy?) live.</p>
<p>He&#8217;s on a Mac too small for him to access the usual VPN icon so he&#8217;s asked the audience for help finding it through system preferences hee! Good he found it. We&#8217;ll see if the network works for him&#8230;</p>
<p>He&#8217;s going through the ugly validation of the wifi here&#8230; &#8220;what-a-fuck?&#8221;</p>
<p>&#8220;Okay so you won&#8217;t have the code today&#8230; Sorry about that. No, I&#8217;ll do my best to push the code in about an hour. I will also deploy <a href="http://symfony-reloaded.org/">symfony-reloaded.org</a>, a temporary website about Symfony 2.0. </p>
<p>I can tell you that at the end of the tutorial, you will be able to create great applications with Symfony 2.0. I am a bit frustrated now because I have so many things to tell you. I really think Symfony 2.0 is awesome. So if you have any good questions&#8230;&#8221;</p>
<p>Standing Ovation</p>
<p>Q. &#8220;Are you trying to reinvent Zend Framework 1.0?&#8221; Heh. This is a reference to a dumb question asked of Jon Wage yesterday. If I heard right.</p>
<p>Q. &#8220;How can we balance future development on 1.x and the new 2.0? What should we do [now] as community working on Symfony framework?&#8221;</p>
<p>A. If you start a new project today use 1.4. If you respect best practices and separate model view and controllers and controllers are thin and model is fat and well-coupled templates it will be easier to migrate to 2.0. Don&#8217;t try to use 2.0 for a site deploying before end of the year. It&#8217;s too early. But Symfony 2 is so flexible. We will find ways to ease the upgrade path.  You can also move to PHP 5.3 today.</p>
<p>Q. About bundle loading: one of the targets is less magic but you first look at the application bundle and things like that. Why keep that sort of magic?</p>
<p>A. It&#8217;s not magic at all.</p>
<p>Q. It&#8217;s not exclusive.</p>
<p>A. It is. You need&#8230; a small amount of magic. Just a small amount. But you can change the directories, the name of the namespaces, you can have just one namespace if you want. </p>
<p>Q. Can you have sub-bundles without the project level having to know about them explicitly? Can you put a bundle into another bundle?</p>
<p>A. Doesn&#8217;t make sense. Too complex. You can&#8230; yeah why not? Not a big deal&#8230; doesn&#8217;t feel right.</p>
<p>Q. (Me): If I write a bundle, and someone wants to extend an action in my bundle, how do they do that?</p>
<p>A. They just extend your class, and make sure their bundle loads first. (I think I get it: now that we get to declare functional names for our stuff, independent of class names, there&#8217;s no barrier to extending someone else&#8217;s class and using the same functional name so it replaces it. As for loading order, you can have more than one bundles folder and call one of them &#8216;overrides&#8217; and put it first, or similar.)</p>
<p>Q. Will there be documentation or support soon to help us create bundles that will work for both 1.4 and 2.0?</p>
<p>A. No. Controllers are too different. No more actions.class.php. Very different, with namespaces. You can create a plugin that is also a bundle but there&#8217;d be a lot of duplicate code.</p>
<p>Q. But what about the impact on migration of existing code?</p>
<p>A. It&#8217;s too early to say but there will be great news to announce re: new bundles. One of the problems with Symfony 1 plugins is the quality. We have a lot of plugins for 1.x but a lot of them are just not really usable, to say the least. This will be better. I can&#8217;t say more right now.</p>
<p>Q. Do we have to enable bundles? Is it possible to set up dependencies between different bundles?</p>
<p>[Fabien opens his sandbox in textmate]</p>
<p>[Points out the registerBundles method seen above]</p>
<p>You need to register bundles. </p>
<pre>
class Hellokernel extends Kernel
</pre>
<p>Symfony is now an operating system! Okay no.</p>
<p>Fabien: &#8220;if you want, if you want, if you really want&#8230;&#8221; somebody remix this please (:</p>
<p>&#8230; And that&#8217;s it! Great stuff. Symfony 2.0 will be a big improvement. My next step is to wrestle DI all the way to the ground (:</p>
]]></content:encoded>
			<wfw:commentRss>http://window.punkave.com/2010/02/17/slive2010-liveblogging-fabien-potencier-symfony-2-0/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Doctrine 2: Jonathan Wage at #sflive2010</title>
		<link>http://window.punkave.com/2010/02/16/doctrine-2-jonathan-wage-at-sflive2010/</link>
		<comments>http://window.punkave.com/2010/02/16/doctrine-2-jonathan-wage-at-sflive2010/#comments</comments>
		<pubDate>Tue, 16 Feb 2010 16:54:24 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[propel]]></category>
		<category><![CDATA[sflive2010]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://window.punkave.com/?p=985</guid>
		<description><![CDATA[I&#8217;m attending Symfony Live. Below are my notes from Jon Wage&#8217;s presentation on the upcoming Doctrine 2.0. My more opinionated, less explanatory comments tend to be in parentheses. Everything paraphrased brutally to keep up.
Doctrine 2 &#8211; Not The Same Old Php Orm
View more presentations from Jonathan Wage.

&#8220;Not the same old PHP ORM&#8221;
100% re-written codebase for [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m attending Symfony Live. Below are my notes from Jon Wage&#8217;s presentation on the upcoming Doctrine 2.0. My more opinionated, less explanatory comments tend to be in parentheses. Everything paraphrased brutally to keep up.</p>
<div style="width:425px;text-align:left" id="__ss_3197175"><a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" href="http://www.slideshare.net/jwage/doctrine-2-not-the-same-old-php-orm" title="Doctrine 2 - Not The Same Old Php Orm">Doctrine 2 &#8211; Not The Same Old Php Orm</a><object style="margin:0px" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=doctrine2-notthesameoldphporm-100216104508-phpapp01&#038;stripped_title=doctrine-2-not-the-same-old-php-orm" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=doctrine2-notthesameoldphporm-100216104508-phpapp01&#038;stripped_title=doctrine-2-not-the-same-old-php-orm" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object>
<div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;">View more <a style="text-decoration:underline;" href="http://www.slideshare.net/">presentations</a> from <a style="text-decoration:underline;" href="http://www.slideshare.net/jwage">Jonathan Wage</a>.</div>
</div>
<p>&#8220;Not the same old PHP ORM&#8221;</p>
<p>100% re-written codebase for PHP 5.3</p>
<p>&#8220;Are you scared? You shouldn&#8217;t be! Change is a good thing!&#8221;</p>
<p>5,000 records hydrate in 1.4 seconds. Whee!</p>
<p>10,000 records in 3.5 seconds.</p>
<p>Twice the data and still faster than Doctrine 1.</p>
<p>% improvement over Doctrine 2 is big. PHP 5.3 is a big win with a heavily OO framework. Better optimized hydration algorithm. New query and result caching implementations. All around more explicit and less magical code. Killed the magical aspect of Doctrine 1.</p>
<p>&#8220;The only valid measurement of code quality: WTFs/minute&#8221;</p>
<p>Good code: &#8220;WTF&#8230;..      WTF&#8221;</p>
<p>Bad code: &#8220;WTF WTF WTF WTF WTF WTF&#8221;</p>
<p><span id="more-985"></span><br />
Doctrine 1 magical features are both a blessing and a curse</p>
<p>Magic is great when it works but also causes pain. When it doesn&#8217;t work it is hard to debug. Edge cases are hard to fix. Edge cases are hard to work around. Everything is okay until you try to go outside the magic box. Magic is slow.</p>
<p>Replacement: OOP. Object composition, inheritance, aggregation, containment, encapsulation, etc.</p>
<p>Will it have behaviors? Yes and no. No &#8220;model behaviors.&#8221; Made up to work with Doctrine&#8217;s &#8220;intrusive architecture.&#8221; Tries to do things PHP doesn&#8217;t allow, results in a lot of problems (like sfMixin did).</p>
<p>If you could do it in 1 you can do it in 2, just differnetly. Natural OOP that wraps/extends Doctrine code or is meant to be wrapped or extended by your entities. (Injection? Factories? How do you apply two extensions?)</p>
<p>What did we use to build it? phpUnit 3.4.10 unit testing. Phing. Symfony YAML. Sismo. Subversion. Jira. And Trac.</p>
<p>Doctrine 2 Architecture</p>
<p>&#8220;Everything is an entity.&#8221; A lightweight persistent domain object, a regular PHP class. You don&#8217;t extend a base doctrine class. No final methods. </p>
<pre>
namespace Entities;
class User
{
  private $id;
  private $name;
  private $address;
}
</pre>
<p>Note use of namespaces.</p>
<p>EntityManager is central access point to ORM. Used to query for persistent objects. Employs transactional write behind strategy that delays execution of SQL to execute it in the most efficient way.</p>
<p>Tests ran against multiple databases. Sqlite, MySQL, Oracle, PgSQL, more to come.</p>
<p>Unit Testing</p>
<p>Impressive #s of test cases and assertions. Tests run in a few seconds instead of 30-40 seconds. Very granular, explicit unit tests, not many functional tests. Easier to debug. Continuously integrated by Sismo.</p>
<p>No, Sismo is not available yet, bug Fabien! (We have a pkcommit script of our own that doesn&#8217;t let you commit without a warning unless you pass symfony tests, which is a nice poor man&#8217;s version)</p>
<p>Database Abstraction Layer</p>
<p>Standalone package and namespace (Doctrine\DBAL).</p>
<p>Can be used standalone.</p>
<p>Much improved over Doctrine 1 in regards to the API for database introspection and schema management. (Can you alter a table in a more db-portable way wrt type names?)</p>
<p>Hopefully defacto standard DBAL for PHP 5.3 in the future like MDB was for 4.x</p>
<pre>
prepare($sql)
executeUpdate($sql, $params)
fetchAll... fetchBoth($sql, $params) - both assoc and flat...
</pre>
<p>DBAL Introspection</p>
<pre>
listDatabases
listFunctions
listSequences
listTableColumns($tableName)
</pre>
<p>Schema Representation</p>
<pre>
$schema = new \Doctrine\DBAL\Schema\Schema();  <- namespaces are nutty
$myTable = $schema->createTable("my_table");
</pre>
<p>Replacing diff tool with the ability to compare schema objects.</p>
<p>Doctrine 2 Annotations</p>
<pre>
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
</pre>
<p>This gets parsed out. Alternative to schema.yml. (Lets you write everything in one place, so perhaps more convenient.)</p>
<p>Things to Notice</p>
<p>Entities no longer require extending a heavy base class<br />
Domain model has no magic, is not imposed on by Doctrine and is defined by raw PHP objects and normal OO<br />
Big performance impact<br />
Easier to understand due to less magic. As Fabien says, &#8220;Kill the magic&#8230;&#8221;</p>
<p>(We&#8217;ve introduced a little magic here and there in our own stuff, maybe it&#8217;s less evil within a shop than without.)</p>
<p>Doctrine 2 *does* allow YAML.</p>
<pre>
Entities\Address:
  type: entity
  table: addresses
  id:
    id:
      type: integer
etc
</pre>
<p>You can do XML too</p>
<p>PHP &#8220;use&#8221; all necessary namespaces and classes</p>
<p>Recommended very long use statement, but it&#8217;s in the bootstrap function<br />
Then require the Doctrine ClassLoader<br />
Core classes, entity classes, proxy classes<br />
Configure your Doctrine implementation by talking to the ORM configuration object</p>
<p>(A class called Configuration? Won&#8217;t this tend to clash with other stuff, like Symfony 2? I know about namespaces but Configuration means we&#8217;ll have to deambiguate every time)</p>
<p>Entity manager&#8230;</p>
<pre>
$connectionOptions = array('driver' => ... )
$em = EntityManger::create($options, $config);
</pre>
<p>Use a closure to lazily load the EntityManager&#8230;?</p>
<pre>
$user = new User;
$user->setName('Jon Wage');
$em->persist($user);
$em->flush();  <- Performance optimizes all that has gone before. Multiple row insert in MySQL? OooOooo
</pre>
<p>No more magic methods!</p>
<p>Inserting 20 records with Doctrine: way fast... compared it to raw MySQL... Doctrine 2 is faster because he's rolling a single transation around the 20 inserts compared to naive PHP that has no transaction calls. </p>
<p>(I didn't realize transactions had such a big performance benefit, I thought of them solely as a consistency and concurrency issue... without them MySQL must do an implicit transaction for every INSERT which slows you down.)</p>
<p><a href="http://www.doctrine-project.org/blog/transactions-and-performance">Doctrine blog piece on transactions and performance</a></p>
<p>DQL parser re-written from scratch. DQL parsed by top down recursive descent parser that constructs an AST. Generates the SQL to execute for your DBMS. DQL has a real BNF now and tells you what your errors are</p>
<p>(You could more easily teach it to manage a non-SQL backend now. Or accidentally de-optimize your SQL. Either way)</p>
<pre>
$q = $em->createQuery('select u from MyProject\Model\User u');
$users = $q->execute();
</pre>
<p>Cache Drivers</p>
<p>fetch, contains, save, delete methods similar to sfCache classes</p>
<p>Wrap existing Symfony, ZF, etc cache driver instances with the Doctrine interface</p>
<p>Slick ways to clear part of the cache:</p>
<pre>
deleteByRegex($regex)
delteByPrefix($prefix)
deleteBySuffix($suffix)
</pre>
<p>These are used with the result caching feature. (Which we should probably leverage in Apostrophe, and use the above delete methods to avoid inconsistency. This stuff has been backported to 1.2)</p>
<pre>
$query->useResultCache(true, 3600, 'my_query_name'); <-- cache key. These are easily purged fast with the above
</pre>
<p>New CLI</p>
<p>dbql:run-sql executes a manually written raw SQL statement or file. (We could make sfSyncContentPlugin db-portable with this perhaps... it's half of it)<br />
orm:clear-cache clears query, result and metadata cache, has options to be more specific<br />
orm:convert-mapping converts metadata information between formats. Convert between YAML and XML and annotated PHP.<br />
orm:ensure-production-settings verifies that Doctrine is properly configured for a production environment, throws an exception if the environment is not suitable for production. (? What's suitable?)<br />
orm:generate-proxies<br />
Proxy objects are objects that are put in place or used instead of the real object, adding behavior to the object being proxied without that object knowing. Makes lazy-loading features possible. (Is this how you add multiple "behaviors" to one object?)<br />
orm:schema-tool --update compares local schema to database and updates database accordingly, period. What about fancy migrations? "They don't exist yet but they will as an extension"</p>
<p>Inheritance</p>
<ul>
<li>Mapped Superclasses</li>
<li>Single Table Inheritance</li>
<li>Class Table Inheritance</ll>
</ul>
<p>A mapped superclass is an abstract class with no table in the database, it is a parent that can have multiple children, you get one table for each child class. "Concrete inheritance" in Doctrine 1, I believe.</p>
<p>Single table inheritance = column aggregation inheritance in Doctrine 1. A single table with the columns of all of the children. One of the fields is used to discriminate between types and hydrate the right class of object. (Will there be a prefix naming scheme of some sort to prevent column name conflicts when subclasses are written by multiple people? There isn't in Doctrine 1.)</p>
<p>Class table inheritance. This is new. Employee extends Person, BlogPost extends Node. MyCustomBlogPost extends BlogPost. Node is a base table with an ID. BlogPost has a foreign key back to node, MyCustomBlogPost has a foreign key back to BlogPost. Doctrine builds a join across all three tables. </p>
<p>Jon's example: a CMS with many node types. BlogPostCustom inherits from BlogPost inherits from node. BlogPostCustom has a blogpost_id field, which is a foreign key on blogpost, which has a node_id field, which is a foreign key on node. Again you still need a field to identify the right class to hydrate. And you need to know which subclasses exist so you know how many LEFT JOINs are needed.</p>
<p>(This is elegant, but is it fast? Going with Jon's CMS example, which seems inspired by Drupal, if you have ten node types on a page you have at least 11 classes in your join. In Apostrophe we use Doctrine column aggregation inheritance for slots, but generally serialize() things into the value column unless we need a foreign key. For the media repository we do use a refclass connecting slots to media objects, which is similar to what Jon proposes here. We'll be adding an API that lets you add additional joins of this kind. It's probably a win to avoid joins for simpler subclasses and use them only when there's a big payoff like ON DELETE CASCADE. Of course I'm prepared to be convinced otherwise by good benchmarks.)</p>
<p>Batch Processing</p>
<p>Batch processing by taking advantage of transactional write-behind behavior of entity manager... insert 10,000 objects with a batch size of 20, calling flush() and clear() every 20 objects. Calling clear() detaches all of the objects from doctrine. I can also detach single objects with detach(). If I don't detach them, Doctrine must keep a reference to them in case they change and therefore need to be included in the next flush(). So to avoid memory leaks in long running tasks one must detach or clear appropriately.</p>
<p>Query::iterate() method avoids loading everything into memory at once, bringing things into memory one at a time. Call flush() to execute your updates, then clear(). Note that you can change fields in your already-persisted object and call flush() and they save. save() is implicit and no longer exists as a method on an object. Remember, your objects are just objects now, and they do not inherit from Doctrine_Record anymore.</p>
<p>Bulk Delete</p>
<p>Same thing, you can iterate and then call remove() on things, and flush() does the actual work, call it at sensible intervals for performance.</p>
<p>Native Queries</p>
<p>You can now define an express mapping between a native SQL query and the fields of your objects to hydrate them exactly as you wish. </p>
<p>Proxies</p>
<p>Proxy objects seem to be wrappers for your original objects that come into play in a way that probably helps to implement stuff like what behaviors do in Doctrine 1, but this wasn't discussed at length. I'd like to know how one does NestedSet, Timestampable and the like elegantly with Doctrine 2.0.</p>
<p>Jon is thinking a stable version is 6-12 months away, possibly dovetailed with the next major releases of Symfony and Zend.</p>
<p>Code Generation</p>
<p>Jon was asked how you would add a phone number to a customer, or other one to many relation, if there is "no magic" in Symfony 2.0. It's clear how columns of a table are handled (a persisted Doctrine object's properties are grabbed and saved on flush() ) but relations seem to require some magic.</p>
<p>Jon replied that before the stable release there will be code generation available for such purposes. </p>
<p>Some people felt this amounted to an admission that Propel does things the right way. </p>
<p>An audience member asked Jon if he'd just reinvented the latest version of Propel. This is the most-tweeted comment of Symfony Live 2010 so far. But the answer is no.</p>
<p>Propel 1.5 resembles Doctrine today because Propel 1.5 borrowed much of the DQL query language for its new convenient, chainable query syntax. If Propel can lift concepts from Doctrine, then it doesn't make sense to knock Doctrine for adopting code generation if that has proven to be the right way to go. </p>
<p>What's more important is that, based on my reading of the <a href="http://propel.phpdb.org/trac/wiki/Users/Documentation/1.5/WhatsNew">what's new in Propel 1.5</a> documentation, Propel has not adopted the flush()/clear() technique that makes Doctrine 2.0 so fast.</p>
<p>Propel 1.5 seems to have caught up with Doctrine 1.3, no more or less.</p>
<p>Doctrine 2.0 does need to do a good job of replacing the behaviors feature it will apparently abandon, especially since Propel 1.5 has a number of behaviors (again imitating several seen in Doctrine). I look forward to hearing Jon articulate his plans for things like nested sets, sortable, and timestampable tables in Doctrine.</p>
<p>(Incidentally, limitations of Propel's nested set feature are one of the reasons why we didn't choose it for Apostrophe. A nested set without a level column can't be used to efficiently fetch children of a page only to a certain depth. This is a potentially fatal flaw in a CMS application with thousands of pages.)</p>
<hr />
<a href="http://www.apostrophenow.com/">Check out Apostrophe, our Symfony and Doctrine-based content management system!</a></p>
]]></content:encoded>
			<wfw:commentRss>http://window.punkave.com/2010/02/16/doctrine-2-jonathan-wage-at-sflive2010/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>#sflive2010 Geoffrey Bachelet, Symfony Internals</title>
		<link>http://window.punkave.com/2010/02/16/sflive2010-geoffrey-bachelet-symfony-internals/</link>
		<comments>http://window.punkave.com/2010/02/16/sflive2010-geoffrey-bachelet-symfony-internals/#comments</comments>
		<pubDate>Tue, 16 Feb 2010 13:17:10 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[dependency injection]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[sflive2010]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://window.punkave.com/?p=983</guid>
		<description><![CDATA[I&#8217;m attending the Symfony Live conference. Below are my notes from Geoffrey Bachelet&#8217;s presentation on Symfony Internals. Geoffrey Bachelet wrote the &#8220;Symfony Internals&#8221; chapter of the Symfony book.
He wrote the Symfony Internals chapter
Customizable, flexible, extensible
Extensible through plugins, events, and overriding entire framework classes
The bootstrap retrieves application configuration, then instantiates sfContext and calls the frontend controller
sfContext [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m attending the Symfony Live conference. Below are my notes from Geoffrey Bachelet&#8217;s presentation on Symfony Internals. Geoffrey Bachelet wrote the &#8220;Symfony Internals&#8221; chapter of the Symfony book.</p>
<p>He wrote the Symfony Internals chapter</p>
<p>Customizable, flexible, extensible</p>
<p>Extensible through plugins, events, and overriding entire framework classes</p>
<p>The bootstrap retrieves application configuration, then instantiates sfContext and calls the frontend controller</p>
<p>sfContext implements the Singleton design pattern. Has pros and cons: mostly cons. Testability issues. Responsible for loading factories.</p>
<p>Application configuration: frontendConfiguration extends ProjectConfiguration, shares methods and constructors. Most customization happens in ProjectConfiguration.</p>
<p>ProjectConfiguration crewates an event dispatcher, then loads plugins. </p>
<p>sfEventDispatcher is available as a separate component. Implements the Observer design pattern. An object can register interest in events.</p>
<p>autoload.filter_config event allows you to customize the autoloader configuration. </p>
<p>There are more than 900 plugins in the repository today.</p>
<p>settings.yml holds all framework leve settings, there are a lot of them. Actions, security strategies, I18N, debug bar, etc.</p>
<p>app.yml is for your use. config/app.yml is a project wide app.yml, we rarely use that.</p>
<p>Environments allow you to specify different behavior for different environments. Different database, mailer strategy, etc.</p>
<p><span id="more-983"></span><br />
Config Handler</p>
<p>Not a well known part of Symfony. Parses and translates configuration files. Each configuration file has a handler. sfDevineEnvrionmentConfigHandler, sfDatabaseConfigHandler, etc.</p>
<p>sfContext instantiates factories. These are components that drive your application: logger, I18N, mailer, request, response, etc. Configured through factories.yml. This allows you to to subclass the normal implementations and let Symfony know that you want it to instantiate yours instead of the parent class.</p>
<p>In your app&#8217;s config/factory.yml.</p>
<p>sfFactoryConfigHandler converts config/factory.yml into executable PHP code to load the factories in question. It&#8217;s all in your cache.</p>
<p>You can override almost every component used by Symfony via factories (see the book).</p>
<p>Events notified include request.filter_parameters, routing.load_configuration, and context.load_factories. context.load_factories is the earliest event at which point database access is possible.</p>
<p>The front controller implements the front controller pattern. Grabs module and action from the request and issues a simple forward().</p>
<p>sfError404Exception forces a redirect to configured 404 handler. (How about exceptions to force redirect to the &#8220;login required&#8221; and &#8220;credentials not good enough&#8221; handlers? That would save effort too.)</p>
<p>sfGeneratorConfigHandler: instantiates a generator. Runs it. There is no step 3.</p>
<p>Controllers dir: you can override getControllersDir(), which gives you control over the controllers&#8217; location. You can add any directory to the list or just replace it. (What class is this method in?)</p>
<p>The Action Stack</p>
<p>A FIFO stack. Holds every action that has been or will be executed. Access it through sfContext&#8217;s getActionStack(). (When do you have multiple actions on the stack? That sounds like internal requests, which are coming in 2.0.)</p>
<p>You can enable and disable modules through sf_enabled_modules in settings.yml and through mod_MyModule_enabled in module.yml. Modules disabled through settings.yml cause an sfConfigurationException to be thrown, use them for permanent disabling of modules. Those disabled through module.yml redirect to the &#8220;disabled module&#8221; action (sf_module_disabled_module/sf_module_disabled/action). Better for temporary disabling of a module.</p>
<p>The Filter Chain</p>
<p>Implements the Chain of Responsibility pattern. Configurable through filters.yml. Has a config handler too.</p>
<p>You can write your own filters to be part of the chain of filter objects that can intercept a request. You add it between security and cache. </p>
<pre>
rendering: ~
functional_test:
  class: swFilterFunctionalTest
remember_me:
  class: sfGuardRememberMeFilter
</pre>
<p>The rendering filter does nothing before calling the next filter down (but it&#8217;ll take action when that filter finally returns).  A filter can execute code before the rest of the stack&#8230; but also after. So if you override the rendering filter you can add magic like autoloading of static stuff for the browser.</p>
<p>The secure filter implements credentials checks based on security.yml. Nice if the credentials are not dynamic and dependent on the object in question (aka &#8220;row ownership&#8221;).</p>
<p>The cache filter has two bits of logic: it can prevent the rest of the chain from executing. It configures the HTTP cache headers. And it fills in the cache before rendering.</p>
<p>The execution filter actually runs your action. Checks for cache, if no cache is found, executes the action. (The cache filter doesn&#8217;t do this?)</p>
<p>Execution workflow: preExecute(), execute via sfActions&#8217; execute() and therefore executeActionName(), then postExecute()</p>
<p>You can return whatever you like from an action to trigger a template by name. SUCCESS, NONE and ERROR are predefined. (You can also return $this->renderPartial or $this->srenderComponent now, which is very useful)</p>
<p>Handling the View</p>
<p>This is sfView&#8217;s job. There are two ways of getting a view object, a custom sfView object for a specific action or a class name based on mod_module_view_class. (We have never overridden the view, we&#8217;ve always used templates)</p>
<p>sfPHPView loads core and standard helpers. Helper, Url, Assets, Tag and Escaping (sf_standard_helpers). Executes a view script (pure PHP) and decorates the result. Also handles some caching (partials caching?).</p>
<p>sfPartialView is responsible for rendering partials and components. Handles cache for partials and components.</p>
<p>Samples of custom views: sfTwigView integrates the Twig template engine by Fabien. sfTemplatingView integrates templating from the symfony components (the standard view?).</p>
<p>The rendering filter sends the response content through sfWebResponse&#8217;s send method (aha, the rendering filter does do something).</p>
<p>Question: why should we not use sfContext? And can we configure multiple databases for one application?</p>
<p>Answer (sfContext): the singleton pattern means you can&#8217;t easily change what singleton is being used, except with dependency injection which is not in Symfony 1. It tends to slow down unit tests to performance similar to functional tests because it is so heavy. (We at P&#8217;unk Avenue generally stick to functional tests especially since we care quite a bit what user is attempting to do what action at the model level.)</p>
<p>Q: can I avoid having five files called actions.class.php in my editor?</p>
<p>A: you could do one action per class. </p>
<p>Q: can you add listings for entirely new factories to factories.yml?</p>
<p>A: no, the list of factories to be instantiated is hardcoded. (But nothing is stopping you from loading a class name from app.yml and instantiating it, which is pretty much all factories.yml is doing. We do this to let you substitute alternative form classes etc. in sfDoctrineApplyPlugin.)</p>
<p>Q: What can you do by overloading the autoloader?</p>
<p>A (from audience): convince it to scan your own folders first. This allows you to override things that otherwise cannot be overridden, like core Propel classes. Poor man&#8217;s dependency injection. (I hear this will be oh so much easier in 2.0)</p>
<hr />
<a href="http://www.apostrophenow.com/">Check out Apostrophe, our CMS plugin for Symfony!</a></p>
]]></content:encoded>
			<wfw:commentRss>http://window.punkave.com/2010/02/16/sflive2010-geoffrey-bachelet-symfony-internals/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Symfony live: John Cleveley on the admin generator</title>
		<link>http://window.punkave.com/2010/02/16/symfony-live-john-cleveley-on-the-admin-generator/</link>
		<comments>http://window.punkave.com/2010/02/16/symfony-live-john-cleveley-on-the-admin-generator/#comments</comments>
		<pubDate>Tue, 16 Feb 2010 10:13:25 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://window.punkave.com/2010/02/16/symfony-live-john-cleveley-on-the-admin-generator/</guid>
		<description><![CDATA[John Cleveley @jcleveley on the Symfony admin generator
@jcleveley gave an excellent talk on the Symfony admin generator at #sflive2010. Here are my notes.

View more presentations from jcleveley.

He&#8217;s using Zend Framework at the moment but has done a lot of good Symfony work in the past
&#8220;Backend development&#8221; photo of a zoopeeper holding up a trash can [...]]]></description>
			<content:encoded><![CDATA[<p>John Cleveley @jcleveley on the Symfony admin generator<br />
@jcleveley gave an excellent talk on the Symfony admin generator at #sflive2010. Here are my notes.</p>
<div id="__ss_3207730" style="width: 425px; text-align: left;"><object style="margin: 0px;" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="425" height="355" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><param name="src" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=workingwiththeadmingenerator-100217074616-phpapp02&amp;stripped_title=working-with-the-admin-generator" /><param name="allowfullscreen" value="true" /><embed style="margin: 0px;" type="application/x-shockwave-flash" width="425" height="355" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=workingwiththeadmingenerator-100217074616-phpapp02&amp;stripped_title=working-with-the-admin-generator" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<div style="font-size: 11px; font-family: tahoma,arial; height: 26px; padding-top: 2px;">View more <a style="text-decoration: underline;" href="http://www.slideshare.net/">presentations</a> from <a style="text-decoration: underline;" href="http://www.slideshare.net/jcleveley">jcleveley</a>.</div>
</div>
<p>He&#8217;s using Zend Framework at the moment but has done a lot of good Symfony work in the past</p>
<p>&#8220;Backend development&#8221; photo of a zoopeeper holding up a trash can to an elephant&#8217;s rump very nice.</p>
<p>John worked with the NHS for a while, they were using Excel spreadsheets for complex workflows, web apps were a big change for the better</p>
<p>A big fan of the admin generator: &#8220;it&#8217;s super awesome&#8221;</p>
<p>Provides most common admin requirements out of the box (CRUD: Create, Retrieve, Update, Delete actions to manage more or less any type of information)</p>
<p>Can be extended to meet bespoke needs</p>
<p>What&#8217;s new since 1.0? &#8220;Completely re-written for the form framework&#8221;</p>
<p><span id="more-980"></span><br />
Many-to-many relations work without further configuration (*cough* as long as you don&#8217;t do embedded forms)<br />
You don&#8217;t have to expressly explain refClasses to the system<br />
generator.yml is validated, finds errors for you<br />
Batch actions (delete many easily) &#8211; wasn&#8217;t this in 1.0?<br />
Less reliance on generator.yml, more DRY, the schema and form framework already spell out a lot.<br />
More templates and actions to override<br />
Separate configuration for &#8216;edit&#8217; and &#8216;new&#8217; actions in generator.yml<br />
Adds a REST route for your module (doctrine route collection)</p>
<p>New PHP configuration file in your module: lib/newsGeneratorConfiguration.class.php</p>
<p>You still want to look in the cache for generated examples to override</p>
<p>First access it, then look at it: cache/frontend/your/module/etc</p>
<p>Held a vote: do you like configuration in PHP or in YAML? More and more he prefers PHP. I have come to like PHP because I&#8217;m not typing symfony cc all the darn time (well I wouldn&#8217;t be). I voted for PHP. Of course YAML is easier to sell to administrators who are not programmers.</p>
<p>Think requirements, don&#8217;t jump to use the admin generator. What do the users actually need to do?</p>
<p>(I&#8217;ve been on the other side of this sometimes though. &#8220;Oh this is a really simple module, I want to customize it a lot and I don&#8217;t need the admin generator.&#8221; Oops, now I have lots of filters and I&#8217;m reinventing the admin generator when I could have customized it)</p>
<p>In general, if it doesn&#8217;t feel like CRUD, you shouldn&#8217;t use it.</p>
<p>@jcleveley says it&#8217;s not good news to allow use by non-trusted admins, or very heavy use. But the admin generator is generated code, not metacode&#8230; performance should be pretty good actually. We&#8217;ve had good success overriding methods with additional security checks and you can also use security.yml</p>
<p>Admin generator 10 commandments:</p>
<ul>
<li>Understand the user&#8217;s workflow and customize admin to suit</li>
<li>Think about security from the start</li>
<li>Look through and understand the cached (autogenerated) PHP files</li>
<li>Change table_method to reduce db calls. By default, on the list page, there are no joins being made. You should substitute a new table_method that does any needed joins to fix performance</li>
<li>Use bespoke Form class for admin if you don&#8217;t want to just let them edit all fields of the model, you can set which form class in generator.yml</li>
<li>Keep all form configuration in the Form class if you can, not generator.yml</li>
<li>If you need to make changes to multiple admin modules &#8211; create a theme. (We have a nice one, take a look at the aAdmin theme in apostrophePlugin)</li>
<li>Think about small screens and target browser. NHS had tiny screens and IE6. Be realistic about your client&#8217;s environment, not your favorite one</li>
<li>Create functional tests &#8211; guard against regression. No excuses</li>
<li>Maintain good MVC and decoupling practices. No HTML in the model, no queries in the partials (realistically you do wind up with some get() calls on objects to get their fields, but no heavy model code)</li>
</ul>
<p>Major Admin Generator Points</p>
<p>1. Think about the URL. Clients don&#8217;t like:</p>
<p>application.com/admin.php</p>
<p>Try:</p>
<p>application.com/admin</p>
<p>Discussed web/.htaccess rewrite rules. His preferred method is a separate virtualhost that goes to an admin.php front controller.</p>
<p>(We at P&#8217;unk Avenue prefer not to have a separate admin controller and app, we prefer to customize the user experience to the user inside a single Symfony app, which makes the URLs even friendlier and the experience more contextual)</p>
<p>2. Dynamic MaxPerPage</p>
<p>People like a choice about how much information appears on one page of the list view.</p>
<p>Add a select box within the _list_header.php &#8220;records per page&#8221;<br />
Override getPagerMaxPerPage</p>
<p>3. Adding relations</p>
<p>Fewest clicks for common tasks<br />
Relevant data all placed together<br />
Currently not directly supported by the generator<br />
But: you can use mergeForm, embedForm, and sfFormDoctrine::embedRelation.</p>
<p>(embedForm has big problems. Many-to-many in the embedded form will not save properly, you must override updateObject and implement your own link/unlink stuff)</p>
<p>You don&#8217;t want to switch back and forth between two admin generators for, say, employees and phone numbers. You want phone numbers in the context of editing employees.</p>
<p>Apparently the solution is embedRelation in your form class configure method:</p>
<pre>public function configure()
{
  $this-&gt;embedRelation('PhoneNumbers');
}</pre>
<p>Make sure you hide the id:</p>
<pre>$this-&gt;widgetSchema['employee_id'] = new sfWidgetFormInputHidden();</pre>
<p>This lets you edit but it doesn&#8217;t let you add and delete. What to do?</p>
<p>There&#8217;s a symfony plugin that contributes delete and add features for embedded relations:</p>
<p>ahDoctrineEasyEmbeddedRelationsPlugin by Daniel Lohse</p>
<p>Has created embedRelations (plural) method</p>
<p>Gives you embedded forms with new and delete capabilities</p>
<p>4. Internationalization</p>
<p>Ha: demonstrated an xliff internationalization file for Vulcan</p>
<p>You tell the admin generator to use it:</p>
<pre>generator:
  class: sfDoctrineGenerator
  param:
    i18n_catalogue: startrek</pre>
<p>5. Tidy up filters</p>
<p>Filters work great but the default style is a bit off on a small screen (800&#215;600 at NHS ouch!)</p>
<p>Fortunately there is a lot of CSS provided so it&#8217;s easy to move them to go across the top.</p>
<p>Again, peek at aAdmin for a nice example.)</p>
<p>6. Timestampable fields</p>
<p>Unset created_at and updated_at. Or make them static:</p>
<p>Override the template, or&#8230;</p>
<p>Create a plaintext widget</p>
<p>sfWidgetFormPlain widget (Stephe.Ostrow has submitted it for the formextra plugin, not in there yet; I wound up doing this for a client project too)</p>
<p>trac.symfony-project.org/attachment/ticket/7963</p>
<p>7. Pre-filter the list</p>
<pre>list:
  object_actions:
    _edit: ~
    viewPhones: { label: Phone numbers, action: viewPhones }</pre>
<p>Set filter attribute in user session. This is a bit complex. (Not certain how this is &#8220;pre-filtering&#8221;)</p>
<p>8. Row-level ownership</p>
<p>Only allow owners of objects access.<br />
Example presumes sfGuard plugin and there is a user_id field as a foreign key on each object</p>
<pre>protected function buildQuery() {
  $query = parent::buildQuery();
  $query-&gt;andWhere('user_id = ?', $this-&gt;getUser()-&gt;getId();
}</pre>
<pre>preExecute() {
  if the action is not new, check getUser()-&gt;isOwner($object)
  which looks for a user_id on the object, PHP is polymorphic
  so we don't care what class it is
}</pre>
<p>(Our preferred solution is a userHasPrivilege module on the model class, which looks at the currently logged in user if no sfGuardUser object is passed. Our standard privilege names are &#8216;new&#8217;, &#8216;edit&#8217;, &#8216;delete&#8217; and others as needed for that particular model. This is very extensible and allows the model to decide what is appropriate for a particular object)</p>
<p>Remove the user_id widget from the form of course, and in doUpdateObject (not updateObject ?), set the user id. Get the user id from the context -&gt; getUser() -&gt; getId()</p>
<p>Cute satan graphic pitchforking the sfContext call, you can inject the user into the form constructor instead</p>
<p>9. Custom filters</p>
<p>Find out who has a birthday today</p>
<p>addBirthdayTodayColumnQuery($query, $field, Value)</p>
<p>(Is this auto-sniffed or is there something in generator.yml?)</p>
<p>sfAdminDashPlugin. Joomla style admin dashboard with configurable admin navigation. Replaces the admin css. Manyally add header component and footer partial to layout. Nice if you don&#8217;t have time to bespoke. By Kevin Bond.</p>
<p>sfAdminThemejRollerPlugin by Gerald Estadieu</p>
<p>Looks stunning, jQuery, theme roller system, popup filters, abs in edit view, completely new admin theme</p>
<p>Extending Methods</p>
<p>Sometimes you want to just define an alternate CSS file in generator.yml.</p>
<p>You can also override templates and actions, but you can&#8217;t re-use that between modules (unless you move it to a theme). Can become untidy and hard for other devs to follow</p>
<p>So: create a theme. &#8220;Steep learning curve &#8211; PHP in PHP!&#8221;</p>
<pre>mkdir -p data/generator/sfDoctrineModule/newtheme</pre>
<p>Then copy the generator files from sfDoctrine plugin</p>
<p>[Screenshot of path. He's on Windows]</p>
<p>Extending admin events</p>
<pre>admin.pre_execute: notified before any action is executed.
admin.build_criteria: helps you filter criteria used for list view. An alternative to overriding buildQuery.
admin.save_object and admin.delete_object</pre>
<p>The future: &#8220;so, what does django do?&#8221;</p>
<p>You get a dashboard with all of your modules laid out. &#8220;Show related models&#8221; is more remarkable, you automatically get embedded forms for related models that let you manage connected objects. Object history. Delete related objects warning: warns you about what ON DELETE CASCADE is about to do to various objects.</p>
<p>Future: scope and features</p>
<p>Better support for embedded forms<br />
sfGrid<br />
Fulltext search<br />
Save and list! Should act like save button in any other application!<br />
Dashboard<br />
Nested sets and ordering as standard features<br />
Inherit from multiple themes. Right now you can only have one</p>
<p>(Symfony 2.0&#8217;s &#8220;everything is a plugin and a dependency injection container&#8221; philosophy should yield great things here)</p>
<hr /><a href="http://apostrophenow.com/">Check out Apostrophe, our Content Management System for Symfony!</a></p>
]]></content:encoded>
			<wfw:commentRss>http://window.punkave.com/2010/02/16/symfony-live-john-cleveley-on-the-admin-generator/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Thomas Rabaix: internationalization and Symfony</title>
		<link>http://window.punkave.com/2010/02/16/thomas-rabaix-internationalization-and-symfony/</link>
		<comments>http://window.punkave.com/2010/02/16/thomas-rabaix-internationalization-and-symfony/#comments</comments>
		<pubDate>Tue, 16 Feb 2010 09:14:45 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[internationalization]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://window.punkave.com/?p=976</guid>
		<description><![CDATA[Hi folks, I'm attending Symfony Live 2010. These are my notes from Thomas Rabaix's talk on internationalization in Symfony, with a few offhand remarks of my own.

Thomas Rabaix talk on internationalization
]]></description>
			<content:encoded><![CDATA[<p>Hi folks, I&#8217;m attending Symfony Live 2010. These are my notes from Thomas Rabaix&#8217;s talk on internationalization in Symfony, with a few offhand remarks of my own.</p>
<p>Thomas Rabaix talk on internationalization</p>
<p>URL: routingModel: doctrine + propel<br />
Form: sfForm</p>
<p>Most of the 18n is based on Prado framework<br />
ICU data updated in sf1.3/sf1.4<br />
You need</p>
<pre>
all:
  .settings:
    i18n: true
    default_culture: en
</pre>
<p>Culture detection</p>
<p>HTTP request -&gt; culture in URL </p>
<p>If no culture in URL, culture in session</p>
<p>What about culture in the header? </p>
<p>Updates sfRouting, sf18n, executes controller</p>
<p>We&#8217;re not big on culture in URL in apostrophe since the session can do it but it&#8217;s nice to be able to bookmark.</p>
<pre>
sfContext::getInstance()->getUser()->setCulture('fr')
</pre>
<p>Notify the user.change_culture event (this doesn&#8217;t do the changing, it just notifies other things that care; why doesn&#8217;t setCulture do this for us?)</p>
<pre>
recipe_esarch:
  url: /:sf_culture/recipe/view
</pre>
<p>Example: /en/recipes/search => sf_culture = en</p>
<p><span id="more-976"></span></p>
<p>It&#8217;s built in, you don&#8217;t have to pay any mind to the culture yourself.</p>
<p>Or:</p>
<pre>
/recettes/recherche
/recipes/search
</pre>
<p>This is not built in, you would need suitable routes. Do we want i18n slugs for Apostrophe?</p>
<p>ysfDimensionPlugin</p>
<p>You can have custom config files per dimension, including a culture dimension</p>
<pre>
SF_APP_FOLDER/config/en/routing.yml
</pre>
<p>Con: duplicate route configurations. Pro: no performance hit from trolling through irrelevant routes</p>
<p>Showed a custom subclass of sfPatternRouting, would like to see that fly by again (:</p>
<p>Doctrine is succinct:</p>
<pre>
Page:
  columns:
    title: string(255)
		content: clob
		enabled: boolean

	actAs:
	  i18n:
	    fields: [title, content]
</pre>
<p>Our friend the clob appears</p>
<p>Default model culture can be accessed through sfDoctrineRecord::getDefaultCulture(), sfDoctrineRecord::setDefaultCulture()</p>
<p>Doctrine:</p>
<pre>
$page->Translation['fr']->title
$page->getTitle()
$page->title
</pre>
<p>Propel is a little cleaner</p>
<pre>
$page->getTitle('fr')
</pre>
<p>But I suspect you are usually setting the default culture to the user&#8217;s culture and then going merrily on</p>
<p>ORM never automatically left joins the translation table when building a fancier query. You must do it.</p>
<p>NumberHelper:<br />
format_currency </p>
<pre>
&lt;?php echo format_currency(1000.999, 'EUR', 'en') ?&gt;: Eur1,0001.99
&lt;?php echo format_currency(1000.999, 'EUR', 'fr') ?&gt;: 1 0001,99Eur
</pre>
<p>format_date and format_datetime helpers are internationalized, we should be using them in preference to date</p>
<p>__() helper translates individual strings in any context</p>
<pre>
&lt;?php echo __("My name is %name%", array("%name" => "Thomas"), "helpers") ?&gt;
=> My name is Thomas
</pre>
<p>format_number_choice helper</p>
<pre>
&lt;?php echo format_number_choice('[0] no file[1] one file[1, Inf]', $n) ?&gt;
</pre>
<p>Probably didn&#8217;t copy this quite right but it is a very convenient way to echo one of several possibilities based on a variable, internationalized</p>
<p>sfNumberFormat</p>
<p>sfForm introduced by Alien pic everyone is a little terrified of it at times</p>
<p>sfWidgetFormI18nChoiceCountry, Currency, Language, Timezone, Date, DateTime, Time are all internationalized (although the date and time widgets are very very basic)</p>
<p>Culture is set in the controller, &#8220;never use sfContext::getInstance() in your form.&#8221; It&#8217;s not clear why not. </p>
<p>Form has a translation callback function through the sfWidgetFormSchemaFormatter class, setTranslationCllable. You can override this to change translation behavior. sf1.3/sf1.4 choice widgets extending sfWidgetFormChoiceBase automatically translate choices provided (so you don&#8217;t have to call __() yourself, and shouldn&#8217;t)</p>
<p>Translations can come from a lot of sources&#8230; MySQL etc. Generating translations is a bit complicated</p>
<p>So Thomas recommends mgl18nPlugin kindly open sourced by menugourmet.com</p>
<p>Can Apostrophe support culture prefix in URLs out of the box? Probably since it&#8217;s at the very beginning, a variation on our suggested routes would work fine. And we already automatically fetch the slots for the user&#8217;s culture.</p>
<hr />
<a href="http://apostrophenow.com/">Check out Apostrophe, our Content Management System for Symfony!</a></p>
]]></content:encoded>
			<wfw:commentRss>http://window.punkave.com/2010/02/16/thomas-rabaix-internationalization-and-symfony/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apostrophe 1.0!</title>
		<link>http://window.punkave.com/2010/02/11/apostrophe-1-0/</link>
		<comments>http://window.punkave.com/2010/02/11/apostrophe-1-0/#comments</comments>
		<pubDate>Fri, 12 Feb 2010 03:26:57 +0000</pubDate>
		<dc:creator>Geoff</dc:creator>
				<category><![CDATA[Apostrophe]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://window.punkave.com/?p=931</guid>
		<description><![CDATA[We have labored long and hard to prepare for the 1.0 release of Apostrophe. As we said before, it has been stable and in use for a long time now, but we wanted to  mark a moment in time with an official release.
Probably the easiest way to get a feel for Apostrophe is to [...]]]></description>
			<content:encoded><![CDATA[<p>We have labored long and hard to prepare for the 1.0 release of Apostrophe. As we said before, <a href="http://window.punkave.com/2010/02/08/apostrophe-1-0-release-wednesday/">it has been stable and in use for a long time now</a>, but we wanted to  mark a moment in time with an official release.</p>
<p><span style="font-weight: normal;">Probably the easiest way to get a feel for Apostrophe is to </span><strong>go to our </strong><a href="http://demo.apostrophenow.com/"><strong>demo site</strong></a><strong> and login. The user name is &#8220;admin&#8221; and the password is &#8220;demo&#8221;.</strong><span style="font-weight: normal;"> Keep in mind that the content resets itself every hour on the hour.</span></p>
<p>If you want to get your bearings before you you do that, head over to the newly revamped <a href="http://apostrophenow.com">Apostrophe</a> site. For the very interested, you should consider checking out our extensive <a href="http://www.apostrophenow.com/home/readme">README</a> which includes an Installation section, an Editor&#8217;s guide, a Designer&#8217;s guide and a Developer&#8217;s guide.</p>
<p>Now for some of the highlights.</p>
<h2>Slots!</h2>
<p><a title="Apostrophe Slot Buttons by P'unk Ave, on Flickr" href="http://www.flickr.com/photos/punkavenue/4355023008/"><img src="http://farm3.static.flickr.com/2679/4355023008_2dc0742811_o.jpg" alt="Apostrophe Slot Buttons" width="480" height="78" /></a></p>
<p>There are rich text, image, slideshow, video, button, PDF, Raw HTML, and other slots built in. If you have used Apostrophe before, you know that you can add a slot on a page, choose what you want to place and then reorder it. New custom slots can also be made for any project.</p>
<h2>Slot Options</h2>
<p><a href="http://www.flickr.com/photos/punkavenue/4354278519/" title="Apostrophe Slideshow: Compact Option by P'unk Ave, on Flickr"><img src="http://farm3.static.flickr.com/2463/4354278519_b56665a5f5_o.jpg" width="223" height="151" alt="Apostrophe Slideshow: Compact Option" /></a></p>
<p>For some time we have wanted to be able to give you more fine control of a specific slot. For instance, what if you wanted to make the background color of a some text slots yellow, and some white? In the past that would have required two slots. Now, it can just be an option of the same text slot. In this screenshot, you can see that we allow you to switch between different types of slideshows. Make sure you try that out in the demo. It is probably our favorite new feature. We really sweated the details on making sure the option control was contextual. We hope you appreciate the effort.</p>
<p>There are many other new features and highlights, but for the moment I will leave you to find them in the <a href="http://demo.apostrophenow.com">demo</a> (remember: user is &#8220;admin&#8221; and password is &#8220;demo&#8221;) or in the <a href="http://www.apostrophenow.com/home/readme">documentation</a>. (We promise to highlight these in future posts.)</p>
<h2>So, what does this mean to you?</h2>
<p>Well, it depends. If you are a PHP developer with <a href="http://www.symfony-project.org/">Symfony</a> skills you can get started today. From our README:</p>
<blockquote><p>Right now Apostrophe is best suited to PHP developers who want to make an intuitive content management system available to their clients. Apostrophe is very easy for your clients to edit, administer and maintain once it is set up. Right now, though, Apostrophe installations does call for some command line skills and a willingness to learn about <a href="http://www.symfony-project.org/">Symfony</a>. We are working to reduce the learning curve.</p></blockquote>
<p>If you are a front-end developer and want to get started using Apostrophe, <a href="mailto:geoff@punkave.com">talk to us</a>. We are in the process of working out a package where we set up a staging server with <a href="http://www.symfony-project.org/">Symfony</a> and <a href="http://www.apostrophenow.com/">Apostrophe</a> running on it. We would train you on templating in Apostrophe and you should be up in running in a day or two.</p>
<p>If you don&#8217;t want to go that route, here is what we suggest:</p>
<blockquote><p>Front-end developers who do not yet have PHP and Symfony skills but wish to set up an Apostrophe site by themselves should consider tackling the Symfony tutorial to get up to speed. It&#8217;s not necessary to complete the entire tutorial, but it helps to have at least a passing familiarity with Symfony.</p></blockquote>
<p>If you just think Apostrophe is rad and you want one&#8230; like now, <a href="mailto:geoff@punkave.com">just drop us a line</a>. We would be happy to design and implement an Apostrophe site for you. You would be joining some good company if you do.</p>
<h2>Surprise!</h2>
<p>We (more accurately <a href="http://window.punkave.com/author/tom/">Tom</a>) decided to build an extra slot last night as a gift for making you wait an extra day. Isn&#8217;t that nice of us (him)?</p>
<p>If you are observant you may have noticed an extra slot in the screenshot above. So without further ado, we present the Feed Slot (commonly referred to as the RSS Feed slot, but it will take Atom feeds as well). Use this to bring in your Twitter updates or maybe our <a href="http://www.apostrophenow.com/svn.rss">Apostrophe SVN code commit feed</a>.</p>
<p>Feel free to thank us anytime. (We like expensive beer.)</p>
<p>This is really just the beginning. We hope you have an opportunity to <a href="http://demo.apostrophenow.com/">check out Apostrophe</a>. We would love to <a href="mailto:geoff@punkave.com">hear from you</a> if you do. Comments below would be much appreciated (and read), as well.<br />
__________________</p>
<p>Don&#8217;t forget to follow us on Twitter for on-going updates: <a href="http://twitter.com/apostrophenow">@apostrophenow</a></p>
<p>If you are a developer, the <a href="http://trac.apostrophenow.org/">Apostrophe Trac</a> is the home of the open source community. On that site you can open support tickets, browse and contribute to the Wiki, and access and potentially contribute to the source code via svn (you&#8217;ll need to contact us if you want write access).</p>
<p>Also, please join the <a href="http://groups.google.com/group/apostrophenow">Apostrophe Google Group</a> if you want to tap into the community of people using Apostrophe to get support and help.</p>
]]></content:encoded>
			<wfw:commentRss>http://window.punkave.com/2010/02/11/apostrophe-1-0/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Oh So Close!</title>
		<link>http://window.punkave.com/2010/02/10/oh-so-close/</link>
		<comments>http://window.punkave.com/2010/02/10/oh-so-close/#comments</comments>
		<pubDate>Thu, 11 Feb 2010 02:18:06 +0000</pubDate>
		<dc:creator>Geoff</dc:creator>
				<category><![CDATA[Apostrophe]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://window.punkave.com/?p=910</guid>
		<description><![CDATA[
We have decided to push back the release of Apostrophe 1.0 until tomorrow. 
We are truly in the home stretch. Just hitting a few bugs, finishing up documentation and adding some instructional content to the demo site. To tide you over, we are publishing this screencast of how you add video to a project by [...]]]></description>
			<content:encoded><![CDATA[<p><object width="500" height="425"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=9366056&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=ff9933&amp;fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=9366056&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=ff9933&amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="500" height="425"></embed></object></p>
<p>We have decided to push back the release of <a href="http://apostrophenow.com">Apostrophe</a> 1.0 until tomorrow. </p>
<p>We are truly in the home stretch. Just hitting a few bugs, finishing up documentation and adding some instructional content to the demo site. To tide you over, we are publishing this screencast of how you add video to a project by searching YouTube from within Apostrophe. Note how we bring in associated metadata (url, title, etc). For the record, you can add video by pasting in embed codes from countless other services like Viddler and Vimeo. </p>
<p>Please tune back in tomorrow.</p>
<p>P.S. We are not blaming the snow for the delay, but did want to note that we are certainly under a deep white blanket here in Philadelphia.</p>
]]></content:encoded>
			<wfw:commentRss>http://window.punkave.com/2010/02/10/oh-so-close/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
