File: //var/www/payments-gateway/vendor/symfony/flex/src/Configurator/EnvConfigurator.php
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Flex\Configurator;
use Composer\Composer;
use Composer\IO\IOInterface;
use Symfony\Flex\Lock;
use Symfony\Flex\Options;
use Symfony\Flex\Recipe;
use Symfony\Flex\Update\RecipeUpdate;
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
class EnvConfigurator extends AbstractConfigurator
{
private string $suffix;
public function __construct(Composer $composer, IOInterface $io, Options $options, string $suffix = '')
{
parent::__construct($composer, $io, $options);
$this->suffix = $suffix;
}
public function configure(Recipe $recipe, $vars, Lock $lock, array $options = [])
{
$this->write('Adding environment variable defaults'.('' === $this->suffix ? '' : ' ('.$this->suffix.')'));
$this->configureEnvDist($recipe, $vars, $options['force'] ?? false);
if ('' !== $this->suffix) {
return;
}
if (!file_exists($this->options->get('root-dir').'/'.($this->options->get('runtime')['dotenv_path'] ?? '.env').'.test')) {
$this->configurePhpUnit($recipe, $vars, $options['force'] ?? false);
}
}
public function unconfigure(Recipe $recipe, $vars, Lock $lock)
{
$this->unconfigureEnvFiles($recipe, $vars);
$this->unconfigurePhpUnit($recipe, $vars);
}
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
{
$recipeUpdate->addOriginalFiles(
$this->getContentsAfterApplyingRecipe($recipeUpdate->getRootDir(), $recipeUpdate->getOriginalRecipe(), $originalConfig)
);
$recipeUpdate->addNewFiles(
$this->getContentsAfterApplyingRecipe($recipeUpdate->getRootDir(), $recipeUpdate->getNewRecipe(), $newConfig)
);
}
private function configureEnvDist(Recipe $recipe, $vars, bool $update)
{
$dotenvPath = $this->options->get('runtime')['dotenv_path'] ?? '.env';
$files = '' === $this->suffix ? [$dotenvPath.'.dist', $dotenvPath] : [$dotenvPath.'.'.$this->suffix];
foreach ($files as $file) {
$env = $this->options->get('root-dir').'/'.$file;
if (!is_file($env)) {
continue;
}
if (!$update && $this->isFileMarked($recipe, $env)) {
continue;
}
$data = '';
foreach ($vars as $key => $value) {
$existingValue = $update ? $this->findExistingValue($key, $env, $recipe) : null;
$value = $this->evaluateValue($value, $existingValue);
if ('#' === $key[0] && is_numeric(substr($key, 1))) {
if ('' === $value) {
$data .= "#\n";
} else {
$data .= '# '.$value."\n";
}
continue;
}
$value = $this->options->expandTargetDir($value);
if (false !== strpbrk($value, " \t\n&!\"")) {
$value = '"'.str_replace(['\\', '"', "\t", "\n"], ['\\\\', '\\"', '\t', '\n'], $value).'"';
}
$data .= "$key=$value\n";
}
$data = $this->markData($recipe, $data);
if (!$this->updateData($env, $data)) {
file_put_contents($env, $data, \FILE_APPEND);
}
}
}
private function configurePhpUnit(Recipe $recipe, $vars, bool $update)
{
foreach (['phpunit.xml.dist', 'phpunit.dist.xml', 'phpunit.xml'] as $file) {
$phpunit = $this->options->get('root-dir').'/'.$file;
if (!is_file($phpunit)) {
continue;
}
if (!$update && $this->isFileXmlMarked($recipe, $phpunit)) {
continue;
}
$data = '';
foreach ($vars as $key => $value) {
$value = $this->evaluateValue($value);
if ('#' === $key[0]) {
if (is_numeric(substr($key, 1))) {
$doc = new \DOMDocument();
$data .= ' '.$doc->saveXML($doc->createComment(' '.$value.' '))."\n";
} else {
$value = $this->options->expandTargetDir($value);
$doc = new \DOMDocument();
$fragment = $doc->createElement('env');
$fragment->setAttribute('name', substr($key, 1));
$fragment->setAttribute('value', $value);
$data .= ' '.str_replace(['<', '/>'], ['<!-- ', ' -->'], $doc->saveXML($fragment))."\n";
}
} else {
$value = $this->options->expandTargetDir($value);
$doc = new \DOMDocument();
$fragment = $doc->createElement('env');
$fragment->setAttribute('name', $key);
$fragment->setAttribute('value', $value);
$data .= ' '.$doc->saveXML($fragment)."\n";
}
}
$data = $this->markXmlData($recipe, $data);
if (!$this->updateData($phpunit, $data)) {
file_put_contents($phpunit, preg_replace('{^(\s+</php>)}m', $data.'$1', file_get_contents($phpunit)));
}
}
}
private function unconfigureEnvFiles(Recipe $recipe, $vars)
{
$dotenvPath = $this->options->get('runtime')['dotenv_path'] ?? '.env';
$files = '' === $this->suffix ? [$dotenvPath, $dotenvPath.'.dist'] : [$dotenvPath.'.'.$this->suffix];
foreach ($files as $file) {
$env = $this->options->get('root-dir').'/'.$file;
if (!file_exists($env)) {
continue;
}
$contents = preg_replace(\sprintf('{%s*###> %s ###.*###< %s ###%s+}s', "\n", $recipe->getName(), $recipe->getName(), "\n"), "\n", file_get_contents($env), -1, $count);
if (!$count) {
continue;
}
$this->write(\sprintf('Removing environment variables from %s', $file));
file_put_contents($env, $contents);
}
}
private function unconfigurePhpUnit(Recipe $recipe, $vars)
{
foreach (['phpunit.dist.xml', 'phpunit.xml.dist', 'phpunit.xml'] as $file) {
$phpunit = $this->options->get('root-dir').'/'.$file;
if (!is_file($phpunit)) {
continue;
}
$contents = preg_replace(\sprintf('{%s*\s+<!-- ###\+ %s ### -->.*<!-- ###- %s ### -->%s+}s', "\n", $recipe->getName(), $recipe->getName(), "\n"), "\n", file_get_contents($phpunit), -1, $count);
if (!$count) {
continue;
}
$this->write(\sprintf('Removing environment variables from %s', $file));
file_put_contents($phpunit, $contents);
}
}
/**
* Evaluates expressions like %generate(secret)%.
*
* If $originalValue is passed, and the value contains an expression.
* the $originalValue is used.
*/
private function evaluateValue($value, ?string $originalValue = null)
{
if ('%generate(secret)%' === $value) {
if (null !== $originalValue) {
return $originalValue;
}
return $this->generateRandomBytes();
}
if (preg_match('~^%generate\(secret,\s*([0-9]+)\)%$~', $value, $matches)) {
if (null !== $originalValue) {
return $originalValue;
}
return $this->generateRandomBytes($matches[1]);
}
return $value;
}
private function generateRandomBytes($length = 16)
{
return bin2hex(random_bytes($length));
}
private function getContentsAfterApplyingRecipe(string $rootDir, Recipe $recipe, array $vars): array
{
$dotenvPath = $this->options->get('runtime')['dotenv_path'] ?? '.env';
$files = '' === $this->suffix ? [$dotenvPath, $dotenvPath.'.dist', 'phpunit.dist.xml', 'phpunit.xml.dist', 'phpunit.xml'] : [$dotenvPath.'.'.$this->suffix];
if (0 === \count($vars)) {
return array_fill_keys($files, null);
}
$originalContents = [];
foreach ($files as $file) {
$originalContents[$file] = file_exists($rootDir.'/'.$file) ? file_get_contents($rootDir.'/'.$file) : null;
}
$this->configureEnvDist(
$recipe,
$vars,
true
);
if ('' === $this->suffix && !file_exists($rootDir.'/'.$dotenvPath.'.test')) {
$this->configurePhpUnit(
$recipe,
$vars,
true
);
}
$updatedContents = [];
foreach ($files as $file) {
$updatedContents[$file] = file_exists($rootDir.'/'.$file) ? file_get_contents($rootDir.'/'.$file) : null;
}
foreach ($originalContents as $file => $contents) {
if (null === $contents) {
if (file_exists($rootDir.'/'.$file)) {
unlink($rootDir.'/'.$file);
}
} else {
file_put_contents($rootDir.'/'.$file, $contents);
}
}
return $updatedContents;
}
/**
* Attempts to find the existing value of an environment variable.
*/
private function findExistingValue(string $var, string $filename, Recipe $recipe): ?string
{
if (!file_exists($filename)) {
return null;
}
$contents = file_get_contents($filename);
$section = $this->extractSection($recipe, $contents);
if (!$section) {
return null;
}
$lines = explode("\n", $section);
foreach ($lines as $line) {
if (!str_starts_with($line, \sprintf('%s=', $var))) {
continue;
}
return trim(substr($line, \strlen($var) + 1));
}
return null;
}
}