hoaproject / Console

The Hoa\Console library.
https://hoa-project.net/
366 stars 32 forks source link

Proposal: Introduce an `Output` class #54

Closed Hywan closed 8 years ago

Hywan commented 9 years ago

Now

We use echo to print strings on the output of the current program.

Issue

Sometimes we don't want to echo but get the output, compute it somewhere with something and delegate the output. For instance, with the Symfony console bridge official contribution, we plug to the Symfony console output object (/cc @jubianchi).

Another issue happens recently with #52. TMUX(1) has some issues with specific control sequences. There is a solution to by-pass TMUX(1) and send control sequences to the upper terminal directly, see #53 for more explanation.

Proposal

We must introduce an output object. How though? This is a coding design discussion.

Output class and object

I would go for a static Output class and use it everywhere instead of echo but this is no easily extensible, nor testable. So we should for an object instead of a class. We do not want to create one instance each time we call the cursor or window API, this would increase memory. Since #51 introduces Console::getStdin, we can imagine introducing Console::getStdout and compute an instance of Output. To be able to extend Hoa\Console to delegate outputs, we can imagine Console::setStdout(Output $output).

What interface Output implements?

Thus, all echos could be replaced by Console::getStdout()->writeAll(). Why writeAll? This is the typical naming from Hoa\Stream interfaces. But I am not likely to implement the Hoa\Stream\IStream\Out interface right now. Or maybe I would be likely. This is one more dependency and it requires to implement write, writeLine etc. It makes a lot of sense!

However, what is the impact of adding a new dependency? Hoa\Stream is almost always a dependency, except if our program does not need any external resources (like files, sockets & co.), which is very rare. So I would argue that adding this dependency will not hurt anyone.

Conclusion

I would like the opinion of several @hoaproject/hoackers please.

Appendix A

Here is a draft for the Output object:

use Hoa\Stream;

class Output implements Stream\IStream\Out
{
    public function write($string, $length)
    {
        echo $string;
    }

    public function writeAll($string)
    {
        return $this->write($string, strlen($length));
    }

    …
}

Here is a draft for the new Hoa\Console methods:

class Console
{
    …

    protected $_output = null;

    public static function setOutput(Output $output)
    {
        $old = static::$_output;
        static::$_output = $output;

        return $old;
    }

    public static function getOutput()
    {
        if (null === static::$_output) {
            static::$_output = new Output();
        }

        return static::$_output;
    }
}
Bonus with outputs and redirection

How to implement redirections (rhetorical question)? Either we add an argument to the Output class constructor representing a stream or a resource, or because we are using Hoa\Stream\IStream\Out, we can do this:

use Hoa\Stream;

class Console
{
    …

    public function setOutput(Stream\IStream\Out $output)
    {
        …
   }

    …
}

and then:

Hoa\Console::setOutput(new Hoa\File\Write('/tmp/a'));

to redirect all outputs to the /tmp/a file!

Tip: Using echo is strictly equivalent to Hoa\Console::setOutput(new Hoa\File\Write(1));, which is equivalent to Hoa\Console::setOutput(new Hoa\File\Write('php://fd/1'));, which is equivalent to Hoa\Console::setOutput(new Hoa\File\Write('php://output'));.

Next

  1. We must open an issue to add a new method on Output like enableDemultiplexerByPassing. This will re-solve #53 more elegantly.
  2. We can imagine introducing an Input class? Not sure. Need more grooming.
shulard commented 9 years ago

Hello,

I think it's a really nice idea to add Output layer here. We can handle more nicely the verbosity or the output destination. Also rely on Stream add powerful capabilities...

Just one question, I saw that there are echo calls to move Cursor, set Window size... Can it be a problem to redirect the output in a file for that cases ? These cases are more for utils than message...

Hywan commented 9 years ago

@shulard The output class will not be responsible to manage the verbosity. We have to check whether Console::isDirect(STDOUT) returns true to run the cursor API for instance. This is not part of this issue. However, you can open another issue if you want to.

jubianchi commented 9 years ago

So I would argue that adding this dependency will not hurt anyone.

:+1:

This seems to be a really nice feature, as @Hywan said, it will solve issues (and simplify code) on the Console contributions:

This will also allow us to bridge the new Output interface to Symfony's output and provide new stuff like piping/redirecting outputs.

-- EDIT --

I forgot to mention this comment: https://github.com/hoaproject/Test/pull/46#discussion_r40193040 Introducing the Output class will also let us solve this issue.

Hywan commented 9 years ago

@jubianchi Good. So let's go!

Hywan commented 9 years ago

Oh, Hoa\Stream is already a dependency of Hoa\Console, so it's fine!