sebastian/global-state
sebastian/global-state snapshots and restores PHP global state (globals, superglobals, ini settings, etc.), extracted from PHPUnit as a standalone component. Useful for test isolation and detecting side effects by capturing state before and after code runs.
Install as a dev dependency:
composer require --dev sebastian/global-state
First use case: Isolate tests modifying global state (e.g., $_ENV, $_SERVER, or static properties). Initialize in your test's setUp():
use SebastianBergmann\GlobalState\Snapshot;
class MyTest extends TestCase
{
private Snapshot $snapshot;
protected function setUp(): void
{
$this->snapshot = Snapshot::snapshot(); // Capture current state
}
protected function tearDown(): void
{
$this->snapshot->restore(); // Reset to captured state
}
}
Where to look first:
Snapshot::snapshot() – Core method for capturing state.$_SERVER/$_ENV resets.// Before test: Capture state
$snapshot = Snapshot::snapshot();
// During test: Modify globals/statics
$_ENV['TEST_VAR'] = 'modified';
MyClass::$staticVar = 'new_value';
// After test: Restore
$snapshot->restore(); // $_ENV and static revert to original
// Only snapshot $_GET and $_POST (skip $_SESSION for speed)
$snapshot = Snapshot::snapshot([
'superGlobals' => ['$_GET', '$_POST'],
]);
// Snapshot statics for App\Services\Cache
$snapshot = Snapshot::snapshot([
'staticProperties' => ['App\Services\Cache::class'],
]);
Extend Laravel’s TestCase and override setUp():
use Illuminate\Foundation\Testing\TestCase as LaravelTestCase;
use SebastianBergmann\GlobalState\Snapshot;
class MyTest extends LaravelTestCase
{
private Snapshot $snapshot;
protected function setUp(): void
{
parent::setUp();
$this->snapshot = Snapshot::snapshot(['superGlobals' => ['$_SERVER']]);
}
protected function tearDown(): void
{
$this->snapshot->restore();
parent::tearDown();
}
}
$_ENV between command runs.$GLOBALS or register_shutdown_function().PHP Version Lock-in:
sebastian/global-state’s release notes.8.x in composer.json if using PHP 8.2:
"require-dev": {
"sebastian/global-state": "^8.0"
}
Super-Global Exclusions:
$_SESSION and $_COOKIE are not snapshotted by default (risk of race conditions in concurrent tests).Snapshot::snapshot(['superGlobals' => ['$_SESSION']]); // Use cautiously!
Static Property Limitations:
$obj->static = 'value') are ignored.ReflectionClass to verify statics before snapshotting.Performance Spikes:
$GLOBALS or large arrays (e.g., $_SERVER with 100+ keys) adds overhead.Snapshot::snapshot(['globals' => ['$_ENV', '$_SERVER']]);
PHPUnit Redundancy:
$_SERVER['REQUEST_METHOD']).Http::fake() in Laravel) over global snapshotting.Verify Restored State:
$snapshot = Snapshot::snapshot();
$_ENV['TEST'] = 'value';
$snapshot->restore();
$this->assertNull($_ENV['TEST'] ?? null, 'State not restored!');
Inspect Captured State:
$snapshot = Snapshot::snapshot();
$state = $snapshot->getState(); // Debug array of captured values
Handle Early Test Failures:
try {
$snapshot = Snapshot::snapshot();
// Test logic...
} finally {
$snapshot?->restore(); // Ensures cleanup even if test fails early
}
Custom State Providers:
Override Snapshot::snapshot() to add support for non-standard globals (e.g., custom App::bind() entries):
$snapshot = Snapshot::snapshot(['custom' => ['App\StateManager::getAll()']]);
Post-Restore Callbacks:
Extend Snapshot to run logic after restoration (e.g., log state changes):
$snapshot = new Snapshot(...);
$snapshot->restore();
$snapshot->afterRestore(function() {
Log::info('State restored at ' . now());
});
Laravel Service Provider: Register a global test helper:
// app/Providers/TestServiceProvider.php
public function boot()
{
if ($this->app->environment('testing')) {
$this->app->singleton('snapshot', function() {
return Snapshot::snapshot(['superGlobals' => ['$_SERVER']]);
});
}
}
Usage in tests:
$this->app['snapshot']->restore();
Artisan Commands:
Global state persists across Artisan commands. Use snapshots in handle():
public function handle()
{
$snapshot = Snapshot::snapshot();
// Command logic...
$snapshot->restore();
}
Queue Workers: Snapshots do not persist between queue jobs. Reset state per job:
public function handle()
{
$snapshot = Snapshot::snapshot(['globals' => ['$_ENV']]);
// Process job...
$snapshot->restore();
}
Testing Middleware:
Avoid snapshotting $_SERVER if middleware relies on it (e.g., Accept header). Instead, mock the Request object:
$this->withHeaders(['Accept' => 'application/json']);
How can I help you explore Laravel packages today?