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 Comparison| Feature | 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 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 wr
Truncated by Planet PHP, read more at the original (another 1645 bytes)