Strange behavior during access to private attribute - php

I wrote next singleton.
class Singleton {
// object instance
private static $instance;
private function __construct() {}
private function __clone() {}
private $val = 'he';
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new Singleton();
self::$instance->val .= 'llo';
}
return self::$instance;
}
public function doAction() {
echo $this->val;
}
}
Singleton::getInstance()->doAction();
When I get instance of it I add 'llo' to private attribute val. And see 'hello' instead of 'he', why I can access to the private method?

Access to private scope if for the whole class. It is not limited by private/protected/public methods. So you can access any private member from within the class but not from outside the class.
private scope when you want your variable/function to be visible in its own class only.
protected scope when you want to make your variable/function visible in all classes that extend current class including the parent class.
public scope to make that variable/function available from anywhere, other classes and instances of the object.
You can read detail from http://php.net/manual/en/language.oop5.visibility.php
class Singleton {
// object instance
private static $instance;
private function __construct() {}
private function __clone() {}
private $val = 'he';
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new Singleton();
self::$instance->val .= 'llo'; // Inside the same class you can access private variable
}
return self::$instance;
}
public function doAction() {
echo $this->val;
}
}
Singleton::getInstance()->doAction();
echo Singleton::getInstance()->val; // can't access

Related

Using a trait constructor

I have a class which I am going to make Singleton. For this reason, I want to use a trait. Something like this:
trait TSingleton
{
private static $instance = null;
private function __construct() {}
private function __clone() {}
private function __wakeup() {}
/**
* #return static
*/
public static function getInstance()
{
if (is_null(static::$instance)) {
static::$instance = new static();
}
return static::$instance;
}
}
And then:
class Db implements IDb
{
use TSingleton;
...
}
The question is if the Db's constructor would be private also, and if it does, why could I create its inheritors?

How to use singleton and static late binding toghter in php father class

