Nginx config for hosting multiple projects in sibling folders

I have recently begun the process of migrating (laravel) apps into using Nginx+PHP-FPM.

Needless to say, it is definitely more challenging than configuring good ol Apache! For starters, you absolutely must know regular expressions fairly well. Nginx makes extensive use of regexes to figure out matching rules for urls. The second paradigm shift is learning that Nginx does configs on a “per-application” basis. What I mean is, you cannot just setup the server one time, and expect to drop in apps into the web folder, and hope things work (this is especially true for apps that use clean urls via htaccess rewrites). So, prior to setting up any web application, expect to work a bit on tweaking the Nginix config. The reward is a nimbler web server that performs much better under load.

I put together a config file that serves multiple Laravel applications stored in sibling folders (on a single server). So, http://192.168.33.10.xip.io/project1 will serve up application 1, and http://192.168.33.10.xip.io/project2 will serve up application 2.

/Vagrant is the root web folder, and /project1 and /project2 are sibling folders within the vagrant folder containing full laravel applications.

Hope you find this useful!

Advertisements

Moving From Zend Framework to Laravel 4

Let me preface this blog with the following: I have been a programmer for the past 12 yrs and have used php for nearly 8 yrs, ZF1 for nearly 3 yrs (10+projects) and ZF2 since its inception. So, I understand the framework(s) quite well and have given it a fair amount of trial time.

About 2 months ago, I stumbled upon the Laravel framework. A couple of hours into studying Laravel, I had an epiphany : This is what all other frameworks should strive to be! Easy, Functional, modern and completely out of the way. While its CodeIgniter underpinnings are easily discernible based on the folder structure and config files, it is quite a radical departure in terms of the underlying code. Laravel 4 in particular embraces all prevailing best practices in the PHP world.

In comparison, both ZF1 and ZF2 are complicated, and require a steep learning curve. I kid you not, I was up and running with Laravel 4 in about 3 hours! (properly understanding my first “hello world” with ZF2 took me a week).
In my humble opinion, ZF2 is over-engineered, demanding more attention than the actual web application at hand. The reliance on (deeply nested) arrays for everything from route configs to parameters makes coding a chore. See, while arrays are speedy and flexible, they are a debugging nightmare. You get pretty much Zero IDE support (autocomplete, code completion etc). You have to remember verbatim all the required keys (I kind of got around this using netbeans code templates.But the templates became so many in number that remembering those presented a problem!).

Two projects later, I can confidently say that there is nothing that I can do in ZF2 that I can’t do using Laravel 4 (and in lesser time). The fact that L4 ties into composer/packagist means that pretty much any open source php project(on packagist) can be utilized in a Laravel project. In fact, L4 uses ‘monolog’ for logging, ‘swiftmailer’ for emailing and ‘symfony’ for the HTTP core and command line interface. All while providing a very ‘laravelesque’ approach to coding. There really are no limits. Very cool indeed!

Although I feel a certain closeness with ZF due to the sheer amount of time I spent with it, I feel the time is right to switch to L4 for future projects. I think L4 has done a LOT of things right, and deserves credit for it. The Eloquent ORM is very easy to work with. Routing in Laravel is an absolute joy! It does much of the heavy lifting when it comes to RESTful interfaces.
Form management is beautifully implemented. It is trivial to implement custom form/html controls.The DI mechanism is very expressive.So are Filters and Events. The Blade templating engine has an uncanny resemblance to the Razr engine used by ASP.NET MVC (which I really like!).
Although it looks like L4 uses an abundance of static methods, in reality it harnesses the __callstatic() php magic method to actually load objects from the DI container. The L4 command line tool “artisan” is also very well executed. Unit testing is very simple and works right out of the box (no lengthy setups required prior to running phpunit).

L4 has indeed improved my productivity quite dramatically 🙂

ODBC Linked Tables With Access 2007 & Windows7

I find MS Access to be a convenient tool to interface with most relational database engines (most notably, MySQL and MSSQL). I am much more productive navigating through linked tables in Access and writing queries against them rather than using custom tools such as PHPMyAdmin or SSMS.

With Windows XP, this workflow as trivial. However more recent Windows 7 systems involving a mix of 64 bit h/w and 32 bit s/w coupled with user account restrictions have made RDBMS access via ODBC a little challenging.

I have documented below the method to get MyODBC (ODBC driver for MySQL) working with MS Access 2007 in a 64 bit windows install. Of course, the same process can be used to link to any other odbc dsn after installing appropriate drivers.

The problem

  1. MOST office installations are 32 bit apps. (Although 64 bit office is available, I have yet to see a production install of it- in fact, even MS strongly discourages its use). Windows 7 uses a WOW64 (Windows 32 On Windows 64) subsystem to ensure 32 bit apps work seamlessly on 64 bit machines.
  2. Windows 7 uses much tighter user account restrictions. Because ODBC creation accesses the windows registry, it is a privileged operation.I assume that you are working using a user account AND have an admin uid/password should you need privilege escalation.
  3. A combination of 1 and 2 above make creating an ODBC DSN from MS Access on win7 a slightly convoluted process(compared to xp).

