{"action":"created","issue":{"url":"https://api.github.com/repos/hoaproject/Console/issues/54","labels_url":"https://api.github.com/repos/hoaproject/Console/issues/54/labels{/name}","comments_url":"https://api.github.com/repos/hoaproject/Console/issues/54/comments","events_url":"https://api.github.com/repos/hoaproject/Console/issues/54/events","html_url":"https://github.com/hoaproject/Console/issues/54","id":109039851,"number":54,"title":"Proposal: Introduce an `Output` class","user":{"login":"Hywan","id":946104,"avatar_url":"https://avatars.githubusercontent.com/u/946104?v=3","gravatar_id":"","url":"https://api.github.com/users/Hywan","html_url":"https://github.com/Hywan","followers_url":"https://api.github.com/users/Hywan/followers","following_url":"https://api.github.com/users/Hywan/following{/other_user}","gists_url":"https://api.github.com/users/Hywan/gists{/gist_id}","starred_url":"https://api.github.com/users/Hywan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Hywan/subscriptions","organizations_url":"https://api.github.com/users/Hywan/orgs","repos_url":"https://api.github.com/users/Hywan/repos","events_url":"https://api.github.com/users/Hywan/events{/privacy}","received_events_url":"https://api.github.com/users/Hywan/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/hoaproject/Console/labels/difficulty:%20medium","name":"difficulty: medium","color":"F0788C"},{"url":"https://api.github.com/repos/hoaproject/Console/labels/enhancement","name":"enhancement","color":"84b6eb"},{"url":"https://api.github.com/repos/hoaproject/Console/labels/question","name":"question","color":"cc317c"}],"state":"open","locked":false,"assignee":{"login":"jubianchi","id":327237,"avatar_url":"https://avatars.githubusercontent.com/u/327237?v=3","gravatar_id":"","url":"https://api.github.com/users/jubianchi","html_url":"https://github.com/jubianchi","followers_url":"https://api.github.com/users/jubianchi/followers","following_url":"https://api.github.com/users/jubianchi/following{/other_user}","gists_url":"https://api.github.com/users/jubianchi/gists{/gist_id}","starred_url":"https://api.github.com/users/jubianchi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jubianchi/subscriptions","organizations_url":"https://api.github.com/users/jubianchi/orgs","repos_url":"https://api.github.com/users/jubianchi/repos","events_url":"https://api.github.com/users/jubianchi/events{/privacy}","received_events_url":"https://api.github.com/users/jubianchi/received_events","type":"User","site_admin":false},"milestone":null,"comments":3,"created_at":"2015-09-30T08:24:14Z","updated_at":"2015-09-30T12:08:49Z","closed_at":null,"body":"#### Now\r\n\r\nWe use `echo` to print strings on the output of the current program.\r\n\r\n#### Issue\r\n\r\nSometimes 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).\r\n\r\nAnother 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.\r\n\r\n#### Proposal\r\n\r\nWe must introduce an output object. How though? This is a coding design discussion.\r\n\r\n##### `Output` class and object\r\n\r\nI 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)`.\r\n\r\n##### What interface `Output` implements?\r\n\r\nThus, all `echo`s 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!\r\n\r\nHowever, 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.\r\n\r\n#### Conclusion\r\n\r\nI would like the opinion of several @hoaproject/hoackers please.\r\n\r\n#### Appendix A\r\n\r\nHere is a draft for the `Output` object:\r\n```php\r\nuse Hoa\\Stream;\r\n\r\nclass Output implements Stream\\IStream\\Out\r\n{\r\n    public function write($string, $length)\r\n    {\r\n        echo $string;\r\n    }\r\n\r\n    public function writeAll($string)\r\n    {\r\n        return $this->write($string, strlen($length));\r\n    }\r\n\r\n    …\r\n}\r\n```\r\n\r\nHere is a draft for the new `Hoa\\Console` methods:\r\n```php\r\nclass Console\r\n{\r\n    …\r\n\r\n    protected $_output = null;\r\n\r\n    public static function setOutput(Output $output)\r\n    {\r\n        $old = static::$_output;\r\n        static::$_output = $output;\r\n\r\n        return $old;\r\n    }\r\n\r\n    public static function getOutput()\r\n    {\r\n        if (null === static::$_output) {\r\n            static::$_output = new Output();\r\n        }\r\n\r\n        return static::$_output;\r\n    }\r\n}\r\n```\r\n\r\n##### Bonus with outputs and redirection\r\n\r\nHow 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:\r\n\r\n```php\r\nuse Hoa\\Stream;\r\n\r\nclass Console\r\n{\r\n    …\r\n\r\n    public function setOutput(Stream\\IStream\\Out $output)\r\n    {\r\n        …\r\n   }\r\n\r\n    …\r\n}\r\n```\r\nand then:\r\n```php\r\nHoa\\Console::setOutput(new Hoa\\File\\Write('/tmp/a'));\r\n```\r\nto redirect all outputs to the `/tmp/a` file!\r\n\r\nTip: 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'));`.\r\n\r\n#### Next\r\n\r\n1. We must open an issue to add a new method on `Output` like `enableDemultiplexerByPassing`. This will re-solve #53 more elegantly.\r\n2. We can imagine introducing an `Input` class? Not sure. Need more grooming."},"comment":{"url":"https://api.github.com/repos/hoaproject/Console/issues/comments/144376115","html_url":"https://github.com/hoaproject/Console/issues/54#issuecomment-144376115","issue_url":"https://api.github.com/repos/hoaproject/Console/issues/54","id":144376115,"user":{"login":"jubianchi","id":327237,"avatar_url":"https://avatars.githubusercontent.com/u/327237?v=3","gravatar_id":"","url":"https://api.github.com/users/jubianchi","html_url":"https://github.com/jubianchi","followers_url":"https://api.github.com/users/jubianchi/followers","following_url":"https://api.github.com/users/jubianchi/following{/other_user}","gists_url":"https://api.github.com/users/jubianchi/gists{/gist_id}","starred_url":"https://api.github.com/users/jubianchi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jubianchi/subscriptions","organizations_url":"https://api.github.com/users/jubianchi/orgs","repos_url":"https://api.github.com/users/jubianchi/repos","events_url":"https://api.github.com/users/jubianchi/events{/privacy}","received_events_url":"https://api.github.com/users/jubianchi/received_events","type":"User","site_admin":false},"created_at":"2015-09-30T12:08:49Z","updated_at":"2015-09-30T12:08:49Z","body":"> So I would argue that adding this dependency will not hurt anyone.\r\n\r\n:+1: \r\n\r\nThis seems to be a really nice feature, as @Hywan said, it will solve issues (and simplify code) on the Console contributions:\r\n\r\n* https://github.com/hoaproject/Contributions-Symfony-ConsoleBridge/blob/master/Formatter/OutputFormatterStyle.php#L88-L107\r\n* https://github.com/hoaproject/Contributions-Symfony-ConsoleBridge/blob/master/Helper/CursorHelper.php#L128-L137\r\n* https://github.com/hoaproject/Contributions-Symfony-ConsoleBridge/blob/master/Helper/WindowHelper.php#L135-L144\r\n\r\nThis will also allow us to bridge the new `Output` interface to Symfony's output and provide new stuff like piping/redirecting outputs."}}