| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 | <?php/* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */namespace Symfony\Component\Console\Helper;use Symfony\Component\Console\Exception\InvalidArgumentException;use Symfony\Component\Console\Exception\LogicException;use Symfony\Component\Console\Output\OutputInterface;/** * @author Kevin Bond <kevinbond@gmail.com> */class ProgressIndicator{    private $output;    private $startTime;    private $format;    private $message;    private $indicatorValues;    private $indicatorCurrent;    private $indicatorChangeInterval;    private $indicatorUpdateTime;    private $started = false;    private static $formatters;    private static $formats;    /**     * @param string|null $format                  Indicator format     * @param int         $indicatorChangeInterval Change interval in milliseconds     * @param array|null  $indicatorValues         Animated indicator characters     */    public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null)    {        $this->output = $output;        if (null === $format) {            $format = $this->determineBestFormat();        }        if (null === $indicatorValues) {            $indicatorValues = ['-', '\\', '|', '/'];        }        $indicatorValues = array_values($indicatorValues);        if (2 > \count($indicatorValues)) {            throw new InvalidArgumentException('Must have at least 2 indicator value characters.');        }        $this->format = self::getFormatDefinition($format);        $this->indicatorChangeInterval = $indicatorChangeInterval;        $this->indicatorValues = $indicatorValues;        $this->startTime = time();    }    /**     * Sets the current indicator message.     *     * @param string|null $message     */    public function setMessage($message)    {        $this->message = $message;        $this->display();    }    /**     * Starts the indicator output.     *     * @param $message     */    public function start($message)    {        if ($this->started) {            throw new LogicException('Progress indicator already started.');        }        $this->message = $message;        $this->started = true;        $this->startTime = time();        $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval;        $this->indicatorCurrent = 0;        $this->display();    }    /**     * Advances the indicator.     */    public function advance()    {        if (!$this->started) {            throw new LogicException('Progress indicator has not yet been started.');        }        if (!$this->output->isDecorated()) {            return;        }        $currentTime = $this->getCurrentTimeInMilliseconds();        if ($currentTime < $this->indicatorUpdateTime) {            return;        }        $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval;        ++$this->indicatorCurrent;        $this->display();    }    /**     * Finish the indicator with message.     *     * @param $message     */    public function finish($message)    {        if (!$this->started) {            throw new LogicException('Progress indicator has not yet been started.');        }        $this->message = $message;        $this->display();        $this->output->writeln('');        $this->started = false;    }    /**     * Gets the format for a given name.     *     * @param string $name The format name     *     * @return string|null A format string     */    public static function getFormatDefinition($name)    {        if (!self::$formats) {            self::$formats = self::initFormats();        }        return isset(self::$formats[$name]) ? self::$formats[$name] : null;    }    /**     * Sets a placeholder formatter for a given name.     *     * This method also allow you to override an existing placeholder.     *     * @param string   $name     The placeholder name (including the delimiter char like %)     * @param callable $callable A PHP callable     */    public static function setPlaceholderFormatterDefinition($name, $callable)    {        if (!self::$formatters) {            self::$formatters = self::initPlaceholderFormatters();        }        self::$formatters[$name] = $callable;    }    /**     * Gets the placeholder formatter for a given name.     *     * @param string $name The placeholder name (including the delimiter char like %)     *     * @return callable|null A PHP callable     */    public static function getPlaceholderFormatterDefinition($name)    {        if (!self::$formatters) {            self::$formatters = self::initPlaceholderFormatters();        }        return isset(self::$formatters[$name]) ? self::$formatters[$name] : null;    }    private function display()    {        if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {            return;        }        $self = $this;        $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) {            if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) {                return \call_user_func($formatter, $self);            }            return $matches[0];        }, $this->format));    }    private function determineBestFormat()    {        switch ($this->output->getVerbosity()) {            // OutputInterface::VERBOSITY_QUIET: display is disabled anyway            case OutputInterface::VERBOSITY_VERBOSE:                return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi';            case OutputInterface::VERBOSITY_VERY_VERBOSE:            case OutputInterface::VERBOSITY_DEBUG:                return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi';            default:                return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi';        }    }    /**     * Overwrites a previous message to the output.     *     * @param string $message The message     */    private function overwrite($message)    {        if ($this->output->isDecorated()) {            $this->output->write("\x0D\x1B[2K");            $this->output->write($message);        } else {            $this->output->writeln($message);        }    }    private function getCurrentTimeInMilliseconds()    {        return round(microtime(true) * 1000);    }    private static function initPlaceholderFormatters()    {        return [            'indicator' => function (self $indicator) {                return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)];            },            'message' => function (self $indicator) {                return $indicator->message;            },            'elapsed' => function (self $indicator) {                return Helper::formatTime(time() - $indicator->startTime);            },            'memory' => function () {                return Helper::formatMemory(memory_get_usage(true));            },        ];    }    private static function initFormats()    {        return [            'normal' => ' %indicator% %message%',            'normal_no_ansi' => ' %message%',            'verbose' => ' %indicator% %message% (%elapsed:6s%)',            'verbose_no_ansi' => ' %message% (%elapsed:6s%)',            'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)',            'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)',        ];    }}
 |