Any PHP code that we write is usually run on the server when users request a webpage. However, it is also possible to run a script through a CLI or command-line interface. Sometimes it becomes necessary to write PHP scripts that can accept arguments when executed inside a command prompt. There can be a variety of reasons for doing this ranging from ease of use to automation of specific tasks.
In this tutorial, you will learn how to access command-line arguments inside your script with $argv
and getopt()
in PHP.
Why Do We Need Them?
Arguments are passed around as query parameters when you request a page from your web browser. On the server side, we access the values inside these parameters using the superglobal $_GET
. Unfortunately, this technique won't work when you are running your script inside the command prompt. This is because a command prompt expects arguments for its script in a different format.
Lets create a simple script to better understand the problem. Put the following code inside a file called reverse.php.
<?php $original = $_GET["name"]; echo strrev($original); ?>
Now, try to run the following command from the directory in which the file reverse.php is located.
php reverse.php?n=monty
This will give you an error similar to the following line.
Could not open input file: reverse.php?name=monty
The reason this happens is because the system tries to run a file called reverse.php?n=monty within the current directory but there is no such file.
You can run the following command successfully but it still won't output the reversed name.
php reverse.php monty
It will show a warning about an undefined array key "n" instead. The use of $argv
and getopt()
solves this problem for us.
Get Command-Line Arguments with $argv
There are two predefined variables in PHP called $argc
and $argv
that can be used to work with command-line arguments.
The variable $argc
simply tells you the number of arguments that were passed to the script. Remember that the name of the script you are running is always counted as an argument. This means that the minimum value of $argc
would be 1.
The variable $argv
is much more helpful because it is an array of arguments passed to the script and contains their actual value. You can iterate over all the values with a foreach
loop.
Lets create a script to change the brightness and contrast of JPEG images. The script will accept three parameters besides its name: the name of the file, desired brightness and desired contrast. Put the following code in a file called im-filters.php.
<?php $filters = array_fill(0, 3, null); for($i = 1; $i < $argc; $i++) { $filters[$i - 1] = $argv[$i]; } $filename = $filters[0]; $brightness = $filters[1]; $contrast = $filters[2]; $new_name = str_replace('.jpg', '-edited.jpg', $filename); $im = imagecreatefromjpeg($filename); imagefilter($im, IMG_FILTER_BRIGHTNESS, $brightness); imagefilter($im, IMG_FILTER_CONTRAST, $contrast); echo 'Edited Image: '.$filename.' (Brightness: '.$brightness.' and Contrast: '.$contrast.')'; imagejpeg($im, $new_name, 80); imagedestroy($im); ?>
We create an array with three elements and autofill it with null
. Now, we loop over the command-line arguments and put their values inside our $filters
array. We have skipped the first argument because that's the name of the script itself. The name of the new file is created by adding -edited at the and of the original file name.
Now, run the following command in the terminal:
php im-filters.php beach.jpg -40 -20
This should create the following output in the terminal while generating a file called beach-edited.jpg.
Edited Image: beach.jpg (Brightness: -40 and Contrast: -20)
Get Command-Line Arguments with getopt()
There are a few shortcomings in the method that we used to access command-line arguments in our previous example. For instance, we are assuming that the arguments will be provided in the order in which we are processing them in the script. However, that might not always be the case.
The getopt()
function in PHP is a great way for accessing all these arguments without worrying too much about their order etc. It has one required parameter and two optional parameters.
short_options
: Short options in the command-line start with a single hyphen. Each character passed into this string is matched against options passed to the script.long_options
: This optional parameter accepts an array. Each element (a string) in the array is matched against options passed to the script starting with two hyphens.rest_index
: The optional third parameter is used to store the index at which the parsing of arguments stopped.
Both the short_options
and long_options
follow a specific convention to parse values passed to the script. Individual characters and strings don't accept any values. Characters and strings followed by a colon indicate a required value. Characters and strings followed by two colons indicate optional value. Here are some examples:
-g
and--grayscale
both will not accept any values. We will use them in our script to convert images to grayscale.-f:
and--filename:
both will require a value passed to them. We will use them to make sure that users provide us an image file on which we can apply our filters.-c::
and--contrast::
both will accept an optional value. We will use them to give users the option to not change the contrast of the image unnecessarily. The same goes for-b::
and--brightness::
.
One thing you should remember is that the array returned by getopt()
does not contain elements which were missing from the argument list. Also, the elements present in argument list which did not have a specified value are set to false
.
Now it is time to create our script with all these points in mind.
<?php $short_options = "f:gc::b::"; $long_options = ["filename:", "grayscale", "contrast::", "brightness::"]; $options = getopt($short_options, $long_options); if(isset($options["f"]) || isset($options["filename"])) { $filename = isset($options["f"]) ? $options["f"] : $options["filename"]; } $new_name = str_replace('.jpg', '-edited.jpg', $filename); $filter_list = ''; $im = imagecreatefromjpeg($filename); if(isset($options["g"]) || isset($options["grayscale"])) { $filter_list .= 'Grayscale '; imagefilter($im, IMG_FILTER_GRAYSCALE); } if(isset($options["b"]) || isset($options["brightness"])) { $brightness = isset($options["b"]) ? $options["b"] : $options["brightness"]; $filter_list .= 'Brightness:'.$brightness.' '; imagefilter($im, IMG_FILTER_BRIGHTNESS, $brightness); } if(isset($options["c"]) || isset($options["contrast"])) { $contrast = isset($options["c"]) ? $options["c"] : $options["contrast"]; $filter_list .= 'Contrast:'.$contrast.' '; imagefilter($im, IMG_FILTER_CONTRAST, $contrast); } echo 'Edited Image: '.$filename.' '.$filter_list; imagejpeg($im, $new_name, 80); imagedestroy($im); ?>
We accept four different arguments for our image editing script and have set only the filename to be required. The grayscale filter does not accept any parameter so we have marked it as such.
We check if the value has been supplied with a long form or short form option for each of our arguments. The code inside the if
block will execute in either case. After that, the ternary operator allows us to pick the correct form to get our value with a single statement. For example, lets say a user has provided a contrast value using the long form option. In this case, isset($options["c"])
will be false
and $contrast
will be set according to the value in $options["contrast"]
.
The $filter_list
string is used to keep track of all the filters applied on the image to show them to the user.
Try running the following command in the console now.
php im-filters.php --filename="beach.jpg" --grayscale -c-20 --brightness="-30"
It should show you the following output:
Edited Image: beach.jpg Grayscale Brightness:-30 Contrast:-20
Avoiding Script Duplication
We have seen that different code needs to be used to access argument values when the script runs on a web server vs when it runs in a CLI. However, this does not mean that you will require two copies of the same script to run in different environments. There is a function called php_sapi_name()
that will tell you whether your code is running inside a web server or a CLI. The predefined constant PHP_SAPI
serves the same purpose.
<?php if (PHP_SAPI == "cli") { // Access argument values using getopt() } else { // Access argument values using $_GET etc. } // Rest of the code. ?>
Final Thoughts
There are a lot of times when we have to write code that will work on provided input to give us some output. Usually, the input is handled by $_GET
and $_PUT
when the script is running on a server. However, you will sometimes need to run your script through a CLI and that requires you to use $argv
or getopt()
. Using getopt()
gives you more flexbility when dealing with the arguments and does some heavy lifting regarding their parsing for you.
Try running the above script on your own images while changing the order of arguments. You should consider creating your own custom scripts to automate boring tasks with a task scheduler.
No comments:
Post a Comment