JonoB
JonoB

Reputation: 5887

PHP maintain class state

I'd like to have a library class that maintains state across the same request. My use case is that I want to pass 'messages' to the class, and then call them at any time from a view. Messages can be added from any part of the application.

I had originally done this via static methods, which worked fine. However, as part of the lib, I also need to call __construct and __destruct(), which can't be done on a static class.

Here's a very simple example of what I am trying to do:

class Messages
{
  private static $messages = array();

  public function __construct()
  {
    // do something
  }

  public function __destruct()
  {
    // do something else
  }

  public static function add($message)
  {
    self::$messages[] = $message;
  }
  public static function get()
  {
    return self::$messages;
  }
}

I can then add messages anywhere in my code by doing

Messages::add('a new message');

I'd like to avoid using static if at all possible (testability). I have looked at DI, but it doesn't seem appropriate, unless I'm missing something.

I could create a class (non-static) instead, but how do I then ensure that all messages are written to the same object - so that I can retrieve them all later?

What's the best way to tackle this?

Upvotes: 5

Views: 900

Answers (5)

Andrew
Andrew

Reputation: 2154

I looks like you could benefit from using the Singleton pattern - it is designed for an object that must have only one instance throughout a request. Basically, you create a private constructor and a static method to retrieve the sole instance. Here is an example of a singleton that will do what you describe.

<?php
class Messages
{
    private static $_instance;
    private $_messages = array();

    private function __construct() {
        // Initialize
    }

    static public function instance() {
        if (! self::$_instance) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    public function add_message( $msg ) {
        $this->_messages[] = $message;
    }

    public function get_messages() {
        return $this->_messages;
    }

    private function __destruct() {
        // Tear-down
    }
}

$my_messages = Messages::instance();
$my_messages->add_message( 'How now, brown cow?' );
// ...
$your_messages = Messages::instance();
$msgs = $your_messages->get_messages();
echo $your_messages[0]; // Prints, "How now, brown cow?"

Since the constructor is private, you can only create a Messages object from within a method of the object itself. Since you have a static method, instance(), you can create a new Messages instance from there. However, if an instance already exists, you want to return that instance.

Basically, a singleton is the gatekeeper to its own instance, and it stubbornly refuses to ever let more than one instance of itself exist.

Upvotes: 3

Rudu
Rudu

Reputation: 15892

Sounds a bit like you want a singleton, although as an anti-pattern I'd avoid it.

You could do a full static class where every static member calls a self::_isBuilt(); method to do your construct elements. Destruct is a little trickier.

The best case for your needs might be a normal (non-static) class that you build right away and then access from a global... not super neat, but allows construct/destruct and members, and your statics to use $this which could be helpful. If you don't like the global variable, you could also wrap it in a method (a trick used in JS a fair bit) but it's not really any neater.

As a normal global class:

$myClass=new myClass();
//Access anywhere as:
globals['myClass']->myFunction(..);

Wrapped in a function

function my_class() {
 static $var=null;
 if ($var===null) $var=new myClass();
 return $var;
}
//Access anywhere as:
my_class()->myFunction(..);

Upvotes: 0

raidenace
raidenace

Reputation: 12826

How about making it a singleton class?

class Messages
{
    // singleton instance of Messages
    private static $instance;

    public function __construct() { ... }

    public static function getInstance()
    {
        if (!self::$instance)
        {
            self::$instance = new Messages();
        }

        return self::$instance;
    }
}

This would ensure that all your messages get written to the same object, and also allow you to call __construct and __destruct

Upvotes: 1

Louis Huppenbauer
Louis Huppenbauer

Reputation: 3714

What you need is the Singleton pattern:

final class Singleton {
    // static variable to store the instance
    private static $instance = NULL;

    // disable normal class constructing
    private function __construct() {}

    // instead of using the normal way to construct the class you'll use this method
    public static function getInstance() {
       if (NULL === self::$instance) {
           self::$instance = new self;
       }
       return self::$instance;
    }
    // disable external cloning of the object
    private function __clone() {}
}

// get the instance across some of your scripts
$singleton = Singleton::getInstance();

Upvotes: 0

Bot
Bot

Reputation: 11855

Sounds like you are wanting to do a Singleton class. This will create an instance in one class and allow you to access that same instance in another class. Check out http://www.developertutorials.com/tutorials/php/php-singleton-design-pattern-050729-1050/ for more information.

Upvotes: 1

Related Questions