Why PHP Won

An excellent article by Eric Reis over on his blog in which he talks about “why PHP won” in his web application development over other (web scripting) languages:

As a language, it’s inelegant. Its object-orientation support is “much improved” – which is another way of saying it’s been horrendous for a long time. Writing unit tests or mock objects in PHP is an exercise in constant frustration. And yet I keep returning to PHP as a development platform, as have most of my fellow startup CTOs. This post is about why.

Its an interesting piece in which Eric chooses to describe PHP’s success in terms of what a new language might have to do better in order to challenge PHP’s popularity/success, in short he suggests the following:

  • Speed of iteration (a good write/test/debug cycle)
  • Better mapping of outputs to inputs
  • A similar standard library
  • A better OOP implementation

I have to confess I found myself agreeing with Eric. His piece is well worth reading!

To Smarty, or not to Smarty?

At Talis we have been doing more than just our fair share of PHP development, in fact we are using the language to implement a number of new prototypes, products and services. One of the questions that I’ve been struggling with recently is whether or not we should be using the Smarty template engine. Whilst we have used it with a measure of success on several projects, I’ve been struggling with what Smarty represents. Smarty is written in PHP and allows you to create templates that are marked up using Smarty’s own language syntax however this means that generated pages are precompiled at runtime down to pure PHP before being served. What strikes me is that Smarty is a Template Engine written in a language that was designed primarily for templating. So the question I’m left struggling with is: Why do you want to add the overhead of a secondary language to do what your first language already does and is fully capable of doing?

The more I think about it, the more I’m convinced that the answer is that you dont! I’m going to try to explain why by examining some of the reasons that are often cited for using Smarty.

Separating PHP from HTML

I’ve often heard his mantra, but I would describe it in a slightly different way and that’s by saying that what’s actually desired is to keep keep your “business logic” separate from your “presentation logic”, which is subtly different. If you accept that the two are indeed different then you should realise that all that’s actually required is a way to keep your Views and Controllers separated which can be achieved using plain PHP in your view scripts (templates), without the overhead of having to add a new language into the mix.

Easier to read

I’ve always struggled with this and here’s why … does anyone really think that this

  1. {section name=aValue loop=$list}
  2. Value is: {$aValue}
  3. {/section}

…is really easier to read than its pure PHP equivelant? here …

  1. <? foreach ( $list as $aValue ) { ?>
  2. Value is: <?=$aValue?>
  3. <? } ?>

… I honestly don’t think that it is. I know that there’s a view in the community that there are front-end only Developers out there or Web Designers that shouldn’t have to learn PHP, but are they really better off being taught a different language? Firstly I’d question if there are actually Developers out there who know Smarty and don’t have an understanding of PHP, but for the purposes of this exercise lets assume that these mythical smarty only developers do exist then I have to confess I agree entirely with Harry Feuks1 when he says

Walk them through a few sections of the PHP Language Reference
and show them how PHP‘s control structures and simple functions like
echo() works and that’s all they need.

and also with Brian Lozier2 who describes it much more convincingly when he says:

Basically, it just provides an interface to PHP with new syntax. When 
stated like that, it seems sort of silly. Is it actually more simple to 
write {foreach --args} than ? If you do think it's
simpler, consider this. Is it so much simpler that there is value in 
including a huge template library to get that separation? Granted, 
Smarty offers many other great features (caching, for instance), but it 
seems like the same benefits could be gained without the huge 
overhead of including the Smarty class libraries.

Speed

I’ve read several articles online that try to explain that there is a performance overhead when including Smarty. Yes I know you can cache your Smarty templates to improve performance in fact it’s not a question of ‘can’ it’s a question of ‘you really have to’ as Harry points out in the same article

if you hadn’t chosen to write your own programming language
and built an interpreter with PHP to parse it, you wouldn’t have slowed
down your application in the first place!

I performed a little test to try and illustrate what happens when you simple include Smarty, let alone actually use it. First off create yourselves a file called test.php and add the following line of code to it:

  1. <?php echo="Hello world" ?>

