Skip to content

Managing the Verbosity of symfony’s Command Object With a Trait

Dear Reader,

Update:
I was wrong and Kris Wallsmith called me on it. When installing the Command component, composer did NOT pull in any other symfony packages, or any other packages at all. I started this project with Silex and I believe that had pulled in a lot of stuff. To Composer’s credit. when I went in and removed Silex and put in the symfony component, it removed all the Silex dependancies and then dropped in the symfony component. Thank you Kris!

Long Rambling Intro of No Concequence

Recently I came up with a project I needed to build. Honestly, it’s one of those “not useful to anyone but me” kind of projects. I decided though that while I was at it, I wanted to learn something new. Since I’ve been a die-hard Zend guy since Zend Framework was 0.2, and more recently a Silex believer, I thought I would drop down a level form Silex and dig into symfony itself.

This project is strictly a command line project. No API, no web interface, just a command and a few switches. Digging around I found Symfony\Component\Console\Command\Command so I decided to give it a try. Honestly, I really like what I saw. It was even better that I was able to use composer to install it.

The project itself was a simple pump to move data from EventBright into MailChimp. Those of you who don’t know me, I do a lot of virtual events like DayCamp4Developers and NomadPHP, so this is a real time saver for me.

The one rough edge that I had was dealing with the verbosity setting of Symfony\Component\Console\Output\OutputInterface. That is what I am going to cover in this blog post.

Verbosity

When reading the symfony Command manual page it talks about verbosity. When you actually run the ./console.sh you can see that the -v tag is supported.

cal$ ./console.sh 
Nomad PHP Maintenance Script version 0.1.0

Usage:
  [options] command [arguments]

Options:
  --help           -h Display this help message.
  --quiet          -q Do not output any message.
  --verbose        -v|vv|vvv Increase the verbosity of messages: 
                             1 for normal output, 
                             2 for more verbose output and 
                             3 for debug
  --version        -V Display this application version.
  --ansi              Force ANSI output.
  --no-ansi           Disable ANSI output.
  --no-interaction -n Do not ask any interactive question.

Available commands:
  autodelete   Handle the autodelete process
  ebdatapump   Populates MailChimp lists from EventBrite events
  help         Displays help for a command
  list         Lists commands
  listLists    Prints a formatted list of the lists for a given mailChimp 
               account.

However, you have to piece things together a bit to finally get to the the point that:

-q OutputInterface::VERBOSITY_QUIETL
No options OutputInterface::VERBOSITY_NORMAL
-v OutputInterface::VERBOSITY_VERBOSE
-vv OutputInterface::VERBOSITY_VERY_VERBOSE
-vvv OutputInterface::VERBOSITY_DEBUG

It’s not confusing or anything but it it’s not really clear from the documentation.

Once I worked out the verbosity level, everything was awesome…at least until I got into my code. Then I realized that I was writing a LOT of lines that looked like this:

if ($this->output->getVerbosity()==OutputInterface::VERBOSITY_VERBOSE) {
    $this->output->writeln("My Message Goes Here");
}

That’s not really that bad until you start writing a lot fo them. In some cases I do like very verbose output, especially when debugging.

If you are curious, it is $this->output-> because I always take the $output object and store it in $this->output so I can get to it from any method in the object.

My First Solution

It didn’t take long to figure out that my Command class needed it’s own writeln() method.

protected function writeln($message,$level=1)
{
	if (is_null($this->output)) {
		return;
	}

	if ($this->output->getVerbosity()>=$level) {
		$this->output->writeln((isset($this->testing) && 
                                        $this->testing?'TESTING - ':'') . 
                                        $message);
	}

	return;
} // protected function writeln($message,$level=1)

Now, we’ve got things a little better. First, if it can’t find the $this->output, it fails silently. Second, This is where the IF statement lives so I don’t have to wrap every writeln() in a IF. It makes for much prettier code. the method takes an optional verbosity parameter so that I can specify what level the message should be displayed at. IF the message is VERBOSITY_VERY_VERBOSE and the verbosity level is VERBOSITY_NORMAL, the method just discards it and keeps going.

Finally. All of my commands support a -t testing mode. This let’s me do a dry run and make sure everything is going to work. I want it to be very clear when I am in testing mode so I prepend “TESTING “ to each line.

At this point I know a lot of you are asking “Why not just use logging?” The easy answer to that one is “I didn’t want to.” I don’t need full-on logging, I just need to be able to print stuff out to the terminal.

The Final Solution

This all looked pretty good to me. I was feeling rather pleased with myself…until I wrote my NEXT command for this toolset. (As you can see from the output above, there are a total of 3 now, it kinda got out of hand.) I started the second Command and realized I needed the writeln() there as well. So I did what most everyone does, I copied and pasted it. (Hush, you’ve done it too.)

I quickly realized the error of my ways. In the past, I would have subclassed Symfony\Component\Console\Command\Command and added it to the child class, and used that. That didn’t feel right either. So I wrote my very first Trait in PHP, this felt right. I love the simplicity of sharing it amongst all the other Commands. I love that I don’t have to manage sub-classes of the parent class, and yet I don’t have to hack the core either. (Again, hush, you’ve done it at least as much as I have.)

So, for better or worse, I now have a trait.

namespace NomadPHP\Traits;

trait WritelineTrait {
	protected $output;

	protected function writeln($message,$level=1)
	{
		if (is_null($this->output)) {
			return;
		}

		if ($this->output->getVerbosity()>=$level) {
			$this->output->writeln((isset($this->testing) && 
                                               $this->testing?'TESTING - ':'') . 
                                               $message);
		}

		return;
	} // protected function writeln($message,$level=1)
} // trait writeline

To use I just add this line inside of my Class definition.

use FollowMe\Traits\WritelineTrait;

Inside my class gets me everything I need and I never have to worry about it again…at least for this project.

Conclusion

Until next time,
I <3 |<
=C=

3 thoughts on “Managing the Verbosity of symfony’s Command Object With a Trait

Comments are closed.