About dismissing things out of hand

If I would have a mantra, wich I don’t, it would be something like „never stop learning“. I try to keep an open mind and take inspiration from new things. But I do this development thingy for quite a while now and time builds opinions and more and more time toughens those opinions. I believe it is a sign of passion if you have strong opinions about something and this is normally a good thing. But every once in a while I catch myself rejecting things a bit too easily. In my career as a developer this happened to me more often then I am happy to admit, that’s for sure. But what would life be without regrets?

This article is about one of those times. The thing is, that I’m not quite sure if I should regret the dismissing part or not.

I’m talking about Traits. Since PHP 5.4.0 Traits are part of PHP. As I first saw the RFC I thought: Who needs this? And when they were integrated later I took some time to fiddle around with them and came to the fast unfunded conclusion: „This encourages bad design“. And *BAM*  I was done with Traits. Never looked at them again.

Just recently I saw a tweet from Anthony Ferrara. I consider him a great adviser and if you do not know who Anthony is I encourage you to check out his website, or follow him on twitter @ircmaxell.

As soon as I saw this tweet, I thought: „I was right to discard traits as fast as I did back in 2012.“ Life is good. Let’s get something to eat.

But then something weird happened. Just a couple of days later I faced a problem and believe it or not, the, best-looking solution, for me, was to use a Trait. But wait, this could not be it. There had to be a better solution for this kind of problem. But even after a day of thinking and a good nights sleep, the trait seemed to be the best option.

So what happened?

Did I, like Anthony said „#$^&ed it up somewhere“. I’m still unsure. But I committed the trait solution and released it. So I was or somehow am confident enough that I did not.

So here’s what happened!

In a project of mine, I use classes to represent CLI binaries like tar, mysqldump or mongodump and use them to generate CLI commands. All those classes have properties that somehow reflect a CLI binary argument or option.

Let’s stick with mysqldump for this example. The Mysqldump class has among others a username property Mysqldump::$username. If it is set, the generated command line includes -u $username. This way you can generate valid mysqldump commands thru out an easy to use PHP interface.

/**
 * Mysqldump Executable class.
 */
class Mysqldump
{
    /**
     * User to connect to mysql
     * --user <username>
     *
     * @var string
     */
    private $user;

Now to the problem I was talking about earlier. It is possible to pass a password to the mysqldump CLI binary. Doing so will make your created command to include the clear password and if you store the executed commands for debugging reasons, your debug log would contain the password for your MySQL user. You could argue that it is not the best idea to pass the password to the CLI binary, but anyway for the sake of the argument lets assume the problem exists and we want to fix it. I came up with an idea to mask passwords with asterisks so the generated commands can “safely” be stored in log files.

/**
 * Return the command line to execute.
 *
 * @return string
 * @throws \phpbu\App\Exception
 */
public function getCommandLine()
{
    // return executable command
}

/**
 * Return the command with masked passwords or keys.
 *
 * @return string
 */
public function getCommandLinePrintable()
{
    // return command with masked passwords
}

To archive this the Mysqldump class within other classes with similar arguments had to know what kind of properties contain sensitive information and also needed some functionality to mask this options with asterisks.

The dilemma I faced here was, all properties were private, so using an OptionMasker base class was not possible since I wanted to keep  those properties private.

Delegating the OptionMasking to another class completely didn’t seem to be a good idea either. Therefore, it would make things harder for the programmer using those classes.

So I opted for the Trait. Traits can access private properties wich seemed to be a big plus in my case. There is no code duplication within the CLI classes. So it looks all good but still feels all kinds of wrong.

/**
 * Binary using credentials not safe for printing.
 */
trait OptionMasker
{
    /**
     * List of properties to mask for print safe output
     *
     * @var array
     */
    protected $maskCandidates = [];

    /**
     * Return the command line to execute.
     *
     * @return string
     */
    public abstract function getCommandLine();

    /**
     * Return the command with masked passwords or keys.
     *
     * @return string
     */
    public function getCommandLinePrintable()
    {
        $propertiesToMask = $this->getPropertiesToMask();
        // no candidates need masking
        if (0 === count($propertiesToMask)) {
            return $this->getCommandLine();
        }
        $masked = $this->mask($propertiesToMask);
        $cmd    = $this->getCommandLine();
        $this->restore($masked);
        return $cmd;
    }

What do you think?
Is this a valid use case for a trait, or would you just have changed all properties to protected and used a base class?
Or do you suggest a completely different solution?

Please let me know in the comments below.

Leave a Reply

Your email address will not be published. Required fields are marked *