Skip to content

Nested Composer projects are not fully indexed when a root composer.json exists #290

Description

@CodeWithKyrian

Verified on latest main

  • I have confirmed this bug still occurs when built from the latest main branch.

PHPantom version / commit

0.9.0

Installation method

Zed's official PHP extension (auto-downloaded)

Operating system

macOS aarch64 (Apple Silicon)

Editor

Zed

Bug description

PHPantom does not appear to fully discover or index nested Composer projects in a monorepo when the workspace also contains a root-level composer.json. When there's no composer.json, everything works well but if there's one in the root level, things start going haywire.

Classes from the nested projects' vendor directories are initially unresolved. However, manually opening an unresolved vendor class causes that individual class to become recognized immediately.

This suggests that nested vendor dependencies are not discovered during initial Composer indexing, but individual vendor files are parsed lazily when opened and then added to a shared workspace-level symbol index.

Project structure

My actual project is structured approximately like this:

project-root/
├── composer.json
├── vendor/
├── mobile/
│   ├── composer.json
│   ├── composer.lock
│   ├── vendor/
│   ├── app/
│   └── ...
└── platform/
    ├── composer.json
    ├── composer.lock
    ├── vendor/
    ├── app/
    └── ...

The root composer.json mainly proxies Composer commands to the applications in the subdirectories.

mobile and platform are separate Laravel applications. Each has its own dependencies, Composer autoload configuration, lock file, and vendor directory.

Observed behaviour

Suppose a package is installed only in platform:

platform/vendor/vendor-name/package/

When I initially reference one of the package's classes from a file under platform/, PHPantom does not recognize it.

For example:

use VendorName\Package\Component;

$component = new Component();

The class may have no completion, hover information, method information, or go-to-definition support.

If I then manually navigate to and open the corresponding file:

platform/vendor/vendor-name/package/src/Component.php

and return to my application file, PHPantom immediately begins recognizing Component.

Only that opened class becomes available. Other classes, traits, interfaces, or parent classes from the same package remain unresolved until I open their files individually.

For example, if Component references an Element class or a trait internally, those symbols may remain unresolved until I manually open:

platform/vendor/vendor-name/package/src/Element.php

After opening that file, Element also becomes recognized. Restarting the LSP server clears the cached discoveries so I have to manually open the files again to get them indexed.

Steps to reproduce

  1. Create a workspace with a root Composer project and two nested Composer projects:

    test-monorepo/
    ├── composer.json
    ├── mobile/
    │   └── composer.json
    └── platform/
        └── composer.json
    
  2. Initialize both nested directories as Composer projects (or Laravel for a closer reproduction)

  3. Install a package only in platform:

    cd platform
    composer require vendor-name/package
  4. Open the repository root as one workspace in Zed.

  5. Inside a PHP file under platform, reference a class from the installed package.

  6. Observe that the class is not initially resolved.

  7. Manually open the class file inside platform/vendor.

  8. Return to the application file and edit it or otherwise trigger analysis.

  9. Observe that the opened class is now resolved, while other unopened classes from the package may remain unresolved.

  10. Reference the same class from a PHP file under mobile.

  11. Observe that the class may also resolve there, even though the package is not installed in the mobile Composer project.

Actual behaviour

  • Nested Composer dependencies are not completely discovered or indexed.
  • A vendor class becomes recognized only after its source file is manually opened.
  • Opening one class does not necessarily make related classes, traits, interfaces, or parent classes available.
  • Once parsed, a vendor class may become visible throughout the workspace.
  • Classes installed in one nested Composer project may incorrectly resolve from another nested project.

Error output or panic trace


.phpantom.toml

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions