Skip to content

Commit 4002862

Browse files
fix(container): use scoped binding for parser
KmlParser holds the loaded SimpleXMLElement as instance state, so the singleton binding kept a parsed document alive across Octane requests and across jobs in a long running queue worker. Any consumer resolving the parser without loading first could read another request's document. A scoped binding keeps the instance stable for the whole request or job (so the documented facade chaining still works) while the container flushes it between lifecycles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013wqTBgVdQQDyAKrchVZu6y
1 parent d768d0b commit 4002862

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

src/KmlParserServiceProvider.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ public function configurePackage(Package $package): void
2121

2222
public function packageRegistered(): void
2323
{
24-
$this->app->singleton(KmlParser::class, function () {
24+
/*
25+
* The parser keeps the loaded document in memory, so a singleton would
26+
* leak that state across requests under Octane and across jobs in a
27+
* long running queue worker. A scoped binding is resolved once per
28+
* request/job lifecycle and flushed in between.
29+
*/
30+
$this->app->scoped(KmlParser::class, function () {
2531
return new KmlParser;
2632
});
2733
}

tests/ServiceProviderTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
use PlinCode\KmlParser\Exceptions\KmlParserException;
4+
use PlinCode\KmlParser\KmlParser;
5+
6+
it('reuses the same parser instance within a single scope', function () {
7+
$parser = app(KmlParser::class);
8+
9+
expect(app(KmlParser::class))->toBe($parser);
10+
});
11+
12+
it('resolves a fresh parser once the scope is flushed', function () {
13+
$parser = app(KmlParser::class);
14+
15+
app()->forgetScopedInstances();
16+
17+
expect(app(KmlParser::class))->not->toBe($parser);
18+
});
19+
20+
it('does not leak a loaded document across scopes', function () {
21+
app(KmlParser::class)->loadFromFile(__DIR__.'/files/kml-example/base.kml');
22+
23+
app()->forgetScopedInstances();
24+
25+
app(KmlParser::class)->getPlacemarks();
26+
})->throws(KmlParserException::class, 'No KML data loaded');

0 commit comments

Comments
 (0)