├── .gitignore ├── src ├── InvalidStyleException.php └── ConsoleColor.php ├── .travis.yml ├── phpunit.xml ├── README.md ├── composer.json ├── example.php ├── LICENSE └── tests └── ConsoleColorTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | vendor 3 | composer.lock 4 | -------------------------------------------------------------------------------- /src/InvalidStyleException.php: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | tests 7 | 8 | 9 | 10 | 11 | 12 | 13 | vendor 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP Console Color 2 | ================= 3 | 4 | **This repository is abandoned.** 5 | 6 | Suggested alternative: https://github.com/php-parallel-lint/PHP-Console-Color 7 | 8 | ------ 9 | 10 | [![Build Status](https://travis-ci.org/JakubOnderka/PHP-Console-Color.svg?branch=master)](https://travis-ci.org/JakubOnderka/PHP-Console-Color) 11 | 12 | Simple library for creating colored console ouput. 13 | 14 | See `example.php` how to use this library. 15 | 16 | ![Example from Windows 10](https://user-images.githubusercontent.com/89590/40762008-687f909a-646c-11e8-88d6-e268a064be4c.png) -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jakub-onderka/php-console-color", 3 | "license": "BSD-2-Clause", 4 | "authors": [ 5 | { 6 | "name": "Jakub Onderka", 7 | "email": "jakub.onderka@gmail.com" 8 | } 9 | ], 10 | "autoload": { 11 | "psr-4": {"JakubOnderka\\PhpConsoleColor\\": "src/"} 12 | }, 13 | "require": { 14 | "php": ">=5.4.0" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "~4.3", 18 | "jakub-onderka/php-parallel-lint": "1.0", 19 | "jakub-onderka/php-var-dump-check": "0.*", 20 | "squizlabs/php_codesniffer": "1.*", 21 | "jakub-onderka/php-code-style": "1.0" 22 | }, 23 | "abandoned": "php-parallel-lint/php-console-color" 24 | } 25 | -------------------------------------------------------------------------------- /example.php: -------------------------------------------------------------------------------- 1 | isSupported() ? 'Yes' : 'No') . "\n"; 7 | echo "256 colors are supported: " . ($consoleColor->are256ColorsSupported() ? 'Yes' : 'No') . "\n\n"; 8 | 9 | if ($consoleColor->isSupported()) { 10 | foreach ($consoleColor->getPossibleStyles() as $style) { 11 | echo $consoleColor->apply($style, $style) . "\n"; 12 | } 13 | } 14 | 15 | echo "\n"; 16 | 17 | if ($consoleColor->are256ColorsSupported()) { 18 | echo "Foreground colors:\n"; 19 | for ($i = 1; $i <= 255; $i++) { 20 | echo $consoleColor->apply("color_$i", str_pad($i, 6, ' ', STR_PAD_BOTH)); 21 | 22 | if ($i % 15 === 0) { 23 | echo "\n"; 24 | } 25 | } 26 | 27 | echo "\nBackground colors:\n"; 28 | 29 | for ($i = 1; $i <= 255; $i++) { 30 | echo $consoleColor->apply("bg_color_$i", str_pad($i, 6, ' ', STR_PAD_BOTH)); 31 | 32 | if ($i % 15 === 0) { 33 | echo "\n"; 34 | } 35 | } 36 | 37 | echo "\n"; 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2018, Jakub Onderka 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 19 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 20 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 26 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /tests/ConsoleColorTest.php: -------------------------------------------------------------------------------- 1 | isSupportedForce = $isSupported; 13 | } 14 | 15 | public function isSupported() 16 | { 17 | return $this->isSupportedForce; 18 | } 19 | 20 | public function setAre256ColorsSupported($are256ColorsSupported) 21 | { 22 | $this->are256ColorsSupportedForce = $are256ColorsSupported; 23 | } 24 | 25 | public function are256ColorsSupported() 26 | { 27 | return $this->are256ColorsSupportedForce; 28 | } 29 | } 30 | 31 | class ConsoleColorTest extends \PHPUnit_Framework_TestCase 32 | { 33 | /** @var ConsoleColorWithForceSupport */ 34 | private $uut; 35 | 36 | protected function setUp() 37 | { 38 | $this->uut = new ConsoleColorWithForceSupport(); 39 | } 40 | 41 | public function testNone() 42 | { 43 | $output = $this->uut->apply('none', 'text'); 44 | $this->assertEquals("text", $output); 45 | } 46 | 47 | public function testBold() 48 | { 49 | $output = $this->uut->apply('bold', 'text'); 50 | $this->assertEquals("\033[1mtext\033[0m", $output); 51 | } 52 | 53 | public function testBoldColorsAreNotSupported() 54 | { 55 | $this->uut->setIsSupported(false); 56 | 57 | $output = $this->uut->apply('bold', 'text'); 58 | $this->assertEquals("text", $output); 59 | } 60 | 61 | public function testBoldColorsAreNotSupportedButAreForced() 62 | { 63 | $this->uut->setIsSupported(false); 64 | $this->uut->setForceStyle(true); 65 | 66 | $output = $this->uut->apply('bold', 'text'); 67 | $this->assertEquals("\033[1mtext\033[0m", $output); 68 | } 69 | 70 | public function testDark() 71 | { 72 | $output = $this->uut->apply('dark', 'text'); 73 | $this->assertEquals("\033[2mtext\033[0m", $output); 74 | } 75 | 76 | public function testBoldAndDark() 77 | { 78 | $output = $this->uut->apply(array('bold', 'dark'), 'text'); 79 | $this->assertEquals("\033[1;2mtext\033[0m", $output); 80 | } 81 | 82 | public function test256ColorForeground() 83 | { 84 | $output = $this->uut->apply('color_255', 'text'); 85 | $this->assertEquals("\033[38;5;255mtext\033[0m", $output); 86 | } 87 | 88 | public function test256ColorWithoutSupport() 89 | { 90 | $this->uut->setAre256ColorsSupported(false); 91 | 92 | $output = $this->uut->apply('color_255', 'text'); 93 | $this->assertEquals("text", $output); 94 | } 95 | 96 | public function test256ColorBackground() 97 | { 98 | $output = $this->uut->apply('bg_color_255', 'text'); 99 | $this->assertEquals("\033[48;5;255mtext\033[0m", $output); 100 | } 101 | 102 | public function test256ColorForegroundAndBackground() 103 | { 104 | $output = $this->uut->apply(array('color_200', 'bg_color_255'), 'text'); 105 | $this->assertEquals("\033[38;5;200;48;5;255mtext\033[0m", $output); 106 | } 107 | 108 | public function testSetOwnTheme() 109 | { 110 | $this->uut->setThemes(array('bold_dark' => array('bold', 'dark'))); 111 | $output = $this->uut->apply(array('bold_dark'), 'text'); 112 | $this->assertEquals("\033[1;2mtext\033[0m", $output); 113 | } 114 | 115 | public function testAddOwnTheme() 116 | { 117 | $this->uut->addTheme('bold_own', 'bold'); 118 | $output = $this->uut->apply(array('bold_own'), 'text'); 119 | $this->assertEquals("\033[1mtext\033[0m", $output); 120 | } 121 | 122 | public function testAddOwnThemeArray() 123 | { 124 | $this->uut->addTheme('bold_dark', array('bold', 'dark')); 125 | $output = $this->uut->apply(array('bold_dark'), 'text'); 126 | $this->assertEquals("\033[1;2mtext\033[0m", $output); 127 | } 128 | 129 | public function testOwnWithStyle() 130 | { 131 | $this->uut->addTheme('bold_dark', array('bold', 'dark')); 132 | $output = $this->uut->apply(array('bold_dark', 'italic'), 'text'); 133 | $this->assertEquals("\033[1;2;3mtext\033[0m", $output); 134 | } 135 | 136 | public function testHasAndRemoveTheme() 137 | { 138 | $this->assertFalse($this->uut->hasTheme('bold_dark')); 139 | 140 | $this->uut->addTheme('bold_dark', array('bold', 'dark')); 141 | $this->assertTrue($this->uut->hasTheme('bold_dark')); 142 | 143 | $this->uut->removeTheme('bold_dark'); 144 | $this->assertFalse($this->uut->hasTheme('bold_dark')); 145 | } 146 | 147 | public function testApplyInvalidArgument() 148 | { 149 | $this->setExpectedException('\InvalidArgumentException'); 150 | $this->uut->apply(new stdClass(), 'text'); 151 | } 152 | 153 | public function testApplyInvalidStyleName() 154 | { 155 | $this->setExpectedException('\JakubOnderka\PhpConsoleColor\InvalidStyleException'); 156 | $this->uut->apply('invalid', 'text'); 157 | } 158 | 159 | public function testApplyInvalid256Color() 160 | { 161 | $this->setExpectedException('\JakubOnderka\PhpConsoleColor\InvalidStyleException'); 162 | $this->uut->apply('color_2134', 'text'); 163 | } 164 | 165 | public function testThemeInvalidStyle() 166 | { 167 | $this->setExpectedException('\JakubOnderka\PhpConsoleColor\InvalidStyleException'); 168 | $this->uut->addTheme('invalid', array('invalid')); 169 | } 170 | 171 | public function testForceStyle() 172 | { 173 | $this->assertFalse($this->uut->isStyleForced()); 174 | $this->uut->setForceStyle(true); 175 | $this->assertTrue($this->uut->isStyleForced()); 176 | } 177 | 178 | public function testGetPossibleStyles() 179 | { 180 | $this->assertInternalType('array', $this->uut->getPossibleStyles()); 181 | $this->assertNotEmpty($this->uut->getPossibleStyles()); 182 | } 183 | } 184 | 185 | -------------------------------------------------------------------------------- /src/ConsoleColor.php: -------------------------------------------------------------------------------- 1 | null, 22 | 'bold' => '1', 23 | 'dark' => '2', 24 | 'italic' => '3', 25 | 'underline' => '4', 26 | 'blink' => '5', 27 | 'reverse' => '7', 28 | 'concealed' => '8', 29 | 30 | 'default' => '39', 31 | 'black' => '30', 32 | 'red' => '31', 33 | 'green' => '32', 34 | 'yellow' => '33', 35 | 'blue' => '34', 36 | 'magenta' => '35', 37 | 'cyan' => '36', 38 | 'light_gray' => '37', 39 | 40 | 'dark_gray' => '90', 41 | 'light_red' => '91', 42 | 'light_green' => '92', 43 | 'light_yellow' => '93', 44 | 'light_blue' => '94', 45 | 'light_magenta' => '95', 46 | 'light_cyan' => '96', 47 | 'white' => '97', 48 | 49 | 'bg_default' => '49', 50 | 'bg_black' => '40', 51 | 'bg_red' => '41', 52 | 'bg_green' => '42', 53 | 'bg_yellow' => '43', 54 | 'bg_blue' => '44', 55 | 'bg_magenta' => '45', 56 | 'bg_cyan' => '46', 57 | 'bg_light_gray' => '47', 58 | 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', 66 | 'bg_white' => '107', 67 | ); 68 | 69 | /** @var array */ 70 | private $themes = array(); 71 | 72 | public function __construct() 73 | { 74 | $this->isSupported = $this->isSupported(); 75 | } 76 | 77 | /** 78 | * @param string|array $style 79 | * @param string $text 80 | * @return string 81 | * @throws InvalidStyleException 82 | * @throws \InvalidArgumentException 83 | */ 84 | public function apply($style, $text) 85 | { 86 | if (!$this->isStyleForced() && !$this->isSupported()) { 87 | return $text; 88 | } 89 | 90 | if (is_string($style)) { 91 | $style = array($style); 92 | } 93 | if (!is_array($style)) { 94 | throw new \InvalidArgumentException("Style must be string or array."); 95 | } 96 | 97 | $sequences = array(); 98 | 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); 104 | } else { 105 | throw new InvalidStyleException($s); 106 | } 107 | } 108 | 109 | $sequences = array_filter($sequences, function ($val) { 110 | return $val !== null; 111 | }); 112 | 113 | if (empty($sequences)) { 114 | return $text; 115 | } 116 | 117 | return $this->escSequence(implode(';', $sequences)) . $text . $this->escSequence(self::RESET_STYLE); 118 | } 119 | 120 | /** 121 | * @param bool $forceStyle 122 | */ 123 | public function setForceStyle($forceStyle) 124 | { 125 | $this->forceStyle = (bool) $forceStyle; 126 | } 127 | 128 | /** 129 | * @return bool 130 | */ 131 | public function isStyleForced() 132 | { 133 | return $this->forceStyle; 134 | } 135 | 136 | /** 137 | * @param array $themes 138 | * @throws InvalidStyleException 139 | * @throws \InvalidArgumentException 140 | */ 141 | public function setThemes(array $themes) 142 | { 143 | $this->themes = array(); 144 | foreach ($themes as $name => $styles) { 145 | $this->addTheme($name, $styles); 146 | } 147 | } 148 | 149 | /** 150 | * @param string $name 151 | * @param array|string $styles 152 | * @throws \InvalidArgumentException 153 | * @throws InvalidStyleException 154 | */ 155 | public function addTheme($name, $styles) 156 | { 157 | if (is_string($styles)) { 158 | $styles = array($styles); 159 | } 160 | if (!is_array($styles)) { 161 | throw new \InvalidArgumentException("Style must be string or array."); 162 | } 163 | 164 | foreach ($styles as $style) { 165 | if (!$this->isValidStyle($style)) { 166 | throw new InvalidStyleException($style); 167 | } 168 | } 169 | 170 | $this->themes[$name] = $styles; 171 | } 172 | 173 | /** 174 | * @return array 175 | */ 176 | public function getThemes() 177 | { 178 | return $this->themes; 179 | } 180 | 181 | /** 182 | * @param string $name 183 | * @return bool 184 | */ 185 | public function hasTheme($name) 186 | { 187 | return isset($this->themes[$name]); 188 | } 189 | 190 | /** 191 | * @param string $name 192 | */ 193 | public function removeTheme($name) 194 | { 195 | unset($this->themes[$name]); 196 | } 197 | 198 | /** 199 | * @return bool 200 | */ 201 | public function isSupported() 202 | { 203 | if (DIRECTORY_SEPARATOR === '\\') { 204 | if (function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT)) { 205 | return true; 206 | } elseif (getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON') { 207 | return true; 208 | } 209 | return false; 210 | } else { 211 | return function_exists('posix_isatty') && @posix_isatty(STDOUT); 212 | } 213 | } 214 | 215 | /** 216 | * @return bool 217 | */ 218 | public function are256ColorsSupported() 219 | { 220 | if (DIRECTORY_SEPARATOR === '\\') { 221 | return function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT); 222 | } else { 223 | return strpos(getenv('TERM'), '256color') !== false; 224 | } 225 | } 226 | 227 | /** 228 | * @return array 229 | */ 230 | public function getPossibleStyles() 231 | { 232 | return array_keys($this->styles); 233 | } 234 | 235 | /** 236 | * @param string $name 237 | * @return string[] 238 | */ 239 | private function themeSequence($name) 240 | { 241 | $sequences = array(); 242 | foreach ($this->themes[$name] as $style) { 243 | $sequences[] = $this->styleSequence($style); 244 | } 245 | return $sequences; 246 | } 247 | 248 | /** 249 | * @param string $style 250 | * @return string 251 | */ 252 | private function styleSequence($style) 253 | { 254 | if (array_key_exists($style, $this->styles)) { 255 | return $this->styles[$style]; 256 | } 257 | 258 | if (!$this->are256ColorsSupported()) { 259 | return null; 260 | } 261 | 262 | preg_match(self::COLOR256_REGEXP, $style, $matches); 263 | 264 | $type = $matches[1] === 'bg_' ? self::BACKGROUND : self::FOREGROUND; 265 | $value = $matches[2]; 266 | 267 | return "$type;5;$value"; 268 | } 269 | 270 | /** 271 | * @param string $style 272 | * @return bool 273 | */ 274 | private function isValidStyle($style) 275 | { 276 | return array_key_exists($style, $this->styles) || preg_match(self::COLOR256_REGEXP, $style); 277 | } 278 | 279 | /** 280 | * @param string|int $value 281 | * @return string 282 | */ 283 | private function escSequence($value) 284 | { 285 | return "\033[{$value}m"; 286 | } 287 | } 288 | --------------------------------------------------------------------------------