In my last blog post, I introduced ContractLib, a simple programming by contract library that I’ve created for PHP 5.3 onwards. And I promised some examples :)

Installing ContractLib

ContractLib is available from the Phix project’s PEAR channel. Installing it is as easy as:

$ pear channel-discover pear.phix-project.org
$ pear install -a phix/ContractLib

At the time of writing, this will install ContractLib-2.1.0. We use semantic versioning, so these examples will continue to work with all future releases of ContractLib-2.x.

Adding ContractLib To Your Project

Assuming you’re using a PSR-0 compatible autoloader, just import the Contract class into your PHP file:

use Phix_Project\ContractLib\Contract;

Adding A Pre-condition Contract To Your Method Or Function

Take a trivial method like this:

class ActionToApply
{
    public function appendNow($params)
    {
        $params[] = time();
    }
}

This method works fine … until someone passes a non-array as the parameter. At that point, your code stops working – not because your code is wrong, but because someone used it in the wrong way. This is a classic cause of buggy PHP apps. Thankfully, it’s very easy to address using ContractLib.

If we were certain that the $params parameter was always an array, then we can keep the method itself extremely simple and clean. We can ensure that by adding a pre-condition using ContractLib.

use Phix_Project\ContractLib\Contract;

class ActionToApply
{
    public function appendNow($params)
    {
        Contract::Preconditions(function() use ($params)
        {
            Contract::RequiresValue(
                $params, 
                is_array($params), 
                '$params must be an array'
            );
        });

        // original method code continues here
        $params[] = time();
    }
}

Now, if someone passes in a non-array, the caller will automatically get an E5xx_ContractFailedException, which makes it clear that the fault is in the caller’s code … not your’s.

PHP 5.4′s upcoming support for better type-hinting is another way to catch this kind of error, but not only does ContractLib work today with PHP 5.3 (which means you don’t have to wait to migrate to PHP 5.4), but also that you can write tests for anything, not just the checking that’s built into PHP.

This means you can make your code much more robust, by tightening up on the quality of the parameter passed into your code by other programmers. To extend our example, we might decide that an empty array is also unacceptable:

use Phix_Project\ContractLib\Contract;

class ActionToApply
{
    public function appendNow($params)
    {
        Contract::Preconditions(function() use ($params)
        {
            Contract::RequiresValue(
                $params, 
                is_array($params), 
                '$params must be an array'
            );
            Contract::RequiresValue(
                $params,
                count($params) > 0,
                '$params cannot be an empty array'
            );
        });

        // original method code continues here
        $params[] = time();
    }
}

The point here is that we can go way beyond type-hinting checks (important as they are) and look inside parameters to make sure they are suitable.

Here’s a real example from Phix’s CommandLineLib:

use Phix_Project\ContractLib\Contract;

class CommandLineParser
{
    // ...

    public function parseSwitches($args, $argIndex, DefinedSwitches $expectedOptions)
    {
        // catch programming errors
        Contract::Preconditions(function() use ($args, $argIndex, $expectedOptions)
        {
            Contract::RequiresValue(
                $args, 
                is_array($args), 
                '$args must be array'
            );
            Contract::RequiresValue(
                $args, 
                count($args) > 0, 
                '$args cannot be an empty array'
            );

            Contract::RequiresValue(
                $argIndex, 
                is_integer($argIndex), 
                '$argIndex must be an integer'
            );
            Contract::RequiresValue(
                $argIndex, 
                count($args) >= $argIndex, 
                '$argIndex cannot be more than +1 beyond the end of $args'
            );

            Contract::RequiresValue(
                $expectedOptions, 
                count($expectedOptions->getSwitches()) > 0,
                '$expectedOptions must have some switches defined'
            );
        });

        // method's code follows on here ...
    }
}

In this real-life code, we start off by checking for basic errors first (by making sure we’re looking at the right type for each parameter), and then we follow up with more specific tests, that ensure that we have data that we’re happy to work with. We’ve done these tests at the start of the method, so that it isn’t cluttered with error checking, which makes our code much cleaner that it might otherwise be. And, because all the tests are in one really easy to spot block, anyone reading your code can immediately see what they have to do to meet the contract you’ve created.

Because these tests are just plain-old PHP code, and don’t rely on annotations or any other such nonsense, the contracts you create and enforce are limited only by your choices.

But Aren’t All Those Tests Slow?

They are. PHP’s getting better and better at this, but function/method calls have always been painfully slow in PHP. I’m afraid that if you want robust code, you can’t have it for free. (You can in C, but that’s a topic to discuss over a decent whiskey at a conference).

I’ve done key two things with ContractLib to keep the runtime cost down:

  1. Contract::Preconditions() accepts a lambda function as its parameter. Your contract’s tests go inside this lambda function, and Contract::Preconditions() only calls the lambda function if contracts are enabled.
  2. By default, ContractLib does not enable contracts. You have to choose to do so by calling Contract::EnforceWrappedContracts().

