I've 3 classes. [1]Singleton [2]Load [3]Dashboard . In Load class there is one method called 'model()'. Where i'm initializing data for singleton object by using this code.
$obj = Singleton::getInstance();
$obj->insertData('email', 'mail#domain.com');
Again, from Dashboard class there is one method called 'show()' from where i'm trying to print the Singleton object data. But, here i can see all the data of Singleton object except the data which has been initialized by 'model' method of 'Load' class.
Here is my full code...
<?php
//---Singletone Class---
class Singleton
{
// A static property to hold the single instance of the class
private static $instance;
// The constructor is private so that outside code cannot instantiate
public function __construct() {
if(isset(self::$instance))
foreach(self::$instance as $key => &$val)
{
$this->{$key} = &$val;
}
}
// All code that needs to get and instance of the class should call
// this function like so: $db = Database::getInstance();
public static function getInstance()
{
// If there is no instance, create one
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
// Block the clone method
private function __clone() {}
// Function for inserting data to object
public function insertData($param, $element)
{
$this->{$param} = $element;
}
}
//---LOAD class---
class Load
{
function __construct()
{
$obj = Singleton::getInstance();
$obj->insertData('country', 'INDIA');
}
function model()
{
$this->name = 'Suresh';
$obj = Singleton::getInstance();
$obj->insertData('email', 'mail#domain.com');
}
function msg()
{
return('<br><br>This message is from LOAD class');
}
}
$obj = Singleton::getInstance();
$load = new load();
$obj->load = $load;
//---Dashboard Class---
class Dashboard extends Singleton
{
function __construct()
{
parent::__construct();
}
function show()
{
echo "Default data in current Object";
echo "<br>";
print_r($this);
echo $this->load->msg();
$this->load->model();
echo "<br><br>Data in current Object after post intialization";
echo "<br>";
print_r($this);
}
}
$dashboard = new dashboard();
$dashboard->show();
If your singleton was truly a singleton then the update would have worked. I'm suspecting that you may have multiple instances of the singleton class that is initialized.
Edit:
Also its not a good idea to inherit from a true singleton class.
You need to remove the inheritance that Dashboard has on Singleton
Edit:
Best practice on PHP singleton classes
I don't like your direct access to an object like an array. This one is a better approach [see here]:
You should call it like this:
$obj = Singleton::getInstance();
$load = new Load();
$obj->insertData( 'load', $load );
Implementation of Singleton:
class Singleton
{
// A static property to hold the single instance of the class
private static $instance;
// my local data
protected $_properties;
// You might want to move setter/getter to the end of the class file
public function __set( $name, $value )
{
$this->_properties[ $name ] = $value;
}
public function __get( $name )
{
if ( ! isset( $this->_properties[ $name ] )) {
return null;
}
return $this->_properties[ $name ];
}
// No need to check, if single instance exists!
// __construct can only be called, if an instance of Singleton actually exists
private function __construct() {
$this->_properties = array();
foreach(self::$instance as $key => &$val)
{
$this->_properties{$key} = &$val;
}
}
public static function getInstance()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
// Function for inserting data to object
public function insertData($param, $element)
{
$this->_properties{$param} = $element;
}
// Block the clone method
private function __clone() {}
}
Related
Need dynamically append method to class.
My code:
<?php
class stdClass1 {
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
}
class stdClass2 {
function stdRunMethod() {
$obj = new stdClass1();
$obj->test = function() {
echo 'a simple function';
};
$obj->test();
$obj2 = new stdClass1();
$obj2->test();
}
}
$obj = new stdClass2();
$obj->stdRunMethod();
Question: why test method run only for first instance of stdClass1 class? How to append this method for all new instances?
try this instead (demo):
<?php
class stdClass1 extends \stdClass
{
private static $addedClosures = array();
public function __set($name, $value)
{
if ($value instanceof \Closure) {
self::$addedClosures[$name] = $value;
}
else {
parent::__set($name, $value);
}
}
public function __call($method, $arguments)
{
if (isset(self::$addedClosures[$method]))
return call_user_func_array(self::$addedClosures[$method], $arguments);
return call_user_func_array($method, $arguments);
}
}
class stdClass2 extends \stdClass
{
function stdRunMethod()
{
$obj = new stdClass1();
$obj->test = function () {
print_r('a simple function');
};
$obj->test();
$obj2 = new stdClass1();
$obj2->test();
}
}
The reason it only runs once is that each copy of stdClass1 maintains their own set of variables. In the following
$obj1 = new stdClass1();
$obj1->a = '1';
$obj2 = new stdClass1();
$obj2->a = '2';
echo $obj1->a;
You'll get the value 1 as the output. Because in most cases they maintain different references. Unless you use the static keyword. Static properties are shared between all instances of the class, and should be used carefully, but that's what you're thinking of. What you're thinking of can be done like this
<?php
class stdClass1 {
private static $methods = [];
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind($this->methods[$method], $this, get_called_class()), $arguments);
}
public function __set($name, $value) {
if (is_callable($value)) {
$this->methods[$name] = $value;
} else {
parent::__set($name, $value);
}
}
}
Here we're using a static, defined property to hold all of the dynamic methods, and we're using the magic __set property to set the methods in to the array.
That being said, dynamically loading methods in to an object is bad. Don't do that
I have something like this:
class MyParent {
protected static $object;
protected static $db_fields;
public function delete() {
// delete stuff
}
public static function find_by_id($id = 0) {
global $database;
$result_array = self::find_by_sql("SELECT * FROM " . static::$table_name . " WHERE id=" . $database -> escape_value($id) . " LIMIT 1");
return !empty($result_array) ? array_shift($result_array) : false;
}
public static function find_by_sql($sql = "") {
global $database;
// Do Query
$result_set = $database -> query($sql);
// Get Results
$object_array = array();
while ($row = $database -> fetch_array($result_set)) {
$object_array[] = self::instantiate($row);
}
return $object_array;
}
private static function instantiate($record) {
$object = self::$object;
foreach ($record as $attribute => $value) {
if (self::has_attribute($attribute)) {
$object -> $attribute = $value;
}
}
return $object;
}
}
class TheChild extends MyParent {
protected static $db_fields = array('id', 'name');
protected static $table_name = "my_table";
function __construct() {
self::$object = new TheChild;
}
}
$child= TheChild::find_by_id($_GET['id']);
$child->delete();
I get this: Call to undefined method stdClass::delete() referring to the last line above. What step am I missing for proper inheritance?
You never actually instanciate the TheChild class, which should be done by
$var = new TheChild();
except in TheChild constructor itself.
So, the static $object field is never affected (at least in your example), so affecting a field to it (the line $object -> $attribute = $value; ) causes the creation of an stdClass object, as demonstrated in this interactive PHP shell session:
php > class Z { public static $object; }
php > Z::$object->toto = 5;
PHP Warning: Creating default object from empty value in php shell code on line 1
php > var_dump(Z::$object);
object(stdClass)#1 (1) {
["toto"]=>
int(5)
}
This object does not have a delete method.
And as said before, actually creating a TheChild instance will result in an infinite recursion.
What you want to do is this, probably:
class TheChild extends MyParent {
protected static $db_fields = array('id', 'name');
protected static $table_name = "my_table";
function __construct() {
self::$object = $this;
}
}
Edit: Your updated code shows a COMPLETE different Example:
class MyParent {
protected static $object;
public function delete() {
// delete stuff
}
}
class TheChild extends MyParent {
function __construct() {
self::$object = new TheChild;
}
}
$child = new TheChild;
$child->delete();
Calling "Child's" Constructor from within "Child's" Constructor will result in an infinite loop:
function __construct() {
self::$object = new TheChild; // will trigger __construct on the child, which in turn will create a new child, and so on.
}
Maybe - i dont know what you try to achieve - you are looking for:
function __construct() {
self::$object = new MyParent;
}
ALSO note, that the :: Notation is not just a different Version for -> - it is completely different. One is a Static access, the other is a access on an actual object instance!
We have a class that holds a public array called $saved that contains lots of data required to share between methods (example below)...
class Common {
public $saved = array();
public function setUser($data) {
$this->saved['user_data'] = $data;
}
public function getUserID() {
return $this->saved['user_data']['id'];
}
}
There are literally thousands of lines of code that work like this.
The problem is that new instance of classes that extend Common are being made within some methods so when they access $saved it does not hold the same data.
The solution is to make $saved a static variable, however I can't change all of the references to $this->saved so I want to try and keep the code identical but make it act static.
Here is my attempt to make $this->saved calls static...
class PropertyTest {
private $data = array();
public function __set($name, $value) {
$this->data[$name] = $value;
}
public function __get($name) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
return null;
}
public function __isset($name) {
return isset($this->data[$name]);
}
public function __unset($name) {
unset($this->data[$name]);
}
}
class Common {
public $saved;
private static $_instance;
public function __construct() {
$this->saved = self::getInstance();
}
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new PropertyTest();
self::$_instance->foo = array();
}
return self::$_instance->foo;
}
}
This doesn't quite work when setting a variable it doesn't seem to stay static (test case below)...
class Template extends Common {
public function __construct() {
parent::__construct();
$this->saved['user_data'] = array('name' => 'bob');
$user = new User();
}
}
class User extends Common {
public function __construct() {
parent::__construct();
$this->saved['user_data']['name'] .= " rocks!";
$this->saved['user_data']['id'] = array(400, 10, 20);
}
}
$tpl = new Template();
print_r($tpl->saved['user_data']);
$this->saved is empty when User gets initialized and doesn't seem to be the same variable, the final print_r only shows an array of name => bob.
Any ideas?
First of all, I have to say that, IMO, it is not that good to use an instance's property as a class's property ($saved is not declared as static but its value is shared with all instance).
Here is a working version http://codepad.org/8hj1MOCT, and here is the commented code. Basically, the trick is located in using both ArrayAccess interface and the singleton pattern.
class Accumulator implements ArrayAccess {
private $container = array();
private static $instance = null;
private function __construct() {
}
public function getInstance() {
if( self::$instance === null ) {
self::$instance = new self();
}
return self::$instance;
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
class Common {
public $saved = null;
public function __construct() {
// initialize the "saved" object's property with the singleton
// that variable can be used with the array syntax thanks to the ArrayAccess interface
// so you won't have to modify your actual code
// but also, since it's an object, this local "$this->saved" is a reference to the singleton object
// so any change made to "$this->saved" is in reality made into the Accumulator::$instance variable
$this->saved = Accumulator::getInstance();
}
public function setUser($data) {
$this->saved['user_data'] = $data;
}
public function getUser() {
return $this->saved['user_data'];
}
}
class Template extends Common {
// you can redeclare the variable or not. Since the property is inherited, IMO you should not redeclare it, but it works in both cases
// public $saved = null;
public function __construct() {
// maybe we can move this initialization in a method in the parent class and call that method here
$this->saved = Accumulator::getInstance();
}
}
I think there are a number of issues with this implementation that could well come back to bite you. However, in your current implementation your contructing a new instance (albeit through a static call) every time.
Instead use getInstance() as your singleton hook, and make your __construct private, as you'll only be accessing it from with the context of the Common class.
Like so:
class Common {
public $saved;
private static $_instance;
private function __construct() {
}
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new self();
... any other modifications you want to make ....
}
return self::$_instance;
}
}
And don't ever run parent::_construct(), instead always use the getInstance() method.
You might also want to ditch the idea of extending this singleton class. This is really a bad antipattern and could cost you a number of issues in the long run. Instead just maintain a Common class that other classes can read / write to. As its a singleton you don't need to worry about injection.
I seem to have solved the problem, by making $this->saved a reference to a static variable it works...
class Common {
private static $savedData = array();
public $saved;
public function __construct() {
$this->saved =& self::$savedData;
}
}
I have this code running on PHP 5.2.6
class Singleton {
static private $instance = false;
private $id = false;
protected function __construct() {
$this->id = uniqid();
}
static public function instance() {
if (!self :: $instance) {
self :: $instance = new self();
}
return self :: $instance;
}
public function get_id() {
return $this->id;
}
}
class Chucknorris extends Singleton {
}
echo "php version = ".phpversion()."<br>";
$singleton = Singleton::instance();
echo get_class($singleton)."<br>";
echo "singleton id = ".$singleton->get_id()."<br>";
$chucknorris = Chucknorris::instance();
echo get_class($chucknorris)."<br>";
echo "chucknorris id = ".$chucknorris->get_id()."<br>";
Here's the output
php version = 5.2.6
Singleton
singleton id = 4ea7dca7d8f23
Singleton
chucknorris id = 4ea7dca7d8f23
When I ask for an instance of Chucknorris, I always get the Singleton one. I'd like to find out a way to extend the Singleton.
I know we can use get_called_class method to do it but it comes only with PHP 5.3. Is there anyway I can extend a Singleton without redefining the design pattern in the extended classes ?
Your best bet in PHP < 5.3 is to use a Singleton Factory:
class Singleton
{
private $id = false;
public function __construct() {
$this->id = uniqid();
}
public function get_id() {
return $this->id;
}
}
class SingletonFactory
{
private static $instance_array = array();
public static function getInstance($class_name)
{
if (!isset(self::$instance_array[$class_name]))
{
self::$instance_array[$class_name] = new $class_name();
}
return self::$instance_array[$class_name];
}
}
class Chucknorris extends Singleton {}
$singleton = SingletonFactory::getInstance('Singleton');
echo get_class($singleton)."<br>";
echo "singleton id = ".$singleton->get_id()."<br>";
$chucknorris = SingletonFactory::getInstance('Chucknorris');
echo get_class($chucknorris)."<br>";
echo "chucknorris id = ".$chucknorris->get_id()."<br>";
The only downside here is that your Singleton constructor is public.. so that's a basic violation of that pattern.
Update:
Here's a version that removes the public constructor (warning: this is getting into messy/hacky/poor design territory)
class Singleton
{
private $id = false;
public function __construct() {
$back = debug_backtrace(false);
if (!isset($back[1]['class']) || $back[1]['class'] != 'SingletonFactory')
{
throw new Exception('Consturctor not available, use SingletonFactory::getInstance("CLASSNAME")');
}
$this->id = uniqid();
}
public function get_id() {
return $this->id;
}
}
class SingletonFactory
{
private static $instance_array = array();
public static function getInstance($class_name)
{
if (!isset(self::$instance_array[$class_name]))
{
self::$instance_array[$class_name] = new $class_name($class_name);
}
return self::$instance_array[$class_name];
}
}
class Chucknorris extends Singleton {}
$singleton = SingletonFactory::getInstance('Singleton');
echo get_class($singleton)."<br>";
echo "singleton id = ".$singleton->get_id()."<br>";
$chucknorris = SingletonFactory::getInstance('Chucknorris');
echo get_class($chucknorris)."<br>";
echo "chucknorris id = ".$chucknorris->get_id()."<br>";
$badchuck = new Chucknorris(); // Exception!
Why don't you simulate the get_class_function if it doesn't exist with 5.3 PHP version ?
This code may answer your question.
if (!function_exists('get_called_class')) {
function get_called_class() {
$bt = debug_backtrace();
$lines = file($bt[1]['file']);
preg_match(
'/([a-zA-Z0-9\_]+)::'.$bt[1]['function'].'/',
$lines[$bt[1]['line']-1],
$matches
);
return $matches[1];
}
}
abstract class Singleton {
private $id = false;
protected function __construct() {
$this->id = uniqid();
}
static public function instance() {
static $instances = array();
$called_class_name = get_called_class();
if (!isset($instances[$called_class_name])) {
$instances[$called_class_name] = new $called_class_name();
}
return $instances[$called_class_name];
}
public function get_id() {
return $this->id;
}
}
class Chucknorris extends Singleton {}
class Brucelee extends Singleton {}
echo "php version = ".phpversion()."<br>";
$chucknorris = Chucknorris::instance();
echo get_class($chucknorris)."<br>";
echo "chucknorris id = ".$chucknorris->get_id()."<br>";
$brucelee = Brucelee::instance();
echo get_class($brucelee)."<br>";
echo "brucelee id = ".$brucelee->get_id()."<br>";
You can redefine just the getinstance method (and the instance itself) in Chucknorris to get an instance of it instead of the parent, but I'm not exactly sure what your end goal is. Just change the extending class to:
class Chucknorris extends Singleton {
static private $instance = false;
static public function instance()
{
if (!self :: $instance) {
self :: $instance = new self();
}
return self :: $instance;
}
}
Is this what you want? And if so - what is the reason you want it? I could think of a few, but would be glad if you share you goal.
Your code will most likely work if you move static private $instance = false; to the subclass and make it protected instead of private.
You also need to replace self:: with static:: so the static var is set in the subclass.
This requires PHP 5.3 - however, this shouldn't be a problem because PHP 5.2 reached end-of-life/support (that includes security updates!) as of january 2011!
Why does the following code print "1,1,1," instead of "4,5,6,"?
class MyClass {
// singleton instance
private static $instance = 3;
function __construct() {
$instance++;
echo $instance . ",";
}
}
for($i = 0; $i < 3; $i++) {
$obj = new MyClass();
}
$instance is a local variable, not a static class property. Unlike Java you always must access variables, or properties in theire scope
$var; // local variable
$this->var; // object property
self::$var; // class property
I just saw
// singleton instance
The singleton pattern is usually implemented different
class SingletonClass {
protected $instance = null;
protected $var = 3;
protected __construct () {}
protected __clone() {}
public static function getInstance () {
if (is_null(self::$instance)) { self::$instance = new self(); }
return self::$instance;
}
public function doSomething () {
$this->var++;
echo $this->var;
}
}
$a = SingletonClass::getInstance();
$a->doSomething();
The singleton pattern ensures, that you always interact with exactly one instance of a class.
In your constructor, $instance is not yet defined. You must use:
self::$instance++;
echo self::$instance . ",";
to reference the static property of your class.