Behaviour of private constructor in singleton design pattern in PHP - php

Consider this example singleton class:
class Model_Acl
{
protected static $_instance;
private function __construct($a) {
echo $a;
}
public static function getInstance()
{
if(!isset(self::$_instance)) {
self::$_instance = new Model_Acl('hello world');
}
return self::$_instance;
}
}
In the static method of the same class, I am able to initialize the class to which constructor is private. Does that mean the scope of class initialization becomes local when trying to instantiate object within the class?
I will appreciate if someone could explain the behaviour of PHP when it comes to class instantiation with reference to access modifiers.

You can only initialize it thru Model_Acl::getinstance().
But yes, it will work.
Singleton is not considered a good practice, you should consider Dependency Injection.
http://fabien.potencier.org/article/11/what-is-dependency-injection.
More information about php Singletons
Best practice on PHP singleton classes

Related

Pass Dependency Injection Container to static method

I have some Legacy classes.
Many classes are instantiated using a Factory Class.
There is also a Singleton-Class.
In the future I want to replace them completely with the DIC.
For the moment the codebase is to large to do this.
Now my goal is to inject the DI-Container into every Service instantiated by the Singleton class.
The Singleton class has a static method with this signature.
final class Singleton
{
private static $singletonCache = array();
public static function getInstance($namespace, $className)
{
}
}
inside of this function I want to check for:
$instance = new $className();
if($instance instanceof ContainerAwareInterface)
{
// TODO: how do we get the container here
$instance->setContainer($container);
}
But how can I best get the container inside of my "singleton class", which is only called statically?
Another approach is to access the container globally when you need it:
public static function getInstance($namespace, $className)
{
$container = $_GLOBAL['kernel']->getContainer();
}
Of course there are sorts of things wrong with this approach but as long as you are transitioning then it's enough to get by.
Somewhere early in your bootstrapping code, but after the container is instantiated, you can pass the container to your singleton class:
Singleton::setContainer($container);
It would store the container in a static property:
final class Singleton
{
// ...
private static $container;
public static function setContainer(ContainerInterface $container)
{
self::$container = $container;
}
}
However, as you learned on the example of your singleton class, all global state gives you is headaches. Passing the container around (and using ContainerAware) is something to avoid. By passing the container to your services you're making them rely on the whole world of services. It's cleaner to only pass collaborator you actually need. It's also far easier to test.
Another solution is presented in this answer, which is pretty much the same as the other answer to this question, just using the global keyword instead of the $_GLOBAL array to access the kernel object.
public static function getInstance($namespace, $className)
{
global $kernel;
$container = $kernel->getContainer();
}

PHP singletons and inheritance

I'm having some trouble with PHP Inheritance. Here's deal:
I have this base class, Singleton:
namespace My_Namespace;
abstract class Singleton {
protected static $instance = null;
static function get() {
if ( null == static::$instance ) {
static::$instance = new static;
}
return static::$instance;
}
private function __construct() {
}
}
I have a bunch of classes inheriting that Singleton class, call them A,B,C,D. One of them looks like this:
namespace My_Namespace;
class A extends Singleton {
protected function __construct() {
B::get();
if ( some_condition() ) {
C::get();
}
else {
D::get();
}
}
}
Now, I just do a A::get() to get it all rolling. The constructor of A is called, as expected. Then, the constructor of B is called, again without a problem. Now it gets weird. Once C::get() is called, it recognizes static::$instance as already an object of class B and doesn't instantiate C at all. I know if I kinda daisy-chain them, that is __construct of B calls C::get or D::get it works but that's not optimal for my purposes. Is that caused by them being in the same scope? If so, is there any way around this? I'm asking this more of curiosity rather than practical purpose - I know I can just as easily implement the singleton pattern in each one of them. So, any ideas? Thanks!
P.S. Please no 'singletons are evil and you should burn in hell' comments. I know that perfectly well.
Note that static::$instance = new static calls the constructor of (in your case) A.
With your solution, you will need a static property for your instance in your subclasses.
Just add
protected static $instance = null;
to them, and it should work fine.
When dealing with static properties if you want the inherited classes's static properties to differ from the base classes you have to provide a home for it to live in.
To solve the problem just define
protected static $instance = null;
on your derived class. If not it will use the base class' property.

PHP class reference