This keeps the overhead down to just one method call (to Contract::Preconditions()) when contracts are not enabled. It isn’t as good as having no overhead, but it’s cheaper than the developer time lost trying to track down bugs in code that always assumes the caller can be trusted to do the right thing every time.

Any Questions?

I hope these examples have given you an idea on how to get started with ContractLib. If you have any questions or suggestions, please let me know, and I’ll do my best to answer them.

4 comments »

The Phix Project Has A New Home

Posted by Stuart Herbert on September 23rd, 2011 in Examples, phix, Toolbox.

Phix now has a new home, as I’ve recently left Gradwell. And a new PEAR channel. And an awesome new logo thanks to Jeremy and Kerry from Magma Digital. And a roadmap on Trello.

And, if you’re using either Ubuntu or Fedora as your dev desktop/laptop, a new one-line installer that takes care of those pesky system-level dependencies that the PEAR Installer can’t help you with :)

How To Move To The New PEAR Channel

If you have already installed an older version of Phix from pear.gradwell.com, you’ll need to run the following commands in order to switch over to our new PEAR channel pear.phix-project.org.

$ sudo pear uninstall Gradwell/ComponentManager 
Gradwell/ComponentManagerPhpLibrary Gradwell/ComponentManagerPhpWebapp
Gradwell/ComponentManagerPhpDocbook Gradwell/ComponentManagerShared
Gradwell/ConsoleDisplayLib Gradwell/CommandLineLib
Gradwell/ValidationLib Gradwell/phix 
Gradwell/Autoloader Gradwell/ExtenderLib

Then, visit the installation instructions on phix-project.org. Try out our new one-line installers for Ubuntu or Fedora. (Got a one-line installer for another platform? Fork the website on GitHub and send me a pull request :)

If all goes well, you should end up with phix-0.13 installed on your computer:

$ pear list -c phix | grep phix
phix                       0.13.2  stable
phix4componentdev          0.13.2  stable

If You Have Components That Depend On Phix’s Components …

… you’ll need to edit their package.xml files:

  • Swap any references to pear.gradwell.com to pear.phix-project.org.
  • Check any <min> and <max> version numbers of any Phix components you depend on, as I’ve bumped all the version numbers.

All of Phix’s components now live in the Phix_Project namespace. You’ll need to update any classes that ‘use’ any of the Phix classes accordingly.

Comments Off

In my Beyond Frameworks talk, I explained how a component-based architecture can help answer some of the important (i.e. expensive!) questions you might face when creating long-lived apps that rely on a PHP framework. In this series of blog posts, I’m going to look at how to go about creating and working with components.

This is a guest post by Martin Wernståhl. Martin is a university student at Chalmers University of Technology, Sweden, currently studying the first year of Engineering Physics. He started programming at an age of 9 by writing Not-Quite-C for LEGO robots. Since then he has been programming a little bit of everything until he started with PHP around five years ago. Using CodeIgniter at first, he soon started to write libraries and hack the core of CodeIgniter until he felt that it was too limiting and resorted to starting to experiment with creating his own ORM tools (IgnitedRecord, discontinued as he no longer uses CodeIgniter and RapidDataMapper, development paused because of indecision related to syntax) and lately he has gone back to experimenting with creating a very modular PHP framework. Martin has also been a very enthusiastic supporter and tester of Phix and ComponentManager since their first releases.

If you would like to contribute a guest post about PHP components, please poke @stuherbert on Twitter.

How to Host a PEAR Channel on GitHub

GitHub has a feature called GitHub pages which they introduced about 2.5 years ago. This enables you to host static files located in a specific branch in a repository (gh-pages) on GitHub’s webserver. This can also be used to host a PEAR channel and can be very useful for smaller projects or if you don’t have the time or money to configure a specific server for PEAR. An added bonus is that the content of your PEAR channel server will be versioned by git.

For an example of a GitHub hosted PEAR channel, see the Inject Framework’s PEAR channel page.

The Repository

First, create a new repository on GitHub for your PEAR channel. You may now wonder why I wrote a new repository, this is because of the fact that it is usually better to separate your projects and the PEAR channel repository because of the difference in content, plus, the repository name will be a part of the URL. You might also not want to limit which packages you can put in the channel because of the naming of the parent repository.

A recommended name for the new repository is pear, as that will be equivalent with ${yourusername}.github.com/pear. A must is that the name is lowercase, as GitHub’s URLs are case sensitive.

For an example repository hosting a PEAR channel, see the Inject Framework’s PEAR repo on GitHub.

Create Your Repository Locally

To create the repository we are going to use Pirum. Pirum is a PEAR channel server "generator" which generates static files which can be used by any PEAR installer to install your packages.

