Null Object Pattern in PHP
Have you heard about the Null Object Pattern? It’s basically a pattern that allows you to replace null
with real objects — and those objects do nothing. And, in this article, I’m going to show how I’ve used it with Symfony’s Console Output feature.
Yesterday I was working with Symfony’s Symfony\Component\Console\Output\ConsoleOutput
class - and this is a class that allows me to “write” stuff to the console.
Now, in my code, I had a “MyClass” that contains a public “getter” to receive the current Output implementation.
use Symfony\Component\Console\Output\ConsoleOutput;
class MyClass
{
public function __construct(private ?ConsoleOutput $output = null)
{
// ..
}
public function getOutput(): ?ConsoleOutput
{
return $this->output;
}
// other things my class do...
}
As you can see, the class above can equally be used without an actually ConsoleOutput
implementation, and one issue with this is: that the return type of getOutput
may be null
. Meaning that consumers of the getOutput
method will need to use it in the following way:
$output = $object->getOutput();
if (! is_null($output) {
$output->write('This works because of the if condition...');
}
Not always, but usually, if a method is returning more than one type, that’s a code smell - specially if is returning a nullable type. Now, there is a simple pattern we can do to improve this. That pattern is called: Null Object Pattern.
For my use case, the Null Object Pattern is about having an object that behaves like ConsoleOutput
, but when you call write
it should do nothing. In fact, Symfony already ships with a NullOutput
for this purpose.
To use the NullOutput
from Symfony, let’s first adapt our MyClass
implementation, so it can receive any kind of Output
. And for that, we can use the Symfony\Component\Console\Output\OutputInterface
interface:
use Symfony\Component\Console\Output\OutputInterface;
class MyClass
{
public function __construct(private OutputInterface $output)
{
// ..
}
public function getOutput(): OutputInterface
{
return $this->output;
}
// ... other things my object do
}
Next, when creating an instance of MyClass
, if we plan to use a MyClass
object without a real ConsoleOutput
, we can simply use the NullOutput
class instead:
use Symfony\Component\Console\Output\NullOutput;
$output = new NullOutput();
$myObject = new MyClass($output);
Finally, because we’ve used the Null Object Pattern, consumers of MyClass
no longer need to verify if an Output
instance exists, before using it:
$output = $myObject->getOutput();
-if (! is_null($output) {
- $output->write('This works because of the if condition...');
-}
+$output->write('This always work...');
I hope you have enjoyed this article. If yes, please consider supporting my work — my mission is to spend more time maintaining the dozens of projects that I've written/collaborated on over the years and continue developing new projects to make PHP development more productive and enjoyable.