2 namespace JakubOnderka\PhpConsoleColor;
9 const COLOR256_REGEXP = '~^(bg_)?color_([0-9]{1,3})$~';
11 const RESET_STYLE = 0;
17 private $forceStyle = false;
20 private $styles = array(
42 'light_green' => '92',
43 'light_yellow' => '93',
45 'light_magenta' => '95',
57 'bg_light_gray' => '47',
59 'bg_dark_gray' => '100',
60 'bg_light_red' => '101',
61 'bg_light_green' => '102',
62 'bg_light_yellow' => '103',
63 'bg_light_blue' => '104',
64 'bg_light_magenta' => '105',
65 'bg_light_cyan' => '106',
70 private $themes = array();
72 public function __construct()
74 $this->isSupported = $this->isSupported();
78 * @param string|array $style
81 * @throws InvalidStyleException
82 * @throws \InvalidArgumentException
84 public function apply($style, $text)
86 if (!$this->isStyleForced() && !$this->isSupported()) {
90 if (is_string($style)) {
91 $style = array($style);
93 if (!is_array($style)) {
94 throw new \InvalidArgumentException("Style must be string or array.");
99 foreach ($style as $s) {
100 if (isset($this->themes[$s])) {
101 $sequences = array_merge($sequences, $this->themeSequence($s));
102 } else if ($this->isValidStyle($s)) {
103 $sequences[] = $this->styleSequence($s);
105 throw new InvalidStyleException($s);
109 $sequences = array_filter($sequences, function ($val) {
110 return $val !== null;
113 if (empty($sequences)) {
117 return $this->escSequence(implode(';', $sequences)) . $text . $this->escSequence(self::RESET_STYLE);
121 * @param bool $forceStyle
123 public function setForceStyle($forceStyle)
125 $this->forceStyle = (bool) $forceStyle;
131 public function isStyleForced()
133 return $this->forceStyle;
137 * @param array $themes
138 * @throws InvalidStyleException
139 * @throws \InvalidArgumentException
141 public function setThemes(array $themes)
143 $this->themes = array();
144 foreach ($themes as $name => $styles) {
145 $this->addTheme($name, $styles);
150 * @param string $name
151 * @param array|string $styles
152 * @throws \InvalidArgumentException
153 * @throws InvalidStyleException
155 public function addTheme($name, $styles)
157 if (is_string($styles)) {
158 $styles = array($styles);
160 if (!is_array($styles)) {
161 throw new \InvalidArgumentException("Style must be string or array.");
164 foreach ($styles as $style) {
165 if (!$this->isValidStyle($style)) {
166 throw new InvalidStyleException($style);
170 $this->themes[$name] = $styles;
176 public function getThemes()
178 return $this->themes;
182 * @param string $name
185 public function hasTheme($name)
187 return isset($this->themes[$name]);
191 * @param string $name
193 public function removeTheme($name)
195 unset($this->themes[$name]);
201 public function isSupported()
203 if (DIRECTORY_SEPARATOR === '\\') {
204 return getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON';
207 return function_exists('posix_isatty') && @posix_isatty(STDOUT);
213 public function are256ColorsSupported()
215 return DIRECTORY_SEPARATOR === '/' && strpos(getenv('TERM'), '256color') !== false;
221 public function getPossibleStyles()
223 return array_keys($this->styles);
227 * @param string $name
229 * @throws InvalidStyleException
231 private function themeSequence($name)
233 $sequences = array();
234 foreach ($this->themes[$name] as $style) {
235 $sequences[] = $this->styleSequence($style);
241 * @param string $style
243 * @throws InvalidStyleException
245 private function styleSequence($style)
247 if (array_key_exists($style, $this->styles)) {
248 return $this->styles[$style];
251 if (!$this->are256ColorsSupported()) {
255 preg_match(self::COLOR256_REGEXP, $style, $matches);
257 $type = $matches[1] === 'bg_' ? self::BACKGROUND : self::FOREGROUND;
258 $value = $matches[2];
260 return "$type;5;$value";
264 * @param string $style
267 private function isValidStyle($style)
269 return array_key_exists($style, $this->styles) || preg_match(self::COLOR256_REGEXP, $style);
273 * @param string|int $value
276 private function escSequence($value)
278 return "\033[{$value}m";