Search code examples
phpstringclassobjectmagic-methods

Singleton access / PHP Magic method __toString/ printing a static object


what I'm trying to achieve (PHP 5.3) is to have an accessor to my representation of, for example, the HTML Body of a page. Instead of echoing everything directly it should be added to an array of entries in that singleton. Example: myBodyClass::add('<h1>Title</h1>');

add() is declared as public static function add($strEntry) {}

Now should I just add them to a static array $entries like self::$entries[] = $strEntry; (class VersionB) or should I use an instance like self::getInstance()->entries[] = $strEntry;? (class VersionA) (whereby getInstance() would of course instanciate ´...new self;´ if necessary)

I don't quite understand the difference yet, I'm afraid.

The second part of my question is how to print the object. The PHP manual is a bit thin about why __toString() cannot be static - but then again I would understand a parser to have a problem distinguishing echo myBodyClass from a constant (so is that the reason?)

Ideally I would like to call add() as often as needed to add all parts of the body, and then use something like echo myHeaderClass, myBodyClass, myFooterClass; at the end of the script, which should invoke the __toString() methods within the classes.

Thanks for pointing me into the correct direction.

Code Example

class VersionA
{
    private static $instance = null;
    private $entries = array();
    private final function __construct(){}
    private final function __clone(){}

    private static final function getInstance()
    {
        if (self::$instance === null) :
            self::$instance = new self;
        endif;
        return self::$instance;
    }
    public static function add($sString)
    {
        self::getInstance()->entries[] = $sString;
    }
    public static function getHtml()
    {
        return implode("\r\n", self::getInstance()->entries);
    }
}

class VersionB
{
    private static $entries = array();
    private final function __construct(){}
    private final function __clone(){}

    public static function add($sString)
    {
        self::$entries[] = $sString;
    }
    public static function getHtml()
    {
        return implode("\r\n", self::$entries);
    }
}

Solution

  • (Copied from comments, as requested by OP...)

    You're missing the point of a singleton. There is a difference between a singleton object and a static class. If you want to use methods that act on an object (like __toString()), then you need it to be an object; a static class isn't good enough. If you want to avoid calling getInstance all the time, then set a variable to the object, and pass it around everywhere like you would with other objects, per the Dependency Injection pattern. That would probably be best practice advice anyway.

    The thing with a static class is that it isn't really OOP; it's just a bunch of global functions with a shared class name. One may as well use plain functions with a namespace declaration.

    But the main reason for using a genuine singleton is swappability. Assuming you follow my advice above and create a single reference to the object that you pass around your code, it becomes a lot easier to swap in an alternative object since you don't have the hard-coded class name being referenced all over the place. This makes it a lot easier to write decent unit tests for your code that uses the class.

    Hope that helps.