Skip to content

PHP: a method called on a freshly constructed object, (new Foo())->bar(), ignores the file's use import when several classes share the short name #3244

Open

Description

@holysor

In PHP, a call on a freshly constructed object, (new SettingClient())->getSet(1), is not linked to the class the file imports when several classes in different namespaces share that short name. The same pattern was fixed for JavaScript/TypeScript in #1945; PHP doesn't seem to have an equivalent.

Repro (3 same-named classes, 3 callers)

// src/A/Client/SettingClient.php  (same file in B/ and C/, namespace App\B\Client / App\C\Client)
<?php
namespace App\A\Client;
class SettingClient { public function getSet($id) { return $id; } }

// src/A/Api/FooApi.php
<?php
namespace App\A\Api;
use App\A\Client\SettingClient;
class FooApi { public function run() { return (new SettingClient())->getSet(1); } }

// src/B/Api/BarApi.php
<?php
namespace App\B\Api;
use App\B\Client\SettingClient;
class BarApi { public function run() { $client = new SettingClient(); return $client->getSet(2); } }

// src/C/Api/BazApi.php
<?php
namespace App\C\Api;
use App\C\Client\SettingClient;
class BazApi {
    private $client;
    public function __construct() { $this->client = new SettingClient(); }
    public function run() { return $this->client->getSet(3); }
}

codegraph init && codegraph callers getSet on 1.6.2 (npm), macOS, Node 25.4:

definition callers reported expected
App\A\Client\SettingClient::getSet BarApi::run, BazApi::run ✗ FooApi::run
App\B\Client\SettingClient::getSet (none) ✗ BarApi::run
App\C\Client\SettingClient::getSet (none) ✗ BazApi::run

So on 1.6.2 every call goes to whichever same-named class was indexed first, and the (new Foo())->bar() call in FooApi gets no edge at all.

On main

The Unreleased changelog entry "In PHP, a call on a variable now reaches the method its class really has … through the file's namespace and use imports" looks like it covers BarApi and BazApi (I couldn't build main locally to confirm). But I couldn't find a PHP counterpart for the parenthesized new receiver: in src/extraction/tree-sitter.ts the PHP member_call_expression branch special-cases a scoped_call_expression receiver (Cls::for()->m(), #608) but not an object_creation_expression / parenthesized_expression one, and none of the __tests__/php-*.test.ts files use (new X())->m().

Why it matters

In a real Laravel 7 codebase (~11k first-party PHP files) about 28% of files share a short name with another file — per-module client wrappers like TeamClient exist in 30+ namespaces — and (new XxxClient())->method() is the dominant call style. On 1.6.2, callers/impact for one copy returned 0 callers while grep found 8+, and attributed callers of the other copies to the wrong class.

Expected

Resolve the class in (new Foo())->bar() (and (new Foo)->bar(), (new Foo($x))->bar()) through the file's namespace and use imports, as for a typed variable, and link bar to that class's own or inherited method.

Related: #2975 (same short-name issue for Laravel route → controller).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions