3

It's a basic website. Based off answers on here, I'm doing this:

private $db;

public function __construct($id = null) {
    $this->db = Db::getInstance(); //singleton from the Db class

But if there is a static method, I can't use the object specific variable.

Is there anything better than having to manually specify the db variable inside the static method?

public static function someFunction($theID){
    $db = Db::getInstance();

EDIT: Making the variable static doesn't solve the problem. Access to undeclared static property. I'd still have to assign the variable within the static function. The question is asking if there's a way around this.

My DB Class (although not important to this discussion):

class Db {
private static $m_pInstance;
private function __construct() { ... }

public static function getInstance(){
    if (!self::$m_pInstance)
        self::$m_pInstance = new Db();
    return self::$m_pInstance;
}

}

Marcus
  • 9,011
  • 10
  • 45
  • 65
  • 1
    Is there some reason you can't just use `$this->db` inside of `someFunction`? Why else are you setting it in the constructor? – DaOgre Dec 28 '11 at 00:17
  • Just don't use static methods – zerkms Dec 28 '11 at 00:18
  • @DaOgre: `someFunction` is static – zerkms Dec 28 '11 at 00:19
  • Fyi: Db::getInstance() isn't a singleton, it's a factory, which returns a singleton. – Mike Purcell Dec 28 '11 at 00:25
  • @Digital Precision: why do you think so? Also, there are **2 different** factory patterns. Which one you mentioned about? – zerkms Dec 28 '11 at 00:30
  • @zerkms: Not sure I understand what you are asking. `Db::getInstance()` is a factory (wrapper method) for returning singleton, `Db::$db` is the actual singleton. – Mike Purcell Dec 28 '11 at 00:34
  • @Digital Precision: nope. `Db::getInstance()` is a classic singleton implementation, which returns class **instance**. `Db::$db` is an *instance* of `DB` class. Fyi: http://en.wikipedia.org/wiki/Singleton_pattern#Lazy_initialization + http://en.wikipedia.org/wiki/Factory_method_pattern + http://en.wikipedia.org/wiki/Abstract_factory_pattern – zerkms Dec 28 '11 at 00:35
  • @zerkms: If you were to pass an argument to getInstance(), say a hash, for allowing more db connections to be accessed via singleton array, then getInstance() is a factory pattern, in that it returns an object based on some preset attributes. – Mike Purcell Dec 28 '11 at 00:40
  • @Digital Precision: if-if-if. Please read wikipedia articles **carefully**. There are **a lot** of differences between singleton and factories, and arguments is not a sufficient criteria. But it is good that at this point you've already changed your point of view about what factories generally return :-) – zerkms Dec 28 '11 at 00:41
  • Please excuse the title - it's the Singleton Design Pattern, I believe we're wondering if the class "Db" is a Factory or not... I don't know. – Marcus Dec 28 '11 at 00:42
  • @Igor K: it is not ;-) You've implemented singleton pattern in its common way. Factories (abstract one and factory method) are not even near here – zerkms Dec 28 '11 at 00:43
  • @zerkms: Sorry friend, rather not blow this out into a 15 response long conversation. – Mike Purcell Dec 28 '11 at 00:53
  • @Digital Precision: it would be a little difficult to blow it out as long as you are trying to argue against **definitions**. As I mentioned above - please read wikipedia articles – zerkms Dec 28 '11 at 00:54
  • @IgorK: Can you post your class definitions as well? – Mike Purcell Dec 28 '11 at 01:02
  • @Digital Precision: ooooooh, dude, factory always implemented as a method of a particular object, singleton - is a static method of a class. Please, read the articles ;-) So `Db::getInstance()` cannot be factory design pattern implementation **by definition** ;-) – zerkms Dec 28 '11 at 01:26
  • @Digital Precision: posted what my Db class looks like – Marcus Dec 28 '11 at 01:37
  • @IgorK: I meant the class definition(s) for the constructor and the someFunction(), just wanted to make sure they were within the same class. – Mike Purcell Dec 28 '11 at 01:45
  • @Digital Precision: yes they are in the same class – Marcus Dec 28 '11 at 11:29

2 Answers2

5

Yes, you can make the $db static:

static private $db;

I'm assuming that's what you need, since you're accessing it from a static method. If there's any reason why you wouldn't want this, that must mean that the method probably shouldn't be static.

EDIT:

As per @zerkms (thanks) comments, you access static variables with self:::

