Upgrade framework

This commit is contained in:
2023-11-14 16:54:35 +01:00
parent 1648a5cd42
commit 4fcf6fffcc
10548 changed files with 693138 additions and 466698 deletions

View File

@@ -0,0 +1,69 @@
<?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\Formatter;
/**
* @author Tien Xuan Vo <tien.xuan.vo@gmail.com>
*/
final class NullOutputFormatter implements OutputFormatterInterface
{
private $style;
/**
* {@inheritdoc}
*/
public function format(?string $message): ?string
{
return null;
}
/**
* {@inheritdoc}
*/
public function getStyle(string $name): OutputFormatterStyleInterface
{
// to comply with the interface we must return a OutputFormatterStyleInterface
return $this->style ?? $this->style = new NullOutputFormatterStyle();
}
/**
* {@inheritdoc}
*/
public function hasStyle(string $name): bool
{
return false;
}
/**
* {@inheritdoc}
*/
public function isDecorated(): bool
{
return false;
}
/**
* {@inheritdoc}
*/
public function setDecorated(bool $decorated): void
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function setStyle(string $name, OutputFormatterStyleInterface $style): void
{
// do nothing
}
}

View File

@@ -0,0 +1,66 @@
<?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\Formatter;
/**
* @author Tien Xuan Vo <tien.xuan.vo@gmail.com>
*/
final class NullOutputFormatterStyle implements OutputFormatterStyleInterface
{
/**
* {@inheritdoc}
*/
public function apply(string $text): string
{
return $text;
}
/**
* {@inheritdoc}
*/
public function setBackground(string $color = null): void
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function setForeground(string $color = null): void
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function setOption(string $option): void
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function setOptions(array $options): void
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function unsetOption(string $option): void
{
// do nothing
}
}

View File

