Wednesday 29 July 2009

An easy way to leak memory using DirectoryIterator

I've been trying to track down a memory leak while running the PHP_CodeSniffer unit tests after being told the PEAR-wide test suite was running out during my run. My own testing showed the unit tests started at 11MB of memory used and ballooned out to about 56MB, even with no error messages being generated.

I already do a bit of cleanup to save memory (unset()ing some member vars mostly) and I tried cleaning just about everything else out, but memory usage didn't drop. I don't have any circular references, so I had nothing obvious to try.

After a bit of playing, I found the problem; a DirectoryIterator.

My code looked a bit like this:

$di = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
foreach ($di as $file) {
$basename = basename($file, '.php');
if (substr($basename, -5) !== 'Sniff') {
continue;
}
}

The $file variable is a DirectoryIterator item. I was passing it into the basename() function and relying on the fact that PHP would cast it to a string (the file name).

I changed the code to grab the file name first and then pass it into the basename() function, like this:
$di = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
foreach ($di as $file) {
$fileName = $file->getFilename();
$basename = basename($fileName, '.php');
if (substr($basename, -5) !== 'Sniff') {
continue;
}
}

So now the tests only use 16MB of memory; a healthy saving of 40MB. There is probably a bit more that can be saved by calling this code less during testing, but this was a good (and easy) result regardless.

Tuesday 7 July 2009

PHP_CodeSniffer 1.2.0RC3 released

I've just uploaded version 1.2.0RC3 of PHP_CodeSniffer. RC3 is a feature release rather than a bug fix release because there were a few nifty features (like error message suppression) that I wanted to get in before the next stable.

This is the last planned release candidate before 1.2.0 stable.

You can view the full changelog and download the release on the package download page.

Friday 3 July 2009

Suppress error and warning messages in PHP_CodeSniffer

Just over 13 months ago, I had a feature request submitted for PHP_CodeSniffer to allow developers to annotate their code with comments that tell PHP_CodeSniffer to ignore some lines. At the time, I remember thinking "This is a hack!", but I've changed my mind. PHP_CodeSniffer version 1.2.0 will have error message suppression comment tags.

You use them like so:

$var = TRUE;
// @codingStandardsIgnoreStart
if ($var === true) {
echo "true that";
}
// @codingStandardsIgnoreEnd

If you use the Squiz coding standard that comes bundled with PHP_CodeSniffer, it will complain about pretty much everything and tell you you can't have "true" in lowercase and that echo'd string can't be in double quotes.

Without the new suppression tags in place, you get this:
FILE: /Users/gsherwood/PHP_CodeSniffer/temp.php
---------------------------------------------------------------
FOUND 3 ERROR(S) AND 0 WARNING(S) AFFECTING 3 LINE(S)
---------------------------------------------------------------
2 | ERROR | Missing file doc comment
4 | ERROR | TRUE, FALSE and NULL must be uppercase; expected
| | "TRUE" but found "true"
5 | ERROR | String "true that" does not require double quotes;
| | use single quotes instead
---------------------------------------------------------------

With them in place, you get this:
FILE: /Users/gsherwood/PHP_CodeSniffer/temp.php
---------------------------------------------------------------
FOUND 1 ERROR(S) AND 0 WARNING(S) AFFECTING 1 LINE(S)
---------------------------------------------------------------
2 | ERROR | Missing file doc comment
---------------------------------------------------------------

Too easy. Sorry it took so long.

Please don't be tempted to wrap your entire file in these tags. The Squiz standard will still find ways to yell at you and you're not fooling anyone.

Grab the code from CVS now or wait for the next release, which will be whenever I get my act together.