MPG UniCenter

UniServer 5.0-Nano
PHP CLI.

CLI user input

Although some scripts you design can run in isolation the majority will require some form of interaction. This page covers user input, generally all that is required is to capture a string of characters entered at a keyboard and place these into a variable for processing. Unlike Window applications a command window is sparse hence you need to both prompt a user for the information required and retrieve it.

No forms:

Unlike Web or Window forms CLI processing is performed on a line-by-line basis there is no chance of going back to correct errors. After validation any user input errors are corrected by reissuing a user prompt and the new information retrieved and revalidated.

There are no specific solutions for user input however the examples provided below are a good starting point and may fit your application.

Initial test setup

Edit our two test files Run.bat and test_1.php contained in folder UniServer to have the following content:

Run.bat test_1.php;
TITLE CLI TEST BAT
COLOR B0
@echo off
cls
echo.
usr\local\php\php.exe -n test_1.php
echo.
pause
<?php
fwrite(STDOUT, "Enter your name\n"); // Output - prompt user
$name = fgets(STDIN);                // Read the input
fwrite(STDOUT, "Hello $name");       // Output - Some text   
exit(0);                             // Script ran OK
?>

Run the batch file (double click on Run.bat) Input your name when prompted.

  • fwrite() – Writes the string defined in quotes to the standard output stream (STDOUT)
  • When the CLI program reaches fgets() it pauses and waits for a user to hit enter.
  • On pressing enter, fgets() receives everything a user typed using the standard input stream STDIN and saves it to variable $name
  • The last fwrite() outputs text including the contents of variable $name to the output stream.

Note: UniServer Mona users change path to udrive\usr\local\php\php.exe -n test_1.php

Top

Echo

My personal preference is to use echo instead of fwrite() choose whatever you are comfortable with. To use echo change test_1.php as follows:

test_1.php;
<?php
echo "Enter your name\n"; // Output - prompt user
$name = fgets(STDIN);     // Read the input
echo "Hello $name";       // Output - Some text   
exit(0);                  // Script ran OK
?>

Run the batch file (double click on Run.bat) Result same as first test.

Top

fgets() - Problem

The above two scripts mask a problem with using function fgets() to see this issue modify script test_1.php as follows:

test_1.php;
<?php
echo "Enter your name\n"; // Output - prompt user
$name = fgets(STDIN);     // Read the input
echo "======$name======"; // Output - Some text   
exit(0);                  // Script ran OK
?>

Run the batch file (double click on Run.bat) Enter "fred" for name.

Expected result: ======fred======

Actual result:

Enter your name
fred
======fred
======
Press any key to continue . . .

This clearly shows fgets() accepts all input from the keyboard including the enter key (\n)

Hence code like this if($name=="fred") will fail

Top

Solution

A useful PHP function is chop() this removes the last character from a string. Use this to cleanup gets() modify test_1.php as follows:

test_1.php;
<?php
echo "Enter your name\n";   // Output - prompt user
$name = chop(fgets(STDIN)); // Read the input
echo "======$name======";   // Output - Some text   
exit(0);                    // Script ran OK
?>

Run the batch file (double click on Run.bat) Enter "fred" for name.

This time we get the expected result of: ======fred======

Note: trim() can be used in place of chop() it strips whitespace (or other characters) from the beginning and end of a string

For both chop() and trim() the following characters are removed:

   "\0" - ASCII 0, NULL
   "\t" - ASCII 9, a tab
   "\n" - ASCII 10, a new line
   "\x0B" - ASCII 11, a vertical tab.
   "\r" - ASCII 13, a carriage return
   " " - ASCII 32, an ordinary white space

Top

Basic user input

For basic user input use:
 chop(fgets(STDIN)) 

I use the term basic but the above is an elegant solution, most languages seem to suffer similar issues.

Top

Never reinvent

I am a lazy code hacker and hate filling in forms or entering data.

I came across a Perl script that I have been using for ages.

  • Lazy – Well I pinched it from the Internet
  • Entering data – Provides default options – allows me to press enter.
  • Forms – Unless a value really needs changing just keep pressing enter

Imagine my surprise when I could not find an equivalent in PHP!

Not a real problem; just convert the existing Perl script to PHP.

Function variable parameters