Now let’s benchmark this using Apache Benchmark, thanks to Vivek for providing some useful documentation on how to do this3. Open up a terminal window and issue the following command:

  1. ab -c 10 -n 100 http://localhost/test.php

Which on my system ( mac osx 10.5.2 4gb ram php5+apache2 ) resulted in the following:

nadeemshabir$ ab -c 10 -n 100 http://localhost/test.php
This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright 2006 The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        Apache/2.2.9
Server Hostname:        localhost
Server Port:            80

Document Path:          /test.php
Document Length:        11 bytes

Concurrency Level:      10
Time taken for tests:   0.58753 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      19695 bytes
HTML transferred:       1111 bytes
Requests per second:    1702.04 [#/sec] (mean)
Time per request:       5.875 [ms] (mean)
Time per request:       0.588 [ms] (mean, across all concurrent requests)
Transfer rate:          323.39 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.5      0       2
Processing:     2    4   5.4      4      34
Waiting:        1    4   5.5      3      33
Total:          2    5   5.3      4      34

Percentage of the requests served within a certain time (ms)
  50%      4
  66%      4
  75%      5
  80%      6
  90%      8
  95%     14
  98%     27
  99%     34
 100%     34 (longest request)
Nadeems-Computer:smarty-test nadeemshabir$

So the important figures are Request Per Second: 1702.04, Time per request: 5.875 [ms] (mean), Time per request: 0.588 [ms] (mean, across all concurrent requests) and Transfer rate: 323.39 [Kbytes/sec] received. Now let’s create a new test file called test-smarty.php, and add to it the following:

  1. <? include_once( ‘smarty/libs/Smarty.class.php’ ); ?>
  2. <?php echo "Hello world" ?>

and run the same test again …

  1. ab -c 10 -n 100 http://localhost/test-smarty.php

which results in the following:

nadeemshabir$ ab -c 10 -n 100 http://localhost/test-smarty.php
This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright 2006 The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        Apache/2.2.9
Server Hostname:        localhost
Server Port:            80

Document Path:          /test-smarty.php
Document Length:        11 bytes

Concurrency Level:      10
Time taken for tests:   0.310262 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      19500 bytes
HTML transferred:       1100 bytes
Requests per second:    322.31 [#/sec] (mean)
Time per request:       31.026 [ms] (mean)
Time per request:       3.103 [ms] (mean, across all concurrent requests)
Transfer rate:          61.24 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2   4.7      0      17
Processing:     9   26  14.3     22      84
Waiting:        8   25  14.1     22      82
Total:         13   28  14.7     24      87

Percentage of the requests served within a certain time (ms)
  50%     24
  66%     28
  75%     35
  80%     37
  90%     46
  95%     70
  98%     84
  99%     87
 100%     87 (longest request)
Nadeems-Computer:smarty-test nadeemshabir$

Now we can see a dramatic difference those important figures are now – Request Per Second: 322.31, Time per request: 31.026 [ms] (mean), Time per request: 3.103 [ms] (mean, across all concurrent requests) and Transfer rate: 61.24[Kbytes/sec] received. If you take simply the requests per second we are now only serving almost a sixth of the requests we originally served, and that’s without even using Smarty, that’s from just simply including the codeline!

Conclusions

To my mind those results and the arguments I’ve cited are pretty emphatic, I can’t justify using a template engine that adds so much overhead when you can achieve the same results using a pure PHP implementation. This view seems to be widely held and accepted, it’s part of the reason why many other Template Engines like Savant, Zend_View and Solar_View all embrace a different ethos to Smarty i.e they don’t compile your templates into PHP because they use PHP as the template language.

If you are at all unconvinced by the arguments I’ve presented then like me you might want to consider the words of Hasin Hayder who is the author of the popular book Smarty PHP Template Programming and Applications which I own a copy of :). He also created this Smarty Cheat Sheet which I along with several of my colleagues have copies of on our desks. Earlier this year Hasin wrote an article entitled Once upon a time there was Smarty, in this article Hasin touches on many of the points that I and others have made, and whilst the depth of my knowledge of smarty could easily be challenged Hasin is without question an expert, so he when he said …

I seriously don’t think there is need of Smarty anymore. Its dead! If 
you guys don’t agree with me, you can spend hell lot of time learning 
that {$name} actually does what you could do with “echo $name”. If 
you write a dispatcher which is smart enough to bring the variables 
from a controller scope to the scope of a view, why do u need to learn 
a separate parser like smarty? ... Learning all those functions, loops, 
logics, caching and others in smarty takes so much time that I would 
love to suggest you to learn writing efficient PHP code in template layer 
rather than learning something like smarty ...

… I stopped and listened … and then I found myself agreeing with his closing statement …

Sometime it’s better to realize thats its time to unlearn something.
  1. phpPatterns, http://www.phppatterns.com/docs/design/templates_and_template_engines[back]
  2. Template Engines, http://www.massassi.com/php/articles/template_engines/[back]
  3. How to performance benchmark a web server, http://www.cyberciti.biz/tips/howto-performance-benchmarks-a-web-server.html[back]

Installing PHP5 +apache2 using Macports on Leopard

I have had all sorts of fun and games trying to get php5 and apache2 installed on Leopard using macports. Six months ago I eventually gave up after lodging a ticket with macports.org no matter how hard I tried or what advise I followed it simply wouldn’t install. In the end my colleague Andrew tar’ed up his /opt folder and I copied that onto my machine and did a chown to my username/group and had a working php5 and apache2 install.

I had some problems yesterday getting yaz installed on ubuntu and decided to follow some instructions that Andrew gave me to install it on Leopard instead. I decided to bite the bullet and attempt to do a pure PHP5 apache2 install under macports again, and then use port to install php-yaz. Suffice to say that I ran into similar problems to those I encountered six months ago.

However after persevering I managed to get it all installed what follows is a summary of how I got it to work, in case anyone else out there ( and judging by the board posts that’s lots of you) is still struggling, or waiting for Macports 1.7.0 to be released.

  1.  
  2. # first install macports 1.6.0 download it from:
  3. # http://svn.macports.org/repository/macports/downloads/MacPorts-1.6.0/MacPorts-1.6.0-10.5-Leopard.dmg
  4.  
  5. # check port is installed ok , port version should be correct
  6.  
  7. sudo port version
  8.  
  9. # do a self update and a sync
  10.  
  11. sudo port selfupdate
  12. sudo port sync
  13.  
  14. # you have to install gawk, gmake and nawk before proceeding
  15.  
  16. sudo port install gawk
  17. sudo port install gmake
  18. sudo port install nawk
  19.  
  20. # now attempt to install php5 +apache2 and mysql
  21. # you seem to have to install it with mysql or it will fail regardless.
  22.  
  23. sudo port install php5 +apache2 +mysql5 +server
  24.  
  25. # the above step will still fail (or did for me)
  26. # it fails during msyql5 install, so clean and install TK
  27. # then retry just mysql
  28.  
  29. sudo port clean mysql5
  30. sudo port install TK
  31. sudo port install mysql5 +server
  32.  
  33. #once this succeeds run the original command again
  34.  
  35. sudo port install php5 +apache2 +mysql5 +server
  36.  
  37. # this should now go all the way through and work
  38.  

Once it is done follow the original instructions here.

This is so convoluted!! I hope the Macports folks sort this out. Even the ticket I raised didn’t specify the steps I took as a fix, and I basically stumbled onto them through trial and error. If anyone has a better explanation for why this worked then please let me know. Otherwise I hope it helps anyone else experiencing the same difficulties.

Interesting PHP / XSL Cross Platform Defect

Came across a rather interesting cross platform problem whilst trying to perform a rather simple xsl transformation using PHP. To understand the problem take a look at this little snippet of XSL and see if you can spot what’s wrong with it:

  1.  
  2.   <xsl:template name="someTemplate">    
  3.     <xsl:for-each select="/rdf:RDF/rss:item" >
  4.     <xsl:param name="foo"/>
  5.     …
  6.     </xsl:for-each>
  7.   </xsl:template>
  8.  

The flaw in the snippet above is that it defines an xsl:param inside an xsl:for-each which is completely invalid. Now earlier today one of my colleagues was refactoring some XSL and whilst he was copying code from one template into another he inadvertently made the mistake above. Now you’d like to think that when you try to run the XSL through PHP that the invalid markup would throw an error. Well under PHP 5.2.2 on Window’s it doesn’t throw an error – in fact the XSL runs and performs the transformation. However when you run the same XSL under PHP running on Linux it quite rightly throws an error and informs you that the xsl:param is invalid.

I haven’t looked too deeply into why the invalid markup is accepted under Windows, I suspect it might have something to do with the php_xsl.dll that is comes as part of PHP 5.2.2, but it is something that made my jaw drop. Has anyone else experienced this?

Calling PHP Functions from XSL

Craig, a colleague of mine who newly joined our development team at Talis showed me this neat little trick. Many things are far easier to do in PHP than they are in XSL, and some things simply can’t be done in pure XSL. A solution is to call PHP functions directly from within your XSL.

1) In your xsl stylesheet add:

   namespace xmlns:php="http://php.net/xsl"
   exclude-result-prefixes="php"

