PHP CLI: User Input
PHP CLI : Introduction | Paths | PHP INI | Process Running | Detached Processes | Hidden Process | User Input | Files | Search & Replace | Recursive Search & Replace
|
|
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
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.
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
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 |
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.
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.
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 ============ |
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.
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.
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.
MPG (Ric) |