@@ -17,23 +17,28 @@ use Symfony\Component\Console\Exception\InvalidArgumentException;
* Formatter class for console output.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
* @author Roland Franssen <franssen.roland@gmail.com>
*/
class OutputFormatter implements OutputFormatterInterface
class OutputFormatter implements WrappableOutputFormatterInterface
{
private $decorated;
private $styles = array();
private bool $decorated;
private array $styles = [];
private $styleStack;
/**
* Escapes "<" special char in given text.
*
* @param string $text Text to escape
*
* @return string Escaped text
*/
public static function escape($text)
public function __clone()
{
$text = preg_replace('/([^\\\\]?)</', '$1\\<', $text);
$this->styleStack = clone $this->styleStack;
foreach ($this->styles as $key => $value) {
$this->styles[$key] = clone $value;
}
}
/**
* Escapes "<" and ">" special chars in given text.
*/
public static function escape(string $text): string
{
$text = preg_replace('/([^\\\\]|^)([<>])/', '$1\\\\$2', $text);
return self::escapeTrailingBackslash($text);
}
@@ -41,18 +46,15 @@ class OutputFormatter implements OutputFormatterInterface
/**
* Escapes trailing "\" in given text.
*
* @param string $text Text to escape
*
* @return string Escaped text
*
* @internal
*/
public static function escapeTrailingBackslash($text)
public static function escapeTrailingBackslash(string $text): string
{
if ('\\' === substr($text, -1)) {
$len = strlen($text);
if (str_ends_with($text, '\\')) {
$len = \strlen($text);
$text = rtrim($text, '\\');
$text .= str_repeat('<<', $len - strlen($text));
$text = str_replace("\0", '', $text);
$text .= str_repeat("\0", $len - \strlen($text));
}
return $text;
@@ -61,12 +63,11 @@ class OutputFormatter implements OutputFormatterInterface
/**
* Initializes console output formatter.
*
* @param bool $decorated Whether this formatter should actually decorate strings
* @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances
* @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances
*/
public function __construct($decorated = false, array $styles = array())
public function __construct(bool $decorated = false, array $styles = [])
{
$this->decorated = (bool) $decorated;
$this->decorated = $decorated;
$this->setStyle('error', new OutputFormatterStyle('white', 'red'));
$this->setStyle('info', new OutputFormatterStyle('green'));
@@ -83,15 +84,15 @@ class OutputFormatter implements OutputFormatterInterface
/**
* {@inheritdoc}
*/
public function setDecorated($decorated)
public function setDecorated(bool $decorated)
{
$this->decorated = (bool) $decorated;
$this->decorated = $decorated;
}
/**
* {@inheritdoc}
*/
public function isDecorated()
public function isDecorated(): bool
{
return $this->decorated;
}
@@ -99,7 +100,7 @@ class OutputFormatter implements OutputFormatterInterface
/**
* {@inheritdoc}
*/
public function setStyle($name, OutputFormatterStyleInterface $style)
public function setStyle(string $name, OutputFormatterStyleInterface $style)
{
$this->styles[strtolower($name)] = $style;
}
@@ -107,7 +108,7 @@ class OutputFormatter implements OutputFormatterInterface
/**
* {@inheritdoc}
*/
public function hasStyle($name)
public function hasStyle(string $name): bool
{
return isset($this->styles[strtolower($name)]);
}
@@ -115,10 +116,10 @@ class OutputFormatter implements OutputFormatterInterface
/**
* {@inheritdoc}
*/
public function getStyle($name)
public function getStyle(string $name): OutputFormatterStyleInterface
{
if (!$this->hasStyle($name)) {
throw new InvalidArgumentException(sprintf('Undefined style: %s', $name));
throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name));
}
return $this->styles[strtolower($name)];
@@ -127,13 +128,26 @@ class OutputFormatter implements OutputFormatterInterface
/**
* {@inheritdoc}
*/
public function format($message)
public function format(?string $message): ?string
{
$message = (string) $message;
return $this->formatAndWrap($message, 0);
}
/**
* {@inheritdoc}
*/
public function formatAndWrap(?string $message, int $width)
{
if (null === $message) {
return '';
}
$offset = 0;
$output = '';
$tagRegex = '[a-z][a-z0-9,_=;-]*+';
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
$openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*';
$closeTagRegex = '[a-z][^<>]*+';
$currentLineLength = 0;
preg_match_all("#<(($openTagRegex) | /($closeTagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
$pos = $match[1];
$text = $match[0];
@@ -143,21 +157,21 @@ class OutputFormatter implements OutputFormatterInterface
}
// add the text up to the next tag
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
$offset = $pos + strlen($text);
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
$offset = $pos + \strlen($text);
// opening tag?
if ($open = '/' != $text[1]) {
$tag = $matches[1][$i][0];
} else {
$tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
$tag = $matches[3][$i][0] ?? '';
}
if (!$open && !$tag) {
// </>
$this->styleStack->pop();
} elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
$output .= $this->applyCurrentStyle($text);
} elseif (null === $style = $this->createStyleFromString($tag)) {
$output .= $this->applyCurrentStyle($text, $output, $width, $currentLineLength);
} elseif ($open) {
$this->styleStack->push($style);
} else {
@@ -165,62 +179,49 @@ class OutputFormatter implements OutputFormatterInterface
}
}
$output .= $this->applyCurrentStyle(substr($message, $offset));
$output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength);
if (false !== strpos($output, '<<')) {
return strtr($output, array('\\<' => '<', '<<' => '\\'));
}
return str_replace('\\<', '<', $output);
return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']);
}
/**
* @return OutputFormatterStyleStack
*/
public function getStyleStack()
public function getStyleStack(): OutputFormatterStyleStack
{
return $this->styleStack;
}
/**
* Tries to create new style instance from string.
*
* @param string $string
*
* @return OutputFormatterStyle|false false if string is not format string
*/
private function createStyleFromString($string)
private function createStyleFromString(string $string): ?OutputFormatterStyleInterface
{
if (isset($this->styles[$string])) {
return $this->styles[$string];
}
if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, PREG_SET_ORDER)) {
return false;
if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, \PREG_SET_ORDER)) {
return null;
}
$style = new OutputFormatterStyle();
foreach ($matches as $match) {
array_shift($match);
$match[0] = strtolower($match[0]);
if ('fg' == $match[0]) {
$style->setForeground($match[1]);
$style->setForeground(strtolower($match[1]));
} elseif ('bg' == $match[0]) {
$style->setBackground($match[1]);
$style->setBackground(strtolower($match[1]));
} elseif ('href' === $match[0]) {
$url = preg_replace('{\\\\([<>])}', '$1', $match[1]);
$style->setHref($url);
} elseif ('options' === $match[0]) {
preg_match_all('([^,;]+)', $match[1], $options);
preg_match_all('([^,;]+)', strtolower($match[1]), $options);
$options = array_shift($options);
foreach ($options as $option) {
try {
$style->setOption($option);
} catch (\InvalidArgumentException $e) {
@trigger_error(sprintf('Unknown style options are deprecated since version 3.2 and will be removed in 4.0. Exception "%s".', $e->getMessage()), E_USER_DEPRECATED);
return false;
}
$style->setOption($option);
}
} else {
return false;
return null;
}
}
@@ -229,13 +230,51 @@ class OutputFormatter implements OutputFormatterInterface
/**
* Applies current style from stack to text, if must be applied.
*
* @param string $text Input text
*
* @return string Styled text
*/
private function applyCurrentStyle($text)
private function applyCurrentStyle(string $text, string $current, int $width, int &$currentLineLength): string
{
return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
if ('' === $text) {
return '';
}
if (!$width) {
return $this->isDecorated() ? $this->styleStack->getCurrent()->apply($text) : $text;
}
if (!$currentLineLength && '' !== $current) {
$text = ltrim($text);
}
if ($currentLineLength) {
$prefix = substr($text, 0, $i = $width - $currentLineLength)."\n";
$text = substr($text, $i);
} else {
$prefix = '';
}
preg_match('~(\\n)$~', $text, $matches);
$text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text);
$text = rtrim($text, "\n").($matches[1] ?? '');
if (!$currentLineLength && '' !== $current && "\n" !== substr($current, -1)) {
$text = "\n".$text;
}
$lines = explode("\n", $text);
foreach ($lines as $line) {
$currentLineLength += \strlen($line);
if ($width <= $currentLineLength) {
$currentLineLength = 0;
}
}
if ($this->isDecorated()) {
foreach ($lines as $i => $line) {
$lines[$i] = $this->styleStack->getCurrent()->apply($line);
}
}
return implode("\n", $lines);
}
}