The Solution

  1.  Download and Install the 32 bit driver (MSI) for MyODBC from the following location: http://dev.mysql.com/downloads/connector/odbc/ (NOTE: There is a 64 bit MyODBC driver available for download.. but it is only to be used with 64 bit office apps. So, please download only the 32 bit version!)
  2.  Navigate to C:\Windows\SysWOW64 and locate the file named odbcad32.exe. Create a shortcut to this file on your desktop so you will have a handy reference available (This is the WOW equivalent of the Data Sources(ODBC) applet found under ControlPanel->System and Security->Administrative Tools) You will launch this shortcut in order to create your (SYSTEM) DSN’s and NOT the applet in your control panel. This operation requires privilege escalation so you will be prompted for your admin uid and password in order to create your dsn.
  3.  Once you have created your system DSN’s in step 2 above, you are ready to use them from within MS Access as before.

Unit Testing Email Functionality in Zend Framework Applications

Testing applications that send out emails could be quite a hassle if the tools provided by the framework are not properly leveraged. Zend Framework is quite a test-friendly suite and, as you will soon see, it provides the necessary components to help with the testing process. My requirements for the tests are:

  1. They should be repeatable and consistent
  2. There should be no need to change production code to enable testing

The framework tools that I rely on to help with email testing (using Zend_Mail) are

  1. Zend_Mail_Transport_File
  2. Zend_Mail_Message_File

The logic used is simple. During testing, intercept the default mail transport and replace it with Zend_Mail_Transport_File. This component causes email to be written as a file to the directory location provided. Thereafter, read back the file, parse it into its components using Zend_Mail_Message_File, and finally assert that all parts of the email tally with the expected result.

The tests that suffice for my purposes are:

  • Test that the email was sent
  • Test subject
  • Test body text
  • Test recipients

Sample code to achieve all the above is detailed below:

1. Create a new Zend Framework project.

2. Edit the .htaccess file and add the following line at the top :

SetEnv APPLICATION_ENV “development”

We will use this environment variable as a cue to determine if we are in the production phase or test/dev phase.

3. Create a folder named ‘sentmail’ inside the ‘application’ folder. This is where email files generated by the tests will be stored.

4. Edit the Bootstrap.php file and add the function _initMailTransport(); the function _initMailTransport() is responsible for changing the Mail Transport to Zend_Mail_Transport_File. The ‘callback’ parameter is a function that returns the name you want to assign to the file that is generated. The names generated by default are random and testing the order/count/sequence of multiple emails becomes difficult. I therefore choose to provide my own file naming convention.

Note: If you are certain that only a single email is ever sent on a request cycle, then you can use the default file name and avoid the ‘callback’ parameter.

<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected function _initMailTransport()
    {
        //change the mail transport only if dev or test
        if (APPLICATION_ENV <> 'production') {
            $callback = function()
            {
                return 'ZendMail_' . microtime(true) .'.tmp';
            };
            $fileTransport = new Zend_Mail_Transport_File(
                array('path' => APPLICATION_PATH . '/sentmail',
                    'callback'=>$callback)
            );
            Zend_Mail::setDefaultTransport($fileTransport);
        }
    }

}

The custom callback function uses the ‘microtime()’ function to generate the file name. We retrieve the emails sorted by their name. This convention ensures that we extract our emails in the sequence in which they are dispatched.

Note: I tried using the php functions filemtime() and filectime() to return the create/modify date of the files to achieve the same effect, but was unsuccessful. Both these functions return time with the least granularity of seconds, making it impossible to reliably sequence multiple emails.

5. Here is the Index controller/Index action.. The index action shoots off two emails using Zend_Mail:

<?php

class IndexController extends Zend_Controller_Action
{

    public function init()
    {
        /* Initialize action controller here */
    }

    public function indexAction()
    {
        $firstMail = new Zend_Mail();
        $firstMail->setFrom('me@yahoo.com');
        $firstMail->addTo('recipient1@yahoo.com');
        $firstMail->addCc('recipient2@yahoo.com');
        $firstMail->setSubject('This is first test email');
        $firstMail->setBodyHtml('Email body goes here!');
        $firstMail->send();

        $secondMail = new Zend_Mail();
        $secondMail->setFrom('me@yahoo.com');
        $secondMail->addTo('recipient3@yahoo.com');
        $secondMail->addCc('recipient4@yahoo.com');
        $secondMail->setSubject('This is second test email');
        $secondMail->setBodyHtml('2nd email');
        $secondMail->send();
    }

}

6. And finally, the test class file (IndexControllerTest.php) is shown below:

<?php

class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{

    private $params;
    private $urlParams;
    private $pageUrl;

    public static function setUpBeforeClass()
    {
        self::clearFiles();
        parent::setUpBeforeClass();
    }

    public function setUp()
    {
        $this->bootstrap = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');
        parent::setUp();

        $this->params = array('action' => 'index', 'controller' => 'Index', 'module' => 'default');
        $this->urlParams = $this->urlizeOptions($this->params);
        $this->pageUrl = $this->url($this->urlParams);
        $this->dispatch($this->pageUrl);
    }

    public function testIndexAction()
    {
        // assertions
        $this->assertModule($this->urlParams['module']);
        $this->assertController($this->urlParams['controller']);
        $this->assertAction($this->urlParams['action']);
        $this->assertQueryContentContains("div#welcome h3", "This is your project's main page");
    }

    public function testTwoEmailsAreSent()
    {
        $fileMails = $this->getEmails();
        $this->assertEquals(2, count($fileMails));
    }

