Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions src/Illuminate/Foundation/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ public function hasBeenBootstrapped()
* ponytail: v13 bootstraps through its kernels (environment, configuration, exception
* handling, facades, providers). The fork runs start.php in their place, the first time
* handleRequest(), handleCommand() or the console Kernel's bootstrap() asks for it.
* The environment defaults to production, as in v13. Remove at the flip.
* Remove at the flip.
*
* @return void
*/
Expand All @@ -438,12 +438,8 @@ public function bootstrapWithStartScript()

$this->hasBeenBootstrapped = true;

if ( ! $this->bound('env')) $this->detectEnvironment(array());

$app = $this;

$env = $this['env'];

require static::getBootstrapFile();
}

Expand Down Expand Up @@ -488,14 +484,16 @@ public function isLocal()
/**
* Detect the application's current environment.
*
* @param array|string $envs
* @param \Closure $callback
* @return string
*/
public function detectEnvironment($envs)
public function detectEnvironment(Closure $callback)
{
$args = isset($_SERVER['argv']) ? $_SERVER['argv'] : null;
$args = $this->runningInConsole() && isset($_SERVER['argv'])
? $_SERVER['argv']
: null;

return $this['env'] = (new EnvironmentDetector())->detect($envs, $args);
return $this['env'] = (new EnvironmentDetector)->detect($callback, $args);
}

