I have encountered a situation where I would like to avoid using $GLOBAL
state, but cannot workout how to do so. I believe Reflection and Dependancy injection could solve this:
Here is a contrived example (Yes I know its a little bent...); Say we have a Calculator
class and helper functions that basically replicate its functionality e.g. add, subtract
. Now we also want a history
of our calculations to be accessible.
How can I use these helper functions without having to manually insert the Calculator
as a dependancy?
class Calculator
{
private $history = [];
public function add(int $a, int $b): int
{
$result = $a + $b;
$this->history[] = $result;
return $result;
}
public function subtract(int $a, int $b): int
{
$result = $a - $b;
$this->history[] = $result;
return $result;
}
public function history(): array
{
return $this->history;
}
}
function add(int $a, int $b): int
{
$calculator = new Calculator;
return $calculator->add($a, $b);
}
function subtract(int $a, int $b): int
{
$calculator = new Calculator;
return $calculator->subtract($a, $b);
}
function history(): array
{
$calculator = new Calculator;
return $calculator->history(); // Clearly this will be empty
}
See what I mean? Currently calling history()
will of course return an empty array...
Of course this would work:
function add(Calculator $calculator, int $a, int $b): int
{
return $calculator->add($a, $b);
}
function history(Calculator $calculator): array
{
return $calculator->history();
}
Although if i use this as a package its very error prone, labour intensive to manually wire up these dependancies... let alone every time I call a helper function.
Another method that would work is globals:
$GLOBALS['calculator'] = new Calculator;
function add(int $a, int $b): int
{
return $GLOBALS['calculator']->add($a, $b);
}
function history(): array
{
return $GLOBALS['calculator']->history();
}
Although ... yuk yuk yuk. No thank you.
HELP!
This is usually when people turn to an IoC, aka DI Container.
You could also use the singleton pattern, providing a static accessor for a single instance of your class. You can track history, or state, in the calculator class because there will only be one instance of this class.
class Calculator
{
private static $instance;
public static getInstance(): Calculator
{
if (static::$instance === null) {
static::$instance = new Calculator;
}
return static::$instance;
}
...rest of code...
}
Calculator::getInstance()->add($x, $y);
The main concern with statics is state and testability, your use of $GLOBALS for global functions doesn't seem too problematic to me as you're essentially using $GLOBALS as a container or service locator.