What Does This Say About Unit-Testing in PHP Land?

Without having done actual research, and depending on my personal experience alone, I would assert that in PHP userland …

  • There are maybe 20 or more general-purpose CMSes.
  • There are perhaps 50 or more public frameworks (of varying quality and approach).
  • There are easily 100 or more templating libraries (even if most of them can be grouped under 4 or 5 approaches).
  • There appear to be only 5 unit-test systems: PhpUnit, SimpleTest, Solar_Test, PHPT, and Lime.

Dispute the specific numbers all you like; they’re not the point. The point is that CMSes, frameworks, template systems, etc. seem to be a very common artifact among PHP developers, but writing (and then publishing) unit-test systems does not seem anywhere near as common an obsession.

What does this say, if anything, about the attention given to unit-testing among PHP developers?

Note that this is an *off-the-cuff opinion* and a *request for comment*, and nothing else. (Some folks get testy about testing; please remember to be nice while commenting. πŸ˜‰

Update: And there are only 3 or 4 documentation systems: PhpDocumentor, Doxygen, and Solar_Docs, along with the various combinations of DocBook tools that Zend, PEAR, and PHP are using. I wonder how that enters into it, if at all.

Solar 1.0.0 alpha1 Released

After more months of breaks, changes, additions, and refactoring, I’m (finallly) at the point where I’m ready to call the Solar framework for PHP 5 feature-complete. The first of this feature-complete series is the new Solar-1.0.0alpha1 release as of Sunday, 11 Nov, 2007. We now have at least 80% of everything you would need to build web-based and cli-based applications. This is a big milestone for the project.

The change notes, of course, are gigantically long, so they’re on a separate page.

Read on for more highlights, especially about the new ORM system replacing the previous Solar_Sql_Table series.

Continue reading

Microsoft Web Developer Summit 2007

I’m spending today and tomorrow in Redmond, at the invitation of Microsoft, with a bunch of other movers-and-shakers in the PHP world. Microsoft says they want to get our feedback on their offerings, and find out how to better interoperate with PHP. This should be fun and interesting. πŸ™‚

Memory Leaks With Objects in PHP 5

Wow, this entry is getting a lot of traffic from DZone and other places. If you like this post, be sure to check out New Year’s Benchmarks for PHP framework comparisons, and the Solar Framework for PHP 5. Thanks for visiting!

One of the nice things about using a scripting language is that it automates garbage collection for you. You don’t have to worry about releasing memory when you’re done with your variables. As each variable passes out of scope, PHP frees that memory for you. If you want to, you can free the memory yourself using unset(), but usually you don’t have to.

But there is at least one circumstance in which PHP will not free memory for you when you call unset(). Cf. http://bugs.php.net/bug.php?id=33595.

Problem

If you have two objects in circular reference, such as in a parent-child relationship, calling unset() on the parent object will not free the memory used for the parent reference in the child object. (Nor will the memory be freed when the parent object is garbage-collected.)

Confusing? Here’s a script you can run to see it in action:

<?php
class Foo {
    function __construct()
    {
        $this->bar = new Bar($this);
    }
}

class Bar {
    function __construct($foo = null)
    {
        $this->foo = $foo;
    }
}

while (true) {
    $foo = new Foo();
    unset($foo);
    echo number_format(memory_get_usage()) . "n";
}
?>

Run that and watch your memory usage climb and climb, until memory runs out.

...
33,551,616
33,551,976
33,552,336
33,552,696
PHP Fatal error:  Allowed memory size of 33554432 bytes exhausted
(tried to allocate 16 bytes) in memleak.php on line 17

For most PHP developers this behavior is not likely to be a problem. However, if you use a lot of objects in parent-child relationships over a long-running script, you can run out of memory pretty quickly, especially if those objects are relatively large. I discovered this myself while testing some ORM-related pieces for Solar and it took me a couple days to figure it out — hence this blog post. πŸ˜‰

Userland Solution

The bugs.php.net link above presents a solution, albeit inelegant and tedious. The “fix” is to call a destructor method before unsetting the object. The destructor method should clear out any internal parent object references, which will free the memory that would otherwise leak.

The “fixed” script looks like this:

<?php
class Foo {
    function __construct()
    {
        $this->bar = new Bar($this);
    }
    function __destruct()
    {
        unset($this->bar);
    }
}

class Bar {
    function __construct($foo = null)
    {
        $this->foo = $foo;
    }
}

while (true) {
    $foo = new Foo();
    $foo->__destruct();
    unset($foo);
    echo number_format(memory_get_usage()) . "n";
}
?>

Note the new Foo::__destruct() method and the call to $foo->__destruct() before unsetting. Now the script will run forever, showing you the amount of memory it uses (which never gets any bigger, thank goodness).

PHP Internals Solution?

Why does the memory leak occur? I am not wise in the ways of PHP internals, but it has something to do with reference counts. The refcount for the child $foo reference inside $bar does not get decremented when the parent $foo is unset, so PHP thinks the $foo object is still needed and doesn’t release the memory for it … or something like that. I am sure to be displaying my ignorance here, but the general idea is the same: a refcount is not decremented, so some memory never gets released.

I get from the bugs.php.net link above that modifying the garbage-collection process to check for this kind of issue would be a performance-killer, and what little I know about refcounts makes me think this is true.

Instead of changing the garbage-collection process, would it make sense to have unset() do some extra processing on the variable to look for internal objects and unset them too? (Or perhaps to call __destruct() on objects being unset?) Maybe a PHP internals person can comment here or elsewhere on the practicality and/or wisdom of such changes.

UPDATE: Martin Fjordvald notes in the comments that a patch from David Wang for garbage collection does exist and is under consideration. (Did I say “patch”? More like “whole sheet of cloth” — it’s huge. See the CVS checkout instructions at the end of that email.) Problem is that it didn’t garner many votes for inclusion in 5.3. A nice compromise solution might be to have the unset() function call the __destruct() method of objects sent to it; that would seem intuitively appropriate here.

Quick Wrapup: php|works 2007

This is waaay late, but better than never. πŸ˜‰

I didn’t get to attend too many talks this time around, mostly because I spent my time practicing and editing my presentations the-day-of. I did get to all three of the talks from Jeff Moore (valuable stuff), as well Ed Finkler‘s PhpSecInfo presentation (it looks like a very useful security tool).

I also got to speak (too briefly) with a couple of fellow framework enthusiasts: Dustin Whittle of Symfony, and Felix Geisendârfer of Cake.

Felix made it to my benchmarking talk and gave valuable feedback on the methodology and results. Dustin did not attend; I’m sure he was preparing for his own presentation in the following hour. I also got to speak (again too briefly) with Chris Jones (no relation), who had very nice things to say about my performance.

I wish I’d had a Code Igniter devotee at that one, but Ed Finkler did make it to my project organization talk as a representative user of CI.

I’ve not seen this linked prominently anywhere, but you can get all the slides from php|works 2007 (well, all the ones that the presenters turned in πŸ˜‰ here: http://works.phparch.com/c/p/works_live,slides

Finally, while I did get to hang around with all the “cool kids” and presenters (Sara Golemon, Chris Shiflett, Sebastian Bergmann, Andrei Zmievski, Cal Evans, Megan Nelson, Ben Ramsey, Derick Rethans, Clay Loveless, and everyone else whose names escape me right now), what I *really* enjoyed was meeting the attendees and hearing about the work they’re doing with PHP. That’s always the high point of these conferences for me. πŸ™‚