I am trying to create a father class like:
class CommonModel extends \Common\Model\CommonModel {
protected static $_instance = null; //singleton
/**
* singleton
* #return null|static
* #Date 2017/4/7 14:06
*/
public static function instance(){
if(!isset(self::$_instance)){
self::$_instance = new static();
}
return self::$_instance;
}
UserModel and UserThirdModel implements the CommonModel
when I create
$user = UserModel::instance(); //an user instance is created
$userThird = UserThirdModel::instance();// userModel retured.
I see the problem comes from "if(!isset(self::$_instance)){"
my question is how to create different instance based on different model
Thanks
You could turn $_instance into an array of instances by class name using get_called_class() http://php.net/manual/en/function.get-called-class.php
First change
protected static $_instance = array(); //singleton array
and then use self::$_instance[get_called_class()] such as...
public static function instance(){
if(!isset(self::$_instance[get_called_class()])){
self::$_instance[get_called_class()] = new static();
}
return self::$_instance[get_called_class()];
}

How to subclass a Singleton in PHP?

I'm trying to subclass a class that uses singleton pattern and populate the instance with the subclass.
I seem to be having a little trouble.
class Singleton {
static private $instance;
static public function instance(){
if(is_null(self::$instance)){
self::$instance = new self();
}
return self::$instance;
}
private function __construct(){}
}
class MySingleton extends Singleton {
}
echo get_class(MySingleton::instance()); //=> Singleton
//=> I'm hoping to see MySingleton
What you're looking for is late static binding which is a new feature of PHP 5.3. Try replacing new self() with new static() and this should work for you.
self always references the containing class, whereas static references the "called" class.
Your singleton base class prevents that as is. if you change the code to this though, it will work.
<?php
class Singleton {
static private $instances = array();
static public function instance(){
$class = get_called_class();
if(!isset(self::$instances[$class])){
self::$instances[$class] = new $class();
}
return self::$instances[$class];
}
private function __construct(){}
}
class MySingleton extends Singleton {
}
echo get_class(MySingleton::instance()); //=> MySingleton
Now it works because Singleton allows for one instance per child class.
this works
<?php
class Singleton {
static private $instance;
static public function instance(){
static $instance = null;
return $instance ?: $instance = new static;
}
public function __construct(){}
}
class MySingleton extends Singleton {
}
But i recommend the following one:
<?php
class Singleton {
static protected $instance; //should not be private
static public function instance(){
if(is_null(static::$instance)){
static::$instance = new static();
}
return static::$instance;
}
public function __construct(){}
}
class MySingleton extends Singleton {
static protected $instance; //must explicitly declared
}

Static methods and Singleton in PHP

I have the next class:
class MyClass {
private $_instance = null;
private function __clone() {}
private function __construct() {}
public static function instance()
{
if (is_null(self::$_instance)) {
self::$_instance = new self;
}
return self::$_instance;
}
public static function methodOne() {}
public static function methodTwo() {}
public static function methodThree() {}
public static function methodFour() {}
}
And I have a lot of methods method...(). But this methods can be executable only if instance is not null. How can I throw an exception if instance is null?
I need to use only static methods. I can not use non-static. I want to use the next design:
MyClass::instance();
MyClass::methodOne(); // If no instance throws an Exception.
Do not make the methods static, only keep instance() static.
It will lead to:
$m = MyClass::instance();
$m->methodOne();

Building a Singleton Trait with PHP 5.4

We recently had a discussion if it was possible to build a trait Singleton PHP Traits and we played around with it a possible Implementation but ran into issues with building one.
This is an academic exercise. I know that Singletons have very little - if not to say no - use in PHP and that one should 'just create one' but just for exploring the possibilities of traits:
<?php
trait Singleton
{
protected static $instance;
final public static function getInstance()
{
return isset(static::$instance)
? static::$instance
: static::$instance = new static;
}
final private function __construct() {
static::init();
}
protected function init() {}
final private function __wakeup() {}
final private function __clone() {}
}
class A {
use Singleton;
public function __construct() {
echo "Doesn't work out!";
}
}
$a = new A(); // Works fine
reproduce: http://codepad.viper-7.com/NmP0nZ
The question is: It is possible to create a Singleton Trait in PHP?
Quick solution we've found (thanks chat!):
If a trait and a class both define the same method, the one of class if used
So the Singleton trait only works if the class that uses it doesn't define a __construct()
Trait:
<?php
trait Singleton
{
protected static $instance;
final public static function getInstance()
{
return isset(static::$instance)
? static::$instance
: static::$instance = new static;
}
final private function __construct() {
$this->init();
}
protected function init() {}
final private function __wakeup() {}
final private function __clone() {}
}
Example for a consuming class:
<?php
class A {
use Singleton;
protected function init() {
$this->foo = 1;
echo "Hi!\n";
}
}
var_dump(A::getInstance());
new A();
The var_dump now produces the expected output:
Hi!
object(A)#1 (1) {
["foo"]=>
int(1)
}
and the new fails:
Fatal error: Call to private A::__construct() from invalid context in ...
Demo
I created one a while ago when i was bored trying to learn traits. It uses reflection and the __CLASS__ constant
Trait:
trait Singleton
{
private static $instance;
public static function getInstance()
{
if (!isset(self::$instance)) {
$reflection = new \ReflectionClass(__CLASS__);
self::$instance = $reflection->newInstanceArgs(func_get_args());
}
return self::$instance;
}
final private function __clone(){}
final private function __wakeup(){}
}
This way you can continue to use the __construct() method and don't need to use an arbitrary function as the constructor.
The thing is that the type of getInstance return will be ambigous since it depends on the consumer. This gives a weak-typed method signature.
For instance it makes it impossible to provide an #return in compliance with the consumer type in the getInstance method doc bloc.
This is guys all what you need.
If you wish you could use private static member, but there is no a real need...
Tested, works despite the fact that you might think that static will be global or something :)
trait Singleton
{
/**
* Singleton pattern implementation
* #return mixed
*/
public static function Instance()
{
static $instance = null;
if (is_null($instance)) {
$instance = new self();
}
return $instance;
}
}
usage:
class MyClass
{
use Singleton;
}
A bit late to the party, but I wanted to show how (in Eclipse Oxygen PDT at least) you can do the DocBlock where auto-completion will work for this
trait SingletonTrait{
/**
*
* #var self
*/
private static $Instance;
final private function __construct()
{
}
final private function __clone()
{
}
final private function __wakeup()
{
}
/**
*
* Arguments passed to getInstance are passed to init(),
* this only happens on instantiation
*
* #return self
*/
final public static function getInstance(){
if(!self::$Instance){
self::$Instance = new self;
self::$Instance->init();
}
return self::$Instance;
}
protected function init()
{
}
}
As you can see both $instance and getInstance are defined as self. Eclipse is smart enough to work this out so that when you use it in a class all the auto-completion works just as normal.
Test

Categories