librenms/LibreNMS/Util/EnvHelper.php
Jellyfrog b0bd0765ad
Prevent ansi colors in key:generate output (#15773)
* Prevent ansi colors in key:generate output

* Apply fixes from StyleCI
2024-01-23 12:35:39 +01:00

223 lines
6.7 KiB
PHP

<?php
/**
* EnvHelper.php
*
* Helper for manipulation of the .env file
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* @link https://www.librenms.org
*
* @copyright 2020 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace LibreNMS\Util;
use ErrorException;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Support\Facades\Artisan;
use LibreNMS\Exceptions\FileWriteFailedException;
class EnvHelper
{
/**
* Set a setting in .env file.
* Will only set non-empty unset variables
*
* @param array $settings KEY => value list of settings
* @param array $unset Remove the given KEYS from the config
* @param string $file
* @return string
*
* @throws \LibreNMS\Exceptions\FileWriteFailedException
*/
public static function writeEnv($settings, $unset = [], $file = '.env')
{
try {
$original_content = file_get_contents($file);
$new_content = self::setEnv($original_content, $settings, $unset);
// only write if the content has changed
if ($new_content !== $original_content) {
if (! file_put_contents($file, $new_content)) {
throw new FileWriteFailedException($file);
}
}
return $new_content;
} catch (ErrorException $e) {
throw new FileWriteFailedException($file, 0, $e);
}
}
/**
* Set a setting in .env file content.
* Will only set non-empty unset variables
*
* @param string $content
* @param array $settings KEY => value list of settings
* @param array $unset Remove the given KEYS from the config
* @return string
*/
public static function setEnv($content, $settings, $unset = [])
{
// ensure trailing line return
if (substr($content, -1) !== PHP_EOL) {
$content .= PHP_EOL;
}
// unset the given keys
if (! empty($unset)) {
$regex = '/^(' . implode('|', $unset) . ')=.*$\n/m';
$content = preg_replace($regex, '', $content);
}
foreach ($settings as $key => $value) {
// only add non-empty settings
if (empty($value)) {
continue;
}
$value = self::escapeValue($value);
if (preg_match("/^(#$key=$|$key=)/m", $content)) {
// enter this block if we have commented and empty or uncommented key
// only replace ones that aren't already set to a value for safety and uncomment
// escape $ in the replacement
$content = preg_replace("/#?$key=\n/", addcslashes("$key=$value\n", '$'), $content);
} else {
$content .= "$key=$value\n";
}
}
return self::fixComments($content);
}
/**
* Copy the example .env file and set APP_KEY
*
* @return bool|string
*
* @throws \LibreNMS\Exceptions\FileWriteFailedException
*/
public static function init()
{
$env_file = base_path('.env');
try {
if (! file_exists($env_file)) {
copy(base_path('.env.example'), $env_file);
$key = null;
if (php_sapi_name() == 'cli') {
$key = trim(exec(PHP_BINARY . ' ' . base_path('artisan') . ' key:generate --show --no-ansi'));
} else {
if (Artisan::call('key:generate', [
'--show' => 'true',
'--no-ansi' => 'true',
]) == 0) {
$key = trim(Artisan::output());
}
}
self::writeEnv([
'APP_KEY' => $key,
'INSTALL' => ! file_exists(base_path('config.php')) ? 'true' : false, // if both .env and config.php are missing, assume install is needed
], [], $env_file);
try {
config(['app.key' => $key]);
} catch (BindingResolutionException $e) {
// called outside of Laravel, ignore config() failure
}
return $key;
}
return false;
} catch (ErrorException $e) {
throw new FileWriteFailedException($env_file, 0, $e);
}
}
/**
* Fix .env with # in them without a space before it
*
* @param string $dotenv
* @return string
*/
private static function fixComments($dotenv)
{
return implode(PHP_EOL, array_map(function ($line) {
$parts = explode('=', $line, 2);
if (isset($parts[1])
&& preg_match('/(?<!\s)#/', $parts[1]) // number symbol without a space before it
&& ! preg_match('/^(".*"|\'.*\')$/', trim($parts[1])) // not already quoted
) {
return trim($parts[0]) . '="' . trim($parts[1]) . '"';
}
return $line;
}, explode(PHP_EOL, $dotenv)));
}
/**
* quote strings with spaces
*
* @param string $value
* @return string
*/
private static function escapeValue($value)
{
if (strpos($value, ' ') !== false) {
return "\"$value\"";
}
return $value;
}
/**
* Parse comma separated environment variable into an array.
*
* @param string $env_name
* @param mixed $default
* @param array $except Ignore these values and return the unexploded string
* @return array|mixed
*/
public static function parseArray($env_name, $default = null, $except = [''])
{
$value = getenv($env_name);
if ($value === false) {
$value = $default;
}
if (is_string($value) && ! in_array($value, $except)) {
$value = explode(',', $value);
}
return $value;
}
/**
* Detect if LibreNMS is installed from the official Docker image.
*
* @return bool
*/
public static function librenmsDocker()
{
return getenv('LIBRENMS_DOCKER') === '1';
}
}