Well! What flawed me, I quickly discovered PHP supports function overloading differently. No big deal but a pain none the less, easily simulated without recourse to Classes and Objects.

To simulate add =false to the end of a parameter as shown below for example $defaultVal=false

Note: This also becomes an issue when designing recursive functions.

Top

Prompt user function

The resulting function as shown below:

//#######################################################################
//# Function: Prompt user and get user input, returns value input by user.
//#           Or if return pressed returns a default if used e.g usage
//# $name = promptUser("Enter your name");
//# $serverName = promptUser("Enter your server name", "localhost");
//# Note: Returned value requires validation 
//#.......................................................................
function promptUser($promptStr,$defaultVal=false){;

  if($defaultVal) {                             // If a default set
     echo $promptStr. "[". $defaultVal. "] : "; // print prompt and default
  }
  else {                                        // No default set
     echo $promptStr. ": ";                     // print prompt only
  } 
  $name = chop(fgets(STDIN));                   // Read input. Remove CR
  if(empty($name)) {                            // No value. Enter was pressed
     return $defaultVal;                        // return default
  }
  else {                                        // Value entered
     return $name;                              // return value
  }
}
//========================================= End promptUser ============

Top

Example of use

Edit file test_1.php to have the following content:

<?php

while(empty($name)){                        // SEE SIDE NOTE Problem 1>>
$name  = promptUser(" Enter server name");
}

$port1 = promptUser(" Enter server port Default ","80");
while (!is_numeric ($port1)){               // SEE SIDE NOTE Problem 2>>
  $port1 = promptUser(" Enter server port Default ","80");
} 

$port2 = promptUser(" Enter SSL    port Default ","443");

echo "\n\n Server name is: $name \n";
echo " Server port is: $port1 \n";
echo " Server SSL  is: $port2 \n";

exit(0); // Script ran OK

//#######################################################################
//# Function: Prompt user and get user input, returns value input by user
//#           Or if return pressed returns a default if used e.g usage
//# $name = promptUser("Enter your name");
//# $serverName = promptUser("Enter your server name", "localhost");
//# Note: Returned value requires validation 
//#.......................................................................
function promptUser($promptStr,$defaultVal=false){;

 if($defaultVal) {                          // If a default set
  echo $promptStr. "[". $defaultVal. "] : ";// print prompt and default
 }
 else {                                     // No default set
  echo $promptStr. ": ";                    // print prompt only
 } 
 $name = chop(fgets(STDIN));                // Read input. Remove CR
 if(empty($name)) {                         // No value. Enter was pressed
  return $defaultVal;                       // return default
 }
 else {                                     // Value entered
  return $name;                             // return value
 }
}
//========================================= End promptUser ============
?>

The example includes some crude validation.

  • while(empty($name)) - A user must input a name it cannot be empty
  • while (!is_numeric ($port1)) - A user must enter a number
  • $port2 - For comparison $port2 is not validated

Validation can be as complex as you wish to make it!.

Its application dependent, keep it sensible and don't go overboard.

Problem 1: Enter a server name of 0 what does this line return while(empty($name)){. Is it a valid check ? - Well 0 is considered empty. Solution:

function is_empty($var) {
 if (!isset($var) || is_null($var)) {   
   return true;
 } else {
   return false;
 }
}

Problem 2: Incorrect validation is worst than no validation. Is this correct is_numeric($port1) ? It will validate port 80.80 a decimal point is a valid numeric character. Solution:

Replace line: while (!is_numeric ($port1)){

With: while (preg_match ("/[^0-9]/", $port1)){

[^0-9] Looks for any character that is NOT a digit and returns true.

As an alternative use [^\d] its identical to [^0-9]

Another alternative use ctype_digit() for example

while (!ctype_digit($port1)){

Validation can be a pain and requires some real though otherwise it can introduce unpredictable problems that are difficult to resolve.

Run the batch file (double click on Run.bat). Experiment with different input.

Top

Summary

For most scripts requiring user input the above is more than adequate. Should you require something more specialised search the Internet.

If you are running UniServer Nano and have created a server certificate or moved the server you will have used the above function. A few pages ago I mentioned this tutorial series has a specific objective, may be the penny is beginning to drop!.

The real power of any scripting language is its ability to manipulate files a few useful techniques are covered on the next page.

Top


  MPG (Ric)