I'm using dependency injection wherever possible in my application.
I've got a Request class that works with superglobals, but to me it feels a little bit weird; injecting superglobals. Isn't it safe to say that superglobals will always be there and instead of injecting them I could just call them directly in the class (tightly coupled)?
Class: Request
public function __construct($get, $post, $server) { ... }
Class: Session
public function __construct() { session_start(); }
This last one is a little bit tricky because in this constructor I am starting the session with session_start()
and $_SESSION
is only defined after the session has been started, so I cannot inject that. However, I could set it with a setter after instantiating the class.
How should I work with superglobals when it comes to OOP?
Do I just couple them tightly with classes or should I still just inject them.
Your code is kind of confusing. What you can do is this:
class Request {
public function __construct($getData, $postData, $serverData) { ... }
}
new Request($_GET, $_POST, $_SERVER);
When it comes to sessions, you'd better wrap it into an object:
class Session {
public function __construct() {
if (!isset($_SESSION)) session_start();
}
// ...
}
Another approach is to use Lazy Initialization:
class LazySession {
private $isStarted = false;
public function __construct() {
}
private function verifyAndStart() {
if (!isset($_SESSION)) session_start();
}
public function set(...) {
$this->verifyAndStart();
// ...
}
public function set(...) {
$this->verifyAndStart();
// ...
}
// ...
}
Then you can inject it anywhere, but the session will only be created when needed. Notice that the session MUST be created before any output.
I was in a hurry when first answered this, but just to make things clear: you cannot decouple everything. What should be decoupled is the implementation of session variables access and its client objects.
For example, you define something like this:
interface SessionStorageInterface {
public function get($key);
public function set($key, $value);
public function remove($key);
public function clear();
}
If you don't want to rewrite PHP built in session system, you'd have to do something like:
final class DefaultSessionStorage implements SessionStorageInterface {
protected function start() {
if (!isset($_SESSION))
session_start();
}
public function set($key, $value) {
$_SESSION[(string) $key] = $value;
}
//...
}
If you want to change this and hold your session in a database, you could create something like:
final class DbSessionStorage implements SessionStorageInterface {
private $id;
private $dbStorage;
private $definition;
public function __construct($sessionId, DbStorage $storage, StorageDefinition $def) {
$this->id = $sessionId;
$this->dbStorage = $storage;
$this->definition = $def;
}
public function set($key, $value) {
$data = [
$this->def->getField(StorageDefinition::IDENTIFIER) => $this->id,
$this->def->getField(StorageDefinition::KEY) => $key,
$this->def->getField(StorageDefinition::KEY) => $value,
];
try {
$this->dbStorage->save($def->getContainer(), $data);
} catch (Exception $e) {
// does something...
}
}
// other methods omitted for brevity...
}
The important thing is that when you have some class that depends on a session storage, you declare it like this:
class SomeClass {
public function __construct(SessionStorageInterface $storage) {
// ...
}
}
$obj1 = new SomeClass(new DefaultSessionStorage());
$obj2 = new SomeClass(new DbSessionStorage(...));