self::$db = Db::getInstance(); 
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 1
    ... but this will throw strict standards violation notification. Not cool – zerkms Dec 28 '11 at 00:21
  • @zerkms I'm not sure I understand what you mean. Are you saying statics are bad? statics are part of OO design. – Luchian Grigore Dec 28 '11 at 00:23
  • http://ideone.com/aupWx. Java, c#, etc will not allow you to access to the static variables using `this` instance reference. PHP does, but it doesn't mean it has something to do with *good OO design* – zerkms Dec 28 '11 at 00:26
  • @zerkms you're right, I didn't know that. I edited my answer. – Luchian Grigore Dec 28 '11 at 00:30
  • @zerkms I now see it's only a matter of syntax and nothing to do with the use of static. – Luchian Grigore Dec 28 '11 at 00:30
  • indeed it is a matter of syntax *and* good OO design. 1. It is static (which means tight coupling) 2. It is a singleton (which means tight coupling x2) – zerkms Dec 28 '11 at 00:33
  • @zerkms I'm not a big fan of always/never rules. You should decide what you need and want on a case-to-case basis. – Luchian Grigore Dec 28 '11 at 00:34
  • Thanks, if I make it a static variable (which did cross my mind), can instances of the class still access it? Is it wise to do this or is there a much better way? Excuse my ignorance, PHP isn't my main programming language. – Marcus Dec 28 '11 at 00:36
  • @Luchian Grigore: sure, I didn't even try to say "never" :-) But as usual there always are better ways to implement your logic rather than using static instance retrieved from singleton – zerkms Dec 28 '11 at 00:38
  • The answer to the first question is yes, you can access statics from instances. For the second question - it has nothing to do with PHP imho, the concept of `static` is the same across OO languages. If you have a variable that is logically connected to the class and is the same for all instances, I see no reason not to make it `static`. – Luchian Grigore Dec 28 '11 at 00:38
  • 1
    @zerkms "static instance retrieved from singleton" makes it sound so dirty :)). I now feel I'm in the dark side of OOP. – Luchian Grigore Dec 28 '11 at 00:39
  • 1
    Problem: the $db variable is assigned in the constructor which isn't called when the static method is called. So how do I deal with this? "Access to undeclared static property" – Marcus Dec 28 '11 at 00:44
  • @Luchian Grigore: php doesn't allow to do so. Hehe, you see - that's why it is bad :-) – zerkms Dec 28 '11 at 00:47
  • How about: `if ( $db == null ) $db = Db::getInstance();`? – Luchian Grigore Dec 28 '11 at 00:48
  • @Luchian Grigore: this means putting this code inside all static functions, so I'm no better off than defining a local $db variable inside my static methods, unless I'm missing something? – Marcus Dec 28 '11 at 00:50
  • @Igor K: you're absolutely right - you need to copy-paste it everywhere or retrieve an instance by singleton in each method (and that's why I mentioned avoiding static methods and variables) – zerkms Dec 28 '11 at 00:51
  • Dammit. It appears in this case statics are bad. :) Do you need to make $db a member. Why not directly use the singleton instance? – Luchian Grigore Dec 28 '11 at 00:55
  • Most methods aren't static. It seemed a lot easier to always have a db instance there as it was automatically created by the constructor. The only way to avoid the static methods is to build another class that returns instances of this class. Seems a waste when the only issue an extra line to set the db variable up! – Marcus Dec 28 '11 at 00:58
3

You've outlined one of the issues you come across when co-mingling static methods within a class that can also be insantiated, setting member variables via instantiation, and expecting access to their values through static method call. The only real way around this is to set a class specific singleton for the database singleton, or pass in the DB object to the static method.

// Option 1
class MyClass
{
    private static $db;

    public function __construct($id = null)
    {
        self::$db = Db::getInstance(); //singleton from the Db class
    }

    public static function someFunction($theID)
    {
        self::$db->query('SELECT * FROM my_table');    
    }
}

// Singleton DB for MyClass will be initalized via constructor
$myClass = new MyClass();    

// This call will have access to DB object set via class specific singleton
MyClass::someFunction(4);

// Option 2
class MyClass
{
    private $db;

    public function __construct($id = null)
    {
        $this->$db = Db::getInstance(); //singleton from the Db class

        if (!is_null($id)) {
            $this->id = $id;
        }
    }

    public function getDb()
    {
        return $this->db;
    }

    public function getId()
    {
        return $this->id;
    }

    // Sub-Option 1: If Id ISNT set via object
    public static function someFunction($object, $theID)
    {
        $object->getDb()->query('SELECT * FROM my_table WHERE id = ' . (int) $theID);    
    }

    // Sub-Option 2: If Id IS set via object
    public static function someFunction($object)
    {
        $object->getDb()->query('SELECT * FROM my_table WHERE id = ' . (int) $object->getId());    
    }
}

// Sub-Option 1 call
$myClass = new MyClass();

MyClass::someFunction($myClass, 4);

// Sub-Option 2 call
$myClass = new MyClass(4);

MyClass::someFunction($myclass);
Mike Purcell
  • 19,847
  • 10
  • 52
  • 89
  • Since most of the time you are calling a static method from an instantiated class, passing the DB object to the static method is a great way to not have to deal with the waterfall of issues trying to declare the DB singleton class as static. In my case this answer worked perfectly and didn't make a mess. – Mike May 13 '19 at 15:08