Detecting the environment in PHP CLI scripts - php

I've seen several questions on StackOverflow that handle how to detect the user environment when a script is served via Apache and have such a system in place which works perfectly.
The script depends on the Environment vars set by Apache.
Now I'm creating several tools that need to run in php CLI mode but I don't know how to detect the environment in CLI mode since the apache env vars aren't present there.
I'm thinking about reading the hostname of the server with gethostname() but don't know if that's the best solution and a reliable solution.
What is considered the best way to detect your environment when running in CLI mode with PHP
Clarification:
I'm not looking for a way to detect if i'm running a script from CLI, that base is covered. I'm looking for a way to determine what server my script is running on (DEV, STAGING or PRODUCTION) through CLI means, thus not reading the apache env variables set in my VHOST that are present when running the same script through a browser.

You can use php_uname('n') to get the hostname of your current machine. From there it's up to your implementation to determine whether it's production, staging, or development, either based on patterns in host name, hard-coded values, or some other configuration file.

I'd set the variable in /etc/environment (or its equivalent based on OS flavor) such as
MYAPP_ENV=STAGING.
This will auto-magically get picked up by CLI php runtime and become available in $_SERVER Global variable as such:
_$SERVER['MYAPP_ENV']
If you expect this to work whether the script runs from HTTPD or CLI, you also need to set this variable (assuming apache) in the .htaccess or the vhosts.conf apache file for you app as such:
SetEnv MYAPP_ENV STAGING
From your application code you just need to reference _$SERVER to access this variable regardless of whether the invocation is from CLI or over http.
It sucks that you have to set this variable twice on your machine - one for Apache and once for CLI but at least from the app layer you can be agnostic about the execution runtime when reading the environment variable.

By environment I presume you mean operating system - and there is a fool-proof way to do this from any PHP instance, Apache or no: the constant PHP_OS and the function php_uname().
If you mean "detect whether it's CLI or not", just test isset($GLOBALS['argv']) - it will not be set using a webserver, it will be if it's CLI. Or you can do (PHP_SAPI === 'cli').

I've always used naming matching with server hostnames:
Imagining the hostnames look like this:
- server.production.project.com
- server.development.project.com
You can determine the host using its name and some bash (or php) magic:
$server_env = `hostname | cut -d \. -f 2`; # production or development
# pure php (5.3+) method:
$server_name = gethostname();
$server_name_arr = explode( '.', $server_name );
$server_env = $server_name_arr[1];
The hostnames should most certainly be different ;)
Cheers

you likely need to set the env system wide then not just from apache, then apache scripts can read it AND cli scripts can read it from ENV , you would set them for interactive shells via /etc/profile like
APP_ENV = 'staging'
export APP_ENV
( you do it via 2 lines like that so other things in the script later can refer to APP_ENV also and get the same value, otherwise APP_ENV is not avail to that shell until its re-run )
then just check $_ENV from your programs on the cli or apache , but apache may need a restart, been a while since I've put this into practice but its great for like json encoded settings for DB connections too, but thats for another post.

To not define the environment variables twice, I did the following (in Ubuntu).
Define your variables in /etc/profile.d/env.sh, for example
export APPLICATION_ENV=production
Include that file in /etc/apache2/envvars by adding the following line:
. /etc/profile.d/env.sh
Restart apache2 and your shell to reload the environment.

$cli = false;
if (isset($argc)) $cli = true;
See: $argc

Even though this is an ancient thread you can also set and retrieve variables in php.ini where you can set values in one place and have them available through server or cli:
In php.ini:
[PHP]
MY_ENVIRONMENT = development
;;;;;;;;;;;;;;;;;;;
; About php.ini ;
;;;;;;;;;;;;;;;;;;;
; PHP's initialization file, generally called php.ini, is responsible for...
Then from cli:
php > echo get_cfg_var('MY_ENVIRONMENT');
development
and a web page (note: you have to restart apache for this variable to be available):
<?php
echo "you are in: " . get_cfg_var('MY_ENVIRONMENT');
// you are in: development

I have a number of cli scripts that rely on sh/Bash environment variables, and there's usually more than a handful. To make my life easier I just load all of them in.
The output is an array with the keys equaling the shell environment variable name, and the value being a string of what the variable was set to.
e.g. $env_var['HOME'] => "/home/user"
To use this code, make sure whatever shell you're running in supports the printenv command.
$sh_env_arr = explode(PHP_EOL, shell_exec('printenv'));
foreach($sh_env_arr as $var_lines) {
$vals = explode('=', $var_lines);
/* Some shell vars can have multiple equals signs as part of the value.
We need to make those into a single string */
if (isset($vals[2])){
foreach (range(2, count($vals)-1) as $i) {
$vals[1] = $vals[1].'='.$vals[$i];
}
}
if (!isset($vals[1])) {$vals[1] = "";}
$env_var[$vals[0]] = $vals[1];
}
I've even played games with this using containers since you can pass environment variables in from the host when they are launched. This can be helpful in getting sensitive info (db creds etc.) into the script.