To be clear, I don't want to instantiate the same class multiple times. I only want to instantiate it once, and keep track of any changes made to that instance via some reference. Is this possible, and if so how can it be done? Thanks!
You can use the Singleton pattern for this. The PHP manual has a good example and description:
The Singleton ensures that there can be only one instance of a Class and provides a global access point to that instance.
Class:
<?php
class Example
{
private static $instance;
private function __construct() {
}
public static function singleton() {
if (!isset(self::$instance)) {
echo 'Creating new instance.';
$className = __CLASS__;
self::$instance = new $className;
}
return self::$instance;
}
public function __clone() {
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
public function __wakeup() {
trigger_error('Unserializing is not allowed.', E_USER_ERROR);
}
}
Usage:
$singleton = Example::singleton();
It is worth also noting these objections to the singleton pattern from the PHP manual:
The Singleton pattern is one of the more controversial patterns. Critics argue that
Singletons introduce Global State into an application and tightly
couple the Singleton and its consuming classes. This leads to hidden
dependencies and unexpected side-effects, which in turn leads to code
that is harder to test and maintain.
Critics further argue that it is pointless to use a Singleton in a
Shared Nothing Architecture like PHP where objects are unique within
the Request only anyways. It is easier and cleaner to create
collaborator object graphs by using Builders and Factory patterns once
at the beginning of the Request.
Singletons also violate several of the "SOLID" OOP design principles
and the Law of Demeter. Singletons cannot be serialized. They cannot
be subtyped (before PHP 5.3) and won't be Garbage Collected because of
the instance being stored as a static attribute of the Singleton.
See as well: Who needs singletons?
You can create Singleton pattern
class Something {
private static $instance;
private function __construct() {
}
public static function getInstance() {
if(Something::$instance == null) {
Something::$instance = new Something();
}
return Something::$instance;
}
public function someMethod() {
return "abc";
}
}
When you want to use it you call Something::getInstance()->someMethod()
Read more about singleton pattern.
To be clear, I don't want to instantiate the same class multiple times. I only want to instantiate it once, and keep track of any changes made to that instance via some reference. Is this possible, and if so how can it be done? Thanks!
Sure this is possible. You can do this literally:
First of all, as you don't want to instantiate the class multiple times, just instantiate it once:
$instance = new Class();
Then you want to keep track of changes made to that instance. I don't specifically know what you mean. Probably you mean to only keep that one instance. You just can do so, as you have only instantiated once, you can refer to that instance with the $instance variable.
Additionally you can "reference" that $instance as well in some other variable:
$reference = $instance;
You can now access the single instance of Class with the $instance and the $reference variable.
If you need to monitor the instance, I suggest you create a Decorator that does the job:
$decorator = new ClassDecorator(new Class());
The decorator can then work as an interceptor before anything reaches Class.
To find out if the inner state of a class has changed or not, you can make use of the serialize and unserialize functions as well:
$instance = new Class();
$snapshot = serialize($instance);
...
# more code, $instance is changed or not, we don't know
...
$changed = $snapshot != serialize($instance);
Hope this is helpful.
What you are trying to do is called the Singleton Pattern .. See http://en.wikipedia.org/wiki/Singleton_pattern

Clarification on Singleton Pattern

I'm writing some utility classes for a PHP app and a lot of them will be singletons. Found myself re-writing the same code over and over, and decided to make an abstract base class Singleton and subclass it. Just want to make sure I've done this correctly!
abstract class Singleton
{
private static $instance = NULL;
public static final function getInstance()
{
if(self::$instance == NULL)
self::$instance = instantiate();
return self::$instance;
}
protected abstract static function instantiate();
}
class LogHelper extends Singleton
{
protected static final function instantiate()
{
return new LogHelper();
}
}
Now, if I have done this correctly, I can call LogHelper $LOGGER = LogHelper::getInstance() from anywhere in my codebase, and get a reference to the same instance every time, yes?
You will probably need to define your getInstance() methods as static so that you can access them without having to instantiate the class. Then, you'll use this:
$objSingleton = LogHelper::getInstance();
And, you'll probably want to define a private constructor:
private function __construct() { }
While singletons seem like the ideal solution at first, they are not. Learn about registries and dependecy injection; they will make your life easier when you start unit testing.

How do I instantiate my database object, to be used in other classes?

I've encountered an architectural issue with my application. I've rolled my own (very basic) MVC, and one of my models is a database object: class MySQLDatabase { }
There's a number of places in which I'd want to use my database object, without creating duplicate instances. Inside my controller, I have declared public $db; and within the __construct { } I have $this->db = new MySQLDatabase;
Question:
How do I use $db within my other classes--they're all instantiated within the controller's __construct { } as well... would I declare global $db at the top of all my classes that require database connectivity?
I'm used to global variables being declared in the global scope as regular variables, and then using the global keyword to reference the global scope... I'm not sure if that applies to variables declared within a class (my controller.)
I would stay away from using globals or the Singleton pattern (which is essentially a global anyway), and try and find some alternatives. Additionally you are talking about a database connection, by using the Singleton pattern you are saying that there will never be more than one database connection, whilst that is generally true in smaller applications, as they grow larger you won't be able to accomodate multiple connections.
Once you make something global then you lose the automatic contraints of where it can be used/modified. Using MVC a view shouldn't be used for anything other than to display data, by using a global/singleton it is up to the developer to not make use of the globals. Whereas with a different design they don't have that option.
You mentioned you've created your own MVC framework, so I imagine the classes you want to use it in are your models? Correct me if they are anywhere else.
If your models extend from a common base class then you could pass your database object to that class as a static variable which can be assigned to any new instances in the construct or using a factory method in the factory method.
This isn't to say that globals or singletons should be avoided at all costs, but definitely try consider the alternatives that could lead to a neater design.
Here's some reading on the Singleton pattern if you're interested:
Patterns I Hate #1: Singleton
Why Singletons are Evil
Singleton Considered Stupid
Use your singletons wisely
There are many more out there...
If I understand correctly you have a single controller that instantiates the database object and it also takes care of instantiating other classes. If so, you could implement some form of dependency injection either passing the db object in the constructor of the other classes or creating a setter method.
A good blog article on the subject:
http://www.potstuck.com/2009/01/08/php-dependency-injection/
I Think you going about this the wrong way, you should not be performaing quesries to the database from you controller.
this means that the below is invalid.
class ControllerIndex extends Controller
{
public function index()
{
$this->db->selectAll("table");
}
}
There should be a layer that separates your controller from your database interface, this is where a Model comes in.
You should have a models folder that contain classes for actions taken such as users,posts,logging etc.
class Users_Model extends Model
{
public function getUser($id)
{
}
}
The model class should be part of your system core, and should extend your Database Class, this way within your main controller you should be loading the models via the ModelLoader class.
for example:
class ModelLoader
{
private $models = array();
public function __get($model)
{
//load (/application/models/?.php) and initiate it here
//Storing it in models array above
}
}
Then in your main controller:
class Controller
{
private $model;
public function __construct()
{
$this->model = new ModelLoader;
}
}
this way your bringing your loader into scope for the child controller:
class Controller_index extends Controller
{
public function index()
{
$user = $this->model->users->getUser(22);
}
}
Hope this helps!
I think what you need here is a singleton for you Database object :)
See here for more details : http://en.wikipedia.org/wiki/Singleton_pattern
Edit with sample singleton for php :
<?php
class UniqueObject {
private $_uniq = null;
//private cause you don't want to instanciate the classic way
private function __construct() {
//...
}
//check if unique object exists or not, then return it
public static function uniq() {
if(!self::$_uniq)
self::$_uniq = new UniqueObject();
return self::$_uniq;
}
}
//call your unique object whenever you need it
UniqueObject::uniq();
?>
(it's late, i hope i didn't do any mistake :))
Don't use singletons. It's much better to explicitly pass around data. For example:
abstract class Controller {
private static $conn; // could be an array for multiple connections
final protected function getDBConnection() {
if (!$this->conn) {
$this->conn = new DBConnection();
}
return $this->conn;
}
abstract public function process(Request $r);
}
class HomePageController extends Controller {
public function process(Request $r) {
$results = $this->getDBConnection()->query('SELECT stuff FROM foo;');
// do stuff with $results
}
}
You could also have an explicit model object you pass around, e.g. the one that represents the user, but that may be overkill for your project.
You'll need to use a singleton pattern. They give examples in the php docs
<?php
class Example
{
// Hold an instance of the class
private static $instance;
// A private constructor; prevents direct creation of object
private function __construct()
{
echo 'I am constructed';
}
// The singleton method
public static function singleton()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
// Example method
public function bark()
{
echo 'Woof!';
}
// Prevent users to clone the instance
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
}
?>
http://php.net/manual/en/language.oop5.patterns.php

Categories