DEV Community

Cover image for writing command line scripts in php: part 5; styling output text
grant horwood
grant horwood

Posted on

writing command line scripts in php: part 5; styling output text

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:

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':

#!/usr/bin/env php
Enter fullscreen mode Exit fullscreen mode

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.

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
echo "\033[31m"."THIS IS RED"."\033[0m";
Enter fullscreen mode Exit fullscreen mode

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:

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

echo ANSI_RED."THIS IS RED".ANSI_CLOSE;
Enter fullscreen mode Exit fullscreen mode

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:

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;
Enter fullscreen mode Exit fullscreen mode

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.

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;
Enter fullscreen mode Exit fullscreen mode

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.

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

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

echo ANSI_BOLD."THIS IS BOLD TEXT".ANSI_CLOSE;
Enter fullscreen mode Exit fullscreen mode

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.

/**
 * 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");
Enter fullscreen mode Exit fullscreen mode

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

// 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;

Enter fullscreen mode Exit fullscreen mode

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.

/**
 * 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");
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (1)

Collapse
 
muhammadmp profile image
Muhammad MP

Why did you use STDOUT for errors?