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.
Related
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();
}
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.
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
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
I'm thinking about putting every class into a separate file and doing the static initialization outside of the class definition.
The problem with this is the fact that the initialization will happen before the said class is actually needed (it will happen when the file which contains the class is included for the first time). It's a problem, because it may happen that the class won't be used at all, hence the initialization is unnecessary. And I think the practice of including the used files not in the beginning of your code is simply a dirty technique.
If anybody has a viable solution to this problem, I would greatly appreciate it.
Take a look at code like this. It uses a singleton instance of the database yet you can still create instances of the class:
class DB
{
private static $_db;
public static function pdo()
{
if (is_null(self::$_db))
{
self::$_db=new PDO('pgsql:host=localhost;
port=5432;
dbname=testdb;
user=postgres;
password=abc123');
self::$_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$_db;
}
public static function factory()
{
return new self;
}
private function __construct() {}
When you want an instance of the DB, you do DB::factory(). DB uses self::pdo() to return the singleton to access the database.
The singleton answer has a problem here... if you use the static methods before an instance of the class is created... then the static initializer has not run... so you might say that one can run the initializer in every static method... that is true but what about just getting and setting static variables... I suppose one could also use __get and __set magic functions for this... there is just a problem with the language in that it does not support static intialization... here is the way it can be done...
in file Hi.php:
class Hi{
public static $v = "0";
}
Hi::$v= "2";
// end of Hi.php file
simple use a .php file per class... and do the static initialization outside of the
class definition...
You might look up for __autoload when a class is not found it is called and supposed to include the file contains that class. You can put static initializers in that function.
Use singleton pattern instead of static calls:
<?php
class Foo {
protected static $_instance;
/**
* #return Foo
*/
public static function getInstance() {
return (null === self::$_instance)?
self::$_instance :
self::$_instance = new Foo();
}
protected function __construct() {
// initialize here
}
}
This is the way I do this. The only cost is testing the $doneinit static member each time you construct.
class Foo {
private static $doneinit=false;
private static init() {
// do static initialisation here
self::$doneinit=true;
}
public function __construct() {
if (!self::$doneinit) self::init();
// go on to do the rest of construction
}
}
Simple, and it keeps everything within the class.