Chris@13
|
1 <?php
|
Chris@13
|
2
|
Chris@13
|
3 /*
|
Chris@13
|
4 * This file is part of Psy Shell.
|
Chris@13
|
5 *
|
Chris@13
|
6 * (c) 2012-2018 Justin Hileman
|
Chris@13
|
7 *
|
Chris@13
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@13
|
9 * file that was distributed with this source code.
|
Chris@13
|
10 */
|
Chris@13
|
11
|
Chris@13
|
12 namespace Psy\Output;
|
Chris@13
|
13
|
Chris@13
|
14 use Symfony\Component\Console\Output\StreamOutput;
|
Chris@13
|
15
|
Chris@13
|
16 /**
|
Chris@13
|
17 * ProcOutputPager class.
|
Chris@13
|
18 *
|
Chris@13
|
19 * A ProcOutputPager instance wraps a regular StreamOutput's stream. Rather
|
Chris@13
|
20 * than writing directly to the stream, it shells out to a pager process and
|
Chris@13
|
21 * gives that process the stream as stdout. This means regular *nix commands
|
Chris@13
|
22 * like `less` and `more` can be used to page large amounts of output.
|
Chris@13
|
23 */
|
Chris@13
|
24 class ProcOutputPager extends StreamOutput implements OutputPager
|
Chris@13
|
25 {
|
Chris@13
|
26 private $proc;
|
Chris@13
|
27 private $pipe;
|
Chris@13
|
28 private $stream;
|
Chris@13
|
29 private $cmd;
|
Chris@13
|
30
|
Chris@13
|
31 /**
|
Chris@13
|
32 * Constructor.
|
Chris@13
|
33 *
|
Chris@13
|
34 * @param StreamOutput $output
|
Chris@13
|
35 * @param string $cmd Pager process command (default: 'less -R -S -F -X')
|
Chris@13
|
36 */
|
Chris@13
|
37 public function __construct(StreamOutput $output, $cmd = 'less -R -S -F -X')
|
Chris@13
|
38 {
|
Chris@13
|
39 $this->stream = $output->getStream();
|
Chris@13
|
40 $this->cmd = $cmd;
|
Chris@13
|
41 }
|
Chris@13
|
42
|
Chris@13
|
43 /**
|
Chris@13
|
44 * Writes a message to the output.
|
Chris@13
|
45 *
|
Chris@13
|
46 * @param string $message A message to write to the output
|
Chris@13
|
47 * @param bool $newline Whether to add a newline or not
|
Chris@13
|
48 *
|
Chris@13
|
49 * @throws \RuntimeException When unable to write output (should never happen)
|
Chris@13
|
50 */
|
Chris@13
|
51 public function doWrite($message, $newline)
|
Chris@13
|
52 {
|
Chris@13
|
53 $pipe = $this->getPipe();
|
Chris@17
|
54 if (false === @\fwrite($pipe, $message . ($newline ? PHP_EOL : ''))) {
|
Chris@13
|
55 // @codeCoverageIgnoreStart
|
Chris@13
|
56 // should never happen
|
Chris@13
|
57 throw new \RuntimeException('Unable to write output');
|
Chris@13
|
58 // @codeCoverageIgnoreEnd
|
Chris@13
|
59 }
|
Chris@13
|
60
|
Chris@17
|
61 \fflush($pipe);
|
Chris@13
|
62 }
|
Chris@13
|
63
|
Chris@13
|
64 /**
|
Chris@13
|
65 * Close the current pager process.
|
Chris@13
|
66 */
|
Chris@13
|
67 public function close()
|
Chris@13
|
68 {
|
Chris@13
|
69 if (isset($this->pipe)) {
|
Chris@17
|
70 \fclose($this->pipe);
|
Chris@13
|
71 }
|
Chris@13
|
72
|
Chris@13
|
73 if (isset($this->proc)) {
|
Chris@17
|
74 $exit = \proc_close($this->proc);
|
Chris@13
|
75 if ($exit !== 0) {
|
Chris@13
|
76 throw new \RuntimeException('Error closing output stream');
|
Chris@13
|
77 }
|
Chris@13
|
78 }
|
Chris@13
|
79
|
Chris@13
|
80 unset($this->pipe, $this->proc);
|
Chris@13
|
81 }
|
Chris@13
|
82
|
Chris@13
|
83 /**
|
Chris@13
|
84 * Get a pipe for paging output.
|
Chris@13
|
85 *
|
Chris@13
|
86 * If no active pager process exists, fork one and return its input pipe.
|
Chris@13
|
87 */
|
Chris@13
|
88 private function getPipe()
|
Chris@13
|
89 {
|
Chris@13
|
90 if (!isset($this->pipe) || !isset($this->proc)) {
|
Chris@17
|
91 $desc = [['pipe', 'r'], $this->stream, \fopen('php://stderr', 'w')];
|
Chris@17
|
92 $this->proc = \proc_open($this->cmd, $desc, $pipes);
|
Chris@13
|
93
|
Chris@17
|
94 if (!\is_resource($this->proc)) {
|
Chris@13
|
95 throw new \RuntimeException('Error opening output stream');
|
Chris@13
|
96 }
|
Chris@13
|
97
|
Chris@13
|
98 $this->pipe = $pipes[0];
|
Chris@13
|
99 }
|
Chris@13
|
100
|
Chris@13
|
101 return $this->pipe;
|
Chris@13
|
102 }
|
Chris@13
|
103 }
|