Can PHP detect if its run from a cron job or from the command line?

Viewed 43632

I'm looking for way to PHP to detect if a script was run from a manual invocation on a shell (me logging in and running it), or if it was run from the crontab entry.

I have various maintenance type scripts written in php that i have set to run in my crontab. Occasionally, and I need to run them manually ahead of schedule or if something failed/broken, i need to run them a couple times.

The problem with this is that I also have some external notifications set into the tasks (posting to twitter, sending an email, etc) that I DONT want to happen everytime I run the script manually.

I'm using php5 (if it matters), its a fairly standard linux server environment.

Any ideas?

22 Answers
if (php_sapi_name() == 'cli') {   
   if (isset($_SERVER['TERM'])) {   
      echo "The script was run from a manual invocation on a shell";   
   } else {   
      echo "The script was run from the crontab entry";   
   }   
} else { 
   echo "The script was run from a webserver, or something else";   
}

Instead of detecting when the script is run from the crontab, it's probably easier to detect when you're running it manually.

There are a lot of environment variables (in the $_ENV array) that are set when you run a script from the command line. What these are will vary depending on your sever setup and how you log in. In my environment, the following environment variables are set when running a script manually that aren't present when running from cron:

  • TERM
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

There are others too. So for example if you always use SSH to access the box, then the following line would detect if the script is running from cron:

$cron = !isset($_ENV['SSH_CLIENT']);

You can setup an extra parameter, or add a line in your crontab, perhaps:

CRON=running

And then you can check your environment variables for "CRON". Also, try checking the $SHELL variable, I'm not sure if/what cron sets it to.

The right approach is to use the posix_isatty() function on e.g. the stdout file descriptor, like so:

if (posix_isatty(STDOUT))
    /* do interactive terminal stuff here */

I don't know about PHP specifically but you could walk up the process tree until you found either init or cron.

Assuming PHP can get it's own process ID and run external commands, it should be a matter of executing ps -ef | grep pid where pid is your own process ID and extract the parent process ID (PPID) from it.

Then do the same to that PPID until you either reach cron as a parent or init as a parent.

For example, this is my process tree and you can see the ownership chain, 1 -> 6386 -> 6390 -> 6408.

UID     PID  PPID  C  STIME  TTY        TIME  CMD
root      1     0  0  16:21  ?      00:00:00  /sbin/init
allan  6386     1  0  19:04  ?      00:00:00  gnome-terminal --geom...
allan  6390  6386  0  19:04  pts/0  00:00:00  bash
allan  6408  6390  0  19:04  pts/0  00:00:00  ps -ef

The same processes run under cron would look like:

UID     PID  PPID  C  STIME  TTY        TIME  CMD
root      1     0  0  16:21  ?      00:00:00  /sbin/init
root   5704     1  0  16:22  ?      00:00:00  /usr/sbin/cron
allan  6390  5704  0  19:04  pts/0  00:00:00  bash
allan  6408  6390  0  19:04  pts/0  00:00:00  ps -ef

This "walking up the process tree" solution means you don't have to worry about introducing an artificial parameter to indicate whether you're running under cron or not - you may forget to do it in your interactive session and stuff things up.

Not that I know of - probably the simplest solution is providing an extra parameter yourself to tell the script how it was invoked.

I would look into $_ENV (var_dump() it) and check if you notice a difference when you run it vs. when the cronjob runs it. Aside from that I don't think there is an "official" switch that tells you what happened.

In my environment, I found that TERM was set in $_SERVER if run from the command line, but not set if run via Apache as a web request. I put this at the top of my script that I might run from the command line, or might access via a web browser:

if (isset($_SERVER{'TERM'}))
{
    class::doStuffShell();
}
else
{
    class::doStuffWeb();
}

In the cron command, add ?source=cron to the end of the script path. Then, in your script, check $_GET['source'].

EDIT: sorry, it's a shell script so can't use qs. You can, I think, pass arguments in the form php script.php arg1 arg2 and then read them with $argv.

$_SERVER['SESSIONNAME'] contains Console if run from the CLI. Maybe that helps.

if(!$_SERVER['HTTP_HOST']) {
 blabla();
}

I think it would be better to run the cron commmand with an additional option at the command line that you wouldn't run manually.

cron would do:

command ext_updates=1

manual would do:

command 

Just add an option in the script itself to have the ext_updates param to have a default value of false.

On my Amazon Linux server this is what worked for me:

$isCron = ( $_SERVER['HOME'] == '/' );

The home directory is set to yours if you just run it. If you use sudo to run it, the home directory is set to /root.

(array) $argv will be set when the cron job runs. The first value of the array will be the /path/to/file you used when creating the cron job. The following values in $argv will be any parameters that followed the /path/to/file.

For example, if your cron job command looks like this:

php /path/to/file.php first second third

The value of $argv will be: ["/path/to/file.php", "first", "second", "third"]

You can then:

if (isset($argv) && is_array($argv) && in_array('first', $argv)) { /* do something */ }

It's easy for me... Just count($_SERVER['argc']) and if you have a result higher than zero it will be running out of a server. You just need to add to your $_SERVER['argv'] your custom variable, like "CronJob"=true;

Related