2) To call the php function and access the result use:

  1.  
  2.   <!– for string use this –>
  3.   <xsl:value-of select="php:functionString(‘phpFunctionName’, /xpath)"/>
  4.  
  5.   <!– for DOM Nodes use this –>
  6.   <xsl:copy-of select="php:function(‘phpFunctionName’, /xpath)"/>
  7.  

You can pass as many parameters as you want to either php:function or php:functionString – the latter merely converts output to a string and otherwise they are identical.

3) you must register them with the XSL Transformer:

  1.  
  2. $doc = new DOMDocument();
  3. $xsl = new XSLTProcessor();
  4.  
  5. $doc->load($xsl_filename);
  6. $xsl->importStyleSheet($doc);
  7.  
  8. $doc->load($xml_filename);
  9.  
  10. $xsl->registerPHPFunctions(); // This is the important call for this functionality
  11.  
  12. echo $xsl->transformToXML($doc);
  13.  

4) In your php function, access parameters passed in as strings as if they are a php string. If you pass a dom structure as a parameter then you need to access it along the lines of:

  1.  
  2. function ProcessDomFromXslCall($DomList) {
  3.    $NodeList = $DomList[0]->getElementsByTagNameNS(‘namespace’, ‘element-name’);
  4.    for($i=0; $i<$NodeList->length; $i++) {
  5.      echo $i, $NodeList->item($i)->nodeValue;
  6.    }
  7.  }
  8.  