    public function testFirstEmailIsAccurate()
    {
        //assertions
        $fileMail = $this->getEmails();
        $this->assertContains('me@yahoo.com', $fileMail[0]->getHeader('from'));
        $this->assertContains('recipient1@yahoo.com', $fileMail[0]->getHeader('to'));
        $this->assertContains('recipient2@yahoo.com', $fileMail[0]->getHeader('cc'));
        $this->assertContains('This is first test email', $fileMail[0]->getHeader('subject'));
        $this->assertContains('Email body goes here', $fileMail[0]->getContent());
    }

    public function testSecondEmailIsAccurate()
    {
        //assertions
        $fileMail = $this->getEmails();
        $this->assertContains('me@yahoo.com', $fileMail[1]->getHeader('from'));
        $this->assertContains('recipient3@yahoo.com', $fileMail[1]->getHeader('to'));
        $this->assertContains('recipient4@yahoo.com', $fileMail[1]->getHeader('cc'));
        $this->assertContains('This is second test email', $fileMail[1]->getHeader('subject'));
        $this->assertContains('2nd email', $fileMail[1]->getContent());
    }

    protected function tearDown()
    {
        self::clearFiles();
        parent::tearDown();
    }
    /**
     *helper function that retrieves emails generated in the folder
     * @return \Zend_Mail_Message_File 
     */
    private function getEmails()
    {
        $directory = APPLICATION_PATH . '/sentmail';
        //remove the pesky .. and .
        $files = array_diff(scandir($directory), array('..', '.'));
        sort($files);  //IMPORTANT - We need them in order!
        $emails = array();
        foreach ($files as $file) {
            $email_str = APPLICATION_PATH . '/sentmail/' . $file;
            $emails[] = new Zend_Mail_Message_File(array('file' => $email_str));
        }
        return $emails;
    }
    /**
     *Internal function to delete all emails created in folder.
     * Need each test case to start clean 
     * It is static because it is also called from "setUpBeforeClass"
     */
    private static function clearFiles()
    {
        //delete all files in folder
        $directory = APPLICATION_PATH . '/sentmail';
        $files1 = array_diff(scandir($directory), array('..', '.'));
        foreach ($files1 as $val) {
            unlink($directory . "/" . $val);
        }
    }

}

The “Setup()” function (called before every test) invokes the index action in the index controller. The getEmails() function opens the /sentmail folder, iterates through every file, parses them using the Zend_Mail_Message_File class, stores the results into an array, and finally returns the entire array to the calling routine. This way, $emails[0] will always be the first email message sent, $emails[1] will be the second email sent and so on..

The clearFiles() method basically blows away the contents of the /sentmail folder. This is invoked on teardown() (after every unit test). I also call this from the setupBeforeClass() method to make absolutely sure that there are no files in the /sentmail folder at startup (for instance, from opening the application index page on the browser).

And finally, the testrunner output generated by these tests is shown below:

image

 

Once you are done with testing, simply edit the .htaccess file and change the environment to “production” like so:

SetEnv APPLICATION_ENV “production”

This will cause the bootstrap to skip the code in the _initMailTransport() function, and normal email service will resume!

Jenkins Continuous Integration, Zend Framework and Netbeans

PHP sure has matured into a serious programming language from its humble beginnings. As more and more complex software is being built using PHP, it makes sense to use sound software engineering principles to insure robustness of built applications.

A good CI tool like Jenkins will help you get an inside look at your code in the form of detailed metrics. In the tutorial that follows, I describe the full install process of Jenkins on a windows workstation running wampserver. I will also detail the process of exercising code written using the Zend Framework. And, since I use Netbeans for all my development, I will describe a couple of nifty Netbeans plugins that make writing standardized code a lot easier.

Part 1: Preparing for Install

Jenkins server has the role of a conductor that orchestrates myriad php tools which in turn do the real heavy-lifting. Our first order of business is to install a working PEAR setup. If you don’t have it installed, please read my earlier blog about how you can set it up relatively easily on WAMP.

In case you already have pear installed, please make sure that all your packages are up-to-date (“pear list-upgrades” will display outdated packages, and “pear upgrade” will perform the upgrade). You should see a screen that looks like:

image

The following are the PHP tools that will be utilized by Jenkins. Please go ahead and install them as described:

Name Purpose How to Install
PHPUnit For Unit testing PHP code pear config-set auto_discover 1
pear install pear.phpunit.de/PHPUnit
phing Build tool written in PHP (similar to ANT) pear install pear.phing.info/phing
PHP_CodeSniffer Ensure coding standards pear install –a PHP_CodeSniffer
PHPDocumentor Automatically generating documentation/API from docblock comments in your code pear install PHPDocumentor
PHPcpd Copy/Paste Detector (CPD) for PHP code. pear install phpunit/phpCPD
PHPmd PHP mess detector. Analyze code to identify poorly written sections pear install -a pear.phpmd.org/PHP_PMD
PHPDepend Generates various software metrics pear install pear.pdepend.org/PHP_Depend
PHPCodeBrowser Generates browsable code annotated with defects identified by codesniffer, cpd and md pear install -a phpunit/PHP_CodeBrowser
PHPLoc Generates statistics for Lines of Code and other software metrics pear install -a phpunit/phploc

