Comparing ContractLib to PHP's Built-In assert()
One of the questions I've been asked after yesterday's blog post about Phix's ContractLib is why not just use PHP's built-in assert() function? I think it's a great question, and the best way to answer it is to take a look at the key differences between two solutions.
Side By Side ComparisonFeature assert() ContractLib Implementation PHP extension written in C (ships as standard part of PHP) PHP library written in PHP Enable / disable execution Partial (there is an overhead when disabled, but it's low) Partial (there is an overhead when disabled, but it's higher) Issues PHP4-style warning when tests fail Yes (configurable) No (throws a ContractFailedException instead) Terminate PHP script when tests fail Yes (configurable) Only if the ContractFailedException is never caught Quiet eval of test expression Yes (configurable) No (not required; test expressions are pure PHP code, not eval() strings) Callback on failed test Yes (configurable) No (unwinds the stack instead by throwing ContractFailedException) Throws Exception when tests fail No (but can emulate if you write your own assert() callback method) Yes (standard behaviour) Tests are pure PHP code No - recommended way is to pass strings into assert() to be eval()'d Yes Error report includes original value that failed the test No Yes Support for per-test custom failure messages No Yes - are required to provide one Support for Old Value storage and recall No (but can emulate by hand) Yes
The Differences Explained
The key difference is one of philosophy. assert() sits well with the PHP4 style of error reporting and handling, whereas ContractLib is firmly in favour of the OO world's approach to reporting errors.
It's a personal preference, but I think that PHP4-style errors have no place in code that has any desire to be robust. Exceptions aren't perfect, don't get me wrong, but their core property of unwinding the call stack in an orderly fashion makes writing robust code much easier. And they also carry a payload - information about what went wrong and why - which PHP's assert() cannot provide to the same extent.
It's much quicker to debug something when there's a record of the value that failed the test. For that reason alone, I'd always prefer something like ContractLib over the built-in assert() approach.
But we can't ignore the fact that these are tests that get shipped to, and executed in, the production environment. Unlike unit tests, adopting programming by contract will slow down your PHP code in production. The question is: by how much?
What About The Performance?
I've done some benchmarking between the two, using the five tests listed in the final example in yesterday's blog post. It's a real-world example of the kind of tests that I would look to add to code to improve robustness.
Here are the results I gathered, calling the tests 10,000 times in a tight loop. The tests were run from the command line, and the times do include PHP start-up / shutdown time and the time taken to parse each test file. I assumed a best-case scenario, where the tests would always pass.Test Approach Time w/ Tests Disabled Time w/ Tests Enabled Tests written using assert() 1.103s (100%) 5.989s (543%) Tests written using ContractLib 3.055s (277%) 3.096s (281%)
When tests are disabled, using assert() is much cheaper than using ContractLib today. That's to be expected, as assert() is written in C. I
Truncated by Planet PHP, read more at the original (another 1631 bytes)