$DomList will include the root element of the XPath used to call the PHP function

If you want to dump what you pass to PHP as a string you need to do:

  1.  
  2.  function DumpStuff($DomElements) {
  3.    echo $DomElements[0]->C14N(); // note this function isn’t yet documented in the PHP manual !
  4.  }
  5.  

It’s a very useful feature … good luck with it.

Continuous Integration with PHP

I’m in the process of setting up a continuous integration environment for a new PHP project I’m starting. On our previous project, which was Java based, we used the following tools to support similar requirements on that project in order to allow us to implement the project using a test driven approach and automate build generation:

  • Cruise Control – For setting up a Continuous Build process.
  • Ant – A Java based build tool.
  • JUnit – A Java Based xunit testing framework
  • PMD – A tool that checks for common coding standards violations.
  • Corbetura – A Java based code coverage too that calculates the percentage of code accessed by unit tests

I’ve managed to set up an analogous process for our PHP project using the following tools:

  • Cruise Control – For setting up a Continuous Build process.
  • Ant – A Java based build tool.
  • PHPUnit – An Xunit testing framework for PHP
  • PHP_CodeSniffer – A PHP tool that checks for common coding standards violations.
  • Xdebug – Debugging tool, which when combined with PHPUnit, can provide Code Coverage Metrics

