Table of Contents
First PHP CLI Program
Here's a typical first program.
<?
echo "hello\n";
Run it with the php
command and no surprises.
$ php hello.php
hello
If you want to run the PHP script directly without the leading php command
then add #!/usr/local/bin/php
as the first line as shown below.
#!/usr/local/bin/php
<?
echo "hello\n";
Run it again...
$ hello.php
-jailshell: hello.php: command not found
Whoops, command not found. The current directory isn't in the path. This
can be fixed by including the current directory in the path like this,
PATH=$PATH:.
. Another alternative is to specify the current directory like
this, ./hello.php
.
The following examples use both methods to run scripts in the current directory.
$ PATH=$PATH:.
$ hello.php
-jailshell: hello.php: Permission denied
Ugh, Permission denied. No worries, the script just needs to be made
executable with the chmod
command.
$ ls -l hello.php
-rw-r--r-- 1 user user Jan 25 09:11 hello.php
$ chmod 744 hello.php
$ ls -l hello.php -rwx r--r-- 1 user user Jan 25 09:11 hello.php*
$ ./hello.php
Content-type: text/html
hello
WARNING: If you see Content-type: text/html then you are not
running the CLI version of PHP. I got this error because I incorrectly
pointed to /usr/bin/php instead of usr/local/bin/php.
Anyway... if you must continue running the cgi version of php then you can
suppress the Content-type: text/html header by running PHP in quiet mode
with #!/usr/bin/php -q
.
#!/usr/bin/php -q
<?
echo "hello\n";
Run the PHP script again and all looks good. Again, the -q switch is only required because I was incorrectly running the cgi version of PHP instead of the cli version. Hostgator makes it transparent and easy to use the CLI version, but I still managed to mess it up.
$ hello.php
hello
SAPI PHP Server API
PHP has a special variant that is tweaked to work from the command-line. Thephp -v
version command will clearly show if your are running
the cli version.
$ which php
/usr/local/bin/php
$ php -v
PHP 5.4.45 (cli) (built: Apr 17 2017 15:59:08)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies
with the ionCube PHP Loader (enabled) + Intrusion Protection from ioncube24.com (unconfigured) v10.2.4, Copyright (c) 2002-2018, by ionCube Ltd.
with Zend Guard Loader v3.3, Copyright (c) 1998-2013, by Zend Technologies
$ /usr/bin/php -v
PHP 5.4.45 (cgi-fcgi) (built: Apr 17 2017 15:59:08)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies
with the ionCube PHP Loader (enabled) + Intrusion Protection from ioncube24.com (unconfigured) v10.2.4, Copyright (c) 2002-2018, by ionCube Ltd.
with Zend Guard Loader v3.3, Copyright (c) 1998-2013, by Zend Technologies
In the above example php -v
, located at /usr/local/bin/php, returns cli
but /usr/bin/php -v
returns cgi-fcgi. My mistake above was to incorrectly
type #!/usr/bin/php
instead of #!/user/local/bin/php
.
CGI-FCGI vs CLI
While I debugged the above issue I stumbled onto, and worked-around, three differences between CLI mode and CGI-FCGI mode.
- The CGI mode prints a Content-type: text/html header, the CLI
does not. The CLI behavior is preferable from the command line. When
I was incorrectly using the CGI version, I worked around the issue by
running PHP in quite mode,
#!/usr/bin/php -q
. Again, this is not needed if you are using CLI mode. - The
getcwd()
function works differently between the two modes. In CGI modegetcwd()
returns the directory where the main is located, often the directory where index.php is. This is intuitively correct when serving web pages, but it's not what is expected from the command line. From the Command Line the PHP script's main is often buried at some-random-path/bin/script.php. So, in CGI mode the functiongetcwd()
returns some-random-path/bin, when you really want the directory you're currently in. In CLI modegetcwd()
behaves exactly as you expect and returns the same value that the Unixpwd
command returns. I worked around this issue by using the variable $_SESSION['PWD'] which returns the expected value. - When in CGI mode the error messages have embedded HTML tags. When in CLI mode the HTML tags are suppressed.
php_sapi_name
The php_sapi_name()
function returns cli or something else, such
as cgi-fcgi. The following example checks the Server API (SAPI) at the
start of the command-line script. If the SAPI is not cli then the
script aborts with an error message.
#!/usr/local/bin/php
<?
$server_api = php_sapi_name();
if ($server_api != 'cli') exit("Wrong Server API (SAPI): '$server_api'\n\n");
echo "Correct SAPI!!\n";
exit;
$ sapi_test.php
Correct SAPI!!
$ /usr/bin/php sapi_test.php
Content-type: text/html
Wrong Server API (SAPI): 'cgi-fcgi'
Version of PHP
Thephp -v
command returns the CLI version of PHP. Note, Apache httpd server
may be configured to use a different version.
$ php -v
PHP 5.4.45 (cli) (built: Apr 17 2017 15:59:08)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies
with the ionCube PHP Loader (enabled) + Intrusion Protection from ioncube24.com (unconfigured) v10.2.4, Copyright (c) 2002-2018, by ionCube Ltd.
with Zend Guard Loader v3.3, Copyright (c) 1998-2013, by Zend Technologies
Which PHP am I using
Thewhich
command returns the location of the program. Sometimes the path
is just a link. The ls -l
command returns where the link points to.
The ls -d
command returns the PHP versions that are available on the
the system.
$ which php
/usr/local/bin/php
$ ls -l /usr/local/bin/php
lrwxrwxrwx 1 root root 18 Jun 26 2018 /usr/local/bin/php -> /opt/php54/bin/php*
$ ls -d /opt/php*
/opt/php52/ /opt/php53/ /opt/php54/ /opt/php55/ /opt/php56/ /opt/php70/ /opt/php71/ /opt/php_with_imap_client/ /opt/phpcur@ /opt/phpedge@ /opt/phpstable@
Change PHP Version
One way to change the PHP version is to create an alias. In the following examplephp -v
returns the current version.
The alias php="/opt/php71/bin/php"
command defines an alias for php.
The php -v
command shows the PHP version changed to 7.1.14.
The unalias
command removes the alias and the PHP version changes back
to 5.4.45.
$ php -v
PHP 5.4.45 (cli) (built: Apr 17 2017 15:59:08)
$ alias php="/opt/php71/bin/php"
$ php -v
PHP 7.1.14 (cli) (built: Feb 23 2018 18:28:03) ( NTS )
$ unalias php
$ php -v
PHP 5.4.45 (cli) (built: Apr 17 2017 15:59:08)
Another way to change the version is to add the desired version to the
beginning of the PATH using the assignment PATH=/opt/php56/bin:$PATH
.
The php -v
and which php
show the version correctly changed. One way to
undo this change is to use the sed
command as shown in the example.
Another way is to exit
the terminal and start a new terminal.
I prefer this method. The alias doesn't seem to always work for me.
$ PATH=/opt/php56/bin:$PATH
$ which php
/opt/php56/bin/php
$ php -v
PHP 5.6.30 (cli) (built: Mar 27 2017 11:48:20)
$ echo $PATH
/opt/php56/bin/php:/usr/local/jdk/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin
$ PATH=`echo $PATH |sed -e 's/[^:]*://'`
$ echo $PATH
/usr/local/jdk/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin
$ php -v
PHP 5.4.45 (cli) (built: Apr 17 2017 15:59:08)
The above alias
and PATH
statements were run from the command line and
the PHP version change will be lost when the terminal is closed. To make these
changes permanent put them into the ~/.bashrc startup script.
Script Tags <? .. ?>
The PHP closing script tag ?>
is not required in any PHP script. It is
often best not to close a script because blank lines after the close
are transmitted to the browser and may cause unexpected, difficult to debug,
behavior. None of my example scripts have a closing tag.
The PHP open tag <?
or <?php
is required in CGI mode. It is not
required in CLI mode when the -r command line option, like
this php -r script.php
.
Arguments $argv and $argc
PHP uses the global variables $argv and $argc for command-line arguments as shown in the following example.
#!/usr/bin/php -q
<?
echo "hello\n";
echo "argc = $argc\n";
echo "count = " . count($argv) . "\n";
echo "sizeof = " . sizeof($argv) . "\n";
$i = 0;
foreach ($argv as $arg) {
echo "argv[$i] = '$arg'\n";
$i++;
}
Array $argv contains the list of arguments. The first item, $argv[0], contains the name of the PHP script. $argc indicates the number of arguments in $argv and is the same as count($argv) or sizeof($argv).
$ hello.php cat dog
hello
argc = 3
count = 3
sizeof = 3
argv[0] = './hello.php'
argv[1] = 'cat'
argv[2] = 'dog'
PHP Functions & Commands
file_exists
- requires full path file_exists($pwd . DIRECTORY_SEPARATOR . $filename)getcwd
- returns the path to php's "main" function.$_SESSION['PWD']
- this is usually what I think pwd should be.copy
- copies just one file at a time. You need to roll your own cp -r