You're asking a rather specific coding question, and not actually for your code to be reviewed. However, I'm going to, because if you want to learn OOP, you might aswell learn what not to do.
Your basic question is answered in the static-tag wiki which, cough I wrote cough.
Be that as it may, your singleton implementation still has a few omissions:
- You can't safely add a second static method
- I can still clone the instance
$state
is an unreliable variable
- Singletons in PHP are pointless, untestable, anti-patterns. Don't use them
- Choose: OO or globals, don't serve globals in an OO sauce
Second static method:
If I add a second static method, that may require access to a non-static method (public static getCurrentState() { /* check if there's an instance already, call getState on that instsance or return 0 */ }
for example), that method won't have access to the $inst
variable.
Generally, singletons have a static property that holds an instance:
class Singleton
{
private static $inst = null;
private $state = null;//<-- why 1?
private function __construct()
{
$this->state = 0;
}
public static function getInstance()
{
if (static::$inst === null)
{
static::$inst = new Singleton();
}
static::$inst->state += 1;//update refcount here
return static::$inst;
}
public static function getStateCount()
{
if (static::$inst === null)
return 0;
static::$inst->state;//no need to call a getter, we're in the right scope for private access
}
}
Great, so now all static (and non-static) methods have access to the $inst
property, and we can use that variable in all methods. Since all methods will, by definition, be defined within the class, they have access to private properties anyway, so we don't need getters and setters. We can just access the properties directly (which is faster).
Cloning a singleton shouldn't be possible
Now imagine your object actually does something even mildly important. For example, a Singleton is sometimes (ab-)used for DB connections, what would be the result of doing this:
$db = Singleton::getInstance();//as you'd expect $db is now an object
$clone = clone $db;//You're in trouble!
That's why Singletons often implement the magic __clone
method (along with other magic methods):
public function __clone()
{//make singleton un-clonable
return false;
//or even:
throw new RuntimeException('You cannot clone a singleton, hooligan!');
}
Do the same for __toString
, __sleep
and other magic methods.
Ok, now we've sorted that out, onwards:
$state
is unreliable:
Because $state
can't be decremented (or no code to decrement it is provided), and because your singleton is clone-able, the value of $state
isn't guaranteed to be shared between all instances. Solving the clone issue addresses this, as does making the property static
, too. But really, as I'm going to explain now: avoid static
whenever you can.
Singleton and PHP is pointless:
PHP is, essentially, stateless. Static
and singletons along with it enable you to persist something, to retain a state. But retaining state in a stateless environment is like placing an empty bottle in a fridge: it still won't give you something cool to drink.
Each request that executes your code starts with a blank slate, and your singleton will be re-initialized. Couple that to the fact that static properties and methods are (marginally) slower, and you end up with pointless overhead.
If you don't want a second instance of class X, don't create a second instance. Think when writing code, don't write code that sort of fixes coding errors on your part.
Testing a singleton is a nightmare, google "PHP test singleton" and read a few articles. It's a well documented downside that will soon have you steer clear of this anti-pattern.
Globals in OO drag
Singletons (and basically all statics), then, what do they do: They couple state (data, variables) and functionality (in the form of methods) together, without the safety and flexibility of an actual instance.
A static method actually is a global function, that calls on some data (in the form of static properties it can access) to do its job. That's what makes code that uses a lot of static
's harder to maintain and understand.
If you want functionality that requires, for example, a DB connection: use an object that does just that. It's safer, you know that if something fails where to look and it is more flexible, simply because you can pass an instance to other methods/functions as an argument.
If you use ClassName::staticMethod()
, you're writing code that looks OO, but could just aswell be written like this: globalFunction($dbConnection, $param);
.
Whereas true OO code would look like this:
$service->queryUsers($dbInstance, $userStatus);
The queryUsers
method could look like this, then:
public function queryUsers(DBConnection $db, $status = self::USER_ACTIVE)
{
if (!in_array($status, $this->acceptedStatusses)
throw new InvalidArgumentException('Invalid user-status');
return $db->query('SELECT * FROM user WHERE userStatus = '.$status)
->fetchResults();
}