`closeSession()` rebuilds a throwaway Application every test (redundant boot + memory leak)
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 68/100
Research direction
Start in Lib/Connector/Yii2.php at closeSession() and resetApplication(), then trace the configureClient() path in Module/Yii2.php. Verify that resetting with no Yii::$app does not construct a throwaway application, while an existing web application can still close its session. Done means the redundant boot and associated per-test memory growth are eliminated.
Written by the indexing model from the issue text.
Description
Summary
In the per-test lifecycle, Connector\Yii2::resetApplication() calls closeSession(), which calls getApplication(). When Yii::$app is already null (the normal state at that point), getApplication() builds a complete throwaway application just to close a session that doesn't exist, then immediately discards it.
The result is two full application boots per test instead of one, and the throwaway app leaves behind process-global state (most visibly the ErrorHandler's register_shutdown_function + its 256 KB memory reserve) that is never released, so memory grows linearly with the number of tests and large suites OOM.
Environment
codeception/module-yii2: 2.0.5codeception/codeception: 5.3.5phpunit/phpunit: 13.x- PHP: 8.x
Root cause
Lib/Connector/Yii2.php (line numbers from 2.0.5):
// getApplication() — rebuilds when no app exists
protected function getApplication(): \yii\base\Application // ~L141
{
if (! isset(Yii::$app)) {
$this->startApp(); // <-- full application boot
}
return Yii::$app ?? throw new RuntimeException('Failed to create Yii2 application');
}
public function closeSession(): void // ~L542
{
$app = $this->getApplication(); // <-- triggers rebuild when null
if ($app instanceof \yii\web\Application && $app->has('session', true)) {
$app->session->close();
}
}
// resetApplication() — calls closeSession() before nulling the app
public function resetApplication(bool $closeSession = true): void // ~L158
{
if ($closeSession) {
$this->closeSession(); // <-- rebuild happens here
}
Yii::$app = null; // ~L166
...
}
The triggering call path each test (Module/Yii2.php):
- _after() → getClient()->resetApplication() (L448) → Yii::$app = null.
- next test _before() → recreateClient() → configureClient() → $client->resetApplication() (L378).
At this point Yii::$app is null, so closeSession() → getApplication() → startApp()
builds a throwaway app. startApp() proper then builds the real app a moment later.
Note configureClient() calls resetApplication() with no argument, so the
closeSessionOnRecreateApplication config (only consulted in beforeRequest(), behind
if ($this->recreateApplication)) does not affect this path.
Reproduction / evidence
Counting Application constructions in a real unit suite (subclass that increments a counter
in __construct): 20 constructions for 10 tests = 2× per test. Backtraces:
build #1: Yii::createObject ← startApp ← Module\Yii2::_before (real app)
build #2: Yii::createObject ← startApp ← getApplication ← closeSession ← resetApplication (throwaway)
A standalone loop replicating the module's per-test cycle leaks ~0.33 MB/iteration;
calling resetApplication(false) (skipping the closeSession rebuild) drops it to ~0.04 MB/iteration.
Impact
- A full, redundant application boot on every test (CPU/time).
- Linear memory growth across a suite (orphaned shutdown-function + 256 KB reserve per throwaway
app), causing OOM on large suites.
Proposed fix
closeSession() should not create an application — there's nothing to close if none exists:
Yii::$app = null; // ~L166
...
}
The triggering call path each test (Module/Yii2.php):
- _after() → getClient()->resetApplication() (L448) → Yii::$app = null.
- next test _before() → recreateClient() → configureClient() → $client->resetApplication() (L378).
At this point Yii::$app is null, so closeSession() → getApplication() → startApp()
builds a throwaway app. startApp() proper then builds the real app a moment later.
Note configureClient() calls resetApplication() with no argument, so the
closeSessionOnRecreateApplication config (only consulted in beforeRequest(), behind
if ($this->recreateApplication)) does not affect this path.
Reproduction / evidence
Counting Application constructions in a real unit suite (subclass that increments a counter
in __construct): 20 constructions for 10 tests = 2× per test. Backtraces:
build #1: Yii::createObject ← startApp ← Module\Yii2::_before (real app)
build #2: Yii::createObject ← startApp ← getApplication ← closeSession ← resetApplication (throwaway)
A standalone loop replicating the module's per-test cycle leaks ~0.33 MB/iteration;
calling resetApplication(false) (skipping the closeSession rebuild) drops it to ~0.04 MB/iteration.
Impact
- A full, redundant application boot on every test (CPU/time).
- Linear memory growth across a suite (orphaned shutdown-function + 256 KB reserve per throwaway
app), causing OOM on large suites.
Proposed fix
closeSession() should not create an application — there's nothing to close if none exists:
public function closeSession(): void
{
$app = Yii::$app; // was: $this->getApplication();
if ($app instanceof \yii\web\Application && $app->has('session', true)) {
$app->session->close();
}
}
(Equivalently, configureClient() could call resetApplication(false), since that pre-startApp reset has no live session to close.)
Happy to open a PR.
- Dominant language
- PHP
- Stars
- 19
- Forks
- 43
- PR merge metrics
- No merged PRs in 30d
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Codeception/module-yii2
-
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
Codeception/module-yii2#145 ·
-
MongoDB support Open
Difficulty 5/5 Over a week Newbie friendliness 25/100
Codeception/module-yii2#142 · 4 comments ·
-
Difficulty 3/5 1-2 days Newbie friendliness 38/100
Codeception/module-yii2#141 · 1 comment ·
-
Codeception/module-yii2#134 · 4 comments · 1 assignee ·
-
bug
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Codeception/module-yii2#95 · 1 reaction ·
All issues in Codeception/module-yii2
Similar issues
-
priority: p3
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
googleapis/librarian#7636 ·
-
0. Needs triage bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
nextcloud/fulltextsearch#1011 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
phpstan/phpstan-doctrine#794 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
Automattic/static-site-importer#1767 ·