/**
Expand Down
78 changes: 26 additions & 52 deletions src/Illuminate/Foundation/EnvironmentDetector.php
Original file line number Diff line number Diff line change
@@ -1,75 +1,52 @@
<?php namespace Illuminate\Foundation;

use Illuminate\Support\Arr;
use Closure;

class EnvironmentDetector {

/**
* Detect the application's current environment.
*
* @param array|string $environments
* @param \Closure $callback
* @param array|null $consoleArgs
* @return string
*/
public function detect($environments, $consoleArgs = null): string
{
public function detect(Closure $callback, $consoleArgs = null)
{
if ($consoleArgs)
{
return $this->detectConsoleEnvironment($environments, $consoleArgs);
return $this->detectConsoleEnvironment($callback, $consoleArgs);
}

return $this->detectWebEnvironment($environments);
return $this->detectWebEnvironment($callback);
}

/**
* Set the application environment for a web request.
*
* @param array|string $environments
* @param \Closure $callback
* @return string
*/
protected function detectWebEnvironment($environments): string
{
// If the given environment is just a Closure, we will defer the environment check
// to the Closure the developer has provided, which allows them to totally swap
// the webs environment detection logic with their own custom Closure's code.
if ($environments instanceof Closure)
{
return call_user_func($environments);
}

foreach ($environments as $environment => $hosts)
{
// To determine the current environment, we'll simply iterate through the possible
// environments and look for the host that matches the host for this request we
// are currently processing here, then return back these environment's names.
foreach ((array) $hosts as $host)
{
if ($this->isMachine($host)) return $environment;
}
}

return 'production';
protected function detectWebEnvironment(Closure $callback)
{
return $callback();
}

/**
* Set the application environment from command-line arguments.
*
* @param mixed $environments
* @param \Closure $callback
* @param array $args
* @return string
*/
protected function detectConsoleEnvironment($environments, array $args): string
{
// First we will check if an environment argument was passed via console arguments
// and if it was that automatically overrides as the environment. Otherwise, we
// will check the environment as a "web" request like a typical HTTP request.
protected function detectConsoleEnvironment(Closure $callback, array $args)
{
if ( ! is_null($value = $this->getEnvironmentArgument($args)))
{
return head(array_slice(explode('=', $value), 1));
return $value;
}

return $this->detectWebEnvironment($environments);
return $this->detectWebEnvironment($callback);
}

/**
Expand All @@ -78,23 +55,20 @@ protected function detectConsoleEnvironment($environments, array $args): string
* @param array $args
* @return string|null
*/
protected function getEnvironmentArgument(array $args): ?string
{
return Arr::first($args, function($value, $key)
protected function getEnvironmentArgument(array $args)
{
foreach ($args as $i => $value)
{
return starts_with($value, '--env');
});
}
if ($value === '--env')
{
return $args[$i + 1] ?? null;
}

/**
* Determine if the name matches the machine name.
*
* @param string $name
* @return bool
*/
public function isMachine($name): bool
{
return str_is($name, gethostname());
if (str_starts_with($value, '--env='))
{
return head(array_slice(explode('=', $value), 1));
}
}
}

}
12 changes: 9 additions & 3 deletions src/Illuminate/Foundation/start.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\Env;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Config\EnvironmentVariables;
use Illuminate\Foundation\Bootstrap\LoadConfiguration;
Expand Down Expand Up @@ -95,14 +96,19 @@
| Register The Environment Variables
|--------------------------------------------------------------------------
|
| v13's LoadEnvironmentVariables loads the .env file first. The L4.2
| .env.{env}.php file loads after it, so its values win on the keys both
| files define.
| v13's LoadEnvironmentVariables loads the .env file first. Unless the
| application has set its environment, APP_ENV then names it, defaulting to
| production as v13's config does. The L4.2 .env.{env}.php file loads after
| that, so its values win on the keys both files define.
|
*/

$app->make(LoadEnvironmentVariables::class)->bootstrap($app);

if ( ! $app->bound('env')) $app->detectEnvironment(fn() => Env::get('APP_ENV', 'production'));

$env = $app['env'];

with($envVariables = new EnvironmentVariables(
$app->getEnvironmentVariablesLoader()))->load($env);

Expand Down
31 changes: 30 additions & 1 deletion tests/Foundation/FoundationApplicationBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ protected function tearDown(): void
error_reporting($this->errorReporting);
ini_set('display_errors', $this->displayErrors);
date_default_timezone_set($this->timezone);
foreach (array('FORK_START_SHARED', 'FORK_START_DOTENV') as $key)
foreach (array('FORK_START_SHARED', 'FORK_START_DOTENV', 'APP_ENV') as $key)
{
putenv($key);
unset($_ENV[$key], $_SERVER[$key]);
Expand Down Expand Up @@ -227,6 +227,35 @@ public function theEnvironmentDefaultsToProduction()
$this->assertSame('production', $app['env']);
}

#[Test]
public function appEnvFromTheEnvFileNamesTheEnvironmentAndItsPhpFile()
{
file_put_contents($this->base.'/.env', "APP_ENV=staging\n");
file_put_contents($this->base.'/.env.staging.php', '<?php return array("FORK_START_SHARED" => "staging");');
$app = Application::configure($this->base)->create();

$app->bootstrapWithStartScript();
$this->bootstraps++;

$this->assertSame('staging', $app['env']);
$this->assertSame('staging', getenv('FORK_START_SHARED'));
}

#[Test]
public function anEnvironmentTheApplicationDetectedWinsOverAppEnv()
{
file_put_contents($this->base.'/.env', "APP_ENV=staging\n");
file_put_contents($this->base.'/.env.local.php', '<?php return array("FORK_START_SHARED" => "local");');
$app = Application::configure($this->base)->create();
$app->detectEnvironment(fn () => 'local');

$app->bootstrapWithStartScript();
$this->bootstraps++;

$this->assertSame('local', $app['env']);
$this->assertSame('local', getenv('FORK_START_SHARED'));
}

#[Test]
public function handleRequestBootstrapsSendsTheResponseAndTerminates()
{
Expand Down
28 changes: 28 additions & 0 deletions tests/Foundation/FoundationApplicationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,34 @@ public function testEnvironment()
$this->assertFalse($app->environment('qux', 'bar'));
$this->assertFalse($app->environment(['qux', 'bar']));
}


public function testDetectEnvironmentTakesTheConsolesEnvOptionOverTheCallback()
{
$argv = $_SERVER['argv'];
$_SERVER['argv'] = array('artisan', 'migrate', '--env', 'staging');

try
{
$app = new Application;

$this->assertSame('staging', $app->detectEnvironment(fn () => 'local'));
$this->assertSame('staging', $app['env']);
}
finally
{
$_SERVER['argv'] = $argv;
}
}


public function testDetectEnvironmentAsksTheCallbackWithoutAnEnvOption()
{
$app = new Application;

$this->assertSame('local', $app->detectEnvironment(fn () => 'local'));
$this->assertSame('local', $app['env']);
}
}

class ApplicationCustomExceptionHandlerStub extends Illuminate\Foundation\Application {
Expand Down
66 changes: 23 additions & 43 deletions tests/Foundation/FoundationEnvironmentDetectorTest.php
Original file line number Diff line number Diff line change
@@ -1,56 +1,36 @@
<?php

use Illuminate\Foundation\EnvironmentDetector;
use L4\Tests\BackwardCompatibleTestCase;
use Mockery as m;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

class FoundationEnvironmentDetectorTest extends BackwardCompatibleTestCase
class FoundationEnvironmentDetectorTest extends TestCase
{

protected function tearDown(): void
{
m::close();
}


public function testEnvironmentDetection(): void
{
$env = m::mock(EnvironmentDetector::class)->makePartial();
$env->shouldReceive('isMachine')->once()->with('localhost')->andReturn(false);
$result = $env->detect(
[
'local' => ['localhost']
]
);
$this->assertEquals('production', $result);


$env = m::mock(EnvironmentDetector::class)->makePartial();
$env->shouldReceive('isMachine')->once()->with('localhost')->andReturn(true);
$result = $env->detect([
'local' => ['localhost']
]);
$this->assertEquals('local', $result);
#[Test]
public function theCallbackNamesTheEnvironment()
{
$this->assertSame('foobar', (new EnvironmentDetector)->detect(fn () => 'foobar'));
}


public function testClosureCanBeUsedForCustomEnvironmentDetection(): void
{
$env = new Illuminate\Foundation\EnvironmentDetector;

$result = $env->detect(function() { return 'foobar'; });
$this->assertEquals('foobar', $result);
#[Test]
public function anEnvOptionWithAnEqualsSignOverridesTheCallback()
{
$this->assertSame('local', (new EnvironmentDetector)->detect(fn () => 'foobar', array('artisan', '--env=local')));
}

#[Test]
public function anEnvOptionFollowedByItsValueOverridesTheCallback()
{
$this->assertSame('local', (new EnvironmentDetector)->detect(fn () => 'foobar', array('artisan', 'migrate', '--env', 'local')));
}

public function testConsoleEnvironmentDetection(): void
{
$env = new Illuminate\Foundation\EnvironmentDetector;
#[Test]
public function consoleArgumentsWithoutAnEnvOptionLeaveItToTheCallback()
{
$detector = new EnvironmentDetector;

$result = $env->detect([
'local' => ['foobar']
], ['--env=local']);
$this->assertEquals('local', $result);
$this->assertSame('foobar', $detector->detect(fn () => 'foobar', array('artisan', 'migrate', '--force')));
$this->assertSame('foobar', $detector->detect(fn () => 'foobar', array('artisan', '--env')));
$this->assertSame('foobar', $detector->detect(fn () => 'foobar', array('artisan', '--environment=local')));
}

}
Loading