View File

@@ -20,52 +20,33 @@ interface OutputFormatterInterface
{
/**
* Sets the decorated flag.
*
* @param bool $decorated Whether to decorate the messages or not
*/
public function setDecorated($decorated);
public function setDecorated(bool $decorated);
/**
* Gets the decorated flag.
*
* @return bool true if the output will decorate messages, false otherwise
* Whether the output will decorate messages.
*/
public function isDecorated();
public function isDecorated(): bool;
/**
* Sets a new style.
*
* @param string $name The style name
* @param OutputFormatterStyleInterface $style The style instance
*/
public function setStyle($name, OutputFormatterStyleInterface $style);
public function setStyle(string $name, OutputFormatterStyleInterface $style);
/**
* Checks if output formatter has style with specified name.
*
* @param string $name
*
* @return bool
*/
public function hasStyle($name);
public function hasStyle(string $name): bool;
/**
* Gets style options from style with specified name.
*
* @param string $name
*
* @return OutputFormatterStyleInterface
*
* @throws \InvalidArgumentException When style isn't defined
*/
public function getStyle($name);
public function getStyle(string $name): OutputFormatterStyleInterface;
/**
* Formats a message according to the given styles.
*
* @param string $message The message to style
*
* @return string The styled message
*/
public function format($message);
public function format(?string $message): ?string;
}

View File

