[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <[email protected]> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12 namespace Symfony\Component\Console\Helper; 13 14 use Symfony\Component\Console\Cursor; 15 use Symfony\Component\Console\Exception\LogicException; 16 use Symfony\Component\Console\Output\ConsoleOutputInterface; 17 use Symfony\Component\Console\Output\ConsoleSectionOutput; 18 use Symfony\Component\Console\Output\OutputInterface; 19 use Symfony\Component\Console\Terminal; 20 21 /** 22 * The ProgressBar provides helpers to display progress output. 23 * 24 * @author Fabien Potencier <[email protected]> 25 * @author Chris Jones <[email protected]> 26 */ 27 final class ProgressBar 28 { 29 public const FORMAT_VERBOSE = 'verbose'; 30 public const FORMAT_VERY_VERBOSE = 'very_verbose'; 31 public const FORMAT_DEBUG = 'debug'; 32 public const FORMAT_NORMAL = 'normal'; 33 34 private const FORMAT_VERBOSE_NOMAX = 'verbose_nomax'; 35 private const FORMAT_VERY_VERBOSE_NOMAX = 'very_verbose_nomax'; 36 private const FORMAT_DEBUG_NOMAX = 'debug_nomax'; 37 private const FORMAT_NORMAL_NOMAX = 'normal_nomax'; 38 39 private $barWidth = 28; 40 private $barChar; 41 private $emptyBarChar = '-'; 42 private $progressChar = '>'; 43 private $format; 44 private $internalFormat; 45 private $redrawFreq = 1; 46 private $writeCount; 47 private $lastWriteTime; 48 private $minSecondsBetweenRedraws = 0; 49 private $maxSecondsBetweenRedraws = 1; 50 private $output; 51 private $step = 0; 52 private $max; 53 private $startTime; 54 private $stepWidth; 55 private $percent = 0.0; 56 private $formatLineCount; 57 private $messages = []; 58 private $overwrite = true; 59 private $terminal; 60 private $previousMessage; 61 private $cursor; 62 63 private static $formatters; 64 private static $formats; 65 66 /** 67 * @param int $max Maximum steps (0 if unknown) 68 */ 69 public function __construct(OutputInterface $output, int $max = 0, float $minSecondsBetweenRedraws = 1 / 25) 70 { 71 if ($output instanceof ConsoleOutputInterface) { 72 $output = $output->getErrorOutput(); 73 } 74 75 $this->output = $output; 76 $this->setMaxSteps($max); 77 $this->terminal = new Terminal(); 78 79 if (0 < $minSecondsBetweenRedraws) { 80 $this->redrawFreq = null; 81 $this->minSecondsBetweenRedraws = $minSecondsBetweenRedraws; 82 } 83 84 if (!$this->output->isDecorated()) { 85 // disable overwrite when output does not support ANSI codes. 86 $this->overwrite = false; 87 88 // set a reasonable redraw frequency so output isn't flooded 89 $this->redrawFreq = null; 90 } 91 92 $this->startTime = time(); 93 $this->cursor = new Cursor($output); 94 } 95 96 /** 97 * Sets a placeholder formatter for a given name. 98 * 99 * This method also allow you to override an existing placeholder. 100 * 101 * @param string $name The placeholder name (including the delimiter char like %) 102 * @param callable $callable A PHP callable 103 */ 104 public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void 105 { 106 if (!self::$formatters) { 107 self::$formatters = self::initPlaceholderFormatters(); 108 } 109 110 self::$formatters[$name] = $callable; 111 } 112 113 /** 114 * Gets the placeholder formatter for a given name. 115 * 116 * @param string $name The placeholder name (including the delimiter char like %) 117 */ 118 public static function getPlaceholderFormatterDefinition(string $name): ?callable 119 { 120 if (!self::$formatters) { 121 self::$formatters = self::initPlaceholderFormatters(); 122 } 123 124 return self::$formatters[$name] ?? null; 125 } 126 127 /** 128 * Sets a format for a given name. 129 * 130 * This method also allow you to override an existing format. 131 * 132 * @param string $name The format name 133 * @param string $format A format string 134 */ 135 public static function setFormatDefinition(string $name, string $format): void 136 { 137 if (!self::$formats) { 138 self::$formats = self::initFormats(); 139 } 140 141 self::$formats[$name] = $format; 142 } 143 144 /** 145 * Gets the format for a given name. 146 * 147 * @param string $name The format name 148 */ 149 public static function getFormatDefinition(string $name): ?string 150 { 151 if (!self::$formats) { 152 self::$formats = self::initFormats(); 153 } 154 155 return self::$formats[$name] ?? null; 156 } 157 158 /** 159 * Associates a text with a named placeholder. 160 * 161 * The text is displayed when the progress bar is rendered but only 162 * when the corresponding placeholder is part of the custom format line 163 * (by wrapping the name with %). 164 * 165 * @param string $message The text to associate with the placeholder 166 * @param string $name The name of the placeholder 167 */ 168 public function setMessage(string $message, string $name = 'message') 169 { 170 $this->messages[$name] = $message; 171 } 172 173 public function getMessage(string $name = 'message') 174 { 175 return $this->messages[$name]; 176 } 177 178 public function getStartTime(): int 179 { 180 return $this->startTime; 181 } 182 183 public function getMaxSteps(): int 184 { 185 return $this->max; 186 } 187 188 public function getProgress(): int 189 { 190 return $this->step; 191 } 192 193 private function getStepWidth(): int 194 { 195 return $this->stepWidth; 196 } 197 198 public function getProgressPercent(): float 199 { 200 return $this->percent; 201 } 202 203 public function getBarOffset(): float 204 { 205 return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth); 206 } 207 208 public function getEstimated(): float 209 { 210 if (!$this->step) { 211 return 0; 212 } 213 214 return round((time() - $this->startTime) / $this->step * $this->max); 215 } 216 217 public function getRemaining(): float 218 { 219 if (!$this->step) { 220 return 0; 221 } 222 223 return round((time() - $this->startTime) / $this->step * ($this->max - $this->step)); 224 } 225 226 public function setBarWidth(int $size) 227 { 228 $this->barWidth = max(1, $size); 229 } 230 231 public function getBarWidth(): int 232 { 233 return $this->barWidth; 234 } 235 236 public function setBarCharacter(string $char) 237 { 238 $this->barChar = $char; 239 } 240 241 public function getBarCharacter(): string 242 { 243 return $this->barChar ?? ($this->max ? '=' : $this->emptyBarChar); 244 } 245 246 public function setEmptyBarCharacter(string $char) 247 { 248 $this->emptyBarChar = $char; 249 } 250 251 public function getEmptyBarCharacter(): string 252 { 253 return $this->emptyBarChar; 254 } 255 256 public function setProgressCharacter(string $char) 257 { 258 $this->progressChar = $char; 259 } 260 261 public function getProgressCharacter(): string 262 { 263 return $this->progressChar; 264 } 265 266 public function setFormat(string $format) 267 { 268 $this->format = null; 269 $this->internalFormat = $format; 270 } 271 272 /** 273 * Sets the redraw frequency. 274 * 275 * @param int|null $freq The frequency in steps 276 */ 277 public function setRedrawFrequency(?int $freq) 278 { 279 $this->redrawFreq = null !== $freq ? max(1, $freq) : null; 280 } 281 282 public function minSecondsBetweenRedraws(float $seconds): void 283 { 284 $this->minSecondsBetweenRedraws = $seconds; 285 } 286 287 public function maxSecondsBetweenRedraws(float $seconds): void 288 { 289 $this->maxSecondsBetweenRedraws = $seconds; 290 } 291 292 /** 293 * Returns an iterator that will automatically update the progress bar when iterated. 294 * 295 * @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable 296 */ 297 public function iterate(iterable $iterable, int $max = null): iterable 298 { 299 $this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0)); 300 301 foreach ($iterable as $key => $value) { 302 yield $key => $value; 303 304 $this->advance(); 305 } 306 307 $this->finish(); 308 } 309 310 /** 311 * Starts the progress output. 312 * 313 * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged 314 */ 315 public function start(int $max = null) 316 { 317 $this->startTime = time(); 318 $this->step = 0; 319 $this->percent = 0.0; 320 321 if (null !== $max) { 322 $this->setMaxSteps($max); 323 } 324 325 $this->display(); 326 } 327 328 /** 329 * Advances the progress output X steps. 330 * 331 * @param int $step Number of steps to advance 332 */ 333 public function advance(int $step = 1) 334 { 335 $this->setProgress($this->step + $step); 336 } 337 338 /** 339 * Sets whether to overwrite the progressbar, false for new line. 340 */ 341 public function setOverwrite(bool $overwrite) 342 { 343 $this->overwrite = $overwrite; 344 } 345 346 public function setProgress(int $step) 347 { 348 if ($this->max && $step > $this->max) { 349 $this->max = $step; 350 } elseif ($step < 0) { 351 $step = 0; 352 } 353 354 $redrawFreq = $this->redrawFreq ?? (($this->max ?: 10) / 10); 355 $prevPeriod = (int) ($this->step / $redrawFreq); 356 $currPeriod = (int) ($step / $redrawFreq); 357 $this->step = $step; 358 $this->percent = $this->max ? (float) $this->step / $this->max : 0; 359 $timeInterval = microtime(true) - $this->lastWriteTime; 360 361 // Draw regardless of other limits 362 if ($this->max === $step) { 363 $this->display(); 364 365 return; 366 } 367 368 // Throttling 369 if ($timeInterval < $this->minSecondsBetweenRedraws) { 370 return; 371 } 372 373 // Draw each step period, but not too late 374 if ($prevPeriod !== $currPeriod || $timeInterval >= $this->maxSecondsBetweenRedraws) { 375 $this->display(); 376 } 377 } 378 379 public function setMaxSteps(int $max) 380 { 381 $this->format = null; 382 $this->max = max(0, $max); 383 $this->stepWidth = $this->max ? Helper::width((string) $this->max) : 4; 384 } 385 386 /** 387 * Finishes the progress output. 388 */ 389 public function finish(): void 390 { 391 if (!$this->max) { 392 $this->max = $this->step; 393 } 394 395 if ($this->step === $this->max && !$this->overwrite) { 396 // prevent double 100% output 397 return; 398 } 399 400 $this->setProgress($this->max); 401 } 402 403 /** 404 * Outputs the current progress string. 405 */ 406 public function display(): void 407 { 408 if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { 409 return; 410 } 411 412 if (null === $this->format) { 413 $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); 414 } 415 416 $this->overwrite($this->buildLine()); 417 } 418 419 /** 420 * Removes the progress bar from the current line. 421 * 422 * This is useful if you wish to write some output 423 * while a progress bar is running. 424 * Call display() to show the progress bar again. 425 */ 426 public function clear(): void 427 { 428 if (!$this->overwrite) { 429 return; 430 } 431 432 if (null === $this->format) { 433 $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); 434 } 435 436 $this->overwrite(''); 437 } 438 439 private function setRealFormat(string $format) 440 { 441 // try to use the _nomax variant if available 442 if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { 443 $this->format = self::getFormatDefinition($format.'_nomax'); 444 } elseif (null !== self::getFormatDefinition($format)) { 445 $this->format = self::getFormatDefinition($format); 446 } else { 447 $this->format = $format; 448 } 449 450 $this->formatLineCount = substr_count($this->format, "\n"); 451 } 452 453 /** 454 * Overwrites a previous message to the output. 455 */ 456 private function overwrite(string $message): void 457 { 458 if ($this->previousMessage === $message) { 459 return; 460 } 461 462 $originalMessage = $message; 463 464 if ($this->overwrite) { 465 if (null !== $this->previousMessage) { 466 if ($this->output instanceof ConsoleSectionOutput) { 467 $messageLines = explode("\n", $message); 468 $lineCount = \count($messageLines); 469 foreach ($messageLines as $messageLine) { 470 $messageLineLength = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $messageLine)); 471 if ($messageLineLength > $this->terminal->getWidth()) { 472 $lineCount += floor($messageLineLength / $this->terminal->getWidth()); 473 } 474 } 475 $this->output->clear($lineCount); 476 } else { 477 for ($i = 0; $i < $this->formatLineCount; ++$i) { 478 $this->cursor->moveToColumn(1); 479 $this->cursor->clearLine(); 480 $this->cursor->moveUp(); 481 } 482 483 $this->cursor->moveToColumn(1); 484 $this->cursor->clearLine(); 485 } 486 } 487 } elseif ($this->step > 0) { 488 $message = \PHP_EOL.$message; 489 } 490 491 $this->previousMessage = $originalMessage; 492 $this->lastWriteTime = microtime(true); 493 494 $this->output->write($message); 495 ++$this->writeCount; 496 } 497 498 private function determineBestFormat(): string 499 { 500 switch ($this->output->getVerbosity()) { 501 // OutputInterface::VERBOSITY_QUIET: display is disabled anyway 502 case OutputInterface::VERBOSITY_VERBOSE: 503 return $this->max ? self::FORMAT_VERBOSE : self::FORMAT_VERBOSE_NOMAX; 504 case OutputInterface::VERBOSITY_VERY_VERBOSE: 505 return $this->max ? self::FORMAT_VERY_VERBOSE : self::FORMAT_VERY_VERBOSE_NOMAX; 506 case OutputInterface::VERBOSITY_DEBUG: 507 return $this->max ? self::FORMAT_DEBUG : self::FORMAT_DEBUG_NOMAX; 508 default: 509 return $this->max ? self::FORMAT_NORMAL : self::FORMAT_NORMAL_NOMAX; 510 } 511 } 512 513 private static function initPlaceholderFormatters(): array 514 { 515 return [ 516 'bar' => function (self $bar, OutputInterface $output) { 517 $completeBars = $bar->getBarOffset(); 518 $display = str_repeat($bar->getBarCharacter(), $completeBars); 519 if ($completeBars < $bar->getBarWidth()) { 520 $emptyBars = $bar->getBarWidth() - $completeBars - Helper::length(Helper::removeDecoration($output->getFormatter(), $bar->getProgressCharacter())); 521 $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); 522 } 523 524 return $display; 525 }, 526 'elapsed' => function (self $bar) { 527 return Helper::formatTime(time() - $bar->getStartTime()); 528 }, 529 'remaining' => function (self $bar) { 530 if (!$bar->getMaxSteps()) { 531 throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); 532 } 533 534 return Helper::formatTime($bar->getRemaining()); 535 }, 536 'estimated' => function (self $bar) { 537 if (!$bar->getMaxSteps()) { 538 throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); 539 } 540 541 return Helper::formatTime($bar->getEstimated()); 542 }, 543 'memory' => function (self $bar) { 544 return Helper::formatMemory(memory_get_usage(true)); 545 }, 546 'current' => function (self $bar) { 547 return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT); 548 }, 549 'max' => function (self $bar) { 550 return $bar->getMaxSteps(); 551 }, 552 'percent' => function (self $bar) { 553 return floor($bar->getProgressPercent() * 100); 554 }, 555 ]; 556 } 557 558 private static function initFormats(): array 559 { 560 return [ 561 self::FORMAT_NORMAL => ' %current%/%max% [%bar%] %percent:3s%%', 562 self::FORMAT_NORMAL_NOMAX => ' %current% [%bar%]', 563 564 self::FORMAT_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', 565 self::FORMAT_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%', 566 567 self::FORMAT_VERY_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', 568 self::FORMAT_VERY_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%', 569 570 self::FORMAT_DEBUG => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', 571 self::FORMAT_DEBUG_NOMAX => ' %current% [%bar%] %elapsed:6s% %memory:6s%', 572 ]; 573 } 574 575 private function buildLine(): string 576 { 577 $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i"; 578 $callback = function ($matches) { 579 if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) { 580 $text = $formatter($this, $this->output); 581 } elseif (isset($this->messages[$matches[1]])) { 582 $text = $this->messages[$matches[1]]; 583 } else { 584 return $matches[0]; 585 } 586 587 if (isset($matches[2])) { 588 $text = sprintf('%'.$matches[2], $text); 589 } 590 591 return $text; 592 }; 593 $line = preg_replace_callback($regex, $callback, $this->format); 594 595 // gets string length for each sub line with multiline format 596 $linesLength = array_map(function ($subLine) { 597 return Helper::width(Helper::removeDecoration($this->output->getFormatter(), rtrim($subLine, "\r"))); 598 }, explode("\n", $line)); 599 600 $linesWidth = max($linesLength); 601 602 $terminalWidth = $this->terminal->getWidth(); 603 if ($linesWidth <= $terminalWidth) { 604 return $line; 605 } 606 607 $this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth); 608 609 return preg_replace_callback($regex, $callback, $this->format); 610 } 611 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |