I love writing (Laravel) artisan commands, basically little scripts that perform some sort of task. But what I love even more, is having those scripts output stuff to the console, so that it can tell me what’s going on! I’m going to show you how to easily enable any PHP class, script, etc to output whatever you want to the console with Symfony.
Some Background Info
If you hate reading, skip ahead to the implementation. Otherwise, we will have to start with the Symfony OutputInterface and StyleInterface. The former handles the actual act of writing to the console screen and how verbose to be. The latter handles grouping or formatting text in different meaningful ways (think titles, sections, asking questions and handling user input, etc) as well as the creation and management of progress bars.
I have to backtrack a bit and say that Symfony has great docs explaining how to use the consoleoutput, and I recommend checking them out. Despite learning a lot of cool in those references, admittedly the one thing I couldn’t comprehend was how to instantiate the actual class which handled the output (writing of text to screen). Most examples shown are written from the context of a Symfony Command class, which has the output class (or OutputInterface rather) injected in.
Things finally started clicking together when I took a look at the SymfonyStyle class. This class marries the OutputInterface and StyleInterface. In order to get an instance of this class you can choose from a few different input and output options here and here. Since I wasn’t interested in the input option I did the following:
new SymfonyStyle(new StringInput(''), new ConsoleOutput(OutputInterface::VERBOSITY_NORMAL))
Now I could finally proceed to my main goal which was giving the ability to output stuff to any class, regardless of whether you were in a Command class or not. I also didn’t want it to be annoying by always outputting extra info if I were using the artisan tinker command with Laravel; so there had to be some level of flexibility. Below is what I came up with.
Implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
We need something which will handle the functionality of the outputter in a sort of on/off fashion. What I mean by that is, if an outputter hasn’t been initialized, any call to one of it’s functions should not result in a null pointer exception. However if it has been initialized, calls should be executed as expected. It should also be able to instantiate an OutputInterface class if necessary.
An Interface is perfect for this job because we can implement this functionality in any class we want. It provides clear documentation as to what it’s capable of doing. We can see if a class is capable of outputting text using instanceof (and most importantly we get type-hinting yo!).
The ability to easily and quickly bring this to any class is achieved via the use of a Trait. Simply include the following statement at the beginning of your class:
use FlexibleConsoleOutputter;
Aside from its code once-use anywhere functionality traits provide, this trait gives us the ability to freely use functions associated with the outputter (line, comment, progressStart) without worrying about null pointer exceptions. In other words, if the output class hasn’t been instantiated, calling these functions will not have any adverse effects because each function checks if the outputter has been set.
This class has been set up with the interface and trait I created. It allows me to use $this->line($message) as if I were inside a command class. It will not output anything unless the $output property has been set. If I want to enable output functionality I can achieve this by doing
(new EloquentTableTruncator())->iniatlizeOutput()->execute()
or by using initializeOutput() straight away in the class constructor.
And that’s it! I hope this leads to more expressive scripts being written, and never having to wonder if your class, script, or command is actually working or not.
Like what you've read? Then please make sure you've left a like or a comment. If you'd be willing to receive email notifications of new ramblings when they arrive, I'd appreciate it! If not, I'd be happy to have you back soon.
Leave a Reply