Once you have installed all the above packages, they are instantly made available on the windows path as the installers typically create a .bat file on the same directory as the php executable (which is already set in the windows path by the pear install process). You should therefore be able to open up a command window and type the name of any of the tools above.

Part 2: Install Jenkins

Next, head over to the Jenkins website and grab the native windows installer. Run the setup process. By default, Jenkins is setup at http://localhost:8080. As you can see, installation of Jenkins is really effortless. This is one of the key features that make it hugely popular.

(This part is optional only if you want to change the default port)

On my workstation, I run IIS on port 80 and Wampserver on 8080. I prefer to run Jenkins on port 8888. Making the change is easy. Head over to the Jenkins directory (c:\Program Files\Jenkins by default) and edit the file named “Jenkins.xml”. Change the port to 8888 in the <arguments> section as shown below:

<arguments>-Xrs -Xmx256m -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar
"%BASE%\jenkins.war" --httpPort=8888</arguments>

The install process sets up a windows service named  “Jenkins” that is set to autostart. Like any other windows process, you can start, stop or restart it (Right click on “My Computer”-> select “Manage”->”Services and Applications”->”Services” and locate “Jenkins” on the list)

image

If all went well, when you restart the service and point your browser to http://localhost:8888 (or http://localhost:8080 if you did not change the default port) you should now see the Jenkins home page.

You can now proceed to install the required plugins. This process is also trivially easy because the Jenkins web interface does an excellent job of managing it. Click on the “Manage Jenkins” link, and then click on the “Manage Plugins” option. Click on the “Available’ Tab. Check all the plugins that need to be installed (list below) and hit the “Install without restart” option. Jenkins will retrieve the plugin from the web, set it up and prompt you to restart!

List of recommended plugins:

Jenkins Plugin Purpose
Checkstyle Generates a summary (and detail) of code style errors returned by the PHP_CodeSniffer tool
Clover_PHP Picks up Coverage_HTML and Coverage_Clover output from phpunit tool
DRY Summarizes output about repeated code generated by the PHPCPD tool
HTML Publisher (Post build) Used to create a snapshot of the documentation (generated by the PHPDocumentor tool) and the code browser (generated by PHPCodeBrowser). The plugin creates and maintains a copy of these two reports for EVERY BUILD. So you can always look at a prior build and analyze the progress.
JDepend Report on output generated by the PDepend tool
Plot Generate graphs from CSV output generated by the PHPLoc tool
PMD Report on output generated by the phpmd tool (PHP Mess Detector)
Violations Generates reports and graphs from output generated by PHP_CodeSniffer, PHPCpd and PHPMD
Mercurial Helps Jenkins acquire source code from Mercurial Source Control System (Ex. Bitbucket)
Phing Integrates Phing build tool into Jenkins

