Reputation: 625
I was doing a little experiment with PHP clases, and I encountered this weird problem.
The class is intended to assign its $instance property an instance of itself in case $instance is null, but this mechanism which is supposed to prevent infinite self-procreation seems to fail, and this is what happens:
Fatal error: Maximum function nesting level of '100' reached, aborting!
<?php
class SeemsLikeInfinity {
public $instance;
public function __construct() {
if ( $this->instance == null ) {
$this->instance = new self;
}
}
}
$looping = new SeemsLikeInfinity();
?>
Why does this happen? The __construct merely creates a new instance of the class which is blank, thus has the empty $instance, and therefore creates the instance again, making the loop go infinite?
Thank you very much
Upvotes: 1
Views: 1148
Reputation: 338
That happen beacuse the param $instance is always null. Each time you create new object inside class. If you want implements singleton you should change __construct to private and use one static method to create just one object
class SeemsLikeInfinity {
private static $instance;
private function __construct() {
// some action;
}
public static function generate_singleton(){
if ( self::$instance == null ) {
self::$instance = new self;
}
return self::$instance
}
}
$singleton = SeemsLikeInfinity::generate_singleton();
// here you have singleton and you cant create more tha one object
Upvotes: 2
Reputation: 5647
When you make an object with the 'new' keyword, it calls the constructor. If you use new self
in the constructor, it will call the construction again. Causing an infinite loop.
A new instance is automatically made when you use new
, so you don't have to store something like $this->instance
. The variable $this
is your instance.
The important thing to remember is that each instance of the class has its own variable $instance
. So it will always be null the whole way down.
Now, if you are trying to make a singleton, you actually have to make the constructor private:
private function __construct() {}
Then we need instance
to be a static variable:
static private $instance;
No no one can create a new object using the new keyword. So then we need to provide a static function to create an instance:
static function newInstance() {
if ( self::instance == null ) {
self::instance = new self;
}
return self::instance;
}
Now you get new instances using this:
$x = SeemsLikeInfinity::newInstance();
Now there will only ever be one SeemsLikeInfinity object and you can always access it with SeemsLikeInfinity::newInstance()
Upvotes: 1
Reputation: 8482
The reason you have to use static variables rather than $this
is that $this
is relative only to the current object - every time you create a new SeemsLikeInfinity
object you're creating a new $this
for that object - therefore $this->instance
is always null
when you instantiate the class.
So, what's happening in your constructor:
if ( $this->instance == null ) {
$this->instance = new self;
}
... is a new $this->instance
(with no assigned value) is created when the object is constructed so a new object is constructed with a new $this->instance
, again without a value, so a new object is constructed with a new $this->instance
, again without a value ... and so on, forever.
Static variables, however, are relative to the class itself... you can instantiate the class as many times as you like and each object created will inherit the last set values of the static variables from the class.
What you're trying to do is create a Singleton so you never call the constructor directly, you instantiate and access the class via a static getInstance()
method:
// I've made this a "final" class - you can't really
// extend Singletons without getting into a mess
final class SeemsLikeInfinity {
// this needs to be a static variable as it'll
// be checked from the static self::getInstance() method
// it should also be private as you'll never call it from
// outside the class
private static $instance;
// the constructor function should also be private
// as it'll ONLY ever be called from the
// self::getInstance() method
private function __construct() {
// ... do stuff here to instantiate your class
}
// Singletons can't be cloned
public function __clone() {
throw new Exception("ERROR : you can't clone Singletons", E_USER_ERROR);
}
// this is the main getInstance() method called
// to actually instantiate or call the class
public static function getInstance() {
if(!self::$instance) { self::$instance = new self(); }
return self::$instance;
}
}
// now, to instantiate or access your class you'd use getInstance()
// you'll no longer have permanently reiterating classes and you
// should be golden
$looping = SeemsLikeInfinity::getInstance();
As an aside, the best use I've ever found for Singletons is building a session management wrapper since a session, by its very nature, has just one instance.
Upvotes: 0