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.  

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

  1.  

… 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. span class=”st0″>"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.  

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. span class=”st0″>’smarty/libs/Smarty.class.php’"Hello world"

and run the same test again …

  1.  

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.  

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.

Automated regression testing and CI with Selenium PHP

I’ve been doing some work this iteration on getting Selenium RC integrated into our build process so we can run a suite of automated functional regression tests against our application on each build. The application I’m working on is written in PHP, normally when you use Selenium IDE to record a test script it saves it as a HTML file.

For example a simple test script that goes to Google and verifies that the text “Search:” is present on the screen and the title of the page is “iGoogle” looks like this:

  1.  
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5. <title>New Test</title>
  6. </head>
  7. <body>
  8. <table cellpadding="1" cellspacing="1" border="1">
  9. <thead>
  10. <tr><td rowspan="1" colspan="3">New Test</td></tr>
  11. </thead><tbody>
  12. <tr>
  13.     <td>open</td>
  14.     <td>/ig?hl=en</td>
  15.     <td></td>
  16. </tr>
  17. <tr>
  18.     <td>verifyTextPresent</td>
  19.     <td>Search:</td>
  20.     <td></td>
  21. </tr>
  22. <tr>
  23.     <td>assertTitle</td>
  24.     <td>iGoogle</td>
  25.     <td></td>
  26. </tr>
  27.  
  28. </tbody></table>
  29. </body>
  30. </html>
  31.  

You can choose to export the script in several other languages, including PHP, in which case the test script it produces looks like this:

  1. span class=”st0″>’Testing/Selenium.php’‘PHPUnit/Framework/TestCase.php’"*firefox", "http://localhost:4444/""/ig?hl=en""Search:""iGoogle"

The Export produces a valid PHPUnit test case that uses the Selenium PHP Client Driver(Selenium.php). Whilst the script is valid and will run you do need add a little more to it before the test will correctly report errors. As it stands all errors captured during the test are added to an array called verificationErrors, by catching the assertion Exceptions that are thrown when an assert fails, in other words if you ran this test as it is, and it did fail you wouldn’t know! To correct this we need to do two things. Firstly, each assert needs to have a message added to it which will printed out in the test report if the assert fails. Secondly we need to modify the tearDown method so that once a test has run, it checks the verificationErrors array, and if any failures have occurred, fails the test. After making these changes the PHP test script looks like this:

  1. span class=”st0″>’Testing/Selenium.php’‘PHPUnit/Framework/TestCase.php’"*firefox",
  2.                          "http://localhost:4444/""VERIFICATION ERRORS:""\n""/ig?hl=en""Search:"),
  3.                "The string Search: was not found""iGoogle",
  4.                    $this->selenium->getTitle(),
  5.                    "The page title did not match iGoogle."

Obviously, I have also given the PHP Class and test function slightly more meaningful names. Now you have a PHP Unit Test case that will use the Selenium PHP Client Driver with Selenium Remote Control to launch a browser, go to the specified URL, and test a couple of assertions. If any of those assertions fail, the tearDown method fails the test … pretty cool, right?

Well now it get’s better. Because the Selenium Client Driver has a published api which is pretty easy to follow, there’s no reason why you can’t just write test cases without using Selenium IDE … for those who want to you could even incorporate this into a TDD process. But for all this to hang together we need to be able to run a build, on a Continuous Integration server which checks out the code, runs unit tests and selenium regression tests against that code line, and only if all tests succeed passes the build.

We are currently using ANT, and CruiseControl to handle our CI/Automated build process. When running the automated suite of tests we need to ensure that the Selenium Remote Control server is also running which creates some complications. The Selenium Remote Control server takes several arguments which can also include the location of a test suite of html based selenium tests – which is really nice because the server will start, execute those tests and then end. Unfortunately you can’t invoke the server and pass it the location of a PHP based test suite. This means you need to find a way to start up the server, then run your tests, and once they are complete, shut the selenium server down.

He are the ANT targets that I have written to achieve this, if anyone can think of better ways of doing this I’d welcome any feedback or suggestions, to run this example you’d simply enter the command “ant selenium” :

  1.  
  2. <target name="selenium" depends="clean, init" description="Run the Selenium tests">
  3.   <parallel>
  4.     <antcall target="StartRCServer" />
  5.     <antcall target="RunSeleniumTests" />
  6.   </parallel>
  7. </target>
  8.                
  9. <target name="StartRCServer" description="Start the Selenium RC server">
  10.   <java jar="dependencies/SeleniumRC/lib/selenium-server.jar"
  11.         fork="true" failonerror="true">
  12.     <jvmarg value="-Dhttp.proxyHost=host.domain.com"/>
  13.     <jvmarg value="-Dhttp.proxyPort=80"/>
  14.   </java>
  15. </target>
  16.    
  17. <target name="RunSeleniumTests" description="RunAllSeleniumTests">    
  18.   <sleep milliseconds="2000" />
  19.   <echo message="======================================" />
  20.   <echo message="Running Selenium Regression Test Suite" />
  21.   <echo message="======================================" />
  22.   <exec executable="php"
  23.        failonerror="false"
  24.        dir="test/seleniumtests/regressiontests/"
  25.        resultproperty="regError">
  26.        <arg line="../../../dependencies/PHPUnit/TextUI/Command.php –log-xml ../../../doc/SeleniumTestReports/RegressionTests/TestReport.xml AllRegressionTests" />
  27.   </exec>
  28.   <get taskname="selenium-shutdown"
  29.     src="http://localhost:4444/selenium-server/driver/?cmd=shutDown"
  30.     dest="result.txt" ignoreerrors="true" />
  31.                    
  32.   <condition property="regressionTest.err">
  33.     <or>
  34.        <equals arg1="1" arg2="${regError}" />
  35.        <equals arg1="2" arg2="${regError}" />
  36.     </or>
  37.   </condition>
  38.  
  39.   <fail if="regressionTest.err" message="ERROR: Selenium Regression Tests Failed" />               
  40. </target>
  41.  

A couple of notes, the reason I have to use a conditional check at the end of the selenium target is because, if the exec task that runs the PHP tests was set to failonerror=true then the build would never reach the next line which shuts the Selenium RC server down. To ensure that always happens I have to set the exec to failonerror=false, but this means I have to check what the result was from the exec. Which if successful will return 0, if test failures exist will return 1, and if there were any errors (preventing a test to be exectuted ) will return 2. Hence the conditional check sets regressionTest.err if either of these latter two conditions are true.

Also in order to start up the server, which could take up to a second, but can’t be sure precisely how long. I have to use the Ant Parallel task, which calls the task to start the server and the task to run the tests at the same time. The task to run the tests has a 2 second sleep in it, which should be more than enough time to allow the server to start. This all kind of feels a little clunky, but at the moment it does work very well.

In a nutshell, thats how you integrate PHP based Automated Selenium Regression tests into a continuous build.

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. span class=”co1″>// This is the important call for this functionality

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. span class=”st0″>’namespace’, ‘element-name’

$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. span class=”co1″>// note this function isn’t yet documented in the PHP manual !
  2.  }
  3.  

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.