It seems to work quite well, here’s the relatively simple ant build script that controls it all.

  1.  
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <project default="all" name="DummyProject" basedir=".">
  4.     <target name="all" depends="clean, init, test, sniff" />
  5.  
  6.     <target name="clean">
  7.         <delete dir="doc/CodeCoverage" />
  8.         <delete dir="doc/UnitTestReport" />
  9.     </target>
  10.  
  11.     <target name="init">
  12.         <mkdir dir="doc/CodeCoverage" />
  13.         <mkdir dir="doc/UnitTestReport" />
  14.     </target>
  15.  
  16.     <target name="test" description="Run PHPUnit tests">
  17.         <exec dir="./" executable="TestRunner.bat" failonerror="true">
  18.         </exec>
  19.     </target>
  20.    
  21.     <target name="sniff" description="">
  22.         <exec dir="./" executable="Sniffer.bat" failonerror="true">
  23.         </exec>
  24.     </target>
  25. </project>
  26.  

I’m currently running this on a windows machine although it’s trivial to change it work in an *ix based environment which I’ll probably configure in the next day or so. I had a couple of problems installing PHP_CodeSniffer although it was because I hadn’t installed PEAR properly. If you have any problems installing PHP_CodeSniffer under Windows then follow these instructions:

To install PEAR under windows do the following, which assumes you have PHP5.2x installed in c:\php :

  cd c:\\php
  go-pear.bat

The interactive installer presents you with some options, if you follow the defaults you should be fine.
Once PEAR has installed you can install PHP_CodeSniffer like this:

  cd c:\\php
  pear install PHP_CodeSniffer-beta

This will download the PHP_CodeSniffer package and install into into your php/PEAR folder.

Once this is done you can check to see if it has installed by calling phpcs with -h flag which will produce the following:

C:\\php>phpcs -h
Usage: phpcs [-nlvi] [--report=] [--standard=]
    [--generator=] [--extensions=]  ...
        -n           Do not print warnings
        -l           Local directory only, no recursion
        -v[v][v]     Print verbose output
        -i           Show a list of installed coding standards
        --help       Print this help message
        --version    Print version information
               One or more files and/or directories to check
         A comma separated list of file extensions to check
                     (only valid if checking a directory)
           The name of the coding standard to use
          The name of a doc genertor to use
                     (forces doc generation instead of checking)
             Print either the "full" or "summary" report

Now try it out …

C:\\php>phpcs C:\\Projects\\dummyproject\\test\\

FILE: C:\\Projects\\dummyproject\\test\\AllTests.php
--------------------------------------------------------------------------------
FOUND 7 ERROR(S) AND 0 WARNING(S) AFFECTING 7 LINE(S)
--------------------------------------------------------------------------------
  1 | ERROR | End of line character is invalid; expected "\n" but found "\r\n"
  2 | ERROR | Missing file doc comment
  3 | ERROR | Constants must be uppercase; expected 'PHPUNIT_MAIN_METHOD' but
    |       | found 'PHPUnit_MAIN_METHOD'
 12 | ERROR | Missing class doc comment
 14 | ERROR | Missing function doc comment
 19 | ERROR | Missing function doc comment
 30 | ERROR | Constants must be uppercase; expected PHPUNIT_MAIN_METHOD but
    |       | found PHPUnit_MAIN_METHOD
--------------------------------------------------------------------------------

Check the documentation for what the various command line switches do, but you can generate summary reports as well on an entire directory tree:

C:\\php>phpcs --report=summary --standard=Squiz C:\\Projects\\dummyproject\\test\\

PHP CODE SNIFFER REPORT SUMMARY
--------------------------------------------------------------------------------
FILE                                                            ERRORS  WARNINGS
--------------------------------------------------------------------------------
C:\\Projects\\dummyproject\\test\\AllTests.php                  24      0
C:\\Projects\\dummyproject\\test\\NodeTest.php                  10      0
C:\\Projects\\dummyproject\\test\\NodeTypeTest.php              11      0
--------------------------------------------------------------------------------
A TOTAL OF 45 ERROR(S) AND 0 WARNING(S) WERE FOUND IN 3 FILE(S)
--------------------------------------------------------------------------------

Overall I’m quite happy with this set up which for the most part was pretty straight forward. I have no doubt it will evolve over time but I think it’s a good foundation on which to build upon.