Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0f31973

Browse files
committed
Remove ProgressBar "fork", replace with tiny wrapper
This is preparation for symfony/console 4.4 which solves major performance issue that was biggest reason for this C&P
1 parent 12717f5 commit 0f31973

3 files changed

Lines changed: 44 additions & 151 deletions

File tree

src/Bufferer/PipeBufferer.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public function __invoke(): Promise
8989
while (null !== $chunk = yield $this->inputStream->read()) {
9090
yield $this->outputStream->write($chunk);
9191

92-
if ($this->progressBar->step === 0) {
92+
if ($this->progressBar->getProgress() === 0) {
9393
$mimeType = (new \finfo(FILEINFO_MIME))->buffer($chunk);
9494
$this->logger->debug(sprintf('Stdin MIME type detected: "%s"', $mimeType));
9595
$this->mimeType->resolve($mimeType);
@@ -99,7 +99,7 @@ public function __invoke(): Promise
9999
$this->resolveDeferrer();
100100

101101
// This happens after write in order to give bufferer a chance to detect mimeType even if bufferSize is 0
102-
if ($this->progressBar->step < $this->bufferSize) {
102+
if ($this->progressBar->getProgress() < $this->bufferSize) {
103103
continue;
104104
}
105105

@@ -116,7 +116,7 @@ public function __invoke(): Promise
116116

117117
$this->buffering = false;
118118
$this->progressBar->finish();
119-
$this->logger->debug("Buffering to file stopped, {$this->progressBar->step} bytes stored");
119+
$this->logger->debug("Buffering to file stopped, {$this->progressBar->getProgress()} bytes stored");
120120
$this->resolveDeferrer();
121121
};
122122

@@ -140,7 +140,7 @@ public function waitForWrite(): Promise
140140

141141
public function getCurrentProgress(): int
142142
{
143-
return $this->progressBar->step;
143+
return $this->progressBar->getProgress();
144144
}
145145

146146
private function resolveDeferrer(): void

src/ProgressBar.php

Lines changed: 38 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -5,171 +5,64 @@
55
namespace Ostrolucky\Stdinho;
66

77
use Symfony\Component\Console\Helper\Helper;
8+
use Symfony\Component\Console\Helper\ProgressBar as SymfonyProgressBar;
89
use Symfony\Component\Console\Output\ConsoleSectionOutput;
9-
use Symfony\Component\Console\Terminal;
1010

11-
/**
12-
* Fork of Symfony ProgressBar, necessary because of multiple problems in Symfony one, such as:
13-
* - Massive performance hit due to redraw frequency being step based instead of time based, see https://github.com/symfony/symfony/pull/26339
14-
* - There is no official mechanism to pass custom data to placeholders https://github.com/symfony/symfony/issues/31193.
15-
* I would have to pass dynamic props each time after initializing ProgressBar and rely on presence of these in callbacks.
16-
* - Since symfony/console 4.2.3 ProgressBar does redraws with single call to output which in theory allows to write Output
17-
* throttling decorator, but in practice ProgressBar does instanceof checks for ConsoleSectionOutput, which makes this difficult.
18-
* Even if this problem is solved, there is another problem - combo of clear() and write*( calls, which need to be carefully synced.
19-
*/
11+
SymfonyProgressBar::setFormatDefinition(
12+
'buffer',
13+
'[Stdin] Buffered: %current_volume% | %speed% | %elapsed_label%: %elapsed%'
14+
);
15+
SymfonyProgressBar::setFormatDefinition(
16+
'portal',
17+
'[%host%] Downloaded: %current_volume% | %speed% | %elapsed%/%estimated% | %percent%%'
18+
);
19+
SymfonyProgressBar::setPlaceholderFormatterDefinition('current_volume', function (SymfonyProgressBar $bar) {
20+
return Helper::formatMemory($bar->getProgress());
21+
});
22+
SymfonyProgressBar::setPlaceholderFormatterDefinition('elapsed_label', function (SymfonyProgressBar $bar) {
23+
return $bar->getMaxSteps() >= $bar->getProgress() ? '<info>Finished</info> in' : 'Elapsed';
24+
});
25+
SymfonyProgressBar::setPlaceholderFormatterDefinition('speed', function (SymfonyProgressBar $bar) {
26+
return Helper::formatMemory($bar->getProgress() / max(1, time() - $bar->getStartTime())).'/s';
27+
});
28+
SymfonyProgressBar::setPlaceholderFormatterDefinition('max_volume', function (SymfonyProgressBar $bar) {
29+
return Helper::formatMemory($bar->getMaxSteps());
30+
});
31+
SymfonyProgressBar::setPlaceholderFormatterDefinition('host', function (SymfonyProgressBar $bar) {
32+
return Helper::formatMemory($bar->host);
33+
});
34+
2035
class ProgressBar
2136
{
2237
/**
23-
* @var string[]
24-
*/
25-
private $format = [];
26-
/**
27-
* @var callable[]
28-
*/
29-
private $placeholders = [];
30-
/**
31-
* @var null|float
32-
*/
33-
private $lastWriteTime;
34-
/**
35-
* @var ConsoleSectionOutput
36-
*/
37-
private $output;
38-
/**
39-
* @var int
40-
*/
41-
public $step = 0;
42-
/**
43-
* @var int
44-
*/
45-
public $max;
46-
/**
47-
* @var int
48-
*/
49-
private $startTime;
50-
/**
51-
* @var Terminal
38+
* @var SymfonyProgressBar
5239
*/
53-
private $terminal;
54-
55-
/**
56-
* @var bool
57-
*/
58-
private $firstRun = true;
40+
private $wrappedProgressBar;
5941

6042
public function __construct(ConsoleSectionOutput $output, int $max, string $format, string $host = null)
6143
{
62-
$this->output = $output;
63-
$this->max = $max;
64-
$this->format = [
65-
'buffer' => '[Stdin] Buffered: %current_volume% | %speed% | %elapsed_label%: %elapsed%',
66-
'portal' => '[%host%] Downloaded: %current_volume% | %speed% | %elapsed%/%estimated% | %percent%%',
67-
][$format];
68-
$this->placeholders = [
69-
'elapsed' => function (self $bar) {
70-
return Helper::formatTime(time() - $bar->startTime);
71-
},
72-
'estimated' => function (self $bar) {
73-
$progress = $bar->step;
74-
75-
return Helper::formatTime($progress ? round((time() - $bar->startTime) / $progress * $bar->max) : 0);
76-
},
77-
'current' => function (self $bar) {
78-
return str_pad($bar->step, strlen($bar->max), ' ', STR_PAD_LEFT);
79-
},
80-
'percent' => function (self $bar) {
81-
return $bar->max ? floor($bar->step / $bar->max * 100) : 0;
82-
},
83-
'current_volume' => function (self $bar) {
84-
return Helper::formatMemory($bar->step);
85-
},
86-
'elapsed_label' => function (self $bar) {
87-
return $bar->max >= $bar->step ? '<info>Finished</info> in' : 'Elapsed';
88-
},
89-
'speed' => function (self $bar) {
90-
return Helper::formatMemory($bar->step / max(1, time() - $bar->startTime)).'/s';
91-
},
92-
'max_volume' => function (self $bar) {
93-
return Helper::formatMemory($bar->max);
94-
},
95-
'host' => function () use ($host) {
96-
return $host;
97-
},
98-
];
99-
$this->terminal = new Terminal();
100-
$this->startTime = time();
44+
$this->wrappedProgressBar = new SymfonyProgressBar($output, $max);
45+
$this->wrappedProgressBar->setFormat($format);
46+
$this->wrappedProgressBar->host = $host;
10147
}
10248

103-
/**
104-
* Advances the progress output X steps.
105-
*
106-
* @param int $step Number of steps to advance
107-
*/
108-
public function advance(int $step): void
49+
public function getProgress(): int
10950
{
110-
$this->step += $step;
111-
112-
if (microtime(true) - $this->lastWriteTime < ($this->output->isDecorated() ? .1 : 1)) {
113-
return;
114-
}
115-
116-
$this->overwrite($this->buildLine());
51+
return $this->wrappedProgressBar->getProgress();
11752
}
11853

119-
/**
120-
* Finishes the progress output.
121-
*/
122-
public function finish(): void
54+
public function advance(int $step): void
12355
{
124-
if ($this->step > $this->max) {
125-
$this->max = $this->step;
126-
}
127-
128-
$this->overwrite($this->buildLine());
56+
$this->wrappedProgressBar->advance($step);
12957
}
13058

131-
/**
132-
* Overwrites a previous message to the output.
133-
*/
134-
private function overwrite(string $message): void
59+
public function setMaxSteps(int $max): void
13560
{
136-
if (!$this->firstRun) {
137-
$this->output->clear(1);
138-
}
139-
140-
$this->firstRun = false;
141-
$this->lastWriteTime = microtime(true);
142-
$this->output->write($message, !$this->output->isDecorated());
61+
$this->wrappedProgressBar->setMaxSteps($max);
14362
}
14463

145-
private function buildLine(): string
64+
public function finish(): void
14665
{
147-
$regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
148-
$callback = function ($matches) {
149-
$text = call_user_func($this->placeholders[$matches[1]], $this, $this->output);
150-
151-
if (isset($matches[2])) {
152-
$text = sprintf('%'.$matches[2], $text);
153-
}
154-
155-
return $text;
156-
};
157-
$line = preg_replace_callback($regex, $callback, $this->format);
158-
159-
// gets string length for each sub line with multiline format
160-
$linesLength = array_map(
161-
function ($subLine) {
162-
return Helper::strlenWithoutDecoration($this->output->getFormatter(), rtrim($subLine, "\r"));
163-
}, explode("\n", $line)
164-
);
165-
166-
$linesWidth = max($linesLength);
167-
168-
$terminalWidth = $this->terminal->getWidth();
169-
if ($linesWidth <= $terminalWidth) {
170-
return $line;
171-
}
172-
173-
return preg_replace_callback($regex, $callback, $this->format);
66+
$this->wrappedProgressBar->finish();
17467
}
17568
}

src/Responder.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ private function write(Socket $socket, ProgressBar $progressBar): \Generator
116116
* @see https://github.com/ostrolucky/stdinho/pull/2
117117
* @see https://github.com/amphp/byte-stream/issues/47
118118
*/
119-
if ($buffererProgress <= $progressBar->step && $this->bufferer->isBuffering()) {
119+
if ($buffererProgress <= $progressBar->getProgress() && $this->bufferer->isBuffering()) {
120120
yield $this->bufferer->waitForWrite();
121121

122122
continue;
@@ -125,7 +125,7 @@ private function write(Socket $socket, ProgressBar $progressBar): \Generator
125125
if (null !== $chunk = yield $this->inputStream->read()) {
126126
yield $socket->write($chunk);
127127

128-
$progressBar->max = $this->bufferer->getCurrentProgress();
128+
$progressBar->setMaxSteps($this->bufferer->getCurrentProgress());
129129
$progressBar->advance(strlen($chunk));
130130

131131
continue;

0 commit comments

Comments
 (0)