Found while validating the fork on lawnstarter/signup-api for PE-80049 (https://lawnstarter.atlassian.net/browse/PE-80049).
Summary
A static call on a class whose use import points outside the corpus binds to an unrelated same-short-named in-corpus class, minting a wrong INFERRED 0.8 calls edge. The file's use statement explicitly disambiguates the receiver and is ignored.
This is the failure mode of closed issue #16, but on the static-call path rather than the property-typed-receiver path. The use-import-map work (#18–#23, in particular #21 "PhpNameResolver decisive refusal") appears not to cover static receivers.
Real-world instance
In signup-api, app/Providers/AppServiceProvider.php:
use Illuminate\Support\Facades\Event; // L22
...
Event::listen(ProspectCreated::class, ProspectCreatedListener::class); // L147
Illuminate\Support\Facades\Event is a vendor class, outside the corpus. The repo also contains an unrelated App\Segment\Event (a Braze payload model, app/Segment/Event.php). The extractor emits:
{
"relation": "calls",
"confidence": "INFERRED",
"confidence_score": 0.8,
"source_file": "app/Providers/AppServiceProvider.php",
"source_location": "L147",
"source": "app_providers_appserviceprovider_appserviceprovider_boot",
"target": "app_segment_event_event"
}
So graphify affected "App\Segment\Event" reports AppServiceProvider::boot() as a dependent of a Braze model it has nothing to do with — a wrong-node answer for the "who calls X" and "what breaks if X changes" shapes.
Minimal reproduction
Two files:
app/Segment/Event.php
<?php
namespace App\Segment;
class Event
{
public function unrelated(): void {}
}
app/Providers/AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Event;
class AppServiceProvider
{
public function boot(): void
{
Event::listen('some.event', 'SomeListener');
}
}
Then:
graphify update .
python3 -c "import json;g=json.load(open('graphify-out/graph.json'));[print(l) for l in g['links'] if l['relation']=='calls']"
Actual
One calls edge, app_providers_appserviceprovider_appserviceprovider_boot -> app_segment_event_event, INFERRED 0.8.
Expected
No edge. The use Illuminate\Support\Facades\Event; in this file binds the short name Event to an FQN outside the corpus, so the receiver should be refused decisively — the same refusal #21 applies elsewhere. Binding to App\Segment\Event requires ignoring an explicit, unambiguous import in the same file.
Why it matters beyond the false edge
Laravel repos import facades by short name constantly (Event, Cache, Log, Http, Auth, Queue, Config). Any repo that also has a domain class sharing one of those names gets silent cross-domain contamination of its blast-radius answers. In signup-api today only one edge is affected, but the trigger is a naming coincidence rather than anything the author did wrong.
Environment: graphify 0.9.40, fork origin/v8 @ 5e9d95c11837aeabbbb5d90cb45544f49dc58ca4, cold cache.
Found while validating the fork on
lawnstarter/signup-apifor PE-80049 (https://lawnstarter.atlassian.net/browse/PE-80049).Summary
A static call on a class whose
useimport points outside the corpus binds to an unrelated same-short-named in-corpus class, minting a wrongINFERRED0.8callsedge. The file'susestatement explicitly disambiguates the receiver and is ignored.This is the failure mode of closed issue #16, but on the static-call path rather than the property-typed-receiver path. The use-import-map work (#18–#23, in particular #21 "PhpNameResolver decisive refusal") appears not to cover static receivers.
Real-world instance
In
signup-api,app/Providers/AppServiceProvider.php:Illuminate\Support\Facades\Eventis a vendor class, outside the corpus. The repo also contains an unrelatedApp\Segment\Event(a Braze payload model,app/Segment/Event.php). The extractor emits:{ "relation": "calls", "confidence": "INFERRED", "confidence_score": 0.8, "source_file": "app/Providers/AppServiceProvider.php", "source_location": "L147", "source": "app_providers_appserviceprovider_appserviceprovider_boot", "target": "app_segment_event_event" }So
graphify affected "App\Segment\Event"reportsAppServiceProvider::boot()as a dependent of a Braze model it has nothing to do with — a wrong-node answer for the "who calls X" and "what breaks if X changes" shapes.Minimal reproduction
Two files:
app/Segment/Event.phpapp/Providers/AppServiceProvider.phpThen:
Actual
One
callsedge,app_providers_appserviceprovider_appserviceprovider_boot -> app_segment_event_event,INFERRED0.8.Expected
No edge. The
use Illuminate\Support\Facades\Event;in this file binds the short nameEventto an FQN outside the corpus, so the receiver should be refused decisively — the same refusal #21 applies elsewhere. Binding toApp\Segment\Eventrequires ignoring an explicit, unambiguous import in the same file.Why it matters beyond the false edge
Laravel repos import facades by short name constantly (
Event,Cache,Log,Http,Auth,Queue,Config). Any repo that also has a domain class sharing one of those names gets silent cross-domain contamination of its blast-radius answers. Insignup-apitoday only one edge is affected, but the trigger is a naming coincidence rather than anything the author did wrong.Environment:
graphify 0.9.40, forkorigin/v8@5e9d95c11837aeabbbb5d90cb45544f49dc58ca4, cold cache.