writing command line scripts in php: part 5; styling output text
Published: 2023-03-15 17:51:37

php is primarly considered a web language, but it can certainly be used to build interactive command-line scripts. this series of posts is designed to cover the basic constructs we will need to write to do exactly that.

this installment focuses on styling and colouring text output.

## previous installments
this is the fifth installment in the series. previously, we have covered arguments and preflighting; reading from both piped and interactive user input; and reading user key-down events.

the articles that compose this series (so far) are:

- [pt 1. arguments, preflights and more](https://dev.to/gbhorwood/writing-command-line-scripts-in-php-part-1-3jpb)
- [pt 2. handling STDIN input](https://dev.to/gbhorwood/writing-command-line-scripts-in-php-part-2-reading-stdin-2enf)
- [pt 3. interactive input](https://dev.to/gbhorwood/writing-command-line-scripts-in-php-part-3-interactive-input-34)
- [pt 4. key-down input](https://dev.to/gbhorwood/writing-command-line-scripts-in-php-part-4-key-down-input-ng5)

## the flyover
we will be looking at styling our console output text, specifically:

- colouring text
- setting the background colour of text
- adding styles like bold or underline
- combining them all
- building some handy output functions for text

as usual, all the examples here should be preceeed by the php 'shebang':

```php
#!/usr/bin/env php
```

## colouring text
colouring and styling our text outpt is useful. maybe we want the word 'ERROR' to be in red, or the default option in a list to be bold.

we can accomplish this by using [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code).

ANSI escape codes are basically simple commands that tell our terminal to do things like change text colour, move the cursor, delete output and so on. they've been around since the seventies and are supported by just about every teriminal emulator in existence.

to apply colour or style to text, we can treat ANSI codes a bit like html tags, wrapping the text we want to style in an open and close code. let's look at a simple example:

```php
<?php
echo "\033[31m"."THIS IS RED"."\033[0m";
```

if this example doesn't make immediate sense, don't worry. ANSI codes were never designed with readability in mind! however, we can break down what's happening here.

the first thing to know is the `\033` is the escape character. since these are ANSI _escape_ codes, they all start with an escape. however, we can't just type an escape character into our text editor. instead, we use the `\033` sequence. in essence, we are using an escape sequence the same as we would to create a new line with `\n`, except the sequence is to create an escape character.

next, we see `[31m`. this is the actual code that tells our terminal to start outputting text in red.

we close our red 'tag' with `\033[0m`. that's an escape character followed by `[0m`. it is important to note that `[0m` closes _all_ ANSI styles; it basically resets the output style to its default. if you wrap some text in an ANSI escape sequence to make it red and bold, you only need one close escape sequence.

### making escape codes more readable
escape codes are messy and difficult to read, and if we start applying a lot of them, they get out of hand quickly.

i like to use constants to give them meaningful, readable names to keep my code clean. for instance, we could write the above example like so:

```php
define ('ESC', "\033");
define ('ANSI_RED', ESC."[31m");
define ('ANSI_CLOSE', ESC."[0m");

echo ANSI_RED."THIS IS RED".ANSI_CLOSE;
```

something important to note here is that when definig `ESC`, you _must_ put `\033` in double quotes. this is so the escape sequence gets evaluated.

we will go over all the colour and style codes in a bit!

## reverse colours
setting text colour is great, but we will also probably want to set _background_ colours as well.

fortunately, if we can set text colours, setting background colours is as straightforward as adding 10. let's look:

```php
define ('ESC', "\033");
define ('ANSI_CLOSE', ESC."[0m");

define ('ANSI_RED', ESC."[31m");
define ('ANSI_BACKGROUND_RED', ESC."[41m"); // the code for ANSI_RED plus 10

echo ANSI_BACKGROUND_RED."THIS IS ON A RED BACKGROUND".ANSI_CLOSE;
```

here we see that the ANSI code for red text is `[31m` and the code for a red background is `[41m`. the background code is ten more than the text code. this works for all ANSI colour codes.

of course, we can combine text colours and background colours if we wish.

```php
define ('ESC', "\033");
define ('ANSI_CLOSE', ESC."[0m");

define('ANSI_WHITE', ESC."[37m");
define ('ANSI_BACKGROUND_RED', ESC."[41m");

echo ANSI_BACKGROUND_RED.ANSI_WHITE."THIS IS WHITE ON A RED BACKGROUND".ANSI_CLOSE.PHP_EOL;
```

## styling text
ANSI codes can also be used to style text: make it bold or underlined or similar. all that is required is knowing some new escape codes.

```php
define ('ESC', "\033");
define ('ANSI_CLOSE', ESC."[0m");

define('ANSI_BOLD', ESC."[1m");

echo ANSI_BOLD."THIS IS BOLD TEXT".ANSI_CLOSE;
```

here we've made our text bold by applying the ANSI escape code for boldness. text style and colour are handled the same way, just using different codes.

of course we can combine style and colour; we can make our text bold and blue on a red background if we wish, although our users will probably hate us for it.


## complete color and style codes
there are a fair number of ANSI colour and style codes. rather than show a table of them, though, i'm going to give a list of `define` statements for constants that we can just copy and paste into our script.

```php
/**
 * Escape character
 */
define ('ESC', "\033");

/**
 * ANSI colours
 */
define('ANSI_BLACK', ESC."[30m");
define('ANSI_RED', ESC."[31m");
define('ANSI_GREEN', ESC."[32m");
define('ANSI_YELLOW', ESC."[33m");
define('ANSI_BLUE', ESC."[34m");
define('ANSI_MAGENTA', ESC."[35m");
define('ANSI_CYAN', ESC."[36m");
define('ANSI_WHITE', ESC."[37m");

/**
 * ANSI background colours
 */
define('ANSI_BACKGROUND_BLACK', ESC."[40m");
define('ANSI_BACKGROUND_RED', ESC."[41m");
define('ANSI_BACKGROUND_GREEN', ESC."[42m");
define('ANSI_BACKGROUND_YELLOW', ESC."[43m");
define('ANSI_BACKGROUND_BLUE', ESC."[44m");
define('ANSI_BACKGROUND_MAGENTA', ESC."[45m");
define('ANSI_BACKGROUND_CYAN', ESC."[46m");
define('ANSI_BACKGROUND_WHITE', ESC."[47m");

/**
 * ANSI styles
 */
define('ANSI_BOLD', ESC."[1m");
define('ANSI_ITALIC', ESC."[3m"); // limited support. ymmv.
define('ANSI_UNDERLINE', ESC."[4m");
define('ANSI_STRIKETHROUGH', ESC."[9m");

/**
 * Clear all ANSI styling
 */
define('ANSI_CLOSE', ESC."[0m");
```

once we have these constants in our script, we can use and combine our ANSI escape styling codes however we want to. for instance:

```php
// colour output
echo ANSI_RED."THIS IS RED".ANSI_CLOSE.PHP_EOL;
echo ANSI_GREEN."THIS IS GREEN".ANSI_CLOSE.PHP_EOL;
echo ANSI_YELLOW."THIS IS YELLOW".ANSI_CLOSE.PHP_EOL;
echo ANSI_BLUE."THIS IS BLUE".ANSI_CLOSE.PHP_EOL;
echo ANSI_MAGENTA."THIS IS MAGENTA".ANSI_CLOSE.PHP_EOL;
echo ANSI_CYAN."THIS IS CYAN".ANSI_CLOSE.PHP_EOL;
echo ANSI_WHITE."THIS IS WHITE".ANSI_CLOSE.PHP_EOL;

// colour background output
echo ANSI_BACKGROUND_WHITE."THIS IS ON A WHITE BACKGROUND".ANSI_CLOSE.PHP_EOL;
echo ANSI_BACKGROUND_RED.ANSI_WHITE."THIS IS WHITE ON A RED BACKGROUND".ANSI_CLOSE.PHP_EOL;

// style output
echo ANSI_BOLD."THIS IS BOLD".ANSI_CLOSE.PHP_EOL;
echo ANSI_ITALIC."THIS MAY OR MAY NOT BE ITALIC".ANSI_CLOSE.PHP_EOL;
echo ANSI_UNDERLINE."THIS IS UNDERLINED".ANSI_CLOSE.PHP_EOL;
echo ANSI_STRIKETHROUGH."THIS IS STRIKETHROUGH".ANSI_CLOSE.PHP_EOL;

// combined colour and style output
echo ANSI_BOLD.ANSI_RED."THIS IS RED AND BOLD".ANSI_CLOSE.PHP_EOL;
echo ANSI_BOLD.ANSI_STRIKETHROUGH.ANSI_RED."THIS IS RED AND BOLD AND STRIKETHROUGH".ANSI_CLOSE.PHP_EOL;
echo ANSI_BACKGROUND_RED.ANSI_WHITE.ANSI_BOLD."THIS IS BOLD WHITE ON A RED BACKGROUND".ANSI_CLOSE.PHP_EOL;

```

## making some functions
certainly, we can manually style our output as needed and, if we don't need to do a lot of styled text output, this is perfectly fine. 

however, one of the very first things i do when i start a php cli script is paste in some simple output helper functions for success and error messages.

```php
/**
 * Output an 'OK' message
 *
 * @param  String $message The message to display
 * @return void
 */
function ok(String $message):void
{
    fwrite(STDOUT,"[".ANSI_GREEN."OK".ANSI_CLOSE."] ".$message.PHP_EOL);
}

/**
 * Output an 'ERROR' message
 *
 * @param  String $message The message to display
 * @return void
 */
function error(String $message):void
{
    fwrite(STDOUT,"[".ANSI_RED."ERROR".ANSI_CLOSE."] ".$message.PHP_EOL);
}

// usage
ok("it worked");
error("something went wrong");
```

these functions, of course, rely on all the constants we defined before. 

## next steps
we've begun focusing on output, and in the next installments will continue by building richer outputs and even some very simple animations.