perf: config classes check every property against the environment on each construction
Maintainer antworten meist innerhalb von 1 Tag
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Anfängerfreundlichkeit
- 25/100
- Issue-Typ
- Refactoring
- Klarheit
- Größtenteils klar
- Aktivitätsstatus
- Aktiv
- Tech-Stack
- php
- Bereich
- backend, performance
Rechercherichtung
Start in BaseConfig::__construct() and getEnvValue(), which do the per-property and per-leaf environment lookups, and read how DotEnv loads $_ENV and $_SERVER. The proposal adds a one-time environment index with snapshot checks and a BaseConfig::reset() hook, so the four matching rules must stay identical. Done means the benchmark in the issue is reproduced and the existing config tests still pass, with the 4.8 behavior changes documented in upgrade_480.rst.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Problem
BaseConfig::__construct() checks every property of a config class against the environment. It also walks into arrays, down to each leaf value. For each leaf, getEnvValue() tries:
- 4
array_key_exists()checks on$_ENV - 4
array_key_exists()checks on$_SERVER - then, when nothing matched, 4
getenv()calls
Most properties have no env override. So the most common path is also the slowest: 8 failed lookups plus 4 failed getenv() calls for every leaf. The cost grows with the number of config properties, not with the number of env overrides.
The .env file itself is parsed only once, by DotEnv. The waste is the per-property lookup, not the parsing.
This matters most in two cases:
- Requests without config caching (
FactoriesCache). - FrankenPHP Worker Mode.
Factories::reset()runs on every request, so configs are rebuilt, and pay this cost, on every request.
Measurements
PHP 8.3 CLI on macOS. 20 core config classes (App, Database, Mimes, Cache, Logger, Security, Session, Cookie, Filters, Routing, Toolbar, Email, Exceptions, Paths, Kint, Format, Validation, View, Feature, Encryption). That's 287 leaf values. Each number is the time to build all 20, averaged over 500 runs.
| Case | Time |
|---|---|
| Current, env override on | ~0.49 ms |
| Current, env override off (lower bound) | ~0.004 ms |
One failed getenv() call |
~100 ns |
Proposal
Scan the environment once and group keys by the text before their first . or _. Each config class then takes only the keys that match its short prefix (app) or full prefix (Config\App).
- If a class has no matching keys, skip the property walk entirely. This is the common case.
- If it has matching keys, run today's walk unchanged, but look up only that class's small key set. No
getenv()calls.
The current matching rules stay the same:
- The short prefix wins over the full prefix.
$_ENVwins over$_SERVER, which wins overgetenv().- The dot form wins over the underscore form.
- Arrays only accept overrides for keys that already exist in the default.
Staying fresh
The index keeps snapshots of $_ENV and $_SERVER. It compares them with === on each construct.
- PHP arrays are copy-on-write. When nothing has written to them, the snapshot and the superglobal are the same array in memory, so the check takes ~22 ns.
- Any write breaks the match, and the index rebuilds. The rebuild also re-reads
getenv()(~3.3 µs for ~100 vars). BaseConfig::reset()clears the index.
In Worker Mode, $_SERVER is refreshed on every request. So the index rebuilds once per request, on its own. The worker template needs no change.
Compatibility
- Overridden hooks: if a subclass overrides
getEnvValue()orinitEnvValue(), that class falls back to today's full walk with livegetenv()reads. Detection uses reflection once per class, cached for the process. Those classes behave exactly as they do today. - New private state only. No new public or protected API.
Estimated gains
Prototype measured with the same setup as above. "Typical overrides" means app.baseURL plus four database.default.* keys.
| Scenario | Current | Proposed | Change |
|---|---|---|---|
| No env overrides, index built once per request | ~0.50 ms | ~0.05 ms | ~90% less |
| Typical overrides, index built once per request | ~0.49 ms | ~0.11 ms | ~78% less |
| No overrides, index reused (e.g. many builds in one request) | ~0.48 ms | ~0.009 ms | ~98% less |
In absolute terms, this saves about 0.4 ms per request for these 20 classes. Apps that load more config classes, or classes with large arrays, will save more.
Caveats on these numbers:
- They come from a scratch prototype, not the final code.
- The prototype skipped Registrar handling in its timings. The current-code timings include it. With no Registrars, that's a small cost, but it makes the prototype look slightly better than the real thing will.
- They come from a single dev machine. Opcache and JIT settings will shift them.
- Requests served from config caching (
FactoriesCache) already skip this work. They won't gain anything.
Behavior changes (4.8)
- A bare
putenv()call, with no matching$_ENV/$_SERVERwrite, after the first config is built is not seen until the snapshot changes orBaseConfig::reset()runs. - On Windows,
getenv()ignores case. Env vars that exist only in the OS environment will now match case-sensitively. Values from.envalready matched case-sensitively through$_ENV/$_SERVER.
Both will be listed in the v4.8.0 changelog under "Behavior Changes", with a note in upgrade_480.rst.
Out of scope (possible follow-ups)
registerProperties()creates aReflectionClasson every construct just to get the short class name.- The
Encryptioninstanceofcheck runs once per property inside the constructor loop.
- Vorherrschende Sprache
- PHP
- Sterne
- 6k
- Forks
- 2k
- Ø Merge
- 1 T. 22 Std.
- Gemergte PRs (30 T.)
- 59
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Hat eine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus codeigniter4/CodeIgniter4
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
codeigniter4/CodeIgniter4#10616 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 35/100
codeigniter4/CodeIgniter4#10618 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 3/5 1-2 Tage Anfängerfreundlichkeit 62/100
codeigniter4/CodeIgniter4#10617 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
bug
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 48/100
codeigniter4/CodeIgniter4#10589 · 3 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
dev
Schwierigkeit 5/5 Über eine Woche Anfängerfreundlichkeit 25/100
codeigniter4/CodeIgniter4#10427 · 5 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in codeigniter4/CodeIgniter4
Ähnliche Issues
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 60/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100
ProfessionalWiki/NeoWiki#1675 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Incorrect documentationOffen
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 88/100
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 70/100
-
WhatsApp link preview crawler is not detected as a botEvtl. vergeben Ein verknüpfter Pull Request ist offen oder bereits gemergt. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
matomo-org/device-detector#8405 ·
Maintainer antworten meist innerhalb von 1 Tag