So, create a folder for your pear repository and then place a pirum.xml file there:

 
<?xml version="1.0" encoding="UTF-8" ?>
<server>
    <name>${yourusername}.github.com/${pear_repository}</name>
    <summary>${page_title}</summary>
    <alias>${short_prefix}</alias>
    <url>http://${yourusername}.github.com/${pear_repository}</url>
</server>

yourusername and page_title are fairly self-explanatory. pear_repository is the name of the GitHub repository you will store your PEAR channel in. But what does the <alias> tag do?

The contents of the <alias> tag is the channel alias which can be used instead of the contents of <name> when installing PEAR packages after you have run pear channel-discover. An example is the InjectFramework: instead of injectframework.github.com/pear/InjectStack you write injectfw/InjectStack [1].

Then we create our new Git repository, run pirum build . to create the PEAR channel, then link our repository with GitHub and finally create and push the gh-pages branch:

 
cd my/pear/repository
git init
pirum build .
git remote add origin git@github.com:${yourusername}/${pear_repository}.git
git add *
git commit
git branch gh-pages
git checkout gh-pages
git push -u origin master
git push origin gh-pages

Now you should receive a notification from GitHub (can be sent via email too depending on your notification settings) telling you that your page build was successful [2]. It might take as long as up to 10 minutes before your pages will appear on http://${yourusername}.github.com/${pear_repository} the first time you push your changes. Following pushes should update the pages almost instantly.

Now on to see if it works as it should; if pear channel-discover http://${yourusername}.github.com/${pear_repository} does not return any errors, everything went fine. You might want to check http://${yourusername}.github.com/${pear_repository} using a browser to see if it has been uploaded first, you will get a 404 if it is not.

Creating A PEAR Package Using Phix

If you are using Phix to help maintain your project, you have an environment which already is configured for generating PEAR packages. But first you have to configure Phix so that it will generate the proper package.xml file:

First we edit the project.channel value in build.properties:

 
project.channel=${yourusername}.github.com/${pear_repository} 

Then we go on to the package.xml file, most of the everyday stuff (like adding files etc.) will be handled by Phix, but the descriptions, version history and package requirements need to be edited manually. See the PEAR package.xml documentation for more information.

Don’t forget to bump version info in build.properties and change-log information in package.xml when you are creating a new package version.

When you have got the package.xml and build.properties configured properly, use the command phing pear-package to create a PEAR package which will be placed in dist/${<packagename>}-X.Y.Z.tgz

Adding The Package To Your PEAR Channel

To add a PEAR package, you just use:

 
pirum add my/pear/repository my_pear_package-X.Y.Z.tgz
cd my/pear/repository
git commit -a -m "Added my_pear_package-X.Y.Z"
git push

Then visit ${yourusername}.github.com/${pear_repository} with your browser to see a list of all the uploaded packages.

Conclusion

In the beginning there was only pear.php.net, the only resource for PEAR packages in the world. Then other package channels arrived, and publishing your own PEAR package channel became really easy with Pirum. Now you don’t even need your own web-server to host it; you can host it on GitHub, along with the source code for your PEAR package.

Now, if we only can get some kind of PEAR channel aggregator or similar thing to avoid having to use channel-discover for every new tool we want to install…

