1

Main goal is to make core classes (instantiated with params) available from any place in application - in controllers, mappers, models, helpers etc.

For example, we have mapper which depends on Database object:

class Foo_Mapper
{
    private $database;

    public function __construct(Database $database)
    {
        $this->database = $database;
    }

    public function getFoo(array $criteria)
    {
        // ...
    }
}

Variant 1: Basic dependency injection. The problem is that every time when I need to create mapper, I also need to instantiate a database object (with params).

$database = new Database($params);

$foo_mapper = new Foo_Mapper($database);

Variant 2: Registry. Core objects are instantiated and put into registry so every other object can easily access them.

// Somewhere in bootstrap...
$registry = Registry::getInstance();
$registry->database = new Database($params));

// Usage
$registry = Registry::getInstance();

$foo_mapper = new Foo_Mapper($registry->database);

Is there a better way to do what I want? Any drawbacks?

Hemaulo
  • 979
  • 2
  • 9
  • 16
  • *(tip)* [ServiceLocator vs Dependency Injection](http://www.martinfowler.com/articles/injection.html#ServiceLocatorVsDependencyInjection) – Gordon Feb 04 '11 at 08:20
  • @Gordon, isn't Registry exactly the same as [ServiceLocator](http://www.martinfowler.com/articles/injection.html#ADynamicServiceLocator)? – Hemaulo Feb 04 '11 at 08:29
  • More or less, yes. I like to see a Registry as a dumb global data container, while the ServiceLocator can also contain some logic to find said data. But since you asked about DI vs Registry, I thought it might be an interesting read. I didnt mean to suggest to use a Service Locator though. – Gordon Feb 04 '11 at 08:51

2 Answers2

1

Variant 1: Basic dependency injection. The problem is that every time when I need to create mapper, I also need to instantiate a database object (with params).

or passing it along. If you use a Dependency Injection Container, you would even have to do that manually: you simply add that you need to retrieve a Database, and a Database will be created (or reused) for you. Ask the Container to create a controller, and make sure you list your dependencies in the constructor. There are a few decent Dependency Injection containers for PHP, to wit:

For full disclosure: I wrote the last one.

Variant 2: Registry. Core objects are instantiated and put into registry so every other object can easily access them.

You could make the registry non-static, but if you're going to rely on a Registry object in your application, you might as well leave it static. This is a perfectly viable solution, with one obvious drawback: you don't know what objects are used by what objects by looking at the API. You'll have to dig into the code.

Berry Langerak
  • 18,561
  • 4
  • 45
  • 58
  • thanks for your answer! I don't know much about DIC, can I use it when dependencies are several instances of some class but with different parameters? For example Mapper::__construct(Database $db): in one mapper I need "mysql1", in other "mssql1", in third "mssql2" connection. How can I specify which instance (not class) I need in this case? – Hemaulo Feb 04 '11 at 08:41
0

You could make this a Singleton. Like this:

class Database
{
  private static $instance = null;
  public static getInstance()
  {
    if (self::$instance == null) self::$instance = new Database();
    return self::$instance;
  }
   // ... METHODS ... //
   // ... METHODS ... //
   //      .....      //
}

Then you can use new Foo_Mapper(Database::getInstance()); It looks like you did this with your registry. Then you don't really need your registry anymore. Though if your classes are coded by someone else you would still have to use something like a registry or make a helper class/function for every of those classes that do not use the singleton pattern.

I think the way you illustrated it seems ok to me. It would be bad if you call your registry from within Foo_Mapper, because that class should probably not know about the existence of the registry, but this way looks ok.

yankee
  • 38,872
  • 15
  • 103
  • 162
  • Singletons should also have a non-public constructor. – binaryLV Feb 04 '11 at 07:44
  • My fault, I should make it clear that database cannot be singleton because my application is using three different database connections (one mysqli, two mssql). Also, if database or any other class is made singleton then how to pass params? Params is in Config object. – Hemaulo Feb 04 '11 at 07:45
  • @Hemaulo, theoretically you might pass some argument to `getInstance()`, like `Database::getInstance('remote_db')`, with `remote_db` being defined in config file like `$dbAccess = array('remote_db' => array(host, port, user, pass))`. In `Database` you would then have `$this->instance = array('remote_db' => $dbObject)` rather than `$this->instance = $dbObject`. You might also think about using PDO rather than DBMS-specific extensions (e.g., MySQLi) to access database, but that's matter of taste. – binaryLV Feb 04 '11 at 07:50
  • Singletons have no use in PHP. Forget they exist. – Gordon Feb 04 '11 at 08:02
  • I don't like singletons because they hide dependencies. This is perfectly outlined in [this answer](http://stackoverflow.com/questions/1020312/are-singletons-really-that-bad/1020384#1020384): Singletons hide dependencies and increase coupling (every class can potentially depend on a singleton, which means the class can not be reused in other projects unless we also reuse all our singletons), and because these dependencies are not immediately visible (as function/constructor parameters), we don't notice them, and typically don't think about it when we create them. – Hemaulo Feb 04 '11 at 08:12
  • Singletons have no use in PHP either way, but they aren't thought up to be used to "provide global access", it's to make sure you only ever have one instance of a class. Don't do it, you'll be worse off. – Berry Langerak Feb 04 '11 at 08:22
  • What the hack is your problem with singleton in php? – yankee Feb 05 '11 at 07:57