@@ -11,7 +11,7 @@
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Color;
/**
* Formatter style class for defining styles.
@@ -20,202 +20,87 @@ use Symfony\Component\Console\Exception\InvalidArgumentException;
*/
class OutputFormatterStyle implements OutputFormatterStyleInterface
{
private static $availableForegroundColors = array(
'black' => array('set' => 30, 'unset' => 39),
'red' => array('set' => 31, 'unset' => 39),
'green' => array('set' => 32, 'unset' => 39),
'yellow' => array('set' => 33, 'unset' => 39),
'blue' => array('set' => 34, 'unset' => 39),
'magenta' => array('set' => 35, 'unset' => 39),
'cyan' => array('set' => 36, 'unset' => 39),
'white' => array('set' => 37, 'unset' => 39),
'default' => array('set' => 39, 'unset' => 39),
);
private static $availableBackgroundColors = array(
'black' => array('set' => 40, 'unset' => 49),
'red' => array('set' => 41, 'unset' => 49),
'green' => array('set' => 42, 'unset' => 49),
'yellow' => array('set' => 43, 'unset' => 49),
'blue' => array('set' => 44, 'unset' => 49),
'magenta' => array('set' => 45, 'unset' => 49),
'cyan' => array('set' => 46, 'unset' => 49),
'white' => array('set' => 47, 'unset' => 49),
'default' => array('set' => 49, 'unset' => 49),
);
private static $availableOptions = array(
'bold' => array('set' => 1, 'unset' => 22),
'underscore' => array('set' => 4, 'unset' => 24),
'blink' => array('set' => 5, 'unset' => 25),
'reverse' => array('set' => 7, 'unset' => 27),
'conceal' => array('set' => 8, 'unset' => 28),
);
private $foreground;
private $background;
private $options = array();
private $color;
private string $foreground;
private string $background;
private array $options;
private ?string $href = null;
private bool $handlesHrefGracefully;
/**
* Initializes output formatter style.
*
* @param string|null $foreground The style foreground color name
* @param string|null $background The style background color name
* @param array $options The style options
*/
public function __construct($foreground = null, $background = null, array $options = array())
public function __construct(string $foreground = null, string $background = null, array $options = [])
{
if (null !== $foreground) {
$this->setForeground($foreground);
}
if (null !== $background) {
$this->setBackground($background);
}
if (count($options)) {
$this->setOptions($options);
}
$this->color = new Color($this->foreground = $foreground ?: '', $this->background = $background ?: '', $this->options = $options);
}
/**
* Sets style foreground color.
*
* @param string|null $color The color name
*
* @throws InvalidArgumentException When the color name isn't defined
* {@inheritdoc}
*/
public function setForeground($color = null)
public function setForeground(string $color = null)
{
if (null === $color) {
$this->foreground = null;
return;
}
if (!isset(static::$availableForegroundColors[$color])) {
throw new InvalidArgumentException(sprintf(
'Invalid foreground color specified: "%s". Expected one of (%s)',
$color,
implode(', ', array_keys(static::$availableForegroundColors))
));
}
$this->foreground = static::$availableForegroundColors[$color];
$this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options);
}
/**
* Sets style background color.
*
* @param string|null $color The color name
*
* @throws InvalidArgumentException When the color name isn't defined
* {@inheritdoc}
*/
public function setBackground($color = null)
public function setBackground(string $color = null)
{
if (null === $color) {
$this->background = null;
$this->color = new Color($this->foreground, $this->background = $color ?: '', $this->options);
}
return;
}
if (!isset(static::$availableBackgroundColors[$color])) {
throw new InvalidArgumentException(sprintf(
'Invalid background color specified: "%s". Expected one of (%s)',
$color,
implode(', ', array_keys(static::$availableBackgroundColors))
));
}
$this->background = static::$availableBackgroundColors[$color];
public function setHref(string $url): void
{
$this->href = $url;
}
/**
* Sets some specific style option.
*
* @param string $option The option name
*
* @throws InvalidArgumentException When the option name isn't defined
* {@inheritdoc}
*/
public function setOption($option)
public function setOption(string $option)
{
if (!isset(static::$availableOptions[$option])) {
throw new InvalidArgumentException(sprintf(
'Invalid option specified: "%s". Expected one of (%s)',
$option,
implode(', ', array_keys(static::$availableOptions))
));
}
if (!in_array(static::$availableOptions[$option], $this->options)) {
$this->options[] = static::$availableOptions[$option];
}
$this->options[] = $option;
$this->color = new Color($this->foreground, $this->background, $this->options);
}
/**
* Unsets some specific style option.
*
* @param string $option The option name
*
* @throws InvalidArgumentException When the option name isn't defined
* {@inheritdoc}
*/
public function unsetOption($option)
public function unsetOption(string $option)
{
if (!isset(static::$availableOptions[$option])) {
throw new InvalidArgumentException(sprintf(
'Invalid option specified: "%s". Expected one of (%s)',
$option,
implode(', ', array_keys(static::$availableOptions))
));
}
$pos = array_search(static::$availableOptions[$option], $this->options);
$pos = array_search($option, $this->options);
if (false !== $pos) {
unset($this->options[$pos]);
}
$this->color = new Color($this->foreground, $this->background, $this->options);
}
/**
* Sets multiple style options at once.
*
* @param array $options
* {@inheritdoc}
*/
public function setOptions(array $options)
{
$this->options = array();
foreach ($options as $option) {
$this->setOption($option);
}
$this->color = new Color($this->foreground, $this->background, $this->options = $options);
}
/**
* Applies the style to a given text.
*
* @param string $text The text to style
*
* @return string
* {@inheritdoc}
*/
public function apply($text)
public function apply(string $text): string
{
$setCodes = array();
$unsetCodes = array();
$this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR')
&& (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100);
if (null !== $this->foreground) {
$setCodes[] = $this->foreground['set'];
$unsetCodes[] = $this->foreground['unset'];
}
if (null !== $this->background) {
$setCodes[] = $this->background['set'];
$unsetCodes[] = $this->background['unset'];
}
if (count($this->options)) {
foreach ($this->options as $option) {
$setCodes[] = $option['set'];
$unsetCodes[] = $option['unset'];
}
if (null !== $this->href && $this->handlesHrefGracefully) {
$text = "\033]8;;$this->href\033\\$text\033]8;;\033\\";
}
if (0 === count($setCodes)) {
return $text;
}
return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes));
return $this->color->apply($text);
}
}