As far as I'm aware you can use $_ENV and getenv() when running in the shell, just like you can while running under a web server, though the actual values available in the env will probably differ.

Related

How does getenv() get the parameters of the http request parsed by nginx?

When writing CGI program for spawn-fcgi, use getenv() to get environment variables, that is, the parameters set by fastcgi_param command in fastcgi.conf. but when I use the env command output on the terminal in Linux, I can't find the corresponding environment variable. Why?
fastcgi_param
Sets a parameter that should be passed to the FastCGI server. The value can contain text, variables, and their combination. These directives are inherited from the previous configuration level if and only if there are no fastcgi_param directives defined on the current level.
Since it says that these parameters are passed by nginx to the FastCGI server, how does getenv() get these parameters? I learned that getenv() is used to obtain environment variables. So did spawn-fcgi create these temporary environment variables according to fastcgi.conf when ngnix passed dynamic requests?
The fastcgi library directly modifies the environ variable of the program, simulating a new environment after accepting a request. See: https://github.com/FastCGI-Archives/fcgi2/blob/874b148dfeadf8ebadff89cf228e63f836e509a3/doc/FCGI_Accept.3
After accepting a new request, FCGI_Accept assigns new values
to the global variables stdin, stdout, stderr, and environ.
After FCGI_Accept returns, these variables have the same
interpretation as on entry to a CGI program.
And https://pubs.opengroup.org/onlinepubs/7908799/xsh/environ.html
Each process has its own set of environment variables.
When a new process gets created it inherits the environment variables from its parent process.
The env command gets started by your terminal process, it inherits the terminal's environment variables and that's what it shows you.
As you know, your terminal does not get started by spawn-fcgi. Although nobody knows which came first, the chicken or the egg, it's fairly certain that your terminal process does not get started by spawn-fcgi. And that's why your terminal does not show the environment variables that spawn-fcgi sets and are inherited by the processes that spawn-fcgi, itself, starts.

Add environment variable to access from php script without reboot?

I need to access an environment variable in my php script. It's running on a remote server. I've added them to /etc/environment but I can't access them as the server needs a reboot.
They can be echoed when I run
source /etc/environment
so it's not a problem with the file.
But the php script isn't running as my user so it doesn't have those variables. Since it's a remote server I don't particularly want to reboot it, as I assume this would then pull in the new environment variables I wrote to /etc/environment.
I've seen I can use /etc/profile but that won't affect the apache user, which I assume is running the php scripts.
I've tried to run
echo exec('source /env/environment && echo $the_var');
but the variable still doesn't return.
Any ideas please?
Thanks in advance

shell_exec in PHP is not working

shell_exec()
is not working on my localhost. I read a lot of websites to resolve this issue but can't find a solution.
This code:
$output = shell_exec("env");
print "<pre>$output</pre>";
Doesn't give any output.
I checked in php.ini disable_functions but shell_exec is not disabled.
I did error_reporting = E_ALL but again no output (no error). All I get is just a blank screen. Even safe_mode is off.
If I write echo "BULB"; after the above code it is printing "BULB".
What could be the issue?
What info are you expecting to get from env? From your comments, it seems to me as if you're trying to use a Linux command on a Windows system -- that's never going to work.
On Linux systems, the env command on its own like that returns a list of environment variables that have been defined. However env is not a valid command in Windows.
If you're simply looking for the list of environment variables, this can actually be obtained in PHP without having to go to a shell command. PHP has a built-in global variable $_ENV which contains a copy of all the environment variables that were defined when the program stated. Simply print_r($_ENV) to see them.
On the other hand, if you really need to use shell_exec() for some reason, then you need to take into account the operating system that you're using. On Linux you would use env command. The equivalent on Windows is set without any arguments. So your code becomes:
$output = shell_exec("set");
Note that the format of the output may not be identical to what you'd get on Linux, so if you're parsing it, that code will have to change too.
If you need your code to be able to run on multiple platforms then you would need to write additional code before the shell_exec() call to determine the operating system and work out the correct command to use.

Setting PHP enviromental variable while running command line script

