This question already has answers here:
Closed 14 years ago.
Is there a way to distinguish if a script was invoked from the command line or by the web server?
(See What is the canonical way to determine commandline vs. http execution of a PHP script? for best answer and more detailed discussion - didn't find that one before posting)
I have a (non-production) server with Apache 2.2.10 and PHP 5.2.6. On it, in a web-accessible directory is my PHP script, maintenance_tasks.php. I would like to invoke this script from the command line or through a HTTP request (by opening in a browser). Is there some variable that allows me to reliably determine how script is invoked?
(I already tackled the issues of different views for each type of invocation and HTTP response timeout, just looking for a way of telling the two invocation types apart)
I'll be trying different things and add my findings below.
Duplicate: What is the canonical way to determine commandline vs. http execution of a PHP script?
If called from command line, the server variable HTTP_USER_AGENT is not set. I use this constant to define, whether the script is called from command line or not:
define("CLI", !isset($_SERVER['HTTP_USER_AGENT']));
UPDATE: Since this answer is still marked as the 'correct' one, I'd like to revise my statement - relying on the "User-Agent" header can be problematic, since it's a user-defined value.
Please use php_sapi_name() == 'cli' or PHP_SAPI == 'cli', as suggested by Eugene/cam8001 in the comments.
Thanks for pointing this out!
I've compared the $_SERVER superglobal in both invocations. It seems that $_SERVER['argc'] (i.e. number of arguments passed to the script) is only set when running from shell/command line:
<?php
if (isset($_SERVER['argc'])) {
define('CLI', true);
} else {
define('CLI', false);
}
That seems to work both on Linux and Windows hosts. (First I thought about checking for some of the environment variables, but those are different for every operating system. Also, all the $_SERVER['HTTP_*'] headers are missing in the CLI version, but I'm not sure if that's reliable enough.)
Related
I am using this code header('Location: http://example.com/test.php?number='.$requestsDone.'');
But looks like it is not working, what is wrong here?
Let me know, if you need more information.
Producing a header in a command line script doesn't make any sense. The header is part of the HTTP protocol, there is no HTTP involved when the script is executed using the CLI version of PHP.
Accordingly, the header() function is not implemented in the CLI version of PHP. It exists, but it doesn't produce any output.
Also, the superglobals that contain information extracted from the HTTP request ($_GET[], $_POST[], $_REQUEST[], $_FILES[], $_COOKIE[] etc) exist but they are empty.
In order to pass arguments to a script using the command line, use the $argc and $argv[] variables.
I'd use the following code:
$SERVER_PATH = dirname(__FILE__);
shell_exec($PHP_LOCATION.' '.$SERVER_PATH."/script.php?k1=v1&k2=v2 > /dev/null 2>/dev/null &");
Where:
$PHP_LOCATION should contain the path to PHP,
$SERVER_PATH - is current working directory (fortunately the script to run is in the same directory),
> /dev/null 2>/dev/null & added to make this call asynchronous (taken from Asynchronous shell exec in PHP question)
This code has two problems:
As far as I remember ?k1=v1&k2=v2 will work for web-call only, so in this particular case parameters will not be passed to the script.
I don't really know how to init the $PHP_LOCATION variable to be flexible and to work on the most hosts.
I conducted some research regarding both problems:
To solve 1 suggested to use -- 'parameters_string' but it is also recommended to modify the script to parse parameters string which looks a bit clumsy. Is there a better solution?
To solve 2 I found a solution to use PHP_BINARY but this is a PHP 5.4+ case (I'm using 5.3). But the original question was about to run PHP of the same version as the original script version. So for me (as I use PHP 5.3 only) is there probably a solution?
EDIT 0
Let me do some explanation why I stuck to this weird (for PHP) approach:
Those PHP scripts should be separate from each other:
one of those will analyze the data and
the second will generate PNG graphs as a final result.
Those scripts aren't intended to run simultaneously, this means that the second can run at it's own schedule it is only needed that the run should be upon its data will be ready (which done by the first script). So no data should be passed back from second script (child) to the first (parent).
EDIT 1
As seeing from most of the comments the main discussion goes to forking direction. However I'd like to make stress on the point 1 and 2 asked in the original questions. I have some reasons to solve the task in the way I pointed out and I tried to point all that reason. If some of my points looks weird, please post a comment - I will make it more clear or I will change the main question.
Thank you in advance!
How to get executable
Assuming you're using Linux, you can use:
function getBinaryRunner($binary)
{
return trim(shell_exec('which '.$binary));
}
For instance, same may be used for checking if needed stuff is installed:
function checkIfCommandExists($command)
{
$result = shell_exec('which '.$command);
return !empty($result);
}
Some points:
Yes, it will work only for Linux
You should be careful with user input if it is allowed to be passed to shell commands: escapeshellarg() and company
Indeed, normally PHP should not be used for stuff like this as if it is about asynchronous requests, better to either implement forking or run commands from external workers.
How to pass parameters
With doing shell_exec() via file system path you're accessing the file and, obviously, all "GET" parameters are becoming just part of file name, it is no longer "URI" as there is no web-server to process that. So you have two options:
Invoke call via accessing your web-server. So it will be like:
//Yes, you will use wget or, better, curl to make web-request from CLI
shell_exec('wget http://your.web-server.domain/script.php?foo=bar');
Downside here: if you'll access your web-server via public DNS, it will cause network gap and all processing overheads. Benefit - obviously, you will not have to expect anything else in your script and make no distinction between CLI and non-CLI calls
Use $_SERVER array in your script and pass parameters as it should be with CLI:
shell_exec('/usr/bin/php /path/to/script.php foo bar');
//inside your script.php you will see:
//$_SERVER['argv'][0] is "script.php"
//$_SERVER['argv'][1] is "foo"
//$_SERVER['argv'][2] is "bar"
Yes, it will require modification in the script, and, probably, some logic of how to map "regular" web-requests and CLI ones. I would suggest even to think of separating CLI-related stuff to different scripts bundle so not to mess that logic.
More about "asynchronous run"
When you do php script.php & you just run it in background mode. That, however, still keeps parent-child relation for your process. That means - if parent process dies, it's childs will also be removed. To be precise, SIGHUP will be triggered and to avoid this situation you should use nohup command. It will allow to emulate "detaching" of a process and therefore making it's run reliable and independent of circumstances happening to parent process.
The welcome.js code is given below
console.log('Wellcome');
and the php file code is given below
$op = shell_exec('node welcome.js').PHP_EOL;
echo $op
If I run the php file in command line it print Wellcome but when I run from browser it does not print any output.
There are most likely errors here that you're not seeing.
Set 'error_reporting' to -1 and 'display_errors' to 1 in your php.ini and be sure to restart your webserver/fastcgi-listeners. This is more reliable than using ini_set() and error_reporting() in the script, which will fail if there are parse errors...see php:errorfunc.configuration for more detail.
Check the appropriate error log (depending on your settings and the Server API, this can be the httpd's error log, syslog, some independent file, or even going nowhere right now). Again, php:errorfunc.configuration can help you get things configured correctly, or suss out the current configuration.
The $PATH (or %PATH% on Win32) for an interactive login session is usually dramatically different than that of a running daemon. Try specifying the full path to the node binary.
I don't know off-hand which file-handle node's "console.log()" goes out to. Assuming you're using a bourne-style shell (such as bash) for the subshell here, you can try piping stderr to stdout, using something like: $op = shell_exec('/foo/bin/node welcome.js 2>&1').PHP_EOL; echo $op;
Make sure that 'welcome.js' is where you think it is in relation to the current working directory of your PHP process (although it's likely that node would warn you via one of the previous suggestions if this were not the case, it seemed worth pointing out as a potential pitfall.)
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
php exec command (or similar) to not wait for result
I have a page that runs a series of exec() commands which forces my PHP script to halt alteration until it receives a response. How can I tell exec() to not wait for a response and just run the command?
I'm using a complex command that has a backend system I can query to check the status, so I'm not concerned with a response.
Depends on what platform you are using, and the command you are running.
For example, on Unix/Linux you can append > /dev/null & to the end of the command to tell the shell to release the process you have started and exec will return immediately. This doesn't work on Windows, but there is an alternative approach using the COM object (See edit below).
Many commands have a command line argument that can be passed so they release their association with the terminal and return immediately. Also, some commands will appear to hang because they have asked a question and are waiting for user input to tell them to continue (e.g. when running gzip and the target file already exists). In these cases, there is usually a command line argument that can be passed to tell the program how to handle this and not ask the question (in the gzip example you would pass -f).
EDIT
Here is the code to do what you want on Windows, as long as COM is available:
$commandToExec = 'somecommand.exe';
$wshShell = new COM("WScript.Shell");
$wshShell->Run($commandToExec, 0, FALSE);
Note that it is the third, FALSE parameter that tells WshShell to launch the program then return immediately (the second 0 parameter is defined as 'window style' and is probably meaningless here - you could pass any integer value). The WshShell object is documented here. This definitely works, I have used it before...
I have also edited above to reflect the fact that piping to /dev/null is also required in order to get & to work with exec() on *nix.
Also just added a bit more info about WshShell.
In the past, I've had good luck with constructs like the following (windows, but I'm sure there's an equivalent command in *nix
pclose(popen('START /B some_command','r'));
What about running command in background ?
exec('./run &');
I'm writing a PHP script that I want to disable from web access (I will ask users to move it out of the web root and execute via CLI, but you never know if they'll listen!)
Is there a simple function that occurs to anyone to make the page die if it's requested by a browser?
Thanks for any ideas.
You could test whether the script is being run through the CLI using php_sapi_name().
It can return a whole bunch of different possible values when run on a HTTP server - difficult to make a reliable distinction there - but there seems to be only one possible return value for the CLI: cli.
If you're looking for an all-purpose solution, make sure you read the comment thread below for more detailed discussion on some potential gotchas.
'PHP_SELF' The filename of the
currently executing script, relative
to the document root. For instance,
$_SERVER['PHP_SELF'] in a script at
the address
http://example.com/test.php/foo.bar
would be /test.php/foo.bar. The
FILE constant contains the full path and filename of the current (i.e.
included) file. If PHP is running as a
command-line processor this variable
contains the script name since PHP
4.3.0. Previously it was not available.
http://www.php.net/manual/en/reserved.variables.server.php
I'm not a PHP expert but you could check the $_SERVER variable ?
You can use php-sapi-name function to detect if script was requested by web server or cli interface.
$sapi_type = php_sapi_name();
if (substr($sapi_type, 0, 3) != 'cli') {
die "You are not using CLI".PHP_EOL;
}
you can use php-sapi-name or you can use the predefined constant which is marginally faster (although you'd never notice!)
<?php
if(PHP_SAPI != 'cli') exit;
// continue