Reputation: 10649
I have been reading up on OOP for a while now and it's always confused me but I'm trying to improve on my skills (albeit I'm still starting out with this so please be kind).
I am designing a main app, whose "core" class is $App
. My thought is that anywhere in the app, you can do everything from within $App
.
For example, I have a $Db
class to query the database, $Mail
class for sending email, etc. These are all autoloaded using spl_autoload_register
.
My question is in regards to actually instantiating them properly. Here's how I'm doing it now:
index.php
require_once('config.php'); // Autoloads classes and some other small stuff
$App = new App;
echo $App->Visitor->getIP();
App.class.php
class App {
public function __construct() {
$this->initialize();
}
private function initialize() {
$this->Visitor = new Visitor($this);
$this->Db = new Db($this);
$this->Mail = new Mail($this);
}
// Then some public methods...
// We are able to use $this->Visitor, $this->Db, etc...
}
Visitor.class.php
class Visitor {
private $App;
public function __construct($App) {
$this->App = $App; // I need access to all of App's methods
}
public function getIP() {
return $_SERVER['REMOTE_ADDR'];
}
}
So the short example above shows how I'm doing this in all my classes. The App
class always creates an instance of whatever class we will be using in the entire app in it's constructor.
The "child" classes all [dependency injects?] the $App
instance so they can use it's methods.
Am I doing this correctly? It works, but I don't know if there is a more efficient/better practice way of doing this.
Upvotes: 0
Views: 74
Reputation: 6319
The problem with injecting the base class on instantiation is that it makes all of your classes have really ambiguous dependencies. Your Visitor
class could rely on a class that parses the $_SERVER
superglobal, but I can't tell just by looking at it, it's only dependency is the whole app object.
It's much better to only define the specific dependencies a class needs (usually in the constructor) and create something whose job it is to properly provide these dependencies. This is the "dependency injection" that you were hinting at, and they usually involve some Container
that stores all the objects that have been instantiated, ready to inject into new classes.
So your app will have the dependency of a container:
$app = new App(new Container);
And then you can ask the container for dependencies
class App {
/**
* @return App\Visitor
*/
public function getVisitor()
{
if (!$this->visitor) {
$this->visitor = $this->container->get('App\Visitor');
}
return $this->visitor;
}
}
If you use a third party dependency injection library (recommended - even when starting out as designing your own is a bit of a tangent) then the library will usually provide some way to automatically resolve dependencies. It can look at the constructor on your class (that's typehinted correctly, e.g. __construct(Visitor $visitor)
) and automatically provide the arguments to the constructor - using the Reflection SPL
I would also recommend if you're starting out with OOP that you first familiarise yourself with PSR-4 and Composer autoloading, as including all of your classes manually is going to become quite a hassle, and doing it the "standard" way at the beginning makes life easier in the future when you will want to switch ;) .
Upvotes: 1