I need to run a PHP script from command line and I need to set some environmental variables. Unfortunately, following does not work:
php -dAPPLICATION_ENV=staging script.php
What I'd like to accomplish is having APPLICATION_ENV variable set.
APPLICATION_ENV=staging php script.php
The variable will be available in the $_SERVER array:
echo $_SERVER['APPLICATION_ENV'];
There is no way to set environment variables from the command line specifically for the execution of a script by passing options to the PHP binary.
You have a few options:
Set the variable globally on the system.
Set the variable on the command line before calling the script. This will persist in the environment after your script has finished executing, which you may not want.
Wrap the PHP script in another script, allowing you to create a temporary variable that exists only for the duration of the script.
Use a command line option instead of an environment variable.
The last two options are probably the cleanest way to do this, in that the variable created only exists for the run time of your script.
The implementation of option 1 is system dependent.
The implementation of option 2 is also system dependent - on Windows you would do set APPLICATION_ENV=staging&& php script.php and on *nix it would be export APPLICATION_ENV='staging' && php script.php.
If you were to go for option 3 you might be tempted to go for a shell script, but this is not portable (you would need a batch file for Windows and a shell script for *nix environments. Instead, I'd suggest you write a simple PHP wrapper script, something like this:
<?php
putenv('APPLICATION_ENV=staging');
include('script.php');
This allows you to leave your target script unchanged and set the environment variable for the script's session only.
A more complex wrapper script could easily be created which would allow you to specify variables on the command line, and even dynamically specify the script which should be executed when these variables are set.
Option 4 can be implemented using the $argv variable:
<?php
$applicationEnv = $argv[1];
// rest of you script
...and call the script like:
php script.php staging
However, it occurs to me that you seem to be indicating to the script which environment is running in (staging, dev, live, etc) - in which case it might be simplest to set a server-wide variable, and rename it as necessary to prevent collision with variables that other applications may be setting. That way you can simply invoke the script and not need to worry about this. This is assuming that you staging environment runs on a different machine to the live (which it should be).
When you execute a PHP script from the command line, it inherits the environment variables defined in your shell. That means you can set an environment variable using the export command like so:
export APPLICATION_ENV='staging'
Here is an example for setting one envirnnomental variable :
ENV_VAR='var' php script.php
Just in case you want to set multiple variables
Try this :
ENV_VAR1=1 ENV_VAR2=2 ENV_VAR3=3 php script.php
You can set a variable in /etc/environment like FOO="bar" which is then accessible with getenv() in both CLI and web requests. You may need to relog for this change to take effect.
Try using putenv and pass the variables through parameters
php script.php APPLICATION_ENV=staging
And in the script.php code:
for($i=1;$i<count($argv);$i++){
putenv($argv[$i]);
}
I have the same situation and i use next code (it works for me):
export APPLICATION_ENV=staging && php script.php
Hope it will helpful for you too.

Apache Server Variable ENV is different on the command line when using Drush

This problem is occuring with Drupal but can be attributed to a general Apache - PHP configuration. In my configuration file I am determing which database to use by a server variable.
if($_SERVER['ENV'] == 'development') {
//Use connection A
} else {
//Use connection B
}
In apache I am setting the env like so:
<VirtualHost *:80>
//Other normal Apache variables set
SetEnv ENV development
</VirtualHost>
In my normal website setting, this works perfectly fine with PHP is accessed through the webserver. But I am trying to run a command using drush, which is on the command line, and it always connects to the wrong database. The problem really is command line php always connects to connection B and I don't think its necessarily drush.
How do I set the ENV to work both on apache on and the command line? The OS is ubuntu.
Drush doesn't use Apache to run commands, rather it loads and runs the PHP files directly. To set the variable globally on the server you can edit /etc/environment and add
ENV=development
You can read more about setting environment variables in Ubuntu, check out this help article.
The --uri switch will inform Drush of what domain to use in the case that HTTP_HOST is unset at runtime.
example: drush --uri=http://uri-to-development cc all
You can add it to the drushrc on each machine so that you don't have to type it every time.
A generic answer,
Apache has its own environment vs the command-line ENV ( run exportto see the difference ).
Apache also include headers/ENV vars that are meaningful only to a web server ( like HTTP_REFERER & DOCUMENT_ROOT ) which are meaningless via command-line ( you don't really have a DOCUMENT_ROOT when using the shell on a regular basis )
The problem in my opinion, starts when you're adding some SetEnv variables in your Apache configuration that you need to also include when you're testing the file via command-line ( such as custom PATH(s) etc .. or a LD_LIBRARY_PATH for Oracle i.e ... )
The bigger problem is that sometimes you can SetEnv variables in your some-local-websiteX.conf and some SetEnv were defined under the global httpd.conf
So I usually do this
1. Print the ENV variables that Apache is using
in my document root I add this small script ( it's in Python but you can adjust it to do the same in PHP )
File: www/env.cgi
#!/usr/bin/python
print "Content-Type: text/html; charset=utf-8\n"
import os
for e in os.environ:
print "export {key}='{value}' <br />".format(key=e, value=os.environ[e])
2. Creating a custom bash env file
I then copy paste the output in the browser to a local file ~/.bash_apache_env
3. Running my website via commandline
Every terminal window will reset the environment to the defaults ( unless you have some settings in your ~/.bashr i.e ofc ), so I'm running the following in each window I need.
source ~/.bash_apache_env
# then I can execute
www/index.cgi
www/admin.cgi
# etc ..
The #1 & #2 are usually just a one-time thing
Hope it helps...

Categories