View File

@@ -20,45 +20,31 @@ interface OutputFormatterStyleInterface
{
/**
* Sets style foreground color.
*
* @param string $color The color name
*/
public function setForeground($color = null);
public function setForeground(string $color = null);
/**
* Sets style background color.
*
* @param string $color The color name
*/
public function setBackground($color = null);
public function setBackground(string $color = null);
/**
* Sets some specific style option.
*
* @param string $option The option name
*/
public function setOption($option);
public function setOption(string $option);
/**
* Unsets some specific style option.
*
* @param string $option The option name
*/
public function unsetOption($option);
public function unsetOption(string $option);
/**
* Sets multiple style options at once.
*
* @param array $options
*/
public function setOptions(array $options);
/**
* Applies the style to a given text.
*
* @param string $text The text to style
*
* @return string
*/
public function apply($text);
public function apply(string $text): string;
}

View File

@@ -12,30 +12,23 @@
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Contracts\Service\ResetInterface;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class OutputFormatterStyleStack
class OutputFormatterStyleStack implements ResetInterface
{
/**
* @var OutputFormatterStyleInterface[]
*/
private $styles;
private array $styles = [];
/**
* @var OutputFormatterStyleInterface
*/
private $emptyStyle;
/**
* Constructor.
*
* @param OutputFormatterStyleInterface|null $emptyStyle
*/
public function __construct(OutputFormatterStyleInterface $emptyStyle = null)
{
$this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle();
$this->emptyStyle = $emptyStyle ?? new OutputFormatterStyle();
$this->reset();
}
@@ -44,13 +37,11 @@ class OutputFormatterStyleStack
*/
public function reset()
{
$this->styles = array();
$this->styles = [];
}
/**
* Pushes a style in the stack.
*
* @param OutputFormatterStyleInterface $style
*/
public function push(OutputFormatterStyleInterface $style)
{
@@ -60,13 +51,9 @@ class OutputFormatterStyleStack
/**
* Pops a style from the stack.
*
* @param OutputFormatterStyleInterface|null $style
*
* @return OutputFormatterStyleInterface
*
* @throws InvalidArgumentException When style tags incorrectly nested
*/
public function pop(OutputFormatterStyleInterface $style = null)
public function pop(OutputFormatterStyleInterface $style = null): OutputFormatterStyleInterface
{
if (empty($this->styles)) {
return $this->emptyStyle;
@@ -78,7 +65,7 @@ class OutputFormatterStyleStack
foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
if ($style->apply('') === $stackedStyle->apply('')) {
$this->styles = array_slice($this->styles, 0, $index);
$this->styles = \array_slice($this->styles, 0, $index);
return $stackedStyle;
}
@@ -89,34 +76,27 @@ class OutputFormatterStyleStack
/**
* Computes current style with stacks top codes.
*
* @return OutputFormatterStyle
*/
public function getCurrent()
public function getCurrent(): OutputFormatterStyleInterface
{
if (empty($this->styles)) {
return $this->emptyStyle;
}
return $this->styles[count($this->styles) - 1];
return $this->styles[\count($this->styles) - 1];
}
/**
* @param OutputFormatterStyleInterface $emptyStyle
*
* @return $this
*/
public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle)
public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle): static
{
$this->emptyStyle = $emptyStyle;
return $this;
}
/**
* @return OutputFormatterStyleInterface
*/
public function getEmptyStyle()
public function getEmptyStyle(): OutputFormatterStyleInterface
{
return $this->emptyStyle;
}

View File

@@ -0,0 +1,25 @@
<?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\Formatter;
/**
* Formatter interface for console output that supports word wrapping.
*
* @author Roland Franssen <franssen.roland@gmail.com>
*/
interface WrappableOutputFormatterInterface extends OutputFormatterInterface
{
/**
* Formats a message according to the given styles, wrapping at `$width` (0 means no wrapping).
*/
public function formatAndWrap(?string $message, int $width);
}