Sebastian Bergmann (creator of PHPUnit and various other tools above) actually has a website dedicated to explaining the nuances of integrating Jenkins with PHP (http://jenkins-php.org/). You might want to peruse through his site if you haven’t already. In part 3 of this tutorial, I borrow heavily from his website.

Part 3: Setup Jenkins for your PHP/Zend Framework Project

There are three distinct phases in any Jenkins job (and these can be batched to run at specified intervals):

  1. Source code retrieval– You will typically want Jenkins to automatically retrieve source code from a repository (I use Mercurial on Bitbucket.. but there are plugins available for almost any source control solution you may be comfortable with).
  2. Build– This is the part where the source code retrieved in step 1 is rigorously analyzed. We will use a phing build file to specify all the actions we want performed on the source code. This step typically generates a bunch of reports in predefined folders.
  3. Post Build – This step takes reports and logs generated by step 2 above and presents pretty reports to the end-user.

Because of the the sheer volume of available tools, processes and options, setting up a Jenkins project that presents meaningful and complete data to the user can be quite challenging. To simplify the adoption of Jenkins for PHP users, Sebastian Bergmann has created a template that users can base their projects off of. This template prefills all the common build and post-build options so the user does not have to bother about them. Keep in mind that setting up a Jenkins job for a project is a one time deal.

  1. Download the template from https://github.com/sebastianbergmann/php-jenkins-template/downloads. The template essentially contains an elaborate XML file that details the required build options.
  2. Extract the contents of the zip file into a folder named ‘php-template-jenkins’ in the c:\Program Files\Jenkins\Jobs folder
    image
  3. Fire up the Jenkins home page (http://localhost”:8888) and click on the “Manage Jenkins” menu option. Click on the “Reload Configuration from Disk” link. This will force Jenkins to recognize the newly copied php template
  4. On the Jenkins home page, click on the “New Job” menu option
  5. Type in your application name in the “Job Name” field. Select the “Copy existing job” option at the end and type ‘php-jenkins-template’ (It has a neat auto-complete box that prompts you with available matches!)
    image
  6. Now, on the homepage, you should see your newly setup job. This job has inherited all settings form the php-template.
    image

We will be using “phing” to bring all our tools together and execute them sequentially. By default, phing looks for a file named “Build.xml” in the project root and executes it. The build file that I use is listed below:

<?xml version="1.0" encoding="UTF-8"?>

<project name="name-of-project" default="build">
    <property name="basedir" value="." />
    <target name="build"
            depends="prepare,lint,phploc,pdepend,phpmd,phpcs,phpcpd,phpdoc,phpunit,phpcb"/>

    <target name="clean" description="Cleanup build artifacts">
        <delete dir="${basedir}/build/api"/>
        <delete dir="${basedir}/build/code-browser"/>
        <delete dir="${basedir}/build/coverage"/>
        <delete dir="${basedir}/build/logs"/>
        <delete dir="${basedir}/build/pdepend"/>
    </target>

    <target name="prepare" depends="clean"
            description="Prepare for build">
        <echo msg="${basedir}" />
        <mkdir dir="${basedir}/build/api"/>
        <mkdir dir="${basedir}/build/code-browser"/>
        <mkdir dir="${basedir}/build/coverage"/>
        <mkdir dir="${basedir}/build/logs"/>
        <mkdir dir="${basedir}/build/pdepend"/>
    </target>

    <target name="lint">
        <phplint>
            <fileset dir="${basedir}">
                <include name="**/*.php" />
                <exclude name="**/tests/**" />
            </fileset>
        </phplint>
    </target>

    <target name="phploc" description="Measure project size using PHPLOC">
        <exec executable="phploc">
            <arg value="--log-csv" />
            <arg value="${basedir}/build/logs/phploc.csv" />
            <arg value="--exclude"/>
            <arg path="${basedir}/tests" />
            <arg value="--suffixes"/>
            <arg value="php" />
            <arg path="${basedir}" />
        </exec>
    </target>

    <target name="pdepend">
        <phpdepend>
            <fileset dir="${basedir}">
                <include name="**/*.php" />
                <exclude name="**/tests/**" />
            </fileset>
            <logger type="jdepend-xml" outfile="${basedir}/build/logs/jdepend.xml"/>
            <logger type="jdepend-chart" outfile="${basedir}/build/pdepend/dependencies.svg"/>
            <logger type="overview-pyramid" outfile="${basedir}/build/pdepend/overview-pyramid.svg"/>
        </phpdepend>
    </target>

    <target name="phpmd"
            description="Generate pmd.xml using PHPMD">
        <phpmd rulesets="codesize,unusedcode,naming,design">
            <fileset dir="${basedir}">
                <include name="**/*.php" />
                <exclude name="**/tests/**" />
            </fileset>
            <formatter type="xml" outfile="${basedir}/build/logs/pmd.xml"/>
        </phpmd>
    </target>

    <target name="phpcs">
        <phpcodesniffer standard="Zend" allowedFileExtensions="php">
            <fileset dir="${basedir}">
                <include name="**/*.php" />
                <exclude name="**/tests/**" />
            </fileset>
            <formatter type="default" usefile="false"  />
            <formatter type="checkstyle" outfile="${basedir}/build/logs/checkstyle.xml" />
        </phpcodesniffer>
    </target>

    <target name="phpcpd">
        <phpcpd>
            <fileset dir="${basedir}">
                <include name="**/*.php" />
            </fileset>
            <formatter type="pmd" outfile="${basedir}/build/logs/pmd-cpd.xml"/>
        </phpcpd>
    </target>

    <target name="phpdoc" description="AutoGenerate php docs">
        <phpdoc title="My API Docs"
                sourcecode="yes"
                destdir="${basedir}/build/api"
                output="HTML:Smarty:PHP">
            <fileset dir="${basedir}">
                <include name="**/*.php" />
                <exclude name="**/tests/**" />
            </fileset>
        </phpdoc>
    </target>

     <target name="phpunit" description="Run unit tests with PHPUnit">
         <exec command="phpunit --configuration=${basedir}/tests/phpunit.xml
        --log-junit ${basedir}/build/logs/junit.xml
        --coverage-clover ${basedir}/build/logs/clover.xml
        --coverage-html ${basedir}/build/coverage"/>
    </target>

    <target name="phpcb"
            description="Aggregate tool output with PHP_CodeBrowser">
        <exec executable="phpcb">
            <arg value="--log" />
            <arg path="${basedir}/build/logs" />
            <arg value="--source" />
            <arg path="${basedir}/application" />
            <arg value="--source" />
            <arg path="${basedir}/library" />
            <arg value="--output" />
            <arg path="${basedir}/build/code-browser" />
        </exec>
    </target>
</project>

This file must be placed in the root of your project. So, if you are coding using Zend Framework, it should be at the same level as “application”, “library” and “public” folders.

The code is fairly self-documenting. A property named “basedir” is declared. This is used throughout the script as ${basedir}. The project has a default build target of “build”. “build” in turn depends on multiple targets that are setup, and all are executed in sequence. “prepare” is the first target executed and that is in charge of creating the required folders.

The “tests” folder is excluded from most metric computations as that is where our unit test cases reside. Note the “–configuration” option in the “phpunit” target – this indicates the location of the phpunit.xml file which in turn contains the location of the bootstrap file, and the folders containing code to test.. It is pretty much the stock phpunit.xml file that gets generated with a new Zend Framework project with the <whitelist/> section added. The “<whitelist/>” section is required – without this, you will have inflated code-coverage reports that document the entire zend framework!

<phpunit bootstrap="./bootstrap.php">
    <testsuite name="Application Test Suite">
        <directory>./application</directory>
    </testsuite>
    <testsuite name="Library Test Suite">
        <directory>./library</directory>
    </testsuite>
  <filter>
        <whitelist>
            <directory suffix=".php">../application</directory>
            <directory suffix=".php">../library</directory>
            <exclude>
                <directory suffix=".phtml">../application</directory>
            </exclude>
        </whitelist>
    </filter>
</phpunit>

I prefer to have actual code for “code-cover generation” inside the phing job and NOT in the phpunit.xml file. The main reasons for this are:

  1. Generating a code coverage report takes time. And this grows along with the length of the code. While a delay in a jenkins job is perfectly acceptable, any delay to the TDD process is not. phpunit.xml is a file that you will run very often from your IDE (if you practice TDD) and the quicker it runs, the better.
  2. Netbeans has an awesome internal code coverage generator that generates wonderful code-coverage reports within the IDE (if you haven’t checked this feature out, I would strongly recommend that you do)
  3. Artifacts generated during code coverage analysis (various html files) do not really belong in the project. The “build/coverage” folder generated by the phing task lies outside the project and is therefore preferred.

Next, we modify our template so that it works with our source code repository and also use phing instead of ANT.

Phing intead of ANT: I prefer not to download and install another binary executable (ant) when we have a perfectly capable native php tool (phing) available for the job.

In Jenkins, click on the newly created job and then on the “Configure” option on your left.

a. Change the tags in the description to use <img/> instead of (I find this works better with the most recent release of Jenkins – 1.454)

b. Provide the source of your code in the “Source Code Management” section (Note how the password is passed in the url)

image

c. Under the “build” section, delete the existing ‘Ant’ target by clicking on the “Delete” button. Click on the “Add build step” button and select “Invoke Phing targets” (this option is made available by the phing plugin for Jenkins)

image

It will add a blank line. Nothing needs to be entered there.. By default, Jenkins will look in the windows path for phing and execute it. Phing looks in the workspace directory for a build.xml file, finds it, and dutifully executes all the targets! Hit the save button. This completes Jenkins setup for our project. Choose the “Build Now” option from the menu and take a good look at all the wonderful reports generated by Jenkins.

image

Part 4: Netbeans and PHP – Still The Perfect Match

Your development process has no doubt become much more organized by the use of Jankins CI server. However, you could be bogged down by error reports if you do not have a solid development methodology to boot. And, this starts with the IDE.

A couple of years ago, I wrote about how Netbeans surpassed all my expectations from an IDE, in spite of it being free! To this day, it remains a  clear winner for php development.

With the addition of a few plugins, you will be able to develop better code and thus have fewer surprises when you submit your code for integration. These have served me very well and I strongly recommend you utilize each one of them.

1. Unix line endings : Windows line endings (CR-LF) causes problems on unix machines (which uses LF). To avoid nasty surprises later on, it is best to install this plugin and set line endings to LF.

2. Path Tools : This plugin makes it incredibly easy to drop into console or explorer. Highlight the file or folder and click on the appropriate toolbar option!

3. phpMD / PHP CodeSniffer Plugin: This plugin uses the phpmd (mess detector) and phpcs (code sniffer) tools and tracks code violations early on and displays them right within the IDE window! Using this plugin is an easy way to standardize your codebase. Please refer to the following website for detailed instructions on how to set up and activate this plugin:  http://www.summasolutions.net/blogposts/applying-zend-coding-standard-netbeans-phpmd-codesniffer

Custom Zend Form Slider Element

In one of my web projects, I had a need for a slider UI element. I use Zend framework extensively in all my projects, and my initial approach was to use pure JQuery coupled with $_POST variables to interact with a JQueryUI slider.

The validation criterion was that a slider HAD to be selected: If the user wanted to indicate a zero value, the slider had to be dragged and brought back to zero.

Very soon, I realized that while the JQuery/php code worked well, it required increasing amounts of code in the controller and view to handle form validation. Also, maintaining the state across form submissions became a laborious process. Error highlighting/reporting became difficult, and all these mundane activities took up lots of space and time. Of course, the problems only grew worse with increasing number of sliders on the page.

I recently refactored my code and arrived at a much better solution: Create a custom zend form element named “slider”, and let the Zend framework handle the grunt work of state persistence and error management – things it was designed to do in the first place.

Requirements: The main requirements of the slider element are summarized below:

1. The ability to create and work with sliders in a way similar to other standard form controls (text/radio etc). For example:

$slider1 = $this->createElement('slider', 'clinical_experience');
$slider1->setLabel('How do you rate your clinical experience?');
$slider1->setRequired();
$this->addElement($slider1);

2. The setRequired() validation should be performed by the framework, and slider state should be retained between form postings.

3. It should be possible to customize left and right anchors of the sliders to reflect varying end points(default least-most).

4. It should be possible to customize the min and max values of each slider (default range from 0-100).

Now that our requirements are clearly defined, we move on to coding the form element. In order to work with the slider, we actually create a combination of a slider div AND a hidden form element. The hidden element will help us get the posted value easily and also help maintain state.

We establish a simple naming convention for associated hidden fields – give their name attribute a value of “controlname-text”

For instance the slider above named “clinical_experience” will have a hidden field named “clinical_experience-text” that holds the value of the slider selection (the numerical value of the slider is set via javascript)

The slider class extends Zend_From_Element_Xhtml. This gives our custom slider element access to all of Zend frameworks’ support for form elements. We also create setter methods for the left and right anchor and the min/max slider values. The init() method sets appropriate defaults (should the user choose not to use the setters while creating the form).

Note that the form helper element (bolded in the code below) is set to ‘SliderElement’ – This will be defined by us next. The framework takes care of passing the control name, value, attribs array and options array to the helper.

<?php
class ZF_Slider extends Zend_Form_Element_Xhtml
{
    public $helper = 'sliderElement';
    public $options = array();
    public function init()
    {
        $this->setMinValue(0);
        $this->setMaxValue(100);
        $this->setLeftAnchor('Least');
        $this->setRightAnchor('Most');
    }
    public function setLeftAnchor($left)
    {
        $this->options['left'] = $left;
    }
    public function setRightAnchor($right)
    {
        $this->options['right'] = $right;
    }
    public function setMinValue($min=0)
    {
        $this->options['min']=$min;
    }
    public function setMaxValue($max=100)
    {
        $this->options['max']=$max;
    }
    public function isValid($value, $context = null)
    {
        //We have named our hidden text field name-text.. get its value
        $field = $this->_name."-text";       
        
        $value = $context[$field];
        $this->setValue($value);
        if (($value === null) or ($value==-1))
        {
            $value=null;
        }
        return parent::isValid($value, $context);
    }

}
?>

The control name is available via $this->_name. Therefore the corresponding hidden element can be retrieved using $this->_name.”-text”

The isValid() method is overridden as shown in the code above. It gets passed a value and a context (which comprises all the posted form values). We extract the hidden field value (based on our naming convention) and assign it to the value of our control ($this->setValue()).

If the value is null or -1, we set the value to null. Finally we make a call to the parent’s isValid() method. This takes care of responding appropriately to missing/null values.

The view helper for our newly created form element is displayed below (SliderElement.php). The “Sliderelement” helper takes care of “visually” displaying our custom control. First, a set of div tags is established. The first div tag establishes the left anchor, followed by a placeholder for the slider (rendered by jquery), followed by the div for the right anchor (I use Blueprint css for positioning)

<?php
class Zend_View_Helper_SliderElement extends Zend_View_Helper_FormElement
{
    //all these input parameters are passed by the Zend_Form_Element
    public function SliderElement($name, $value=null, $attribs=null, $options=null)
    {
        $str ='<div class="box">
            <div class="span-3">' . $options['left'] . '</div>
            <div class="span-9" id="' . $name . '"></div>
            <div class="span-3">' . $options['right'] . '</div>
                <input type="hidden" value="' . $value . '" id="' .
                $name . '-text" name="' . $name . '-text"/>
            </div>';
        //$value is injected into the javascript.. passing null will result in js error!
        $value=($value == null)?-1:$value;
        $str.=$this->SliderGen($name, $value,$options['min'],$options['max']);
        return $str;
    }

    //return a javscript string that renders the slider using 
    //jquery ui..
    private function SliderGen($id, $value,$min,$max)
    {
        $str = '<script>
    $(document).ready(function(){';
        $str.="
            \$(\"#$id\").slider({
    animate: true ,
    range: \"min\",
    value: $value,
    min: $min,
    max: $max,
    slide: function(event, ui) {\$(\"#$id-text\").val(ui.value)}
    });";
        $str.= "\$(\"#$id\").css('cursor', 'pointer');";
        $str.='});
        </script>';
        return $str;
    }
}
?>

The internal (private) method SliderGen() emits javascript code to render the slider using JQuery. The “slide” function/callback populates the hidden text box with the slider selection.

Please look up the JQueryUI site if you need more information regarding the syntax for displaying the slider component on a web page.

Finally, we are ready to create our Zend form. The code is as shown below (forms/slider.php):

<?php
class Form_Slider extends Zend_Form
{
    public function init()
    {
        //tell this form where to find our slider element
        $this->addPrefixPath('ZF', 'ZF','element');
        
        $slider1 = $this->createElement('slider', 'clinical_experience');
        $slider1->setLabel('How do you rate your clinical experience?');
        $slider1->setRequired();
        $slider1->setLeftAnchor('Poor');
        $slider1->setRightAnchor('Excellent');
        $slider1->setMinValue(100);
        $slider1->setMaxValue(500);
        $this->addElement($slider1);
        
        $slider2 = $this->createElement('slider', 'teaching_experience');
        $slider2->setRequired();
        $slider2->setLabel('How do you rate your teaching experience?');
        $slider2->setLeftAnchor('Poor');
        $slider2->setRightAnchor('Excellent');
        $this->addElement($slider2);
            
        $submit = $this->createElement('submit', 'next');
        $submit->setIgnore(true);
        $submit->setLabel('Next');
        $this->addElement($submit);
    }
}
?>

The “addPrefixPath()” command is used to instruct set the actual location of our ‘slider’ component (plugin). This class is named “ZF_Slider” and is in the path /library/ZF/slider.php

We create two sliders using the “slider” component that we just created.. Set all the required values and add it to Form_Slider.

Finally, to tie it all together and generate some output, we create an indexController. It simply initiates our form class, passes it on to the view and dumps the response when the form is submitted:

<?php
class IndexController extends Zend_Controller_Action
{
    public function indexAction()
    {
       $form = new Form_Slider();
       $this->view->form = $form;
       if ($this->_request->isPost())
       {
           if ($form->isValid($_POST))
           {
               var_dump($form->getValues());
           }
       }
    }
}
?>

Since all the heavy weight lifting has been delegated to the Zend form, the view (index.phtml) is a simple one-liner:

<?php
echo $this->form;
?>

That’s it! No additional javascript coding and messing with posted values (Note that a reference to both JQuery and JQueryUI is necessary to make this function.. I setup all this in my bootstrap and render it on my layout.phtml)

This is what the rendered form looks like (note that I use blueprint css for positioning):

 

clip_image002

When the form is submitted without interacting with either of the sliders, the standard error messages (setRequired())are displayed underneath the appropriate controls.

clip_image004

When selections are made and the “Next” button is hit, the posted values are displayed as expected:

clip_image006

Note that when we initialized the “clinical_experience” slider, we had min and max set at 100 and 500 respectively. The posted value reflects that. Notice also how the state of the slider is maintained after post back.

ASUS Transformer TF101

Last week, in the midst of the HP Touchpad frenzy (which I could not get in on, by the way), I decided to pull the trigger on an ASUS Transformer (TF101). After having played with it sufficiently, I am really happy and excited about my purchase!

If Apple has taught us anything, it is that the hardware and software go hand in hand towards a great user experience, and Asus definitely followed this. Out of the box, people want to surf the web, listen to music, watch movies, play games, buy apps, and read books – The transformer really excels in ALL these areas as an extremely user-friendly tablet.

If you are on the fence about buying a TF101, hopefully, this blog should help you make up your mind!

Android 3.0/Honeycomb is a glorious OS, and the Transformer has the right hardware to make everything extremely fluid and fast. Asus has also paid close attention to customizing the software for the Transformer. Oh.. and the screen on the Transformer is gorgeous! 10.1 inch, 1280*800 pixels of super-responsive real estate.

Most software that you require to be productive has already been included on the Transformer(or available as a free download). The Android market is literally littered with apps. Writing “good” software that plays well with the available hardware is hard.. So, I am extremely cautious about downloading stuff off the market. I always prefer apps that have been tried and tested.

Connecting the Transformer to a computer to transfer data is also a cinch.. Just use the USB cord shipped along with the unit. The transformer shows up as a drive (Windows 7 automatically recognizes and installs the required drivers).

Here’s what my home screen looks like – with my most commonly used apps lined up at the bottom:

P20110827210023

Here is a list of apps that I use regularly (and I strongly recommend each one of them):

1. Browser : A version of Chrome. Very snappy and full featured (You will be prompted to download Flash on first use). There is really no reason to download another browser.

2. YouTube : Great app customized for tablets. Makes flipping through videos a joy!

3. File Manager : Folder manager app created by ASUS. I actually prefer this to the highly acclaimed “Astro” file manager.

4. Polaris Office: Included by Asus. Lets users easily read and edit various office document formats (.docx, .xlsx, .pdf etc). I prefer this to “Documents to go”. (Although this reads pdf files, the Adobe reader does a much better job of rendering pages)

5. Amazon Kindle: Enough said!

6. 2X Client : This is a free remote desktop client. 2X actually designs and develops load balancing servers (which we use for our terminal services at work).. So, I have absolutely no qualms in recommending their RDP client. Works flawlessly with minimal resources.

7. Repligo PDF Reader: The most feature rich PDF reader available for android (The feature that I absolutely love about Repligo is that it remembers the last page read. The Day/Night mode is also very helpful). The official PDF reader by Adobe is not yet there in terms of required features.. but is a close 2nd choice.

8. MX Video Player : An awesome media player for Android.. It uses custom codecs to take advantage of processor specific features(H/W acceleration). It allows me to play a wide variety of video files on my android (The full-featured free version displays ads only when you pause.. not at all obtrusive).

9. Due Today : A full featured task management/reminder system with a very good UI. It also syncs with ToodleDo when online.. so you have your tasks securely backed up. There is another highly acclaimed app named “Beautiful Notes” that competes in the same category. I personally chose “Due Today” because 1. it is more structured and captures data in numerous fields 2. It is much more light weight (500KB) vs Beautiful Notes (8.5 MB)

10. Windows 8 Notepad: A very functional notepad replacement. I love its clean and minimalistic design. Available for free from the Android market.

Configuring the Keyboard (Setting->Language & input)

The ASUS keyboard is vastly superior to the stock android keyboard.. But it can be further enhanced using the configuration options available:

image

image

I checked ALL available options on mine.. Note that the Swype style functionality is built into the ASUS keyboard app. This is really handy and works great (not to mention the ‘cool’ factor!).

image

Setting up VPN (Settings->Wireless & networks)

Honeycomb also has built in support for VPN –  Click on the “VPN Settings” option:

image

Next, select the type of VPN (L2TP is the easiest to set up)

image

Enter your values for VPN Name, Server, and pre-shared key.. Yes.. its that easy!

image

And for those of you curious about how I did these awesome screen captures – No.. it is not an app! The feature is built right into the Honeycomb OS (Settings->Screen->Screenshot). This takes a screenshot if you keep the “Recent apps” key pressed for about 3 seconds (this is the icon next to the “Home” icon at the bottom of the screen. The pictures are saved in the “Screenshot” folder of your main SD card  (you can get there using the File Manager app)

P20110827203742