Uberfuzzy
Uberfuzzy

Reputation: 8372

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

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?

Upvotes: 71

Views: 49678

Answers (22)

iaps
iaps

Reputation: 51

(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 */ }

Upvotes: 1

Phil Marshall
Phil Marshall

Reputation: 11

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.

Upvotes: 0

davethebrave
davethebrave

Reputation: 823

Here's what I use to discover where the script is executed from. Look at the php_sapi_name function for more information: http://www.php.net/manual/en/function.php-sapi-name.php

$sapi_type = php_sapi_name();
if(substr($sapi_type, 0, 3) == 'cli' || empty($_SERVER['REMOTE_ADDR'])) {
    echo "shell";
} else {
    echo "webserver";
}

EDIT: If php_sapi_name() does not include cli (could be cli or cli_server) then we check if $_SERVER['REMOTE_ADDR'] is empty. When called from the command line, this should be empty.

Upvotes: 31

eosphere
eosphere

Reputation: 344

Another option would be to test a specific environment variable that is set when the php file is invoked through the web and not set if runned by the commandline.

On my web server I'm testing if the APACHE_RUN_DIR environment variable is set like this :

if (isset($_ENV["APACHE_RUN_DIR"])) {
  // I'm called by a web user
}
else {
  // I'm called by crontab
} 

To make sure it will work on your web server, you can put a dummy php file on your web server with this single statement :

<?php var_dump($_ENV);  ?>

Then 1) load it with your web browser and 2) load it from the commandline like this

/usr/bin/php /var/www/yourpath/dummy.php

Compare the differences and test the appropriate variable.

Upvotes: 2

user213154
user213154

Reputation:

getenv('TERM')

Padding for SO's 30 char min.

Upvotes: 3

Gabor Garami
Gabor Garami

Reputation: 1265

This is very easy. Cron Daemons always export MAILTO environment variable. Check if it exists and has a non-empty value - then you running from cron.

Upvotes: 0

MingalevME
MingalevME

Reputation: 1975

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";   
}

Upvotes: 56

Adam Hopkinson
Adam Hopkinson

Reputation: 28795

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.

Upvotes: 2

Bouke
Bouke

Reputation: 12138

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

Upvotes: 1

Christopher Rivera
Christopher Rivera

Reputation:

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.

Upvotes: 0

Kurausu
Kurausu

Reputation: 1

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;

Upvotes: -4

PeterDerMeter
PeterDerMeter

Reputation: 61

Creepy. Try

if (!isset($_SERVER['HTTP_USER_AGENT'])) {

instead. PHP Client Binary dont send it. Term Type just works when PHP is used as module (ie apache) but when running php through CGI interface, use the example above!

Upvotes: 6

Ivor
Ivor

Reputation:

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

Upvotes: 0

agi
agi

Reputation: 201

I think that the most universal solution is to add an environment variable to the cron command, and look for it on the code. It will work on every system.

If the command executed by the cron is, for example:

"/usr/bin/php -q /var/www/vhosts/myuser/index.php"

Change it to

"CRON_MODE=1 /usr/bin/php -q /var/www/vhosts/myuser/index.php"

Then you can check it on the code:

if (!getenv('CRON_MODE'))
    print "Sorry, only CRON can access this script";

Upvotes: 20

Daniel
Daniel

Reputation: 27

posix_isatty(STDOUT) return FALSE if the output of the cli call is redirected (pipe or file)...

Upvotes: 1

Wouter Bolsterlee
Wouter Bolsterlee

Reputation: 171

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 */

Upvotes: 17

Drew Stephens
Drew Stephens

Reputation: 17827

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();
}

Upvotes: 4

Paul Stone
Paul Stone

Reputation: 6266

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']);

Upvotes: 48

paxdiablo
paxdiablo

Reputation: 881363

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.

Upvotes: 6

Matthew Scharley
Matthew Scharley

Reputation: 132254

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.

Upvotes: 33

Till
Till

Reputation: 22408

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.

Upvotes: 5

Dominic Rodger
Dominic Rodger

Reputation: 99751

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

Upvotes: 5

Related Questions