1

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);
    }
}
hakre
  • 193,403
  • 52
  • 435
  • 836
ExternalUse
  • 2,053
  • 2
  • 25
  • 37
  • 2
    How would a _static_ method be able to display the state of a given _instance_? Just think about that. If there are multiple instances of that class, which one would it use? – ppeterka Jan 11 '13 at 12:45
  • Let's suppose all of the property $entries, the methods add() and getEntriesAsHtml() were static. Then if add() would add an entry to self::$entries, and something like `implode("\n", self::$entries)` was in getEntriesAsHtml(), there wouldn't be any need for an instance, or am I mistaken? If __construct and __clone are private one could only use the methods statically. – ExternalUse Jan 11 '13 at 13:12
  • If you don't want to use OOP, why do you do it? Having everything static doesn't seem to be legit OOP design... If you don't haev an instance, it can't also be named singleton - as that implies you have exactly one instance of the given class... – ppeterka Jan 11 '13 at 13:14
  • OK, agreed - I should not have called it singleton, I guess. Let me try to explain it differently: In my script I could instantiate my body-object like this: $body = new bodyClass(). In further classes, functions etc I would need to either - refer to that variable, like $GLOBALS['body']->add()... or - retrieve the instance using bodyClass->getInstance()->add()... I thought there might be a "clever" (?) way to circumvent the need to retrieve the instance every time, by using static methods. – ExternalUse Jan 11 '13 at 13:23
  • Personally I wouldn't take either option. I would create my BodyClass as a standard class and inject it into all the objects that needed it. That leaves you with the freedom to create multiple separate instances in the future if the need arises, and arguably makes the code more testable. I would definitely favour passing a reference between objects to inserting it into the global namespace. – shanethehat Jan 11 '13 at 13:45
  • I have added Code samples for both versions - both of them work; but where is the difference? VersionA::add('

    Something

    '); VersionA::add('

    Something else entirely

    '); echo VersionA::getHtml(); VersionB::add('

    Something

    '); VersionB::add('

    Something else entirely

    '); echo VersionB::getHtml();`
    – ExternalUse Jan 11 '13 at 13:47

3 Answers3

1

You should probably not use a static add method.

The idea of a singleton is that you create a single instance of a class so that external objects can interact with that instance. That means that your add method should not be static.

You could do something like:

class MyBodyClass
{
    protected $entries = array();

    protected $instance;

    public static function getInstance()
    {
        if (is_null($this->instance)) {
            $this->instance = new self();
        }
        return $this->instance;
    }

    private function __construct() {}

    public function add($strEntry)
    {
        $this->entires[] = $strEntry;
    }
}

And call it like this:

MyBodyClass::getInstance()->add('<h1>blah</h1>');
shanethehat
  • 15,460
  • 11
  • 57
  • 87
  • Well, I'm trying to avoid having to type anything like getInstance() every time. The instance could be retrieved within the static add() itself. – ExternalUse Jan 11 '13 at 13:07
  • 1
    @ExternalUse - your comment indicates that 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. – SDC Jan 11 '13 at 13:57
  • @SDC: You are probably right. I am trying to understand the why, though. Why would it be smarter to type mySingletonBody::getInstance()->doSomething(); than myNotQuiteSingletonBody::doSomething(), using either just a static array to manipulate something or a non-static array whereby doSomething() retrieves the instance itself? I am not insisting on my solution - I am just trying to understand the Do's and Dont's of OOP in PHP. – ExternalUse Jan 11 '13 at 14:03
  • 1
    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. – SDC Jan 11 '13 at 14:21
  • See also the accepted answer here: http://stackoverflow.com/questions/2969599/why-use-singleton-instead-of-static-class – SDC Jan 11 '13 at 14:21
  • Thanks for taking the time to clarify this, SDC. If you wouldn't mind copying your recent comments to an answer, I'd be happy to accept it. – ExternalUse Jan 11 '13 at 15:18
  • @SDC: I'd really love to close this question; would you mind repeating your comment in an answer that I can accept? Thanks! – ExternalUse Jan 15 '13 at 13:47
  • @ExternalUse - done. Sorry I missed your previous comment. :) – SDC Jan 15 '13 at 13:52
1

(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.

SDC
  • 14,192
  • 2
  • 35
  • 48
  • 1
    For anybody stumbling across the same question, please also see SDC's comment referring to another accepted answer that helped clarify this for me: http://stackoverflow.com/questions/2969599/why-use-singleton-instead-of-static-class – ExternalUse Jan 15 '13 at 18:17
0

Something like this should work:

class MySingleton
{

    public static function getInstance()
    {
        static $inst = null;
        if ($inst === null) {
            $inst = new MySingleton();
        }
    return $inst;
    }

    private function __construct() { }

    public static function add() {}

    public function __toString() {
        echo 'Something';
    }

}

$body = MySingleton::getInstance();
$body::add('Something');
echo $body;
Vlad Preda
  • 9,780
  • 7
  • 36
  • 63