Footnotes

  1. The channel name must match the regular expression [a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*
  2. You might want to turn off email notifications for successful page builds, because the emails can easily pile up if you make many pushes.

6 comments »

In my Beyond Frameworks talk, I explained how a component-based architecture can help answer some of the important (i.e. expensive!) questions you might face when creating long-lived apps that rely on a PHP framework. In this series of blog posts, I’m going to look at how to go about creating and working with components.

I’m now going under the bonnet of our components, and looking at the different file roles that the PEAR installer expects to find when we distribute our component as a PEAR-compatible package. It isn’t very often that a component needs to ship web pages too, but should the need arise, here’s how to do it.

What Is A Web Page?

A web page (the ‘www’ file role supported by the PEAR Installer) is any sort of content meant to be served up by web server software such as Apache. This can be a HTML file, a PHP script, CSS files, Javascript files, images … you name it.

Where Do Web Pages Go Inside Your Component’s Structure?

If we look at the PhpInfo component, you’ll find that the web pages live in the src/www/ folder:

src/www/ is meant to be a folder that holds all of the web pages (and related content) that you want installed into the computer system.

Where Does The PEAR Installer Install The Web Pages?

When you use the PEAR installer to install your component:

$ pear install phix/PhpInfo

all of the web pages get installed into /usr/share/php/www/ on your computer:

The PEAR installer’s behaviour here is almost identical to command-line scripts; the installer installs your web pages directly into the main www_dir folder. This does create the potential for clashes, where two or more packages want to install files with identical names. In practice, PHP components include web pages so rarely that the problem is unlikely to happen.

The test file src/www/phpinfo.php therefore ends up installed onto your computer as /usr/share/php/www/phpinfo.php.

There’s always the possibility that some Linux distros (and Mac OS X) will install doc files into a different folder. You can see where the PEAR installer will put these files on your computer by running:

$ sudo pear config-show | grep www_dir
PEAR www files directory   www_dir          /usr/share/php/www

How Do I Get Apache To Serve These Files?

By default, and for very good reasons, Apache does not serve content directly from /usr/share/php/www/. You will need to copy any files from /usr/share/php/www/ to your website folders (normally found under /var/www/) before they can be used.

4 comments »

In my Beyond Frameworks talk, I explained how a component-based architecture can help answer some of the important (i.e. expensive!) questions you might face when creating long-lived apps that rely on a PHP framework. In this series of blog posts, I’m going to look at how to go about creating and working with components.

I’m now going under the bonnet of our components, and looking at the different file roles that the PEAR installer expects to find when we distribute our component as a PEAR-compatible package. One of the most important file roles allows you to ship your tests with your package – and it’s vital that this is something that you get in the habit of doing routinely.

Test Your Code, And Ship Your Tests

If you want other developers to re-use your code in their projects, it is essential that you earn their trust. Trust has to be earned over time, by establishing a track record of dependability. There are many things you need to do to establish such a reputation, such as semantic versioning to flag backwards-compatibility breaks, and writing unit tests to prove that your code works as expected.

Although the PEAR Installer has long supported shipping unit test code, to date the PHP community hasn’t really embraced this, and it is still very rare for a PEAR package to include unit tests when downloaded. This is also compounded by unfortunate legacy choices in some Linux distributions (ubuntu bug, fedora bug), where unit test code currently ends up being installed under the same directory tree as the main code from the PEAR packages – something that only really matters since the PHP Community started to adopt PSR-0 as the standard for autoloading.

What Are PHP Tests?

PHP tests (the ‘test’ file role supported by the PEAR Installer) are executable tests. There is currently no formal standard for these tests; however, PHPUnit has emerged as the community’s de facto standard for unit tests, and is the approach that the PHP components skeleton supports out of the box to make life much easier for you.

Just as important as shipping tests is making sure that it is extremely easy for someone who knows nothing about your code to safely and reliably execute any tests that you ship. The components skeleton handles much of this for you, by including a PHPUnit bootstrap.php file, and a build.xml file with full instructions on how to execute the tests.

However, it is important that you write your tests in a way that doesn’t rely on anything that your users won’t have installed or configured.

Where Do Tests Go Inside Your Component’s Structure?

If we look at the CommandLineLibrary component, you’ll find that the unit test files live in the src/tests/unit-tests/ folder:

Important notes:

  1. The folder structure underneath src/tests/unit-tests exactly matches the folder structure for both PHP scripts and PHP files under the src/ folder.
  2. Each unit test script is designed for PSR-0 autoloading, just like the PHP scripts that they test.
  3. Each unit test script ends in the word ‘Test’. This is a convention that is supported in IDEs such as Netbeans.

Where Does The PEAR Installer Install The Tests?

When you use the PEAR installer to install your component:

$ pear install phix/CommandLineLib

all of the test files get installed into /usr/share/php/tests/<package-name> on your computer:

The PEAR installer’s behaviour here is different to both command-line scripts and PHP code; the installer creates a sub-folder with the name of your package, and then installs your test files into this sub-folder, and not into the main test_dir folder. This isn’t a problem in practice, as long as you are aware of the different behaviour here.

The test file src/php/unit-tests/Phix_Project/CommandLineLib/CommandLineParserTest.php therefore ends up installed onto your computer as /usr/share/php/tests/CommandLineLib/Phix_Project/CommandLineLib/CommandLineParserTest.php.

There’s always the possibility that some Linux distros (and Mac OS X) will install doc files into a different folder. You can see where the PEAR installer will put these files on your computer by running:

$ sudo pear config-show | grep test_dir
PEAR test directory   test_dir          /usr/share/php/tests

What About Other Kinds Of Test Code?

In the PHP component skeleton, the src/tests/functional-tests folder is available for anyone who wants to add any automated testing to prove that their app works. Selenium is a tool that might help with this.

There’s also the src/tests/integration-tests folder available for anyone who wants to maintain a strict separation between unit tests (which, technically, should prove that a single class works as required) and integration tests (which, technically, should prove that a collection of classes work together as required, especially across system boundaries).

At the time of writing, the component skeleton doesn’t do anything with code inside these folders. Patches are most welcome!

5 comments »
Page 1 of 612345...Last »

This Month

September 2014
S M T W T F S
« Jan    
 123456
78910111213
14151617181920
21222324252627
282930  

Recent Comments