3

I'm new to this level of PHP programming and I've been reading a post about singleton and static classes. I'm in the process of writing a class that would facilitate my DB connections.

I came across the following code by Jon Raphaelson (here) :

class ConnectionFactory
{
    private static $factory;
    public static function getFactory()
    {
        if (!self::$factory)
            self::$factory = new ConnectionFactory(...);
        return self::$factory;
    }

    private $db;

    public function getConnection() {
        if (!$this->db)                 // this line was modified due to comment
            $this->db = new PDO(...);       // this line was modified due to comment
        return $db;
    }
}

function getSomething()
{
    $conn = ConnectionFactory::getFactory()->getConnection();
    .
    .
    .
}

It seemed like I had found what I was looking for, however I have a few questions about it.

  1. self::$factory = new ConnectionFactory(...); - I don't see a constructor in this class. Do I just create this constructor and pass in the db details ('dbname', 'user', 'pass', etc)?
  2. the getSomething() function, I'm assuming that the intent was to put all of the actual functions that would retrieve data within the ConnectionFactory class - and this is the reason for this function being in this class. Otherwise, I would have expected this function to be within another class. [edit] SKIP THIS QUESTION, I didn't see the one bracket.
  3. What happens when two users are logged into the site and are requesting the DB connection (both are doing updates, etc)? Will it be an issue that this is a singleton?

Thanks!

Community
  • 1
  • 1
NEW2WEB
  • 503
  • 2
  • 8
  • 22
  • 2
    I'd advise against using this pattern at all. See [How Not To Kill Your Testability Using Statics](http://kunststube.net/static/) for why and alternatives. – deceze Nov 14 '12 at 15:18
  • @deceze Are you recommending people in PHP don't use the Factory pattern or the Singleton pattern? I'm not sure I get why these are intrinsically bad--they're pretty well vetted OOP patterns. – Ray Nov 14 '12 at 15:35
  • @Ray I'm advocating against the Singleton pattern, more specifically against statically calling `ConnectionFactory::getFactory()` anywhere. If you're using a factory, that factory should be injected as an object, not called statically. A singleton factory is pretty pointless. A factory should be used to give an object A the means to construct some other object B, without statically coupling A to B. Statically coupling A to factory B is therefore of no advantage. – deceze Nov 14 '12 at 15:43
  • @deceze how would you enforce not creating multiple copies of a factory or connection? Say for something with limited resources such as connections to a db where you or someone using your code may accidentally create more connections than available. True it's more testable methods and classes, but in doing so it's also more possible to create situations where your unit test happily work, but the entire code breaks fails when an expected connection is denied or times out due to resource contention. – Ray Nov 14 '12 at 15:53
  • @Ray Save the instantiated connection as a `static` property of the Factory. You can instantiate as many factories as you want, but they will all dole out only one connection. Better yet, create the connection only once somewhere high up in the call chain and pass it down into every object that needs it using dependency injection. – deceze Nov 14 '12 at 15:55
  • @Ray I expanded this into an answer, see below. – deceze Nov 14 '12 at 16:06
  • @deceze Thanks for your feedback. I need to do some long thinking on this in general, but I'm not quite sold on trying to eliminate use of static methods for the goal of ensuring total DI. I find both the singleton pattern and factory patterns using statics are very practically useful. My gut instinct is the added complexity needed to avoid using them to allow for total DI across 100% of your code does not result in a great enough good to warrant it--especially when used only in the sense of singletons. Like I said, this is something I'll be tossing around in my head for a bit... – Ray Nov 14 '12 at 16:11
  • @Ray Well, read the above-linked article. Once it clicks that static calls create irreversible coupling and dependency, and you extrapolate from there to a large project with many such interdependencies, the advantages of the DI approach should become obvious. Just try unit testing your classes when they have a rigid dependency on a running database. Not much fun. – deceze Nov 14 '12 at 16:14

3 Answers3

2

This is mostly to expand on my comments under the question...

The better "singleton" pattern would be this:

class ConnectionFactory {

    protected static $connection;

    public function getConnection() {
        if (!self::$connection) {
            self::$connection = new PDO(...);
        }
        return self::$connection;
    }

}

The users of this factory should expect an instance of it, not call it themselves:

class Foo {

    protected $connectionFactory;

    public function __construct(ConnectionFactory $factory) {
        $this->connectionFactory = $factory;
    }

    public function somethingThatNeedsAConnection() {
        $connection = $this->connectionFactory->getConnection();
        ...
    }

}

$foo = new Foo(new ConnectionFactory);

This allows Foo to get a connection itself when needed, but also allows you to inject some alternative connection into Foo, for example for testing purposes.

As a convenience measure and to cut down on instantiation complexity, this pattern is also good:

class Foo {

    protected $connectionFactory;

    public function __construct(ConnectionFactory $factory = null) {
        if (!$factory) {
            $factory = new ConnectionFactory;
        }
        $this->connectionFactory = $factory;
    }

    ...

}

This still allows the dependency to be injected and overridden, but it doesn't require you to inject a factory every time you instantiate Foo.

deceze
  • 510,633
  • 85
  • 743
  • 889
0

The factory is a singleton because you're not want more than one factory.

  1. You don't need a constructor, you can setup whatever state you need for the factory the getFactory() method. The getFactory() function doesn't get a connection, so don't do any connection related setup here, but not the connection object itself. Factories 'build instances' of classes for you to use, so you'd use the getConnection() method to set the state and construct the connection object the factory is supposed to generate.
  2. getSomething() isn't a method in the ConnectionFactory class, it's just a example of using the factory class to get a factory, then get a connection.

I personally hate method chaining in PHP. What's really happening in getSomething() is:

function getSomething()
{
   $factory = ConnectionFactory::getFactory();
   $conn = $factory->getConnection();
   .
   .
   . 
}
Ray
  • 40,256
  • 21
  • 101
  • 138
0
  1. A class doesn't need an explicit constructor in order to be instantiated with new ClassName(). However, if the class is supposed to be a singleton (and it looks like it in this case), the entire point of the pattern is making such instantiations impossible from outside of the class which is why I believe there should be a constructor declared with private keyword.

  2. The get getSomething() is a "standalone" function with an example of using your class.

  3. No. Multiple users use multiple instances of PHP processing your script and nothing is shared among them.

lafor
  • 12,472
  • 4
  • 32
  • 35