zuc0001
zuc0001

Reputation: 931

PHP Classes - global or __construct

So I've come across a "problem" with PHP classes.

I have a few classes that require functions from one another, so at the moment I am doing the following:

$db = new blueConnect;
$core = new blueCore($db);
$users = new blueUsers($db, $core);

then within the file:

public function __construct(blueConnect $db, blueCore $core) {
    $this->db = $db;
    $this->core = $core;
}

However instead of doing this for each file that required additional functions, would it be better to write

global $db, $core

within each of the functions that require it?

Upvotes: 2

Views: 217

Answers (4)

chozilla
chozilla

Reputation: 159

The Name of the Pattern that you are talking about is called "Dependency Injection" or DI in short.

Depending on your Project using global might solve the problem in a short term but if you plan to create a big project that you later on test and share with multiple people you would like to avoid using global at all. - You can not test or debug that stuff well.

A (bad) solution is to make your database and core Class use the Singleton Pattern to avoid global but have the same effect. (not testable, not configurable)

public function __construct() {
    $this->db = blueConnect::getInstance();
    $this->core = blueCore::getInstance();
}

The solution for this is usually to create a Factory function that creates all of your Services that need the Database and the Core.

public function createService($name) {
    $serviceClass = 'blue'.ucfirst($name).'Service';
    return new $serviceClass($this->getDatabase(), $this->getCore());
}

And this Function is usually part of a Registry or better an DI Container like for example PIMPLE

Example with Only one Instance of each Service:

public function createService($name) {
    $serviceClass = 'blue'.ucfirst($name).'Service';

    static $services = array();
    if(!isset($services[$name])) {
        $services[$name] = new $serviceClass($this->getDatabase(), $this->getCore());
    }

    return $services[$name];
}

Be aware that you should NOT test with your Registry / DI Container since you have a "global" state inside of your container. (e.g. fetching the same service twice)

Upvotes: 3

mrun
mrun

Reputation: 744

I would not use globals.

Reasons:

  1. It's just not the OOP way.

  2. Hard to debug.

  3. The resulting code would not be testable. If you want your application to be covered by unit tests you should use Dependcy Injection as you currently do. I'll try to briefly explain why. Unit testing as its name implies is just that: testing a single unit of your application, i.e. your classes (their public methods).

    Now let's suppose you use global variables that get set somewhere in your app. You won't be able to test your blueUsers class as a standalone unit because you will need the $db and $core objects to be instantiated. Therefore you won't be able to just include the blueUsers.php file and test the class because you will need other parts of your application (those which define the global variables $db and $core).

    If on the other hand you used Dependcy Injection you wouldn't face such an issue. The only things you'll need to do in order to test the blueUsers class would be to include the class, create mocks of the dependencies $db and $core, and pass them to the blueUsers constructor.

Here's a nice source for more detailed explanation on how to write testable and maintainable code.

And here you could find more information about mocking and whether and what you can benefit from using it.

Upvotes: 2

ColOfAbRiX
ColOfAbRiX

Reputation: 1059

I'd use a singletone to contain the global objects. It's easily accessible by other objects and it provides a safe access to those variables:

final class GlobalObjects
{
    public static function Instance()
    {
        static $inst = null;
        if ($inst === null) {
            $inst = new GlobalObjects();
        }
        return $inst;
    }

    private function __construct() {
        $db = new blueConnect;
        $core = new blueCore($db);
    }

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

    public function getCore() { return $this->core; }
}

// ...

public function __construct() {
    $this->db = GlobalObjects::Instance()->getDb();
    $this->core = GlobalObjects::Instance()->getCore();
}

Another approach that comes into my mind, a simpler and dirtier one, is: as those variables are global objects it might be worth to define them as constants to avoid mistakes:

define( "db", new blueConnect );
define( "core", new blueCore($db) );

$users = new blueUsers($db, $core);

And then

public function __construct() {
    global $db, $core;

    $this->db = $db;
    $this->core = $core;
}

Upvotes: 1

hamed
hamed

Reputation: 8033

If the parameters are dynamic and variable, you should pass them in constructor, so new blueUsers($db, $core, ...) is better. But if these are static parameter, and you don't need to pass them when initialization of the class, you should define as global.

Upvotes: 0

Related Questions